From 4c4856f0549ad4010f6a802c5e4cdc218b716150 Mon Sep 17 00:00:00 2001 From: Marcelo Salhab Brogliato Date: Mon, 29 Jan 2024 14:12:52 -0600 Subject: [PATCH 01/23] chore(release): Add release script (#403) chore(ci): update to run on release branch --- .github/workflows/main.yml | 6 ++++-- .gitignore | 3 +++ pre_release.sh | 28 ++++++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) create mode 100755 pre_release.sh diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2224aaf79..a71681774 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -3,13 +3,15 @@ on: push: branches: - master - - dev + - release + - release-candidate tags: - v* pull_request: branches: - - dev - master + - release + - release-candidate jobs: test: runs-on: 'ubuntu-latest' diff --git a/.gitignore b/.gitignore index dbf2c6c04..c1798d7a2 100644 --- a/.gitignore +++ b/.gitignore @@ -82,3 +82,6 @@ ios/HathorMobile.xcodeproj/project.xcworkspace/ .metro-health-check* # testing /coverage + +# Env file used by pre_release.sh. +/env diff --git a/pre_release.sh b/pre_release.sh new file mode 100755 index 000000000..5c22eb3fd --- /dev/null +++ b/pre_release.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# +# Script to release new versions for iOS and Android. After running it, you still +# have to build the project on XCode and Android Studio. +# +# This script expects a file `./env` with exported envvars. +# + +set -e # Exit on any command failure. +set -u # Exit on unset variables. +set -v # Verbose mode for better debugging + +rm -rf node_modules/ + +rm -f ./android/app/google-services.json +rm -f ./android/app/GoogleService-Info.plist +rm -f ./notifications/GoogleService-Info.plist + +npm ci + +(cd ios/ && pod install) + +make i18n + +source ./env +mkdir -p ./notifications +aws s3 cp ${HATHOR_WALLET_MOBILE_S3_PATH}/google-services.json ./android/app +aws s3 cp ${HATHOR_WALLET_MOBILE_S3_PATH}/GoogleService-Info.plist ./notifications/ From cdbe0cd9f81bc8417b1be7a6ef3775122bd90ea7 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Thu, 1 Feb 2024 01:16:50 +0000 Subject: [PATCH 02/23] fix: push notification registration latency on PinScreen (#423) * fix: PinScreen dismiss response * This fix doesn't solve the problem of long latency, however it decreases latency perception for users. * fix: exits script after dispatch failure effect * fix: replace BLOCKED to DENIED enum value * fix: push notification authorization logic * chore: extract delay from race --- src/sagas/pushNotification.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/sagas/pushNotification.js b/src/sagas/pushNotification.js index ff71565d0..98f6a8518 100644 --- a/src/sagas/pushNotification.js +++ b/src/sagas/pushNotification.js @@ -15,6 +15,7 @@ import { take, takeLatest, debounce, + delay, } from 'redux-saga/effects'; import messaging from '@react-native-firebase/messaging'; import notifee from '@notifee/react-native'; @@ -358,6 +359,11 @@ export function* loadWallet() { const network = new Network(networkSettings.network); const pin = yield call(showPinScreenForResult, dispatch); + + // Delay 300ms to resume script execution in the next loop. + // This solution liberates the PinScreen to dismiss. + yield delay(300); + const seed = yield STORE.getWalletWords(pin); walletService = new HathorWalletServiceWallet({ seed, @@ -372,6 +378,7 @@ export function* loadWallet() { }); } catch (error) { yield put(pushLoadWalletFailed({ error })); + return; } } else { walletService = yield select((state) => state.wallet); @@ -386,19 +393,19 @@ export function* loadWallet() { */ const hasPostNotificationAuthorization = async () => { let status = await messaging().hasPermission(); - if (status === messaging.AuthorizationStatus.BLOCKED) { + if (status === messaging.AuthorizationStatus.DENIED) { log.debug('Device not authorized to send push notification and blocked to ask permission.'); return false; } - if (status === messaging.AuthorizationStatus.NOT_DETERMINED) { - log.debug('Device clean. Asking for permission to send push notification.'); + if (status === messaging.AuthorizationStatus.NOT_DETERMINED + || status === messaging.AuthorizationStatus.EPHEMERAL + || status === messaging.AuthorizationStatus.PROVISIONAL) { + log.debug('Asking for permission to send push notification.'); status = await messaging().requestPermission(); } - log.debug('Device permission status: ', status); - return status === messaging.AuthorizationStatus.AUTHORIZED - || status === messaging.AuthorizationStatus.PROVISIONAL; + return status === messaging.AuthorizationStatus.AUTHORIZED; }; /** From 0e2c6375a520d2cd7546829384a27a091bc88d60 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Fri, 2 Feb 2024 01:04:03 +0000 Subject: [PATCH 03/23] fix: opt-in modal render time (#424) * fix: push notification opt-in modal render time * chore: improve code readability * lint: comply with rules and improve comment * fix: typo * fix: add delay --- src/components/AskForPushNotification.js | 19 +++++++++++----- src/sagas/pushNotification.js | 28 +++++++++++++++++++----- 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/src/components/AskForPushNotification.js b/src/components/AskForPushNotification.js index 1b88e7982..f1a468d89 100644 --- a/src/components/AskForPushNotification.js +++ b/src/components/AskForPushNotification.js @@ -13,16 +13,23 @@ export default function AskForPushNotification(props) { dispatch(pushDismissOptInQuestion()); }; - if (!showOptIn) { - return null; - } - return ( + const putDismissOptInQuestion = () => { + dispatch(pushDismissOptInQuestion()); + }; + + const pushNotificationOptInModal = () => ( onEnablePushNotifications()} - onDismiss={() => dispatch(pushDismissOptInQuestion())} + onAction={onEnablePushNotifications} + onDismiss={putDismissOptInQuestion} /> ); + + if (showOptIn) { + return pushNotificationOptInModal(); + } + + return null; } diff --git a/src/sagas/pushNotification.js b/src/sagas/pushNotification.js index 98f6a8518..4c5e9b478 100644 --- a/src/sagas/pushNotification.js +++ b/src/sagas/pushNotification.js @@ -15,6 +15,7 @@ import { take, takeLatest, debounce, + spawn, delay, } from 'redux-saga/effects'; import messaging from '@react-native-firebase/messaging'; @@ -315,12 +316,27 @@ export function* init() { } } - // If the user has not been asked yet, we should ask him if he wants to enable push notifications - // We should appear only once, so we should save the fact that the user has dismissed the question - const optInDismissed = STORE.getItem(pushNotificationKey.optInDismissed); - if (optInDismissed === null || !optInDismissed) { - yield put(pushAskOptInQuestion()); - } + // Gives users the option to opt-in the Push Notification + // after its initialization, but only opens the opt-in + // modal after wallet become ready. + // Spwan creates a detached thread from this current thread. + yield spawn(function* handleOptIn() { + const { ready } = yield race({ + ready: take(types.WALLET_STATE_READY), + // Do nothing with error, only terminates the thread. + error: take(types.WALLET_STATE_ERROR), + }); + + if (ready) { + // Ask user for push notification opt-in if it has not + // been asked previously. It appears only once because + // we persist the dismiss action on store. + const optInDismissed = STORE.getItem(pushNotificationKey.optInDismissed); + if (optInDismissed === null || !optInDismissed) { + yield put(pushAskOptInQuestion()); + } + } + }); } /** From efc7ec3a8af680f51517ff92a4ece6cda898f828 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Abadesso?= Date: Thu, 8 Feb 2024 12:34:16 -0300 Subject: [PATCH 04/23] refactor: feature toggle using hathor unleash client (#421) * refactor: feature toggle using hathor unleash client * feat: handling feature updates * refactor: using FetchTogglesStatus enum * chore: added @hathor/unleash-client as a dependency --- package-lock.json | 9 +++ package.json | 1 + src/sagas/featureToggle.js | 129 +++++++++---------------------------- 3 files changed, 42 insertions(+), 97 deletions(-) diff --git a/package-lock.json b/package-lock.json index 78a9c79ff..c78b02124 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@fortawesome/free-regular-svg-icons": "6.4.0", "@fortawesome/free-solid-svg-icons": "6.4.0", "@fortawesome/react-native-fontawesome": "0.2.7", + "@hathor/unleash-client": "0.1.0", "@hathor/wallet-lib": "1.0.1", "@notifee/react-native": "5.7.0", "@react-native-async-storage/async-storage": "1.19.0", @@ -2540,6 +2541,14 @@ "@hapi/hoek": "^9.0.0" } }, + "node_modules/@hathor/unleash-client": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@hathor/unleash-client/-/unleash-client-0.1.0.tgz", + "integrity": "sha512-SR1JBQkegKMLNhU5yWYjHcZVC9EZ9kkDz/X5a2RHZsr+dhMic1oriqin3S8jjvIhmjn/uBZFlvzaTm7ll7h3mw==", + "engines": { + "node": ">=18" + } + }, "node_modules/@hathor/wallet-lib": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@hathor/wallet-lib/-/wallet-lib-1.0.1.tgz", diff --git a/package.json b/package.json index 019c133e5..a68ad985e 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "@fortawesome/free-regular-svg-icons": "6.4.0", "@fortawesome/free-solid-svg-icons": "6.4.0", "@fortawesome/react-native-fontawesome": "0.2.7", + "@hathor/unleash-client": "0.1.0", "@hathor/wallet-lib": "1.0.1", "@notifee/react-native": "5.7.0", "@react-native-async-storage/async-storage": "1.19.0", diff --git a/src/sagas/featureToggle.js b/src/sagas/featureToggle.js index 688887d8b..9300bf984 100644 --- a/src/sagas/featureToggle.js +++ b/src/sagas/featureToggle.js @@ -7,29 +7,25 @@ import { Platform } from 'react-native'; import VersionNumber from 'react-native-version-number'; -import { UnleashClient, EVENTS as UnleashEvents } from 'unleash-proxy-client'; +import UnleashClient, { FetchTogglesStatus } from '@hathor/unleash-client'; import { get } from 'lodash'; import { - takeEvery, all, call, delay, put, - cancelled, select, - race, - take, fork, spawn, + takeEvery, } from 'redux-saga/effects'; -import { eventChannel } from 'redux-saga'; import { getUniqueId } from 'react-native-device-info'; import { + types, setUnleashClient, setFeatureToggles, featureToggleInitialized, - types, } from '../actions'; import { UNLEASH_URL, @@ -40,7 +36,6 @@ import { } from '../constants'; import { disableFeaturesIfNeeded } from './helpers'; -const CONNECT_TIMEOUT = 10000; const MAX_RETRIES = 5; export function* handleInitFailed(currentRetry) { @@ -71,9 +66,10 @@ export function* fetchTogglesRoutine() { const unleashClient = yield select((state) => state.unleashClient); try { - // This call always make unleash to emit the event 'UPDATE', - // which by its turn triggers the action 'FEATURE_TOGGLE_UPDATE' - yield call(() => unleashClient.fetchToggles()); + const state = yield call(() => unleashClient.fetchToggles()); + if (state === FetchTogglesStatus.Updated) { + yield put({ type: types.FEATURE_TOGGLE_UPDATE }); + } } catch (e) { // No need to do anything here as it will try again automatically in // UNLEASH_POLLING_INTERVAL. Just prevent it from crashing the saga. @@ -82,15 +78,19 @@ export function* fetchTogglesRoutine() { } } -export function* monitorFeatureFlags(currentRetry = 0) { - const unleashClient = new UnleashClient({ - url: UNLEASH_URL, - clientKey: UNLEASH_CLIENT_KEY, - refreshInterval: -1, - disableRefresh: true, // Disable it, we will handle it ourselves - appName: `wallet-mobile-${Platform.OS}`, - }); +export function* handleToggleUpdate() { + console.log('Handling feature toggle update'); + const unleashClient = yield select((state) => state.unleashClient); + const networkSettings = yield select((state) => state.networkSettings); + const toggles = unleashClient.getToggles(); + const featureToggles = disableFeaturesIfNeeded(networkSettings, mapFeatureToggles(toggles)); + + yield put(setFeatureToggles(featureToggles)); + yield put({ type: types.FEATURE_TOGGLE_UPDATED }); +} + +export function* monitorFeatureFlags(currentRetry = 0) { const { appVersion } = VersionNumber; const options = { @@ -102,40 +102,30 @@ export function* monitorFeatureFlags(currentRetry = 0) { }, }; + const unleashClient = new UnleashClient({ + url: UNLEASH_URL, + clientKey: UNLEASH_CLIENT_KEY, + refreshInterval: -1, + disableRefresh: true, // Disable it, we will handle it ourselves + appName: `wallet-mobile-${Platform.OS}`, + context: options, + }); + try { - yield call(() => unleashClient.updateContext(options)); yield put(setUnleashClient(unleashClient)); - // Listeners should be set before unleashClient.start so we don't miss - // updates - yield fork(setupUnleashListeners, unleashClient); - - // Start without awaiting it so we can listen for the - // READY event - unleashClient.start(); - - const { error, timeout } = yield race({ - error: take(types.FEATURE_TOGGLE_ERROR), - success: take(types.FEATURE_TOGGLE_READY), - timeout: delay(CONNECT_TIMEOUT), - }); - - if (error || timeout) { - throw new Error('Error or timeout while connecting to unleash proxy.'); - } + yield call(() => unleashClient.fetchToggles()); // Fork the routine to download toggles. yield fork(fetchTogglesRoutine); - // At this point, unleashClient.start() already fetched the toggles - const featureToggles = mapFeatureToggles(unleashClient.toggles); + // At this point, unleashClient.fetchToggles() already fetched the toggles + // (this will throw if it hasn't) + const featureToggles = mapFeatureToggles(unleashClient.getToggles()); yield put(setFeatureToggles(featureToggles)); yield put(featureToggleInitialized()); } catch (e) { - console.error('Error initializing unleash'); - unleashClient.stop(); - yield put(setUnleashClient(null)); // Wait 500ms before retrying @@ -143,45 +133,6 @@ export function* monitorFeatureFlags(currentRetry = 0) { // Spawn so it's detached from the current thread yield spawn(handleInitFailed, currentRetry); - } finally { - if (yield cancelled()) { - yield call(() => unleashClient.stop()); - } - } -} - -export function* setupUnleashListeners(unleashClient) { - const channel = eventChannel((emitter) => { - const l1 = () => emitter({ type: types.FEATURE_TOGGLE_UPDATE }); - const l2 = () => emitter({ type: types.FEATURE_TOGGLE_READY }); - const l3 = (err) => emitter({ type: types.FEATURE_TOGGLE_ERROR, data: err }); - - unleashClient.on(UnleashEvents.UPDATE, l1); - unleashClient.on(UnleashEvents.READY, l2); - unleashClient.on(UnleashEvents.ERROR, l3); - - return () => { - // XXX: This should be a cleanup but removeListener does not exist - // This will throw an error and it will interfere with other sagas - // Since it works without the cleanup i will leave this method empty - // until have determined the best cleanup approach - }; - }); - - try { - while (true) { - const message = yield take(channel); - - yield put({ - type: message.type, - payload: message.data, - }); - } - } finally { - if (yield cancelled()) { - // When we close the channel, it will remove the event listener - channel.close(); - } } } @@ -198,22 +149,6 @@ function mapFeatureToggles(toggles) { }, {}); } -export function* handleToggleUpdate() { - const unleashClient = yield select((state) => state.unleashClient); - const featureTogglesInitialized = yield select((state) => state.featureTogglesInitialized); - const networkSettings = yield select((state) => state.networkSettings); - - if (!unleashClient || !featureTogglesInitialized) { - return; - } - - const { toggles } = unleashClient; - const featureToggles = disableFeaturesIfNeeded(networkSettings, mapFeatureToggles(toggles)); - - yield put(setFeatureToggles(featureToggles)); - yield put({ type: types.FEATURE_TOGGLE_UPDATED }); -} - export function* saga() { yield all([ fork(monitorFeatureFlags), From 4e3b4106e322e35dce9170d428b0bdc07422a00e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Abadesso?= Date: Thu, 8 Feb 2024 13:18:40 -0300 Subject: [PATCH 05/23] refactor: reset wallet service ignore flag (#426) * refactor: resetting wallet-service ignore flag * chore: updated translations * refactor: rollback modal in load history screen * fix: error modal handler failing because of invalid access data access * refactor: displaying regular retry screen on wallet-service error and reporting thrown exceptions to sentry * refactor: capturing all start wallet sentry instead of only wallet-service * Revert "chore: updated translations" This reverts commit ab7eddf9bddad3cb6188908c425d49eceb9742f8. * chore: updated translations * chore: updated translations * chore: lint * chore: fix locale * refactor: added ttag to missing translation * fix: typo * refactor: use the same key for the wallet service flag --- locale/da/texts.po | 130 +++++++++++++++---------------- locale/pt-br/texts.po | 130 +++++++++++++++---------------- locale/ru-ru/texts.po | 130 +++++++++++++++---------------- locale/texts.pot | 125 ++++++++++++++--------------- src/sagas/errorHandler.js | 10 ++- src/sagas/wallet.js | 45 ++++++++--- src/screens/LoadHistoryScreen.js | 110 ++++++++++++-------------- 7 files changed, 350 insertions(+), 330 deletions(-) diff --git a/locale/da/texts.po b/locale/da/texts.po index 5c5020b49..3cb0dc14e 100644 --- a/locale/da/texts.po +++ b/locale/da/texts.po @@ -107,19 +107,19 @@ msgstr "" msgid "New transaction received" msgstr "Ingen transaktioner" -#: src/screens/About.js:82 +#: src/screens/About.js:83 msgid "ABOUT" msgstr "OM" -#: src/screens/About.js:94 +#: src/screens/About.js:95 msgid "This app is developed by Hathor Labs and is distributed for free." msgstr "Denne app er udviklet af Hathor Labs og distribueres gratis." -#: src/screens/About.js:98 src/screens/InitWallet.js:64 +#: src/screens/About.js:99 src/screens/InitWallet.js:64 msgid "This wallet is connected to the **mainnet**." msgstr "Denne wallet er tilsluttet til ** mainnet **." -#: src/screens/About.js:101 src/screens/InitWallet.js:67 +#: src/screens/About.js:102 src/screens/InitWallet.js:67 msgid "" "A mobile wallet is not the safest place to store your tokens.\n" "So, we advise you to keep only a small amount of tokens here, such as pocket " @@ -129,7 +129,7 @@ msgstr "" "Så vi råder dig til kun at opbevare en lille mængde tokens her, såsom " "lommepenge." -#: src/screens/About.js:106 +#: src/screens/About.js:107 msgid "" "For further information, check out the |link1:Terms of Service| and |link2:" "Privacy Policy|, or our website |link3:https://hathor.network/|." @@ -206,30 +206,30 @@ msgstr "PIN-koder stemmer ikke overens. Prøv igen." msgid "Start the Wallet" msgstr "Start Wallet" -#: src/screens/CreateTokenAmount.js:119 src/screens/CreateTokenConfirm.js:163 +#: src/screens/CreateTokenAmount.js:120 src/screens/CreateTokenConfirm.js:163 #: src/screens/CreateTokenDepositNotice.js:42 src/screens/CreateTokenName.js:43 #: src/screens/CreateTokenSymbol.js:62 msgid "CREATE TOKEN" msgstr "OPRET TOKEN" -#: src/screens/CreateTokenAmount.js:127 +#: src/screens/CreateTokenAmount.js:128 #, javascript-format msgid "Amount of ${ this.name } (${ this.symbol })" msgstr "Antal ${ this.name } (${ this.symbol })" -#: src/screens/CreateTokenAmount.js:139 +#: src/screens/CreateTokenAmount.js:140 msgid "Deposit:" msgstr "Indsæt:" -#: src/screens/CreateTokenAmount.js:143 +#: src/screens/CreateTokenAmount.js:144 #, javascript-format msgid "You have ${ amountAvailableText } HTR available" msgstr "Du har ${ amountAvailableText } HTR tilgængelig" -#: src/screens/CreateTokenAmount.js:148 src/screens/CreateTokenName.js:64 +#: src/screens/CreateTokenAmount.js:149 src/screens/CreateTokenName.js:64 #: src/screens/CreateTokenSymbol.js:84 src/screens/InitWallet.js:220 #: src/screens/InitWallet.js:341 src/screens/SendAddressInput.js:66 -#: src/screens/SendAmountInput.js:179 +#: src/screens/SendAmountInput.js:185 msgid "Next" msgstr "Næste" @@ -425,30 +425,30 @@ msgstr "Ord" msgid "Enter your seed words separated by space" msgstr "Indtast dine seed-ord adskilt med mellemrum" -#: src/screens/LoadHistoryScreen.js:67 +#: src/screens/LoadHistoryScreen.js:51 src/screens/LoadWalletErrorScreen.js:20 +msgid "Try again" +msgstr "" + +#: src/screens/LoadHistoryScreen.js:60 msgid "Loading your transactions" msgstr "Indlæser dine transaktioner" -#: src/screens/LoadHistoryScreen.js:70 +#: src/screens/LoadHistoryScreen.js:63 #, javascript-format -msgid "**${ _this.props.loadedData.transactions } transactions** found" -msgstr "**${ _this.props.loadedData.transactions } transaktioner** fundet" +msgid "**${ loadedData.transactions } transactions** found" +msgstr "**${ props.loadedData.transactions } transaktioner** fundet" -#: src/screens/LoadHistoryScreen.js:73 +#: src/screens/LoadHistoryScreen.js:66 #, javascript-format -msgid "**${ _this.props.loadedData.addresses } addresses** found" -msgstr "**${ _this.props.loadedData.addresses } adresser** fundet" +msgid "**${ loadedData.addresses } addresses** found" +msgstr "**${ props.loadedData.addresses } adresser** fundet" #: src/screens/LoadWalletErrorScreen.js:19 msgid "There's been an error connecting to the server." msgstr "" -#: src/screens/LoadWalletErrorScreen.js:20 -msgid "Try again" -msgstr "" - #: src/screens/LoadWalletErrorScreen.js:21 src/screens/PinScreen.js:268 -#: src/screens/Settings.js:151 +#: src/screens/Settings.js:154 msgid "Reset wallet" msgstr "Nulstil wallet" @@ -530,7 +530,7 @@ msgstr "Lås Hathor-wallet op" msgid "Cancel" msgstr "Annuller" -#: src/screens/PushNotification.js:58 src/screens/Settings.js:125 +#: src/screens/PushNotification.js:58 src/screens/Settings.js:128 msgid "Push Notification" msgstr "" @@ -559,7 +559,7 @@ msgstr "" msgid "Payment Request" msgstr "Betalingsanmodning" -#: src/screens/Receive.js:106 +#: src/screens/Receive.js:107 msgid "RECEIVE" msgstr "MODTAG" @@ -657,18 +657,18 @@ msgstr "SEND" msgid "Address to send" msgstr "Adresse, der skal sendes til" -#: src/screens/SendAmountInput.js:105 +#: src/screens/SendAmountInput.js:110 msgid "Insufficient funds" msgstr "Utilstrækkelige midler" -#: src/screens/SendAmountInput.js:136 src/screens/SendConfirmScreen.js:125 +#: src/screens/SendAmountInput.js:141 src/screens/SendConfirmScreen.js:125 #, javascript-format msgid "${ amountAndToken } available" msgid_plural "${ amountAndToken } available" msgstr[0] "${ amountAndToken } tilgængelig" -msgstr[1] "${ amountAndToken } tilgængelig\n" +msgstr[1] "${ amountAndToken } tilgængelig" -#: src/screens/SendAmountInput.js:148 src/screens/SendConfirmScreen.js:133 +#: src/screens/SendAmountInput.js:154 src/screens/SendConfirmScreen.js:133 msgid "SEND ${ tokenNameUpperCase }" msgstr "SEND ${ tokenNameUpperCase }" @@ -693,7 +693,7 @@ msgstr "Din overførsel af **${ _this.amountAndToken }** er bekræftet" msgid "Address" msgstr "Adresse" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:258 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:264 #: src/screens/SendConfirmScreen.js:168 msgid "Send" msgstr "Send" @@ -716,43 +716,43 @@ msgstr "Du har ikke den anmodede token [${ tokenLabel }]" msgid "Scan the QR code" msgstr "Scan QR-koden" -#: src/screens/Settings.js:94 +#: src/screens/Settings.js:97 msgid "You are connected to" msgstr "Du er tilsluttet til" -#: src/screens/Settings.js:102 +#: src/screens/Settings.js:105 msgid "General Settings" msgstr "" -#: src/screens/Settings.js:106 +#: src/screens/Settings.js:109 msgid "Connected to" msgstr "Forbundet til" -#: src/screens/Settings.js:119 +#: src/screens/Settings.js:122 msgid "Security" msgstr "Sikkerhed" -#: src/screens/Settings.js:132 +#: src/screens/Settings.js:135 msgid "Create a new token" msgstr "Opret en ny token" -#: src/screens/Settings.js:139 +#: src/screens/Settings.js:142 msgid "Register a token" msgstr "Registrer en token" -#: src/screens/Settings.js:155 +#: src/screens/Settings.js:158 msgid "About" msgstr "Om" -#: src/screens/Settings.js:162 +#: src/screens/Settings.js:165 msgid "Unique app identifier" msgstr "" -#: src/screens/Settings.js:174 +#: src/screens/Settings.js:179 msgid "Developer Settings" msgstr "" -#: src/screens/Settings.js:176 +#: src/screens/Settings.js:181 msgid "Network Settings" msgstr "" @@ -831,70 +831,70 @@ msgstr "" msgid "Manual" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:17 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:18 msgid "Custom Network Settings" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:18 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:19 msgid "" "Any token outside mainnet network bear no value. Only change if you know " "what you are doing." msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:19 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:20 #: src/screens/NetworkSettings/helper.js:4 msgid "Updating custom network settings..." msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:20 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:21 #: src/screens/NetworkSettings/helper.js:5 msgid "Network settings successfully customized." msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:21 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:22 #: src/screens/NetworkSettings/helper.js:6 msgid "" "There was an error while customizing network settings. Please try again " "later." msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:40 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:41 msgid "nodeUrl is required." msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:44 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:45 msgid "explorerUrl is required." msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:48 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:49 msgid "explorerServiceUrl is required." msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:52 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:53 msgid "txMiningServiceUrl is required." msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:202 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:205 msgid "Node URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:211 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:214 msgid "Explorer URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:220 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:223 msgid "Explorer Service URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:229 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:232 msgid "Transaction Mining Service URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:238 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:243 msgid "Wallet Service URL (optional)" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:247 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:251 msgid "Wallet Service WS URL (optional)" msgstr "" @@ -917,45 +917,45 @@ msgid "" "this can potentially make you susceptible to fraudulent schemes." msgstr "" -#: src/sagas/networkSettings.js:87 +#: src/sagas/networkSettings.js:88 msgid "Custom Network Settings cannot be empty." msgstr "" -#: src/sagas/networkSettings.js:94 +#: src/sagas/networkSettings.js:95 msgid "explorerUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:101 +#: src/sagas/networkSettings.js:102 msgid "explorerServiceUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:108 +#: src/sagas/networkSettings.js:109 msgid "txMiningServiceUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:115 +#: src/sagas/networkSettings.js:116 msgid "nodeUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:122 +#: src/sagas/networkSettings.js:123 msgid "walletServiceUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:129 +#: src/sagas/networkSettings.js:130 msgid "walletServiceWsUrl should be a valid URL." msgstr "" #. If we fall into this situation, the app should be killed #. for the custom new network settings take effect. -#: src/sagas/networkSettings.js:288 +#: src/sagas/networkSettings.js:290 msgid "Wallet not found while trying to persist the custom network settings." msgstr "" -#: src/sagas/pushNotification.js:53 +#: src/sagas/pushNotification.js:57 msgid "Transaction" msgstr "Ingen transaktioner" -#: src/sagas/pushNotification.js:54 +#: src/sagas/pushNotification.js:58 msgid "Open" msgstr "Åben" diff --git a/locale/pt-br/texts.po b/locale/pt-br/texts.po index b76ce4767..6d1acef0b 100644 --- a/locale/pt-br/texts.po +++ b/locale/pt-br/texts.po @@ -10,7 +10,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n!=1);\n" -"X-Generator: Poedit 3.4\n" +"X-Generator: Poedit 3.3.1\n" #. This should never happen! #: src/models.js:24 @@ -112,20 +112,20 @@ msgstr "Há uma nova transação na sua carteira." msgid "New transaction received" msgstr "Nova transação recebida" -#: src/screens/About.js:82 +#: src/screens/About.js:83 msgid "ABOUT" msgstr "SOBRE" -#: src/screens/About.js:94 +#: src/screens/About.js:95 msgid "This app is developed by Hathor Labs and is distributed for free." msgstr "" "Este aplicativo foi desenvolvido pela Hathor Labs e é distribuído de graça." -#: src/screens/About.js:98 src/screens/InitWallet.js:64 +#: src/screens/About.js:99 src/screens/InitWallet.js:64 msgid "This wallet is connected to the **mainnet**." msgstr "Esta wallet está conectada à **mainnet**." -#: src/screens/About.js:101 src/screens/InitWallet.js:67 +#: src/screens/About.js:102 src/screens/InitWallet.js:67 msgid "" "A mobile wallet is not the safest place to store your tokens.\n" "So, we advise you to keep only a small amount of tokens here, such as pocket " @@ -135,7 +135,7 @@ msgstr "" "Por isso, nós aconselhamos que você mantenha apenas uma pequena quantidade " "de tokens aqui." -#: src/screens/About.js:106 +#: src/screens/About.js:107 msgid "" "For further information, check out the |link1:Terms of Service| and |link2:" "Privacy Policy|, or our website |link3:https://hathor.network/|." @@ -214,30 +214,30 @@ msgstr "Os PINs estão diferentes. Tente novamente." msgid "Start the Wallet" msgstr "Iniciar a Wallet" -#: src/screens/CreateTokenAmount.js:119 src/screens/CreateTokenConfirm.js:163 +#: src/screens/CreateTokenAmount.js:120 src/screens/CreateTokenConfirm.js:163 #: src/screens/CreateTokenDepositNotice.js:42 src/screens/CreateTokenName.js:43 #: src/screens/CreateTokenSymbol.js:62 msgid "CREATE TOKEN" msgstr "CRIAR TOKEN" -#: src/screens/CreateTokenAmount.js:127 +#: src/screens/CreateTokenAmount.js:128 #, javascript-format msgid "Amount of ${ this.name } (${ this.symbol })" msgstr "Quantidade de ${ this.name } (${ this.symbol })" -#: src/screens/CreateTokenAmount.js:139 +#: src/screens/CreateTokenAmount.js:140 msgid "Deposit:" msgstr "Depósito:" -#: src/screens/CreateTokenAmount.js:143 +#: src/screens/CreateTokenAmount.js:144 #, javascript-format msgid "You have ${ amountAvailableText } HTR available" msgstr "Você tem ${ amountAvailableText } HTR disponíveis" -#: src/screens/CreateTokenAmount.js:148 src/screens/CreateTokenName.js:64 +#: src/screens/CreateTokenAmount.js:149 src/screens/CreateTokenName.js:64 #: src/screens/CreateTokenSymbol.js:84 src/screens/InitWallet.js:220 #: src/screens/InitWallet.js:341 src/screens/SendAddressInput.js:66 -#: src/screens/SendAmountInput.js:179 +#: src/screens/SendAmountInput.js:185 msgid "Next" msgstr "Próximo" @@ -436,30 +436,30 @@ msgstr "Palavras" msgid "Enter your seed words separated by space" msgstr "Digite suas palavras separadas por espaços" -#: src/screens/LoadHistoryScreen.js:67 +#: src/screens/LoadHistoryScreen.js:51 src/screens/LoadWalletErrorScreen.js:20 +msgid "Try again" +msgstr "Tente novamente" + +#: src/screens/LoadHistoryScreen.js:60 msgid "Loading your transactions" msgstr "Carregando suas transações" -#: src/screens/LoadHistoryScreen.js:70 +#: src/screens/LoadHistoryScreen.js:63 #, javascript-format -msgid "**${ _this.props.loadedData.transactions } transactions** found" -msgstr "**${ _this.props.loadedData.transactions } transações** encontradas" +msgid "**${ loadedData.transactions } transactions** found" +msgstr "**${ loadedData.transactions } transações** encontradas" -#: src/screens/LoadHistoryScreen.js:73 +#: src/screens/LoadHistoryScreen.js:66 #, javascript-format -msgid "**${ _this.props.loadedData.addresses } addresses** found" -msgstr "**${ _this.props.loadedData.addresses } endereços** encontrados" +msgid "**${ loadedData.addresses } addresses** found" +msgstr "**${ loadedData.addresses } endereços** encontrados" #: src/screens/LoadWalletErrorScreen.js:19 msgid "There's been an error connecting to the server." msgstr "Ocorreu um erro ao conectar com o servidor." -#: src/screens/LoadWalletErrorScreen.js:20 -msgid "Try again" -msgstr "Tente novamente" - #: src/screens/LoadWalletErrorScreen.js:21 src/screens/PinScreen.js:268 -#: src/screens/Settings.js:151 +#: src/screens/Settings.js:154 msgid "Reset wallet" msgstr "Resetar Wallet" @@ -543,7 +543,7 @@ msgstr "Desbloqueie sua Hathor Wallet" msgid "Cancel" msgstr "Cancelar" -#: src/screens/PushNotification.js:58 src/screens/Settings.js:125 +#: src/screens/PushNotification.js:58 src/screens/Settings.js:128 msgid "Push Notification" msgstr "Notificação" @@ -574,7 +574,7 @@ msgstr "" msgid "Payment Request" msgstr "Requisição de Pagamento" -#: src/screens/Receive.js:106 +#: src/screens/Receive.js:107 msgid "RECEIVE" msgstr "RECEBER" @@ -673,18 +673,18 @@ msgstr "ENVIAR" msgid "Address to send" msgstr "Endereço para enviar" -#: src/screens/SendAmountInput.js:105 +#: src/screens/SendAmountInput.js:110 msgid "Insufficient funds" msgstr "Saldo insuficiente" -#: src/screens/SendAmountInput.js:136 src/screens/SendConfirmScreen.js:125 +#: src/screens/SendAmountInput.js:141 src/screens/SendConfirmScreen.js:125 #, javascript-format msgid "${ amountAndToken } available" msgid_plural "${ amountAndToken } available" msgstr[0] "${ amountAndToken } disponível" msgstr[1] "${ amountAndToken } disponíveis" -#: src/screens/SendAmountInput.js:148 src/screens/SendConfirmScreen.js:133 +#: src/screens/SendAmountInput.js:154 src/screens/SendConfirmScreen.js:133 msgid "SEND ${ tokenNameUpperCase }" msgstr "ENVIAR ${ tokenNameUpperCase }" @@ -709,7 +709,7 @@ msgstr "Sua transferência de **${ this.amountAndToken }** foi confirmada" msgid "Address" msgstr "Endereço" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:258 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:264 #: src/screens/SendConfirmScreen.js:168 msgid "Send" msgstr "Enviar" @@ -732,43 +732,43 @@ msgstr "Você não tem o token requisitado [${ tokenLabel }]" msgid "Scan the QR code" msgstr "Leia o QR code" -#: src/screens/Settings.js:94 +#: src/screens/Settings.js:97 msgid "You are connected to" msgstr "Você está conectado à" -#: src/screens/Settings.js:102 +#: src/screens/Settings.js:105 msgid "General Settings" msgstr "Configurações Gerais" -#: src/screens/Settings.js:106 +#: src/screens/Settings.js:109 msgid "Connected to" msgstr "Conectado ao servidor" -#: src/screens/Settings.js:119 +#: src/screens/Settings.js:122 msgid "Security" msgstr "Segurança" -#: src/screens/Settings.js:132 +#: src/screens/Settings.js:135 msgid "Create a new token" msgstr "Criar um novo token" -#: src/screens/Settings.js:139 +#: src/screens/Settings.js:142 msgid "Register a token" msgstr "Registrar um token" -#: src/screens/Settings.js:155 +#: src/screens/Settings.js:158 msgid "About" msgstr "Sobre" -#: src/screens/Settings.js:162 +#: src/screens/Settings.js:165 msgid "Unique app identifier" msgstr "Identificador único do aplicativo" -#: src/screens/Settings.js:174 +#: src/screens/Settings.js:179 msgid "Developer Settings" msgstr "Configurações do Desenvolvedor" -#: src/screens/Settings.js:176 +#: src/screens/Settings.js:181 msgid "Network Settings" msgstr "Configurações de Rede" @@ -850,11 +850,11 @@ msgstr "Conectar" msgid "Manual" msgstr "Digitar" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:17 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:18 msgid "Custom Network Settings" msgstr "Configurações de Rede Personalizada" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:18 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:19 msgid "" "Any token outside mainnet network bear no value. Only change if you know " "what you are doing." @@ -862,17 +862,17 @@ msgstr "" "Nenhum token fora da rede mainnet possui valor. Apenas mude a rede se você " "souber o que está fazendo." -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:19 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:20 #: src/screens/NetworkSettings/helper.js:4 msgid "Updating custom network settings..." msgstr "Atualizando configurações de rede..." -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:20 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:21 #: src/screens/NetworkSettings/helper.js:5 msgid "Network settings successfully customized." msgstr "Configuração de rede atualizada com sucesso." -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:21 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:22 #: src/screens/NetworkSettings/helper.js:6 msgid "" "There was an error while customizing network settings. Please try again " @@ -881,43 +881,43 @@ msgstr "" "Ocorreu um erro ao atualizar as configurações de rede. Por favor, tente " "novamente mais tarde." -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:40 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:41 msgid "nodeUrl is required." msgstr "nodeUrl é obrigatório." -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:44 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:45 msgid "explorerUrl is required." msgstr "explorerUrl é obrigatório." -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:48 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:49 msgid "explorerServiceUrl is required." msgstr "explorerServiceUrl é obrigatório." -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:52 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:53 msgid "txMiningServiceUrl is required." msgstr "txMiningServiceUrl é obrigatório." -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:202 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:205 msgid "Node URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:211 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:214 msgid "Explorer URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:220 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:223 msgid "Explorer Service URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:229 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:232 msgid "Transaction Mining Service URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:238 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:243 msgid "Wallet Service URL (optional)" msgstr "Wallet Service URL (opcional)" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:247 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:251 msgid "Wallet Service WS URL (optional)" msgstr "Wallet Service WS URL (opcional)" @@ -943,46 +943,46 @@ msgstr "" "circunstância altere a rede por sugestão de terceiros, uma vez que ao " "alterar a rede você pode ser vítima de fraude." -#: src/sagas/networkSettings.js:87 +#: src/sagas/networkSettings.js:88 msgid "Custom Network Settings cannot be empty." msgstr "As Configurações de Rede não podem estar vazias." -#: src/sagas/networkSettings.js:94 +#: src/sagas/networkSettings.js:95 msgid "explorerUrl should be a valid URL." msgstr "explorerUrl deve ser uma URL válida." -#: src/sagas/networkSettings.js:101 +#: src/sagas/networkSettings.js:102 msgid "explorerServiceUrl should be a valid URL." msgstr "explorerServiceUrl deve ser uma URL válida." -#: src/sagas/networkSettings.js:108 +#: src/sagas/networkSettings.js:109 msgid "txMiningServiceUrl should be a valid URL." msgstr "txMiningServiceUrl deve ser uma URL válida." -#: src/sagas/networkSettings.js:115 +#: src/sagas/networkSettings.js:116 msgid "nodeUrl should be a valid URL." msgstr "nodeUrl deve ser uma URL válida." -#: src/sagas/networkSettings.js:122 +#: src/sagas/networkSettings.js:123 msgid "walletServiceUrl should be a valid URL." msgstr "walletServiceUrl deve ser uma URL válida." -#: src/sagas/networkSettings.js:129 +#: src/sagas/networkSettings.js:130 msgid "walletServiceWsUrl should be a valid URL." msgstr "walletServiceWsUrl deve ser uma URL válida." #. If we fall into this situation, the app should be killed #. for the custom new network settings take effect. -#: src/sagas/networkSettings.js:288 +#: src/sagas/networkSettings.js:290 msgid "Wallet not found while trying to persist the custom network settings." msgstr "" "Wallet não encontrada ao persistir a configuração personalizada da rede." -#: src/sagas/pushNotification.js:53 +#: src/sagas/pushNotification.js:57 msgid "Transaction" msgstr "Transação" -#: src/sagas/pushNotification.js:54 +#: src/sagas/pushNotification.js:58 msgid "Open" msgstr "Abrir" diff --git a/locale/ru-ru/texts.po b/locale/ru-ru/texts.po index 943fb348e..ffa5a05d9 100644 --- a/locale/ru-ru/texts.po +++ b/locale/ru-ru/texts.po @@ -11,7 +11,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n" -"X-Generator: Poedit 3.2.2\n" +"X-Generator: Poedit 3.3.1\n" #. This should never happen! #: src/models.js:24 @@ -109,19 +109,19 @@ msgstr "" msgid "New transaction received" msgstr "Нет транзакций" -#: src/screens/About.js:82 +#: src/screens/About.js:83 msgid "ABOUT" msgstr "О НАС" -#: src/screens/About.js:94 +#: src/screens/About.js:95 msgid "This app is developed by Hathor Labs and is distributed for free." msgstr "Это приложение разработано Hathor Labs и распространяется бесплатно." -#: src/screens/About.js:98 src/screens/InitWallet.js:64 +#: src/screens/About.js:99 src/screens/InitWallet.js:64 msgid "This wallet is connected to the **mainnet**." msgstr "Этот кошелек подключен к **mainnet**." -#: src/screens/About.js:101 src/screens/InitWallet.js:67 +#: src/screens/About.js:102 src/screens/InitWallet.js:67 msgid "" "A mobile wallet is not the safest place to store your tokens.\n" "So, we advise you to keep only a small amount of tokens here, such as pocket " @@ -130,7 +130,7 @@ msgstr "" "Мобильный кошелек - не самый безопасный способ для хранения ваших токенов.\n" "Поэтому мы не советуем хранить на нем большие суммы." -#: src/screens/About.js:106 +#: src/screens/About.js:107 msgid "" "For further information, check out the |link1:Terms of Service| and |link2:" "Privacy Policy|, or our website |link3:https://hathor.network/|." @@ -207,30 +207,30 @@ msgstr "PIN-коды не совпадают. Попробуйте еще раз msgid "Start the Wallet" msgstr "Запустить кошелек" -#: src/screens/CreateTokenAmount.js:119 src/screens/CreateTokenConfirm.js:163 +#: src/screens/CreateTokenAmount.js:120 src/screens/CreateTokenConfirm.js:163 #: src/screens/CreateTokenDepositNotice.js:42 src/screens/CreateTokenName.js:43 #: src/screens/CreateTokenSymbol.js:62 msgid "CREATE TOKEN" msgstr "СОЗДАТЬ ТОКЕН" -#: src/screens/CreateTokenAmount.js:127 +#: src/screens/CreateTokenAmount.js:128 #, javascript-format msgid "Amount of ${ this.name } (${ this.symbol })" msgstr "Количество ${ this.name } (${ this.symbol })" -#: src/screens/CreateTokenAmount.js:139 +#: src/screens/CreateTokenAmount.js:140 msgid "Deposit:" msgstr "Депозит:" -#: src/screens/CreateTokenAmount.js:143 +#: src/screens/CreateTokenAmount.js:144 #, javascript-format msgid "You have ${ amountAvailableText } HTR available" msgstr "У вас ${ amountAvailableText } HTR" -#: src/screens/CreateTokenAmount.js:148 src/screens/CreateTokenName.js:64 +#: src/screens/CreateTokenAmount.js:149 src/screens/CreateTokenName.js:64 #: src/screens/CreateTokenSymbol.js:84 src/screens/InitWallet.js:220 #: src/screens/InitWallet.js:341 src/screens/SendAddressInput.js:66 -#: src/screens/SendAmountInput.js:179 +#: src/screens/SendAmountInput.js:185 msgid "Next" msgstr "Далее" @@ -426,30 +426,30 @@ msgstr "Слова" msgid "Enter your seed words separated by space" msgstr "Введите seed-фразу" -#: src/screens/LoadHistoryScreen.js:67 +#: src/screens/LoadHistoryScreen.js:51 src/screens/LoadWalletErrorScreen.js:20 +msgid "Try again" +msgstr "" + +#: src/screens/LoadHistoryScreen.js:60 msgid "Loading your transactions" msgstr "Загрузка ваших транзакций" -#: src/screens/LoadHistoryScreen.js:70 +#: src/screens/LoadHistoryScreen.js:63 #, javascript-format -msgid "**${ _this.props.loadedData.transactions } transactions** found" -msgstr "**${ _this.props.loadedData.transactions } транзакций** найдено" +msgid "**${ loadedData.transactions } transactions** found" +msgstr "**${ loadedData.transactions } транзакций** найдено" -#: src/screens/LoadHistoryScreen.js:73 +#: src/screens/LoadHistoryScreen.js:66 #, javascript-format -msgid "**${ _this.props.loadedData.addresses } addresses** found" -msgstr "**${ _this.props.loadedData.addresses } адресов** найдено" +msgid "**${ loadedData.addresses } addresses** found" +msgstr "**${ loadedData.addresses } адресов** найдено" #: src/screens/LoadWalletErrorScreen.js:19 msgid "There's been an error connecting to the server." msgstr "" -#: src/screens/LoadWalletErrorScreen.js:20 -msgid "Try again" -msgstr "" - #: src/screens/LoadWalletErrorScreen.js:21 src/screens/PinScreen.js:268 -#: src/screens/Settings.js:151 +#: src/screens/Settings.js:154 msgid "Reset wallet" msgstr "Сбросить кошелек" @@ -532,7 +532,7 @@ msgstr "Разблокировать Hathor Wallet" msgid "Cancel" msgstr "Отмена" -#: src/screens/PushNotification.js:58 src/screens/Settings.js:125 +#: src/screens/PushNotification.js:58 src/screens/Settings.js:128 msgid "Push Notification" msgstr "" @@ -561,7 +561,7 @@ msgstr "" msgid "Payment Request" msgstr "Запрос Средств" -#: src/screens/Receive.js:106 +#: src/screens/Receive.js:107 msgid "RECEIVE" msgstr "ПОЛУЧИТЬ" @@ -658,11 +658,11 @@ msgstr "ОТПРАВИТЬ" msgid "Address to send" msgstr "Адрес отправки" -#: src/screens/SendAmountInput.js:105 +#: src/screens/SendAmountInput.js:110 msgid "Insufficient funds" msgstr "Недостаточно средств" -#: src/screens/SendAmountInput.js:136 src/screens/SendConfirmScreen.js:125 +#: src/screens/SendAmountInput.js:141 src/screens/SendConfirmScreen.js:125 #, javascript-format msgid "${ amountAndToken } available" msgid_plural "${ amountAndToken } available" @@ -670,7 +670,7 @@ msgstr[0] "${ amountAndToken } доступно" msgstr[1] "${ amountAndToken } доступно" msgstr[2] "${ amountAndToken } доступно" -#: src/screens/SendAmountInput.js:148 src/screens/SendConfirmScreen.js:133 +#: src/screens/SendAmountInput.js:154 src/screens/SendConfirmScreen.js:133 msgid "SEND ${ tokenNameUpperCase }" msgstr "ОТПРАВИТЬ ${ tokenNameUpperCase }" @@ -695,7 +695,7 @@ msgstr "Ваш перевод **${ this.amountAndToken }** был подтвер msgid "Address" msgstr "Адрес" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:258 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:264 #: src/screens/SendConfirmScreen.js:168 msgid "Send" msgstr "Отправить" @@ -718,43 +718,43 @@ msgstr "У вас нет запрошенного токена [${ tokenLabel }] msgid "Scan the QR code" msgstr "Сканировать QR-код" -#: src/screens/Settings.js:94 +#: src/screens/Settings.js:97 msgid "You are connected to" msgstr "Вы подключены к" -#: src/screens/Settings.js:102 +#: src/screens/Settings.js:105 msgid "General Settings" msgstr "" -#: src/screens/Settings.js:106 +#: src/screens/Settings.js:109 msgid "Connected to" msgstr "Подключены к" -#: src/screens/Settings.js:119 +#: src/screens/Settings.js:122 msgid "Security" msgstr "Безопасность" -#: src/screens/Settings.js:132 +#: src/screens/Settings.js:135 msgid "Create a new token" msgstr "Создать новый токен" -#: src/screens/Settings.js:139 +#: src/screens/Settings.js:142 msgid "Register a token" msgstr "Зарегистрировать токен" -#: src/screens/Settings.js:155 +#: src/screens/Settings.js:158 msgid "About" msgstr "О нас" -#: src/screens/Settings.js:162 +#: src/screens/Settings.js:165 msgid "Unique app identifier" msgstr "" -#: src/screens/Settings.js:174 +#: src/screens/Settings.js:179 msgid "Developer Settings" msgstr "" -#: src/screens/Settings.js:176 +#: src/screens/Settings.js:181 msgid "Network Settings" msgstr "" @@ -835,70 +835,70 @@ msgstr "" msgid "Manual" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:17 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:18 msgid "Custom Network Settings" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:18 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:19 msgid "" "Any token outside mainnet network bear no value. Only change if you know " "what you are doing." msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:19 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:20 #: src/screens/NetworkSettings/helper.js:4 msgid "Updating custom network settings..." msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:20 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:21 #: src/screens/NetworkSettings/helper.js:5 msgid "Network settings successfully customized." msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:21 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:22 #: src/screens/NetworkSettings/helper.js:6 msgid "" "There was an error while customizing network settings. Please try again " "later." msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:40 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:41 msgid "nodeUrl is required." msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:44 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:45 msgid "explorerUrl is required." msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:48 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:49 msgid "explorerServiceUrl is required." msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:52 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:53 msgid "txMiningServiceUrl is required." msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:202 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:205 msgid "Node URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:211 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:214 msgid "Explorer URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:220 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:223 msgid "Explorer Service URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:229 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:232 msgid "Transaction Mining Service URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:238 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:243 msgid "Wallet Service URL (optional)" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:247 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:251 msgid "Wallet Service WS URL (optional)" msgstr "" @@ -921,45 +921,45 @@ msgid "" "this can potentially make you susceptible to fraudulent schemes." msgstr "" -#: src/sagas/networkSettings.js:87 +#: src/sagas/networkSettings.js:88 msgid "Custom Network Settings cannot be empty." msgstr "" -#: src/sagas/networkSettings.js:94 +#: src/sagas/networkSettings.js:95 msgid "explorerUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:101 +#: src/sagas/networkSettings.js:102 msgid "explorerServiceUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:108 +#: src/sagas/networkSettings.js:109 msgid "txMiningServiceUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:115 +#: src/sagas/networkSettings.js:116 msgid "nodeUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:122 +#: src/sagas/networkSettings.js:123 msgid "walletServiceUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:129 +#: src/sagas/networkSettings.js:130 msgid "walletServiceWsUrl should be a valid URL." msgstr "" #. If we fall into this situation, the app should be killed #. for the custom new network settings take effect. -#: src/sagas/networkSettings.js:288 +#: src/sagas/networkSettings.js:290 msgid "Wallet not found while trying to persist the custom network settings." msgstr "" -#: src/sagas/pushNotification.js:53 +#: src/sagas/pushNotification.js:57 msgid "Transaction" msgstr "Нет транзакций" -#: src/sagas/pushNotification.js:54 +#: src/sagas/pushNotification.js:58 msgid "Open" msgstr "Открыть" diff --git a/locale/texts.pot b/locale/texts.pot index c88c574bd..11ec5275c 100644 --- a/locale/texts.pot +++ b/locale/texts.pot @@ -98,20 +98,20 @@ msgstr "" msgid "New transaction received" msgstr "" -#: src/screens/About.js:82 +#: src/screens/About.js:83 msgid "ABOUT" msgstr "" -#: src/screens/About.js:94 +#: src/screens/About.js:95 msgid "This app is developed by Hathor Labs and is distributed for free." msgstr "" -#: src/screens/About.js:98 +#: src/screens/About.js:99 #: src/screens/InitWallet.js:64 msgid "This wallet is connected to the **mainnet**." msgstr "" -#: src/screens/About.js:101 +#: src/screens/About.js:102 #: src/screens/InitWallet.js:67 msgid "" "A mobile wallet is not the safest place to store your tokens.\n" @@ -119,7 +119,7 @@ msgid "" "pocket money." msgstr "" -#: src/screens/About.js:106 +#: src/screens/About.js:107 msgid "" "For further information, check out the |link1:Terms of Service| and " "|link2:Privacy Policy|, or our website |link3:https://hathor.network/|." @@ -196,7 +196,7 @@ msgstr "" msgid "Start the Wallet" msgstr "" -#: src/screens/CreateTokenAmount.js:119 +#: src/screens/CreateTokenAmount.js:120 #: src/screens/CreateTokenConfirm.js:163 #: src/screens/CreateTokenDepositNotice.js:42 #: src/screens/CreateTokenName.js:43 @@ -204,27 +204,27 @@ msgstr "" msgid "CREATE TOKEN" msgstr "" -#: src/screens/CreateTokenAmount.js:127 +#: src/screens/CreateTokenAmount.js:128 #, javascript-format msgid "Amount of ${ this.name } (${ this.symbol })" msgstr "" -#: src/screens/CreateTokenAmount.js:139 +#: src/screens/CreateTokenAmount.js:140 msgid "Deposit:" msgstr "" -#: src/screens/CreateTokenAmount.js:143 +#: src/screens/CreateTokenAmount.js:144 #, javascript-format msgid "You have ${ amountAvailableText } HTR available" msgstr "" -#: src/screens/CreateTokenAmount.js:148 +#: src/screens/CreateTokenAmount.js:149 #: src/screens/CreateTokenName.js:64 #: src/screens/CreateTokenSymbol.js:84 #: src/screens/InitWallet.js:220 #: src/screens/InitWallet.js:341 #: src/screens/SendAddressInput.js:66 -#: src/screens/SendAmountInput.js:179 +#: src/screens/SendAmountInput.js:185 msgid "Next" msgstr "" @@ -412,31 +412,32 @@ msgstr "" msgid "Enter your seed words separated by space" msgstr "" -#: src/screens/LoadHistoryScreen.js:67 +#: src/screens/LoadHistoryScreen.js:51 +#: src/screens/LoadWalletErrorScreen.js:20 +msgid "Try again" +msgstr "" + +#: src/screens/LoadHistoryScreen.js:60 msgid "Loading your transactions" msgstr "" -#: src/screens/LoadHistoryScreen.js:70 +#: src/screens/LoadHistoryScreen.js:63 #, javascript-format -msgid "**${ _this.props.loadedData.transactions } transactions** found" +msgid "**${ loadedData.transactions } transactions** found" msgstr "" -#: src/screens/LoadHistoryScreen.js:73 +#: src/screens/LoadHistoryScreen.js:66 #, javascript-format -msgid "**${ _this.props.loadedData.addresses } addresses** found" +msgid "**${ loadedData.addresses } addresses** found" msgstr "" #: src/screens/LoadWalletErrorScreen.js:19 msgid "There's been an error connecting to the server." msgstr "" -#: src/screens/LoadWalletErrorScreen.js:20 -msgid "Try again" -msgstr "" - #: src/screens/LoadWalletErrorScreen.js:21 #: src/screens/PinScreen.js:268 -#: src/screens/Settings.js:151 +#: src/screens/Settings.js:154 msgid "Reset wallet" msgstr "" @@ -523,7 +524,7 @@ msgid "Cancel" msgstr "" #: src/screens/PushNotification.js:58 -#: src/screens/Settings.js:125 +#: src/screens/Settings.js:128 msgid "Push Notification" msgstr "" @@ -552,7 +553,7 @@ msgstr "" msgid "Payment Request" msgstr "" -#: src/screens/Receive.js:106 +#: src/screens/Receive.js:107 msgid "RECEIVE" msgstr "" @@ -648,11 +649,11 @@ msgstr "" msgid "Address to send" msgstr "" -#: src/screens/SendAmountInput.js:105 +#: src/screens/SendAmountInput.js:110 msgid "Insufficient funds" msgstr "" -#: src/screens/SendAmountInput.js:136 +#: src/screens/SendAmountInput.js:141 #: src/screens/SendConfirmScreen.js:125 #, javascript-format msgid "${ amountAndToken } available" @@ -660,7 +661,7 @@ msgid_plural "${ amountAndToken } available" msgstr[0] "" msgstr[1] "" -#: src/screens/SendAmountInput.js:148 +#: src/screens/SendAmountInput.js:154 #: src/screens/SendConfirmScreen.js:133 msgid "SEND ${ tokenNameUpperCase }" msgstr "" @@ -688,7 +689,7 @@ msgstr "" msgid "Address" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:258 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:264 #: src/screens/SendConfirmScreen.js:168 msgid "Send" msgstr "" @@ -711,43 +712,43 @@ msgstr "" msgid "Scan the QR code" msgstr "" -#: src/screens/Settings.js:94 +#: src/screens/Settings.js:97 msgid "You are connected to" msgstr "" -#: src/screens/Settings.js:102 +#: src/screens/Settings.js:105 msgid "General Settings" msgstr "" -#: src/screens/Settings.js:106 +#: src/screens/Settings.js:109 msgid "Connected to" msgstr "" -#: src/screens/Settings.js:119 +#: src/screens/Settings.js:122 msgid "Security" msgstr "" -#: src/screens/Settings.js:132 +#: src/screens/Settings.js:135 msgid "Create a new token" msgstr "" -#: src/screens/Settings.js:139 +#: src/screens/Settings.js:142 msgid "Register a token" msgstr "" -#: src/screens/Settings.js:155 +#: src/screens/Settings.js:158 msgid "About" msgstr "" -#: src/screens/Settings.js:162 +#: src/screens/Settings.js:165 msgid "Unique app identifier" msgstr "" -#: src/screens/Settings.js:174 +#: src/screens/Settings.js:179 msgid "Developer Settings" msgstr "" -#: src/screens/Settings.js:176 +#: src/screens/Settings.js:181 msgid "Network Settings" msgstr "" @@ -825,70 +826,70 @@ msgstr "" msgid "Manual" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:17 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:18 msgid "Custom Network Settings" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:18 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:19 msgid "" "Any token outside mainnet network bear no value. Only change if you know " "what you are doing." msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:19 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:20 #: src/screens/NetworkSettings/helper.js:4 msgid "Updating custom network settings..." msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:20 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:21 #: src/screens/NetworkSettings/helper.js:5 msgid "Network settings successfully customized." msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:21 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:22 #: src/screens/NetworkSettings/helper.js:6 msgid "" "There was an error while customizing network settings. Please try again " "later." msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:40 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:41 msgid "nodeUrl is required." msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:44 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:45 msgid "explorerUrl is required." msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:48 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:49 msgid "explorerServiceUrl is required." msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:52 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:53 msgid "txMiningServiceUrl is required." msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:202 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:205 msgid "Node URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:211 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:214 msgid "Explorer URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:220 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:223 msgid "Explorer Service URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:229 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:232 msgid "Transaction Mining Service URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:238 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:243 msgid "Wallet Service URL (optional)" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:247 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:251 msgid "Wallet Service WS URL (optional)" msgstr "" @@ -911,45 +912,45 @@ msgid "" "this can potentially make you susceptible to fraudulent schemes." msgstr "" -#: src/sagas/networkSettings.js:87 +#: src/sagas/networkSettings.js:88 msgid "Custom Network Settings cannot be empty." msgstr "" -#: src/sagas/networkSettings.js:94 +#: src/sagas/networkSettings.js:95 msgid "explorerUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:101 +#: src/sagas/networkSettings.js:102 msgid "explorerServiceUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:108 +#: src/sagas/networkSettings.js:109 msgid "txMiningServiceUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:115 +#: src/sagas/networkSettings.js:116 msgid "nodeUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:122 +#: src/sagas/networkSettings.js:123 msgid "walletServiceUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:129 +#: src/sagas/networkSettings.js:130 msgid "walletServiceWsUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:288 +#: src/sagas/networkSettings.js:290 #. If we fall into this situation, the app should be killed #. for the custom new network settings take effect. msgid "Wallet not found while trying to persist the custom network settings." msgstr "" -#: src/sagas/pushNotification.js:53 +#: src/sagas/pushNotification.js:57 msgid "Transaction" msgstr "" -#: src/sagas/pushNotification.js:54 +#: src/sagas/pushNotification.js:58 msgid "Open" msgstr "" diff --git a/src/sagas/errorHandler.js b/src/sagas/errorHandler.js index 277f53cae..1958d1243 100644 --- a/src/sagas/errorHandler.js +++ b/src/sagas/errorHandler.js @@ -60,7 +60,15 @@ export function* errorModalHandler(action) { if (reportError) { const wallet = yield select((state) => state.wallet); - const accessData = yield call(() => wallet.getAccessData().catch(() => null)); + + let accessData = null; + try { + accessData = yield call(() => wallet.getAccessData()); + } catch (_e) { + // Nothing to do, the error might have ocurred before the wallet is + // started. + } + sentryReportError(error, accessData); } diff --git a/src/sagas/wallet.js b/src/sagas/wallet.js index db28b6cdc..01f06d878 100644 --- a/src/sagas/wallet.js +++ b/src/sagas/wallet.js @@ -12,6 +12,7 @@ import { Network, constants as hathorLibConstants, config, + errors, } from '@hathor/wallet-lib'; import AsyncStorage from '@react-native-async-storage/async-storage'; import { @@ -61,6 +62,7 @@ import { setAvailablePushNotification, resetWalletSuccess, setTokens, + onExceptionCaptured, } from '../actions'; import { fetchTokenData } from './tokens'; import { @@ -81,6 +83,7 @@ export const WALLET_STATUS = { }; export const IGNORE_WS_TOGGLE_FLAG = 'featureFlags:ignoreWalletServiceFlag'; +export const EXPIRE_WS_IGNORE_FLAG = 24 * 60 * 60 * 1000; // 24 hours /** * Returns the value of the PUSH_NOTIFICATION_FEATURE_TOGGLE feature flag @@ -97,12 +100,22 @@ export function* isPushNotificationEnabled() { * @returns {Generator} */ export function* isWalletServiceEnabled() { + // Users might have had issues with the wallet-service in the past, we can detect + // old flags because they were booleans, new flags are integers (timestamps) const shouldIgnoreFlag = yield call(() => AsyncStorage.getItem(IGNORE_WS_TOGGLE_FLAG)); + const shouldIgnoreFlagTs = parseInt(shouldIgnoreFlag, 10); - // If we should ignore flag, it shouldn't matter what the featureToggle is, wallet service - // is definitely disabled. - if (shouldIgnoreFlag) { - return false; + if (!Number.isNaN(shouldIgnoreFlagTs)) { + const now = new Date().getTime(); + const delta = now - shouldIgnoreFlagTs; + + if (delta < EXPIRE_WS_IGNORE_FLAG) { + console.log(`Still ignoring wallet-service, will expire in ${EXPIRE_WS_IGNORE_FLAG - delta}ms`); + return false; + } + } else { + // We can safely remove the old flag and continue + yield call(() => AsyncStorage.removeItem(IGNORE_WS_TOGGLE_FLAG)); } const walletServiceEnabled = yield call(checkForFeatureFlag, WALLET_SERVICE_FEATURE_TOGGLE); @@ -193,20 +206,28 @@ export function* startWallet(action) { password: pin, }); } catch (e) { + // WalletRequestError can either be a network error making the request + // fail or the wallet might have failed to start and returned status: error. + // We don't need to send those to Sentry, so we'll capture all the others + // here: + if (!(e instanceof errors.WalletRequestError)) { + yield put(onExceptionCaptured(e, false)); + } + if (useWalletService) { // Wallet Service start wallet will fail if the status returned from // the service is 'error' or if the start wallet request failed. + // // We should fallback to the old facade by storing the flag to ignore // the feature flag - yield call(() => AsyncStorage.setItem(IGNORE_WS_TOGGLE_FLAG, 'true')); - - // Yield the same action so it will now load on the old facade - yield put(action); - } else { - console.log('failed to start fullnode wallet'); - yield put(startWalletFailed()); - return; + // + // This might be a temporary issue on the wallet-service side, we should + // store the timestamp of when this flag was set, so we're able to expire it + yield call(() => AsyncStorage.setItem(IGNORE_WS_TOGGLE_FLAG, `${new Date().getTime()}`)); } + + yield put(startWalletFailed()); + return; } setKeychainPin(pin); diff --git a/src/screens/LoadHistoryScreen.js b/src/screens/LoadHistoryScreen.js index 1bac0fee0..9bccf50c6 100644 --- a/src/screens/LoadHistoryScreen.js +++ b/src/screens/LoadHistoryScreen.js @@ -5,11 +5,11 @@ * LICENSE file in the root directory of this source tree. */ -import React from 'react'; +import React, { useEffect } from 'react'; import { StyleSheet, Text, View, } from 'react-native'; -import { connect } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import { t } from 'ttag'; import { @@ -21,66 +21,58 @@ import Spinner from '../components/Spinner'; import TextFmt from '../components/TextFmt'; import { COLORS } from '../styles/themes'; -/** - * loadHistoryStatus {Object} progress on loading tx history { - * active {boolean} indicates we're loading the tx history - * error {boolean} error loading history - * } - */ -const mapStateToProps = (state) => ({ - loadHistoryStatus: state.loadHistoryStatus, - loadedData: state.loadedData, -}); +export default function LoadHistoryScreen() { + const dispatch = useDispatch(); + /** + * loadHistoryStatus {Object} progress on loading tx history { + * active {boolean} indicates we're loading the tx history + * error {boolean} error loading history + * } + */ + const loadHistoryStatus = useSelector((state) => state.loadHistoryStatus); + const loadedData = useSelector((state) => state.loadedData); -const mapDispatchToProps = (dispatch) => ({ - reloadHistory: () => dispatch(onWalletReload()), - resetLoadedData: () => dispatch(resetLoadedData()), -}); - -class LoadHistoryScreen extends React.Component { - componentDidMount() { - this.props.resetLoadedData(); - } + useEffect(() => { + dispatch(resetLoadedData()); + }, []); - render() { - const renderError = () => ( - - - There's been an error connecting to the server - - this.props.reloadHistory()} - title='Try again' - /> - - ); + const renderError = () => ( + + + There's been an error connecting to the server + + dispatch(onWalletReload())} + title={t`Try again`} + /> + + ); - const renderLoading = () => ( - - - - {t`Loading your transactions`} - - - {t`**${this.props.loadedData.transactions} transactions** found`} - - - {t`**${this.props.loadedData.addresses} addresses** found`} - - - ); + const renderLoading = () => ( + + + + {t`Loading your transactions`} + + + {t`**${loadedData.transactions} transactions** found`} + + + {t`**${loadedData.addresses} addresses** found`} + + + ); - return ( - - {this.props.loadHistoryStatus.error ? renderError() : renderLoading()} - - ); - } + return ( + + {loadHistoryStatus.error ? renderError() : renderLoading()} + + ); } const styles = StyleSheet.create({ @@ -91,5 +83,3 @@ const styles = StyleSheet.create({ marginTop: 16, }, }); - -export default connect(mapStateToProps, mapDispatchToProps)(LoadHistoryScreen); From 99d69708e9feda9e4e73bd003777668e9afacb38 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Tue, 13 Feb 2024 17:21:22 +0000 Subject: [PATCH 06/23] fix: isTokenRegistered call from getTxDetails (#428) --- src/sagas/pushNotification.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sagas/pushNotification.js b/src/sagas/pushNotification.js index 4c5e9b478..09793ed30 100644 --- a/src/sagas/pushNotification.js +++ b/src/sagas/pushNotification.js @@ -586,7 +586,7 @@ export const getTxDetails = async (wallet, txId) => { name: each.tokenName, symbol: each.tokenSymbol, balance: each.balance, - isRegistered: await isTokenRegistered(each.tokenId), + isRegistered: await isTokenRegistered(wallet, each.tokenId), }))), }); From 3d5c3e2334cf18b79fd8f1e2b0dd46f2a4206cd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Abadesso?= Date: Thu, 15 Feb 2024 13:35:12 -0300 Subject: [PATCH 07/23] feat: sending more exceptions on load wallet to sentry (#430) --- src/sagas/wallet.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sagas/wallet.js b/src/sagas/wallet.js index 01f06d878..71e6b9188 100644 --- a/src/sagas/wallet.js +++ b/src/sagas/wallet.js @@ -254,6 +254,7 @@ export function* startWallet(action) { yield call(loadTokens); } catch (e) { console.error('Tokens load failed: ', e); + yield put(onExceptionCaptured(e, false)); yield put(startWalletFailed()); return; } @@ -657,6 +658,8 @@ export function* onWalletReloadData() { // Finally, set the wallet to READY by dispatching startWalletSuccess yield put(startWalletSuccess()); } catch (e) { + console.log('Wallet reload data failed: ', e); + yield put(onExceptionCaptured(e, false)); yield put(startWalletFailed()); } } From af2c0bce48d311c1af46a67a19eecaff3ce51013 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Mon, 4 Mar 2024 19:22:44 +0000 Subject: [PATCH 08/23] fix: token duplication on dashboard (#431) * fix: token duplication on dashboard * lint: comply with rules --- src/App.js | 8 ++------ src/actions.js | 2 +- src/components/TokenSelect.js | 5 +++-- src/config.js | 15 ++++++++++----- src/constants.js | 14 +++++++++++--- src/reducers/reducer.js | 33 +++++++++++++++++++++++++++------ src/sagas/helpers.js | 29 ++++++++++++++++++++--------- src/sagas/tokens.js | 7 ++++--- src/sagas/wallet.js | 21 +++++++++++---------- src/screens/UnregisterToken.js | 8 ++------ 10 files changed, 91 insertions(+), 51 deletions(-) diff --git a/src/App.js b/src/App.js index e89eea26b..29deff761 100644 --- a/src/App.js +++ b/src/App.js @@ -534,7 +534,7 @@ class _AppStackWrapper extends React.Component { if (!this.props.wallet?.storage) { return; } - const tokens = [...INITIAL_TOKENS]; + const tokens = { ...INITIAL_TOKENS }; const iterator = this.props.wallet.storage.getRegisteredTokens(); let next = await iterator.next(); // XXX: The "for await" syntax wouldbe better but this is failing due to @@ -542,11 +542,7 @@ class _AppStackWrapper extends React.Component { while (!next.done) { const token = next.value; // We need to filter the token data to remove the metadata from this list (e.g. balance) - tokens.push({ - uid: token.uid, - symbol: token.symbol, - name: token.name, - }); + tokens[token.uid] = { ...token }; // eslint-disable-next-line no-await-in-loop next = await iterator.next(); } diff --git a/src/actions.js b/src/actions.js index 379d9360a..520d2df2f 100644 --- a/src/actions.js +++ b/src/actions.js @@ -252,7 +252,7 @@ export const updateSelectedToken = (selectedToken) => ( export const newToken = (token) => ({ type: types.NEW_TOKEN, payload: token }); /** - * tokens {Array} list of tokens to update state + * @param {Object.} tokens map of tokens to update state */ export const setTokens = (tokens) => ({ type: types.SET_TOKENS, payload: tokens }); diff --git a/src/components/TokenSelect.js b/src/components/TokenSelect.js index 8596af203..d3d151a01 100644 --- a/src/components/TokenSelect.js +++ b/src/components/TokenSelect.js @@ -30,12 +30,13 @@ import { COLORS } from '../styles/themes'; * @param {Record} props.tokensBalance * @param {{ uid: string }} props.selectedToken * @param {unknown} props.tokenMetadata - * @param {unknown} props.tokens + * @param {{ [uid: string]: { uid: string; name: string; symbol: string; }}} props.tokens * @param {unknown} props.header * @param {boolean} props.renderArrow * @param {function} props.onItemPress */ const TokenSelect = (props) => { + const tokens = Object.values(props.tokens); const renderItem = ({ item, index }) => { const symbolWrapperStyle = [styles.symbolWrapper]; const symbolTextStyle = [styles.text, styles.leftText, styles.symbolText]; @@ -95,7 +96,7 @@ const TokenSelect = (props) => { {props.header} } Map of tokens added plus initial tokens, + * @see {@link INITIAL_TOKENS} + */ tokens: INITIAL_TOKENS, + /** + * selectedToken {{ + * uid: string; + * name; string; + * symbol: string + * }} Token selected to operate with + * @example + * { + * name: 'YanCoin', + * symbol: 'YAN', + * uid: '000003a3b261e142d3dfd84970d3a50a93b5bc3a66a3b6ba973956148a3eb824' + * } + */ selectedToken: DEFAULT_TOKEN, isOnline: false, serverInfo: { version: '', network: '' }, @@ -499,24 +515,29 @@ const onUpdateSelectedToken = (state, action) => ({ /** * Add a new token to the list of available tokens in this wallet + * @param {object} state + * @param {{payload: { uid: string }}} action containing a token data in payload, + * @see {@link initialState.tokens} */ -const onNewToken = (state, action) => ({ +const onNewToken = (state, { payload }) => ({ ...state, - tokens: [...state.tokens, action.payload], + tokens: { ...state.tokens, [payload.uid]: { ...payload } }, }); /** * Set the list of tokens added in this wallet + * @param {object} state + * @param {{ payload }} action containing all registered tokens in payload */ -const onSetTokens = (state, action) => { +const onSetTokens = (state, { payload }) => { let { selectedToken } = state; - if (action.payload.indexOf(selectedToken) === -1) { + if (payload[selectedToken.uid] == null) { // We have unregistered this token selectedToken = DEFAULT_TOKEN; } return { ...state, - tokens: [...action.payload], + tokens: { ...payload }, selectedToken, }; }; diff --git a/src/sagas/helpers.js b/src/sagas/helpers.js index 042e1b9d1..3ffcd993e 100644 --- a/src/sagas/helpers.js +++ b/src/sagas/helpers.js @@ -153,11 +153,17 @@ export function isUnlockScreen(action) { * Get registered tokens from the wallet instance. * @param {HathorWallet} wallet * @param {boolean} excludeHTR If we should exclude the HTR token. - * @returns {Promise<{ uid: string, symbol: string, name: string }[]>} + * @returns {Promise<{ + * [uid: string]: { + * uid: string; + * symbol: string; + * name: string; + * } + * }>} */ export async function getRegisteredTokens(wallet, excludeHTR = false) { const htrUid = hathorLib.constants.HATHOR_TOKEN_CONFIG.uid; - const tokens = []; + const tokens = {}; // redux-saga generator magic does not work well with the "for await..of" syntax // The asyncGenerator is not recognized as an iterable and it throws an exception @@ -167,11 +173,7 @@ export async function getRegisteredTokens(wallet, excludeHTR = false) { while (!next.done) { const token = next.value; if ((!excludeHTR) || token.uid !== htrUid) { - tokens.push({ - uid: token.uid, - symbol: token.symbol, - name: token.name, - }); + tokens[token.uid] = { ...token }; } // eslint-disable-next-line no-await-in-loop next = await iterator.next(); @@ -179,12 +181,21 @@ export async function getRegisteredTokens(wallet, excludeHTR = false) { // XXX: This will add any default tokens configured, not only HTR if (!excludeHTR) { - tokens.unshift(...INITIAL_TOKENS); + return { ...INITIAL_TOKENS, ...tokens }; } return tokens; } +/** + * Flat registered tokens to uid. + * @param {{ tokens: Object }} Map of registered tokens by uid + * @returns {string[]} Array of token uid + */ +export function getRegisteredTokenUids({ tokens }) { + return Object.keys(tokens); +} + /** * Check if a token is registered in the context of the saga functions. * @param {HathorWallet} wallet @@ -193,7 +204,7 @@ export async function getRegisteredTokens(wallet, excludeHTR = false) { */ export async function isTokenRegistered(wallet, tokenUid) { const tokens = await getRegisteredTokens(wallet); - return tokens.some((token) => token.uid === tokenUid); + return tokens[tokenUid] != null; } export async function getFullnodeNetwork() { diff --git a/src/sagas/tokens.js b/src/sagas/tokens.js index 0c236d268..22ac99e7e 100644 --- a/src/sagas/tokens.js +++ b/src/sagas/tokens.js @@ -18,7 +18,7 @@ import { import { metadataApi } from '@hathor/wallet-lib'; import { channel } from 'redux-saga'; import { get } from 'lodash'; -import { specificTypeAndPayload, dispatchAndWait } from './helpers'; +import { specificTypeAndPayload, dispatchAndWait, getRegisteredTokenUids } from './helpers'; import { mapTokenHistory } from '../utils'; import { types, @@ -139,6 +139,7 @@ function* fetchTokenHistory(action) { * This saga will route the actions dispatched from SET_TOKEN and NEW_TOKEN to the * TOKEN_FETCH_BALANCE_REQUESTED saga, the idea is to load the balance for new tokens * registered or created on the app. + * @param {{type: string; payload: Object;}} action to route */ function* routeTokenChange(action) { const wallet = yield select((state) => state.wallet); @@ -153,8 +154,8 @@ function* routeTokenChange(action) { break; case 'SET_TOKENS': default: - for (const token of action.payload) { - yield put({ type: types.TOKEN_FETCH_BALANCE_REQUESTED, tokenId: token.uid }); + for (const uid of getRegisteredTokenUids({ tokens: action.payload })) { + yield put({ type: types.TOKEN_FETCH_BALANCE_REQUESTED, tokenId: uid }); } break; } diff --git a/src/sagas/wallet.js b/src/sagas/wallet.js index 71e6b9188..6f84f7028 100644 --- a/src/sagas/wallet.js +++ b/src/sagas/wallet.js @@ -72,6 +72,7 @@ import { checkForFeatureFlag, getRegisteredTokens, getNetworkSettings, + getRegisteredTokenUids, } from './helpers'; import { setKeychainPin } from '../utils'; @@ -289,6 +290,7 @@ export function* startWallet(action) { * and dispatch actions to asynchronously load all registered tokens. * * Will throw an error if the download fails for any token. + * @returns {string[]} Array of token uid */ export function* loadTokens() { const customTokenUid = DEFAULT_TOKEN.uid; @@ -304,11 +306,11 @@ export function* loadTokens() { const wallet = yield select((state) => state.wallet); - const registeredTokens = yield getRegisteredTokens(wallet); + const tokens = yield getRegisteredTokens(wallet); - yield put(setTokens(registeredTokens)); + yield put(setTokens(tokens)); - const registeredUids = registeredTokens.map((t) => t.uid); + const registeredUids = getRegisteredTokenUids({ tokens }); // We don't need to wait for the metadatas response, so we can just // spawn a new "thread" to handle it. @@ -451,8 +453,7 @@ export function* handleTx(action) { } // find tokens affected by the transaction - const stateTokens = yield select((state) => state.tokens); - const registeredTokens = stateTokens.map((token) => token.uid); + const registeredUids = yield select(getRegisteredTokenUids); // To be able to only download balances for tokens belonging to this wallet, we // need a list of tokens and addresses involved in the transaction from both the @@ -467,18 +468,18 @@ export function* handleTx(action) { return acc; } - const { token, decoded: { address } } = io; + const { token: tokenUid, decoded: { address } } = io; // We are only interested in registered tokens - if (registeredTokens.indexOf(token) === -1) { + if (registeredUids.indexOf(tokenUid) === -1) { return acc; } - if (!acc[0][token]) { - acc[0][token] = new Set([]); + if (!acc[0][tokenUid]) { + acc[0][tokenUid] = new Set([]); } - acc[0][token].add(address); + acc[0][tokenUid].add(address); acc[1].add(address); return acc; diff --git a/src/screens/UnregisterToken.js b/src/screens/UnregisterToken.js index 18d91a2d5..c5384b5b5 100644 --- a/src/screens/UnregisterToken.js +++ b/src/screens/UnregisterToken.js @@ -76,7 +76,7 @@ class UnregisterToken extends React.Component { // XXX: maybe we should create a new action `removeToken` // so we dont need to get all registered tokens to call setTokens const promise = this.props.storage.unregisterToken(tokenUnregister).then(async () => { - const newTokens = []; + const newTokens = {}; const iterator = this.props.storage.getRegisteredTokens(); let next = await iterator.next(); @@ -85,11 +85,7 @@ class UnregisterToken extends React.Component { while (!next.done) { const token = next.value; // We need to filter the token data to remove the metadata from this list (e.g. balance) - newTokens.push({ - uid: token.uid, - symbol: token.symbol, - name: token.name, - }); + newTokens[token.uid] = { ...token }; // eslint-disable-next-line no-await-in-loop next = await iterator.next(); } From 36ee5f0b484f8f9db473f82e0391a793710dfd08 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Wed, 6 Mar 2024 22:34:24 +0000 Subject: [PATCH 09/23] chore: add release-candidate template (#436) * chore: add release-candidate template * chore: rename to release_candidate_pr_template and add the option to PULL_REQUEST_TEMPLATE * chore: add a dot between rc and the version number --- .github/PULL_REQUEST_TEMPLATE.md | 1 + .../release_candidate_pr_template.md | 8 ++++++++ 2 files changed, 9 insertions(+) create mode 100644 .github/PULL_REQUEST_TEMPLATE/release_candidate_pr_template.md diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 5c236d630..d16b915db 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -2,3 +2,4 @@ Please go the the `Preview` tab and select the appropriate Pull Request template * [Feature Branch](?expand=1&template=feature_branch_pr_template.md) - Use this template when you are creating a feature branch pull-request * [Version Bump](?expand=1&template=version_bump_pr_template.md) - Use this template when you are creating a version bump pull-request +* [Release Candidate](?expand=1&template=release_candidate_pr_template.md) - Use this template when you are starting a new release-candidate process diff --git a/.github/PULL_REQUEST_TEMPLATE/release_candidate_pr_template.md b/.github/PULL_REQUEST_TEMPLATE/release_candidate_pr_template.md new file mode 100644 index 000000000..db61eced3 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/release_candidate_pr_template.md @@ -0,0 +1,8 @@ + +[//]: # (This should be discussed in the daily meeting) +[//]: # (Remember to bump the version to the next release candidate after this is merged) + +### Description + +Start the release candidate process for version `X.X.X-rc.1`. +This PR will merge any changes on `master` into `release-candidate` branch. From 2bbf7ec2f7197440e9dfd0437c6272b1e80fc5b5 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Fri, 8 Mar 2024 17:41:33 +0000 Subject: [PATCH 10/23] bump: v0.27.0-rc.1 (#439) --- android/app/build.gradle | 4 ++-- ios/HathorMobile.xcodeproj/project.pbxproj | 8 ++++---- package-lock.json | 2 +- package.json | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 8c0ea910c..550edf60d 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -79,8 +79,8 @@ android { applicationId "network.hathor.wallet" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 63 - versionName "0.26.1" + versionCode 64 + versionName "0.27.0-rc.1" missingDimensionStrategy "react-native-camera", "general" } signingConfigs { diff --git a/ios/HathorMobile.xcodeproj/project.pbxproj b/ios/HathorMobile.xcodeproj/project.pbxproj index 2869af2a4..34223f755 100644 --- a/ios/HathorMobile.xcodeproj/project.pbxproj +++ b/ios/HathorMobile.xcodeproj/project.pbxproj @@ -512,7 +512,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = HathorMobile/HathorMobile.entitlements; - CURRENT_PROJECT_VERSION = 1.0.0; + CURRENT_PROJECT_VERSION = 0.1.0; DEVELOPMENT_TEAM = 55SHY647CG; ENABLE_BITCODE = NO; INFOPLIST_FILE = HathorMobile/Info.plist; @@ -521,7 +521,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 0.26.1; + MARKETING_VERSION = 0.27.0; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -542,7 +542,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = HathorMobile/HathorMobile.entitlements; - CURRENT_PROJECT_VERSION = 1.0.0; + CURRENT_PROJECT_VERSION = 0.1.0; DEVELOPMENT_TEAM = 55SHY647CG; INFOPLIST_FILE = HathorMobile/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; @@ -550,7 +550,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 0.26.1; + MARKETING_VERSION = 0.27.0; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", diff --git a/package-lock.json b/package-lock.json index c78b02124..604bd1f1e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "HathorMobile", - "version": "0.26.1", + "version": "0.27.0-rc.1", "lockfileVersion": 3, "requires": true, "packages": { diff --git a/package.json b/package.json index a68ad985e..093179592 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "HathorMobile", - "version": "0.26.1", + "version": "0.27.0-rc.1", "engines": { "node": ">=18.0.0", "npm": ">=9.0.0" From 836a3aeb56da9a43671a81a0f5ff2eba8f24e3a1 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Thu, 14 Mar 2024 15:31:17 +0000 Subject: [PATCH 11/23] feat: add nano contract feature toggle (#441) * feat: add nano contract feature toggle * lint: comply with lint * feat: remove custom nano contract feature toggle state to favor default state --- src/actions.js | 2 +- src/constants.js | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/actions.js b/src/actions.js index 520d2df2f..51660effa 100644 --- a/src/actions.js +++ b/src/actions.js @@ -135,7 +135,7 @@ export const types = { NETWORKSETTINGS_UPDATE_INVALID: 'NETWORKSETTINGS_UPDATE_INVALID', /* It indicates the update request has failed. */ NETWORKSETTINGS_UPDATE_FAILURE: 'NETWORK_SETTINGS_UPDATE_FAILURE', - /* It updates the redux state of network settings status */ + /* It updates the redux state of network settings status. */ NETWORKSETTINGS_UPDATE_READY: 'NETWORK_SETTINGS_UPDATE_READY', }; diff --git a/src/constants.js b/src/constants.js index 810325331..f669d14d6 100644 --- a/src/constants.js +++ b/src/constants.js @@ -158,6 +158,7 @@ export const WALLET_SERVICE_FEATURE_TOGGLE = 'wallet-service-mobile.rollout'; export const PUSH_NOTIFICATION_FEATURE_TOGGLE = 'push-notification.rollout'; export const WALLET_CONNECT_FEATURE_TOGGLE = 'wallet-connect-mobile.rollout'; export const NETWORK_SETTINGS_FEATURE_TOGGLE = 'network-settings.rollout'; +export const NANO_CONTRACT_FEATURE_TOGGLE = 'nano-contract.rollout'; /** * Default feature toggle values. @@ -172,6 +173,7 @@ export const FEATURE_TOGGLE_DEFAULTS = { [PUSH_NOTIFICATION_FEATURE_TOGGLE]: false, [WALLET_CONNECT_FEATURE_TOGGLE]: false, [NETWORK_SETTINGS_FEATURE_TOGGLE]: false, + [NANO_CONTRACT_FEATURE_TOGGLE]: false, }; // Project id configured in https://walletconnect.com From 6cf8ee89c729eeb485b1531c59858a2d0654f54b Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Fri, 15 Mar 2024 16:28:19 +0000 Subject: [PATCH 12/23] fix: missing transaction after reload (#442) * feat: add force param to featchTokenData * chore: improve documentation --- src/sagas/tokens.js | 4 ++-- src/sagas/wallet.js | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/sagas/tokens.js b/src/sagas/tokens.js index 22ac99e7e..c777b74d8 100644 --- a/src/sagas/tokens.js +++ b/src/sagas/tokens.js @@ -243,7 +243,7 @@ export function* fetchTokenMetadata({ tokenId }) { } } -export function* fetchTokenData(tokenId) { +export function* fetchTokenData(tokenId, force = false) { const fetchBalanceResponse = yield call( dispatchAndWait, tokenFetchBalanceRequested(tokenId), @@ -256,7 +256,7 @@ export function* fetchTokenData(tokenId) { ); const fetchHistoryResponse = yield call( dispatchAndWait, - tokenFetchHistoryRequested(tokenId), + tokenFetchHistoryRequested(tokenId, force), specificTypeAndPayload(types.TOKEN_FETCH_HISTORY_SUCCESS, { tokenId, }), diff --git a/src/sagas/wallet.js b/src/sagas/wallet.js index 6f84f7028..ef51cd0e7 100644 --- a/src/sagas/wallet.js +++ b/src/sagas/wallet.js @@ -287,7 +287,7 @@ export function* startWallet(action) { /** * This saga will load both HTR and DEFAULT_TOKEN (if they are different) - * and dispatch actions to asynchronously load all registered tokens. + * and dispatch actions to asynchronously load all registered tokens forcefully. * * Will throw an error if the download fails for any token. * @returns {string[]} Array of token uid @@ -298,9 +298,12 @@ export function* loadTokens() { // fetchTokenData will throw an error if the download failed, we should just // let it crash as throwing an error is the default behavior for loadTokens - yield call(fetchTokenData, htrUid); + yield call(fetchTokenData, htrUid, true); if (customTokenUid !== htrUid) { + // custom tokens doesn't need to be forced to download because its history status + // will be marked as invalidated, and history will get requested the next time a user + // enters the history screen. yield call(fetchTokenData, customTokenUid); } @@ -628,6 +631,7 @@ export function* onWalletReloadData() { } try { + // Here we force the download of tokens history const registeredTokens = yield call(loadTokens); const customTokenUid = DEFAULT_TOKEN.uid; From 22381ca5ad75056f7fd1f865df4a39f71949b3a2 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Fri, 22 Mar 2024 14:52:22 +0000 Subject: [PATCH 13/23] fix: wallet-service network error (#444) * feat: add an abstraction method to call checkAddressMine with retry strategy * review: apply suggestions * lint: resolve rules * refactor: extract progressiveRetryRequest to helper * lint: comply with rules * review: apply suggestions * chore: add docstring to progressive retry mechanism constants * refactor: progressiveRetryRequest * lint: comply with rule * chore: remove custom maxRetries --- src/constants.js | 8 +++++++ src/sagas/helpers.js | 54 ++++++++++++++++++++++++++++++++++++++++++++ src/sagas/wallet.js | 13 +++++++++-- 3 files changed, 73 insertions(+), 2 deletions(-) diff --git a/src/constants.js b/src/constants.js index f669d14d6..2bee3e091 100644 --- a/src/constants.js +++ b/src/constants.js @@ -246,3 +246,11 @@ export const HTTP_REQUEST_TIMEOUT = 3000; * Any network that is not mainnet or testnet should be a privatenet. */ export const NETWORK_PRIVATENET = 'privatenet'; + +/** + * The following constants are used on a progressive retry mechanism. + * @see `src/sagas/helper.js@progressiveRetryRequest` + */ +export const MAX_RETRIES = 8; +export const INITIAL_RETRY_LATENCY = 300; // ms +export const LATENCY_MULTIPLIER = 30; // multiplier per iteration diff --git a/src/sagas/helpers.js b/src/sagas/helpers.js index 3ffcd993e..6a77228e4 100644 --- a/src/sagas/helpers.js +++ b/src/sagas/helpers.js @@ -13,6 +13,7 @@ import { take, call, select, + delay, } from 'redux-saga/effects'; import { t } from 'ttag'; import axiosWrapperCreateRequestInstance from '@hathor/wallet-lib/lib/api/axiosWrapper'; @@ -28,6 +29,9 @@ import { WALLET_SERVICE_FEATURE_TOGGLE, WALLET_SERVICE_REQUEST_TIMEOUT, networkSettingsKeyMap, + MAX_RETRIES, + INITIAL_RETRY_LATENCY, + LATENCY_MULTIPLIER, } from '../constants'; import { STORE } from '../store'; @@ -275,3 +279,53 @@ export function getNetworkSettings(state) { // has precedence, once it indicates a custom network. return STORE.getItem(networkSettingsKeyMap.networkSettings) ?? state.networkSettings; } + +/** + * A request abstraction that applies a progressive retry strategy. + * One can define how many retries it should make or use the default value. + * + * @param {Promise} request The async callback function to be executed. + * @param {number} maxRetries The max retries allowed, with default value. + * Notice this param should be at least 1 to make sense. + * @returns {any} A success object from the request. + * @throws An error after retries exhausted. + * + * @example + * yield call(progressiveRetryRequest, async () => asyncFn()); + * // use default maxRetries + * + * @example + * yield call(progressiveRetryRequest, async () => asyncFn(), 3); + * // use custom maxRetries equal to 3 + */ +export function* progressiveRetryRequest(request, maxRetries = MAX_RETRIES) { + let lastError = null; + + // eslint-disable-next-line no-plusplus + for (let i = 0; i <= maxRetries; i++) { + try { + // return if success + return yield call(request) + } catch (error) { + lastError = error; + } + + // skip delay for last call + if (i === maxRetries) { + continue; + } + + // attempt 0: 300ms + // attempt 1: 330ms + // attempt 2: 420ms + // attempt 3: 570ms + // attempt 4: 780ms + // attempt 5: 1050ms + // attempt 6: 1380ms + // attempt 7: 1770ms + yield delay(INITIAL_RETRY_LATENCY + LATENCY_MULTIPLIER * (i * i)); + } + + // throw last error after retries exhausted + throw lastError; +} diff --git a/src/sagas/wallet.js b/src/sagas/wallet.js index ef51cd0e7..25c25f8c9 100644 --- a/src/sagas/wallet.js +++ b/src/sagas/wallet.js @@ -73,6 +73,7 @@ import { getRegisteredTokens, getNetworkSettings, getRegisteredTokenUids, + progressiveRetryRequest, } from './helpers'; import { setKeychainPin } from '../utils'; @@ -488,9 +489,17 @@ export function* handleTx(action) { return acc; }, [{}, new Set([])],); - const txWalletAddresses = yield call(wallet.checkAddressesMine.bind(wallet), [...txAddresses]); - const tokensToDownload = []; + let txWalletAddresses = null; + try { + const request = async () => wallet.checkAddressesMine.bind(wallet)([...txAddresses]); + txWalletAddresses = yield call(progressiveRetryRequest, request); + } catch (error) { + // Emmit a fatal error feedback to user and halts tx processing. + yield put(onExceptionCaptured(error, true)); + return; + } + const tokensToDownload = []; for (const [tokenUid, addresses] of Object.entries(tokenAddressesMap)) { for (const [address] of addresses.entries()) { // txWalletAddresses should always have the address we requested, but we should double check From 2753ce1d6446779d107564fc68f6d7002400ffa2 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Fri, 22 Mar 2024 16:51:26 +0000 Subject: [PATCH 14/23] bump: v0.27.0-rc.2 (#449) --- android/app/build.gradle | 4 ++-- ios/HathorMobile.xcodeproj/project.pbxproj | 4 ++-- package-lock.json | 4 ++-- package.json | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 550edf60d..c1d07431c 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -79,8 +79,8 @@ android { applicationId "network.hathor.wallet" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 64 - versionName "0.27.0-rc.1" + versionCode 65 + versionName "0.27.0-rc.2" missingDimensionStrategy "react-native-camera", "general" } signingConfigs { diff --git a/ios/HathorMobile.xcodeproj/project.pbxproj b/ios/HathorMobile.xcodeproj/project.pbxproj index 34223f755..68f2d5152 100644 --- a/ios/HathorMobile.xcodeproj/project.pbxproj +++ b/ios/HathorMobile.xcodeproj/project.pbxproj @@ -512,7 +512,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = HathorMobile/HathorMobile.entitlements; - CURRENT_PROJECT_VERSION = 0.1.0; + CURRENT_PROJECT_VERSION = 0.2.0; DEVELOPMENT_TEAM = 55SHY647CG; ENABLE_BITCODE = NO; INFOPLIST_FILE = HathorMobile/Info.plist; @@ -542,7 +542,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = HathorMobile/HathorMobile.entitlements; - CURRENT_PROJECT_VERSION = 0.1.0; + CURRENT_PROJECT_VERSION = 0.2.0; DEVELOPMENT_TEAM = 55SHY647CG; INFOPLIST_FILE = HathorMobile/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; diff --git a/package-lock.json b/package-lock.json index 604bd1f1e..0679b4c91 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "HathorMobile", - "version": "0.27.0-rc.1", + "version": "0.27.0-rc.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "HathorMobile", - "version": "0.26.1", + "version": "0.27.0-rc.2", "hasInstallScript": true, "dependencies": { "@fortawesome/fontawesome-svg-core": "1.2.36", diff --git a/package.json b/package.json index 093179592..de494b75a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "HathorMobile", - "version": "0.27.0-rc.1", + "version": "0.27.0-rc.2", "engines": { "node": ">=18.0.0", "npm": ">=9.0.0" From 7a9ae2a97f56bb2e3cdd5b37b8488778fc14511f Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Fri, 5 Apr 2024 21:10:41 +0100 Subject: [PATCH 15/23] fix: layout disposition and input configuration (#461) feat: add form validation for wallet-service fields lint: comply with rules chore(i18n): update translation files and translate to portuguese review: apply suggestions --- locale/da/texts.po | 46 ++-- locale/pt-br/texts.po | 46 ++-- locale/ru-ru/texts.po | 46 ++-- locale/texts.pot | 46 ++-- .../CustomNetworkSettingsScreen.js | 224 ++++++++++-------- .../NetworkPreSettingsScreen.js | 4 +- src/screens/NetworkSettings/helper.js | 2 +- 7 files changed, 237 insertions(+), 177 deletions(-) diff --git a/locale/da/texts.po b/locale/da/texts.po index 3cb0dc14e..c22598be6 100644 --- a/locale/da/texts.po +++ b/locale/da/texts.po @@ -677,11 +677,11 @@ msgstr "SEND ${ tokenNameUpperCase }" msgid "Your transfer is being processed" msgstr "Din overførsel behandles" -#: src/sagas/helpers.js:132 src/screens/SendConfirmScreen.js:104 +#: src/sagas/helpers.js:136 src/screens/SendConfirmScreen.js:104 msgid "Enter your 6-digit pin to authorize operation" msgstr "Indtast din 6-cifrede pin for at godkende overførslen" -#: src/sagas/helpers.js:133 src/screens/SendConfirmScreen.js:105 +#: src/sagas/helpers.js:137 src/screens/SendConfirmScreen.js:105 msgid "Authorize operation" msgstr "Autoriserer overførslen" @@ -693,7 +693,7 @@ msgstr "Din overførsel af **${ _this.amountAndToken }** er bekræftet" msgid "Address" msgstr "Adresse" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:264 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:288 #: src/screens/SendConfirmScreen.js:168 msgid "Send" msgstr "Send" @@ -760,11 +760,11 @@ msgstr "" msgid "Unregister" msgstr "Afmeld" -#: src/screens/UnregisterToken.js:112 +#: src/screens/UnregisterToken.js:108 msgid "UNREGISTER TOKEN" msgstr "AFREGISTRER TOKEN" -#: src/screens/UnregisterToken.js:117 +#: src/screens/UnregisterToken.js:113 msgid "" "If you unregister this token **you won't be able to execute operations with " "it**, unless you register it again." @@ -772,17 +772,17 @@ msgstr "" "Hvis du afregistrerer denne token, **kan du ikke udføre funktioner med " "den**, medmindre du registrerer den igen." -#: src/screens/UnregisterToken.js:120 +#: src/screens/UnregisterToken.js:116 msgid "" "You won't lose your tokens, they will just not appear on this wallet anymore." msgstr "Du mister ikke dine tokens, de vises bare ikke længere i denne wallet." -#: src/screens/UnregisterToken.js:124 +#: src/screens/UnregisterToken.js:120 #, javascript-format msgid "I want to unregister the token **${ tokenLabel }**" msgstr "Jeg vil afregistrere token **${ tokenLabel }**" -#: src/screens/UnregisterToken.js:139 +#: src/screens/UnregisterToken.js:135 msgid "Unregister token" msgstr "Afregistrer token" @@ -874,27 +874,35 @@ msgstr "" msgid "txMiningServiceUrl is required." msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:205 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:62 +msgid "walletServiceUrl is required when walletServiceWsUrl is filled." +msgstr "" + +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:65 +msgid "walletServiceWsUrl is required when walletServiceUrl is filled." +msgstr "" + +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:229 msgid "Node URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:214 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:239 msgid "Explorer URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:223 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:248 msgid "Explorer Service URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:232 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:257 msgid "Transaction Mining Service URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:243 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:268 msgid "Wallet Service URL (optional)" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:251 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:277 msgid "Wallet Service WS URL (optional)" msgstr "" @@ -951,23 +959,23 @@ msgstr "" msgid "Wallet not found while trying to persist the custom network settings." msgstr "" -#: src/sagas/pushNotification.js:57 +#: src/sagas/pushNotification.js:59 msgid "Transaction" msgstr "Ingen transaktioner" -#: src/sagas/pushNotification.js:58 +#: src/sagas/pushNotification.js:60 msgid "Open" msgstr "Åben" -#: src/components/AskForPushNotification.js:21 +#: src/components/AskForPushNotification.js:22 msgid "Do you want to enable push notifications for this wallet?" msgstr "" -#: src/components/AskForPushNotification.js:22 +#: src/components/AskForPushNotification.js:23 msgid "You can always change this later in the settings menu" msgstr "" -#: src/components/AskForPushNotification.js:23 +#: src/components/AskForPushNotification.js:24 msgid "Yes, enable" msgstr "" diff --git a/locale/pt-br/texts.po b/locale/pt-br/texts.po index 6d1acef0b..3901f0f91 100644 --- a/locale/pt-br/texts.po +++ b/locale/pt-br/texts.po @@ -693,11 +693,11 @@ msgstr "ENVIAR ${ tokenNameUpperCase }" msgid "Your transfer is being processed" msgstr "Sua transferência está sendo processada" -#: src/sagas/helpers.js:132 src/screens/SendConfirmScreen.js:104 +#: src/sagas/helpers.js:136 src/screens/SendConfirmScreen.js:104 msgid "Enter your 6-digit pin to authorize operation" msgstr "Digite seu PIN de 6 dígitos para autorizar a operação" -#: src/sagas/helpers.js:133 src/screens/SendConfirmScreen.js:105 +#: src/sagas/helpers.js:137 src/screens/SendConfirmScreen.js:105 msgid "Authorize operation" msgstr "Autorizar operação" @@ -709,7 +709,7 @@ msgstr "Sua transferência de **${ this.amountAndToken }** foi confirmada" msgid "Address" msgstr "Endereço" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:264 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:288 #: src/screens/SendConfirmScreen.js:168 msgid "Send" msgstr "Enviar" @@ -776,11 +776,11 @@ msgstr "Configurações de Rede" msgid "Unregister" msgstr "Desregistrar" -#: src/screens/UnregisterToken.js:112 +#: src/screens/UnregisterToken.js:108 msgid "UNREGISTER TOKEN" msgstr "DESREGISTRAR TOKEN" -#: src/screens/UnregisterToken.js:117 +#: src/screens/UnregisterToken.js:113 msgid "" "If you unregister this token **you won't be able to execute operations with " "it**, unless you register it again." @@ -788,19 +788,19 @@ msgstr "" "Se você desregistrar esse token **você não conseguirá mais executar " "operações com ele**, a não ser que você o registre novamente." -#: src/screens/UnregisterToken.js:120 +#: src/screens/UnregisterToken.js:116 msgid "" "You won't lose your tokens, they will just not appear on this wallet anymore." msgstr "" "Você não irá perder os seus tokens, eles apenas não irão mais aparecer nessa " "wallet." -#: src/screens/UnregisterToken.js:124 +#: src/screens/UnregisterToken.js:120 #, javascript-format msgid "I want to unregister the token **${ tokenLabel }**" msgstr "Eu quero desregistrar o token **${ tokenLabel }**" -#: src/screens/UnregisterToken.js:139 +#: src/screens/UnregisterToken.js:135 msgid "Unregister token" msgstr "Desregistrar token" @@ -897,27 +897,35 @@ msgstr "explorerServiceUrl é obrigatório." msgid "txMiningServiceUrl is required." msgstr "txMiningServiceUrl é obrigatório." -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:205 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:62 +msgid "walletServiceUrl is required when walletServiceWsUrl is filled." +msgstr "walletServiceUrl é obrigatório quando walletServiceWsUrl está preenchido." + +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:65 +msgid "walletServiceWsUrl is required when walletServiceUrl is filled." +msgstr "walletServiceWsUrl é obrigatório quando walletServiceUrl está preenchido" + +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:229 msgid "Node URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:214 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:239 msgid "Explorer URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:223 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:248 msgid "Explorer Service URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:232 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:257 msgid "Transaction Mining Service URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:243 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:268 msgid "Wallet Service URL (optional)" msgstr "Wallet Service URL (opcional)" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:251 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:277 msgid "Wallet Service WS URL (optional)" msgstr "Wallet Service WS URL (opcional)" @@ -978,23 +986,23 @@ msgid "Wallet not found while trying to persist the custom network settings." msgstr "" "Wallet não encontrada ao persistir a configuração personalizada da rede." -#: src/sagas/pushNotification.js:57 +#: src/sagas/pushNotification.js:59 msgid "Transaction" msgstr "Transação" -#: src/sagas/pushNotification.js:58 +#: src/sagas/pushNotification.js:60 msgid "Open" msgstr "Abrir" -#: src/components/AskForPushNotification.js:21 +#: src/components/AskForPushNotification.js:22 msgid "Do you want to enable push notifications for this wallet?" msgstr "Você deseja habilitar as notificações para esta wallet?" -#: src/components/AskForPushNotification.js:22 +#: src/components/AskForPushNotification.js:23 msgid "You can always change this later in the settings menu" msgstr "Você sempre pode alterar as configurações depois no menu" -#: src/components/AskForPushNotification.js:23 +#: src/components/AskForPushNotification.js:24 msgid "Yes, enable" msgstr "Sim, habilitar" diff --git a/locale/ru-ru/texts.po b/locale/ru-ru/texts.po index ffa5a05d9..598ae15b2 100644 --- a/locale/ru-ru/texts.po +++ b/locale/ru-ru/texts.po @@ -679,11 +679,11 @@ msgstr "ОТПРАВИТЬ ${ tokenNameUpperCase }" msgid "Your transfer is being processed" msgstr "Ваш перевод обрабатывается" -#: src/sagas/helpers.js:132 src/screens/SendConfirmScreen.js:104 +#: src/sagas/helpers.js:136 src/screens/SendConfirmScreen.js:104 msgid "Enter your 6-digit pin to authorize operation" msgstr "Введите 6-значный PIN-код для авторизации операции" -#: src/sagas/helpers.js:133 src/screens/SendConfirmScreen.js:105 +#: src/sagas/helpers.js:137 src/screens/SendConfirmScreen.js:105 msgid "Authorize operation" msgstr "Авторизовать операцию" @@ -695,7 +695,7 @@ msgstr "Ваш перевод **${ this.amountAndToken }** был подтвер msgid "Address" msgstr "Адрес" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:264 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:288 #: src/screens/SendConfirmScreen.js:168 msgid "Send" msgstr "Отправить" @@ -762,11 +762,11 @@ msgstr "" msgid "Unregister" msgstr "Отменить регистрацию" -#: src/screens/UnregisterToken.js:112 +#: src/screens/UnregisterToken.js:108 msgid "UNREGISTER TOKEN" msgstr "ОТМЕНИТЬ РЕГИСТРАЦИЮ ТОКЕНА" -#: src/screens/UnregisterToken.js:117 +#: src/screens/UnregisterToken.js:113 msgid "" "If you unregister this token **you won't be able to execute operations with " "it**, unless you register it again." @@ -774,19 +774,19 @@ msgstr "" "Если вы отмените регистрацию этого токена, **вы не сможете выполнять " "операции с ним**, пока не зарегистрируете его снова." -#: src/screens/UnregisterToken.js:120 +#: src/screens/UnregisterToken.js:116 msgid "" "You won't lose your tokens, they will just not appear on this wallet anymore." msgstr "" "Вы не потеряете свои токены, они просто больше не будут появляться на этом " "кошельке." -#: src/screens/UnregisterToken.js:124 +#: src/screens/UnregisterToken.js:120 #, javascript-format msgid "I want to unregister the token **${ tokenLabel }**" msgstr "Я хочу отменить регистрацию токена **${ tokenLabel }**" -#: src/screens/UnregisterToken.js:139 +#: src/screens/UnregisterToken.js:135 msgid "Unregister token" msgstr "Отменить регистрацию токена" @@ -878,27 +878,35 @@ msgstr "" msgid "txMiningServiceUrl is required." msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:205 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:62 +msgid "walletServiceUrl is required when walletServiceWsUrl is filled." +msgstr "" + +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:65 +msgid "walletServiceWsUrl is required when walletServiceUrl is filled." +msgstr "" + +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:229 msgid "Node URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:214 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:239 msgid "Explorer URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:223 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:248 msgid "Explorer Service URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:232 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:257 msgid "Transaction Mining Service URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:243 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:268 msgid "Wallet Service URL (optional)" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:251 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:277 msgid "Wallet Service WS URL (optional)" msgstr "" @@ -955,23 +963,23 @@ msgstr "" msgid "Wallet not found while trying to persist the custom network settings." msgstr "" -#: src/sagas/pushNotification.js:57 +#: src/sagas/pushNotification.js:59 msgid "Transaction" msgstr "Нет транзакций" -#: src/sagas/pushNotification.js:58 +#: src/sagas/pushNotification.js:60 msgid "Open" msgstr "Открыть" -#: src/components/AskForPushNotification.js:21 +#: src/components/AskForPushNotification.js:22 msgid "Do you want to enable push notifications for this wallet?" msgstr "" -#: src/components/AskForPushNotification.js:22 +#: src/components/AskForPushNotification.js:23 msgid "You can always change this later in the settings menu" msgstr "" -#: src/components/AskForPushNotification.js:23 +#: src/components/AskForPushNotification.js:24 msgid "Yes, enable" msgstr "" diff --git a/locale/texts.pot b/locale/texts.pot index 11ec5275c..497ab9564 100644 --- a/locale/texts.pot +++ b/locale/texts.pot @@ -671,12 +671,12 @@ msgstr "" msgid "Your transfer is being processed" msgstr "" -#: src/sagas/helpers.js:132 +#: src/sagas/helpers.js:136 #: src/screens/SendConfirmScreen.js:104 msgid "Enter your 6-digit pin to authorize operation" msgstr "" -#: src/sagas/helpers.js:133 +#: src/sagas/helpers.js:137 #: src/screens/SendConfirmScreen.js:105 msgid "Authorize operation" msgstr "" @@ -689,7 +689,7 @@ msgstr "" msgid "Address" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:264 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:288 #: src/screens/SendConfirmScreen.js:168 msgid "Send" msgstr "" @@ -756,28 +756,28 @@ msgstr "" msgid "Unregister" msgstr "" -#: src/screens/UnregisterToken.js:112 +#: src/screens/UnregisterToken.js:108 msgid "UNREGISTER TOKEN" msgstr "" -#: src/screens/UnregisterToken.js:117 +#: src/screens/UnregisterToken.js:113 msgid "" "If you unregister this token **you won't be able to execute operations with " "it**, unless you register it again." msgstr "" -#: src/screens/UnregisterToken.js:120 +#: src/screens/UnregisterToken.js:116 msgid "" "You won't lose your tokens, they will just not appear on this wallet " "anymore." msgstr "" -#: src/screens/UnregisterToken.js:124 +#: src/screens/UnregisterToken.js:120 #, javascript-format msgid "I want to unregister the token **${ tokenLabel }**" msgstr "" -#: src/screens/UnregisterToken.js:139 +#: src/screens/UnregisterToken.js:135 msgid "Unregister token" msgstr "" @@ -869,27 +869,35 @@ msgstr "" msgid "txMiningServiceUrl is required." msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:205 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:62 +msgid "walletServiceUrl is required when walletServiceWsUrl is filled." +msgstr "" + +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:65 +msgid "walletServiceWsUrl is required when walletServiceUrl is filled." +msgstr "" + +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:229 msgid "Node URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:214 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:239 msgid "Explorer URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:223 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:248 msgid "Explorer Service URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:232 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:257 msgid "Transaction Mining Service URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:243 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:268 msgid "Wallet Service URL (optional)" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:251 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:277 msgid "Wallet Service WS URL (optional)" msgstr "" @@ -946,23 +954,23 @@ msgstr "" msgid "Wallet not found while trying to persist the custom network settings." msgstr "" -#: src/sagas/pushNotification.js:57 +#: src/sagas/pushNotification.js:59 msgid "Transaction" msgstr "" -#: src/sagas/pushNotification.js:58 +#: src/sagas/pushNotification.js:60 msgid "Open" msgstr "" -#: src/components/AskForPushNotification.js:21 +#: src/components/AskForPushNotification.js:22 msgid "Do you want to enable push notifications for this wallet?" msgstr "" -#: src/components/AskForPushNotification.js:22 +#: src/components/AskForPushNotification.js:23 msgid "You can always change this later in the settings menu" msgstr "" -#: src/components/AskForPushNotification.js:23 +#: src/components/AskForPushNotification.js:24 msgid "Yes, enable" msgstr "" diff --git a/src/screens/NetworkSettings/CustomNetworkSettingsScreen.js b/src/screens/NetworkSettings/CustomNetworkSettingsScreen.js index 3eaf44166..6572028f5 100644 --- a/src/screens/NetworkSettings/CustomNetworkSettingsScreen.js +++ b/src/screens/NetworkSettings/CustomNetworkSettingsScreen.js @@ -1,5 +1,5 @@ import React, { useEffect, useState } from 'react'; -import { View, Text, StyleSheet, Image } from 'react-native'; +import { View, Text, StyleSheet, Image, ScrollView, KeyboardAvoidingView } from 'react-native'; import { useDispatch, useSelector } from 'react-redux'; import { t } from 'ttag'; import { isEmpty } from 'lodash'; @@ -11,14 +11,14 @@ import SimpleInput from '../../components/SimpleInput'; import errorIcon from '../../assets/images/icErrorBig.png'; import checkIcon from '../../assets/images/icCheckBig.png'; import Spinner from '../../components/Spinner'; -import { hasSucceed, hasFailed, isLoading } from './helper'; +import { hasSucceeded, hasFailed, isLoading } from './helper'; import { AlertUI } from '../../styles/themes'; import { WALLET_SERVICE_FEATURE_TOGGLE } from '../../constants'; const customNetworkSettingsTitleText = t`Custom Network Settings`.toUpperCase(); const warningText = t`Any token outside mainnet network bear no value. Only change if you know what you are doing.`; const feedbackLoadingText = t`Updating custom network settings...`; -const feedbackSucceedText = t`Network settings successfully customized.`; +const feedbackSucceededText = t`Network settings successfully customized.`; const feedbackFailedText = t`There was an error while customizing network settings. Please try again later.`; /** @@ -53,26 +53,44 @@ function validate(formModel) { invalidModel.txMiningServiceUrl = t`txMiningServiceUrl is required.`; } + // if any wallet-service fields have a value, then evaluate, otherwise pass + if (formModel.walletServiceUrl || formModel.walletServiceWsUrl) { + // if not both wallet-service fields have a value, then evaluate, otherwise pass + if (!(formModel.walletServiceUrl && formModel.walletServiceWsUrl)) { + // invalidade the one that don't have a value + if (!formModel.walletServiceUrl) { + invalidModel.walletServiceUrl = t`walletServiceUrl is required when walletServiceWsUrl is filled.`; + } + if (!formModel.walletServiceWsUrl) { + invalidModel.walletServiceWsUrl = t`walletServiceWsUrl is required when walletServiceUrl is filled.`; + } + } + } + return invalidModel; } const styles = StyleSheet.create({ - container: { + wrapper: { flex: 1, }, - content: { + keyboardWrapper: { flex: 1, - padding: 16, - paddingBottom: 48, + }, + container: { + paddingHorizontal: 16, }, feedbackModalIcon: { height: 105, width: 105 }, - warningContainer: { + warningWrapper: { + paddingVertical: 16, + }, + warningCard: { + flexShrink: 1, borderRadius: 8, backgroundColor: AlertUI.lightColor, - marginBottom: 32, borderWidth: 1, borderColor: AlertUI.baseHslColor.addLightness(4).toString(), }, @@ -81,13 +99,12 @@ const styles = StyleSheet.create({ color: AlertUI.darkColor, padding: 12, }, + formWrapper: { + paddingBottom: 16, + }, input: { marginBottom: 24, }, - buttonContainer: { - alignSelf: 'stretch', - marginTop: 'auto', - }, }); export const CustomNetworkSettingsNav = Symbol('CustomNetworkSettings').toString(); @@ -104,18 +121,18 @@ export const CustomNetworkSettingsScreen = ({ navigation }) => { nodeUrl: networkSettings.nodeUrl, explorerUrl: networkSettings.explorerUrl, explorerServiceUrl: networkSettings.explorerServiceUrl, + txMiningServiceUrl: networkSettings.txMiningServiceUrl || '', walletServiceUrl: networkSettings.walletServiceUrl || '', walletServiceWsUrl: networkSettings.walletServiceWsUrl || '', - txMiningServiceUrl: networkSettings.txMiningServiceUrl || '', }); const [invalidModel, setInvalidModel] = useState({ nodeUrl: networkSettingsInvalid?.nodeUrl || '', explorerUrl: networkSettingsInvalid?.explorerUrl || '', explorerServiceUrl: networkSettingsInvalid?.explorerServiceUrl || '', + txMiningServiceUrl: networkSettingsInvalid.txMiningServiceUrl || '', walletServiceUrl: networkSettingsInvalid?.walletServiceUrl || '', walletServiceWsUrl: networkSettingsInvalid?.walletServiceWsUrl || '', - txMiningServiceUrl: networkSettingsInvalid.txMiningServiceUrl || '', }); // eslint-disable-next-line max-len @@ -167,104 +184,115 @@ export const CustomNetworkSettingsScreen = ({ navigation }) => { }, []); return ( - - navigation.goBack()} - /> - - {isLoading(networkSettingsStatus) && ( - } - text={feedbackLoadingText} + + + navigation.goBack()} /> - )} - {hasSucceed(networkSettingsStatus) && ( - )} - text={feedbackSucceedText} - onDismiss={handleFeedbackModalDismiss} - /> - )} + {isLoading(networkSettingsStatus) && ( + } + text={feedbackLoadingText} + /> + )} - {hasFailed(networkSettingsStatus) && ( - )} - text={feedbackFailedText} - onDismiss={handleFeedbackModalDismiss} - /> - )} + {hasSucceeded(networkSettingsStatus) && ( + )} + text={feedbackSucceededText} + onDismiss={handleFeedbackModalDismiss} + /> + )} - - - {warningText} - - + {hasFailed(networkSettingsStatus) && ( + )} + text={feedbackFailedText} + onDismiss={handleFeedbackModalDismiss} + /> + )} - + + + {warningText} + + - + + + - + - {walletServiceEnabled && ( - <> + - - )} - - - + {walletServiceEnabled && ( + <> + + + + + )} + + + + - + ); }; diff --git a/src/screens/NetworkSettings/NetworkPreSettingsScreen.js b/src/screens/NetworkSettings/NetworkPreSettingsScreen.js index f87064a22..0d10617bd 100644 --- a/src/screens/NetworkSettings/NetworkPreSettingsScreen.js +++ b/src/screens/NetworkSettings/NetworkPreSettingsScreen.js @@ -21,7 +21,7 @@ import FeedbackModal from '../../components/FeedbackModal'; import { networkSettingsPersistStore, networkSettingsUpdateReady } from '../../actions'; import { PRE_SETTINGS_MAINNET, PRE_SETTINGS_TESTNET } from '../../constants'; import { CustomNetworkSettingsNav } from './CustomNetworkSettingsScreen'; -import { feedbackSucceedText, feedbackFailedText, feedbackLoadingText, hasFailed, isLoading, hasSucceed } from './helper'; +import { feedbackSucceedText, feedbackFailedText, feedbackLoadingText, hasFailed, isLoading, hasSucceeded } from './helper'; import errorIcon from '../../assets/images/icErrorBig.png'; import checkIcon from '../../assets/images/icCheckBig.png'; @@ -102,7 +102,7 @@ export function NetworkPreSettingsScreen({ navigation }) { /> )} - {hasSucceed(networkSettingsStatus) && ( + {hasSucceeded(networkSettingsStatus) && ( )} text={feedbackSucceedText} diff --git a/src/screens/NetworkSettings/helper.js b/src/screens/NetworkSettings/helper.js index 11b7ffd45..187ad08be 100644 --- a/src/screens/NetworkSettings/helper.js +++ b/src/screens/NetworkSettings/helper.js @@ -10,7 +10,7 @@ export const feedbackFailedText = t`There was an error while customizing network * @param {object} networkSettingsStatus - status from redux store * @returns {boolean} - true if the status is successful, false otherwise */ -export function hasSucceed(networkSettingsStatus) { +export function hasSucceeded(networkSettingsStatus) { return networkSettingsStatus === NETWORKSETTINGS_STATUS.SUCCESSFUL; } From 1284d1ff7659a2256c0507f478fe113469db68dd Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Tue, 9 Apr 2024 21:27:55 +0100 Subject: [PATCH 16/23] bump: v0.27.0-rc.3 (#463) --- android/app/build.gradle | 4 ++-- ios/HathorMobile.xcodeproj/project.pbxproj | 4 ++-- package-lock.json | 4 ++-- package.json | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index c1d07431c..3679295d6 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -79,8 +79,8 @@ android { applicationId "network.hathor.wallet" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 65 - versionName "0.27.0-rc.2" + versionCode 66 + versionName "0.27.0-rc.3" missingDimensionStrategy "react-native-camera", "general" } signingConfigs { diff --git a/ios/HathorMobile.xcodeproj/project.pbxproj b/ios/HathorMobile.xcodeproj/project.pbxproj index 68f2d5152..f961252b2 100644 --- a/ios/HathorMobile.xcodeproj/project.pbxproj +++ b/ios/HathorMobile.xcodeproj/project.pbxproj @@ -512,7 +512,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = HathorMobile/HathorMobile.entitlements; - CURRENT_PROJECT_VERSION = 0.2.0; + CURRENT_PROJECT_VERSION = 0.3.0; DEVELOPMENT_TEAM = 55SHY647CG; ENABLE_BITCODE = NO; INFOPLIST_FILE = HathorMobile/Info.plist; @@ -542,7 +542,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = HathorMobile/HathorMobile.entitlements; - CURRENT_PROJECT_VERSION = 0.2.0; + CURRENT_PROJECT_VERSION = 0.3.0; DEVELOPMENT_TEAM = 55SHY647CG; INFOPLIST_FILE = HathorMobile/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; diff --git a/package-lock.json b/package-lock.json index 0679b4c91..032425f12 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "HathorMobile", - "version": "0.27.0-rc.2", + "version": "0.27.0-rc.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "HathorMobile", - "version": "0.27.0-rc.2", + "version": "0.27.0-rc.3", "hasInstallScript": true, "dependencies": { "@fortawesome/fontawesome-svg-core": "1.2.36", diff --git a/package.json b/package.json index de494b75a..90aba6ead 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "HathorMobile", - "version": "0.27.0-rc.2", + "version": "0.27.0-rc.3", "engines": { "node": ">=18.0.0", "npm": ">=9.0.0" From b88fa51e5f07b3411759de700e2ad4548369d7b8 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Thu, 11 Apr 2024 23:05:01 +0100 Subject: [PATCH 17/23] fix: network settings processing on wallet (#465) fix: wallet behavior consistency with custom network settings chore: remove unused import --- src/actions.js | 2 +- src/reducers/reducer.js | 12 ++++++++++++ src/sagas/networkSettings.js | 26 +++++++++++--------------- src/sagas/wallet.js | 27 ++++++++++++++++++++++++--- src/screens/Settings.js | 13 ++++++++++--- 5 files changed, 58 insertions(+), 22 deletions(-) diff --git a/src/actions.js b/src/actions.js index 51660effa..f188dd893 100644 --- a/src/actions.js +++ b/src/actions.js @@ -913,8 +913,8 @@ export const networkSettingsUpdateState = (customNetwork) => ({ * network: string, * nodeUrl: string, * explorerUrl: string, - * txMiningServiceUrl: string, * explorerServiceUrl: string, + * txMiningServiceUrl: string, * walletServiceUrl?: string * walletServiceWsUrl?: string * }} customNetwork Settings to persist diff --git a/src/reducers/reducer.js b/src/reducers/reducer.js index 0c121c622..98f2d1700 100644 --- a/src/reducers/reducer.js +++ b/src/reducers/reducer.js @@ -227,6 +227,18 @@ const initialState = { featureToggles: { ...FEATURE_TOGGLE_DEFAULTS, }, + /** + * @param {{ + * stage: string; + * network: string; + * nodeUrl: string; + * explorerUrl: string; + * explorerServiceUrl: string; + * txMiningServiceUrl: string; + * walletServiceUrl: string; + * walletServiceWsUrl: string; + * }} + */ networkSettings: PRE_SETTINGS_MAINNET, networkSettingsInvalid: {}, networkSettingsStatus: NETWORKSETTINGS_STATUS.READY, diff --git a/src/sagas/networkSettings.js b/src/sagas/networkSettings.js index eb445b835..f1af235f1 100644 --- a/src/sagas/networkSettings.js +++ b/src/sagas/networkSettings.js @@ -6,7 +6,6 @@ import { networkSettingsPersistStore, networkSettingsUpdateInvalid, networkSettingsUpdateFailure, - networkSettingsUpdateState, networkSettingsUpdateSuccess, networkSettingsUpdateWaiting, types, @@ -36,11 +35,6 @@ import { isWalletServiceEnabled } from './wallet'; * Initialize the network settings saga when the wallet starts successfully. */ export function* initNetworkSettings() { - const customNetwork = STORE.getItem(networkSettingsKeyMap.networkSettings); - if (customNetwork) { - yield put(networkSettingsUpdateState(customNetwork)); - } - const status = yield select((state) => state.networkSettingsStatus); if (status === NETWORKSETTINGS_STATUS.WAITING) { // This branch completes the network update by delivering @@ -60,13 +54,14 @@ export function* initNetworkSettings() { * * @param {{ * payload: { - * stage: string, - * network: string, - * nodeUrl: string, - * explorerUrl: string, - * explorerServiceUrl: string, - * walletServiceUrl?: string - * walletServiceWsUrl?: string + * stage: string; + * network: string; + * nodeUrl: string; + * explorerUrl: string; + * explorerServiceUrl: string; + * txMiningServiceUrl: string; + * walletServiceUrl?: string; + * walletServiceWsUrl?: string; * } * }} action contains the payload with the new * network settings requested by the user to be processd. @@ -148,15 +143,15 @@ export function* updateNetworkSettings(action) { txMiningServiceUrl: networkSettings.txMiningServiceUrl, }; + config.setTxMiningUrl(txMiningServiceUrl); config.setExplorerServiceBaseUrl(explorerServiceUrl); config.setServerUrl(nodeUrl); - config.setTxMiningUrl(txMiningServiceUrl); // - walletServiceUrl has precedence // - nodeUrl as fallback let potentialNetwork; let network; - if (walletServiceUrl && useWalletService) { + if (useWalletService && !isEmpty(walletServiceUrl)) { config.setWalletServiceBaseUrl(walletServiceUrl); config.setWalletServiceBaseWsUrl(walletServiceWsUrl); @@ -222,6 +217,7 @@ export function* updateNetworkSettings(action) { nodeUrl, explorerUrl, explorerServiceUrl, + txMiningServiceUrl, walletServiceUrl, walletServiceWsUrl, }; diff --git a/src/sagas/wallet.js b/src/sagas/wallet.js index 25c25f8c9..c275e7382 100644 --- a/src/sagas/wallet.js +++ b/src/sagas/wallet.js @@ -31,11 +31,12 @@ import { } from 'redux-saga/effects'; import { eventChannel } from 'redux-saga'; import { getUniqueId } from 'react-native-device-info'; -import { get } from 'lodash'; +import { get, isEmpty } from 'lodash'; import { DEFAULT_TOKEN, WALLET_SERVICE_FEATURE_TOGGLE, PUSH_NOTIFICATION_FEATURE_TOGGLE, + networkSettingsKeyMap, } from '../constants'; import { STORE } from '../store'; import { @@ -63,6 +64,7 @@ import { resetWalletSuccess, setTokens, onExceptionCaptured, + networkSettingsUpdateState, } from '../actions'; import { fetchTokenData } from './tokens'; import { @@ -151,10 +153,29 @@ export function* startWallet(action) { dispatch = _dispatch; }); - const networkSettings = yield select(getNetworkSettings); + // Network settings either from store or redux state + let networkSettings; + // Custom network settings are persisted in the app storage + const customNetwork = STORE.getItem(networkSettingsKeyMap.networkSettings); + if (customNetwork) { + networkSettings = customNetwork; + // On custom network settings one may use a different + // URL for the services from the ones registered by default + // for mainnet and testnet in the lib, and the wallet must + // behave consistently to the URLs set + config.setExplorerServiceBaseUrl(networkSettings.explorerServiceUrl); + config.setServerUrl(networkSettings.nodeUrl); + config.setTxMiningUrl(networkSettings.txMiningServiceUrl); + + // If the wallet is initialized from quit state it must + // update the network settings on redux state + yield put(networkSettingsUpdateState(networkSettings)); + } else { + networkSettings = yield select(getNetworkSettings); + } let wallet; - if (useWalletService) { + if (useWalletService && !isEmpty(networkSettings.walletServiceUrl)) { const network = new Network(networkSettings.network); // Set urls for wallet service diff --git a/src/screens/Settings.js b/src/screens/Settings.js index 3fd30fc31..150364eda 100644 --- a/src/screens/Settings.js +++ b/src/screens/Settings.js @@ -14,6 +14,7 @@ import { Text, View, } from 'react-native'; +import { isEmpty } from 'lodash'; import OfflineBar from '../components/OfflineBar'; import Logo from '../components/Logo'; import { HathorList, ListItem, ListMenu } from '../components/HathorList'; @@ -32,9 +33,15 @@ import { isPushNotificationAvailableForUser } from '../utils'; * server {str} URL of server this wallet is connected to */ const mapStateToProps = (state) => { - const server = state.useWalletService - ? state.wallet.storage.config.getWalletServiceBaseUrl() - : state.wallet.storage.config.getServerUrl(); + let server; + const { walletServiceUrl } = state.networkSettings; + if (state.useWalletService && !isEmpty(walletServiceUrl)) { + server = walletServiceUrl; + } + + if (!server) { + server = state.networkSettings.nodeUrl; + } return { selectedToken: state.selectedToken, From bb06304ac4e5edb268bdb176ce988461a7d361ab Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Tue, 16 Apr 2024 15:52:13 +0100 Subject: [PATCH 18/23] bump: v0.27.0-rc.4 (#468) --- android/app/build.gradle | 4 ++-- ios/HathorMobile.xcodeproj/project.pbxproj | 4 ++-- package-lock.json | 4 ++-- package.json | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 3679295d6..59434a7d5 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -79,8 +79,8 @@ android { applicationId "network.hathor.wallet" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 66 - versionName "0.27.0-rc.3" + versionCode 67 + versionName "0.27.0-rc.4" missingDimensionStrategy "react-native-camera", "general" } signingConfigs { diff --git a/ios/HathorMobile.xcodeproj/project.pbxproj b/ios/HathorMobile.xcodeproj/project.pbxproj index f961252b2..2230345db 100644 --- a/ios/HathorMobile.xcodeproj/project.pbxproj +++ b/ios/HathorMobile.xcodeproj/project.pbxproj @@ -512,7 +512,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = HathorMobile/HathorMobile.entitlements; - CURRENT_PROJECT_VERSION = 0.3.0; + CURRENT_PROJECT_VERSION = 0.4.0; DEVELOPMENT_TEAM = 55SHY647CG; ENABLE_BITCODE = NO; INFOPLIST_FILE = HathorMobile/Info.plist; @@ -542,7 +542,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = HathorMobile/HathorMobile.entitlements; - CURRENT_PROJECT_VERSION = 0.3.0; + CURRENT_PROJECT_VERSION = 0.4.0; DEVELOPMENT_TEAM = 55SHY647CG; INFOPLIST_FILE = HathorMobile/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; diff --git a/package-lock.json b/package-lock.json index 032425f12..490d60dad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "HathorMobile", - "version": "0.27.0-rc.3", + "version": "0.27.0-rc.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "HathorMobile", - "version": "0.27.0-rc.3", + "version": "0.27.0-rc.4", "hasInstallScript": true, "dependencies": { "@fortawesome/fontawesome-svg-core": "1.2.36", diff --git a/package.json b/package.json index 90aba6ead..e803d6d1b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "HathorMobile", - "version": "0.27.0-rc.3", + "version": "0.27.0-rc.4", "engines": { "node": ">=18.0.0", "npm": ">=9.0.0" From dbf1c5b0a4a4bdbab91d6777626cdd30c8e4fad0 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Mon, 22 Apr 2024 18:42:43 +0100 Subject: [PATCH 19/23] fix: useWalletService by forcefully disabling it when wallet service URL is empty (#470) refactor: clean tx history and metadata first on wallet start --- src/sagas/wallet.js | 47 ++++++++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/src/sagas/wallet.js b/src/sagas/wallet.js index c275e7382..05c20b0f0 100644 --- a/src/sagas/wallet.js +++ b/src/sagas/wallet.js @@ -122,7 +122,17 @@ export function* isWalletServiceEnabled() { yield call(() => AsyncStorage.removeItem(IGNORE_WS_TOGGLE_FLAG)); } - const walletServiceEnabled = yield call(checkForFeatureFlag, WALLET_SERVICE_FEATURE_TOGGLE); + let walletServiceEnabled = yield call(checkForFeatureFlag, WALLET_SERVICE_FEATURE_TOGGLE); + + // At this point, the networkSettings have already been set by startWallet. + const networkSettings = yield select(getNetworkSettings); + if (walletServiceEnabled && isEmpty(networkSettings.walletServiceUrl)) { + // In case of an empty value for walletServiceUrl, it means the user + // doesn't intend to use the Wallet Service. Therefore, we need to force + // a disable on it. + walletServiceEnabled = false; + yield put(setUseWalletService(false)); + } return walletServiceEnabled; } @@ -133,26 +143,14 @@ export function* startWallet(action) { pin, } = action.payload; - const uniqueDeviceId = getUniqueId(); - const useWalletService = yield call(isWalletServiceEnabled); - const usePushNotification = yield call(isPushNotificationEnabled); - - yield put(setUseWalletService(useWalletService)); - yield put(setAvailablePushNotification(usePushNotification)); - - // clean storage and metadata before starting the wallet - // this should be cleaned when stopping the wallet, + // clean memory storage and metadata before starting the wallet. + // This should be cleaned when stopping the wallet, // but the wallet may be closed unexpectedly const storage = STORE.getStorage(); - yield storage.store.cleanMetadata(); - yield storage.cleanStorage(true); - - // This is a work-around so we can dispatch actions from inside callbacks. - let dispatch; - yield put((_dispatch) => { - dispatch = _dispatch; - }); + yield call([storage.store, storage.store.cleanMetadata]); // clean metadata on memory + yield call([storage, storage.cleanStorage], true); // clean transaction history + // As this is a core setting for the wallet, it should be loaded first. // Network settings either from store or redux state let networkSettings; // Custom network settings are persisted in the app storage @@ -174,6 +172,19 @@ export function* startWallet(action) { networkSettings = yield select(getNetworkSettings); } + const uniqueDeviceId = getUniqueId(); + const useWalletService = yield call(isWalletServiceEnabled); + const usePushNotification = yield call(isPushNotificationEnabled); + + yield put(setUseWalletService(useWalletService)); + yield put(setAvailablePushNotification(usePushNotification)); + + // This is a work-around so we can dispatch actions from inside callbacks. + let dispatch; + yield put((_dispatch) => { + dispatch = _dispatch; + }); + let wallet; if (useWalletService && !isEmpty(networkSettings.walletServiceUrl)) { const network = new Network(networkSettings.network); From 4b01c207fed95e3383407e983068e13148c99ea6 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Tue, 23 Apr 2024 18:43:15 +0100 Subject: [PATCH 20/23] bump: v0.27.0-rc.5 (#472) --- android/app/build.gradle | 4 ++-- ios/HathorMobile.xcodeproj/project.pbxproj | 4 ++-- package-lock.json | 4 ++-- package.json | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 59434a7d5..34a6e4c8a 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -79,8 +79,8 @@ android { applicationId "network.hathor.wallet" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 67 - versionName "0.27.0-rc.4" + versionCode 68 + versionName "0.27.0-rc.5" missingDimensionStrategy "react-native-camera", "general" } signingConfigs { diff --git a/ios/HathorMobile.xcodeproj/project.pbxproj b/ios/HathorMobile.xcodeproj/project.pbxproj index 2230345db..a64b2f13c 100644 --- a/ios/HathorMobile.xcodeproj/project.pbxproj +++ b/ios/HathorMobile.xcodeproj/project.pbxproj @@ -512,7 +512,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = HathorMobile/HathorMobile.entitlements; - CURRENT_PROJECT_VERSION = 0.4.0; + CURRENT_PROJECT_VERSION = 0.5.0; DEVELOPMENT_TEAM = 55SHY647CG; ENABLE_BITCODE = NO; INFOPLIST_FILE = HathorMobile/Info.plist; @@ -542,7 +542,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = HathorMobile/HathorMobile.entitlements; - CURRENT_PROJECT_VERSION = 0.4.0; + CURRENT_PROJECT_VERSION = 0.5.0; DEVELOPMENT_TEAM = 55SHY647CG; INFOPLIST_FILE = HathorMobile/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; diff --git a/package-lock.json b/package-lock.json index 490d60dad..edb542b0f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "HathorMobile", - "version": "0.27.0-rc.4", + "version": "0.27.0-rc.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "HathorMobile", - "version": "0.27.0-rc.4", + "version": "0.27.0-rc.5", "hasInstallScript": true, "dependencies": { "@fortawesome/fontawesome-svg-core": "1.2.36", diff --git a/package.json b/package.json index e803d6d1b..eaec5cd67 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "HathorMobile", - "version": "0.27.0-rc.4", + "version": "0.27.0-rc.5", "engines": { "node": ">=18.0.0", "npm": ">=9.0.0" From 91d000a40c2ada3cce2536e9c4e7dd381d4e1fad Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Tue, 30 Apr 2024 21:21:03 +0100 Subject: [PATCH 21/23] fix: disable push notification if wallet service url is empty (#474) --- src/utils.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/utils.js b/src/utils.js index d595649c0..8b561795b 100644 --- a/src/utils.js +++ b/src/utils.js @@ -11,6 +11,7 @@ import React from 'react'; import { t } from 'ttag'; import { Linking, Platform, Text } from 'react-native'; import { getStatusBarHeight } from 'react-native-status-bar-height'; +import { isEmpty } from 'lodash'; import baseStyle from './styles/init'; import { KEYCHAIN_USER } from './constants'; import { STORE } from './store'; @@ -383,5 +384,10 @@ export function combineURLs(baseURL, relativeURL) { * @returns {Boolean} true if available, false otherwise. */ export const isPushNotificationAvailableForUser = (state) => ( - state.pushNotification.available && state.pushNotification.deviceRegistered + state.pushNotification.available + // On iOS a simulator can't register a device token on APNS + && state.pushNotification.deviceRegistered + // If Wallet Service URLs are empty it makes impossible to use the + // Wallet Service API to register the device's token. + && !isEmpty(state.networkSettings.walletServiceUrl) ); From b72f1dd57500d84a242747cfc91b019a67eb88b8 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Tue, 30 Apr 2024 21:21:03 +0100 Subject: [PATCH 22/23] fix: disable push notification if wallet service url is empty (#473) chore: add dependency --- src/utils.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/utils.js b/src/utils.js index 8b561795b..86436201c 100644 --- a/src/utils.js +++ b/src/utils.js @@ -13,7 +13,7 @@ import { Linking, Platform, Text } from 'react-native'; import { getStatusBarHeight } from 'react-native-status-bar-height'; import { isEmpty } from 'lodash'; import baseStyle from './styles/init'; -import { KEYCHAIN_USER } from './constants'; +import { KEYCHAIN_USER, NETWORK_MAINNET } from './constants'; import { STORE } from './store'; import { TxHistory } from './models'; import { COLORS, STYLE } from './styles/themes'; @@ -387,6 +387,11 @@ export const isPushNotificationAvailableForUser = (state) => ( state.pushNotification.available // On iOS a simulator can't register a device token on APNS && state.pushNotification.deviceRegistered + // TODO: We should drop this condition when we add support other networks + // XXX: We don't have support in this app to generate device tokens + // to the FCM testnet app. Currently we embbed only the mainnet + // configuration file during the build. + && state.networkSettings.network === NETWORK_MAINNET // If Wallet Service URLs are empty it makes impossible to use the // Wallet Service API to register the device's token. && !isEmpty(state.networkSettings.walletServiceUrl) From 4a0f2e82b0b5370414886c74d11333cb245f809e Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Fri, 3 May 2024 22:37:56 +0100 Subject: [PATCH 23/23] chore: bump v0.27.0-rc.6 (#477) --- android/app/build.gradle | 4 ++-- ios/HathorMobile.xcodeproj/project.pbxproj | 4 ++-- package-lock.json | 4 ++-- package.json | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 34a6e4c8a..e01054374 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -79,8 +79,8 @@ android { applicationId "network.hathor.wallet" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 68 - versionName "0.27.0-rc.5" + versionCode 69 + versionName "0.27.0-rc.6" missingDimensionStrategy "react-native-camera", "general" } signingConfigs { diff --git a/ios/HathorMobile.xcodeproj/project.pbxproj b/ios/HathorMobile.xcodeproj/project.pbxproj index a64b2f13c..1740df2e5 100644 --- a/ios/HathorMobile.xcodeproj/project.pbxproj +++ b/ios/HathorMobile.xcodeproj/project.pbxproj @@ -512,7 +512,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = HathorMobile/HathorMobile.entitlements; - CURRENT_PROJECT_VERSION = 0.5.0; + CURRENT_PROJECT_VERSION = 0.6.0; DEVELOPMENT_TEAM = 55SHY647CG; ENABLE_BITCODE = NO; INFOPLIST_FILE = HathorMobile/Info.plist; @@ -542,7 +542,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = HathorMobile/HathorMobile.entitlements; - CURRENT_PROJECT_VERSION = 0.5.0; + CURRENT_PROJECT_VERSION = 0.6.0; DEVELOPMENT_TEAM = 55SHY647CG; INFOPLIST_FILE = HathorMobile/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; diff --git a/package-lock.json b/package-lock.json index edb542b0f..1e72e4509 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "HathorMobile", - "version": "0.27.0-rc.5", + "version": "0.27.0-rc.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "HathorMobile", - "version": "0.27.0-rc.5", + "version": "0.27.0-rc.6", "hasInstallScript": true, "dependencies": { "@fortawesome/fontawesome-svg-core": "1.2.36", diff --git a/package.json b/package.json index eaec5cd67..ba569c195 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "HathorMobile", - "version": "0.27.0-rc.5", + "version": "0.27.0-rc.6", "engines": { "node": ">=18.0.0", "npm": ">=9.0.0"