From c5e07d81809f48793095ec381f7740549083dc3e Mon Sep 17 00:00:00 2001 From: N3TC4T Date: Thu, 28 Sep 2023 12:18:50 +0200 Subject: [PATCH 01/12] chore: update yarn.lock --- yarn.lock | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/yarn.lock b/yarn.lock index bf7709993..c16f1557f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8478,7 +8478,7 @@ react-cosmos-core@6.0.0-beta.5: lodash-es "^4.17.21" react-is "^18.2.0" -react-cosmos-native@^6.0.0-beta.6: +react-cosmos-native@6.0.0-beta.6: version "6.0.0-beta.6" resolved "https://registry.yarnpkg.com/react-cosmos-native/-/react-cosmos-native-6.0.0-beta.6.tgz#9a2385aa8f78e97064aa3e1e7610205588c525ec" integrity sha512-sd7WkXg2AUfwcMH8CzwMZRv4qL2Wwa9dEuObKF8owlLzHwduZZbUZ10DovyAq/PYbiX9kYvpq9cFGisEr30jDQ== @@ -8503,7 +8503,7 @@ react-cosmos-ui@6.0.0-beta.6: lodash-es "^4.17.21" react-cosmos-core "6.0.0-beta.5" -react-cosmos@^6.0.0-beta.6: +react-cosmos@6.0.0-beta.6: version "6.0.0-beta.6" resolved "https://registry.yarnpkg.com/react-cosmos/-/react-cosmos-6.0.0-beta.6.tgz#8c8be3b01b9cf97367290510ca2727775e3a08c0" integrity sha512-dhzxa78JSXQhY5Cpb+W5SMJVYxTEW0P5gkBaAfL2nRTkofKy/PBLUJB20Y5rglSMKdGlhOS3wmjF4IFknhQ9Uw== @@ -8532,14 +8532,6 @@ react-devtools-core@^4.27.2: shell-quote "^1.6.1" ws "^7" -react-dom@^18.2.0: - version "18.2.0" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" - integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== - dependencies: - loose-envify "^1.1.0" - scheduler "^0.23.0" - "react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.0.0, react-is@^18.2.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" @@ -8583,11 +8575,6 @@ react-native-navigation@7.37.0: react-lifecycles-compat "2.0.0" tslib "1.9.3" -react-native-safe-area-context@^4.7.2: - version "4.7.2" - resolved "https://registry.yarnpkg.com/react-native-safe-area-context/-/react-native-safe-area-context-4.7.2.tgz#1673aa99b6a9235e7faaf5a248e69795d6e54e07" - integrity sha512-5fy/hRNJ7bI/U2SliOeKf0D80J4lXPc1NsRiNS7Xaz8YTnqlzWib1ViItkwKPfufe54YKzVBMmM32RpdzvO2gg== - react-native@0.72.5: version "0.72.5" resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.72.5.tgz#2c343fa6f3ead362cf07376634a33a4078864357" From 620632a61e3dba5896dff27be1b2d7cd71a7640d Mon Sep 17 00:00:00 2001 From: N3TC4T Date: Thu, 28 Sep 2023 12:23:01 +0200 Subject: [PATCH 02/12] fix: remove storybook --- .../__stories__/AmountText.stories.tsx | 41 --------- .../General/__stories__/Avatar.stories.tsx | 19 ----- .../General/__stories__/Badge.stories.tsx | 83 ------------------- .../__stories__/DragSortableView.stories.tsx | 54 ------------ .../__stories__/InfoMessage.stories.tsx | 39 --------- .../__stories__/SegmentButton.stories.tsx | 17 ---- .../__stories__/RecipientElement.stories.tsx | 32 ------- .../Modules/__stories__/XAppList.stories.tsx | 38 --------- 8 files changed, 323 deletions(-) delete mode 100644 src/components/General/__stories__/AmountText.stories.tsx delete mode 100644 src/components/General/__stories__/Avatar.stories.tsx delete mode 100644 src/components/General/__stories__/Badge.stories.tsx delete mode 100644 src/components/General/__stories__/DragSortableView.stories.tsx delete mode 100644 src/components/General/__stories__/InfoMessage.stories.tsx delete mode 100644 src/components/General/__stories__/SegmentButton.stories.tsx delete mode 100644 src/components/Modules/__stories__/RecipientElement.stories.tsx delete mode 100644 src/components/Modules/__stories__/XAppList.stories.tsx diff --git a/src/components/General/__stories__/AmountText.stories.tsx b/src/components/General/__stories__/AmountText.stories.tsx deleted file mode 100644 index 4f0cc14e2..000000000 --- a/src/components/General/__stories__/AmountText.stories.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react'; -import { storiesOf } from '@storybook/react-native'; - -import withPropsCombinations from '../../../../storybook/matrix'; -import { withContainer } from '../../../../storybook/decoration'; - -import { AmountText } from '../AmountText'; - -const VALUES = ['0.00000000000001', '2.2222', '99999.123456'] as const; - -storiesOf('AmountText', module) - .addDecorator(withContainer) - .add( - 'original', - withPropsCombinations(AmountText, { - value: VALUES, - }), - ) - .add( - 'withPrefix', - withPropsCombinations(AmountText, { - value: VALUES, - prefix: ['-'], - }), - ) - .add( - 'withCurrency', - withPropsCombinations(AmountText, { - value: VALUES, - currency: ['USD'], - }), - ) - .add('discreet', () => ) - .add( - 'immutable;', - withPropsCombinations(AmountText, { - value: VALUES, - currency: ['USD'], - immutable: [true], - }), - ); diff --git a/src/components/General/__stories__/Avatar.stories.tsx b/src/components/General/__stories__/Avatar.stories.tsx deleted file mode 100644 index aa41497ca..000000000 --- a/src/components/General/__stories__/Avatar.stories.tsx +++ /dev/null @@ -1,19 +0,0 @@ -/* eslint-disable implicit-arrow-linebreak */ -/* eslint-disable import/no-relative-packages */ - -import React from 'react'; -import { storiesOf } from '@storybook/react-native'; - -import { withContainer } from '../../../../storybook/decoration'; - -import { Avatar } from '../Avatar'; - -const URI = { uri: 'https://xumm.app/assets/icons/currencies/ex-bitstamp.png' }; - -storiesOf('Avatar', module) - .addDecorator(withContainer) - .add('Original', () => ) - .add('With Border', () => ) - .add('Big', () => ) - .add('badge', () => ) - .add('badge Color', () => ); diff --git a/src/components/General/__stories__/Badge.stories.tsx b/src/components/General/__stories__/Badge.stories.tsx deleted file mode 100644 index 4ec4563f9..000000000 --- a/src/components/General/__stories__/Badge.stories.tsx +++ /dev/null @@ -1,83 +0,0 @@ -/* eslint-disable implicit-arrow-linebreak */ - -import { storiesOf } from '@storybook/react-native'; - -import withPropsCombinations from '../../../../storybook/matrix'; -import { withContainer } from '../../../../storybook/decoration'; - -import { Badge } from '../Badge'; - -const SIZES = ['small', 'medium', 'large'] as const; - -storiesOf('Badge', module) - .addDecorator(withContainer) - .add( - 'Bithomp', - withPropsCombinations(Badge, { - size: SIZES, - type: ['bithomp'], - }), - ) - .add( - 'XRPScan', - withPropsCombinations(Badge, { - size: SIZES, - type: ['xrpscan'], - }), - ) - .add( - 'XRPLNS', - withPropsCombinations(Badge, { - size: SIZES, - type: ['xrplns'], - }), - ) - .add( - 'PayId', - withPropsCombinations(Badge, { - size: SIZES, - type: ['payid'], - }), - ) - .add( - 'FioProtocol', - withPropsCombinations(Badge, { - size: SIZES, - type: ['fioprotocol'], - }), - ) - .add( - 'Contacts', - withPropsCombinations(Badge, { - size: SIZES, - type: ['contacts'], - }), - ) - .add( - 'Accounts', - withPropsCombinations(Badge, { - size: SIZES, - type: ['accounts'], - }), - ) - .add( - 'Success', - withPropsCombinations(Badge, { - size: SIZES, - type: ['success'], - }), - ) - .add( - 'Open', - withPropsCombinations(Badge, { - size: SIZES, - type: ['open'], - }), - ) - .add( - 'Planned', - withPropsCombinations(Badge, { - size: SIZES, - type: ['planned'], - }), - ); diff --git a/src/components/General/__stories__/DragSortableView.stories.tsx b/src/components/General/__stories__/DragSortableView.stories.tsx deleted file mode 100644 index 10501b004..000000000 --- a/src/components/General/__stories__/DragSortableView.stories.tsx +++ /dev/null @@ -1,54 +0,0 @@ -/* eslint-disable react-native/no-color-literals */ -/* eslint-disable react-native/no-inline-styles */ -/* eslint-disable implicit-arrow-linebreak */ -/* eslint-disable import/no-relative-packages */ - -import React, { Component } from 'react'; -import { View, Text } from 'react-native'; -import { storiesOf } from '@storybook/react-native'; - -import { AppSizes } from '@theme'; - -import { SortableFlatList } from '../SortableFlatList'; - -import { withContainer } from '../../../../storybook/decoration'; - -const DATASOURCE = Array(1000).fill('ITEM '); - -const ITEM_HEIGHT = 100; -const ITEM_WIDTH = AppSizes.screen.width * 0.9; - -class Item extends Component { - render() { - const { item, index } = this.props; - return ( - - {`${item} ${index}`} - - ); - } -} - -storiesOf('SortableFlatList', module) - .addDecorator(withContainer) - .add('Default', () => ( - - `${item}${index}`} - renderItem={({ item, index }) => } - sortable={false} - /> - - )); diff --git a/src/components/General/__stories__/InfoMessage.stories.tsx b/src/components/General/__stories__/InfoMessage.stories.tsx deleted file mode 100644 index 728d55dbc..000000000 --- a/src/components/General/__stories__/InfoMessage.stories.tsx +++ /dev/null @@ -1,39 +0,0 @@ -/* eslint-disable implicit-arrow-linebreak */ -import { storiesOf } from '@storybook/react-native'; - -import withPropsCombinations from '../../../../storybook/matrix'; -import { withContainer } from '../../../../storybook/decoration'; - -import { InfoMessage } from '../InfoMessage'; - -const TYPES = ['info', 'warning', 'error', 'success', 'neutral']; -const label = 'Lorem Ipsum is simply dummy text of the printing and typesetting industry'; - -storiesOf('InfoMessage', module) - .addDecorator(withContainer) - .add( - 'All', - withPropsCombinations(InfoMessage, { - type: TYPES, - label: [label], - icon: ['IconInfo'], - }), - ) - .add( - 'Flat', - withPropsCombinations(InfoMessage, { - type: TYPES, - label: [label], - icon: ['IconInfo'], - flat: [true], - }), - ) - .add( - 'With More info', - withPropsCombinations(InfoMessage, { - type: TYPES, - label: [label], - icon: ['IconInfo'], - onMoreButtonPress: [() => {}], - }), - ); diff --git a/src/components/General/__stories__/SegmentButton.stories.tsx b/src/components/General/__stories__/SegmentButton.stories.tsx deleted file mode 100644 index b82d94cf3..000000000 --- a/src/components/General/__stories__/SegmentButton.stories.tsx +++ /dev/null @@ -1,17 +0,0 @@ -/* eslint-disable react-native/no-color-literals */ -/* eslint-disable react-native/no-inline-styles */ -/* eslint-disable implicit-arrow-linebreak */ - -import React from 'react'; -import { action } from '@storybook/addon-actions'; -import { storiesOf } from '@storybook/react-native'; - -import { SegmentButton } from '../SegmentButton'; - -import { withContainer } from '../../../../storybook/decoration'; - -const BUTTONS = ['All', 'Planned', 'Requests']; - -storiesOf('SegmentButton', module) - .addDecorator(withContainer) - .add('Three Button', () => ); diff --git a/src/components/Modules/__stories__/RecipientElement.stories.tsx b/src/components/Modules/__stories__/RecipientElement.stories.tsx deleted file mode 100644 index 1cb7a797f..000000000 --- a/src/components/Modules/__stories__/RecipientElement.stories.tsx +++ /dev/null @@ -1,32 +0,0 @@ -/* eslint-disable spellcheck/spell-checker */ -import React from 'react'; -import { action } from '@storybook/addon-actions'; -import { storiesOf } from '@storybook/react-native'; - -import { withContainer } from '../../../../storybook/decoration'; - -import { RecipientElement } from '../RecipientElement'; - -export const recipientData = { - id: 'id', - address: 'rwiETSee2wMz3SBnAG8hkMsCgvGy9LWbZ1', - name: 'Wietse', - source: 'contacts', -}; - -storiesOf('RecipientElement', module) - .addDecorator(withContainer) - .add('default', () => ) - .add('WithSource', () => ( - - )) - .add('Selected', () => ( - - )); diff --git a/src/components/Modules/__stories__/XAppList.stories.tsx b/src/components/Modules/__stories__/XAppList.stories.tsx deleted file mode 100644 index 3c57d0ae2..000000000 --- a/src/components/Modules/__stories__/XAppList.stories.tsx +++ /dev/null @@ -1,38 +0,0 @@ -/* eslint-disable spellcheck/spell-checker */ -import React from 'react'; -import { storiesOf } from '@storybook/react-native'; - -import { withContainer } from '../../../../storybook/decoration'; - -import { XAppShortList } from '../XAppShortList'; - -const apps = [ - { - _type: 'featured', - icon: 'https://xumm-cdn.imgix.net/app-logo/03ca3259-5f08-446c-a528-d37c478c0e26.png', - identifier: 'xumm.tangem-backup', - title: 'Tangem Backup', - }, - { - _type: 'featured', - icon: 'https://xumm-cdn.imgix.net/app-logo/2c6110b9-0806-4396-aa68-73604113257e.png', - identifier: 'gatehub.trade', - title: 'GateHub Trade', - }, - { - _type: 'featured', - icon: 'https://xumm-cdn.imgix.net/app-logo/dd9e7154-9d09-4027-babf-e6cee6c9ac6b.png', - identifier: 'nixer.escrow', - title: 'Escrow creator', - }, - { - _type: 'featured', - icon: 'https://xumm-cdn.imgix.net/app-logo/7de2eddb-78f2-40dd-88ff-8d365aed52c0.png', - identifier: 'xumm.push-beta', - title: 'Pro Push Beta', - }, -]; - -storiesOf('XAppList', module) - .addDecorator(withContainer) - .add('default', () => ); From 949cc6bbe0f407b887d37721c320458b04c4bf8f Mon Sep 17 00:00:00 2001 From: N3TC4T Date: Thu, 28 Sep 2023 12:24:01 +0200 Subject: [PATCH 03/12] chore: add cosmos XAppShortList --- .cosmos/cosmos.imports.ts | 26 ++++++++++-------- .cosmos/fixtures/XAppShortList.fixture.tsx | 32 ++++++++++++++++++++++ 2 files changed, 46 insertions(+), 12 deletions(-) create mode 100644 .cosmos/fixtures/XAppShortList.fixture.tsx diff --git a/.cosmos/cosmos.imports.ts b/.cosmos/cosmos.imports.ts index f99ba00f2..e4848540f 100644 --- a/.cosmos/cosmos.imports.ts +++ b/.cosmos/cosmos.imports.ts @@ -5,12 +5,13 @@ import { RendererConfig, UserModuleWrappers } from 'react-cosmos-core'; import './prepare'; -import * as fixture0 from './fixtures/SegmentButton.fixture'; -import * as fixture1 from './fixtures/RecipientElement.fixture'; -import * as fixture2 from './fixtures/InfoMessage.fixture'; -import * as fixture3 from './fixtures/Badge.fixture'; -import * as fixture4 from './fixtures/Avatar.fixture'; -import * as fixture5 from './fixtures/AmountText.fixture'; +import * as fixture0 from './fixtures/XAppShortList.fixture'; +import * as fixture1 from './fixtures/SegmentButton.fixture'; +import * as fixture2 from './fixtures/RecipientElement.fixture'; +import * as fixture3 from './fixtures/InfoMessage.fixture'; +import * as fixture4 from './fixtures/Badge.fixture'; +import * as fixture5 from './fixtures/Avatar.fixture'; +import * as fixture6 from './fixtures/AmountText.fixture'; export const rendererConfig: RendererConfig = { "playgroundUrl": "http://localhost:5001", @@ -18,12 +19,13 @@ export const rendererConfig: RendererConfig = { }; const fixtures = { - 'fixtures/SegmentButton.fixture.tsx': { module: fixture0 }, - 'fixtures/RecipientElement.fixture.tsx': { module: fixture1 }, - 'fixtures/InfoMessage.fixture.tsx': { module: fixture2 }, - 'fixtures/Badge.fixture.tsx': { module: fixture3 }, - 'fixtures/Avatar.fixture.tsx': { module: fixture4 }, - 'fixtures/AmountText.fixture.tsx': { module: fixture5 } + 'fixtures/XAppShortList.fixture.tsx': { module: fixture0 }, + 'fixtures/SegmentButton.fixture.tsx': { module: fixture1 }, + 'fixtures/RecipientElement.fixture.tsx': { module: fixture2 }, + 'fixtures/InfoMessage.fixture.tsx': { module: fixture3 }, + 'fixtures/Badge.fixture.tsx': { module: fixture4 }, + 'fixtures/Avatar.fixture.tsx': { module: fixture5 }, + 'fixtures/AmountText.fixture.tsx': { module: fixture6 } }; const decorators = {}; diff --git a/.cosmos/fixtures/XAppShortList.fixture.tsx b/.cosmos/fixtures/XAppShortList.fixture.tsx new file mode 100644 index 000000000..1165a8b61 --- /dev/null +++ b/.cosmos/fixtures/XAppShortList.fixture.tsx @@ -0,0 +1,32 @@ +import React from 'react'; + +import { XAppShortList } from '@components/Modules/XAppShortList'; + +const apps = [ + { + _type: 'featured', + icon: 'https://xumm-cdn.imgix.net/app-logo/03ca3259-5f08-446c-a528-d37c478c0e26.png', + identifier: 'xumm.tangem-backup', + title: 'Tangem Backup', + }, + { + _type: 'featured', + icon: 'https://xumm-cdn.imgix.net/app-logo/2c6110b9-0806-4396-aa68-73604113257e.png', + identifier: 'gatehub.trade', + title: 'GateHub Trade', + }, + { + _type: 'featured', + icon: 'https://xumm-cdn.imgix.net/app-logo/dd9e7154-9d09-4027-babf-e6cee6c9ac6b.png', + identifier: 'nixer.escrow', + title: 'Escrow creator', + }, + { + _type: 'featured', + icon: 'https://xumm-cdn.imgix.net/app-logo/7de2eddb-78f2-40dd-88ff-8d365aed52c0.png', + identifier: 'xumm.push-beta', + title: 'Pro Push Beta', + }, +]; + +export default ; From 52e47667a190921a87a3e5c0a917322abb1141ab Mon Sep 17 00:00:00 2001 From: N3TC4T Date: Thu, 28 Sep 2023 13:31:24 +0200 Subject: [PATCH 04/12] chore: replace ripple-binary-codec with AccountLib.binary --- package.json | 1 - src/common/libs/payload/digest/codec.ts | 4 ++-- .../Import/Steps/VerifySignature/VerifySignatureStep.tsx | 7 +++++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 3f41e74f4..5eb0b4b3d 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,6 @@ "react-native-interactable": "2.0.1", "react-native-navigation": "7.37.0", "realm": "11.10.2", - "ripple-address-codec": "4.3.0", "ripple-binary-codec": "npm:xrpl-binary-codec-prerelease@3.0.0", "tangem-sdk-react-native": "2.3.1", "uuid": "9.0.1", diff --git a/src/common/libs/payload/digest/codec.ts b/src/common/libs/payload/digest/codec.ts index 102fec34e..562fc2883 100644 --- a/src/common/libs/payload/digest/codec.ts +++ b/src/common/libs/payload/digest/codec.ts @@ -3,7 +3,7 @@ */ import { mapKeys } from 'lodash'; -import * as codec from 'ripple-binary-codec'; +import * as AccountLib from 'xrpl-accountlib'; import { GetDeviceUniqueId } from '@common/helpers/device'; import { SHA1 } from '@common/libs/crypto'; @@ -44,7 +44,7 @@ class DigestCodecWithSHA1 extends Digest { // calculate checksum // @ts-ignore - const checksum = codec[hashEncodingMethod](normalizedRequestJson); + const checksum = AccountLib.binary[hashEncodingMethod](normalizedRequestJson); // calculate digest SHA1{checksum}+{deviceId} const deviceId = GetDeviceUniqueId(); diff --git a/src/screens/Account/Add/Import/Steps/VerifySignature/VerifySignatureStep.tsx b/src/screens/Account/Add/Import/Steps/VerifySignature/VerifySignatureStep.tsx index 8d6ac768e..5d3a2c3c2 100644 --- a/src/screens/Account/Add/Import/Steps/VerifySignature/VerifySignatureStep.tsx +++ b/src/screens/Account/Add/Import/Steps/VerifySignature/VerifySignatureStep.tsx @@ -6,7 +6,6 @@ import React, { Component } from 'react'; import { SafeAreaView, View, Text, Alert, Image } from 'react-native'; import * as AccountLib from 'xrpl-accountlib'; -import { encodeForSigning } from 'ripple-binary-codec'; import RNTangemSdk, { Card } from 'tangem-sdk-react-native'; @@ -103,7 +102,11 @@ class VerifySignatureStep extends Component { const { txJson, signedTransaction } = AccountLib.rawSigning.complete(preparedTx, signature); // verify signature - const verified = AccountLib.utils.verifySignature(encodeForSigning(txJson), txJson.TxnSignature, publicKey); + const verified = AccountLib.utils.verifySignature( + AccountLib.binary.encodeForSigning(txJson), + txJson.TxnSignature, + publicKey, + ); // set signature if verified if (verified) { From 2899ee22c3de4ec0f2d1fea8f69becb27550cda6 Mon Sep 17 00:00:00 2001 From: N3TC4T Date: Tue, 3 Oct 2023 18:32:33 +0200 Subject: [PATCH 05/12] chore: upgrade Realm + more unit tests --- ios/Podfile.lock | 4 +- ios/Xaman.xcodeproj/project.pbxproj | 2 + jest.setup.js | 4 +- package.json | 7 +- src/screens/Account/List/AccountsListView.tsx | 4 +- .../DestinationPicker/DestinationPicker.tsx | 9 +- .../SwitchAccount/SwitchAccountModal.tsx | 4 +- .../Send/Steps/Recipient/RecipientStep.tsx | 10 +- .../Settings/AddressBook/AddressBookView.tsx | 8 +- .../AddressBook/Edit/EditContactView.tsx | 2 +- .../ChangePasscode/ChangePasscodeView.tsx | 7 +- .../fixture/{data.ts => v1.test.data.ts} | 0 .../__tests__/integration/migrations.test.ts | 133 +++--- src/store/__tests__/repositories/base.test.ts | 410 ++++++++++++++++++ src/store/__tests__/utils/index.ts | 31 ++ src/store/models/objects/account.ts | 57 ++- src/store/models/objects/accountDetails.ts | 12 +- src/store/models/objects/contact.ts | 11 +- src/store/models/objects/core.ts | 32 +- src/store/models/objects/counterParty.ts | 13 +- src/store/models/objects/currency.ts | 26 ++ src/store/models/objects/network.ts | 42 +- src/store/models/objects/node.ts | 17 +- src/store/models/objects/profile.ts | 16 + src/store/models/objects/trustLine.ts | 48 +- src/store/models/schemas/v1/account.ts | 8 +- src/store/models/schemas/v1/contact.ts | 4 +- src/store/models/schemas/v1/core.ts | 3 +- src/store/models/schemas/v1/counterParty.ts | 4 +- src/store/models/schemas/v1/currency.ts | 10 +- src/store/models/schemas/v1/profile.ts | 6 +- src/store/models/schemas/v1/trustLine.ts | 6 +- src/store/models/schemas/v10/core.ts | 4 +- src/store/models/schemas/v11/trustLine.ts | 4 +- src/store/models/schemas/v12/account.ts | 4 +- src/store/models/schemas/v12/core.ts | 4 +- src/store/models/schemas/v12/currency.ts | 4 +- src/store/models/schemas/v13/profile.ts | 4 +- src/store/models/schemas/v14/account.ts | 6 +- .../models/schemas/v14/accountDetails.ts | 3 +- src/store/models/schemas/v14/core.ts | 3 +- src/store/models/schemas/v14/network.ts | 4 +- src/store/models/schemas/v14/node.ts | 4 +- src/store/models/schemas/v14/trustLine.ts | 4 +- src/store/models/schemas/v2/account.ts | 4 +- src/store/models/schemas/v2/core.ts | 4 +- src/store/models/schemas/v3/core.ts | 4 +- src/store/models/schemas/v4/core.ts | 4 +- src/store/models/schemas/v4/counterParty.ts | 4 +- src/store/models/schemas/v4/currency.ts | 4 +- src/store/models/schemas/v4/profile.ts | 4 +- src/store/models/schemas/v4/trustLine.ts | 6 +- src/store/models/schemas/v5/core.ts | 4 +- src/store/models/schemas/v6/account.ts | 5 +- src/store/models/schemas/v6/profile.ts | 6 +- src/store/models/schemas/v7/account.ts | 5 +- src/store/models/schemas/v7/core.ts | 4 +- src/store/models/schemas/v8/customNode.ts | 4 +- src/store/models/schemas/v9/trustLine.ts | 4 +- src/store/repositories/account.ts | 84 ++-- src/store/repositories/base.ts | 284 +++++++----- src/store/repositories/contact.ts | 10 +- src/store/repositories/core.ts | 8 +- src/store/repositories/counterParty.ts | 10 +- src/store/repositories/currency.ts | 13 +- src/store/repositories/network.ts | 14 +- src/store/repositories/node.ts | 14 +- src/store/repositories/profile.ts | 38 +- src/store/repositories/trustLine.ts | 6 +- src/store/types.ts | 9 +- yarn.lock | 372 ++-------------- 71 files changed, 1193 insertions(+), 733 deletions(-) rename src/store/__tests__/fixture/{data.ts => v1.test.data.ts} (100%) create mode 100644 src/store/__tests__/repositories/base.test.ts create mode 100644 src/store/__tests__/utils/index.ts diff --git a/ios/Podfile.lock b/ios/Podfile.lock index da13074fa..0a76a7aa8 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -549,7 +549,7 @@ PODS: - React-CoreModules - React-RCTImage - React-RCTText - - RealmJS (11.10.2): + - RealmJS (12.2.0): - React - RNFBAnalytics (18.5.0): - Firebase/AnalyticsWithoutAdIdSupport (= 10.15.0) @@ -819,7 +819,7 @@ SPEC CHECKSUMS: React-utils: 40cdd9acee23230df5b78bf78a64ce8a2084c2d0 ReactCommon: 4ec7cc40d091eed0a510755619940b6a03cb507b ReactNativeNavigation: 2651d0bc1479e9df0789b077ae93320dc2c9b73b - RealmJS: 73a36da3cbbe85e1bdcbf55683172b51f35070d3 + RealmJS: a480cb21d4b0ba82c843c77c3a211770411eec75 RNFBAnalytics: 403369236afba1d1083e236c17028d8a62f31527 RNFBApp: 082df9f8ffba99f23f7aee3ecb64df703e7e1adb RNFBCrashlytics: 9d5b36a7e280178c5d61695936cb812005bf17ad diff --git a/ios/Xaman.xcodeproj/project.pbxproj b/ios/Xaman.xcodeproj/project.pbxproj index 0a4d9a55e..02431824c 100644 --- a/ios/Xaman.xcodeproj/project.pbxproj +++ b/ios/Xaman.xcodeproj/project.pbxproj @@ -1234,6 +1234,7 @@ OTHER_LDFLAGS = ( "$(inherited)", " ", + "-Wl -ld_classic ", ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; @@ -1306,6 +1307,7 @@ OTHER_LDFLAGS = ( "$(inherited)", " ", + "-Wl -ld_classic ", ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; diff --git a/jest.setup.js b/jest.setup.js index 43e02b59c..09c4d8f5b 100644 --- a/jest.setup.js +++ b/jest.setup.js @@ -1,9 +1,11 @@ -// hide console.log console.error in jest tests +/* Hide console.log console.error in jest tests */ jest.spyOn(global.console, 'log').mockImplementation(() => jest.fn()); jest.spyOn(global.console, 'error').mockImplementation(() => jest.fn()); jest.spyOn(global.console, 'debug').mockImplementation(() => jest.fn()); +/* Mock RNN event listeners */ jest.mock('react-native/Libraries/EventEmitter/NativeEventEmitter.js'); jest.mock('react-native/Libraries/Animated/NativeAnimatedHelper'); +/* Realm */ process.env.REALM_DISABLE_ANALYTICS = true; diff --git a/package.json b/package.json index 5eb0b4b3d..a5e2fe9ea 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "react-native-camera": "4.2.1", "react-native-interactable": "2.0.1", "react-native-navigation": "7.37.0", - "realm": "11.10.2", + "realm": "12.2.0", "ripple-binary-codec": "npm:xrpl-binary-codec-prerelease@3.0.0", "tangem-sdk-react-native": "2.3.1", "uuid": "9.0.1", @@ -50,14 +50,13 @@ "@types/i18n-js": "3.8.5", "@types/jest": "29.5.5", "@types/lodash": "4.14.199", - "@types/react": "18.2.22", + "@types/react": "18.2.23", "@types/react-native": "0.72.2", - "@types/react-test-renderer": "18.0.2", + "@types/react-test-renderer": "18.0.3", "@types/uuid": "9.0.4", "@typescript-eslint/eslint-plugin": "6.7.2", "@typescript-eslint/parser": "6.7.2", "babel-jest": "29.7.0", - "babel-loader": "9.1.3", "babel-plugin-rewrite-require": "1.14.5", "babel-plugin-transform-remove-console": "6.9.4", "coveralls": "3.1.1", diff --git a/src/screens/Account/List/AccountsListView.tsx b/src/screens/Account/List/AccountsListView.tsx index eb70b2df4..8ea44a7e1 100644 --- a/src/screens/Account/List/AccountsListView.tsx +++ b/src/screens/Account/List/AccountsListView.tsx @@ -3,7 +3,7 @@ */ import { find } from 'lodash'; -import { Results } from 'realm'; +import Realm from 'realm'; import React, { Component } from 'react'; import { View, Text, Image, ImageBackground, InteractionManager } from 'react-native'; @@ -38,7 +38,7 @@ import styles from './styles'; export interface Props {} export interface State { - accounts: Results; + accounts: Realm.Results; dataSource: any; signableAccount: Array; reorderEnabled: boolean; diff --git a/src/screens/Modal/DestinationPicker/DestinationPicker.tsx b/src/screens/Modal/DestinationPicker/DestinationPicker.tsx index 22d67cf6e..17f510019 100644 --- a/src/screens/Modal/DestinationPicker/DestinationPicker.tsx +++ b/src/screens/Modal/DestinationPicker/DestinationPicker.tsx @@ -2,9 +2,10 @@ * Destination Picker modal */ -import React, { Component } from 'react'; -import { Results } from 'realm'; import { isEmpty, flatMap, get, uniqBy, toNumber } from 'lodash'; +import Realm from 'realm'; + +import React, { Component } from 'react'; import { View, Text, SectionList, BackHandler, NativeEventSubscription } from 'react-native'; import { StringType, XrplDestination } from 'xumm-string-decode'; @@ -43,8 +44,8 @@ export interface State { isSearching: boolean; isLoading: boolean; searchText: string; - accounts: Results; - contacts: Results; + accounts: Realm.Results; + contacts: Realm.Results; dataSource: any[]; destination: Destination; destinationInfo: AccountInfoType; diff --git a/src/screens/Overlay/SwitchAccount/SwitchAccountModal.tsx b/src/screens/Overlay/SwitchAccount/SwitchAccountModal.tsx index cbc452b30..020c7ca3a 100644 --- a/src/screens/Overlay/SwitchAccount/SwitchAccountModal.tsx +++ b/src/screens/Overlay/SwitchAccount/SwitchAccountModal.tsx @@ -2,8 +2,8 @@ * Switch Account Overlay */ -import { Results } from 'realm'; import { find } from 'lodash'; +import Realm from 'realm'; import React, { Component } from 'react'; import { View, Text, ScrollView } from 'react-native'; @@ -34,7 +34,7 @@ export interface Props { export interface State { defaultAccount: AccountModel; - accounts: Results; + accounts: Realm.Results; signableAccount: Array; contentHeight: number; paddingBottom: number; diff --git a/src/screens/Send/Steps/Recipient/RecipientStep.tsx b/src/screens/Send/Steps/Recipient/RecipientStep.tsx index 82f7558a3..8edf5257a 100644 --- a/src/screens/Send/Steps/Recipient/RecipientStep.tsx +++ b/src/screens/Send/Steps/Recipient/RecipientStep.tsx @@ -2,10 +2,12 @@ * Send / Recipient step */ -import React, { Component } from 'react'; -import { Results } from 'realm'; import { isEmpty, flatMap, remove, get, uniqBy, toNumber } from 'lodash'; +import Realm from 'realm'; + +import React, { Component } from 'react'; import { View, Text, SectionList, Alert, RefreshControl } from 'react-native'; + import { StringType, XrplDestination } from 'xumm-string-decode'; import { AccountRepository, ContactRepository } from '@store/repositories'; @@ -42,8 +44,8 @@ export interface State { isSearching: boolean; isLoading: boolean; searchText: string; - accounts: Results; - contacts: Results; + accounts: Realm.Results; + contacts: Realm.Results; dataSource: any[]; } diff --git a/src/screens/Settings/AddressBook/AddressBookView.tsx b/src/screens/Settings/AddressBook/AddressBookView.tsx index 8442a226d..24da25d29 100644 --- a/src/screens/Settings/AddressBook/AddressBookView.tsx +++ b/src/screens/Settings/AddressBook/AddressBookView.tsx @@ -3,8 +3,8 @@ */ import { isEmpty, sortBy, flatMap } from 'lodash'; +import Realm from 'realm'; import Fuse from 'fuse.js'; -import { Results } from 'realm'; import React, { Component } from 'react'; import { View, Text, SectionList, Image, ImageBackground } from 'react-native'; @@ -30,7 +30,7 @@ import styles from './styles'; export interface Props {} export interface State { - contacts: Results; + contacts: Realm.Results; dataSource: any; } @@ -76,7 +76,7 @@ class AddressBookView extends Component { }); } - convertContactsArrayToMap = (contacts: Results) => { + convertContactsArrayToMap = (contacts: Realm.Results) => { const contactsCategoryMap = [] as any; sortBy(contacts, 'name').forEach((item) => { @@ -114,7 +114,7 @@ class AddressBookView extends Component { return; } - const contactsFilter = new Fuse(contacts, { + const contactsFilter = new Fuse(contacts as unknown as readonly unknown[], { keys: ['name', 'address', 'destinationTag'], shouldSort: false, includeScore: false, diff --git a/src/screens/Settings/AddressBook/Edit/EditContactView.tsx b/src/screens/Settings/AddressBook/Edit/EditContactView.tsx index 36b5d469c..345bab8ba 100644 --- a/src/screens/Settings/AddressBook/Edit/EditContactView.tsx +++ b/src/screens/Settings/AddressBook/Edit/EditContactView.tsx @@ -171,7 +171,7 @@ class EditContactView extends Component { { text: Localize.t('global.doIt'), onPress: () => { - ContactRepository.deleteBy('id', contact.id); + ContactRepository.deleteById(contact.id); Toast(Localize.t('settings.contactSuccessDeleted')); Navigator.pop(); diff --git a/src/screens/Settings/Security/ChangePasscode/ChangePasscodeView.tsx b/src/screens/Settings/Security/ChangePasscode/ChangePasscodeView.tsx index 35ec28c9b..254211cbd 100644 --- a/src/screens/Settings/Security/ChangePasscode/ChangePasscodeView.tsx +++ b/src/screens/Settings/Security/ChangePasscode/ChangePasscodeView.tsx @@ -3,11 +3,9 @@ */ import React, { Component } from 'react'; -import { Results } from 'realm'; import { View, Text, Alert, InteractionManager } from 'react-native'; import { CoreRepository, AccountRepository } from '@store/repositories'; -import { AccountModel } from '@store/models'; import { EncryptionLevels } from '@store/types'; import Vault from '@common/libs/vault'; @@ -141,10 +139,7 @@ class ChangePasscodeView extends Component { } // get all accounts with encryption level Passcode - const accounts = AccountRepository.findBy( - 'encryptionLevel', - EncryptionLevels.Passcode, - ) as Results; + const accounts = AccountRepository.findBy('encryptionLevel', EncryptionLevels.Passcode); const passcodeVaultNames = accounts.map((account) => account.publicKey); diff --git a/src/store/__tests__/fixture/data.ts b/src/store/__tests__/fixture/v1.test.data.ts similarity index 100% rename from src/store/__tests__/fixture/data.ts rename to src/store/__tests__/fixture/v1.test.data.ts diff --git a/src/store/__tests__/integration/migrations.test.ts b/src/store/__tests__/integration/migrations.test.ts index 366f9143f..012fb2787 100644 --- a/src/store/__tests__/integration/migrations.test.ts +++ b/src/store/__tests__/integration/migrations.test.ts @@ -1,63 +1,45 @@ import Realm from 'realm'; -import { flatMap, find } from 'lodash'; -// eslint-disable-next-line -import { AppConfig, NetworkConfig } from '../../../common/constants'; +// @ts-expect-error +// eslint-disable-next-line import/no-unresolved +import { AppConfig, NetworkConfig } from '@constants'; -import SampleData from '../fixture/data'; -import { AccountTypes } from '../../types'; - -const path = './.jest/realmTemp'; - -const getSchema = (version: number) => { - return find(require('../../models/schemas').default, { schemaVersion: version }); -}; +import SampleDataV1 from '../fixture/v1.test.data'; -const getObject = (instance: Realm, type: string): any => { - return instance.objects(type)[0] as any; -}; +import RealmTestUtils from '../utils'; -const getInstance = (version: number): Realm => { - return new Realm({ - path, - schema: flatMap(getSchema(version).schemas, 'schema'), - schemaVersion: getSchema(version).schemaVersion, - onMigration: getSchema(version).migration, - }); -}; +import { AccountTypes } from '../../types'; describe('Storage', () => { describe('Migrations', () => { beforeAll(() => { // populate the first schema version - const instance = new Realm({ - path, - schema: flatMap(getSchema(1).schemas, 'schema'), - schemaVersion: getSchema(1).schemaVersion, - }); + const initInstance = RealmTestUtils.getRealmInstanceWithVersion(1); - // should be the first schema version - expect(instance.schemaVersion).toBe(1); - - // populate sample data - instance.write(() => { - Object.keys(SampleData).forEach((schemaName) => { + // populate + initInstance.write(() => { + Object.keys(SampleDataV1).forEach((schemaName) => { // @ts-ignore - instance.create(schemaName, SampleData[schemaName], Realm.UpdateMode.All); + initInstance.create(schemaName, SampleDataV1[schemaName], Realm.UpdateMode.All); }); }); - instance.close(); + // should be the first schema version + expect(initInstance.schemaVersion).toBe(1); + + // close + initInstance.close(); }); it('should run v2 migrations successfully', async () => { - const instance = getInstance(2); + const instance = RealmTestUtils.getRealmInstanceWithVersion(2); + expect(instance.schemaVersion).toBe(2); - const account = getObject(instance, 'Account'); + const account = RealmTestUtils.getFirstModelItem(instance, 'Account'); expect(account.order).toBe(0); - const core = getObject(instance, 'Core'); + const core = RealmTestUtils.getFirstModelItem(instance, 'Core'); expect(core.lastPasscodeFailedTimestamp).toBe(0); expect(core.passcodeFailedAttempts).toBe(0); expect(core.lastUnlockedTimestamp).toBe(0); @@ -68,10 +50,10 @@ describe('Storage', () => { }); it('should run v3 migrations successfully', async () => { - const instance = getInstance(3); + const instance = RealmTestUtils.getRealmInstanceWithVersion(3); expect(instance.schemaVersion).toBe(3); - const core = getObject(instance, 'Core'); + const core = RealmTestUtils.getFirstModelItem(instance, 'Core'); expect(core.hapticFeedback).toBe(true); expect(core.defaultExplorer).toBe(NetworkConfig.legacy.defaultExplorer); @@ -79,22 +61,22 @@ describe('Storage', () => { }); it('should run v4 migrations successfully', async () => { - const instance = getInstance(4); + const instance = RealmTestUtils.getRealmInstanceWithVersion(4); expect(instance.schemaVersion).toBe(4); - const core = getObject(instance, 'Core'); + const core = RealmTestUtils.getFirstModelItem(instance, 'Core'); expect(core.discreetMode).toBe(false); - const counterParty = getObject(instance, 'CounterParty'); + const counterParty = RealmTestUtils.getFirstModelItem(instance, 'CounterParty'); expect(counterParty.shortlist).toBe(true); - const currency = getObject(instance, 'Currency'); + const currency = RealmTestUtils.getFirstModelItem(instance, 'Currency'); expect(currency.shortlist).toBe(true); - const profile = getObject(instance, 'Profile'); + const profile = RealmTestUtils.getFirstModelItem(instance, 'Profile'); expect(profile.deviceUUID).toBe(''); - const trustLine = getObject(instance, 'TrustLine'); + const trustLine = RealmTestUtils.getFirstModelItem(instance, 'TrustLine'); expect(trustLine.limit_peer).toBe(0); expect(trustLine.authorized).toBe(false); expect(trustLine.peer_authorized).toBe(false); @@ -106,36 +88,36 @@ describe('Storage', () => { }); it('should run v5 migrations successfully', async () => { - const instance = getInstance(5); + const instance = RealmTestUtils.getRealmInstanceWithVersion(5); expect(instance.schemaVersion).toBe(5); - const core = getObject(instance, 'Core'); + const core = RealmTestUtils.getFirstModelItem(instance, 'Core'); expect(core.useSystemSeparators).toBe(true); instance.close(); }); it('should run v6 migrations successfully', async () => { - const instance = getInstance(6); + const instance = RealmTestUtils.getRealmInstanceWithVersion(6); expect(instance.schemaVersion).toBe(6); - const account = getObject(instance, 'Account'); + const account = RealmTestUtils.getFirstModelItem(instance, 'Account'); expect(account.type).toBe(AccountTypes.Regular); - const profile = getObject(instance, 'Profile'); + const profile = RealmTestUtils.getFirstModelItem(instance, 'Profile'); expect(profile.hasPro).toBe(false); instance.close(); }); it('should run v7 migrations successfully', async () => { - const instance = getInstance(7); + const instance = RealmTestUtils.getRealmInstanceWithVersion(7); expect(instance.schemaVersion).toBe(7); - const account = getObject(instance, 'Account'); + const account = RealmTestUtils.getFirstModelItem(instance, 'Account'); expect(account.hidden).toBe(false); - const core = getObject(instance, 'Core'); + const core = RealmTestUtils.getFirstModelItem(instance, 'Core'); expect(core.currency).toBe(AppConfig.defaultCurrency); expect(core.developerMode).toBe(false); @@ -143,20 +125,20 @@ describe('Storage', () => { }); it('should run v8 migrations successfully', async () => { - const instance = getInstance(8); + const instance = RealmTestUtils.getRealmInstanceWithVersion(8); expect(instance.schemaVersion).toBe(8); instance.close(); }); it('should run v9 migrations successfully', async () => { - const instance = getInstance(9); + const instance = RealmTestUtils.getRealmInstanceWithVersion(9); expect(instance.schemaVersion).toBe(9); - const currency = getObject(instance, 'Currency'); + const currency = RealmTestUtils.getFirstModelItem(instance, 'Currency'); expect(currency.id).toBe(`${currency.issuer}.${currency.currency}`); - const trustLine = getObject(instance, 'TrustLine'); + const trustLine = RealmTestUtils.getFirstModelItem(instance, 'TrustLine'); const account = trustLine.linkingObjects('Account', 'lines')[0] as any; expect(trustLine.id).toBe(`${account.address}.${trustLine.currency.id}`); @@ -164,10 +146,10 @@ describe('Storage', () => { }); it('should run v10 migrations successfully', async () => { - const instance = getInstance(10); + const instance = RealmTestUtils.getRealmInstanceWithVersion(10); expect(instance.schemaVersion).toBe(10); - const core = getObject(instance, 'Core'); + const core = RealmTestUtils.getFirstModelItem(instance, 'Core'); expect(core.baseReserve).toBe(NetworkConfig.baseReserve); expect(core.ownerReserve).toBe(NetworkConfig.ownerReserve); @@ -175,10 +157,10 @@ describe('Storage', () => { }); it('should run v11 migrations successfully', async () => { - const instance = getInstance(11); + const instance = RealmTestUtils.getRealmInstanceWithVersion(11); expect(instance.schemaVersion).toBe(11); - const trustLine = getObject(instance, 'TrustLine'); + const trustLine = RealmTestUtils.getFirstModelItem(instance, 'TrustLine'); expect(trustLine.order).toBe(0); expect(trustLine.favorite).toBe(false); @@ -187,34 +169,34 @@ describe('Storage', () => { it('should run v12 migrations successfully', async () => { // for the check if migration running fine - const oldInstance = getInstance(11); - const oldCore = getObject(oldInstance, 'Core'); + const oldInstance = RealmTestUtils.getRealmInstanceWithVersion(11); + const oldCore = RealmTestUtils.getFirstModelItem(oldInstance, 'Core'); oldInstance.write(() => { oldCore.defaultExplorer = 'xrplorer'; }); oldInstance.close(); - const instance = getInstance(12); + const instance = RealmTestUtils.getRealmInstanceWithVersion(12); expect(instance.schemaVersion).toBe(12); - const account = getObject(instance, 'Account'); + const account = RealmTestUtils.getFirstModelItem(instance, 'Account'); expect(account.encryptionVersion).toBe(1); - const core = getObject(instance, 'Core'); + const core = RealmTestUtils.getFirstModelItem(instance, 'Core'); expect(core.showFiatPanel).toBe(true); expect(core.defaultExplorer).toBe('bithomp'); - const currency = getObject(instance, 'Currency'); + const currency = RealmTestUtils.getFirstModelItem(instance, 'Currency'); expect(currency.xapp_identifier).toBe(null); instance.close(); }); it('should run v13 migrations successfully', async () => { - const instance = getInstance(13); + const instance = RealmTestUtils.getRealmInstanceWithVersion(13); expect(instance.schemaVersion).toBe(13); - const profile = getObject(instance, 'Profile'); + const profile = RealmTestUtils.getFirstModelItem(instance, 'Profile'); expect(profile.refreshToken).toBe(null); expect(profile.bearerHash).toBe(null); @@ -222,12 +204,12 @@ describe('Storage', () => { }); it('should run v14 migrations successfully', async () => { - const instance = getInstance(14); + const instance = RealmTestUtils.getRealmInstanceWithVersion(14); expect(instance.schemaVersion).toBe(14); - const account = getObject(instance, 'Account'); - const accountDetails = getObject(instance, 'AccountDetails'); - const core = getObject(instance, 'Core'); + const account = RealmTestUtils.getFirstModelItem(instance, 'Account'); + const accountDetails = RealmTestUtils.getFirstModelItem(instance, 'AccountDetails'); + const core = RealmTestUtils.getFirstModelItem(instance, 'Core'); expect(instance.objects('Network').length).toBeGreaterThan(0); expect(instance.objects('Node').length).toBeGreaterThan(0); @@ -245,8 +227,7 @@ describe('Storage', () => { }); afterAll(() => { - // sort the migrations - Realm.deleteFile({ path }); + RealmTestUtils.cleanup(); }); }); }); diff --git a/src/store/__tests__/repositories/base.test.ts b/src/store/__tests__/repositories/base.test.ts new file mode 100644 index 000000000..1a9158c63 --- /dev/null +++ b/src/store/__tests__/repositories/base.test.ts @@ -0,0 +1,410 @@ +/* eslint-disable spellcheck/spell-checker */ + +import Realm from 'realm'; + +import BaseRepository from '../../repositories/base'; + +// Helpers +const generatedIds = Array.from(Array(10).keys()); +function generateRandomData() { + const getRandomInt = (min: number, max: number) => Math.floor(Math.random() * (max - min + 1)) + min; + const getRandomId = () => { + let id; + while (true) { + id = Math.floor(Math.random() * 1000); + if (!generatedIds.includes(id)) { + break; + } + } + generatedIds.push(id); + return id; + }; + const getRandomString = (length: number) => { + const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; + let result = ''; + for (let i = 0; i < length; i++) { + result += characters.charAt(Math.floor(Math.random() * characters.length)); + } + return result; + }; + + return { + id: getRandomId(), + name: getRandomString(10), + isActive: Math.random() < 0.5, + age: getRandomInt(18, 99), + }; +} + +describe('BaseRepository', () => { + let repo: any; + let model: any; + let instance: any; + let data: any; + + beforeAll(() => { + jest.useFakeTimers(); + + repo = new BaseRepository(); + model = class SampleModel extends Realm.Object { + // @ts-ignore + public static schema: Realm.ObjectSchema = { + name: 'SampleSchema', + primaryKey: 'id', + properties: { + id: 'int', + name: 'string', + isActive: 'bool?', + age: 'int?', + }, + }; + }; + instance = new Realm({ schema: [model], path: './.jest/sampleRealmInMemory', inMemory: true }); + data = [ + { id: 1, name: 'John', isActive: false, age: 20 }, + { id: 2, name: 'Ari', isActive: true, age: 20 }, + { id: 3, name: 'Baltazar', isActive: false, age: 30 }, + { id: 4, name: 'Alice', isActive: true, age: 35 }, + { id: 5, name: 'Bob', isActive: false, age: 40 }, + ]; + repo.realm = instance; + // populate + repo.realm.write(() => { + data.forEach((d: any) => { + repo.realm.create(model, d, Realm.UpdateMode.All); + }); + }); + repo.model = model; + }); + + describe('normalizeQuery', () => { + it('should return the same string if query is a string', () => { + expect(repo.normalizeQuery('name == "John"')).toBe('name == "John"'); + }); + + it('should convert a valid query object to a string', () => { + const queryObject = { + name: 'John', + isActive: true, + age: 30, + }; + expect(repo.normalizeQuery(queryObject)).toBe('name == "John" AND isActive == "true" AND age == "30"'); + }); + + it('should throw an error for unrecognized value set', () => { + const invalidQueryObject = { + name: {}, + }; + expect(() => repo.normalizeQuery(invalidQueryObject)).toThrowError( + /Unrecognized value set for query param/, + ); + }); + + it('should throw an error for unrecognized query field names', () => { + const unrecognizedQueryObject = { + name: 'John', + unknownField: 'value', + }; + expect(() => repo.normalizeQuery(unrecognizedQueryObject)).toThrowError(/Unrecognized query field names/); + }); + }); + + describe('safeWrite', () => { + let mockIsInTransaction: jest.SpyInstance; + let mockWrite: jest.SpyInstance; + let setTimeoutSpy: jest.SpyInstance; + + beforeEach(() => { + mockIsInTransaction = jest.spyOn(repo.realm, 'isInTransaction', 'get'); + mockWrite = jest.spyOn(repo.realm, 'write'); + setTimeoutSpy = jest.spyOn(global, 'setTimeout'); + }); + + afterEach(() => { + mockIsInTransaction.mockRestore(); + mockWrite.mockRestore(); + setTimeoutSpy.mockRestore(); + }); + + it('should directly call the write method if realm is not in transaction', () => { + mockIsInTransaction.mockReturnValue(false); + + const mockFunction = jest.fn(); + repo.safeWrite(mockFunction); + + expect(mockWrite).toHaveBeenCalledWith(mockFunction); + expect(mockFunction).toHaveBeenCalled(); + }); + + it('should wait until realm is not in transaction to call the write method', () => { + // eslint-disable-next-line max-len + mockIsInTransaction.mockReturnValueOnce(true).mockReturnValue(false); + + const mockFunction = jest.fn(); + repo.safeWrite(mockFunction); + + expect(setTimeoutSpy).toHaveBeenCalledTimes(1); + jest.runAllTimers(); + + expect(mockWrite).toHaveBeenCalledWith(mockFunction); + expect(mockFunction).toHaveBeenCalled(); + }); + }); + + describe('count', () => { + it('should return count of all objects', () => { + const spyObjects = jest.spyOn(repo.realm, 'objects'); + expect(repo.count()).toEqual(data.length); + expect(spyObjects).toBeCalledWith(model); + spyObjects.mockClear(); + }); + }); + + describe('findAll', () => { + it('should return all objects', () => { + const spyObjects = jest.spyOn(repo.realm, 'objects'); + expect(repo.findAll().toJSON()).toStrictEqual(data); + expect(spyObjects).toBeCalledWith(model); + spyObjects.mockClear(); + }); + }); + + describe('findOne', () => { + it('should return undefined if no results are found', () => { + expect(repo.findOne('name == "Jacob"')).toBeUndefined(); + }); + + it('should return a single result if one match is found', () => { + expect(repo.findOne('name == "Alice"').toJSON()).toMatchObject(data.find((d: any) => d.name === 'Alice')); + }); + + it('should throw an error if multiple matches are found', () => { + expect(() => repo.findOne('age == "20"')).toThrow('Got more than one result'); + }); + }); + + describe('query', () => { + let objectsSpy: jest.SpyInstance; + let normalizeQuerySpy: jest.SpyInstance; + + beforeEach(() => { + objectsSpy = jest.spyOn(repo.realm, 'objects'); + normalizeQuerySpy = jest.spyOn(repo, 'normalizeQuery'); + }); + + it('should call realm.objects and filtered with correct arguments for string query', () => { + const testQuery = 'name == "John"'; + repo.query(testQuery); + + expect(objectsSpy).toHaveBeenCalledWith(model); + expect(normalizeQuerySpy).toHaveBeenCalledWith(testQuery); + }); + + it('should call realm.objects and filtered with correct arguments for object query', () => { + const testQuery = { name: 'John' }; + repo.query(testQuery); + + expect(objectsSpy).toHaveBeenCalledWith(model); + expect(normalizeQuerySpy).toHaveBeenCalledWith(testQuery); + }); + + afterEach(() => { + objectsSpy.mockRestore(); + normalizeQuerySpy.mockRestore(); + }); + }); + + describe('upsert', () => { + it('should throw an error if data does not contain an id', async () => { + await expect(repo.upsert({ name: 'John' })).rejects.toThrow('ID require primary key to be set'); + }); + + it('should use UpdateMode.All if object exists', async () => { + const realmCreateSpy = jest.spyOn(repo.realm, 'create'); + await repo.upsert(data[0]); + expect(realmCreateSpy).toHaveBeenCalledWith(model, data[0], Realm.UpdateMode.All); + }); + + it('should use UpdateMode.Never if object does not exist', async () => { + const sampleData = generateRandomData(); + const realmCreateSpy = jest.spyOn(repo.realm, 'create'); + await repo.upsert(sampleData); + expect(realmCreateSpy).toHaveBeenCalledWith(model, sampleData, Realm.UpdateMode.Never); + }); + + it('should reject the promise if there is an error in safeWrite', async () => { + const sampleData = generateRandomData(); + const error = new Error('Some unexpected error'); + const safeWriteSpy = jest.spyOn(repo, 'safeWrite').mockImplementation(() => { + throw error; + }); + await expect(repo.upsert(sampleData)).rejects.toEqual(error); + safeWriteSpy.mockRestore(); + }); + }); + + describe('create', () => { + it('should resolve with the created object', async () => { + const mockData = generateRandomData(); + await expect(repo.create(mockData)).resolves.toEqual(mockData); + }); + + it('should call realm.create with UpdateMode.All when update is true', async () => { + const createSpy = jest.spyOn(repo.realm, 'create'); + const mockData = generateRandomData(); + await repo.create(mockData, true); + expect(createSpy).toHaveBeenCalledWith(model, mockData, Realm.UpdateMode.All); + createSpy.mockClear(); + }); + + it('should call realm.create with UpdateMode.Never when update is false or undefined', async () => { + const createSpy = jest.spyOn(repo.realm, 'create'); + const mockData1 = generateRandomData(); + await repo.create(mockData1); + expect(createSpy).toHaveBeenCalledWith(model, mockData1, Realm.UpdateMode.Never); + const mockData2 = generateRandomData(); + await repo.create(mockData2, false); + expect(createSpy).toHaveBeenCalledWith(model, mockData2, Realm.UpdateMode.Never); + createSpy.mockClear(); + }); + }); + + describe('createList', () => { + it('should return the dataList if no errors are thrown', () => { + const mockDataList = Array.from(Array(5), generateRandomData); + const result = repo.createList(mockDataList); + expect(result).toBe(mockDataList); + }); + + it('should call realm.create with UpdateMode.All when update is true', async () => { + const createSpy = jest.spyOn(repo.realm, 'create'); + const mockDataList = Array.from(Array(3), generateRandomData); + await repo.createList(mockDataList, true); + expect(createSpy).toHaveBeenCalledTimes(3); + mockDataList.forEach((d) => { + expect(createSpy).toHaveBeenCalledWith(model, d, Realm.UpdateMode.All); + }); + createSpy.mockClear(); + }); + + it('should call realm.create with UpdateMode.Never when update is false or undefined', async () => { + const createSpy = jest.spyOn(repo.realm, 'create'); + const mockDataList1 = Array.from(Array(3), generateRandomData); + await repo.createList(mockDataList1); + expect(createSpy).toHaveBeenCalledTimes(3); + mockDataList1.forEach((d) => { + expect(createSpy).toHaveBeenCalledWith(model, d, Realm.UpdateMode.Never); + }); + createSpy.mockReset(); + const mockDataList2 = Array.from(Array(3), generateRandomData); + await repo.createList(mockDataList2, false); + expect(createSpy).toHaveBeenCalledTimes(3); + mockDataList2.forEach((d) => { + expect(createSpy).toHaveBeenCalledWith(model, d, Realm.UpdateMode.Never); + }); + createSpy.mockClear(); + }); + }); + + describe('deleteById', () => { + it('should call this.delete with the found item if an item is found', async () => { + const objectForPrimaryKeySpy = jest.spyOn(repo.realm, 'objectForPrimaryKey'); + const deleteSpy = jest.spyOn(repo, 'delete'); + + const item = generateRandomData(); + await repo.create(item); + await repo.deleteById(item.id); + + expect(objectForPrimaryKeySpy).toHaveBeenCalledWith(model, item.id); + expect(deleteSpy).toHaveBeenCalledTimes(1); + + objectForPrimaryKeySpy.mockClear(); + deleteSpy.mockClear(); + }); + + it('should throw an "Item not found!" error if no item is found', async () => { + const objectForPrimaryKeySpy = jest.spyOn(repo.realm, 'objectForPrimaryKey'); + const deleteSpy = jest.spyOn(repo, 'delete'); + + await expect(repo.deleteById(999999)).rejects.toThrow('Item not found!'); + expect(objectForPrimaryKeySpy).toHaveBeenCalledWith(model, 999999); + expect(deleteSpy).not.toHaveBeenCalled(); + + objectForPrimaryKeySpy.mockClear(); + deleteSpy.mockClear(); + }); + }); + + describe('delete', () => { + it('should call safeWrite and realm.delete with the provided object and resolve the promise', async () => { + const realmDeleteSpy = jest.spyOn(repo.realm, 'delete'); + const safeWriteSpy = jest.spyOn(repo, 'safeWrite'); + + const item = generateRandomData(); + const createdItem = await repo.create(item); + + await expect(repo.delete(createdItem)).resolves.toBeUndefined(); + expect(safeWriteSpy).toHaveBeenCalled(); + expect(realmDeleteSpy).toHaveBeenCalledTimes(1); + + realmDeleteSpy.mockClear(); + safeWriteSpy.mockRestore(); + }); + + it('should reject the promise and handle error properly if an error occurs during deletion', async () => { + const realmDeleteSpy = jest.spyOn(repo.realm, 'delete'); + + const objectMock = { id: 1 }; + const errorMock = new Error('Deletion error'); + const safeWriteSpy = jest.spyOn(repo, 'safeWrite').mockImplementation(() => { + throw errorMock; + }); + await expect(repo.delete(objectMock)).rejects.toThrow(errorMock); + expect(repo.safeWrite).toHaveBeenCalled(); + expect(realmDeleteSpy).not.toHaveBeenCalled(); + safeWriteSpy.mockRestore(); + }); + }); + + describe('deleteAll', () => { + it('should call safeWrite and realm.delete with the found items and return true if items are found', () => { + const findAllSpy = jest.spyOn(repo, 'findAll'); + const safeWriteSpy = jest.spyOn(repo, 'safeWrite'); + const realmDeleteSpy = jest.spyOn(repo.realm, 'delete'); + + const items = repo.findAll(); + const result = repo.deleteAll(); + + expect(findAllSpy).toHaveBeenCalled(); + expect(safeWriteSpy).toHaveBeenCalled(); + expect(realmDeleteSpy.mock.calls[0][0]).toHaveLength(items.length); + expect(result).toBe(true); + + findAllSpy.mockClear(); + safeWriteSpy.mockClear(); + realmDeleteSpy.mockClear(); + }); + + it('should not call safeWrite and realm.delete and still return true if no items are found', () => { + const safeWriteSpy = jest.spyOn(repo, 'safeWrite'); + const realmDeleteSpy = jest.spyOn(repo.realm, 'delete'); + + repo.findAll = jest.fn(() => []); + const result = repo.deleteAll(); + + expect(repo.findAll).toHaveBeenCalled(); + expect(safeWriteSpy).not.toHaveBeenCalled(); + expect(realmDeleteSpy).not.toHaveBeenCalled(); + expect(result).toBe(true); + }); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + afterAll(() => { + instance.close(); + Realm.deleteFile({ path: './.jest/sampleRealmInMemory' }); + }); +}); diff --git a/src/store/__tests__/utils/index.ts b/src/store/__tests__/utils/index.ts new file mode 100644 index 000000000..da30448db --- /dev/null +++ b/src/store/__tests__/utils/index.ts @@ -0,0 +1,31 @@ +import { find, flatMap } from 'lodash'; +import Realm from 'realm'; + +const RealmTestUtils = { + RealmPath: './.jest/realmTemp', + EncryptionKey: new Int8Array(64), + + getFirstModelItem: (instance: Realm, schemaName: string): any => { + return instance.objects(schemaName)[0] as any; + }, + + getSchemaWithVersion: (version: number) => { + return find(require('../../models/schemas').default, { schemaVersion: version }); + }, + + getRealmInstanceWithVersion: (version: number): Realm => { + return new Realm({ + path: RealmTestUtils.RealmPath, + schema: flatMap(RealmTestUtils.getSchemaWithVersion(version).schemas, 'schema'), + encryptionKey: RealmTestUtils.EncryptionKey, + schemaVersion: RealmTestUtils.getSchemaWithVersion(version).schemaVersion, + onMigration: RealmTestUtils.getSchemaWithVersion(version).migration, + }); + }, + + cleanup: () => { + Realm.deleteFile({ path: RealmTestUtils.RealmPath }); + }, +}; + +export default RealmTestUtils; diff --git a/src/store/models/objects/account.ts b/src/store/models/objects/account.ts index b629a072e..12c563929 100644 --- a/src/store/models/objects/account.ts +++ b/src/store/models/objects/account.ts @@ -1,6 +1,10 @@ /** - * Account Model + * Account model + * + * @class + * @extends Realm.Object */ + import get from 'lodash/get'; import Realm from 'realm'; @@ -17,26 +21,44 @@ import AccountDetails from './accountDetails'; class Account extends Realm.Object { public static schema: Realm.ObjectSchema = AccountSchema.schema; + /** Type of the account. @type {AccountTypes} */ public type: AccountTypes; + /** Address of the account. @type {string} */ public address: string; + /** Label of the account. @type {string} */ public label: string; + /** Public key associated with the account. @type {string} */ public publicKey: string; + /** Access level of the account. @type {AccessLevels} */ public accessLevel: AccessLevels; + /** Encryption level for the account. @type {EncryptionLevels} */ public encryptionLevel: EncryptionLevels; + /** CipherEncryption version used to encrypt this account. @type {number} */ public encryptionVersion: number; + /** Index Order of the account when showing as list, if any. @type {number?} */ public order?: number; + /** Whether the account is hidden in the accounts list. @type {boolean?} */ public hidden?: boolean; + /** Additional information string about the account (contains stringify version of object). @type {string?} */ public additionalInfoString?: string; + /** Detailed data associated with the account. @type {AccountDetails[]?} */ + public details?: AccountDetails[]; + /** Date when the account was registered. @type {Date?} */ public registerAt?: Date; + /** Date when the account was last updated. @type {Date?} */ public updatedAt?: Date; - // NOT accessible publicly - private details?: AccountDetails[]; - + /** + * Returns the parsed additional information object. + * @type {Object} + */ get additionalInfo(): Object { return JSON.parse(this.additionalInfoString); } + /** + * Set the additional information after stringify it. + */ set additionalInfo(data: Object) { this.additionalInfoString = JSON.stringify(data); } @@ -73,7 +95,11 @@ class Account extends Realm.Object { return get(this.getDetails(), 'lines', undefined); } - public getStateVersion() { + /** + * Calculates and retrieves the state version of the account based on its details. + * @returns {number} The state version identifier. + */ + public getStateVersion(): number { const details = this.getDetails(); if (!details) { @@ -81,24 +107,25 @@ class Account extends Realm.Object { } const state = JSON.stringify(details.toJSON(), (key, val) => { - if (val != null && typeof val === 'object' && ['owners', 'currency'].includes(key)) { - return; + if (val !== null && typeof val === 'object') { + if (['owners', 'currency'].includes(key)) { + return undefined; // exclude these fields + } } - // eslint-disable-next-line consistent-return return val; }); return StringIdentifier(state); } + /** + * Gets the details of the account based on the selected network. + * @private + * @returns {AccountDetails} The details of the account for the selected network. + */ + private getDetails(): AccountDetails { const network = CoreRepository.getSelectedNetwork(); - const details = this.details.filter((d) => d.network.id === network.id); - - if (details) { - return details[0]; - } - - return undefined; + return this.details.find((d) => d.network.id === network.id); } } diff --git a/src/store/models/objects/accountDetails.ts b/src/store/models/objects/accountDetails.ts index 009b40425..27aaab9f9 100644 --- a/src/store/models/objects/accountDetails.ts +++ b/src/store/models/objects/accountDetails.ts @@ -1,5 +1,8 @@ /** - * Account Details Model + * Account Details model + * + * @class + * @extends Realm.Object */ import Realm from 'realm'; @@ -26,10 +29,17 @@ class AccountDetails extends Realm.Object { public registerAt?: Date; public updatedAt?: Date; + /** + * Returns the parsed flags as an object. + * @type {Object} + */ get flags(): { [key: string]: boolean } { return JSON.parse(this.flagsString); } + /** + * Set the flags after stringify them. + */ set flags(data: { [key: string]: boolean }) { this.flagsString = JSON.stringify(data); } diff --git a/src/store/models/objects/contact.ts b/src/store/models/objects/contact.ts index 555550ef6..940ad257e 100644 --- a/src/store/models/objects/contact.ts +++ b/src/store/models/objects/contact.ts @@ -1,5 +1,8 @@ /** - * Contact Model ( aka Address book ) + * Contact model + * + * @class + * @extends Realm.Object */ import Realm from 'realm'; @@ -10,11 +13,17 @@ import { ContactSchema } from '@store/models/schemas/latest'; class Contact extends Realm.Object { public static schema: Realm.ObjectSchema = ContactSchema.schema; + /** Unique identifier of the contact UUID v4. @type {string} */ public id: string; + /** Address associated with the contact. @type {string} */ public address: string; + /** Name of the contact. @type {string} */ public name: string; + /** Destination tag associated with the contact. @type {string} */ public destinationTag: string; + /** Date when the contact was registered. @type {Date?} */ public registerAt?: Date; + /** Date when the contact was last updated. @type {Date?} */ public updatedAt?: Date; } diff --git a/src/store/models/objects/core.ts b/src/store/models/objects/core.ts index a11d5b5b2..6b4306072 100644 --- a/src/store/models/objects/core.ts +++ b/src/store/models/objects/core.ts @@ -1,5 +1,8 @@ /** - * App Core Model + * Core model + * + * @class + * @extends Realm.Object */ import Realm from 'realm'; @@ -12,24 +15,43 @@ import NetworkModel from '@store/models/objects/network'; class Core extends Realm.Object { public static schema: Realm.ObjectSchema = CoreSchema.schema; + /** Indicates whether the app has been initialized. */ public initialized: boolean; - public passcode: string; + /** hashed passcode. */ + public passcode?: string; + /** Minutes after which auto-locking should occur. */ public minutesAutoLock: number; - public lastPasscodeFailedTimestamp: number; + /** Timestamp of the last failed passcode attempt. */ + public lastPasscodeFailedTimestamp?: number; + /** Number of failed passcode attempts. */ public passcodeFailedAttempts: number; - public lastUnlockedTimestamp: number; + /** Timestamp of the last successful unlock event. */ + public lastUnlockedTimestamp?: number; + /** Flag to indicate whether to purge data on brute force attempts. */ public purgeOnBruteForce: boolean; - public biometricMethod: BiometryType; + /** Type of biometric method used, if any. */ + public biometricMethod?: BiometryType; + /** Indicates if fallback to passcode is allowed. */ public passcodeFallback: boolean; + /** Selected language for the application. */ public language: string; + /** Selected currency for the application. */ public currency: string; + /** Selected network for the application. */ public network: NetworkModel; + /** Default account for the application. */ public account: any; + /** Indicates whether haptic feedback is enabled. */ public hapticFeedback: boolean; + /** Indicates whether discreet mode is enabled. */ public discreetMode: boolean; + /** Indicates whether the reserve panel should be displayed in home screen. */ public showReservePanel: boolean; + /** Indicates if system separators should be used. */ public useSystemSeparators: boolean; + /** Indicates if developer mode is enabled. */ public developerMode: boolean; + /** Selected theme for the application. */ public theme: Themes; } diff --git a/src/store/models/objects/counterParty.ts b/src/store/models/objects/counterParty.ts index 327117e08..5da44c862 100644 --- a/src/store/models/objects/counterParty.ts +++ b/src/store/models/objects/counterParty.ts @@ -1,5 +1,8 @@ /** * Counter Parties Model + * + * @class + * @extends Realm.Object */ import Realm from 'realm'; @@ -10,14 +13,22 @@ import { CounterPartySchema } from '@store/models/schemas/latest'; class CounterParty extends Realm.Object { public static schema: Realm.ObjectSchema = CounterPartySchema.schema; + /** Unique identifier for the counterparty. */ public id: number; + /** Name of the counterparty. */ public name: string; + /** Domain associated with the counterparty. */ public domain: string; + /** Avatar image URL or path for the counterparty. */ public avatar: string; + /** Indicates if the counterparty is on the shortlist. */ public shortlist: boolean; + /** List of currencies associated with the counterparty. */ + public currencies?: any[]; + /** Date when the counterparty was registered. */ public registerAt?: Date; + /** Date when the counterparty details were last updated. */ public updatedAt?: Date; - public currencies?: any[]; } export default CounterParty; diff --git a/src/store/models/objects/currency.ts b/src/store/models/objects/currency.ts index a45f15f2c..31fb283af 100644 --- a/src/store/models/objects/currency.ts +++ b/src/store/models/objects/currency.ts @@ -1,5 +1,8 @@ /** * Currency Model + * + * @class + * @extends Realm.Object */ import Realm from 'realm'; @@ -10,12 +13,35 @@ import { CurrencySchema } from '@store/models/schemas/latest'; class Currency extends Realm.Object { public static schema: Realm.ObjectSchema = CurrencySchema.schema; + /** + * A unique identifier representing this specific currency instance. + * combination of the currency's code and its issuer. + */ public id: string; + /** + * The account of entity or organization responsible for issuing and maintaining the currency. + */ public issuer: string; + /** + * The standardized code or symbol of the currency (ex: EUR), + */ public currency: string; + /** + * A descriptive, user-friendly name for the currency (ex: Euro) , + */ public name: string; + /** + * URL or local path pointing to an image that visually represents + */ public avatar: string; + /** + * A flag indicating whether this currency is highlighted or preferred in the shortlist + */ public shortlist: boolean; + /** + * An optional xApp identifier linking the currency to a corresponding xApp + */ + public xapp_identifier?: string; } export default Currency; diff --git a/src/store/models/objects/network.ts b/src/store/models/objects/network.ts index 93ebd7e3f..0ab18b8eb 100644 --- a/src/store/models/objects/network.ts +++ b/src/store/models/objects/network.ts @@ -1,5 +1,8 @@ /** * Network Model + * + * @class + * @extends Realm.Object */ import Realm from 'realm'; @@ -9,35 +12,61 @@ import { Amendments } from '@common/constants'; import { NetworkSchema } from '@store/models/schemas/latest'; /* Dictionary ==================================================================== */ -interface NativeAsset extends Realm.Dictionary { +interface NativeAsset { asset: string; icon: string; iconSquare: string; } + /* Model ==================================================================== */ class Network extends Realm.Object { - public static schema: Realm.ObjectSchema = NetworkSchema.schema; + static schema: Realm.ObjectSchema = NetworkSchema.schema; + /** Unique identifier representing this specific network. (ex: 1) */ public id: number; + /** A unique key identifier for the network. (ex: TESTNET) */ public key: string; + /** Descriptive name of the network. */ public name: string; + /** Hex Color associated with the network. */ public color: string; + /** Specifies the type or category of the network. */ public type: string; + /** Details of the network's native asset, including its visual representation. */ public nativeAsset: NativeAsset; + /** The basic reserve required on this network. */ public baseReserve: number; + /** The owner's reserve requirement for this network. */ public ownerReserve: number; + /** Default node associated with this network. */ public defaultNode: any; + /** Collection of nodes that belong to this network. */ public nodes: any[]; + /** List of amendments that apply to this network. */ public amendments?: string[]; + /** Serialized string representation of network definitions. */ public definitionsString?: string; + /** Date when the network was initially registered in the system. */ public registerAt?: Date; + /** Date when the network's data was last updated in the system. */ public updatedAt?: Date; + /** + * Determines if a given feature, represented by an amendment, is enabled for this network. + * + * @param amendment - The amendment or feature to check. + * @returns {boolean} - True if the feature is enabled, false otherwise. + */ public isFeatureEnabled(amendment: keyof typeof Amendments): boolean { - return this.amendments.indexOf(Amendments[amendment]) > -1; + return this.amendments?.indexOf(Amendments[amendment]) > -1; } - get definitions(): Record { + /** + * Retrieves the network definitions as a parsed object. + * + * @returns {Record | undefined} - The parsed definitions or undefined if not set. + */ + get definitions(): Record | undefined { if (this.definitionsString) { return JSON.parse(this.definitionsString); } @@ -45,6 +74,11 @@ class Network extends Realm.Object { return undefined; } + /** + * Serializes and sets the network definitions from an object. + * + * @param data - The definitions data to set. + */ set definitions(data: Record) { this.definitionsString = JSON.stringify(data); } diff --git a/src/store/models/objects/node.ts b/src/store/models/objects/node.ts index 7487aecd3..79e069e1b 100644 --- a/src/store/models/objects/node.ts +++ b/src/store/models/objects/node.ts @@ -1,5 +1,8 @@ /** * Node Model + * + * @class + * @extends Realm.Object */ import Realm from 'realm'; @@ -12,12 +15,24 @@ import NetworkModel from './network'; class Node extends Realm.Object { public static schema: Realm.ObjectSchema = NodeSchema.schema; + /** Unique identifier representing this specific node. */ public id: Realm.BSON.ObjectId; + /** The endpoint or URL for this node. */ public endpoint: string; + /** Date when the node was initially registered in the system. */ public registerAt?: Date; + /** Date when the node's data was last updated in the system. */ public updatedAt?: Date; - get network(): NetworkModel { + /** + * Retrieves and returns the associated network for this node. + * + * If the node is linked to an existing Network model, it will + * return that network. Otherwise, it will return undefined. + * + * @returns {NetworkModel | undefined} The associated network or undefined if not linked. + */ + get network(): NetworkModel | undefined { const networks = this.linkingObjects('Network', 'nodes'); if (!networks.isEmpty()) { return networks[0]; diff --git a/src/store/models/objects/profile.ts b/src/store/models/objects/profile.ts index 2989fedde..26080e259 100644 --- a/src/store/models/objects/profile.ts +++ b/src/store/models/objects/profile.ts @@ -1,5 +1,8 @@ /** * Profile Model + * + * @class + * @extends Realm.Object */ import Realm from 'realm'; @@ -10,18 +13,31 @@ import { ProfileSchema } from '@store/models/schemas/latest'; class Profile extends Realm.Object { public static schema: Realm.ObjectSchema = ProfileSchema.schema; + /** Display name or identifier chosen by the user. */ public username: string; + /** URL-friendly version of the username, often used for profile URLs. */ public slug: string; + /** Unique identifier representing this specific user profile. */ public uuid: string; + /** Unique identifier for the user's device. */ public deviceUUID: string; + /** Version number of the Terms of Service the user agreed to. */ public signedTOSVersion: number; + /** Date when the user accepted the current Terms of Service. */ public signedTOSDate: Date; + /** Token used to authenticate the user for API calls. */ public accessToken: string; + /** Token used to refresh the access token once it expires. */ public refreshToken: string; + /** Hash value associated with the bearer token for security checks. */ public bearerHash: string; + /** Unique number generated for ensuring idempotent requests. */ public idempotency: number; + /** Date when the user initially registered their profile. */ public registerAt?: Date; + /** Date when the user's profile data was last synchronized with the backend. */ public lastSync?: Date; + /** Indicates if the user has a Pro membership or subscription. */ public hasPro?: boolean; } diff --git a/src/store/models/objects/trustLine.ts b/src/store/models/objects/trustLine.ts index f93961059..c8f373aae 100644 --- a/src/store/models/objects/trustLine.ts +++ b/src/store/models/objects/trustLine.ts @@ -1,42 +1,70 @@ /** - * Account Trust Line Model + * TrustLine Model + * + * @class + * @extends Realm.Object */ import Realm from 'realm'; import { Truncate } from '@common/utils/string'; + +import CounterPartyModel from './counterParty'; +import CurrencyModel from '@store/models/objects/currency'; + import { TrustLineSchema } from '@store/models/schemas/latest'; /* Model ==================================================================== */ class TrustLine extends Realm.Object { public static schema: Realm.ObjectSchema = TrustLineSchema.schema; + /** Unique identifier representing this specific trust line. `${address}.${currency.id}}` */ public id: string; - public currency: any; + /** Currency model associated with the trust line. */ + public currency: CurrencyModel; + /** The current balance held for this trust line. */ public balance: string; + /** Indicates if rippling is disabled on this trust line. */ public no_ripple?: boolean; + /** Reflects if the peer has disabled rippling on this trust line. */ public no_ripple_peer?: boolean; + /** The maximum amount the user is willing to owe the counterparty. */ public limit?: string; + /** The maximum amount the counterparty is willing to owe the user. */ public limit_peer?: string; + /** Quality or rate at which incoming funds are valued on this trust line. */ public quality_in?: number; + /** Quality or rate at which outgoing funds are valued on this trust line. */ public quality_out?: number; + /** Indicates if the user has authorized the counterparty to hold their issued currency. */ public authorized?: boolean; + /** Indicates if the counterparty has authorized the user to hold their issued currency. */ public peer_authorized?: boolean; + /** Specifies if the user has frozen this trust line, preventing all transfers. */ public freeze?: boolean; + /** Specifies if the counterparty has frozen this trust line. */ public freeze_peer?: boolean; + /** Reflects if this trust line represents an obligation or a regular balance. */ public obligation?: boolean; + /** Order in which the trust line should appear. */ public order?: number; + /** Indicates if this trust line is marked as a favorite by the user. */ public favorite?: boolean; + /** + * Represents the counterparties details associated with this trust line. + * + * If the trust line is linked to an existing CounterParty model, it will + * return the name, avatar, and domain of that counterparty. Otherwise, it + * will truncate the currency issuer's name and default the avatar and domain. + * + * @returns {object} An object containing name, avatar, and domain of the counterparty. + */ get counterParty() { - const counterParty = this.currency.linkingObjects('CounterParty', 'currencies'); - if (!counterParty.isEmpty()) { - const item = counterParty[0]; - return { - name: item.name, - avatar: item.avatar, - domain: item.domain, - }; + const counterParties = this.currency.linkingObjects('CounterParty', 'currencies'); + if (!counterParties.isEmpty()) { + const { name, avatar, domain } = counterParties[0]; + return { name, avatar, domain }; } return { diff --git a/src/store/models/schemas/v1/account.ts b/src/store/models/schemas/v1/account.ts index 5b5b583bd..d4e8d75d1 100644 --- a/src/store/models/schemas/v1/account.ts +++ b/src/store/models/schemas/v1/account.ts @@ -2,7 +2,9 @@ * Account Schema v1 */ +import { ExtendedSchemaType } from '@store/types'; /* Schema ==================================================================== */ + const AccountSchema = { schema: { name: 'Account', @@ -15,8 +17,8 @@ const AccountSchema = { sequence: { type: 'int', default: 0 }, publicKey: { type: 'string', optional: true }, regularKey: { type: 'string', optional: true }, - accessLevel: 'string', - encryptionLevel: 'string', + accessLevel: { type: 'string' }, + encryptionLevel: { type: 'string' }, flags: { type: 'int', default: 0 }, default: { type: 'bool', default: false }, lines: { type: 'list', objectType: 'TrustLine' }, @@ -26,4 +28,4 @@ const AccountSchema = { }, }; -export default AccountSchema; +export default AccountSchema; diff --git a/src/store/models/schemas/v1/contact.ts b/src/store/models/schemas/v1/contact.ts index 684d1cf23..da25bc21f 100644 --- a/src/store/models/schemas/v1/contact.ts +++ b/src/store/models/schemas/v1/contact.ts @@ -2,7 +2,9 @@ * Contact Schema v1 */ +import { ExtendedSchemaType } from '@store/types'; /* Schema ==================================================================== */ + const ContactSchema = { schema: { name: 'Contact', @@ -18,4 +20,4 @@ const ContactSchema = { }, }; -export default ContactSchema; +export default ContactSchema; diff --git a/src/store/models/schemas/v1/core.ts b/src/store/models/schemas/v1/core.ts index 828c620b3..8939926e6 100644 --- a/src/store/models/schemas/v1/core.ts +++ b/src/store/models/schemas/v1/core.ts @@ -3,6 +3,7 @@ */ import { AppConfig, NetworkConfig } from '@common/constants'; +import { ExtendedSchemaType } from '@store/types'; /* Schema ==================================================================== */ const CoreSchema = { @@ -24,4 +25,4 @@ const CoreSchema = { }, }; -export default CoreSchema; +export default CoreSchema; diff --git a/src/store/models/schemas/v1/counterParty.ts b/src/store/models/schemas/v1/counterParty.ts index 2518d883b..2daac688c 100644 --- a/src/store/models/schemas/v1/counterParty.ts +++ b/src/store/models/schemas/v1/counterParty.ts @@ -2,6 +2,8 @@ * Counter Parties Schema v1 */ +import { ExtendedSchemaType } from '@store/types'; + /* Schema ==================================================================== */ const CounterPartySchema = { schema: { @@ -19,4 +21,4 @@ const CounterPartySchema = { }, }; -export default CounterPartySchema; +export default CounterPartySchema; diff --git a/src/store/models/schemas/v1/currency.ts b/src/store/models/schemas/v1/currency.ts index 4ffb8b49b..8b42b40cd 100644 --- a/src/store/models/schemas/v1/currency.ts +++ b/src/store/models/schemas/v1/currency.ts @@ -2,15 +2,17 @@ * Currency schema v1 */ +import { ExtendedSchemaType } from '@store/types'; + /* Schema ==================================================================== */ const CurrencySchema = { schema: { name: 'Currency', primaryKey: 'id', properties: { - id: 'string', - issuer: 'string', - currency: 'string', + id: { type: 'string' }, + issuer: { type: 'string' }, + currency: { type: 'string' }, name: { type: 'string', optional: true }, avatar: { type: 'string', optional: true }, owners: { type: 'linkingObjects', objectType: 'CounterParty', property: 'currencies' }, @@ -18,4 +20,4 @@ const CurrencySchema = { }, }; -export default CurrencySchema; +export default CurrencySchema; diff --git a/src/store/models/schemas/v1/profile.ts b/src/store/models/schemas/v1/profile.ts index 0b45ef22b..a9b6b7c17 100644 --- a/src/store/models/schemas/v1/profile.ts +++ b/src/store/models/schemas/v1/profile.ts @@ -2,7 +2,9 @@ * Profile Schema v1 */ +import { ExtendedSchemaType } from '@store/types'; /* Schema ==================================================================== */ + const ProfileSchema = { schema: { name: 'Profile', @@ -11,7 +13,7 @@ const ProfileSchema = { slug: { type: 'string', optional: true }, uuid: { type: 'string', optional: true }, signedTOSVersion: { type: 'int', optional: true }, - signedTOSDate: 'date?', + signedTOSDate: { type: 'date', optional: true }, accessToken: { type: 'string', optional: true }, idempotency: { type: 'int', default: 0 }, registerAt: { type: 'date', default: new Date() }, @@ -20,4 +22,4 @@ const ProfileSchema = { }, }; -export default ProfileSchema; +export default ProfileSchema; diff --git a/src/store/models/schemas/v1/trustLine.ts b/src/store/models/schemas/v1/trustLine.ts index d49869b4a..9d1119966 100644 --- a/src/store/models/schemas/v1/trustLine.ts +++ b/src/store/models/schemas/v1/trustLine.ts @@ -2,12 +2,14 @@ * Account Trust Lines Schema v1 */ +import { ExtendedSchemaType } from '@store/types'; + /* Schema ==================================================================== */ const TrustLineSchema = { schema: { name: 'TrustLine', properties: { - currency: { type: 'Currency' }, + currency: { type: 'object', objectType: 'Currency' }, balance: { type: 'double', default: 0 }, transfer_rate: { type: 'double', default: 0 }, no_ripple: { type: 'bool', optional: true }, @@ -20,4 +22,4 @@ const TrustLineSchema = { }, }; -export default TrustLineSchema; +export default TrustLineSchema; diff --git a/src/store/models/schemas/v10/core.ts b/src/store/models/schemas/v10/core.ts index 82bca2869..ce754a124 100644 --- a/src/store/models/schemas/v10/core.ts +++ b/src/store/models/schemas/v10/core.ts @@ -3,7 +3,9 @@ */ import Realm from 'realm'; + import { AppConfig, NetworkConfig } from '@common/constants'; +import { ExtendedSchemaType } from '@store/types'; /* Schema ==================================================================== */ const CoreSchema = { @@ -46,4 +48,4 @@ const CoreSchema = { }, }; -export default CoreSchema; +export default CoreSchema; diff --git a/src/store/models/schemas/v11/trustLine.ts b/src/store/models/schemas/v11/trustLine.ts index c381b4103..101f51653 100644 --- a/src/store/models/schemas/v11/trustLine.ts +++ b/src/store/models/schemas/v11/trustLine.ts @@ -4,6 +4,8 @@ import Realm from 'realm'; +import { ExtendedSchemaType } from '@store/types'; + /* Schema ==================================================================== */ const TrustLineSchema = { schema: { @@ -44,4 +46,4 @@ const TrustLineSchema = { }, }; -export default TrustLineSchema; +export default TrustLineSchema; diff --git a/src/store/models/schemas/v12/account.ts b/src/store/models/schemas/v12/account.ts index 514212591..a907e9aaf 100644 --- a/src/store/models/schemas/v12/account.ts +++ b/src/store/models/schemas/v12/account.ts @@ -3,7 +3,7 @@ */ import Realm from 'realm'; -import { EncryptionLevels, AccountTypes } from '@store/types'; +import { EncryptionLevels, AccountTypes, ExtendedSchemaType } from '@store/types'; /* Schema ==================================================================== */ const AccountSchema = { @@ -48,4 +48,4 @@ const AccountSchema = { }, }; -export default AccountSchema; +export default AccountSchema; diff --git a/src/store/models/schemas/v12/core.ts b/src/store/models/schemas/v12/core.ts index 26456da0a..770141af4 100644 --- a/src/store/models/schemas/v12/core.ts +++ b/src/store/models/schemas/v12/core.ts @@ -3,7 +3,9 @@ */ import Realm from 'realm'; + import { AppConfig, NetworkConfig } from '@common/constants'; +import { ExtendedSchemaType } from '@store/types'; /* Schema ==================================================================== */ const CoreSchema = { @@ -52,4 +54,4 @@ const CoreSchema = { }, }; -export default CoreSchema; +export default CoreSchema; diff --git a/src/store/models/schemas/v12/currency.ts b/src/store/models/schemas/v12/currency.ts index 92e4f570d..17ca6797b 100644 --- a/src/store/models/schemas/v12/currency.ts +++ b/src/store/models/schemas/v12/currency.ts @@ -4,6 +4,8 @@ import Realm from 'realm'; +import { ExtendedSchemaType } from '@store/types'; + /* Schema ==================================================================== */ const CurrencySchema = { schema: { @@ -33,4 +35,4 @@ const CurrencySchema = { }, }; -export default CurrencySchema; +export default CurrencySchema; diff --git a/src/store/models/schemas/v13/profile.ts b/src/store/models/schemas/v13/profile.ts index 74360eb23..80a51bba5 100644 --- a/src/store/models/schemas/v13/profile.ts +++ b/src/store/models/schemas/v13/profile.ts @@ -2,6 +2,8 @@ * Profile Schema v13 */ +import { ExtendedSchemaType } from '@store/types'; + /* Schema ==================================================================== */ const ProfileSchema = { schema: { @@ -36,4 +38,4 @@ const ProfileSchema = { }, }; -export default ProfileSchema; +export default ProfileSchema; diff --git a/src/store/models/schemas/v14/account.ts b/src/store/models/schemas/v14/account.ts index 68063e71d..158cf95d0 100644 --- a/src/store/models/schemas/v14/account.ts +++ b/src/store/models/schemas/v14/account.ts @@ -1,10 +1,10 @@ /** * Account Schema v14 */ - -import { AccountTypes } from '@store/types'; import Realm from 'realm'; +import { AccountTypes, ExtendedSchemaType } from '@store/types'; + /* Schema ==================================================================== */ const AccountSchema = { schema: { @@ -65,4 +65,4 @@ const AccountSchema = { }, }; -export default AccountSchema; +export default AccountSchema; diff --git a/src/store/models/schemas/v14/accountDetails.ts b/src/store/models/schemas/v14/accountDetails.ts index c92b97e7c..b90ed0546 100644 --- a/src/store/models/schemas/v14/accountDetails.ts +++ b/src/store/models/schemas/v14/accountDetails.ts @@ -2,6 +2,7 @@ * Account Details Schema */ +import { ExtendedSchemaType } from '@store/types'; /* Schema ==================================================================== */ const AccountDetailsSchema = { schema: { @@ -26,4 +27,4 @@ const AccountDetailsSchema = { }, }; -export default AccountDetailsSchema; +export default AccountDetailsSchema; diff --git a/src/store/models/schemas/v14/core.ts b/src/store/models/schemas/v14/core.ts index 3c1253bfa..8fa10dbf9 100644 --- a/src/store/models/schemas/v14/core.ts +++ b/src/store/models/schemas/v14/core.ts @@ -4,6 +4,7 @@ import Realm from 'realm'; import { AppConfig, NetworkConfig } from '@common/constants'; +import { ExtendedSchemaType } from '@store/types'; /* Schema ==================================================================== */ const CoreSchema = { @@ -94,4 +95,4 @@ const CoreSchema = { }, }; -export default CoreSchema; +export default CoreSchema; diff --git a/src/store/models/schemas/v14/network.ts b/src/store/models/schemas/v14/network.ts index 97d8101c7..251227197 100644 --- a/src/store/models/schemas/v14/network.ts +++ b/src/store/models/schemas/v14/network.ts @@ -3,7 +3,9 @@ */ import Realm from 'realm'; + import { NetworkConfig } from '@common/constants'; +import { ExtendedSchemaType } from '@store/types'; /* Schema ==================================================================== */ const NetworkSchema = { @@ -64,4 +66,4 @@ const NetworkSchema = { }, }; -export default NetworkSchema; +export default NetworkSchema; diff --git a/src/store/models/schemas/v14/node.ts b/src/store/models/schemas/v14/node.ts index 24d58e20d..5d19e2998 100644 --- a/src/store/models/schemas/v14/node.ts +++ b/src/store/models/schemas/v14/node.ts @@ -5,6 +5,8 @@ import Realm from 'realm'; import { NetworkConfig } from '@common/constants'; +import { ExtendedSchemaType } from '@store/types'; + /* Schema ==================================================================== */ const NodeSchema = { schema: { @@ -55,4 +57,4 @@ const NodeSchema = { }, }; -export default NodeSchema; +export default NodeSchema; diff --git a/src/store/models/schemas/v14/trustLine.ts b/src/store/models/schemas/v14/trustLine.ts index 09352ea85..eb26353e9 100644 --- a/src/store/models/schemas/v14/trustLine.ts +++ b/src/store/models/schemas/v14/trustLine.ts @@ -4,6 +4,8 @@ import Realm from 'realm'; +import { ExtendedSchemaType } from '@store/types'; + /* Schema ==================================================================== */ const TrustLineSchema = { schema: { @@ -72,4 +74,4 @@ const TrustLineSchema = { }, }; -export default TrustLineSchema; +export default TrustLineSchema; diff --git a/src/store/models/schemas/v2/account.ts b/src/store/models/schemas/v2/account.ts index 8e6789254..37e151727 100644 --- a/src/store/models/schemas/v2/account.ts +++ b/src/store/models/schemas/v2/account.ts @@ -4,6 +4,8 @@ import Realm from 'realm'; +import { ExtendedSchemaType } from '@store/types'; + /* Schema ==================================================================== */ const AccountSchema = { schema: { @@ -41,4 +43,4 @@ const AccountSchema = { }, }; -export default AccountSchema; +export default AccountSchema; diff --git a/src/store/models/schemas/v2/core.ts b/src/store/models/schemas/v2/core.ts index a654bdb06..1a895dbbb 100644 --- a/src/store/models/schemas/v2/core.ts +++ b/src/store/models/schemas/v2/core.ts @@ -3,7 +3,9 @@ */ import Realm from 'realm'; + import { AppConfig, NetworkConfig } from '@common/constants'; +import { ExtendedSchemaType } from '@store/types'; /* Schema ==================================================================== */ const CoreSchema = { @@ -42,4 +44,4 @@ const CoreSchema = { }, }; -export default CoreSchema; +export default CoreSchema; diff --git a/src/store/models/schemas/v3/core.ts b/src/store/models/schemas/v3/core.ts index dda687e9a..77adca6d9 100644 --- a/src/store/models/schemas/v3/core.ts +++ b/src/store/models/schemas/v3/core.ts @@ -3,7 +3,9 @@ */ import Realm from 'realm'; + import { AppConfig, NetworkConfig } from '@common/constants'; +import { ExtendedSchemaType } from '@store/types'; /* Schema ==================================================================== */ const CoreSchema = { @@ -40,4 +42,4 @@ const CoreSchema = { }, }; -export default CoreSchema; +export default CoreSchema; diff --git a/src/store/models/schemas/v4/core.ts b/src/store/models/schemas/v4/core.ts index fb8a40838..987a68efe 100644 --- a/src/store/models/schemas/v4/core.ts +++ b/src/store/models/schemas/v4/core.ts @@ -3,7 +3,9 @@ */ import Realm from 'realm'; + import { AppConfig, NetworkConfig } from '@common/constants'; +import { ExtendedSchemaType } from '@store/types'; /* Schema ==================================================================== */ const CoreSchema = { @@ -41,4 +43,4 @@ const CoreSchema = { }; /* Migration ==================================================================== */ -export default CoreSchema; +export default CoreSchema; diff --git a/src/store/models/schemas/v4/counterParty.ts b/src/store/models/schemas/v4/counterParty.ts index e88b3f91c..68d03ce41 100644 --- a/src/store/models/schemas/v4/counterParty.ts +++ b/src/store/models/schemas/v4/counterParty.ts @@ -4,6 +4,8 @@ import Realm from 'realm'; +import { ExtendedSchemaType } from '@store/types'; + /* Schema ==================================================================== */ const CounterPartySchema = { schema: { @@ -33,4 +35,4 @@ const CounterPartySchema = { }, }; -export default CounterPartySchema; +export default CounterPartySchema; diff --git a/src/store/models/schemas/v4/currency.ts b/src/store/models/schemas/v4/currency.ts index 77056986b..e728aa433 100644 --- a/src/store/models/schemas/v4/currency.ts +++ b/src/store/models/schemas/v4/currency.ts @@ -4,6 +4,8 @@ import Realm from 'realm'; +import { ExtendedSchemaType } from '@store/types'; + /* Schema ==================================================================== */ const CurrencySchema = { schema: { @@ -32,4 +34,4 @@ const CurrencySchema = { }, }; -export default CurrencySchema; +export default CurrencySchema; diff --git a/src/store/models/schemas/v4/profile.ts b/src/store/models/schemas/v4/profile.ts index dd175bf27..342918c83 100644 --- a/src/store/models/schemas/v4/profile.ts +++ b/src/store/models/schemas/v4/profile.ts @@ -4,6 +4,8 @@ import Realm from 'realm'; +import { ExtendedSchemaType } from '@store/types'; + /* Schema ==================================================================== */ const ProfileSchema = { schema: { @@ -34,4 +36,4 @@ const ProfileSchema = { }, }; -export default ProfileSchema; +export default ProfileSchema; diff --git a/src/store/models/schemas/v4/trustLine.ts b/src/store/models/schemas/v4/trustLine.ts index bffcf94fc..82af3d473 100644 --- a/src/store/models/schemas/v4/trustLine.ts +++ b/src/store/models/schemas/v4/trustLine.ts @@ -4,12 +4,14 @@ import Realm from 'realm'; +import { ExtendedSchemaType } from '@store/types'; + /* Schema ==================================================================== */ const TrustLineSchema = { schema: { name: 'TrustLine', properties: { - currency: { type: 'Currency' }, + currency: { type: 'object', objectType: 'Currency' }, balance: { type: 'double', default: 0 }, transfer_rate: { type: 'double', default: 0 }, no_ripple: { type: 'bool', default: false }, @@ -44,4 +46,4 @@ const TrustLineSchema = { }, }; -export default TrustLineSchema; +export default TrustLineSchema; diff --git a/src/store/models/schemas/v5/core.ts b/src/store/models/schemas/v5/core.ts index 364f77f02..cca5e5aa1 100644 --- a/src/store/models/schemas/v5/core.ts +++ b/src/store/models/schemas/v5/core.ts @@ -3,7 +3,9 @@ */ import Realm from 'realm'; + import { AppConfig, NetworkConfig } from '@common/constants'; +import { ExtendedSchemaType } from '@store/types'; /* Schema ==================================================================== */ const CoreSchema = { @@ -41,4 +43,4 @@ const CoreSchema = { }, }; -export default CoreSchema; +export default CoreSchema; diff --git a/src/store/models/schemas/v6/account.ts b/src/store/models/schemas/v6/account.ts index 9a2dcaefa..b62ded3d9 100644 --- a/src/store/models/schemas/v6/account.ts +++ b/src/store/models/schemas/v6/account.ts @@ -3,7 +3,8 @@ */ import Realm from 'realm'; -import { AccountTypes } from '@store/types'; + +import { AccountTypes, ExtendedSchemaType } from '@store/types'; /* Schema ==================================================================== */ const AccountSchema = { @@ -43,4 +44,4 @@ const AccountSchema = { }, }; -export default AccountSchema; +export default AccountSchema; diff --git a/src/store/models/schemas/v6/profile.ts b/src/store/models/schemas/v6/profile.ts index 93ae3b4e4..08bccc083 100644 --- a/src/store/models/schemas/v6/profile.ts +++ b/src/store/models/schemas/v6/profile.ts @@ -4,6 +4,8 @@ import Realm from 'realm'; +import { ExtendedSchemaType } from '@store/types'; + /* Schema ==================================================================== */ const ProfileSchema = { schema: { @@ -14,7 +16,7 @@ const ProfileSchema = { uuid: { type: 'string', optional: true }, deviceUUID: { type: 'string', optional: true }, signedTOSVersion: { type: 'int', optional: true }, - signedTOSDate: 'date?', + signedTOSDate: { type: 'date', optional: true }, accessToken: { type: 'string', optional: true }, idempotency: { type: 'int', default: 0 }, hasPro: { type: 'bool', default: false }, @@ -35,4 +37,4 @@ const ProfileSchema = { }, }; -export default ProfileSchema; +export default ProfileSchema; diff --git a/src/store/models/schemas/v7/account.ts b/src/store/models/schemas/v7/account.ts index 0e1bc7508..d9ff10bd4 100644 --- a/src/store/models/schemas/v7/account.ts +++ b/src/store/models/schemas/v7/account.ts @@ -3,7 +3,8 @@ */ import Realm from 'realm'; -import { AccountTypes } from '@store/types'; + +import { AccountTypes, ExtendedSchemaType } from '@store/types'; /* Schema ==================================================================== */ const AccountSchema = { @@ -45,4 +46,4 @@ const AccountSchema = { }, }; -export default AccountSchema; +export default AccountSchema; diff --git a/src/store/models/schemas/v7/core.ts b/src/store/models/schemas/v7/core.ts index fc9aa3bee..2a61900a5 100644 --- a/src/store/models/schemas/v7/core.ts +++ b/src/store/models/schemas/v7/core.ts @@ -3,7 +3,9 @@ */ import Realm from 'realm'; + import { AppConfig, NetworkConfig } from '@common/constants'; +import { ExtendedSchemaType } from '@store/types'; /* Schema ==================================================================== */ const CoreSchema = { @@ -44,4 +46,4 @@ const CoreSchema = { }, }; -export default CoreSchema; +export default CoreSchema; diff --git a/src/store/models/schemas/v8/customNode.ts b/src/store/models/schemas/v8/customNode.ts index 64e82d612..3b45c8c9f 100644 --- a/src/store/models/schemas/v8/customNode.ts +++ b/src/store/models/schemas/v8/customNode.ts @@ -2,6 +2,8 @@ * Custom Node Schema */ +import { ExtendedSchemaType } from '@store/types'; + /* Schema ==================================================================== */ const CustomNodeSchema = { schema: { @@ -17,4 +19,4 @@ const CustomNodeSchema = { }, }; -export default CustomNodeSchema; +export default CustomNodeSchema; diff --git a/src/store/models/schemas/v9/trustLine.ts b/src/store/models/schemas/v9/trustLine.ts index 4eecf6877..c9a98ecc8 100644 --- a/src/store/models/schemas/v9/trustLine.ts +++ b/src/store/models/schemas/v9/trustLine.ts @@ -4,6 +4,8 @@ import Realm from 'realm'; +import { ExtendedSchemaType } from '@store/types'; + /* Schema ==================================================================== */ const TrustLineSchema = { schema: { @@ -57,4 +59,4 @@ const TrustLineSchema = { }, }; -export default TrustLineSchema; +export default TrustLineSchema; diff --git a/src/store/repositories/account.ts b/src/store/repositories/account.ts index 94d3b4545..f145cc08e 100644 --- a/src/store/repositories/account.ts +++ b/src/store/repositories/account.ts @@ -1,5 +1,5 @@ import { flatMap, has, filter, find } from 'lodash'; -import Realm, { Results } from 'realm'; +import Realm from 'realm'; import { AccountModel, AccountDetailsModel, CurrencyModel, TrustLineModel } from '@store/models'; import { AccessLevels, EncryptionLevels, AccountTypes } from '@store/types'; @@ -7,6 +7,7 @@ import { AccessLevels, EncryptionLevels, AccountTypes } from '@store/types'; import Vault from '@common/libs/vault'; import BaseRepository from './base'; + /* Events ==================================================================== */ declare interface AccountRepository { on(event: 'changeDefaultAccount', listener: (defaultAccount: AccountModel) => void): this; @@ -17,47 +18,56 @@ declare interface AccountRepository { } /* Repository ==================================================================== */ -class AccountRepository extends BaseRepository { +class AccountRepository extends BaseRepository { + /** + * Initialize the repository with realm instance and default model. + * @param {Realm} realm - The realm instance. + */ initialize(realm: Realm) { this.realm = realm; - this.schema = AccountModel.schema; + this.model = AccountModel; } /** - * add new regular account to the store - * this will store private key in the vault if full access + * Adds a new account to the store. + * @param {Partial} account - Partial data for the account. + * @param {string} [privateKey] - Private key of the account (optional). + * @param {string} [encryptionKey] - Encryption key (optional). + * @returns {Promise} Promise that resolves to the created AccountModel. */ - add = (account: Partial, privateKey?: string, encryptionKey?: string): Promise => { - // READONLY || TANGEM CARD + add = async ( + account: Partial, + privateKey?: string, + encryptionKey?: string, + ): Promise => { + // Handle special cases for Readonly or Tangem card accounts if (account.accessLevel === AccessLevels.Readonly || account.type === AccountTypes.Tangem) { - return this.create(account).then((createdAccount: AccountModel) => { - this.emit('accountCreate', createdAccount); - return createdAccount; - }); + const createdAccount = await this.create(account); + this.emit('accountCreate', createdAccount); + return createdAccount; } - // FULL ACCESS - return Vault.create(account.publicKey, privateKey, encryptionKey).then(() => { - return this.create(account, true).then((createdAccount: AccountModel) => { - this.emit('accountCreate', createdAccount); - return createdAccount; - }); - }); + // Handle full access accounts + await Vault.create(account.publicKey, privateKey, encryptionKey); + const createdFullAccessAccount = await this.create(account, true); + this.emit('accountCreate', createdFullAccessAccount); + return createdFullAccessAccount; }; /** - * update account object + * Update an existing account. + * @param {Partial} object Data to update the account with. + * @returns {Promise} Updated account. */ - update = (object: Partial): Promise => { - // the primary key should be in the object - if (!has(object, this.schema.primaryKey)) { + update = async (object: Partial): Promise => { + // Validate object has a primary key + if (!has(object, this.model.schema.primaryKey)) { throw new Error('Update require primary key to be set'); } - return this.create(object, true).then((updatedAccount: AccountModel) => { - this.emit('accountUpdate', updatedAccount, object); - return updatedAccount; - }); + const updatedAccount = await this.create(object, true); + this.emit('accountUpdate', updatedAccount, object); + return updatedAccount; }; /** @@ -107,7 +117,7 @@ class AccountRepository extends BaseRepository { /** * get list all accounts */ - getAccounts = (filters?: Partial): Results => { + getAccounts = (filters?: Partial) => { // sorted('default', true) will put the default account on top if (filters) { return this.query(filters); @@ -125,14 +135,14 @@ class AccountRepository extends BaseRepository { /** * get list of accounts with full access */ - getFullAccessAccounts = (): Array => { + getFullAccessAccounts = (): AccountModel[] => { return flatMap(this.query({ accessLevel: AccessLevels.Full })); }; /** * get list of available accounts for spending */ - getSpendableAccounts = (includeHidden = false): Array => { + getSpendableAccounts = (includeHidden = false): AccountModel[] => { const signableAccounts = this.getSignableAccounts(); return filter(signableAccounts, (a) => a.balance > 0 && (includeHidden ? true : !a.hidden)); @@ -141,7 +151,7 @@ class AccountRepository extends BaseRepository { /** * get list of available accounts for signing */ - getSignableAccounts = (): Array => { + getSignableAccounts = (): AccountModel[] => { const accounts = this.findAll(); const availableAccounts = [] as Array; @@ -225,24 +235,26 @@ class AccountRepository extends BaseRepository { }; /** - * Remove account - * WARNING: this will be permanently and irreversible + * Remove an account permanently. + * WARNING: This operation is irreversible. + * @param {AccountModel} account The account to be removed. + * @returns {Promise} Whether the account was successfully removed. */ purge = async (account: AccountModel): Promise => { - // remove private key from vault + // Remove private key if account has full access if (account.accessLevel === AccessLevels.Full) { await Vault.purge(account.publicKey); } - // remove account lines + // Remove account trust lines for (const line of account.lines) { await this.delete(line); } - // remove the account + // Delete the account await this.delete(account); - // emit the account remove event + // Emit account removal event this.emit('accountRemove'); return true; diff --git a/src/store/repositories/base.ts b/src/store/repositories/base.ts index 5947016e2..2c07656ef 100644 --- a/src/store/repositories/base.ts +++ b/src/store/repositories/base.ts @@ -1,83 +1,109 @@ import EventEmitter from 'events'; -import { forEach, isObject, isString, has } from 'lodash'; +import { has } from 'lodash'; -import Realm, { Results, ObjectSchema } from 'realm'; +import Realm from 'realm'; /* Repository ==================================================================== */ -export default class BaseRepository extends EventEmitter { +export default class BaseRepository> extends EventEmitter { realm: Realm; - schema: ObjectSchema; - + model: Realm.ObjectClass; + + /** + * Normalizes the query for realm objects. + * + * @param {string | { [key: string]: any }} query - The query to normalize. + * @returns {string} - A normalized query string. + */ normalizeQuery = (query: string | { [key: string]: any }): string => { - if (isString(query)) return query; - - if (isObject(query)) { - const props = this.schema.properties; - const queries = [] as string[]; - let queryString = ''; - - forEach(query, (value, key) => { - if (Object.prototype.hasOwnProperty.call(props, key)) { - const properties = props[key]; - let type = ''; - let filter = ''; - - if (isObject(properties)) { - type = properties.type; - } else { - type = properties; - } - - if (type === 'bool') { - filter = value; - } else { - filter = ` "${value}"`; - } - - queries.push(`${key} == ${filter}`); - } - }); - forEach(queries, (value, index) => { - if (index < queries.length - 1) { - queryString += ` ${value} AND `; - } else { - queryString += ` ${value} `; + if (typeof query === 'string') return query; + + const queryObject = { ...query }; + + const props = this.model.schema.properties; + const queries = Object.entries(queryObject) + .filter(([key]) => props[key]) + .map(([key, value]) => { + // remove from query object + delete queryObject[key]; + // @ts-expect-error + const type = typeof props[key] === 'object' ? props[key].type : props[key]; + + // check type + if (!['string', 'boolean', 'number'].includes(typeof value)) { + throw new Error( + `Unrecognized value set for query param ${key}, expected ${type} but provided ${typeof value}`, + ); } + + return type === 'bool' ? `${key} == ${value}` : `${key} == "${value}"`; }); - return queryString; + // unrecognized field names in query params + if (Object.entries(queryObject).length > 0) { + throw new Error(`Unrecognized query field names ${Object.keys(queryObject).join(',')}`); } - return ''; + // nothing to query + if (queries.length === 0) { + throw new Error('Cannot convert query object to string'); + } + + return queries.join(' AND '); }; + /** + * Safely writes to the Realm. + * + * @param {Function} f - The write function to execute. + */ safeWrite = (f: any) => { if (this.realm.isInTransaction) { setTimeout(() => { this.safeWrite(f); }, 50); } else { - this.realm.write(() => { - f(); - }); + this.realm.write(f); } }; + /** + * Gets the count of objects. + * + * @returns {number} - Count of objects. + */ count = (): number => { - const result = this.findAll(); - return result.length; + return this.findAll().length; }; - findAll = (): Results => { - return this.realm.objects(this.schema.name); + /** + * Finds all objects. + * + * @returns {Realm.Results} - All objects. + */ + findAll = (): Realm.Results => { + return this.realm.objects(this.model); }; - findBy = (key: string, val: string): Results => { - return this.realm.objects(this.schema.name).filtered(`${key} == "${val}"`); + /** + * Finds objects by a given key-value. + * + * @param {string} key - The key. + * @param {string} val - The value. + * @returns {Realm.Results} - Objects found. + */ + findBy = (key: string, val: string): Realm.Results => { + return this.findAll().filtered(`${key} == "${val}"`); }; - findOne = (query: string | { [key: string]: any }): any => { - const result = this.realm.objects(this.schema.name).filtered(this.normalizeQuery(query)); + /** + * Finds one object based on the query. + * + * @param {string | { [key: string]: any }} query - The query to search. + * @returns {T} - Found object. + * @throws will throw an error if more than one result found. + */ + findOne = (query: string | { [key: string]: any }): T => { + const result = this.realm.objects(this.model).filtered(this.normalizeQuery(query)); if (result.length === 0) { return undefined; @@ -90,43 +116,58 @@ export default class BaseRepository extends EventEmitter { throw new Error('Got more than one result'); }; - query = (query: string | { [key: string]: any }): Results => { - return this.realm.objects(this.schema.name).filtered(this.normalizeQuery(query)); + /** + * Queries objects based on the provided query. + * + * @param {string | { [key: string]: any }} query - The query. + * @returns {Realm.Results} - Resulting objects. + */ + query = (query: string | { [key: string]: any }) => { + return this.realm.objects(this.model).filtered(this.normalizeQuery(query)); }; - upsert = (data: any): Promise => { - return new Promise((resolve, reject) => { - if (!has(data, 'id')) { - throw new Error('ID require primary key to be set'); - } + /** + * Inserts or updates an object. + * + * @param {any} data - The data to upsert. + * @returns {Promise} - The created or updated object. + */ + upsert = async (data: any): Promise => { + if (!has(data, 'id')) throw new Error('ID require primary key to be set'); - const object = this.realm.objectForPrimaryKey(this.schema.name, data.id) as any; + const objectExists = !!this.realm.objectForPrimaryKey(this.model, data.id); - if (object) { - try { - this.safeWrite(() => { - resolve(this.realm.create(this.schema.name, data, Realm.UpdateMode.All)); - }); - } catch (error) { - reject(error); - } - } else { - try { - this.safeWrite(() => { - resolve(this.realm.create(this.schema.name, data)); - }); - } catch (error) { - reject(error); - } + return new Promise((resolve, reject) => { + try { + this.safeWrite(() => { + resolve( + this.realm.create( + this.model, + data, + objectExists ? Realm.UpdateMode.All : Realm.UpdateMode.Never, + ), + ); + }); + } catch (error) { + reject(error); } }); }; - create = (data: any, update = false): Promise => { + /** + * Creates a new object. + * + * @param {any} data - The data to create from. + * @param {boolean} [update=false] - Whether to update existing data. + * @returns {Promise} - The created object. + */ + create = (data: any, update: boolean = false): Promise => { return new Promise((resolve, reject) => { try { this.safeWrite(() => { - resolve(this.realm.create(this.schema.name, data, update && Realm.UpdateMode.All)); + resolve( + this.realm.create(this.model, data, update ? Realm.UpdateMode.All : Realm.UpdateMode.Never), + ); }); } catch (error) { reject(error); @@ -134,70 +175,73 @@ export default class BaseRepository extends EventEmitter { }); }; - createList = (dataList: any[], update = false) => { + /** + * Creates a list of objects. + * + * @param {any[]} dataList - The list of data to create from. + * @param {boolean} [update=false] - Whether to update existing data. + * @returns {any[] | Error} - The created objects or an error. + */ + createList = (dataList: any[], update: boolean = false): any[] | Error => { try { this.safeWrite(() => { - for (let i = 0; i < dataList.length; i++) { - const data = dataList[i]; - - this.realm.create(this.schema.name, data, update && Realm.UpdateMode.All); - } + dataList.forEach((data) => + this.realm.create(this.model, data, update ? Realm.UpdateMode.All : Realm.UpdateMode.Never), + ); }); return dataList; - } catch (error) { + } catch (error: any) { return error; } }; - deleteBy = (key: string, val: string): Promise => { - return new Promise((resolve, reject) => { - try { - const items = this.realm.objects(this.schema.name).filtered(`${key} == "${val}"`); - const item = items[0]; - - if (item) { - this.safeWrite(() => { - resolve(this.realm.delete(item)); - }); - } else { - resolve(); - } - } catch (error) { - reject(error); + /** + * Deletes an object by a given id + * + * @returns {Promise} - A promise. + * @param id + */ + deleteById = (id: any): Promise => { + return new Promise((resolve, reject) => { + const item = this.realm.objectForPrimaryKey(this.model, id); + if (!item) { + reject(new Error('Item not found!')); + return; } + this.delete(item).then(resolve).catch(reject); }); }; - delete = (object: Realm.Object | Realm.Object[] | Realm.List | Realm.Results | any): Promise => { - return new Promise((resolve, reject) => { + /** + * Deletes an object or a set of objects. + * + * @param {Realm.Object | Realm.Object[] | Realm.List | Realm.Results} object - The object(s) to delete. + * @returns {Promise} - A promise. + */ + delete = async (object: Realm.Object | Realm.Object[]): Promise => { + return new Promise((resolve, reject) => { try { - if (object) { - this.safeWrite(() => { - resolve(this.realm.delete(object)); - }); - } else { + this.safeWrite(() => { + this.realm.delete(object); resolve(); - } + }); } catch (error) { reject(error); } }); }; - deleteAll = () => { - try { - const items = this.realm.objects(this.schema.name); - - if (items.length > 0) { - this.safeWrite(() => { - this.realm.delete(items); - }); - } - - return true; - } catch (error) { - return error; + /** + * Deletes all objects of the model. + * + * @returns {boolean | Error} - True if successful, error otherwise. + */ + deleteAll = (): boolean => { + const items = this.findAll(); + if (items.length > 0) { + this.safeWrite(() => this.realm.delete(items)); } + return true; }; } diff --git a/src/store/repositories/contact.ts b/src/store/repositories/contact.ts index c2b197051..2b032cabc 100644 --- a/src/store/repositories/contact.ts +++ b/src/store/repositories/contact.ts @@ -1,25 +1,25 @@ -import Realm, { Results } from 'realm'; +import Realm from 'realm'; import has from 'lodash/has'; import { ContactModel } from '@store/models'; import BaseRepository from './base'; /* Repository ==================================================================== */ -class ContactRepository extends BaseRepository { +class ContactRepository extends BaseRepository { initialize(realm: Realm) { this.realm = realm; - this.schema = ContactModel.schema; + this.model = ContactModel; } update = (object: Partial) => { // the primary key should be in the object - if (!has(object, this.schema.primaryKey)) { + if (!has(object, this.model.schema.primaryKey)) { throw new Error('Update require primary key to be set'); } return this.create(object, true); }; - getContacts = (): Results => { + getContacts = () => { return this.findAll(); }; diff --git a/src/store/repositories/core.ts b/src/store/repositories/core.ts index a1368fd79..fa2ea390b 100644 --- a/src/store/repositories/core.ts +++ b/src/store/repositories/core.ts @@ -1,5 +1,5 @@ import { assign } from 'lodash'; -import Realm, { Results } from 'realm'; +import Realm from 'realm'; import { Platform } from 'react-native'; @@ -20,10 +20,10 @@ declare interface CoreRepository { } /* Repository ==================================================================== */ -class CoreRepository extends BaseRepository { +class CoreRepository extends BaseRepository { initialize(realm: Realm) { this.realm = realm; - this.schema = CoreModel.schema; + this.model = CoreModel; } saveSettings = (settings: Partial) => { @@ -83,7 +83,7 @@ class CoreRepository extends BaseRepository { }; getSettings = (): CoreModel => { - const result = this.findAll() as Results; + const result = this.findAll(); // settings exist if (!result.isEmpty()) { diff --git a/src/store/repositories/counterParty.ts b/src/store/repositories/counterParty.ts index ce7ed3662..1a3cd35bb 100644 --- a/src/store/repositories/counterParty.ts +++ b/src/store/repositories/counterParty.ts @@ -1,23 +1,23 @@ -import Realm from 'realm'; import has from 'lodash/has'; +import Realm from 'realm'; import { CounterPartyModel } from '@store/models'; import BaseRepository from './base'; /* Repository ==================================================================== */ -class CounterPartyRepository extends BaseRepository { +class CounterPartyRepository extends BaseRepository { initialize(realm: Realm) { this.realm = realm; - this.schema = CounterPartyModel.schema; + this.model = CounterPartyModel; } update = (object: CounterPartyModel) => { // the primary key should be in the object - if (!has(object, this.schema.primaryKey)) { + if (!has(object, this.model.schema.primaryKey)) { throw new Error('Update require primary key to be set'); } - this.create(object, true); + return this.create(object, true); }; } diff --git a/src/store/repositories/currency.ts b/src/store/repositories/currency.ts index 6f70ffb01..178d43d84 100644 --- a/src/store/repositories/currency.ts +++ b/src/store/repositories/currency.ts @@ -1,5 +1,5 @@ -import Realm from 'realm'; import { has } from 'lodash'; +import Realm from 'realm'; import { CurrencyModel, CounterPartyModel } from '@store/models'; import { Issuer } from '@common/libs/ledger/parser/types'; @@ -7,10 +7,10 @@ import { Issuer } from '@common/libs/ledger/parser/types'; import BaseRepository from './base'; /* Repository ==================================================================== */ -class CurrencyRepository extends BaseRepository { +class CurrencyRepository extends BaseRepository { initialize(realm: Realm) { this.realm = realm; - this.schema = CurrencyModel.schema; + this.model = CurrencyModel; } include = (data: any): Promise => { @@ -21,12 +21,12 @@ class CurrencyRepository extends BaseRepository { return this.upsert(data); }; - update = (object: CurrencyModel): void => { + update = (object: CurrencyModel) => { // the primary key should be in the object if (!has(object, 'id')) { throw new Error('Update require primary key (id) to be set'); } - this.create(object, true); + return this.create(object, true); }; isVettedCurrency = (issuer: Issuer): boolean => { @@ -38,7 +38,8 @@ class CurrencyRepository extends BaseRepository { }; getCounterParty = (currency: CurrencyModel): CounterPartyModel => { - const counterParty = currency.linkingObjects('CounterParty', 'currencies'); + const counterParty = currency.linkingObjects('CounterParty', 'currencies'); + if (!counterParty.isEmpty()) { return counterParty[0] as CounterPartyModel; } diff --git a/src/store/repositories/network.ts b/src/store/repositories/network.ts index 9c1fa4511..68192ebda 100644 --- a/src/store/repositories/network.ts +++ b/src/store/repositories/network.ts @@ -1,4 +1,4 @@ -import Realm, { Results } from 'realm'; +import Realm from 'realm'; import { has } from 'lodash'; import { NetworkModel } from '@store/models'; @@ -6,10 +6,10 @@ import { NetworkModel } from '@store/models'; import BaseRepository from './base'; /* Repository ==================================================================== */ -class NetworkRepository extends BaseRepository { +class NetworkRepository extends BaseRepository { initialize(realm: Realm) { this.realm = realm; - this.schema = NetworkModel.schema; + this.model = NetworkModel; } add = (network: Partial) => { @@ -18,11 +18,13 @@ class NetworkRepository extends BaseRepository { // if not exist add it to the store if (!exist) { - this.create(network); + return this.create(network); } + + return undefined; }; - getNetworks = (filters?: Partial): Results => { + getNetworks = (filters?: Partial) => { if (filters) { return this.query(filters); } @@ -31,7 +33,7 @@ class NetworkRepository extends BaseRepository { update = (object: Partial) => { // the primary key should be in the object - if (!has(object, this.schema.primaryKey)) { + if (!has(object, this.model.schema.primaryKey)) { throw new Error('Update require primary key to be set'); } return this.create(object, true); diff --git a/src/store/repositories/node.ts b/src/store/repositories/node.ts index 4c8e3fada..7e5ac6484 100644 --- a/src/store/repositories/node.ts +++ b/src/store/repositories/node.ts @@ -1,4 +1,4 @@ -import Realm, { Results } from 'realm'; +import Realm from 'realm'; import { has } from 'lodash'; import { NodeModel } from '@store/models'; @@ -6,10 +6,10 @@ import { NodeModel } from '@store/models'; import BaseRepository from './base'; /* Repository ==================================================================== */ -class NodeRepository extends BaseRepository { +class NodeRepository extends BaseRepository { initialize(realm: Realm) { this.realm = realm; - this.schema = NodeModel.schema; + this.model = NodeModel; } add = (node: Partial) => { @@ -18,17 +18,19 @@ class NodeRepository extends BaseRepository { // if not exist add it to the store if (!exist) { - this.create(node); + return this.create(node); } + + return undefined; }; - getNodes = (): Results => { + getNodes = () => { return this.findAll(); }; update = (object: Partial) => { // the primary key should be in the object - if (!has(object, this.schema.primaryKey)) { + if (!has(object, this.model.schema.primaryKey)) { throw new Error('Update require primary key to be set'); } return this.create(object, true); diff --git a/src/store/repositories/profile.ts b/src/store/repositories/profile.ts index 824442a0b..7cd8ef0fc 100644 --- a/src/store/repositories/profile.ts +++ b/src/store/repositories/profile.ts @@ -12,10 +12,10 @@ declare interface ProfileRepository { } /* Repository ==================================================================== */ -class ProfileRepository extends BaseRepository { +class ProfileRepository extends BaseRepository { initialize(realm: Realm) { this.realm = realm; - this.schema = ProfileModel.schema; + this.model = ProfileModel; } updateIdempotency = (idempotency: number) => { @@ -25,26 +25,30 @@ class ProfileRepository extends BaseRepository { }); }; - saveProfile = (object: Partial) => { - const current = this.getProfile(); - if (current) { - this.safeWrite(() => { - assign(current, object); - }); - } else { - this.create(object); - } - - // send the event - this.emit('profileUpdate', object); + saveProfile = (object: Partial): Promise => { + return new Promise((resolve, reject) => { + const current = this.getProfile(); + + if (current) { + this.safeWrite(() => { + assign(current, object); + resolve(current); + }); + } else { + this.create(object).then(resolve).catch(reject); + } + + // send the event + this.emit('profileUpdate', object); + }); }; getProfile = (): ProfileModel => { - const profile = Array.from(this.findAll()) as ProfileModel[]; + const profiles = this.findAll(); // get profile - if (profile.length > 0) { - return profile[0]; + if (profiles.length > 0) { + return profiles[0]; } return undefined; diff --git a/src/store/repositories/trustLine.ts b/src/store/repositories/trustLine.ts index 1c0d9fae2..a8473b571 100644 --- a/src/store/repositories/trustLine.ts +++ b/src/store/repositories/trustLine.ts @@ -11,15 +11,15 @@ declare interface TrustLineRepository { } /* Repository ==================================================================== */ -class TrustLineRepository extends BaseRepository { +class TrustLineRepository extends BaseRepository { initialize(realm: Realm) { this.realm = realm; - this.schema = TrustLineModel.schema; + this.model = TrustLineModel; } update = (object: Partial) => { // the primary key should be in the object - if (!has(object, this.schema.primaryKey)) { + if (!has(object, this.model.schema.primaryKey)) { throw new Error('Update require primary key to be set'); } diff --git a/src/store/types.ts b/src/store/types.ts index be359476d..f68e40faa 100644 --- a/src/store/types.ts +++ b/src/store/types.ts @@ -1,4 +1,11 @@ -/* Global ==================================================================== */ +import Realm from 'realm'; + +export type ExtendedSchemaType = { + schema: Realm.ObjectSchema; + populate?: (realm: Realm) => void; + migration?: (oldRealm: Realm, newRealm: Realm) => void; +}; + export enum AccountTypes { Regular = 'Regular', // XRPL Regular account Tangem = 'Tangem', // Tangem Card account diff --git a/yarn.lock b/yarn.lock index c16f1557f..b945cbccb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1829,20 +1829,6 @@ invariant "^2.2.4" nullthrows "^1.1.1" -"@realm/common@^0.1.4": - version "0.1.4" - resolved "https://registry.yarnpkg.com/@realm/common/-/common-0.1.4.tgz#48ff628a22b27c61ba886caff395909632240927" - integrity sha512-bKpIRZIQ4ykribFi0igCwuvf7P4+Ex2XYKqDw1JDe6sCGAaPMwhazooyM6h32fUjtXRTbdAWH2S9JH8Xh/LrqQ== - -"@realm/network-transport@^0.7.2": - version "0.7.2" - resolved "https://registry.yarnpkg.com/@realm/network-transport/-/network-transport-0.7.2.tgz#167b85fd4744b66d9e37d096e1e9e2720d7abd26" - integrity sha512-IZ6yd+mGOYvSMVEVFf/v5qtZOi8bk4ZBxoj25GNQFyeFKxOs1WH+z4IDZscMC2GhQ4hdmI3Sg+RUEphimtHupQ== - dependencies: - "@realm/common" "^0.1.4" - abort-controller "^3.0.0" - node-fetch "^2.6.0" - "@sideway/address@^4.1.3": version "4.1.4" resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.4.tgz#03dccebc6ea47fdc226f7d3d1ad512955d4783f0" @@ -2035,10 +2021,10 @@ "@react-native/virtualized-lists" "^0.72.4" "@types/react" "*" -"@types/react-test-renderer@18.0.2": - version "18.0.2" - resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-18.0.2.tgz#44243977eec18ab8cda88d8977437f47a0d3fdbe" - integrity sha512-tJzMn+9GHDrdrLe0O4rwJELDfTrmdJbCn/UdYyzjlnPiXYXDl5FBNzdw4PVk2R3hJvSHKFjZcRgvZc12lV0p5Q== +"@types/react-test-renderer@18.0.3": + version "18.0.3" + resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-18.0.3.tgz#67922bf5e5f0096581b1efd67dcdeabdd400cfea" + integrity sha512-4wcNLnY6nIT+L6g94CpzL4CXX2P18JvKPU9CDlaHr3DnbP3GiaQLhDotJqjWlVqOcE4UhLRjp0MtxqwuNKONnA== dependencies: "@types/react" "*" @@ -2051,10 +2037,10 @@ "@types/scheduler" "*" csstype "^3.0.2" -"@types/react@18.2.22": - version "18.2.22" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.22.tgz#abe778a1c95a07fa70df40a52d7300a40b949ccb" - integrity sha512-60fLTOLqzarLED2O3UQImc/lsNRgG0jE/a1mPW9KjMemY0LMITWEsbS4VvZ4p6rorEHd5YKxxmMKSDK505GHpA== +"@types/react@18.2.23": + version "18.2.23" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.23.tgz#60ad6cf4895e93bed858db0e03bcc4ff97d0410e" + integrity sha512-qHLW6n1q2+7KyBEYnrZpcsAmU/iiCh9WGCKgXvMxx89+TYdJWRjZohVIo9XTcoLhfX3+/hP0Pbulu3bCZQ9PSA== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -2286,20 +2272,6 @@ acorn@^8.9.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== -ajv-formats@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" - integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== - dependencies: - ajv "^8.0.0" - -ajv-keywords@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" - integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== - dependencies: - fast-deep-equal "^3.1.3" - ajv@^6.12.3, ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" @@ -2310,7 +2282,7 @@ ajv@^6.12.3, ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.0, ajv@^8.6.3, ajv@^8.9.0: +ajv@^8.6.3: version "8.12.0" resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1" integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== @@ -2422,11 +2394,6 @@ aria-query@^5.1.3: dependencies: dequal "^2.0.3" -array-back@^3.0.1, array-back@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/array-back/-/array-back-3.1.0.tgz#b8859d7a508871c9a7b2cf42f99428f65e96bfb0" - integrity sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q== - array-buffer-byte-length@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz#fabe8bc193fea865f317fe7807085ee0dee5aead" @@ -2511,7 +2478,7 @@ arraybuffer.prototype.slice@^1.0.2: is-array-buffer "^3.0.2" is-shared-array-buffer "^1.0.2" -asap@~2.0.3, asap@~2.0.6: +asap@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== @@ -2654,14 +2621,6 @@ babel-jest@29.7.0, babel-jest@^29.7.0: graceful-fs "^4.2.9" slash "^3.0.0" -babel-loader@9.1.3: - version "9.1.3" - resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-9.1.3.tgz#3d0e01b4e69760cc694ee306fe16d358aa1c6f9a" - integrity sha512-xG3ST4DglodGf8qSwv0MdeWLhrDsw/32QMdTO5T1ZIp9gQur0HkCyFs7Awskr10JKXFXwpAhiCuYX5oGXnRGbw== - dependencies: - find-cache-dir "^4.0.0" - schema-utils "^4.0.0" - babel-plugin-istanbul@^6.1.1: version "6.1.1" resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" @@ -2866,7 +2825,7 @@ binary-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== -bindings@^1.3.0, bindings@^1.5.0: +bindings@^1.3.0: version "1.5.0" resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== @@ -3075,10 +3034,10 @@ bser@2.1.1: dependencies: node-int64 "^0.4.0" -bson@4.4.1: - version "4.4.1" - resolved "https://registry.yarnpkg.com/bson/-/bson-4.4.1.tgz#682c3cb8b90b222414ce14ef8398154ba2cc21bc" - integrity sha512-Uu4OCZa0jouQJCKOk1EmmyqtdWAP5HVLru4lQxTwzJzxT+sJ13lVpEZU/MATDxtHiekWMAL84oQY3Xn1LpJVSg== +bson@^4.7.2: + version "4.7.2" + resolved "https://registry.yarnpkg.com/bson/-/bson-4.7.2.tgz#320f4ad0eaf5312dd9b45dc369cc48945e2a5f2e" + integrity sha512-Ry9wCtIZ5kGqkJoi6aD8KjxFZEx78guTQDnpXWiNthsxzrxAK/i8E6pCHAIZTbaEFWcOCvbecMukfK7XUvyLpQ== dependencies: buffer "^5.6.0" @@ -3215,11 +3174,6 @@ caniuse-lite@^1.0.30001502: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001503.tgz#88b6ff1b2cf735f1f3361dc1a15b59f0561aa398" integrity sha512-Sf9NiF+wZxPfzv8Z3iS0rXM1Do+iOy2Lxvib38glFX+08TCYYYGR5fRJXk4d77C4AYwhUjgYgMsMudbh2TqCKw== -caseless@~0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7" - integrity sha512-ODLXH644w9C2fMPAm7bMDQ3GRvipZWZfKc+8As6hIadRIelE0n0xZuN38NS6kiK3KPEVrpymmQD8bvncAHWQkQ== - caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" @@ -3281,11 +3235,6 @@ chownr@^1.1.1: resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== -chownr@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" - integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== - ci-info@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" @@ -3429,16 +3378,6 @@ command-exists@^1.2.8: resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69" integrity sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w== -command-line-args@^5.1.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/command-line-args/-/command-line-args-5.2.1.tgz#c44c32e437a57d7c51157696893c5909e9cec42e" - integrity sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg== - dependencies: - array-back "^3.1.0" - find-replace "^3.0.0" - lodash.camelcase "^4.3.0" - typical "^4.0.0" - commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -3459,11 +3398,6 @@ commander@~2.13.0: resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c" integrity sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA== -common-path-prefix@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/common-path-prefix/-/common-path-prefix-3.0.0.tgz#7d007a7e07c58c4b4d5f433131a19141b29f11e0" - integrity sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w== - commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" @@ -3494,16 +3428,6 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== -concat-stream@^1.4.6, concat-stream@^1.4.7: - version "1.6.2" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" - integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - confusing-browser-globals@^1.0.10: version "1.0.11" resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz#ae40e9b57cdd3915408a2805ebd3a5585608dc81" @@ -3763,11 +3687,6 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" -data-uri-to-buffer@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz#d8feb2b2881e6a4f58c2e08acfd0e2834e26222e" - integrity sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A== - dayjs@^1.8.15: version "1.11.8" resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.8.tgz#4282f139c8c19dd6d0c7bd571e30c2d0ba7698ea" @@ -3831,11 +3750,6 @@ deep-is@^0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== -deepmerge@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.1.0.tgz#511a54fff405fc346f0240bb270a3e9533a31102" - integrity sha512-Q89Z26KAfA3lpPGhbF6XMfYAm3jIV3avViy6KOJ2JLzFbeWHOvPQUu5aSJIWXap3gDZC2y1eF5HXEPI2wGqgvw== - deepmerge@^4.2.2, deepmerge@^4.3.0: version "4.3.1" resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" @@ -4900,14 +4814,6 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" -fetch-blob@^3.1.2, fetch-blob@^3.1.4: - version "3.2.0" - resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9" - integrity sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ== - dependencies: - node-domexception "^1.0.0" - web-streams-polyfill "^3.0.3" - fetch-mock@9.11.0: version "9.11.0" resolved "https://registry.yarnpkg.com/fetch-mock/-/fetch-mock-9.11.0.tgz#371c6fb7d45584d2ae4a18ee6824e7ad4b637a3f" @@ -4985,21 +4891,6 @@ find-cache-dir@^2.0.0: make-dir "^2.0.0" pkg-dir "^3.0.0" -find-cache-dir@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-4.0.0.tgz#a30ee0448f81a3990708f6453633c733e2f6eec2" - integrity sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg== - dependencies: - common-path-prefix "^3.0.0" - pkg-dir "^7.0.0" - -find-replace@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/find-replace/-/find-replace-3.0.0.tgz#3e7e23d3b05167a76f770c9fbd5258b0def68c38" - integrity sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ== - dependencies: - array-back "^3.0.1" - find-up@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" @@ -5023,7 +4914,7 @@ find-up@^5.0.0: locate-path "^6.0.0" path-exists "^4.0.0" -find-up@^6.2.0, find-up@^6.3.0: +find-up@^6.2.0: version "6.3.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-6.3.0.tgz#2abab3d3280b2dc7ac10199ef324c4e002c8c790" integrity sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw== @@ -5122,13 +5013,6 @@ form-data@~2.3.2: combined-stream "^1.0.6" mime-types "^2.1.12" -formdata-polyfill@^4.0.10: - version "4.0.10" - resolved "https://registry.yarnpkg.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz#24807c31c9d402e002ab3d8c720144ceb8848423" - integrity sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g== - dependencies: - fetch-blob "^3.1.2" - forwarded@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" @@ -5153,15 +5037,6 @@ fs-extra@^11.0.0: jsonfile "^6.0.1" universalify "^2.0.0" -fs-extra@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94" - integrity sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg== - dependencies: - graceful-fs "^4.1.2" - jsonfile "^4.0.0" - universalify "^0.1.0" - fs-extra@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" @@ -5181,13 +5056,6 @@ fs-extra@^9.0.0: jsonfile "^6.0.1" universalify "^2.0.0" -fs-minipass@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" - integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== - dependencies: - minipass "^3.0.0" - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -5402,7 +5270,7 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" -graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.9: +graceful-fs@^4.1.11, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.9: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== @@ -5528,15 +5396,6 @@ html-escaper@^2.0.0: resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== -http-basic@^2.5.1: - version "2.5.1" - resolved "https://registry.yarnpkg.com/http-basic/-/http-basic-2.5.1.tgz#8ce447bdb5b6c577f8a63e3fa78056ec4bb4dbfb" - integrity sha512-q/qOkgjcnZ90v0wSaMwamhfAhIf6lhOsH0ehHFnQHAt1lA9MedSnmqEEnh8bq0njTBAK3IsmS2gEuXryfWCDkw== - dependencies: - caseless "~0.11.0" - concat-stream "^1.4.6" - http-response-object "^1.0.0" - http-errors@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" @@ -5568,11 +5427,6 @@ http-proxy@^1.18.1: follow-redirects "^1.0.0" requires-port "^1.0.0" -http-response-object@^1.0.0, http-response-object@^1.0.1, http-response-object@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/http-response-object/-/http-response-object-1.1.0.tgz#a7c4e75aae82f3bb4904e4f43f615673b4d518c3" - integrity sha512-adERueQxEMtIfGk4ee/9CG7AGUjS09OyHeKrubTjmHUsEVXesrGlZLWYnCL8fajPZIX9H4NDnXyyzBPrF078sA== - http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" @@ -5692,7 +5546,7 @@ inherits@2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== -ini@^1.3.4, ini@^1.3.7, ini@~1.3.0: +ini@^1.3.4, ini@~1.3.0: version "1.3.8" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== @@ -6910,11 +6764,6 @@ lodash-es@^4.17.21: resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== -lodash.camelcase@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" - integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== - lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" @@ -7446,31 +7295,11 @@ minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5, minimist@^1.2.6: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== -minipass@^3.0.0: - version "3.3.6" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" - integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== - dependencies: - yallist "^4.0.0" - -minipass@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" - integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== - "minipass@^5.0.0 || ^6.0.2 || ^7.0.0": version "7.0.2" resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.2.tgz#58a82b7d81c7010da5bd4b2c0c85ac4b4ec5131e" integrity sha512-eL79dXrE1q9dBbDCLg7xfn/vl7MS4F1gvJAgjJrQli/jbQWdUttuVawphqpffoIYfRdq78LHx6GP4bU/EQ2ATA== -minizlib@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" - integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== - dependencies: - minipass "^3.0.0" - yallist "^4.0.0" - mixin-object@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/mixin-object/-/mixin-object-2.0.1.tgz#4fb949441dab182540f1fe035ba60e1947a5e57e" @@ -7491,11 +7320,6 @@ mkdirp@^0.5.1, mkdirp@~0.5.1: dependencies: minimist "^1.2.6" -mkdirp@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" - integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== - moment-timezone@0.5.43: version "0.5.43" resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.43.tgz#3dd7f3d0c67f78c23cd1906b9b2137a09b3c4790" @@ -7613,11 +7437,6 @@ node-abort-controller@^3.1.1: resolved "https://registry.yarnpkg.com/node-abort-controller/-/node-abort-controller-3.1.1.tgz#a94377e964a9a37ac3976d848cb5c765833b8548" integrity sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ== -node-addon-api@4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.2.0.tgz#117cbb5a959dff0992e1c586ae0393573e4d2a87" - integrity sha512-eazsqzwG2lskuzBqCGPi7Ac2UgOoMz8JVOXVhTvvPDYhthvNpefx8jWD8Np7Gv+2Sz0FlPWZk0nJV0z598Wn8Q== - node-dir@^0.1.17: version "0.1.17" resolved "https://registry.yarnpkg.com/node-dir/-/node-dir-0.1.17.tgz#5f5665d93351335caabef8f1c554516cf5f1e4e5" @@ -7625,11 +7444,6 @@ node-dir@^0.1.17: dependencies: minimatch "^3.0.2" -node-domexception@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" - integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== - node-fetch@^2.2.0, node-fetch@^2.6.0: version "2.6.11" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.11.tgz#cde7fc71deef3131ef80a738919f999e6edfff25" @@ -7637,14 +7451,12 @@ node-fetch@^2.2.0, node-fetch@^2.6.0: dependencies: whatwg-url "^5.0.0" -node-fetch@^3.2.10: - version "3.3.2" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.3.2.tgz#d1e889bacdf733b4ff3b2b243eb7a12866a0b78b" - integrity sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA== +node-fetch@^2.6.9: + version "2.7.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== dependencies: - data-uri-to-buffer "^4.0.0" - fetch-blob "^3.1.4" - formdata-polyfill "^4.0.10" + whatwg-url "^5.0.0" node-gyp-build@^4.3.0: version "4.6.0" @@ -7694,7 +7506,7 @@ node-libs-browser@2.2.1: util "^0.11.0" vm-browserify "^1.0.1" -node-machine-id@^1.1.10: +node-machine-id@^1.1.12: version "1.1.12" resolved "https://registry.yarnpkg.com/node-machine-id/-/node-machine-id-1.1.12.tgz#37904eee1e59b320bb9c5d6c0a59f3b469cb6267" integrity sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ== @@ -8193,13 +8005,6 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" -pkg-dir@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-7.0.0.tgz#8f0c08d6df4476756c5ff29b3282d0bab7517d11" - integrity sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA== - dependencies: - find-up "^6.3.0" - pkg-up@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-4.0.0.tgz#b2ca5e845979e31497d81520b621f4cdac2dcd75" @@ -8207,7 +8012,7 @@ pkg-up@^4.0.0: dependencies: find-up "^6.2.0" -prebuild-install@^7.0.1: +prebuild-install@^7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.1.tgz#de97d5b34a70a0c81334fd24641f2a1702352e45" integrity sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw== @@ -8280,7 +8085,7 @@ process@^0.11.10: resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== -progress@^2.0.0, progress@^2.0.3: +progress@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== @@ -8290,13 +8095,6 @@ promise-polyfill@^6.0.1: resolved "https://registry.yarnpkg.com/promise-polyfill/-/promise-polyfill-6.1.0.tgz#dfa96943ea9c121fca4de9b5868cb39d3472e057" integrity sha512-g0LWaH0gFsxovsU7R5LrrhHhWAWiHRnh1GPrhXnPgYsDkIqjRYUYSZEsej/wtleDrz5xVSIDbeKfidztp2XHFQ== -promise@^7.1.1: - version "7.3.1" - resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" - integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg== - dependencies: - asap "~2.0.3" - promise@^8.3.0: version "8.3.0" resolved "https://registry.yarnpkg.com/promise/-/promise-8.3.0.tgz#8cb333d1edeb61ef23869fbb8a4ea0279ab60e0a" @@ -8390,7 +8188,7 @@ qs@6.11.0: dependencies: side-channel "^1.0.4" -qs@^6.1.0, qs@^6.11.0: +qs@^6.11.0: version "6.11.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9" integrity sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA== @@ -8412,11 +8210,6 @@ querystring@^0.2.0: resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.1.tgz#40d77615bb09d16902a85c3e38aa8b5ed761c2dd" integrity sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg== -querystringify@^2.1.1: - version "2.2.0" - resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" - integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== - queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -8646,7 +8439,7 @@ react@18.2.0: dependencies: loose-envify "^1.1.0" -readable-stream@^2.0.2, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: +readable-stream@^2.0.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.8" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== @@ -8680,30 +8473,16 @@ readline@^1.3.0: resolved "https://registry.yarnpkg.com/readline/-/readline-1.3.0.tgz#c580d77ef2cfc8752b132498060dc9793a7ac01c" integrity sha512-k2d6ACCkiNYz222Fs/iNze30rRJ1iIicW7JuX/7/cozvih6YCkFZH+J6mAFDVgv0dRBaAyr4jDqC95R2y4IADg== -realm@11.10.2: - version "11.10.2" - resolved "https://registry.yarnpkg.com/realm/-/realm-11.10.2.tgz#03b395c817c2a9ff9a6afd2ded59af2f0c07fca1" - integrity sha512-P6L5OGFC8hEFVcIkwr6Dppkqg3Sq8Kxnb/bGE9rEdTQeuv6Mcr5VwFLqVAMn0OhGKJJsbmSQtcyqu+kax4komQ== - dependencies: - "@realm/common" "^0.1.4" - "@realm/network-transport" "^0.7.2" - bindings "^1.5.0" - bson "4.4.1" - command-line-args "^5.1.1" - deepmerge "2.1.0" - fs-extra "^4.0.3" - ini "^1.3.7" - node-addon-api "4.2.0" - node-fetch "^3.2.10" - node-machine-id "^1.1.10" - prebuild-install "^7.0.1" - progress "^2.0.3" - prop-types "^15.6.2" - request "^2.88.0" - stream-counter "^1.0.0" - sync-request "^3.0.1" - tar "^6.0.1" - url-parse "^1.4.4" +realm@12.2.0: + version "12.2.0" + resolved "https://registry.yarnpkg.com/realm/-/realm-12.2.0.tgz#a1758b5051bb6996bce90492cf94b6030ca7950d" + integrity sha512-4MFmWWl5eARaU0toMGX6cjgSX53/4jLNoHyc3UN30bXoTPKw3JVAsL070GM+Ywuhl4D5ubFZOU4y+x+r9vDgew== + dependencies: + bson "^4.7.2" + debug "^4.3.4" + node-fetch "^2.6.9" + node-machine-id "^1.1.12" + prebuild-install "^7.1.1" recast@^0.21.0: version "0.21.5" @@ -8793,7 +8572,7 @@ repeat-string@^1.5.2, repeat-string@^1.6.1: resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w== -request@^2.88.0, request@^2.88.2: +request@^2.88.2: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== @@ -8948,7 +8727,7 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" -ripple-address-codec@4.3.0, ripple-address-codec@^4.2.4, ripple-address-codec@^4.2.5, ripple-address-codec@^4.3.0: +ripple-address-codec@^4.2.4, ripple-address-codec@^4.2.5, ripple-address-codec@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/ripple-address-codec/-/ripple-address-codec-4.3.0.tgz#45edeb0312b4fe4607b37b7c4cff467802ad571d" integrity sha512-Tvd81i7hpDmNqHvkj6iYlj8Tv3I1Romw5gfjni9eacewJvGV2xe+p2y0FAw39z72qfciRMhQyHvpnviBcWVBNw== @@ -9061,16 +8840,6 @@ scheduler@^0.23.0: dependencies: loose-envify "^1.1.0" -schema-utils@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.1.0.tgz#4cff1e434c12ed39502378b9a3e24787b37df41d" - integrity sha512-Jw+GZVbP5IggB2WAn6UHI02LBwGmsIeYN/lNbSMZyDziQ7jmtAUrqKqDja+W89YHVs+KL/3IkIMltAklqB1vAw== - dependencies: - "@types/json-schema" "^7.0.9" - ajv "^8.9.0" - ajv-formats "^2.1.1" - ajv-keywords "^5.1.0" - seed-random@~2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/seed-random/-/seed-random-2.2.0.tgz#2a9b19e250a817099231a5b99a4daf80b7fbed54" @@ -9402,11 +9171,6 @@ stream-chain@^2.2.5: resolved "https://registry.yarnpkg.com/stream-chain/-/stream-chain-2.2.5.tgz#b30967e8f14ee033c5b9a19bbe8a2cba90ba0d09" integrity sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA== -stream-counter@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/stream-counter/-/stream-counter-1.0.0.tgz#91cf2569ce4dc5061febcd7acb26394a5a114751" - integrity sha512-4nfHc1016AhNOs0CFDR3S0FNeqnYbT7xZ408coajcx2Msj8malNNjvFHzWYIfIAXNK5i0eaKIVfgBYPOkyOTIg== - stream-http@^2.7.2: version "2.8.3" resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc" @@ -9653,15 +9417,6 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -sync-request@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/sync-request/-/sync-request-3.0.1.tgz#caa1235aaf889ba501076a1834c436830a82fb73" - integrity sha512-bnOSypECs6aB9ScWHcJAkS9z55jOhO3tdLefLfJ+J58vC2HCi5tjxmFMxLv0RxvuAFFQ/G4BupVehqpAlbi+3Q== - dependencies: - concat-stream "^1.4.7" - http-response-object "^1.0.1" - then-request "^2.0.1" - synckit@^0.8.5: version "0.8.5" resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.8.5.tgz#b7f4358f9bb559437f9f167eb6bc46b3c9818fa3" @@ -9701,18 +9456,6 @@ tar-stream@^2.1.4: inherits "^2.0.3" readable-stream "^3.1.1" -tar@^6.0.1: - version "6.2.0" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.0.tgz#b14ce49a79cb1cd23bc9b016302dea5474493f73" - integrity sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ== - dependencies: - chownr "^2.0.0" - fs-minipass "^2.0.0" - minipass "^5.0.0" - minizlib "^2.1.1" - mkdirp "^1.0.3" - yallist "^4.0.0" - telnet-client@1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/telnet-client/-/telnet-client-1.2.8.tgz#946c0dadc8daa3f19bb40a3e898cb870403a4ca4" @@ -9764,18 +9507,6 @@ text-table@^0.2.0: resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== -then-request@^2.0.1: - version "2.2.0" - resolved "https://registry.yarnpkg.com/then-request/-/then-request-2.2.0.tgz#6678b32fa0ca218fe569981bbd8871b594060d81" - integrity sha512-YM/Fho1bQ3JFX9dgFQsBswc3aSTePXvtNHl3aXJTZNz/444yC86EVJR92aWMRNA0O9X0UfmojyCTUcT8Lbo5yA== - dependencies: - caseless "~0.11.0" - concat-stream "^1.4.7" - http-basic "^2.5.1" - http-response-object "^1.1.0" - promise "^7.1.1" - qs "^6.1.0" - thenify-all@^1.0.0: version "1.6.0" resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" @@ -10066,11 +9797,6 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -typedarray@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== - typeforce@^1.11.5: version "1.18.0" resolved "https://registry.yarnpkg.com/typeforce/-/typeforce-1.18.0.tgz#d7416a2c5845e085034d70fcc5b6cc4a90edbfdc" @@ -10081,11 +9807,6 @@ typescript@5.2.2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.2.tgz#5ebb5e5a5b75f085f22bc3f8460fba308310fa78" integrity sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w== -typical@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/typical/-/typical-4.0.0.tgz#cbeaff3b9d7ae1e2bbfaf5a4e6f11eccfde94fc4" - integrity sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw== - uglify-es@^3.1.9: version "3.3.9" resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677" @@ -10167,14 +9888,6 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -url-parse@^1.4.4: - version "1.5.10" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" - integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== - dependencies: - querystringify "^2.1.1" - requires-port "^1.0.0" - url@^0.11.0: version "0.11.1" resolved "https://registry.yarnpkg.com/url/-/url-0.11.1.tgz#26f90f615427eca1b9f4d6a28288c147e2302a32" @@ -10306,11 +10019,6 @@ wcwidth@^1.0.1: dependencies: defaults "^1.0.3" -web-streams-polyfill@^3.0.3: - version "3.2.1" - resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz#71c2718c52b45fd49dbeee88634b3a60ceab42a6" - integrity sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q== - webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" From b20a4f2470027d0a630745ac7e84e4369c4cd402 Mon Sep 17 00:00:00 2001 From: N3TC4T Date: Wed, 4 Oct 2023 12:22:59 +0200 Subject: [PATCH 06/12] chore: cleanup --- src/components/Modules/__tests__/storyshots.test-ignore.tsx | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 src/components/Modules/__tests__/storyshots.test-ignore.tsx diff --git a/src/components/Modules/__tests__/storyshots.test-ignore.tsx b/src/components/Modules/__tests__/storyshots.test-ignore.tsx deleted file mode 100644 index 3fb7cac50..000000000 --- a/src/components/Modules/__tests__/storyshots.test-ignore.tsx +++ /dev/null @@ -1,5 +0,0 @@ -// import initStoryshots from '@storybook/addon-storyshots'; -// -// initStoryshots({ -// configPath: 'storybook', -// }); From d73e90e80361f790f657a435f8e7dd8d06af61e2 Mon Sep 17 00:00:00 2001 From: N3TC4T Date: Wed, 4 Oct 2023 14:28:21 +0200 Subject: [PATCH 07/12] fix: pathfinding is not resolving --- src/common/libs/ledger/pathFinding.ts | 2 +- src/services/NetworkService.ts | 89 +++++++++++++++------------ 2 files changed, 50 insertions(+), 41 deletions(-) diff --git a/src/common/libs/ledger/pathFinding.ts b/src/common/libs/ledger/pathFinding.ts index b390ec5c0..6e8fd8b53 100644 --- a/src/common/libs/ledger/pathFinding.ts +++ b/src/common/libs/ledger/pathFinding.ts @@ -154,7 +154,7 @@ class LedgerPathFinding extends EventEmitter { // request is canceled if (id !== this.requestId) { - reject(new Error('CANCELED')); + reject(new Error('Request has been canceled and invalidated')); return; } diff --git a/src/services/NetworkService.ts b/src/services/NetworkService.ts index bf236f67b..1de02cbb6 100644 --- a/src/services/NetworkService.ts +++ b/src/services/NetworkService.ts @@ -1,6 +1,7 @@ /** * Network service */ + import { v4 as uuidv4 } from 'uuid'; import EventEmitter from 'events'; import { Platform } from 'react-native'; @@ -21,8 +22,8 @@ import { NormalizeFeeDataSet, PrepareTxForHookFee } from '@common/utils/fee'; import { AppScreens, NetworkConfig } from '@common/constants'; import AppService, { AppStateStatus, NetStateStatus } from '@services/AppService'; -import LoggerService from '@services/LoggerService'; import NavigationService, { RootType } from '@services/NavigationService'; +import LoggerService from '@services/LoggerService'; /* Types ==================================================================== */ export enum NetworkStateStatus { @@ -342,7 +343,7 @@ class NetworkService extends EventEmitter { }; /** - * Destroy the connection + * Attempts to destroy the current socket connection. */ destroyConnection = () => { try { @@ -355,7 +356,7 @@ class NetworkService extends EventEmitter { }; /** - * Close socket connection + * Attempts to close the current socket connection. */ closeConnection = () => { try { @@ -368,7 +369,7 @@ class NetworkService extends EventEmitter { }; /** - * Reinstate current socket connection + * Attempts to reinstate the current socket connection. */ reinstateConnection = () => { try { @@ -381,7 +382,7 @@ class NetworkService extends EventEmitter { }; /** - * Reconnect current connection + * Re-establishes the current network connection. */ reconnect = () => { try { @@ -396,40 +397,38 @@ class NetworkService extends EventEmitter { }; /** - * Sends passed payload to the connected node - * @param payload - * @returns {Promise} + * Asynchronously sends a payload to the connected node and ensures that the response is valid by + * matching the network ID. + * + * @param {Object} payload - The data object that needs to be sent to the connected node. Should contain an 'id' + * property or it will be automatically assigned a unique identifier. + * @returns {Promise} The response from the node. If the response is an object, it is augmented with + * the original ID and network ID before being returned. + * @throws {Error} Throws an error if the network ID in the response does not match the current network ID. + * + * @todo refining the error handling to distinguish between network issues and ID mismatches. */ - send = (payload: any): Promise => { - return new Promise((resolve, reject) => { - this.connection - .send( - { ...payload, id: `${uuidv4()}.${this.network.id}` }, - { timeoutSeconds: NetworkService.TIMEOUT_SECONDS }, - ) - .then((res) => { - // check if we are still on the same network - if (res.id?.split('.')[1] !== `${this.network.id}`) { - reject(new Error('Invalidated response, wrong network id!')); - return; - } - - if (typeof res === 'object') { - resolve({ - ...res, - networkId: this.network.id, - }); - return; - } - - resolve(res); - }) - .catch(reject); + send = async (payload: any): Promise => { + const payloadWithNetworkId = { + ...payload, + id: `${payload.id || uuidv4()}.${this.network.id}`, + }; + + const res = await this.connection.send(payloadWithNetworkId, { + timeoutSeconds: NetworkService.TIMEOUT_SECONDS, }); + + const [resId, resNetworkId] = res.id?.split('.') || []; + + if (resNetworkId !== this.network.id.toString()) { + throw new Error('Mismatched network ID in response.'); + } + + return typeof res === 'object' ? { ...res, id: resId, networkId: this.network.id } : res; }; /** - * Update network definitions + * Updates the network definitions and persists them to the `NetworkRepository`. */ updateNetworkDefinitions = () => { // include definitions hash if exist in the request @@ -472,7 +471,7 @@ class NetworkService extends EventEmitter { }; /** - * Update network enabled amendments + * Updates the network's enabled amendments by fetching and persisting them. */ updateNetworkFeatures = () => { this.send({ @@ -497,7 +496,8 @@ class NetworkService extends EventEmitter { }; /** - * Update network reserve state + * Updates the network reserve state by querying the server for current values and + * persists any changes locally and to the `NetworkRepository`. */ updateNetworkReserve = () => { this.send({ command: 'server_info' }) @@ -534,9 +534,9 @@ class NetworkService extends EventEmitter { /** * Logs socket errors - * @param err + * @param error */ - onError = (err: any) => { + onError = (error: any) => { // set connection status this.setConnectionStatus(NetworkStateStatus.Disconnected); @@ -546,7 +546,7 @@ class NetworkService extends EventEmitter { this.lastNetworkErrorId = this.network.id; } - this.logger.error('Socket Error: ', err || 'Tried all nodes!'); + this.logger.error('Socket Error: ', error || 'Tried all nodes!'); }; /** @@ -593,7 +593,16 @@ class NetworkService extends EventEmitter { }; /** - * Establish connection to the network + * Establishes a network connection using predefined or custom nodes. + * + * This method initiates the connection by: + * - Setting the connection status to 'Connecting'. + * - Determining the nodes to connect based on the network type. + * - Instantiating a new `XrplClient` with specified nodes and configuration. + * - Setting up event listeners for various connection states ('online', 'offline', and 'error'). + * + * @throws Will log an error if connection instantiation fails or event listeners encounter an issue. + * */ connect = () => { // set the connection status to connecting From 106043e601ac3e74cc9d8dc39d50be2a0a477cf1 Mon Sep 17 00:00:00 2001 From: N3TC4T Date: Wed, 4 Oct 2023 15:15:35 +0200 Subject: [PATCH 08/12] fix: NFTokenOfferTemplate is not rendrered correctly --- src/components/Modules/RecipientElement/styles.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/Modules/RecipientElement/styles.ts b/src/components/Modules/RecipientElement/styles.ts index e3dbabb5a..948a9d51a 100644 --- a/src/components/Modules/RecipientElement/styles.ts +++ b/src/components/Modules/RecipientElement/styles.ts @@ -4,7 +4,6 @@ import { AppFonts } from '@theme'; /* Styles ==================================================================== */ export default StyleService.create({ container: { - flex: 1, flexDirection: 'row', alignItems: 'center', padding: 5, From 29ca65504744009a1fb54c5cff520bafe950782c Mon Sep 17 00:00:00 2001 From: N3TC4T Date: Wed, 4 Oct 2023 16:04:57 +0200 Subject: [PATCH 09/12] feat: include network label to SendView and ReviewStep --- src/components/General/Header/Header.tsx | 6 +- .../Modules/NetworkLabel/NetworkLabel.tsx | 98 +++++++++++++++++++ src/components/Modules/NetworkLabel/index.ts | 1 + src/components/Modules/NetworkLabel/styles.ts | 18 ++++ src/components/Modules/index.ts | 1 + .../Steps/Review/ReviewStep.tsx | 3 +- src/screens/Send/SendView.tsx | 3 +- src/services/NetworkService.ts | 4 + 8 files changed, 131 insertions(+), 3 deletions(-) create mode 100644 src/components/Modules/NetworkLabel/NetworkLabel.tsx create mode 100644 src/components/Modules/NetworkLabel/index.ts create mode 100644 src/components/Modules/NetworkLabel/styles.ts diff --git a/src/components/General/Header/Header.tsx b/src/components/General/Header/Header.tsx index fcd8b8fcf..630b8a597 100644 --- a/src/components/General/Header/Header.tsx +++ b/src/components/General/Header/Header.tsx @@ -22,12 +22,14 @@ interface ChildrenProps { iconStyle?: ImageStyle; render?: any; onPress?: () => void; + extraComponent?: React.ReactNode; } interface Props { placement: placementType; leftComponent?: ChildrenProps; centerComponent?: ChildrenProps; + subComponent?: ChildrenProps; rightComponent?: ChildrenProps; backgroundColor?: string; containerStyle?: ViewStyle; @@ -88,7 +90,7 @@ const Children = ({ onPress={onPress} > {children.text && children.icon && ( - + {(placement === 'left' || placement === 'center') && ( )} @@ -115,6 +117,8 @@ const Children = ({ style={[styles.iconStyle, children.iconStyle]} /> )} + + {children.extraComponent && children.extraComponent} ); }; diff --git a/src/components/Modules/NetworkLabel/NetworkLabel.tsx b/src/components/Modules/NetworkLabel/NetworkLabel.tsx new file mode 100644 index 000000000..4f2f110e9 --- /dev/null +++ b/src/components/Modules/NetworkLabel/NetworkLabel.tsx @@ -0,0 +1,98 @@ +import React, { PureComponent } from 'react'; +import { Text, TextStyle, View, ViewStyle } from 'react-native'; + +import NetworkService from '@services/NetworkService'; + +import { NetworkModel } from '@store/models'; + +import { TouchableDebounce } from '@components/General'; + +import { AppSizes } from '@theme'; +import styles from './styles'; + +/* Types ==================================================================== */ +interface Props { + containerStyle?: ViewStyle | ViewStyle[]; + textStyle?: TextStyle | TextStyle[]; + type: 'circle' | 'text' | 'both'; + onPress?: (network: NetworkModel) => void; +} + +interface State { + network: NetworkModel; +} + +/* Component ==================================================================== */ +class NetworkLabel extends PureComponent { + public static CircleSize = AppSizes.scale(10); + + constructor(props: Props) { + super(props); + + this.state = { + network: NetworkService.getNetwork(), + }; + } + + componentDidMount() { + NetworkService.on('networkChange', this.onNetworkChange); + } + + componentWillUnmount() { + NetworkService.off('networkChange', this.onNetworkChange); + } + + onNetworkChange = (network: NetworkModel) => { + this.setState({ + network, + }); + }; + + onPress = () => { + const { onPress } = this.props; + const { network } = this.state; + + if (typeof onPress === 'function') { + onPress(network); + } + }; + + render() { + const { type, containerStyle, textStyle } = this.props; + const { network } = this.state; + + if (!network) { + return null; + } + + return ( + + {['text', 'both'].includes(type) && ( + + {network.name} + + )} + {['circle', 'both'].includes(type) && ( + + )} + + ); + } +} + +export default NetworkLabel; diff --git a/src/components/Modules/NetworkLabel/index.ts b/src/components/Modules/NetworkLabel/index.ts new file mode 100644 index 000000000..8d21b4f3a --- /dev/null +++ b/src/components/Modules/NetworkLabel/index.ts @@ -0,0 +1 @@ +export { default as NetworkLabel } from './NetworkLabel'; diff --git a/src/components/Modules/NetworkLabel/styles.ts b/src/components/Modules/NetworkLabel/styles.ts new file mode 100644 index 000000000..3ad02ad54 --- /dev/null +++ b/src/components/Modules/NetworkLabel/styles.ts @@ -0,0 +1,18 @@ +import StyleService from '@services/StyleService'; + +import { AppFonts } from '@theme'; +/* Styles ==================================================================== */ +export default StyleService.create({ + container: { + flexDirection: 'row', + justifyContent: 'center', + alignItems: 'center', + }, + textStyle: { + fontFamily: AppFonts.base.familyExtraBold, + fontSize: AppFonts.small.size, + color: '$textPrimary', + textAlign: 'center', + paddingHorizontal: 5, + }, +}); diff --git a/src/components/Modules/index.ts b/src/components/Modules/index.ts index f54769156..2cf809d5a 100644 --- a/src/components/Modules/index.ts +++ b/src/components/Modules/index.ts @@ -14,5 +14,6 @@ export * from './PaymentOptionsPicker'; export * from './InactiveAccount'; export * from './NFTokenElement'; export * from './NetworkSwitchButton'; +export * from './NetworkLabel'; export * from './AccountSwitchElement'; export * from './XAppBrowserHeader'; diff --git a/src/screens/Modal/ReviewTransaction/Steps/Review/ReviewStep.tsx b/src/screens/Modal/ReviewTransaction/Steps/Review/ReviewStep.tsx index f1d6c0e9f..8a0c8ae75 100644 --- a/src/screens/Modal/ReviewTransaction/Steps/Review/ReviewStep.tsx +++ b/src/screens/Modal/ReviewTransaction/Steps/Review/ReviewStep.tsx @@ -16,7 +16,7 @@ import { AccountModel } from '@store/models'; // components import { Avatar, Button, KeyboardAwareScrollView, Spacer, SwipeButton } from '@components/General'; -import { AccountPicker } from '@components/Modules'; +import { AccountPicker, NetworkLabel } from '@components/Modules'; import Localize from '@locale'; @@ -308,6 +308,7 @@ class ReviewStep extends Component { ? Localize.t('global.signIn') : Localize.t('global.reviewTransaction')} +