diff --git a/.env.development.template b/.env.development.template index 8a43863f9..24c3391ec 100644 --- a/.env.development.template +++ b/.env.development.template @@ -23,10 +23,15 @@ ENABLE_LDK_LOGS=true BACKUPS_SHARED_SECRET=6dabb95493023d5c45229331490a9a67fcde2a618798f9dea5c1247eabb13451 BACKUPS_SERVER_SLASHTAG=slash:3phbmj4jkzs7b6e6t1h8jwy1u6o9w9y39nscsc6r1q89t1mxcsuy +# LDK backups +BACKUPS_SERVER_HOST=https://blocktank.synonym.to/staging-backups-ldk +BACKUPS_SERVER_PUBKEY=02c03b8b8c1b5500b622646867d99bf91676fac0f38e2182c91a9ff0d053a21d6d + # Slashtags DISABLE_SLASHTAGS=false SLASHTAGS_SEEDER_BASE_URL=https://blocktank.synonym.to SLASHTAGS_SEEDER_TOPIC=3b9f8ccd062ca9fc0b7dd407b4cd287ca6e2d8b32f046d7958fa7bea4d78fd75 +WEB_RELAY=https://webrelay.slashtags.to # Blocktank BLOCKTANK_HOST=https://api.stag.blocktank.to @@ -47,6 +52,11 @@ ELECTRUM_SIGNET_SSL_PORT=18484 ELECTRUM_SIGNET_TCP_PORT=18483 ELECTRUM_SIGNET_PROTO=tcp +TREASURE_HUNT_HOST= + +# format: ", , ..." +TRUSTED_ZERO_CONF_PEERS="0296b2db342fcf87ea94d981757fdf4d3e545bd5cef4919f58b5d38dfdd73bf5c9,03b9a456fb45d5ac98c02040d39aec77fa3eeb41fd22cf40b862b393bcfc43473a" + WALLET_DEFAULT_SELECTED_NETWORK=bitcoin E2E=false diff --git a/.env.test.template b/.env.test.template index 6973e8c0e..29ed60579 100644 --- a/.env.test.template +++ b/.env.test.template @@ -23,10 +23,15 @@ ENABLE_LDK_LOGS=false BACKUPS_SHARED_SECRET=6dabb95493023d5c45229331490a9a67fcde2a618798f9dea5c1247eabb13451 BACKUPS_SERVER_SLASHTAG=slash:3phbmj4jkzs7b6e6t1h8jwy1u6o9w9y39nscsc6r1q89t1mxcsuy +# LDK backups +BACKUPS_SERVER_HOST=https://blocktank.synonym.to/staging-backups-ldk +BACKUPS_SERVER_PUBKEY=02c03b8b8c1b5500b622646867d99bf91676fac0f38e2182c91a9ff0d053a21d6d + # Slashtags DISABLE_SLASHTAGS=false SLASHTAGS_SEEDER_BASE_URL=https://blocktank.synonym.to SLASHTAGS_SEEDER_TOPIC=3b9f8ccd062ca9fc0b7dd407b4cd287ca6e2d8b32f046d7958fa7bea4d78fd75 +WEB_RELAY=https://webrelay.slashtags.to # Blocktank BLOCKTANK_HOST=https://api.stag.blocktank.to @@ -47,6 +52,11 @@ ELECTRUM_SIGNET_SSL_PORT=18484 ELECTRUM_SIGNET_TCP_PORT=18483 ELECTRUM_SIGNET_PROTO=tcp +TREASURE_HUNT_HOST= + +# format: ", , ..." +TRUSTED_ZERO_CONF_PEERS="03b9a456fb45d5ac98c02040d39aec77fa3eeb41fd22cf40b862b393bcfc43473a" + WALLET_DEFAULT_SELECTED_NETWORK=bitcoinRegtest E2E=true diff --git a/.github/workflows/e2e-ios.yml b/.github/workflows/e2e-ios.yml index 31faaa782..e600ba6cc 100644 --- a/.github/workflows/e2e-ios.yml +++ b/.github/workflows/e2e-ios.yml @@ -5,6 +5,7 @@ on: pull_request env: NO_FLIPPER: 1 E2E_TESTS: 1 # build without transform-remove-console babel plugin + DEBUG: 'lnurl* lnurl server' jobs: e2e: @@ -52,7 +53,7 @@ jobs: - name: Setup Node uses: actions/setup-node@v3 with: - node-version: 18 + node-version: 18.17 cache: 'yarn' # cache packages, but not node_modules - name: Activate enviroment variables @@ -83,7 +84,7 @@ jobs: run: pod install --project-directory=ios || pod install --project-directory=ios - name: Build - run: yarn e2e:build:ios-release + run: yarn e2e:build:ios-release || yarn e2e:build:ios-release - name: Test attempt 1 continue-on-error: true diff --git a/.github/workflows/jest.yml b/.github/workflows/jest.yml index d7b23094f..9dcee609f 100644 --- a/.github/workflows/jest.yml +++ b/.github/workflows/jest.yml @@ -27,11 +27,11 @@ jobs: - name: Set up Node.js uses: actions/setup-node@v3 with: - node-version: 18 + node-version: 18.17 cache: 'yarn' - name: Install Node.js dependencies - run: yarn install + run: yarn install || yarn install - name: Activate enviroment variables run: cp .env.development.template .env diff --git a/.github/workflows/lint-check.yml b/.github/workflows/lint-check.yml index a52086919..0b9e29994 100644 --- a/.github/workflows/lint-check.yml +++ b/.github/workflows/lint-check.yml @@ -16,7 +16,7 @@ jobs: - name: Set up Node.js uses: actions/setup-node@v3 with: - node-version: 18 + node-version: 18.17 cache: 'yarn' - name: Install Node.js dependencies diff --git a/.github/workflows/react-native-skia-stub.patch b/.github/workflows/react-native-skia-stub.patch index b8bd8088c..f70220b71 100644 --- a/.github/workflows/react-native-skia-stub.patch +++ b/.github/workflows/react-native-skia-stub.patch @@ -5,7 +5,6 @@ index 935e19e8..fa64a969 100644 @@ -178,7 +178,8 @@ "stream": "stream-browserify", "vm": "vm-browserify", - "sodium-native": "react-native-libsodium", - "net": "react-native-tcp-socket" + "net": "react-native-tcp-socket", + "@shopify/react-native-skia": "react-native-skia-stub" diff --git a/.github/workflows/type-check.yml b/.github/workflows/type-check.yml index 02426e440..25615f770 100644 --- a/.github/workflows/type-check.yml +++ b/.github/workflows/type-check.yml @@ -20,7 +20,7 @@ jobs: cache: 'yarn' - name: Install Node.js dependencies - run: yarn install + run: yarn install || yarn install - name: Type check run: yarn tsc:check diff --git a/__mocks__/@react-native-async-storage/async-storage.js b/__mocks__/@react-native-async-storage/async-storage.js new file mode 100644 index 000000000..69d0e5302 --- /dev/null +++ b/__mocks__/@react-native-async-storage/async-storage.js @@ -0,0 +1 @@ +export * from '@react-native-async-storage/async-storage/jest/async-storage-mock'; diff --git a/__tests__/backups.ts b/__tests__/backups.ts index 41c72891a..f1d142d6e 100644 --- a/__tests__/backups.ts +++ b/__tests__/backups.ts @@ -14,7 +14,7 @@ import { addTag, addMetaTxTag, resetMetaStore, - updateMetaIncTxTags, + updatePendingInvoice, addMetaSlashTagsUrlTag, } from '../src/store/actions/metadata'; import { @@ -100,17 +100,19 @@ describe('Remote backups', () => { if (fetchRes.isErr()) { throw fetchRes.error; } - const bytesToStringRes = bytesToString(fetchRes.value.content); - if (bytesToStringRes.isErr()) { - throw bytesToStringRes.error; - } - expect(bytesToStringRes.value).toEqual(message); + const jsonString = bytesToString(fetchRes.value.content); + expect(jsonString).toEqual(message); }); it('Backups and restores metadata', async () => { addMetaTxTag('txid1', 'tag'); addTag('tag'); - updateMetaIncTxTags('address', 'invoice', ['futuretag']); + updatePendingInvoice({ + id: 'id123', + tags: ['futuretag'], + address: 'address', + payReq: 'lightningInvoice', + }); addMetaSlashTagsUrlTag('txid2', 'slashtag'); const backup = getMetaDataStore(); @@ -182,15 +184,16 @@ describe('Remote backups', () => { }); it('Backups and restores widgets', async () => { - setFeedWidget('url', { - name: 'name', + setFeedWidget({ + url: 'url', type: 'type', - icon: 'icon', - field: { - name: 'name', - main: 'main', - files: {}, - }, + fields: [ + { + name: 'name', + main: 'main', + files: {}, + }, + ], }); updateWidgets({ onboardedWidgets: true }); diff --git a/__tests__/scanner.ts b/__tests__/scanner.ts index 86882f883..736894168 100644 --- a/__tests__/scanner.ts +++ b/__tests__/scanner.ts @@ -1,4 +1,5 @@ -import { decodeQRData } from '../src/utils/scanner'; +import { findlnurl } from '../src/utils/lnurl'; +import { TBitcoinUrl, decodeQRData } from '../src/utils/scanner'; describe('QR codes', () => { it('decodes a bitcoin URI with params', async () => { @@ -8,7 +9,7 @@ describe('QR codes', () => { if (res.isErr()) { throw res.error; } - const qrData = res.value[0]; + const qrData = res.value[0] as TBitcoinUrl; expect(qrData.network).toEqual('bitcoin'); expect(qrData.qrDataType).toEqual('bitcoinAddress'); expect(qrData.sats).toEqual(50000); @@ -22,7 +23,7 @@ describe('QR codes', () => { if (res.isErr()) { throw res.error; } - const qrData = res.value[0]; + const qrData = res.value[0] as TBitcoinUrl; expect(qrData.network).toEqual('bitcoin'); expect(qrData.qrDataType).toEqual('bitcoinAddress'); }); @@ -34,7 +35,7 @@ describe('QR codes', () => { if (res.isErr()) { throw res.error; } - const qrData = res.value[0]; + const qrData = res.value[0] as TBitcoinUrl; expect(qrData.network).toEqual('bitcoin'); expect(qrData.qrDataType).toEqual('bitcoinAddress'); }); @@ -46,7 +47,7 @@ describe('QR codes', () => { if (res.isErr()) { throw res.error; } - const qrData = res.value[0]; + const qrData = res.value[0] as TBitcoinUrl; expect(qrData.network).toEqual('bitcoin'); expect(qrData.qrDataType).toEqual('bitcoinAddress'); }); @@ -58,8 +59,29 @@ describe('QR codes', () => { if (res.isErr()) { throw res.error; } - const qrData = res.value[0]; + const qrData = res.value[0] as TBitcoinUrl; expect(qrData.network).toEqual('bitcoin'); expect(qrData.qrDataType).toEqual('bitcoinAddress'); }); + + it('finds lnurl', async () => { + const base = + 'lnurl1dp68gurn8ghj7mrww3uxymm59e3xjemnw4hzu7re0ghkcmn4wfkz7urp0ylh2um9wf5kg0fhxycnv9g9w58'; + expect(findlnurl(base)).toEqual(base); + expect(findlnurl(base.toUpperCase())).toEqual(base); + expect(findlnurl('https://site.com/?lightning=' + base)).toEqual(base); + expect( + findlnurl('https://site.com/?lightning=' + base.toUpperCase()), + ).toEqual(base); + expect(findlnurl('https://site.com/?nada=nada&lightning=' + base)).toEqual( + base, + ); + expect( + findlnurl('https://site.com/?nada=nada&lightning=' + base.toUpperCase()), + ).toEqual(base); + expect(findlnurl('bs')).toEqual(null); + expect(findlnurl('https://site.com')).toEqual(null); + expect(findlnurl('https://site.com/?bs=' + base)).toEqual(null); + expect(findlnurl('bitcoin:site.com/?lightning=' + base)).toEqual(base); + }); }); diff --git a/__tests__/slashtags.ts b/__tests__/slashtags.ts new file mode 100644 index 000000000..5a4d79fbc --- /dev/null +++ b/__tests__/slashtags.ts @@ -0,0 +1,15 @@ +import { getNewProfileUrl } from '../src/utils/slashtags2'; + +describe('Slashtags', () => { + it('profile url convert function woks', () => { + expect( + getNewProfileUrl( + 'slash:c7xk1b11o8k8jw6cn9a8asjcau77aenf7iq79tbc9u933wyoyjxy', + 'https://dht-relay.synonym.to/staging/web-relay', + ), + ).toEqual( + 'slash:c7xk1b11o8k8jw6cn9a8asjcau77aenf7iq79tbc9u933wyoyjxy?relay=https://dht-relay.synonym.to/staging/web-relay', + ); + expect(() => getNewProfileUrl('xxx', 'yyy')).toThrow(Error); + }); +}); diff --git a/__tests__/todos.ts b/__tests__/todos.ts new file mode 100644 index 000000000..e7ac0fcc4 --- /dev/null +++ b/__tests__/todos.ts @@ -0,0 +1,200 @@ +import assert from 'node:assert'; +import cloneDeep from 'lodash/cloneDeep'; + +import '../src/utils/i18n'; +import { todosFullSelector } from '../src/store/reselect/todos'; +import store from '../src/store'; +import Store from '../src/store/types'; +import { createNewWallet } from '../src/utils/startup'; +import { updateWallet } from '../src/store/actions/wallet'; +import { + backupSeedPhraseTodo, + buyBitcoinTodo, + lightningConnectingTodo, + lightningReadyTodo, + lightningSettingUpTodo, + lightningTodo, + pinTodo, + slashtagsProfileTodo, + transferClosingChannel, + transferToSavingsTodo, + transferToSpendingTodo, +} from '../src/store/shapes/todos'; +import { IBtOrder } from '@synonymdev/blocktank-lsp-http-client'; +import { TChannel } from '@synonymdev/react-native-ldk'; + +describe('Todos selector', () => { + let s: Store; + + beforeAll(async () => { + require('../nodejs-assets/nodejs-project/main.js'); + let res = await createNewWallet(); + if (res.isErr()) { + throw res.error; + } + updateWallet({ selectedNetwork: 'bitcoinRegtest' }); + s = store.getState(); + }); + + it('should return default set of todos', () => { + assert.deepEqual(todosFullSelector(s), [ + backupSeedPhraseTodo, + lightningTodo, + pinTodo, + slashtagsProfileTodo, + buyBitcoinTodo, + ]); + }); + + it('should not return pinTodo if PIN is set', () => { + const state = cloneDeep(s); + state.settings.pin = true; + + expect(todosFullSelector(state)).not.toEqual( + expect.arrayContaining([pinTodo]), + ); + }); + + it('should not return backupSeedPhraseTodo if backup is verified', () => { + const state = cloneDeep(s); + state.user.backupVerified = true; + + expect(todosFullSelector(state)).not.toEqual( + expect.arrayContaining([backupSeedPhraseTodo]), + ); + }); + + it('should not return slashtagsProfileTodo if profile is set', () => { + const state = cloneDeep(s); + state.slashtags.onboardingProfileStep = 'Done'; + + expect(todosFullSelector(state)).not.toEqual( + expect.arrayContaining([slashtagsProfileTodo]), + ); + }); + + it('should not return hidden todos', () => { + const state = cloneDeep(s); + state.todos.hide = { + backupSeedPhrase: +new Date(), + pin: +new Date(), + slashtagsProfile: +new Date(), + buyBitcoin: +new Date(), + lightning: +new Date(), + }; + + assert.deepEqual(todosFullSelector(state), []); + }); + + it('should return lightningSettingUpTodo if there is a pending BT order', () => { + const state = cloneDeep(s); + state.blocktank.orders.push({ + id: 'order1', + state: 'created', + } as IBtOrder); + state.blocktank.paidOrders = { order1: 'txid' }; + + expect(todosFullSelector(state)).toEqual( + expect.arrayContaining([lightningSettingUpTodo]), + ); + }); + + it('should return lightningConnectingTodo if there is a pending channel', () => { + const state = cloneDeep(s); + + const channel1 = { + channel_id: 'channel1', + is_channel_ready: false, + } as TChannel; + state.lightning.nodes.wallet0.channels.bitcoinRegtest = { channel1 }; + state.lightning.nodes.wallet0.openChannelIds.bitcoinRegtest = ['channel1']; + + expect(todosFullSelector(state)).toEqual( + expect.arrayContaining([lightningConnectingTodo]), + ); + }); + + it('should return transferClosingChannel if there are gracefully closing channels', () => { + const state = cloneDeep(s); + + const channel1 = { + channel_id: 'channel1', + is_channel_ready: true, + } as TChannel; + state.lightning.nodes.wallet0.channels.bitcoinRegtest = { channel1 }; + state.lightning.nodes.wallet0.openChannelIds.bitcoinRegtest = ['channel1']; + state.user.startCoopCloseTimestamp = 123; + + expect(todosFullSelector(state)).toEqual( + expect.arrayContaining([transferClosingChannel]), + ); + }); + + it('should return transferToSpendingTodo if there are new pending BT orders', () => { + const state = cloneDeep(s); + + const channel1 = { + channel_id: 'channel1', + is_channel_ready: true, + } as TChannel; + state.lightning.nodes.wallet0.channels.bitcoinRegtest = { channel1 }; + state.lightning.nodes.wallet0.openChannelIds.bitcoinRegtest = ['channel1']; + state.blocktank.orders.push({ id: 'order1', state: 'created' } as IBtOrder); + state.blocktank.paidOrders = { order1: 'txid' }; + + expect(todosFullSelector(state)).toEqual( + expect.arrayContaining([transferToSpendingTodo]), + ); + }); + + it('should return transferToSavingsTodo if there is a new claimable balance', () => { + const state = cloneDeep(s); + + const channel1 = { + channel_id: 'channel1', + is_channel_ready: true, + } as TChannel; + state.lightning.nodes.wallet0.channels.bitcoinRegtest = { channel1 }; + state.lightning.nodes.wallet0.openChannelIds.bitcoinRegtest = ['channel1']; + state.lightning.nodes.wallet0.claimableBalance.bitcoinRegtest = 123; + + expect(todosFullSelector(state)).toEqual( + expect.arrayContaining([transferToSavingsTodo]), + ); + }); + + it('should return lightningReadyTodo if there is a new open channel', () => { + const state = cloneDeep(s); + + const channel1 = { + channel_id: 'channel1', + is_channel_ready: true, + confirmations: 1, + confirmations_required: 1, + } as TChannel; + state.lightning.nodes.wallet0.channels.bitcoinRegtest = { channel1 }; + state.lightning.nodes.wallet0.openChannelIds.bitcoinRegtest = ['channel1']; + + expect(todosFullSelector(state)).toEqual( + expect.arrayContaining([lightningReadyTodo]), + ); + }); + + it('should return not lightningReadyTodo if notification has already been shown', () => { + const state = cloneDeep(s); + + const channel1 = { + channel_id: 'channel1', + is_channel_ready: true, + confirmations: 1, + confirmations_required: 1, + } as TChannel; + state.lightning.nodes.wallet0.channels.bitcoinRegtest = { channel1 }; + state.lightning.nodes.wallet0.openChannelIds.bitcoinRegtest = ['channel1']; + state.todos.newChannelsNotifications = { channel1: +new Date() }; + + expect(todosFullSelector(state)).not.toEqual( + expect.arrayContaining([lightningReadyTodo]), + ); + }); +}); diff --git a/__tests__/wallet-restore.ts b/__tests__/wallet-restore.ts index 09bb7c63b..483bd4523 100644 --- a/__tests__/wallet-restore.ts +++ b/__tests__/wallet-restore.ts @@ -15,6 +15,8 @@ import initWaitForElectrumToSync from './utils/wait-for-electrum'; jest.setTimeout(60_000); const bitcoinURL = 'http://polaruser:polarpass@127.0.0.1:43782'; +const electrumHost = '127.0.0.1'; +const electrumPort = 60001; describe('Wallet - wallet restore and receive', () => { let waitForElectrum; @@ -33,7 +35,7 @@ describe('Wallet - wallet restore and receive', () => { } waitForElectrum = await initWaitForElectrumToSync( - { port: 60001, host: '127.0.0.1' }, + { host: electrumHost, port: electrumPort }, bitcoinURL, ); }); diff --git a/__tests__/wallet-send.ts b/__tests__/wallet-send.ts index 08feee0d1..eae065ae2 100644 --- a/__tests__/wallet-send.ts +++ b/__tests__/wallet-send.ts @@ -27,6 +27,8 @@ import { runStorageCheck } from '../src/utils/wallet/checks'; jest.setTimeout(60_000); const bitcoinURL = 'http://polaruser:polarpass@127.0.0.1:43782'; +const electrumHost = '127.0.0.1'; +const electrumPort = 60001; describe('Wallet - new wallet, send and receive', () => { let waitForElectrum; @@ -45,7 +47,7 @@ describe('Wallet - new wallet, send and receive', () => { } waitForElectrum = await initWaitForElectrumToSync( - { port: 60001, host: '127.0.0.1' }, + { host: electrumHost, port: electrumPort }, bitcoinURL, ); }); @@ -70,7 +72,7 @@ describe('Wallet - new wallet, send and receive', () => { expect(res.value).toEqual('Wallet created'); // switch to regtest - await updateWallet({ selectedNetwork: 'bitcoinRegtest' }); + updateWallet({ selectedNetwork: 'bitcoinRegtest' }); expect(store.getState().wallet.selectedNetwork).toEqual('bitcoinRegtest'); res = await addElectrumPeer({ @@ -173,7 +175,7 @@ describe('Wallet - new wallet, send and receive', () => { }); // @ts-ignore expect(updateFeeRes.error.message).toBe( - 'Unable to increase the fee any further. Otherwise, it will exceed half the current balance.', + 'Unable to increase the fee. It would exceed half of your current balance.', ); // set fee to 3 vsat/byte diff --git a/android/app/build.gradle b/android/app/build.gradle index 5c714af83..71cd22160 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -78,7 +78,7 @@ android { applicationId "to.synonym.bitkit.wallet" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 60 + versionCode 85 versionName "1.0" multiDexEnabled true missingDimensionStrategy 'react-native-camera', 'general' @@ -124,7 +124,9 @@ dependencies { exclude group:'com.squareup.okhttp3', module:'okhttp' } - debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") + // 0.182.0 is the latest one on https://mvnrepository.com/artifact/com.facebook.flipper/flipper-fresco-plugin + // debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") + debugImplementation("com.facebook.flipper:flipper-fresco-plugin:0.182.0") if (hermesEnabled.toBoolean()) { implementation("com.facebook.react:hermes-android") } else { diff --git a/android/app/src/debug/java/com/bitkit/ReactNativeFlipper.java b/android/app/src/debug/java/com/bitkit/ReactNativeFlipper.java index 96fedeb2a..3f6dc6458 100644 --- a/android/app/src/debug/java/com/bitkit/ReactNativeFlipper.java +++ b/android/app/src/debug/java/com/bitkit/ReactNativeFlipper.java @@ -38,15 +38,16 @@ public static void initializeFlipper(Context context, ReactInstanceManager react client.addPlugin(new SharedPreferencesFlipperPlugin(context)); client.addPlugin(CrashReporterPlugin.getInstance()); - NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin(); - NetworkingModule.setCustomClientBuilder( - new NetworkingModule.CustomClientBuilder() { - @Override - public void apply(OkHttpClient.Builder builder) { - builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin)); - } - }); - client.addPlugin(networkFlipperPlugin); + // NOTE: disable NetworkFlipperPlugin for react-native-fetch-api to work in debug build + // NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin(); + // NetworkingModule.setCustomClientBuilder( + // new NetworkingModule.CustomClientBuilder() { + // @Override + // public void apply(OkHttpClient.Builder builder) { + // builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin)); + // } + // }); + // client.addPlugin(networkFlipperPlugin); client.start(); // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 460449211..f910a5e28 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,65 +1,89 @@ + xmlns:tools="http://schemas.android.com/tools"> - - - - - + + + + + - - - - + + + + - - - - + - - - - - - - - - - - - - - - - - - - - - - - + android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode" + android:launchMode="singleTask" + android:screenOrientation="portrait" + android:windowSoftInputMode="adjustPan" + android:exported="true"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - + + + + + + + + diff --git a/android/app/src/main/res/drawable/ic_launcher_background.xml b/android/app/src/main/res/drawable/ic_launcher_background.xml deleted file mode 100644 index ca3826a46..000000000 --- a/android/app/src/main/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/android/app/src/main/res/drawable/ic_launcher_foreground.xml b/android/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 000000000..b0604dcd4 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,18 @@ + + + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 000000000..74d50b2a5 --- /dev/null +++ b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 000000000..74d50b2a5 --- /dev/null +++ b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png index 9bbaa5b30..5a5fa9a21 100644 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png index b59c85a99..f43a86de4 100644 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png index 02bcb85b0..4d9b3cde1 100644 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png index 1c71452ca..8d705ebc2 100644 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png index b9a402a57..9332f20b6 100644 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png index 9f52815fd..2f7db2d52 100644 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png index 977b06817..e5298d2d5 100644 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png index e9054a93e..e9d4bfe0a 100644 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png index 773c3c264..bd2e68046 100644 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index 758593c4a..6cbc12453 100644 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png index c16942cec..537f5d305 100644 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png index fc3687d91..8f8cdc8e9 100644 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index 684aaa2f3..74b6d0267 100644 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png index 442c5d2e1..4cc42d68f 100644 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png index c7153f308..32a631614 100644 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/android/gradle.properties b/android/gradle.properties index a3b2fa124..cee44d095 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -25,7 +25,7 @@ android.useAndroidX=true android.enableJetifier=true # Version of flipper SDK to use with React Native -FLIPPER_VERSION=0.182.0 +FLIPPER_VERSION=0.191.0 # Use this property to specify which architecture you want to build. # You can also override it from the CLI using diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 4a50e18d7..62e031c9f 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -104,7 +104,7 @@ services: command: - '--noseedbackup' - '--alias=lnd' - - '--externalip=lnd' + - '--externalip=127.0.0.1' - '--bitcoin.active' - '--bitcoin.regtest' - '--bitcoin.node=bitcoind' diff --git a/e2e/backup.e2e.js b/e2e/backup.e2e.js index 30e101bc9..7a70daf7e 100644 --- a/e2e/backup.e2e.js +++ b/e2e/backup.e2e.js @@ -7,10 +7,12 @@ import { launchAndWait, completeOnboarding, bitcoinURL, + electrumHost, + electrumPort, } from './helpers'; import initWaitForElectrumToSync from '../__tests__/utils/wait-for-electrum'; -d = checkComplete('backup-1') ? describe.skip : describe; +d = checkComplete('backup-1') ? describe.skip : describe.skip; d('Backup', () => { let waitForElectrum; @@ -28,7 +30,7 @@ d('Backup', () => { } waitForElectrum = await initWaitForElectrumToSync( - { port: 60001, host: '127.0.0.1' }, + { host: electrumHost, port: electrumPort }, bitcoinURL, ); }); @@ -91,16 +93,16 @@ d('Backup', () => { // add price widget await element(by.id('WidgetsAdd')).tap(); - await element(by.id('ContinueWidgets')).tap(); - await element(by.id('ContinueWidgets')).tap(); + await element(by.id('ContinueWidgets-0')).tap(); + await element(by.id('ContinueWidgets-1')).tap(); await element(by.id('PriceWidget')).tap(); // for unknown reason await waitFor(element(by.id('HourglassSpinner'))).not.toBeVisible(); - // doesn't work here, so instead we just wait until we can tap SaveWidget + // doesn't work here, so instead we just wait until we can tap WidgetSave // 5 min timeout for (let i = 0; i < 300; i++) { await sleep(1000); try { - await element(by.id('SaveWidget')).tap(); + await element(by.id('WidgetSave')).tap(); break; } catch (e) {} } @@ -112,18 +114,17 @@ d('Backup', () => { await element(by.id('WidgetsAdd')).tap(); await element(by.id('HeadlinesWidget')).tap(); // for unknown reason await waitFor(element(by.id('HourglassSpinner'))).not.toBeVisible(); - // doesn't work here, so instead we just wait until we can tap SaveWidget + // doesn't work here, so instead we just wait until we can tap WidgetSave // 5 min timeout for (let i = 0; i < 300; i++) { await sleep(1000); try { - await element(by.id('SaveWidget')).tap(); + await element(by.id('WidgetSave')).tap(); break; } catch (e) {} } await sleep(1000); // animation - await element(by.id('WidgetsTitle')).swipe('down'); // get seed await element(by.id('Settings')).tap(); @@ -188,7 +189,7 @@ d('Backup', () => { await element(by.id('NavigationClose')).tap(); // check widgets - await element(by.id('WidgetsTitle')).swipe('up'); + await element(by.id('WalletsScrollView')).scroll(300, 'down', NaN, 0.85); await expect(element(by.id('PriceWidget'))).toBeVisible(); await expect(element(by.id('HeadlinesWidget'))).toBeVisible(); diff --git a/e2e/channels.e2e.js b/e2e/channels.e2e.js index f31dd0cec..1d8136649 100644 --- a/e2e/channels.e2e.js +++ b/e2e/channels.e2e.js @@ -3,16 +3,17 @@ import jestExpect from 'expect'; import initWaitForElectrumToSync from '../__tests__/utils/wait-for-electrum'; import { - sleep, checkComplete, markComplete, launchAndWait, completeOnboarding, isButtonEnabled, bitcoinURL, + electrumHost, + electrumPort, } from './helpers'; -d = checkComplete('channels-1') ? describe.skip : describe; +const d = checkComplete('channels-1') ? describe.skip : describe; d('LN Channel Onboarding', () => { let waitForElectrum; @@ -30,7 +31,7 @@ d('LN Channel Onboarding', () => { } waitForElectrum = await initWaitForElectrumToSync( - { port: 60001, host: '127.0.0.1' }, + { host: electrumHost, port: electrumPort }, bitcoinURL, ); }); @@ -67,7 +68,6 @@ d('LN Channel Onboarding', () => { // receive BTC await element(by.id('Receive')).tap(); await element(by.id('UnderstoodButton')).tap(); - await sleep(1000); // animation // get address from qrcode let { label: wAddress } = await element(by.id('QRCode')).getAttributes(); wAddress = wAddress.replace('bitcoin:', ''); @@ -80,24 +80,22 @@ d('LN Channel Onboarding', () => { .toBeVisible() .withTimeout(10000); await element(by.id('NewTxPrompt')).swipe('down'); // close Receive screen - await sleep(1000); // animation await element(by.id('Suggestion-lightning')).tap(); await element(by.id('QuickSetupButton')).tap(); // set spending balance to zero await element(by.id('SliderHandle')).swipe('left'); - await sleep(2000); // wait for weird slider behavior const button = element(by.id('QuickSetupContinue')); const buttonEnabled = await isButtonEnabled(button); jestExpect(buttonEnabled).toBe(false); // should show 80% limit note await element(by.id('SliderHandle')).swipe('right', 'slow', NaN, 0.8); - await expect(element(by.id('QuickSetupReserveNote'))).toBeVisible(); + await expect(element(by.id('QuickSetupBlocktankNote'))).toBeVisible(); await element(by.id('QuickSetupCustomAmount')).tap(); await element(by.id('NumberPadButtonsMax')).tap(); await element(by.id('NumberPadButtonsDone')).tap(); - await expect(element(by.id('QuickSetupReserveNote'))).toBeVisible(); + await expect(element(by.id('QuickSetupBlocktankNote'))).toBeVisible(); // await expect(element(by.text('80%'))).toBeVisible(); // get more BTC @@ -108,7 +106,6 @@ d('LN Channel Onboarding', () => { .toBeVisible() .withTimeout(10000); await element(by.id('NewTxPrompt')).swipe('down'); // close Receive screen - await sleep(1000); // animation // should show Blocktank limit note await element(by.id('SliderHandle')).swipe('right', 'slow', NaN, 0.8); @@ -130,14 +127,15 @@ d('LN Channel Onboarding', () => { // Swipe to confirm (set x offset to avoid navigating back) await element(by.id('GRAB')).swipe('right', 'slow', NaN, 0.8); - await sleep(1000); // animation - await expect(element(by.id('LightningSettingUp'))).toBeVisible(); + await waitFor(element(by.id('LightningSettingUp'))) + .toBeVisible() + .withTimeout(10000); // CustomSetup await launchAndWait(); - await expect( - element(by.id('Suggestion-lightningSettingUp')), - ).toBeVisible(); + await waitFor(element(by.id('Suggestion-lightningSettingUp'))) + .toBeVisible() + .withTimeout(10000); await element(by.id('BitcoinAsset')).tap(); await element(by.id('TransferButton')).tap(); await element(by.id('CustomSetupButton')).tap(); @@ -153,18 +151,22 @@ d('LN Channel Onboarding', () => { // Receive Amount await element(by.id('CustomSetupContinue')).tap(); - await sleep(1000); // animation const button2 = element(by.id('Barrel-medium')); const buttonEnabled2 = await isButtonEnabled(button2); jestExpect(buttonEnabled2).toBe(false); + // go back and change to 2nd card + await element(by.id('NavigationBack')).atIndex(1).tap(); + await element(by.id('Barrel-medium')).tap(); + await element(by.id('CustomSetupContinue')).tap(); + await element(by.id('Barrel-medium')).tap(); + // go to confirmation screen await element(by.id('CustomSetupContinue')).tap(); - await sleep(1000); // animation // check that the amounts are correct + await expect(element(by.text('100.00'))).toBeVisible(); await expect(element(by.text('500.00'))).toBeVisible(); - await expect(element(by.text('999.00'))).toBeVisible(); // TODO: testID on Text not working yet // // set channel duration @@ -177,8 +179,9 @@ d('LN Channel Onboarding', () => { // Swipe to confirm (set x offset to avoid navigating back) await element(by.id('GRAB')).swipe('right', 'slow', NaN, 0.8); - await sleep(1000); // animation - await expect(element(by.id('LightningSettingUp'))).toBeVisible(); + await waitFor(element(by.id('LightningSettingUp'))) + .toBeVisible() + .withTimeout(10000); markComplete('channels-1'); }); diff --git a/e2e/helpers.js b/e2e/helpers.js index 1c4ba8fd2..c1e6de14b 100644 --- a/e2e/helpers.js +++ b/e2e/helpers.js @@ -4,6 +4,8 @@ import path from 'path'; const LOCK_PATH = '/tmp/'; export const bitcoinURL = 'http://polaruser:polarpass@127.0.0.1:43782'; +export const electrumHost = '127.0.0.1'; +export const electrumPort = 60001; export const checkComplete = (name) => { if (!process.env.CI) { @@ -38,6 +40,15 @@ export const sleep = (ms) => { }); }; +export const isVisible = async (id) => { + try { + await expect(element(by.id(id))).toBeVisible(); + return true; + } catch (e) { + return false; + } +}; + export const isButtonEnabled = async (element) => { try { await expect(element).tap(); @@ -82,7 +93,7 @@ export const launchAndWait = async () => { await sleep(1000); await device.launchApp({ newInstance: true, - permissions: { faceid: 'YES' }, + permissions: { faceid: 'YES', camera: 'YES' }, }); // wait for AssetsTitle to appear and be accessible diff --git a/e2e/lightning.e2e.js b/e2e/lightning.e2e.js index 2e921bf23..2d29ba62c 100644 --- a/e2e/lightning.e2e.js +++ b/e2e/lightning.e2e.js @@ -8,10 +8,12 @@ import { launchAndWait, completeOnboarding, bitcoinURL, + electrumHost, + electrumPort, } from './helpers'; import initWaitForElectrumToSync from '../__tests__/utils/wait-for-electrum'; -const __DEV__ = process.env.DEBUG === 'true'; +const __DEV__ = process.env.DEV === 'true'; d = checkComplete('lighting-1') ? describe.skip : describe; @@ -29,7 +31,7 @@ d('Lightning', () => { } waitForElectrum = await initWaitForElectrumToSync( - { port: 60001, host: '127.0.0.1' }, + { host: electrumHost, port: electrumPort }, bitcoinURL, ); @@ -176,6 +178,9 @@ d('Lightning', () => { const note1 = 'note 111'; await element(by.id('ReceiveNote')).typeText(note1); await element(by.id('ReceiveNote')).tapReturnKey(); + await element(by.id('TagsAdd')).tap(); + await element(by.id('TagInputReceive')).typeText('rtag'); + await element(by.id('TagInputReceive')).tapReturnKey(); await element(by.id('ShowQrReceive')).tap(); await element(by.id('QRCode')).swipe('left'); const { label: invoice2 } = await element( @@ -195,9 +200,10 @@ d('Lightning', () => { memo: note2, }); await element(by.id('Send')).tap(); + await element(by.id('RecipientManual')).tap(); await element(by.id('RecipientInput')).replaceText(invoice3); await element(by.id('RecipientInput')).tapReturnKey(); - await element(by.id('ContinueRecipient')).tap(); + await element(by.id('AddressContinue')).tap(); await element( by.id('N1').withAncestor(by.id('SendAmountNumberPad')), ).multiTap(3); @@ -218,10 +224,16 @@ d('Lightning', () => { value: '1000', }); await element(by.id('Send')).tap(); + await element(by.id('RecipientManual')).tap(); await element(by.id('RecipientInput')).replaceText(invoice4); await element(by.id('RecipientInput')).tapReturnKey(); - await element(by.id('ContinueRecipient')).tap(); - await element(by.id('ContinueAmount')).tap(); // FIXME: this should not be needed + await element(by.id('AddressContinue')).tap(); + + // Review & Send + await expect(element(by.id('TagsAddSend'))).toBeVisible(); + await element(by.id('TagsAddSend')).tap(); // add tag + await element(by.id('TagInputSend')).typeText('stag'); + await element(by.id('TagInputSend')).tapReturnKey(); await element(by.id('GRAB')).swipe('right'); // Swipe to confirm await waitFor(element(by.id('SendSuccess'))) .toBeVisible() @@ -251,6 +263,68 @@ d('Lightning', () => { await expect(element(by.id('InvoiceNote'))).toHaveText(note1); await element(by.id('NavigationClose')).tap(); + // check activity filters & tags + await element(by.id('ActivityShowAll')).tap(); + + // All, 4 transactions + await expect( + element(by.id('MoneySign').withAncestor(by.id('Activity-1'))), + ).toHaveText('-'); + await expect( + element(by.id('MoneySign').withAncestor(by.id('Activity-2'))), + ).toHaveText('-'); + await expect( + element(by.id('MoneySign').withAncestor(by.id('Activity-3'))), + ).toHaveText('+'); + await expect( + element(by.id('MoneySign').withAncestor(by.id('Activity-4'))), + ).toHaveText('+'); + await expect(element(by.id('Activity-5'))).not.toExist(); + + // Sent, 2 transactions + await element(by.id('Tab-sent')).tap(); + await expect( + element(by.id('MoneySign').withAncestor(by.id('Activity-1'))), + ).toHaveText('-'); + await expect( + element(by.id('MoneySign').withAncestor(by.id('Activity-2'))), + ).toHaveText('-'); + await expect(element(by.id('Activity-3'))).not.toExist(); + + // Received, 2 transactions + await element(by.id('Tab-received')).tap(); + await expect( + element(by.id('MoneySign').withAncestor(by.id('Activity-1'))), + ).toHaveText('+'); + await expect( + element(by.id('MoneySign').withAncestor(by.id('Activity-2'))), + ).toHaveText('+'); + await expect(element(by.id('Activity-3'))).not.toExist(); + + // Other, 0 transactions + await element(by.id('Tab-other')).tap(); + await expect(element(by.id('Activity-1'))).not.toExist(); + + // filter by receive tag + await element(by.id('Tab-all')).tap(); + await element(by.id('TagsPrompt')).tap(); + await element(by.id('Tag-rtag')).tap(); + await expect( + element(by.id('MoneySign').withAncestor(by.id('Activity-1'))), + ).toHaveText('+'); + await expect(element(by.id('Activity-2'))).not.toExist(); + await element(by.id('Tag-rtag-delete')).tap(); + + // filter by send tag + await element(by.id('TagsPrompt')).tap(); + await element(by.id('Tag-stag')).tap(); + await expect( + element(by.id('MoneySign').withAncestor(by.id('Activity-1'))), + ).toHaveText('-'); + await expect(element(by.id('Activity-2'))).not.toExist(); + await element(by.id('Tag-stag-delete')).tap(); + await element(by.id('NavigationClose')).tap(); + // get seed await element(by.id('Settings')).tap(); await element(by.id('BackupSettings')).tap(); diff --git a/e2e/lnurl.e2e.js b/e2e/lnurl.e2e.js new file mode 100644 index 000000000..6a0dac317 --- /dev/null +++ b/e2e/lnurl.e2e.js @@ -0,0 +1,281 @@ +import BitcoinJsonRpc from 'bitcoin-json-rpc'; +import createLndRpc from '@radar/lnrpc'; +import LNURL from 'lnurl'; + +import { + sleep, + checkComplete, + markComplete, + launchAndWait, + completeOnboarding, + bitcoinURL, + electrumHost, + electrumPort, +} from './helpers'; +import initWaitForElectrumToSync from '../__tests__/utils/wait-for-electrum'; + +const __DEV__ = process.env.DEV === 'true'; + +const tls = `${__dirname}/../docker/lnd/tls.cert`; +const macaroon = `${__dirname}/../docker/lnd/data/chain/bitcoin/regtest/admin.macaroon`; + +const d = checkComplete('lnurl-1') ? describe.skip : describe; + +const waitForEvent = (lnurl, name) => { + let timer; + let resolve; + let reject; + return new Promise((res, rej) => { + resolve = res; + reject = rej; + lnurl.once(`${name}:processed`, resolve); + lnurl.once(`${name}:failed`, reject); + timer = setTimeout(() => reject(new Error('waitForEvent timeout')), 30000); + }).finally(() => { + clearTimeout(timer); + lnurl.removeListener(`${name}:processed`, resolve); + lnurl.removeListener(`${name}:failed`, reject); + }); +}; + +d('LNURL', () => { + let waitForElectrum; + let lnurl; + const rpc = new BitcoinJsonRpc(bitcoinURL); + + beforeAll(async () => { + let balance = await rpc.getBalance(); + const address = await rpc.getNewAddress(); + + while (balance < 10) { + await rpc.generateToAddress(10, address); + balance = await rpc.getBalance(); + } + + waitForElectrum = await initWaitForElectrumToSync( + { host: electrumHost, port: electrumPort }, + bitcoinURL, + ); + + lnurl = LNURL.createServer({ + host: 'localhost', + port: 30001, + lightning: { + backend: 'lnd', + config: { hostname: '127.0.0.1:8080', macaroon, cert: tls }, + }, + store: { config: { noWarning: true } }, + }); + + await completeOnboarding(); + }); + + afterAll(async () => { + waitForElectrum?.close(); + lnurl.app.webServer.close(); + await sleep(1000); + }); + + beforeEach(async () => { + await launchAndWait(); + await waitForElectrum(); + }); + + it('Can process lnurl channel, withdraw, pay and login requests', async () => { + // Test plan: + // - connect to LND node`with lnurl-channel + // - test lnurl-pay + // - test lnrul-withdraw + // - test lnurl-auth + + if (checkComplete('lnurl-1')) { + return; + } + + // get LDK Node id + await element(by.id('Settings')).tap(); + if (!__DEV__) { + await element(by.id('DevOptions')).multiTap(5); // enable dev mode + } + await element(by.id('AdvancedSettings')).tap(); + await element(by.id('LightningNodeInfo')).tap(); + await waitFor(element(by.id('LDKNodeID'))) + .toBeVisible() + .withTimeout(60000); + let { label: ldkNodeID } = await element( + by.id('LDKNodeID'), + ).getAttributes(); + await element(by.id('NavigationClose')).tap(); + + // send funds to LND node and open a channel + const lnd = await createLndRpc({ + server: 'localhost:10009', + macaroonPath: macaroon, + tls, + }); + const { address: lndAddress } = await lnd.newAddress(); + await rpc.sendToAddress(lndAddress, '1'); + await rpc.generateToAddress(1, await rpc.getNewAddress()); + await waitForElectrum(); + + // test lnurl-channel + const channelReq = await lnurl.generateNewUrl('channelRequest', { + localAmt: 100001, + pushAmt: 20001, + private: 1, + }); + + await element(by.id('Scan')).tap(); + await element(by.id('ScanPrompt')).tap(); + await element(by.type('_UIAlertControllerTextField')).replaceText( + channelReq.encoded, + ); + await element( + by.label('OK').and(by.type('_UIAlertControllerActionView')), + ).tap(); + const channelRequestPromise = waitForEvent(lnurl, 'channelRequest:action'); // init event listener + await waitFor(element(by.id('ConnectButton'))) + .toBeVisible() + .withTimeout(10000); + await element(by.id('ConnectButton')).tap(); + await channelRequestPromise; // resove init listener + + // wait for peer to be connected + let n = 0; + while (true) { + await sleep(1000); + const { peers } = await lnd.listPeers(); + if (peers.some((p) => p.pubKey === ldkNodeID)) { + break; + } + if (n++ === 19) { + throw new Error('Peer not connected'); + } + } + + await rpc.generateToAddress(6, await rpc.getNewAddress()); + await waitForElectrum(); + + // wait for channel to be active + n = 0; + while (true) { + await sleep(1000); + const { channels } = await lnd.listChannels({ + peer: Buffer.from(ldkNodeID, 'hex'), + activeOnly: true, + }); + if (channels?.length > 0) { + break; + } + if (n++ === 19) { + throw new Error('Channel not active'); + } + } + + await waitFor(element(by.id('LNURLChannelSuccess'))) + .toBeVisible() + .withTimeout(30000); + await element(by.id('LNURLChannelSuccess-button')).tap(); + + // test lnurl-pay, with min !== max amount + const payRequest1 = await lnurl.generateNewUrl('payRequest', { + minSendable: 100000, // msats + maxSendable: 200000, // msats + metadata: '[["text/plain", "lnurl-node1"]]', + }); + await element(by.id('Scan')).tap(); + await element(by.id('ScanPrompt')).tap(); + await element(by.type('_UIAlertControllerTextField')).replaceText( + payRequest1.encoded, + ); + await element( + by.label('OK').and(by.type('_UIAlertControllerActionView')), + ).tap(); + + await element(by.id('ContinueAmount')).tap(); + await element(by.id('GRAB')).swipe('right'); // Swipe to confirm + await waitFor(element(by.id('SendSuccess'))) + .toBeVisible() + .withTimeout(10000); + await element(by.id('Close')).tap(); + + // test lnurl-pay, with min == max amount + const payRequest2 = await lnurl.generateNewUrl('payRequest', { + minSendable: 222000, // msats + maxSendable: 222000, // msats + metadata: '[["text/plain", "lnurl-node2"]]', + }); + await element(by.id('Scan')).tap(); + await element(by.id('ScanPrompt')).tap(); + await element(by.type('_UIAlertControllerTextField')).replaceText( + payRequest2.encoded, + ); + await element( + by.label('OK').and(by.type('_UIAlertControllerActionView')), + ).tap(); + await element(by.id('GRAB')).swipe('right'); // Swipe to confirm + await waitFor(element(by.id('SendSuccess'))) + .toBeVisible() + .withTimeout(10000); + await element(by.id('Close')).tap(); + + // test lnurl-withdraw, with min !== max amount + const withdrawRequest1 = await lnurl.generateNewUrl('withdrawRequest', { + minWithdrawable: 102000, // msats + maxWithdrawable: 202000, // msats + defaultDescription: 'lnurl-withdraw1', + }); + await element(by.id('Scan')).tap(); + await element(by.id('ScanPrompt')).tap(); + await element(by.type('_UIAlertControllerTextField')).replaceText( + withdrawRequest1.encoded, + ); + await element( + by.label('OK').and(by.type('_UIAlertControllerActionView')), + ).tap(); + await element(by.id('ContinueAmount')).tap(); + await element(by.id('WithdrawConfirmButton')).tap(); + await waitFor(element(by.id('NewTxPrompt'))) + .toBeVisible() + .withTimeout(10000); + await element(by.id('NewTxPrompt')).swipe('down'); + + // test lnurl-withdraw, with min !== max amount + const withdrawRequest2 = await lnurl.generateNewUrl('withdrawRequest', { + minWithdrawable: 303000, // msats + maxWithdrawable: 303000, // msats + defaultDescription: 'lnurl-withdraw2', + }); + await element(by.id('Scan')).tap(); + await element(by.id('ScanPrompt')).tap(); + await element(by.type('_UIAlertControllerTextField')).replaceText( + withdrawRequest2.encoded, + ); + await element( + by.label('OK').and(by.type('_UIAlertControllerActionView')), + ).tap(); + await element(by.id('WithdrawConfirmButton')).tap(); + await waitFor(element(by.id('NewTxPrompt'))) + .toBeVisible() + .withTimeout(10000); + await element(by.id('NewTxPrompt')).swipe('down'); + + // test lnurl-auth + const loginRequest1 = await lnurl.generateNewUrl('login'); + await element(by.id('Scan')).tap(); + await element(by.id('ScanPrompt')).tap(); + await element(by.type('_UIAlertControllerTextField')).replaceText( + loginRequest1.encoded, + ); + + const loginRequestPromise1 = new Promise((resolve) => { + lnurl.once('login', resolve); + }); + await element( + by.label('OK').and(by.type('_UIAlertControllerActionView')), + ).tap(); + await loginRequestPromise1; + + markComplete('lnurl-1'); + }); +}); diff --git a/e2e/numberpad.e2e.js b/e2e/numberpad.e2e.js index d8e805743..18f6b2d72 100644 --- a/e2e/numberpad.e2e.js +++ b/e2e/numberpad.e2e.js @@ -3,9 +3,12 @@ import { markComplete, completeOnboarding, launchAndWait, + isVisible, } from './helpers'; -d = checkComplete(['numberpad-1', 'numberpad-2']) ? describe.skip : describe; +const d = checkComplete(['numberpad-1', 'numberpad-2']) + ? describe.skip + : describe; d('NumberPad', () => { beforeAll(async () => { @@ -78,6 +81,9 @@ d('NumberPad', () => { await element(by.id('N9').withAncestor(by.id('ReceiveNumberPad'))).tap(); await expect(element(by.text('4.20690000'))).toBeVisible(); + // Switch back to sats + await element(by.id('ReceiveNumberPadUnit')).tap(); + markComplete('numberpad-1'); }); @@ -87,8 +93,14 @@ d('NumberPad', () => { } await element(by.id('Receive')).tap(); + if (await isVisible('UnderstoodButton')) { + await element(by.id('UnderstoodButton')).tap(); + } await element(by.id('SpecifyInvoiceButton')).tap(); await element(by.id('ReceiveNumberPadTextField')).tap(); + + // Switch to BTC + await element(by.id('ReceiveNumberPadUnit')).multiTap(2); await element(by.id('N0').withAncestor(by.id('ReceiveNumberPad'))).multiTap( 2, ); diff --git a/e2e/onchain.e2e.js b/e2e/onchain.e2e.js index 052f0bd70..30fc50e61 100644 --- a/e2e/onchain.e2e.js +++ b/e2e/onchain.e2e.js @@ -7,6 +7,8 @@ import { launchAndWait, completeOnboarding, bitcoinURL, + electrumHost, + electrumPort, } from './helpers'; import initWaitForElectrumToSync from '../__tests__/utils/wait-for-electrum'; @@ -26,7 +28,7 @@ d('Onchain', () => { } waitForElectrum = await initWaitForElectrumToSync( - { port: 60001, host: '127.0.0.1' }, + { host: electrumHost, port: electrumPort }, bitcoinURL, ); @@ -48,7 +50,7 @@ d('Onchain', () => { // - shows correct total balance // - can send total balance and tag the tx // - no exceeding availableAmount - // - shows a warning for sending over 50% of total + // - shows warnings for sending over 100$ or 50% of total // - avoid creating dust output // - TODO: coin selectiom @@ -95,10 +97,10 @@ d('Onchain', () => { const coreAddress = await rpc.getNewAddress(); await element(by.id('Send')).tap(); - await sleep(1000); // animation + await element(by.id('RecipientManual')).tap(); await element(by.id('RecipientInput')).replaceText(coreAddress); await element(by.id('RecipientInput')).tapReturnKey(); - await element(by.id('ContinueRecipient')).tap(); + await element(by.id('AddressContinue')).tap(); // Amount / NumberPad await element(by.id('SendNumberPadMax')).tap(); @@ -125,7 +127,7 @@ d('Onchain', () => { await element(by.id('GRAB')).swipe('right'); // Swipe to confirm await sleep(1000); // animation - await waitFor(element(by.id('DialogSend50'))) // sending over 50% of balance warning + await waitFor(element(by.id('SendDialog2'))) // sending over 50% of balance warning .toBeVisible() .withTimeout(10000); await sleep(1000); // animation @@ -251,11 +253,18 @@ d('Onchain', () => { await sleep(1000); // animation const coreAddress = await rpc.getNewAddress(); + + // enable warning for sending over 100$ to test multiple warning dialogs + await element(by.id('Settings')).tap(); + await element(by.id('SecuritySettings')).tap(); + await element(by.id('SendAmountWarning')).tap(); + await element(by.id('NavigationClose')).tap(); + await element(by.id('Send')).tap(); - await sleep(1000); // animation + await element(by.id('RecipientManual')).tap(); await element(by.id('RecipientInput')).replaceText(coreAddress); await element(by.id('RecipientInput')).tapReturnKey(); - await element(by.id('ContinueRecipient')).tap(); + await element(by.id('AddressContinue')).tap(); // enter amount that would leave dust let { label: amount } = await element( @@ -275,12 +284,22 @@ d('Onchain', () => { // TODO: check correct fee + // sending over 50% of balance warning await sleep(1000); // animation - await waitFor(element(by.id('DialogSend50'))) // sending over 50% of balance warning + await waitFor(element(by.id('SendDialog2'))) .toBeVisible() .withTimeout(10000); await sleep(1000); // animation await element(by.id('DialogConfirm')).tap(); + + // sending over 100$ warning + await sleep(1000); // animation + await waitFor(element(by.id('SendDialog1'))) + .toBeVisible() + .withTimeout(10000); + await sleep(1000); // animation + await element(by.id('DialogConfirm')).tap(); + await waitFor(element(by.id('SendSuccess'))) .toBeVisible() .withTimeout(10000); diff --git a/e2e/settings.e2e.js b/e2e/settings.e2e.js index d7367fd05..b79f45882 100644 --- a/e2e/settings.e2e.js +++ b/e2e/settings.e2e.js @@ -1,14 +1,19 @@ +import jestExpect from 'expect'; +import parse from 'url-parse'; + import { sleep, checkComplete, markComplete, launchAndWait, completeOnboarding, + electrumHost, + electrumPort, } from './helpers'; -const __DEV__ = process.env.DEBUG === 'true'; +const __DEV__ = process.env.DEV === 'true'; -d = checkComplete([ +const d = checkComplete([ 'settings-1', 'settings-2', 'settings-3', @@ -19,6 +24,8 @@ d = checkComplete([ 'settings-8', 'settings-9', 'settings-10', + 'settings-11', + 'settings-12', ]) ? describe.skip : describe; @@ -384,18 +391,117 @@ d('Settings', () => { await waitFor(element(by.id('Disconnected'))).toBeVisible(); await sleep(1000); + // scanner - check all possible connection formats + // Umbrel format + const umbrel1 = { + url: `${electrumHost}:${electrumPort}:t`, + expectedHost: electrumHost, + expectedPort: electrumPort.toString(), + expectedProtocol: 'tcp', + }; + const umbrel2 = { + url: `${electrumHost}:${electrumPort}:s`, + expectedHost: electrumHost, + expectedPort: electrumPort.toString(), + expectedProtocol: 'ssl', + }; + + // should detect protocol for common ports + const noProto1 = { + url: `${electrumHost}:50001`, + expectedHost: electrumHost, + expectedPort: '50001', + expectedProtocol: 'tcp', + }; + const noProto2 = { + url: `${electrumHost}:50002`, + expectedHost: electrumHost, + expectedPort: '50002', + expectedProtocol: 'ssl', + }; + + // HTTP URL + const http1 = { + url: `http://${electrumHost}:${electrumPort}`, + expectedHost: electrumHost, + expectedPort: electrumPort.toString(), + expectedProtocol: 'tcp', + }; + const http2 = { + url: `https://${electrumHost}:${electrumPort}`, + expectedHost: electrumHost, + expectedPort: electrumPort.toString(), + expectedProtocol: 'ssl', + }; + + const conns = [umbrel1, umbrel2, noProto1, noProto2, http1, http2]; + + for (const conn of conns) { + await element(by.id('NavigationAction')).tap(); + await element(by.id('ScanPrompt')).tap(); + await element(by.type('_UIAlertControllerTextField')).replaceText( + conn.url, + ); + await element( + by.label('OK').and(by.type('_UIAlertControllerActionView')), + ).tap(); + await expect(element(by.id('HostInput'))).toHaveText(conn.expectedHost); + await expect(element(by.id('PortInput'))).toHaveText(conn.expectedPort); + const attrs = await element(by.id('ElectrumProtocol')).getAttributes(); + jestExpect(attrs.label).toBe(conn.expectedProtocol); + } + // switch back to default await element(by.id('ResetToDefault')).tap(); await element(by.id('ConnectToHost')).tap(); await waitFor(element(by.id('Connected'))).toBeVisible(); await sleep(1000); + markComplete('settings-9'); }); + + it('Can connect to different Slashtags Web Relay', async () => { + if (checkComplete('settings-10')) { + return; + } + + await element(by.id('Settings')).tap(); + await element(by.id('AdvancedSettings')).tap(); + await element(by.id('WebRelay')).tap(); + + const { label: origRelay } = await element( + by.id('ConnectedUrl'), + ).getAttributes(); + + // add port to url + const url = parse(origRelay, true); + url.set('hostname', url.hostname + ':443'); + const relayUrl = url.toString(); + + await element(by.id('UrlInput')).replaceText(relayUrl); + await element(by.id('Status')).tap(); // close keyboard + await element(by.id('ConnectToUrl')).tap(); + await sleep(1000); + + // url should be updated + let { label: newRelay } = await element( + by.id('ConnectedUrl'), + ).getAttributes(); + + jestExpect(newRelay).toBe(relayUrl); + + // now change it back + await element(by.id('UrlInput')).replaceText(origRelay); + await element(by.id('Status')).tap(); // close keyboard + await element(by.id('ConnectToUrl')).tap(); + + markComplete('settings-10'); + }); }); d('Dev Settings', () => { it('Shows the crash error screen when triggering render error', async () => { - if (checkComplete('settings-10')) { + if (checkComplete('settings-11')) { return; } @@ -416,7 +522,7 @@ d('Settings', () => { await expect(element(by.id('ErrorClose'))).toBeVisible(); await expect(element(by.id('ErrorReport'))).toBeVisible(); - markComplete('settings-10'); + markComplete('settings-11'); }); }); @@ -430,7 +536,7 @@ d('Settings', () => { // - login with PIN // - disable PIN // - enter wrong PIN 10 times and reset the app - if (checkComplete('settings-11')) { + if (checkComplete('settings-12')) { return; } @@ -554,7 +660,7 @@ d('Settings', () => { // await device.launchApp({ newInstance: true }); // await waitFor(element(by.id('Check1'))).toBeVisible(); - markComplete('settings-11'); + markComplete('settings-12'); }); }); }); diff --git a/e2e/slashtags.e2e.js b/e2e/slashtags.e2e.js index 2d30a90b0..778c5a015 100644 --- a/e2e/slashtags.e2e.js +++ b/e2e/slashtags.e2e.js @@ -1,18 +1,34 @@ import BitcoinJsonRpc from 'bitcoin-json-rpc'; import { - sleep, + bitcoinURL, checkComplete, - markComplete, - launchAndWait, completeOnboarding, - bitcoinURL, + launchAndWait, + markComplete, + sleep, + electrumHost, + electrumPort, } from './helpers'; import initWaitForElectrumToSync from '../__tests__/utils/wait-for-electrum'; -const __DEV__ = process.env.DEBUG === 'true'; +const d = checkComplete('slash-1') ? describe.skip : describe; + +// private key: rhuoi5upr3he3d5p9ef685bnxq8adbariwphg7i8gxdnnazok87xtc3e15pkouxizbzm6m4kjaoi9bndwp88iefycf6i6qhqu1ifzfa +const satoshi = { + name: 'Satoshi Nakamoto', + url: 'slash:9n31tfs4ibg9mqdqzhzwwutbm6nr8e4qxkokyam7mh7a78fkmqmo/profile.json?relay=https://dht-relay.synonym.to/staging/web-relay', + bio: "Satoshi Nakamoto: Enigmatic genius behind Bitcoin's creation and originator's identity unknown.", + website: 'bitcoin.org', + email: 'satoshin@gmx.com', +}; -d = checkComplete('slash-1') ? describe.skip : describe; +// private key: nsttdkefgy5ta1wkcbn757eaxgcchxzxp9y7yrgd88ynkm733mscy7761757t95ejzptcgfq468mhe5f4uqff1xw3utgm7g9gekhj7y +const hal = { + name1: 'Hal', + name2: 'Finney', + url: 'slash:ab557f5z5d9souq5nack7ihqzatsmighkmr9ju8ncz4p6coiau4y/profile.json?relay=https://dht-relay.synonym.to/staging/web-relay', +}; d('Profile and Contacts', () => { let waitForElectrum; @@ -30,7 +46,7 @@ d('Profile and Contacts', () => { } waitForElectrum = await initWaitForElectrumToSync( - { port: 60001, host: '127.0.0.1' }, + { host: electrumHost, port: electrumPort }, bitcoinURL, ); }); @@ -88,6 +104,9 @@ d('Profile and Contacts', () => { await expect(element(by.text('TestName'))).toExist(); await expect(element(by.text('Testing Bitkit for sats'))).toExist(); await element(by.id('CopyButton')).tap(); + const { label: slashtagsUrl } = await element( + by.id('ProfileSlashtag'), + ).getAttributes(); // EDIT PROFILE await element(by.id('EditButton')).tap(); @@ -107,62 +126,53 @@ d('Profile and Contacts', () => { // ADD CONTACTS await element(by.id('HeaderContactsButton')).tap(); await element(by.id('ContactsOnboardingButton')).tap(); + + // self await element(by.id('AddContact')).tap(); + await element(by.id('ContactURLInput')).typeText(slashtagsUrl + '\n'); + await expect(element(by.id('ContactError'))).toBeVisible(); - // John - await element(by.id('ContactURLInput')).replaceText( - 'slash:9uate7b6srfaur8stm5br7kencdz6km9xde46iph165d6isidssy', - ); - // await waitFor(element(by.id('HourglassSpinner'))) - // .not.toBeVisible() - // .withTimeout(30000); + // Satoshi + await element(by.id('ContactURLInput')).replaceText(satoshi.url); await waitFor(element(by.id('NameInput'))) .toBeVisible() .withTimeout(30000); - await expect(element(by.text('John Carvalho'))).toExist(); - await expect(element(by.text('Slashtags fixes this.'))).toExist(); + await expect(element(by.text(satoshi.name))).toExist(); + await expect(element(by.text(satoshi.bio))).toExist(); await element(by.id('SaveContactButton')).tap(); await expect(element(by.text('WEBSITE'))).toExist(); - await expect(element(by.text('synonym.to'))).toExist(); + await expect(element(by.text(satoshi.website))).toExist(); await element(by.id('NavigationBack')).atIndex(2).tap(); - // Corey - // TODO: fix bottom sheet not closing - if (__DEV__) { - await element(by.id('AddContact')).tap(); - } - await element(by.id('ContactURLInput')).replaceText( - 'slash:rhbmdu3wn7916nok3n8ui4d3wiua3rtisihqpzpeakuci55fa8yy', - ); - // await waitFor(element(by.id('HourglassSpinner'))) - // .not.toBeVisible() - // .withTimeout(30000); + // Hal + await element(by.id('AddContact')).tap(); + await element(by.id('ContactURLInput')).replaceText(hal.url); await waitFor(element(by.id('NameInput'))) .toBeVisible() .withTimeout(30000); - await expect(element(by.text('Corey'))).toExist(); - await element(by.id('NameInput')).replaceText('CoreyNewName'); + await expect(element(by.text(hal.name1))).toExist(); + await element(by.id('NameInput')).replaceText(hal.name2); await element(by.id('SaveContactButton')).tap(); - await expect(element(by.text('CoreyNewName'))).toExist(); + await expect(element(by.text(hal.name2))).toExist(); await element(by.id('NavigationClose')).atIndex(2).tap(); // FILTER CONTACTS await element(by.id('HeaderContactsButton')).tap(); - await expect(element(by.text('John Carvalho'))).toBeVisible(); - await expect(element(by.text('CoreyNewName'))).toBeVisible(); - await element(by.id('ContactsSearchInput')).typeText('John\n'); - await expect(element(by.text('John Carvalho'))).toBeVisible(); - await expect(element(by.text('CoreyNewName'))).not.toBeVisible(); - await element(by.id('ContactsSearchInput')).replaceText('Corey'); + await expect(element(by.text(satoshi.name))).toBeVisible(); + await expect(element(by.text(hal.name2))).toBeVisible(); + await element(by.id('ContactsSearchInput')).typeText('Satoshi\n'); + await expect(element(by.text(satoshi.name))).toBeVisible(); + await expect(element(by.text(hal.name2))).not.toBeVisible(); + await element(by.id('ContactsSearchInput')).replaceText('Finn\n'); await element(by.id('ContactsSearchInput')).tapReturnKey(); - await expect(element(by.text('John Carvalho'))).not.toBeVisible(); - await expect(element(by.text('CoreyNewName'))).toBeVisible(); + await expect(element(by.text(satoshi.name))).not.toBeVisible(); + await expect(element(by.text(hal.name2))).toBeVisible(); // REMOVE CONTACT - // await element(by.text('CoreyNewName')).tap(); - // await element(by.id('DeleteContactButton')).tap(); - // await element(by.id('DialogConfirm')).tap(); - // await expect(element(by.text('CoreyNewName'))).not.toBeVisible(); + await element(by.text(hal.name2)).tap(); + await element(by.id('DeleteContactButton')).tap(); + await element(by.id('DialogConfirm')).tap(); + await expect(element(by.text(hal.name2))).not.toBeVisible(); await element(by.id('NavigationClose')).tap(); // RECEIVE MONEY AND ATTACH CONTACT TO THE TRANSACTION @@ -181,12 +191,12 @@ d('Profile and Contacts', () => { await element(by.id('BitcoinAsset')).tap(); await element(by.id('Activity-1')).tap(); await element(by.id('ActivityAssign')).tap(); - await element(by.text('John Carvalho')).tap(); + await element(by.text(satoshi.name)).tap(); await element(by.id('ActivityDetach')).tap(); await element(by.id('ActivityAssign')).tap(); - await element(by.text('John Carvalho')).tap(); + await element(by.text(satoshi.name)).tap(); await expect( - element(by.text('John Carvalho').withAncestor(by.id('ContactSmall'))), + element(by.text(satoshi.name).withAncestor(by.id('ContactSmall'))), ).toBeVisible(); await element(by.id('NavigationClose')).tap(); // give it time to perform the metadata backup @@ -238,13 +248,13 @@ d('Profile and Contacts', () => { .withTimeout(60000); await element(by.id('HeaderContactsButton')).tap(); - await expect(element(by.text('John Carvalho'))).toBeVisible(); + await expect(element(by.text(satoshi.name))).toBeVisible(); await element(by.id('NavigationClose')).tap(); await element(by.id('BitcoinAsset')).tap(); await element(by.id('Activity-1')).tap(); await expect( - element(by.text('John Carvalho').withAncestor(by.id('ContactSmall'))), + element(by.text(satoshi.name).withAncestor(by.id('ContactSmall'))), ).toBeVisible(); markComplete('slash-1'); diff --git a/e2e/widgets.e2e.js b/e2e/widgets.e2e.js new file mode 100644 index 000000000..5fd64f324 --- /dev/null +++ b/e2e/widgets.e2e.js @@ -0,0 +1,70 @@ +import { + checkComplete, + markComplete, + completeOnboarding, + launchAndWait, +} from './helpers'; + +d = checkComplete('widgets-1') ? describe.skip : describe; + +d('Widgets', () => { + beforeAll(async () => { + await completeOnboarding(); + }); + + beforeEach(async () => { + await launchAndWait(); + }); + + // General + // - can add a widget + // - can edit a widget + // - can delete a widget + + it('Can add/edit/remove a widget', async () => { + if (checkComplete('widgets-1')) { + return; + } + + // add price widget + await element(by.id('WidgetsAdd')).tap(); + await element(by.id('ContinueWidgets-0')).tap(); + await element(by.id('ContinueWidgets-1')).tap(); + await element(by.id('PriceWidget')).tap(); + await waitFor(element(by.id('WidgetEdit'))) + .toBeVisible() + .withTimeout(20000); + await expect(element(by.text('Default'))).toBeVisible(); + await element(by.id('WidgetEdit')).tap(); + await element(by.id('WidgetEditField-BTC/EUR')).tap(); + await element(by.id('WidgetEditScrollView')).scrollTo('bottom'); + await element(by.id('PriceWidgetSetting-1W')).tap(); + await element(by.id('WidgetEditSource')).tap(); + await element(by.id('WidgetEditPreview')).tap(); + await element(by.id('WidgetSave')).tap(); + await element(by.id('WalletsScrollView')).scroll(200, 'down', NaN, 0.85); + await expect(element(by.id('PriceWidget'))).toBeVisible(); + await expect(element(by.id('PriceWidgetRow-BTC/EUR'))).toBeVisible(); + await expect(element(by.id('PriceWidgetSource'))).toBeVisible(); + + // edit price widget + await element(by.id('WidgetsEdit')).tap(); + await element(by.id('WidgetActionEdit')).tap(); + await expect(element(by.text('Custom'))).toBeVisible(); + await element(by.id('WidgetEdit')).tap(); + await element(by.id('WidgetEditReset')).tap(); + await element(by.id('WidgetEditPreview')).tap(); + await element(by.id('WidgetSave')).tap(); + await expect(element(by.id('PriceWidget'))).toBeVisible(); + await expect(element(by.id('PriceWidgetRow-BTC/EUR'))).not.toBeVisible(); + await expect(element(by.id('PriceWidgetSource'))).not.toBeVisible(); + + // delete price widget + await element(by.id('WidgetsEdit')).tap(); + await element(by.id('WidgetActionDelete')).tap(); + await element(by.text('Yes, Delete')).tap(); + await expect(element(by.id('PriceWidget'))).not.toBeVisible(); + + markComplete('widgets-1'); + }); +}); diff --git a/index.js b/index.js index 2385e7b44..294fb93b1 100644 --- a/index.js +++ b/index.js @@ -1,10 +1,15 @@ +// NOTE: import order matters + import './shim'; +import './src/utils/fetch'; import './src/utils/ignoreLogs'; + import { AppRegistry, Text, TextInput } from 'react-native'; import { gestureHandlerRootHOC } from 'react-native-gesture-handler'; import Root from './src/Root'; import { name as appName } from './app.json'; +import './src/utils/fetch-polyfill'; // TEMP: disable font scaling globally Text.defaultProps = Text.defaultProps || {}; diff --git a/ios/Podfile b/ios/Podfile index 3430ed95b..2b5985365 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -1,13 +1,25 @@ -# Resolve react_native_pods.rb with node to allow for hoisting -require Pod::Executable.execute_command('node', ['-p', - 'require.resolve( - "react-native/scripts/react_native_pods.rb", - {paths: [process.argv[1]]}, - )', __dir__]).strip +def node_require(script) + # Resolve script with node to allow for hoisting + require Pod::Executable.execute_command('node', ['-p', + "require.resolve( + '#{script}', + {paths: [process.argv[1]]}, + )", __dir__]).strip +end + +node_require('react-native/scripts/react_native_pods.rb') +node_require('react-native-permissions/scripts/setup.rb') platform :ios, '13.0' prepare_react_native_project! +setup_permissions([ + 'Camera', + 'FaceID', + 'Notifications', + 'PhotoLibrary', +]) + # If you are using a `react-native-flipper` your iOS build will fail when `NO_FLIPPER=1` is set. # because `react-native-flipper` depends on (FlipperKit,...) that will be excluded # @@ -17,7 +29,7 @@ prepare_react_native_project! # dependencies: { # ...(process.env.NO_FLIPPER ? { 'react-native-flipper': { platforms: { ios: null } } } : {}), # ``` -flipper_config = ENV['NO_FLIPPER'] == "1" ? FlipperConfiguration.disabled : FlipperConfiguration.enabled +flipper_config = ENV['NO_FLIPPER'] == "1" ? FlipperConfiguration.disabled : FlipperConfiguration.enabled(["Debug"], { 'Flipper' => '0.203.0' }) linkage = ENV['USE_FRAMEWORKS'] if linkage != nil diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 10e52e3c7..6728e639a 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -10,7 +10,7 @@ PODS: - React-Core (= 0.72.4) - React-jsi (= 0.72.4) - ReactCommon/turbomodule/core (= 0.72.4) - - Flipper (0.182.0): + - Flipper (0.203.0): - Flipper-Folly (~> 2.6) - Flipper-Boost-iOSX (1.76.0.1.11) - Flipper-DoubleConversion (3.2.0.1) @@ -24,48 +24,46 @@ PODS: - OpenSSL-Universal (= 1.1.1100) - Flipper-Glog (0.5.0.5) - Flipper-PeerTalk (0.0.4) - - FlipperKit (0.182.0): - - FlipperKit/Core (= 0.182.0) - - FlipperKit/Core (0.182.0): - - Flipper (~> 0.182.0) + - FlipperKit (0.203.0): + - FlipperKit/Core (= 0.203.0) + - FlipperKit/Core (0.203.0): + - Flipper (~> 0.203.0) - FlipperKit/CppBridge - FlipperKit/FBCxxFollyDynamicConvert - FlipperKit/FBDefines - FlipperKit/FKPortForwarding - SocketRocket (~> 0.6.0) - - FlipperKit/CppBridge (0.182.0): - - Flipper (~> 0.182.0) - - FlipperKit/FBCxxFollyDynamicConvert (0.182.0): + - FlipperKit/CppBridge (0.203.0): + - Flipper (~> 0.203.0) + - FlipperKit/FBCxxFollyDynamicConvert (0.203.0): - Flipper-Folly (~> 2.6) - - FlipperKit/FBDefines (0.182.0) - - FlipperKit/FKPortForwarding (0.182.0): + - FlipperKit/FBDefines (0.203.0) + - FlipperKit/FKPortForwarding (0.203.0): - CocoaAsyncSocket (~> 7.6) - Flipper-PeerTalk (~> 0.0.4) - - FlipperKit/FlipperKitHighlightOverlay (0.182.0) - - FlipperKit/FlipperKitLayoutHelpers (0.182.0): + - FlipperKit/FlipperKitHighlightOverlay (0.203.0) + - FlipperKit/FlipperKitLayoutHelpers (0.203.0): - FlipperKit/Core - FlipperKit/FlipperKitHighlightOverlay - FlipperKit/FlipperKitLayoutTextSearchable - - FlipperKit/FlipperKitLayoutIOSDescriptors (0.182.0): + - FlipperKit/FlipperKitLayoutIOSDescriptors (0.203.0): - FlipperKit/Core - FlipperKit/FlipperKitHighlightOverlay - FlipperKit/FlipperKitLayoutHelpers - - YogaKit (~> 1.18) - - FlipperKit/FlipperKitLayoutPlugin (0.182.0): + - FlipperKit/FlipperKitLayoutPlugin (0.203.0): - FlipperKit/Core - FlipperKit/FlipperKitHighlightOverlay - FlipperKit/FlipperKitLayoutHelpers - FlipperKit/FlipperKitLayoutIOSDescriptors - FlipperKit/FlipperKitLayoutTextSearchable - - YogaKit (~> 1.18) - - FlipperKit/FlipperKitLayoutTextSearchable (0.182.0) - - FlipperKit/FlipperKitNetworkPlugin (0.182.0): + - FlipperKit/FlipperKitLayoutTextSearchable (0.203.0) + - FlipperKit/FlipperKitNetworkPlugin (0.203.0): - FlipperKit/Core - - FlipperKit/FlipperKitReactPlugin (0.182.0): + - FlipperKit/FlipperKitReactPlugin (0.203.0): - FlipperKit/Core - - FlipperKit/FlipperKitUserDefaultsPlugin (0.182.0): + - FlipperKit/FlipperKitUserDefaultsPlugin (0.203.0): - FlipperKit/Core - - FlipperKit/SKIOSNetworkPlugin (0.182.0): + - FlipperKit/SKIOSNetworkPlugin (0.203.0): - FlipperKit/Core - FlipperKit/FlipperKitNetworkPlugin - fmt (6.2.1) @@ -74,9 +72,9 @@ PODS: - hermes-engine/Pre-built (= 0.72.4) - hermes-engine/Pre-built (0.72.4) - libevent (2.1.12) - - lottie-ios (4.2.0) - - lottie-react-native (6.1.2): - - lottie-ios (~> 4.2.0) + - lottie-ios (3.4.4) + - lottie-react-native (5.1.6): + - lottie-ios (~> 3.4.0) - React-Core - MMKV (1.3.1): - MMKVCore (~> 1.3.1) @@ -386,36 +384,28 @@ PODS: - glog - react-native-biometrics (3.0.1): - React-Core - - react-native-blur (4.3.0): + - react-native-blur (4.3.2): - React-Core - - react-native-document-picker (8.2.0): + - react-native-flipper (0.212.0): - React-Core - - react-native-flipper (0.191.0): + - react-native-image-picker (7.0.2): - React-Core - - react-native-image-picker (5.6.0): + - react-native-keep-awake (1.2.2): - React-Core - - react-native-keep-awake (1.2.0): - - React-Core - - react-native-ldk (0.0.103): + - react-native-ldk (0.0.120): - React - - react-native-libsodium (0.7.0): - - React-Core - - react-native-mmkv (2.10.1): + - react-native-mmkv (2.10.2): - MMKV (>= 1.2.13) - React-Core - - react-native-netinfo (9.3.9): + - react-native-netinfo (10.0.0): - React-Core - react-native-randombytes (3.6.1): - React-Core - react-native-restart (0.0.27): - React-Core - - react-native-safe-area-context (4.5.1): - - RCT-Folly - - RCTRequired - - RCTTypeSafety + - react-native-safe-area-context (4.7.4): - React-Core - - ReactCommon/turbomodule/core - - react-native-skia (0.1.200): + - react-native-skia (0.1.216): - React - React-callinvoker - React-Core @@ -532,23 +522,26 @@ PODS: - React-jsi (= 0.72.4) - React-logger (= 0.72.4) - React-perflogger (= 0.72.4) - - ReactNativeCameraKit (14.0.0-beta11): + - ReactNativeCameraKit (14.0.0-beta13): - React-Core - - RNCClipboard (1.11.2): + - RNCAsyncStorage (1.19.4): - React-Core - - RNDeviceInfo (10.6.0): + - RNCClipboard (1.12.1): + - React-Core + - RNDeviceInfo (10.11.0): + - React-Core + - RNExitApp (2.0.0): - React-Core - - RNExitApp (1.1.0): - - React - RNFS (2.20.0): - React-Core - - RNGestureHandler (2.9.0): + - RNGestureHandler (2.13.4): + - RCT-Folly (= 2021.07.22.00) - React-Core - - RNKeychain (8.1.1): + - RNKeychain (8.1.2): - React-Core - - RNLocalize (2.2.6): + - RNLocalize (3.0.2): - React-Core - - RNPermissions (3.8.0): + - RNPermissions (3.10.1): - React-Core - RNQrGenerator (1.3.1): - React @@ -557,9 +550,9 @@ PODS: - React - RNRate (1.2.12): - React-Core - - RNReactNativeHapticFeedback (1.14.0): + - RNReactNativeHapticFeedback (2.2.0): - React-Core - - RNReanimated (3.4.2): + - RNReanimated (3.5.4): - DoubleConversion - FBLazyVector - glog @@ -588,18 +581,18 @@ PODS: - React-RCTText - ReactCommon/turbomodule/core - Yoga - - RNScreens (3.24.0): + - RNScreens (3.27.0): + - RCT-Folly (= 2021.07.22.00) - React-Core - - React-RCTImage - - RNShare (8.2.2): + - RNShare (9.4.1): - React-Core - - RNSVG (12.5.1): + - RNSVG (13.14.0): - React-Core - - RNZipArchive (6.0.9): + - RNZipArchive (6.1.0): - React-Core - - RNZipArchive/Core (= 6.0.9) + - RNZipArchive/Core (= 6.1.0) - SSZipArchive (~> 2.2) - - RNZipArchive/Core (6.0.9): + - RNZipArchive/Core (6.1.0): - React-Core - SSZipArchive (~> 2.2) - SocketRocket (0.6.1) @@ -607,8 +600,6 @@ PODS: - TouchID (4.4.1): - React - Yoga (1.14.0) - - YogaKit (1.18.1): - - Yoga (~> 1.14) - ZXingObjC (3.6.9): - ZXingObjC/All (= 3.6.9) - ZXingObjC/All (3.6.9) @@ -618,26 +609,26 @@ DEPENDENCIES: - 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/React/FBReactNativeSpec`) - - Flipper (= 0.182.0) + - Flipper (= 0.203.0) - Flipper-Boost-iOSX (= 1.76.0.1.11) - Flipper-DoubleConversion (= 3.2.0.1) - Flipper-Fmt (= 7.1.7) - Flipper-Folly (= 2.6.10) - Flipper-Glog (= 0.5.0.5) - Flipper-PeerTalk (= 0.0.4) - - FlipperKit (= 0.182.0) - - FlipperKit/Core (= 0.182.0) - - FlipperKit/CppBridge (= 0.182.0) - - FlipperKit/FBCxxFollyDynamicConvert (= 0.182.0) - - FlipperKit/FBDefines (= 0.182.0) - - FlipperKit/FKPortForwarding (= 0.182.0) - - FlipperKit/FlipperKitHighlightOverlay (= 0.182.0) - - FlipperKit/FlipperKitLayoutPlugin (= 0.182.0) - - FlipperKit/FlipperKitLayoutTextSearchable (= 0.182.0) - - FlipperKit/FlipperKitNetworkPlugin (= 0.182.0) - - FlipperKit/FlipperKitReactPlugin (= 0.182.0) - - FlipperKit/FlipperKitUserDefaultsPlugin (= 0.182.0) - - FlipperKit/SKIOSNetworkPlugin (= 0.182.0) + - FlipperKit (= 0.203.0) + - FlipperKit/Core (= 0.203.0) + - FlipperKit/CppBridge (= 0.203.0) + - FlipperKit/FBCxxFollyDynamicConvert (= 0.203.0) + - FlipperKit/FBDefines (= 0.203.0) + - FlipperKit/FKPortForwarding (= 0.203.0) + - FlipperKit/FlipperKitHighlightOverlay (= 0.203.0) + - FlipperKit/FlipperKitLayoutPlugin (= 0.203.0) + - FlipperKit/FlipperKitLayoutTextSearchable (= 0.203.0) + - FlipperKit/FlipperKitNetworkPlugin (= 0.203.0) + - FlipperKit/FlipperKitReactPlugin (= 0.203.0) + - FlipperKit/FlipperKitUserDefaultsPlugin (= 0.203.0) + - FlipperKit/SKIOSNetworkPlugin (= 0.203.0) - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`) - hermes-engine (from `../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`) - libevent (~> 2.1.12) @@ -663,12 +654,10 @@ DEPENDENCIES: - React-logger (from `../node_modules/react-native/ReactCommon/logger`) - react-native-biometrics (from `../node_modules/react-native-biometrics`) - "react-native-blur (from `../node_modules/@react-native-community/blur`)" - - react-native-document-picker (from `../node_modules/react-native-document-picker`) - react-native-flipper (from `../node_modules/react-native-flipper`) - react-native-image-picker (from `../node_modules/react-native-image-picker`) - "react-native-keep-awake (from `../node_modules/@sayem314/react-native-keep-awake`)" - "react-native-ldk (from `../node_modules/@synonymdev/react-native-ldk`)" - - react-native-libsodium (from `../node_modules/react-native-libsodium`) - react-native-mmkv (from `../node_modules/react-native-mmkv`) - "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)" - react-native-randombytes (from `../node_modules/react-native-randombytes`) @@ -694,6 +683,7 @@ DEPENDENCIES: - React-utils (from `../node_modules/react-native/ReactCommon/react/utils`) - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`) - ReactNativeCameraKit (from `../node_modules/react-native-camera-kit`) + - "RNCAsyncStorage (from `../node_modules/@react-native-async-storage/async-storage`)" - "RNCClipboard (from `../node_modules/@react-native-clipboard/clipboard`)" - RNDeviceInfo (from `../node_modules/react-native-device-info`) - RNExitApp (from `../node_modules/react-native-exit-app`) @@ -733,7 +723,6 @@ SPEC REPOS: - OpenSSL-Universal - SocketRocket - SSZipArchive - - YogaKit - ZXingObjC EXTERNAL SOURCES: @@ -788,8 +777,6 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-biometrics" react-native-blur: :path: "../node_modules/@react-native-community/blur" - react-native-document-picker: - :path: "../node_modules/react-native-document-picker" react-native-flipper: :path: "../node_modules/react-native-flipper" react-native-image-picker: @@ -798,8 +785,6 @@ EXTERNAL SOURCES: :path: "../node_modules/@sayem314/react-native-keep-awake" react-native-ldk: :path: "../node_modules/@synonymdev/react-native-ldk" - react-native-libsodium: - :path: "../node_modules/react-native-libsodium" react-native-mmkv: :path: "../node_modules/react-native-mmkv" react-native-netinfo: @@ -850,6 +835,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon" ReactNativeCameraKit: :path: "../node_modules/react-native-camera-kit" + RNCAsyncStorage: + :path: "../node_modules/@react-native-async-storage/async-storage" RNCClipboard: :path: "../node_modules/@react-native-clipboard/clipboard" RNDeviceInfo: @@ -895,20 +882,20 @@ SPEC CHECKSUMS: DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54 FBLazyVector: 5d4a3b7f411219a45a6d952f77d2c0a6c9989da5 FBReactNativeSpec: 3fc2d478e1c4b08276f9dd9128f80ec6d5d85c1f - Flipper: 6edb735e6c3e332975d1b17956bcc584eccf5818 + Flipper: c70899db07015da7ec9025f29463a997a0184d01 Flipper-Boost-iOSX: fd1e2b8cbef7e662a122412d7ac5f5bea715403c Flipper-DoubleConversion: 2dc99b02f658daf147069aad9dbd29d8feb06d30 Flipper-Fmt: 60cbdd92fc254826e61d669a5d87ef7015396a9b Flipper-Folly: 584845625005ff068a6ebf41f857f468decd26b3 Flipper-Glog: 70c50ce58ddaf67dc35180db05f191692570f446 Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9 - FlipperKit: 2efad7007d6745a3f95e4034d547be637f89d3f6 + FlipperKit: 2bd3b69628fedcbdc66766d0dac0a8abd2de5f8f fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9 glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b hermes-engine: 81191603c4eaa01f5e4ae5737a9efcf64756c7b2 libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 - lottie-ios: 809ecf2d460ed650a6aed7aa88b2ec45fab4779c - lottie-react-native: 3d6f1b0db1fd9e18b92af1fbfbf67037611bae67 + lottie-ios: 8f97d3271e155c2d688875c29cd3c74908aef5f8 + lottie-react-native: 8f9d4be452e23f6e5ca0fdc11669dc99ab52be81 MMKV: 5a07930c70c70b86cd87761a42c8f3836fb681d7 MMKVCore: e50135dbd33235b6ab390635991bab437ab873c0 nodejs-mobile-react-native: e35e7ed7ecfca168f168983e9557f1c5278d864b @@ -929,19 +916,17 @@ SPEC CHECKSUMS: React-jsinspector: aaed4cf551c4a1c98092436518c2d267b13a673f React-logger: da1ebe05ae06eb6db4b162202faeafac4b435e77 react-native-biometrics: 352e5a794bfffc46a0c86725ea7dc62deb085bdc - react-native-blur: 50c9feabacbc5f49b61337ebc32192c6be7ec3c3 - react-native-document-picker: 495c444c0c773c6e83a5d91165890ecb1c0a399a - react-native-flipper: 5d8dcbcb905a7e8076c9a61a3709944c23cf48ee - react-native-image-picker: db60857e03d63721f19b6f4027de20429ddd9cba - react-native-keep-awake: caee3ff89eaa21dfe29010f0d143566874a04441 - react-native-ldk: e31bd28e7e29fb4dd99b86bceed14025f8bf53eb - react-native-libsodium: 2834a805c906aef4b7609019bcc87e7d9eb86b23 - react-native-mmkv: dea675cf9697ad35940f1687e98e133e1358ef9f - react-native-netinfo: 22c082970cbd99071a4e5aa7a612ac20d66b08f0 + react-native-blur: cfdad7b3c01d725ab62a8a729f42ea463998afa2 + react-native-flipper: 9c1957af24b76493ba74f46d000a5c1d485e7731 + react-native-image-picker: 2e2e82aba9b6a91a7c78f7d9afde341a2659c7b8 + react-native-keep-awake: ad1d67f617756b139536977a0bf06b27cec0714a + react-native-ldk: fc83520c891e58888c8f975a02ed394a4c4e1c36 + react-native-mmkv: 9ae7ca3977e8ef48dbf7f066974eb844c20b5fd7 + react-native-netinfo: b16b9df4de3ae252984ca7a18097a3d30d27ba21 react-native-randombytes: 421f1c7d48c0af8dbcd471b0324393ebf8fe7846 react-native-restart: 7595693413fe3ca15893702f2c8306c62a708162 - react-native-safe-area-context: f5549f36508b1b7497434baa0cd97d7e470920d4 - react-native-skia: d0b0aab6bb1f146eb6f379fb671b719deabd20fb + react-native-safe-area-context: 2cd91d532de12acdb0a9cbc8d43ac72a8e4c897c + react-native-skia: f4d52c69fd3534143365287d03db35270d7b09b7 react-native-tcp-socket: c1b7297619616b4c9caae6889bcb0aba78086989 React-NativeModulesApple: edb5ace14f73f4969df6e7b1f3e41bef0012740f React-perflogger: 496a1a3dc6737f964107cb3ddae7f9e265ddda58 @@ -960,31 +945,31 @@ SPEC CHECKSUMS: React-runtimescheduler: 4941cc1b3cf08b792fbf666342c9fc95f1969035 React-utils: b79f2411931f9d3ea5781404dcbb2fa8a837e13a ReactCommon: 4b2bdcb50a3543e1c2b2849ad44533686610826d - ReactNativeCameraKit: 5c9b3d2366199b6d6d4a1a72c13d78fff7296332 - RNCClipboard: 3f0451a8100393908bea5c5c5b16f96d45f30bfc - RNDeviceInfo: 475a4c447168d0ad4c807e48ef5e0963a0f4eb1b - RNExitApp: c4e052df2568b43bec8a37c7cd61194d4cfee2c3 + ReactNativeCameraKit: d95d3e19c514526a234d9f93c6db7e7f10eef9ea + RNCAsyncStorage: 3a8f7145d17cdd9f88e7b77666c94a09e4f759c8 + RNCClipboard: d77213bfa269013bf4b857b7a9ca37ee062d8ef1 + RNDeviceInfo: bf8a32acbcb875f568217285d1793b0e8588c974 + RNExitApp: 00036cabe7bacbb413d276d5520bf74ba39afa6a RNFS: 4ac0f0ea233904cb798630b3c077808c06931688 - RNGestureHandler: 071d7a9ad81e8b83fe7663b303d132406a7d8f39 - RNKeychain: ff836453cba46938e0e9e4c22e43d43fa2c90333 - RNLocalize: d4b8af4e442d4bcca54e68fc687a2129b4d71a81 - RNPermissions: c5eafcb7dc107815baf0f4c795410c653eb9635a + RNGestureHandler: 6e46dde1f87e5f018a54fe5d40cd0e0b942b49ee + RNKeychain: a65256b6ca6ba6976132cc4124b238a5b13b3d9c + RNLocalize: dbea38dcb344bf80ff18a1757b1becf11f70cae4 + RNPermissions: 2c0eec471f4de66d04d226c339898d10a6e123c4 RNQrGenerator: 90461ba3ca88c1d38ef73da50fade35d9648215d RNQuickAction: 6d404a869dc872cde841ad3147416a670d13fa93 RNRate: ef3bcff84f39bb1d1e41c5593d3eea4aab2bd73a - RNReactNativeHapticFeedback: 1e3efeca9628ff9876ee7cdd9edec1b336913f8c - RNReanimated: 726395a2fa2f04cea340274ba57a4e659bc0d9c1 - RNScreens: b21dc57dfa2b710c30ec600786a3fc223b1b92e7 - RNShare: d82e10f6b7677f4b0048c23709bd04098d5aee6c - RNSVG: d7d7bc8229af3842c9cfc3a723c815a52cdd1105 - RNZipArchive: 68a0c6db4b1c103f846f1559622050df254a3ade + RNReactNativeHapticFeedback: ec56a5f81c3941206fd85625fa669ffc7b4545f9 + RNReanimated: ab2e96c6d5591c3dfbb38a464f54c8d17fb34a87 + RNScreens: 3c2d122f5e08c192e254c510b212306da97d2581 + RNShare: 32e97adc8d8c97d4a26bcdd3c45516882184f8b6 + RNSVG: d00c8f91c3cbf6d476451313a18f04d220d4f396 + RNZipArchive: ef9451b849c45a29509bf44e65b788829ab07801 SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17 SSZipArchive: fe6a26b2a54d5a0890f2567b5cc6de5caa600aef TouchID: ba4c656d849cceabc2e4eef722dea5e55959ecf4 Yoga: 3efc43e0d48686ce2e8c60f99d4e6bd349aff981 - YogaKit: f782866e155069a2cca2517aafea43200b01fd5a ZXingObjC: 8898711ab495761b2dbbdec76d90164a6d7e14c5 -PODFILE CHECKSUM: cf6332fd1adc6afe5176032f8ddfb72684376530 +PODFILE CHECKSUM: b01d3271ab99feb247ef10569ea8ace3cf7254ec COCOAPODS: 1.12.1 diff --git a/ios/bitkit.xcodeproj/project.pbxproj b/ios/bitkit.xcodeproj/project.pbxproj index e44c4a8a7..f4e60ee07 100644 --- a/ios/bitkit.xcodeproj/project.pbxproj +++ b/ios/bitkit.xcodeproj/project.pbxproj @@ -11,13 +11,13 @@ 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; - 5F503AADACF24608F5918DBC /* libPods-bitkit-bitkitTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B53683A434E450032E4871B1 /* libPods-bitkit-bitkitTests.a */; }; + 1D31B4ECA6A9499631F51C66 /* libPods-bitkit-bitkitTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CCD44B1F8F13B6653BBFE4F8 /* libPods-bitkit-bitkitTests.a */; }; + 3905CE2A043D4091D9F2B0F8 /* libPods-bitkit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D921B161B0BAC3FC92D18D99 /* libPods-bitkit.a */; }; 777F5BE129EDEB75005E0E4B /* InterTight-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 777F5BDD29EDEB75005E0E4B /* InterTight-Bold.ttf */; }; 777F5BE229EDEB75005E0E4B /* InterTight-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 777F5BDE29EDEB75005E0E4B /* InterTight-Regular.ttf */; }; 777F5BE329EDEB75005E0E4B /* InterTight-SemiBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 777F5BDF29EDEB75005E0E4B /* InterTight-SemiBold.ttf */; }; 777F5BE429EDEB75005E0E4B /* InterTight-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 777F5BE029EDEB75005E0E4B /* InterTight-Medium.ttf */; }; 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; - A6A7D18CCA855DF8DCFDCD5E /* libPods-bitkit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 18C32352660895B32F76F551 /* libPods-bitkit.a */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -31,6 +31,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 00303E763958FA7DBF3E7C36 /* Pods-bitkit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-bitkit.debug.xcconfig"; path = "Target Support Files/Pods-bitkit/Pods-bitkit.debug.xcconfig"; sourceTree = ""; }; 00E356EE1AD99517003FC87E /* bitkitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = bitkitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 00E356F21AD99517003FC87E /* bitkitTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = bitkitTests.m; sourceTree = ""; }; @@ -40,18 +41,17 @@ 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = bitkit/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = bitkit/Info.plist; sourceTree = ""; }; 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = bitkit/main.m; sourceTree = ""; }; - 18C32352660895B32F76F551 /* libPods-bitkit.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-bitkit.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 3A2A790D063C00580DACF5E0 /* Pods-bitkit-bitkitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-bitkit-bitkitTests.release.xcconfig"; path = "Target Support Files/Pods-bitkit-bitkitTests/Pods-bitkit-bitkitTests.release.xcconfig"; sourceTree = ""; }; - 444EAABECD73CC86C582C108 /* Pods-bitkit-bitkitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-bitkit-bitkitTests.debug.xcconfig"; path = "Target Support Files/Pods-bitkit-bitkitTests/Pods-bitkit-bitkitTests.debug.xcconfig"; sourceTree = ""; }; + 15D1684D2578F0264F7B5934 /* Pods-bitkit-bitkitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-bitkit-bitkitTests.debug.xcconfig"; path = "Target Support Files/Pods-bitkit-bitkitTests/Pods-bitkit-bitkitTests.debug.xcconfig"; sourceTree = ""; }; + 15D1FA07DC889152B70FF40F /* Pods-bitkit-bitkitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-bitkit-bitkitTests.release.xcconfig"; path = "Target Support Files/Pods-bitkit-bitkitTests/Pods-bitkit-bitkitTests.release.xcconfig"; sourceTree = ""; }; + 58FF2C827EF32BB38C94041B /* Pods-bitkit.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-bitkit.release.xcconfig"; path = "Target Support Files/Pods-bitkit/Pods-bitkit.release.xcconfig"; sourceTree = ""; }; 777F5BDD29EDEB75005E0E4B /* InterTight-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "InterTight-Bold.ttf"; sourceTree = ""; }; 777F5BDE29EDEB75005E0E4B /* InterTight-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "InterTight-Regular.ttf"; sourceTree = ""; }; 777F5BDF29EDEB75005E0E4B /* InterTight-SemiBold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "InterTight-SemiBold.ttf"; sourceTree = ""; }; 777F5BE029EDEB75005E0E4B /* InterTight-Medium.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "InterTight-Medium.ttf"; sourceTree = ""; }; 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = bitkit/LaunchScreen.storyboard; sourceTree = ""; }; - B53683A434E450032E4871B1 /* libPods-bitkit-bitkitTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-bitkit-bitkitTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - D13A1262BE3ECB2581FC2EDC /* Pods-bitkit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-bitkit.debug.xcconfig"; path = "Target Support Files/Pods-bitkit/Pods-bitkit.debug.xcconfig"; sourceTree = ""; }; + CCD44B1F8F13B6653BBFE4F8 /* libPods-bitkit-bitkitTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-bitkit-bitkitTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + D921B161B0BAC3FC92D18D99 /* libPods-bitkit.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-bitkit.a"; sourceTree = BUILT_PRODUCTS_DIR; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; - F615571D2777FBACDAED24E2 /* Pods-bitkit.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-bitkit.release.xcconfig"; path = "Target Support Files/Pods-bitkit/Pods-bitkit.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -59,7 +59,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 5F503AADACF24608F5918DBC /* libPods-bitkit-bitkitTests.a in Frameworks */, + 1D31B4ECA6A9499631F51C66 /* libPods-bitkit-bitkitTests.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -67,7 +67,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - A6A7D18CCA855DF8DCFDCD5E /* libPods-bitkit.a in Frameworks */, + 3905CE2A043D4091D9F2B0F8 /* libPods-bitkit.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -108,8 +108,8 @@ isa = PBXGroup; children = ( ED297162215061F000B7C4FE /* JavaScriptCore.framework */, - 18C32352660895B32F76F551 /* libPods-bitkit.a */, - B53683A434E450032E4871B1 /* libPods-bitkit-bitkitTests.a */, + D921B161B0BAC3FC92D18D99 /* libPods-bitkit.a */, + CCD44B1F8F13B6653BBFE4F8 /* libPods-bitkit-bitkitTests.a */, ); name = Frameworks; sourceTree = ""; @@ -161,10 +161,10 @@ BBD78D7AC51CEA395F1C20DB /* Pods */ = { isa = PBXGroup; children = ( - D13A1262BE3ECB2581FC2EDC /* Pods-bitkit.debug.xcconfig */, - F615571D2777FBACDAED24E2 /* Pods-bitkit.release.xcconfig */, - 444EAABECD73CC86C582C108 /* Pods-bitkit-bitkitTests.debug.xcconfig */, - 3A2A790D063C00580DACF5E0 /* Pods-bitkit-bitkitTests.release.xcconfig */, + 00303E763958FA7DBF3E7C36 /* Pods-bitkit.debug.xcconfig */, + 58FF2C827EF32BB38C94041B /* Pods-bitkit.release.xcconfig */, + 15D1684D2578F0264F7B5934 /* Pods-bitkit-bitkitTests.debug.xcconfig */, + 15D1FA07DC889152B70FF40F /* Pods-bitkit-bitkitTests.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -176,12 +176,12 @@ isa = PBXNativeTarget; buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "bitkitTests" */; buildPhases = ( - 4F668DDA4EDFD6DF63FA3B0A /* [CP] Check Pods Manifest.lock */, + 49961A17683E4BF957CF9144 /* [CP] Check Pods Manifest.lock */, 00E356EA1AD99517003FC87E /* Sources */, 00E356EB1AD99517003FC87E /* Frameworks */, 00E356EC1AD99517003FC87E /* Resources */, - 64383155207573885C83E29B /* [CP] Embed Pods Frameworks */, - 0F88CC10F942A17639D1DE30 /* [CP] Copy Pods Resources */, + C0692D29AC5588826905D8D9 /* [CP] Embed Pods Frameworks */, + 997A687F32B69C8849C5C048 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -197,18 +197,18 @@ isa = PBXNativeTarget; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "bitkit" */; buildPhases = ( - 7423DCEEFDC3099766E2D590 /* [CP] Check Pods Manifest.lock */, + 23A125A5835F81CB48C5DFF8 /* [CP] Check Pods Manifest.lock */, FD10A7F022414F080027D42C /* Start Packager */, 13B07F871A680F5B00A75B9A /* Sources */, 13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8E1A680F5B00A75B9A /* Resources */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, - 858A1B0A99257412E7E49F95 /* [CP] Embed Pods Frameworks */, - B1532D37C5F5312586AA8B59 /* [CP] Copy Pods Resources */, - D53FEE3A6B78AC158AEEDE16 /* [CP-User] [NODEJS MOBILE] Copy Node.js Project files */, - E62D6EB5874E6BC8745C22E1 /* [CP-User] [NODEJS MOBILE] Build Native Modules */, - 0ED46121177E47B0E2C921D5 /* [CP-User] [NODEJS MOBILE] Sign Native Modules */, - 32DD0831F26A205A155B06CA /* [CP-User] [NODEJS MOBILE] Remove Simulator Strip */, + D914C711382F045CC20BB344 /* [CP] Embed Pods Frameworks */, + 3582A9C50D55B4C466E85115 /* [CP] Copy Pods Resources */, + 6E9639A6F85D10A0470D936E /* [CP-User] [NODEJS MOBILE] Copy Node.js Project files */, + 8F6712CFBBBE7B9FC873B9FB /* [CP-User] [NODEJS MOBILE] Build Native Modules */, + 854D8CEF6F1DFCEF4900A849 /* [CP-User] [NODEJS MOBILE] Sign Native Modules */, + 54B55F36CB435E4978065887 /* [CP-User] [NODEJS MOBILE] Remove Simulator Strip */, ); buildRules = ( ); @@ -295,44 +295,46 @@ shellPath = /bin/sh; shellScript = "set -e\n\nWITH_ENVIRONMENT=\"../node_modules/react-native/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"../node_modules/react-native/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n"; }; - 0ED46121177E47B0E2C921D5 /* [CP-User] [NODEJS MOBILE] Sign Native Modules */ = { + 23A125A5835F81CB48C5DFF8 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); - name = "[CP-User] [NODEJS MOBILE] Sign Native Modules"; + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-bitkit-checkManifestLockResult.txt", + ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "#!/bin/sh\nset -e\nif [ -z \"$NODEJS_MOBILE_BUILD_NATIVE_MODULES\" ]; then\n# If build native modules preference is not set, look for it in the project's\n#nodejs-assets/BUILD_NATIVE_MODULES.txt file.\nNODEJS_ASSETS_DIR=\"$( cd \"$PROJECT_DIR\" && cd ../nodejs-assets/ && pwd )\"\nPREFERENCE_FILE_PATH=\"$NODEJS_ASSETS_DIR/BUILD_NATIVE_MODULES.txt\"\n if [ -f \"$PREFERENCE_FILE_PATH\" ]; then\n NODEJS_MOBILE_BUILD_NATIVE_MODULES=\"$(cat $PREFERENCE_FILE_PATH | xargs)\"\n fi\nfi\nif [ -z \"$NODEJS_MOBILE_BUILD_NATIVE_MODULES\" ]; then\n# If build native modules preference is not set, try to find .gyp files\n#to turn it on.\n gypfiles=($(find \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -type f -name \"*.gyp\"))\n if [ ${#gypfiles[@]} -gt 0 ]; then\n NODEJS_MOBILE_BUILD_NATIVE_MODULES=1\n else\n NODEJS_MOBILE_BUILD_NATIVE_MODULES=0\n fi\nfi\nif [ \"1\" != \"$NODEJS_MOBILE_BUILD_NATIVE_MODULES\" ]; then exit 0; fi\n# Delete object files\nfind \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -name \"*.o\" -type f -delete\nfind \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -name \"*.a\" -type f -delete\n# Create Info.plist for each framework built and loader override.\nPATCH_SCRIPT_DIR=\"$( cd \"$PROJECT_DIR\" && cd ../node_modules/nodejs-mobile-react-native/scripts/ && pwd )\"\nNODEJS_PROJECT_DIR=\"$( cd \"$CODESIGNING_FOLDER_PATH\" && cd nodejs-project/ && pwd )\"\nnode \"$PATCH_SCRIPT_DIR\"/ios-create-plists-and-dlopen-override.js $NODEJS_PROJECT_DIR\n# Embed every resulting .framework in the application and delete them afterwards.\nembed_framework()\n{\n FRAMEWORK_NAME=\"$(basename \"$1\")\"\n cp -r \"$1\" \"$TARGET_BUILD_DIR/$FRAMEWORKS_FOLDER_PATH/\"\n /usr/bin/codesign --force --sign $EXPANDED_CODE_SIGN_IDENTITY --preserve-metadata=identifier,entitlements,flags --timestamp=none \"$TARGET_BUILD_DIR/$FRAMEWORKS_FOLDER_PATH/$FRAMEWORK_NAME\"\n}\nfind \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -name \"*.framework\" -type d | while read frmwrk_path; do embed_framework \"$frmwrk_path\"; done\n\n#Delete gyp temporary .deps dependency folders from the project structure.\nfind \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -path \"*/.deps/*\" -delete\nfind \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -name \".deps\" -type d -delete\n\n#Delete frameworks from their build paths\nfind \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -path \"*/*.framework/*\" -delete\nfind \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -name \"*.framework\" -type d -delete\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; }; - 0F88CC10F942A17639D1DE30 /* [CP] Copy Pods Resources */ = { + 3582A9C50D55B4C466E85115 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-bitkit-bitkitTests/Pods-bitkit-bitkitTests-resources-${CONFIGURATION}-input-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-bitkit/Pods-bitkit-resources-${CONFIGURATION}-input-files.xcfilelist", ); name = "[CP] Copy Pods Resources"; outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-bitkit-bitkitTests/Pods-bitkit-bitkitTests-resources-${CONFIGURATION}-output-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-bitkit/Pods-bitkit-resources-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-bitkit-bitkitTests/Pods-bitkit-bitkitTests-resources.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-bitkit/Pods-bitkit-resources.sh\"\n"; showEnvVarsInLog = 0; }; - 32DD0831F26A205A155B06CA /* [CP-User] [NODEJS MOBILE] Remove Simulator Strip */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - name = "[CP-User] [NODEJS MOBILE] Remove Simulator Strip"; - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "#!/bin/sh\nset -e\nFRAMEWORK_BINARY_PATH=\"$TARGET_BUILD_DIR/$FRAMEWORKS_FOLDER_PATH/NodeMobile.framework/NodeMobile\"\nFRAMEWORK_STRIPPED_PATH=\"$FRAMEWORK_BINARY_PATH-strip\"\nif [ \"$PLATFORM_NAME\" != \"iphonesimulator\" ]; then\n if $(lipo \"$FRAMEWORK_BINARY_PATH\" -verify_arch \"x86_64\") ; then\n lipo -output \"$FRAMEWORK_STRIPPED_PATH\" -remove \"x86_64\" \"$FRAMEWORK_BINARY_PATH\"\n rm \"$FRAMEWORK_BINARY_PATH\"\n mv \"$FRAMEWORK_STRIPPED_PATH\" \"$FRAMEWORK_BINARY_PATH\"\n echo \"Removed simulator strip from NodeMobile.framework\"\n fi\nfi\n"; - }; - 4F668DDA4EDFD6DF63FA3B0A /* [CP] Check Pods Manifest.lock */ = { + 49961A17683E4BF957CF9144 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -354,98 +356,96 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 64383155207573885C83E29B /* [CP] Embed Pods Frameworks */ = { + 54B55F36CB435E4978065887 /* [CP-User] [NODEJS MOBILE] Remove Simulator Strip */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-bitkit-bitkitTests/Pods-bitkit-bitkitTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-bitkit-bitkitTests/Pods-bitkit-bitkitTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); + name = "[CP-User] [NODEJS MOBILE] Remove Simulator Strip"; runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-bitkit-bitkitTests/Pods-bitkit-bitkitTests-frameworks.sh\"\n"; - showEnvVarsInLog = 0; + shellScript = "#!/bin/sh\nset -e\nFRAMEWORK_BINARY_PATH=\"$TARGET_BUILD_DIR/$FRAMEWORKS_FOLDER_PATH/NodeMobile.framework/NodeMobile\"\nFRAMEWORK_STRIPPED_PATH=\"$FRAMEWORK_BINARY_PATH-strip\"\nif [ \"$PLATFORM_NAME\" != \"iphonesimulator\" ]; then\n if $(lipo \"$FRAMEWORK_BINARY_PATH\" -verify_arch \"x86_64\") ; then\n lipo -output \"$FRAMEWORK_STRIPPED_PATH\" -remove \"x86_64\" \"$FRAMEWORK_BINARY_PATH\"\n rm \"$FRAMEWORK_BINARY_PATH\"\n mv \"$FRAMEWORK_STRIPPED_PATH\" \"$FRAMEWORK_BINARY_PATH\"\n echo \"Removed simulator strip from NodeMobile.framework\"\n fi\nfi\n"; }; - 7423DCEEFDC3099766E2D590 /* [CP] Check Pods Manifest.lock */ = { + 6E9639A6F85D10A0470D936E /* [CP-User] [NODEJS MOBILE] Copy Node.js Project files */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-bitkit-checkManifestLockResult.txt", - ); + name = "[CP-User] [NODEJS MOBILE] Copy Node.js Project files"; runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; + shellScript = "#!/bin/sh\nset -e\nNODEJS_ASSETS_DIR=\"$( cd \"$PROJECT_DIR\" && cd ../nodejs-assets/ && pwd )\"\nNODEJS_BUILT_IN_MODULES_DIR=\"$( cd \"$PROJECT_DIR\" && cd ../node_modules/nodejs-mobile-react-native/install/resources/nodejs-modules/ && pwd )\"\nif [ -d \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" ]\nthen\nrm -rf \"$CODESIGNING_FOLDER_PATH/nodejs-project/\"\nfi\nif [ -d \"$CODESIGNING_FOLDER_PATH/builtin_modules/\" ]\nthen\nrm -rf \"$CODESIGNING_FOLDER_PATH/builtin_modules/\"\nfi\nrsync -av --delete \"$NODEJS_ASSETS_DIR/nodejs-project\" \"$CODESIGNING_FOLDER_PATH\"\nrsync -av --delete \"$NODEJS_BUILT_IN_MODULES_DIR/builtin_modules\" \"$CODESIGNING_FOLDER_PATH\"\n"; }; - 858A1B0A99257412E7E49F95 /* [CP] Embed Pods Frameworks */ = { + 854D8CEF6F1DFCEF4900A849 /* [CP-User] [NODEJS MOBILE] Sign Native Modules */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-bitkit/Pods-bitkit-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-bitkit/Pods-bitkit-frameworks-${CONFIGURATION}-output-files.xcfilelist", + name = "[CP-User] [NODEJS MOBILE] Sign Native Modules"; + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "#!/bin/sh\nset -e\nif [ -z \"$NODEJS_MOBILE_BUILD_NATIVE_MODULES\" ]; then\n# If build native modules preference is not set, look for it in the project's\n#nodejs-assets/BUILD_NATIVE_MODULES.txt file.\nNODEJS_ASSETS_DIR=\"$( cd \"$PROJECT_DIR\" && cd ../nodejs-assets/ && pwd )\"\nPREFERENCE_FILE_PATH=\"$NODEJS_ASSETS_DIR/BUILD_NATIVE_MODULES.txt\"\n if [ -f \"$PREFERENCE_FILE_PATH\" ]; then\n NODEJS_MOBILE_BUILD_NATIVE_MODULES=\"$(cat $PREFERENCE_FILE_PATH | xargs)\"\n fi\nfi\nif [ -z \"$NODEJS_MOBILE_BUILD_NATIVE_MODULES\" ]; then\n# If build native modules preference is not set, try to find .gyp files\n#to turn it on.\n gypfiles=($(find \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -type f -name \"*.gyp\"))\n if [ ${#gypfiles[@]} -gt 0 ]; then\n NODEJS_MOBILE_BUILD_NATIVE_MODULES=1\n else\n NODEJS_MOBILE_BUILD_NATIVE_MODULES=0\n fi\nfi\nif [ \"1\" != \"$NODEJS_MOBILE_BUILD_NATIVE_MODULES\" ]; then exit 0; fi\n# Delete object files\nfind \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -name \"*.o\" -type f -delete\nfind \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -name \"*.a\" -type f -delete\n# Create Info.plist for each framework built and loader override.\nPATCH_SCRIPT_DIR=\"$( cd \"$PROJECT_DIR\" && cd ../node_modules/nodejs-mobile-react-native/scripts/ && pwd )\"\nNODEJS_PROJECT_DIR=\"$( cd \"$CODESIGNING_FOLDER_PATH\" && cd nodejs-project/ && pwd )\"\nnode \"$PATCH_SCRIPT_DIR\"/ios-create-plists-and-dlopen-override.js $NODEJS_PROJECT_DIR\n# Embed every resulting .framework in the application and delete them afterwards.\nembed_framework()\n{\n FRAMEWORK_NAME=\"$(basename \"$1\")\"\n cp -r \"$1\" \"$TARGET_BUILD_DIR/$FRAMEWORKS_FOLDER_PATH/\"\n /usr/bin/codesign --force --sign $EXPANDED_CODE_SIGN_IDENTITY --preserve-metadata=identifier,entitlements,flags --timestamp=none \"$TARGET_BUILD_DIR/$FRAMEWORKS_FOLDER_PATH/$FRAMEWORK_NAME\"\n}\nfind \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -name \"*.framework\" -type d | while read frmwrk_path; do embed_framework \"$frmwrk_path\"; done\n\n#Delete gyp temporary .deps dependency folders from the project structure.\nfind \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -path \"*/.deps/*\" -delete\nfind \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -name \".deps\" -type d -delete\n\n#Delete frameworks from their build paths\nfind \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -path \"*/*.framework/*\" -delete\nfind \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -name \"*.framework\" -type d -delete\n"; + }; + 8F6712CFBBBE7B9FC873B9FB /* [CP-User] [NODEJS MOBILE] Build Native Modules */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( ); + name = "[CP-User] [NODEJS MOBILE] Build Native Modules"; runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-bitkit/Pods-bitkit-frameworks.sh\"\n"; - showEnvVarsInLog = 0; + shellScript = "#!/bin/sh\nset -e\nif [ -z \"$NODEJS_MOBILE_BUILD_NATIVE_MODULES\" ]; then\n# If build native modules preference is not set, look for it in the project's\n#nodejs-assets/BUILD_NATIVE_MODULES.txt file.\nNODEJS_ASSETS_DIR=\"$( cd \"$PROJECT_DIR\" && cd ../nodejs-assets/ && pwd )\"\nPREFERENCE_FILE_PATH=\"$NODEJS_ASSETS_DIR/BUILD_NATIVE_MODULES.txt\"\n if [ -f \"$PREFERENCE_FILE_PATH\" ]; then\n NODEJS_MOBILE_BUILD_NATIVE_MODULES=\"$(cat $PREFERENCE_FILE_PATH | xargs)\"\n fi\nfi\nif [ -z \"$NODEJS_MOBILE_BUILD_NATIVE_MODULES\" ]; then\n# If build native modules preference is not set, try to find .gyp files\n#to turn it on.\n gypfiles=($(find \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -type f -name \"*.gyp\"))\n if [ ${#gypfiles[@]} -gt 0 ]; then\n NODEJS_MOBILE_BUILD_NATIVE_MODULES=1\n else\n NODEJS_MOBILE_BUILD_NATIVE_MODULES=0\n fi\nfi\nif [ \"1\" != \"$NODEJS_MOBILE_BUILD_NATIVE_MODULES\" ]; then exit 0; fi\n# Delete object files that may already come from within the npm package.\nfind \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -name \"*.o\" -type f -delete\nfind \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -name \"*.a\" -type f -delete\nfind \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -name \"*.node\" -type f -delete\n# Delete bundle contents that may be there from previous builds.\nfind \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -path \"*/*.node/*\" -delete\nfind \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -name \"*.node\" -type d -delete\nfind \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -path \"*/*.framework/*\" -delete\nfind \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -name \"*.framework\" -type d -delete\n# Apply patches to the modules package.json\nif [ -d \"$CODESIGNING_FOLDER_PATH\"/nodejs-project/node_modules/ ]; then\n PATCH_SCRIPT_DIR=\"$( cd \"$PROJECT_DIR\" && cd ../node_modules/nodejs-mobile-react-native/scripts/ && pwd )\"\n NODEJS_PROJECT_MODULES_DIR=\"$( cd \"$CODESIGNING_FOLDER_PATH\" && cd nodejs-project/node_modules/ && pwd )\"\n node \"$PATCH_SCRIPT_DIR\"/patch-package.js $NODEJS_PROJECT_MODULES_DIR\nfi\n# Get the nodejs-mobile-gyp location\nif [ -d \"$PROJECT_DIR/../node_modules/nodejs-mobile-gyp/\" ]; then\n NODEJS_MOBILE_GYP_DIR=\"$( cd \"$PROJECT_DIR\" && cd ../node_modules/nodejs-mobile-gyp/ && pwd )\"\nelse\n NODEJS_MOBILE_GYP_DIR=\"$( cd \"$PROJECT_DIR\" && cd ../node_modules/nodejs-mobile-react-native/node_modules/nodejs-mobile-gyp/ && pwd )\"\nfi\nNODEJS_MOBILE_GYP_BIN_FILE=\"$NODEJS_MOBILE_GYP_DIR\"/bin/node-gyp.js\n# Support building neon-bindings (Rust) native modules\nif [ -f ~/.cargo/env ]; then\n source ~/.cargo/env;\nfi\n# Rebuild modules with right environment\nNODEJS_HEADERS_DIR=\"$( cd \"$PROJECT_DIR\" && cd ../node_modules/nodejs-mobile-react-native/ios/libnode/ && pwd )\"\npushd $CODESIGNING_FOLDER_PATH/nodejs-project/\nif [ \"$PLATFORM_NAME\" == \"iphoneos\" ]\nthen\n GYP_DEFINES=\"OS=ios\" CARGO_BUILD_TARGET=\"aarch64-apple-ios\" npm_config_nodedir=\"$NODEJS_HEADERS_DIR\" npm_config_node_gyp=\"$NODEJS_MOBILE_GYP_BIN_FILE\" npm_config_platform=\"ios\" npm_config_format=\"make-ios\" npm_config_node_engine=\"chakracore\" npm_config_arch=\"arm64\" npm --verbose rebuild --build-from-source\nelse\n GYP_DEFINES=\"OS=ios\" CARGO_BUILD_TARGET=\"x86_64-apple-ios\" npm_config_nodedir=\"$NODEJS_HEADERS_DIR\" npm_config_node_gyp=\"$NODEJS_MOBILE_GYP_BIN_FILE\" npm_config_platform=\"ios\" npm_config_format=\"make-ios\" npm_config_node_engine=\"chakracore\" npm_config_arch=\"x64\" npm --verbose rebuild --build-from-source\nfi\npopd\n"; }; - B1532D37C5F5312586AA8B59 /* [CP] Copy Pods Resources */ = { + 997A687F32B69C8849C5C048 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-bitkit/Pods-bitkit-resources-${CONFIGURATION}-input-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-bitkit-bitkitTests/Pods-bitkit-bitkitTests-resources-${CONFIGURATION}-input-files.xcfilelist", ); name = "[CP] Copy Pods Resources"; outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-bitkit/Pods-bitkit-resources-${CONFIGURATION}-output-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-bitkit-bitkitTests/Pods-bitkit-bitkitTests-resources-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-bitkit/Pods-bitkit-resources.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-bitkit-bitkitTests/Pods-bitkit-bitkitTests-resources.sh\"\n"; showEnvVarsInLog = 0; }; - D53FEE3A6B78AC158AEEDE16 /* [CP-User] [NODEJS MOBILE] Copy Node.js Project files */ = { + C0692D29AC5588826905D8D9 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); - name = "[CP-User] [NODEJS MOBILE] Copy Node.js Project files"; + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-bitkit-bitkitTests/Pods-bitkit-bitkitTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-bitkit-bitkitTests/Pods-bitkit-bitkitTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "#!/bin/sh\nset -e\nNODEJS_ASSETS_DIR=\"$( cd \"$PROJECT_DIR\" && cd ../nodejs-assets/ && pwd )\"\nNODEJS_BUILT_IN_MODULES_DIR=\"$( cd \"$PROJECT_DIR\" && cd ../node_modules/nodejs-mobile-react-native/install/resources/nodejs-modules/ && pwd )\"\nif [ -d \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" ]\nthen\nrm -rf \"$CODESIGNING_FOLDER_PATH/nodejs-project/\"\nfi\nif [ -d \"$CODESIGNING_FOLDER_PATH/builtin_modules/\" ]\nthen\nrm -rf \"$CODESIGNING_FOLDER_PATH/builtin_modules/\"\nfi\nrsync -av --delete \"$NODEJS_ASSETS_DIR/nodejs-project\" \"$CODESIGNING_FOLDER_PATH\"\nrsync -av --delete \"$NODEJS_BUILT_IN_MODULES_DIR/builtin_modules\" \"$CODESIGNING_FOLDER_PATH\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-bitkit-bitkitTests/Pods-bitkit-bitkitTests-frameworks.sh\"\n"; + showEnvVarsInLog = 0; }; - E62D6EB5874E6BC8745C22E1 /* [CP-User] [NODEJS MOBILE] Build Native Modules */ = { + D914C711382F045CC20BB344 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); - name = "[CP-User] [NODEJS MOBILE] Build Native Modules"; + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-bitkit/Pods-bitkit-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-bitkit/Pods-bitkit-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "#!/bin/sh\nset -e\nif [ -z \"$NODEJS_MOBILE_BUILD_NATIVE_MODULES\" ]; then\n# If build native modules preference is not set, look for it in the project's\n#nodejs-assets/BUILD_NATIVE_MODULES.txt file.\nNODEJS_ASSETS_DIR=\"$( cd \"$PROJECT_DIR\" && cd ../nodejs-assets/ && pwd )\"\nPREFERENCE_FILE_PATH=\"$NODEJS_ASSETS_DIR/BUILD_NATIVE_MODULES.txt\"\n if [ -f \"$PREFERENCE_FILE_PATH\" ]; then\n NODEJS_MOBILE_BUILD_NATIVE_MODULES=\"$(cat $PREFERENCE_FILE_PATH | xargs)\"\n fi\nfi\nif [ -z \"$NODEJS_MOBILE_BUILD_NATIVE_MODULES\" ]; then\n# If build native modules preference is not set, try to find .gyp files\n#to turn it on.\n gypfiles=($(find \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -type f -name \"*.gyp\"))\n if [ ${#gypfiles[@]} -gt 0 ]; then\n NODEJS_MOBILE_BUILD_NATIVE_MODULES=1\n else\n NODEJS_MOBILE_BUILD_NATIVE_MODULES=0\n fi\nfi\nif [ \"1\" != \"$NODEJS_MOBILE_BUILD_NATIVE_MODULES\" ]; then exit 0; fi\n# Delete object files that may already come from within the npm package.\nfind \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -name \"*.o\" -type f -delete\nfind \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -name \"*.a\" -type f -delete\nfind \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -name \"*.node\" -type f -delete\n# Delete bundle contents that may be there from previous builds.\nfind \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -path \"*/*.node/*\" -delete\nfind \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -name \"*.node\" -type d -delete\nfind \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -path \"*/*.framework/*\" -delete\nfind \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -name \"*.framework\" -type d -delete\n# Apply patches to the modules package.json\nif [ -d \"$CODESIGNING_FOLDER_PATH\"/nodejs-project/node_modules/ ]; then\n PATCH_SCRIPT_DIR=\"$( cd \"$PROJECT_DIR\" && cd ../node_modules/nodejs-mobile-react-native/scripts/ && pwd )\"\n NODEJS_PROJECT_MODULES_DIR=\"$( cd \"$CODESIGNING_FOLDER_PATH\" && cd nodejs-project/node_modules/ && pwd )\"\n node \"$PATCH_SCRIPT_DIR\"/patch-package.js $NODEJS_PROJECT_MODULES_DIR\nfi\n# Get the nodejs-mobile-gyp location\nif [ -d \"$PROJECT_DIR/../node_modules/nodejs-mobile-gyp/\" ]; then\n NODEJS_MOBILE_GYP_DIR=\"$( cd \"$PROJECT_DIR\" && cd ../node_modules/nodejs-mobile-gyp/ && pwd )\"\nelse\n NODEJS_MOBILE_GYP_DIR=\"$( cd \"$PROJECT_DIR\" && cd ../node_modules/nodejs-mobile-react-native/node_modules/nodejs-mobile-gyp/ && pwd )\"\nfi\nNODEJS_MOBILE_GYP_BIN_FILE=\"$NODEJS_MOBILE_GYP_DIR\"/bin/node-gyp.js\n# Support building neon-bindings (Rust) native modules\nif [ -f ~/.cargo/env ]; then\n source ~/.cargo/env;\nfi\n# Rebuild modules with right environment\nNODEJS_HEADERS_DIR=\"$( cd \"$PROJECT_DIR\" && cd ../node_modules/nodejs-mobile-react-native/ios/libnode/ && pwd )\"\npushd $CODESIGNING_FOLDER_PATH/nodejs-project/\nif [ \"$PLATFORM_NAME\" == \"iphoneos\" ]\nthen\n GYP_DEFINES=\"OS=ios\" CARGO_BUILD_TARGET=\"aarch64-apple-ios\" npm_config_nodedir=\"$NODEJS_HEADERS_DIR\" npm_config_node_gyp=\"$NODEJS_MOBILE_GYP_BIN_FILE\" npm_config_platform=\"ios\" npm_config_format=\"make-ios\" npm_config_node_engine=\"chakracore\" npm_config_arch=\"arm64\" npm --verbose rebuild --build-from-source\nelse\n GYP_DEFINES=\"OS=ios\" CARGO_BUILD_TARGET=\"x86_64-apple-ios\" npm_config_nodedir=\"$NODEJS_HEADERS_DIR\" npm_config_node_gyp=\"$NODEJS_MOBILE_GYP_BIN_FILE\" npm_config_platform=\"ios\" npm_config_format=\"make-ios\" npm_config_node_engine=\"chakracore\" npm_config_arch=\"x64\" npm --verbose rebuild --build-from-source\nfi\npopd\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-bitkit/Pods-bitkit-frameworks.sh\"\n"; + showEnvVarsInLog = 0; }; FD10A7F022414F080027D42C /* Start Packager */ = { isa = PBXShellScriptBuildPhase; @@ -499,7 +499,7 @@ /* Begin XCBuildConfiguration section */ 00E356F61AD99517003FC87E /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 444EAABECD73CC86C582C108 /* Pods-bitkit-bitkitTests.debug.xcconfig */; + baseConfigurationReference = 15D1684D2578F0264F7B5934 /* Pods-bitkit-bitkitTests.debug.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; @@ -527,7 +527,7 @@ }; 00E356F71AD99517003FC87E /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 3A2A790D063C00580DACF5E0 /* Pods-bitkit-bitkitTests.release.xcconfig */; + baseConfigurationReference = 15D1FA07DC889152B70FF40F /* Pods-bitkit-bitkitTests.release.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; @@ -552,11 +552,11 @@ }; 13B07F941A680F5B00A75B9A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D13A1262BE3ECB2581FC2EDC /* Pods-bitkit.debug.xcconfig */; + baseConfigurationReference = 00303E763958FA7DBF3E7C36 /* Pods-bitkit.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 60; + CURRENT_PROJECT_VERSION = 84; DEVELOPMENT_TEAM = KYH47R284B; ENABLE_BITCODE = NO; INFOPLIST_FILE = bitkit/Info.plist; @@ -583,11 +583,11 @@ }; 13B07F951A680F5B00A75B9A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = F615571D2777FBACDAED24E2 /* Pods-bitkit.release.xcconfig */; + baseConfigurationReference = 58FF2C827EF32BB38C94041B /* Pods-bitkit.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 60; + CURRENT_PROJECT_VERSION = 84; DEVELOPMENT_TEAM = KYH47R284B; ENABLE_BITCODE = NO; INFOPLIST_FILE = bitkit/Info.plist; diff --git a/ios/bitkit/AppDelegate.mm b/ios/bitkit/AppDelegate.mm index 36deb730c..d5669c8c3 100644 --- a/ios/bitkit/AppDelegate.mm +++ b/ios/bitkit/AppDelegate.mm @@ -43,6 +43,14 @@ - (BOOL)application:(UIApplication *)application return [RCTLinkingManager application:application openURL:url options:options]; } +- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity + restorationHandler:(nonnull void (^)(NSArray> * _Nullable))restorationHandler +{ + return [RCTLinkingManager application:application + continueUserActivity:userActivity + restorationHandler:restorationHandler]; +} + // Quick action - (void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL succeeded)) completionHandler { [RNQuickActionManager onQuickActionPress:shortcutItem completionHandler:completionHandler]; diff --git a/ios/bitkit/Info.plist b/ios/bitkit/Info.plist index 293ce75f4..e50c7e0c2 100644 --- a/ios/bitkit/Info.plist +++ b/ios/bitkit/Info.plist @@ -73,8 +73,10 @@ CFBundleURLSchemes + bitkit slash slashauth + slashfeed bitcoin BITCOIN lightning diff --git a/ios/bitkit/bitkit.entitlements b/ios/bitkit/bitkit.entitlements new file mode 100644 index 000000000..10feb18bc --- /dev/null +++ b/ios/bitkit/bitkit.entitlements @@ -0,0 +1,11 @@ + + + + + com.apple.developer.associated-domains + + applinks:bitkit.to + applinks:www.bitkit.to + + + diff --git a/jest.setup.js b/jest.setup.js index 8558c3ef0..4b98e94e0 100644 --- a/jest.setup.js +++ b/jest.setup.js @@ -14,6 +14,9 @@ jest.mock('react-native-permissions', () => jest.mock('@react-native-community/netinfo', () => mockRNCNetInfo); jest.mock('react-native-localize', () => mockRNLocalize); jest.mock('@synonymdev/react-native-ldk', () => mockLDK); +jest.mock('@react-native-async-storage/async-storage', () => + require('@react-native-async-storage/async-storage/jest/async-storage-mock'), +); global.net = require('net'); // needed by Electrum client. For RN it is proviced in shim.js global.tls = require('tls'); // needed by Electrum client. For RN it is proviced in shim.js diff --git a/metro.config.js b/metro.config.js index c16a19506..ef2b3d61c 100644 --- a/metro.config.js +++ b/metro.config.js @@ -1,5 +1,4 @@ -const path = require('path'); -const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config'); +const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config'); const exclusionList = require('metro-config/src/defaults/exclusionList'); /** @@ -8,7 +7,6 @@ const exclusionList = require('metro-config/src/defaults/exclusionList'); * * @type {import('metro-config').MetroConfig} */ - const customConfig = { transformer: { getTransformOptions: async () => ({ @@ -20,22 +18,18 @@ const customConfig = { babelTransformerPath: require.resolve('react-native-svg-transformer'), }, resolver: { - extraNodeModules: { - "sodium-native": path.resolve(__dirname, './node_modules/react-native-libsodium'), - }, blacklistRE: exclusionList([ /android\/build\/nodejs-native-assets-temp-build\/.*/, /\/nodejs-assets\/.*/, - /\/node_modules\/sodium-native\/.*/, /\/android\/build\/*/, ]) }, -}; +} module.exports = (async () => { const { resolver: { sourceExts, assetExts }, - } = await getDefaultConfig(); + } = getDefaultConfig(); customConfig.resolver.assetExts = assetExts.filter((ext) => ext !== 'svg'); customConfig.resolver.sourceExts = [...sourceExts, 'svg']; return mergeConfig(getDefaultConfig(__dirname), customConfig); diff --git a/package.json b/package.json index a49e2b3b0..1643304c4 100644 --- a/package.json +++ b/package.json @@ -1,24 +1,22 @@ { "name": "bitkit", "author": "Synonym", - "version": "1.0.0-beta.60", + "version": "1.0.0-beta.85", "private": false, "scripts": { - "setup-android": "yarn add -D github:sodium-friends/react-native-libsodium#6fbfee572fdfde7e7eec7a1956951d106b342145", + "start": "react-native start", "android": "react-native run-android", - "setup-ios": "yarn add -D react-native-libsodium@0.7.0", "ios": "react-native run-ios --simulator='iPhone 14'", "ios:se": "react-native run-ios --simulator='iPhone SE (3rd generation)'", - "start": "react-native start", "test": "jest --forceExit", "test:coverage": "jest --forceExit --coverage=true", "e2e:build:ios-debug": "detox build --configuration ios.debug", "e2e:build:ios-release": "detox build --configuration ios.release", "e2e:build:android-debug": "detox build --configuration android.debug", "e2e:build:android-release": "detox build --configuration android.release", - "e2e:test:ios-debug": "DEBUG=true detox test --configuration ios.debug", + "e2e:test:ios-debug": "DEV=true detox test --configuration ios.debug", "e2e:test:ios-release": "detox test --configuration ios.release", - "e2e:test:android-debug": "DEBUG=true detox test --configuration android.debug", + "e2e:test:android-debug": "DEV=true detox test --configuration android.debug", "e2e:test:android-release": "detox test --configuration android.release", "lint:check": "eslint . --ext .js,.jsx,.ts,.tsx", "lint:fix": "eslint . --fix --ext .js,.jsx,.ts,.tsx", @@ -37,30 +35,40 @@ }, "dependencies": { "@bitcoinerlab/secp256k1": "^1.0.5", - "@formatjs/intl-datetimeformat": "6.5.1", - "@formatjs/intl-getcanonicallocales": "2.1.0", - "@formatjs/intl-locale": "^3.1.1", - "@formatjs/intl-numberformat": "^8.3.5", - "@formatjs/intl-pluralrules": "^5.1.10", - "@formatjs/intl-relativetimeformat": "^11.1.10", - "@gorhom/bottom-sheet": "^4.4.7", - "@react-native-clipboard/clipboard": "^1.11.2", - "@react-native-community/blur": "^4.3.0", - "@react-native-community/netinfo": "^9.3.7", - "@react-native/eslint-config": "^0.72.2", - "@react-native/metro-config": "^0.72.11", - "@react-navigation/native": "^6.1.7", - "@react-navigation/native-stack": "^6.9.13", - "@react-navigation/stack": "^6.3.17", - "@reduxjs/toolkit": "^1.9.5", - "@sayem314/react-native-keep-awake": "^1.2.0", - "@shopify/react-native-skia": "0.1.200", + "@formatjs/intl-datetimeformat": "6.11.1", + "@formatjs/intl-getcanonicallocales": "2.3.0", + "@formatjs/intl-locale": "3.4.0", + "@formatjs/intl-numberformat": "8.8.0", + "@formatjs/intl-pluralrules": "5.2.7", + "@formatjs/intl-relativetimeformat": "11.2.7", + "@gorhom/bottom-sheet": "^4.5.1", + "@react-native-async-storage/async-storage": "1.19.4", + "@react-native-clipboard/clipboard": "1.12.1", + "@react-native-community/blur": "4.3.2", + "@react-native-community/netinfo": "10.0.0", + "@react-navigation/bottom-tabs": "6.5.11", + "@react-navigation/native": "6.1.9", + "@react-navigation/native-stack": "6.9.16", + "@react-navigation/stack": "6.3.20", + "@reduxjs/toolkit": "^1.9.3", + "@sayem314/react-native-keep-awake": "1.2.2", + "@shopify/react-native-skia": "0.1.216", "@synonymdev/blocktank-client": "0.0.50", - "@synonymdev/react-native-ldk": "^0.0.103", + "@synonymdev/blocktank-lsp-http-client": "0.6.0", + "@synonymdev/feeds": "^2.1.1", + "@synonymdev/react-native-ldk": "0.0.120", "@synonymdev/react-native-lnurl": "0.0.5", "@synonymdev/result": "0.0.2", - "@synonymdev/slashtags-auth": "^1.0.0-alpha.5", + "@synonymdev/slashtags-auth": "1.0.0-alpha.6", + "@synonymdev/slashtags-keychain": "1.0.0", + "@synonymdev/slashtags-profile": "1.0.2", "@synonymdev/slashtags-sdk": "1.0.0-alpha.38", + "@synonymdev/slashtags-url": "1.0.0-alpha.4", + "@synonymdev/slashtags-widget-bitcoin-feed": "1.0.0", + "@synonymdev/slashtags-widget-facts-feed": "1.1.0", + "@synonymdev/slashtags-widget-news-feed": "1.1.0", + "@synonymdev/slashtags-widget-price-feed": "1.1.0", + "@synonymdev/web-relay": "1.0.6", "assert": "^2.0.0", "backpack-client": "github:synonymdev/bitkit-backup-client#f08fdb28529d8a3f8bfecc789443c43b966a7581", "bech32": "^2.0.0", @@ -74,64 +82,64 @@ "buffer": "^6.0.3", "compact-encoding": "^2.11.0", "crypto": "^1.0.1", + "detox": "^20.4.0", "events": "^3.3.0", "fast-sha256": "^1.3.0", "fuzzysort": "^1.9.0", - "i18next": "22.4.10", + "i18next": "23.6.0", "i18next-icu": "^2.3.0", - "immer": "^9.0.19", - "intl-messageformat": "^10.5.0", + "immer": "10.0.3", + "intl-messageformat": "10.5.4", "jdenticon": "^3.2.0", - "lodash.clonedeep": "^4.5.0", - "lodash.debounce": "^4.0.8", - "lodash.isequal": "^4.5.0", - "lodash.throttle": "^4.1.1", - "lottie-react-native": "^6.1.2", + "lodash": "^4.17.21", + "lottie-react-native": "5.1.6", + "mime": "3.0.0", "nodejs-mobile-react-native": "github:Shopify/nodejs-mobile-react-native#a74b13a8f92b39805611c3458e453e57e5abe98f", "process": "^0.11.10", "protobufjs": "^6.11.2", "random-access-web-storage": "^2.0.0", "react": "18.2.0", - "react-i18next": "12.2.0", + "react-i18next": "13.3.1", "react-native": "0.72.4", "react-native-biometrics": "3.0.1", - "react-native-camera-kit": "14.0.0-beta11", + "react-native-camera-kit": "14.0.0-beta13", "react-native-crypto": "^2.2.0", - "react-native-device-info": "^10.4.0", - "react-native-document-picker": "^8.1.3", - "react-native-dotenv": "^3.4.8", + "react-native-device-info": "10.11.0", + "react-native-dotenv": "3.4.9", "react-native-draggable-flatlist": "^4.0.1", - "react-native-exit-app": "^1.1.0", + "react-native-exit-app": "2.0.0", + "react-native-fetch-api": "^3.0.0", "react-native-fs": "^2.20.0", - "react-native-gesture-handler": "2.9.0", - "react-native-haptic-feedback": "^1.14.0", - "react-native-image-picker": "^5.6.0", + "react-native-gesture-handler": "2.13.4", + "react-native-haptic-feedback": "2.2.0", + "react-native-image-picker": "7.0.2", "react-native-keyboard-accessory": "^0.1.16", "react-native-keyboard-aware-scroll-view": "^0.9.5", - "react-native-keychain": "^8.1.1", - "react-native-localize": "^2.2.6", - "react-native-mmkv": "^2.10.1", + "react-native-keychain": "8.1.2", + "react-native-localize": "3.0.2", + "react-native-mmkv": "2.10.2", "react-native-modal": "^13.0.1", - "react-native-permissions": "^3.7.3", + "react-native-permissions": "3.10.1", + "react-native-polyfill-globals": "^3.1.0", "react-native-qrcode-svg": "^6.2.0", "react-native-quick-actions": "^0.3.13", "react-native-randombytes": "^3.6.1", "react-native-rate": "^1.2.12", - "react-native-reanimated": "3.4.2", + "react-native-reanimated": "3.5.4", "react-native-reanimated-carousel": "3.5.1", "react-native-restart": "^0.0.27", - "react-native-safe-area-context": "^4.5.0", - "react-native-screens": "3.24.0", - "react-native-share": "^8.2.1", - "react-native-skia-stub": "github:limpbrains/react-native-skia-stub", - "react-native-svg": "^12.3.0", + "react-native-safe-area-context": "4.7.4", + "react-native-screens": "3.27.0", + "react-native-share": "9.4.1", + "react-native-skia-stub": "github:limpbrains/react-native-skia-stub#c2cafde63c3893bf0ae762d559075328d514d6d7", + "react-native-svg": "13.14.0", "react-native-tcp-socket": "5.6.2", - "react-native-toast-message": "^2.1.6", + "react-native-toast-message": "2.1.7", "react-native-touch-id": "^4.4.1", - "react-native-url-polyfill": "^1.3.0", - "react-native-zip-archive": "^6.0.9", - "react-redux": "8.1.2", - "readable-stream": "^4.2.0", + "react-native-url-polyfill": "^2.0.0", + "react-native-zip-archive": "6.1.0", + "react-redux": "8.1.3", + "readable-stream": "^4.4.2", "redux": "^4.2.1", "redux-logger": "^3.0.6", "redux-persist": "^6.0.0", @@ -141,74 +149,64 @@ "rn-qr-generator": "^1.3.1", "secp256k1": "^4.0.3", "stream-browserify": "^3.0.0", - "styled-components": "^5.3.11", + "styled-components": "5.3.11", + "text-encoding": "^0.7.0", "url-parse": "^1.5.10", "uuid": "^8.3.2", - "vm-browserify": "^1.1.2" + "vm-browserify": "^1.1.2", + "web-streams-polyfill": "^3.2.1" }, "devDependencies": { - "@babel/core": "^7.21.8", + "@babel/core": "^7.23.2", "@babel/plugin-proposal-async-generator-functions": "^7.20.7", - "@babel/preset-env": "^7.21.5", - "@babel/runtime": "^7.21.5", - "@commitlint/cli": "^17.6.3", - "@commitlint/config-conventional": "^17.6.3", + "@babel/preset-env": "^7.23.2", + "@babel/runtime": "^7.23.2", + "@commitlint/cli": "^18.2.0", + "@commitlint/config-conventional": "^18.1.0", "@ptsecurity/commitlint-config": "^2.0.0", "@radar/lnrpc": "^0.11.1-beta.1", "@react-native-community/eslint-config": "^3.2.0", - "@tradle/react-native-http": "^2.0.1", + "@react-native/eslint-config": "0.72.2", + "@react-native/metro-config": "0.72.11", "@tsconfig/react-native": "^3.0.2", "@types/b4a": "^1.6.0", "@types/bip21": "^2.0.0", - "@types/jest": "^29.5.1", - "@types/lodash.debounce": "^4.0.7", - "@types/lodash.isequal": "^4.5.6", - "@types/lodash.throttle": "^4.1.7", + "@types/jest": "^29.5.7", + "@types/lodash": "^4.14.199", + "@types/mime": "^3.0.1", "@types/react": "^18.2.7", "@types/react-native": "0.72.2", "@types/react-native-dotenv": "^0.2.0", - "@types/react-redux": "^7.1.25", - "@types/react-test-renderer": "^18.0.0", - "@types/redux-logger": "^3.0.9", - "@types/styled-components": "^5.1.26", - "@types/styled-components-react-native": "^5.2.1", + "@types/react-redux": "^7.1.28", + "@types/redux-logger": "^3.0.11", + "@types/styled-components": "^5.1.29", + "@types/styled-components-react-native": "^5.2.4", + "@types/text-encoding": "^0.0.36", "@types/url-parse": "^1.4.8", "@types/uuid": "^9.0.1", - "babel-jest": "^29.5.0", + "babel-jest": "^29.7.0", "babel-plugin-transform-remove-console": "^6.9.4", "bitcoin-json-rpc": "^1.2.7", - "detox": "20.7.0", - "browserify-zlib": "^0.2.0", - "dns.js": "^1.0.1", - "electrum-client": "github:BlueWallet/rn-electrum-client#76c0ea35e1a50c47f3a7f818d529ebd100161496", - "esbuild": "^0.17.19", - "eslint": "^8.41.0", + "electrum-client": "github:BlueWallet/rn-electrum-client#47acb51149e97fab249c3f8a314f708dbee4fb6e", + "eslint": "^8.52.0", "from": "^0.1.7", "https-browserify": "^1.0.0", "husky": "^8.0.3", - "jest": "^29.5.0", + "jest": "^29.7.0", + "lnurl": "github:limpbrains/lnurl-node#private", "metro-config": "^0.76.5", - "metro-react-native-babel-preset": "0.76.8", + "metro-react-native-babel-preset": "0.76.5", "nano-staged": "^0.8.0", "node-fetch": "^2.6.7", "prettier": "^2.8.8", - "punycode": "^2.3.0", - "react-native-flipper": "^0.191.0", - "react-native-libsodium": "0.7.0", + "react-native-flipper": "0.212.0", "react-native-mmkv-flipper-plugin": "^1.0.0", - "react-native-svg-transformer": "^1.0.0", - "react-test-renderer": "18.2.0", + "react-native-svg-transformer": "^1.1.0", "redux-flipper": "^2.0.2", - "rimraf": "^5.0.1", + "rimraf": "^5.0.5", "rn-nodeify": "^10.3.0", - "typescript": "5.0.4" + "typescript": "5.2.2" }, - "reactNativePermissionsIOS": [ - "Camera", - "FaceID", - "Notifications", - "PhotoLibrary" - ], "react-native": { "crypto": "react-native-crypto", "_stream_transform": "readable-stream/transform", @@ -218,7 +216,6 @@ "_stream_passthrough": "readable-stream/passthrough", "stream": "stream-browserify", "vm": "vm-browserify", - "sodium-native": "react-native-libsodium", "net": "react-native-tcp-socket" }, "browser": { diff --git a/src/@types/env.d.ts b/src/@types/env.d.ts index c49c0ca54..fd9da4c72 100644 --- a/src/@types/env.d.ts +++ b/src/@types/env.d.ts @@ -9,6 +9,8 @@ declare module '@env' { export const BACKUPS_SHARED_SECRET: string; export const BACKUPS_SERVER_SLASHTAG: string; + export const BACKUPS_SERVER_HOST: string; + export const BACKUPS_SERVER_PUBKEY: string; export const DISABLE_SLASHTAGS: string; export const SLASHTAGS_SEEDER_BASE_URL: string; @@ -29,7 +31,12 @@ declare module '@env' { export const ELECTRUM_SIGNET_TCP_PORT: number; export const ELECTRUM_SIGNET_PROTO: string; + export const TREASURE_HUNT_HOST: string; + + export const TRUSTED_ZERO_CONF_PEERS: string; + export const WALLET_DEFAULT_SELECTED_NETWORK: string; export const E2E: string; + export const WEB_RELAY: string; } diff --git a/src/App.tsx b/src/App.tsx index 2ac39f73d..9b08b43b6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -16,6 +16,7 @@ import { SafeAreaProvider, StatusBar } from './styles/components'; import { getTheme } from './styles/themes'; import OnboardingNavigator from './navigation/onboarding/OnboardingNavigator'; import { SlashtagsProvider } from './components/SlashtagsProvider'; +import { SlashtagsProvider2 } from './components/SlashtagsProvider2'; import { toastConfig } from './components/Toast'; import RecoveryNavigator from './screens/Recovery/RecoveryNavigator'; import RestoringScreen from './screens/Onboarding/Restoring'; @@ -74,7 +75,9 @@ const App = (): ReactElement => { if (walletExists) { return ( - {requiresRemoteRestore ? : } + + {requiresRemoteRestore ? : } + ); } diff --git a/src/AppOnboarded.tsx b/src/AppOnboarded.tsx index f7aa448d8..dc62127d8 100644 --- a/src/AppOnboarded.tsx +++ b/src/AppOnboarded.tsx @@ -19,6 +19,7 @@ import { selectedNetworkSelector, selectedWalletSelector, } from './store/reselect/wallet'; +import { useMigrateSlashtags2 } from './hooks/slashtags2'; const onElectrumConnectionChange = (isConnected: boolean): void => { // get state fresh from store everytime @@ -52,6 +53,9 @@ const AppOnboarded = (): ReactElement => { const pinOnLaunch = useSelector(pinOnLaunchSelector); const isOnline = useSelector(isOnlineSelector); + // migrate slashtags from v1 to v2 + useMigrateSlashtags2(); + // on App start useEffect(() => { startWalletServices({ selectedNetwork, selectedWallet }); diff --git a/src/assets/fonts/Damion-Regular.ttf b/src/assets/fonts/Damion-Regular.ttf new file mode 100644 index 000000000..d0548dd38 Binary files /dev/null and b/src/assets/fonts/Damion-Regular.ttf differ diff --git a/src/assets/icons/wallet.ts b/src/assets/icons/wallet.ts index e0e84029a..458ec3a83 100644 --- a/src/assets/icons/wallet.ts +++ b/src/assets/icons/wallet.ts @@ -49,8 +49,27 @@ export const bitcoinCircleIcon = (color = 'white'): string => export const lightningIcon = ( color = 'white', ): string => ` - -`; + + `; + +export const lightningCircleIcon = (): string => ` + + + + +`; + +export const unifiedCircleIcon = (): string => ` + + + + + +`; export const lockIcon = ( color = 'white', @@ -345,6 +364,16 @@ export const plusCircledIcon = ( `; +export const questionMarkIcon = + (): string => ` + + + + + + +`; + export const keyIcon = (color = 'white'): string => ``; @@ -356,3 +385,19 @@ export const exclamationIcon = (color = 'white'): string => export const fingerPrintIcon = (color = 'white'): string => ``; + +export const mapTrifoldIcon = (color = 'white'): string => + ` + + + + +`; + +export const mapPinLineIcon = (color = 'white'): string => + ` + + + + +`; diff --git a/src/assets/lottie/confetti-yellow.json b/src/assets/lottie/confetti-yellow.json new file mode 100644 index 000000000..8fcb4c656 --- /dev/null +++ b/src/assets/lottie/confetti-yellow.json @@ -0,0 +1 @@ +{"v":"5.5.8","fr":30,"ip":0,"op":300,"w":2000,"h":2000,"ddd":0,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":0,"refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[1000,1000,0]},"a":{"a":0,"k":[1000,1000,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":2000,"h":2000,"ip":150,"op":450,"st":150,"bm":0},{"ddd":0,"ind":2,"ty":0,"refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[1000,1000,0]},"a":{"a":0,"k":[1000,1000,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":2000,"h":2000,"ip":0,"op":300,"st":-150,"bm":0},{"ddd":0,"ind":3,"ty":0,"refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[1000,1000,0]},"a":{"a":0,"k":[1000,1000,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":2000,"h":2000,"ip":150,"op":450,"st":150,"bm":0},{"ddd":0,"ind":4,"ty":0,"refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[1000,1000,0]},"a":{"a":0,"k":[1000,1000,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":2000,"h":2000,"ip":0,"op":299,"st":-150,"bm":0},{"ddd":0,"ind":5,"ty":0,"refId":"comp_3","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[1000,1000,0]},"a":{"a":0,"k":[1000,1000,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":2000,"h":2000,"ip":150,"op":516,"st":150,"bm":0},{"ddd":0,"ind":6,"ty":0,"refId":"comp_3","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[1000,1000,0]},"a":{"a":0,"k":[1000,1000,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":2000,"h":2000,"ip":0,"op":300,"st":-149,"bm":0},{"ddd":0,"ind":7,"ty":0,"refId":"comp_4","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[1000,1000,0]},"a":{"a":0,"k":[1000,1000,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":2000,"h":2000,"ip":150,"op":750,"st":150,"bm":0},{"ddd":0,"ind":8,"ty":0,"refId":"comp_4","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[1000,1000,0]},"a":{"a":0,"k":[1000,1000,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":2000,"h":2000,"ip":0,"op":450,"st":-150,"bm":0}]},{"id":"comp_1","layers":[{"ddd":0,"ind":1,"ty":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[688,732,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,32]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0}},{"ty":"st","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"bm":0},{"ty":"fl","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":175,"s":[-256,-748],"to":[0,4.32],"ti":[0.52,-4.83]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":176,"s":[-256.8,-794.27],"to":[-6.57,60.82],"ti":[0,-108.39]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":196,"s":[-308,-464.77],"to":[0,131.09],"ti":[-6.46,-163.13]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":231,"s":[-227.95,-12.54],"to":[7.89,199.33],"ti":[9.34,-174.95]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":276,"s":[-297.15,568.73],"to":[-8.73,163.59],"ti":[0,-75.1]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":305,"s":[-256,943],"to":[0,279.83],"ti":[0,-0.67]},{"t":349,"s":[-168,1299]}]},"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":177,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":211,"s":[100,1]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":243,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":301,"s":[100,1]},{"t":349,"s":[100,100]}]},"r":{"a":1,"k":[{"i":{"x":[0.83],"y":[0.83]},"o":{"x":[0.17],"y":[0.17]},"t":175,"s":[0]},{"t":349,"s":[277.58]}]},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0}],"ip":175,"op":1075,"st":175,"bm":0},{"ddd":0,"ind":2,"ty":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[1088,732,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,32]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0}},{"ty":"st","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"bm":0},{"ty":"fl","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":225,"s":[-256,-748],"to":[0,4.32],"ti":[0.52,-4.83]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":226,"s":[-256.8,-794.27],"to":[-6.57,60.82],"ti":[0,-108.39]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":246,"s":[-308,-464.77],"to":[0,131.09],"ti":[-6.46,-163.13]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":281,"s":[-227.95,-12.54],"to":[7.89,199.33],"ti":[9.34,-174.95]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":326,"s":[-297.15,568.73],"to":[-8.73,163.59],"ti":[0,-75.1]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":355,"s":[-256,943],"to":[0,279.83],"ti":[0,-0.67]},{"t":399,"s":[-168,1299]}]},"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":227,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":261,"s":[100,1]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":293,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":351,"s":[100,1]},{"t":399,"s":[100,100]}]},"r":{"a":1,"k":[{"i":{"x":[0.83],"y":[0.83]},"o":{"x":[0.17],"y":[0.17]},"t":225,"s":[0]},{"t":399,"s":[277.58]}]},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0}],"ip":225,"op":1125,"st":225,"bm":0},{"ddd":0,"ind":3,"ty":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[1408,732,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,32]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0}},{"ty":"st","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"bm":0},{"ty":"fl","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":81,"s":[-256,-748],"to":[0,4.32],"ti":[0.52,-4.83]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":82,"s":[-256.8,-794.27],"to":[-6.57,60.82],"ti":[0,-108.39]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":102,"s":[-308,-464.77],"to":[0,131.09],"ti":[-6.46,-163.13]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":137,"s":[-227.95,-12.54],"to":[7.89,199.33],"ti":[9.34,-174.95]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":182,"s":[-297.15,568.73],"to":[-8.73,163.59],"ti":[0,-75.1]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":211,"s":[-256,943],"to":[0,279.83],"ti":[0,-0.67]},{"t":255,"s":[-168,1299]}]},"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":83,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":117,"s":[100,1]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":149,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":207,"s":[100,1]},{"t":255,"s":[100,100]}]},"r":{"a":1,"k":[{"i":{"x":[0.83],"y":[0.83]},"o":{"x":[0.17],"y":[0.17]},"t":81,"s":[0]},{"t":255,"s":[277.58]}]},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0}],"ip":81,"op":981,"st":81,"bm":0},{"ddd":0,"ind":4,"ty":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[1968,732,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,32]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0}},{"ty":"st","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"bm":0},{"ty":"fl","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":145,"s":[-256,-748],"to":[0,4.32],"ti":[0.52,-4.83]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":146,"s":[-256.8,-794.27],"to":[-6.57,60.82],"ti":[0,-108.39]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":166,"s":[-308,-464.77],"to":[0,131.09],"ti":[-6.46,-163.13]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":201,"s":[-227.95,-12.54],"to":[7.89,199.33],"ti":[9.34,-174.95]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":246,"s":[-297.15,568.73],"to":[-8.73,163.59],"ti":[0,-75.1]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":275,"s":[-256,943],"to":[0,279.83],"ti":[0,-0.67]},{"t":319,"s":[-168,1299]}]},"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":147,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":181,"s":[100,1]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":213,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":271,"s":[100,1]},{"t":319,"s":[100,100]}]},"r":{"a":1,"k":[{"i":{"x":[0.83],"y":[0.83]},"o":{"x":[0.17],"y":[0.17]},"t":145,"s":[0]},{"t":319,"s":[277.58]}]},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0}],"ip":145,"op":1045,"st":145,"bm":0},{"ddd":0,"ind":5,"ty":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[848,732,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,32]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0}},{"ty":"st","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"bm":0},{"ty":"fl","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":98,"s":[-256,-748],"to":[0,4.32],"ti":[0.52,-4.83]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":99,"s":[-256.8,-794.27],"to":[-6.57,60.82],"ti":[0,-108.39]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":119,"s":[-308,-464.77],"to":[0,131.09],"ti":[-6.46,-163.13]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":154,"s":[-227.95,-12.54],"to":[7.89,199.33],"ti":[9.34,-174.95]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":199,"s":[-297.15,568.73],"to":[-8.73,163.59],"ti":[0,-75.1]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":228,"s":[-256,943],"to":[0,279.83],"ti":[0,-0.67]},{"t":272,"s":[-168,1299]}]},"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":100,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":134,"s":[100,1]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":166,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":224,"s":[100,1]},{"t":272,"s":[100,100]}]},"r":{"a":1,"k":[{"i":{"x":[0.83],"y":[0.83]},"o":{"x":[0.17],"y":[0.17]},"t":98,"s":[0]},{"t":272,"s":[277.58]}]},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0}],"ip":98,"op":369,"st":98,"bm":0},{"ddd":0,"ind":6,"ty":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[368,732,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,32]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0}},{"ty":"st","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"bm":0},{"ty":"fl","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":50,"s":[-256,-748],"to":[0,4.32],"ti":[0.52,-4.83]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":51,"s":[-256.8,-794.27],"to":[-6.57,60.82],"ti":[0,-108.39]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":71,"s":[-308,-464.77],"to":[0,131.09],"ti":[-6.46,-163.13]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":106,"s":[-227.95,-12.54],"to":[7.89,199.33],"ti":[9.34,-174.95]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":151,"s":[-297.15,568.73],"to":[-8.73,163.59],"ti":[0,-75.1]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":180,"s":[-256,943],"to":[0,279.83],"ti":[0,-0.67]},{"t":224,"s":[-168,1299]}]},"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":52,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":86,"s":[100,1]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":118,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":176,"s":[100,1]},{"t":224,"s":[100,100]}]},"r":{"a":1,"k":[{"i":{"x":[0.83],"y":[0.83]},"o":{"x":[0.17],"y":[0.17]},"t":50,"s":[0]},{"t":224,"s":[277.58]}]},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0}],"ip":50,"op":300,"st":50,"bm":0},{"ddd":0,"ind":7,"ty":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[1728,732,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,32]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0}},{"ty":"st","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"bm":0},{"ty":"fl","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":14,"s":[-256,-748],"to":[0,4.32],"ti":[0.52,-4.83]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":15,"s":[-256.8,-794.27],"to":[-6.57,60.82],"ti":[0,-108.39]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":35,"s":[-308,-464.77],"to":[0,131.09],"ti":[-6.46,-163.13]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":70,"s":[-227.95,-12.54],"to":[7.89,199.33],"ti":[9.34,-174.95]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":115,"s":[-297.15,568.73],"to":[-8.73,163.59],"ti":[0,-75.1]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":144,"s":[-256,943],"to":[0,279.83],"ti":[0,-0.67]},{"t":188,"s":[-168,1299]}]},"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":16,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":50,"s":[100,1]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":82,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":140,"s":[100,1]},{"t":188,"s":[100,100]}]},"r":{"a":1,"k":[{"i":{"x":[0.83],"y":[0.83]},"o":{"x":[0.17],"y":[0.17]},"t":14,"s":[0]},{"t":188,"s":[277.58]}]},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0}],"ip":14,"op":300,"st":14,"bm":0},{"ddd":0,"ind":8,"ty":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[1248,732,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,32]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0}},{"ty":"st","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"bm":0},{"ty":"fl","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":-1,"s":[-256,-748],"to":[0,4.32],"ti":[0.52,-4.83]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":0,"s":[-256.8,-794.27],"to":[-6.57,60.82],"ti":[0,-108.39]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":20,"s":[-308,-464.77],"to":[0,131.09],"ti":[-6.46,-163.13]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":55,"s":[-227.95,-12.54],"to":[7.89,199.33],"ti":[9.34,-174.95]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":100,"s":[-297.15,568.73],"to":[-8.73,163.59],"ti":[0,-75.1]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":129,"s":[-256,943],"to":[0,279.83],"ti":[0,-0.67]},{"t":173,"s":[-168,1299]}]},"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":1,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":35,"s":[100,1]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":67,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":125,"s":[100,1]},{"t":173,"s":[100,100]}]},"r":{"a":1,"k":[{"i":{"x":[0.83],"y":[0.83]},"o":{"x":[0.17],"y":[0.17]},"t":-1,"s":[0]},{"t":173,"s":[277.58]}]},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0}],"ip":0,"op":300,"st":-1,"bm":0}]},{"id":"comp_2","layers":[{"ddd":0,"ind":1,"ty":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[368,732,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,32]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0}},{"ty":"st","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"bm":0},{"ty":"fl","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":62,"s":[-256,-748],"to":[0,4.32],"ti":[0.52,-4.83]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":63,"s":[-256.8,-794.27],"to":[-6.57,60.82],"ti":[0,-108.39]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":83,"s":[-308,-464.77],"to":[0,131.09],"ti":[-6.46,-163.13]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":118,"s":[-227.95,-12.54],"to":[7.89,199.33],"ti":[9.34,-174.95]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":163,"s":[-297.15,568.73],"to":[-8.73,163.59],"ti":[0,-75.1]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":192,"s":[-256,943],"to":[0,279.83],"ti":[0,-0.67]},{"t":236,"s":[-168,1299]}]},"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":64,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":98,"s":[100,1]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":130,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":188,"s":[100,1]},{"t":236,"s":[100,100]}]},"r":{"a":1,"k":[{"i":{"x":[0.83],"y":[0.83]},"o":{"x":[0.17],"y":[0.17]},"t":62,"s":[0]},{"t":236,"s":[277.58]}]},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0}],"ip":62,"op":962,"st":62,"bm":0},{"ddd":0,"ind":2,"ty":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[1328,732,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,32]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0}},{"ty":"st","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"bm":0},{"ty":"fl","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":190,"s":[-256,-748],"to":[0,4.32],"ti":[0.52,-4.83]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":191,"s":[-256.8,-794.27],"to":[-6.57,60.82],"ti":[0,-108.39]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":211,"s":[-308,-464.77],"to":[0,131.09],"ti":[-6.46,-163.13]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":246,"s":[-227.95,-12.54],"to":[7.89,199.33],"ti":[9.34,-174.95]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":291,"s":[-297.15,568.73],"to":[-8.73,163.59],"ti":[0,-75.1]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":320,"s":[-256,943],"to":[0,279.83],"ti":[0,-0.67]},{"t":364,"s":[-168,1299]}]},"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":192,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":226,"s":[100,1]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":258,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":316,"s":[100,1]},{"t":364,"s":[100,100]}]},"r":{"a":1,"k":[{"i":{"x":[0.83],"y":[0.83]},"o":{"x":[0.17],"y":[0.17]},"t":190,"s":[0]},{"t":364,"s":[277.58]}]},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0}],"ip":190,"op":1090,"st":190,"bm":0},{"ddd":0,"ind":3,"ty":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[848,732,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,32]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0}},{"ty":"st","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"bm":0},{"ty":"fl","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":125,"s":[-256,-748],"to":[0,4.32],"ti":[0.52,-4.83]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":126,"s":[-256.8,-794.27],"to":[-6.57,60.82],"ti":[0,-108.39]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":146,"s":[-308,-464.77],"to":[0,131.09],"ti":[-6.46,-163.13]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":181,"s":[-227.95,-12.54],"to":[7.89,199.33],"ti":[9.34,-174.95]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":226,"s":[-297.15,568.73],"to":[-8.73,163.59],"ti":[0,-75.1]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":255,"s":[-256,943],"to":[0,279.83],"ti":[0,-0.67]},{"t":299,"s":[-168,1299]}]},"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":127,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":161,"s":[100,1]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":193,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":251,"s":[100,1]},{"t":299,"s":[100,100]}]},"r":{"a":1,"k":[{"i":{"x":[0.83],"y":[0.83]},"o":{"x":[0.17],"y":[0.17]},"t":125,"s":[0]},{"t":299,"s":[277.58]}]},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0}],"ip":125,"op":1025,"st":125,"bm":0},{"ddd":0,"ind":4,"ty":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[1328,732,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,32]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0}},{"ty":"st","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"bm":0},{"ty":"fl","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":41,"s":[-256,-748],"to":[0,4.32],"ti":[0.52,-4.83]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":42,"s":[-256.8,-794.27],"to":[-6.57,60.82],"ti":[0,-108.39]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":62,"s":[-308,-464.77],"to":[0,131.09],"ti":[-6.46,-163.13]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":97,"s":[-227.95,-12.54],"to":[7.89,199.33],"ti":[9.34,-174.95]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":142,"s":[-297.15,568.73],"to":[-8.73,163.59],"ti":[0,-75.1]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":171,"s":[-256,943],"to":[0,279.83],"ti":[0,-0.67]},{"t":215,"s":[-168,1299]}]},"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":43,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":77,"s":[100,1]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":109,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":167,"s":[100,1]},{"t":215,"s":[100,100]}]},"r":{"a":1,"k":[{"i":{"x":[0.83],"y":[0.83]},"o":{"x":[0.17],"y":[0.17]},"t":41,"s":[0]},{"t":215,"s":[277.58]}]},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0}],"ip":41,"op":941,"st":41,"bm":0},{"ddd":0,"ind":5,"ty":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[1088,732,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,32]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0}},{"ty":"st","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"bm":0},{"ty":"fl","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":142,"s":[-256,-748],"to":[0,4.32],"ti":[0.52,-4.83]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":143,"s":[-256.8,-794.27],"to":[-6.57,60.82],"ti":[0,-108.39]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":163,"s":[-308,-464.77],"to":[0,131.09],"ti":[-6.46,-163.13]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":198,"s":[-227.95,-12.54],"to":[7.89,199.33],"ti":[9.34,-174.95]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":243,"s":[-297.15,568.73],"to":[-8.73,163.59],"ti":[0,-75.1]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":272,"s":[-256,943],"to":[0,279.83],"ti":[0,-0.67]},{"t":316,"s":[-168,1299]}]},"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":144,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":178,"s":[100,1]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":210,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":268,"s":[100,1]},{"t":316,"s":[100,100]}]},"r":{"a":1,"k":[{"i":{"x":[0.83],"y":[0.83]},"o":{"x":[0.17],"y":[0.17]},"t":142,"s":[0]},{"t":316,"s":[277.58]}]},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0}],"ip":142,"op":413,"st":142,"bm":0},{"ddd":0,"ind":6,"ty":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[608,732,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,32]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0}},{"ty":"st","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"bm":0},{"ty":"fl","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":212,"s":[-256,-748],"to":[0,4.32],"ti":[0.52,-4.83]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":213,"s":[-256.8,-794.27],"to":[-6.57,60.82],"ti":[0,-108.39]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":233,"s":[-308,-464.77],"to":[0,131.09],"ti":[-6.46,-163.13]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":268,"s":[-227.95,-12.54],"to":[7.89,199.33],"ti":[9.34,-174.95]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":313,"s":[-297.15,568.73],"to":[-8.73,163.59],"ti":[0,-75.1]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":342,"s":[-256,943],"to":[0,279.83],"ti":[0,-0.67]},{"t":386,"s":[-168,1299]}]},"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":214,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":248,"s":[100,1]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":280,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":338,"s":[100,1]},{"t":386,"s":[100,100]}]},"r":{"a":1,"k":[{"i":{"x":[0.83],"y":[0.83]},"o":{"x":[0.17],"y":[0.17]},"t":212,"s":[0]},{"t":386,"s":[277.58]}]},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0}],"ip":212,"op":462,"st":212,"bm":0},{"ddd":0,"ind":7,"ty":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[1088,732,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,32]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0}},{"ty":"st","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"bm":0},{"ty":"fl","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":-9,"s":[-256,-748],"to":[0,4.32],"ti":[0.52,-4.83]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":-8,"s":[-256.8,-794.27],"to":[-6.57,60.82],"ti":[0,-108.39]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":12,"s":[-308,-464.77],"to":[0,131.09],"ti":[-6.46,-163.13]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":47,"s":[-227.95,-12.54],"to":[7.89,199.33],"ti":[9.34,-174.95]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":92,"s":[-297.15,568.73],"to":[-8.73,163.59],"ti":[0,-75.1]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":121,"s":[-256,943],"to":[0,279.83],"ti":[0,-0.67]},{"t":165,"s":[-168,1299]}]},"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":-7,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":27,"s":[100,1]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":59,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":117,"s":[100,1]},{"t":165,"s":[100,100]}]},"r":{"a":1,"k":[{"i":{"x":[0.83],"y":[0.83]},"o":{"x":[0.17],"y":[0.17]},"t":-9,"s":[0]},{"t":165,"s":[277.58]}]},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0}],"ip":-9,"op":277,"st":-9,"bm":0},{"ddd":0,"ind":8,"ty":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[1728,732,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,32]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0}},{"ty":"st","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"bm":0},{"ty":"fl","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":37,"s":[-256,-748],"to":[0,4.32],"ti":[0.52,-4.83]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":38,"s":[-256.8,-794.27],"to":[-6.57,60.82],"ti":[0,-108.39]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":58,"s":[-308,-464.77],"to":[0,131.09],"ti":[-6.46,-163.13]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":93,"s":[-227.95,-12.54],"to":[7.89,199.33],"ti":[9.34,-174.95]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":138,"s":[-297.15,568.73],"to":[-8.73,163.59],"ti":[0,-75.1]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":167,"s":[-256,943],"to":[0,279.83],"ti":[0,-0.67]},{"t":211,"s":[-168,1299]}]},"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":39,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":73,"s":[100,1]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":105,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":163,"s":[100,1]},{"t":211,"s":[100,100]}]},"r":{"a":1,"k":[{"i":{"x":[0.83],"y":[0.83]},"o":{"x":[0.17],"y":[0.17]},"t":37,"s":[0]},{"t":211,"s":[277.58]}]},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0}],"ip":38,"op":338,"st":37,"bm":0}]},{"id":"comp_3","layers":[{"ddd":0,"ind":1,"ty":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[288,732,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,32]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0}},{"ty":"st","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"bm":0},{"ty":"fl","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":-1,"s":[-256,-748],"to":[0,4.32],"ti":[0.52,-4.83]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":0,"s":[-256.8,-794.27],"to":[-6.57,60.82],"ti":[0,-108.39]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":20,"s":[-308,-464.77],"to":[0,131.09],"ti":[-6.46,-163.13]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":55,"s":[-227.95,-12.54],"to":[7.89,199.33],"ti":[9.34,-174.95]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":100,"s":[-297.15,568.73],"to":[-8.73,163.59],"ti":[0,-75.1]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":129,"s":[-256,943],"to":[0,279.83],"ti":[0,-0.67]},{"t":173,"s":[-168,1299]}]},"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":1,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":35,"s":[100,1]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":67,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":125,"s":[100,1]},{"t":173,"s":[100,100]}]},"r":{"a":1,"k":[{"i":{"x":[0.83],"y":[0.83]},"o":{"x":[0.17],"y":[0.17]},"t":-1,"s":[0]},{"t":173,"s":[277.58]}]},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0}],"ip":-1,"op":899,"st":-1,"bm":0},{"ddd":0,"ind":2,"ty":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[1424,732,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,32]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0}},{"ty":"st","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"bm":0},{"ty":"fl","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":160,"s":[-256,-748],"to":[0,4.32],"ti":[0.52,-4.83]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":161,"s":[-256.8,-794.27],"to":[-6.57,60.82],"ti":[0,-108.39]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":181,"s":[-308,-464.77],"to":[0,131.09],"ti":[-6.46,-163.13]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":216,"s":[-227.95,-12.54],"to":[7.89,199.33],"ti":[9.34,-174.95]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":261,"s":[-297.15,568.73],"to":[-8.73,163.59],"ti":[0,-75.1]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":290,"s":[-256,943],"to":[0,279.83],"ti":[0,-0.67]},{"t":334,"s":[-168,1299]}]},"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":162,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":196,"s":[100,1]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":228,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":286,"s":[100,1]},{"t":334,"s":[100,100]}]},"r":{"a":1,"k":[{"i":{"x":[0.83],"y":[0.83]},"o":{"x":[0.17],"y":[0.17]},"t":160,"s":[0]},{"t":334,"s":[277.58]}]},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0}],"ip":160,"op":1060,"st":160,"bm":0},{"ddd":0,"ind":3,"ty":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[528,732,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,32]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0}},{"ty":"st","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"bm":0},{"ty":"fl","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":12,"s":[-256,-748],"to":[0,4.32],"ti":[0.52,-4.83]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":13,"s":[-256.8,-794.27],"to":[-6.57,60.82],"ti":[0,-108.39]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":33,"s":[-308,-464.77],"to":[0,131.09],"ti":[-6.46,-163.13]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":68,"s":[-227.95,-12.54],"to":[7.89,199.33],"ti":[9.34,-174.95]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":113,"s":[-297.15,568.73],"to":[-8.73,163.59],"ti":[0,-75.1]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":142,"s":[-256,943],"to":[0,279.83],"ti":[0,-0.67]},{"t":186,"s":[-168,1299]}]},"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":14,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":48,"s":[100,1]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":80,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":138,"s":[100,1]},{"t":186,"s":[100,100]}]},"r":{"a":1,"k":[{"i":{"x":[0.83],"y":[0.83]},"o":{"x":[0.17],"y":[0.17]},"t":12,"s":[0]},{"t":186,"s":[277.58]}]},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0}],"ip":12,"op":912,"st":12,"bm":0},{"ddd":0,"ind":4,"ty":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[1168,732,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,32]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0}},{"ty":"st","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"bm":0},{"ty":"fl","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":45,"s":[-256,-748],"to":[0,4.32],"ti":[0.52,-4.83]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":46,"s":[-256.8,-794.27],"to":[-6.57,60.82],"ti":[0,-108.39]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":66,"s":[-308,-464.77],"to":[0,131.09],"ti":[-6.46,-163.13]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":101,"s":[-227.95,-12.54],"to":[7.89,199.33],"ti":[9.34,-174.95]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":146,"s":[-297.15,568.73],"to":[-8.73,163.59],"ti":[0,-75.1]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":175,"s":[-256,943],"to":[0,279.83],"ti":[0,-0.67]},{"t":219,"s":[-168,1299]}]},"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":47,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":81,"s":[100,1]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":113,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":171,"s":[100,1]},{"t":219,"s":[100,100]}]},"r":{"a":1,"k":[{"i":{"x":[0.83],"y":[0.83]},"o":{"x":[0.17],"y":[0.17]},"t":45,"s":[0]},{"t":219,"s":[277.58]}]},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0}],"ip":45,"op":945,"st":45,"bm":0},{"ddd":0,"ind":5,"ty":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[768,732,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,32]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0}},{"ty":"st","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"bm":0},{"ty":"fl","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":220,"s":[-256,-748],"to":[0,4.32],"ti":[0.52,-4.83]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":221,"s":[-256.8,-794.27],"to":[-6.57,60.82],"ti":[0,-108.39]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":241,"s":[-308,-464.77],"to":[0,131.09],"ti":[-6.46,-163.13]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":276,"s":[-227.95,-12.54],"to":[7.89,199.33],"ti":[9.34,-174.95]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":321,"s":[-297.15,568.73],"to":[-8.73,163.59],"ti":[0,-75.1]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":350,"s":[-256,943],"to":[0,279.83],"ti":[0,-0.67]},{"t":394,"s":[-168,1299]}]},"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":222,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":256,"s":[100,1]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":288,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":346,"s":[100,1]},{"t":394,"s":[100,100]}]},"r":{"a":1,"k":[{"i":{"x":[0.83],"y":[0.83]},"o":{"x":[0.17],"y":[0.17]},"t":220,"s":[0]},{"t":394,"s":[277.58]}]},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0}],"ip":220,"op":491,"st":220,"bm":0},{"ddd":0,"ind":6,"ty":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[2016,732,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,32]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0}},{"ty":"st","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"bm":0},{"ty":"fl","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":-1,"s":[-256,-748],"to":[0,4.32],"ti":[0.52,-4.83]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":0,"s":[-256.8,-794.27],"to":[-6.57,60.82],"ti":[0,-108.39]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":20,"s":[-308,-464.77],"to":[0,131.09],"ti":[-6.46,-163.13]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":55,"s":[-227.95,-12.54],"to":[7.89,199.33],"ti":[9.34,-174.95]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":100,"s":[-297.15,568.73],"to":[-8.73,163.59],"ti":[0,-75.1]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":129,"s":[-256,943],"to":[0,279.83],"ti":[0,-0.67]},{"t":173,"s":[-168,1299]}]},"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":1,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":35,"s":[100,1]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":67,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":125,"s":[100,1]},{"t":173,"s":[100,100]}]},"r":{"a":1,"k":[{"i":{"x":[0.83],"y":[0.83]},"o":{"x":[0.17],"y":[0.17]},"t":-1,"s":[0]},{"t":173,"s":[277.58]}]},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0}],"ip":-1,"op":249,"st":-1,"bm":0},{"ddd":0,"ind":7,"ty":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[1968,732,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,32]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0}},{"ty":"st","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"bm":0},{"ty":"fl","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":178,"s":[-256,-748],"to":[0,4.32],"ti":[0.52,-4.83]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":179,"s":[-256.8,-794.27],"to":[-6.57,60.82],"ti":[0,-108.39]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":199,"s":[-308,-464.77],"to":[0,131.09],"ti":[-6.46,-163.13]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":234,"s":[-227.95,-12.54],"to":[7.89,199.33],"ti":[9.34,-174.95]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":279,"s":[-297.15,568.73],"to":[-8.73,163.59],"ti":[0,-75.1]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":308,"s":[-256,943],"to":[0,279.83],"ti":[0,-0.67]},{"t":352,"s":[-168,1299]}]},"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":180,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":214,"s":[100,1]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":246,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":304,"s":[100,1]},{"t":352,"s":[100,100]}]},"r":{"a":1,"k":[{"i":{"x":[0.83],"y":[0.83]},"o":{"x":[0.17],"y":[0.17]},"t":178,"s":[0]},{"t":352,"s":[277.58]}]},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0}],"ip":178,"op":464,"st":178,"bm":0},{"ddd":0,"ind":8,"ty":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[1568,732,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,32]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0}},{"ty":"st","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"bm":0},{"ty":"fl","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":94,"s":[-256,-748],"to":[0,4.32],"ti":[0.52,-4.83]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":95,"s":[-256.8,-794.27],"to":[-6.57,60.82],"ti":[0,-108.39]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":115,"s":[-308,-464.77],"to":[0,131.09],"ti":[-6.46,-163.13]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":150,"s":[-227.95,-12.54],"to":[7.89,199.33],"ti":[9.34,-174.95]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":195,"s":[-297.15,568.73],"to":[-8.73,163.59],"ti":[0,-75.1]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":224,"s":[-256,943],"to":[0,279.83],"ti":[0,-0.67]},{"t":268,"s":[-168,1299]}]},"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":96,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":130,"s":[100,1]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":162,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":220,"s":[100,1]},{"t":268,"s":[100,100]}]},"r":{"a":1,"k":[{"i":{"x":[0.83],"y":[0.83]},"o":{"x":[0.17],"y":[0.17]},"t":94,"s":[0]},{"t":268,"s":[277.58]}]},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0}],"ip":95,"op":395,"st":94,"bm":0}]},{"id":"comp_4","layers":[{"ddd":0,"ind":1,"ty":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[1568,732,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,32]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0}},{"ty":"st","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"bm":0},{"ty":"fl","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":169,"s":[-256,-748],"to":[0,4.32],"ti":[0.52,-4.83]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":170,"s":[-256.8,-794.27],"to":[-6.57,60.82],"ti":[0,-108.39]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":190,"s":[-308,-464.77],"to":[0,131.09],"ti":[-6.46,-163.13]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":225,"s":[-227.95,-12.54],"to":[7.89,199.33],"ti":[9.34,-174.95]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":270,"s":[-297.15,568.73],"to":[-8.73,163.59],"ti":[0,-75.1]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":299,"s":[-256,943],"to":[0,279.83],"ti":[0,-0.67]},{"t":343,"s":[-168,1299]}]},"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":171,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":205,"s":[100,1]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":237,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":295,"s":[100,1]},{"t":343,"s":[100,100]}]},"r":{"a":1,"k":[{"i":{"x":[0.83],"y":[0.83]},"o":{"x":[0.17],"y":[0.17]},"t":169,"s":[0]},{"t":343,"s":[277.58]}]},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0}],"ip":169,"op":1069,"st":169,"bm":0},{"ddd":0,"ind":2,"ty":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[1104,732,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,32]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0}},{"ty":"st","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"bm":0},{"ty":"fl","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":67,"s":[-256,-748],"to":[0,4.32],"ti":[0.52,-4.83]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":68,"s":[-256.8,-794.27],"to":[-6.57,60.82],"ti":[0,-108.39]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":88,"s":[-308,-464.77],"to":[0,131.09],"ti":[-6.46,-163.13]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":123,"s":[-227.95,-12.54],"to":[7.89,199.33],"ti":[9.34,-174.95]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":168,"s":[-297.15,568.73],"to":[-8.73,163.59],"ti":[0,-75.1]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":197,"s":[-256,943],"to":[0,279.83],"ti":[0,-0.67]},{"t":241,"s":[-168,1299]}]},"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":69,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":103,"s":[100,1]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":135,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":193,"s":[100,1]},{"t":241,"s":[100,100]}]},"r":{"a":1,"k":[{"i":{"x":[0.83],"y":[0.83]},"o":{"x":[0.17],"y":[0.17]},"t":67,"s":[0]},{"t":241,"s":[277.58]}]},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0}],"ip":67,"op":967,"st":67,"bm":0},{"ddd":0,"ind":3,"ty":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[928,732,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,32]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0}},{"ty":"st","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"bm":0},{"ty":"fl","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":232,"s":[-256,-748],"to":[0,4.32],"ti":[0.52,-4.83]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":233,"s":[-256.8,-794.27],"to":[-6.57,60.82],"ti":[0,-108.39]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":253,"s":[-308,-464.77],"to":[0,131.09],"ti":[-6.46,-163.13]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":288,"s":[-227.95,-12.54],"to":[7.89,199.33],"ti":[9.34,-174.95]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":333,"s":[-297.15,568.73],"to":[-8.73,163.59],"ti":[0,-75.1]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":362,"s":[-256,943],"to":[0,279.83],"ti":[0,-0.67]},{"t":406,"s":[-168,1299]}]},"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":234,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":268,"s":[100,1]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":300,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":358,"s":[100,1]},{"t":406,"s":[100,100]}]},"r":{"a":1,"k":[{"i":{"x":[0.83],"y":[0.83]},"o":{"x":[0.17],"y":[0.17]},"t":232,"s":[0]},{"t":406,"s":[277.58]}]},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0}],"ip":232,"op":1132,"st":232,"bm":0},{"ddd":0,"ind":4,"ty":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[1968,732,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,32]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0}},{"ty":"st","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"bm":0},{"ty":"fl","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":112,"s":[-256,-748],"to":[0,4.32],"ti":[0.52,-4.83]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":113,"s":[-256.8,-794.27],"to":[-6.57,60.82],"ti":[0,-108.39]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":133,"s":[-308,-464.77],"to":[0,131.09],"ti":[-6.46,-163.13]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":168,"s":[-227.95,-12.54],"to":[7.89,199.33],"ti":[9.34,-174.95]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":213,"s":[-297.15,568.73],"to":[-8.73,163.59],"ti":[0,-75.1]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":242,"s":[-256,943],"to":[0,279.83],"ti":[0,-0.67]},{"t":286,"s":[-168,1299]}]},"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":114,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":148,"s":[100,1]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":180,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":238,"s":[100,1]},{"t":286,"s":[100,100]}]},"r":{"a":1,"k":[{"i":{"x":[0.83],"y":[0.83]},"o":{"x":[0.17],"y":[0.17]},"t":112,"s":[0]},{"t":286,"s":[277.58]}]},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0}],"ip":112,"op":1012,"st":112,"bm":0},{"ddd":0,"ind":5,"ty":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[1248,732,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,32]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0}},{"ty":"st","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"bm":0},{"ty":"fl","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":328,"s":[-256,-748],"to":[0,4.32],"ti":[0.52,-4.83]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":329,"s":[-256.8,-794.27],"to":[-6.57,60.82],"ti":[0,-108.39]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":349,"s":[-308,-464.77],"to":[0,131.09],"ti":[-6.46,-163.13]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":384,"s":[-227.95,-12.54],"to":[7.89,199.33],"ti":[9.34,-174.95]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":429,"s":[-297.15,568.73],"to":[-8.73,163.59],"ti":[0,-75.1]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":458,"s":[-256,943],"to":[0,279.83],"ti":[0,-0.67]},{"t":502,"s":[-168,1299]}]},"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":330,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":364,"s":[100,1]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":396,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":454,"s":[100,1]},{"t":502,"s":[100,100]}]},"r":{"a":1,"k":[{"i":{"x":[0.83],"y":[0.83]},"o":{"x":[0.17],"y":[0.17]},"t":328,"s":[0]},{"t":502,"s":[277.58]}]},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0}],"ip":328,"op":599,"st":328,"bm":0},{"ddd":0,"ind":6,"ty":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[2016,732,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,32]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0}},{"ty":"st","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"bm":0},{"ty":"fl","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":259,"s":[-256,-748],"to":[0,4.32],"ti":[0.52,-4.83]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":260,"s":[-256.8,-794.27],"to":[-6.57,60.82],"ti":[0,-108.39]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":280,"s":[-308,-464.77],"to":[0,131.09],"ti":[-6.46,-163.13]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":315,"s":[-227.95,-12.54],"to":[7.89,199.33],"ti":[9.34,-174.95]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":360,"s":[-297.15,568.73],"to":[-8.73,163.59],"ti":[0,-75.1]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":389,"s":[-256,943],"to":[0,279.83],"ti":[0,-0.67]},{"t":433,"s":[-168,1299]}]},"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":261,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":295,"s":[100,1]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":327,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":385,"s":[100,1]},{"t":433,"s":[100,100]}]},"r":{"a":1,"k":[{"i":{"x":[0.83],"y":[0.83]},"o":{"x":[0.17],"y":[0.17]},"t":259,"s":[0]},{"t":433,"s":[277.58]}]},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0}],"ip":259,"op":509,"st":259,"bm":0},{"ddd":0,"ind":7,"ty":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[608,732,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,32]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0}},{"ty":"st","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"bm":0},{"ty":"fl","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":0,"s":[-256,-748],"to":[0,4.32],"ti":[0.52,-4.83]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":1,"s":[-256.8,-794.27],"to":[-6.57,60.82],"ti":[0,-108.39]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":21,"s":[-308,-464.77],"to":[0,131.09],"ti":[-6.46,-163.13]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":56,"s":[-227.95,-12.54],"to":[7.89,199.33],"ti":[9.34,-174.95]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":101,"s":[-297.15,568.73],"to":[-8.73,163.59],"ti":[0,-75.1]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":130,"s":[-256,943],"to":[0,279.83],"ti":[0,-0.67]},{"t":174,"s":[-168,1299]}]},"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":2,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":36,"s":[100,1]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":68,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":126,"s":[100,1]},{"t":174,"s":[100,100]}]},"r":{"a":1,"k":[{"i":{"x":[0.83],"y":[0.83]},"o":{"x":[0.17],"y":[0.17]},"t":0,"s":[0]},{"t":174,"s":[277.58]}]},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0}],"ip":0,"op":286,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[1808,732,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,32]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0}},{"ty":"st","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"bm":0},{"ty":"fl","c":{"a":0,"k":[1,0.8,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":55,"s":[-256,-748],"to":[0,4.32],"ti":[0.52,-4.83]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":56,"s":[-256.8,-794.27],"to":[-6.57,60.82],"ti":[0,-108.39]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":76,"s":[-308,-464.77],"to":[0,131.09],"ti":[-6.46,-163.13]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":111,"s":[-227.95,-12.54],"to":[7.89,199.33],"ti":[9.34,-174.95]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":156,"s":[-297.15,568.73],"to":[-8.73,163.59],"ti":[0,-75.1]},{"i":{"x":0.83,"y":0.83},"o":{"x":0.17,"y":0.17},"t":185,"s":[-256,943],"to":[0,279.83],"ti":[0,-0.67]},{"t":229,"s":[-168,1299]}]},"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":57,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":91,"s":[100,1]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":123,"s":[100,100]},{"i":{"x":[0.83,0.83],"y":[0.83,0.83]},"o":{"x":[0.17,0.17],"y":[0.17,0.17]},"t":181,"s":[100,1]},{"t":229,"s":[100,100]}]},"r":{"a":1,"k":[{"i":{"x":[0.83],"y":[0.83]},"o":{"x":[0.17],"y":[0.17]},"t":55,"s":[0]},{"t":229,"s":[277.58]}]},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0}],"ip":56,"op":356,"st":55,"bm":0}]}],"layers":[{"ddd":0,"ind":2,"ty":0,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[1000,1000,0]},"a":{"a":0,"k":[1000,1000,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":2000,"h":2000,"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":0,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[592,592,0]},"a":{"a":0,"k":[1000,1000,0]},"s":{"a":0,"k":[146.4,143.2,100]}},"ao":0,"w":2000,"h":2000,"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":0,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[1400,1360,0]},"a":{"a":0,"k":[1000,1000,0]},"s":{"a":0,"k":[153.6,143.2,100]}},"ao":0,"w":2000,"h":2000,"ip":0,"op":300,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/src/assets/treasure-hunt/airdrop.jpg b/src/assets/treasure-hunt/airdrop.jpg new file mode 100644 index 000000000..598df7b4e Binary files /dev/null and b/src/assets/treasure-hunt/airdrop.jpg differ diff --git a/src/assets/treasure-hunt/consolation-1.jpg b/src/assets/treasure-hunt/consolation-1.jpg new file mode 100644 index 000000000..979602b3a Binary files /dev/null and b/src/assets/treasure-hunt/consolation-1.jpg differ diff --git a/src/assets/treasure-hunt/consolation-2.jpg b/src/assets/treasure-hunt/consolation-2.jpg new file mode 100644 index 000000000..51a7994fe Binary files /dev/null and b/src/assets/treasure-hunt/consolation-2.jpg differ diff --git a/src/assets/treasure-hunt/consolation-3.jpg b/src/assets/treasure-hunt/consolation-3.jpg new file mode 100644 index 000000000..fbbd911b6 Binary files /dev/null and b/src/assets/treasure-hunt/consolation-3.jpg differ diff --git a/src/assets/treasure-hunt/consolation-4.jpg b/src/assets/treasure-hunt/consolation-4.jpg new file mode 100644 index 000000000..0d1f24914 Binary files /dev/null and b/src/assets/treasure-hunt/consolation-4.jpg differ diff --git a/src/assets/treasure-hunt/consolation-5.jpg b/src/assets/treasure-hunt/consolation-5.jpg new file mode 100644 index 000000000..f20d48326 Binary files /dev/null and b/src/assets/treasure-hunt/consolation-5.jpg differ diff --git a/src/assets/treasure-hunt/consolation-6.jpg b/src/assets/treasure-hunt/consolation-6.jpg new file mode 100644 index 000000000..0be05892e Binary files /dev/null and b/src/assets/treasure-hunt/consolation-6.jpg differ diff --git a/src/assets/treasure-hunt/empty.jpg b/src/assets/treasure-hunt/empty.jpg new file mode 100644 index 000000000..c0183c554 Binary files /dev/null and b/src/assets/treasure-hunt/empty.jpg differ diff --git a/src/assets/treasure-hunt/error.jpg b/src/assets/treasure-hunt/error.jpg new file mode 100644 index 000000000..f7c50edf8 Binary files /dev/null and b/src/assets/treasure-hunt/error.jpg differ diff --git a/src/assets/treasure-hunt/loading.jpg b/src/assets/treasure-hunt/loading.jpg new file mode 100644 index 000000000..409feb70d Binary files /dev/null and b/src/assets/treasure-hunt/loading.jpg differ diff --git a/src/assets/treasure-hunt/prize.jpg b/src/assets/treasure-hunt/prize.jpg new file mode 100644 index 000000000..97c0ce349 Binary files /dev/null and b/src/assets/treasure-hunt/prize.jpg differ diff --git a/src/assets/treasure-hunt/treasure.jpg b/src/assets/treasure-hunt/treasure.jpg new file mode 100644 index 000000000..ff610b4b7 Binary files /dev/null and b/src/assets/treasure-hunt/treasure.jpg differ diff --git a/src/components/AssetCard.tsx b/src/components/AssetCard.tsx index 2ee405417..3f1d8b91d 100644 --- a/src/components/AssetCard.tsx +++ b/src/components/AssetCard.tsx @@ -1,9 +1,12 @@ import React, { memo, ReactElement } from 'react'; +import { useSelector } from 'react-redux'; import { View, GestureResponderEvent, StyleSheet } from 'react-native'; + import { ClockIcon } from '../styles/icons'; import { Text01M, Caption13M } from '../styles/text'; import { TouchableOpacity } from '../styles/components'; import Money from '../components/Money'; +import { openChannelIdsSelector } from '../store/reselect/lightning'; const AssetCard = ({ name, @@ -22,6 +25,10 @@ const AssetCard = ({ testID?: string; onPress: (event: GestureResponderEvent) => void; }): ReactElement => { + const openChannelIds = useSelector(openChannelIdsSelector); + + const isTransferToSavings = openChannelIds.length === 0; + return ( - {/* TODO: change color depending on pending savings/spending */} - {pending && } + {pending && ( + + )} - {requireBiometrics && !requirePin ? ( + if (requireBiometrics && !requirePin) { + return ( + onSuccess?.()} onFailure={(): void => setRequireBiometrics(false)} /> - ) : ( - onSuccess?.()} - /> - )} + + ); + } + + return ( + + onSuccess?.()} + /> ); }; diff --git a/src/components/AuthWidget.tsx b/src/components/AuthWidget.tsx index eb3ee39e5..c15cdcb30 100644 --- a/src/components/AuthWidget.tsx +++ b/src/components/AuthWidget.tsx @@ -5,58 +5,61 @@ import React, { useMemo, useState, } from 'react'; -import { Linking, StyleSheet } from 'react-native'; +import { View, Linking, StyleSheet, StyleProp, ViewStyle } from 'react-native'; import { Client } from '@synonymdev/slashtags-auth'; import { useTranslation } from 'react-i18next'; -import { useProfile, useSelectedSlashtag } from '../hooks/slashtags'; -import { TouchableOpacity, View } from '../styles/components'; import { Text01M } from '../styles/text'; -import { KeyIcon, ListIcon, TrashIcon } from '../styles/icons'; +import { TouchableOpacity } from '../styles/components'; +import { KeyIcon, ListIcon, SettingsIcon, TrashIcon } from '../styles/icons'; +import { useProfile, useSelectedSlashtag } from '../hooks/slashtags'; import { showToast } from '../utils/notifications'; -import Button from './Button'; -import ProfileImage from './ProfileImage'; import { IWidget } from '../store/types/widgets'; import { deleteWidget } from '../store/actions/widgets'; +import Button from './Button'; import Dialog from './Dialog'; +import ProfileImage from './ProfileImage'; +import { rootNavigation } from '../navigation/root/RootNavigator'; const AuthWidget = ({ url, widget, isEditing = false, + style, + testID, onLongPress, onPressIn, - testID, }: { url: string; widget: IWidget; isEditing?: boolean; + style?: StyleProp; + testID?: string; onLongPress?: () => void; onPressIn?: () => void; - testID?: string; }): ReactElement => { const { t } = useTranslation('slashtags'); - const [showButtons, setShowButtons] = useState(false); - const [showDialog, setShowDialog] = useState(false); - - const { profile } = useProfile(url, { resolve: true }); const { slashtag } = useSelectedSlashtag(); + const { profile } = useProfile(url); + const [showDialog, setShowDialog] = useState(false); + const [isSigningIn, setIsSigningIn] = useState(false); - const switchShowButtons = (): void => { - setShowButtons((b) => !b); - }; + const client = useMemo(() => new Client(slashtag), [slashtag]); - const client = useMemo(() => { - return new Client(slashtag); - }, [slashtag]); + const onSignIn = useCallback(async () => { + setIsSigningIn(true); - const openMagicLink = useCallback(async () => { const magiclink = await client.magiclink(url).catch((e: Error) => { + console.log(e.message); + const message = + e.message === 'channel closed' + ? t('auth_error_peer') + : `An error occurred: ${e.message}`; + showToast({ type: 'error', title: t('auth_error_link'), - description: - e.message === 'channel closed' ? t('auth_error_peer') : e.message, + description: message, }); }); @@ -64,59 +67,87 @@ const AuthWidget = ({ Linking.openURL(magiclink.url).catch((e) => { showToast({ type: 'error', - title: t('auth_error_open'), + title: t('auth_error_link'), description: e.message, }); }); } + + setIsSigningIn(false); }, [client, url, t]); + const onEdit = (): void => { + rootNavigation.navigate('Widget', { url }); + }; + const onDelete = (): void => { setShowDialog(true); }; return ( - - - - {profile?.name || ' '} - - - {showButtons && widget.magiclink && !isEditing && ( - -