From 13011af133dbd2aefd3ce7ec19a386a71fabd2d5 Mon Sep 17 00:00:00 2001 From: Jon Tzeng Date: Wed, 16 Oct 2024 16:42:32 -0700 Subject: [PATCH 01/89] Disable Florida ACH sell --- CHANGELOG.md | 1 + src/constants/plugins/sellPluginList.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 000d05dce1a..83d6964ae78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ - changed: Remove legacy non-segwit wallets from new account wallet selection scene - fixed: Use account default fiat for transaction fee display in `SweepPrivateKeyCalculateFeeScene` - fixed: Crash when archiving a wallet that recently accessed the trade modal +- fixed: ACH sell option for Florida visible with no accepted currencies ## 4.15.0 (2024-10-16) diff --git a/src/constants/plugins/sellPluginList.json b/src/constants/plugins/sellPluginList.json index 8d12f309ce4..57a14b5368e 100644 --- a/src/constants/plugins/sellPluginList.json +++ b/src/constants/plugins/sellPluginList.json @@ -12,7 +12,7 @@ "US" ], "notStateProvinces": { - "US": ["AK", "AR", "CT", "NC", "NY", "TX"] + "US": ["AK", "AR", "CT", "NC", "NY", "TX", "FL"] }, "cryptoCodes": [], "paymentTypeLogoKey": "bank" From 7a07d17c030fdb9a1a522a2499c8283bb67a3be5 Mon Sep 17 00:00:00 2001 From: Jon Tzeng Date: Thu, 17 Oct 2024 15:31:30 -0700 Subject: [PATCH 02/89] Upgrade edge-login-ui-rn@^3.22.5 --- ios/Podfile.lock | 8 ++++---- package.json | 2 +- yarn.lock | 12 ++++++------ 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index fa977fba828..f543f1f3435 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -23,7 +23,7 @@ PODS: - React-Core - edge-exchange-plugins (2.12.0): - React-Core - - edge-login-ui-rn (3.22.4): + - edge-login-ui-rn (3.22.5): - React-Core - EXApplication (5.1.1): - ExpoModulesCore @@ -1079,12 +1079,12 @@ SPEC CHECKSUMS: CNIOLinux: 62e3505f50de558c393dc2f273dde71dcce518da CNIOWindows: 3047f2d8165848a3936a0a755fee27c6b5ee479b disklet: e7ed3e673ccad9d175a1675f9f3589ffbf69a5fd - DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54 + DoubleConversion: 76ab83afb40bddeeee456813d9c04f67f78771b5 edge-core-js: c49ecfb894c2a86ca7475848419174a2a9ed95db edge-currency-accountbased: ba759ec765d487d86e5f7e26a38e14edfc1d64dd edge-currency-plugins: 38eaf53c2d9fdbdd30ade3ad09fd698f428f208f edge-exchange-plugins: 5037e196e652d1dca42afacd86b2395bd0d7f298 - edge-login-ui-rn: 59a7ab5b9d5bbebd045fa77b09a1e26647845c85 + edge-login-ui-rn: 33f78f4089a63314ca3d652b0ed235f1e1a1bd4c EXApplication: d8f53a7eee90a870a75656280e8d4b85726ea903 EXConstants: f348da07e21b23d2b085e270d7b74f282df1a7d9 EXFileSystem: 844e86ca9b5375486ecc4ef06d3838d5597d895d @@ -1101,7 +1101,7 @@ SPEC CHECKSUMS: FirebaseInstallations: 766dabca09fd94aef922538aaf144cc4a6fb6869 FirebaseMessaging: 585984d0a1df120617eb10b44cad8968b859815e fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9 - glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b + glog: c5d68082e772fa1c511173d6b30a9de2c05a69a2 GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15 gRPC-Swift: 74adcaaa62ac5e0a018938840328cb1fdfb09e7b diff --git a/package.json b/package.json index 643cdde8c78..46af41738ad 100644 --- a/package.json +++ b/package.json @@ -103,7 +103,7 @@ "edge-currency-plugins": "^3.4.3", "edge-exchange-plugins": "^2.12.0", "edge-info-server": "^3.0.1", - "edge-login-ui-rn": "^3.22.4", + "edge-login-ui-rn": "^3.22.5", "ethers": "^5.7.2", "expo": "^48.0.0", "jsrsasign": "^11.1.0", diff --git a/yarn.lock b/yarn.lock index c6065406526..1c9016c2087 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9072,9 +9072,9 @@ ecdsa-sig-formatter@1.0.11: dependencies: safe-buffer "^5.0.1" -"ecpair@git+https://github.com/EdgeApp/ecpair.git#b193eb8ea2ec0c93b528f4b0223a605407ff43e4": +"ecpair@https://github.com/EdgeApp/ecpair.git#b193eb8ea2ec0c93b528f4b0223a605407ff43e4": version "2.1.0" - resolved "git+https://github.com/EdgeApp/ecpair.git#b193eb8ea2ec0c93b528f4b0223a605407ff43e4" + resolved "https://github.com/EdgeApp/ecpair.git#b193eb8ea2ec0c93b528f4b0223a605407ff43e4" dependencies: "@types/bs58check" "^2.1.0" base-x "^4.0.0" @@ -9238,10 +9238,10 @@ edge-info-server@^3.0.1: dependencies: cleaners "^0.3.16" -edge-login-ui-rn@^3.22.4: - version "3.22.4" - resolved "https://registry.yarnpkg.com/edge-login-ui-rn/-/edge-login-ui-rn-3.22.4.tgz#6ec456e71a37bde287a9841a970553493234228f" - integrity sha512-0DvlOiRJ68vhmzJxutP4DgA48qQBkpJyVHt7Wbhc4+vBacquYd4pVg2pYTQV42Tuqzak1aX/jX5ehu7CTeHt1w== +edge-login-ui-rn@^3.22.5: + version "3.22.5" + resolved "https://registry.yarnpkg.com/edge-login-ui-rn/-/edge-login-ui-rn-3.22.5.tgz#49108f305baee084cf8ff1dd84ca61f0a9396524" + integrity sha512-3b3cFcPRZWBW6g2CKdDAsfdCQRrMEZkKsfO31wfrBizPt2pU/Uy7u/Hb4Bmt25Wq0l9ldHjxt6xEafHL3l/d+A== dependencies: base-x "^4.0.0" cleaners "^0.3.12" From e190ba272b6661a366c99cbdefb1ae59ae9aa9bb Mon Sep 17 00:00:00 2001 From: Jon Tzeng Date: Wed, 16 Oct 2024 14:19:56 -0700 Subject: [PATCH 03/89] Fix `backup_for_transfer_message` --- src/components/scenes/RequestScene.tsx | 2 +- src/locales/en_US.ts | 2 +- src/locales/strings/enUS.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/scenes/RequestScene.tsx b/src/components/scenes/RequestScene.tsx index cb7ae90adf5..a5043d49c13 100644 --- a/src/components/scenes/RequestScene.tsx +++ b/src/components/scenes/RequestScene.tsx @@ -297,7 +297,7 @@ export class RequestSceneComponent extends React.Component - {lstrings.backup_for_transfer_message} + {sprintf(lstrings.backup_for_transfer_message, config.appName)} diff --git a/src/locales/en_US.ts b/src/locales/en_US.ts index cbc6c6ce6d8..b879bb559ce 100644 --- a/src/locales/en_US.ts +++ b/src/locales/en_US.ts @@ -1559,7 +1559,7 @@ const strings = { backup_web3_handle_warning_message: 'Without a backup, you risk losing your web3 handle!', tap_to_learn_more: 'Tap to learn more.', backup_for_transfer_message: - 'To buy, sell, and receive funds, please create a full account. Edge full accounts require no personal information and use a familiar username and password method that will safeguard your assets and help prevent loss of funds.\n\nNever share your username and password, and store your credentials securely!', + 'To buy, sell, and receive funds, please create a full account. %1$s full accounts require no personal information and use a familiar username and password method that will safeguard your assets and help prevent loss of funds.\n\nNever share your username and password, and store your credentials securely!', guest_account: 'Guest Account', tap_to_create_username_password: 'Tap to create a username and password', diff --git a/src/locales/strings/enUS.json b/src/locales/strings/enUS.json index 3c3a02ebd08..c88e5b9dbc2 100644 --- a/src/locales/strings/enUS.json +++ b/src/locales/strings/enUS.json @@ -1367,7 +1367,7 @@ "backup_warning_message": "Without a backup, you risk losing your funds!", "backup_web3_handle_warning_message": "Without a backup, you risk losing your web3 handle!", "tap_to_learn_more": "Tap to learn more.", - "backup_for_transfer_message": "To buy, sell, and receive funds, please create a full account. Edge full accounts require no personal information and use a familiar username and password method that will safeguard your assets and help prevent loss of funds.\n\nNever share your username and password, and store your credentials securely!", + "backup_for_transfer_message": "To buy, sell, and receive funds, please create a full account. %1$s full accounts require no personal information and use a familiar username and password method that will safeguard your assets and help prevent loss of funds.\n\nNever share your username and password, and store your credentials securely!", "guest_account": "Guest Account", "tap_to_create_username_password": "Tap to create a username and password", "backup_title": "Create Full Account", From 5f620b7e5e839f0d9c760f64a450a0b359420efc Mon Sep 17 00:00:00 2001 From: Jon Tzeng Date: Wed, 16 Oct 2024 16:03:44 -0700 Subject: [PATCH 04/89] Update copy for light account notification --- CHANGELOG.md | 2 ++ .../scenes/__snapshots__/SwapSuccessScene.test.tsx.snap | 4 ++-- src/components/notification/NotificationView.tsx | 4 ++-- src/locales/en_US.ts | 4 ++-- src/locales/strings/enUS.json | 3 +-- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 83d6964ae78..e75147b0794 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +- changed: Wording in light account persistent notification + ## 4.16.0 - added: "Transaction Details" button to `SwapSuccessScene` diff --git a/src/__tests__/scenes/__snapshots__/SwapSuccessScene.test.tsx.snap b/src/__tests__/scenes/__snapshots__/SwapSuccessScene.test.tsx.snap index 29da9d903b8..7f52bdba8e7 100644 --- a/src/__tests__/scenes/__snapshots__/SwapSuccessScene.test.tsx.snap +++ b/src/__tests__/scenes/__snapshots__/SwapSuccessScene.test.tsx.snap @@ -511,7 +511,7 @@ exports[`SwapSuccessSceneComponent should render with loading props 1`] = ` } type="warning" > - Guest Account + Create Username & Password - Tap to create a username and password + Funds will be lost if your device is lost or Edge is uninstalled. diff --git a/src/components/notification/NotificationView.tsx b/src/components/notification/NotificationView.tsx index ba10a5fb15f..81b3cf8beb5 100644 --- a/src/components/notification/NotificationView.tsx +++ b/src/components/notification/NotificationView.tsx @@ -146,8 +146,8 @@ const NotificationViewComponent = (props: Props) => { diff --git a/src/locales/en_US.ts b/src/locales/en_US.ts index b879bb559ce..fe1af80e3b8 100644 --- a/src/locales/en_US.ts +++ b/src/locales/en_US.ts @@ -1561,8 +1561,8 @@ const strings = { backup_for_transfer_message: 'To buy, sell, and receive funds, please create a full account. %1$s full accounts require no personal information and use a familiar username and password method that will safeguard your assets and help prevent loss of funds.\n\nNever share your username and password, and store your credentials securely!', - guest_account: 'Guest Account', - tap_to_create_username_password: 'Tap to create a username and password', + backup_notification_title: 'Create Username & Password', + backup_notification_body: 'Funds will be lost if your device is lost or %1$s is uninstalled.', // Backup Message Variants backup_title: 'Create Full Account', diff --git a/src/locales/strings/enUS.json b/src/locales/strings/enUS.json index c88e5b9dbc2..52ca4bc2902 100644 --- a/src/locales/strings/enUS.json +++ b/src/locales/strings/enUS.json @@ -1368,8 +1368,7 @@ "backup_web3_handle_warning_message": "Without a backup, you risk losing your web3 handle!", "tap_to_learn_more": "Tap to learn more.", "backup_for_transfer_message": "To buy, sell, and receive funds, please create a full account. %1$s full accounts require no personal information and use a familiar username and password method that will safeguard your assets and help prevent loss of funds.\n\nNever share your username and password, and store your credentials securely!", - "guest_account": "Guest Account", - "tap_to_create_username_password": "Tap to create a username and password", + "backup_notification_body": "Funds will be lost if your device is lost, stolen, or %1$s is uninstalled. Create a username and password to secure your funds.", "backup_title": "Create Full Account", "backup_message": "Create a username and password to continue.", "backup_message_subtext": "Creating a full account ensures you can safely recover your funds in the event that you lose access to your device.", From a223883ffa2ed7ccf78dda413d6b672962112058 Mon Sep 17 00:00:00 2001 From: Jon Tzeng Date: Thu, 17 Oct 2024 15:19:19 -0700 Subject: [PATCH 05/89] Add `customTitleKey` prop to `GuiPluginRow` --- src/types/GuiPluginTypes.ts | 3 +++ src/util/GuiPluginTools.ts | 1 + 2 files changed, 4 insertions(+) diff --git a/src/types/GuiPluginTypes.ts b/src/types/GuiPluginTypes.ts index a6ca6209c78..735c9faa4c2 100644 --- a/src/types/GuiPluginTypes.ts +++ b/src/types/GuiPluginTypes.ts @@ -78,6 +78,8 @@ export interface GuiPluginRow { title: string description: string + /** Overrides `title` with a defined React node definition */ + customTitleKey?: string paymentType?: FiatPaymentType partnerIconPath?: string paymentTypeLogoKey?: string @@ -105,6 +107,7 @@ const asGuiPluginJsonRow = asObject({ // List display options: title: asOptional(asString), + customTitleKey: asOptional(asString), description: asOptional(asString), partnerIconPath: asOptional(asString), paymentTypeLogoKey: asOptional(asString), diff --git a/src/util/GuiPluginTools.ts b/src/util/GuiPluginTools.ts index 3ac92238789..1cf81a3698b 100644 --- a/src/util/GuiPluginTools.ts +++ b/src/util/GuiPluginTools.ts @@ -62,6 +62,7 @@ export function filterGuiPluginJson( if (row.deepQuery != null) merged.deepQuery = { ...merged.deepQuery, ...row.deepQuery } if (row.paymentType != null) merged.paymentType = row.paymentType if (row.title != null) merged.title = row.title + if (row.customTitleKey != null) merged.customTitleKey = row.customTitleKey if (row.description != null) merged.description = row.description if (row.partnerIconPath != null) merged.partnerIconPath = row.partnerIconPath if (row.paymentTypeLogoKey != null) merged.paymentTypeLogoKey = row.paymentTypeLogoKey From 8b066194c4a9ce89cd13b2fbf55bfe5aaf523287 Mon Sep 17 00:00:00 2001 From: Jon Tzeng Date: Thu, 17 Oct 2024 15:20:44 -0700 Subject: [PATCH 06/89] Add custom title renderer for Apple Pay --- CHANGELOG.md | 4 ++ .../__snapshots__/GuiPlugins.test.ts.snap | 3 +- src/components/scenes/GuiPluginListScene.tsx | 43 +++++++++++++++++-- src/constants/plugins/buyPluginList.json | 3 +- 4 files changed, 47 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e75147b0794..29bddda41fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ - changed: Wording in light account persistent notification +## 4.15.1 + +- fixed: "Apple Pay" title per latest branding requirements + ## 4.16.0 - added: "Transaction Details" button to `SwapSuccessScene` diff --git a/src/__tests__/__snapshots__/GuiPlugins.test.ts.snap b/src/__tests__/__snapshots__/GuiPlugins.test.ts.snap index 3555fe4005c..e0d14f1a264 100644 --- a/src/__tests__/__snapshots__/GuiPlugins.test.ts.snap +++ b/src/__tests__/__snapshots__/GuiPlugins.test.ts.snap @@ -211,6 +211,7 @@ Settlement: 5 min - 24 hours", }, { "cryptoCodes": [], + "customTitleKey": "applepay", "deepPath": "", "deepQuery": {}, "description": "Fee: 5-7% @@ -221,7 +222,7 @@ Settlement: 10 - 30 minutes", "credit", ], "pluginId": "creditcard", - "title": "Pay with Apple Pay", + "title": "", }, { "cryptoCodes": [], diff --git a/src/components/scenes/GuiPluginListScene.tsx b/src/components/scenes/GuiPluginListScene.tsx index 74c9e4bfe46..63e64a0272b 100644 --- a/src/components/scenes/GuiPluginListScene.tsx +++ b/src/components/scenes/GuiPluginListScene.tsx @@ -12,6 +12,7 @@ import { checkAndShowLightBackupModal } from '../../actions/BackupModalActions' import { checkAndSetRegion, showCountrySelectionModal } from '../../actions/CountryListActions' import { getDeviceSettings, writeDeveloperPluginUri } from '../../actions/DeviceSettingsActions' import { NestedDisableMap } from '../../actions/ExchangeInfoActions' +import paymentTypeLogoApplePay from '../../assets/images/paymentTypes/paymentTypeLogoApplePay.png' import { FLAG_LOGO_URL } from '../../constants/CdnConstants' import { COUNTRY_CODES } from '../../constants/CountryConstants' import buyPluginJsonRaw from '../../constants/plugins/buyPluginList.json' @@ -316,6 +317,30 @@ class GuiPluginList extends React.PureComponent { onPluginOpened() } + renderTitle = (guiPluginRow: GuiPluginRow) => { + const styles = getStyles(this.props.theme) + const { title, customTitleKey } = guiPluginRow + + switch (customTitleKey) { + case 'applepay': + // Per Apple branding guidelines, "Pay With" is NOT to be translated. + return ( + + + {'Pay with '} + + + + ) + default: + return ( + + {title} + + ) + } + } + renderPlugin = ({ item, index }: ListRenderItemInfo) => { const { theme } = this.props const { pluginId } = item @@ -328,7 +353,6 @@ class GuiPluginList extends React.PureComponent { const partnerLogoThemeKey = pluginPartnerLogos[pluginId] const pluginPartnerLogo = partnerLogoThemeKey ? theme[partnerLogoThemeKey] : { uri: getPartnerIconUri(item.partnerIconPath ?? '') } const poweredBy = plugin.poweredBy ?? plugin.displayName - return ( { paddingRem={[1, 0.5, 1, 0.5]} > - - {item.title} - + {this.renderTitle(item)} {item.description === '' ? null : {item.description}} {poweredBy != null && item.partnerIconPath != null ? ( <> @@ -529,6 +551,19 @@ const getStyles = cacheStyles((theme: Theme) => ({ titleText: { fontFamily: theme.fontFaceMedium }, + titleAppleContainer: { + flexDirection: 'row', + justifyContent: 'flex-start', + alignItems: 'flex-end', + flexShrink: 1 + }, + titleAppleLogo: { + height: theme.rem(1), + width: 'auto', + aspectRatio: 150 / 64, + resizeMode: 'contain', + marginBottom: 1 + }, subtitleText: { marginTop: theme.rem(0.25), fontSize: theme.rem(0.75), diff --git a/src/constants/plugins/buyPluginList.json b/src/constants/plugins/buyPluginList.json index 59e6e83785c..6412dfb6caf 100644 --- a/src/constants/plugins/buyPluginList.json +++ b/src/constants/plugins/buyPluginList.json @@ -319,7 +319,8 @@ "paymentType": "applepay", "description": "Fee: 5-7%\nSettlement: 10 - 30 minutes", "paymentTypes": ["credit"], - "title": "Pay with Apple Pay", + "title": "", + "customTitleKey": "applepay", "forCountries": [ "AD", "AE", "AF", "AG", "AI", "AL", "AM", "AO", "AQ", "AR", "AS", "AT", "AU", "AW", "AZ", "BA", "BB", "BD", "BE", "BF", From e21a3c592ec6e8db6cfb4281c6ba46226e050790 Mon Sep 17 00:00:00 2001 From: Jon Tzeng Date: Mon, 21 Oct 2024 12:28:42 -0700 Subject: [PATCH 07/89] Remove Bank Wire Transfer buy for Florida --- src/constants/plugins/buyPluginList.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/constants/plugins/buyPluginList.json b/src/constants/plugins/buyPluginList.json index 6412dfb6caf..ed531b92ffe 100644 --- a/src/constants/plugins/buyPluginList.json +++ b/src/constants/plugins/buyPluginList.json @@ -66,7 +66,7 @@ "title": "Bank Wire Transfer", "description": "Fee: ~2%\nSettlement: ~5 minutes\nLimit $10000", "forCountries": ["US"], - "notStateProvinces": { "US": ["AK", "AR", "CT", "NC", "NY", "TX"] }, + "notStateProvinces": { "US": ["AK", "AR", "CT", "NC", "NY", "TX", "FL"] }, "cryptoCodes": [], "paymentTypeLogoKey": "bank" }, From 6e3db493d38c6e377cc03deafd58407f93a6fc67 Mon Sep 17 00:00:00 2001 From: Jon Tzeng Date: Mon, 21 Oct 2024 12:30:49 -0700 Subject: [PATCH 08/89] Disable Paypal Sell for Canada --- CHANGELOG.md | 2 ++ src/constants/plugins/sellPluginList.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 29bddda41fc..7efc4da829a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ ## Unreleased - changed: Wording in light account persistent notification +- removed: Bank Wire Transfer Buy for Florida +- removed: Paypal Sell for Canada ## 4.15.1 diff --git a/src/constants/plugins/sellPluginList.json b/src/constants/plugins/sellPluginList.json index 57a14b5368e..b4d4ef2a50e 100644 --- a/src/constants/plugins/sellPluginList.json +++ b/src/constants/plugins/sellPluginList.json @@ -118,7 +118,7 @@ "title": "Paypal", "description": "Fee: ~5%\nSettlement: 5 min - 24 hours", "forCountries": [ - "AE", "AR", "AT", "AU", "BE", "BR", "CA", "CH", "CL", "CO", + "AE", "AR", "AT", "AU", "BE", "BR", "CH", "CL", "CO", "CZ", "DE", "DK", "EG", "ES", "FI", "FR", "GH", "GR", "HK", "HU", "IE", "IL", "IN", "IT", "JP", "KE", "KR", "MX", "MY", "NG", "NL", "NO", "NZ", "PE", "PH", "PL", "PT", "RU", "SA", From ba4a125e698402b45783d52ac678fd29bbb355f1 Mon Sep 17 00:00:00 2001 From: Jon Tzeng Date: Wed, 16 Oct 2024 16:17:43 -0700 Subject: [PATCH 09/89] Allow 0 amount entry in `SpendingLimitsScene` --- CHANGELOG.md | 1 + src/components/scenes/SpendingLimitsScene.tsx | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7efc4da829a..99ec25c3c04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ - added: "Go to Parent Wallet" `WalletListMenuModal` option - added: Earn Scene - added: Include wallet creation date in wallet data in log output +- changed: Allow 0 amount entry in `SpendingLimitsScene` - changed: Wording on `SwapSuccessScene` - changed: `SettingsScene` disables "Restore Wallets" option if there are no wallets to restore - changed: FIO Home Scene tile replaced with Earn diff --git a/src/components/scenes/SpendingLimitsScene.tsx b/src/components/scenes/SpendingLimitsScene.tsx index 076419376e7..565b24368b5 100644 --- a/src/components/scenes/SpendingLimitsScene.tsx +++ b/src/components/scenes/SpendingLimitsScene.tsx @@ -45,7 +45,7 @@ export const SpendingLimitsScene = (props: Props) => { const spendingLimits = { transaction: { isEnabled: transactionIsEnabled, - amount: parseFloat(transactionAmount) + amount: zeroString(transactionAmount) ? 0 : parseFloat(transactionAmount) } } await writeSpendingLimits(account, spendingLimits) @@ -61,8 +61,7 @@ export const SpendingLimitsScene = (props: Props) => { handleSubmitAsync().catch(err => showError(err)) }) - const amount = parseFloat(transactionAmount) - const enableSlider = password.length > 8 && !isNaN(amount) && amount > 0 + const enableSlider = password.length > 8 return ( From 9e7c9564cc186a86f1eacd52b60158bfd3951e40 Mon Sep 17 00:00:00 2001 From: Jon Tzeng Date: Thu, 17 Oct 2024 16:30:49 -0700 Subject: [PATCH 10/89] Rename validateMemos.ts->memoUtils.ts --- src/components/scenes/SendScene2.tsx | 2 +- src/components/scenes/TransactionDetailsScene.tsx | 2 +- src/util/{validateMemos.ts => memoUtils.ts} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename src/util/{validateMemos.ts => memoUtils.ts} (100%) diff --git a/src/components/scenes/SendScene2.tsx b/src/components/scenes/SendScene2.tsx index 65f7d929dbd..11896cc7fc9 100644 --- a/src/components/scenes/SendScene2.tsx +++ b/src/components/scenes/SendScene2.tsx @@ -40,8 +40,8 @@ import { getCurrencyCode } from '../../util/CurrencyInfoHelpers' import { getWalletName } from '../../util/CurrencyWalletHelpers' import { addToFioAddressCache, checkRecordSendFee, FIO_FEE_EXCEEDS_SUPPLIED_MAXIMUM, FIO_NO_BUNDLED_ERR_CODE, recordSend } from '../../util/FioAddressUtils' import { logActivity } from '../../util/logger' +import { getMemoError, getMemoLabel, getMemoTitle } from '../../util/memoUtils' import { convertTransactionFeeToDisplayFee, darkenHexColor, DECIMAL_PRECISION, zeroString } from '../../util/utils' -import { getMemoError, getMemoLabel, getMemoTitle } from '../../util/validateMemos' import { AlertCardUi4 } from '../cards/AlertCard' import { EdgeCard } from '../cards/EdgeCard' import { AccentColors } from '../common/DotsBackground' diff --git a/src/components/scenes/TransactionDetailsScene.tsx b/src/components/scenes/TransactionDetailsScene.tsx index fa12392fd62..3f8e2f56c03 100644 --- a/src/components/scenes/TransactionDetailsScene.tsx +++ b/src/components/scenes/TransactionDetailsScene.tsx @@ -22,8 +22,8 @@ import { useSelector } from '../../types/reactRedux' import { EdgeSceneProps } from '../../types/routerTypes' import { getCurrencyCodeWithAccount } from '../../util/CurrencyInfoHelpers' import { matchJson } from '../../util/matchJson' +import { getMemoTitle } from '../../util/memoUtils' import { convertCurrencyFromExchangeRates, convertNativeToExchange, darkenHexColor, removeIsoPrefix } from '../../util/utils' -import { getMemoTitle } from '../../util/validateMemos' import { ButtonsView } from '../buttons/ButtonsView' import { AdvancedDetailsCard } from '../cards/AdvancedDetailsCard' import { EdgeCard } from '../cards/EdgeCard' diff --git a/src/util/validateMemos.ts b/src/util/memoUtils.ts similarity index 100% rename from src/util/validateMemos.ts rename to src/util/memoUtils.ts From f96774364c5317977d033819ce3d05889f15923d Mon Sep 17 00:00:00 2001 From: Jon Tzeng Date: Thu, 17 Oct 2024 16:41:04 -0700 Subject: [PATCH 11/89] Factor out `SendScene2` memo methods into `memoUtils` --- src/components/scenes/SendScene2.tsx | 15 ++++----------- src/util/memoUtils.ts | 19 ++++++++++++++++++- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/components/scenes/SendScene2.tsx b/src/components/scenes/SendScene2.tsx index 11896cc7fc9..42a0c8b0815 100644 --- a/src/components/scenes/SendScene2.tsx +++ b/src/components/scenes/SendScene2.tsx @@ -5,7 +5,6 @@ import { EdgeAccount, EdgeCurrencyWallet, EdgeDenomination, - EdgeMemo, EdgeSpendInfo, EdgeSpendTarget, EdgeTokenId, @@ -40,7 +39,7 @@ import { getCurrencyCode } from '../../util/CurrencyInfoHelpers' import { getWalletName } from '../../util/CurrencyWalletHelpers' import { addToFioAddressCache, checkRecordSendFee, FIO_FEE_EXCEEDS_SUPPLIED_MAXIMUM, FIO_NO_BUNDLED_ERR_CODE, recordSend } from '../../util/FioAddressUtils' import { logActivity } from '../../util/logger' -import { getMemoError, getMemoLabel, getMemoTitle } from '../../util/memoUtils' +import { createEdgeMemo, getDefaultMemoString, getMemoError, getMemoLabel, getMemoTitle } from '../../util/memoUtils' import { convertTransactionFeeToDisplayFee, darkenHexColor, DECIMAL_PRECISION, zeroString } from '../../util/utils' import { AlertCardUi4 } from '../cards/AlertCard' import { EdgeCard } from '../cards/EdgeCard' @@ -585,7 +584,7 @@ const SendComponent = (props: Props) => { // Only supports the first spendTarget that has a `memo` or `uniqueIdentifier` const renderUniqueIdentifier = () => { const spendTarget = spendInfo.spendTargets[0] - const uniqueIdentifier = spendInfo.memos?.[0]?.value ?? spendTarget?.memo ?? spendTarget?.uniqueIdentifier + const uniqueIdentifier = getDefaultMemoString(spendInfo, spendTarget) const [memoOption] = memoOptions.filter(option => option.hidden !== true) if (memoOption != null && spendTarget.publicAddress != null) { @@ -602,12 +601,6 @@ const SendComponent = (props: Props) => { maxLength = 2 * memoOption.maxBytes } - const createEdgeMemo = (text: string): EdgeMemo => ({ - type: memoOption.type, - memoName: memoOption.memoName, - value: text - }) - const handleUniqueIdentifier = async () => { await Airship.show(bridge => ( { message={sprintf(lstrings.unique_identifier_modal_description, memoLabel)} submitLabel={lstrings.unique_identifier_modal_confirm} title={memoTitle} - onSubmit={async text => getMemoError(createEdgeMemo(text), memoOption) ?? true} + onSubmit={async text => getMemoError(createEdgeMemo(memoOptions, text), memoOption) ?? true} /> )).then(value => { if (value == null) return - spendInfo.memos = [createEdgeMemo(value)] + spendInfo.memos = [createEdgeMemo(memoOptions, value)] setSpendInfo({ ...spendInfo }) }) } diff --git a/src/util/memoUtils.ts b/src/util/memoUtils.ts index d2dd7258238..d3aa7d6dd82 100644 --- a/src/util/memoUtils.ts +++ b/src/util/memoUtils.ts @@ -1,12 +1,29 @@ import { gt } from 'biggystring' import { asMaybe } from 'cleaners' -import { EdgeMemo, EdgeMemoOption } from 'edge-core-js' +import { EdgeMemo, EdgeMemoOption, EdgeSpendInfo, EdgeSpendTarget } from 'edge-core-js' import { sprintf } from 'sprintf-js' import { lstrings } from '../locales/strings' import { asBase16 } from './cleaners/asHex' import { asIntegerString } from './cleaners/asIntegerString' +/** + * Creates an EdgeMemo from a memo option and text + */ +export const createEdgeMemo = (memoOptions: EdgeMemoOption[], text: string): EdgeMemo => { + const [memoOption] = memoOptions.filter(option => option.hidden !== true) + return { + type: memoOption.type, + memoName: memoOption.memoName, + value: text + } +} + +/** Get a default memo, compatible with all potentially deprecated props */ +export const getDefaultMemoString = (spendInfo: EdgeSpendInfo, spendTarget?: EdgeSpendTarget) => { + return spendInfo.memos?.[0]?.value ?? spendTarget?.memo ?? spendTarget?.uniqueIdentifier +} + /** * Checks a memo against a memo option. * Returns `undefined` if valid, or an error string if invalid. From 90be20b8a7c7037ce40a719c8f8c4ade4f52af2c Mon Sep 17 00:00:00 2001 From: Jon Tzeng Date: Thu, 17 Oct 2024 17:47:43 -0700 Subject: [PATCH 12/89] Initialize `spendInfo` with memos --- CHANGELOG.md | 1 + src/components/scenes/SendScene2.tsx | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99ec25c3c04..28f656281c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ - changed: Navigate to wallet list after restoring wallets - changed: Update `NotificationCard` to UI4 styling - changed: Remove legacy non-segwit wallets from new account wallet selection scene +- fixed: Deeplinking to `SendScene` may not retain memo information in the resulting broadcast - fixed: Use account default fiat for transaction fee display in `SweepPrivateKeyCalculateFeeScene` - fixed: Crash when archiving a wallet that recently accessed the trade modal - fixed: ACH sell option for Florida visible with no accepted currencies diff --git a/src/components/scenes/SendScene2.tsx b/src/components/scenes/SendScene2.tsx index 42a0c8b0815..a5cb44a973d 100644 --- a/src/components/scenes/SendScene2.tsx +++ b/src/components/scenes/SendScene2.tsx @@ -151,7 +151,6 @@ const SendComponent = (props: Props) => { const initExpireDate = isoExpireDate != null ? new Date(isoExpireDate) : undefined const [processingAmountChanged, setProcessingAmountChanged] = React.useState(false) const [walletId, setWalletId] = useState(initWalletId) - const [spendInfo, setSpendInfo] = useState(initSpendInfo ?? { tokenId: null, spendTargets: [{}] }) const [fieldChanged, setFieldChanged] = useState('fiat') const [feeNativeAmount, setFeeNativeAmount] = useState('') const [minNativeAmount, setMinNativeAmount] = useState(initMinNativeAmount) @@ -179,9 +178,24 @@ const SendComponent = (props: Props) => { const hasNotifications = useSelector(state => state.ui.notificationHeight > 0) const currencyWallets = useWatch(account, 'currencyWallets') - const [tokenId, setTokenId] = useState(spendInfo.tokenId ?? tokenIdProp) const coreWallet = currencyWallets[walletId] const { pluginId, memoOptions = [] } = coreWallet.currencyInfo + + // Initialize `spendInfo` from route params, including possible memos + const [spendInfo, setSpendInfo] = useState(() => { + if (initSpendInfo == null) return { tokenId: null, spendTargets: [{}] } + + const spendTarget = initSpendInfo.spendTargets[0] + const uniqueIdentifier = getDefaultMemoString(initSpendInfo, spendTarget) + + if (uniqueIdentifier == null || spendTarget.publicAddress == null) { + return initSpendInfo + } else { + return { ...initSpendInfo, memos: [createEdgeMemo(memoOptions, uniqueIdentifier)] } + } + }) + + const [tokenId, setTokenId] = useState(spendInfo.tokenId ?? tokenIdProp) const currencyCode = getCurrencyCode(coreWallet, tokenId) const cryptoDisplayDenomination = useDisplayDenom(coreWallet.currencyConfig, tokenId) const cryptoExchangeDenomination = getExchangeDenom(coreWallet.currencyConfig, tokenId) From f05506a9f5d517db64a8e5630b98856af36e4c42 Mon Sep 17 00:00:00 2001 From: Jon Tzeng Date: Mon, 21 Oct 2024 15:33:08 -0700 Subject: [PATCH 13/89] Fix `WalletRestoreScene` `handlePressDone` --- src/components/scenes/WalletRestoreScene.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/scenes/WalletRestoreScene.tsx b/src/components/scenes/WalletRestoreScene.tsx index 41eb36b2c45..83427f9cbad 100644 --- a/src/components/scenes/WalletRestoreScene.tsx +++ b/src/components/scenes/WalletRestoreScene.tsx @@ -58,7 +58,7 @@ export function WalletRestoreScene(props: Props) { )) if (response === 'confirm') { const states: EdgeWalletStates = {} - for (const info of restoreWalletInfos) { + for (const info of selectedWalletInfos) { states[info.id] = { archived: false, deleted: false } } await account.changeWalletStates(states) From 61b19105bed6c7ea2cbff50e4802bb77889901fd Mon Sep 17 00:00:00 2001 From: Jon Tzeng Date: Tue, 22 Oct 2024 16:25:41 -0700 Subject: [PATCH 14/89] Differentiate tx action bought/sold vs buy/sell, cleanup duplicate strings - We need a distinction now for a past buy/sell that already occurred, vs the verb to buy/sell. - Would incur a small cost to re-translate, but worth the organizational benefits to remove duplicates at the same time. Most of the localizations for the `TX_ACTION_LABEL_MAP` renamed keys are not translated, anyway. - Only the `buy_crypto_modal_buy_action` had translations, which were ported over for the interim while we wait for translations. --- src/actions/ScanActions.tsx | 2 +- src/components/modals/InsufficientFeesModal.tsx | 2 +- src/components/scenes/GuiPluginListScene.tsx | 6 +----- src/components/themed/TransactionListTop.tsx | 4 ++-- src/constants/txActionConstants.ts | 4 ++-- src/locales/en_US.ts | 9 ++++----- src/locales/strings/de.json | 7 ++++--- src/locales/strings/enUS.json | 12 ++++++------ src/locales/strings/es.json | 7 ++++--- src/locales/strings/esMX.json | 7 ++++--- src/locales/strings/fr.json | 6 +++--- src/locales/strings/it.json | 7 ++++--- src/locales/strings/ja.json | 7 ++++--- src/locales/strings/kaa.json | 7 ++++--- src/locales/strings/ko.json | 6 +++--- src/locales/strings/pt.json | 7 ++++--- src/locales/strings/ru.json | 6 +++--- src/locales/strings/vi.json | 6 +++--- src/locales/strings/zh.json | 6 +++--- src/plugins/gui/amountQuotePlugin.ts | 2 +- 20 files changed, 61 insertions(+), 59 deletions(-) diff --git a/src/actions/ScanActions.tsx b/src/actions/ScanActions.tsx index e990e839eb5..2dcdc895819 100644 --- a/src/actions/ScanActions.tsx +++ b/src/actions/ScanActions.tsx @@ -355,7 +355,7 @@ export function checkAndShowGetCryptoModal(navigation: NavigationBase, wallet: E title={lstrings.buy_crypto_modal_title} message={messageSyntax} buttons={{ - buy: { label: sprintf(lstrings.buy_crypto_modal_buy_action, currencyCode) }, + buy: { label: sprintf(lstrings.buy_1s, currencyCode) }, exchange: { label: lstrings.buy_crypto_modal_exchange, type: 'primary' }, decline: { label: lstrings.buy_crypto_decline } }} diff --git a/src/components/modals/InsufficientFeesModal.tsx b/src/components/modals/InsufficientFeesModal.tsx index d18e23a9c53..04f53418e84 100644 --- a/src/components/modals/InsufficientFeesModal.tsx +++ b/src/components/modals/InsufficientFeesModal.tsx @@ -59,7 +59,7 @@ export function InsufficientFeesModal(props: Props) { {message} diff --git a/src/components/scenes/GuiPluginListScene.tsx b/src/components/scenes/GuiPluginListScene.tsx index 63e64a0272b..8cd6791731b 100644 --- a/src/components/scenes/GuiPluginListScene.tsx +++ b/src/components/scenes/GuiPluginListScene.tsx @@ -418,11 +418,7 @@ class GuiPluginList extends React.PureComponent { return ( <> - + {hasCountryData ? ( diff --git a/src/components/themed/TransactionListTop.tsx b/src/components/themed/TransactionListTop.tsx index 0292e809c3f..ed875f8cb11 100644 --- a/src/components/themed/TransactionListTop.tsx +++ b/src/components/themed/TransactionListTop.tsx @@ -244,7 +244,7 @@ export class TransactionListTopComponent extends React.PureComponent this.handleTradeBuy(bridge)} icon={ @@ -255,7 +255,7 @@ export class TransactionListTopComponent extends React.PureComponent this.handleTradeSell(bridge)} icon={ diff --git a/src/constants/txActionConstants.ts b/src/constants/txActionConstants.ts index fbca05b3f20..69a65edeb4b 100644 --- a/src/constants/txActionConstants.ts +++ b/src/constants/txActionConstants.ts @@ -3,10 +3,10 @@ import { EdgeAssetActionType } from 'edge-core-js' import { lstrings } from '../locales/strings' export const TX_ACTION_LABEL_MAP: Record = { - buy: lstrings.fiat_plugin_buy_currencycode, + buy: lstrings.transaction_details_bought_1s, claim: lstrings.transaction_details_claim, claimOrder: lstrings.transaction_details_claim_order, - sell: lstrings.fiat_plugin_sell_currencycode_s, + sell: lstrings.transaction_details_sold_1s, sellNetworkFee: lstrings.fiat_plugin_sell_network_fee, swap: lstrings.transaction_details_swap, swapNetworkFee: lstrings.transaction_details_swap_network_fee, diff --git a/src/locales/en_US.ts b/src/locales/en_US.ts index fe1af80e3b8..97059f001a7 100644 --- a/src/locales/en_US.ts +++ b/src/locales/en_US.ts @@ -654,10 +654,10 @@ const strings = { title_edit_token: 'Edit Token', title_add_token: 'Add Token', title_password_recovery: 'Password Recovery', - title_plugin_buy_s: 'Buy %1$s', + transaction_details_bought_1s: 'Buy %1$s', title_select_region: 'Select Region', title_select_payment_method: 'Select Payment Method', - title_plugin_sell_s: 'Sell %1$s', + transaction_details_sold_1s: 'Sell %1$s', title_otp: '2-Factor Security', title_register_fio_address: 'Register FIO Address', title_register_fio_domain: 'Register FIO Domain', @@ -836,7 +836,8 @@ const strings = { buy_parent_crypto_modal_message_3s: '%1$s%2$s (on %3$s) is required to send this transaction. Would you like to buy %2$s or exchange another crypto into %2$s?', buy_crypto_decline: 'Not at this time', - buy_crypto_modal_buy_action: 'Buy %s', + buy_1s: 'Buy %1$s', + sell_1s: 'Sell %1$s', buy_crypto_modal_exchange: 'Exchange', exchange_crypto_modal_message: 'Your %s wallet is empty. Would you like to exchange another crypto into %s?', private_key_modal_sweep_from_private_address: 'Sweep Funds From Private Key', @@ -1441,8 +1442,6 @@ const strings = { fiat_plugin_select_asset_to_purchase: 'Select Asset to Purchase', fiat_plugin_select_asset_to_sell: 'Select Asset to Sell', - fiat_plugin_buy_currencycode: 'Buy %s', // TODO: string key convention - fiat_plugin_sell_currencycode_s: 'Sell %s', fiat_plugin_sell_network_fee: 'Sell Network Fee', fiat_plugin_amount_currencycode: 'Amount %s', fiat_plugin_buy_amount_over_limit: 'Max purchase amount is %s', diff --git a/src/locales/strings/de.json b/src/locales/strings/de.json index ee9fc90a3ef..f880b8ffe3e 100644 --- a/src/locales/strings/de.json +++ b/src/locales/strings/de.json @@ -586,10 +586,10 @@ "title_edit_token": "Token hinzufügen", "title_add_token": "Token hinzufügen", "title_password_recovery": "Passwort-Wiederherstellung", - "title_plugin_buy_s": "Buy %1$s", + "transaction_details_bought_1s": "Buy %1$s", "title_select_region": "Select Region", "title_select_payment_method": "Select Payment Method", - "title_plugin_sell_s": "Sell %1$s", + "transaction_details_sold_1s": "Sell %1$s", "title_otp": "2-Faktor Sicherheit", "title_register_fio_address": "FIO-Adresse registrieren", "title_register_fio_domain": "FIO-Domain registrieren", @@ -745,7 +745,8 @@ "buy_parent_crypto_modal_message_2s": "%1$s%2$s is required to send this transaction. Would you like to buy %2$s or exchange another crypto into %2$s?", "buy_parent_crypto_modal_message_3s": "%1$s%2$s (on %3$s) is required to send this transaction. Would you like to buy %2$s or exchange another crypto into %2$s?", "buy_crypto_decline": "Momentan nicht", - "buy_crypto_modal_buy_action": "%s Kaufen ", + "buy_1s": "%1$s Kaufen", + "sell_1s": "Sell %1$s", "buy_crypto_modal_exchange": "Tauschen", "exchange_crypto_modal_message": "Deine %s -Wallet ist leer. Möchtest du eine andere Kryptowährung in %s tauschen?", "private_key_modal_sweep_from_private_address": "Guthaben vom privaten Schlüssel verwerfen", diff --git a/src/locales/strings/enUS.json b/src/locales/strings/enUS.json index 52ca4bc2902..19d7ca6407d 100644 --- a/src/locales/strings/enUS.json +++ b/src/locales/strings/enUS.json @@ -586,10 +586,10 @@ "title_edit_token": "Edit Token", "title_add_token": "Add Token", "title_password_recovery": "Password Recovery", - "title_plugin_buy_s": "Buy %1$s", + "transaction_details_bought_1s": "Buy %1$s", "title_select_region": "Select Region", "title_select_payment_method": "Select Payment Method", - "title_plugin_sell_s": "Sell %1$s", + "transaction_details_sold_1s": "Sell %1$s", "title_otp": "2-Factor Security", "title_register_fio_address": "Register FIO Address", "title_register_fio_domain": "Register FIO Domain", @@ -745,7 +745,8 @@ "buy_parent_crypto_modal_message_2s": "%1$s%2$s is required to send this transaction. Would you like to buy %2$s or exchange another crypto into %2$s?", "buy_parent_crypto_modal_message_3s": "%1$s%2$s (on %3$s) is required to send this transaction. Would you like to buy %2$s or exchange another crypto into %2$s?", "buy_crypto_decline": "Not at this time", - "buy_crypto_modal_buy_action": "Buy %s", + "buy_1s": "Buy %1$s", + "sell_1s": "Sell %1$s", "buy_crypto_modal_exchange": "Exchange", "exchange_crypto_modal_message": "Your %s wallet is empty. Would you like to exchange another crypto into %s?", "private_key_modal_sweep_from_private_address": "Sweep Funds From Private Key", @@ -1270,8 +1271,6 @@ "stake_resource_energy": "Energy", "fiat_plugin_select_asset_to_purchase": "Select Asset to Purchase", "fiat_plugin_select_asset_to_sell": "Select Asset to Sell", - "fiat_plugin_buy_currencycode": "Buy %s", - "fiat_plugin_sell_currencycode_s": "Sell %s", "fiat_plugin_sell_network_fee": "Sell Network Fee", "fiat_plugin_amount_currencycode": "Amount %s", "fiat_plugin_buy_amount_over_limit": "Max purchase amount is %s", @@ -1368,7 +1367,8 @@ "backup_web3_handle_warning_message": "Without a backup, you risk losing your web3 handle!", "tap_to_learn_more": "Tap to learn more.", "backup_for_transfer_message": "To buy, sell, and receive funds, please create a full account. %1$s full accounts require no personal information and use a familiar username and password method that will safeguard your assets and help prevent loss of funds.\n\nNever share your username and password, and store your credentials securely!", - "backup_notification_body": "Funds will be lost if your device is lost, stolen, or %1$s is uninstalled. Create a username and password to secure your funds.", + "backup_notification_title": "Create Username & Password", + "backup_notification_body": "Funds will be lost if your device is lost or %1$s is uninstalled.", "backup_title": "Create Full Account", "backup_message": "Create a username and password to continue.", "backup_message_subtext": "Creating a full account ensures you can safely recover your funds in the event that you lose access to your device.", diff --git a/src/locales/strings/es.json b/src/locales/strings/es.json index a13670ed3f4..a3b9d2ba873 100644 --- a/src/locales/strings/es.json +++ b/src/locales/strings/es.json @@ -586,10 +586,10 @@ "title_edit_token": "Editar token", "title_add_token": "Añadir Token", "title_password_recovery": "Recuperación de contraseña", - "title_plugin_buy_s": "Buy %1$s", + "transaction_details_bought_1s": "Buy %1$s", "title_select_region": "Select Region", "title_select_payment_method": "Select Payment Method", - "title_plugin_sell_s": "Sell %1$s", + "transaction_details_sold_1s": "Sell %1$s", "title_otp": "Seguridad de 2 factores", "title_register_fio_address": "Registrar dirección FIO", "title_register_fio_domain": "Registrar dominio FIO", @@ -745,7 +745,8 @@ "buy_parent_crypto_modal_message_2s": "%1$s%2$s is required to send this transaction. Would you like to buy %2$s or exchange another crypto into %2$s?", "buy_parent_crypto_modal_message_3s": "%1$s%2$s (on %3$s) is required to send this transaction. Would you like to buy %2$s or exchange another crypto into %2$s?", "buy_crypto_decline": "No en este momento", - "buy_crypto_modal_buy_action": "Comprar %s", + "buy_1s": "Comprar %1$s", + "sell_1s": "Sell %1$s", "buy_crypto_modal_exchange": "Cambiar", "exchange_crypto_modal_message": "Tu cartera %s está vacía. ¿Quieres cambiar otra cripto a %s?", "private_key_modal_sweep_from_private_address": "Barrer fondos de clave privada", diff --git a/src/locales/strings/esMX.json b/src/locales/strings/esMX.json index d98af4ce87c..0268f30f9e7 100644 --- a/src/locales/strings/esMX.json +++ b/src/locales/strings/esMX.json @@ -586,10 +586,10 @@ "title_edit_token": "Editar token", "title_add_token": "Añadir token", "title_password_recovery": "Recuperación de contraseña", - "title_plugin_buy_s": "Comprar %1$s", + "transaction_details_bought_1s": "Comprar %1$s", "title_select_region": "Seleccionar región", "title_select_payment_method": "Seleccione el método de pago", - "title_plugin_sell_s": "Enviado %1$s", + "transaction_details_sold_1s": "Enviado %1$s", "title_otp": "Seguridad de 2 factores", "title_register_fio_address": "Registrar dirección FIO", "title_register_fio_domain": "Registrar dominio FIO", @@ -745,7 +745,8 @@ "buy_parent_crypto_modal_message_2s": "Se requiere %1$s%2$s para enviar esta transacción. ¿Te gustaría comprar %2$s o cambiar otra criptomoneda por %2$s?", "buy_parent_crypto_modal_message_3s": "Se requiere %1$s%2$s (en %3$s) para enviar esta transacción. ¿Te gustaría comprar %2$s o cambiar otra criptomoneda por %2$s?", "buy_crypto_decline": "No en este momento", - "buy_crypto_modal_buy_action": "Comprar %s", + "buy_1s": "Comprar %1$s", + "sell_1s": "Sell %1$s", "buy_crypto_modal_exchange": "Cambiar", "exchange_crypto_modal_message": "Tu cartera %s está vacía. ¿Quieres cambiar otra cripto a %s?", "private_key_modal_sweep_from_private_address": "Barrer fondos de clave privada", diff --git a/src/locales/strings/fr.json b/src/locales/strings/fr.json index 7badf842654..228633391d7 100644 --- a/src/locales/strings/fr.json +++ b/src/locales/strings/fr.json @@ -586,10 +586,10 @@ "title_edit_token": "Modifier les Tokens", "title_add_token": "Ajouter les tokens", "title_password_recovery": "Récupération du mot de passe", - "title_plugin_buy_s": "Buy %1$s", + "transaction_details_bought_1s": "Buy %1$s", "title_select_region": "Select Region", "title_select_payment_method": "Select Payment Method", - "title_plugin_sell_s": "Sell %1$s", + "transaction_details_sold_1s": "Sell %1$s", "title_otp": "2-Factor Security", "title_register_fio_address": "Register FIO Address", "title_register_fio_domain": "Register FIO Domain", @@ -745,7 +745,7 @@ "buy_parent_crypto_modal_message_2s": "%1$s%2$s is required to send this transaction. Would you like to buy %2$s or exchange another crypto into %2$s?", "buy_parent_crypto_modal_message_3s": "%1$s%2$s (on %3$s) is required to send this transaction. Would you like to buy %2$s or exchange another crypto into %2$s?", "buy_crypto_decline": "Pas cette fois", - "buy_crypto_modal_buy_action": "Acheter %s", + "buy_1s": "Acheter %s", "buy_crypto_modal_exchange": "Echange", "exchange_crypto_modal_message": "Votre portefeuille %s est vide. Voulez-vous échanger un autre crypto dans %s?", "private_key_modal_sweep_from_private_address": "Effacer complétement vos fonds de la clé privée ", diff --git a/src/locales/strings/it.json b/src/locales/strings/it.json index 03f8ea1543d..1ea61ea49f3 100644 --- a/src/locales/strings/it.json +++ b/src/locales/strings/it.json @@ -586,10 +586,10 @@ "title_edit_token": "Modifica token", "title_add_token": "Aggiungi token", "title_password_recovery": "Recupero password", - "title_plugin_buy_s": "Compra %1$s", + "transaction_details_bought_1s": "Compra %1$s", "title_select_region": "Seleziona la regione", "title_select_payment_method": "Seleziona un metodo di pagamento", - "title_plugin_sell_s": "Vendi %1$s", + "transaction_details_sold_1s": "Vendi %1$s", "title_otp": "Sicurezza a 2 fattori (2FA)", "title_register_fio_address": "Registra un indirizzo FIO", "title_register_fio_domain": "Registra un dominio FIO", @@ -745,7 +745,8 @@ "buy_parent_crypto_modal_message_2s": "%1$s%2$s necessari per inviare questa transazione. Vuoi acquistare %2$s o scambiare un'altra criptovaluta in %2$s?", "buy_parent_crypto_modal_message_3s": "%1$s%2$s (su %3$s) necessari per inviare questa transazione. Vuoi comprare %2$s o scambiare un'altra cripto per %2$s?", "buy_crypto_decline": "Non ora", - "buy_crypto_modal_buy_action": "Compra %s", + "buy_1s": "Compra %1$s", + "sell_1s": "Vendi %1$s", "buy_crypto_modal_exchange": "Scambia", "exchange_crypto_modal_message": "Il tuo portafoglio %s è vuoto. Vuoi scambiare delle criptovalute per %s?", "private_key_modal_sweep_from_private_address": "Importa fondi da chiave privata", diff --git a/src/locales/strings/ja.json b/src/locales/strings/ja.json index bbc21df67ef..e4439c778f0 100644 --- a/src/locales/strings/ja.json +++ b/src/locales/strings/ja.json @@ -586,10 +586,10 @@ "title_edit_token": "トークンを編集", "title_add_token": "トークンを追加", "title_password_recovery": "パスワード復元", - "title_plugin_buy_s": "Buy %1$s", + "transaction_details_bought_1s": "Buy %1$s", "title_select_region": "Select Region", "title_select_payment_method": "Select Payment Method", - "title_plugin_sell_s": "Sell %1$s", + "transaction_details_sold_1s": "Sell %1$s", "title_otp": "二段階認証", "title_register_fio_address": "FIOアドレスを登録", "title_register_fio_domain": "FIOドメインを登録", @@ -745,7 +745,8 @@ "buy_parent_crypto_modal_message_2s": "このトランザクションを送信するには、%1$s%2$s が必要です。 %2$s を購入するか、別の暗号通貨を %2$s に交換しますか?", "buy_parent_crypto_modal_message_3s": "%1$s%2$s (on %3$s) is required to send this transaction. Would you like to buy %2$s or exchange another crypto into %2$s?", "buy_crypto_decline": "キャンセル", - "buy_crypto_modal_buy_action": "%sを購入", + "buy_1s": "%sを購入", + "sell_1s": "%sを売却", "buy_crypto_modal_exchange": "取引所", "exchange_crypto_modal_message": "あなたの%sウォレットは空です。 別の暗号資産を%sに交換しますか?", "private_key_modal_sweep_from_private_address": "プライベートキーから資産をスウィープする", diff --git a/src/locales/strings/kaa.json b/src/locales/strings/kaa.json index 736dc2f3b43..79b429330f0 100644 --- a/src/locales/strings/kaa.json +++ b/src/locales/strings/kaa.json @@ -586,10 +586,10 @@ "title_edit_token": "Edit Token", "title_add_token": "Add Token", "title_password_recovery": "Password Recovery", - "title_plugin_buy_s": "Buy %1$s", + "transaction_details_bought_1s": "Buy %1$s", "title_select_region": "Select Region", "title_select_payment_method": "Select Payment Method", - "title_plugin_sell_s": "Sell %1$s", + "transaction_details_sold_1s": "Sell %1$s", "title_otp": "2-Factor Security", "title_register_fio_address": "Register FIO Address", "title_register_fio_domain": "Register FIO Domain", @@ -745,7 +745,8 @@ "buy_parent_crypto_modal_message_2s": "%1$s%2$s is required to send this transaction. Would you like to buy %2$s or exchange another crypto into %2$s?", "buy_parent_crypto_modal_message_3s": "%1$s%2$s (on %3$s) is required to send this transaction. Would you like to buy %2$s or exchange another crypto into %2$s?", "buy_crypto_decline": "Not at this time", - "buy_crypto_modal_buy_action": "Satıp alıw %s", + "buy_1s": "Satıp alıw %s", + "sell_1s": "Sell %1$s", "buy_crypto_modal_exchange": "Exchange", "exchange_crypto_modal_message": "Your %s wallet is empty. Would you like to exchange another crypto into %s?", "private_key_modal_sweep_from_private_address": "Sweep Funds From Private Key", diff --git a/src/locales/strings/ko.json b/src/locales/strings/ko.json index 1551f8c2a70..c202d17af61 100644 --- a/src/locales/strings/ko.json +++ b/src/locales/strings/ko.json @@ -586,10 +586,10 @@ "title_edit_token": "토큰 편집", "title_add_token": "토큰 추가", "title_password_recovery": "암호 복구", - "title_plugin_buy_s": "Buy %1$s", + "transaction_details_bought_1s": "Buy %1$s", "title_select_region": "Select Region", "title_select_payment_method": "Select Payment Method", - "title_plugin_sell_s": "Sell %1$s", + "transaction_details_sold_1s": "Sell %1$s", "title_otp": "2-Factor Security", "title_register_fio_address": "Register FIO Address", "title_register_fio_domain": "Register FIO Domain", @@ -745,7 +745,7 @@ "buy_parent_crypto_modal_message_2s": "%1$s%2$s is required to send this transaction. Would you like to buy %2$s or exchange another crypto into %2$s?", "buy_parent_crypto_modal_message_3s": "%1$s%2$s (on %3$s) is required to send this transaction. Would you like to buy %2$s or exchange another crypto into %2$s?", "buy_crypto_decline": "현재 불가한 동작입니다.", - "buy_crypto_modal_buy_action": "%s 구매", + "buy_1s": "%s 구매", "buy_crypto_modal_exchange": "거래", "exchange_crypto_modal_message": "고객님의 %s 월릿은 현재 비어있는 상태입니다. 다른 가상화폐를 %s 로 교환하시겠습니까?", "private_key_modal_sweep_from_private_address": "개인 키에서 자산을 스윕 (Sweep)", diff --git a/src/locales/strings/pt.json b/src/locales/strings/pt.json index 06c4c99da21..57b282ae653 100644 --- a/src/locales/strings/pt.json +++ b/src/locales/strings/pt.json @@ -586,10 +586,10 @@ "title_edit_token": "Editar Token", "title_add_token": "Adicionar Token", "title_password_recovery": "Recuperação de Senha", - "title_plugin_buy_s": "Buy %1$s", + "transaction_details_bought_1s": "Buy %1$s", "title_select_region": "Select Region", "title_select_payment_method": "Select Payment Method", - "title_plugin_sell_s": "Sell %1$s", + "transaction_details_sold_1s": "Sell %1$s", "title_otp": "Segurança de 2 fatores", "title_register_fio_address": "Registar Endereço FIO", "title_register_fio_domain": "Registrar Domínio FIO", @@ -745,7 +745,8 @@ "buy_parent_crypto_modal_message_2s": "%1$s%2$s é necessário para enviar esta transação. Gostaria de comprar %2$s ou trocar outra criptomoeda por %2$s?", "buy_parent_crypto_modal_message_3s": "%1$s%2$s (on %3$s) is required to send this transaction. Would you like to buy %2$s or exchange another crypto into %2$s?", "buy_crypto_decline": "Agora não", - "buy_crypto_modal_buy_action": "Comprar %s", + "buy_1s": "Comprar %1$s", + "sell_1s": "Vender %1$s", "buy_crypto_modal_exchange": "Exchange", "exchange_crypto_modal_message": "A sua carteira %s está vazia. Gostaria de trocar outra criptomoeda por %s?", "private_key_modal_sweep_from_private_address": "Resgatar Fundos da Chave Privada", diff --git a/src/locales/strings/ru.json b/src/locales/strings/ru.json index bf395bbfca7..db746901642 100644 --- a/src/locales/strings/ru.json +++ b/src/locales/strings/ru.json @@ -586,10 +586,10 @@ "title_edit_token": "Редактировать токен", "title_add_token": "Добавить токен", "title_password_recovery": "Восстановление пароля", - "title_plugin_buy_s": "Buy %1$s", + "transaction_details_bought_1s": "Buy %1$s", "title_select_region": "Select Region", "title_select_payment_method": "Select Payment Method", - "title_plugin_sell_s": "Sell %1$s", + "transaction_details_sold_1s": "Sell %1$s", "title_otp": "2-факторная безопасность", "title_register_fio_address": "Регистрация адреса FIO", "title_register_fio_domain": "Регистрация домена FIO", @@ -745,7 +745,7 @@ "buy_parent_crypto_modal_message_2s": "%1$s%2$s is required to send this transaction. Would you like to buy %2$s or exchange another crypto into %2$s?", "buy_parent_crypto_modal_message_3s": "%1$s%2$s (on %3$s) is required to send this transaction. Would you like to buy %2$s or exchange another crypto into %2$s?", "buy_crypto_decline": "Не в этот раз", - "buy_crypto_modal_buy_action": "Купить %s", + "buy_1s": "Купить %s", "buy_crypto_modal_exchange": "Обмен", "exchange_crypto_modal_message": "Ваш кошелек %s пуст. Вы хотели бы обменять другую валюту на %s?", "private_key_modal_sweep_from_private_address": "Перевести средства с приватного ключа", diff --git a/src/locales/strings/vi.json b/src/locales/strings/vi.json index 40486dd1c7c..7bf05a66413 100644 --- a/src/locales/strings/vi.json +++ b/src/locales/strings/vi.json @@ -586,10 +586,10 @@ "title_edit_token": "Chỉnh sửa mã thông báo", "title_add_token": "Thêm mã thông báo", "title_password_recovery": "Khôi phục mật khẩu", - "title_plugin_buy_s": "Buy %1$s", + "transaction_details_bought_1s": "Buy %1$s", "title_select_region": "Select Region", "title_select_payment_method": "Select Payment Method", - "title_plugin_sell_s": "Sell %1$s", + "transaction_details_sold_1s": "Sell %1$s", "title_otp": "2-Factor Security", "title_register_fio_address": "Register FIO Address", "title_register_fio_domain": "Register FIO Domain", @@ -745,7 +745,7 @@ "buy_parent_crypto_modal_message_2s": "%1$s%2$s is required to send this transaction. Would you like to buy %2$s or exchange another crypto into %2$s?", "buy_parent_crypto_modal_message_3s": "%1$s%2$s (on %3$s) is required to send this transaction. Would you like to buy %2$s or exchange another crypto into %2$s?", "buy_crypto_decline": "Không phải lúc này", - "buy_crypto_modal_buy_action": "Mua %s", + "buy_1s": "Mua %s", "buy_crypto_modal_exchange": "Chuyển đổi", "exchange_crypto_modal_message": "Ví %s của bạn trống. Bạn có muốn mua %s hay chuyển đổi crypto khác vào ví?", "private_key_modal_sweep_from_private_address": "Quét số tiền từ khóa riêng", diff --git a/src/locales/strings/zh.json b/src/locales/strings/zh.json index ecaaf226723..3bf1cd4de62 100644 --- a/src/locales/strings/zh.json +++ b/src/locales/strings/zh.json @@ -586,10 +586,10 @@ "title_edit_token": "编辑代币", "title_add_token": "添加代币", "title_password_recovery": "密码复原", - "title_plugin_buy_s": "Buy %1$s", + "transaction_details_bought_1s": "Buy %1$s", "title_select_region": "Select Region", "title_select_payment_method": "Select Payment Method", - "title_plugin_sell_s": "Sell %1$s", + "transaction_details_sold_1s": "Sell %1$s", "title_otp": "2-Factor Security", "title_register_fio_address": "Register FIO Address", "title_register_fio_domain": "Register FIO Domain", @@ -745,7 +745,7 @@ "buy_parent_crypto_modal_message_2s": "%1$s%2$s is required to send this transaction. Would you like to buy %2$s or exchange another crypto into %2$s?", "buy_parent_crypto_modal_message_3s": "%1$s%2$s (on %3$s) is required to send this transaction. Would you like to buy %2$s or exchange another crypto into %2$s?", "buy_crypto_decline": "不是在这个时候", - "buy_crypto_modal_buy_action": "购买 %s", + "buy_1s": "购买 %s", "buy_crypto_modal_exchange": "交易", "exchange_crypto_modal_message": "您的 %s 钱包现在是空的。您想将另一个加密产品兑转换成 %s?", "private_key_modal_sweep_from_private_address": "从私钥移除资金", diff --git a/src/plugins/gui/amountQuotePlugin.ts b/src/plugins/gui/amountQuotePlugin.ts index 116f789d357..e25f2b0766a 100644 --- a/src/plugins/gui/amountQuotePlugin.ts +++ b/src/plugins/gui/amountQuotePlugin.ts @@ -260,7 +260,7 @@ export const amountQuoteFiatPlugin: FiatPluginFactory = async (params: FiatPlugi // Navigate to scene to have user enter amount const enterAmount: InternalFiatPluginEnterAmountParams = { disableInput, - headerTitle: isBuy ? sprintf(lstrings.fiat_plugin_buy_currencycode, currencyCode) : sprintf(lstrings.fiat_plugin_sell_currencycode_s, currencyCode), + headerTitle: isBuy ? sprintf(lstrings.buy_1s, currencyCode) : sprintf(lstrings.sell_1s, currencyCode), initState: { value1: initialValue1, statusText: initialValue1 == null ? { content: lstrings.enter_amount_label } : { content: '' } From 2721aa10392ad6454525680d98432bb940058e71 Mon Sep 17 00:00:00 2001 From: Jon Tzeng Date: Tue, 22 Oct 2024 16:50:14 -0700 Subject: [PATCH 15/89] Implement `ukComplianceUtils.ts` --- src/locales/en_US.ts | 7 +++++++ src/locales/strings/enUS.json | 2 ++ src/util/ukComplianceUtils.ts | 19 +++++++++++++++++++ 3 files changed, 28 insertions(+) create mode 100644 src/util/ukComplianceUtils.ts diff --git a/src/locales/en_US.ts b/src/locales/en_US.ts index 97059f001a7..a986878a924 100644 --- a/src/locales/en_US.ts +++ b/src/locales/en_US.ts @@ -1636,6 +1636,13 @@ const strings = { // #endregion Home + // #region UK Compliance + + uk_ways_to_buy_1s: 'Ways to Buy %1$s', + uk_ways_to_sell_1s: 'Ways to Sell %1$s', + + // #endregion UK Compliance + redacted_placeholder: '●●●●', insufficient_funds_2s: 'Insufficient %1$s (%2$s).', split_from_1s: 'Split from %1$s', diff --git a/src/locales/strings/enUS.json b/src/locales/strings/enUS.json index 19d7ca6407d..44961d37c1a 100644 --- a/src/locales/strings/enUS.json +++ b/src/locales/strings/enUS.json @@ -1415,6 +1415,8 @@ "title_support": "Need Support?", "body_support": "Contact our team of experts to troubleshoot your crypto needs", "button_support": "Contact Support", + "uk_ways_to_buy_1s": "Ways to Buy %1$s", + "uk_ways_to_sell_1s": "Ways to Sell %1$s", "redacted_placeholder": "●●●●", "insufficient_funds_2s": "Insufficient %1$s (%2$s).", "split_from_1s": "Split from %1$s", diff --git a/src/util/ukComplianceUtils.ts b/src/util/ukComplianceUtils.ts new file mode 100644 index 00000000000..b4db84ff434 --- /dev/null +++ b/src/util/ukComplianceUtils.ts @@ -0,0 +1,19 @@ +import { LocaleStringKey } from '../locales/en_US' +import { lstrings } from '../locales/strings' + +const UK_COMPLIANT_STRING_MAP: { [key: string]: LocaleStringKey } = { + buy_1s: 'uk_ways_to_buy_1s', + sell_1s: 'uk_ways_to_sell_1s', + stake_earn_1s: 'stake_stake_1s', + stake_earn_button_label: 'fragment_stake_label', + stake_x_to_earn_y: 'transaction_details_stake_subcat_1s' +} + +const formatString = (template: string, values: string[]): string => { + return template.replace(/%(\d+)\$s/g, (_, index) => values[parseInt(index) - 1] || '') +} + +export const getUkCompliantString = (countryCode: string | undefined, key: LocaleStringKey, ...values: string[]): string => { + const template = countryCode === 'GB' ? lstrings[UK_COMPLIANT_STRING_MAP[key]] : lstrings[key] + return formatString(template, values) +} From b49d5f81e29cdf84a780853c54e95ea604b641d5 Mon Sep 17 00:00:00 2001 From: Jon Tzeng Date: Wed, 23 Oct 2024 13:42:28 -0700 Subject: [PATCH 16/89] Add countryCode to `firstOpenInfo` --- src/actions/FirstOpenActions.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/actions/FirstOpenActions.tsx b/src/actions/FirstOpenActions.tsx index 64340ed86a1..b0ba34f93b1 100644 --- a/src/actions/FirstOpenActions.tsx +++ b/src/actions/FirstOpenActions.tsx @@ -1,15 +1,17 @@ -import { asNumber, asObject, asString, asValue } from 'cleaners' +import { asNumber, asObject, asOptional, asString, asValue } from 'cleaners' import { makeReactNativeDisklet } from 'disklet' import { FIRST_OPEN } from '../constants/constantSettings' import { makeUuid } from '../util/rnUtils' +import { getCountryCodeByIp } from './AccountReferralActions' const firstOpenDisklet = makeReactNativeDisklet() const asFirstOpenInfo = asObject({ isFirstOpen: asValue('true', 'false'), deviceId: asString, - firstOpenEpoch: asNumber + firstOpenEpoch: asNumber, + countryCode: asOptional(asString) }) type FirstOpenInfo = ReturnType @@ -27,9 +29,15 @@ export const getFirstOpenInfo = async (): Promise => { firstOpenText = await firstOpenDisklet.getText(FIRST_OPEN) firstOpenInfo = asFirstOpenInfo(JSON.parse(firstOpenText)) firstOpenInfo.isFirstOpen = 'false' + + if (firstOpenInfo.countryCode == null) { + // Not critical if we can't get the country code + firstOpenInfo.countryCode = await getCountryCodeByIp().catch(() => undefined) + } } catch (error: any) { // Generate new values. firstOpenInfo = { + countryCode: await getCountryCodeByIp(), deviceId: await makeUuid(), firstOpenEpoch: Date.now(), // If firstOpen != null: This is not the first time they opened the app, From 8f3e0c676d7cf0245960dbb6e6e60b8a2a977fd8 Mon Sep 17 00:00:00 2001 From: Jon Tzeng Date: Tue, 22 Oct 2024 17:33:39 -0700 Subject: [PATCH 17/89] Update strings to be UK Compliant --- src/actions/ScanActions.tsx | 5 ++++- src/components/Main.tsx | 14 +++++++++++++- src/components/cards/StakingReturnsCard.tsx | 8 ++++++-- src/components/modals/InsufficientFeesModal.tsx | 6 ++++-- src/components/scenes/GuiPluginListScene.tsx | 10 +++++++--- .../scenes/MigrateWalletCalculateFeeScene.tsx | 4 +++- src/components/scenes/SendScene2.tsx | 6 +++++- .../scenes/Staking/StakeOptionsScene.tsx | 14 +++++++++++++- .../scenes/Staking/StakeOverviewScene.tsx | 8 ++++++-- src/components/scenes/SwapProcessingScene.tsx | 10 +++++++++- .../scenes/SweepPrivateKeyCalculateFeeScene.tsx | 4 +++- src/components/themed/TransactionListTop.tsx | 15 +++++++++++---- src/plugins/gui/amountQuotePlugin.ts | 6 +++++- src/util/stakeUtils.ts | 5 +++-- 14 files changed, 92 insertions(+), 23 deletions(-) diff --git a/src/actions/ScanActions.tsx b/src/actions/ScanActions.tsx index 2dcdc895819..c435be6c7ca 100644 --- a/src/actions/ScanActions.tsx +++ b/src/actions/ScanActions.tsx @@ -19,9 +19,11 @@ import { getCurrencyCode } from '../util/CurrencyInfoHelpers' import { parseDeepLink } from '../util/DeepLinkParser' import { logActivity } from '../util/logger' import { makeCurrencyCodeTable, upgradeCurrencyCodes } from '../util/tokenIdTools' +import { getUkCompliantString } from '../util/ukComplianceUtils' import { getPluginIdFromChainCode, toListString, zeroString } from '../util/utils' import { cleanQueryFlags, openBrowserUri } from '../util/WebUtils' import { checkAndShowLightBackupModal } from './BackupModalActions' +import { getFirstOpenInfo } from './FirstOpenActions' /** * Handle Request for Address Links (WIP - pending refinement). @@ -340,6 +342,7 @@ const shownWalletGetCryptoModals: string[] = [] export function checkAndShowGetCryptoModal(navigation: NavigationBase, wallet: EdgeCurrencyWallet, tokenId: EdgeTokenId): ThunkAction> { return async dispatch => { try { + const { countryCode } = await getFirstOpenInfo() const currencyCode = getCurrencyCode(wallet, tokenId) // check if balance is zero const balance = wallet.balanceMap.get(tokenId) @@ -355,7 +358,7 @@ export function checkAndShowGetCryptoModal(navigation: NavigationBase, wallet: E title={lstrings.buy_crypto_modal_title} message={messageSyntax} buttons={{ - buy: { label: sprintf(lstrings.buy_1s, currencyCode) }, + buy: { label: getUkCompliantString(countryCode, 'buy_1s', currencyCode) }, exchange: { label: lstrings.buy_crypto_modal_exchange, type: 'primary' }, decline: { label: lstrings.buy_crypto_decline } }} diff --git a/src/components/Main.tsx b/src/components/Main.tsx index f9f3853edee..f66d139da24 100644 --- a/src/components/Main.tsx +++ b/src/components/Main.tsx @@ -6,6 +6,7 @@ import * as React from 'react' import { Platform } from 'react-native' import { getDeviceSettings } from '../actions/DeviceSettingsActions' +import { getFirstOpenInfo } from '../actions/FirstOpenActions' import { SwapCreateScene as SwapCreateSceneComponent } from '../components/scenes/SwapCreateScene' import { ENV } from '../env' import { DEFAULT_EXPERIMENT_CONFIG, ExperimentConfig, getExperimentConfig } from '../experimentConfig' @@ -33,6 +34,7 @@ import { } from '../types/routerTypes' import { isMaestro } from '../util/maestro' import { logEvent } from '../util/tracking' +import { getUkCompliantString } from '../util/ukComplianceUtils' import { ifLoggedIn } from './hoc/IfLoggedIn' import { BackButton } from './navigation/BackButton' import { CurrencySettingsTitle } from './navigation/CurrencySettingsTitle' @@ -423,6 +425,16 @@ const EdgeTabs = () => { // ------------------------------------------------------------------------- const EdgeAppStack = () => { + const [countryCode, setCountryCode] = React.useState() + + useAsyncEffect( + async () => { + setCountryCode((await getFirstOpenInfo()).countryCode) + }, + [], + 'EdgeAppStack' + ) + return ( { name="earnScene" component={EarnScene} options={{ - title: lstrings.stake_earn_button_label + title: getUkCompliantString(countryCode, 'stake_earn_button_label') }} /> diff --git a/src/components/cards/StakingReturnsCard.tsx b/src/components/cards/StakingReturnsCard.tsx index 07596ba4467..556acea8d61 100644 --- a/src/components/cards/StakingReturnsCard.tsx +++ b/src/components/cards/StakingReturnsCard.tsx @@ -7,6 +7,7 @@ import { toPercentString } from '../../locales/intl' import { lstrings } from '../../locales/strings' import { StakePolicy } from '../../plugins/stake-plugins/types' import { getPolicyIconUris } from '../../util/stakeUtils' +import { getUkCompliantString } from '../../util/ukComplianceUtils' import { PairIcons } from '../icons/PairIcons' import { cacheStyles, Theme, useTheme } from '../services/ThemeContext' import { TitleText } from '../text/TitleText' @@ -17,6 +18,7 @@ interface Props { stakePolicy: StakePolicy wallet: EdgeCurrencyWallet + countryCode?: string /** If false, show "Stake"/"Earn" * If true, show "Staked"/"Earned" */ isOpenPosition?: boolean @@ -27,7 +29,7 @@ export function StakingReturnsCard(props: Props) { const theme = useTheme() const styles = getStyles(theme) - const { stakePolicy, wallet, isOpenPosition, onPress } = props + const { stakePolicy, wallet, isOpenPosition, countryCode, onPress } = props const { apy, yieldType, stakeProviderInfo } = stakePolicy const { stakeAssets, rewardAssets } = stakePolicy @@ -35,7 +37,9 @@ export function StakingReturnsCard(props: Props) { const rewardCurrencyCodes = rewardAssets.map(asset => asset.currencyCode).join(', ') const stakeText = sprintf(isOpenPosition ? lstrings.stake_staked_1s : lstrings.stake_stake_1s, stakeCurrencyCodes) - const rewardText = sprintf(isOpenPosition ? lstrings.stake_earning_1s : lstrings.stake_earn_1s, rewardCurrencyCodes) + const rewardText = isOpenPosition + ? sprintf(lstrings.stake_earning_1s, rewardCurrencyCodes) + : getUkCompliantString(countryCode, 'stake_earn_1s', rewardCurrencyCodes) const policyIcons = getPolicyIconUris(wallet.currencyInfo, stakePolicy) diff --git a/src/components/modals/InsufficientFeesModal.tsx b/src/components/modals/InsufficientFeesModal.tsx index 04f53418e84..ac171a1d695 100644 --- a/src/components/modals/InsufficientFeesModal.tsx +++ b/src/components/modals/InsufficientFeesModal.tsx @@ -8,6 +8,7 @@ import { useHandler } from '../../hooks/useHandler' import { lstrings } from '../../locales/strings' import { NavigationBase } from '../../types/routerTypes' import { getCurrencyCode } from '../../util/CurrencyInfoHelpers' +import { getUkCompliantString } from '../../util/ukComplianceUtils' import { roundedFee } from '../../util/utils' import { ButtonsView } from '../buttons/ButtonsView' import { Paragraph } from '../themed/EdgeText' @@ -19,6 +20,7 @@ interface Props { navigation: NavigationBase wallet: EdgeCurrencyWallet + countryCode?: string // Called when the user wants to swap. // The default behavior is to navigate to the swap scene, // but the swap scene itself needs a different behavior here. @@ -29,7 +31,7 @@ interface Props { * Show this modal when the wallet doesn't have enough funds to cover fees. */ export function InsufficientFeesModal(props: Props) { - const { bridge, coreError, navigation, wallet, onSwap } = props + const { bridge, countryCode, coreError, navigation, wallet, onSwap } = props // Get the display amount: const { tokenId, networkFee = '' } = coreError @@ -59,7 +61,7 @@ export function InsufficientFeesModal(props: Props) { {message} diff --git a/src/components/scenes/GuiPluginListScene.tsx b/src/components/scenes/GuiPluginListScene.tsx index 8cd6791731b..0bd9463fbb5 100644 --- a/src/components/scenes/GuiPluginListScene.tsx +++ b/src/components/scenes/GuiPluginListScene.tsx @@ -6,7 +6,6 @@ import { Image, ListRenderItemInfo, Platform, View } from 'react-native' import { getBuildNumber, getVersion } from 'react-native-device-info' import FastImage from 'react-native-fast-image' import Animated from 'react-native-reanimated' -import { sprintf } from 'sprintf-js' import { checkAndShowLightBackupModal } from '../../actions/BackupModalActions' import { checkAndSetRegion, showCountrySelectionModal } from '../../actions/CountryListActions' @@ -37,6 +36,7 @@ import { filterGuiPluginJson } from '../../util/GuiPluginTools' import { infoServerData } from '../../util/network' import { bestOfPlugins } from '../../util/ReferralHelpers' import { logEvent, OnLogEvent } from '../../util/tracking' +import { getUkCompliantString } from '../../util/ukComplianceUtils' import { base58ToUuid, getOsVersion } from '../../util/utils' import { EdgeCard } from '../cards/EdgeCard' import { filterInfoCards } from '../cards/InfoCardCarousel' @@ -404,7 +404,7 @@ class GuiPluginList extends React.PureComponent { const titleAsset = forcedWalletResult == null || forcedWalletResult.type !== 'wallet' || forcedWallet == null ? lstrings.cryptocurrency - : getCurrencyCodeWithAccount(account, forcedWallet.currencyInfo.pluginId, forcedWalletResult.tokenId ?? null) + : getCurrencyCodeWithAccount(account, forcedWallet.currencyInfo.pluginId, forcedWalletResult.tokenId) ?? '' const countryCard = stateProvinceData == null ? ( @@ -418,7 +418,11 @@ class GuiPluginList extends React.PureComponent { return ( <> - + {hasCountryData ? ( diff --git a/src/components/scenes/MigrateWalletCalculateFeeScene.tsx b/src/components/scenes/MigrateWalletCalculateFeeScene.tsx index dbe6e809bc9..24fa3fba9a0 100644 --- a/src/components/scenes/MigrateWalletCalculateFeeScene.tsx +++ b/src/components/scenes/MigrateWalletCalculateFeeScene.tsx @@ -4,6 +4,7 @@ import * as React from 'react' import { ActivityIndicator, ListRenderItemInfo, View } from 'react-native' import { FlatList } from 'react-native-gesture-handler' +import { getFirstOpenInfo } from '../../actions/FirstOpenActions' import { SCROLL_INDICATOR_INSET_FIX } from '../../constants/constantSettings' import { SPECIAL_CURRENCY_INFO } from '../../constants/WalletAndCurrencyConstants' import { useAsyncEffect } from '../../hooks/useAsyncEffect' @@ -124,7 +125,8 @@ const MigrateWalletCalculateFeeComponent = (props: Props) => { }) const handleInsufficientFunds = useHandler(async (wallet, error) => { - await Airship.show(bridge => ) + const { countryCode } = await getFirstOpenInfo() + await Airship.show(bridge => ) }) const handleSlidingComplete = useHandler(() => { diff --git a/src/components/scenes/SendScene2.tsx b/src/components/scenes/SendScene2.tsx index a5cb44a973d..229b37ecdb4 100644 --- a/src/components/scenes/SendScene2.tsx +++ b/src/components/scenes/SendScene2.tsx @@ -16,6 +16,7 @@ import { ActivityIndicator, TextInput, View } from 'react-native' import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view' import { sprintf } from 'sprintf-js' +import { getFirstOpenInfo } from '../../actions/FirstOpenActions' import { showSendScamWarningModal } from '../../actions/ScamWarningActions' import { checkAndShowGetCryptoModal } from '../../actions/ScanActions' import { playSendSound } from '../../actions/SoundActions' @@ -215,7 +216,10 @@ const SendComponent = (props: Props) => { const pendingInsufficientFees = React.useRef(undefined) async function showInsufficientFeesModal(error: InsufficientFundsError): Promise { - await Airship.show(bridge => ) + const { countryCode } = await getFirstOpenInfo() + await Airship.show(bridge => ( + + )) } const handleChangeAddress = diff --git a/src/components/scenes/Staking/StakeOptionsScene.tsx b/src/components/scenes/Staking/StakeOptionsScene.tsx index af553a85202..02bddfbf2f2 100644 --- a/src/components/scenes/Staking/StakeOptionsScene.tsx +++ b/src/components/scenes/Staking/StakeOptionsScene.tsx @@ -4,7 +4,9 @@ import { View } from 'react-native' import { FlatList } from 'react-native-gesture-handler' import { sprintf } from 'sprintf-js' +import { getFirstOpenInfo } from '../../../actions/FirstOpenActions' import { SCROLL_INDICATOR_INSET_FIX } from '../../../constants/constantSettings' +import { useAsyncEffect } from '../../../hooks/useAsyncEffect' import { useIconColor } from '../../../hooks/useIconColor' import { lstrings } from '../../../locales/strings' import { StakePlugin, StakePolicy, StakePositionMap } from '../../../plugins/stake-plugins/types' @@ -46,6 +48,16 @@ const StakeOptionsSceneComponent = (props: Props) => { const tokenId = pluginId ? getTokenIdForced(account, pluginId, currencyCode) : null const iconColor = useIconColor({ pluginId, tokenId }) + const [countryCode, setCountryCode] = React.useState() + + useAsyncEffect( + async () => { + setCountryCode((await getFirstOpenInfo()).countryCode) + }, + [], + 'StakeOptionsSceneComponent' + ) + // // Handlers // @@ -64,7 +76,7 @@ const StakeOptionsSceneComponent = (props: Props) => { const renderOptions = ({ item }: { item: StakePolicy }) => { const primaryText = getPolicyAssetName(item, 'stakeAssets') - const secondaryText = getPolicyTitleName(item) + const secondaryText = getPolicyTitleName(item, countryCode) const key = [primaryText, secondaryText].join() const policyIcons = getPolicyIconUris(wallet.currencyInfo, item) return ( diff --git a/src/components/scenes/Staking/StakeOverviewScene.tsx b/src/components/scenes/Staking/StakeOverviewScene.tsx index a94b8f1d6d9..2d12892694e 100644 --- a/src/components/scenes/Staking/StakeOverviewScene.tsx +++ b/src/components/scenes/Staking/StakeOverviewScene.tsx @@ -4,6 +4,7 @@ import { View } from 'react-native' import { FlatList } from 'react-native-gesture-handler' import { sprintf } from 'sprintf-js' +import { getFirstOpenInfo } from '../../../actions/FirstOpenActions' import { SCROLL_INDICATOR_INSET_FIX } from '../../../constants/constantSettings' import { useAsyncEffect } from '../../../hooks/useAsyncEffect' import { lstrings } from '../../../locales/strings' @@ -60,6 +61,7 @@ const StakeOverviewSceneComponent = (props: Props) => { const [rewardAllocations, setRewardAllocations] = React.useState([]) const [unstakedAllocations, setUnstakedAllocations] = React.useState([]) const [stakePosition, setStakePosition] = React.useState(startingStakePosition) + const [countryCode, setCountryCode] = React.useState() // Background loop to force fetchStakePosition updates const [updateCounter, setUpdateCounter] = React.useState(0) @@ -73,6 +75,8 @@ const StakeOverviewSceneComponent = (props: Props) => { useAsyncEffect( async () => { + setCountryCode((await getFirstOpenInfo()).countryCode) + let sp: StakePosition try { if (stakePosition == null) { @@ -97,7 +101,7 @@ const StakeOverviewSceneComponent = (props: Props) => { // Handlers const handleModifyPress = (modification: ChangeQuoteRequest['action'] | 'unstakeAndClaim') => () => { const sceneTitleMap = { - stake: getPolicyTitleName(stakePolicy), + stake: getPolicyTitleName(stakePolicy, countryCode), claim: lstrings.stake_claim_rewards, unstake: lstrings.stake_unstake, unstakeAndClaim: lstrings.stake_unstake_claim, @@ -146,7 +150,7 @@ const StakeOverviewSceneComponent = (props: Props) => { return ( - + {stakePosition == null ? ( <> diff --git a/src/components/scenes/SwapProcessingScene.tsx b/src/components/scenes/SwapProcessingScene.tsx index 7c257a6f645..bab1dcf6271 100644 --- a/src/components/scenes/SwapProcessingScene.tsx +++ b/src/components/scenes/SwapProcessingScene.tsx @@ -12,6 +12,7 @@ import { import * as React from 'react' import { sprintf } from 'sprintf-js' +import { getFirstOpenInfo } from '../../actions/FirstOpenActions' import { useDisplayDenom } from '../../hooks/useDisplayDenom' import { lstrings } from '../../locales/strings' import { useSelector } from '../../types/reactRedux' @@ -67,8 +68,15 @@ export function SwapProcessingScene(props: Props) { const insufficientFunds = asMaybeInsufficientFundsError(error) if (insufficientFunds != null && swapRequest.fromTokenId !== insufficientFunds.tokenId) { + const { countryCode } = await getFirstOpenInfo() await Airship.show(bridge => ( - + )) } } diff --git a/src/components/scenes/SweepPrivateKeyCalculateFeeScene.tsx b/src/components/scenes/SweepPrivateKeyCalculateFeeScene.tsx index 9cc52ba773a..118b576ca3a 100644 --- a/src/components/scenes/SweepPrivateKeyCalculateFeeScene.tsx +++ b/src/components/scenes/SweepPrivateKeyCalculateFeeScene.tsx @@ -12,6 +12,7 @@ import * as React from 'react' import { ActivityIndicator, ListRenderItemInfo, View } from 'react-native' import { FlatList } from 'react-native-gesture-handler' +import { getFirstOpenInfo } from '../../actions/FirstOpenActions' import { SCROLL_INDICATOR_INSET_FIX } from '../../constants/constantSettings' import { useAsyncEffect } from '../../hooks/useAsyncEffect' import { useHandler } from '../../hooks/useHandler' @@ -110,7 +111,8 @@ const SweepPrivateKeyCalculateFeeComponent = (props: Props) => { }) const handleInsufficientFunds = useHandler(async (wallet, error) => { - await Airship.show(bridge => ) + const { countryCode } = await getFirstOpenInfo() + await Airship.show(bridge => ) }) const handleSlidingComplete = useHandler(() => { diff --git a/src/components/themed/TransactionListTop.tsx b/src/components/themed/TransactionListTop.tsx index ed875f8cb11..62e0a77de2f 100644 --- a/src/components/themed/TransactionListTop.tsx +++ b/src/components/themed/TransactionListTop.tsx @@ -11,6 +11,7 @@ import Ionicons from 'react-native-vector-icons/Ionicons' import { sprintf } from 'sprintf-js' import { checkAndShowLightBackupModal } from '../../actions/BackupModalActions' +import { getFirstOpenInfo } from '../../actions/FirstOpenActions' import { toggleAccountBalanceVisibility } from '../../actions/LocalSettingsActions' import { getFiatSymbol, SPECIAL_CURRENCY_INFO } from '../../constants/WalletAndCurrencyConstants' import { ENV } from '../../env' @@ -31,6 +32,7 @@ import { GuiExchangeRates } from '../../types/types' import { CryptoAmount } from '../../util/CryptoAmount' import { triggerHaptic } from '../../util/haptic' import { getFioStakingBalances, getPluginFromPolicy, getPositionAllocations } from '../../util/stakeUtils' +import { getUkCompliantString } from '../../util/ukComplianceUtils' import { convertNativeToDenomination, datelog, DECIMAL_PRECISION, removeIsoPrefix, zeroString } from '../../util/utils' import { IconButton } from '../buttons/IconButton' import { EdgeCard } from '../cards/EdgeCard' @@ -40,7 +42,7 @@ import { CryptoIcon } from '../icons/CryptoIcon' import { EdgeModal } from '../modals/EdgeModal' import { WalletListMenuModal } from '../modals/WalletListMenuModal' import { WalletListModal, WalletListResult } from '../modals/WalletListModal' -import { Airship, showError } from '../services/AirshipInstance' +import { Airship, showDevError, showError } from '../services/AirshipInstance' import { cacheStyles, Theme, ThemeProps, useTheme } from '../services/ThemeContext' import { DividerLine } from './DividerLine' import { EdgeText } from './EdgeText' @@ -95,6 +97,7 @@ interface DispatchProps { } interface State { + countryCode: string | undefined input: string stakePolicies: StakePolicy[] | null stakePlugins: StakePlugin[] | null @@ -108,6 +111,7 @@ export class TransactionListTopComponent extends React.PureComponent showError(err)) + getFirstOpenInfo() + .then(firstOpenInfo => this.setState({ countryCode: firstOpenInfo.countryCode })) + .catch(err => showDevError(err)) } updatePluginsAndPolicies = async () => { @@ -244,7 +251,7 @@ export class TransactionListTopComponent extends React.PureComponent this.handleTradeBuy(bridge)} icon={ @@ -525,7 +532,7 @@ export class TransactionListTopComponent extends React.PureComponent ) : ( isStakingAvailable && ( - + ) diff --git a/src/plugins/gui/amountQuotePlugin.ts b/src/plugins/gui/amountQuotePlugin.ts index e25f2b0766a..160ea5cbb4f 100644 --- a/src/plugins/gui/amountQuotePlugin.ts +++ b/src/plugins/gui/amountQuotePlugin.ts @@ -2,6 +2,7 @@ import { div, eq, gt, round, toFixed } from 'biggystring' import { asNumber, asObject } from 'cleaners' import { sprintf } from 'sprintf-js' +import { getFirstOpenInfo } from '../../actions/FirstOpenActions' import { formatNumber, isValidInput } from '../../locales/intl' import { lstrings } from '../../locales/strings' import { EdgeAsset } from '../../types/types' @@ -10,6 +11,7 @@ import { getCurrencyCode } from '../../util/CurrencyInfoHelpers' import { getHistoricalRate } from '../../util/exchangeRates' import { infoServerData } from '../../util/network' import { logEvent } from '../../util/tracking' +import { getUkCompliantString } from '../../util/ukComplianceUtils' import { DECIMAL_PRECISION, fuzzyTimeout, removeIsoPrefix } from '../../util/utils' import { FiatPlugin, FiatPluginFactory, FiatPluginFactoryArgs, FiatPluginStartParams } from './fiatPluginTypes' import { FiatProvider, FiatProviderAssetMap, FiatProviderGetQuoteParams, FiatProviderQuote } from './fiatProviderTypes' @@ -127,6 +129,8 @@ export const amountQuoteFiatPlugin: FiatPluginFactory = async (params: FiatPlugi pluginId, startPlugin: async (params: FiatPluginStartParams) => { const { defaultIsoFiat, direction, defaultFiatAmount, forceFiatCurrencyCode, regionCode, paymentTypes, pluginPromotion, providerId } = params + const { countryCode } = await getFirstOpenInfo() + // TODO: Address 'paymentTypes' vs 'paymentType'. Both are defined in the // buy/sellPluginList.jsons. if (paymentTypes.length === 0) console.warn('No payment types given to FiatPlugin: ' + pluginId) @@ -260,7 +264,7 @@ export const amountQuoteFiatPlugin: FiatPluginFactory = async (params: FiatPlugi // Navigate to scene to have user enter amount const enterAmount: InternalFiatPluginEnterAmountParams = { disableInput, - headerTitle: isBuy ? sprintf(lstrings.buy_1s, currencyCode) : sprintf(lstrings.sell_1s, currencyCode), + headerTitle: isBuy ? getUkCompliantString(countryCode, 'buy_1s', currencyCode) : getUkCompliantString(countryCode, 'sell_1s', currencyCode), initState: { value1: initialValue1, statusText: initialValue1 == null ? { content: lstrings.enter_amount_label } : { content: '' } diff --git a/src/util/stakeUtils.ts b/src/util/stakeUtils.ts index 5638e404310..a723c8033d9 100644 --- a/src/util/stakeUtils.ts +++ b/src/util/stakeUtils.ts @@ -6,6 +6,7 @@ import { formatTimeDate } from '../locales/intl' import { lstrings } from '../locales/strings' import { PositionAllocation, StakePlugin, StakePolicy, StakePosition } from '../plugins/stake-plugins/types' import { getCurrencyIconUris } from './CdnUris' +import { getUkCompliantString } from './ukComplianceUtils' /** * Returns an array of all currency codes for a particular asset type @@ -41,7 +42,7 @@ export const getPolicyAssetName = (stakePolicy: StakePolicy, assetType: 'stakeAs /** * Returns the policy title */ -export const getPolicyTitleName = (stakePolicy: StakePolicy) => { +export const getPolicyTitleName = (stakePolicy: StakePolicy, countryCode?: string) => { const stakeCurrencyCodes = getAssetDisplayName(stakePolicy, 'stakeAssets') const rewardCurrencyCodes = getAssetDisplayName(stakePolicy, 'rewardAssets') @@ -52,7 +53,7 @@ export const getPolicyTitleName = (stakePolicy: StakePolicy) => { const yieldText = yieldType === 'stable' ? ` ${lstrings.stake_stable_yield}` : yieldType === 'variable' ? ` ${lstrings.stake_variable_yield}` : '' - return `${sprintf(lstrings.stake_x_to_earn_y, stakeName, rewardName)}${yieldText}` + return `${getUkCompliantString(countryCode, 'stake_x_to_earn_y', stakeName, rewardName)}${yieldText}` } /** From 4ae9a8897fefcd8347e9b55ea755a90aee2b5de7 Mon Sep 17 00:00:00 2001 From: Jon Tzeng Date: Tue, 22 Oct 2024 18:23:09 -0700 Subject: [PATCH 18/89] Remove UK warning banner --- src/actions/LoginActions.tsx | 19 ++----------------- src/locales/en_US.ts | 1 - src/locales/strings/enUS.json | 1 - 3 files changed, 2 insertions(+), 19 deletions(-) diff --git a/src/actions/LoginActions.tsx b/src/actions/LoginActions.tsx index b58b932ab23..6c9d33b274b 100644 --- a/src/actions/LoginActions.tsx +++ b/src/actions/LoginActions.tsx @@ -10,7 +10,6 @@ import { readSyncedSettings } from '../actions/SettingsActions' import { ConfirmContinueModal } from '../components/modals/ConfirmContinueModal' import { FioCreateHandleModal } from '../components/modals/FioCreateHandleModal' import { SurveyModal } from '../components/modals/SurveyModal' -import { AlertDropdown } from '../components/navigation/AlertDropdown' import { Airship, showError } from '../components/services/AirshipInstance' import { ENV } from '../env' import { getExperimentConfig } from '../experimentConfig' @@ -23,8 +22,8 @@ import { NavigationBase, NavigationProp } from '../types/routerTypes' import { currencyCodesToEdgeAssets } from '../util/CurrencyInfoHelpers' import { logActivity } from '../util/logger' import { logEvent, trackError } from '../util/tracking' -import { openLink, runWithTimeout } from '../util/utils' -import { getCountryCodeByIp, loadAccountReferral, refreshAccountReferral } from './AccountReferralActions' +import { runWithTimeout } from '../util/utils' +import { loadAccountReferral, refreshAccountReferral } from './AccountReferralActions' import { getUniqueWalletName } from './CreateWalletActions' import { getDeviceSettings, writeIsSurveyDiscoverShown } from './DeviceSettingsActions' import { readLocalAccountSettings } from './LocalSettingsActions' @@ -267,20 +266,6 @@ export function initializeAccount(navigation: NavigationBase, account: EdgeAccou await Airship.show(bridge => ) await writeIsSurveyDiscoverShown(true) } - - if ((await getCountryCodeByIp()) === 'GB') { - await Airship.show(bridge => ( - { - await openLink('https://edge.app/due-diligence/') - }} - /> - )) - } } } diff --git a/src/locales/en_US.ts b/src/locales/en_US.ts index a986878a924..6f6460e2858 100644 --- a/src/locales/en_US.ts +++ b/src/locales/en_US.ts @@ -125,7 +125,6 @@ const strings = { warning_token_code_override_2s: 'The entered contract address differs from the contract address of built-in token %1$s. Please proceed with caution and verify the contract is legitimate as use of this token can result in loss of funds. If you have questions about this feature or contract please contact %2$s.', warning_token_exists_1s: 'The entered token already exists as a built-in token %1$s', - warning_uk_risk: `Don't invest unless you're prepared to lose all the money you invest. This is a high-risk investment and you should not expect to be protected if something goes wrong. Take 2 min to learn more.`, warning_battery_saver: `Battery Saver mode detected. Balances and transactions may be inaccurate`, // Alert component: diff --git a/src/locales/strings/enUS.json b/src/locales/strings/enUS.json index 44961d37c1a..8d72caa8852 100644 --- a/src/locales/strings/enUS.json +++ b/src/locales/strings/enUS.json @@ -80,7 +80,6 @@ "warning_scam_message_yes_1s": "Please proceed with caution! Assistance with account creation has the potential for fraud. Users should never share passwords or private keys. Social media and chat platforms have been involved in attacks. Do not send cryptocurrency to strangers. If you believe you’re being taken advantage of, please contact our support team at %1$s.", "warning_token_code_override_2s": "The entered contract address differs from the contract address of built-in token %1$s. Please proceed with caution and verify the contract is legitimate as use of this token can result in loss of funds. If you have questions about this feature or contract please contact %2$s.", "warning_token_exists_1s": "The entered token already exists as a built-in token %1$s", - "warning_uk_risk": "Don't invest unless you're prepared to lose all the money you invest. This is a high-risk investment and you should not expect to be protected if something goes wrong. Take 2 min to learn more.", "warning_battery_saver": "Battery Saver mode detected. Balances and transactions may be inaccurate", "alert_dropdown_alert": "Alert! ", "alert_dropdown_warning": "Warning! ", From c6816cff08bf6f2594ab8ef69fa45e14c124d488 Mon Sep 17 00:00:00 2001 From: Jon Tzeng Date: Tue, 22 Oct 2024 18:28:09 -0700 Subject: [PATCH 19/89] Disable Moonpay, Paybis, and Simplex providers --- CHANGELOG.md | 3 +++ src/plugins/gui/providers/moonpayProvider.ts | 2 +- src/plugins/gui/providers/paybisProvider.ts | 4 ++-- src/plugins/gui/providers/simplexProvider.ts | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28f656281c9..79be93b296d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,12 @@ ## Unreleased +- changed: Various strings updated to UK compliance spec - changed: Wording in light account persistent notification - removed: Bank Wire Transfer Buy for Florida - removed: Paypal Sell for Canada +- removed: Moonpay, Simplex, and Paybis for UK +- removed: UK persistent investment risk banner ## 4.15.1 diff --git a/src/plugins/gui/providers/moonpayProvider.ts b/src/plugins/gui/providers/moonpayProvider.ts index 1d53724e5ba..255326388c9 100644 --- a/src/plugins/gui/providers/moonpayProvider.ts +++ b/src/plugins/gui/providers/moonpayProvider.ts @@ -203,7 +203,7 @@ export const moonpayProvider: FiatProviderFactory = { // Return nothing if paymentTypes are not supported by this provider const assetMap = allowedCurrencyCodes[direction][paymentType] - if (assetMap == null) throw new FiatProviderError({ providerId, errorType: 'paymentUnsupported' }) + if (assetMap == null || regionCode.countryCode === 'GB') throw new FiatProviderError({ providerId, errorType: 'paymentUnsupported' }) if (Object.keys(assetMap.crypto).length === 0 || isDailyCheckDue(lastChecked)) { const response = await fetch(`https://api.moonpay.com/v3/currencies?apiKey=${apiKey}`).catch(e => undefined) diff --git a/src/plugins/gui/providers/paybisProvider.ts b/src/plugins/gui/providers/paybisProvider.ts index 9059aef6ed5..b0c93f808f9 100644 --- a/src/plugins/gui/providers/paybisProvider.ts +++ b/src/plugins/gui/providers/paybisProvider.ts @@ -350,8 +350,8 @@ export const paybisProvider: FiatProviderFactory = { partnerIcon, pluginDisplayName, getSupportedAssets: async ({ direction, paymentTypes, regionCode }): Promise => { - // Do not allow sell to debit in US - if (direction === 'sell' && paymentTypes.includes('credit') && regionCode.countryCode === 'US') { + // Do not allow sell to debit in US, disable all UK + if (regionCode.countryCode === 'GB' || (direction === 'sell' && paymentTypes.includes('credit') && regionCode.countryCode === 'US')) { throw new FiatProviderError({ providerId, errorType: 'paymentUnsupported' }) } validateRegion(providerId, regionCode, SUPPORTED_REGIONS) diff --git a/src/plugins/gui/providers/simplexProvider.ts b/src/plugins/gui/providers/simplexProvider.ts index bfcd3c1b48d..259cd5c9628 100644 --- a/src/plugins/gui/providers/simplexProvider.ts +++ b/src/plugins/gui/providers/simplexProvider.ts @@ -200,7 +200,7 @@ export const simplexProvider: FiatProviderFactory = { partnerIcon, pluginDisplayName, getSupportedAssets: async ({ direction, regionCode, paymentTypes }): Promise => { - if (direction !== 'buy') { + if (direction !== 'buy' || regionCode.countryCode === 'GB') { throw new FiatProviderError({ providerId, errorType: 'paymentUnsupported' }) } From 01486e3f94946baef25f1db53d7ea8efe8e00601 Mon Sep 17 00:00:00 2001 From: Jon Tzeng Date: Thu, 24 Oct 2024 12:33:31 -0700 Subject: [PATCH 20/89] Fix routing for swap flow --- src/components/scenes/SwapSuccessScene.tsx | 2 +- src/components/scenes/TransactionDetailsScene.tsx | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/scenes/SwapSuccessScene.tsx b/src/components/scenes/SwapSuccessScene.tsx index b363840a53f..cdcc5d64699 100644 --- a/src/components/scenes/SwapSuccessScene.tsx +++ b/src/components/scenes/SwapSuccessScene.tsx @@ -46,7 +46,7 @@ export const SwapSuccessScene = (props: Props) => { }) const handleTransactionDetails = useHandler(() => { - navigation.replace('transactionDetails', { edgeTransaction, walletId }) + navigation.replace('transactionDetails', { edgeTransaction, walletId, onDone: handleDone }) }) useAsyncEffect( diff --git a/src/components/scenes/TransactionDetailsScene.tsx b/src/components/scenes/TransactionDetailsScene.tsx index 3f8e2f56c03..2c7fcfd527c 100644 --- a/src/components/scenes/TransactionDetailsScene.tsx +++ b/src/components/scenes/TransactionDetailsScene.tsx @@ -50,11 +50,12 @@ interface Props extends EdgeSceneProps<'transactionDetails'> { export interface TransactionDetailsParams { edgeTransaction: EdgeTransaction walletId: string + onDone?: () => void } const TransactionDetailsComponent = (props: Props) => { const { navigation, route, wallet } = props - const { edgeTransaction: transaction, walletId } = route.params + const { edgeTransaction: transaction, walletId, onDone } = route.params const { currencyCode, metadata, nativeAmount, date, txid, tokenId } = transaction const { currencyInfo } = wallet @@ -144,6 +145,8 @@ const TransactionDetailsComponent = (props: Props) => { .catch(error => showError(error)) }) + const handleDone = useHandler(() => (onDone == null ? navigation.pop() : onDone())) + // #endregion Crypto Fiat Rows React.useEffect(() => { @@ -413,7 +416,7 @@ const TransactionDetailsComponent = (props: Props) => { Date: Thu, 24 Oct 2024 15:38:49 -0700 Subject: [PATCH 21/89] Upgrade to edge-core-js v2.19.1 --- ios/Podfile.lock | 8 ++++---- package.json | 2 +- yarn.lock | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index f543f1f3435..373b4a1f1f0 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -15,7 +15,7 @@ PODS: - disklet (0.5.2): - React - DoubleConversion (1.1.6) - - edge-core-js (2.19.0): + - edge-core-js (2.19.1): - React-Core - edge-currency-accountbased (4.26.1): - React-Core @@ -1079,8 +1079,8 @@ SPEC CHECKSUMS: CNIOLinux: 62e3505f50de558c393dc2f273dde71dcce518da CNIOWindows: 3047f2d8165848a3936a0a755fee27c6b5ee479b disklet: e7ed3e673ccad9d175a1675f9f3589ffbf69a5fd - DoubleConversion: 76ab83afb40bddeeee456813d9c04f67f78771b5 - edge-core-js: c49ecfb894c2a86ca7475848419174a2a9ed95db + DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54 + edge-core-js: 09206fe34acb8f4eebafe61e8c2eda6cbe81ba69 edge-currency-accountbased: ba759ec765d487d86e5f7e26a38e14edfc1d64dd edge-currency-plugins: 38eaf53c2d9fdbdd30ade3ad09fd698f428f208f edge-exchange-plugins: 5037e196e652d1dca42afacd86b2395bd0d7f298 @@ -1101,7 +1101,7 @@ SPEC CHECKSUMS: FirebaseInstallations: 766dabca09fd94aef922538aaf144cc4a6fb6869 FirebaseMessaging: 585984d0a1df120617eb10b44cad8968b859815e fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9 - glog: c5d68082e772fa1c511173d6b30a9de2c05a69a2 + glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15 gRPC-Swift: 74adcaaa62ac5e0a018938840328cb1fdfb09e7b diff --git a/package.json b/package.json index 46af41738ad..407b2c80e0b 100644 --- a/package.json +++ b/package.json @@ -97,7 +97,7 @@ "deepmerge": "^4.3.1", "detect-bundler": "^1.1.0", "disklet": "^0.5.2", - "edge-core-js": "^2.19.0", + "edge-core-js": "^2.19.1", "edge-currency-accountbased": "^4.26.1", "edge-currency-monero": "^1.3.1", "edge-currency-plugins": "^3.4.3", diff --git a/yarn.lock b/yarn.lock index 1c9016c2087..0b318c73dbb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9107,10 +9107,10 @@ ed25519@0.0.4: bindings "^1.2.1" nan "^2.0.9" -edge-core-js@^2.19.0: - version "2.19.0" - resolved "https://registry.yarnpkg.com/edge-core-js/-/edge-core-js-2.19.0.tgz#ff84b4ae3c5a35cc71af5f082c1eed124d9cde2c" - integrity sha512-W0VTkKyuYSsERMPtAB1Ud925AWG0PpDFtWyvtVCgMPVL8b6p8YGy8L5STZKttv1U7jXK3Cg5stVVxVIxQrPAew== +edge-core-js@^2.19.1: + version "2.19.1" + resolved "https://registry.yarnpkg.com/edge-core-js/-/edge-core-js-2.19.1.tgz#84e6c67e38fe72e53217538af40f41666713f500" + integrity sha512-1BiyRHw+k/0DM+f44Lvs0RUN5DXfoWpazXYGWuuDL1Uz7dbV+JsAUm1LqYD8+fYBnwFjeb+5RLSPrKPIFUMB4g== dependencies: aes-js "^3.1.0" base-x "^4.0.0" From ae570896c2edc305cff0783a5d71ae8b7c89948d Mon Sep 17 00:00:00 2001 From: Jon Tzeng Date: Thu, 24 Oct 2024 12:11:15 -0700 Subject: [PATCH 22/89] Update `getUkComplaintString` to support more than 1-1 mapping --- src/util/ukComplianceUtils.ts | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/util/ukComplianceUtils.ts b/src/util/ukComplianceUtils.ts index b4db84ff434..61cf0c2f0df 100644 --- a/src/util/ukComplianceUtils.ts +++ b/src/util/ukComplianceUtils.ts @@ -1,19 +1,20 @@ import { LocaleStringKey } from '../locales/en_US' import { lstrings } from '../locales/strings' -const UK_COMPLIANT_STRING_MAP: { [key: string]: LocaleStringKey } = { - buy_1s: 'uk_ways_to_buy_1s', - sell_1s: 'uk_ways_to_sell_1s', - stake_earn_1s: 'stake_stake_1s', - stake_earn_button_label: 'fragment_stake_label', - stake_x_to_earn_y: 'transaction_details_stake_subcat_1s' +const UK_COMPLIANT_STRING_MAP: { [key: string]: { default: LocaleStringKey; gb: LocaleStringKey } } = { + buy_1s: { default: 'buy_1s', gb: 'uk_ways_to_buy_1s' }, + sell_1s: { default: 'buy_1s', gb: 'uk_ways_to_sell_1s' }, + stake_earn_1s: { default: 'stake_earn_1s', gb: 'stake_stake_1s' }, + stake_earn_button_label: { default: 'stake_earn_button_label', gb: 'fragment_stake_label' }, + stake_x_to_earn_y: { default: 'stake_x_to_earn_y', gb: 'transaction_details_stake_subcat_1s' } } const formatString = (template: string, values: string[]): string => { return template.replace(/%(\d+)\$s/g, (_, index) => values[parseInt(index) - 1] || '') } -export const getUkCompliantString = (countryCode: string | undefined, key: LocaleStringKey, ...values: string[]): string => { - const template = countryCode === 'GB' ? lstrings[UK_COMPLIANT_STRING_MAP[key]] : lstrings[key] +export const getUkCompliantString = (countryCode: string | undefined, key: string, ...values: string[]): string => { + const compliantStringKeys = UK_COMPLIANT_STRING_MAP[key] + const template = countryCode === 'GB' ? lstrings[compliantStringKeys.gb] : lstrings[compliantStringKeys.default] return formatString(template, values) } From 59b743a7f558413caaf3a2bc82394d1414ec4aa2 Mon Sep 17 00:00:00 2001 From: Jon Tzeng Date: Thu, 24 Oct 2024 12:15:10 -0700 Subject: [PATCH 23/89] Update `GuiPluginListScene` with new title --- src/util/ukComplianceUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/ukComplianceUtils.ts b/src/util/ukComplianceUtils.ts index 61cf0c2f0df..63035826b0d 100644 --- a/src/util/ukComplianceUtils.ts +++ b/src/util/ukComplianceUtils.ts @@ -3,7 +3,7 @@ import { lstrings } from '../locales/strings' const UK_COMPLIANT_STRING_MAP: { [key: string]: { default: LocaleStringKey; gb: LocaleStringKey } } = { buy_1s: { default: 'buy_1s', gb: 'uk_ways_to_buy_1s' }, - sell_1s: { default: 'buy_1s', gb: 'uk_ways_to_sell_1s' }, + sell_1s: { default: 'sell_1s', gb: 'uk_ways_to_sell_1s' }, stake_earn_1s: { default: 'stake_earn_1s', gb: 'stake_stake_1s' }, stake_earn_button_label: { default: 'stake_earn_button_label', gb: 'fragment_stake_label' }, stake_x_to_earn_y: { default: 'stake_x_to_earn_y', gb: 'transaction_details_stake_subcat_1s' } From 729988a82d0fb5ef6a37441bbf041a76e049eadd Mon Sep 17 00:00:00 2001 From: Jon Tzeng Date: Thu, 24 Oct 2024 12:11:46 -0700 Subject: [PATCH 24/89] Fix Trade modal compliant sell string --- src/components/themed/TransactionListTop.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/themed/TransactionListTop.tsx b/src/components/themed/TransactionListTop.tsx index 62e0a77de2f..24884c1ccce 100644 --- a/src/components/themed/TransactionListTop.tsx +++ b/src/components/themed/TransactionListTop.tsx @@ -262,7 +262,7 @@ export class TransactionListTopComponent extends React.PureComponent this.handleTradeSell(bridge)} icon={ From 540b05c7597139224f87d5afa17e1729cc19e1b0 Mon Sep 17 00:00:00 2001 From: Jon Tzeng Date: Thu, 24 Oct 2024 12:15:10 -0700 Subject: [PATCH 25/89] Update `GuiPluginListScene` with new title --- src/components/scenes/GuiPluginListScene.tsx | 4 +++- src/locales/en_US.ts | 1 + src/locales/strings/enUS.json | 1 + src/util/ukComplianceUtils.ts | 1 + 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/scenes/GuiPluginListScene.tsx b/src/components/scenes/GuiPluginListScene.tsx index 0bd9463fbb5..2691a279e22 100644 --- a/src/components/scenes/GuiPluginListScene.tsx +++ b/src/components/scenes/GuiPluginListScene.tsx @@ -419,7 +419,9 @@ class GuiPluginList extends React.PureComponent { <> diff --git a/src/locales/en_US.ts b/src/locales/en_US.ts index 6f6460e2858..c4a18d70a45 100644 --- a/src/locales/en_US.ts +++ b/src/locales/en_US.ts @@ -1639,6 +1639,7 @@ const strings = { uk_ways_to_buy_1s: 'Ways to Buy %1$s', uk_ways_to_sell_1s: 'Ways to Sell %1$s', + uk_get_quote_provider_1s: 'Get %1$s Quote from 3rd Party Provider', // #endregion UK Compliance diff --git a/src/locales/strings/enUS.json b/src/locales/strings/enUS.json index 8d72caa8852..5af5b210fae 100644 --- a/src/locales/strings/enUS.json +++ b/src/locales/strings/enUS.json @@ -1416,6 +1416,7 @@ "button_support": "Contact Support", "uk_ways_to_buy_1s": "Ways to Buy %1$s", "uk_ways_to_sell_1s": "Ways to Sell %1$s", + "uk_get_quote_provider_1s": "Get %1$s Quote from 3rd Party Provider", "redacted_placeholder": "●●●●", "insufficient_funds_2s": "Insufficient %1$s (%2$s).", "split_from_1s": "Split from %1$s", diff --git a/src/util/ukComplianceUtils.ts b/src/util/ukComplianceUtils.ts index 63035826b0d..638bd2a3dd0 100644 --- a/src/util/ukComplianceUtils.ts +++ b/src/util/ukComplianceUtils.ts @@ -3,6 +3,7 @@ import { lstrings } from '../locales/strings' const UK_COMPLIANT_STRING_MAP: { [key: string]: { default: LocaleStringKey; gb: LocaleStringKey } } = { buy_1s: { default: 'buy_1s', gb: 'uk_ways_to_buy_1s' }, + buy_1s_title: { default: 'buy_1s', gb: 'uk_get_quote_provider_1s' }, sell_1s: { default: 'sell_1s', gb: 'uk_ways_to_sell_1s' }, stake_earn_1s: { default: 'stake_earn_1s', gb: 'stake_stake_1s' }, stake_earn_button_label: { default: 'stake_earn_button_label', gb: 'fragment_stake_label' }, From de20e83bd4d2cd981f0599c16582643f9521c947 Mon Sep 17 00:00:00 2001 From: William Swanson Date: Wed, 23 Oct 2024 14:51:16 -0700 Subject: [PATCH 26/89] Rename ENV.AIRBITZ_API_KEY to ENV.EDGE_API_KEY --- scripts/makeNativeHeaders.ts | 2 +- src/actions/NotificationActions.ts | 6 +++--- src/components/services/EdgeCoreManager.tsx | 2 +- src/envConfig.ts | 2 +- src/util/PushClient/PushClient.ts | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/scripts/makeNativeHeaders.ts b/scripts/makeNativeHeaders.ts index cbdb35277b8..b60653f9ea0 100644 --- a/scripts/makeNativeHeaders.ts +++ b/scripts/makeNativeHeaders.ts @@ -7,7 +7,7 @@ function makeNativeHeaders() { // Grab the API key: let apiKey = 'Error: Set up env.json & re-run scripts/makeNativeHeaders.js' try { - apiKey = require('../env.json').AIRBITZ_API_KEY + apiKey = require('../env.json').EDGE_API_KEY } catch (e) { console.log(apiKey) } diff --git a/src/actions/NotificationActions.ts b/src/actions/NotificationActions.ts index 5c72ac2644c..1131a1bb692 100644 --- a/src/actions/NotificationActions.ts +++ b/src/actions/NotificationActions.ts @@ -46,7 +46,7 @@ export function registerNotificationsV2(changeFiat: boolean = false): ThunkActio .catch(() => '') const body = { - apiKey: ENV.AIRBITZ_API_KEY, + apiKey: ENV.EDGE_API_KEY, deviceId: state.core.context.clientId, deviceToken, loginId: base64.stringify(base58.parse(state.core.account.rootLoginId)) @@ -193,7 +193,7 @@ async function updateServerSettings(context: EdgeContext, data: DeviceUpdatePayl .catch(() => '') const body = { - apiKey: ENV.AIRBITZ_API_KEY, + apiKey: ENV.EDGE_API_KEY, deviceId, deviceToken, data: { ...data, loginIds } @@ -279,7 +279,7 @@ async function legacyGet(path: string) { method: 'GET', headers: { 'Content-Type': 'application/json', - 'X-Api-Key': ENV.AIRBITZ_API_KEY + 'X-Api-Key': ENV.EDGE_API_KEY } }) if (response != null && response.ok) { diff --git a/src/components/services/EdgeCoreManager.tsx b/src/components/services/EdgeCoreManager.tsx index d4669143962..0ad1cbbd136 100644 --- a/src/components/services/EdgeCoreManager.tsx +++ b/src/components/services/EdgeCoreManager.tsx @@ -29,7 +29,7 @@ const SYNC_TEST_SERVER = 'https://sync-tester-us1.edge.app' interface Props {} const contextOptions: EdgeContextOptions = { - apiKey: ENV.AIRBITZ_API_KEY, + apiKey: ENV.EDGE_API_KEY, appId: '', deviceDescription: `${getBrand()} ${getDeviceId()}`, diff --git a/src/envConfig.ts b/src/envConfig.ts index 5231e1a7fa3..1f8f6aab2fc 100644 --- a/src/envConfig.ts +++ b/src/envConfig.ts @@ -29,7 +29,7 @@ const asEvmApiKeys = asObject({ export const asEnvConfig = asObject({ // API keys: - AIRBITZ_API_KEY: asOptional(asString, ''), + EDGE_API_KEY: asOptional(asString, ''), COINGECKO_API_KEY: asOptional(asString, 'a0000000000000000000000000000000'), IP_API_KEY: asOptional(asString, ''), SENTRY_DSN_URL: asOptional(asString, 'SENTRY_DSN_URL'), diff --git a/src/util/PushClient/PushClient.ts b/src/util/PushClient/PushClient.ts index 33ba7def846..6500f8e6e4c 100644 --- a/src/util/PushClient/PushClient.ts +++ b/src/util/PushClient/PushClient.ts @@ -13,7 +13,7 @@ import { import { ENV } from '../../env' import { base58 } from '../encoding' -const { ACTION_QUEUE, AIRBITZ_API_KEY } = ENV +const { ACTION_QUEUE, EDGE_API_KEY } = ENV const { pushServerUri } = ACTION_QUEUE export interface PushClient { @@ -46,7 +46,7 @@ export const makePushClient = (account: EdgeAccount, clientId: string): PushClie getPushRequestBody(payload?: LoginUpdatePayload): PushRequestBody { const data = payload != null ? wasLoginUpdatePayload(payload) : undefined return { - apiKey: AIRBITZ_API_KEY, + apiKey: EDGE_API_KEY, deviceId: clientId, loginId: base58.parse(account.rootLoginId), data From aa63545aa51584670106f29b913e14e86052f7e6 Mon Sep 17 00:00:00 2001 From: William Swanson Date: Wed, 23 Oct 2024 14:55:16 -0700 Subject: [PATCH 27/89] Add the optional Edge API secret --- src/components/services/EdgeCoreManager.tsx | 1 + src/envConfig.ts | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/components/services/EdgeCoreManager.tsx b/src/components/services/EdgeCoreManager.tsx index 0ad1cbbd136..294201b6cdb 100644 --- a/src/components/services/EdgeCoreManager.tsx +++ b/src/components/services/EdgeCoreManager.tsx @@ -30,6 +30,7 @@ interface Props {} const contextOptions: EdgeContextOptions = { apiKey: ENV.EDGE_API_KEY, + apiSecret: ENV.EDGE_API_SECRET, appId: '', deviceDescription: `${getBrand()} ${getDeviceId()}`, diff --git a/src/envConfig.ts b/src/envConfig.ts index 1f8f6aab2fc..4bf3d927afa 100644 --- a/src/envConfig.ts +++ b/src/envConfig.ts @@ -1,5 +1,7 @@ import { asArray, asBoolean, asEither, asNumber, asObject, asOptional, asString, asValue, Cleaner } from 'cleaners' +import { asBase16 } from './util/cleaners/asHex' + function asNullable(cleaner: Cleaner): Cleaner { return function asNullable(raw) { if (raw == null) return null @@ -30,6 +32,8 @@ const asEvmApiKeys = asObject({ export const asEnvConfig = asObject({ // API keys: EDGE_API_KEY: asOptional(asString, ''), + EDGE_API_SECRET: asOptional(asBase16), + COINGECKO_API_KEY: asOptional(asString, 'a0000000000000000000000000000000'), IP_API_KEY: asOptional(asString, ''), SENTRY_DSN_URL: asOptional(asString, 'SENTRY_DSN_URL'), From 1b26758ab62edd623de3aa0f782a3ce934b49cdd Mon Sep 17 00:00:00 2001 From: Jon Tzeng Date: Fri, 25 Oct 2024 12:18:15 -0700 Subject: [PATCH 28/89] Only access `countryCode` from `getFirstOpenInfo()` --- CHANGELOG.md | 1 + src/components/scenes/HomeScene.tsx | 4 ++-- src/components/themed/WalletListSwipeable.tsx | 4 ++-- src/components/themed/WalletListSwipeableCurrencyRow.tsx | 4 ++-- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 79be93b296d..e4f99babe85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ - fixed: Use account default fiat for transaction fee display in `SweepPrivateKeyCalculateFeeScene` - fixed: Crash when archiving a wallet that recently accessed the trade modal - fixed: ACH sell option for Florida visible with no accepted currencies +- fixed: Slow `TransactionDetailsScene` navigation on slow networks ## 4.15.0 (2024-10-16) diff --git a/src/components/scenes/HomeScene.tsx b/src/components/scenes/HomeScene.tsx index d328237f76f..9b5e9fc3bb3 100644 --- a/src/components/scenes/HomeScene.tsx +++ b/src/components/scenes/HomeScene.tsx @@ -5,7 +5,7 @@ import FastImage from 'react-native-fast-image' import Animated from 'react-native-reanimated' import { useSafeAreaFrame } from 'react-native-safe-area-context' -import { getCountryCodeByIp } from '../../actions/AccountReferralActions' +import { getFirstOpenInfo } from '../../actions/FirstOpenActions' import { SCROLL_INDICATOR_INSET_FIX } from '../../constants/constantSettings' import { ENV } from '../../env' import { useAsyncEffect } from '../../hooks/useAsyncEffect' @@ -95,7 +95,7 @@ export const HomeScene = (props: Props) => { // Set countryCode once useAsyncEffect( async () => { - const countryCode = await getCountryCodeByIp() + const { countryCode } = await getFirstOpenInfo() setCountryCode(countryCode) }, [], diff --git a/src/components/themed/WalletListSwipeable.tsx b/src/components/themed/WalletListSwipeable.tsx index a5fad91fbdf..a24fc8b30d7 100644 --- a/src/components/themed/WalletListSwipeable.tsx +++ b/src/components/themed/WalletListSwipeable.tsx @@ -4,7 +4,7 @@ import { useMemo } from 'react' import { FlatList, RefreshControl } from 'react-native' import Animated from 'react-native-reanimated' -import { getCountryCodeByIp } from '../../actions/AccountReferralActions' +import { getFirstOpenInfo } from '../../actions/FirstOpenActions' import { selectWalletToken } from '../../actions/WalletActions' import { SCROLL_INDICATOR_INSET_FIX } from '../../constants/constantSettings' import { useHandler } from '../../hooks/useHandler' @@ -67,7 +67,7 @@ function WalletListSwipeableComponent(props: Props) { const handleCreateWallet = useHandler(async (walletId: string, tokenId: EdgeTokenId) => { const wallet = account.currencyWallets[walletId] - const countryCode = await getCountryCodeByIp() + const { countryCode } = await getFirstOpenInfo() dispatch(selectWalletToken({ navigation, walletId, tokenId })) .then( activationNotRequired => diff --git a/src/components/themed/WalletListSwipeableCurrencyRow.tsx b/src/components/themed/WalletListSwipeableCurrencyRow.tsx index 080b23f9c16..73c45d5d7eb 100644 --- a/src/components/themed/WalletListSwipeableCurrencyRow.tsx +++ b/src/components/themed/WalletListSwipeableCurrencyRow.tsx @@ -3,8 +3,8 @@ import * as React from 'react' import { Text } from 'react-native' import { SharedValue } from 'react-native-reanimated' -import { getCountryCodeByIp } from '../../actions/AccountReferralActions' import { checkAndShowLightBackupModal } from '../../actions/BackupModalActions' +import { getFirstOpenInfo } from '../../actions/FirstOpenActions' import { selectWalletToken } from '../../actions/WalletActions' import { Fontello } from '../../assets/vector/index' import { useHandler } from '../../hooks/useHandler' @@ -70,7 +70,7 @@ function WalletListSwipeableCurrencyRowComponent(props: Props) { closeRow() dispatch(selectWalletToken({ navigation, walletId: wallet.id, tokenId, alwaysActivate: true })) .then(async activated => { - const countryCode = await getCountryCodeByIp() + const { countryCode } = await getFirstOpenInfo() if (activated) { navigation.navigate('transactionList', { tokenId, walletId: wallet.id, walletName: wallet.name ?? wallet.currencyInfo.displayName, countryCode }) } From 8290ab2a9deae442c07cf4459c0497dc39a15e53 Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Wed, 23 Oct 2024 10:54:38 -0700 Subject: [PATCH 29/89] Allow metadata to be include in trackError without tag --- src/util/tracking.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/util/tracking.ts b/src/util/tracking.ts index 30781cc479d..0837510ba2e 100644 --- a/src/util/tracking.ts +++ b/src/util/tracking.ts @@ -160,11 +160,15 @@ export function trackError( err = 'Unknown error occurred' } - if (tag == null) { - captureException(err) - } else { - captureException(err, { event_id: tag, data: metadata }) + let hint: { event_id?: string; data?: { [key: string]: any } } | undefined + if (tag != null) { + hint = { event_id: tag } + } + if (metadata != null) { + hint = { data: metadata } } + + captureException(err, hint) } /** From 7a0e5a6f33098de4818eabc3d6f2b2f8659ab0e9 Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Fri, 25 Oct 2024 15:55:02 -0700 Subject: [PATCH 30/89] Fix error massaging in trackError The error captured should always be of type `Error` some stack traces can be included. We cast unexpected error types to String and include that in the unknown error message. --- CHANGELOG.md | 1 + src/util/tracking.ts | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e4f99babe85..f4b09935d46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - changed: Various strings updated to UK compliance spec - changed: Wording in light account persistent notification +- fixed: Fix error massaging in trackError - removed: Bank Wire Transfer Buy for Florida - removed: Paypal Sell for Canada - removed: Moonpay, Simplex, and Paybis for UK diff --git a/src/util/tracking.ts b/src/util/tracking.ts index 0837510ba2e..450dc3e111a 100644 --- a/src/util/tracking.ts +++ b/src/util/tracking.ts @@ -152,12 +152,14 @@ export function trackError( [key: string]: any } ): void { - let err: Error | string - if (error instanceof Error || typeof error === 'string') { + let err: Error + if (error instanceof Error) { err = error + } else if (typeof error === 'string') { + err = new Error(error) } else { - // At least send an error which should give us the callstack - err = 'Unknown error occurred' + // At least send an error which should give us the call-stack + err = new Error(`Unknown error occurred: ${String(error)}`) } let hint: { event_id?: string; data?: { [key: string]: any } } | undefined From f01851d298a293e01d9b94e92c9f6e9bec79badc Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Wed, 23 Oct 2024 15:57:47 -0700 Subject: [PATCH 31/89] Capture swap quote failures as warnings We'll capture these warning type errors using Sentry's API. We'll include details relevant for the swap for indexing purposes, but NEVER include personal or sensitive information from the user's activity. --- CHANGELOG.md | 1 + src/components/scenes/SwapProcessingScene.tsx | 37 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f4b09935d46..13e843de238 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +- added: Log swap errors to Sentry. - changed: Various strings updated to UK compliance spec - changed: Wording in light account persistent notification - fixed: Fix error massaging in trackError diff --git a/src/components/scenes/SwapProcessingScene.tsx b/src/components/scenes/SwapProcessingScene.tsx index bab1dcf6271..38f26b2ced4 100644 --- a/src/components/scenes/SwapProcessingScene.tsx +++ b/src/components/scenes/SwapProcessingScene.tsx @@ -1,3 +1,4 @@ +import { captureException } from '@sentry/react-native' import { asMaybeInsufficientFundsError, asMaybeSwapAboveLimitError, @@ -111,6 +112,9 @@ function processSwapQuoteError({ // Some plugins get the insufficient funds error wrong: const errorMessage = error instanceof Error ? error.message : String(error) + // Track swap errors to sentry: + trackSwapError(error, swapRequest) + // Check for known error types: const insufficientFunds = asMaybeInsufficientFundsError(error) if (insufficientFunds != null || errorMessage === 'InsufficientFundsError') { @@ -179,3 +183,36 @@ function processSwapQuoteError({ error } } + +/** + * REVIEWER BEWARE!! + * + * No specific account/wallet information should be included within the + * scope for this capture. No personal information such as wallet IDs, + * public keys, or transaction details, amounts, should be collected + * according to Edge's company policy. + */ +function trackSwapError(error: unknown, swapRequest: EdgeSwapRequest): void { + captureException(error, scope => { + // This is a warning level error because it's expected to occur but not wanted. + scope.setLevel('warning') + // Searchable tags: + scope.setTags({ + errorType: 'swapQuoteFailure', + swapFromWalletKind: swapRequest.fromWallet.currencyInfo.pluginId, + swapFromCurrency: getCurrencyCode(swapRequest.fromWallet, swapRequest.fromTokenId), + swapToCurrency: getCurrencyCode(swapRequest.toWallet, swapRequest.toTokenId), + swapToWalletKind: swapRequest.toWallet.currencyInfo.pluginId, + swapDirectionType: swapRequest.quoteFor + }) + // Unsearchable context data: + scope.setContext('Swap Request Details', { + fromTokenId: String(swapRequest.fromTokenId), // Stringify to include "null" + fromWalletType: swapRequest.fromWallet.type, + toTokenId: String(swapRequest.toTokenId), // Stringify to include "null" + toWalletType: swapRequest.fromWallet.type, + quoteFor: swapRequest.quoteFor + }) + return scope + }) +} From 80cfc4e4f811ec2ca160ea34494e11e6a911b1f2 Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Wed, 23 Oct 2024 16:55:24 -0700 Subject: [PATCH 32/89] Leverage context for crash logs in EdgeCrashReporter By using the context object, we allow for error name/message information to be indexed properly in Sentry without losing the contextual information that comes along with the error. --- CHANGELOG.md | 1 + src/components/services/EdgeCoreManager.tsx | 51 ++++++++++++++++++++- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 13e843de238..243d5190782 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - changed: Various strings updated to UK compliance spec - changed: Wording in light account persistent notification - fixed: Fix error massaging in trackError +- fixed: Use Sentry context for logging metadata in `EdgeCrashEvent` - removed: Bank Wire Transfer Buy for Florida - removed: Paypal Sell for Canada - removed: Moonpay, Simplex, and Paybis for UK diff --git a/src/components/services/EdgeCoreManager.tsx b/src/components/services/EdgeCoreManager.tsx index 294201b6cdb..1ea910a4007 100644 --- a/src/components/services/EdgeCoreManager.tsx +++ b/src/components/services/EdgeCoreManager.tsx @@ -68,8 +68,55 @@ const crashReporter: EdgeCrashReporter = { }) }, logCrash(event) { - const eventString = JSON.stringify(event, null, 2) - captureException(eventString, { level: 'fatal' }) + // Index the crash error by the source and original error name: + const error = new Error(`${event.source}: ${String(event.error)}`) + // All of these crash errors are grouped together using this error name: + error.name = 'EdgeCrashLog' + + captureException(error, scope => { + scope.setLevel('fatal') + + const context: Record = {} + addMetadataToContext(context, event.metadata) + scope.setContext('Edge Crash Metadata', context) + + return scope + }) + } +} + +/** + * Recursively adds metadata to a context object. + * + * @param context The context object to which to add metadata + * @param metadata The metadata object to add to the context + * @param prefixKeys optional prefix keys to add to each entry + * @returns void (modifies context object) + */ +function addMetadataToContext(context: Record, metadata: object, prefixKeys: string[] = []): void { + for (const [key, value] of Object.entries(metadata)) { + const allKeys = [...prefixKeys, key] + const fullKey = allKeys.join(' > ') + + // Serialize error objects manually because JSON.stringify doesn't + // include all the error properties: + if (value instanceof Error) { + context[fullKey] = `${value.name}: ${value.message}` + if (value.stack != null) { + // Include the stack trace with indentation: + context[fullKey] += `\n ${value.stack.replace(/\n/g, '\n ')}` + } + continue + } + + // Recurse over objects with added prefix keys: + if (typeof value === 'object' && value !== null) { + addMetadataToContext(context, value, allKeys) + continue + } + + // Default + context[key] = value } } From 7e761e56f475ab69c819a97e3f284eeb4bb115a7 Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Fri, 25 Oct 2024 15:46:22 -0700 Subject: [PATCH 33/89] Remove console logs from Sentry breadcrumbs --- src/app.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/app.ts b/src/app.ts index afb82ebe6b2..d602ad0ca3e 100644 --- a/src/app.ts +++ b/src/app.ts @@ -34,7 +34,13 @@ if (ENV.SENTRY_ORGANIZATION_SLUG.includes('SENTRY_ORGANIZATION')) { // Initialize Sentry within native iOS and Android code so we can catch crashes at // early app startup. - autoInitializeNativeSdk: false + autoInitializeNativeSdk: false, + + integrations: [ + Sentry.breadcrumbsIntegration({ + console: false + }) + ] }) } From ab3bc421468bfa7c8f744d33db31383484bc67c0 Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Fri, 25 Oct 2024 16:05:16 -0700 Subject: [PATCH 34/89] Normalize error tracking 1. Isolation normalization into new utility to de-dup error handling 2. Use `normalizeError` util at the end (trackError) 3. Fully remove i18n from error objects; only present localized strings in the UI. Translating error messages is be a locale issue. Maintaining our errors in the default language is more consistent and consistency is better when it comes to error tracking. If "to translate" an error means something other than i18n, such as to translate the error object and any special properties into a useful message for reporting, then a new function should be introduced for that purpose (call it `encodeError` or something of that nature). --- CHANGELOG.md | 1 + src/components/services/AirshipInstance.tsx | 11 ++--------- src/util/normalizeError.ts | 17 +++++++++++++++++ src/util/tracking.ts | 18 ++++++++---------- 4 files changed, 28 insertions(+), 19 deletions(-) create mode 100644 src/util/normalizeError.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 243d5190782..819423765c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - changed: Various strings updated to UK compliance spec - changed: Wording in light account persistent notification - fixed: Fix error massaging in trackError +- fixed: Normalized error messages for tracking; removing localization from error messages. - fixed: Use Sentry context for logging metadata in `EdgeCrashEvent` - removed: Bank Wire Transfer Buy for Florida - removed: Paypal Sell for Canada diff --git a/src/components/services/AirshipInstance.tsx b/src/components/services/AirshipInstance.tsx index 7f015a5ee0b..98e6cb82d92 100644 --- a/src/components/services/AirshipInstance.tsx +++ b/src/components/services/AirshipInstance.tsx @@ -24,14 +24,7 @@ export function showError(error: unknown, options: ShowErrorWarningOptions = {}) const tagMessage = tag == null ? '' : ` Tag: ${tag}.` const translatedMessage = translateError(error) + tagMessage if (doTrackError) { - if (error instanceof Error) { - // Log error with stack trace and a translated message to bug tracker - error.message = translatedMessage - trackError(error) - } else { - // Any other types we just send the translated message to bug tracker - trackError(translatedMessage) - } + trackError(error) } console.log(redText('Showing error drop-down alert: ' + makeErrorLog(error))) @@ -84,7 +77,7 @@ export function showWarning(error: unknown, options: ShowErrorWarningOptions = { const { trackError: doTrackError = true, tag } = options const translatedError = tag ? `Tag: ${tag}. ` + translateError(error) : translateError(error) if (doTrackError) { - trackError(`showWarning: ${translatedError}`) + trackError(error, tag) } console.log(yellowText('Showing warning drop-down alert: ' + makeErrorLog(error))) Airship.show(bridge => ).catch(err => console.error(err)) diff --git a/src/util/normalizeError.ts b/src/util/normalizeError.ts new file mode 100644 index 00000000000..51413987ad1 --- /dev/null +++ b/src/util/normalizeError.ts @@ -0,0 +1,17 @@ +/** + * Normalizes exceptions through into error instances. It will return all + * instances which extend Error untouched, but convert other types into Error + * instances. + * + * @param error some unknown caught exception + * @returns An instance of Error or subclass of Error + */ +export function normalizeError(error: unknown): Error { + if (error instanceof Error) { + return error + } + if (typeof error === 'string') { + return new Error(error) + } + return new Error(`Unknown error: ${String(error)}`) +} diff --git a/src/util/tracking.ts b/src/util/tracking.ts index 450dc3e111a..493b72490cf 100644 --- a/src/util/tracking.ts +++ b/src/util/tracking.ts @@ -10,6 +10,7 @@ import { ExperimentConfig, getExperimentConfig } from '../experimentConfig' import { ThunkAction } from '../types/reduxTypes' import { CryptoAmount } from './CryptoAmount' import { fetchReferral } from './network' +import { normalizeError } from './normalizeError' import { makeErrorLog } from './translateError' import { consify, monthsBetween } from './utils' @@ -143,7 +144,12 @@ if (ENV.POSTHOG_INIT) { } /** - * Track error to external reporting service (ie. Bugsnag) + * Track error to external reporting service (ie. Sentry). + * + * It will take an exception of `unknown` type and normalize it into an error + * for reporting. + * + * All normalization rules should be isolated to `normalizeError` utility. */ export function trackError( error: unknown, @@ -152,15 +158,7 @@ export function trackError( [key: string]: any } ): void { - let err: Error - if (error instanceof Error) { - err = error - } else if (typeof error === 'string') { - err = new Error(error) - } else { - // At least send an error which should give us the call-stack - err = new Error(`Unknown error occurred: ${String(error)}`) - } + const err = normalizeError(error) let hint: { event_id?: string; data?: { [key: string]: any } } | undefined if (tag != null) { From f055432cae6a388716295131953134f0f988c1ae Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Thu, 24 Oct 2024 18:32:19 -0700 Subject: [PATCH 35/89] Handle array exceptions Should an array be thrown, we'll normalize into an AggregateError type of object, and we'll concatenate localized error messages for i18n. --- src/util/normalizeError.ts | 15 +++++++++++++++ src/util/translateError.ts | 7 +++++++ 2 files changed, 22 insertions(+) diff --git a/src/util/normalizeError.ts b/src/util/normalizeError.ts index 51413987ad1..ff35a848662 100644 --- a/src/util/normalizeError.ts +++ b/src/util/normalizeError.ts @@ -13,5 +13,20 @@ export function normalizeError(error: unknown): Error { if (typeof error === 'string') { return new Error(error) } + if (Array.isArray(error)) { + const normalizedErrors = error.map(normalizeError) + const normalizedMessage = `[${normalizedErrors.map(e => e.message).join(', ')}]` + return new AggregateErrorFix(normalizedErrors, normalizedMessage) + } + return new Error(`Unknown error: ${String(error)}`) } + +// This is a temporary patch for AggregateError until it is available in Hermes. +// FIX: Remove this in newer versions of hermes. +class AggregateErrorFix extends Error { + constructor(public errors: Error[], message?: string) { + super(message) + this.name = 'AggregateError' + } +} diff --git a/src/util/translateError.ts b/src/util/translateError.ts index 6a267f2576c..e14d0dd8ccb 100644 --- a/src/util/translateError.ts +++ b/src/util/translateError.ts @@ -30,6 +30,13 @@ export function makeErrorLog(error: unknown): string { * @returns A translated, human-friendly string (in many cases). */ export function translateError(error: unknown): string { + // Translate each individual error: + if (Array.isArray(error)) { + // Join multiple errors together separated by a semi-colon. This should + // also flatten nested arrays because of the recursion. + return error.map(translateError).join('; ') + } + // GUI Error types: if (error instanceof PaymentProtoError) return translatePaymentProtoError(error) if (error instanceof ResolutionError) return translateResolutionError(error) From 679284a4da915145ca1ad80c413ad6a896c9dc66 Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Thu, 24 Oct 2024 18:52:00 -0700 Subject: [PATCH 36/89] Track errors in an AggregateError individually with a common tag --- CHANGELOG.md | 1 + src/util/normalizeError.ts | 2 +- src/util/tracking.ts | 9 ++++++++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 819423765c5..0143d4afbb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - added: Log swap errors to Sentry. - changed: Various strings updated to UK compliance spec +- changed: Track array of errors or AggregateErrors separately with a common tag - changed: Wording in light account persistent notification - fixed: Fix error massaging in trackError - fixed: Normalized error messages for tracking; removing localization from error messages. diff --git a/src/util/normalizeError.ts b/src/util/normalizeError.ts index ff35a848662..d5b1222e646 100644 --- a/src/util/normalizeError.ts +++ b/src/util/normalizeError.ts @@ -24,7 +24,7 @@ export function normalizeError(error: unknown): Error { // This is a temporary patch for AggregateError until it is available in Hermes. // FIX: Remove this in newer versions of hermes. -class AggregateErrorFix extends Error { +export class AggregateErrorFix extends Error { constructor(public errors: Error[], message?: string) { super(message) this.name = 'AggregateError' diff --git a/src/util/tracking.ts b/src/util/tracking.ts index 493b72490cf..371575a077b 100644 --- a/src/util/tracking.ts +++ b/src/util/tracking.ts @@ -10,7 +10,7 @@ import { ExperimentConfig, getExperimentConfig } from '../experimentConfig' import { ThunkAction } from '../types/reduxTypes' import { CryptoAmount } from './CryptoAmount' import { fetchReferral } from './network' -import { normalizeError } from './normalizeError' +import { AggregateErrorFix, normalizeError } from './normalizeError' import { makeErrorLog } from './translateError' import { consify, monthsBetween } from './utils' @@ -160,6 +160,13 @@ export function trackError( ): void { const err = normalizeError(error) + if (err instanceof AggregateErrorFix) { + // Track each error individually using a common event_id: + const aggTag = tag == null ? `AggregateError:${Date.now()}` : tag + err.errors.forEach(e => trackError(e, aggTag, metadata)) + return + } + let hint: { event_id?: string; data?: { [key: string]: any } } | undefined if (tag != null) { hint = { event_id: tag } From 9b6056292dba8360273f0407e4057710d213b1f5 Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Thu, 24 Oct 2024 19:05:12 -0700 Subject: [PATCH 37/89] Fix trackErrors "hint" data The hint data is incorrectly implemented and causing major issues: 1. `event_id` is a hex value and any other value cause the error to not be reported. 2. `data` in the hint is only used locally before sending the event to Sentry. Instead we will use the `scope` API to add the proper event tagging and metadata. We can leverage the `addMetadataToContext` function to consistently process metadata for a Sentry event. --- CHANGELOG.md | 1 + src/components/services/EdgeCoreManager.tsx | 36 +-------------------- src/util/addMetadataToContext.ts | 34 +++++++++++++++++++ src/util/tracking.ts | 32 ++++++++++-------- 4 files changed, 54 insertions(+), 49 deletions(-) create mode 100644 src/util/addMetadataToContext.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 0143d4afbb1..6aa1f0d1c69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - changed: Wording in light account persistent notification - fixed: Fix error massaging in trackError - fixed: Normalized error messages for tracking; removing localization from error messages. +- fixed: Sentry tagging and metadata data wasn't formed properly and caused some loss of tracked errors. - fixed: Use Sentry context for logging metadata in `EdgeCrashEvent` - removed: Bank Wire Transfer Buy for Florida - removed: Paypal Sell for Canada diff --git a/src/components/services/EdgeCoreManager.tsx b/src/components/services/EdgeCoreManager.tsx index 1ea910a4007..7cbcf301133 100644 --- a/src/components/services/EdgeCoreManager.tsx +++ b/src/components/services/EdgeCoreManager.tsx @@ -14,6 +14,7 @@ import { useAsyncEffect } from '../../hooks/useAsyncEffect' import { useHandler } from '../../hooks/useHandler' import { useIsAppForeground } from '../../hooks/useIsAppForeground' import { lstrings } from '../../locales/strings' +import { addMetadataToContext } from '../../util/addMetadataToContext' import { allPlugins } from '../../util/corePlugins' import { fakeUser } from '../../util/fake-user' import { isMaestro } from '../../util/maestro' @@ -85,41 +86,6 @@ const crashReporter: EdgeCrashReporter = { } } -/** - * Recursively adds metadata to a context object. - * - * @param context The context object to which to add metadata - * @param metadata The metadata object to add to the context - * @param prefixKeys optional prefix keys to add to each entry - * @returns void (modifies context object) - */ -function addMetadataToContext(context: Record, metadata: object, prefixKeys: string[] = []): void { - for (const [key, value] of Object.entries(metadata)) { - const allKeys = [...prefixKeys, key] - const fullKey = allKeys.join(' > ') - - // Serialize error objects manually because JSON.stringify doesn't - // include all the error properties: - if (value instanceof Error) { - context[fullKey] = `${value.name}: ${value.message}` - if (value.stack != null) { - // Include the stack trace with indentation: - context[fullKey] += `\n ${value.stack.replace(/\n/g, '\n ')}` - } - continue - } - - // Recurse over objects with added prefix keys: - if (typeof value === 'object' && value !== null) { - addMetadataToContext(context, value, allKeys) - continue - } - - // Default - context[key] = value - } -} - /** * Mounts the edge-core-js WebView, and then mounts the rest of the app * once the core context is ready. diff --git a/src/util/addMetadataToContext.ts b/src/util/addMetadataToContext.ts new file mode 100644 index 00000000000..672387f702d --- /dev/null +++ b/src/util/addMetadataToContext.ts @@ -0,0 +1,34 @@ +/** + * Recursively adds metadata to a context object. + * + * @param context The context object to which to add metadata + * @param metadata The metadata object to add to the context + * @param prefixKeys optional prefix keys to add to each entry + * @returns void (modifies context object) + */ +export function addMetadataToContext(context: Record, metadata: object, prefixKeys: string[] = []): void { + for (const [key, value] of Object.entries(metadata)) { + const allKeys = [...prefixKeys, key] + const fullKey = allKeys.join(' > ') + + // Serialize error objects manually because JSON.stringify doesn't + // include all the error properties: + if (value instanceof Error) { + context[fullKey] = `${value.name}: ${value.message}` + if (value.stack != null) { + // Include the stack trace with indentation: + context[fullKey] += `\n ${value.stack.replace(/\n/g, '\n ')}` + } + continue + } + + // Recurse over objects with added prefix keys: + if (typeof value === 'object' && value !== null) { + addMetadataToContext(context, value, allKeys) + continue + } + + // Default + context[key] = value + } +} diff --git a/src/util/tracking.ts b/src/util/tracking.ts index 371575a077b..8c98b689b8b 100644 --- a/src/util/tracking.ts +++ b/src/util/tracking.ts @@ -1,4 +1,4 @@ -import { captureException } from '@sentry/react-native' +import { captureException, withScope } from '@sentry/react-native' import { TrackingEventName as LoginTrackingEventName, TrackingValues as LoginTrackingValues } from 'edge-login-ui-rn' import PostHog from 'posthog-react-native' import { getBuildNumber, getVersion } from 'react-native-device-info' @@ -8,6 +8,7 @@ import { getFirstOpenInfo } from '../actions/FirstOpenActions' import { ENV } from '../env' import { ExperimentConfig, getExperimentConfig } from '../experimentConfig' import { ThunkAction } from '../types/reduxTypes' +import { addMetadataToContext } from './addMetadataToContext' import { CryptoAmount } from './CryptoAmount' import { fetchReferral } from './network' import { AggregateErrorFix, normalizeError } from './normalizeError' @@ -153,7 +154,7 @@ if (ENV.POSTHOG_INIT) { */ export function trackError( error: unknown, - tag?: string, + nameTag?: string, metadata?: { [key: string]: any } @@ -161,21 +162,24 @@ export function trackError( const err = normalizeError(error) if (err instanceof AggregateErrorFix) { - // Track each error individually using a common event_id: - const aggTag = tag == null ? `AggregateError:${Date.now()}` : tag - err.errors.forEach(e => trackError(e, aggTag, metadata)) + // Track each error individually using a common group tag: + const aggregateId = Date.now().toString(16) + withScope(scope => { + scope.setTag('aggregate.id', aggregateId) + err.errors.forEach(e => trackError(e, nameTag, metadata)) + }) return } - let hint: { event_id?: string; data?: { [key: string]: any } } | undefined - if (tag != null) { - hint = { event_id: tag } - } - if (metadata != null) { - hint = { data: metadata } - } - - captureException(err, hint) + captureException(err, scope => { + scope.setTag('event.name', nameTag) + if (metadata) { + const context: Record = {} + addMetadataToContext(context, metadata) + scope.setContext('Metadata', context) + } + return scope + }) } /** From 27bd48d51a8418c051e53d384dc6c9475f753eee Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Thu, 24 Oct 2024 19:11:20 -0700 Subject: [PATCH 38/89] Track fiat plugin `getSupportedAssets` errors --- CHANGELOG.md | 1 + src/plugins/gui/amountQuotePlugin.ts | 6 ++---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6aa1f0d1c69..7cbcea65482 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased - added: Log swap errors to Sentry. +- added: Tracking for unexpected fiat provider errors. - changed: Various strings updated to UK compliance spec - changed: Track array of errors or AggregateErrors separately with a common tag - changed: Wording in light account persistent notification diff --git a/src/plugins/gui/amountQuotePlugin.ts b/src/plugins/gui/amountQuotePlugin.ts index 160ea5cbb4f..7389bf75d27 100644 --- a/src/plugins/gui/amountQuotePlugin.ts +++ b/src/plugins/gui/amountQuotePlugin.ts @@ -168,9 +168,7 @@ export const amountQuoteFiatPlugin: FiatPluginFactory = async (params: FiatPlugi assetPromises.push(provider.getSupportedAssets({ direction, regionCode, paymentTypes })) } - const ps = fuzzyTimeout(assetPromises, 5000).catch(e => { - console.error('amountQuotePlugin error fetching assets: ', String(e)) - }) + const ps = fuzzyTimeout(assetPromises, 5000) const assetArray = await showUi.showToastSpinner(lstrings.fiat_plugin_fetching_assets, ps) @@ -180,7 +178,7 @@ export const amountQuoteFiatPlugin: FiatPluginFactory = async (params: FiatPlugi const allowedAssets: EdgeAsset[] = [] const allowedFiats: { [fiatCurrencyCode: string]: boolean } = {} const allowedProviders: { [providerId: string]: boolean } = {} - for (const assetMap of assetArray ?? []) { + for (const assetMap of assetArray) { if (assetMap == null) continue allowedProviders[assetMap.providerId] = true requireAmountCrypto[assetMap.providerId] = false From d6d94a32d3182aab2532485538257bfed223c108 Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Thu, 24 Oct 2024 19:14:11 -0700 Subject: [PATCH 39/89] Fix `FiatProviderError` error message The name was the message, and the message didn't exist. Now we'll create a useful message from the info parameter. --- CHANGELOG.md | 1 + src/plugins/gui/fiatProviderTypes.ts | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7cbcea65482..9858b0632ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - added: Log swap errors to Sentry. - added: Tracking for unexpected fiat provider errors. +- changed: `FiatProviderError` messages now include `FiatProviderQuoteError` info. - changed: Various strings updated to UK compliance spec - changed: Track array of errors or AggregateErrors separately with a common tag - changed: Wording in light account persistent notification diff --git a/src/plugins/gui/fiatProviderTypes.ts b/src/plugins/gui/fiatProviderTypes.ts index 25eae6c1745..a97f4909134 100644 --- a/src/plugins/gui/fiatProviderTypes.ts +++ b/src/plugins/gui/fiatProviderTypes.ts @@ -44,7 +44,22 @@ export class FiatProviderError extends Error { readonly quoteError: FiatProviderQuoteError constructor(info: FiatProviderQuoteError) { - super('FiatProviderError') + function getMessage(): string { + switch (info.errorType) { + case 'overLimit': + return `Over limit: ${info.errorAmount} ${info.displayCurrencyCode}` + case 'underLimit': + return `Under limit: ${info.errorAmount} ${info.displayCurrencyCode}` + case 'regionRestricted': + return `Region restricted: ${info.displayCurrencyCode}` + case 'assetUnsupported': + return `Asset unsupported` + case 'paymentUnsupported': + return 'Payment unsupported' + } + } + super(getMessage()) + this.name = 'FiatProviderError' this.quoteError = info } } From e0e70032bffd6444e5d77a840513ff7374789e9c Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Mon, 28 Oct 2024 10:48:27 -0700 Subject: [PATCH 40/89] Upgrade edge-core-js@^2.20.0 --- ios/Podfile.lock | 4 ++-- package.json | 2 +- yarn.lock | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 373b4a1f1f0..41ca220e495 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -15,7 +15,7 @@ PODS: - disklet (0.5.2): - React - DoubleConversion (1.1.6) - - edge-core-js (2.19.1): + - edge-core-js (2.20.0): - React-Core - edge-currency-accountbased (4.26.1): - React-Core @@ -1080,7 +1080,7 @@ SPEC CHECKSUMS: CNIOWindows: 3047f2d8165848a3936a0a755fee27c6b5ee479b disklet: e7ed3e673ccad9d175a1675f9f3589ffbf69a5fd DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54 - edge-core-js: 09206fe34acb8f4eebafe61e8c2eda6cbe81ba69 + edge-core-js: 6ffab9921d98affca1bf7c2f681c7ce0e29aad8a edge-currency-accountbased: ba759ec765d487d86e5f7e26a38e14edfc1d64dd edge-currency-plugins: 38eaf53c2d9fdbdd30ade3ad09fd698f428f208f edge-exchange-plugins: 5037e196e652d1dca42afacd86b2395bd0d7f298 diff --git a/package.json b/package.json index 407b2c80e0b..b45c5972541 100644 --- a/package.json +++ b/package.json @@ -97,7 +97,7 @@ "deepmerge": "^4.3.1", "detect-bundler": "^1.1.0", "disklet": "^0.5.2", - "edge-core-js": "^2.19.1", + "edge-core-js": "^2.20.0", "edge-currency-accountbased": "^4.26.1", "edge-currency-monero": "^1.3.1", "edge-currency-plugins": "^3.4.3", diff --git a/yarn.lock b/yarn.lock index 0b318c73dbb..d2d020585ce 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9107,10 +9107,10 @@ ed25519@0.0.4: bindings "^1.2.1" nan "^2.0.9" -edge-core-js@^2.19.1: - version "2.19.1" - resolved "https://registry.yarnpkg.com/edge-core-js/-/edge-core-js-2.19.1.tgz#84e6c67e38fe72e53217538af40f41666713f500" - integrity sha512-1BiyRHw+k/0DM+f44Lvs0RUN5DXfoWpazXYGWuuDL1Uz7dbV+JsAUm1LqYD8+fYBnwFjeb+5RLSPrKPIFUMB4g== +edge-core-js@^2.20.0: + version "2.20.0" + resolved "https://registry.yarnpkg.com/edge-core-js/-/edge-core-js-2.20.0.tgz#0812c3c2434798ca2f500898dddf17b332a2ecc9" + integrity sha512-/NqrRqMAzUmPD5YIadfge3065DCyFl9uUXKp4Nb4wG1qCHcjNFldnCMMMLI00nre7dr94OOYh79f6msLAfyAfQ== dependencies: aes-js "^3.1.0" base-x "^4.0.0" From 0676f288e5307559cf0210eceddf98e822180c24 Mon Sep 17 00:00:00 2001 From: William Swanson Date: Mon, 28 Oct 2024 14:39:55 -0700 Subject: [PATCH 41/89] Add explicit gas limit for Kiln staking --- CHANGELOG.md | 1 + .../generic/policyAdapters/EthereumKilnAdaptor.ts | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9858b0632ba..7d2f7c2b3dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - added: Log swap errors to Sentry. - added: Tracking for unexpected fiat provider errors. - changed: `FiatProviderError` messages now include `FiatProviderQuoteError` info. +- changed: Add explicit gas limit for Kiln staking. - changed: Various strings updated to UK compliance spec - changed: Track array of errors or AggregateErrors separately with a common tag - changed: Wording in light account persistent notification diff --git a/src/plugins/stake-plugins/generic/policyAdapters/EthereumKilnAdaptor.ts b/src/plugins/stake-plugins/generic/policyAdapters/EthereumKilnAdaptor.ts index 6e72d21a408..0d77af98193 100644 --- a/src/plugins/stake-plugins/generic/policyAdapters/EthereumKilnAdaptor.ts +++ b/src/plugins/stake-plugins/generic/policyAdapters/EthereumKilnAdaptor.ts @@ -117,7 +117,13 @@ export const makeEthereumKilnAdapter = (policyConfig: StakePolicyConfig { const { maxFeePerGas, maxPriorityFeePerGas, nextNonce, walletSigner } = await workflowUtils(wallet) - const tx = await integrationContract.populateTransaction.stake({ value: requestNativeAmount, maxFeePerGas, maxPriorityFeePerGas, nonce: nextNonce() }) + const tx = await integrationContract.populateTransaction.stake({ + gasLimit: '250000', // Typically uses 190000-225000 gas + maxFeePerGas, + maxPriorityFeePerGas, + nonce: nextNonce(), + value: requestNativeAmount + }) const allocations: QuoteAllocation[] = [ { From 90328fbbb787d0801e2ddb0c6f94f5a1dbabb736 Mon Sep 17 00:00:00 2001 From: William Swanson Date: Mon, 28 Oct 2024 14:58:00 -0700 Subject: [PATCH 42/89] Fix Kiln contract address comparison --- CHANGELOG.md | 1 + .../stake-plugins/generic/policyAdapters/EthereumKilnAdaptor.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d2f7c2b3dd..c0f1b4c1a18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - changed: Various strings updated to UK compliance spec - changed: Track array of errors or AggregateErrors separately with a common tag - changed: Wording in light account persistent notification +- fixed: Correctly report ETH Kiln balances - fixed: Fix error massaging in trackError - fixed: Normalized error messages for tracking; removing localization from error messages. - fixed: Sentry tagging and metadata data wasn't formed properly and caused some loss of tracked errors. diff --git a/src/plugins/stake-plugins/generic/policyAdapters/EthereumKilnAdaptor.ts b/src/plugins/stake-plugins/generic/policyAdapters/EthereumKilnAdaptor.ts index 0d77af98193..f7b789be889 100644 --- a/src/plugins/stake-plugins/generic/policyAdapters/EthereumKilnAdaptor.ts +++ b/src/plugins/stake-plugins/generic/policyAdapters/EthereumKilnAdaptor.ts @@ -172,7 +172,7 @@ export const makeEthereumKilnAdapter = (policyConfig: StakePolicyConfig position.integration_address === contractAddress) + const position = allPositions.find(position => position.integration_address.toLowerCase() === contractAddress.toLowerCase()) // After fully unstaking, users are left with a single wei of the liquidity token. We should ignore this. const positionBalance = position?.balance ?? '0' From 9b6f816d91b999192014cd3d1551101bd78ac993 Mon Sep 17 00:00:00 2001 From: Jon Tzeng Date: Mon, 14 Oct 2024 16:48:47 -0700 Subject: [PATCH 43/89] Add new navigation types --- src/types/routerTypes.tsx | 44 +++++++++- src/util/fake/fakeSceneProps.ts | 149 +++++++++++++++++++++++++++++++- 2 files changed, 190 insertions(+), 3 deletions(-) diff --git a/src/types/routerTypes.tsx b/src/types/routerTypes.tsx index 60f208df801..89b1e03cb6f 100644 --- a/src/types/routerTypes.tsx +++ b/src/types/routerTypes.tsx @@ -1,6 +1,9 @@ +import { BottomTabScreenProps } from '@react-navigation/bottom-tabs' import type { NavigatorScreenParams } from '@react-navigation/core' import * as NavigationCore from '@react-navigation/core' -import type { StackActionHelpers } from '@react-navigation/native' +import { DrawerScreenProps } from '@react-navigation/drawer' +import type { CompositeScreenProps, StackActionHelpers } from '@react-navigation/native' +import type { StackScreenProps } from '@react-navigation/stack' import type { ChangeMiningFeeParams } from '../components/scenes/ChangeMiningFeeScene' import type { CoinRankingDetailsParams } from '../components/scenes/CoinRankingDetailsScene' @@ -86,6 +89,7 @@ export type WalletsTabParamList = {} & { transactionDetails: TransactionDetailsParams } +// TODO: Split this up into distinct param lists? export type BuyTabParamList = {} & { pluginListBuy: GuiPluginListParams | undefined pluginListSell: GuiPluginListParams | undefined @@ -100,6 +104,8 @@ export type BuyTabParamList = {} & { rewardsCardWelcome: RewardsCardWelcomeParams } +export type SellTabParamList = {} & BuyTabParamList + export type SwapTabParamList = {} & { swapCreate: SwapCreateParams | undefined swapConfirmation: SwapConfirmationParams @@ -110,7 +116,7 @@ export type EdgeTabsParamList = {} & { home: undefined walletsTab: NavigatorScreenParams | undefined buyTab: NavigatorScreenParams | undefined - sellTab: NavigatorScreenParams | undefined + sellTab: NavigatorScreenParams | undefined swapTab: NavigatorScreenParams | undefined extraTab: undefined devTab: undefined @@ -214,6 +220,40 @@ export type RootParamList = {} & { login: LoginParams } +// Upgraded types to comply with the navigation upgrade requirements +export type RootSceneProps = StackScreenProps + +export type DrawerSceneProps = CompositeScreenProps< + DrawerScreenProps, + RootSceneProps +> + +export type EdgeAppSceneProps = CompositeScreenProps< + StackScreenProps, + DrawerSceneProps +> + +export type EdgeTabsSceneProps = CompositeScreenProps< + BottomTabScreenProps, + EdgeAppSceneProps +> +export type BuyTabSceneProps = CompositeScreenProps< + StackScreenProps, + EdgeTabsSceneProps +> +export type SellTabSceneProps = CompositeScreenProps< + StackScreenProps, + EdgeTabsSceneProps +> +export type SwapTabSceneProps = CompositeScreenProps< + StackScreenProps, + EdgeTabsSceneProps +> +export type WalletsTabSceneProps = CompositeScreenProps< + StackScreenProps, + EdgeTabsSceneProps +> + // ------------------------------------------------------------------------- // Legacy types // diff --git a/src/util/fake/fakeSceneProps.ts b/src/util/fake/fakeSceneProps.ts index f1778fde6cd..9978f26de54 100644 --- a/src/util/fake/fakeSceneProps.ts +++ b/src/util/fake/fakeSceneProps.ts @@ -1,4 +1,22 @@ -import { AppParamList, EdgeSceneProps, NavigationProp } from '../../types/routerTypes' +import { + AppParamList, + BuyTabParamList, + BuyTabSceneProps, + DrawerParamList, + DrawerSceneProps, + EdgeAppSceneProps, + EdgeAppStackParamList, + EdgeSceneProps, + NavigationProp, + RootParamList, + RootSceneProps, + SellTabParamList, + SellTabSceneProps, + SwapTabParamList, + SwapTabSceneProps, + WalletsTabParamList, + WalletsTabSceneProps +} from '../../types/routerTypes' export const fakeNavigation: NavigationProp = { addListener() { @@ -52,3 +70,132 @@ export function fakeSceneProps(name: Name, para } as any } } + +export const fakeCompositeNavigation = { + navigate: jest.fn(), + goBack: jest.fn(), + pop: jest.fn(), + push: jest.fn(), + replace: jest.fn(), + addListener: jest.fn() + // Include other methods as needed +} as any // HACK: Type assertion due to incomplete implementation + +export function fakeRootSceneProps(name: Name, params: RootParamList[Name]): RootSceneProps { + const navigation: RootSceneProps['navigation'] = fakeCompositeNavigation + + const route: RootSceneProps['route'] = { + key: `${String(name)}-0`, + name: name as Extract, + params + } + + const sceneProps: RootSceneProps = { + navigation, + route + } + + return sceneProps +} + +export function fakeDrawerSceneProps(name: Name, params: DrawerParamList[Name]): DrawerSceneProps { + const navigation: DrawerSceneProps['navigation'] = fakeCompositeNavigation + + const route: DrawerSceneProps['route'] = { + key: `${String(name)}-0`, + name: name as Extract, + params + } + + const sceneProps: DrawerSceneProps = { + navigation, + route + } + + return sceneProps +} + +export function fakeEdgeAppSceneProps(name: Name, params: EdgeAppStackParamList[Name]): EdgeAppSceneProps { + const navigation: EdgeAppSceneProps['navigation'] = fakeCompositeNavigation + + const route: EdgeAppSceneProps['route'] = { + key: `${String(name)}-0`, + name: name as Extract, + params + } + + const sceneProps: EdgeAppSceneProps = { + navigation, + route + } + + return sceneProps +} + +export function fakBuyTabSceneProps(name: Name, params: BuyTabParamList[Name]): BuyTabSceneProps { + const navigation: BuyTabSceneProps['navigation'] = fakeCompositeNavigation + + const route: BuyTabSceneProps['route'] = { + key: `${String(name)}-0`, + name: name as Extract, + params + } + + const sceneProps: BuyTabSceneProps = { + navigation, + route + } + + return sceneProps +} + +export function fakSellTabSceneProps(name: Name, params: SellTabParamList[Name]): SellTabSceneProps { + const navigation: SellTabSceneProps['navigation'] = fakeCompositeNavigation + + const route: SellTabSceneProps['route'] = { + key: `${String(name)}-0`, + name: name as Extract, + params + } + + const sceneProps: SellTabSceneProps = { + navigation, + route + } + + return sceneProps +} + +export function fakeSwapTabSceneProps(name: Name, params: SwapTabParamList[Name]): SwapTabSceneProps { + const navigation: SwapTabSceneProps['navigation'] = fakeCompositeNavigation + + const route: SwapTabSceneProps['route'] = { + key: `${String(name)}-0`, + name: name as Extract, + params + } + + const sceneProps: SwapTabSceneProps = { + navigation, + route + } + + return sceneProps +} + +export function fakeWalletsTabSceneProps(name: Name, params: WalletsTabParamList[Name]): WalletsTabSceneProps { + const navigation: WalletsTabSceneProps['navigation'] = fakeCompositeNavigation + + const route: WalletsTabSceneProps['route'] = { + key: `${String(name)}-0`, + name: name as Extract, + params + } + + const sceneProps: WalletsTabSceneProps = { + navigation, + route + } + + return sceneProps +} From 79bb101489afc4a7939e84eede9e3bffaba55fd7 Mon Sep 17 00:00:00 2001 From: Jon Tzeng Date: Mon, 14 Oct 2024 17:17:34 -0700 Subject: [PATCH 44/89] Update `RootParamList` scenes --- src/components/Main.tsx | 6 +++--- src/components/scenes/GettingStartedScene.tsx | 4 ++-- src/components/scenes/LoginScene.tsx | 14 +++++++------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/components/Main.tsx b/src/components/Main.tsx index f66d139da24..58316790381 100644 --- a/src/components/Main.tsx +++ b/src/components/Main.tsx @@ -25,10 +25,10 @@ import { BuyTabParamList, DrawerParamList, EdgeAppStackParamList, - EdgeSceneProps, EdgeTabsParamList, NavigationBase, RootParamList, + RootSceneProps, SwapTabParamList, WalletsTabParamList } from '../types/routerTypes' @@ -874,14 +874,14 @@ export const Main = () => { - {(props: EdgeSceneProps<'gettingStarted'>) => { + {(props: RootSceneProps<'gettingStarted'>) => { if (navigation == null) setTimeout(() => setNavigation(props.navigation as NavigationBase), 0) return }} - {(props: EdgeSceneProps<'login'>) => { + {(props: RootSceneProps<'login'>) => { if (navigation == null) setTimeout(() => setNavigation(props.navigation as NavigationBase), 0) return }} diff --git a/src/components/scenes/GettingStartedScene.tsx b/src/components/scenes/GettingStartedScene.tsx index 9329c35e619..768b17a2ead 100644 --- a/src/components/scenes/GettingStartedScene.tsx +++ b/src/components/scenes/GettingStartedScene.tsx @@ -24,7 +24,7 @@ import { ExperimentConfig, UspSigninCtaType } from '../../experimentConfig' import { useHandler } from '../../hooks/useHandler' import { lstrings } from '../../locales/strings' import { useDispatch, useSelector } from '../../types/reactRedux' -import { EdgeSceneProps } from '../../types/routerTypes' +import { RootSceneProps } from '../../types/routerTypes' import { ImageProp } from '../../types/Theme' import { parseMarkedText } from '../../util/parseMarkedText' import { logEvent } from '../../util/tracking' @@ -43,7 +43,7 @@ export interface GettingStartedParams { experimentConfig: ExperimentConfig // TODO: Create a new provider instead to serve the experimentConfig globally } -interface Props extends EdgeSceneProps<'gettingStarted'> {} +interface Props extends RootSceneProps<'gettingStarted'> {} interface SectionData { image: ImageProp diff --git a/src/components/scenes/LoginScene.tsx b/src/components/scenes/LoginScene.tsx index 10c28759328..4dd1860e18f 100644 --- a/src/components/scenes/LoginScene.tsx +++ b/src/components/scenes/LoginScene.tsx @@ -16,7 +16,7 @@ import { lstrings } from '../../locales/strings' import { performanceMarkersFromLoginUiPerfEvents } from '../../perf' import { config } from '../../theme/appConfig' import { useDispatch, useSelector } from '../../types/reactRedux' -import { EdgeSceneProps } from '../../types/routerTypes' +import { NavigationBase, RootSceneProps } from '../../types/routerTypes' import { logEvent } from '../../util/tracking' import { DotsBackground } from '../common/DotsBackground' import { showHelpModal } from '../modals/HelpModal' @@ -34,7 +34,7 @@ export interface LoginParams { // @ts-expect-error global.ReactNativeBlurView = BlurView -interface Props extends EdgeSceneProps<'login'> {} +interface Props extends RootSceneProps<'login'> {} let firstRun = true @@ -66,7 +66,7 @@ export function LoginScene(props: Props) { context .loginWithPIN(YOLO_USERNAME, YOLO_PIN) .then(async account => { - await dispatch(initializeAccount(navigation, account)) + await dispatch(initializeAccount(navigation as NavigationBase, account)) }) .catch(error => showError(error)) } @@ -74,7 +74,7 @@ export function LoginScene(props: Props) { context .loginWithPassword(YOLO_USERNAME, YOLO_PASSWORD) .then(async account => { - await dispatch(initializeAccount(navigation, account)) + await dispatch(initializeAccount(navigation as NavigationBase, account)) }) .catch(error => showError(error)) } @@ -84,7 +84,7 @@ export function LoginScene(props: Props) { context .loginWithPIN(context.localUsers[0].loginId, YOLO_PIN, { useLoginId: true }) .then(async account => { - await dispatch(initializeAccount(navigation, account)) + await dispatch(initializeAccount(navigation as NavigationBase, account)) }) .catch(error => showError(error)) } @@ -98,7 +98,7 @@ export function LoginScene(props: Props) { () => ({ callback() { Keyboard.dismiss() - showHelpModal(navigation).catch(err => showDevError(err)) + showHelpModal(navigation as NavigationBase).catch(err => showDevError(err)) }, text: lstrings.string_help }), @@ -112,7 +112,7 @@ export function LoginScene(props: Props) { : undefined const handleLogin = useHandler(async (account: EdgeAccount) => { - await dispatch(initializeAccount(navigation, account)) + await dispatch(initializeAccount(navigation as NavigationBase, account)) }) const handleSendLogs = useHandler(() => { From 6b35e0140ae513ac5e5540d4cb308a585e1f0f10 Mon Sep 17 00:00:00 2001 From: Jon Tzeng Date: Mon, 14 Oct 2024 17:17:46 -0700 Subject: [PATCH 45/89] Update `DrawerNavigation` scenes --- src/components/themed/SideMenu.tsx | 32 ++++++++++++++++-------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/components/themed/SideMenu.tsx b/src/components/themed/SideMenu.tsx index 08dfd3f5f78..d1d9d5d7169 100644 --- a/src/components/themed/SideMenu.tsx +++ b/src/components/themed/SideMenu.tsx @@ -26,7 +26,7 @@ import { lstrings } from '../../locales/strings' import { getDefaultFiat } from '../../selectors/SettingsSelectors' import { config } from '../../theme/appConfig' import { useDispatch, useSelector } from '../../types/reactRedux' -import { NavigationBase } from '../../types/routerTypes' +import { DrawerSceneProps, NavigationBase } from '../../types/routerTypes' import { parseDeepLink } from '../../util/DeepLinkParser' import { getDisplayUsername } from '../../util/utils' import { IONIA_SUPPORTED_FIATS } from '../cards/VisaCardCard' @@ -45,8 +45,10 @@ const footerGradientStart = { x: 0, y: 0 } const footerGradientEnd = { x: 0, y: 0.75 } export function SideMenu(props: DrawerContentComponentProps) { - // Fix this type assertion (seems like DrawerContentComponentProps works just fine as NavigationBase?) - const navigation: NavigationBase = props.navigation as any + // Fix this type assertion (seems like DrawerContentComponentProps works just + // fine as NavigationBase?) + const { navigation } = props as any as DrawerSceneProps<'edgeAppStack'> + const navigationBase = props.navigation as any as NavigationBase const isDrawerOpen = useDrawerStatus() === 'open' const dispatch = useDispatch() @@ -90,7 +92,7 @@ export function SideMenu(props: DrawerContentComponentProps) { const handleDeleteAccount = (userInfo: EdgeUserInfo) => () => { if (userInfo.username == null) { - showBackupModal({ navigation, forgetLoginId: userInfo.loginId }) + showBackupModal({ navigation: navigationBase, forgetLoginId: userInfo.loginId }) } else { Airship.show<'ok' | 'cancel' | undefined>(bridge => ( () => { dispatch( - logoutRequest(navigation, { + logoutRequest(navigationBase, { nextLoginId: userInfo.loginId }) ).catch(err => showError(err)) } const handleBorrow = () => { - navigation.navigate('loanDashboard') + navigation.navigate('edgeAppStack', { screen: 'loanDashboard' }) navigation.dispatch(DrawerActions.closeDrawer()) } @@ -141,14 +143,14 @@ export function SideMenu(props: DrawerContentComponentProps) { .then(async (result: string | undefined) => { if (result) { const deepLink = parseDeepLink(result) - await dispatch(launchDeepLink(navigation, deepLink)) + await dispatch(launchDeepLink(navigationBase, deepLink)) } }) .catch(err => showError(err)) } const handleMarketsPress = () => { - navigation.navigate('coinRanking') + navigation.navigate('edgeAppStack', { screen: 'coinRanking' }) } const handleShareApp = () => { @@ -216,7 +218,7 @@ export function SideMenu(props: DrawerContentComponentProps) { }> = [ { pressHandler: () => { - navigation.navigate('fioAddressList') + navigation.navigate('edgeAppStack', { screen: 'fioAddressList' }) navigation.dispatch(DrawerActions.closeDrawer()) }, iconName: 'control-panel-fio-names', @@ -224,7 +226,7 @@ export function SideMenu(props: DrawerContentComponentProps) { }, { pressHandler: () => { - navigation.navigate('fioRequestList') + navigation.navigate('edgeAppStack', { screen: 'fioRequestList' }) navigation.dispatch(DrawerActions.closeDrawer()) }, iconName: 'control-panel-fio', @@ -232,7 +234,7 @@ export function SideMenu(props: DrawerContentComponentProps) { }, { pressHandler: () => { - navigation.navigate('wcConnections', {}) + navigation.navigate('edgeAppStack', { screen: 'wcConnections', params: {} }) navigation.dispatch(DrawerActions.closeDrawer()) }, iconName: 'control-panel-wallet-connect', @@ -252,14 +254,14 @@ export function SideMenu(props: DrawerContentComponentProps) { { pressHandler: handleShareApp, iconName: 'control-panel-share', title: lstrings.string_share + ' ' + config.appName }, { pressHandler: () => { - navigation.navigate('settingsOverview') + navigation.navigate('edgeAppStack', { screen: 'settingsOverview' }) navigation.dispatch(DrawerActions.closeDrawer()) }, iconName: 'control-panel-settings', title: lstrings.settings_title }, { - pressHandler: async () => await dispatch(logoutRequest(navigation)), + pressHandler: async () => await dispatch(logoutRequest(navigationBase)), iconName: 'control-panel-logout', title: lstrings.settings_button_logout }, @@ -273,7 +275,7 @@ export function SideMenu(props: DrawerContentComponentProps) { if (ENV.ENABLE_VISA_PROGRAM && IONIA_SUPPORTED_FIATS.includes(defaultFiat)) { rowDatas.unshift({ pressHandler: () => { - dispatch(executePluginAction(navigation, 'rewardscard', 'sell')).catch(err => showError(err)) + dispatch(executePluginAction(navigationBase, 'rewardscard', 'sell')).catch(err => showError(err)) navigation.dispatch(DrawerActions.closeDrawer()) }, iconNameFontAwesome: 'credit-card', @@ -283,7 +285,7 @@ export function SideMenu(props: DrawerContentComponentProps) { const footerTopColor = theme.modal + '00' // Add full transparency to the modal color const footerBottomColor = theme.modal - const rootNavigation = getRootNavigation(navigation) + const rootNavigation = getRootNavigation(navigationBase) /// ---- Renderers ---- From a8af86daecf1f711b61b091c0ea372b1f9502a78 Mon Sep 17 00:00:00 2001 From: Jon Tzeng Date: Tue, 15 Oct 2024 15:51:24 -0700 Subject: [PATCH 46/89] Update `EdgeTabs` scenes --- src/components/scenes/DevTestScene.tsx | 17 +++++++++++------ src/components/scenes/HomeScene.tsx | 18 +++++++++++------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/components/scenes/DevTestScene.tsx b/src/components/scenes/DevTestScene.tsx index eb044f26ef1..901abb4444c 100644 --- a/src/components/scenes/DevTestScene.tsx +++ b/src/components/scenes/DevTestScene.tsx @@ -15,7 +15,7 @@ import { lstrings } from '../../locales/strings' import { HomeAddress } from '../../types/FormTypes' import { useState } from '../../types/reactHooks' import { useDispatch } from '../../types/reactRedux' -import { EdgeSceneProps, NavigationBase } from '../../types/routerTypes' +import { EdgeTabsSceneProps, NavigationBase } from '../../types/routerTypes' import { parseDeepLink } from '../../util/DeepLinkParser' import { consify } from '../../util/utils' import { ButtonsView } from '../buttons/ButtonsView' @@ -47,7 +47,7 @@ import { SceneHeader } from '../themed/SceneHeader' import { SceneHeaderUi4 } from '../themed/SceneHeaderUi4' import { SimpleTextInput } from '../themed/SimpleTextInput' -interface Props extends EdgeSceneProps<'devTab'> {} +interface Props extends EdgeTabsSceneProps<'devTab'> {} export function DevTestScene(props: Props) { const { navigation } = props @@ -235,7 +235,7 @@ export function DevTestScene(props: Props) { label="PasswordReminderModal" marginRem={0.25} onPress={async () => { - await Airship.show(bridge => ) + await Airship.show(bridge => ) }} /> { if (coreWallet == null) return await Airship.show(bridge => ( - + )) }} /> @@ -263,7 +268,7 @@ export function DevTestScene(props: Props) { label="BackupModal (Long, Original with image)" marginRem={0.25} onPress={async () => { - showBackupModal({ navigation, forgetLoginId: 'test' }) + showBackupModal({ navigation: navigation as NavigationBase, forgetLoginId: 'test' }) }} /> { const parsed = parseDeepLink(deepLinkInputValue) console.debug('parsed deeplink: ', parsed) - dispatch(launchDeepLink(navigation, parsed)).catch(e => showError(e)) + dispatch(launchDeepLink(navigation as NavigationBase, parsed)).catch(e => showError(e)) }} label="Activate DeepLink" type="primary" diff --git a/src/components/scenes/HomeScene.tsx b/src/components/scenes/HomeScene.tsx index 9b5e9fc3bb3..328f5f544b3 100644 --- a/src/components/scenes/HomeScene.tsx +++ b/src/components/scenes/HomeScene.tsx @@ -13,7 +13,7 @@ import { useHandler } from '../../hooks/useHandler' import { lstrings } from '../../locales/strings' import { useSceneScrollHandler } from '../../state/SceneScrollState' import { config } from '../../theme/appConfig' -import { EdgeSceneProps } from '../../types/routerTypes' +import { EdgeTabsSceneProps, NavigationBase } from '../../types/routerTypes' import { getUi4ImageUri } from '../../util/CdnUris' import { infoServerData } from '../../util/network' import { BalanceCard } from '../cards/BalanceCard' @@ -29,7 +29,7 @@ import { SectionView } from '../layout/SectionView' import { AccountSyncBar } from '../progress-indicators/AccountSyncBar' import { cacheStyles, Theme, useTheme } from '../services/ThemeContext' -interface Props extends EdgeSceneProps<'home'> {} +interface Props extends EdgeTabsSceneProps<'home'> {} const TEMP_PADDING_REM = 0.5 // To be built-in to SceneWrapper when fully UI4 @@ -88,7 +88,7 @@ export const HomeScene = (props: Props) => { navigation.navigate('swapTab') }) const handleViewAssetsPress = useHandler(() => { - navigation.navigate('walletsTab', { screen: 'walletList' }) + navigation.navigate('edgeTabs', { screen: 'walletsTab', params: { screen: 'walletList' } }) }) const handleScroll = useSceneScrollHandler() @@ -138,14 +138,14 @@ export const HomeScene = (props: Props) => { <> - + {/* Animation inside PromoCardsUi4 component */} @@ -210,9 +210,13 @@ export const HomeScene = (props: Props) => { )} <> - navigation.navigate('coinRanking')} /> + navigation.navigate('edgeAppStack', { screen: 'coinRanking' })} + /> - + {videoPosts == null || videoPosts.length === 0 ? null : ( From 207c2e232d6f42c04ec789ea3e8cade1d451d8bb Mon Sep 17 00:00:00 2001 From: Jon Tzeng Date: Tue, 15 Oct 2024 15:57:18 -0700 Subject: [PATCH 47/89] Update `SwapTab` scenes --- src/__tests__/scenes/SwapConfirmationScene.test.tsx | 4 ++-- src/__tests__/scenes/SwapCreateScene.test.tsx | 4 ++-- src/components/scenes/SwapConfirmationScene.tsx | 4 ++-- src/components/scenes/SwapCreateScene.tsx | 6 +++--- src/components/scenes/SwapProcessingScene.tsx | 6 +++--- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/__tests__/scenes/SwapConfirmationScene.test.tsx b/src/__tests__/scenes/SwapConfirmationScene.test.tsx index 3da8c014c26..4bd64124584 100644 --- a/src/__tests__/scenes/SwapConfirmationScene.test.tsx +++ b/src/__tests__/scenes/SwapConfirmationScene.test.tsx @@ -20,7 +20,7 @@ import { makeFakePlugin } from '../../util/fake/fakeCurrencyPlugin' import { ethCurrencyInfo } from '../../util/fake/fakeEthInfo' import { FakeProviders, FakeState } from '../../util/fake/FakeProviders' import { fakeRootState } from '../../util/fake/fakeRootState' -import { fakeSceneProps } from '../../util/fake/fakeSceneProps' +import { fakeSwapTabSceneProps } from '../../util/fake/fakeSceneProps' import fakeUser from '../../util/fake/fakeUserDump.json' jest.useRealTimers() @@ -134,7 +134,7 @@ describe('SwapConfirmationScene', () => { const renderer = TestRenderer.create( undefined diff --git a/src/__tests__/scenes/SwapCreateScene.test.tsx b/src/__tests__/scenes/SwapCreateScene.test.tsx index 3981c385f7b..e07e4693e64 100644 --- a/src/__tests__/scenes/SwapCreateScene.test.tsx +++ b/src/__tests__/scenes/SwapCreateScene.test.tsx @@ -5,7 +5,7 @@ import TestRenderer from 'react-test-renderer' import { SwapCreateScene } from '../../components/scenes/SwapCreateScene' import { FakeProviders, FakeState } from '../../util/fake/FakeProviders' import { fakeRootState } from '../../util/fake/fakeRootState' -import { fakeSceneProps } from '../../util/fake/fakeSceneProps' +import { fakeSwapTabSceneProps } from '../../util/fake/fakeSceneProps' describe('SwapCreateScene', () => { it('should render with loading props', () => { @@ -13,7 +13,7 @@ describe('SwapCreateScene', () => { const renderer = TestRenderer.create( - + ) diff --git a/src/components/scenes/SwapConfirmationScene.tsx b/src/components/scenes/SwapConfirmationScene.tsx index 8f34b223aa3..24e033fd995 100644 --- a/src/components/scenes/SwapConfirmationScene.tsx +++ b/src/components/scenes/SwapConfirmationScene.tsx @@ -17,7 +17,7 @@ import { getExchangeDenom, selectDisplayDenom } from '../../selectors/Denominati import { convertCurrency } from '../../selectors/WalletSelectors' import { useDispatch, useSelector } from '../../types/reactRedux' import { ThunkAction } from '../../types/reduxTypes' -import { EdgeSceneProps } from '../../types/routerTypes' +import { SwapTabSceneProps } from '../../types/routerTypes' import { GuiSwapInfo } from '../../types/types' import { getSwapPluginIconUri } from '../../util/CdnUris' import { CryptoAmount } from '../../util/CryptoAmount' @@ -50,7 +50,7 @@ export interface SwapConfirmationParams { onApprove: () => void } -interface Props extends EdgeSceneProps<'swapConfirmation'> {} +interface Props extends SwapTabSceneProps<'swapConfirmation'> {} interface Section { title: { title: string; rightTitle: string } diff --git a/src/components/scenes/SwapCreateScene.tsx b/src/components/scenes/SwapCreateScene.tsx index 4d30a9fe90c..ab8b9cb5c94 100644 --- a/src/components/scenes/SwapCreateScene.tsx +++ b/src/components/scenes/SwapCreateScene.tsx @@ -22,7 +22,7 @@ import { useHandler } from '../../hooks/useHandler' import { useWatch } from '../../hooks/useWatch' import { lstrings } from '../../locales/strings' import { useDispatch, useSelector } from '../../types/reactRedux' -import { EdgeSceneProps } from '../../types/routerTypes' +import { NavigationBase, SwapTabSceneProps } from '../../types/routerTypes' import { getCurrencyCode } from '../../util/CurrencyInfoHelpers' import { getWalletName } from '../../util/CurrencyWalletHelpers' import { zeroString } from '../../util/utils' @@ -59,7 +59,7 @@ export interface SwapErrorDisplayInfo { error: unknown } -interface Props extends EdgeSceneProps<'swapCreate'> {} +interface Props extends SwapTabSceneProps<'swapCreate'> {} export const SwapCreateScene = (props: Props) => { const { navigation, route } = props @@ -223,7 +223,7 @@ export const SwapCreateScene = (props: Props) => { const result = await Airship.show(bridge => ( void } -interface Props extends EdgeSceneProps<'swapProcessing'> {} +interface Props extends SwapTabSceneProps<'swapProcessing'> {} export function SwapProcessingScene(props: Props) { const { route, navigation } = props @@ -84,7 +84,7 @@ export function SwapProcessingScene(props: Props) { return ( - navigation={navigation} + navigation={navigation as NavigationBase} doWork={doWork} onCancel={onCancel} onDone={onDone} From 3897aef4520e7ace63a9496b25512e2f7f60f58e Mon Sep 17 00:00:00 2001 From: Jon Tzeng Date: Tue, 15 Oct 2024 16:30:32 -0700 Subject: [PATCH 48/89] Update `WalletsTab` Scenes --- .../components/TransactionListTop.test.tsx | 6 +-- src/actions/WalletListMenuActions.tsx | 4 +- src/components/cards/VisaCardCard.tsx | 6 +-- src/components/modals/WalletListMenuModal.tsx | 4 +- .../scenes/TransactionListScene.tsx | 12 +++--- src/components/scenes/WalletListScene.tsx | 6 +-- src/components/themed/TransactionListTop.tsx | 12 +++--- src/components/themed/WalletListSwipeable.tsx | 7 ++-- .../themed/WalletListSwipeableCurrencyRow.tsx | 12 +++--- .../themed/WalletListSwipeableLoadingRow.tsx | 4 +- src/hooks/useAsyncNavigation.tsx | 37 +++++++++++-------- 11 files changed, 59 insertions(+), 51 deletions(-) diff --git a/src/__tests__/components/TransactionListTop.test.tsx b/src/__tests__/components/TransactionListTop.test.tsx index 2168d8f91f8..08cfdb34f6c 100644 --- a/src/__tests__/components/TransactionListTop.test.tsx +++ b/src/__tests__/components/TransactionListTop.test.tsx @@ -6,7 +6,7 @@ import TestRenderer from 'react-test-renderer' import { TransactionListTop } from '../../components/themed/TransactionListTop' import { ENV } from '../../env' import { FakeProviders, FakeState } from '../../util/fake/FakeProviders' -import { fakeNavigation } from '../../util/fake/fakeSceneProps' +import { fakeCompositeNavigation } from '../../util/fake/fakeSceneProps' describe('TransactionListTop', () => { const currencyInfo: EdgeCurrencyInfo = { @@ -66,7 +66,7 @@ describe('TransactionListTop', () => { { | NavigationProp<'transactionList'>, + navigation: WalletsTabSceneProps<'walletList' | 'transactionList'>['navigation'], walletId: string, option: WalletListMenuKey, tokenId: EdgeTokenId, diff --git a/src/components/cards/VisaCardCard.tsx b/src/components/cards/VisaCardCard.tsx index 515b9ca3fb4..301b38c83a4 100644 --- a/src/components/cards/VisaCardCard.tsx +++ b/src/components/cards/VisaCardCard.tsx @@ -9,7 +9,7 @@ import { useHandler } from '../../hooks/useHandler' import { lstrings } from '../../locales/strings' import { getDefaultFiat } from '../../selectors/SettingsSelectors' import { useDispatch, useSelector } from '../../types/reactRedux' -import { NavigationProp } from '../../types/routerTypes' +import { NavigationBase, WalletsTabSceneProps } from '../../types/routerTypes' import { getCurrencyIconUris } from '../../util/CdnUris' import { logEvent } from '../../util/tracking' import { EdgeTouchableOpacity } from '../common/EdgeTouchableOpacity' @@ -25,7 +25,7 @@ export const ioniaPluginIds = Object.keys(SPECIAL_CURRENCY_INFO).filter(pluginId interface Props { wallet: EdgeCurrencyWallet tokenId: EdgeTokenId - navigation: NavigationProp<'transactionList'> + navigation: WalletsTabSceneProps<'transactionList'>['navigation'] } export const VisaCardCard = (props: Props) => { @@ -36,7 +36,7 @@ export const VisaCardCard = (props: Props) => { const handlePress = useHandler(() => { dispatch(logEvent('Visa_Card_Launch')) - dispatch(executePluginAction(navigation, 'rewardscard', 'sell')).catch(err => showError(err)) + dispatch(executePluginAction(navigation as NavigationBase, 'rewardscard', 'sell')).catch(err => showError(err)) }) const defaultFiat = useSelector(state => getDefaultFiat(state)) diff --git a/src/components/modals/WalletListMenuModal.tsx b/src/components/modals/WalletListMenuModal.tsx index 9ece3fd0abf..301d63ace93 100644 --- a/src/components/modals/WalletListMenuModal.tsx +++ b/src/components/modals/WalletListMenuModal.tsx @@ -11,7 +11,7 @@ import { useHandler } from '../../hooks/useHandler' import { useWatch } from '../../hooks/useWatch' import { lstrings } from '../../locales/strings' import { useDispatch, useSelector } from '../../types/reactRedux' -import { NavigationProp } from '../../types/routerTypes' +import { WalletsTabSceneProps } from '../../types/routerTypes' import { getCurrencyCode, isKeysOnlyPlugin } from '../../util/CurrencyInfoHelpers' import { getWalletName } from '../../util/CurrencyWalletHelpers' import { EdgeTouchableOpacity } from '../common/EdgeTouchableOpacity' @@ -28,7 +28,7 @@ interface Option { interface Props { bridge: AirshipBridge - navigation: NavigationProp<'walletList'> | NavigationProp<'transactionList'> + navigation: WalletsTabSceneProps<'walletList' | 'transactionList'>['navigation'] // Wallet identity: tokenId: EdgeTokenId diff --git a/src/components/scenes/TransactionListScene.tsx b/src/components/scenes/TransactionListScene.tsx index 4643098b6ba..034f3bf8c2b 100644 --- a/src/components/scenes/TransactionListScene.tsx +++ b/src/components/scenes/TransactionListScene.tsx @@ -17,7 +17,7 @@ import { getExchangeDenomByCurrencyCode } from '../../selectors/DenominationSele import { FooterRender } from '../../state/SceneFooterState' import { useSceneScrollHandler } from '../../state/SceneScrollState' import { useDispatch, useSelector } from '../../types/reactRedux' -import { EdgeSceneProps } from '../../types/routerTypes' +import { NavigationBase, WalletsTabSceneProps } from '../../types/routerTypes' import { infoServerData } from '../../util/network' import { calculateSpamThreshold, darkenHexColor, unixToLocaleDateTime, zeroString } from '../../util/utils' import { InfoCardCarousel } from '../cards/InfoCardCarousel' @@ -41,7 +41,7 @@ export interface TransactionListParams { } type ListItem = EdgeTransaction | string | null -interface Props extends EdgeSceneProps<'transactionList'> { +interface Props extends WalletsTabSceneProps<'transactionList'> { wallet: EdgeCurrencyWallet } @@ -150,7 +150,7 @@ function TransactionListComponent(props: Props) { async () => { if (unactivatedTokenIds.length > 0) { if (unactivatedTokenIds.some(unactivatedTokenId => unactivatedTokenId === tokenId)) { - await dispatch(activateWalletTokens(navigation, wallet, [tokenId])) + await dispatch(activateWalletTokens(navigation as NavigationBase, wallet, [tokenId])) } } }, @@ -218,7 +218,7 @@ function TransactionListComponent(props: Props) { @@ -232,7 +232,7 @@ function TransactionListComponent(props: Props) { } else if (isSearching) { return } else { - return + return } }, [isTransactionListUnsupported, navigation, isSearching, tokenId, wallet]) @@ -251,7 +251,7 @@ function TransactionListComponent(props: Props) { } return ( - + ) }) diff --git a/src/components/scenes/WalletListScene.tsx b/src/components/scenes/WalletListScene.tsx index 5af75810981..417a98b8d89 100644 --- a/src/components/scenes/WalletListScene.tsx +++ b/src/components/scenes/WalletListScene.tsx @@ -8,7 +8,7 @@ import { useWatch } from '../../hooks/useWatch' import { lstrings } from '../../locales/strings' import { FooterRender, useSceneFooterState } from '../../state/SceneFooterState' import { useDispatch, useSelector } from '../../types/reactRedux' -import { EdgeSceneProps } from '../../types/routerTypes' +import { NavigationBase, WalletsTabSceneProps } from '../../types/routerTypes' import { EdgeButton } from '../buttons/EdgeButton' import { SceneButtons } from '../buttons/SceneButtons' import { CrossFade } from '../common/CrossFade' @@ -23,7 +23,7 @@ import { WalletListHeader } from '../themed/WalletListHeader' import { WalletListSortable } from '../themed/WalletListSortable' import { WalletListSwipeable } from '../themed/WalletListSwipeable' -interface Props extends EdgeSceneProps<'walletList'> {} +interface Props extends WalletsTabSceneProps<'walletList'> {} export function WalletListScene(props: Props) { const { navigation } = props @@ -94,7 +94,7 @@ export function WalletListScene(props: Props) { // const renderHeader = React.useMemo(() => { - return + return }, [handleSort, navigation, isSearching, sorting]) const renderListFooter = React.useMemo(() => { diff --git a/src/components/themed/TransactionListTop.tsx b/src/components/themed/TransactionListTop.tsx index 24884c1ccce..ef8ae6c391c 100644 --- a/src/components/themed/TransactionListTop.tsx +++ b/src/components/themed/TransactionListTop.tsx @@ -27,7 +27,7 @@ import { getExchangeDenomByCurrencyCode, selectDisplayDenomByCurrencyCode } from import { getExchangeRate } from '../../selectors/WalletSelectors' import { config } from '../../theme/appConfig' import { useDispatch, useSelector } from '../../types/reactRedux' -import { NavigationProp } from '../../types/routerTypes' +import { NavigationBase, WalletsTabSceneProps } from '../../types/routerTypes' import { GuiExchangeRates } from '../../types/types' import { CryptoAmount } from '../../util/CryptoAmount' import { triggerHaptic } from '../../util/haptic' @@ -64,7 +64,7 @@ const SWAP_ASSET_PRIORITY: Array<{ pluginId: string; tokenId: EdgeTokenId }> = [ ] interface OwnProps { - navigation: NavigationProp<'transactionList'> + navigation: WalletsTabSceneProps<'transactionList'>['navigation'] isLightAccount: boolean @@ -214,7 +214,7 @@ export class TransactionListTopComponent extends React.PureComponent )) .then(result => { @@ -602,7 +602,7 @@ export class TransactionListTopComponent extends React.PureComponent )} - {isEmpty || searching ? null : } + {isEmpty || searching ? null : } {isEmpty || searching ? null : ( diff --git a/src/components/themed/WalletListSwipeable.tsx b/src/components/themed/WalletListSwipeable.tsx index a24fc8b30d7..76d9c804b5c 100644 --- a/src/components/themed/WalletListSwipeable.tsx +++ b/src/components/themed/WalletListSwipeable.tsx @@ -11,7 +11,7 @@ import { useHandler } from '../../hooks/useHandler' import { filterWalletCreateItemListBySearchText, getCreateWalletList, WalletCreateItem } from '../../selectors/getCreateWalletList' import { useSceneScrollHandler } from '../../state/SceneScrollState' import { useDispatch, useSelector } from '../../types/reactRedux' -import { NavigationProp } from '../../types/routerTypes' +import { NavigationBase, WalletsTabSceneProps } from '../../types/routerTypes' import { FlatListItem } from '../../types/types' import { EdgeAnim, MAX_LIST_ITEMS_ANIM } from '../common/EdgeAnim' import { InsetStyle } from '../common/SceneWrapper' @@ -24,7 +24,8 @@ import { WalletListSwipeableLoadingRow } from './WalletListSwipeableLoadingRow' interface Props { footer?: React.ComponentType<{}> | React.ReactElement header?: React.ComponentType<{}> | React.ReactElement - navigation: NavigationProp<'walletList'> + navigation: WalletsTabSceneProps<'walletList'>['navigation'] + searching: boolean searchText: string insetStyle: InsetStyle @@ -68,7 +69,7 @@ function WalletListSwipeableComponent(props: Props) { const handleCreateWallet = useHandler(async (walletId: string, tokenId: EdgeTokenId) => { const wallet = account.currencyWallets[walletId] const { countryCode } = await getFirstOpenInfo() - dispatch(selectWalletToken({ navigation, walletId, tokenId })) + dispatch(selectWalletToken({ navigation: navigation as NavigationBase, walletId, tokenId })) .then( activationNotRequired => activationNotRequired && diff --git a/src/components/themed/WalletListSwipeableCurrencyRow.tsx b/src/components/themed/WalletListSwipeableCurrencyRow.tsx index 73c45d5d7eb..b88abd6d19f 100644 --- a/src/components/themed/WalletListSwipeableCurrencyRow.tsx +++ b/src/components/themed/WalletListSwipeableCurrencyRow.tsx @@ -9,7 +9,7 @@ import { selectWalletToken } from '../../actions/WalletActions' import { Fontello } from '../../assets/vector/index' import { useHandler } from '../../hooks/useHandler' import { useDispatch, useSelector } from '../../types/reactRedux' -import { NavigationProp } from '../../types/routerTypes' +import { NavigationBase, WalletsTabSceneProps } from '../../types/routerTypes' import { EdgeTouchableOpacity } from '../common/EdgeTouchableOpacity' import { SwipeableRowIcon } from '../icons/SwipeableRowIcon' import { WalletListMenuModal } from '../modals/WalletListMenuModal' @@ -19,7 +19,7 @@ import { SwipableRowRef, SwipeableRow } from '../themed/SwipeableRow' import { WalletListCurrencyRow } from '../themed/WalletListCurrencyRow' interface Props { - navigation: NavigationProp<'walletList'> + navigation: WalletsTabSceneProps<'walletList'>['navigation'] token?: EdgeToken tokenId: EdgeTokenId @@ -55,8 +55,8 @@ function WalletListSwipeableCurrencyRowComponent(props: Props) { const handleRequest = useHandler(() => { closeRow() - if (!checkAndShowLightBackupModal(account, navigation)) { - dispatch(selectWalletToken({ navigation, walletId: wallet.id, tokenId, alwaysActivate: true })) + if (!checkAndShowLightBackupModal(account, navigation as NavigationBase)) { + dispatch(selectWalletToken({ navigation: navigation as NavigationBase, walletId: wallet.id, tokenId, alwaysActivate: true })) .then(activated => { if (activated) { navigation.navigate('request', { tokenId, walletId: wallet.id }) @@ -68,7 +68,7 @@ function WalletListSwipeableCurrencyRowComponent(props: Props) { const handleSelect = useHandler(() => { closeRow() - dispatch(selectWalletToken({ navigation, walletId: wallet.id, tokenId, alwaysActivate: true })) + dispatch(selectWalletToken({ navigation: navigation as NavigationBase, walletId: wallet.id, tokenId, alwaysActivate: true })) .then(async activated => { const { countryCode } = await getFirstOpenInfo() if (activated) { @@ -80,7 +80,7 @@ function WalletListSwipeableCurrencyRowComponent(props: Props) { const handleSend = useHandler(() => { closeRow() - dispatch(selectWalletToken({ navigation, walletId: wallet.id, tokenId, alwaysActivate: true })) + dispatch(selectWalletToken({ navigation: navigation as NavigationBase, walletId: wallet.id, tokenId, alwaysActivate: true })) .then(activated => { if (activated) { navigation.navigate('send2', { diff --git a/src/components/themed/WalletListSwipeableLoadingRow.tsx b/src/components/themed/WalletListSwipeableLoadingRow.tsx index bee2b436df1..540cf8f8e89 100644 --- a/src/components/themed/WalletListSwipeableLoadingRow.tsx +++ b/src/components/themed/WalletListSwipeableLoadingRow.tsx @@ -5,7 +5,7 @@ import { SharedValue } from 'react-native-reanimated' import { useHandler } from '../../hooks/useHandler' import { useWatch } from '../../hooks/useWatch' import { useSelector } from '../../types/reactRedux' -import { NavigationProp } from '../../types/routerTypes' +import { WalletsTabSceneProps } from '../../types/routerTypes' import { EdgeTouchableOpacity } from '../common/EdgeTouchableOpacity' import { SwipeableRowIcon } from '../icons/SwipeableRowIcon' import { WalletListMenuModal } from '../modals/WalletListMenuModal' @@ -16,7 +16,7 @@ import { WalletListErrorRow } from './WalletListErrorRow' import { WalletListLoadingRow } from './WalletListLoadingRow' interface Props { - navigation: NavigationProp<'walletList'> + navigation: WalletsTabSceneProps<'walletList'>['navigation'] walletId: string } diff --git a/src/hooks/useAsyncNavigation.tsx b/src/hooks/useAsyncNavigation.tsx index 4939b46888d..0d69e637d0c 100644 --- a/src/hooks/useAsyncNavigation.tsx +++ b/src/hooks/useAsyncNavigation.tsx @@ -1,16 +1,12 @@ -import { NavigationProp as NavigationCoreProp, StackActionHelpers } from '@react-navigation/native' +import { NavigationProp } from '@react-navigation/native' import * as React from 'react' -import { AppParamList, NavigationBase } from '../types/routerTypes' - /** - * Use this in place of NavigationProp/NavigationBase methods to prevent + * Use this in place of navigation methods to prevent * multiple navigations from rapid tapping. Navigation calls are only executed * if there isn't already another one in flight */ -export const useAsyncNavigation = ( - navigation: NavigationBase | (NavigationCoreProp & StackActionHelpers) -): NavigationBase & (NavigationCoreProp & StackActionHelpers) => { +export const useAsyncNavigation =