mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-02-23 07:15:35 +01:00
Merge branch 'master' into limpbrains-passphrase
This commit is contained in:
commit
5938a8f8c1
208 changed files with 5485 additions and 4275 deletions
75
.flowconfig
75
.flowconfig
|
@ -1,75 +0,0 @@
|
|||
[ignore]
|
||||
; We fork some components by platform
|
||||
.*/*[.]android.js
|
||||
|
||||
; Ignore "BUCK" generated dirs
|
||||
<PROJECT_ROOT>/\.buckd/
|
||||
|
||||
; Ignore polyfills
|
||||
node_modules/react-native/Libraries/polyfills/.*
|
||||
|
||||
; These should not be required directly
|
||||
; require from fbjs/lib instead: require('fbjs/lib/warning')
|
||||
node_modules/warning/.*
|
||||
|
||||
; Flow doesn't support platforms
|
||||
.*/Libraries/Utilities/LoadingView.js
|
||||
|
||||
[untyped]
|
||||
.*/node_modules/@react-native-community/cli/.*/.*
|
||||
|
||||
[include]
|
||||
|
||||
[libs]
|
||||
node_modules/react-native/interface.js
|
||||
node_modules/react-native/flow/
|
||||
|
||||
[options]
|
||||
emoji=true
|
||||
|
||||
esproposal.optional_chaining=enable
|
||||
esproposal.nullish_coalescing=enable
|
||||
|
||||
module.file_ext=.js
|
||||
module.file_ext=.json
|
||||
module.file_ext=.ios.js
|
||||
|
||||
munge_underscores=true
|
||||
|
||||
module.name_mapper='^react-native/\(.*\)$' -> '<PROJECT_ROOT>/node_modules/react-native/\1'
|
||||
module.name_mapper='^@?[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> '<PROJECT_ROOT>/node_modules/react-native/Libraries/Image/RelativeImageStub'
|
||||
|
||||
suppress_type=$FlowIssue
|
||||
suppress_type=$FlowFixMe
|
||||
suppress_type=$FlowFixMeProps
|
||||
suppress_type=$FlowFixMeState
|
||||
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\)
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\)?:? #[0-9]+
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
|
||||
|
||||
[lints]
|
||||
sketchy-null-number=warn
|
||||
sketchy-null-mixed=warn
|
||||
sketchy-number=warn
|
||||
untyped-type-import=warn
|
||||
nonstrict-import=warn
|
||||
deprecated-type=warn
|
||||
unsafe-getters-setters=warn
|
||||
inexact-spread=warn
|
||||
unnecessary-invariant=warn
|
||||
signature-verification-failure=warn
|
||||
deprecated-utility=error
|
||||
|
||||
[strict]
|
||||
deprecated-type
|
||||
nonstrict-import
|
||||
sketchy-null
|
||||
unclear-type
|
||||
unsafe-getters-setters
|
||||
untyped-import
|
||||
untyped-type-import
|
||||
|
||||
[version]
|
||||
^0.122.0
|
||||
|
3
.gitattributes
vendored
3
.gitattributes
vendored
|
@ -1,4 +1,5 @@
|
|||
*.pbxproj -text
|
||||
*.patch -text
|
||||
# specific for windows script files
|
||||
# Windows files should use crlf line endings
|
||||
# https://help.github.com/articles/dealing-with-line-endings/
|
||||
*.bat text eol=crlf
|
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
|
@ -7,7 +7,7 @@ on: [pull_request]
|
|||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: macos-latest
|
||||
runs-on: macos-10.15 # tmp fix for https://github.com/ReactiveCircus/android-emulator-runner/issues/160
|
||||
steps:
|
||||
- name: Checkout project
|
||||
uses: actions/checkout@v2
|
||||
|
@ -44,6 +44,9 @@ jobs:
|
|||
HD_MNEMONIC_BIP49_MANY_TX: ${{ secrets.HD_MNEMONIC_BIP49_MANY_TX }}
|
||||
HD_MNEMONIC_BIP84: ${{ secrets.HD_MNEMONIC_BIP84 }}
|
||||
HD_MNEMONIC_BREAD: ${{ secrets.HD_MNEMONIC_BREAD }}
|
||||
FAULTY_ZPUB: ${{ secrets.FAULTY_ZPUB }}
|
||||
MNEMONICS_COBO: ${{ secrets.MNEMONICS_COBO }}
|
||||
MNEMONICS_COLDCARD: ${{ secrets.MNEMONICS_COLDCARD }}
|
||||
|
||||
e2e:
|
||||
runs-on: macos-latest
|
||||
|
@ -89,6 +92,7 @@ jobs:
|
|||
uses: reactivecircus/android-emulator-runner@v2
|
||||
with:
|
||||
api-level: 29
|
||||
emulator-build: 6110076 # tmp fix for https://github.com/ReactiveCircus/android-emulator-runner/issues/160
|
||||
target: google_apis
|
||||
avd-name: Pixel_API_29_AOSP
|
||||
emulator-options: -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim -camera-back none -camera-front none
|
||||
|
|
|
@ -90,5 +90,3 @@ script:
|
|||
- npm i -g detox-cli
|
||||
- npm run e2e:release-build
|
||||
- npm run e2e:release-test || npm run e2e:release-test || npm run e2e:release-test
|
||||
|
||||
after_failure: ./tests/e2e/upload-artifacts.sh
|
||||
|
|
10
App.js
10
App.js
|
@ -21,8 +21,6 @@ import * as NavigationService from './NavigationService';
|
|||
import { BlueTextCentered, BlueButton, SecondButton } from './BlueComponents';
|
||||
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||
import { Chain } from './models/bitcoinUnits';
|
||||
import QuickActions from 'react-native-quick-actions';
|
||||
import * as Sentry from '@sentry/react-native';
|
||||
import OnAppLaunch from './class/on-app-launch';
|
||||
import DeeplinkSchemaMatch from './class/deeplink-schema-match';
|
||||
import loc from './loc';
|
||||
|
@ -42,12 +40,6 @@ const A = require('./blue_modules/analytics');
|
|||
|
||||
const eventEmitter = new NativeEventEmitter(NativeModules.EventEmitter);
|
||||
|
||||
if (process.env.NODE_ENV !== 'development') {
|
||||
Sentry.init({
|
||||
dsn: 'https://23377936131848ca8003448a893cb622@sentry.io/1295736',
|
||||
});
|
||||
}
|
||||
|
||||
const ClipboardContentType = Object.freeze({
|
||||
BITCOIN: 'BITCOIN',
|
||||
LIGHTNING: 'LIGHTNING',
|
||||
|
@ -125,7 +117,7 @@ const App = () => {
|
|||
Linking.addEventListener('url', handleOpenURL);
|
||||
AppState.addEventListener('change', handleAppStateChange);
|
||||
DeviceEventEmitter.addListener('quickActionShortcut', walletQuickActions);
|
||||
QuickActions.popInitialAction().then(popInitialAction);
|
||||
DeviceQuickActions.popInitialAction().then(popInitialAction);
|
||||
handleAppStateChange(undefined);
|
||||
/*
|
||||
When a notification on iOS is shown while the app is on foreground;
|
||||
|
|
|
@ -15,6 +15,7 @@ const startAndDecrypt = async retry => {
|
|||
console.log('App already has some wallets, so we are in already started state, exiting startAndDecrypt');
|
||||
return true;
|
||||
}
|
||||
await BlueApp.migrateKeys();
|
||||
let password = false;
|
||||
if (await BlueApp.storageIsEncrypted()) {
|
||||
do {
|
||||
|
@ -28,7 +29,7 @@ const startAndDecrypt = async retry => {
|
|||
} catch (error) {
|
||||
// in case of exception reading from keystore, lets retry instead of assuming there is no storage and
|
||||
// proceeding with no wallets
|
||||
console.warn(error);
|
||||
console.warn('exception loading from disk:', error);
|
||||
wasException = true;
|
||||
}
|
||||
|
||||
|
@ -37,7 +38,9 @@ const startAndDecrypt = async retry => {
|
|||
try {
|
||||
await new Promise(resolve => setTimeout(resolve, 3000)); // sleep
|
||||
success = await BlueApp.loadFromDisk(password);
|
||||
} catch (_) {}
|
||||
} catch (error) {
|
||||
console.warn('second exception loading from disk:', error);
|
||||
}
|
||||
}
|
||||
|
||||
if (success) {
|
||||
|
|
|
@ -80,6 +80,7 @@ export const BlueButton = props => {
|
|||
alignItems: 'center',
|
||||
paddingHorizontal: 16,
|
||||
}}
|
||||
accessibilityRole="button"
|
||||
{...props}
|
||||
>
|
||||
<View style={{ flexDirection: 'row', justifyContent: 'center', alignItems: 'center' }}>
|
||||
|
@ -101,6 +102,7 @@ export const SecondButton = forwardRef((props, ref) => {
|
|||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
style={{
|
||||
flex: 1,
|
||||
borderWidth: 0.7,
|
||||
|
@ -127,7 +129,7 @@ export const SecondButton = forwardRef((props, ref) => {
|
|||
export const BitcoinButton = props => {
|
||||
const { colors } = useTheme();
|
||||
return (
|
||||
<TouchableOpacity testID={props.testID} onPress={props.onPress}>
|
||||
<TouchableOpacity accessibilityRole="button" testID={props.testID} onPress={props.onPress}>
|
||||
<View
|
||||
style={{
|
||||
borderColor: (props.active && colors.newBlue) || colors.buttonDisabledBackgroundColor,
|
||||
|
@ -146,8 +148,19 @@ export const BitcoinButton = props => {
|
|||
<Image style={{ width: 34, height: 34, marginRight: 8 }} source={require('./img/addWallet/bitcoin.png')} />
|
||||
</View>
|
||||
<View>
|
||||
<Text style={{ color: colors.newBlue, fontWeight: 'bold', fontSize: 18 }}>{loc.wallets.add_bitcoin}</Text>
|
||||
<Text style={{ color: colors.alternativeTextColor, fontSize: 13, fontWeight: '500' }}>{loc.wallets.add_bitcoin_explain}</Text>
|
||||
<Text style={{ color: colors.newBlue, fontWeight: 'bold', fontSize: 18, writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr' }}>
|
||||
{loc.wallets.add_bitcoin}
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
color: colors.alternativeTextColor,
|
||||
fontSize: 13,
|
||||
fontWeight: '500',
|
||||
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr',
|
||||
}}
|
||||
>
|
||||
{loc.wallets.add_bitcoin_explain}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
@ -158,7 +171,7 @@ export const BitcoinButton = props => {
|
|||
export const VaultButton = props => {
|
||||
const { colors } = useTheme();
|
||||
return (
|
||||
<TouchableOpacity testID={props.testID} onPress={props.onPress}>
|
||||
<TouchableOpacity accessibilityRole="button" testID={props.testID} onPress={props.onPress}>
|
||||
<View
|
||||
style={{
|
||||
borderColor: (props.active && colors.foregroundColor) || colors.buttonDisabledBackgroundColor,
|
||||
|
@ -176,8 +189,24 @@ export const VaultButton = props => {
|
|||
<Image style={{ width: 34, height: 34, marginRight: 8 }} source={require('./img/addWallet/vault.png')} />
|
||||
</View>
|
||||
<View>
|
||||
<Text style={{ color: colors.foregroundColor, fontWeight: 'bold', fontSize: 18 }}>{loc.multisig.multisig_vault}</Text>
|
||||
<Text style={{ color: colors.alternativeTextColor, fontSize: 13, fontWeight: '500' }}>
|
||||
<Text
|
||||
style={{
|
||||
color: colors.foregroundColor,
|
||||
fontWeight: 'bold',
|
||||
fontSize: 18,
|
||||
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr',
|
||||
}}
|
||||
>
|
||||
{loc.multisig.multisig_vault}
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
color: colors.alternativeTextColor,
|
||||
fontSize: 13,
|
||||
fontWeight: '500',
|
||||
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr',
|
||||
}}
|
||||
>
|
||||
{loc.multisig.multisig_vault_explain}
|
||||
</Text>
|
||||
</View>
|
||||
|
@ -190,7 +219,7 @@ export const VaultButton = props => {
|
|||
export const LightningButton = props => {
|
||||
const { colors } = useTheme();
|
||||
return (
|
||||
<TouchableOpacity onPress={props.onPress}>
|
||||
<TouchableOpacity accessibilityRole="button" onPress={props.onPress}>
|
||||
<View
|
||||
style={{
|
||||
borderColor: (props.active && colors.lnborderColor) || colors.buttonDisabledBackgroundColor,
|
||||
|
@ -209,8 +238,21 @@ export const LightningButton = props => {
|
|||
<Image style={{ width: 34, height: 34, marginRight: 8 }} source={require('./img/addWallet/lightning.png')} />
|
||||
</View>
|
||||
<View>
|
||||
<Text style={{ color: colors.lnborderColor, fontWeight: 'bold', fontSize: 18 }}>{loc.wallets.add_lightning}</Text>
|
||||
<Text style={{ color: colors.alternativeTextColor, fontSize: 13, fontWeight: '500' }}>{loc.wallets.add_lightning_explain}</Text>
|
||||
<Text
|
||||
style={{ color: colors.lnborderColor, fontWeight: 'bold', fontSize: 18, writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr' }}
|
||||
>
|
||||
{loc.wallets.add_lightning}
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
color: colors.alternativeTextColor,
|
||||
fontSize: 13,
|
||||
fontWeight: '500',
|
||||
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr',
|
||||
}}
|
||||
>
|
||||
{loc.wallets.add_lightning_explain}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
@ -383,6 +425,7 @@ export class BlueWalletNavigationHeader extends Component {
|
|||
}
|
||||
/>
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
style={styles.balance}
|
||||
onPress={this.changeWalletBalanceUnit}
|
||||
ref={this.walletBalanceText}
|
||||
|
@ -409,7 +452,7 @@ export class BlueWalletNavigationHeader extends Component {
|
|||
)}
|
||||
</TouchableOpacity>
|
||||
{this.state.wallet.type === LightningCustodianWallet.type && this.state.allowOnchainAddress && (
|
||||
<TouchableOpacity onPress={this.manageFundsPressed}>
|
||||
<TouchableOpacity accessibilityRole="button" onPress={this.manageFundsPressed}>
|
||||
<View
|
||||
style={{
|
||||
marginTop: 14,
|
||||
|
@ -437,7 +480,7 @@ export class BlueWalletNavigationHeader extends Component {
|
|||
</TouchableOpacity>
|
||||
)}
|
||||
{this.state.wallet.type === MultisigHDWallet.type && (
|
||||
<TouchableOpacity onPress={this.manageFundsPressed}>
|
||||
<TouchableOpacity accessibilityRole="button" onPress={this.manageFundsPressed}>
|
||||
<View
|
||||
style={{
|
||||
marginTop: 14,
|
||||
|
@ -473,6 +516,7 @@ export const BlueButtonLink = forwardRef((props, ref) => {
|
|||
const { colors } = useTheme();
|
||||
return (
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
style={{
|
||||
minHeight: 60,
|
||||
minWidth: 100,
|
||||
|
@ -517,7 +561,7 @@ export const BluePrivateBalance = () => {
|
|||
|
||||
export const BlueCopyToClipboardButton = ({ stringToCopy, displayText = false }) => {
|
||||
return (
|
||||
<TouchableOpacity onPress={() => Clipboard.setString(stringToCopy)}>
|
||||
<TouchableOpacity accessibilityRole="button" onPress={() => Clipboard.setString(stringToCopy)}>
|
||||
<Text style={{ fontSize: 13, fontWeight: '400', color: '#68bbe1' }}>{displayText || loc.transactions.details_copy}</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
|
@ -559,7 +603,12 @@ export class BlueCopyTextToClipboard extends Component {
|
|||
render() {
|
||||
return (
|
||||
<View style={{ justifyContent: 'center', alignItems: 'center', paddingHorizontal: 16 }}>
|
||||
<TouchableOpacity onPress={this.copyToClipboard} disabled={this.state.hasTappedText} testID="BlueCopyTextToClipboard">
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
onPress={this.copyToClipboard}
|
||||
disabled={this.state.hasTappedText}
|
||||
testID="BlueCopyTextToClipboard"
|
||||
>
|
||||
<Animated.Text style={styleCopyTextToClipboard.address} numberOfLines={0}>
|
||||
{this.state.address}
|
||||
</Animated.Text>
|
||||
|
@ -596,7 +645,9 @@ export const BlueText = props => {
|
|||
{...props}
|
||||
style={{
|
||||
color: colors.foregroundColor,
|
||||
|
||||
...props.style,
|
||||
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
@ -670,7 +721,17 @@ export const BlueListItem = React.memo(props => {
|
|||
export const BlueFormLabel = props => {
|
||||
const { colors } = useTheme();
|
||||
|
||||
return <Text {...props} style={{ color: colors.foregroundColor, fontWeight: '400', marginHorizontal: 20 }} />;
|
||||
return (
|
||||
<Text
|
||||
{...props}
|
||||
style={{
|
||||
color: colors.foregroundColor,
|
||||
fontWeight: '400',
|
||||
marginHorizontal: 20,
|
||||
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const BlueFormInput = props => {
|
||||
|
@ -1141,7 +1202,7 @@ export const BlueReceiveButtonIcon = props => {
|
|||
const { colors } = useTheme();
|
||||
|
||||
return (
|
||||
<TouchableOpacity {...props} style={{ flex: 1 }}>
|
||||
<TouchableOpacity accessibilityRole="button" {...props} style={{ flex: 1 }}>
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
|
@ -1349,7 +1410,7 @@ export const BlueTransactionListItem = React.memo(({ item, itemPriceUnit = Bitco
|
|||
if (item.hash) {
|
||||
navigate('TransactionStatus', { hash: item.hash });
|
||||
} else if (item.type === 'user_invoice' || item.type === 'payment_request' || item.type === 'paid_invoice') {
|
||||
const lightningWallet = wallets.filter(wallet => wallet?.getSecret() === item.fromWallet);
|
||||
const lightningWallet = wallets.filter(wallet => wallet?.getID() === item.walletID);
|
||||
if (lightningWallet.length === 1) {
|
||||
try {
|
||||
// is it a successful lnurl-pay?
|
||||
|
@ -1576,6 +1637,7 @@ export class BlueReplaceFeeSuggestions extends Component {
|
|||
},
|
||||
].map(({ label, type, time, rate, active }, index) => (
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
key={label}
|
||||
onPress={() => this.onFeeSelected(type)}
|
||||
style={[
|
||||
|
@ -1602,6 +1664,7 @@ export class BlueReplaceFeeSuggestions extends Component {
|
|||
</TouchableOpacity>
|
||||
))}
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
onPress={() => this.customTextInput.focus()}
|
||||
style={[
|
||||
{ paddingHorizontal: 16, paddingVertical: 8, marginBottom: 10 },
|
||||
|
@ -1700,6 +1763,7 @@ export const BlueTabs = ({ active, onSwitch, tabs }) => (
|
|||
{tabs.map((Tab, i) => (
|
||||
<TouchableOpacity
|
||||
key={i}
|
||||
accessibilityRole="button"
|
||||
onPress={() => onSwitch(i)}
|
||||
style={[
|
||||
tabsStyles.tabRoot,
|
||||
|
@ -1807,18 +1871,21 @@ export class DynamicQRCode extends Component {
|
|||
<BlueSpacing20 />
|
||||
<View style={animatedQRCodeStyle.controller}>
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
style={[animatedQRCodeStyle.button, { width: '25%', alignItems: 'flex-start' }]}
|
||||
onPress={this.moveToPreviousFragment}
|
||||
>
|
||||
<Text style={animatedQRCodeStyle.text}>{loc.send.dynamic_prev}</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
style={[animatedQRCodeStyle.button, { width: '50%' }]}
|
||||
onPress={this.state.intervalHandler ? this.stopAutoMove : this.startAutoMove}
|
||||
>
|
||||
<Text style={animatedQRCodeStyle.text}>{this.state.intervalHandler ? loc.send.dynamic_stop : loc.send.dynamic_start}</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
style={[animatedQRCodeStyle.button, { width: '25%', alignItems: 'flex-end' }]}
|
||||
onPress={this.moveToNextFragment}
|
||||
>
|
||||
|
|
|
@ -80,7 +80,7 @@ import LnurlPay from './screen/lnd/lnurlPay';
|
|||
import LnurlPaySuccess from './screen/lnd/lnurlPaySuccess';
|
||||
import UnlockWith from './UnlockWith';
|
||||
import DrawerList from './screen/wallets/drawerList';
|
||||
import { isCatalyst, isTablet } from './blue_modules/environment';
|
||||
import { isDesktop, isTablet } from './blue_modules/environment';
|
||||
import SettingsPrivacy from './screen/settings/SettingsPrivacy';
|
||||
import LNDViewAdditionalInvoicePreImage from './screen/lnd/lndViewAdditionalInvoicePreImage';
|
||||
|
||||
|
@ -89,7 +89,6 @@ const defaultScreenOptions =
|
|||
? ({ route, navigation }) => ({
|
||||
gestureEnabled: true,
|
||||
cardOverlayEnabled: true,
|
||||
cardStyle: { backgroundColor: '#FFFFFF' },
|
||||
headerStatusBarHeight: navigation.dangerouslyGetState().routes.indexOf(route) > 0 ? 10 : undefined,
|
||||
...TransitionPresets.ModalPresentationIOS,
|
||||
gestureResponseDistance: { vertical: Dimensions.get('window').height, horizontal: 50 },
|
||||
|
@ -104,7 +103,6 @@ const defaultStackScreenOptions =
|
|||
? {
|
||||
gestureEnabled: true,
|
||||
cardOverlayEnabled: true,
|
||||
cardStyle: { backgroundColor: '#FFFFFF' },
|
||||
headerStatusBarHeight: 10,
|
||||
}
|
||||
: {
|
||||
|
@ -349,7 +347,7 @@ const Drawer = createDrawerNavigator();
|
|||
function DrawerRoot() {
|
||||
const dimensions = useWindowDimensions();
|
||||
const isLargeScreen =
|
||||
Platform.OS === 'android' ? isTablet() : dimensions.width >= Dimensions.get('screen').width / 2 && (isTablet() || isCatalyst);
|
||||
Platform.OS === 'android' ? isTablet() : dimensions.width >= Dimensions.get('screen').width / 2 && (isTablet() || isDesktop);
|
||||
const drawerStyle = { width: '0%' };
|
||||
|
||||
return (
|
||||
|
|
|
@ -97,13 +97,13 @@ const UnlockWith = () => {
|
|||
const color = colorScheme === 'dark' ? '#FFFFFF' : '#000000';
|
||||
if ((biometricType === Biometric.TouchID || biometricType === Biometric.Biometrics) && !isStorageEncryptedEnabled) {
|
||||
return (
|
||||
<TouchableOpacity disabled={isAuthenticating} onPress={unlockWithBiometrics}>
|
||||
<TouchableOpacity accessibilityRole="button" disabled={isAuthenticating} onPress={unlockWithBiometrics}>
|
||||
<Icon name="fingerprint" size={64} type="font-awesome5" color={color} />
|
||||
</TouchableOpacity>
|
||||
);
|
||||
} else if (biometricType === Biometric.FaceID && !isStorageEncryptedEnabled) {
|
||||
return (
|
||||
<TouchableOpacity disabled={isAuthenticating} onPress={unlockWithBiometrics}>
|
||||
<TouchableOpacity accessibilityRole="button" disabled={isAuthenticating} onPress={unlockWithBiometrics}>
|
||||
<Image
|
||||
source={colorScheme === 'dark' ? require('./img/faceid-default.png') : require('./img/faceid-dark.png')}
|
||||
style={styles.icon}
|
||||
|
@ -112,7 +112,7 @@ const UnlockWith = () => {
|
|||
);
|
||||
} else if (isStorageEncryptedEnabled) {
|
||||
return (
|
||||
<TouchableOpacity disabled={isAuthenticating} onPress={unlockWithKey}>
|
||||
<TouchableOpacity accessibilityRole="button" disabled={isAuthenticating} onPress={unlockWithKey}>
|
||||
<Icon name="lock" size={64} type="font-awesome5" color={color} />
|
||||
</TouchableOpacity>
|
||||
);
|
||||
|
|
|
@ -69,11 +69,17 @@ function WatchConnectivity() {
|
|||
if (message.request === 'createInvoice') {
|
||||
handleLightningInvoiceCreateRequest(message.walletIndex, message.amount, message.description)
|
||||
.then(createInvoiceRequest => reply({ invoicePaymentRequest: createInvoiceRequest }))
|
||||
.catch(e => console.log(e));
|
||||
.catch(e => {
|
||||
console.log(e);
|
||||
reply({});
|
||||
});
|
||||
} else if (message.message === 'sendApplicationContext') {
|
||||
sendWalletsToWatch();
|
||||
reply({});
|
||||
} else if (message.message === 'fetchTransactions') {
|
||||
fetchWalletTransactions().then(() => saveToDisk());
|
||||
fetchWalletTransactions()
|
||||
.then(() => saveToDisk())
|
||||
.finally(() => reply({}));
|
||||
} else if (message.message === 'hideBalance') {
|
||||
const walletIndex = message.walletIndex;
|
||||
const wallet = wallets[walletIndex];
|
||||
|
@ -110,7 +116,7 @@ function WatchConnectivity() {
|
|||
if (!Array.isArray(wallets)) {
|
||||
console.log('No Wallets set to sync with Watch app. Exiting...');
|
||||
return;
|
||||
} else if (wallets.length === 0) {
|
||||
} else if (walletsInitialized && wallets.length === 0) {
|
||||
console.log('Wallets array is set. No Wallets set to sync with Watch app. Exiting...');
|
||||
updateApplicationContext({ wallets: [], randomID: Math.floor(Math.random() * 11) });
|
||||
return;
|
||||
|
@ -207,7 +213,7 @@ function WatchConnectivity() {
|
|||
hideBalance: wallet.hideBalance,
|
||||
};
|
||||
if (wallet.chain === Chain.ONCHAIN && wallet.type !== MultisigHDWallet.type) {
|
||||
walletInformation.push({ xpub: wallet.getXpub() ? wallet.getXpub() : wallet.getSecret() });
|
||||
walletInformation.xpub = wallet.getXpub() ? wallet.getXpub() : wallet.getSecret();
|
||||
}
|
||||
walletsToProcess.push(walletInformation);
|
||||
}
|
||||
|
|
4
_editorconfig
Normal file
4
_editorconfig
Normal file
|
@ -0,0 +1,4 @@
|
|||
# Windows files
|
||||
[*.bat]
|
||||
end_of_line = crlf
|
||||
|
|
@ -20,7 +20,7 @@ import com.android.build.OutputFile
|
|||
* // default. Can be overridden with ENTRY_FILE environment variable.
|
||||
* entryFile: "index.android.js",
|
||||
*
|
||||
* // https://facebook.github.io/react-native/docs/performance#enable-the-ram-format
|
||||
* // https://reactnative.dev/docs/performance#enable-the-ram-format
|
||||
* bundleCommand: "ram-bundle",
|
||||
*
|
||||
* // whether to bundle JS and assets in debug mode
|
||||
|
@ -121,7 +121,10 @@ def jscFlavor = 'org.webkit:android-jsc-intl:+'
|
|||
def enableHermes = project.ext.react.get("enableHermes", false);
|
||||
|
||||
android {
|
||||
ndkVersion rootProject.ext.ndkVersion
|
||||
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
|
@ -136,7 +139,7 @@ android {
|
|||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 1
|
||||
versionName "6.1.3"
|
||||
versionName "6.1.7"
|
||||
multiDexEnabled true
|
||||
missingDimensionStrategy 'react-native-camera', 'general'
|
||||
testBuildType System.getProperty('testBuildType', 'debug') // This will later be used to control the test apk build type
|
||||
|
@ -150,10 +153,11 @@ android {
|
|||
include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
// Caution! In production, you need to generate your own keystore file.
|
||||
// see https://facebook.github.io/react-native/docs/signed-apk-android.
|
||||
// see https://reactnative.dev/docs/signed-apk-android.
|
||||
minifyEnabled enableProguardInReleaseBuilds
|
||||
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
|
||||
}
|
||||
|
@ -164,11 +168,11 @@ android {
|
|||
variant.outputs.each { output ->
|
||||
// For each separate APK per architecture, set a unique version code as described here:
|
||||
// https://developer.android.com/studio/build/configure-apk-splits.html
|
||||
// Example: versionCode 1 will generate 1001 for armeabi-v7a, 1002 for x86, etc.
|
||||
def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
|
||||
def abi = output.getFilter(OutputFile.ABI)
|
||||
if (abi != null) { // null for the universal-debug, universal-release variants
|
||||
output.versionCodeOverride =
|
||||
versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
|
||||
output.versionCodeOverride = versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -177,13 +181,8 @@ android {
|
|||
|
||||
dependencies {
|
||||
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||
implementation files("../../node_modules/react-native-tor/android/libs/sifir_android.aar")
|
||||
//noinspection GradleDynamicVersion
|
||||
|
||||
androidTestImplementation('com.wix:detox:+') {
|
||||
exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jdk8'
|
||||
}
|
||||
|
||||
implementation files("../../node_modules/react-native-tor/android/libs/sifir_android.aar")
|
||||
implementation "com.facebook.react:react-native:+" // From node_modules
|
||||
|
||||
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
|
||||
|
@ -194,12 +193,17 @@ dependencies {
|
|||
|
||||
debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
|
||||
exclude group:'com.facebook.flipper'
|
||||
exclude group:'com.squareup.okhttp3', module:'okhttp'
|
||||
}
|
||||
|
||||
debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") {
|
||||
exclude group:'com.facebook.flipper'
|
||||
}
|
||||
|
||||
androidTestImplementation('com.wix:detox:+') {
|
||||
exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jdk8'
|
||||
}
|
||||
|
||||
if (enableHermes) {
|
||||
def hermesPath = "../../node_modules/hermes-engine/android/";
|
||||
debugImplementation files(hermesPath + "hermes-debug.aar")
|
||||
|
@ -217,4 +221,4 @@ task copyDownloadableDepsToLibs(type: Copy) {
|
|||
}
|
||||
|
||||
apply plugin: 'com.google.gms.google-services' // Google Services plugin
|
||||
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
|
||||
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
|
|
@ -5,7 +5,8 @@
|
|||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||
<uses-permission android:name="android.permission.CAMERA"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="28" />
|
||||
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
||||
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
|
||||
<application
|
||||
|
@ -15,7 +16,7 @@
|
|||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:allowBackup="false"
|
||||
android:extractNativeLibs="true"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:requestLegacyExternalStorage="false"
|
||||
android:usesCleartextTraffic="true"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="android:textColor">#000000</item>
|
||||
</style>
|
||||
|
|
|
@ -11,14 +11,15 @@ buildscript {
|
|||
googlePlayServicesIidVersion = "16.0.1"
|
||||
firebaseVersion = "17.3.4"
|
||||
firebaseMessagingVersion = "20.2.1"
|
||||
ndkVersion = "20.1.5948944"
|
||||
}
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
ext.kotlinVersion = '1.4.32'
|
||||
dependencies {
|
||||
classpath('com.android.tools.build:gradle:4.0.1')
|
||||
classpath('com.android.tools.build:gradle:4.2.1')
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
|
||||
classpath 'com.google.gms:google-services:4.3.5' // Google Services plugin
|
||||
|
||||
|
@ -30,6 +31,25 @@ buildscript {
|
|||
|
||||
allprojects {
|
||||
repositories {
|
||||
jcenter() {
|
||||
content {
|
||||
includeModule("com.facebook.yoga", "proguard-annotations")
|
||||
includeModule("com.facebook.fbjni", "fbjni-java-only")
|
||||
includeModule("com.facebook.fresco", "fresco")
|
||||
includeModule("com.facebook.fresco", "stetho")
|
||||
includeModule("com.facebook.fresco", "fbcore")
|
||||
includeModule("com.facebook.fresco", "drawee")
|
||||
includeModule("com.facebook.fresco", "imagepipeline")
|
||||
includeModule("com.facebook.fresco", "imagepipeline-native")
|
||||
includeModule("com.facebook.fresco", "memory-type-native")
|
||||
includeModule("com.facebook.fresco", "memory-type-java")
|
||||
includeModule("com.facebook.fresco", "nativeimagefilters")
|
||||
includeModule("com.facebook.stetho", "stetho")
|
||||
includeModule("com.wei.android.lib", "fingerprintidentify")
|
||||
includeModule("com.eightbitlab", "blurview")
|
||||
}
|
||||
}
|
||||
mavenCentral()
|
||||
mavenLocal()
|
||||
maven {
|
||||
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
|
||||
|
@ -44,7 +64,6 @@ allprojects {
|
|||
url "$rootDir/../node_modules/detox/Detox-android"
|
||||
}
|
||||
google()
|
||||
jcenter()
|
||||
maven { url 'https://www.jitpack.io' }
|
||||
}
|
||||
}
|
||||
|
@ -54,6 +73,9 @@ subprojects {
|
|||
if (project.hasProperty("android")) {
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
defaultConfig {
|
||||
minSdkVersion 28
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,4 +31,4 @@ org.gradle.configureondemand=true
|
|||
org.gradle.jvmargs=-Xmx4g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||
|
||||
# Version of flipper SDK to use with React Native
|
||||
FLIPPER_VERSION=0.54.0
|
||||
FLIPPER_VERSION=0.75.1
|
|
@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
|||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
|
||||
|
|
7
android/gradlew.bat
vendored
7
android/gradlew.bat
vendored
|
@ -37,7 +37,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
|||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
@ -51,7 +51,7 @@ goto fail
|
|||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
|
@ -82,8 +82,7 @@ set CMD_LINE_ARGS=%*
|
|||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
|
|
@ -18,13 +18,7 @@ if [ -f $FILENAME ]; then
|
|||
PR=`node scripts/appcenter-post-build-get-pr-number.js`
|
||||
echo PR: $PR
|
||||
|
||||
# uploading file
|
||||
HASH=`date +%s`
|
||||
FILENAME_UNIQ="$APPCENTER_OUTPUT_DIRECTORY/$BRANCH-$HASH.apk"
|
||||
cp "$FILENAME" "$FILENAME_UNIQ"
|
||||
curl "http://filestorage.bluewallet.io:1488/upload.php" -F "fileToUpload=@$FILENAME_UNIQ"
|
||||
rm "$FILENAME_UNIQ"
|
||||
DLOAD_APK="http://filestorage.bluewallet.io:1488/$BRANCH-$HASH.apk"
|
||||
DLOAD_APK="https://lambda-download-android-build.herokuapp.com/download/$BUILD_BUILDID"
|
||||
|
||||
curl -X POST --data "{\"body\":\"♫ This was a triumph. I'm making a note here: HUGE SUCCESS ♫\n\n [android in browser] $APPURL\n\n[download apk]($DLOAD_APK) \"}" -u "$GITHUB" "https://api.github.com/repos/BlueWallet/BlueWallet/issues/$PR/comments"
|
||||
fi
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* global alert */
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
import { Alert } from 'react-native';
|
||||
import { AppStorage, LegacyWallet, SegwitBech32Wallet, SegwitP2SHWallet } from '../class';
|
||||
import { LegacyWallet, SegwitBech32Wallet, SegwitP2SHWallet } from '../class';
|
||||
import DefaultPreference from 'react-native-default-preference';
|
||||
import RNWidgetCenter from 'react-native-widget-center';
|
||||
import loc from '../loc';
|
||||
|
@ -12,6 +12,11 @@ const BigNumber = require('bignumber.js');
|
|||
const torrific = require('../blue_modules/torrific');
|
||||
const Realm = require('realm');
|
||||
|
||||
const ELECTRUM_HOST = 'electrum_host';
|
||||
const ELECTRUM_TCP_PORT = 'electrum_tcp_port';
|
||||
const ELECTRUM_SSL_PORT = 'electrum_ssl_port';
|
||||
const ELECTRUM_SERVER_HISTORY = 'electrum_server_history';
|
||||
|
||||
let _realm;
|
||||
async function _getRealm() {
|
||||
if (_realm) return _realm;
|
||||
|
@ -79,13 +84,13 @@ async function connectMain() {
|
|||
try {
|
||||
if (usingPeer.host.endsWith('onion')) {
|
||||
const randomPeer = await getRandomHardcodedPeer();
|
||||
await DefaultPreference.set(AppStorage.ELECTRUM_HOST, randomPeer.host);
|
||||
await DefaultPreference.set(AppStorage.ELECTRUM_TCP_PORT, randomPeer.tcp);
|
||||
await DefaultPreference.set(AppStorage.ELECTRUM_SSL_PORT, randomPeer.ssl);
|
||||
await DefaultPreference.set(ELECTRUM_HOST, randomPeer.host);
|
||||
await DefaultPreference.set(ELECTRUM_TCP_PORT, randomPeer.tcp);
|
||||
await DefaultPreference.set(ELECTRUM_SSL_PORT, randomPeer.ssl);
|
||||
} else {
|
||||
await DefaultPreference.set(AppStorage.ELECTRUM_HOST, usingPeer.host);
|
||||
await DefaultPreference.set(AppStorage.ELECTRUM_TCP_PORT, usingPeer.tcp);
|
||||
await DefaultPreference.set(AppStorage.ELECTRUM_SSL_PORT, usingPeer.ssl);
|
||||
await DefaultPreference.set(ELECTRUM_HOST, usingPeer.host);
|
||||
await DefaultPreference.set(ELECTRUM_TCP_PORT, usingPeer.tcp);
|
||||
await DefaultPreference.set(ELECTRUM_SSL_PORT, usingPeer.ssl);
|
||||
}
|
||||
|
||||
RNWidgetCenter.reloadAllTimelines();
|
||||
|
@ -187,14 +192,14 @@ async function presentNetworkErrorAlert(usingPeer) {
|
|||
text: loc._.ok,
|
||||
style: 'destructive',
|
||||
onPress: async () => {
|
||||
await AsyncStorage.setItem(AppStorage.ELECTRUM_HOST, '');
|
||||
await AsyncStorage.setItem(AppStorage.ELECTRUM_TCP_PORT, '');
|
||||
await AsyncStorage.setItem(AppStorage.ELECTRUM_SSL_PORT, '');
|
||||
await AsyncStorage.setItem(ELECTRUM_HOST, '');
|
||||
await AsyncStorage.setItem(ELECTRUM_TCP_PORT, '');
|
||||
await AsyncStorage.setItem(ELECTRUM_SSL_PORT, '');
|
||||
try {
|
||||
await DefaultPreference.setName('group.io.bluewallet.bluewallet');
|
||||
await DefaultPreference.clear(AppStorage.ELECTRUM_HOST);
|
||||
await DefaultPreference.clear(AppStorage.ELECTRUM_SSL_PORT);
|
||||
await DefaultPreference.clear(AppStorage.ELECTRUM_TCP_PORT);
|
||||
await DefaultPreference.clear(ELECTRUM_HOST);
|
||||
await DefaultPreference.clear(ELECTRUM_SSL_PORT);
|
||||
await DefaultPreference.clear(ELECTRUM_TCP_PORT);
|
||||
RNWidgetCenter.reloadAllTimelines();
|
||||
} catch (e) {
|
||||
// Must be running on Android
|
||||
|
@ -236,9 +241,9 @@ async function getRandomHardcodedPeer() {
|
|||
}
|
||||
|
||||
async function getSavedPeer() {
|
||||
const host = await AsyncStorage.getItem(AppStorage.ELECTRUM_HOST);
|
||||
const port = await AsyncStorage.getItem(AppStorage.ELECTRUM_TCP_PORT);
|
||||
const sslPort = await AsyncStorage.getItem(AppStorage.ELECTRUM_SSL_PORT);
|
||||
const host = await AsyncStorage.getItem(ELECTRUM_HOST);
|
||||
const port = await AsyncStorage.getItem(ELECTRUM_TCP_PORT);
|
||||
const sslPort = await AsyncStorage.getItem(ELECTRUM_SSL_PORT);
|
||||
return { host, tcp: port, ssl: sslPort };
|
||||
}
|
||||
|
||||
|
@ -845,6 +850,10 @@ module.exports.setBatchingEnabled = () => {
|
|||
|
||||
module.exports.hardcodedPeers = hardcodedPeers;
|
||||
module.exports.getRandomHardcodedPeer = getRandomHardcodedPeer;
|
||||
module.exports.ELECTRUM_HOST = ELECTRUM_HOST;
|
||||
module.exports.ELECTRUM_TCP_PORT = ELECTRUM_TCP_PORT;
|
||||
module.exports.ELECTRUM_SSL_PORT = ELECTRUM_SSL_PORT;
|
||||
module.exports.ELECTRUM_SERVER_HISTORY = ELECTRUM_SERVER_HISTORY;
|
||||
|
||||
const splitIntoChunks = function (arr, chunkSize) {
|
||||
const groups = [];
|
||||
|
|
|
@ -1,9 +1,16 @@
|
|||
import * as Sentry from '@sentry/react-native';
|
||||
import amplitude from 'amplitude-js';
|
||||
import { getVersion, getSystemName } from 'react-native-device-info';
|
||||
import { getVersion, getSystemName, getUniqueId } from 'react-native-device-info';
|
||||
import { Platform } from 'react-native';
|
||||
const BlueApp = require('../BlueApp');
|
||||
|
||||
if (process.env.NODE_ENV !== 'development') {
|
||||
Sentry.init({
|
||||
dsn: 'https://23377936131848ca8003448a893cb622@sentry.io/1295736',
|
||||
});
|
||||
Sentry.setUser({ id: getUniqueId() });
|
||||
}
|
||||
|
||||
amplitude.getInstance().init('8b7cf19e8eea3cdcf16340f5fbf16330', null, {
|
||||
useNativeDeviceInfo: true,
|
||||
platform: getSystemName().toLocaleLowerCase().includes('mac') ? getSystemName() : Platform.OS,
|
||||
|
|
|
@ -4,9 +4,11 @@ import RNWidgetCenter from 'react-native-widget-center';
|
|||
import * as RNLocalize from 'react-native-localize';
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
import { AppStorage } from '../class';
|
||||
import { FiatUnit, getFiatRate } from '../models/fiatUnit';
|
||||
|
||||
const PREFERRED_CURRENCY = 'preferredCurrency';
|
||||
const EXCHANGE_RATES = 'currency';
|
||||
|
||||
let preferredFiatCurrency = FiatUnit.USD;
|
||||
const exchangeRates = {};
|
||||
|
||||
|
@ -22,7 +24,7 @@ const STRUCT = {
|
|||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function setPrefferedCurrency(item) {
|
||||
await AsyncStorage.setItem(AppStorage.PREFERRED_CURRENCY, JSON.stringify(item));
|
||||
await AsyncStorage.setItem(PREFERRED_CURRENCY, JSON.stringify(item));
|
||||
await DefaultPreference.setName('group.io.bluewallet.bluewallet');
|
||||
await DefaultPreference.set('preferredCurrency', item.endPointKey);
|
||||
await DefaultPreference.set('preferredCurrencyLocale', item.locale.replace('-', '_'));
|
||||
|
@ -30,7 +32,7 @@ async function setPrefferedCurrency(item) {
|
|||
}
|
||||
|
||||
async function getPreferredCurrency() {
|
||||
const preferredCurrency = await JSON.parse(await AsyncStorage.getItem(AppStorage.PREFERRED_CURRENCY));
|
||||
const preferredCurrency = await JSON.parse(await AsyncStorage.getItem(PREFERRED_CURRENCY));
|
||||
await DefaultPreference.setName('group.io.bluewallet.bluewallet');
|
||||
await DefaultPreference.set('preferredCurrency', preferredCurrency.endPointKey);
|
||||
await DefaultPreference.set('preferredCurrencyLocale', preferredCurrency.locale.replace('-', '_'));
|
||||
|
@ -44,7 +46,7 @@ async function updateExchangeRate() {
|
|||
}
|
||||
|
||||
try {
|
||||
preferredFiatCurrency = JSON.parse(await AsyncStorage.getItem(AppStorage.PREFERRED_CURRENCY));
|
||||
preferredFiatCurrency = JSON.parse(await AsyncStorage.getItem(PREFERRED_CURRENCY));
|
||||
if (preferredFiatCurrency === null) {
|
||||
throw Error('No Preferred Fiat selected');
|
||||
}
|
||||
|
@ -61,15 +63,15 @@ async function updateExchangeRate() {
|
|||
try {
|
||||
rate = await getFiatRate(preferredFiatCurrency.endPointKey);
|
||||
} catch (Err) {
|
||||
const lastSavedExchangeRate = JSON.parse(await AsyncStorage.getItem(AppStorage.EXCHANGE_RATES));
|
||||
const lastSavedExchangeRate = JSON.parse(await AsyncStorage.getItem(EXCHANGE_RATES));
|
||||
exchangeRates['BTC_' + preferredFiatCurrency.endPointKey] = lastSavedExchangeRate['BTC_' + preferredFiatCurrency.endPointKey] * 1;
|
||||
return;
|
||||
}
|
||||
|
||||
exchangeRates[STRUCT.LAST_UPDATED] = +new Date();
|
||||
exchangeRates['BTC_' + preferredFiatCurrency.endPointKey] = rate;
|
||||
await AsyncStorage.setItem(AppStorage.EXCHANGE_RATES, JSON.stringify(exchangeRates));
|
||||
await AsyncStorage.setItem(AppStorage.PREFERRED_CURRENCY, JSON.stringify(preferredFiatCurrency));
|
||||
await AsyncStorage.setItem(EXCHANGE_RATES, JSON.stringify(exchangeRates));
|
||||
await AsyncStorage.setItem(PREFERRED_CURRENCY, JSON.stringify(preferredFiatCurrency));
|
||||
await setPrefferedCurrency(preferredFiatCurrency);
|
||||
}
|
||||
|
||||
|
@ -179,3 +181,5 @@ module.exports.btcToSatoshi = btcToSatoshi;
|
|||
module.exports.getCurrencySymbol = getCurrencySymbol;
|
||||
module.exports._setPreferredFiatCurrency = _setPreferredFiatCurrency; // export it to mock data in tests
|
||||
module.exports._setExchangeRate = _setExchangeRate; // export it to mock data in tests
|
||||
module.exports.PREFERRED_CURRENCY = PREFERRED_CURRENCY;
|
||||
module.exports.EXCHANGE_RATES = EXCHANGE_RATES;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { getSystemName, isTablet } from 'react-native-device-info';
|
||||
import isCatalyst from 'react-native-is-catalyst';
|
||||
import { getSystemName, isTablet, getDeviceType } from 'react-native-device-info';
|
||||
|
||||
const isMacCatalina = getSystemName() === 'Mac OS X';
|
||||
|
||||
module.exports.isMacCatalina = isMacCatalina;
|
||||
module.exports.isCatalyst = isCatalyst;
|
||||
module.exports.isDesktop = getDeviceType() === 'Desktop';
|
||||
module.exports.isHandset = getDeviceType() === 'Handset';
|
||||
module.exports.isTablet = isTablet;
|
||||
|
|
|
@ -4,9 +4,9 @@ import RNFS from 'react-native-fs';
|
|||
import Share from 'react-native-share';
|
||||
import loc from '../loc';
|
||||
import DocumentPicker from 'react-native-document-picker';
|
||||
import isCatalyst from 'react-native-is-catalyst';
|
||||
import { launchCamera, launchImageLibrary } from 'react-native-image-picker';
|
||||
import { presentCameraNotAuthorizedAlert } from '../class/camera';
|
||||
import { isDesktop } from '../blue_modules/environment';
|
||||
import ActionSheet from '../screen/ActionSheet';
|
||||
import BlueClipboard from './clipboard';
|
||||
const LocalQRCode = require('@remobile/react-native-qrcode-local-image');
|
||||
|
@ -17,7 +17,7 @@ const writeFileAndExport = async function (filename, contents) {
|
|||
await RNFS.writeFile(filePath, contents);
|
||||
Share.open({
|
||||
url: 'file://' + filePath,
|
||||
saveToFiles: isCatalyst,
|
||||
saveToFiles: isDesktop,
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error);
|
||||
|
|
36
blue_modules/slip39/package-lock.json
generated
36
blue_modules/slip39/package-lock.json
generated
|
@ -576,16 +576,30 @@
|
|||
"dev": true
|
||||
},
|
||||
"browserslist": {
|
||||
"version": "4.16.4",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.4.tgz",
|
||||
"integrity": "sha512-d7rCxYV8I9kj41RH8UKYnvDYCRENUlHRgyXy/Rhr/1BaeLGfiCptEdFE8MIrvGfWbBFNjVYx76SQWvNX1j+/cQ==",
|
||||
"version": "4.16.6",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz",
|
||||
"integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"caniuse-lite": "^1.0.30001208",
|
||||
"caniuse-lite": "^1.0.30001219",
|
||||
"colorette": "^1.2.2",
|
||||
"electron-to-chromium": "^1.3.712",
|
||||
"electron-to-chromium": "^1.3.723",
|
||||
"escalade": "^3.1.1",
|
||||
"node-releases": "^1.1.71"
|
||||
},
|
||||
"dependencies": {
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30001228",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001228.tgz",
|
||||
"integrity": "sha512-QQmLOGJ3DEgokHbMSA8cj2a+geXqmnpyOFT0lhQV6P3/YOJvGDEwoedcwxEQ30gJIwIIunHIicunJ2rzK5gB2A==",
|
||||
"dev": true
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.3.737",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.737.tgz",
|
||||
"integrity": "sha512-P/B84AgUSQXaum7a8m11HUsYL8tj9h/Pt5f7Hg7Ty6bm5DxlFq+e5+ouHUoNQMsKDJ7u4yGfI8mOErCmSH9wyg==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"cache-base": {
|
||||
|
@ -622,12 +636,6 @@
|
|||
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
|
||||
"dev": true
|
||||
},
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30001208",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001208.tgz",
|
||||
"integrity": "sha512-OE5UE4+nBOro8Dyvv0lfx+SRtfVIOM9uhKqFmJeUbGriqhhStgp1A0OyBpgy3OUF8AhYCT+PVwPC1gMl2ZcQMA==",
|
||||
"dev": true
|
||||
},
|
||||
"chalk": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||
|
@ -1016,12 +1024,6 @@
|
|||
"integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
|
||||
"dev": true
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.3.713",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.713.tgz",
|
||||
"integrity": "sha512-HWgkyX4xTHmxcWWlvv7a87RHSINEcpKYZmDMxkUlHcY+CJcfx7xEfBHuXVsO1rzyYs1WQJ7EgDp2CoErakBIow==",
|
||||
"dev": true
|
||||
},
|
||||
"emoji-regex": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
|
||||
|
|
|
@ -2,10 +2,11 @@
|
|||
import { useAsyncStorage } from '@react-native-async-storage/async-storage';
|
||||
import React, { createContext, useEffect, useState } from 'react';
|
||||
import { LayoutAnimation } from 'react-native';
|
||||
import { AppStorage } from '../class';
|
||||
import { FiatUnit } from '../models/fiatUnit';
|
||||
import loc from '../loc';
|
||||
const BlueApp = require('../BlueApp');
|
||||
const BlueElectrum = require('./BlueElectrum');
|
||||
const currency = require('../blue_modules/currency');
|
||||
|
||||
const _lastTimeTriedToRefetchWallet = {}; // hashmap of timestamps we _started_ refetching some wallet
|
||||
|
||||
|
@ -19,14 +20,14 @@ export const BlueStorageProvider = ({ children }) => {
|
|||
const [walletsInitialized, setWalletsInitialized] = useState(false);
|
||||
const [preferredFiatCurrency, _setPreferredFiatCurrency] = useState(FiatUnit.USD);
|
||||
const [language, _setLanguage] = useState();
|
||||
const getPreferredCurrencyAsyncStorage = useAsyncStorage(AppStorage.PREFERRED_CURRENCY).getItem;
|
||||
const getLanguageAsyncStorage = useAsyncStorage(AppStorage.LANG).getItem;
|
||||
const getPreferredCurrencyAsyncStorage = useAsyncStorage(currency.PREFERRED_CURRENCY).getItem;
|
||||
const getLanguageAsyncStorage = useAsyncStorage(loc.LANG).getItem;
|
||||
const [isHandOffUseEnabled, setIsHandOffUseEnabled] = useState(false);
|
||||
const [isDrawerListBlurred, _setIsDrawerListBlurred] = useState(false);
|
||||
|
||||
const setIsHandOffUseEnabledAsyncStorage = value => {
|
||||
setIsHandOffUseEnabled(value);
|
||||
return BlueApp.setItem(AppStorage.HANDOFF_STORAGE_KEY, value === true ? '1' : '');
|
||||
return BlueApp.setIsHandoffEnabled(value);
|
||||
};
|
||||
|
||||
const saveToDisk = async () => {
|
||||
|
@ -43,7 +44,7 @@ export const BlueStorageProvider = ({ children }) => {
|
|||
useEffect(() => {
|
||||
(async () => {
|
||||
try {
|
||||
const enabledHandoff = await BlueApp.getItem(AppStorage.HANDOFF_STORAGE_KEY);
|
||||
const enabledHandoff = await BlueApp.isHandoffEnabled();
|
||||
setIsHandOffUseEnabled(!!enabledHandoff);
|
||||
} catch (_e) {
|
||||
setIsHandOffUseEnabledAsyncStorage(false);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/* global alert */
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
import RNSecureKeyStore, { ACCESSIBLE } from 'react-native-secure-key-store';
|
||||
import * as Keychain from 'react-native-keychain';
|
||||
import {
|
||||
HDLegacyBreadwalletWallet,
|
||||
HDSegwitP2SHWallet,
|
||||
|
@ -20,21 +21,16 @@ import {
|
|||
SLIP39LegacyP2PKHWallet,
|
||||
SLIP39SegwitBech32Wallet,
|
||||
} from './';
|
||||
import { randomBytes } from './rng';
|
||||
const encryption = require('../blue_modules/encryption');
|
||||
const Realm = require('realm');
|
||||
const createHash = require('create-hash');
|
||||
let usedBucketNum = false;
|
||||
let savingInProgress = 0; // its both a flag and a counter of attempts to write to disk
|
||||
|
||||
export class AppStorage {
|
||||
static FLAG_ENCRYPTED = 'data_encrypted';
|
||||
static LANG = 'lang';
|
||||
static EXCHANGE_RATES = 'currency';
|
||||
static LNDHUB = 'lndhub';
|
||||
static ELECTRUM_HOST = 'electrum_host';
|
||||
static ELECTRUM_TCP_PORT = 'electrum_tcp_port';
|
||||
static ELECTRUM_SSL_PORT = 'electrum_ssl_port';
|
||||
static ELECTRUM_SERVER_HISTORY = 'electrum_server_history';
|
||||
static PREFERRED_CURRENCY = 'preferredCurrency';
|
||||
static ADVANCED_MODE_ENABLED = 'advancedmodeenabled';
|
||||
static DO_NOT_TRACK = 'donottrack';
|
||||
static HODL_HODL_API_KEY = 'HODL_HODL_API_KEY';
|
||||
|
@ -42,6 +38,8 @@ export class AppStorage {
|
|||
static HODL_HODL_CONTRACTS = 'HODL_HODL_CONTRACTS';
|
||||
static HANDOFF_STORAGE_KEY = 'HandOff';
|
||||
|
||||
static keys2migrate = [AppStorage.HANDOFF_STORAGE_KEY, AppStorage.DO_NOT_TRACK, AppStorage.ADVANCED_MODE_ENABLED];
|
||||
|
||||
constructor() {
|
||||
/** {Array.<AbstractWallet>} */
|
||||
this.wallets = [];
|
||||
|
@ -49,6 +47,19 @@ export class AppStorage {
|
|||
this.cachedPassword = false;
|
||||
}
|
||||
|
||||
async migrateKeys() {
|
||||
if (!(typeof navigator !== 'undefined' && navigator.product === 'ReactNative')) return;
|
||||
for (const key of this.constructor.keys2migrate) {
|
||||
try {
|
||||
const value = await RNSecureKeyStore.get(key);
|
||||
if (value) {
|
||||
await AsyncStorage.setItem(key, value);
|
||||
await RNSecureKeyStore.remove(key);
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for storage call. Secure store works only in RN environment. AsyncStorage is
|
||||
* used for cli/tests
|
||||
|
@ -80,11 +91,36 @@ export class AppStorage {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @throws Error
|
||||
* @param key {string}
|
||||
* @returns {Promise<*>|null}
|
||||
*/
|
||||
getItemWithFallbackToRealm = async key => {
|
||||
let value;
|
||||
try {
|
||||
return await this.getItem(key);
|
||||
} catch (error) {
|
||||
console.warn('error reading', key, error.message);
|
||||
console.warn('fallback to realm');
|
||||
const realmKeyValue = await this.openRealmKeyValue();
|
||||
const obj = realmKeyValue.objectForPrimaryKey('KeyValue', key); // search for a realm object with a primary key
|
||||
value = obj?.value;
|
||||
realmKeyValue.close();
|
||||
if (value) {
|
||||
console.warn('successfully recovered', value.length, 'bytes from realm for key', key);
|
||||
return value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
storageIsEncrypted = async () => {
|
||||
let data;
|
||||
try {
|
||||
data = await this.getItem(AppStorage.FLAG_ENCRYPTED);
|
||||
data = await this.getItemWithFallbackToRealm(AppStorage.FLAG_ENCRYPTED);
|
||||
} catch (error) {
|
||||
console.warn('error reading `' + AppStorage.FLAG_ENCRYPTED + '` key:', error.message);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -212,6 +248,58 @@ export class AppStorage {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns instace of the Realm database, which is encrypted by device unique id
|
||||
* Database file is static.
|
||||
*
|
||||
* @returns {Promise<Realm>}
|
||||
*/
|
||||
async openRealmKeyValue() {
|
||||
const service = 'realm_encryption_key';
|
||||
let password;
|
||||
const credentials = await Keychain.getGenericPassword({ service });
|
||||
if (credentials) {
|
||||
password = credentials.password;
|
||||
} else {
|
||||
const buf = await randomBytes(64);
|
||||
password = buf.toString('hex');
|
||||
await Keychain.setGenericPassword(service, password, { service });
|
||||
}
|
||||
|
||||
const buf = Buffer.from(password, 'hex');
|
||||
const encryptionKey = Int8Array.from(buf);
|
||||
const path = 'keyvalue.realm';
|
||||
|
||||
const schema = [
|
||||
{
|
||||
name: 'KeyValue',
|
||||
primaryKey: 'key',
|
||||
properties: {
|
||||
key: { type: 'string', indexed: true },
|
||||
value: 'string', // stringified json, or whatever
|
||||
},
|
||||
},
|
||||
];
|
||||
return Realm.open({
|
||||
schema,
|
||||
path,
|
||||
encryptionKey,
|
||||
});
|
||||
}
|
||||
|
||||
saveToRealmKeyValue(realmkeyValue, key, value) {
|
||||
realmkeyValue.write(() => {
|
||||
realmkeyValue.create(
|
||||
'KeyValue',
|
||||
{
|
||||
key: key,
|
||||
value: value,
|
||||
},
|
||||
Realm.UpdateMode.Modified,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads from storage all wallets and
|
||||
* maps them to `this.wallets`
|
||||
|
@ -220,7 +308,7 @@ export class AppStorage {
|
|||
* @returns {Promise.<boolean>}
|
||||
*/
|
||||
async loadFromDisk(password) {
|
||||
let data = await this.getItem('data');
|
||||
let data = await this.getItemWithFallbackToRealm('data');
|
||||
if (password) {
|
||||
data = this.decryptData(data, password);
|
||||
if (data) {
|
||||
|
@ -406,71 +494,87 @@ export class AppStorage {
|
|||
* @returns {Promise} Result of storage save
|
||||
*/
|
||||
async saveToDisk() {
|
||||
const walletsToSave = [];
|
||||
const realm = await this.getRealm();
|
||||
for (const key of this.wallets) {
|
||||
if (typeof key === 'boolean' || key.type === PlaceholderWallet.type) continue;
|
||||
key.prepareForSerialization();
|
||||
delete key.current;
|
||||
const keyCloned = Object.assign({}, key); // stripped-down version of a wallet to save to secure keystore
|
||||
if (key._hdWalletInstance) keyCloned._hdWalletInstance = Object.assign({}, key._hdWalletInstance);
|
||||
this.offloadWalletToRealm(realm, key);
|
||||
// stripping down:
|
||||
if (key._txs_by_external_index) {
|
||||
keyCloned._txs_by_external_index = {};
|
||||
keyCloned._txs_by_internal_index = {};
|
||||
}
|
||||
if (key._hdWalletInstance) {
|
||||
keyCloned._hdWalletInstance._txs_by_external_index = {};
|
||||
keyCloned._hdWalletInstance._txs_by_internal_index = {};
|
||||
}
|
||||
walletsToSave.push(JSON.stringify({ ...keyCloned, type: keyCloned.type }));
|
||||
if (savingInProgress) {
|
||||
console.warn('saveToDisk is in progress');
|
||||
if (++savingInProgress > 10) alert('Critical error. Last actions were not saved'); // should never happen
|
||||
await new Promise(resolve => setTimeout(resolve, 1000 * savingInProgress)); // sleep
|
||||
return this.saveToDisk();
|
||||
}
|
||||
realm.close();
|
||||
let data = {
|
||||
wallets: walletsToSave,
|
||||
tx_metadata: this.tx_metadata,
|
||||
};
|
||||
savingInProgress = 1;
|
||||
|
||||
if (this.cachedPassword) {
|
||||
// should find the correct bucket, encrypt and then save
|
||||
let buckets = await this.getItem('data');
|
||||
buckets = JSON.parse(buckets);
|
||||
const newData = [];
|
||||
let num = 0;
|
||||
for (const bucket of buckets) {
|
||||
let decrypted;
|
||||
// if we had `usedBucketNum` during loadFromDisk(), no point to try to decode each bucket to find the one we
|
||||
// need, we just to find bucket with the same index
|
||||
if (usedBucketNum !== false) {
|
||||
if (num === usedBucketNum) {
|
||||
decrypted = true;
|
||||
}
|
||||
num++;
|
||||
} else {
|
||||
// we dont have `usedBucketNum` for whatever reason, so lets try to decrypt each bucket after bucket
|
||||
// till we find the right one
|
||||
decrypted = encryption.decrypt(bucket, this.cachedPassword);
|
||||
}
|
||||
|
||||
if (!decrypted) {
|
||||
// no luck decrypting, its not our bucket
|
||||
newData.push(bucket);
|
||||
} else {
|
||||
// decrypted ok, this is our bucket
|
||||
// we serialize our object's data, encrypt it, and add it to buckets
|
||||
newData.push(encryption.encrypt(JSON.stringify(data), this.cachedPassword));
|
||||
await this.setItem(AppStorage.FLAG_ENCRYPTED, '1');
|
||||
}
|
||||
}
|
||||
data = newData;
|
||||
} else {
|
||||
await this.setItem(AppStorage.FLAG_ENCRYPTED, ''); // drop the flag
|
||||
}
|
||||
try {
|
||||
return await this.setItem('data', JSON.stringify(data));
|
||||
const walletsToSave = [];
|
||||
const realm = await this.getRealm();
|
||||
for (const key of this.wallets) {
|
||||
if (typeof key === 'boolean' || key.type === PlaceholderWallet.type) continue;
|
||||
key.prepareForSerialization();
|
||||
delete key.current;
|
||||
const keyCloned = Object.assign({}, key); // stripped-down version of a wallet to save to secure keystore
|
||||
if (key._hdWalletInstance) keyCloned._hdWalletInstance = Object.assign({}, key._hdWalletInstance);
|
||||
this.offloadWalletToRealm(realm, key);
|
||||
// stripping down:
|
||||
if (key._txs_by_external_index) {
|
||||
keyCloned._txs_by_external_index = {};
|
||||
keyCloned._txs_by_internal_index = {};
|
||||
}
|
||||
if (key._hdWalletInstance) {
|
||||
keyCloned._hdWalletInstance._txs_by_external_index = {};
|
||||
keyCloned._hdWalletInstance._txs_by_internal_index = {};
|
||||
}
|
||||
walletsToSave.push(JSON.stringify({ ...keyCloned, type: keyCloned.type }));
|
||||
}
|
||||
realm.close();
|
||||
let data = {
|
||||
wallets: walletsToSave,
|
||||
tx_metadata: this.tx_metadata,
|
||||
};
|
||||
|
||||
if (this.cachedPassword) {
|
||||
// should find the correct bucket, encrypt and then save
|
||||
let buckets = await this.getItemWithFallbackToRealm('data');
|
||||
buckets = JSON.parse(buckets);
|
||||
const newData = [];
|
||||
let num = 0;
|
||||
for (const bucket of buckets) {
|
||||
let decrypted;
|
||||
// if we had `usedBucketNum` during loadFromDisk(), no point to try to decode each bucket to find the one we
|
||||
// need, we just to find bucket with the same index
|
||||
if (usedBucketNum !== false) {
|
||||
if (num === usedBucketNum) {
|
||||
decrypted = true;
|
||||
}
|
||||
num++;
|
||||
} else {
|
||||
// we dont have `usedBucketNum` for whatever reason, so lets try to decrypt each bucket after bucket
|
||||
// till we find the right one
|
||||
decrypted = encryption.decrypt(bucket, this.cachedPassword);
|
||||
}
|
||||
|
||||
if (!decrypted) {
|
||||
// no luck decrypting, its not our bucket
|
||||
newData.push(bucket);
|
||||
} else {
|
||||
// decrypted ok, this is our bucket
|
||||
// we serialize our object's data, encrypt it, and add it to buckets
|
||||
newData.push(encryption.encrypt(JSON.stringify(data), this.cachedPassword));
|
||||
}
|
||||
}
|
||||
data = newData;
|
||||
}
|
||||
|
||||
await this.setItem('data', JSON.stringify(data));
|
||||
await this.setItem(AppStorage.FLAG_ENCRYPTED, this.cachedPassword ? '1' : '');
|
||||
|
||||
// now, backing up same data in realm:
|
||||
const realmkeyValue = await this.openRealmKeyValue();
|
||||
this.saveToRealmKeyValue(realmkeyValue, 'data', JSON.stringify(data));
|
||||
this.saveToRealmKeyValue(realmkeyValue, AppStorage.FLAG_ENCRYPTED, this.cachedPassword ? '1' : '');
|
||||
realmkeyValue.close();
|
||||
} catch (error) {
|
||||
alert(error.message);
|
||||
console.error('save to disk exception:', error.message);
|
||||
alert('save to disk exception: ' + error.message);
|
||||
} finally {
|
||||
savingInProgress = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -645,24 +749,35 @@ export class AppStorage {
|
|||
|
||||
isAdancedModeEnabled = async () => {
|
||||
try {
|
||||
return !!(await this.getItem(AppStorage.ADVANCED_MODE_ENABLED));
|
||||
return !!(await AsyncStorage.getItem(AppStorage.ADVANCED_MODE_ENABLED));
|
||||
} catch (_) {}
|
||||
return false;
|
||||
};
|
||||
|
||||
setIsAdancedModeEnabled = async value => {
|
||||
await this.setItem(AppStorage.ADVANCED_MODE_ENABLED, value ? '1' : '');
|
||||
await AsyncStorage.setItem(AppStorage.ADVANCED_MODE_ENABLED, value ? '1' : '');
|
||||
};
|
||||
|
||||
isHandoffEnabled = async () => {
|
||||
try {
|
||||
return !!(await AsyncStorage.getItem(AppStorage.HANDOFF_STORAGE_KEY));
|
||||
} catch (_) {}
|
||||
return false;
|
||||
};
|
||||
|
||||
setIsHandoffEnabled = async value => {
|
||||
await AsyncStorage.setItem(AppStorage.HANDOFF_STORAGE_KEY, value ? '1' : '');
|
||||
};
|
||||
|
||||
isDoNotTrackEnabled = async () => {
|
||||
try {
|
||||
return !!(await this.getItem(AppStorage.DO_NOT_TRACK));
|
||||
return !!(await AsyncStorage.getItem(AppStorage.DO_NOT_TRACK));
|
||||
} catch (_) {}
|
||||
return false;
|
||||
};
|
||||
|
||||
setDoNotTrack = async value => {
|
||||
await this.setItem(AppStorage.DO_NOT_TRACK, value ? '1' : '');
|
||||
await AsyncStorage.setItem(AppStorage.DO_NOT_TRACK, value ? '1' : '');
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,9 +8,10 @@ import RNSecureKeyStore from 'react-native-secure-key-store';
|
|||
import loc from '../loc';
|
||||
import { useContext } from 'react';
|
||||
import { BlueStorageContext } from '../blue_modules/storage-context';
|
||||
import * as Sentry from '@sentry/react-native';
|
||||
|
||||
function Biometric() {
|
||||
const { getItem, setItem, setResetOnAppUninstallTo } = useContext(BlueStorageContext);
|
||||
const { getItem, setItem } = useContext(BlueStorageContext);
|
||||
Biometric.STORAGEKEY = 'Biometrics';
|
||||
Biometric.FaceID = 'Face ID';
|
||||
Biometric.TouchID = 'Touch ID';
|
||||
|
@ -42,10 +43,9 @@ function Biometric() {
|
|||
try {
|
||||
const enabledBiometrics = await getItem(Biometric.STORAGEKEY);
|
||||
return !!enabledBiometrics;
|
||||
} catch (_e) {
|
||||
await setItem(Biometric.STORAGEKEY, '');
|
||||
return false;
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
Biometric.isBiometricUseCapableAndEnabled = async () => {
|
||||
|
@ -72,10 +72,10 @@ function Biometric() {
|
|||
};
|
||||
|
||||
Biometric.clearKeychain = async () => {
|
||||
Sentry.captureMessage('Biometric.clearKeychain()');
|
||||
await RNSecureKeyStore.remove('data');
|
||||
await RNSecureKeyStore.remove('data_encrypted');
|
||||
await RNSecureKeyStore.remove(Biometric.STORAGEKEY);
|
||||
await setResetOnAppUninstallTo(true);
|
||||
NavigationService.dispatch(StackActions.replace('WalletsRoot'));
|
||||
};
|
||||
|
||||
|
|
|
@ -254,8 +254,7 @@ class DeeplinkSchemaMatch {
|
|||
{
|
||||
screen: 'LappBrowser',
|
||||
params: {
|
||||
fromSecret: lnWallet.getSecret(),
|
||||
fromWallet: lnWallet,
|
||||
walletID: lnWallet.getID(),
|
||||
url: urlObject.query.url,
|
||||
},
|
||||
},
|
||||
|
|
|
@ -34,6 +34,12 @@ function DeviceQuickActions() {
|
|||
});
|
||||
};
|
||||
|
||||
DeviceQuickActions.popInitialAction = async () => {
|
||||
const data = await QuickActions.popInitialAction();
|
||||
return data;
|
||||
};
|
||||
|
||||
|
||||
DeviceQuickActions.getEnabled = async () => {
|
||||
try {
|
||||
const isEnabled = await AsyncStorage.getItem(DeviceQuickActions.STORAGE_KEY);
|
||||
|
|
|
@ -7,6 +7,8 @@ function DeviceQuickActions() {
|
|||
return false;
|
||||
};
|
||||
|
||||
DeviceQuickActions.popInitialAction = () => {};
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -230,6 +230,15 @@ function WalletImport() {
|
|||
|
||||
// if we're here - nope, its not a valid WIF
|
||||
|
||||
// maybe its a watch-only address?
|
||||
const watchOnly = new WatchOnlyWallet();
|
||||
watchOnly.setSecret(importText);
|
||||
if (watchOnly.valid()) {
|
||||
await watchOnly.fetchBalance();
|
||||
return WalletImport._saveWallet(watchOnly, additionalProperties);
|
||||
}
|
||||
// nope, not watch-only
|
||||
|
||||
try {
|
||||
const hdElectrumSeedLegacy = new HDSegwitElectrumSeedP2WPKHWallet();
|
||||
hdElectrumSeedLegacy.setSecret(importText);
|
||||
|
@ -270,15 +279,6 @@ function WalletImport() {
|
|||
}
|
||||
} catch (_) {}
|
||||
|
||||
// not valid? maybe its a watch-only address?
|
||||
|
||||
const watchOnly = new WatchOnlyWallet();
|
||||
watchOnly.setSecret(importText);
|
||||
if (watchOnly.valid()) {
|
||||
await watchOnly.fetchBalance();
|
||||
return WalletImport._saveWallet(watchOnly, additionalProperties);
|
||||
}
|
||||
|
||||
// if it is multi-line string, then it is probably SLIP39 wallet
|
||||
// each line - one share
|
||||
if (importText.includes('\n')) {
|
||||
|
@ -298,9 +298,7 @@ function WalletImport() {
|
|||
|
||||
const s3 = new SLIP39SegwitBech32Wallet();
|
||||
s3.setSecret(importText);
|
||||
if (await s3.wasEverUsed()) {
|
||||
return WalletImport._saveWallet(s3);
|
||||
}
|
||||
return WalletImport._saveWallet(s3);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -824,11 +824,18 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
}
|
||||
|
||||
weOwnAddress(address) {
|
||||
if (!address) return false;
|
||||
let cleanAddress = address;
|
||||
|
||||
if (this.segwitType === 'p2wpkh') {
|
||||
cleanAddress = address.toLowerCase();
|
||||
}
|
||||
|
||||
for (let c = 0; c < this.next_free_address_index + this.gap_limit; c++) {
|
||||
if (this._getExternalAddressByIndex(c) === address) return true;
|
||||
if (this._getExternalAddressByIndex(c) === cleanAddress) return true;
|
||||
}
|
||||
for (let c = 0; c < this.next_free_change_address_index + this.gap_limit; c++) {
|
||||
if (this._getInternalAddressByIndex(c) === address) return true;
|
||||
if (this._getInternalAddressByIndex(c) === cleanAddress) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -247,15 +247,6 @@ export class AbstractHDWallet extends LegacyWallet {
|
|||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
weOwnAddress(addr) {
|
||||
const hashmap = {};
|
||||
for (const a of this.usedAddresses) {
|
||||
hashmap[a] = 1;
|
||||
}
|
||||
|
||||
return hashmap[addr] === 1;
|
||||
}
|
||||
|
||||
_getDerivationPathByAddress(address) {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
|
|
@ -162,4 +162,9 @@ export class HDLegacyP2PKHWallet extends AbstractHDElectrumWallet {
|
|||
|
||||
return psbt;
|
||||
}
|
||||
|
||||
_getDerivationPathByAddress(address, BIP = 44) {
|
||||
// only changing defaults for function arguments
|
||||
return super._getDerivationPathByAddress(address, BIP);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -148,4 +148,9 @@ export class HDSegwitP2SHWallet extends AbstractHDElectrumWallet {
|
|||
});
|
||||
return address;
|
||||
}
|
||||
|
||||
_getDerivationPathByAddress(address, BIP = 49) {
|
||||
// only changing defaults for function arguments
|
||||
return super._getDerivationPathByAddress(address, BIP);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -465,7 +465,14 @@ export class LegacyWallet extends AbstractWallet {
|
|||
}
|
||||
|
||||
weOwnAddress(address) {
|
||||
return this.getAddress() === address || this._address === address;
|
||||
if (!address) return false;
|
||||
let cleanAddress = address;
|
||||
|
||||
if (this.segwitType === 'p2wpkh') {
|
||||
cleanAddress = address.toLowerCase();
|
||||
}
|
||||
|
||||
return this.getAddress() === cleanAddress || this._address === cleanAddress;
|
||||
}
|
||||
|
||||
weOwnTransaction(txid) {
|
||||
|
|
|
@ -377,7 +377,7 @@ export class LightningCustodianWallet extends LegacyWallet {
|
|||
txs = txs.concat(this.pending_transactions_raw.slice(), this.transactions_raw.slice().reverse(), this.user_invoices_raw.slice()); // slice so array is cloned
|
||||
// transforming to how wallets/list screen expects it
|
||||
for (const tx of txs) {
|
||||
tx.fromWallet = this.getSecret();
|
||||
tx.walletID = this.getID();
|
||||
if (tx.amount) {
|
||||
// pending tx
|
||||
tx.amt = tx.amount * -100000000;
|
||||
|
|
|
@ -221,6 +221,8 @@ export class WatchOnlyWallet extends LegacyWallet {
|
|||
throw new Error('Not initialized');
|
||||
}
|
||||
|
||||
if (address && address.startsWith('BC1')) address = address.toLowerCase();
|
||||
|
||||
return this.getAddress() === address;
|
||||
}
|
||||
|
||||
|
|
|
@ -66,10 +66,15 @@ const AddressInput = ({
|
|||
});
|
||||
}
|
||||
}}
|
||||
accessibilityRole="button"
|
||||
style={[styles.scan, stylesHook.scan]}
|
||||
accessibilityLabel={loc.send.details_scan}
|
||||
accessibilityHint={loc.send.details_scan_hint}
|
||||
>
|
||||
<Image source={require('../img/scan-white.png')} />
|
||||
<Text style={[styles.scanText, stylesHook.scanText]}>{loc.send.details_scan}</Text>
|
||||
<Image source={require('../img/scan-white.png')} accessible={false} />
|
||||
<Text style={[styles.scanText, stylesHook.scanText]} accessible={false}>
|
||||
{loc.send.details_scan}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
|
|
|
@ -240,7 +240,12 @@ class AmountInput extends Component {
|
|||
</View>
|
||||
</View>
|
||||
{!disabled && amount !== BitcoinUnit.MAX && (
|
||||
<TouchableOpacity testID="changeAmountUnitButton" style={styles.changeAmountUnit} onPress={this.changeAmountUnit}>
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
testID="changeAmountUnitButton"
|
||||
style={styles.changeAmountUnit}
|
||||
onPress={this.changeAmountUnit}
|
||||
>
|
||||
<Image source={require('../img/round-compare-arrows-24-px.png')} />
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
|
|
|
@ -1,21 +1,33 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { StyleSheet, useWindowDimensions } from 'react-native';
|
||||
import { StyleSheet, Platform, useWindowDimensions, View } from 'react-native';
|
||||
import Modal from 'react-native-modal';
|
||||
import { BlueButton, BlueSpacing40 } from '../BlueComponents';
|
||||
import loc from '../loc';
|
||||
import { useTheme } from '@react-navigation/native';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
justifyContent: 'flex-end',
|
||||
margin: 0,
|
||||
},
|
||||
hasDoneButton: {
|
||||
padding: 16,
|
||||
paddingBottom: 24,
|
||||
},
|
||||
});
|
||||
|
||||
const BottomModal = ({ onBackButtonPress, onBackdropPress, onClose, windowHeight, windowWidth, ...props }) => {
|
||||
const BottomModal = ({ onBackButtonPress, onBackdropPress, onClose, windowHeight, windowWidth, doneButton, ...props }) => {
|
||||
const valueWindowHeight = useWindowDimensions().height;
|
||||
const valueWindowWidth = useWindowDimensions().width;
|
||||
const handleBackButtonPress = onBackButtonPress ?? onClose;
|
||||
const handleBackdropPress = onBackdropPress ?? onClose;
|
||||
|
||||
const { colors } = useTheme();
|
||||
const stylesHook = StyleSheet.create({
|
||||
hasDoneButton: {
|
||||
backgroundColor: colors.elevated,
|
||||
},
|
||||
});
|
||||
return (
|
||||
<Modal
|
||||
style={styles.root}
|
||||
|
@ -26,8 +38,16 @@ const BottomModal = ({ onBackButtonPress, onBackdropPress, onClose, windowHeight
|
|||
{...props}
|
||||
accessibilityViewIsModal
|
||||
useNativeDriver
|
||||
useNativeDriverForBackdrop
|
||||
/>
|
||||
useNativeDriverForBackdrop={Platform.OS === 'android'}
|
||||
>
|
||||
{props.children}
|
||||
{doneButton && (
|
||||
<View style={[styles.hasDoneButton, stylesHook.hasDoneButton]}>
|
||||
<BlueButton title={loc.send.input_done} onPress={onClose} />
|
||||
<BlueSpacing40 />
|
||||
</View>
|
||||
)}
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -36,6 +56,7 @@ BottomModal.propTypes = {
|
|||
onBackButtonPress: PropTypes.func,
|
||||
onBackdropPress: PropTypes.func,
|
||||
onClose: PropTypes.func,
|
||||
doneButton: PropTypes.bool,
|
||||
windowHeight: PropTypes.number,
|
||||
windowWidth: PropTypes.number,
|
||||
};
|
||||
|
|
|
@ -35,11 +35,11 @@ const styles = StyleSheet.create({
|
|||
});
|
||||
|
||||
const CoinsSelected = ({ number, onContainerPress, onClose }) => (
|
||||
<TouchableOpacity style={styles.root} onPress={onContainerPress}>
|
||||
<TouchableOpacity accessibilityRole="button" style={styles.root} onPress={onContainerPress}>
|
||||
<View style={styles.labelContainer}>
|
||||
<Text style={styles.labelText}>{loc.formatString(loc.cc.coins_selected, { number })}</Text>
|
||||
</View>
|
||||
<TouchableOpacity style={styles.buttonContainer} onPress={onClose}>
|
||||
<TouchableOpacity accessibilityRole="button" style={styles.buttonContainer} onPress={onClose}>
|
||||
<Avatar rounded containerStyle={[styles.ball]} icon={{ name: 'close', size: 22, type: 'ionicons', color: 'white' }} />
|
||||
</TouchableOpacity>
|
||||
</TouchableOpacity>
|
||||
|
|
|
@ -105,6 +105,7 @@ export class DynamicQRCode extends Component {
|
|||
return (
|
||||
<View style={animatedQRCodeStyle.container}>
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
testID="DynamicCode"
|
||||
style={animatedQRCodeStyle.qrcodeContainer}
|
||||
onPress={() => {
|
||||
|
@ -136,18 +137,21 @@ export class DynamicQRCode extends Component {
|
|||
<BlueSpacing20 />
|
||||
<View style={animatedQRCodeStyle.controller}>
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
style={[animatedQRCodeStyle.button, { width: '25%', alignItems: 'flex-start' }]}
|
||||
onPress={this.moveToPreviousFragment}
|
||||
>
|
||||
<Text style={animatedQRCodeStyle.text}>{loc.send.dynamic_prev}</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
style={[animatedQRCodeStyle.button, { width: '50%' }]}
|
||||
onPress={this.state.intervalHandler ? this.stopAutoMove : this.startAutoMove}
|
||||
>
|
||||
<Text style={animatedQRCodeStyle.text}>{this.state.intervalHandler ? loc.send.dynamic_stop : loc.send.dynamic_start}</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
style={[animatedQRCodeStyle.button, { width: '25%', alignItems: 'flex-end' }]}
|
||||
onPress={this.moveToNextFragment}
|
||||
>
|
||||
|
|
|
@ -119,7 +119,7 @@ export const FButton = ({ text, icon, width, first, last, ...props }) => {
|
|||
}
|
||||
|
||||
return (
|
||||
<TouchableOpacity style={[bStyles.root, bStylesHook.root, style]} {...props}>
|
||||
<TouchableOpacity accessibilityRole="button" style={[bStyles.root, bStylesHook.root, style]} {...props}>
|
||||
<View style={bStyles.icon}>{icon}</View>
|
||||
<Text numberOfLines={1} style={[bStyles.text, props.disabled ? bStylesHook.textDisabled : bStylesHook.text]}>
|
||||
{text}
|
||||
|
|
|
@ -139,6 +139,7 @@ const MultipleStepsListItem = props => {
|
|||
{props.button.buttonType === undefined ||
|
||||
(props.button.buttonType === MultipleStepsListItemButtohType.full && (
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
disabled={props.button.disabled}
|
||||
style={[styles.provideKeyButton, stylesHook.provideKeyButton, buttonOpacity]}
|
||||
onPress={props.button.onPress}
|
||||
|
@ -152,6 +153,7 @@ const MultipleStepsListItem = props => {
|
|||
{props.button.leftText}
|
||||
</Text>
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
disabled={props.button.disabled}
|
||||
style={[styles.rowPartialRightButton, stylesHook.provideKeyButton, rightButtonOpacity]}
|
||||
onPress={props.button.onPress}
|
||||
|
@ -166,7 +168,12 @@ const MultipleStepsListItem = props => {
|
|||
)}
|
||||
{!showActivityIndicator && props.rightButton && checked && (
|
||||
<View style={styles.rightButtonContainer} accessibilityComponentType>
|
||||
<TouchableOpacity disabled={props.rightButton.disabled} style={styles.rightButton} onPress={props.rightButton.onPress}>
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
disabled={props.rightButton.disabled}
|
||||
style={styles.rightButton}
|
||||
onPress={props.rightButton.onPress}
|
||||
>
|
||||
<Text style={[styles.provideKeyButtonText, stylesHook.provideKeyButtonText]}>{props.rightButton.text}</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
|
|
@ -29,6 +29,7 @@ export const SquareButton = forwardRef((props, ref) => {
|
|||
}}
|
||||
{...props}
|
||||
ref={ref}
|
||||
accessibilityRole="button"
|
||||
>
|
||||
<View style={{ flexDirection: 'row', justifyContent: 'center', alignItems: 'center' }}>
|
||||
{props.icon && <Icon name={props.icon.name} type={props.icon.type} color={props.icon.color} />}
|
||||
|
|
|
@ -35,7 +35,11 @@ const SquareEnumeratedWords = props => {
|
|||
);
|
||||
} else {
|
||||
component.push(
|
||||
<TouchableOpacity style={[styles.entryTextContainer, stylesHook.entryTextContainer]} key={`${secret}${index}`}>
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
style={[styles.entryTextContainer, stylesHook.entryTextContainer]}
|
||||
key={`${secret}${index}`}
|
||||
>
|
||||
<Text textBreakStrategy="simple" style={[styles.entryText, stylesHook.entryText]}>
|
||||
{secret}
|
||||
</Text>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useRef, useCallback, useState, useImperativeHandle, forwardRef, useContext } from 'react';
|
||||
import React, { useRef, useCallback, useImperativeHandle, forwardRef, useContext } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
ActivityIndicator,
|
||||
|
@ -12,22 +12,19 @@ import {
|
|||
TouchableWithoutFeedback,
|
||||
useWindowDimensions,
|
||||
View,
|
||||
FlatList,
|
||||
} from 'react-native';
|
||||
import { useTheme } from '@react-navigation/native';
|
||||
import LinearGradient from 'react-native-linear-gradient';
|
||||
import Carousel from 'react-native-snap-carousel';
|
||||
|
||||
import loc, { formatBalance, transactionTimeToReadable } from '../loc';
|
||||
import { LightningCustodianWallet, MultisigHDWallet, PlaceholderWallet } from '../class';
|
||||
import WalletGradient from '../class/wallet-gradient';
|
||||
import { BluePrivateBalance } from '../BlueComponents';
|
||||
import { BlueStorageContext } from '../blue_modules/storage-context';
|
||||
import { isHandset } from '../blue_modules/environment';
|
||||
|
||||
const nStyles = StyleSheet.create({
|
||||
root: {
|
||||
marginVertical: 17,
|
||||
paddingRight: 10,
|
||||
},
|
||||
root: {},
|
||||
container: {
|
||||
paddingHorizontal: 24,
|
||||
paddingVertical: 16,
|
||||
|
@ -58,9 +55,12 @@ const nStyles = StyleSheet.create({
|
|||
|
||||
const NewWalletPanel = ({ onPress }) => {
|
||||
const { colors } = useTheme();
|
||||
const { width } = useWindowDimensions();
|
||||
const itemWidth = width * 0.82 > 375 ? 375 : width * 0.82;
|
||||
|
||||
return (
|
||||
<TouchableOpacity testID="CreateAWallet" onPress={onPress} style={nStyles.root}>
|
||||
<View style={[nStyles.container, { backgroundColor: WalletGradient.createWallet() }]}>
|
||||
<TouchableOpacity accessibilityRole="button" testID="CreateAWallet" onPress={onPress} style={{ width: itemWidth * 1.2 }}>
|
||||
<View style={[nStyles.container, { backgroundColor: WalletGradient.createWallet(), width: itemWidth }]}>
|
||||
<Text style={[nStyles.addAWAllet, { color: colors.foregroundColor }]}>{loc.wallets.list_create_a_wallet}</Text>
|
||||
<Text style={[nStyles.addLine, { color: colors.alternativeTextColor }]}>{loc.wallets.list_create_a_wallet_text}</Text>
|
||||
<View style={nStyles.button}>
|
||||
|
@ -76,10 +76,7 @@ NewWalletPanel.propTypes = {
|
|||
};
|
||||
|
||||
const iStyles = StyleSheet.create({
|
||||
root: {
|
||||
paddingRight: 10,
|
||||
marginVertical: 17,
|
||||
},
|
||||
root: { paddingRight: 20 },
|
||||
grad: {
|
||||
padding: 15,
|
||||
borderRadius: 12,
|
||||
|
@ -132,6 +129,8 @@ const WalletCarouselItem = ({ item, index, onPress, handleLongPress, isSelectedW
|
|||
const scaleValue = new Animated.Value(1.0);
|
||||
const { colors } = useTheme();
|
||||
const { walletTransactionUpdateStatus } = useContext(BlueStorageContext);
|
||||
const { width } = useWindowDimensions();
|
||||
const itemWidth = width * 0.82 > 375 ? 375 : width * 0.82;
|
||||
|
||||
const onPressedIn = () => {
|
||||
const props = { duration: 50 };
|
||||
|
@ -160,7 +159,7 @@ const WalletCarouselItem = ({ item, index, onPress, handleLongPress, isSelectedW
|
|||
if (item.type === PlaceholderWallet.type) {
|
||||
return (
|
||||
<Animated.View
|
||||
style={[iStyles.root, { transform: [{ scale: scaleValue }] }]}
|
||||
style={[iStyles.root, { width: itemWidth }, { transform: [{ scale: scaleValue }] }]}
|
||||
shadowOpacity={40 / 100}
|
||||
shadowOffset={{ width: 0, height: 0 }}
|
||||
shadowRadius={5}
|
||||
|
@ -224,7 +223,7 @@ const WalletCarouselItem = ({ item, index, onPress, handleLongPress, isSelectedW
|
|||
|
||||
return (
|
||||
<Animated.View
|
||||
style={[iStyles.root, { opacity, transform: [{ scale: scaleValue }] }]}
|
||||
style={[iStyles.root, { width: itemWidth }, { opacity, transform: [{ scale: scaleValue }] }]}
|
||||
shadowOpacity={25 / 100}
|
||||
shadowOffset={{ width: 0, height: 3 }}
|
||||
shadowRadius={8}
|
||||
|
@ -286,14 +285,12 @@ const cStyles = StyleSheet.create({
|
|||
alignItems: 'center',
|
||||
},
|
||||
content: {
|
||||
left: 16,
|
||||
flexDirection: I18nManager.isRTL ? 'row-reverse' : 'row',
|
||||
paddingTop: 16,
|
||||
},
|
||||
separatorStyle: { width: 16, height: 20 },
|
||||
});
|
||||
|
||||
const WalletsCarousel = forwardRef((props, ref) => {
|
||||
const carouselRef = useRef();
|
||||
const [loading, setLoading] = useState(true);
|
||||
const { preferredFiatCurrency, language } = useContext(BlueStorageContext);
|
||||
const renderItem = useCallback(
|
||||
({ item, index }) => (
|
||||
|
@ -308,49 +305,44 @@ const WalletsCarousel = forwardRef((props, ref) => {
|
|||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[props.vertical, props.selectedWallet, props.handleLongPress, props.onPress, preferredFiatCurrency, language],
|
||||
);
|
||||
const flatListRef = useRef();
|
||||
const ListHeaderComponent = () => <View style={cStyles.separatorStyle} />;
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
snapToItem: item => carouselRef?.current?.snapToItem(item),
|
||||
scrollToItem: ({ item }) => {
|
||||
setTimeout(() => {
|
||||
flatListRef?.current?.scrollToItem({ item, viewPosition: 0.3 });
|
||||
}, 300);
|
||||
},
|
||||
scrollToIndex: index => {
|
||||
setTimeout(() => {
|
||||
flatListRef?.current?.scrollToIndex({ index, viewPosition: 0.3 });
|
||||
}, 300);
|
||||
},
|
||||
}));
|
||||
|
||||
const { width } = useWindowDimensions();
|
||||
const sliderWidth = width * 1;
|
||||
const itemWidth = width * 0.82 > 375 ? 375 : width * 0.82;
|
||||
const sliderHeight = 190;
|
||||
|
||||
const onLayout = () => setLoading(false);
|
||||
const itemWidth = width * 0.82 > 375 ? 375 : width * 0.82;
|
||||
|
||||
return (
|
||||
<>
|
||||
{loading && (
|
||||
<View
|
||||
style={[
|
||||
cStyles.loading,
|
||||
{
|
||||
paddingVertical: sliderHeight / 2,
|
||||
paddingHorizontal: sliderWidth / 2,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<ActivityIndicator />
|
||||
</View>
|
||||
)}
|
||||
<Carousel
|
||||
ref={carouselRef}
|
||||
renderItem={renderItem}
|
||||
sliderWidth={sliderWidth}
|
||||
sliderHeight={sliderHeight}
|
||||
itemWidth={itemWidth}
|
||||
inactiveSlideScale={1}
|
||||
inactiveSlideOpacity={I18nManager.isRTL ? 1.0 : 0.7}
|
||||
activeSlideAlignment="start"
|
||||
initialNumToRender={10}
|
||||
inverted={I18nManager.isRTL && Platform.OS === 'android'}
|
||||
onLayout={onLayout}
|
||||
contentContainerCustomStyle={cStyles.content}
|
||||
{...props}
|
||||
/>
|
||||
</>
|
||||
<FlatList
|
||||
ref={flatListRef}
|
||||
renderItem={renderItem}
|
||||
keyExtractor={(_, index) => index.toString()}
|
||||
showsVerticalScrollIndicator={false}
|
||||
pagingEnabled
|
||||
disableIntervalMomentum={isHandset}
|
||||
snapToInterval={itemWidth} // Adjust to your content width
|
||||
decelerationRate="fast"
|
||||
contentContainerStyle={cStyles.content}
|
||||
directionalLockEnabled
|
||||
showsHorizontalScrollIndicator={false}
|
||||
initialNumToRender={10}
|
||||
ListHeaderComponent={ListHeaderComponent}
|
||||
style={props.vertical ? {} : { height: sliderHeight + 9 }}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useRef } from 'react';
|
||||
import { StyleSheet, Text, View } from 'react-native';
|
||||
import { useTheme } from '@react-navigation/native';
|
||||
import { useNavigation, useTheme } from '@react-navigation/native';
|
||||
import { ListItem } from 'react-native-elements';
|
||||
import PropTypes from 'prop-types';
|
||||
import { AddressTypeBadge } from './AddressTypeBadge';
|
||||
|
@ -9,11 +9,13 @@ import TooltipMenu from '../TooltipMenu';
|
|||
import Clipboard from '@react-native-clipboard/clipboard';
|
||||
import Share from 'react-native-share';
|
||||
|
||||
const AddressItem = ({ item, balanceUnit, onPress }) => {
|
||||
const AddressItem = ({ item, balanceUnit, walletID, allowSignVerifyMessage }) => {
|
||||
const { colors } = useTheme();
|
||||
const tooltip = useRef();
|
||||
const listItem = useRef();
|
||||
|
||||
const hasTransactions = item.transactions > 0;
|
||||
|
||||
const stylesHook = StyleSheet.create({
|
||||
container: {
|
||||
borderBottomColor: colors.lightBorder,
|
||||
|
@ -28,8 +30,33 @@ const AddressItem = ({ item, balanceUnit, onPress }) => {
|
|||
balance: {
|
||||
color: colors.alternativeTextColor,
|
||||
},
|
||||
address: {
|
||||
color: hasTransactions ? colors.darkGray : colors.buttonTextColor,
|
||||
},
|
||||
});
|
||||
|
||||
const { navigate } = useNavigation();
|
||||
|
||||
const navigateToReceive = () => {
|
||||
navigate('ReceiveDetailsRoot', {
|
||||
screen: 'ReceiveDetails',
|
||||
params: {
|
||||
walletID,
|
||||
address: item.address,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const navigateToSignVerify = () => {
|
||||
navigate('SignVerifyRoot', {
|
||||
screen: 'SignVerify',
|
||||
params: {
|
||||
walletID,
|
||||
address: item.address,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const showToolTipMenu = () => {
|
||||
tooltip.current.showMenu();
|
||||
};
|
||||
|
@ -44,30 +71,40 @@ const AddressItem = ({ item, balanceUnit, onPress }) => {
|
|||
Share.open({ message: item.address }).catch(error => console.log(error));
|
||||
};
|
||||
|
||||
const getAvailableActions = () => {
|
||||
const actions = [
|
||||
{
|
||||
id: 'copyToClipboard',
|
||||
text: loc.transactions.details_copy,
|
||||
onPress: handleCopyPress,
|
||||
},
|
||||
{
|
||||
id: 'share',
|
||||
text: loc.receive.details_share,
|
||||
onPress: handleSharePress,
|
||||
},
|
||||
];
|
||||
|
||||
if (allowSignVerifyMessage) {
|
||||
actions.push({
|
||||
id: 'signVerify',
|
||||
text: loc.addresses.sign_title,
|
||||
onPress: navigateToSignVerify,
|
||||
});
|
||||
}
|
||||
|
||||
return actions;
|
||||
};
|
||||
|
||||
const render = () => {
|
||||
return (
|
||||
<View>
|
||||
<TooltipMenu
|
||||
ref={tooltip}
|
||||
anchorRef={listItem}
|
||||
actions={[
|
||||
{
|
||||
id: 'copyToClipboard',
|
||||
text: loc.transactions.details_copy,
|
||||
onPress: handleCopyPress,
|
||||
},
|
||||
{
|
||||
id: 'share',
|
||||
text: loc.receive.details_share,
|
||||
onPress: handleSharePress,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<TooltipMenu ref={tooltip} anchorRef={listItem} actions={getAvailableActions()} />
|
||||
<ListItem
|
||||
ref={listItem}
|
||||
key={`${item.key}`}
|
||||
button
|
||||
onPress={onPress}
|
||||
onPress={navigateToReceive}
|
||||
containerStyle={stylesHook.container}
|
||||
onLongPress={showToolTipMenu}
|
||||
>
|
||||
|
@ -76,9 +113,16 @@ const AddressItem = ({ item, balanceUnit, onPress }) => {
|
|||
<Text style={[styles.index, stylesHook.index]}>{item.index + 1}</Text>{' '}
|
||||
<Text style={[stylesHook.address, styles.address]}>{item.address}</Text>
|
||||
</ListItem.Title>
|
||||
<ListItem.Subtitle style={[stylesHook.list, styles.balance, stylesHook.balance]}>{balance}</ListItem.Subtitle>
|
||||
<View style={styles.subtitle}>
|
||||
<Text style={[stylesHook.list, styles.balance, stylesHook.balance]}>{balance}</Text>
|
||||
</View>
|
||||
</ListItem.Content>
|
||||
<AddressTypeBadge isInternal={item.isInternal} />
|
||||
<View style={styles.labels}>
|
||||
<AddressTypeBadge isInternal={item.isInternal} hasTransactions={hasTransactions} />
|
||||
<Text style={[stylesHook.list, styles.balance, stylesHook.balance]}>
|
||||
{loc.addresses.transactions}: {item.transactions}
|
||||
</Text>
|
||||
</View>
|
||||
</ListItem>
|
||||
</View>
|
||||
);
|
||||
|
@ -89,7 +133,7 @@ const AddressItem = ({ item, balanceUnit, onPress }) => {
|
|||
|
||||
const styles = StyleSheet.create({
|
||||
address: {
|
||||
fontWeight: '600',
|
||||
fontWeight: 'bold',
|
||||
marginHorizontal: 40,
|
||||
},
|
||||
index: {
|
||||
|
@ -99,6 +143,12 @@ const styles = StyleSheet.create({
|
|||
marginTop: 8,
|
||||
marginLeft: 14,
|
||||
},
|
||||
subtitle: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
width: '100%',
|
||||
},
|
||||
});
|
||||
|
||||
AddressItem.propTypes = {
|
||||
|
|
|
@ -9,27 +9,46 @@ const styles = StyleSheet.create({
|
|||
paddingVertical: 4,
|
||||
paddingHorizontal: 10,
|
||||
borderRadius: 20,
|
||||
alignSelf: 'flex-end',
|
||||
},
|
||||
badgeText: {
|
||||
fontSize: 12,
|
||||
textAlign: 'center',
|
||||
},
|
||||
});
|
||||
|
||||
const AddressTypeBadge = ({ isInternal }) => {
|
||||
const AddressTypeBadge = ({ isInternal, hasTransactions }) => {
|
||||
const { colors } = useTheme();
|
||||
|
||||
const stylesHook = StyleSheet.create({
|
||||
changeBadge: { backgroundColor: colors.changeBackground },
|
||||
receiveBadge: { backgroundColor: colors.receiveBackground },
|
||||
usedBadge: { backgroundColor: colors.buttonDisabledBackgroundColor },
|
||||
changeText: { color: colors.changeText },
|
||||
receiveText: { color: colors.receiveText },
|
||||
usedText: { color: colors.alternativeTextColor },
|
||||
});
|
||||
|
||||
const badgeLabel = isInternal ? loc.addresses.type_change : loc.addresses.type_receive;
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
const badgeLabel = hasTransactions
|
||||
? loc.addresses.type_used
|
||||
: isInternal
|
||||
? loc.addresses.type_change
|
||||
: loc.addresses.type_receive;
|
||||
|
||||
const badgeStyle = isInternal ? stylesHook.changeBadge : stylesHook.receiveBadge;
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
const badgeStyle = hasTransactions
|
||||
? stylesHook.usedBadge
|
||||
: isInternal
|
||||
? stylesHook.changeBadge
|
||||
: stylesHook.receiveBadge;
|
||||
|
||||
const textStyle = isInternal ? stylesHook.changeText : stylesHook.receiveText;
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
const textStyle = hasTransactions
|
||||
? stylesHook.usedText
|
||||
: isInternal
|
||||
? stylesHook.changeText
|
||||
: stylesHook.receiveText;
|
||||
|
||||
return (
|
||||
<View style={[styles.container, badgeStyle]}>
|
||||
|
@ -40,6 +59,7 @@ const AddressTypeBadge = ({ isInternal }) => {
|
|||
|
||||
AddressTypeBadge.propTypes = {
|
||||
isInternal: PropTypes.bool,
|
||||
hasTransactions: PropTypes.bool,
|
||||
};
|
||||
|
||||
export { AddressTypeBadge };
|
||||
|
|
94
components/addresses/AddressTypeTabs.js
Normal file
94
components/addresses/AddressTypeTabs.js
Normal file
|
@ -0,0 +1,94 @@
|
|||
import { useTheme } from '@react-navigation/native';
|
||||
import React from 'react';
|
||||
import { View, Text, StyleSheet } from 'react-native';
|
||||
import loc from '../../loc';
|
||||
|
||||
export const TABS = {
|
||||
EXTERNAL: 'receive',
|
||||
INTERNAL: 'change',
|
||||
};
|
||||
|
||||
const AddressTypeTabs = ({ currentTab, setCurrentTab }) => {
|
||||
const { colors } = useTheme();
|
||||
|
||||
const stylesHook = StyleSheet.create({
|
||||
activeTab: {
|
||||
backgroundColor: colors.modal,
|
||||
},
|
||||
activeText: {
|
||||
fontWeight: 'bold',
|
||||
color: colors.foregroundColor,
|
||||
},
|
||||
inactiveTab: {
|
||||
fontWeight: 'normal',
|
||||
color: colors.foregroundColor,
|
||||
},
|
||||
backTabs: {
|
||||
backgroundColor: colors.buttonDisabledBackgroundColor,
|
||||
},
|
||||
});
|
||||
|
||||
const tabs = Object.entries(TABS).map(([key, value]) => {
|
||||
return {
|
||||
key,
|
||||
value,
|
||||
name: loc.addresses[`type_${value}`],
|
||||
};
|
||||
});
|
||||
|
||||
const changeToTab = tabKey => {
|
||||
if (tabKey in TABS) {
|
||||
setCurrentTab(TABS[tabKey]);
|
||||
}
|
||||
};
|
||||
|
||||
const render = () => {
|
||||
const tabsButtons = tabs.map(tab => {
|
||||
const isActive = tab.value === currentTab;
|
||||
|
||||
const tabStyle = isActive ? stylesHook.activeTab : stylesHook.inactiveTab;
|
||||
const textStyle = isActive ? stylesHook.activeText : stylesHook.inactiveTab;
|
||||
|
||||
return (
|
||||
<View key={tab.key} onPress={() => changeToTab(tab.key)} style={[styles.tab, tabStyle]}>
|
||||
<Text onPress={() => changeToTab(tab.key)} style={textStyle}>{tab.name}</Text>
|
||||
</View>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View style={[stylesHook.backTabs, styles.backTabs]}>
|
||||
<View style={styles.tabs}>{tabsButtons}</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
return render();
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
backTabs: {
|
||||
padding: 4,
|
||||
marginVertical: 8,
|
||||
borderRadius: 8,
|
||||
},
|
||||
tabs: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
tab: {
|
||||
borderRadius: 6,
|
||||
paddingVertical: 8,
|
||||
paddingHorizontal: 16,
|
||||
},
|
||||
});
|
||||
|
||||
export { AddressTypeTabs };
|
|
@ -21,7 +21,7 @@ const navigationStyle = ({ closeButton = false, closeButtonFunc, ...opts }, form
|
|||
navigation.goBack(null);
|
||||
};
|
||||
headerRight = () => (
|
||||
<TouchableOpacity style={styles.button} onPress={handleClose} testID="NavigationCloseButton">
|
||||
<TouchableOpacity accessibilityRole="button" style={styles.button} onPress={handleClose} testID="NavigationCloseButton">
|
||||
<Image source={theme.closeImage} />
|
||||
</TouchableOpacity>
|
||||
);
|
||||
|
@ -71,6 +71,7 @@ export const navigationStyleTx = (opts, formatter) => {
|
|||
headerTintColor: theme.colors.foregroundColor,
|
||||
headerLeft: () => (
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
style={styles.button}
|
||||
onPress={() => {
|
||||
Keyboard.dismiss();
|
||||
|
|
44
fastlane/metadata/android/en-US/full_description.txt
Normal file
44
fastlane/metadata/android/en-US/full_description.txt
Normal file
|
@ -0,0 +1,44 @@
|
|||
A Bitcoin wallet that allows you to store, send Bitcoin, receive Bitcoin and buy Bitcoin with focus on security and simplicity.
|
||||
On BlueWallet, a bitcoin wallet you own you private keys. A Bitcoin wallet made by Bitcoin users for the community.
|
||||
You can instantly transact with anyone in the world and transform the financial system right from your pocket.
|
||||
Create for free unlimited number of bitcoin wallets or import your existing one on your Android device. It's simple and fast.
|
||||
_____
|
||||
|
||||
Here's what you get:
|
||||
|
||||
1 - Security by design
|
||||
|
||||
• Open Source
|
||||
MIT licensed, you can build it and run it on your own! Made with ReactNative
|
||||
|
||||
• Plausible deniability
|
||||
Password which decrypts fake bitcoin wallets if you are forced to disclose your access
|
||||
|
||||
• Full encryption
|
||||
On top of the iOS multi-layer encryption, we encrypt everything with added passwords
|
||||
|
||||
• SegWit & HD wallets
|
||||
SegWit supported and HD wallets enable
|
||||
|
||||
2 - Focused on your experience
|
||||
|
||||
• Be in control
|
||||
Private keys never leave your device.
You control your private keys
|
||||
|
||||
• Flexible fees
|
||||
Starting from 1 Satoshi. Defined by you, the user
|
||||
|
||||
• Replace-By-Fee
|
||||
(RBF) Speed-up your transactions by increasing the fee (BIP125)
|
||||
|
||||
• Watch-only wallets
|
||||
Watch-only wallets allow you to keep an eye on your cold storage without touching the hardware.
|
||||
|
||||
• Lightning Network
|
||||
Lightning wallet with zero-configuration. Unfairly cheap and fast transactions with the best Bitcoin user experience.
|
||||
|
||||
• Buy Bitcoin
|
||||
Enter in the open financial revolution with the ability to buy Bitcoin directly in your wallet.
|
||||
|
||||
• Local Trader
|
||||
A p2p Bitcoin Trading platform, that allows you to buy and sell bitcoin directly to other users without 3rd parties.
|
BIN
fastlane/metadata/android/en-US/images/icon.png
Normal file
BIN
fastlane/metadata/android/en-US/images/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
Binary file not shown.
After Width: | Height: | Size: 92 KiB |
Binary file not shown.
After Width: | Height: | Size: 88 KiB |
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
Binary file not shown.
After Width: | Height: | Size: 52 KiB |
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
1
fastlane/metadata/android/en-US/short_description.txt
Normal file
1
fastlane/metadata/android/en-US/short_description.txt
Normal file
|
@ -0,0 +1 @@
|
|||
Thin Bitcoin Wallet Built with React Native and Electrum
|
1
fastlane/metadata/android/en-US/title.txt
Normal file
1
fastlane/metadata/android/en-US/title.txt
Normal file
|
@ -0,0 +1 @@
|
|||
BlueWallet Bitcoin Wallet
|
File diff suppressed because it is too large
Load diff
|
@ -65,10 +65,8 @@
|
|||
allowLocationSimulation = "YES"
|
||||
launchAutomaticallySubstyle = "8"
|
||||
notificationPayloadFile = "BlueWalletWatch Extension/PushNotificationPayload.apns">
|
||||
<RemoteRunnable
|
||||
runnableDebuggingMode = "2"
|
||||
BundleIdentifier = "com.apple.Carousel"
|
||||
RemotePath = "/BlueWallet">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B40D4E2F225841EC00428FCC"
|
||||
|
@ -76,7 +74,7 @@
|
|||
BlueprintName = "BlueWalletWatch"
|
||||
ReferencedContainer = "container:BlueWallet.xcodeproj">
|
||||
</BuildableReference>
|
||||
</RemoteRunnable>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
|
@ -86,10 +84,8 @@
|
|||
debugDocumentVersioning = "YES"
|
||||
launchAutomaticallySubstyle = "8"
|
||||
notificationPayloadFile = "BlueWalletWatch Extension/PushNotificationPayload.apns">
|
||||
<RemoteRunnable
|
||||
runnableDebuggingMode = "2"
|
||||
BundleIdentifier = "com.apple.Carousel"
|
||||
RemotePath = "/BlueWallet">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B40D4E2F225841EC00428FCC"
|
||||
|
@ -97,16 +93,7 @@
|
|||
BlueprintName = "BlueWalletWatch"
|
||||
ReferencedContainer = "container:BlueWallet.xcodeproj">
|
||||
</BuildableReference>
|
||||
</RemoteRunnable>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B40D4E2F225841EC00428FCC"
|
||||
BuildableName = "BlueWalletWatch.app"
|
||||
BlueprintName = "BlueWalletWatch"
|
||||
ReferencedContainer = "container:BlueWallet.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
|
|
|
@ -64,10 +64,8 @@
|
|||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES"
|
||||
notificationPayloadFile = "BlueWalletWatch Extension/PushNotificationPayload.apns">
|
||||
<RemoteRunnable
|
||||
runnableDebuggingMode = "2"
|
||||
BundleIdentifier = "com.apple.Carousel"
|
||||
RemotePath = "/BlueWallet">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B40D4E2F225841EC00428FCC"
|
||||
|
@ -75,7 +73,7 @@
|
|||
BlueprintName = "BlueWalletWatch"
|
||||
ReferencedContainer = "container:BlueWallet.xcodeproj">
|
||||
</BuildableReference>
|
||||
</RemoteRunnable>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
|
@ -83,10 +81,8 @@
|
|||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<RemoteRunnable
|
||||
runnableDebuggingMode = "2"
|
||||
BundleIdentifier = "com.apple.Carousel"
|
||||
RemotePath = "/BlueWallet">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B40D4E2F225841EC00428FCC"
|
||||
|
@ -94,16 +90,7 @@
|
|||
BlueprintName = "BlueWalletWatch"
|
||||
ReferencedContainer = "container:BlueWallet.xcodeproj">
|
||||
</BuildableReference>
|
||||
</RemoteRunnable>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B40D4E2F225841EC00428FCC"
|
||||
BuildableName = "BlueWalletWatch.app"
|
||||
BlueprintName = "BlueWalletWatch"
|
||||
ReferencedContainer = "container:BlueWallet.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
|
|
|
@ -167,10 +167,6 @@
|
|||
<string>In order to import an image for scanning, we need your permission to access your photo library.</string>
|
||||
<key>NSSpeechRecognitionUsageDescription</key>
|
||||
<string>This alert should not show up as we do not require this data</string>
|
||||
<key>NSUserActivityTypes</key>
|
||||
<array>
|
||||
<string>ConfigurationIntent</string>
|
||||
</array>
|
||||
<key>UIAppFonts</key>
|
||||
<array>
|
||||
<string>AntDesign.ttf</string>
|
||||
|
|
|
@ -39,12 +39,14 @@ class ComplicationController: NSObject, CLKComplicationDataSource {
|
|||
let entry: CLKComplicationTimelineEntry
|
||||
let date: Date
|
||||
let valueLabel: String
|
||||
let valueSmallLabel: String
|
||||
let currencySymbol: String
|
||||
let timeLabel: String
|
||||
if let price = marketData?.formattedRateForComplication, let marketDatadata = marketData?.date, let lastUpdated = marketData?.formattedDate {
|
||||
if let price = marketData?.formattedRateForComplication, let priceAbbreviated = marketData?.formattedRateForSmallComplication, let marketDatadata = marketData?.date, let lastUpdated = marketData?.formattedDate {
|
||||
date = marketDatadata
|
||||
valueLabel = price
|
||||
timeLabel = lastUpdated
|
||||
valueSmallLabel = priceAbbreviated
|
||||
if let preferredFiatCurrency = UserDefaults.standard.string(forKey: "preferredFiatCurrency"), let preferredFiatUnit = fiatUnit(currency: preferredFiatCurrency) {
|
||||
currencySymbol = preferredFiatUnit.symbol
|
||||
} else {
|
||||
|
@ -53,24 +55,25 @@ class ComplicationController: NSObject, CLKComplicationDataSource {
|
|||
} else {
|
||||
valueLabel = "--"
|
||||
timeLabel = "--"
|
||||
valueSmallLabel = "--"
|
||||
currencySymbol = fiatUnit(currency: "USD")!.symbol
|
||||
date = Date()
|
||||
}
|
||||
|
||||
let line1Text = CLKSimpleTextProvider(text:valueLabel)
|
||||
let line2Text = CLKSimpleTextProvider(text:currencySymbol)
|
||||
let line1SmallText = CLKSimpleTextProvider(text: valueSmallLabel)
|
||||
|
||||
switch complication.family {
|
||||
case .circularSmall:
|
||||
let template = CLKComplicationTemplateCircularSmallStackText()
|
||||
template.line1TextProvider = line1Text
|
||||
template.line1TextProvider = line1SmallText
|
||||
template.line2TextProvider = line2Text
|
||||
entry = CLKComplicationTimelineEntry(date: date, complicationTemplate: template)
|
||||
handler(entry)
|
||||
case .utilitarianSmallFlat:
|
||||
let template = CLKComplicationTemplateUtilitarianSmallFlat()
|
||||
if #available(watchOSApplicationExtension 6.0, *) {
|
||||
template.textProvider = CLKTextProvider(format: "%@%@", currencySymbol, valueLabel)
|
||||
template.textProvider = CLKTextProvider(format: "%@%@", currencySymbol, valueSmallLabel)
|
||||
} else {
|
||||
handler(nil)
|
||||
}
|
||||
|
@ -85,7 +88,7 @@ class ComplicationController: NSObject, CLKComplicationDataSource {
|
|||
case .graphicCircular:
|
||||
if #available(watchOSApplicationExtension 6.0, *) {
|
||||
let template = CLKComplicationTemplateGraphicCircularStackText()
|
||||
template.line1TextProvider = line1Text
|
||||
template.line1TextProvider = line1SmallText
|
||||
template.line2TextProvider = line2Text
|
||||
entry = CLKComplicationTimelineEntry(date: date, complicationTemplate: template)
|
||||
handler(entry)
|
||||
|
@ -94,14 +97,14 @@ class ComplicationController: NSObject, CLKComplicationDataSource {
|
|||
}
|
||||
case .modularSmall:
|
||||
let template = CLKComplicationTemplateModularSmallStackText()
|
||||
template.line1TextProvider = line1Text
|
||||
template.line1TextProvider = line1SmallText
|
||||
template.line2TextProvider = line2Text
|
||||
entry = CLKComplicationTimelineEntry(date: date, complicationTemplate: template)
|
||||
handler(entry)
|
||||
case .graphicCorner:
|
||||
let template = CLKComplicationTemplateGraphicCornerStackText()
|
||||
if #available(watchOSApplicationExtension 6.0, *) {
|
||||
template.outerTextProvider = CLKTextProvider(format: "%@", valueLabel)
|
||||
template.outerTextProvider = CLKTextProvider(format: "%@", valueSmallLabel)
|
||||
template.innerTextProvider = CLKTextProvider(format: "%@", currencySymbol)
|
||||
} else {
|
||||
handler(nil)
|
||||
|
@ -111,7 +114,7 @@ class ComplicationController: NSObject, CLKComplicationDataSource {
|
|||
case .graphicBezel:
|
||||
let template = CLKComplicationTemplateGraphicBezelCircularText()
|
||||
if #available(watchOSApplicationExtension 6.0, *) {
|
||||
template.textProvider = CLKTextProvider(format: "%@%@", currencySymbol, valueLabel)
|
||||
template.textProvider = CLKTextProvider(format: "%@%@", currencySymbol, valueSmallLabel)
|
||||
let imageProvider = CLKFullColorImageProvider(fullColorImage: UIImage(named: "Complication/Graphic Bezel")!)
|
||||
let circularTemplate = CLKComplicationTemplateGraphicCircularImage()
|
||||
circularTemplate.imageProvider = imageProvider
|
||||
|
@ -135,7 +138,7 @@ class ComplicationController: NSObject, CLKComplicationDataSource {
|
|||
if #available(watchOSApplicationExtension 6.0, *) {
|
||||
template.headerTextProvider = CLKTextProvider(format: "Bitcoin Price")
|
||||
template.body1TextProvider = CLKTextProvider(format: "%@%@", currencySymbol, valueLabel)
|
||||
template.body2TextProvider = CLKTextProvider(format: "%@", timeLabel)
|
||||
template.body2TextProvider = CLKTextProvider(format: "at %@", timeLabel)
|
||||
entry = CLKComplicationTimelineEntry(date: date, complicationTemplate: template)
|
||||
handler(entry)
|
||||
} else {
|
||||
|
@ -145,7 +148,7 @@ class ComplicationController: NSObject, CLKComplicationDataSource {
|
|||
let template = CLKComplicationTemplateExtraLargeStackText()
|
||||
if #available(watchOSApplicationExtension 6.0, *) {
|
||||
template.line1TextProvider = CLKTextProvider(format: "%@%@", currencySymbol, valueLabel)
|
||||
template.line2TextProvider = CLKTextProvider(format: "%@", timeLabel)
|
||||
template.line2TextProvider = CLKTextProvider(format: "at %@", timeLabel)
|
||||
entry = CLKComplicationTimelineEntry(date: date, complicationTemplate: template)
|
||||
handler(entry)
|
||||
} else {
|
||||
|
@ -156,7 +159,7 @@ class ComplicationController: NSObject, CLKComplicationDataSource {
|
|||
if #available(watchOSApplicationExtension 6.0, *) {
|
||||
template.headerTextProvider = CLKTextProvider(format: "Bitcoin Price")
|
||||
template.body1TextProvider = CLKTextProvider(format: "%@%@", currencySymbol, valueLabel)
|
||||
template.body2TextProvider = CLKTextProvider(format: "%@", timeLabel)
|
||||
template.body2TextProvider = CLKTextProvider(format: "at %@", timeLabel)
|
||||
entry = CLKComplicationTimelineEntry(date: date, complicationTemplate: template)
|
||||
handler(entry)
|
||||
} else {
|
||||
|
@ -166,7 +169,7 @@ class ComplicationController: NSObject, CLKComplicationDataSource {
|
|||
if #available(watchOSApplicationExtension 7.0, *) {
|
||||
let template = CLKComplicationTemplateGraphicExtraLargeCircularStackText()
|
||||
template.line1TextProvider = CLKTextProvider(format: "%@%@", currencySymbol, valueLabel)
|
||||
template.line1TextProvider = CLKTextProvider(format: "%@", timeLabel)
|
||||
template.line1TextProvider = CLKTextProvider(format: "at %@", timeLabel)
|
||||
entry = CLKComplicationTimelineEntry(date: date, complicationTemplate: template)
|
||||
handler(entry)
|
||||
} else {
|
||||
|
|
18
ios/Podfile
18
ios/Podfile
|
@ -2,19 +2,23 @@ platform :ios, '11.1'
|
|||
workspace 'BlueWallet'
|
||||
require_relative '../node_modules/react-native/scripts/react_native_pods'
|
||||
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
|
||||
|
||||
|
||||
target 'BlueWallet' do
|
||||
config = use_native_modules!
|
||||
|
||||
use_react_native!(:path => config["reactNativePath"])
|
||||
use_react_native!(
|
||||
:path => config[:reactNativePath],
|
||||
# to enable hermes on iOS, change `false` to `true` and then install pods
|
||||
:hermes_enabled => false
|
||||
)
|
||||
|
||||
# Enables Flipper.
|
||||
#
|
||||
# Note that if you have use_frameworks! enabled, Flipper will not work and
|
||||
# you should disable these next few lines.
|
||||
use_flipper!({ 'Flipper-Folly' => '2.5.3', 'Flipper' => '0.87.0', 'Flipper-RSocket' => '1.3.1' })
|
||||
use_flipper!()
|
||||
post_install do |installer|
|
||||
flipper_post_install(installer)
|
||||
react_native_post_install(installer)
|
||||
installer.pods_project.targets.each do |target|
|
||||
target.build_configurations.each do |config|
|
||||
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '10.0'
|
||||
|
@ -30,10 +34,6 @@ target 'BlueWallet' do
|
|||
|
||||
end
|
||||
|
||||
target 'MarketWidgetExtension' do
|
||||
pod 'SwiftSocket', :git => 'https://github.com/swiftsocket/SwiftSocket.git', :branch => 'master'
|
||||
end
|
||||
|
||||
target 'WalletInformationAndMarketWidgetExtension' do
|
||||
target 'WidgetsExtension' do
|
||||
pod 'SwiftSocket', :git => 'https://github.com/swiftsocket/SwiftSocket.git', :branch => 'master'
|
||||
end
|
648
ios/Podfile.lock
648
ios/Podfile.lock
|
@ -4,15 +4,15 @@ PODS:
|
|||
- React
|
||||
- CocoaAsyncSocket (7.6.5)
|
||||
- DoubleConversion (1.1.6)
|
||||
- FBLazyVector (0.63.4)
|
||||
- FBReactNativeSpec (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- RCTRequired (= 0.63.4)
|
||||
- RCTTypeSafety (= 0.63.4)
|
||||
- React-Core (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- ReactCommon/turbomodule/core (= 0.63.4)
|
||||
- Flipper (0.87.0):
|
||||
- FBLazyVector (0.64.2)
|
||||
- FBReactNativeSpec (0.64.2):
|
||||
- RCT-Folly (= 2020.01.13.00)
|
||||
- RCTRequired (= 0.64.2)
|
||||
- RCTTypeSafety (= 0.64.2)
|
||||
- React-Core (= 0.64.2)
|
||||
- React-jsi (= 0.64.2)
|
||||
- ReactCommon/turbomodule/core (= 0.64.2)
|
||||
- Flipper (0.75.1):
|
||||
- Flipper-Folly (~> 2.5)
|
||||
- Flipper-RSocket (~> 1.3)
|
||||
- Flipper-DoubleConversion (1.1.7)
|
||||
|
@ -26,58 +26,38 @@ PODS:
|
|||
- Flipper-PeerTalk (0.0.4)
|
||||
- Flipper-RSocket (1.3.1):
|
||||
- Flipper-Folly (~> 2.5)
|
||||
- FlipperKit (0.87.0):
|
||||
- FlipperKit/Core (= 0.87.0)
|
||||
- FlipperKit/Core (0.87.0):
|
||||
- Flipper (~> 0.87.0)
|
||||
- FlipperKit (0.75.1):
|
||||
- FlipperKit/Core (= 0.75.1)
|
||||
- FlipperKit/Core (0.75.1):
|
||||
- Flipper (~> 0.75.1)
|
||||
- FlipperKit/CppBridge
|
||||
- FlipperKit/FBCxxFollyDynamicConvert
|
||||
- FlipperKit/FBDefines
|
||||
- FlipperKit/FKPortForwarding
|
||||
- FlipperKit/CppBridge (0.87.0):
|
||||
- Flipper (~> 0.87.0)
|
||||
- FlipperKit/FBCxxFollyDynamicConvert (0.87.0):
|
||||
- FlipperKit/CppBridge (0.75.1):
|
||||
- Flipper (~> 0.75.1)
|
||||
- FlipperKit/FBCxxFollyDynamicConvert (0.75.1):
|
||||
- Flipper-Folly (~> 2.5)
|
||||
- FlipperKit/FBDefines (0.87.0)
|
||||
- FlipperKit/FKPortForwarding (0.87.0):
|
||||
- FlipperKit/FBDefines (0.75.1)
|
||||
- FlipperKit/FKPortForwarding (0.75.1):
|
||||
- CocoaAsyncSocket (~> 7.6)
|
||||
- Flipper-PeerTalk (~> 0.0.4)
|
||||
- FlipperKit/FlipperKitHighlightOverlay (0.87.0)
|
||||
- FlipperKit/FlipperKitLayoutHelpers (0.87.0):
|
||||
- FlipperKit/FlipperKitHighlightOverlay (0.75.1)
|
||||
- FlipperKit/FlipperKitLayoutPlugin (0.75.1):
|
||||
- FlipperKit/Core
|
||||
- FlipperKit/FlipperKitHighlightOverlay
|
||||
- FlipperKit/FlipperKitLayoutTextSearchable
|
||||
- FlipperKit/FlipperKitLayoutIOSDescriptors (0.87.0):
|
||||
- FlipperKit/Core
|
||||
- FlipperKit/FlipperKitHighlightOverlay
|
||||
- FlipperKit/FlipperKitLayoutHelpers
|
||||
- YogaKit (~> 1.18)
|
||||
- FlipperKit/FlipperKitLayoutPlugin (0.87.0):
|
||||
- FlipperKit/FlipperKitLayoutTextSearchable (0.75.1)
|
||||
- FlipperKit/FlipperKitNetworkPlugin (0.75.1):
|
||||
- FlipperKit/Core
|
||||
- FlipperKit/FlipperKitHighlightOverlay
|
||||
- FlipperKit/FlipperKitLayoutHelpers
|
||||
- FlipperKit/FlipperKitLayoutIOSDescriptors
|
||||
- FlipperKit/FlipperKitLayoutTextSearchable
|
||||
- YogaKit (~> 1.18)
|
||||
- FlipperKit/FlipperKitLayoutTextSearchable (0.87.0)
|
||||
- FlipperKit/FlipperKitNetworkPlugin (0.87.0):
|
||||
- FlipperKit/FlipperKitReactPlugin (0.75.1):
|
||||
- FlipperKit/Core
|
||||
- FlipperKit/FlipperKitReactPlugin (0.87.0):
|
||||
- FlipperKit/FlipperKitUserDefaultsPlugin (0.75.1):
|
||||
- FlipperKit/Core
|
||||
- FlipperKit/FlipperKitUserDefaultsPlugin (0.87.0):
|
||||
- FlipperKit/Core
|
||||
- FlipperKit/SKIOSNetworkPlugin (0.87.0):
|
||||
- FlipperKit/SKIOSNetworkPlugin (0.75.1):
|
||||
- FlipperKit/Core
|
||||
- FlipperKit/FlipperKitNetworkPlugin
|
||||
- Folly (2020.01.13.00):
|
||||
- boost-for-react-native
|
||||
- DoubleConversion
|
||||
- Folly/Default (= 2020.01.13.00)
|
||||
- glog
|
||||
- Folly/Default (2020.01.13.00):
|
||||
- boost-for-react-native
|
||||
- DoubleConversion
|
||||
- glog
|
||||
- GCDWebServer (3.5.4):
|
||||
- GCDWebServer/Core (= 3.5.4)
|
||||
- GCDWebServer/Core (3.5.4)
|
||||
|
@ -90,183 +70,210 @@ PODS:
|
|||
- OpenSSL-Universal (1.1.180)
|
||||
- PasscodeAuth (1.0.0):
|
||||
- React
|
||||
- RCTRequired (0.63.4)
|
||||
- RCTTypeSafety (0.63.4):
|
||||
- FBLazyVector (= 0.63.4)
|
||||
- Folly (= 2020.01.13.00)
|
||||
- RCTRequired (= 0.63.4)
|
||||
- React-Core (= 0.63.4)
|
||||
- React (0.63.4):
|
||||
- React-Core (= 0.63.4)
|
||||
- React-Core/DevSupport (= 0.63.4)
|
||||
- React-Core/RCTWebSocket (= 0.63.4)
|
||||
- React-RCTActionSheet (= 0.63.4)
|
||||
- React-RCTAnimation (= 0.63.4)
|
||||
- React-RCTBlob (= 0.63.4)
|
||||
- React-RCTImage (= 0.63.4)
|
||||
- React-RCTLinking (= 0.63.4)
|
||||
- React-RCTNetwork (= 0.63.4)
|
||||
- React-RCTSettings (= 0.63.4)
|
||||
- React-RCTText (= 0.63.4)
|
||||
- React-RCTVibration (= 0.63.4)
|
||||
- React-callinvoker (0.63.4)
|
||||
- React-Core (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- RCT-Folly (2020.01.13.00):
|
||||
- boost-for-react-native
|
||||
- DoubleConversion
|
||||
- glog
|
||||
- React-Core/Default (= 0.63.4)
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsiexecutor (= 0.63.4)
|
||||
- RCT-Folly/Default (= 2020.01.13.00)
|
||||
- RCT-Folly/Default (2020.01.13.00):
|
||||
- boost-for-react-native
|
||||
- DoubleConversion
|
||||
- glog
|
||||
- RCTRequired (0.64.2)
|
||||
- RCTTypeSafety (0.64.2):
|
||||
- FBLazyVector (= 0.64.2)
|
||||
- RCT-Folly (= 2020.01.13.00)
|
||||
- RCTRequired (= 0.64.2)
|
||||
- React-Core (= 0.64.2)
|
||||
- React (0.64.2):
|
||||
- React-Core (= 0.64.2)
|
||||
- React-Core/DevSupport (= 0.64.2)
|
||||
- React-Core/RCTWebSocket (= 0.64.2)
|
||||
- React-RCTActionSheet (= 0.64.2)
|
||||
- React-RCTAnimation (= 0.64.2)
|
||||
- React-RCTBlob (= 0.64.2)
|
||||
- React-RCTImage (= 0.64.2)
|
||||
- React-RCTLinking (= 0.64.2)
|
||||
- React-RCTNetwork (= 0.64.2)
|
||||
- React-RCTSettings (= 0.64.2)
|
||||
- React-RCTText (= 0.64.2)
|
||||
- React-RCTVibration (= 0.64.2)
|
||||
- React-callinvoker (0.64.2)
|
||||
- React-Core (0.64.2):
|
||||
- glog
|
||||
- RCT-Folly (= 2020.01.13.00)
|
||||
- React-Core/Default (= 0.64.2)
|
||||
- React-cxxreact (= 0.64.2)
|
||||
- React-jsi (= 0.64.2)
|
||||
- React-jsiexecutor (= 0.64.2)
|
||||
- React-perflogger (= 0.64.2)
|
||||
- Yoga
|
||||
- React-Core/CoreModulesHeaders (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- React-Core/CoreModulesHeaders (0.64.2):
|
||||
- glog
|
||||
- RCT-Folly (= 2020.01.13.00)
|
||||
- React-Core/Default
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsiexecutor (= 0.63.4)
|
||||
- React-cxxreact (= 0.64.2)
|
||||
- React-jsi (= 0.64.2)
|
||||
- React-jsiexecutor (= 0.64.2)
|
||||
- React-perflogger (= 0.64.2)
|
||||
- Yoga
|
||||
- React-Core/Default (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- React-Core/Default (0.64.2):
|
||||
- glog
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsiexecutor (= 0.63.4)
|
||||
- RCT-Folly (= 2020.01.13.00)
|
||||
- React-cxxreact (= 0.64.2)
|
||||
- React-jsi (= 0.64.2)
|
||||
- React-jsiexecutor (= 0.64.2)
|
||||
- React-perflogger (= 0.64.2)
|
||||
- Yoga
|
||||
- React-Core/DevSupport (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- React-Core/DevSupport (0.64.2):
|
||||
- glog
|
||||
- React-Core/Default (= 0.63.4)
|
||||
- React-Core/RCTWebSocket (= 0.63.4)
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsiexecutor (= 0.63.4)
|
||||
- React-jsinspector (= 0.63.4)
|
||||
- RCT-Folly (= 2020.01.13.00)
|
||||
- React-Core/Default (= 0.64.2)
|
||||
- React-Core/RCTWebSocket (= 0.64.2)
|
||||
- React-cxxreact (= 0.64.2)
|
||||
- React-jsi (= 0.64.2)
|
||||
- React-jsiexecutor (= 0.64.2)
|
||||
- React-jsinspector (= 0.64.2)
|
||||
- React-perflogger (= 0.64.2)
|
||||
- Yoga
|
||||
- React-Core/RCTActionSheetHeaders (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- React-Core/RCTActionSheetHeaders (0.64.2):
|
||||
- glog
|
||||
- RCT-Folly (= 2020.01.13.00)
|
||||
- React-Core/Default
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsiexecutor (= 0.63.4)
|
||||
- React-cxxreact (= 0.64.2)
|
||||
- React-jsi (= 0.64.2)
|
||||
- React-jsiexecutor (= 0.64.2)
|
||||
- React-perflogger (= 0.64.2)
|
||||
- Yoga
|
||||
- React-Core/RCTAnimationHeaders (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- React-Core/RCTAnimationHeaders (0.64.2):
|
||||
- glog
|
||||
- RCT-Folly (= 2020.01.13.00)
|
||||
- React-Core/Default
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsiexecutor (= 0.63.4)
|
||||
- React-cxxreact (= 0.64.2)
|
||||
- React-jsi (= 0.64.2)
|
||||
- React-jsiexecutor (= 0.64.2)
|
||||
- React-perflogger (= 0.64.2)
|
||||
- Yoga
|
||||
- React-Core/RCTBlobHeaders (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- React-Core/RCTBlobHeaders (0.64.2):
|
||||
- glog
|
||||
- RCT-Folly (= 2020.01.13.00)
|
||||
- React-Core/Default
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsiexecutor (= 0.63.4)
|
||||
- React-cxxreact (= 0.64.2)
|
||||
- React-jsi (= 0.64.2)
|
||||
- React-jsiexecutor (= 0.64.2)
|
||||
- React-perflogger (= 0.64.2)
|
||||
- Yoga
|
||||
- React-Core/RCTImageHeaders (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- React-Core/RCTImageHeaders (0.64.2):
|
||||
- glog
|
||||
- RCT-Folly (= 2020.01.13.00)
|
||||
- React-Core/Default
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsiexecutor (= 0.63.4)
|
||||
- React-cxxreact (= 0.64.2)
|
||||
- React-jsi (= 0.64.2)
|
||||
- React-jsiexecutor (= 0.64.2)
|
||||
- React-perflogger (= 0.64.2)
|
||||
- Yoga
|
||||
- React-Core/RCTLinkingHeaders (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- React-Core/RCTLinkingHeaders (0.64.2):
|
||||
- glog
|
||||
- RCT-Folly (= 2020.01.13.00)
|
||||
- React-Core/Default
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsiexecutor (= 0.63.4)
|
||||
- React-cxxreact (= 0.64.2)
|
||||
- React-jsi (= 0.64.2)
|
||||
- React-jsiexecutor (= 0.64.2)
|
||||
- React-perflogger (= 0.64.2)
|
||||
- Yoga
|
||||
- React-Core/RCTNetworkHeaders (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- React-Core/RCTNetworkHeaders (0.64.2):
|
||||
- glog
|
||||
- RCT-Folly (= 2020.01.13.00)
|
||||
- React-Core/Default
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsiexecutor (= 0.63.4)
|
||||
- React-cxxreact (= 0.64.2)
|
||||
- React-jsi (= 0.64.2)
|
||||
- React-jsiexecutor (= 0.64.2)
|
||||
- React-perflogger (= 0.64.2)
|
||||
- Yoga
|
||||
- React-Core/RCTSettingsHeaders (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- React-Core/RCTSettingsHeaders (0.64.2):
|
||||
- glog
|
||||
- RCT-Folly (= 2020.01.13.00)
|
||||
- React-Core/Default
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsiexecutor (= 0.63.4)
|
||||
- React-cxxreact (= 0.64.2)
|
||||
- React-jsi (= 0.64.2)
|
||||
- React-jsiexecutor (= 0.64.2)
|
||||
- React-perflogger (= 0.64.2)
|
||||
- Yoga
|
||||
- React-Core/RCTTextHeaders (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- React-Core/RCTTextHeaders (0.64.2):
|
||||
- glog
|
||||
- RCT-Folly (= 2020.01.13.00)
|
||||
- React-Core/Default
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsiexecutor (= 0.63.4)
|
||||
- React-cxxreact (= 0.64.2)
|
||||
- React-jsi (= 0.64.2)
|
||||
- React-jsiexecutor (= 0.64.2)
|
||||
- React-perflogger (= 0.64.2)
|
||||
- Yoga
|
||||
- React-Core/RCTVibrationHeaders (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- React-Core/RCTVibrationHeaders (0.64.2):
|
||||
- glog
|
||||
- RCT-Folly (= 2020.01.13.00)
|
||||
- React-Core/Default
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsiexecutor (= 0.63.4)
|
||||
- React-cxxreact (= 0.64.2)
|
||||
- React-jsi (= 0.64.2)
|
||||
- React-jsiexecutor (= 0.64.2)
|
||||
- React-perflogger (= 0.64.2)
|
||||
- Yoga
|
||||
- React-Core/RCTWebSocket (0.63.4):
|
||||
- Folly (= 2020.01.13.00)
|
||||
- React-Core/RCTWebSocket (0.64.2):
|
||||
- glog
|
||||
- React-Core/Default (= 0.63.4)
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsiexecutor (= 0.63.4)
|
||||
- RCT-Folly (= 2020.01.13.00)
|
||||
- React-Core/Default (= 0.64.2)
|
||||
- React-cxxreact (= 0.64.2)
|
||||
- React-jsi (= 0.64.2)
|
||||
- React-jsiexecutor (= 0.64.2)
|
||||
- React-perflogger (= 0.64.2)
|
||||
- Yoga
|
||||
- React-CoreModules (0.63.4):
|
||||
- FBReactNativeSpec (= 0.63.4)
|
||||
- Folly (= 2020.01.13.00)
|
||||
- RCTTypeSafety (= 0.63.4)
|
||||
- React-Core/CoreModulesHeaders (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-RCTImage (= 0.63.4)
|
||||
- ReactCommon/turbomodule/core (= 0.63.4)
|
||||
- React-cxxreact (0.63.4):
|
||||
- React-CoreModules (0.64.2):
|
||||
- FBReactNativeSpec (= 0.64.2)
|
||||
- RCT-Folly (= 2020.01.13.00)
|
||||
- RCTTypeSafety (= 0.64.2)
|
||||
- React-Core/CoreModulesHeaders (= 0.64.2)
|
||||
- React-jsi (= 0.64.2)
|
||||
- React-RCTImage (= 0.64.2)
|
||||
- ReactCommon/turbomodule/core (= 0.64.2)
|
||||
- React-cxxreact (0.64.2):
|
||||
- boost-for-react-native (= 1.63.0)
|
||||
- DoubleConversion
|
||||
- Folly (= 2020.01.13.00)
|
||||
- glog
|
||||
- React-callinvoker (= 0.63.4)
|
||||
- React-jsinspector (= 0.63.4)
|
||||
- React-jsi (0.63.4):
|
||||
- RCT-Folly (= 2020.01.13.00)
|
||||
- React-callinvoker (= 0.64.2)
|
||||
- React-jsi (= 0.64.2)
|
||||
- React-jsinspector (= 0.64.2)
|
||||
- React-perflogger (= 0.64.2)
|
||||
- React-runtimeexecutor (= 0.64.2)
|
||||
- React-jsi (0.64.2):
|
||||
- boost-for-react-native (= 1.63.0)
|
||||
- DoubleConversion
|
||||
- Folly (= 2020.01.13.00)
|
||||
- glog
|
||||
- React-jsi/Default (= 0.63.4)
|
||||
- React-jsi/Default (0.63.4):
|
||||
- RCT-Folly (= 2020.01.13.00)
|
||||
- React-jsi/Default (= 0.64.2)
|
||||
- React-jsi/Default (0.64.2):
|
||||
- boost-for-react-native (= 1.63.0)
|
||||
- DoubleConversion
|
||||
- Folly (= 2020.01.13.00)
|
||||
- glog
|
||||
- React-jsiexecutor (0.63.4):
|
||||
- RCT-Folly (= 2020.01.13.00)
|
||||
- React-jsiexecutor (0.64.2):
|
||||
- DoubleConversion
|
||||
- Folly (= 2020.01.13.00)
|
||||
- glog
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-jsinspector (0.63.4)
|
||||
- RCT-Folly (= 2020.01.13.00)
|
||||
- React-cxxreact (= 0.64.2)
|
||||
- React-jsi (= 0.64.2)
|
||||
- React-perflogger (= 0.64.2)
|
||||
- React-jsinspector (0.64.2)
|
||||
- react-native-blue-crypto (1.0.0):
|
||||
- React
|
||||
- react-native-blur (0.8.0):
|
||||
- React
|
||||
- react-native-camera (3.43.5):
|
||||
- react-native-camera (3.44.1):
|
||||
- React-Core
|
||||
- react-native-camera/RCT (= 3.43.5)
|
||||
- react-native-camera/RN (= 3.43.5)
|
||||
- react-native-camera/RCT (3.43.5):
|
||||
- react-native-camera/RCT (= 3.44.1)
|
||||
- react-native-camera/RN (= 3.44.1)
|
||||
- react-native-camera/RCT (3.44.1):
|
||||
- React-Core
|
||||
- react-native-camera/RN (3.43.5):
|
||||
- react-native-camera/RN (3.44.1):
|
||||
- React-Core
|
||||
- react-native-document-picker (3.5.4):
|
||||
- React
|
||||
|
@ -274,11 +281,9 @@ PODS:
|
|||
- React
|
||||
- react-native-idle-timer (2.1.6):
|
||||
- React-Core
|
||||
- react-native-image-picker (3.3.2):
|
||||
- react-native-image-picker (3.8.1):
|
||||
- React-Core
|
||||
- react-native-is-catalyst (1.0.0):
|
||||
- React
|
||||
- react-native-randombytes (3.6.0):
|
||||
- react-native-randombytes (3.6.1):
|
||||
- React-Core
|
||||
- react-native-safe-area-context (3.2.0):
|
||||
- React-Core
|
||||
|
@ -287,111 +292,115 @@ PODS:
|
|||
- React
|
||||
- react-native-tor (0.1.7):
|
||||
- React
|
||||
- react-native-webview (11.4.0):
|
||||
- react-native-webview (11.6.4):
|
||||
- React-Core
|
||||
- react-native-widget-center (0.0.4):
|
||||
- React
|
||||
- React-RCTActionSheet (0.63.4):
|
||||
- React-Core/RCTActionSheetHeaders (= 0.63.4)
|
||||
- React-RCTAnimation (0.63.4):
|
||||
- FBReactNativeSpec (= 0.63.4)
|
||||
- Folly (= 2020.01.13.00)
|
||||
- RCTTypeSafety (= 0.63.4)
|
||||
- React-Core/RCTAnimationHeaders (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- ReactCommon/turbomodule/core (= 0.63.4)
|
||||
- React-RCTBlob (0.63.4):
|
||||
- FBReactNativeSpec (= 0.63.4)
|
||||
- Folly (= 2020.01.13.00)
|
||||
- React-Core/RCTBlobHeaders (= 0.63.4)
|
||||
- React-Core/RCTWebSocket (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-RCTNetwork (= 0.63.4)
|
||||
- ReactCommon/turbomodule/core (= 0.63.4)
|
||||
- React-RCTImage (0.63.4):
|
||||
- FBReactNativeSpec (= 0.63.4)
|
||||
- Folly (= 2020.01.13.00)
|
||||
- RCTTypeSafety (= 0.63.4)
|
||||
- React-Core/RCTImageHeaders (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- React-RCTNetwork (= 0.63.4)
|
||||
- ReactCommon/turbomodule/core (= 0.63.4)
|
||||
- React-RCTLinking (0.63.4):
|
||||
- FBReactNativeSpec (= 0.63.4)
|
||||
- React-Core/RCTLinkingHeaders (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- ReactCommon/turbomodule/core (= 0.63.4)
|
||||
- React-RCTNetwork (0.63.4):
|
||||
- FBReactNativeSpec (= 0.63.4)
|
||||
- Folly (= 2020.01.13.00)
|
||||
- RCTTypeSafety (= 0.63.4)
|
||||
- React-Core/RCTNetworkHeaders (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- ReactCommon/turbomodule/core (= 0.63.4)
|
||||
- React-RCTSettings (0.63.4):
|
||||
- FBReactNativeSpec (= 0.63.4)
|
||||
- Folly (= 2020.01.13.00)
|
||||
- RCTTypeSafety (= 0.63.4)
|
||||
- React-Core/RCTSettingsHeaders (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- ReactCommon/turbomodule/core (= 0.63.4)
|
||||
- React-RCTText (0.63.4):
|
||||
- React-Core/RCTTextHeaders (= 0.63.4)
|
||||
- React-RCTVibration (0.63.4):
|
||||
- FBReactNativeSpec (= 0.63.4)
|
||||
- Folly (= 2020.01.13.00)
|
||||
- React-Core/RCTVibrationHeaders (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- ReactCommon/turbomodule/core (= 0.63.4)
|
||||
- ReactCommon/turbomodule/core (0.63.4):
|
||||
- React-perflogger (0.64.2)
|
||||
- React-RCTActionSheet (0.64.2):
|
||||
- React-Core/RCTActionSheetHeaders (= 0.64.2)
|
||||
- React-RCTAnimation (0.64.2):
|
||||
- FBReactNativeSpec (= 0.64.2)
|
||||
- RCT-Folly (= 2020.01.13.00)
|
||||
- RCTTypeSafety (= 0.64.2)
|
||||
- React-Core/RCTAnimationHeaders (= 0.64.2)
|
||||
- React-jsi (= 0.64.2)
|
||||
- ReactCommon/turbomodule/core (= 0.64.2)
|
||||
- React-RCTBlob (0.64.2):
|
||||
- FBReactNativeSpec (= 0.64.2)
|
||||
- RCT-Folly (= 2020.01.13.00)
|
||||
- React-Core/RCTBlobHeaders (= 0.64.2)
|
||||
- React-Core/RCTWebSocket (= 0.64.2)
|
||||
- React-jsi (= 0.64.2)
|
||||
- React-RCTNetwork (= 0.64.2)
|
||||
- ReactCommon/turbomodule/core (= 0.64.2)
|
||||
- React-RCTImage (0.64.2):
|
||||
- FBReactNativeSpec (= 0.64.2)
|
||||
- RCT-Folly (= 2020.01.13.00)
|
||||
- RCTTypeSafety (= 0.64.2)
|
||||
- React-Core/RCTImageHeaders (= 0.64.2)
|
||||
- React-jsi (= 0.64.2)
|
||||
- React-RCTNetwork (= 0.64.2)
|
||||
- ReactCommon/turbomodule/core (= 0.64.2)
|
||||
- React-RCTLinking (0.64.2):
|
||||
- FBReactNativeSpec (= 0.64.2)
|
||||
- React-Core/RCTLinkingHeaders (= 0.64.2)
|
||||
- React-jsi (= 0.64.2)
|
||||
- ReactCommon/turbomodule/core (= 0.64.2)
|
||||
- React-RCTNetwork (0.64.2):
|
||||
- FBReactNativeSpec (= 0.64.2)
|
||||
- RCT-Folly (= 2020.01.13.00)
|
||||
- RCTTypeSafety (= 0.64.2)
|
||||
- React-Core/RCTNetworkHeaders (= 0.64.2)
|
||||
- React-jsi (= 0.64.2)
|
||||
- ReactCommon/turbomodule/core (= 0.64.2)
|
||||
- React-RCTSettings (0.64.2):
|
||||
- FBReactNativeSpec (= 0.64.2)
|
||||
- RCT-Folly (= 2020.01.13.00)
|
||||
- RCTTypeSafety (= 0.64.2)
|
||||
- React-Core/RCTSettingsHeaders (= 0.64.2)
|
||||
- React-jsi (= 0.64.2)
|
||||
- ReactCommon/turbomodule/core (= 0.64.2)
|
||||
- React-RCTText (0.64.2):
|
||||
- React-Core/RCTTextHeaders (= 0.64.2)
|
||||
- React-RCTVibration (0.64.2):
|
||||
- FBReactNativeSpec (= 0.64.2)
|
||||
- RCT-Folly (= 2020.01.13.00)
|
||||
- React-Core/RCTVibrationHeaders (= 0.64.2)
|
||||
- React-jsi (= 0.64.2)
|
||||
- ReactCommon/turbomodule/core (= 0.64.2)
|
||||
- React-runtimeexecutor (0.64.2):
|
||||
- React-jsi (= 0.64.2)
|
||||
- ReactCommon/turbomodule/core (0.64.2):
|
||||
- DoubleConversion
|
||||
- Folly (= 2020.01.13.00)
|
||||
- glog
|
||||
- React-callinvoker (= 0.63.4)
|
||||
- React-Core (= 0.63.4)
|
||||
- React-cxxreact (= 0.63.4)
|
||||
- React-jsi (= 0.63.4)
|
||||
- RealmJS (10.2.0):
|
||||
- RCT-Folly (= 2020.01.13.00)
|
||||
- React-callinvoker (= 0.64.2)
|
||||
- React-Core (= 0.64.2)
|
||||
- React-cxxreact (= 0.64.2)
|
||||
- React-jsi (= 0.64.2)
|
||||
- React-perflogger (= 0.64.2)
|
||||
- RealmJS (10.4.2):
|
||||
- GCDWebServer
|
||||
- React
|
||||
- RemobileReactNativeQrcodeLocalImage (1.0.4):
|
||||
- React
|
||||
- RNCAsyncStorage (1.15.2):
|
||||
- RNCAsyncStorage (1.15.5):
|
||||
- React-Core
|
||||
- RNCClipboard (1.7.0):
|
||||
- React-Core
|
||||
- RNCMaskedView (0.1.10):
|
||||
- RNCMaskedView (0.1.11):
|
||||
- React
|
||||
- RNCPushNotificationIOS (1.8.0):
|
||||
- React-Core
|
||||
- RNDefaultPreference (1.4.3):
|
||||
- React
|
||||
- RNDeviceInfo (8.1.2):
|
||||
- RNDeviceInfo (8.1.3):
|
||||
- React-Core
|
||||
- RNFS (2.16.6):
|
||||
- RNFS (2.18.0):
|
||||
- React
|
||||
- RNGestureHandler (1.10.3):
|
||||
- React-Core
|
||||
- RNHandoff (0.0.3):
|
||||
- React
|
||||
- RNInAppBrowser (3.5.1):
|
||||
- RNKeychain (7.0.0):
|
||||
- React-Core
|
||||
- RNLocalize (2.0.2):
|
||||
- RNLocalize (2.1.1):
|
||||
- React-Core
|
||||
- RNPrivacySnapshot (1.0.0):
|
||||
- React
|
||||
- RNQuickAction (0.3.13):
|
||||
- React
|
||||
- RNRate (1.2.4):
|
||||
- React
|
||||
- RNRate (1.2.6):
|
||||
- React-Core
|
||||
- RNReactNativeHapticFeedback (1.11.0):
|
||||
- React-Core
|
||||
- RNReanimated (2.1.0):
|
||||
- RNReanimated (2.2.0):
|
||||
- DoubleConversion
|
||||
- FBLazyVector
|
||||
- FBReactNativeSpec
|
||||
- Folly
|
||||
- glog
|
||||
- RCT-Folly
|
||||
- RCTRequired
|
||||
- RCTTypeSafety
|
||||
- React
|
||||
|
@ -415,14 +424,15 @@ PODS:
|
|||
- React-RCTVibration
|
||||
- ReactCommon/turbomodule/core
|
||||
- Yoga
|
||||
- RNScreens (2.18.1):
|
||||
- RNScreens (3.4.0):
|
||||
- React-Core
|
||||
- React-RCTImage
|
||||
- RNSecureKeyStore (1.0.0):
|
||||
- React
|
||||
- RNSentry (2.5.0-beta.1):
|
||||
- RNSentry (2.6.0):
|
||||
- React-Core
|
||||
- Sentry (= 7.0.0)
|
||||
- RNShare (5.2.2):
|
||||
- RNShare (6.2.1):
|
||||
- React-Core
|
||||
- RNSVG (12.1.1):
|
||||
- React
|
||||
|
@ -444,31 +454,31 @@ DEPENDENCIES:
|
|||
- BVLinearGradient (from `../node_modules/react-native-linear-gradient`)
|
||||
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
|
||||
- FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
|
||||
- FBReactNativeSpec (from `../node_modules/react-native/Libraries/FBReactNativeSpec`)
|
||||
- Flipper (= 0.87.0)
|
||||
- FBReactNativeSpec (from `../node_modules/react-native/React/FBReactNativeSpec`)
|
||||
- Flipper (~> 0.75.1)
|
||||
- Flipper-DoubleConversion (= 1.1.7)
|
||||
- Flipper-Folly (= 2.5.3)
|
||||
- Flipper-Folly (~> 2.5.3)
|
||||
- Flipper-Glog (= 0.3.6)
|
||||
- Flipper-PeerTalk (~> 0.0.4)
|
||||
- Flipper-RSocket (= 1.3.1)
|
||||
- FlipperKit (= 0.87.0)
|
||||
- FlipperKit/Core (= 0.87.0)
|
||||
- FlipperKit/CppBridge (= 0.87.0)
|
||||
- FlipperKit/FBCxxFollyDynamicConvert (= 0.87.0)
|
||||
- FlipperKit/FBDefines (= 0.87.0)
|
||||
- FlipperKit/FKPortForwarding (= 0.87.0)
|
||||
- FlipperKit/FlipperKitHighlightOverlay (= 0.87.0)
|
||||
- FlipperKit/FlipperKitLayoutPlugin (= 0.87.0)
|
||||
- FlipperKit/FlipperKitLayoutTextSearchable (= 0.87.0)
|
||||
- FlipperKit/FlipperKitNetworkPlugin (= 0.87.0)
|
||||
- FlipperKit/FlipperKitReactPlugin (= 0.87.0)
|
||||
- FlipperKit/FlipperKitUserDefaultsPlugin (= 0.87.0)
|
||||
- FlipperKit/SKIOSNetworkPlugin (= 0.87.0)
|
||||
- Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`)
|
||||
- Flipper-RSocket (~> 1.3)
|
||||
- FlipperKit (~> 0.75.1)
|
||||
- FlipperKit/Core (~> 0.75.1)
|
||||
- FlipperKit/CppBridge (~> 0.75.1)
|
||||
- FlipperKit/FBCxxFollyDynamicConvert (~> 0.75.1)
|
||||
- FlipperKit/FBDefines (~> 0.75.1)
|
||||
- FlipperKit/FKPortForwarding (~> 0.75.1)
|
||||
- FlipperKit/FlipperKitHighlightOverlay (~> 0.75.1)
|
||||
- FlipperKit/FlipperKitLayoutPlugin (~> 0.75.1)
|
||||
- FlipperKit/FlipperKitLayoutTextSearchable (~> 0.75.1)
|
||||
- FlipperKit/FlipperKitNetworkPlugin (~> 0.75.1)
|
||||
- FlipperKit/FlipperKitReactPlugin (~> 0.75.1)
|
||||
- FlipperKit/FlipperKitUserDefaultsPlugin (~> 0.75.1)
|
||||
- FlipperKit/SKIOSNetworkPlugin (~> 0.75.1)
|
||||
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
|
||||
- lottie-ios (from `../node_modules/lottie-ios`)
|
||||
- lottie-react-native (from `../node_modules/lottie-react-native`)
|
||||
- PasscodeAuth (from `../node_modules/react-native-passcode-auth`)
|
||||
- RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
|
||||
- RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`)
|
||||
- RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`)
|
||||
- React (from `../node_modules/react-native/`)
|
||||
|
@ -488,13 +498,13 @@ DEPENDENCIES:
|
|||
- react-native-fingerprint-scanner (from `../node_modules/react-native-fingerprint-scanner`)
|
||||
- react-native-idle-timer (from `../node_modules/react-native-idle-timer`)
|
||||
- react-native-image-picker (from `../node_modules/react-native-image-picker`)
|
||||
- react-native-is-catalyst (from `../node_modules/react-native-is-catalyst`)
|
||||
- react-native-randombytes (from `../node_modules/react-native-randombytes`)
|
||||
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
|
||||
- react-native-tcp-socket (from `../node_modules/react-native-tcp-socket`)
|
||||
- react-native-tor (from `../node_modules/react-native-tor`)
|
||||
- react-native-webview (from `../node_modules/react-native-webview`)
|
||||
- react-native-widget-center (from `../node_modules/react-native-widget-center`)
|
||||
- React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`)
|
||||
- React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`)
|
||||
- React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`)
|
||||
- React-RCTBlob (from `../node_modules/react-native/Libraries/Blob`)
|
||||
|
@ -504,6 +514,7 @@ DEPENDENCIES:
|
|||
- React-RCTSettings (from `../node_modules/react-native/Libraries/Settings`)
|
||||
- React-RCTText (from `../node_modules/react-native/Libraries/Text`)
|
||||
- React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`)
|
||||
- React-runtimeexecutor (from `../node_modules/react-native/ReactCommon/runtimeexecutor`)
|
||||
- ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`)
|
||||
- RealmJS (from `../node_modules/realm`)
|
||||
- "RemobileReactNativeQrcodeLocalImage (from `../node_modules/@remobile/react-native-qrcode-local-image`)"
|
||||
|
@ -516,7 +527,7 @@ DEPENDENCIES:
|
|||
- RNFS (from `../node_modules/react-native-fs`)
|
||||
- RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
|
||||
- RNHandoff (from `../node_modules/react-native-handoff`)
|
||||
- RNInAppBrowser (from `../node_modules/react-native-inappbrowser-reborn`)
|
||||
- RNKeychain (from `../node_modules/react-native-keychain`)
|
||||
- RNLocalize (from `../node_modules/react-native-localize`)
|
||||
- RNPrivacySnapshot (from `../node_modules/react-native-privacy-snapshot`)
|
||||
- RNQuickAction (from `../node_modules/react-native-quick-actions`)
|
||||
|
@ -559,9 +570,7 @@ EXTERNAL SOURCES:
|
|||
FBLazyVector:
|
||||
:path: "../node_modules/react-native/Libraries/FBLazyVector"
|
||||
FBReactNativeSpec:
|
||||
:path: "../node_modules/react-native/Libraries/FBReactNativeSpec"
|
||||
Folly:
|
||||
:podspec: "../node_modules/react-native/third-party-podspecs/Folly.podspec"
|
||||
:path: "../node_modules/react-native/React/FBReactNativeSpec"
|
||||
glog:
|
||||
:podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec"
|
||||
lottie-ios:
|
||||
|
@ -570,6 +579,8 @@ EXTERNAL SOURCES:
|
|||
:path: "../node_modules/lottie-react-native"
|
||||
PasscodeAuth:
|
||||
:path: "../node_modules/react-native-passcode-auth"
|
||||
RCT-Folly:
|
||||
:podspec: "../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec"
|
||||
RCTRequired:
|
||||
:path: "../node_modules/react-native/Libraries/RCTRequired"
|
||||
RCTTypeSafety:
|
||||
|
@ -604,8 +615,6 @@ EXTERNAL SOURCES:
|
|||
:path: "../node_modules/react-native-idle-timer"
|
||||
react-native-image-picker:
|
||||
:path: "../node_modules/react-native-image-picker"
|
||||
react-native-is-catalyst:
|
||||
:path: "../node_modules/react-native-is-catalyst"
|
||||
react-native-randombytes:
|
||||
:path: "../node_modules/react-native-randombytes"
|
||||
react-native-safe-area-context:
|
||||
|
@ -618,6 +627,8 @@ EXTERNAL SOURCES:
|
|||
:path: "../node_modules/react-native-webview"
|
||||
react-native-widget-center:
|
||||
:path: "../node_modules/react-native-widget-center"
|
||||
React-perflogger:
|
||||
:path: "../node_modules/react-native/ReactCommon/reactperflogger"
|
||||
React-RCTActionSheet:
|
||||
:path: "../node_modules/react-native/Libraries/ActionSheetIOS"
|
||||
React-RCTAnimation:
|
||||
|
@ -636,6 +647,8 @@ EXTERNAL SOURCES:
|
|||
:path: "../node_modules/react-native/Libraries/Text"
|
||||
React-RCTVibration:
|
||||
:path: "../node_modules/react-native/Libraries/Vibration"
|
||||
React-runtimeexecutor:
|
||||
:path: "../node_modules/react-native/ReactCommon/runtimeexecutor"
|
||||
ReactCommon:
|
||||
:path: "../node_modules/react-native/ReactCommon"
|
||||
RealmJS:
|
||||
|
@ -660,8 +673,8 @@ EXTERNAL SOURCES:
|
|||
:path: "../node_modules/react-native-gesture-handler"
|
||||
RNHandoff:
|
||||
:path: "../node_modules/react-native-handoff"
|
||||
RNInAppBrowser:
|
||||
:path: "../node_modules/react-native-inappbrowser-reborn"
|
||||
RNKeychain:
|
||||
:path: "../node_modules/react-native-keychain"
|
||||
RNLocalize:
|
||||
:path: "../node_modules/react-native-localize"
|
||||
RNPrivacySnapshot:
|
||||
|
@ -705,89 +718,90 @@ SPEC CHECKSUMS:
|
|||
boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
|
||||
BVLinearGradient: e3aad03778a456d77928f594a649e96995f1c872
|
||||
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
|
||||
DoubleConversion: cde416483dac037923206447da6e1454df403714
|
||||
FBLazyVector: 3bb422f41b18121b71783a905c10e58606f7dc3e
|
||||
FBReactNativeSpec: f2c97f2529dd79c083355182cc158c9f98f4bd6e
|
||||
Flipper: 1bd2db48dcc31e4b167b9a33ec1df01c2ded4893
|
||||
DoubleConversion: cf9b38bf0b2d048436d9a82ad2abe1404f11e7de
|
||||
FBLazyVector: e686045572151edef46010a6f819ade377dfeb4b
|
||||
FBReactNativeSpec: da2b9104721789106ad3943049ccf61ef6f4db39
|
||||
Flipper: d3da1aa199aad94455ae725e9f3aa43f3ec17021
|
||||
Flipper-DoubleConversion: 38631e41ef4f9b12861c67d17cb5518d06badc41
|
||||
Flipper-Folly: 755929a4f851b2fb2c347d533a23f191b008554c
|
||||
Flipper-Glog: 1dfd6abf1e922806c52ceb8701a3599a79a200a6
|
||||
Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9
|
||||
Flipper-RSocket: 127954abe8b162fcaf68d2134d34dc2bd7076154
|
||||
FlipperKit: 651f50a42eb95c01b3e89a60996dd6aded529eeb
|
||||
Folly: b73c3869541e86821df3c387eb0af5f65addfab4
|
||||
FlipperKit: 8a20b5c5fcf9436cac58551dc049867247f64b00
|
||||
GCDWebServer: 2c156a56c8226e2d5c0c3f208a3621ccffbe3ce4
|
||||
glog: 40a13f7840415b9a77023fbcae0f1e6f43192af3
|
||||
glog: 73c2498ac6884b13ede40eda8228cb1eee9d9d62
|
||||
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
|
||||
lottie-ios: 3a3758ef5a008e762faec9c9d50a39842f26d124
|
||||
lottie-react-native: 4dff8fe8d10ddef9e7880e770080f4a56121397e
|
||||
OpenSSL-Universal: 1aa4f6a6ee7256b83db99ec1ccdaa80d10f9af9b
|
||||
PasscodeAuth: 1cc99b13d8e4de4716d7e2b4069af2f1a9de30b2
|
||||
RCTRequired: 082f10cd3f905d6c124597fd1c14f6f2655ff65e
|
||||
RCTTypeSafety: 8c9c544ecbf20337d069e4ae7fd9a377aadf504b
|
||||
React: b0a957a2c44da4113b0c4c9853d8387f8e64e615
|
||||
React-callinvoker: c3f44dd3cb195b6aa46621fff95ded79d59043fe
|
||||
React-Core: d3b2a1ac9a2c13c3bcde712d9281fc1c8a5b315b
|
||||
React-CoreModules: 0581ff36cb797da0943d424f69e7098e43e9be60
|
||||
React-cxxreact: c1480d4fda5720086c90df537ee7d285d4c57ac3
|
||||
React-jsi: a0418934cf48f25b485631deb27c64dc40fb4c31
|
||||
React-jsiexecutor: 93bd528844ad21dc07aab1c67cb10abae6df6949
|
||||
React-jsinspector: 58aef7155bc9a9683f5b60b35eccea8722a4f53a
|
||||
RCT-Folly: ec7a233ccc97cc556cf7237f0db1ff65b986f27c
|
||||
RCTRequired: 6d3e854f0e7260a648badd0d44fc364bc9da9728
|
||||
RCTTypeSafety: c1f31d19349c6b53085766359caac425926fafaa
|
||||
React: bda6b6d7ae912de97d7a61aa5c160db24aa2ad69
|
||||
React-callinvoker: 9840ea7e8e88ed73d438edb725574820b29b5baa
|
||||
React-Core: b5e385da7ce5f16a220fc60fd0749eae2c6120f0
|
||||
React-CoreModules: 17071a4e2c5239b01585f4aa8070141168ab298f
|
||||
React-cxxreact: 9be7b6340ed9f7c53e53deca7779f07cd66525ba
|
||||
React-jsi: 67747b9722f6dab2ffe15b011bcf6b3f2c3f1427
|
||||
React-jsiexecutor: 80c46bd381fd06e418e0d4f53672dc1d1945c4c3
|
||||
React-jsinspector: cc614ec18a9ca96fd275100c16d74d62ee11f0ae
|
||||
react-native-blue-crypto: 23f1558ad3d38d7a2edb7e2f6ed1bc520ed93e56
|
||||
react-native-blur: cad4d93b364f91e7b7931b3fa935455487e5c33c
|
||||
react-native-camera: 8a79f048c146e08e416c542bcf57984cbc7ed404
|
||||
react-native-camera: 6e6d25f6318980dd2837747760628b4442aac01a
|
||||
react-native-document-picker: c5752781fbc0c126c627c1549b037c139444a4cf
|
||||
react-native-fingerprint-scanner: c68136ca57e3704d7bdf5faa554ea535ce15b1d0
|
||||
react-native-idle-timer: 97b8283237d45146a7a5c25bdebe9e1e85f3687b
|
||||
react-native-image-picker: 5b2f1ea1f9230b131abbfb8a4aa1eb209aba9ed9
|
||||
react-native-is-catalyst: 52ee70e0123c82419dd4ce47dc4cc94b22467512
|
||||
react-native-randombytes: 45ae693012df24c9a07a5e578b3b567c01468581
|
||||
react-native-image-picker: 5a7dd93d4f9dd0d910da5dde3707fd96f70d03b3
|
||||
react-native-randombytes: 5fc412efe7b5c55b9002c0004d75fe5fabcaa507
|
||||
react-native-safe-area-context: e471852c5ed67eea4b10c5d9d43c1cebae3b231d
|
||||
react-native-tcp-socket: 96a4f104cdcc9c6621aafe92937f163d88447c5b
|
||||
react-native-tor: 4f389f5719dad633542b57ea32744e954730e7ef
|
||||
react-native-webview: fdd3c7c2ad173e8ca8c82729ed05f3fda2491a82
|
||||
react-native-webview: 4288b81c682bca4dcc9037afb3ee118af2655c03
|
||||
react-native-widget-center: 0f81d17beb163e7fb5848b06754d7d277fe7d99a
|
||||
React-RCTActionSheet: 89a0ca9f4a06c1f93c26067af074ccdce0f40336
|
||||
React-RCTAnimation: 1bde3ecc0c104c55df246eda516e0deb03c4e49b
|
||||
React-RCTBlob: a97d378b527740cc667e03ebfa183a75231ab0f0
|
||||
React-RCTImage: c1b1f2d3f43a4a528c8946d6092384b5c880d2f0
|
||||
React-RCTLinking: 35ae4ab9dc0410d1fcbdce4d7623194a27214fb2
|
||||
React-RCTNetwork: 29ec2696f8d8cfff7331fac83d3e893c95ef43ae
|
||||
React-RCTSettings: 60f0691bba2074ef394f95d4c2265ec284e0a46a
|
||||
React-RCTText: 5c51df3f08cb9dedc6e790161195d12bac06101c
|
||||
React-RCTVibration: ae4f914cfe8de7d4de95ae1ea6cc8f6315d73d9d
|
||||
ReactCommon: 73d79c7039f473b76db6ff7c6b159c478acbbb3b
|
||||
RealmJS: 5195064e9aeccf94ae3756bd9d0f2301b9074b07
|
||||
React-perflogger: 25373e382fed75ce768a443822f07098a15ab737
|
||||
React-RCTActionSheet: af7796ba49ffe4ca92e7277a5d992d37203f7da5
|
||||
React-RCTAnimation: 6a2e76ab50c6f25b428d81b76a5a45351c4d77aa
|
||||
React-RCTBlob: 02a2887023e0eed99391b6445b2e23a2a6f9226d
|
||||
React-RCTImage: ce5bf8e7438f2286d9b646a05d6ab11f38b0323d
|
||||
React-RCTLinking: ccd20742de14e020cb5f99d5c7e0bf0383aefbd9
|
||||
React-RCTNetwork: dfb9d089ab0753e5e5f55fc4b1210858f7245647
|
||||
React-RCTSettings: b14aef2d83699e48b410fb7c3ba5b66cd3291ae2
|
||||
React-RCTText: 41a2e952dd9adc5caf6fb68ed46b275194d5da5f
|
||||
React-RCTVibration: 24600e3b1aaa77126989bc58b6747509a1ba14f3
|
||||
React-runtimeexecutor: a9904c6d0218fb9f8b19d6dd88607225927668f9
|
||||
ReactCommon: 149906e01aa51142707a10665185db879898e966
|
||||
RealmJS: 90f2a558fdda19ebb9fcbc0a52c7b1d1b049fb88
|
||||
RemobileReactNativeQrcodeLocalImage: 57aadc12896b148fb5e04bc7c6805f3565f5c3fa
|
||||
RNCAsyncStorage: 502fbf425b9ee2d73ca6fa4b299dce0be411b422
|
||||
RNCAsyncStorage: 8324611026e8dc3706f829953aa6e3899f581589
|
||||
RNCClipboard: dac13db8b1ce9b998f1cbc7ca33440113602847f
|
||||
RNCMaskedView: f5c7d14d6847b7b44853f7acb6284c1da30a3459
|
||||
RNCMaskedView: f127cd9652acfa31b91dcff613e07ba18b774db6
|
||||
RNCPushNotificationIOS: 5b1cf9ad2aaa107ecb92d5d2d7005ba521b2b97a
|
||||
RNDefaultPreference: 21816c0a6f61a2829ccc0cef034392e9b509ee5f
|
||||
RNDeviceInfo: 4f480456c7ac8c9919448375399c1a6f14479549
|
||||
RNFS: 2bd9eb49dc82fa9676382f0585b992c424cd59df
|
||||
RNDeviceInfo: 49f6d50f861c7810fac2dd9b71cfb56cc1940e14
|
||||
RNFS: 3ab21fa6c56d65566d1fb26c2228e2b6132e5e32
|
||||
RNGestureHandler: a479ebd5ed4221a810967000735517df0d2db211
|
||||
RNHandoff: d3b0754cca3a6bcd9b25f544f733f7f033ccf5fa
|
||||
RNInAppBrowser: 3733c1aa6699983a1c9b4963e85d5e5a48ad297e
|
||||
RNLocalize: 47e22ef8c36df1d572e42a87c8ae22e3fcf551dd
|
||||
RNKeychain: f75b8c8b2f17d3b2aa1f25b4a0ac5b83d947ff8f
|
||||
RNLocalize: b9a5d22c7e4ecb5db604861f1b1fff6013e03340
|
||||
RNPrivacySnapshot: 71919dde3c6a29dd332115409c2aec564afee8f4
|
||||
RNQuickAction: 6d404a869dc872cde841ad3147416a670d13fa93
|
||||
RNRate: 2b31dad120cd1b78e33c6034808561c386a3dddf
|
||||
RNRate: e0af7e724e5fcf89578dbd22ab6395c85402ef29
|
||||
RNReactNativeHapticFeedback: 653a8c126a0f5e88ce15ffe280b3ff37e1fbb285
|
||||
RNReanimated: 70f662b5232dd5d19ccff581e919a54ea73df51c
|
||||
RNScreens: f7ad633b2e0190b77b6a7aab7f914fad6f198d8d
|
||||
RNReanimated: 9c13c86454bfd54dab7505c1a054470bfecd2563
|
||||
RNScreens: 21b73c94c9117e1110a79ee0ee80c93ccefed8ce
|
||||
RNSecureKeyStore: f1ad870e53806453039f650720d2845c678d89c8
|
||||
RNSentry: 1868bcfe8c69b2c3b2451439a38b3ebea0a7510f
|
||||
RNShare: 5cfe16bfd42cd2c4869a7692462181ac8cc15a6d
|
||||
RNSentry: a2b02b326ae4fce91ce7c57d3f7dcf8df4f72269
|
||||
RNShare: 5ac8f6532ca4cd80fc71caef1cfbba1854a6a045
|
||||
RNSVG: 551acb6562324b1d52a4e0758f7ca0ec234e278f
|
||||
RNVectorIcons: bc69e6a278b14842063605de32bec61f0b251a59
|
||||
RNWatch: e4c5d19506c94506860032fb68aedd5991beb985
|
||||
Sentry: 89d26e036063b9cb9caa59b6951dd2f8277aa13b
|
||||
SwiftSocket: c8d482e867ae4d3eb4c769e9382e123c1f1f833b
|
||||
ToolTipMenu: 4d89d95ddffd7539230bdbe02ee51bbde362e37e
|
||||
Yoga: 4bd86afe9883422a7c4028c00e34790f560923d6
|
||||
Yoga: 575c581c63e0d35c9a83f4b46d01d63abc1100ac
|
||||
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
|
||||
|
||||
PODFILE CHECKSUM: e5cd06c8e5cb8f554b34954eef0d4d4f18e7d9d2
|
||||
PODFILE CHECKSUM: d6f6c7992cb83adb1c2ca00fa4d3e3a3402f4787
|
||||
|
||||
COCOAPODS: 1.10.1
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.io.bluewallet.bluewallet</string>
|
||||
</array>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
|
@ -1,29 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>WalletInformationWidget</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
<string>com.apple.widgetkit-extension</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
|
@ -9,34 +9,35 @@
|
|||
import WidgetKit
|
||||
import SwiftUI
|
||||
|
||||
struct Provider: TimelineProvider {
|
||||
func placeholder(in context: Context) -> SimpleEntry {
|
||||
return SimpleEntry(date: Date(), marketData: MarketData(nextBlock: "26", sats: "9 134", price: "$10,000", rate: 10000), allWalletsBalance: WalletData(balance: 1000000, latestTransactionTime: LatestTransaction(isUnconfirmed: false, epochValue: 1568804029000)))
|
||||
struct WalletInformationWidgetProvider: TimelineProvider {
|
||||
typealias Entry = WalletInformationWidgetEntry
|
||||
func placeholder(in context: Context) -> WalletInformationWidgetEntry {
|
||||
return WalletInformationWidgetEntry(date: Date(), marketData: MarketData(nextBlock: "26", sats: "9 134", price: "$10,000", rate: 10000), allWalletsBalance: WalletData(balance: 1000000, latestTransactionTime: LatestTransaction(isUnconfirmed: false, epochValue: 1568804029000)))
|
||||
}
|
||||
|
||||
func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) {
|
||||
let entry: SimpleEntry
|
||||
func getSnapshot(in context: Context, completion: @escaping (WalletInformationWidgetEntry) -> ()) {
|
||||
let entry: WalletInformationWidgetEntry
|
||||
if (context.isPreview) {
|
||||
entry = SimpleEntry(date: Date(), marketData: MarketData(nextBlock: "26", sats: "9 134", price: "$10,000", rate: 10000), allWalletsBalance: WalletData(balance: 1000000, latestTransactionTime: LatestTransaction(isUnconfirmed: false, epochValue: 1568804029000)))
|
||||
entry = WalletInformationWidgetEntry(date: Date(), marketData: MarketData(nextBlock: "26", sats: "9 134", price: "$10,000", rate: 10000), allWalletsBalance: WalletData(balance: 1000000, latestTransactionTime: LatestTransaction(isUnconfirmed: false, epochValue: 1568804029000)))
|
||||
} else {
|
||||
entry = SimpleEntry(date: Date(), marketData: emptyMarketData)
|
||||
entry = WalletInformationWidgetEntry(date: Date(), marketData: emptyMarketData)
|
||||
}
|
||||
completion(entry)
|
||||
}
|
||||
|
||||
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
|
||||
var entries: [SimpleEntry] = []
|
||||
var entries: [WalletInformationWidgetEntry] = []
|
||||
let userPreferredCurrency = WidgetAPI.getUserPreferredCurrency();
|
||||
|
||||
let marketDataEntry = MarketData(nextBlock: "...", sats: "...", price: "...", rate: 0)
|
||||
let allwalletsBalance = WalletData(balance: UserDefaultsGroup.getAllWalletsBalance(), latestTransactionTime: UserDefaultsGroup.getAllWalletsLatestTransactionTime())
|
||||
WidgetAPI.fetchPrice(currency: userPreferredCurrency, completion: { (result, error) in
|
||||
let entry: SimpleEntry
|
||||
let entry: WalletInformationWidgetEntry
|
||||
if let result = result {
|
||||
entry = SimpleEntry(date: Date(), marketData: MarketData(nextBlock: "", sats: "", price: result.formattedRate ?? "!", rate: result.rateDouble), allWalletsBalance: allwalletsBalance)
|
||||
entry = WalletInformationWidgetEntry(date: Date(), marketData: MarketData(nextBlock: "", sats: "", price: result.formattedRate ?? "!", rate: result.rateDouble), allWalletsBalance: allwalletsBalance)
|
||||
|
||||
} else {
|
||||
entry = SimpleEntry(date: Date(), marketData: marketDataEntry, allWalletsBalance: allwalletsBalance)
|
||||
entry = WalletInformationWidgetEntry(date: Date(), marketData: marketDataEntry, allWalletsBalance: allwalletsBalance)
|
||||
}
|
||||
entries.append(entry)
|
||||
let timeline = Timeline(entries: entries, policy: .atEnd)
|
||||
|
@ -45,14 +46,14 @@ struct Provider: TimelineProvider {
|
|||
}
|
||||
}
|
||||
|
||||
struct SimpleEntry: TimelineEntry {
|
||||
struct WalletInformationWidgetEntry: TimelineEntry {
|
||||
let date: Date
|
||||
let marketData: MarketData
|
||||
var allWalletsBalance: WalletData = WalletData(balance: 0)
|
||||
}
|
||||
|
||||
struct WalletInformationWidgetEntryView : View {
|
||||
var entry: Provider.Entry
|
||||
let entry: WalletInformationWidgetEntry
|
||||
|
||||
var WalletBalance: some View {
|
||||
WalletInformationView(allWalletsBalance: entry.allWalletsBalance, marketData: entry.marketData)
|
||||
|
@ -65,22 +66,21 @@ struct WalletInformationWidgetEntryView : View {
|
|||
}
|
||||
}
|
||||
|
||||
@main
|
||||
struct WalletInformationWidget: Widget {
|
||||
let kind: String = "WalletInformationWidget"
|
||||
|
||||
var body: some WidgetConfiguration {
|
||||
StaticConfiguration(kind: kind, provider: Provider()) { entry in
|
||||
StaticConfiguration(kind: kind, provider: WalletInformationWidgetProvider()) { entry in
|
||||
WalletInformationWidgetEntryView(entry: entry)
|
||||
}
|
||||
.configurationDisplayName("Wallets")
|
||||
.description("View your total wallet balance.").supportedFamilies([.systemSmall])
|
||||
.configurationDisplayName("Balance")
|
||||
.description("View your accumulated balance.").supportedFamilies([.systemSmall])
|
||||
}
|
||||
}
|
||||
|
||||
struct WalletInformationWidget_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
WalletInformationWidgetEntryView(entry: SimpleEntry(date: Date(), marketData: MarketData(nextBlock: "26", sats: "9 134", price: "$10,000", rate: Double(0)), allWalletsBalance: WalletData(balance: 10000, latestTransactionTime: LatestTransaction(isUnconfirmed: false, epochValue: 1568804029000))))
|
||||
WalletInformationWidgetEntryView(entry: WalletInformationWidgetEntry(date: Date(), marketData: MarketData(nextBlock: "26", sats: "9 134", price: "$10,000", rate: Double(0)), allWalletsBalance: WalletData(balance: 10000, latestTransactionTime: LatestTransaction(isUnconfirmed: false, epochValue: 1568804029000))))
|
||||
.previewContext(WidgetPreviewContext(family: .systemSmall))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>MarketWidget</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
<string>com.apple.widgetkit-extension</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
|
@ -1,30 +0,0 @@
|
|||
//
|
||||
// FiatUnit.swift
|
||||
// BlueWallet
|
||||
//
|
||||
// Created by Marcos Rodriguez on 11/20/20.
|
||||
// Copyright © 2020 BlueWallet. All rights reserved.
|
||||
//
|
||||
import Foundation
|
||||
|
||||
typealias FiatUnits = [FiatUnit]
|
||||
struct FiatUnit: Codable {
|
||||
let endPointKey: String
|
||||
let symbol: String
|
||||
let locale: String
|
||||
let source: String
|
||||
}
|
||||
|
||||
func fiatUnit(currency: String) -> FiatUnit? {
|
||||
guard let file = Bundle.main.path(forResource: "FiatUnits", ofType: "plist") else {
|
||||
return nil
|
||||
}
|
||||
let fileURL: URL = URL(fileURLWithPath: file)
|
||||
var fiatUnits: FiatUnits?
|
||||
|
||||
if let data = try? Data(contentsOf: fileURL) {
|
||||
let decoder = PropertyListDecoder()
|
||||
fiatUnits = try? decoder.decode(FiatUnits.self, from: data)
|
||||
}
|
||||
return fiatUnits?.first(where: {$0.endPointKey == currency})
|
||||
}
|
|
@ -1,416 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<array>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
<string>USD</string>
|
||||
<key>symbol</key>
|
||||
<string>$</string>
|
||||
<key>locale</key>
|
||||
<string>en-US</string>
|
||||
<key>source</key>
|
||||
<string>CoinDesk</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
<string>ANG</string>
|
||||
<key>symbol</key>
|
||||
<string>ƒ</string>
|
||||
<key>locale</key>
|
||||
<string>en-SX</string>
|
||||
<key>source</key>
|
||||
<string>CoinDesk</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
<string>ARS</string>
|
||||
<key>symbol</key>
|
||||
<string>$</string>
|
||||
<key>locale</key>
|
||||
<string>es-AR</string>
|
||||
<key>source</key>
|
||||
<string>Yadio</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
<string>AUD</string>
|
||||
<key>symbol</key>
|
||||
<string>$</string>
|
||||
<key>locale</key>
|
||||
<string>en-AU</string>
|
||||
<key>source</key>
|
||||
<string>CoinDesk</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
<string>AWG</string>
|
||||
<key>symbol</key>
|
||||
<string>ƒ</string>
|
||||
<key>locale</key>
|
||||
<string>nl-AW</string>
|
||||
<key>source</key>
|
||||
<string>CoinDesk</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
<string>BRL</string>
|
||||
<key>symbol</key>
|
||||
<string>R$</string>
|
||||
<key>locale</key>
|
||||
<string>pt-BR</string>
|
||||
<key>source</key>
|
||||
<string>CoinDesk</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
<string>CAD</string>
|
||||
<key>symbol</key>
|
||||
<string>$</string>
|
||||
<key>locale</key>
|
||||
<string>en-CA</string>
|
||||
<key>source</key>
|
||||
<string>CoinDesk</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
<string>CHF</string>
|
||||
<key>symbol</key>
|
||||
<string>CHF</string>
|
||||
<key>locale</key>
|
||||
<string>de-CH</string>
|
||||
<key>source</key>
|
||||
<string>CoinDesk</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
<string>CLP</string>
|
||||
<key>symbol</key>
|
||||
<string>$</string>
|
||||
<key>locale</key>
|
||||
<string>es-CL</string>
|
||||
<key>source</key>
|
||||
<string>CoinDesk</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
<string>COP</string>
|
||||
<key>symbol</key>
|
||||
<string>$</string>
|
||||
<key>locale</key>
|
||||
<string>es-CO</string>
|
||||
<key>source</key>
|
||||
<string>CoinDesk</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
<string>CZK</string>
|
||||
<key>symbol</key>
|
||||
<string>Kč</string>
|
||||
<key>locale</key>
|
||||
<string>cs-CZ</string>
|
||||
<key>source</key>
|
||||
<string>CoinDesk</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
<string>CNY</string>
|
||||
<key>symbol</key>
|
||||
<string>¥</string>
|
||||
<key>locale</key>
|
||||
<string>zh-CN</string>
|
||||
<key>source</key>
|
||||
<string>CoinDesk</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
<string>EUR</string>
|
||||
<key>symbol</key>
|
||||
<string>€</string>
|
||||
<key>locale</key>
|
||||
<string>en-IE</string>
|
||||
<key>source</key>
|
||||
<string>CoinDesk</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
<string>GBP</string>
|
||||
<key>symbol</key>
|
||||
<string>£</string>
|
||||
<key>locale</key>
|
||||
<string>en-GB</string>
|
||||
<key>source</key>
|
||||
<string>CoinDesk</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
<string>HRK</string>
|
||||
<key>symbol</key>
|
||||
<string>HRK</string>
|
||||
<key>locale</key>
|
||||
<string>hr-HR</string>
|
||||
<key>source</key>
|
||||
<string>CoinDesk</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
<string>HUF</string>
|
||||
<key>symbol</key>
|
||||
<string>Ft</string>
|
||||
<key>locale</key>
|
||||
<string>hu-HU</string>
|
||||
<key>source</key>
|
||||
<string>CoinDesk</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
<string>ILS</string>
|
||||
<key>symbol</key>
|
||||
<string>₪</string>
|
||||
<key>locale</key>
|
||||
<string>he-IL</string>
|
||||
<key>source</key>
|
||||
<string>CoinDesk</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
<string>INR</string>
|
||||
<key>symbol</key>
|
||||
<string>₹</string>
|
||||
<key>locale</key>
|
||||
<string>hi-HN</string>
|
||||
<key>source</key>
|
||||
<string>CoinDesk</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
<string>JPY</string>
|
||||
<key>symbol</key>
|
||||
<string>¥</string>
|
||||
<key>locale</key>
|
||||
<string>ja-JP</string>
|
||||
<key>source</key>
|
||||
<string>CoinDesk</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
<string>KES</string>
|
||||
<key>symbol</key>
|
||||
<string>Ksh</string>
|
||||
<key>locale</key>
|
||||
<string>en-KE</string>
|
||||
<key>source</key>
|
||||
<string>CoinDesk</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
<string>KRW</string>
|
||||
<key>symbol</key>
|
||||
<string>₩</string>
|
||||
<key>locale</key>
|
||||
<string>ko-KR</string>
|
||||
<key>source</key>
|
||||
<string>CoinDesk</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
<string>LBP</string>
|
||||
<key>symbol</key>
|
||||
<string>ل.ل.</string>
|
||||
<key>locale</key>
|
||||
<string>ar-LB</string>
|
||||
<key>source</key>
|
||||
<string>BitcoinduLiban</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
<string>MXN</string>
|
||||
<key>symbol</key>
|
||||
<string>$</string>
|
||||
<key>locale</key>
|
||||
<string>es-MX</string>
|
||||
<key>source</key>
|
||||
<string>CoinDesk</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
<string>MYR</string>
|
||||
<key>symbol</key>
|
||||
<string>RM</string>
|
||||
<key>locale</key>
|
||||
<string>ms-MY</string>
|
||||
<key>source</key>
|
||||
<string>CoinDesk</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
<string>NGN</string>
|
||||
<key>symbol</key>
|
||||
<string>₦</string>
|
||||
<key>locale</key>
|
||||
<string>en-NG</string>
|
||||
<key>source</key>
|
||||
<string>CoinDesk</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
<string>NOK</string>
|
||||
<key>symbol</key>
|
||||
<string>kr</string>
|
||||
<key>locale</key>
|
||||
<string>nb-NO</string>
|
||||
<key>source</key>
|
||||
<string>CoinDesk</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
<string>NZD</string>
|
||||
<key>symbol</key>
|
||||
<string>$</string>
|
||||
<key>locale</key>
|
||||
<string>en-NZ</string>
|
||||
<key>source</key>
|
||||
<string>CoinDesk</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
<string>PLN</string>
|
||||
<key>symbol</key>
|
||||
<string>zł</string>
|
||||
<key>locale</key>
|
||||
<string>pl-PL</string>
|
||||
<key>source</key>
|
||||
<string>CoinDesk</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
<string>PHP</string>
|
||||
<key>symbol</key>
|
||||
<string>₱</string>
|
||||
<key>locale</key>
|
||||
<string>en-PH</string>
|
||||
<key>source</key>
|
||||
<string>CoinDesk</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
<string>RUB</string>
|
||||
<key>symbol</key>
|
||||
<string>₽</string>
|
||||
<key>locale</key>
|
||||
<string>ru-RU</string>
|
||||
<key>source</key>
|
||||
<string>CoinDesk</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
<string>SGD</string>
|
||||
<key>symbol</key>
|
||||
<string>S$</string>
|
||||
<key>locale</key>
|
||||
<string>zh-SG</string>
|
||||
<key>source</key>
|
||||
<string>CoinDesk</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
<string>SEK</string>
|
||||
<key>symbol</key>
|
||||
<string>kr</string>
|
||||
<key>locale</key>
|
||||
<string>sv-SE</string>
|
||||
<key>source</key>
|
||||
<string>CoinDesk</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
<string>TRY</string>
|
||||
<key>symbol</key>
|
||||
<string>₺</string>
|
||||
<key>locale</key>
|
||||
<string>tr-TR</string>
|
||||
<key>source</key>
|
||||
<string>CoinDesk</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
<string>THB</string>
|
||||
<key>symbol</key>
|
||||
<string>฿</string>
|
||||
<key>locale</key>
|
||||
<string>th-TH</string>
|
||||
<key>source</key>
|
||||
<string>CoinDesk</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
<string>TWD</string>
|
||||
<key>symbol</key>
|
||||
<string>NT$</string>
|
||||
<key>locale</key>
|
||||
<string>zh-Hant-TW</string>
|
||||
<key>source</key>
|
||||
<string>CoinDesk</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
<string>TZS</string>
|
||||
<key>symbol</key>
|
||||
<string>TSh</string>
|
||||
<key>locale</key>
|
||||
<string>en-TZ</string>
|
||||
<key>source</key>
|
||||
<string>CoinDesk</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
<string>UAH</string>
|
||||
<key>symbol</key>
|
||||
<string>₴</string>
|
||||
<key>locale</key>
|
||||
<string>uk-UA</string>
|
||||
<key>source</key>
|
||||
<string>CoinDesk</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
<string>UYU</string>
|
||||
<key>symbol</key>
|
||||
<string>$</string>
|
||||
<key>locale</key>
|
||||
<string>es-UY</string>
|
||||
<key>source</key>
|
||||
<string>CoinDesk</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
<string>VEF</string>
|
||||
<key>symbol</key>
|
||||
<string>Bs.</string>
|
||||
<key>locale</key>
|
||||
<string>es-VE</string>
|
||||
<key>source</key>
|
||||
<string>CoinDesk</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
<string>VES</string>
|
||||
<key>symbol</key>
|
||||
<string>Bs.</string>
|
||||
<key>locale</key>
|
||||
<string>es-VE</string>
|
||||
<key>source</key>
|
||||
<string>Yadio</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>endPointKey</key>
|
||||
<string>ZAR</string>
|
||||
<key>symbol</key>
|
||||
<string>R</string>
|
||||
<key>locale</key>
|
||||
<string>en-ZA</string>
|
||||
<key>source</key>
|
||||
<string>CoinDesk</string>
|
||||
</dict>
|
||||
</array>
|
||||
</plist>
|
|
@ -1,29 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>WalletInformationAndMarketWidget</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
<string>com.apple.widgetkit-extension</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
|
@ -1,14 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.io.bluewallet.bluewallet</string>
|
||||
</array>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
|
@ -5,7 +5,7 @@
|
|||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>PriceWidget</string>
|
||||
<string>Widgets</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
|
@ -9,25 +9,25 @@
|
|||
import WidgetKit
|
||||
import SwiftUI
|
||||
|
||||
struct Provider: TimelineProvider {
|
||||
func placeholder(in context: Context) -> SimpleEntry {
|
||||
return SimpleEntry(date: Date(), marketData: MarketData(nextBlock: "26", sats: "9 134", price: "$10 000", rate: 10000))
|
||||
struct MarketWidgetProvider: TimelineProvider {
|
||||
func placeholder(in context: Context) -> MarketWidgetEntry {
|
||||
return MarketWidgetEntry(date: Date(), marketData: MarketData(nextBlock: "26", sats: "9 134", price: "$10 000", rate: 10000))
|
||||
}
|
||||
|
||||
func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) {
|
||||
let entry: SimpleEntry
|
||||
func getSnapshot(in context: Context, completion: @escaping (MarketWidgetEntry) -> ()) {
|
||||
let entry: MarketWidgetEntry
|
||||
if (context.isPreview) {
|
||||
entry = SimpleEntry(date: Date(), marketData: MarketData(nextBlock: "26", sats: "9 134", price: "$10 000", rate: 10000))
|
||||
entry = MarketWidgetEntry(date: Date(), marketData: MarketData(nextBlock: "26", sats: "9 134", price: "$10 000", rate: 10000))
|
||||
} else {
|
||||
entry = SimpleEntry(date: Date(), marketData: emptyMarketData)
|
||||
entry = MarketWidgetEntry(date: Date(), marketData: emptyMarketData)
|
||||
}
|
||||
completion(entry)
|
||||
}
|
||||
|
||||
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
|
||||
var entries: [SimpleEntry] = []
|
||||
var entries: [MarketWidgetEntry] = []
|
||||
if context.isPreview {
|
||||
let entry = SimpleEntry(date: Date(), marketData: MarketData(nextBlock: "26", sats: "9 134", price: "$10 000", rate: 10000))
|
||||
let entry = MarketWidgetEntry(date: Date(), marketData: MarketData(nextBlock: "26", sats: "9 134", price: "$10 000", rate: 10000))
|
||||
entries.append(entry)
|
||||
let timeline = Timeline(entries: entries, policy: .atEnd)
|
||||
completion(timeline)
|
||||
|
@ -35,12 +35,12 @@ struct Provider: TimelineProvider {
|
|||
let userPreferredCurrency = WidgetAPI.getUserPreferredCurrency();
|
||||
let marketDataEntry = MarketData(nextBlock: "...", sats: "...", price: "...", rate: 0)
|
||||
WidgetAPI.fetchMarketData(currency: userPreferredCurrency, completion: { (result, error) in
|
||||
let entry: SimpleEntry
|
||||
let entry: MarketWidgetEntry
|
||||
if let result = result {
|
||||
entry = SimpleEntry(date: Date(), marketData: result)
|
||||
entry = MarketWidgetEntry(date: Date(), marketData: result)
|
||||
|
||||
} else {
|
||||
entry = SimpleEntry(date: Date(), marketData: marketDataEntry)
|
||||
entry = MarketWidgetEntry(date: Date(), marketData: marketDataEntry)
|
||||
}
|
||||
entries.append(entry)
|
||||
let timeline = Timeline(entries: entries, policy: .atEnd)
|
||||
|
@ -50,13 +50,13 @@ struct Provider: TimelineProvider {
|
|||
}
|
||||
}
|
||||
|
||||
struct SimpleEntry: TimelineEntry {
|
||||
struct MarketWidgetEntry: TimelineEntry {
|
||||
let date: Date
|
||||
let marketData: MarketData
|
||||
}
|
||||
|
||||
struct MarketWidgetEntryView : View {
|
||||
var entry: Provider.Entry
|
||||
var entry: MarketWidgetProvider.Entry
|
||||
|
||||
var MarketStack: some View {
|
||||
MarketView(marketData: entry.marketData).padding(EdgeInsets(top: 18, leading: 11, bottom: 18, trailing: 11))
|
||||
|
@ -69,12 +69,11 @@ struct MarketWidgetEntryView : View {
|
|||
}
|
||||
}
|
||||
|
||||
@main
|
||||
struct MarketWidget: Widget {
|
||||
let kind: String = "MarketWidget"
|
||||
|
||||
var body: some WidgetConfiguration {
|
||||
StaticConfiguration(kind: kind, provider: Provider()) { entry in
|
||||
StaticConfiguration(kind: kind, provider: MarketWidgetProvider()) { entry in
|
||||
MarketWidgetEntryView(entry: entry)
|
||||
}
|
||||
.configurationDisplayName("Market")
|
||||
|
@ -84,7 +83,7 @@ struct MarketWidget: Widget {
|
|||
|
||||
struct MarketWidget_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
MarketWidgetEntryView(entry: SimpleEntry(date: Date(), marketData: MarketData(nextBlock: "26", sats: "9 134", price: "$10,000", rate: 0)))
|
||||
MarketWidgetEntryView(entry: MarketWidgetEntry(date: Date(), marketData: MarketData(nextBlock: "26", sats: "9 134", price: "$10,000", rate: 0)))
|
||||
.previewContext(WidgetPreviewContext(family: .systemSmall))
|
||||
}
|
||||
}
|
|
@ -10,26 +10,27 @@ import WidgetKit
|
|||
import SwiftUI
|
||||
|
||||
var marketData: [MarketDataTimeline: MarketData?] = [ .Current: nil, .Previous: nil]
|
||||
struct Provider: TimelineProvider {
|
||||
struct PriceWidgetProvider: TimelineProvider {
|
||||
typealias Entry = PriceWidgetEntry
|
||||
|
||||
func placeholder(in context: Context) -> SimpleEntry {
|
||||
return SimpleEntry(date: Date(), currentMarketData: MarketData(nextBlock: "", sats: "", price: "$10,000", rate: 10000, dateString: "2019-09-18T17:27:00+00:00"))
|
||||
func placeholder(in context: Context) -> PriceWidgetEntry {
|
||||
return PriceWidgetEntry(date: Date(), currentMarketData: MarketData(nextBlock: "", sats: "", price: "$10,000", rate: 10000, dateString: "2019-09-18T17:27:00+00:00"))
|
||||
}
|
||||
|
||||
func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) {
|
||||
let entry: SimpleEntry
|
||||
func getSnapshot(in context: Context, completion: @escaping (PriceWidgetEntry) -> ()) {
|
||||
let entry: PriceWidgetEntry
|
||||
if (context.isPreview) {
|
||||
entry = SimpleEntry(date: Date(), currentMarketData: MarketData(nextBlock: "", sats: "", price: "$10,000", rate: 10000, dateString: "2019-09-18T17:27:00+00:00"))
|
||||
entry = PriceWidgetEntry(date: Date(), currentMarketData: MarketData(nextBlock: "", sats: "", price: "$10,000", rate: 10000, dateString: "2019-09-18T17:27:00+00:00"))
|
||||
} else {
|
||||
entry = SimpleEntry(date: Date(), currentMarketData: emptyMarketData)
|
||||
entry = PriceWidgetEntry(date: Date(), currentMarketData: emptyMarketData)
|
||||
}
|
||||
completion(entry)
|
||||
}
|
||||
|
||||
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
|
||||
var entries: [SimpleEntry] = []
|
||||
var entries: [PriceWidgetEntry] = []
|
||||
if (context.isPreview) {
|
||||
let entry = SimpleEntry(date: Date(), currentMarketData: MarketData(nextBlock: "", sats: "", price: "$10,000", rate: 10000, dateString: "2019-09-18T17:27:00+00:00"))
|
||||
let entry = PriceWidgetEntry(date: Date(), currentMarketData: MarketData(nextBlock: "", sats: "", price: "$10,000", rate: 10000, dateString: "2019-09-18T17:27:00+00:00"))
|
||||
entries.append(entry)
|
||||
let timeline = Timeline(entries: entries, policy: .atEnd)
|
||||
completion(timeline)
|
||||
|
@ -48,9 +49,9 @@ struct Provider: TimelineProvider {
|
|||
marketData[.Previous] = marketData[.Current]
|
||||
marketData[.Current] = currentMarketData
|
||||
entryMarketData = currentMarketData
|
||||
entries.append(SimpleEntry(date:Date(), currentMarketData: entryMarketData))
|
||||
entries.append(PriceWidgetEntry(date:Date(), currentMarketData: entryMarketData))
|
||||
} else {
|
||||
entries.append(SimpleEntry(date:Date(), currentMarketData: currentMarketData))
|
||||
entries.append(PriceWidgetEntry(date:Date(), currentMarketData: currentMarketData))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,7 +62,7 @@ struct Provider: TimelineProvider {
|
|||
}
|
||||
}
|
||||
|
||||
struct SimpleEntry: TimelineEntry {
|
||||
struct PriceWidgetEntry: TimelineEntry {
|
||||
let date: Date
|
||||
let currentMarketData: MarketData?
|
||||
var previousMarketData: MarketData? {
|
||||
|
@ -70,7 +71,7 @@ struct SimpleEntry: TimelineEntry {
|
|||
}
|
||||
|
||||
struct PriceWidgetEntryView : View {
|
||||
var entry: Provider.Entry
|
||||
let entry: PriceWidgetEntry
|
||||
var priceView: some View {
|
||||
PriceView(currentMarketData: entry.currentMarketData, previousMarketData: marketData[.Previous] ?? emptyMarketData).padding()
|
||||
}
|
||||
|
@ -82,12 +83,11 @@ struct PriceWidgetEntryView : View {
|
|||
}
|
||||
}
|
||||
|
||||
@main
|
||||
struct PriceWidget: Widget {
|
||||
let kind: String = "PriceWidget"
|
||||
|
||||
var body: some WidgetConfiguration {
|
||||
StaticConfiguration(kind: kind, provider: Provider()) { entry in
|
||||
StaticConfiguration(kind: kind, provider: PriceWidgetProvider()) { entry in
|
||||
PriceWidgetEntryView(entry: entry)
|
||||
}
|
||||
.configurationDisplayName("Price")
|
||||
|
@ -97,7 +97,7 @@ struct PriceWidget: Widget {
|
|||
|
||||
struct PriceWidget_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
PriceWidgetEntryView(entry: SimpleEntry(date: Date(), currentMarketData: MarketData(nextBlock: "", sats: "", price: "$10,000", rate: 10000, dateString: "2019-09-18T17:27:00+00:00")))
|
||||
PriceWidgetEntryView(entry: PriceWidgetEntry(date: Date(), currentMarketData: MarketData(nextBlock: "", sats: "", price: "$10,000", rate: 10000, dateString: "2019-09-18T17:27:00+00:00")))
|
||||
.previewContext(WidgetPreviewContext(family: .systemSmall))
|
||||
}
|
||||
}
|
51
ios/Widgets/Shared/Fiat/FiatUnit.swift
Normal file
51
ios/Widgets/Shared/Fiat/FiatUnit.swift
Normal file
|
@ -0,0 +1,51 @@
|
|||
//
|
||||
// FiatUnit.swift
|
||||
// BlueWallet
|
||||
//
|
||||
// Created by Marcos Rodriguez on 11/20/20.
|
||||
// Copyright © 2020 BlueWallet. All rights reserved.
|
||||
//
|
||||
import Foundation
|
||||
|
||||
struct FiatUnit: Codable {
|
||||
let endPointKey: String
|
||||
let symbol: String
|
||||
let locale: String
|
||||
let source: String
|
||||
|
||||
}
|
||||
|
||||
func fiatUnit(currency: String) -> FiatUnit? {
|
||||
return Bundle.main.decode([String: FiatUnit].self, from: "fiatUnit.json").first(where: {$1.endPointKey == currency})?.value
|
||||
}
|
||||
|
||||
extension Bundle {
|
||||
func decode<T: Decodable>(_ type: T.Type, from file: String, dateDecodingStrategy: JSONDecoder.DateDecodingStrategy = .deferredToDate, keyDecodingStrategy: JSONDecoder.KeyDecodingStrategy = .useDefaultKeys) -> T {
|
||||
guard let url = self.url(forResource: file, withExtension: nil) else {
|
||||
fatalError("Failed to locate \(file) in bundle.")
|
||||
}
|
||||
|
||||
guard let data = try? Data(contentsOf: url) else {
|
||||
fatalError("Failed to load \(file) from bundle.")
|
||||
}
|
||||
|
||||
let decoder = JSONDecoder()
|
||||
|
||||
decoder.dateDecodingStrategy = dateDecodingStrategy
|
||||
decoder.keyDecodingStrategy = keyDecodingStrategy
|
||||
|
||||
do {
|
||||
return try decoder.decode(T.self, from: data)
|
||||
} catch DecodingError.keyNotFound(let key, let context) {
|
||||
fatalError("Failed to decode \(file) from bundle due to missing key '\(key.stringValue)' not found – \(context.debugDescription)")
|
||||
} catch DecodingError.typeMismatch(_, let context) {
|
||||
fatalError("Failed to decode \(file) from bundle due to type mismatch – \(context.debugDescription)")
|
||||
} catch DecodingError.valueNotFound(let type, let context) {
|
||||
fatalError("Failed to decode \(file) from bundle due to missing \(type) value – \(context.debugDescription)")
|
||||
} catch DecodingError.dataCorrupted(_) {
|
||||
fatalError("Failed to decode \(file) from bundle because it appears to be invalid JSON")
|
||||
} catch {
|
||||
fatalError("Failed to decode \(file) from bundle: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -31,6 +31,8 @@ class WidgetAPI {
|
|||
urlString = "https://api.yadio.io/json/\(endPointKey)"
|
||||
case "BitcoinduLiban":
|
||||
urlString = "https://bitcoinduliban.org/api.php?key=lbpusd"
|
||||
case "Exir":
|
||||
urlString = "https://api.exir.io/v1/ticker?symbol=btc-irt"
|
||||
default:
|
||||
urlString = "https://api.coindesk.com/v1/bpi/currentprice/\(endPointKey).json"
|
||||
}
|
||||
|
@ -59,7 +61,12 @@ class WidgetAPI {
|
|||
latestRateDataStore = WidgetDataStore(rate: String(rateDouble), lastUpdate: lastUpdatedString, rateDouble: rateDouble)
|
||||
case "BitcoinduLiban":
|
||||
guard let rateString = json["BTC/LBP"] as? String else { break }
|
||||
guard let rateDouble = Double(rateString) else {return}
|
||||
guard let rateDouble = Double(rateString) else { break }
|
||||
let lastUpdatedString = ISO8601DateFormatter().string(from: Date())
|
||||
latestRateDataStore = WidgetDataStore(rate: rateString, lastUpdate: lastUpdatedString, rateDouble: rateDouble)
|
||||
case "Exir":
|
||||
guard let rateDouble = json["last"] as? Double else { break }
|
||||
let rateString = String(rateDouble)
|
||||
let lastUpdatedString = ISO8601DateFormatter().string(from: Date())
|
||||
latestRateDataStore = WidgetDataStore(rate: rateString, lastUpdate: lastUpdatedString, rateDouble: rateDouble)
|
||||
default:
|
|
@ -8,6 +8,23 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
extension Numeric {
|
||||
|
||||
var abbreviated: String {
|
||||
let bytecountFormatter = ByteCountFormatter()
|
||||
bytecountFormatter.zeroPadsFractionDigits = true
|
||||
bytecountFormatter.countStyle = .decimal
|
||||
bytecountFormatter.isAdaptive = false
|
||||
let bytesString = bytecountFormatter.string(fromByteCount: (self as! NSNumber).int64Value)
|
||||
|
||||
let numericString = bytesString
|
||||
.replacingOccurrences(of: "bytes", with: "")
|
||||
.replacingOccurrences(of: "B", with: "") // removes B (bytes) in 'KB'/'MB'/'GB'
|
||||
.replacingOccurrences(of: "G", with: "B") // replace G (Giga) to just B (billions)
|
||||
return numericString.replacingOccurrences(of: " ", with: "")
|
||||
}
|
||||
}
|
||||
|
||||
struct WidgetDataStore: Codable {
|
||||
let rate: String
|
||||
let lastUpdate: String
|
||||
|
@ -23,19 +40,21 @@ struct WidgetDataStore: Codable {
|
|||
}
|
||||
return rate
|
||||
}
|
||||
var formattedRateForSmallComplication: String? {
|
||||
return rateDouble.abbreviated
|
||||
}
|
||||
|
||||
var formattedRateForComplication: String? {
|
||||
let numberFormatter = NumberFormatter()
|
||||
numberFormatter.locale = Locale(identifier: WidgetAPI.getUserPreferredCurrencyLocale())
|
||||
numberFormatter.numberStyle = .currency
|
||||
numberFormatter.usesSignificantDigits = true
|
||||
numberFormatter.maximumFractionDigits = 0
|
||||
numberFormatter.minimumFractionDigits = 0
|
||||
numberFormatter.currencySymbol = ""
|
||||
if let rateString = numberFormatter.string(from: NSNumber(value: rateDouble)) {
|
||||
return rateString
|
||||
}
|
||||
return rate
|
||||
}
|
||||
|
||||
var date: Date? {
|
||||
let isoDateFormatter = ISO8601DateFormatter()
|
||||
let dateFormatter = DateFormatter()
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue