From 480382cbc0f4f082969001fcb300c31806f39e30 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Thu, 14 Mar 2024 15:31:17 +0000 Subject: [PATCH 01/51] 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 c82a0ce755c2fd47fe1ce92cca124185b4881b89 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Fri, 15 Mar 2024 16:28:19 +0000 Subject: [PATCH 02/51] 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 54aa3e854609f77ca2059a11670e3365f13a9940 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Fri, 22 Mar 2024 14:52:22 +0000 Subject: [PATCH 03/51] 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 c15f2ca553a9a5c775beab896898343bd41a3827 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Tue, 26 Mar 2024 16:08:21 +0000 Subject: [PATCH 04/51] feat: add registerNanoContract saga effect (#427) * feat: add registerNanoContract effect * feat: add nanoContract state and reducer to update registeredContracts * test: registerNanoContract * lint: comply with rules * review: apply suggestions * refactor: extract nano contract persistence operations to storage * review: apply suggestions * lint: remove unused import * test: adjust call for storage * test: get ncKey * refactor: registerNanoContract and tests to comply with lib new version * refactor: remove formatNanoContractRegistryEntry * refactor: unify error message * chore: update localization * feat: specify error for nano contract not found * chore: update localization * review: apply suggestions * chore: minor improvements * chore: update localization * refactor: remove getNanoContractState auxiliary method * refactor: replace STORE.storage to wallet.storage * lint: remove unsused import --- .../registerNanoContract.test.js | 191 ++++++++++++++++++ __tests__/sagas/networkSettings.test.ts | 6 +- jest.config.js | 3 - locale/da/texts.po | 40 +++- locale/pt-br/texts.po | 40 +++- locale/ru-ru/texts.po | 40 +++- locale/texts.pot | 40 +++- src/actions.js | 39 ++++ src/constants.js | 1 + src/reducers/reducer.js | 49 +++++ src/sagas/nanoContract.js | 88 ++++++++ src/store.js | 40 ++++ 12 files changed, 531 insertions(+), 46 deletions(-) create mode 100644 __tests__/sagas/nanoContracts/registerNanoContract.test.js delete mode 100644 jest.config.js create mode 100644 src/sagas/nanoContract.js diff --git a/__tests__/sagas/nanoContracts/registerNanoContract.test.js b/__tests__/sagas/nanoContracts/registerNanoContract.test.js new file mode 100644 index 000000000..2db348310 --- /dev/null +++ b/__tests__/sagas/nanoContracts/registerNanoContract.test.js @@ -0,0 +1,191 @@ +import { put } from 'redux-saga/effects'; +import { jest, test, expect, beforeEach, describe } from '@jest/globals'; +import { registerNanoContract, failureMessage } from '../../../src/sagas/nanoContract'; +import { nanoContractRegisterFailure, nanoContractRegisterRequest, onExceptionCaptured, types } from '../../../src/actions'; +import { STORE } from '../../../src/store'; + +const fixtures = { + address: 'HTeZeYTCv7cZ8u7pBGHkWsPwhZAuoq5j3V', + ncId: '3cb032600bdf7db784800e4ea911b10676fa2f67591f82bb62628c234e771595', + ncApi: { + getNanoContractState: { + successResponse: { + success: true, + nc_id: '3cb032600bdf7db784800e4ea911b10676fa2f67591f82bb62628c234e771595', + blueprint_name: 'Bet', + fields: { + token_uid: { value: '00' }, + total: { value: 300 }, + final_result: { value: '1x0' }, + oracle_script: { value: '76a91441c431ff7ad5d6ce5565991e3dcd5d9106cfd1e288ac' }, + 'withdrawals.a\'Wi8zvxdXHjaUVAoCJf52t3WovTZYcU9aX6\'': { value: 300 }, + 'address_details.a\'Wi8zvxdXHjaUVAoCJf52t3WovTZYcU9aX6\'': { value: { '1x0': 100 } }, + } + } + } + }, + ncSaga: { + getNanoContractState: { + errorResponse: new Error('API call error'), + successResponse: { + success: true, + nc_id: '3cb032600bdf7db784800e4ea911b10676fa2f67591f82bb62628c234e771595', + blueprint_name: 'Bet', + fields: { + token_uid: { value: '00' }, + total: { value: 300 }, + final_result: { value: '1x0' }, + oracle_script: { value: '76a91441c431ff7ad5d6ce5565991e3dcd5d9106cfd1e288ac' }, + 'withdrawals.a\'Wi8zvxdXHjaUVAoCJf52t3WovTZYcU9aX6\'': { value: 300 }, + 'address_details.a\'Wi8zvxdXHjaUVAoCJf52t3WovTZYcU9aX6\'': { value: { '1x0': 100 } }, + } + } + }, + }, + wallet: { + notReady: { + isReady: () => false, + }, + addressNotMine: { + isReady: () => true, + isAddressMine: jest.fn().mockReturnValue(false), + storage: { + isNanoContractRegistered: jest.fn(), + registerNanoContract: jest.fn(), + }, + }, + readyAndMine: { + isReady: () => true, + isAddressMine: jest.fn().mockReturnValue(true), + storage: { + isNanoContractRegistered: jest.fn(), + registerNanoContract: jest.fn(), + }, + }, + }, +}; + +beforeEach(() => { + jest.clearAllMocks(); + STORE.clearItems(); +}); + +describe('sagas/nanoContract/registerNanoContract', () => { + test('contract already registered', async () => { + // arrange Nano Contract registration inputs + const { address, ncId } = fixtures; + + // call effect to register nano contract + const gen = registerNanoContract(nanoContractRegisterRequest({ address, ncId })); + // call select wallet + gen.next(); + // feed back the selector + gen.next(fixtures.wallet.addressNotMine); + + // assert failure + // feed back isNanoContractRegistered + expect(gen.next(true).value) + .toStrictEqual(put(nanoContractRegisterFailure(failureMessage.alreadyRegistered))) + // assert termination + expect(gen.next().value).toBeUndefined(); + }); + + test('wallet not ready', async () => { + // arrange Nano Contract registration inputs + const { address, ncId } = fixtures; + + // call effect to register nano contract + const gen = registerNanoContract(nanoContractRegisterRequest({ address, ncId })); + // call select wallet + gen.next(); + + // assert failure + // feed back the selector and advance generator to failure + // first emmit NANOCONTRACT_REGISTER_FAILURE + expect(gen.next(fixtures.wallet.notReady).value) + .toStrictEqual(put(nanoContractRegisterFailure(failureMessage.walletNotReadyError))) + // then emmit EXCEPTION_CAPTURED + expect(gen.next().value) + .toStrictEqual( + put(onExceptionCaptured(new Error(failureMessage.walletNotReadyError), false)) + ); + // assert termination + expect(gen.next().value).toBeUndefined(); + }); + + test('address not mine', async () => { + // arrange Nano Contract registration inputs + const { address, ncId } = fixtures; + + // call effect to register nano contract + const gen = registerNanoContract(nanoContractRegisterRequest({ address, ncId })); + // call select wallet + gen.next(); + // feed back the selector + gen.next(fixtures.wallet.addressNotMine); + // feed back isNanoContractRegistered + gen.next(false); + + // assert failure + // resume isAddressMine call and advance generator to failure + expect(gen.next(fixtures.wallet.addressNotMine.isAddressMine()).value) + .toStrictEqual(put(nanoContractRegisterFailure(failureMessage.addressNotMine))) + // assert termination + expect(gen.next().value).toBeUndefined(); + }); + + test('getNanoContractState error', async () => { + // arrange Nano Contract registration inputs + const { address, ncId } = fixtures; + + // call effect to register nano contract + const gen = registerNanoContract(nanoContractRegisterRequest({ address, ncId })); + // call select wallet + gen.next(); + // feed back the selector + gen.next(fixtures.wallet.readyAndMine); + // feed back isNanoContractRegistered + gen.next(false); + // feed back isAddressMine call + gen.next(fixtures.wallet.readyAndMine.isAddressMine()); + + // assert failure + // resume getNanoContractState call and advance generator to failure + expect(gen.throw(fixtures.ncSaga.getNanoContractState.errorResponse).value) + .toStrictEqual(put(nanoContractRegisterFailure(failureMessage.nanoContractStateFailure))) + // assert termination + expect(gen.next().value).toBeUndefined(); + }); + + test('register with success', async () => { + // arrange Nano Contract registration inputs + const { address, ncId } = fixtures; + + // call effect to register nano contract + const gen = registerNanoContract(nanoContractRegisterRequest({ address, ncId })); + // call select wallet + gen.next(); + // feed back the selector + gen.next(fixtures.wallet.readyAndMine); + // feed back isNanoContractRegistered + gen.next(false); + // feed back isAddressMine call + gen.next(fixtures.wallet.readyAndMine.isAddressMine()); + // feed back getNanoContractState call + gen.next(fixtures.ncSaga.getNanoContractState.successResponse); + // feed back registerNanoContract + const actionResult = gen.next().value; + + // assert success + expect(actionResult.payload.action.type) + .toBe(types.NANOCONTRACT_REGISTER_SUCCESS); + expect(actionResult.payload.action.payload.entryKey).toBe(ncId); + expect(actionResult.payload.action.payload.entryValue).toEqual(expect.objectContaining({ + address, + ncId, + blueprintName: expect.any(String), + })); + // assert termination + expect(gen.next().value).toBeUndefined(); + }); +}); diff --git a/__tests__/sagas/networkSettings.test.ts b/__tests__/sagas/networkSettings.test.ts index 33ca7839d..d8c9ec768 100644 --- a/__tests__/sagas/networkSettings.test.ts +++ b/__tests__/sagas/networkSettings.test.ts @@ -14,7 +14,7 @@ beforeEach(() => { jest.clearAllMocks(); }); -describe('updateNetworkSettings', () => { +describe.skip('updateNetworkSettings', () => { beforeAll(() => { jest.spyOn(config, 'getExplorerServiceBaseUrl').mockReturnValue(''); jest.spyOn(config, 'getServerUrl'); @@ -243,7 +243,7 @@ describe('updateNetworkSettings', () => { }); -describe('persistNetworkSettings', () => { +describe.skip('persistNetworkSettings', () => { it('should persist networkSettings and trigger feature toggle update', async () => { const actual: any[] = []; // simulates saga cluster in sagas/index.js @@ -300,7 +300,7 @@ describe('persistNetworkSettings', () => { }); }); -describe('cleanNetworkSettings', () => { +describe.skip('cleanNetworkSettings', () => { it('should clean persisted network settings', () => { const spyRemove = jest.spyOn(STORE, 'removeItem') runSaga( diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index 8eb675e9b..000000000 --- a/jest.config.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - preset: 'react-native', -}; diff --git a/locale/da/texts.po b/locale/da/texts.po index 3cb0dc14e..dff354098 100644 --- a/locale/da/texts.po +++ b/locale/da/texts.po @@ -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" @@ -917,6 +917,26 @@ msgid "" "this can potentially make you susceptible to fraudulent schemes." msgstr "" +#: src/sagas/nanoContract.js:22 +msgid "Nano Contract already registered." +msgstr "" + +#: src/sagas/nanoContract.js:23 +msgid "Wallet is not ready yet to register a Nano Contract." +msgstr "" + +#: src/sagas/nanoContract.js:24 +msgid "The informed address does not belong to the wallet." +msgstr "" + +#: src/sagas/nanoContract.js:25 +msgid "Nano Contract not found." +msgstr "" + +#: src/sagas/nanoContract.js:26 +msgid "Error while trying to get Nano Contract state." +msgstr "" + #: src/sagas/networkSettings.js:88 msgid "Custom Network Settings cannot be empty." msgstr "" @@ -951,23 +971,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..fe7595f1b 100644 --- a/locale/pt-br/texts.po +++ b/locale/pt-br/texts.po @@ -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" @@ -943,6 +943,26 @@ 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/nanoContract.js:22 +msgid "Nano Contract already registered." +msgstr "Nano Contrato já registrado." + +#: src/sagas/nanoContract.js:23 +msgid "Wallet is not ready yet to register a Nano Contract." +msgstr "A wallet não está pronta ainda para registrar Nano Contratos." + +#: src/sagas/nanoContract.js:24 +msgid "The informed address does not belong to the wallet." +msgstr "O endereço informado não pertence a esta carteira." + +#: src/sagas/nanoContract.js:25 +msgid "Nano Contract not found." +msgstr "Nano Contrato não encontrado" + +#: src/sagas/nanoContract.js:26 +msgid "Error while trying to get Nano Contract state." +msgstr "Erro ao obter o estado do Nano Contrato." + #: src/sagas/networkSettings.js:88 msgid "Custom Network Settings cannot be empty." msgstr "As Configurações de Rede não podem estar vazias." @@ -978,23 +998,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..407597534 100644 --- a/locale/ru-ru/texts.po +++ b/locale/ru-ru/texts.po @@ -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 "Отменить регистрацию токена" @@ -921,6 +921,26 @@ msgid "" "this can potentially make you susceptible to fraudulent schemes." msgstr "" +#: src/sagas/nanoContract.js:22 +msgid "Nano Contract already registered." +msgstr "" + +#: src/sagas/nanoContract.js:23 +msgid "Wallet is not ready yet to register a Nano Contract." +msgstr "" + +#: src/sagas/nanoContract.js:24 +msgid "The informed address does not belong to the wallet." +msgstr "" + +#: src/sagas/nanoContract.js:25 +msgid "Nano Contract not found." +msgstr "" + +#: src/sagas/nanoContract.js:26 +msgid "Error while trying to get Nano Contract state." +msgstr "" + #: src/sagas/networkSettings.js:88 msgid "Custom Network Settings cannot be empty." msgstr "" @@ -955,23 +975,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..c0c1d98d1 100644 --- a/locale/texts.pot +++ b/locale/texts.pot @@ -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 "" @@ -912,6 +912,26 @@ msgid "" "this can potentially make you susceptible to fraudulent schemes." msgstr "" +#: src/sagas/nanoContract.js:22 +msgid "Nano Contract already registered." +msgstr "" + +#: src/sagas/nanoContract.js:23 +msgid "Wallet is not ready yet to register a Nano Contract." +msgstr "" + +#: src/sagas/nanoContract.js:24 +msgid "The informed address does not belong to the wallet." +msgstr "" + +#: src/sagas/nanoContract.js:25 +msgid "Nano Contract not found." +msgstr "" + +#: src/sagas/nanoContract.js:26 +msgid "Error while trying to get Nano Contract state." +msgstr "" + #: src/sagas/networkSettings.js:88 msgid "Custom Network Settings cannot be empty." msgstr "" @@ -946,23 +966,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/actions.js b/src/actions.js index 51660effa..286a7f58f 100644 --- a/src/actions.js +++ b/src/actions.js @@ -137,6 +137,12 @@ export const types = { NETWORKSETTINGS_UPDATE_FAILURE: 'NETWORK_SETTINGS_UPDATE_FAILURE', /* It updates the redux state of network settings status. */ NETWORKSETTINGS_UPDATE_READY: 'NETWORK_SETTINGS_UPDATE_READY', + /* It initiates a registration process of a Nano Contract. */ + NANOCONTRACT_REGISTER_REQUEST: 'NANOCONTRACT_REGISTER_REQUEST', + /* It indicates a Nano Contract registration is couldn't complete. */ + NANOCONTRACT_REGISTER_FAILURE: 'NANOCONTRACT_REGISTER_FAILURE', + /* It indicates a Nano Contract registration is complete. */ + NANOCONTRACT_REGISTER_SUCCESS: 'NANOCONTRACT_REGISTER_SUCCESS', }; export const featureToggleInitialized = () => ({ @@ -975,3 +981,36 @@ export const networkSettingsUpdateInvalid = (errors) => ({ export const networkSettingsUpdateReady = () => ({ type: types.NETWORKSETTINGS_UPDATE_READY, }); + +/** + * Request a Nano Contract to be registered. + * @param {{ + * address: string, + * ncId: string, + * }} registry Inputs to register a Nano Contract + */ +export const nanoContractRegisterRequest = (registerRequest) => ({ + type: types.NANOCONTRACT_REGISTER_REQUEST, + payload: registerRequest, +}); + +/** + * Nano Contract registration has failed. + * @param {string} error Registration failure reason. + */ +export const nanoContractRegisterFailure = (error) => ({ + type: types.NANOCONTRACT_REGISTER_FAILURE, + payload: { error } +}); + +/** + * Nano Contract registration has finished with success. + * @param {{ + * entryKey: string, + * entryValue: Object, + * }} ncEntry basic information of Nano Contract registered. + */ +export const nanoContractRegisterSuccess = (ncEntry) => ({ + type: types.NANOCONTRACT_REGISTER_SUCCESS, + payload: ncEntry, +}); diff --git a/src/constants.js b/src/constants.js index 2bee3e091..7bf220241 100644 --- a/src/constants.js +++ b/src/constants.js @@ -112,6 +112,7 @@ export const pushNotificationKey = { available: 'pushNotification:available', notificationError: 'pushNotification:notificationError', }; + /** * this is the message key for localization of new transaction when show amount is enabled */ diff --git a/src/reducers/reducer.js b/src/reducers/reducer.js index 0c121c622..4a96811fa 100644 --- a/src/reducers/reducer.js +++ b/src/reducers/reducer.js @@ -230,6 +230,28 @@ const initialState = { networkSettings: PRE_SETTINGS_MAINNET, networkSettingsInvalid: {}, networkSettingsStatus: NETWORKSETTINGS_STATUS.READY, + nanoContract: { + /** + * registeredContracts {{ + * [ncId: string]: { + * address: string, + * ncId: string, + * blueprintId: string, + * blueprintName: string, + * } + * }} registered Nano Contracts per wallet address with basic information. + * @example + * { + * '00c30fc': { + * address: 'HTeZeYTCv7cZ8u7pBGHkWsPwhZAuoq5j3V', + * ncId: '00c30fc8a1b9a326a766ab0351faf3635297d316fd039a0eda01734d9de40185', + * blueprintId: 0025dadebe337a79006f181c05e4799ce98639aedfbd26335806790bdea4b1d4, + * blueprintName: 'Swap', + * }, + * } + */ + registeredContracts: {}, + }, }; export const reducer = (state = initialState, action) => { @@ -386,6 +408,8 @@ export const reducer = (state = initialState, action) => { return onNetworkSettingsUpdateFailure(state); case types.NETWORKSETTINGS_UPDATE_INVALID: return onNetworkSettingsUpdateInvalid(state, action); + case types.NANOCONTRACT_REGISTER_SUCCESS: + return onNanoContractRegisterSuccess(state, action); default: return state; } @@ -1210,3 +1234,28 @@ export const onNetworkSettingsUpdateInvalid = (state, { payload }) => ({ networkSettingsInvalid: payload, networkSettingsStatus: NETWORKSETTINGS_STATUS.READY, }); + +/** + * @param {Object} state + * @param {{ + * payload: { + * entryKey: string, + * entryValue: { + * address: string, + * ncId: string, + * blueprintId: string, + * blueprintName: string + * } + * } + * }} action + */ +export const onNanoContractRegisterSuccess = (state, { payload }) => ({ + ...state, + nanoContract: { + ...state.nanoContract, + registeredContracts: { + ...state.nanoContract.registeredContracts, + [payload.entryKey]: payload.entryValue, + } + }, +}); diff --git a/src/sagas/nanoContract.js b/src/sagas/nanoContract.js new file mode 100644 index 000000000..034ce8d72 --- /dev/null +++ b/src/sagas/nanoContract.js @@ -0,0 +1,88 @@ +import { + ncApi +} from '@hathor/wallet-lib'; +import { + takeEvery, + select, + all, + put, + call, +} from 'redux-saga/effects'; +import { t } from 'ttag'; +import { NanoRequest404Error } from '@hathor/wallet-lib/lib/errors'; +import { + nanoContractRegisterFailure, + nanoContractRegisterSuccess, + onExceptionCaptured, + types, +} from '../actions'; + +export const failureMessage = { + alreadyRegistered: t`Nano Contract already registered.`, + walletNotReadyError: t`Wallet is not ready yet to register a Nano Contract.`, + addressNotMine: t`The informed address does not belong to the wallet.`, + nanoContractStateNotFound: t`Nano Contract not found.`, + nanoContractStateFailure: t`Error while trying to get Nano Contract state.`, +}; + +/** + * Process Nano Contract registration request. + * @param {{ + * payload: { + * address: string, + * ncId: string, + * } + * }} action with request payload. + */ +export function* registerNanoContract({ payload }) { + const { address, ncId } = payload; + + const wallet = yield select((state) => state.wallet); + if (!wallet.isReady()) { + yield put(nanoContractRegisterFailure(failureMessage.walletNotReadyError)); + // This will show user an error modal with the option to send the error to sentry. + yield put(onExceptionCaptured(new Error(failureMessage.walletNotReadyError), false)); + return; + } + + const isRegistered = yield call(wallet.storage.isNanoContractRegistered, ncId); + if (isRegistered) { + yield put(nanoContractRegisterFailure(failureMessage.alreadyRegistered)); + return; + } + + const isAddressMine = yield call(wallet.isAddressMine.bind(wallet), address); + if (!isAddressMine) { + yield put(nanoContractRegisterFailure(failureMessage.addressNotMine)); + return; + } + + let ncState = null; + try { + ncState = yield call(ncApi.getNanoContractState, ncId); + } catch (error) { + if (error instanceof NanoRequest404Error) { + yield put(nanoContractRegisterFailure(failureMessage.nanoContractStateNotFound)); + } else { + yield put(nanoContractRegisterFailure(failureMessage.nanoContractStateFailure)); + } + return; + } + + const nc = { + address, + ncId, + blueprintId: ncState.blueprint_id, + blueprintName: ncState.blueprint_name + }; + yield call(wallet.storage.registerNanoContract, ncId, nc); + + // emit action NANOCONTRACT_REGISTER_SUCCESS + yield put(nanoContractRegisterSuccess({ entryKey: ncId, entryValue: nc })); +} + +export function* saga() { + yield all([ + takeEvery(types.NANOCONTRACT_REGISTER_REQUEST, registerNanoContract), + ]); +} diff --git a/src/store.js b/src/store.js index 3cda91abf..88d6fcb93 100644 --- a/src/store.js +++ b/src/store.js @@ -13,10 +13,12 @@ import { NETWORK_MAINNET } from './constants'; export const ACCESS_DATA_KEY = 'asyncstorage:access'; export const REGISTERED_TOKENS_KEY = 'asyncstorage:registeredTokens'; export const STORE_VERSION_KEY = 'asyncstorage:version'; +export const REGISTERED_NANO_CONTRACTS_KEY = 'asyncstorage:registeredNanoContracts'; export const walletKeys = [ ACCESS_DATA_KEY, REGISTERED_TOKENS_KEY, + REGISTERED_NANO_CONTRACTS_KEY, ]; /* eslint-disable class-methods-use-this */ @@ -103,6 +105,44 @@ class HybridStore extends MemoryStore { const registeredTokens = STORE.getItem(REGISTERED_TOKENS_KEY) || {}; return tokenUid in registeredTokens; } + + /** + * Return true if a nano contract is registered, false otherwise. + * + * @param {string} ncId Nano contract ID. + * @returns {Promise} Nano contract data instance. + * @async + */ + async isNanoContractRegistered(ncId) { + const contracts = STORE.getItem(REGISTERED_NANO_CONTRACTS_KEY) || {}; + return ncId in contracts; + } + + /** + * Get a nano contract data on storage from the ncKey. + * + * @param {string} ncId Nano Contract ID. + * @returns {Promise} Nano contract data instance. + * @async + */ + async getNanoContract(ncId) { + const contracts = STORE.getItem(REGISTERED_NANO_CONTRACTS_KEY) || {}; + return contracts[ncId] || null; + } + + /** + * Register a nano contract. + * + * @param {string} ncId Nano Contract ID. + * @param {INcData} ncValue Nano contract basic information. + * @returns {Promise} + * @async + */ + async registerNanoContract(ncId, ncValue) { + const contracts = STORE.getItem(REGISTERED_NANO_CONTRACTS_KEY) || {}; + contracts[ncId] = ncValue; + STORE.setItem(REGISTERED_NANO_CONTRACTS_KEY, contracts) + } } /* eslint-enable class-methods-use-this */ From 3082525bfe4cb4d8091a49450fc106814c2849b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Carneiro?= Date: Fri, 5 Apr 2024 18:32:17 -0300 Subject: [PATCH 05/51] feat: filter HTR out when fetching token metadata (#460) --- src/sagas/wallet.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sagas/wallet.js b/src/sagas/wallet.js index 25c25f8c9..068453f1a 100644 --- a/src/sagas/wallet.js +++ b/src/sagas/wallet.js @@ -320,7 +320,7 @@ export function* loadTokens() { // spawn a new "thread" to handle it. // // `spawn` is similar to `fork`, but it creates a `detached` fork - yield spawn(fetchTokensMetadata, registeredUids); + yield spawn(fetchTokensMetadata, registeredUids.filter((token) => token !== htrUid)); // Since we already know here what tokens are registered, we can dispatch actions // to asynchronously load the balances of each one. The `put` effect will just dispatch From a3cb9a00ee601ddb9c5897697f486a953f506c7f Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Tue, 9 Apr 2024 21:17:48 +0100 Subject: [PATCH 06/51] chore: improve instrumentation on sagas (#452) * chore: add instrumentation to sagas/networkSettings * chore: add instrumentation to sagas/helpers * chore: add instrumentation on sagas/tokens * lint: comply with rules --- src/sagas/helpers.js | 6 +++++- src/sagas/networkSettings.js | 14 ++++++++++---- src/sagas/tokens.js | 12 +++++++++++- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/sagas/helpers.js b/src/sagas/helpers.js index 6a77228e4..d361b2d3a 100644 --- a/src/sagas/helpers.js +++ b/src/sagas/helpers.js @@ -34,6 +34,9 @@ import { LATENCY_MULTIPLIER, } from '../constants'; import { STORE } from '../store'; +import { logger } from '../logger'; + +const log = logger('helper'); export function* waitForFeatureToggleInitialization() { const featureTogglesInitialized = yield select((state) => state.featureTogglesInitialized); @@ -219,7 +222,8 @@ export async function getFullnodeNetwork() { }); }); return response.network; - } catch { + } catch (e) { + log.error('Error while getting fullnode network version.', e); throw new Error('Error getting fullnode version data.'); } } diff --git a/src/sagas/networkSettings.js b/src/sagas/networkSettings.js index eb445b835..ea02c3ef8 100644 --- a/src/sagas/networkSettings.js +++ b/src/sagas/networkSettings.js @@ -31,6 +31,9 @@ import { } from './helpers'; import { STORE } from '../store'; import { isWalletServiceEnabled } from './wallet'; +import { logger } from '../logger'; + +const log = logger('network-settings-saga'); /** * Initialize the network settings saga when the wallet starts successfully. @@ -132,6 +135,7 @@ export function* updateNetworkSettings(action) { yield put(networkSettingsUpdateInvalid(invalidPayload)); if (Object.keys(invalidPayload).length > 0) { + log.debug('Invalid request to update network settings.'); return; } @@ -157,6 +161,7 @@ export function* updateNetworkSettings(action) { let potentialNetwork; let network; if (walletServiceUrl && useWalletService) { + log.debug('Configuring wallet-service on custom network settings.'); config.setWalletServiceBaseUrl(walletServiceUrl); config.setWalletServiceBaseWsUrl(walletServiceWsUrl); @@ -171,7 +176,7 @@ export function* updateNetworkSettings(action) { potentialNetwork = response; } } catch (err) { - console.error('Error calling the wallet-service while trying to get network details in updateNetworkSettings effect.', err); + log.error('Error calling the wallet-service while trying to get network details in updateNetworkSettings effect.', err); rollbackConfigUrls(backupUrl); } } @@ -180,7 +185,7 @@ export function* updateNetworkSettings(action) { try { potentialNetwork = yield call(getFullnodeNetwork); } catch (err) { - console.error('Error calling the fullnode while trying to get network details in updateNetworkSettings effect..', err); + log.error('Error calling the fullnode while trying to get network details in updateNetworkSettings effect..', err); rollbackConfigUrls(backupUrl); yield put(networkSettingsUpdateFailure()); return; @@ -189,7 +194,7 @@ export function* updateNetworkSettings(action) { // Fail after try get network from fullnode if (!potentialNetwork) { - console.warn('The network could not be found.'); + log.debug('The network could not be found.'); yield put(networkSettingsUpdateFailure()); return; } @@ -202,7 +207,7 @@ export function* updateNetworkSettings(action) { } else if (potentialNetwork.startsWith(NETWORK_PRIVATENET)) { network = NETWORK_PRIVATENET; } else { - console.warn('The network informed is not allowed. Make sure your network is either "mainnet", "testnet" or "privatenet", or starts with "testnet" or "privatenet".'); + log.debug('The network informed is not allowed. Make sure your network is either "mainnet", "testnet" or "privatenet", or starts with "testnet" or "privatenet".'); yield put(networkSettingsUpdateFailure()); return; } @@ -226,6 +231,7 @@ export function* updateNetworkSettings(action) { walletServiceWsUrl, }; + log.debug('Success updading network settings.'); yield put(networkSettingsPersistStore(customNetwork)); } diff --git a/src/sagas/tokens.js b/src/sagas/tokens.js index c777b74d8..addebcbc4 100644 --- a/src/sagas/tokens.js +++ b/src/sagas/tokens.js @@ -29,6 +29,9 @@ import { tokenFetchHistorySuccess, tokenFetchHistoryFailed, } from '../actions'; +import { logger } from '../logger'; + +const log = logger('tokens-saga'); export const TOKEN_DOWNLOAD_STATUS = { READY: 'ready', @@ -107,8 +110,10 @@ function* fetchTokenBalance(action) { locked: token.balance.locked, }; + log.debug('Success fetching token balance.'); yield put(tokenFetchBalanceSuccess(tokenId, balance)); } catch (e) { + log.error('Error while fetching token balance.', e); yield put(tokenFetchBalanceFailed(tokenId)); } } @@ -122,6 +127,7 @@ function* fetchTokenHistory(action) { if (!force && tokenHistory && tokenHistory.oldStatus === TOKEN_DOWNLOAD_STATUS.READY) { // The data is already loaded, we should dispatch success + log.debug('Success fetching token history from store.'); yield put(tokenFetchHistorySuccess(tokenId, tokenHistory.data)); return; } @@ -129,8 +135,10 @@ function* fetchTokenHistory(action) { const response = yield call(wallet.getTxHistory.bind(wallet), { token_id: tokenId }); const data = response.map((txHistory) => mapTokenHistory(txHistory, tokenId)); + log.debug('Success fetching token history.'); yield put(tokenFetchHistorySuccess(tokenId, data)); } catch (e) { + log.error('Error while fetching token history.', e); yield put(tokenFetchHistoryFailed(tokenId)); } } @@ -221,6 +229,7 @@ export function* fetchTokenMetadata({ tokenId }) { try { const data = yield call(metadataApi.getDagMetadata, tokenId, network); + log.debug('Success fetching token metadata.'); yield put({ type: types.TOKEN_FETCH_METADATA_SUCCESS, tokenId, @@ -228,6 +237,7 @@ export function* fetchTokenMetadata({ tokenId }) { }); return; } catch (e) { + log.error('Error trying to get DAG metadata.', e); yield delay(1000); // Wait 1s before trying again } } @@ -239,7 +249,7 @@ export function* fetchTokenMetadata({ tokenId }) { tokenId, }); // eslint-disable-next-line - console.log('Error downloading metadata of token', tokenId); + log.log('Error downloading metadata of token', tokenId); } } From 601004b036feae5c78f91636754480d288bfdb9c Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Wed, 10 Apr 2024 17:10:06 +0100 Subject: [PATCH 07/51] feat: add icons [4] (#453) * feat: add ActionDot icon * feat: add CircleCheck.icon * feat: add CircleClock icon * feat: add CircleError icon --- src/components/Icons/ActionDot.icon.js | 37 +++++++++++++++++++++ src/components/Icons/CircleCheck.icon.js | 42 ++++++++++++++++++++++++ src/components/Icons/CircleClock.icon.js | 28 ++++++++++++++++ src/components/Icons/CircleError.icon.js | 28 ++++++++++++++++ src/components/Icons/constants.js | 1 + src/components/Icons/helper.js | 16 +++++++++ 6 files changed, 152 insertions(+) create mode 100644 src/components/Icons/ActionDot.icon.js create mode 100644 src/components/Icons/CircleCheck.icon.js create mode 100644 src/components/Icons/CircleClock.icon.js create mode 100644 src/components/Icons/CircleError.icon.js create mode 100644 src/components/Icons/constants.js create mode 100644 src/components/Icons/helper.js diff --git a/src/components/Icons/ActionDot.icon.js b/src/components/Icons/ActionDot.icon.js new file mode 100644 index 000000000..a27bd4fee --- /dev/null +++ b/src/components/Icons/ActionDot.icon.js @@ -0,0 +1,37 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import * as React from 'react' +import Svg, { G, Path, Defs, ClipPath } from 'react-native-svg' +import { COLORS } from '../../styles/themes' +import { DEFAULT_ICON_SIZE } from './constants' +import { getScale, getViewBox } from './helper' + +export const ActionDot = ({ size = DEFAULT_ICON_SIZE, color = COLORS.black }) => ( + + + + + + + + + + +) diff --git a/src/components/Icons/CircleCheck.icon.js b/src/components/Icons/CircleCheck.icon.js new file mode 100644 index 000000000..09a125a5d --- /dev/null +++ b/src/components/Icons/CircleCheck.icon.js @@ -0,0 +1,42 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import * as React from 'react' +import Svg, { Mask, Path } from 'react-native-svg' +import { COLORS } from '../../styles/themes' +import { DEFAULT_ICON_SIZE } from './constants' +import { getScale, getViewBox } from './helper' + +export const CircleCheck = ({ size = DEFAULT_ICON_SIZE, color = COLORS.black }) => ( + + + + + + + +) diff --git a/src/components/Icons/CircleClock.icon.js b/src/components/Icons/CircleClock.icon.js new file mode 100644 index 000000000..9567ed7ca --- /dev/null +++ b/src/components/Icons/CircleClock.icon.js @@ -0,0 +1,28 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import * as React from 'react' +import Svg, { Path } from 'react-native-svg' +import { COLORS } from '../../styles/themes' +import { DEFAULT_ICON_SIZE } from './constants' +import { getScale, getViewBox } from './helper' + +export const CircleClock = ({ size = DEFAULT_ICON_SIZE, color = COLORS.black }) => ( + + + +) diff --git a/src/components/Icons/CircleError.icon.js b/src/components/Icons/CircleError.icon.js new file mode 100644 index 000000000..34a063eb1 --- /dev/null +++ b/src/components/Icons/CircleError.icon.js @@ -0,0 +1,28 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import * as React from 'react' +import Svg, { Path } from 'react-native-svg' +import { COLORS } from '../../styles/themes' +import { DEFAULT_ICON_SIZE } from './constants' +import { getScale, getViewBox } from './helper' + +export const CircleError = ({ size = DEFAULT_ICON_SIZE, color = COLORS.black }) => ( + + + +) diff --git a/src/components/Icons/constants.js b/src/components/Icons/constants.js new file mode 100644 index 000000000..26778fd73 --- /dev/null +++ b/src/components/Icons/constants.js @@ -0,0 +1 @@ +export const DEFAULT_ICON_SIZE = 24; diff --git a/src/components/Icons/helper.js b/src/components/Icons/helper.js new file mode 100644 index 000000000..4c9482e99 --- /dev/null +++ b/src/components/Icons/helper.js @@ -0,0 +1,16 @@ +/** + * @param {number} size + * @example + * getViewBox(24) + * // return `0 0 24 24` + */ +export const getViewBox = (size) => `0 0 ${size} ${size}`; + +/** + * @param {number} size + * @param {number} baseSize + * @example + * getScale(48, 24) + * // return `scale(2)` + */ +export const getScale = (size, baseSize) => `scale(${size / baseSize})`; From e4267db2749e48123b571c77671402d94eca536b Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Sat, 20 Apr 2024 14:08:14 +0100 Subject: [PATCH 08/51] feat: add components [5] (#454) * feat: add components * refactor: simplifies TransactionStatusLabel * chore: update translation files * feat: restore registeredUids filter --- locale/da/texts.po | 26 ++++-- locale/pt-br/texts.po | 26 ++++-- locale/ru-ru/texts.po | 26 ++++-- locale/texts.pot | 26 ++++-- src/components/HathorFlatList.js | 55 ++++++++++++ src/components/TransactionStatusLabel.js | 104 +++++++++++++++++++++++ src/styles/themes.js | 86 ++++++++++++------- 7 files changed, 289 insertions(+), 60 deletions(-) create mode 100644 src/components/HathorFlatList.js create mode 100644 src/components/TransactionStatusLabel.js diff --git a/locale/da/texts.po b/locale/da/texts.po index dff354098..46d99422d 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" @@ -917,23 +917,23 @@ msgid "" "this can potentially make you susceptible to fraudulent schemes." msgstr "" -#: src/sagas/nanoContract.js:22 +#: src/sagas/nanoContract.js:21 msgid "Nano Contract already registered." msgstr "" -#: src/sagas/nanoContract.js:23 +#: src/sagas/nanoContract.js:22 msgid "Wallet is not ready yet to register a Nano Contract." msgstr "" -#: src/sagas/nanoContract.js:24 +#: src/sagas/nanoContract.js:23 msgid "The informed address does not belong to the wallet." msgstr "" -#: src/sagas/nanoContract.js:25 +#: src/sagas/nanoContract.js:24 msgid "Nano Contract not found." msgstr "" -#: src/sagas/nanoContract.js:26 +#: src/sagas/nanoContract.js:25 msgid "Error while trying to get Nano Contract state." msgstr "" @@ -1131,6 +1131,18 @@ msgid "" msgstr "" "Her er konfigurationsstrengen for token ${ tokenLabel }: ${ configString }" +#: src/components/TransactionStatusLabel.js:71 +msgid "Executed" +msgstr "" + +#: src/components/TransactionStatusLabel.js:76 +msgid "Processing" +msgstr "" + +#: src/components/TransactionStatusLabel.js:81 +msgid "Voided" +msgstr "" + #: src/components/TxDetailsModal.js:60 msgid "Description" msgstr "Beskrivelse" diff --git a/locale/pt-br/texts.po b/locale/pt-br/texts.po index fe7595f1b..685918920 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" @@ -943,23 +943,23 @@ 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/nanoContract.js:22 +#: src/sagas/nanoContract.js:21 msgid "Nano Contract already registered." msgstr "Nano Contrato já registrado." -#: src/sagas/nanoContract.js:23 +#: src/sagas/nanoContract.js:22 msgid "Wallet is not ready yet to register a Nano Contract." msgstr "A wallet não está pronta ainda para registrar Nano Contratos." -#: src/sagas/nanoContract.js:24 +#: src/sagas/nanoContract.js:23 msgid "The informed address does not belong to the wallet." msgstr "O endereço informado não pertence a esta carteira." -#: src/sagas/nanoContract.js:25 +#: src/sagas/nanoContract.js:24 msgid "Nano Contract not found." msgstr "Nano Contrato não encontrado" -#: src/sagas/nanoContract.js:26 +#: src/sagas/nanoContract.js:25 msgid "Error while trying to get Nano Contract state." msgstr "Erro ao obter o estado do Nano Contrato." @@ -1160,6 +1160,18 @@ msgid "" "Here is the configuration string of token ${ tokenLabel }: ${ configString }" msgstr "Essa é a configuração do seu token ${ tokenLabel }: ${ configString }" +#: src/components/TransactionStatusLabel.js:71 +msgid "Executed" +msgstr "Executada" + +#: src/components/TransactionStatusLabel.js:76 +msgid "Processing" +msgstr "Processando" + +#: src/components/TransactionStatusLabel.js:81 +msgid "Voided" +msgstr "Inválida" + #: src/components/TxDetailsModal.js:60 msgid "Description" msgstr "Descrição" diff --git a/locale/ru-ru/texts.po b/locale/ru-ru/texts.po index 407597534..cfc3f8e58 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 "Авторизовать операцию" @@ -921,23 +921,23 @@ msgid "" "this can potentially make you susceptible to fraudulent schemes." msgstr "" -#: src/sagas/nanoContract.js:22 +#: src/sagas/nanoContract.js:21 msgid "Nano Contract already registered." msgstr "" -#: src/sagas/nanoContract.js:23 +#: src/sagas/nanoContract.js:22 msgid "Wallet is not ready yet to register a Nano Contract." msgstr "" -#: src/sagas/nanoContract.js:24 +#: src/sagas/nanoContract.js:23 msgid "The informed address does not belong to the wallet." msgstr "" -#: src/sagas/nanoContract.js:25 +#: src/sagas/nanoContract.js:24 msgid "Nano Contract not found." msgstr "" -#: src/sagas/nanoContract.js:26 +#: src/sagas/nanoContract.js:25 msgid "Error while trying to get Nano Contract state." msgstr "" @@ -1120,6 +1120,18 @@ msgid "" "Here is the configuration string of token ${ tokenLabel }: ${ configString }" msgstr "Это конфигурация вашего токена ${ tokenLabel }: ${ configString }" +#: src/components/TransactionStatusLabel.js:71 +msgid "Executed" +msgstr "" + +#: src/components/TransactionStatusLabel.js:76 +msgid "Processing" +msgstr "" + +#: src/components/TransactionStatusLabel.js:81 +msgid "Voided" +msgstr "" + #: src/components/TxDetailsModal.js:60 msgid "Description" msgstr "Описание" diff --git a/locale/texts.pot b/locale/texts.pot index c0c1d98d1..5a1fa4ba1 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 "" @@ -912,23 +912,23 @@ msgid "" "this can potentially make you susceptible to fraudulent schemes." msgstr "" -#: src/sagas/nanoContract.js:22 +#: src/sagas/nanoContract.js:21 msgid "Nano Contract already registered." msgstr "" -#: src/sagas/nanoContract.js:23 +#: src/sagas/nanoContract.js:22 msgid "Wallet is not ready yet to register a Nano Contract." msgstr "" -#: src/sagas/nanoContract.js:24 +#: src/sagas/nanoContract.js:23 msgid "The informed address does not belong to the wallet." msgstr "" -#: src/sagas/nanoContract.js:25 +#: src/sagas/nanoContract.js:24 msgid "Nano Contract not found." msgstr "" -#: src/sagas/nanoContract.js:26 +#: src/sagas/nanoContract.js:25 msgid "Error while trying to get Nano Contract state." msgstr "" @@ -1112,6 +1112,18 @@ msgstr "" msgid "Here is the configuration string of token ${ tokenLabel }: ${ configString }" msgstr "" +#: src/components/TransactionStatusLabel.js:71 +msgid "Executed" +msgstr "" + +#: src/components/TransactionStatusLabel.js:76 +msgid "Processing" +msgstr "" + +#: src/components/TransactionStatusLabel.js:81 +msgid "Voided" +msgstr "" + #: src/components/TxDetailsModal.js:60 msgid "Description" msgstr "" diff --git a/src/components/HathorFlatList.js b/src/components/HathorFlatList.js new file mode 100644 index 000000000..043e2dcec --- /dev/null +++ b/src/components/HathorFlatList.js @@ -0,0 +1,55 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import { + FlatList, StyleSheet, View, +} from 'react-native'; +import { COLORS } from '../styles/themes'; + +/** + * param {FlatListProps} props + */ +export const HathorFlatList = (props) => ( + + + +); + +const ItemSeparator = () => ( + +); + +const styles = StyleSheet.create({ + wrapper: { + flexShrink: 1, + alignSelf: 'stretch', + marginTop: 16, + marginBottom: 24, + backgroundColor: COLORS.backgroundColor, + marginHorizontal: 16, + borderTopLeftRadius: 16, + borderTopRightRadius: 16, + borderBottomLeftRadius: 16, + borderBottomRightRadius: 16, + shadowOffset: { height: 2, width: 0 }, + shadowRadius: 4, + shadowColor: COLORS.textColor, + shadowOpacity: 0.08, + paddingTop: 16, + paddingBottom: 16, + }, + itemSeparator: { + width: '100%', + height: 1, + backgroundColor: COLORS.borderColor + }, +}); diff --git a/src/components/TransactionStatusLabel.js b/src/components/TransactionStatusLabel.js new file mode 100644 index 000000000..e3e0e8efa --- /dev/null +++ b/src/components/TransactionStatusLabel.js @@ -0,0 +1,104 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import { StyleSheet, Text, View, } from 'react-native'; +import { t } from 'ttag'; +import { COLORS } from '../styles/themes'; +import { CircleCheck } from './Icons/CircleCheck.icon'; +import { CircleClock } from './Icons/CircleClock.icon'; +import { CircleError } from './Icons/CircleError.icon'; + +const styles = StyleSheet.create({ + wrapper: { + flexShrink: 1, + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + gap: 6, + borderRadius: 100, + paddingVertical: 4, + paddingLeft: 12, + // Adds optical effect of centralization for the whole object + paddingRight: 14, + }, + label: { + fontSize: 12, + lineHeight: 20, + }, + feedbackSuccess: { + backgroundColor: COLORS.feedbackSuccess100, + color: COLORS.feedbackSuccess400, + }, + feedbackWarning: { + backgroundColor: COLORS.feedbackWarning100, + color: COLORS.feedbackWarning300, + }, + feedbackError: { + backgroundColor: COLORS.feedbackError200, + color: COLORS.feedbackError600, + }, + freeze: { + backgroundColor: COLORS.freeze100, + color: COLORS.freeze300, + }, +}); + +/** + * @param {Object} param + * @param {string} param.label Status text as label + * @param {Object} param.style Style props to customize the base component + * @param {Object} param.children Icon component to compose with the label + */ +const TransactionStatusBase = ({ label, style, children: icon }) => ( + + + {icon} + + + + {label.toUpperCase()} + + + +); + +const Executed = () => ( + + + +); +const Processing = () => ( + + + +); +const Voided = () => ( + + + +); + +/** + * @description + * This component was devised to be used in Nano Contract context + * to provide a visual indication for transaction "status". + * @param {Object} param + * @param {boolean} param.hasFirstBlock It indicates if a transaction has a first block + * @param {boolean} param.isVoided Transaction's void flag + */ +export const TransactionStatusLabel = ({ hasFirstBlock, isVoided = false }) => { + if (isVoided) { + return Voided(); + } + + if (hasFirstBlock) { + return Executed(); + } + + return Processing(); +}; diff --git a/src/styles/themes.js b/src/styles/themes.js index f0ba087d9..2cb8bdfcb 100644 --- a/src/styles/themes.js +++ b/src/styles/themes.js @@ -6,69 +6,91 @@ */ import { DefaultTheme } from '@react-navigation/native'; - import { _PRIMARY_COLOR as PRIMARY_COLOR } from '../config'; import { HslColor } from '../HslColor'; /** * Light theme color scheme - * @type {{ - * midContrastDetail: string, - * errorBgColor: string, - * backgroundColor: string, - * borderColor: string, - * lowContrastDetail: string, - * textColorShadow: string, - * positiveBalanceColor: string, - * textColor: string, - * errorTextColor: string, - * tabBarBackground: string, - * primaryOpacity30: string, - * textColorShadowLight: string, - * primaryOpacity10: string, - * errorTextShadow: string, - * primary: string}} - * @property {string} backgroundColor The main background color - * @property {string} lowContrastDetail Low contrast with background: separator lines, containers... - * @property {string} midContrastDetail Medium contrast with background: placeholders, ... - * @property {string} borderColor Defines borders - * @property {string} textColor Maximum contrast with the background color, for reading - * @property {string} textColorShadow Washed down version of the text - * @property {string} textColorShadowLight More washed down version of the text - * @property {string} tabBarBackground Specific for tab bar selectors - * @property {string} positiveBalanceColor Represents a positive feedback for the user - * @property {string} errorBgColor For containers with error feedbacks - * @property {string} errorTextColor For texts with error messages - * @property {string} errorTextShadow Washed down version of error texts - * @property {string} primary Primary color, as set on the application config file - * @property {string} primaryOpacity10 Primary color washed down to 10% opacity - * @property {string} primaryOpacity30 Primary color washed down to 30% opacity */ export const COLORS = { + /** + * @type {string} The main background color + */ backgroundColor: '#fff', + /** + * @type {string} Low contrast with background, like separator lines + */ lowContrastDetail: '#f7f7f7', + /** + * @type {string} Medium contrast with background, like placeholders + */ midContrastDetail: '#9e9e9e', darkContrastDetail: '#808080', + /** + * @type {string} Defines borders + */ borderColor: '#eee', borderColorMid: '#dcdcdc', borderColorDark: '#cecece', + /** + * @type {string} + * Maximum contrast with the background color, for better reading + */ textColor: '#000', + /** + * @type {string} Washed down version of the text + */ textColorShadow: 'rgba(0, 0, 0, 0.5)', textColorShadowOpacity005: 'rgba(0, 0, 0, 0.05)', textColorShadowLighter: 'rgba(0, 0, 0, 0.1)', + /** + * @type {string} More washed down version of the text + */ textColorShadowLight: 'rgba(0, 0, 0, 0.3)', textColorShadowOpacity06: 'rgba(0, 0, 0, 0.6)', textColorShadowOpacity07: 'rgba(0, 0, 0, 0.7)', textColorShadowDark: 'rgba(0, 0, 0, 0.8)', textColorShadowOpacity09: 'rgba(0, 0, 0, 0.9)', + /** + * @type {string} Specific for tab bar selectors + */ tabBarBackground: '#333', + /** + * @type {string} Represents a positive feedback for the user + */ positiveBalanceColor: '#0DA0A0', + /** + * @type {string} For containers with error feedbacks + */ errorBgColor: '#DE3535', + /** + * @type {string} For texts with error messages + */ errorTextColor: '#F00', + /** + * @type {string} Washed down version of error texts + * */ errorTextShadow: `rgba(255, 0, 0, 0.7)`, + /** + * @type {string} Primary color, as set on the application config file + * */ primary: PRIMARY_COLOR, + /** + * @type {string} Primary color washed down to 10% opacity + */ primaryOpacity10: `${PRIMARY_COLOR}1A`, + /** + * @type {string} Primary color washed down to 30% opacity + */ primaryOpacity30: `${PRIMARY_COLOR}4D`, + feedbackSuccess100: 'hsla(161, 30%, 85%, 1)', + feedbackSuccess400: 'hsla(159, 75%, 17%, 1)', + feedbackWarning100: 'hsla(21, 100%, 90%, 1)', + feedbackWarning300: 'hsla(21, 54%, 49%, 1)', + feedbackError200: 'hsla(7, 69%, 95%, 1)', + feedbackError600: 'hsla(7, 100%, 30%, 1)', + freeze100: 'hsla(0, 0%, 90%, 1)', + freeze300: 'hsla(0, 0%, 45%, 1)', }; /** From 53bb86ca5b2b8162cae6cd71b381c68d3474c000 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Tue, 23 Apr 2024 15:42:58 +0100 Subject: [PATCH 09/51] feat: nano contract history [3] (#432) * feat: add nano contract history effect, actions and reducer * test: add history effect tests and extract fixtures * refactor: persistence to use store interface from lib * chore: add note about lack of support for methods on wallet service wallet --- __tests__/sagas/nanoContracts/fixtures.js | 144 ++++++++++++++ .../nanoContracts/historyNanoContract.test.js | 169 +++++++++++++++++ .../registerNanoContract.test.js | 62 +------ locale/da/texts.po | 8 + locale/pt-br/texts.po | 16 +- locale/ru-ru/texts.po | 8 + locale/texts.pot | 8 + package.json | 3 +- src/actions.js | 47 ++++- src/constants.js | 4 + src/reducers/reducer.js | 158 +++++++++++++++- src/sagas/index.js | 2 + src/sagas/nanoContract.js | 175 +++++++++++++++++- 13 files changed, 723 insertions(+), 81 deletions(-) create mode 100644 __tests__/sagas/nanoContracts/fixtures.js create mode 100644 __tests__/sagas/nanoContracts/historyNanoContract.test.js diff --git a/__tests__/sagas/nanoContracts/fixtures.js b/__tests__/sagas/nanoContracts/fixtures.js new file mode 100644 index 000000000..0944f7716 --- /dev/null +++ b/__tests__/sagas/nanoContracts/fixtures.js @@ -0,0 +1,144 @@ +import { jest } from '@jest/globals'; + +export const fixtures = { + address: 'HTeZeYTCv7cZ8u7pBGHkWsPwhZAuoq5j3V', + ncId: '3cb032600bdf7db784800e4ea911b10676fa2f67591f82bb62628c234e771595', + ncApi: { + getNanoContractState: { + successResponse: { + success: true, + nc_id: '3cb032600bdf7db784800e4ea911b10676fa2f67591f82bb62628c234e771595', + blueprint_name: 'Bet', + fields: { + token_uid: { value: '00' }, + total: { value: 300 }, + final_result: { value: '1x0' }, + oracle_script: { value: '76a91441c431ff7ad5d6ce5565991e3dcd5d9106cfd1e288ac' }, + 'withdrawals.a\'Wi8zvxdXHjaUVAoCJf52t3WovTZYcU9aX6\'': { value: 300 }, + 'address_details.a\'Wi8zvxdXHjaUVAoCJf52t3WovTZYcU9aX6\'': { value: { '1x0': 100 } }, + }, + }, + }, + getNanoContractHistory: { + failureResponse: { + success: false, + }, + errorResponse: { + error: new Error('API call error'), + }, + successResponse: { + success: true, + history: [ + { + hash: '000000203e87e8575f121de16d0eb347bd1473eedd9f46cc76c1bc8d4e5a5fce', + nonce: 2104638, + timestamp: 1708356261, + version: 4, + weight: 21.89480540500889, + signal_bits: 0, + parents: [ + '0000008fbebdf8d78be50c88aceebf3c6b9e92f4affd7dfc96d48a7a49f23e69', + '00000121c46366b19de5efa8e6c23f62895322486395a0e31e987f9073025989' + ], + inputs: [ + { + tx_id: '0000008fbebdf8d78be50c88aceebf3c6b9e92f4affd7dfc96d48a7a49f23e69', + index: 0, + data: 'RjBEAiBtWa0q8uzMvBfkh83Y+t4Tv5OeyJSD8NazaGp19Hc2UwIgbV2m5unBEHlTAcLJsZLsCBlnfpua8LrUkVORiW/t4OQhAolqAR4yFaeCBeu/kOG1SwWnRj2X62zT9mU+Deutnbqq' + } + ], + outputs: [ + { + value: 78500, + token_data: 1, + script: 'dqkU5W5CR9734WcxaZMJkuToO8XlD3mIrA==' + }, + { + value: 300, + token_data: 2, + script: 'dqkU5W5CR9734WcxaZMJkuToO8XlD3mIrA==' + } + ], + tokens: [ + '00000117b0502e9eef9ccbe987af65f153aa899d6eba88d50a6c89e78644713d', + '0000038c49253f86e6792006dd9124e2c50e6487fde3296b7bd637e3e1a497e7' + ], + nc_id: '000001342d3c5b858a4d4835baea93fcc683fa615ff5892bd044459621a0340a', + nc_method: 'swap', + nc_args: '', + nc_pubkey: '020b120c8ad037ceb2e5b51b3edda7cd15a44f843b56e49880f6647fa9aadadffa' + }, + ], + }, + }, + }, + ncSaga: { + getNanoContractState: { + errorResponse: { + error: new Error('API call error'), + }, + successResponse: { + ncState: { + success: true, + nc_id: '3cb032600bdf7db784800e4ea911b10676fa2f67591f82bb62628c234e771595', + blueprint_name: 'Bet', + fields: { + token_uid: { value: '00' }, + total: { value: 300 }, + final_result: { value: '1x0' }, + oracle_script: { value: '76a91441c431ff7ad5d6ce5565991e3dcd5d9106cfd1e288ac' }, + 'withdrawals.a\'Wi8zvxdXHjaUVAoCJf52t3WovTZYcU9aX6\'': { value: 300 }, + 'address_details.a\'Wi8zvxdXHjaUVAoCJf52t3WovTZYcU9aX6\'': { value: { '1x0': 100 } }, + }, + }, + }, + }, + fetchHistory: { + successResponse: { + history: [ + { + txId: '000000203e87e8575f121de16d0eb347bd1473eedd9f46cc76c1bc8d4e5a5fce', + timestamp: 1708356261, + tokens: [ + '00000117b0502e9eef9ccbe987af65f153aa899d6eba88d50a6c89e78644713d', + '0000038c49253f86e6792006dd9124e2c50e6487fde3296b7bd637e3e1a497e7' + ], + isVoided: false, // review + ncId: '000001342d3c5b858a4d4835baea93fcc683fa615ff5892bd044459621a0340a', + ncMethod: 'swap', + }, + ], + next: null, + }, + }, + }, + wallet: { + notReady: { + isReady: () => false, + }, + addressNotMine: { + isReady: () => true, + isAddressMine: jest.fn().mockReturnValue(false), + storage: { + isNanoContractRegistered: jest.fn(), + registerNanoContract: jest.fn(), + }, + }, + readyAndMine: { + isReady: () => true, + isAddressMine: jest.fn().mockReturnValue(true), + storage: { + isNanoContractRegistered: jest.fn(), + registerNanoContract: jest.fn(), + getNanoContract: jest.fn(), + }, + }, + }, + store: { + nanoContractAddressAlreadyRegistered: { + ncId: '3cb032600bdf7db784800e4ea911b10676fa2f67591f82bb62628c234e771595', + blueprintName: 'Bet', + addresses: new Set(['HTeZeYTCv7cZ8u7pBGHkWsPwhZAuoq5j3V']), + }, + }, +}; diff --git a/__tests__/sagas/nanoContracts/historyNanoContract.test.js b/__tests__/sagas/nanoContracts/historyNanoContract.test.js new file mode 100644 index 000000000..c7d3c277c --- /dev/null +++ b/__tests__/sagas/nanoContracts/historyNanoContract.test.js @@ -0,0 +1,169 @@ +import { put } from 'redux-saga/effects'; +import { ncApi, addressUtils, transactionUtils } from '@hathor/wallet-lib'; +import { jest, test, expect, beforeEach, describe } from '@jest/globals'; +import { + failureMessage, + requestHistoryNanoContract, + fetchHistory +} from '../../../src/sagas/nanoContract'; +import { + nanoContractHistoryFailure, + nanoContractHistoryRequest, + nanoContractHistorySuccess, + onExceptionCaptured +} from '../../../src/actions'; +import { STORE } from '../../../src/store'; +import { fixtures } from './fixtures'; + +jest.mock('@hathor/wallet-lib'); + +beforeEach(() => { + jest.clearAllMocks(); + STORE.clearItems(); +}); + +describe('sagas/nanoContract/fetchHistory', () => { + test('success', async () => { + // arrange wallet mock + const mockedWallet = { + getNetworkObject: jest.fn(), + storage: { + isAddressMine: jest.fn(), + }, + }; + // arrange ncApi mock + const mockedNcApi = jest.mocked(ncApi); + mockedNcApi.getNanoContractHistory + .mockReturnValue(fixtures.ncApi.getNanoContractHistory.successResponse); + // arrange addressUtils mock + const mockedAddressUtils = jest.mocked(addressUtils); + mockedAddressUtils.getAddressFromPubkey + .mockResolvedValue('123'); + // arrange transactionUtils + const mockedTransactionUtils = jest.mocked(transactionUtils); + mockedTransactionUtils.getTxBalance + .mockResolvedValue({}); + + // call fetchHistory + const count = 1; + const after = null; + const result = await fetchHistory(fixtures.ncId, count, after, mockedWallet); + + // assert result is defined + expect(result.history).toBeDefined(); + expect(result.next).toBeDefined(); + // assert next value is a txId from the last element of history + expect(result.next).toBe(fixtures.ncSaga.fetchHistory.successResponse.history[0].txId); + // assert call count to API + expect(mockedNcApi.getNanoContractHistory).toBeCalledTimes(1); + }); + + test('failure', async () => { + // arrange ncApi mock + const mockedNcApi = jest.mocked(ncApi); + mockedNcApi.getNanoContractHistory + .mockReturnValue(fixtures.ncApi.getNanoContractHistory.failureResponse); + + // call fetchHistory and assert exception + const count = 1; + const after = null; + await expect(fetchHistory(fixtures.ncId, count, after)).rejects.toThrow('Failed to fetch nano contract history'); + }); +}); + +describe('sagas/nanoContract/requestHistoryNanoContract', () => { + test('history loading', () => { + // arrange Nano Contract registration inputs + const { ncId } = fixtures; + + // call effect to request history + const gen = requestHistoryNanoContract(nanoContractHistoryRequest({ ncId })); + // select wallet + gen.next(); + // feed back historyMeta + gen.next({ [ncId]: { isLoading: true, after: null } }); + + // assert termination + expect(gen.next().value).toBeUndefined(); + }); + + test('history without registered contract', () => { + // arrange Nano Contract registration inputs + const { ncId } = fixtures; + + // call effect to request history + const gen = requestHistoryNanoContract(nanoContractHistoryRequest({ ncId })); + // select wallet + gen.next(); + // feed back historyMeta + gen.next({}); + // feed back wallet + gen.next(fixtures.wallet.readyAndMine); + + // expect failure + // feed back isNanoContractRegistered + expect(gen.next(false).value).toStrictEqual( + put(nanoContractHistoryFailure({ ncId, error: failureMessage.notRegistered })) + ); + }); + + test('fetch history fails', () => { + // arrange Nano Contract registration inputs + const { ncId } = fixtures; + const storage = STORE.getStorage(); + storage.registerNanoContract(ncId, { ncId }); + + // call effect to request history + const gen = requestHistoryNanoContract(nanoContractHistoryRequest({ ncId })); + // select wallet + gen.next(); + // feed back historyMeta + gen.next({}); + // feed back wallet + gen.next(fixtures.wallet.readyAndMine); + // feed back isNanoContractRegistered + const fetchHistoryCall = gen.next(true).value; + + // throws on fetchHistory call + const failureCall = gen.throw(new Error('history')).value; + const onErrorCall = gen.next().value; + + // assert failure + expect(fetchHistoryCall.payload.fn).toBe(fetchHistory); + expect(failureCall).toStrictEqual( + put(nanoContractHistoryFailure({ ncId, error: failureMessage.nanoContractHistoryFailure })) + ); + expect(onErrorCall).toStrictEqual(put(onExceptionCaptured(new Error('history'), false))); + }); + + test('history with success', () => { + // arrange Nano Contract registration inputs + const { ncId } = fixtures; + + // call effect to request history + const gen = requestHistoryNanoContract(nanoContractHistoryRequest({ ncId })); + // select wallet + gen.next(); + // feed back historyMeta + gen.next({}); + // feed back wallet + gen.next(fixtures.wallet.readyAndMine); + // feed back isNanoContractRegistered + const fetchHistoryCall = gen.next(true).value; + // feed back fetchHistory + const sucessCall = gen.next(fixtures.ncSaga.fetchHistory.successResponse).value; + + // assert success + const expectedHistory = fixtures.ncSaga.fetchHistory.successResponse.history; + expect(fetchHistoryCall.payload.fn).toBe(fetchHistory); + expect(sucessCall.payload).toHaveProperty('action.payload.ncId'); + expect(sucessCall.payload).toHaveProperty('action.payload.history'); + expect(sucessCall.payload.action.payload.ncId).toStrictEqual(ncId); + expect(sucessCall.payload.action.payload.history).toStrictEqual(expectedHistory); + expect(sucessCall).toStrictEqual( + put(nanoContractHistorySuccess({ ncId, history: expectedHistory, after: null })) + ); + // assert termination + expect(gen.next().value).toBeUndefined(); + }); +}); diff --git a/__tests__/sagas/nanoContracts/registerNanoContract.test.js b/__tests__/sagas/nanoContracts/registerNanoContract.test.js index 2db348310..2a2eb385d 100644 --- a/__tests__/sagas/nanoContracts/registerNanoContract.test.js +++ b/__tests__/sagas/nanoContracts/registerNanoContract.test.js @@ -3,67 +3,7 @@ import { jest, test, expect, beforeEach, describe } from '@jest/globals'; import { registerNanoContract, failureMessage } from '../../../src/sagas/nanoContract'; import { nanoContractRegisterFailure, nanoContractRegisterRequest, onExceptionCaptured, types } from '../../../src/actions'; import { STORE } from '../../../src/store'; - -const fixtures = { - address: 'HTeZeYTCv7cZ8u7pBGHkWsPwhZAuoq5j3V', - ncId: '3cb032600bdf7db784800e4ea911b10676fa2f67591f82bb62628c234e771595', - ncApi: { - getNanoContractState: { - successResponse: { - success: true, - nc_id: '3cb032600bdf7db784800e4ea911b10676fa2f67591f82bb62628c234e771595', - blueprint_name: 'Bet', - fields: { - token_uid: { value: '00' }, - total: { value: 300 }, - final_result: { value: '1x0' }, - oracle_script: { value: '76a91441c431ff7ad5d6ce5565991e3dcd5d9106cfd1e288ac' }, - 'withdrawals.a\'Wi8zvxdXHjaUVAoCJf52t3WovTZYcU9aX6\'': { value: 300 }, - 'address_details.a\'Wi8zvxdXHjaUVAoCJf52t3WovTZYcU9aX6\'': { value: { '1x0': 100 } }, - } - } - } - }, - ncSaga: { - getNanoContractState: { - errorResponse: new Error('API call error'), - successResponse: { - success: true, - nc_id: '3cb032600bdf7db784800e4ea911b10676fa2f67591f82bb62628c234e771595', - blueprint_name: 'Bet', - fields: { - token_uid: { value: '00' }, - total: { value: 300 }, - final_result: { value: '1x0' }, - oracle_script: { value: '76a91441c431ff7ad5d6ce5565991e3dcd5d9106cfd1e288ac' }, - 'withdrawals.a\'Wi8zvxdXHjaUVAoCJf52t3WovTZYcU9aX6\'': { value: 300 }, - 'address_details.a\'Wi8zvxdXHjaUVAoCJf52t3WovTZYcU9aX6\'': { value: { '1x0': 100 } }, - } - } - }, - }, - wallet: { - notReady: { - isReady: () => false, - }, - addressNotMine: { - isReady: () => true, - isAddressMine: jest.fn().mockReturnValue(false), - storage: { - isNanoContractRegistered: jest.fn(), - registerNanoContract: jest.fn(), - }, - }, - readyAndMine: { - isReady: () => true, - isAddressMine: jest.fn().mockReturnValue(true), - storage: { - isNanoContractRegistered: jest.fn(), - registerNanoContract: jest.fn(), - }, - }, - }, -}; +import { fixtures } from './fixtures'; beforeEach(() => { jest.clearAllMocks(); diff --git a/locale/da/texts.po b/locale/da/texts.po index 46d99422d..99cb9d499 100644 --- a/locale/da/texts.po +++ b/locale/da/texts.po @@ -937,6 +937,14 @@ msgstr "" msgid "Error while trying to get Nano Contract state." msgstr "" +#: src/sagas/nanoContract.js:35 +msgid "Nano Contract not registered." +msgstr "" + +#: src/sagas/nanoContract.js:36 +msgid "Error while trying to fetch Nano Contract history." +msgstr "" + #: src/sagas/networkSettings.js:88 msgid "Custom Network Settings cannot be empty." msgstr "" diff --git a/locale/pt-br/texts.po b/locale/pt-br/texts.po index 685918920..a9342f023 100644 --- a/locale/pt-br/texts.po +++ b/locale/pt-br/texts.po @@ -945,11 +945,11 @@ msgstr "" #: src/sagas/nanoContract.js:21 msgid "Nano Contract already registered." -msgstr "Nano Contrato já registrado." +msgstr "Nano Contract já registrado." #: src/sagas/nanoContract.js:22 msgid "Wallet is not ready yet to register a Nano Contract." -msgstr "A wallet não está pronta ainda para registrar Nano Contratos." +msgstr "A wallet não está pronta ainda para registrar Nano Contracts." #: src/sagas/nanoContract.js:23 msgid "The informed address does not belong to the wallet." @@ -957,11 +957,19 @@ msgstr "O endereço informado não pertence a esta carteira." #: src/sagas/nanoContract.js:24 msgid "Nano Contract not found." -msgstr "Nano Contrato não encontrado" +msgstr "Nano Contract não encontrado" #: src/sagas/nanoContract.js:25 msgid "Error while trying to get Nano Contract state." -msgstr "Erro ao obter o estado do Nano Contrato." +msgstr "Erro ao obter o estado do Nano Contract." + +#: src/sagas/nanoContract.js:35 +msgid "Nano Contract not registered." +msgstr "Nano Contract não registrado." + +#: src/sagas/nanoContract.js:36 +msgid "Error while trying to fetch Nano Contract history." +msgstr "Error ao fazer download do histórico de transações do Nano Contract." #: src/sagas/networkSettings.js:88 msgid "Custom Network Settings cannot be empty." diff --git a/locale/ru-ru/texts.po b/locale/ru-ru/texts.po index cfc3f8e58..ec417dbaf 100644 --- a/locale/ru-ru/texts.po +++ b/locale/ru-ru/texts.po @@ -941,6 +941,14 @@ msgstr "" msgid "Error while trying to get Nano Contract state." msgstr "" +#: src/sagas/nanoContract.js:35 +msgid "Nano Contract not registered." +msgstr "" + +#: src/sagas/nanoContract.js:36 +msgid "Error while trying to fetch Nano Contract history." +msgstr "" + #: src/sagas/networkSettings.js:88 msgid "Custom Network Settings cannot be empty." msgstr "" diff --git a/locale/texts.pot b/locale/texts.pot index 5a1fa4ba1..293d8fa2c 100644 --- a/locale/texts.pot +++ b/locale/texts.pot @@ -932,6 +932,14 @@ msgstr "" msgid "Error while trying to get Nano Contract state." msgstr "" +#: src/sagas/nanoContract.js:35 +msgid "Nano Contract not registered." +msgstr "" + +#: src/sagas/nanoContract.js:36 +msgid "Error while trying to fetch Nano Contract history." +msgstr "" + #: src/sagas/networkSettings.js:88 msgid "Custom Network Settings cannot be empty." msgstr "" diff --git a/package.json b/package.json index a68ad985e..47f518e47 100644 --- a/package.json +++ b/package.json @@ -131,6 +131,7 @@ "setupFiles": [ "/jestMockSetup.js" ], - "cacheDirectory": ".jest/cache" + "cacheDirectory": ".jest/cache", + "testMatch": ["**/*.test.js"] } } diff --git a/src/actions.js b/src/actions.js index 286a7f58f..ceb528368 100644 --- a/src/actions.js +++ b/src/actions.js @@ -137,12 +137,18 @@ export const types = { NETWORKSETTINGS_UPDATE_FAILURE: 'NETWORK_SETTINGS_UPDATE_FAILURE', /* It updates the redux state of network settings status. */ NETWORKSETTINGS_UPDATE_READY: 'NETWORK_SETTINGS_UPDATE_READY', - /* It initiates a registration process of a Nano Contract. */ + /* It initiates a registration process of a Nano Contract. */ NANOCONTRACT_REGISTER_REQUEST: 'NANOCONTRACT_REGISTER_REQUEST', - /* It indicates a Nano Contract registration is couldn't complete. */ + /* It indicates a Nano Contract registration is couldn't complete. */ NANOCONTRACT_REGISTER_FAILURE: 'NANOCONTRACT_REGISTER_FAILURE', - /* It indicates a Nano Contract registration is complete. */ + /* It indicates a Nano Contract registration is complete. */ NANOCONTRACT_REGISTER_SUCCESS: 'NANOCONTRACT_REGISTER_SUCCESS', + /* It indicates a Nano Contract hitory was requested to load. */ + NANOCONTRACT_HISTORY_REQUEST: 'NANOCONTRACT_HISTORY_REQUEST', + /* It indicates a Nano Contract history was successfully loaded. */ + NANOCONTRACT_HISTORY_SUCCESS: 'NANOCONTRACT_HISTORY_SUCCESS', + /* It indicates a Nano Contract history failed to load. */ + NANOCONTRACT_HISTORY_FAILURE: 'NANOCONTRACT_HISTORY_FAILURE', }; export const featureToggleInitialized = () => ({ @@ -1014,3 +1020,38 @@ export const nanoContractRegisterSuccess = (ncEntry) => ({ type: types.NANOCONTRACT_REGISTER_SUCCESS, payload: ncEntry, }); + +/** + * Nano Contract request fetch history. + * @param {{ + * ncId: string; + * after: string; + * }} ncEntry Basic information of Nano Contract registered. + */ +export const nanoContractHistoryRequest = (ncEntry) => ({ + type: types.NANOCONTRACT_HISTORY_REQUEST, + payload: ncEntry, +}); + +/** + * Nano Contract history has loaded success. + * @param {Object} payload + * @param {string} payload.ncId Nano Contract ID. + * @param {Object[]} payload.history Nano Contract's history chunk as array. + * @param {string} payload.after A new history chunk will be fetched after this hash. + */ +export const nanoContractHistorySuccess = (payload) => ({ + type: types.NANOCONTRACT_HISTORY_SUCCESS, + payload, +}); + +/** + * Nano Contract history has failed. + * @param {Object} payload + * @param {string} payload.ncId Nano Contract ID. + * @param {string} payload.error History failure reason. + */ +export const nanoContractHistoryFailure = (payload) => ({ + type: types.NANOCONTRACT_HISTORY_FAILURE, + payload, +}); diff --git a/src/constants.js b/src/constants.js index 7bf220241..45d672404 100644 --- a/src/constants.js +++ b/src/constants.js @@ -255,3 +255,7 @@ export const NETWORK_PRIVATENET = 'privatenet'; export const MAX_RETRIES = 8; export const INITIAL_RETRY_LATENCY = 300; // ms export const LATENCY_MULTIPLIER = 30; // multiplier per iteration +/** + * Nano Contract's transaction history list size. + */ +export const NANO_CONTRACT_TX_HISTORY_SIZE = 20; diff --git a/src/reducers/reducer.js b/src/reducers/reducer.js index 4a96811fa..40a5305f9 100644 --- a/src/reducers/reducer.js +++ b/src/reducers/reducer.js @@ -232,7 +232,7 @@ const initialState = { networkSettingsStatus: NETWORKSETTINGS_STATUS.READY, nanoContract: { /** - * registeredContracts {{ + * registered {{ * [ncId: string]: { * address: string, * ncId: string, @@ -242,15 +242,72 @@ const initialState = { * }} registered Nano Contracts per wallet address with basic information. * @example * { - * '00c30fc': { + * '000001342d3c5b858a4d4835baea93fcc683fa615ff5892bd044459621a0340a': { * address: 'HTeZeYTCv7cZ8u7pBGHkWsPwhZAuoq5j3V', - * ncId: '00c30fc8a1b9a326a766ab0351faf3635297d316fd039a0eda01734d9de40185', - * blueprintId: 0025dadebe337a79006f181c05e4799ce98639aedfbd26335806790bdea4b1d4, + * ncId: '000001342d3c5b858a4d4835baea93fcc683fa615ff5892bd044459621a0340a', + * blueprintId: '0025dadebe337a79006f181c05e4799ce98639aedfbd26335806790bdea4b1d4', * blueprintName: 'Swap', * }, * } */ - registeredContracts: {}, + registered: {}, + /** + * history {{ + * [ncId: string]: { + * txId: string; + * timestamp: number; + * tokens: string[]; + * isVoided: boolean; + * ncId: string; + * ncMethod: string; + * blueprintId: string; + * caller: Object; // address object + * isMine: boolean; + * balance: {[uid: string]: Object}; + * }[]; + * }} history of Nano Contracts registered per wallet address. + * @example + * { + * '000001342d3c5b858a4d4835baea93fcc683fa615ff5892bd044459621a0340a': [ + * { + * txId: '000000203e87e8575f121de16d0eb347bd1473eedd9f46cc76c1bc8d4e5a5fce', + * timestamp: 1708356261, + * tokens: [ + * '00000117b0502e9eef9ccbe987af65f153aa899d6eba88d50a6c89e78644713d', + * '0000038c49253f86e6792006dd9124e2c50e6487fde3296b7bd637e3e1a497e7' + * ], + * isVoided: false, + * ncId: '000001342d3c5b858a4d4835baea93fcc683fa615ff5892bd044459621a0340a', + * ncMethod: 'swap', + * blueprintId: '0025dadebe337a79006f181c05e4799ce98639aedfbd26335806790bdea4b1d4'; + * caller: { base58: 'HTeZeYTCv7cZ8u7pBGHkWsPwhZAuoq5j3V' }, + * isMine: true, + * balance: { + * '00': 300, + * }, + * }, + * ], + * } + */ + history: {}, + /** + * historyMeta {{ + * [ncId: string]: { + * isLoading: boolean; + * error: string; + * after: string; + * }; + * }} holds the load state for each nano contract, including the after hash + * from which a new history chunk should be fetched, exclusively. + * @example + * { + * '000001342d3c5b858a4d4835baea93fcc683fa615ff5892bd044459621a0340a': { + * isLoading: false, + * after: '000075e15f015dc768065763acd9b563ec002e37182869965ff2c712bed83e1e', + * }, + * } + */ + historyMeta: {}, }, }; @@ -410,6 +467,12 @@ export const reducer = (state = initialState, action) => { return onNetworkSettingsUpdateInvalid(state, action); case types.NANOCONTRACT_REGISTER_SUCCESS: return onNanoContractRegisterSuccess(state, action); + case types.NANOCONTRACT_HISTORY_REQUEST: + return onNanoContractHistoryRequest(state, action); + case types.NANOCONTRACT_HISTORY_FAILURE: + return onNanoContractHistoryFailure(state, action); + case types.NANOCONTRACT_HISTORY_SUCCESS: + return onNanoContractHistorySuccess(state, action); default: return state; } @@ -1253,9 +1316,88 @@ export const onNanoContractRegisterSuccess = (state, { payload }) => ({ ...state, nanoContract: { ...state.nanoContract, - registeredContracts: { - ...state.nanoContract.registeredContracts, + registered: { + ...state.nanoContract.registered, [payload.entryKey]: payload.entryValue, - } + }, + }, +}); + +/** + * @param {Object} state + * @param {{ + * payload: { + * ncId: string; + * after: string; + * } + * }} action + */ +export const onNanoContractHistoryRequest = (state, { payload }) => ({ + ...state, + nanoContract: { + ...state.nanoContract, + historyMeta: { + ...state.nanoContract.historyMeta, + [payload.ncId]: { + isLoading: true, + after: payload.after, + }, + }, + }, +}); + +/** + * @param {Object} state + * @param {{ + * payload: { + * ncId: string; + * error: string; + * } + * }} action + */ +export const onNanoContractHistoryFailure = (state, { payload }) => ({ + ...state, + nanoContract: { + ...state.nanoContract, + historyMeta: { + ...state.nanoContract.historyMeta, + [payload.ncId]: { + ...(state.nanoContract.historyMeta[payload.ncId]), + isLoading: false, + error: payload.error, + }, + }, + }, +}); + +/** + * @param {Object} state + * @param {{ + * payload: { + * ncId: string; + * history: Object[]; + * after: string; + * } + * }} action + */ +export const onNanoContractHistorySuccess = (state, { payload }) => ({ + ...state, + nanoContract: { + ...state.nanoContract, + history: { + ...state.nanoContract.history, + [payload.ncId]: [ + ...(state.nanoContract.history[payload.ncId] || []), + // we are putting at the bottom because we expect an array with descending order. + ...payload.history, + ], + }, + historyMeta: { + ...state.nanoContract.historyMeta, + [payload.ncId]: { + isLoading: false, + after: payload.after, + }, + }, }, }); diff --git a/src/sagas/index.js b/src/sagas/index.js index fbcebea0f..9506ad358 100644 --- a/src/sagas/index.js +++ b/src/sagas/index.js @@ -14,6 +14,7 @@ import { saga as featureToggleSagas } from './featureToggle'; import { saga as permissionsSagas } from './permissions'; import { saga as walletConnectSagas } from './walletConnect'; import { saga as networkSettingsSagas } from './networkSettings'; +import { saga as nanoContractSagas } from './nanoContract'; const sagas = [ walletSagas, @@ -24,6 +25,7 @@ const sagas = [ featureToggleSagas, permissionsSagas, walletConnectSagas, + nanoContractSagas, ]; function* defaultSaga() { diff --git a/src/sagas/nanoContract.js b/src/sagas/nanoContract.js index 034ce8d72..228244b83 100644 --- a/src/sagas/nanoContract.js +++ b/src/sagas/nanoContract.js @@ -1,5 +1,7 @@ import { - ncApi + ncApi, + transactionUtils, + addressUtils, } from '@hathor/wallet-lib'; import { takeEvery, @@ -11,11 +13,17 @@ import { import { t } from 'ttag'; import { NanoRequest404Error } from '@hathor/wallet-lib/lib/errors'; import { + nanoContractHistoryFailure, + nanoContractHistorySuccess, nanoContractRegisterFailure, nanoContractRegisterSuccess, onExceptionCaptured, types, } from '../actions'; +import { logger } from '../logger'; +import { NANO_CONTRACT_TX_HISTORY_SIZE } from '../constants'; + +const log = logger('nano-contract-saga'); export const failureMessage = { alreadyRegistered: t`Nano Contract already registered.`, @@ -23,6 +31,8 @@ export const failureMessage = { addressNotMine: t`The informed address does not belong to the wallet.`, nanoContractStateNotFound: t`Nano Contract not found.`, nanoContractStateFailure: t`Error while trying to get Nano Contract state.`, + notRegistered: t`Nano Contract not registered.`, + nanoContractHistoryFailure: t`Error while trying to fetch Nano Contract history.`, }; /** @@ -39,31 +49,36 @@ export function* registerNanoContract({ payload }) { const wallet = yield select((state) => state.wallet); if (!wallet.isReady()) { + log.debug('Fail registering Nano Contract because wallet is not ready yet.'); yield put(nanoContractRegisterFailure(failureMessage.walletNotReadyError)); // This will show user an error modal with the option to send the error to sentry. yield put(onExceptionCaptured(new Error(failureMessage.walletNotReadyError), false)); return; } - const isRegistered = yield call(wallet.storage.isNanoContractRegistered, ncId); + const isNanoContractRegisteredFn = wallet.storage.isNanoContractRegistered.bind(wallet.storage); + const isRegistered = yield call(isNanoContractRegisteredFn, ncId); if (isRegistered) { + log.debug('Fail registering Nano Contract because it is already registered.'); yield put(nanoContractRegisterFailure(failureMessage.alreadyRegistered)); return; } const isAddressMine = yield call(wallet.isAddressMine.bind(wallet), address); if (!isAddressMine) { + log.debug('Fail registering Nano Contract because address do not belongs to this wallet.'); yield put(nanoContractRegisterFailure(failureMessage.addressNotMine)); return; } let ncState = null; try { - ncState = yield call(ncApi.getNanoContractState, ncId); + ncState = yield call(ncApi.getNanoContractState.bind(ncApi), ncId); } catch (error) { if (error instanceof NanoRequest404Error) { yield put(nanoContractRegisterFailure(failureMessage.nanoContractStateNotFound)); } else { + log.error('Error while registering Nano Contract.', error); yield put(nanoContractRegisterFailure(failureMessage.nanoContractStateFailure)); } return; @@ -75,14 +90,166 @@ export function* registerNanoContract({ payload }) { blueprintId: ncState.blueprint_id, blueprintName: ncState.blueprint_name }; - yield call(wallet.storage.registerNanoContract, ncId, nc); + yield call(wallet.storage.registerNanoContract.bind(wallet.storage), ncId, nc); + log.debug('Success registering Nano Contract.'); // emit action NANOCONTRACT_REGISTER_SUCCESS yield put(nanoContractRegisterSuccess({ entryKey: ncId, entryValue: nc })); } +/** + * @typedef {Object} RawNcTxHistory + * @property {string} hash + * @property {Object[]} inputs + * @property {Object[]} outputs + * @property {string} nc_args + * @property {string} nc_blueprint_id + * @property {string} nc_id + * @property {string} nc_method + * @property {string} nc_pubkey + * @property {number} nonce + * @property {Object[]} parents + * @property {number} signal_bits + * @property {Date} timestamp + * @property {string[]} tokens + * @property {number} version + * @property {number} weight + * @property {boolean} is_voided + * + * @typedef {Object} RawNcTxHistoryResponse + * @property {boolean} success + * @property {number|null} after + * @property {RawNcTxHistory} history + */ + +/** + * @typedef {Object} NcTxHistory - Nano Contract's Transactions History + * @property {string} txId - transaction hash + * @property {number} timestamp + * @property {string} tokens - list of token's uid + * @property {{[uid: string]: Object}} balance - balance per token + * @property {boolean} isVoided - flag for transaction void + * @property {string} ncId - ID of Nano Contract + * @property {string} ncMethod - method called on Nano Contract + * @property {string} blueprintId - id of the Blueprint instantiated by Nano Contract + * @property {string} caller - address of the caller + * @property {boolean} isMine - flag indicating the caller address belongs to this wallet + */ + +/** + * Fetch history from Nano Contract wallet's API. + * @param {string} ncId Nano Contract ID + * @param {number} count Maximum quantity of history items + * @param {string} after Transaction hash to start to get items + * @param {Object} wallet Wallet instance from redux state + * + * @throws {Error} when request code is greater then 399 or when response's success is false + */ +export async function fetchHistory(ncId, count, after, wallet) { + /** + * @type {RawNcTxHistoryResponse} response + */ + const response = await ncApi.getNanoContractHistory(ncId, count, after); + const { success, history: rawHistory } = response; + + if (!success) { + throw new Error('Failed to fetch nano contract history'); + } + + const history = []; + for (const rawTx of rawHistory) { + const network = wallet.getNetworkObject(); + const caller = addressUtils.getAddressFromPubkey(rawTx.nc_pubkey, network).base58; + // XXX: Wallet Service Wallet doesn't implement isAddressMine. + // It means this method can't run under wallet-service without + // throwing an exception. + // eslint-disable-next-line no-await-in-loop + const isMine = await wallet.isAddressMine(caller); + const getTxBalanceFn = transactionUtils.getTxBalance.bind(transactionUtils); + // XXX: Wallet Service Wallet doesn't support getTxBalanceFn. + // It means this method can't run under wallet-service without + // throwing an exception. + // eslint-disable-next-line no-await-in-loop + const balance = await getTxBalanceFn(rawTx, wallet.storage); + const tx = { + txId: rawTx.hash, + timestamp: rawTx.timestamp, + tokens: rawTx.tokens, + isVoided: rawTx.is_voided, + ncId: rawTx.nc_id, + ncMethod: rawTx.nc_method, + blueprintId: rawTx.nc_blueprint_id, + caller, + isMine, + balance, + }; + history.push(tx); + } + + let next = null; + if (history && history.length === count) { + next = history[history.length - 1].txId; + } + + return { history, next }; +} + +/** + * Process Nano Contract history request. + * @param {{ + * payload: { + * ncId: string; + * after: string; + * } + * }} action with request payload. + */ +export function* requestHistoryNanoContract({ payload }) { + const { ncId, after } = payload; + const count = NANO_CONTRACT_TX_HISTORY_SIZE; + + const historyMeta = yield select((state) => state.nanoContract.historyMeta); + if (historyMeta[ncId] && historyMeta[ncId].isLoading) { + // Do nothing if nano contract already loading... + return; + } + + const wallet = yield select((state) => state.wallet); + if (!wallet.isReady()) { + log.debug('Fail fetching Nano Contract history because wallet is not ready.'); + yield put(nanoContractHistoryFailure({ ncId, error: failureMessage.walletNotReadyError })); + // This will show user an error modal with the option to send the error to sentry. + yield put(onExceptionCaptured(new Error(failureMessage.walletNotReadyError), false)); + return; + } + + const fn = wallet.storage.isNanoContractRegistered.bind(wallet.storage); + const isNcRegistered = yield call(fn, ncId); + if (!isNcRegistered) { + log.debug('Fail fetching Nano Contract history because Nano Contract is not registered yet.'); + yield put(nanoContractHistoryFailure({ ncId, error: failureMessage.notRegistered })); + return; + } + + try { + // fetch from fullnode + const { history, next } = yield call(fetchHistory, ncId, count, after, wallet); + + log.debug('Success fetching Nano Contract history.'); + yield put(nanoContractHistorySuccess({ ncId, history, after: next })); + } catch (error) { + log.error('Error while fetching Nano Contract history.', error); + // break loading process and give feedback to user + yield put( + nanoContractHistoryFailure({ ncId, error: failureMessage.nanoContractHistoryFailure }) + ); + // give opportunity for users to send the error to our team + yield put(onExceptionCaptured(error, false)); + } +} + export function* saga() { yield all([ takeEvery(types.NANOCONTRACT_REGISTER_REQUEST, registerNanoContract), + takeEvery(types.NANOCONTRACT_HISTORY_REQUEST, requestHistoryNanoContract), ]); } From 836e3419a49d2e0b3ec30d58ed11f95a0cb95230 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Fri, 3 May 2024 22:47:04 +0100 Subject: [PATCH 10/51] feat: add nano contract info to tx history [6] (#434) * feat: add nano contract info to tx history --- locale/da/texts.po | 100 +++++++++++-------- locale/pt-br/texts.po | 92 +++++++++++------- locale/ru-ru/texts.po | 100 +++++++++++-------- locale/texts.pot | 93 +++++++++++------- src/actions.js | 10 +- src/components/Icons/constants.js | 7 ++ src/components/Icons/helper.js | 7 ++ src/components/PublicExplorerListButton.js | 19 +++- src/components/SimpleButton.js | 33 +++++-- src/components/TokenSelect.js | 48 +++------ src/components/TxDetailsModal.js | 84 ++++++++++++---- src/models.js | 106 +++++++++++++++++++- src/reducers/reducer.js | 20 ++-- src/sagas/nanoContract.js | 7 ++ src/sagas/tokens.js | 11 ++- src/screens/MainScreen.js | 107 ++++++++------------- src/styles/themes.js | 1 + src/utils.js | 35 ++++--- 18 files changed, 566 insertions(+), 314 deletions(-) diff --git a/locale/da/texts.po b/locale/da/texts.po index 99cb9d499..6c791c070 100644 --- a/locale/da/texts.po +++ b/locale/da/texts.po @@ -13,55 +13,55 @@ msgstr "" "X-Generator: Poedit 3.3.1\n" #. This should never happen! -#: src/models.js:24 +#: src/models.js:79 msgid "Unknown" msgstr "Ukendt" -#: src/models.js:27 +#: src/models.js:82 #, javascript-format msgid "Received ${ symbol }" msgstr "Modtaget ${ symbol }" -#: src/models.js:29 +#: src/models.js:84 msgid "Sent ${ symbol }" msgstr "Sendt ${ symbol }" -#: src/models.js:31 +#: src/models.js:86 #, javascript-format msgid "You sent ${ symbol } to yourself" msgstr "Du sendte ${ symbol } til dig selv" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:41 +#: src/models.js:96 msgid "[Today •] HH:mm" msgstr "[I dag •] HH:mm" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:42 +#: src/models.js:97 msgid "[Tomorrow •] HH:mm" msgstr "[I morgen •] HH:mm" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:43 +#: src/models.js:98 msgid "dddd [•] HH:mm" msgstr "dddd [•] HH:mm" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:44 +#: src/models.js:99 msgid "[Yesterday •] HH:mm" msgstr "[I går •] HH:mm" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:45 +#: src/models.js:100 msgid "[Last] dddd [•] HH:mm" msgstr "[Sidste] dddd [•] HH:mm" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:46 +#: src/models.js:101 msgid "DD MMM YYYY [•] HH:mm" msgstr "DD MMM YYYY [•] HH:mm" -#: src/utils.js:146 +#: src/utils.js:159 msgid "Invalid address" msgstr "Ugyldig adresse" @@ -105,7 +105,7 @@ msgstr "" #: src/workers/pushNotificationHandler.js:118 msgid "New transaction received" -msgstr "Ingen transaktioner" +msgstr "" #: src/screens/About.js:83 msgid "ABOUT" @@ -472,11 +472,11 @@ msgstr "" msgid "Please |tryAgain:try again|" msgstr "" -#: src/screens/MainScreen.js:560 src/screens/MainScreen.js:592 +#: src/screens/MainScreen.js:535 src/screens/MainScreen.js:567 msgid "Available Balance" msgstr "Tilgængelig saldo" -#: src/screens/MainScreen.js:569 +#: src/screens/MainScreen.js:544 msgid "Locked" msgstr "Låst" @@ -489,11 +489,11 @@ msgstr "Du har lige modtaget **${ amount } ${ symbol }**" msgid "PAYMENT REQUEST" msgstr "BETALINGSANMODNING" -#: src/components/TxDetailsModal.js:59 src/screens/PaymentRequestDetail.js:123 +#: src/components/TxDetailsModal.js:99 src/screens/PaymentRequestDetail.js:123 msgid "Token" msgstr "Token" -#: src/components/TxDetailsModal.js:106 src/screens/PaymentRequestDetail.js:127 +#: src/components/TxDetailsModal.js:152 src/screens/PaymentRequestDetail.js:127 msgid "Amount" msgstr "Antal" @@ -677,11 +677,11 @@ msgstr "SEND ${ tokenNameUpperCase }" msgid "Your transfer is being processed" msgstr "Din overførsel behandles" -#: src/sagas/helpers.js:136 src/screens/SendConfirmScreen.js:104 +#: src/sagas/helpers.js:139 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:137 src/screens/SendConfirmScreen.js:105 +#: src/sagas/helpers.js:140 src/screens/SendConfirmScreen.js:105 msgid "Authorize operation" msgstr "Autoriserer overførslen" @@ -917,71 +917,71 @@ msgid "" "this can potentially make you susceptible to fraudulent schemes." msgstr "" -#: src/sagas/nanoContract.js:21 +#: src/sagas/nanoContract.js:36 msgid "Nano Contract already registered." msgstr "" -#: src/sagas/nanoContract.js:22 +#: src/sagas/nanoContract.js:37 msgid "Wallet is not ready yet to register a Nano Contract." msgstr "" -#: src/sagas/nanoContract.js:23 +#: src/sagas/nanoContract.js:38 msgid "The informed address does not belong to the wallet." msgstr "" -#: src/sagas/nanoContract.js:24 +#: src/sagas/nanoContract.js:39 msgid "Nano Contract not found." msgstr "" -#: src/sagas/nanoContract.js:25 +#: src/sagas/nanoContract.js:40 msgid "Error while trying to get Nano Contract state." msgstr "" -#: src/sagas/nanoContract.js:35 +#: src/sagas/nanoContract.js:41 msgid "Nano Contract not registered." msgstr "" -#: src/sagas/nanoContract.js:36 +#: src/sagas/nanoContract.js:42 msgid "Error while trying to fetch Nano Contract history." msgstr "" -#: src/sagas/networkSettings.js:88 +#: src/sagas/networkSettings.js:91 msgid "Custom Network Settings cannot be empty." msgstr "" -#: src/sagas/networkSettings.js:95 +#: src/sagas/networkSettings.js:98 msgid "explorerUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:102 +#: src/sagas/networkSettings.js:105 msgid "explorerServiceUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:109 +#: src/sagas/networkSettings.js:112 msgid "txMiningServiceUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:116 +#: src/sagas/networkSettings.js:119 msgid "nodeUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:123 +#: src/sagas/networkSettings.js:126 msgid "walletServiceUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:130 +#: src/sagas/networkSettings.js:133 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:290 +#: src/sagas/networkSettings.js:296 msgid "Wallet not found while trying to persist the custom network settings." msgstr "" #: src/sagas/pushNotification.js:59 msgid "Transaction" -msgstr "Ingen transaktioner" +msgstr "Transaktion" #: src/sagas/pushNotification.js:60 msgid "Open" @@ -1086,21 +1086,21 @@ msgstr "Opret betalingsanmodning" msgid "No internet connection" msgstr "Ingen internetforbindelse" -#: src/components/PublicExplorerListButton.js:19 +#: src/components/PublicExplorerListButton.js:17 msgid "Public Explorer" msgstr "Public Explorer" -#: src/components/PushTxDetailsModal.js:69 src/components/TxDetailsModal.js:61 +#: src/components/PushTxDetailsModal.js:69 src/components/TxDetailsModal.js:101 msgid "Date & Time" msgstr "Dato & Tid" -#: src/components/PushTxDetailsModal.js:70 src/components/TxDetailsModal.js:62 +#: src/components/PushTxDetailsModal.js:70 msgid "ID" msgstr "ID" #: src/components/PushTxDetailsModal.js:94 msgid "New Transaction" -msgstr "Ingen transaktioner" +msgstr "" #: src/components/ReceiveMyAddress.js:34 #, javascript-format @@ -1121,7 +1121,7 @@ msgstr "" #: src/components/ShowPushNotificationTxDetails.js:8 msgid "Transation not found" -msgstr "Ingen transaktioner" +msgstr "" #: src/components/ShowPushNotificationTxDetails.js:9 msgid "" @@ -1151,10 +1151,30 @@ msgstr "" msgid "Voided" msgstr "" -#: src/components/TxDetailsModal.js:60 +#: src/components/TxDetailsModal.js:100 msgid "Description" msgstr "Beskrivelse" +#: src/components/TxDetailsModal.js:102 +msgid "Transaction ID" +msgstr "" + +#: src/components/TxDetailsModal.js:103 +msgid "Blueprint Method" +msgstr "" + +#: src/components/TxDetailsModal.js:104 +msgid "Nano Contract ID" +msgstr "" + +#: src/components/TxDetailsModal.js:105 +msgid "Nano Contract Caller" +msgstr "" + +#: src/components/TxDetailsModal.js:106 +msgid "Nano Contract" +msgstr "" + #: src/components/WalletConnect/ApproveRejectModal.js:63 msgid "Reject" msgstr "" diff --git a/locale/pt-br/texts.po b/locale/pt-br/texts.po index a9342f023..ed908ed8e 100644 --- a/locale/pt-br/texts.po +++ b/locale/pt-br/texts.po @@ -13,55 +13,55 @@ msgstr "" "X-Generator: Poedit 3.3.1\n" #. This should never happen! -#: src/models.js:24 +#: src/models.js:79 msgid "Unknown" msgstr "Desconhecido" -#: src/models.js:27 +#: src/models.js:82 #, javascript-format msgid "Received ${ symbol }" msgstr "Recebido ${ symbol }" -#: src/models.js:29 +#: src/models.js:84 msgid "Sent ${ symbol }" msgstr "Enviado ${ symbol }" -#: src/models.js:31 +#: src/models.js:86 #, javascript-format msgid "You sent ${ symbol } to yourself" msgstr "Você enviou ${ symbol } para você mesmo" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:41 +#: src/models.js:96 msgid "[Today •] HH:mm" msgstr "[Hoje •] HH:mm" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:42 +#: src/models.js:97 msgid "[Tomorrow •] HH:mm" msgstr "[Amanhã •] HH:mm" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:43 +#: src/models.js:98 msgid "dddd [•] HH:mm" msgstr "dddd [•] HH:mm" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:44 +#: src/models.js:99 msgid "[Yesterday •] HH:mm" msgstr "[Ontem •] HH:mm" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:45 +#: src/models.js:100 msgid "[Last] dddd [•] HH:mm" msgstr "[Última] dddd [•] HH:mm" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:46 +#: src/models.js:101 msgid "DD MMM YYYY [•] HH:mm" msgstr "DD MMM YYYY [•] HH:mm" -#: src/utils.js:146 +#: src/utils.js:159 msgid "Invalid address" msgstr "Endereço inválido" @@ -485,11 +485,11 @@ msgstr "Ocorreu um erro ao carregar seu histórico de transações" msgid "Please |tryAgain:try again|" msgstr "Por favor |tryAgain:tente novamente|" -#: src/screens/MainScreen.js:560 src/screens/MainScreen.js:592 +#: src/screens/MainScreen.js:535 src/screens/MainScreen.js:567 msgid "Available Balance" msgstr "Saldo Disponível" -#: src/screens/MainScreen.js:569 +#: src/screens/MainScreen.js:544 msgid "Locked" msgstr "Bloqueado" @@ -502,11 +502,11 @@ msgstr "Você recebeu **${ amount } ${ symbol }**" msgid "PAYMENT REQUEST" msgstr "REQUISIÇÃO DE PAGAMENTO" -#: src/components/TxDetailsModal.js:59 src/screens/PaymentRequestDetail.js:123 +#: src/components/TxDetailsModal.js:99 src/screens/PaymentRequestDetail.js:123 msgid "Token" msgstr "Token" -#: src/components/TxDetailsModal.js:106 src/screens/PaymentRequestDetail.js:127 +#: src/components/TxDetailsModal.js:152 src/screens/PaymentRequestDetail.js:127 msgid "Amount" msgstr "Quantidade" @@ -693,11 +693,11 @@ msgstr "ENVIAR ${ tokenNameUpperCase }" msgid "Your transfer is being processed" msgstr "Sua transferência está sendo processada" -#: src/sagas/helpers.js:136 src/screens/SendConfirmScreen.js:104 +#: src/sagas/helpers.js:139 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:137 src/screens/SendConfirmScreen.js:105 +#: src/sagas/helpers.js:140 src/screens/SendConfirmScreen.js:105 msgid "Authorize operation" msgstr "Autorizar operação" @@ -943,65 +943,65 @@ 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/nanoContract.js:21 +#: src/sagas/nanoContract.js:36 msgid "Nano Contract already registered." msgstr "Nano Contract já registrado." -#: src/sagas/nanoContract.js:22 +#: src/sagas/nanoContract.js:37 msgid "Wallet is not ready yet to register a Nano Contract." msgstr "A wallet não está pronta ainda para registrar Nano Contracts." -#: src/sagas/nanoContract.js:23 +#: src/sagas/nanoContract.js:38 msgid "The informed address does not belong to the wallet." msgstr "O endereço informado não pertence a esta carteira." -#: src/sagas/nanoContract.js:24 +#: src/sagas/nanoContract.js:39 msgid "Nano Contract not found." msgstr "Nano Contract não encontrado" -#: src/sagas/nanoContract.js:25 +#: src/sagas/nanoContract.js:40 msgid "Error while trying to get Nano Contract state." msgstr "Erro ao obter o estado do Nano Contract." -#: src/sagas/nanoContract.js:35 +#: src/sagas/nanoContract.js:41 msgid "Nano Contract not registered." msgstr "Nano Contract não registrado." -#: src/sagas/nanoContract.js:36 +#: src/sagas/nanoContract.js:42 msgid "Error while trying to fetch Nano Contract history." msgstr "Error ao fazer download do histórico de transações do Nano Contract." -#: src/sagas/networkSettings.js:88 +#: src/sagas/networkSettings.js:91 msgid "Custom Network Settings cannot be empty." msgstr "As Configurações de Rede não podem estar vazias." -#: src/sagas/networkSettings.js:95 +#: src/sagas/networkSettings.js:98 msgid "explorerUrl should be a valid URL." msgstr "explorerUrl deve ser uma URL válida." -#: src/sagas/networkSettings.js:102 +#: src/sagas/networkSettings.js:105 msgid "explorerServiceUrl should be a valid URL." msgstr "explorerServiceUrl deve ser uma URL válida." -#: src/sagas/networkSettings.js:109 +#: src/sagas/networkSettings.js:112 msgid "txMiningServiceUrl should be a valid URL." msgstr "txMiningServiceUrl deve ser uma URL válida." -#: src/sagas/networkSettings.js:116 +#: src/sagas/networkSettings.js:119 msgid "nodeUrl should be a valid URL." msgstr "nodeUrl deve ser uma URL válida." -#: src/sagas/networkSettings.js:123 +#: src/sagas/networkSettings.js:126 msgid "walletServiceUrl should be a valid URL." msgstr "walletServiceUrl deve ser uma URL válida." -#: src/sagas/networkSettings.js:130 +#: src/sagas/networkSettings.js:133 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:290 +#: src/sagas/networkSettings.js:296 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." @@ -1116,15 +1116,15 @@ msgstr "Criar requisição de pagamento" msgid "No internet connection" msgstr "Sem conexão com a internet" -#: src/components/PublicExplorerListButton.js:19 +#: src/components/PublicExplorerListButton.js:17 msgid "Public Explorer" msgstr "Explorer Público" -#: src/components/PushTxDetailsModal.js:69 src/components/TxDetailsModal.js:61 +#: src/components/PushTxDetailsModal.js:69 src/components/TxDetailsModal.js:101 msgid "Date & Time" msgstr "Data & Hora" -#: src/components/PushTxDetailsModal.js:70 src/components/TxDetailsModal.js:62 +#: src/components/PushTxDetailsModal.js:70 msgid "ID" msgstr "ID" @@ -1180,10 +1180,30 @@ msgstr "Processando" msgid "Voided" msgstr "Inválida" -#: src/components/TxDetailsModal.js:60 +#: src/components/TxDetailsModal.js:100 msgid "Description" msgstr "Descrição" +#: src/components/TxDetailsModal.js:102 +msgid "Transaction ID" +msgstr "ID da Transação" + +#: src/components/TxDetailsModal.js:103 +msgid "Blueprint Method" +msgstr "Método do Blueprint" + +#: src/components/TxDetailsModal.js:104 +msgid "Nano Contract ID" +msgstr "ID do Nano Contract" + +#: src/components/TxDetailsModal.js:105 +msgid "Nano Contract Caller" +msgstr "Endereço de assinatura do Nano Contract" + +#: src/components/TxDetailsModal.js:106 +msgid "Nano Contract" +msgstr "Nano Contract" + #: src/components/WalletConnect/ApproveRejectModal.js:63 msgid "Reject" msgstr "Rejeitar" diff --git a/locale/ru-ru/texts.po b/locale/ru-ru/texts.po index ec417dbaf..307436867 100644 --- a/locale/ru-ru/texts.po +++ b/locale/ru-ru/texts.po @@ -14,55 +14,55 @@ msgstr "" "X-Generator: Poedit 3.3.1\n" #. This should never happen! -#: src/models.js:24 +#: src/models.js:79 msgid "Unknown" msgstr "Неизвестно" -#: src/models.js:27 +#: src/models.js:82 #, javascript-format msgid "Received ${ symbol }" msgstr "Получено ${ symbol }" -#: src/models.js:29 +#: src/models.js:84 msgid "Sent ${ symbol }" msgstr "Отправлено ${ symbol }" -#: src/models.js:31 +#: src/models.js:86 #, javascript-format msgid "You sent ${ symbol } to yourself" msgstr "Вы отправили себе ${ symbol }" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:41 +#: src/models.js:96 msgid "[Today •] HH:mm" msgstr "[Сегодня •] HH:mm" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:42 +#: src/models.js:97 msgid "[Tomorrow •] HH:mm" msgstr "[Завтра •] HH:mm" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:43 +#: src/models.js:98 msgid "dddd [•] HH:mm" msgstr "dddd [•] HH:mm" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:44 +#: src/models.js:99 msgid "[Yesterday •] HH:mm" msgstr "[Вчера •] HH:mm" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:45 +#: src/models.js:100 msgid "[Last] dddd [•] HH:mm" msgstr "[Последний] dddd [•] HH:mm" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:46 +#: src/models.js:101 msgid "DD MMM YYYY [•] HH:mm" msgstr "DD MMM YYYY [•] HH:mm" -#: src/utils.js:146 +#: src/utils.js:159 msgid "Invalid address" msgstr "Неправильный адрес" @@ -107,7 +107,7 @@ msgstr "" #: src/workers/pushNotificationHandler.js:118 msgid "New transaction received" -msgstr "Нет транзакций" +msgstr "" #: src/screens/About.js:83 msgid "ABOUT" @@ -474,11 +474,11 @@ msgstr "" msgid "Please |tryAgain:try again|" msgstr "" -#: src/screens/MainScreen.js:560 src/screens/MainScreen.js:592 +#: src/screens/MainScreen.js:535 src/screens/MainScreen.js:567 msgid "Available Balance" msgstr "Доступный Баланс" -#: src/screens/MainScreen.js:569 +#: src/screens/MainScreen.js:544 msgid "Locked" msgstr "Заблокированный" @@ -491,11 +491,11 @@ msgstr "Вы только что получили **${ amount } ${ symbol }**" msgid "PAYMENT REQUEST" msgstr "ЗАПРОС СРЕДСТВ" -#: src/components/TxDetailsModal.js:59 src/screens/PaymentRequestDetail.js:123 +#: src/components/TxDetailsModal.js:99 src/screens/PaymentRequestDetail.js:123 msgid "Token" msgstr "Токен" -#: src/components/TxDetailsModal.js:106 src/screens/PaymentRequestDetail.js:127 +#: src/components/TxDetailsModal.js:152 src/screens/PaymentRequestDetail.js:127 msgid "Amount" msgstr "Количество" @@ -679,11 +679,11 @@ msgstr "ОТПРАВИТЬ ${ tokenNameUpperCase }" msgid "Your transfer is being processed" msgstr "Ваш перевод обрабатывается" -#: src/sagas/helpers.js:136 src/screens/SendConfirmScreen.js:104 +#: src/sagas/helpers.js:139 src/screens/SendConfirmScreen.js:104 msgid "Enter your 6-digit pin to authorize operation" msgstr "Введите 6-значный PIN-код для авторизации операции" -#: src/sagas/helpers.js:137 src/screens/SendConfirmScreen.js:105 +#: src/sagas/helpers.js:140 src/screens/SendConfirmScreen.js:105 msgid "Authorize operation" msgstr "Авторизовать операцию" @@ -921,71 +921,71 @@ msgid "" "this can potentially make you susceptible to fraudulent schemes." msgstr "" -#: src/sagas/nanoContract.js:21 +#: src/sagas/nanoContract.js:36 msgid "Nano Contract already registered." msgstr "" -#: src/sagas/nanoContract.js:22 +#: src/sagas/nanoContract.js:37 msgid "Wallet is not ready yet to register a Nano Contract." msgstr "" -#: src/sagas/nanoContract.js:23 +#: src/sagas/nanoContract.js:38 msgid "The informed address does not belong to the wallet." msgstr "" -#: src/sagas/nanoContract.js:24 +#: src/sagas/nanoContract.js:39 msgid "Nano Contract not found." msgstr "" -#: src/sagas/nanoContract.js:25 +#: src/sagas/nanoContract.js:40 msgid "Error while trying to get Nano Contract state." msgstr "" -#: src/sagas/nanoContract.js:35 +#: src/sagas/nanoContract.js:41 msgid "Nano Contract not registered." msgstr "" -#: src/sagas/nanoContract.js:36 +#: src/sagas/nanoContract.js:42 msgid "Error while trying to fetch Nano Contract history." msgstr "" -#: src/sagas/networkSettings.js:88 +#: src/sagas/networkSettings.js:91 msgid "Custom Network Settings cannot be empty." msgstr "" -#: src/sagas/networkSettings.js:95 +#: src/sagas/networkSettings.js:98 msgid "explorerUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:102 +#: src/sagas/networkSettings.js:105 msgid "explorerServiceUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:109 +#: src/sagas/networkSettings.js:112 msgid "txMiningServiceUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:116 +#: src/sagas/networkSettings.js:119 msgid "nodeUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:123 +#: src/sagas/networkSettings.js:126 msgid "walletServiceUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:130 +#: src/sagas/networkSettings.js:133 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:290 +#: src/sagas/networkSettings.js:296 msgid "Wallet not found while trying to persist the custom network settings." msgstr "" #: src/sagas/pushNotification.js:59 msgid "Transaction" -msgstr "Нет транзакций" +msgstr "" #: src/sagas/pushNotification.js:60 msgid "Open" @@ -1076,21 +1076,21 @@ msgstr "Создать запрос на получение средств" msgid "No internet connection" msgstr "Нет соединения с интернетом" -#: src/components/PublicExplorerListButton.js:19 +#: src/components/PublicExplorerListButton.js:17 msgid "Public Explorer" msgstr "Public Explorer" -#: src/components/PushTxDetailsModal.js:69 src/components/TxDetailsModal.js:61 +#: src/components/PushTxDetailsModal.js:69 src/components/TxDetailsModal.js:101 msgid "Date & Time" msgstr "Дата и Время" -#: src/components/PushTxDetailsModal.js:70 src/components/TxDetailsModal.js:62 +#: src/components/PushTxDetailsModal.js:70 msgid "ID" msgstr "ID" #: src/components/PushTxDetailsModal.js:94 msgid "New Transaction" -msgstr "Нет транзакций" +msgstr "" #: src/components/ReceiveMyAddress.js:34 #, javascript-format @@ -1111,7 +1111,7 @@ msgstr "" #: src/components/ShowPushNotificationTxDetails.js:8 msgid "Transation not found" -msgstr "Нет транзакций" +msgstr "" #: src/components/ShowPushNotificationTxDetails.js:9 msgid "" @@ -1140,10 +1140,30 @@ msgstr "" msgid "Voided" msgstr "" -#: src/components/TxDetailsModal.js:60 +#: src/components/TxDetailsModal.js:100 msgid "Description" msgstr "Описание" +#: src/components/TxDetailsModal.js:102 +msgid "Transaction ID" +msgstr "" + +#: src/components/TxDetailsModal.js:103 +msgid "Blueprint Method" +msgstr "" + +#: src/components/TxDetailsModal.js:104 +msgid "Nano Contract ID" +msgstr "" + +#: src/components/TxDetailsModal.js:105 +msgid "Nano Contract Caller" +msgstr "" + +#: src/components/TxDetailsModal.js:106 +msgid "Nano Contract" +msgstr "" + #: src/components/WalletConnect/ApproveRejectModal.js:63 msgid "Reject" msgstr "" diff --git a/locale/texts.pot b/locale/texts.pot index 293d8fa2c..de3cb6fdf 100644 --- a/locale/texts.pot +++ b/locale/texts.pot @@ -3,56 +3,56 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Plural-Forms: nplurals=2; plural=(n!=1);\n" -#: src/models.js:24 +#: src/models.js:79 #. This should never happen! msgid "Unknown" msgstr "" -#: src/models.js:27 +#: src/models.js:82 #, javascript-format msgid "Received ${ symbol }" msgstr "" -#: src/models.js:29 +#: src/models.js:84 msgid "Sent ${ symbol }" msgstr "" -#: src/models.js:31 +#: src/models.js:86 #, javascript-format msgid "You sent ${ symbol } to yourself" msgstr "" -#: src/models.js:41 +#: src/models.js:96 #. See https://momentjs.com/docs/#/displaying/calendar-time/ msgid "[Today •] HH:mm" msgstr "" -#: src/models.js:42 +#: src/models.js:97 #. See https://momentjs.com/docs/#/displaying/calendar-time/ msgid "[Tomorrow •] HH:mm" msgstr "" -#: src/models.js:43 +#: src/models.js:98 #. See https://momentjs.com/docs/#/displaying/calendar-time/ msgid "dddd [•] HH:mm" msgstr "" -#: src/models.js:44 +#: src/models.js:99 #. See https://momentjs.com/docs/#/displaying/calendar-time/ msgid "[Yesterday •] HH:mm" msgstr "" -#: src/models.js:45 +#: src/models.js:100 #. See https://momentjs.com/docs/#/displaying/calendar-time/ msgid "[Last] dddd [•] HH:mm" msgstr "" -#: src/models.js:46 +#: src/models.js:101 #. See https://momentjs.com/docs/#/displaying/calendar-time/ msgid "DD MMM YYYY [•] HH:mm" msgstr "" -#: src/utils.js:146 +#: src/utils.js:159 msgid "Invalid address" msgstr "" @@ -461,12 +461,12 @@ msgstr "" msgid "Please |tryAgain:try again|" msgstr "" -#: src/screens/MainScreen.js:560 -#: src/screens/MainScreen.js:592 +#: src/screens/MainScreen.js:535 +#: src/screens/MainScreen.js:567 msgid "Available Balance" msgstr "" -#: src/screens/MainScreen.js:569 +#: src/screens/MainScreen.js:544 msgid "Locked" msgstr "" @@ -479,12 +479,12 @@ msgstr "" msgid "PAYMENT REQUEST" msgstr "" -#: src/components/TxDetailsModal.js:59 +#: src/components/TxDetailsModal.js:99 #: src/screens/PaymentRequestDetail.js:123 msgid "Token" msgstr "" -#: src/components/TxDetailsModal.js:106 +#: src/components/TxDetailsModal.js:152 #: src/screens/PaymentRequestDetail.js:127 msgid "Amount" msgstr "" @@ -671,12 +671,12 @@ msgstr "" msgid "Your transfer is being processed" msgstr "" -#: src/sagas/helpers.js:136 +#: src/sagas/helpers.js:139 #: src/screens/SendConfirmScreen.js:104 msgid "Enter your 6-digit pin to authorize operation" msgstr "" -#: src/sagas/helpers.js:137 +#: src/sagas/helpers.js:140 #: src/screens/SendConfirmScreen.js:105 msgid "Authorize operation" msgstr "" @@ -912,63 +912,63 @@ msgid "" "this can potentially make you susceptible to fraudulent schemes." msgstr "" -#: src/sagas/nanoContract.js:21 +#: src/sagas/nanoContract.js:36 msgid "Nano Contract already registered." msgstr "" -#: src/sagas/nanoContract.js:22 +#: src/sagas/nanoContract.js:37 msgid "Wallet is not ready yet to register a Nano Contract." msgstr "" -#: src/sagas/nanoContract.js:23 +#: src/sagas/nanoContract.js:38 msgid "The informed address does not belong to the wallet." msgstr "" -#: src/sagas/nanoContract.js:24 +#: src/sagas/nanoContract.js:39 msgid "Nano Contract not found." msgstr "" -#: src/sagas/nanoContract.js:25 +#: src/sagas/nanoContract.js:40 msgid "Error while trying to get Nano Contract state." msgstr "" -#: src/sagas/nanoContract.js:35 +#: src/sagas/nanoContract.js:41 msgid "Nano Contract not registered." msgstr "" -#: src/sagas/nanoContract.js:36 +#: src/sagas/nanoContract.js:42 msgid "Error while trying to fetch Nano Contract history." msgstr "" -#: src/sagas/networkSettings.js:88 +#: src/sagas/networkSettings.js:91 msgid "Custom Network Settings cannot be empty." msgstr "" -#: src/sagas/networkSettings.js:95 +#: src/sagas/networkSettings.js:98 msgid "explorerUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:102 +#: src/sagas/networkSettings.js:105 msgid "explorerServiceUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:109 +#: src/sagas/networkSettings.js:112 msgid "txMiningServiceUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:116 +#: src/sagas/networkSettings.js:119 msgid "nodeUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:123 +#: src/sagas/networkSettings.js:126 msgid "walletServiceUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:130 +#: src/sagas/networkSettings.js:133 msgid "walletServiceWsUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:290 +#: src/sagas/networkSettings.js:296 #. 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." @@ -1067,17 +1067,16 @@ msgstr "" msgid "No internet connection" msgstr "" -#: src/components/PublicExplorerListButton.js:19 +#: src/components/PublicExplorerListButton.js:17 msgid "Public Explorer" msgstr "" #: src/components/PushTxDetailsModal.js:69 -#: src/components/TxDetailsModal.js:61 +#: src/components/TxDetailsModal.js:101 msgid "Date & Time" msgstr "" #: src/components/PushTxDetailsModal.js:70 -#: src/components/TxDetailsModal.js:62 msgid "ID" msgstr "" @@ -1132,10 +1131,30 @@ msgstr "" msgid "Voided" msgstr "" -#: src/components/TxDetailsModal.js:60 +#: src/components/TxDetailsModal.js:100 msgid "Description" msgstr "" +#: src/components/TxDetailsModal.js:102 +msgid "Transaction ID" +msgstr "" + +#: src/components/TxDetailsModal.js:103 +msgid "Blueprint Method" +msgstr "" + +#: src/components/TxDetailsModal.js:104 +msgid "Nano Contract ID" +msgstr "" + +#: src/components/TxDetailsModal.js:105 +msgid "Nano Contract Caller" +msgstr "" + +#: src/components/TxDetailsModal.js:106 +msgid "Nano Contract" +msgstr "" + #: src/components/WalletConnect/ApproveRejectModal.js:63 msgid "Reject" msgstr "" diff --git a/src/actions.js b/src/actions.js index ceb528368..2347b759f 100644 --- a/src/actions.js +++ b/src/actions.js @@ -13,7 +13,7 @@ import { import { METADATA_CONCURRENT_DOWNLOAD, } from './constants'; -import { mapTokenHistory } from './utils'; +import { mapToTxHistory } from './utils'; // TODO: We should apply the agreed taxonomy to all the actions. // See: https://github.com/HathorNetwork/hathor-wallet-mobile/issues/334 @@ -356,7 +356,7 @@ export const fetchHistoryAndBalance = async (wallet) => { const tokenBalance = balance[0].balance; tokensBalance[token] = { available: tokenBalance.unlocked, locked: tokenBalance.locked }; const history = await wallet.getTxHistory({ token_id: token }); - tokensHistory[token] = history.map((element) => mapTokenHistory(element, token)); + tokensHistory[token] = history.map(mapToTxHistory(token)); /* eslint-enable no-await-in-loop */ } @@ -372,7 +372,7 @@ export const fetchHistoryAndBalance = async (wallet) => { */ export const fetchMoreHistory = async (wallet, token, history) => { const newHistory = await wallet.getTxHistory({ token_id: token, skip: history.length }); - const newHistoryObjects = newHistory.map((element) => mapTokenHistory(element, token)); + const newHistoryObjects = newHistory.map(mapToTxHistory(token)); return newHistoryObjects; }; @@ -546,8 +546,8 @@ export const tokenFetchHistoryRequested = (tokenId, force) => ({ }); /** - * tokenId: The tokenId to store history data - * data: The downloaded history data + * @param {string} tokenId: The tokenId to store history data + * @param {TxHistory} data: The downloaded history data */ export const tokenFetchHistorySuccess = (tokenId, data) => ({ type: types.TOKEN_FETCH_HISTORY_SUCCESS, diff --git a/src/components/Icons/constants.js b/src/components/Icons/constants.js index 26778fd73..eacdd0110 100644 --- a/src/components/Icons/constants.js +++ b/src/components/Icons/constants.js @@ -1 +1,8 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + export const DEFAULT_ICON_SIZE = 24; diff --git a/src/components/Icons/helper.js b/src/components/Icons/helper.js index 4c9482e99..0610ad1c0 100644 --- a/src/components/Icons/helper.js +++ b/src/components/Icons/helper.js @@ -1,3 +1,10 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + /** * @param {number} size * @example diff --git a/src/components/PublicExplorerListButton.js b/src/components/PublicExplorerListButton.js index 827b33c32..338ed6c55 100644 --- a/src/components/PublicExplorerListButton.js +++ b/src/components/PublicExplorerListButton.js @@ -1,3 +1,10 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + import React from 'react'; import { t } from 'ttag'; import { Image, Linking } from 'react-native'; @@ -7,8 +14,9 @@ import { ListButton } from './HathorList'; import { COLORS } from '../styles/themes'; import { combineURLs } from '../utils'; -export function PublicExplorerListButton(props) { - const { txId } = props; +const DEFAULT_TITLE = t`Public Explorer`; + +export function PublicExplorerListButton({ txId, title }) { const explorerIcon = ; const baseExplorerUrl = useSelector((state) => state.networkSettings.explorerUrl); const txUrl = `transaction/${txId}`; @@ -16,6 +24,11 @@ export function PublicExplorerListButton(props) { const explorerLink = combineURLs(baseExplorerUrl, txUrl); return ( - { Linking.openURL(explorerLink); }} titleStyle={{ color: COLORS.textColorShadow }} isLast /> + { Linking.openURL(explorerLink) }} + titleStyle={{ color: COLORS.textColorShadow }} + /> ); } diff --git a/src/components/SimpleButton.js b/src/components/SimpleButton.js index 4c8e87e3e..9dfef6f11 100644 --- a/src/components/SimpleButton.js +++ b/src/components/SimpleButton.js @@ -11,24 +11,33 @@ import { } from 'react-native'; import { PRIMARY_COLOR } from '../constants'; -const SimpleButton = (props) => { +const SimpleButton = ({ + title, + color, + icon, + onPress, + containerStyle, + textStyle, + iconStyle, + children +}) => { const renderTitle = () => { - if (props.title) { - const textStyles = [styles.text, props.textStyle]; - if (props.color) { - textStyles.push({ color: props.color }); + if (title) { + const textStyles = [styles.text, textStyle]; + if (color) { + textStyles.push({ color }); } - return {props.title}; + return {title}; } return null; }; const renderIcon = () => { - if (props.icon) { + if (icon) { return ( - - + + ); } @@ -37,9 +46,13 @@ const SimpleButton = (props) => { }; return ( - + {renderTitle()} {renderIcon()} + {children} ); }; diff --git a/src/components/TokenSelect.js b/src/components/TokenSelect.js index d3d151a01..06c391627 100644 --- a/src/components/TokenSelect.js +++ b/src/components/TokenSelect.js @@ -7,7 +7,7 @@ import React from 'react'; import { - FlatList, Image, StyleSheet, View, Text, TouchableHighlight, + Image, StyleSheet, View, Text, TouchableHighlight, } from 'react-native'; import { get } from 'lodash'; import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome'; @@ -18,6 +18,7 @@ import Spinner from './Spinner'; import { renderValue, isTokenNFT } from '../utils'; import { TOKEN_DOWNLOAD_STATUS } from '../sagas/tokens'; import { COLORS } from '../styles/themes'; +import { HathorFlatList } from './HathorFlatList'; /** * @typedef TokenBalance @@ -37,7 +38,7 @@ import { COLORS } from '../styles/themes'; */ const TokenSelect = (props) => { const tokens = Object.values(props.tokens); - const renderItem = ({ item, index }) => { + const renderItem = ({ item }) => { const symbolWrapperStyle = [styles.symbolWrapper]; const symbolTextStyle = [styles.text, styles.leftText, styles.symbolText]; if (props.selectedToken && props.selectedToken.uid === item.uid) { @@ -50,7 +51,6 @@ const TokenSelect = (props) => { return ( { props.onItemPress(item); }} underlayColor={COLORS.primaryOpacity30} > @@ -94,51 +94,35 @@ const TokenSelect = (props) => { return ( {props.header} - - item.uid} - /> - + item.uid} + ItemSeparatorComponent={ItemSeparator} + /> ); }; +const ItemSeparator = () => ( + +); + const styles = StyleSheet.create({ wrapper: { flex: 1, - justifyContent: 'center', + justifyContent: 'flex-start', alignItems: 'center', backgroundColor: COLORS.lowContrastDetail, // Defines an outer area on the main list content }, - listWrapper: { - alignSelf: 'stretch', - flex: 1, - marginTop: 16, - backgroundColor: COLORS.backgroundColor, - marginHorizontal: 16, - borderTopLeftRadius: 16, - borderTopRightRadius: 16, - shadowOffset: { height: 2, width: 0 }, - shadowRadius: 4, - shadowColor: COLORS.textColor, - shadowOpacity: 0.08, - }, itemWrapper: { height: 80, paddingHorizontal: 16, flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', - borderBottomWidth: 1, - borderColor: COLORS.borderColor, - }, - firstItemWrapper: { - borderTopLeftRadius: 16, - borderTopRightRadius: 16, }, itemLeftWrapper: { flexDirection: 'row', diff --git a/src/components/TxDetailsModal.js b/src/components/TxDetailsModal.js index 52ca9b16d..ed67c7865 100644 --- a/src/components/TxDetailsModal.js +++ b/src/components/TxDetailsModal.js @@ -6,11 +6,11 @@ */ import React, { Component } from 'react'; -import { Text, StyleSheet, View } from 'react-native'; +import { Text, StyleSheet, View, ScrollView, TouchableWithoutFeedback } from 'react-native'; import Modal from 'react-native-modal'; import { t } from 'ttag'; -import { getShortHash, getTokenLabel, renderValue } from '../utils'; +import { getShortContent, getShortHash, getTokenLabel, renderValue } from '../utils'; import { ListItem } from './HathorList'; import SlideIndicatorBar from './SlideIndicatorBar'; import CopyClipboard from './CopyClipboard'; @@ -21,26 +21,63 @@ class TxDetailsModal extends Component { style = StyleSheet.create({ modal: { justifyContent: 'flex-end', + marginVertical: 0, + marginHorizontal: 0, + }, + wrapper: { + flex: 1, + paddingTop: 96, + paddingHorizontal: 8, }, inner: { - backgroundColor: COLORS.backgroundColor, + flexShrink: 1, + marginTop: 'auto', borderRadius: 8, + paddingBottom: 24, + backgroundColor: COLORS.backgroundColor, }, }); + getCopyClipboard = ({ text, copyText }) => ( + + ); + render() { + /** + * @type {{ + * token: unknown; + * tx: TxHistory; + * isNFT: boolean; + * }} TxDetailsModal properties + */ const { token, tx, isNFT } = this.props; + const { txId, ncId, ncMethod, ncCaller } = tx; + const ncCallerAddr = ncCaller && ncCaller.base58; + const fullTokenStr = getTokenLabel(token); const description = tx.getDescription(token); const timestampStr = tx.getTimestampFormat(); - const idStr = getShortHash(tx.txId, 12); - const txIdComponent = ( - - ); + const shortTxId = getShortHash(txId, 7); + const shortNcId = ncId && getShortHash(ncId, 7); + const shortNcCallerAddr = ncCallerAddr && getShortContent(ncCallerAddr, 7); + const txIdComponent = this.getCopyClipboard({ + text: shortTxId, + copyText: txId + }); + const ncIdComponent = ncId && this.getCopyClipboard({ + text: shortNcId, + copyText: ncId + }); + const ncCallerAddrComponent = ncCaller && this.getCopyClipboard({ + text: shortNcCallerAddr, + copyText: ncCallerAddr + }); + const isNc = tx.isNanoContract(); + return ( - + - - - - - - - + + + + + + + + {isNc && } + {isNc && } + {isNc && } + {isNc && } + + + + diff --git a/src/models.js b/src/models.js index e5a14340f..16aa5f5ae 100644 --- a/src/models.js +++ b/src/models.js @@ -7,14 +7,69 @@ import moment from 'moment'; import { t } from 'ttag'; +import { transactionUtils, constants } from '@hathor/wallet-lib' export class TxHistory { - constructor({ txId, timestamp, tokenUid, balance, isVoided }) { + /** + * @param {{ + * txId: string; + * timestamp: number; + * tokenUid: string; + * balance: number; + * voided: boolean; + * version: number; + * ncId?: string; + * ncMethod?: string; + * ncCaller?: Address; + * }} + */ + constructor({ + txId, + timestamp, + tokenUid, + balance, + isVoided, + version, + ncId, + ncMethod, + ncCaller + }) { + /** + * @type {string} + */ this.txId = txId; + /** + * @type {number} + */ this.timestamp = timestamp; + /** + * @type {string} + */ this.tokenUid = tokenUid; + /** + * @type {number} + */ this.balance = balance; + /** + * @type {boolean} + */ this.isVoided = isVoided; + /** + * @type {number} + */ + this.version = version; + /** + * @type {string?} + */ + this.ncId = ncId; + /** + * @type {string?} + */ + this.ncMethod = ncMethod; + /** + * @type {Address?} + */ + this.ncCaller = ncCaller; } getDescription(token) { @@ -46,4 +101,53 @@ export class TxHistory { sameElse: t`DD MMM YYYY [•] HH:mm`, }); } + + getVersionInfo() { + return transactionUtils.getTxType(this) + } + + isNanoContract() { + return this.version === constants.NANO_CONTRACTS_VERSION; + } + + /** + * Creates a TxHistory instance from raw transaction history data. + * + * @param {{ + * txId: string; + * balance: number; + * timestamp: number; + * voided: boolean; + * version: number; + * ncId?: string; + * ncMethod?: string; + * ncCaller?: Address; + * }} rawTxHistory - The raw transaction history data. + * @param {string} tokenUid - The UID of the token associated with the transaction. + * + * @returns {TxHistory} A TxHistory instance representing the transaction. + */ + static from(rawTxHistory, tokenUid) { + const { + txId, + timestamp, + balance, + version, + ncId, + ncMethod, + ncCaller, + } = rawTxHistory; + return new TxHistory({ + txId, + timestamp, + balance, + version, + ncId, + ncMethod, + ncCaller, + tokenUid, + // in wallet service this comes as 0/1 and in the full node comes with true/false + isVoided: Boolean(rawTxHistory.voided), + }); + } } diff --git a/src/reducers/reducer.js b/src/reducers/reducer.js index 40a5305f9..f3537535d 100644 --- a/src/reducers/reducer.js +++ b/src/reducers/reducer.js @@ -22,12 +22,6 @@ import { WALLET_STATUS } from '../sagas/wallet'; * locked: int * } * }]) - * tokensHistory {Object} stores the history for each token (Dict[tokenUid: str, { - * status: string, - * oldStatus: string, - * updatedAt: int, - * data: TxHistory[] - * }]) * loadHistoryStatus {Object} progress on loading tx history { * active {boolean} indicates we're loading the tx history * error {boolean} error loading history @@ -75,6 +69,16 @@ import { WALLET_STATUS } from '../sagas/wallet'; * lastSharedIndex {int} The current address index to use */ const initialState = { + /** + * @type {{ + * [tokenUid: string]: { + * status: TOKEN_DOWNLOAD_STATUS; + * oldStatus: string; + * updatedAt: number; + * data: TxHistory[]; + * } + * }} stores the history for each token () + */ tokensHistory: {}, tokensBalance: {}, loadHistoryStatus: { active: true, error: false }, @@ -820,8 +824,10 @@ export const onTokenFetchBalanceFailed = (state, action) => { }; /** + * @param {Object} state - redux state + * @param {Object} action - token's history * @param {String} action.tokenId - The tokenId to mark as success - * @param {Object} action.data - The token history information to store on redux + * @param {TxHistory} action.data - The token history information to store on redux */ export const onTokenFetchHistorySuccess = (state, action) => { const { tokenId, data } = action; diff --git a/src/sagas/nanoContract.js b/src/sagas/nanoContract.js index 228244b83..6e9fd3e27 100644 --- a/src/sagas/nanoContract.js +++ b/src/sagas/nanoContract.js @@ -1,3 +1,10 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + import { ncApi, transactionUtils, diff --git a/src/sagas/tokens.js b/src/sagas/tokens.js index addebcbc4..f2d3f43f2 100644 --- a/src/sagas/tokens.js +++ b/src/sagas/tokens.js @@ -19,7 +19,7 @@ import { metadataApi } from '@hathor/wallet-lib'; import { channel } from 'redux-saga'; import { get } from 'lodash'; import { specificTypeAndPayload, dispatchAndWait, getRegisteredTokenUids } from './helpers'; -import { mapTokenHistory } from '../utils'; +import { mapToTxHistory } from '../utils'; import { types, tokenFetchBalanceRequested, @@ -33,6 +33,10 @@ import { logger } from '../logger'; const log = logger('tokens-saga'); +/** + * @readonly + * @enum {string} + */ export const TOKEN_DOWNLOAD_STATUS = { READY: 'ready', FAILED: 'failed', @@ -133,7 +137,7 @@ function* fetchTokenHistory(action) { } const response = yield call(wallet.getTxHistory.bind(wallet), { token_id: tokenId }); - const data = response.map((txHistory) => mapTokenHistory(txHistory, tokenId)); + const data = response.map(mapToTxHistory(tokenId)); log.debug('Success fetching token history.'); yield put(tokenFetchHistorySuccess(tokenId, data)); @@ -248,8 +252,7 @@ export function* fetchTokenMetadata({ tokenId }) { type: types.TOKEN_FETCH_METADATA_FAILED, tokenId, }); - // eslint-disable-next-line - log.log('Error downloading metadata of token', tokenId); + log.log(`Error downloading metadata of token ${tokenId}`); } } diff --git a/src/screens/MainScreen.js b/src/screens/MainScreen.js index ac3f7f6a1..d0ff54cf0 100644 --- a/src/screens/MainScreen.js +++ b/src/screens/MainScreen.js @@ -8,7 +8,6 @@ import React from 'react'; import { ActivityIndicator, - FlatList, StyleSheet, Text, View, @@ -31,12 +30,13 @@ import { HathorList } from '../components/HathorList'; import { Strong, str2jsx, renderValue, isTokenNFT } from '../utils'; import chevronUp from '../assets/icons/chevron-up.png'; import chevronDown from '../assets/icons/chevron-down.png'; -import infoIcon from '../assets/icons/info-circle.png'; import { IS_MULTI_TOKEN } from '../constants'; import { fetchMoreHistory, updateTokenHistory } from '../actions'; import Spinner from '../components/Spinner'; import { TOKEN_DOWNLOAD_STATUS } from '../sagas/tokens'; import { COLORS } from '../styles/themes'; +import { HathorFlatList } from '../components/HathorFlatList'; +import { ActionDot } from '../components/Icons/ActionDot.icon'; /** * txList {Array} array with transactions of the selected token @@ -201,10 +201,9 @@ class MainScreen extends React.Component { const renderRightElement = () => { if (this.props.selectedToken.uid !== hathorConstants.HATHOR_TOKEN_CONFIG.uid) { return ( - + + + ); } @@ -213,7 +212,7 @@ class MainScreen extends React.Component { return ( {this.state.modal} @@ -228,9 +227,7 @@ class MainScreen extends React.Component { token={this.props.selectedToken} isNFT={this.isNFT()} /> - - {renderTxHistory()} - + {renderTxHistory()} ); @@ -287,16 +284,14 @@ class TxHistoryView extends React.Component { render() { return ( - - item.txId} - onEndReached={this.loadMoreHistory} - onEndReachedThreshold={0.2} - ListFooterComponent={this.renderFooter} - /> - + item.txId} + onEndReached={this.loadMoreHistory} + onEndReachedThreshold={0.2} + ListFooterComponent={this.renderFooter} + /> ); } } @@ -305,30 +300,11 @@ class TxListItem extends React.Component { state = { timestamp: null }; style = StyleSheet.create({ - container: { - marginLeft: 16, - marginRight: 16, - marginTop: 0, - borderColor: COLORS.borderColor, - borderBottomWidth: 1, - shadowOffset: { height: 2, width: 0 }, - shadowRadius: 4, - shadowColor: COLORS.textColor, - shadowOpacity: 0.08, - }, view: { flexDirection: 'row', - alignItems: 'center', - backgroundColor: COLORS.backgroundColor, - height: 80, - }, - firstItemBorder: { - borderTopLeftRadius: 16, - borderTopRightRadius: 16, - }, - lastItemBorder: { - borderBottomLeftRadius: 16, - borderBottomRightRadius: 16, + alignItems: 'flex-start', + minHeight: 80, + paddingVertical: 24, }, middleView: { flex: 1, @@ -351,11 +327,14 @@ class TxListItem extends React.Component { lineHeight: 20, fontWeight: 'bold', }, - timestamp: { + secondaryText: { fontSize: 12, lineHeight: 20, color: COLORS.textColorShadow, }, + bold: { + fontWeight: 'bold', + }, }); styleVoided = ({ ...this.style, @@ -365,7 +344,7 @@ class TxListItem extends React.Component { color: COLORS.textColorShadowLight, }, timestamp: { - ...this.style.timestamp, + ...this.style.secondaryText, color: COLORS.textColorShadowLight, }, balance: { @@ -454,37 +433,33 @@ class TxListItem extends React.Component { } render() { + /** + * @type {{ + * item: TxHistory; + * }} TxListItem properties + */ const { item } = this.props; const style = this.getStyle(item); const image = this.getImage(item); - const viewStyle = [style.view]; - const touchStyle = []; - if (this.props.isFirst) { - viewStyle.push(style.firstItemBorder); - touchStyle.push(style.firstItemBorder); - } - if (this.props.isLast) { - viewStyle.push(style.lastItemBorder); - touchStyle.push(style.lastItemBorder); - } - const balanceStr = renderValue(item.balance, this.props.isNFT); const description = item.getDescription(this.props.token); const { timestamp } = this.state; return ( - - this.onItemPress(item)}> - - {image} - - {description} - {timestamp} - - {balanceStr} + this.onItemPress(item)} + underlayColor={COLORS.primaryOpacity30} + > + + {image} + + {description} + {timestamp} + {item.getVersionInfo().label} - - + {balanceStr} + + ); } } diff --git a/src/styles/themes.js b/src/styles/themes.js index 2cb8bdfcb..e574f2278 100644 --- a/src/styles/themes.js +++ b/src/styles/themes.js @@ -13,6 +13,7 @@ import { HslColor } from '../HslColor'; * Light theme color scheme */ export const COLORS = { + black: '#000', /** * @type {string} The main background color */ diff --git a/src/utils.js b/src/utils.js index d595649c0..9ec88fcb6 100644 --- a/src/utils.js +++ b/src/utils.js @@ -31,6 +31,19 @@ export const getShortHash = (hash, length = 4) => ( `${hash.substring(0, length)}...${hash.substring(64 - length, 64)}` ); +/** + * It short any string content without length bound. + * @param {string} content Content to be sliced in two parts + * @param {string} length Size of the substrigs in both sides of `...` + * + * @example + * getShortContent('00c30fc8a1b9a326a766ab0351faf3635297d316fd039a0eda01734d9de40185', 3) + * // output: '00c...185' + */ +export const getShortContent = (content, length = 4) => ( + `${content.substring(0, length)}...${content.substring(content.length - length, content.length)}` +); + /** * Get amount text value and transform in its integer value * @@ -329,22 +342,16 @@ export const changePin = async (wallet, oldPin, newPin) => { }; /** - * Map history element to expected TxHistory model object + * Curry function that maps a raw history element to an expected TxHistory model object. + * + * @param {string} tokenUid - Token uid * - * element {Object} Tx history element with {txId, timestamp, balance, voided?} - * token {string} Token uid + * @returns {(rawTxHistory: Object) => TxHistory} A function that maps a raw + * transaction history element to a TxHistory object */ -export const mapTokenHistory = (element, token) => { - const data = { - txId: element.txId, - timestamp: element.timestamp, - balance: element.balance, - // in wallet service this comes as 0/1 and in the full node comes with true/false - isVoided: Boolean(element.voided), - tokenUid: token - }; - return new TxHistory(data); -}; +export const mapToTxHistory = (tokenUid) => (rawTxHistory) => ( + TxHistory.from(rawTxHistory, tokenUid) +); /** * Select the push notification settings from redux state From 674d65bde63ce55d9520206be836dbed3268369c Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Tue, 21 May 2024 17:33:51 +0100 Subject: [PATCH 11/51] bump: v0.27.0 to release (#487) --- 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 e01054374..762d930d3 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 69 - versionName "0.27.0-rc.6" + versionCode 70 + versionName "0.27.0" missingDimensionStrategy "react-native-camera", "general" } signingConfigs { diff --git a/ios/HathorMobile.xcodeproj/project.pbxproj b/ios/HathorMobile.xcodeproj/project.pbxproj index 1740df2e5..1c671c37d 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.6.0; + CURRENT_PROJECT_VERSION = 1.0.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.6.0; + CURRENT_PROJECT_VERSION = 1.0.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 1e72e4509..fa0db62fa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "HathorMobile", - "version": "0.27.0-rc.6", + "version": "0.27.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "HathorMobile", - "version": "0.27.0-rc.6", + "version": "0.27.0", "hasInstallScript": true, "dependencies": { "@fortawesome/fontawesome-svg-core": "1.2.36", diff --git a/package.json b/package.json index ba569c195..dcdfd631f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "HathorMobile", - "version": "0.27.0-rc.6", + "version": "0.27.0", "engines": { "node": ">=18.0.0", "npm": ">=9.0.0" From 594ca2c19f7408ff345db5693cfbfd2e469d3b1e Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Thu, 23 May 2024 14:29:51 +0100 Subject: [PATCH 12/51] bump: wallet-lib to v1.5.0 (#488) * bump: wallet-lib to v1.5.0 * chore: add package-lock.json --- package-lock.json | 150 ++++++++++++++++++++++++---------------------- package.json | 2 +- 2 files changed, 80 insertions(+), 72 deletions(-) diff --git a/package-lock.json b/package-lock.json index c78b02124..e933ecc8a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@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", + "@hathor/wallet-lib": "1.5.0", "@notifee/react-native": "5.7.0", "@react-native-async-storage/async-storage": "1.19.0", "@react-native-firebase/app": "16.7.0", @@ -2550,20 +2550,24 @@ } }, "node_modules/@hathor/wallet-lib": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@hathor/wallet-lib/-/wallet-lib-1.0.1.tgz", - "integrity": "sha512-Pfjq0oks3qxD2BJXNYv7X6jv0oIW0vdSBvpkLMVsKQ/VikXV2RHP+av2vQdnkC2lMdkvBeG/gtoREF21tQzing==", - "dependencies": { - "axios": "^0.21.4", - "bitcore-lib": "^8.25.10", - "bitcore-mnemonic": "^8.25.10", - "buffer": "^6.0.3", - "crypto-js": "^3.1.9-1", - "isomorphic-ws": "^4.0.1", - "level": "^8.0.0", - "lodash": "^4.17.21", - "long": "^4.0.0", - "ws": "^7.5.9" + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@hathor/wallet-lib/-/wallet-lib-1.5.0.tgz", + "integrity": "sha512-H0wOuzaS+iUhADGG8fxvNpLddkM0BFjCAjt1nLml5Oe6DkTj5VtA2a7+aT1DR4qxUcPRmCo9D+azbMDiwbXtIA==", + "dependencies": { + "axios": "1.6.8", + "bitcore-lib": "8.25.10", + "bitcore-mnemonic": "8.25.10", + "buffer": "6.0.3", + "crypto-js": "4.2.0", + "isomorphic-ws": "4.0.1", + "level": "8.0.0", + "lodash": "4.17.21", + "long": "4.0.0", + "ws": "7.5.9" + }, + "engines": { + "node": ">=20.0.0", + "npm": ">=10.0.0" } }, "node_modules/@hathor/wallet-lib/node_modules/buffer": { @@ -2590,9 +2594,9 @@ } }, "node_modules/@hathor/wallet-lib/node_modules/crypto-js": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.3.0.tgz", - "integrity": "sha512-DIT51nX0dCfKltpRiXV+/TVZq+Qq2NgF4644+K7Ttnla7zEzqc+kjJyiB96BHNyUTBxyjzRcZYpUdZa+QAqi6Q==" + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.11", @@ -7340,6 +7344,11 @@ "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "node_modules/available-typed-arrays": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", @@ -7361,11 +7370,13 @@ } }, "node_modules/axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", + "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", "dependencies": { - "follow-redirects": "^1.14.0" + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" } }, "node_modules/axobject-query": { @@ -7843,9 +7854,9 @@ ] }, "node_modules/bech32": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz", - "integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==" + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.3.tgz", + "integrity": "sha512-yuVFUvrNcoJi0sv5phmqc6P+Fl1HjRDRNOOkHY2X/3LBy2bIGNSFx4fZ95HMaXHupuS7cZR15AsvtmCIF4UEyg==" }, "node_modules/big-integer": { "version": "1.6.51", @@ -7855,33 +7866,12 @@ "node": ">=0.6" } }, - "node_modules/bigi": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/bigi/-/bigi-1.4.2.tgz", - "integrity": "sha512-ddkU+dFIuEIW8lE7ZwdIAf2UPoM90eaprg5m3YXAVVTmKlqV/9BX4A2M8BOK2yOq6/VgZFVhK6QAxJebhlbhzw==" - }, - "node_modules/bip-schnorr": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/bip-schnorr/-/bip-schnorr-0.6.4.tgz", - "integrity": "sha512-dNKw7Lea8B0wMIN4OjEmOk/Z5qUGqoPDY0P2QttLqGk1hmDPytLWW8PR5Pb6Vxy6CprcdEgfJpOjUu+ONQveyg==", - "dependencies": { - "bigi": "^1.4.2", - "ecurve": "^1.0.6", - "js-sha256": "^0.9.0", - "randombytes": "^2.1.0", - "safe-buffer": "^5.2.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/bitcore-lib": { - "version": "8.25.47", - "resolved": "https://registry.npmjs.org/bitcore-lib/-/bitcore-lib-8.25.47.tgz", - "integrity": "sha512-qDZr42HuP4P02I8kMGZUx/vvwuDsz8X3rQxXLfM0BtKzlQBcbSM7ycDkDN99Xc5jzpd4fxNQyyFXOmc6owUsrQ==", + "version": "8.25.10", + "resolved": "https://registry.npmjs.org/bitcore-lib/-/bitcore-lib-8.25.10.tgz", + "integrity": "sha512-MyHpSg7aFRHe359RA/gdkaQAal3NswYZTLEuu0tGX1RGWXAYN9i/24fsjPqVKj+z0ua+gzAT7aQs0KiKXWCgKA==", "dependencies": { - "bech32": "=2.0.0", - "bip-schnorr": "=0.6.4", + "bech32": "=1.1.3", "bn.js": "=4.11.8", "bs58": "^4.0.1", "buffer-compare": "=1.1.1", @@ -7891,11 +7881,11 @@ } }, "node_modules/bitcore-mnemonic": { - "version": "8.25.47", - "resolved": "https://registry.npmjs.org/bitcore-mnemonic/-/bitcore-mnemonic-8.25.47.tgz", - "integrity": "sha512-wTa0imZZpFTqwlpyokvU8CNl+YdaIvQIrWKp/0AEL9gPX2vuzBnE+U8Ok6D5lHCnbG6dvmoesmtyf6R3aYI86A==", + "version": "8.25.10", + "resolved": "https://registry.npmjs.org/bitcore-mnemonic/-/bitcore-mnemonic-8.25.10.tgz", + "integrity": "sha512-FeXxO37BLV5JRvxPmVFB91zRHalavV8H4TdQGt1/hz0AkoPymIV68OkuB+TptpjeYgatcgKPoPvPhglJkTzFQQ==", "dependencies": { - "bitcore-lib": "^8.25.47", + "bitcore-lib": "^8.25.10", "unorm": "^1.4.1" }, "peerDependencies": { @@ -8502,6 +8492,17 @@ "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/command-exists": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", @@ -9016,6 +9017,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", @@ -9180,15 +9189,6 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, - "node_modules/ecurve": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/ecurve/-/ecurve-1.0.6.tgz", - "integrity": "sha512-/BzEjNfiSuB7jIWKcS/z8FK9jNjmEWvUV2YZ4RLSmcDtP7Lq0m6FvDuSnJpBlDpGRpfRQeTLGLBI8H+kEv0r+w==", - "dependencies": { - "bigi": "^1.1.0", - "safe-buffer": "^5.0.1" - } - }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -10522,9 +10522,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", "funding": [ { "type": "individual", @@ -10573,6 +10573,19 @@ "integrity": "sha512-J+ler7Ta54FwwNcx6wQRDhTIbNeyDcARMkOcguEqnEdtm0jKvN3Li3PDAb2Du3ubJYEWfYL83XMROXdsXAXycw==", "dev": true }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/formidable": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz", @@ -14665,11 +14678,6 @@ "@sideway/pinpoint": "^2.0.0" } }, - "node_modules/js-sha256": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz", - "integrity": "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==" - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", diff --git a/package.json b/package.json index 47f518e47..6c3f14ba8 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "@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", + "@hathor/wallet-lib": "1.5.0", "@notifee/react-native": "5.7.0", "@react-native-async-storage/async-storage": "1.19.0", "@react-native-firebase/app": "16.7.0", From d391fe19cc789f82a04e1079b432d4240fc8b32a Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Thu, 23 May 2024 14:31:30 +0100 Subject: [PATCH 13/51] feat: add firstBlock to TxHistory model (#476) * feat: add firstBlock and TransactionStatusLabel * fix: status label and status badge --- locale/da/texts.po | 42 +++++++++++++++++--------------- locale/pt-br/texts.po | 42 +++++++++++++++++--------------- locale/ru-ru/texts.po | 42 +++++++++++++++++--------------- locale/texts.pot | 42 +++++++++++++++++--------------- src/components/TxDetailsModal.js | 16 +++++++++++- src/models.js | 16 +++++++++++- src/sagas/tokens.js | 2 +- src/screens/MainScreen.js | 2 +- 8 files changed, 124 insertions(+), 80 deletions(-) diff --git a/locale/da/texts.po b/locale/da/texts.po index 6c791c070..987506761 100644 --- a/locale/da/texts.po +++ b/locale/da/texts.po @@ -13,51 +13,51 @@ msgstr "" "X-Generator: Poedit 3.3.1\n" #. This should never happen! -#: src/models.js:79 +#: src/models.js:85 msgid "Unknown" msgstr "Ukendt" -#: src/models.js:82 +#: src/models.js:88 #, javascript-format msgid "Received ${ symbol }" msgstr "Modtaget ${ symbol }" -#: src/models.js:84 +#: src/models.js:90 msgid "Sent ${ symbol }" msgstr "Sendt ${ symbol }" -#: src/models.js:86 +#: src/models.js:92 #, javascript-format msgid "You sent ${ symbol } to yourself" msgstr "Du sendte ${ symbol } til dig selv" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:96 +#: src/models.js:102 msgid "[Today •] HH:mm" msgstr "[I dag •] HH:mm" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:97 +#: src/models.js:103 msgid "[Tomorrow •] HH:mm" msgstr "[I morgen •] HH:mm" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:98 +#: src/models.js:104 msgid "dddd [•] HH:mm" msgstr "dddd [•] HH:mm" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:99 +#: src/models.js:105 msgid "[Yesterday •] HH:mm" msgstr "[I går •] HH:mm" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:100 +#: src/models.js:106 msgid "[Last] dddd [•] HH:mm" msgstr "[Sidste] dddd [•] HH:mm" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:101 +#: src/models.js:107 msgid "DD MMM YYYY [•] HH:mm" msgstr "DD MMM YYYY [•] HH:mm" @@ -489,11 +489,11 @@ msgstr "Du har lige modtaget **${ amount } ${ symbol }**" msgid "PAYMENT REQUEST" msgstr "BETALINGSANMODNING" -#: src/components/TxDetailsModal.js:99 src/screens/PaymentRequestDetail.js:123 +#: src/components/TxDetailsModal.js:101 src/screens/PaymentRequestDetail.js:123 msgid "Token" msgstr "Token" -#: src/components/TxDetailsModal.js:152 src/screens/PaymentRequestDetail.js:127 +#: src/components/TxDetailsModal.js:166 src/screens/PaymentRequestDetail.js:127 msgid "Amount" msgstr "Antal" @@ -1090,7 +1090,7 @@ msgstr "Ingen internetforbindelse" msgid "Public Explorer" msgstr "Public Explorer" -#: src/components/PushTxDetailsModal.js:69 src/components/TxDetailsModal.js:101 +#: src/components/PushTxDetailsModal.js:69 src/components/TxDetailsModal.js:103 msgid "Date & Time" msgstr "Dato & Tid" @@ -1151,27 +1151,31 @@ msgstr "" msgid "Voided" msgstr "" -#: src/components/TxDetailsModal.js:100 +#: src/components/TxDetailsModal.js:102 msgid "Description" msgstr "Beskrivelse" -#: src/components/TxDetailsModal.js:102 +#: src/components/TxDetailsModal.js:104 msgid "Transaction ID" msgstr "" -#: src/components/TxDetailsModal.js:103 +#: src/components/TxDetailsModal.js:105 msgid "Blueprint Method" msgstr "" -#: src/components/TxDetailsModal.js:104 +#: src/components/TxDetailsModal.js:106 msgid "Nano Contract ID" msgstr "" -#: src/components/TxDetailsModal.js:105 +#: src/components/TxDetailsModal.js:107 msgid "Nano Contract Caller" msgstr "" -#: src/components/TxDetailsModal.js:106 +#: src/components/TxDetailsModal.js:111 +msgid "Nano Contract Status" +msgstr "" + +#: src/components/TxDetailsModal.js:120 msgid "Nano Contract" msgstr "" diff --git a/locale/pt-br/texts.po b/locale/pt-br/texts.po index ed908ed8e..9a44a469a 100644 --- a/locale/pt-br/texts.po +++ b/locale/pt-br/texts.po @@ -13,51 +13,51 @@ msgstr "" "X-Generator: Poedit 3.3.1\n" #. This should never happen! -#: src/models.js:79 +#: src/models.js:85 msgid "Unknown" msgstr "Desconhecido" -#: src/models.js:82 +#: src/models.js:88 #, javascript-format msgid "Received ${ symbol }" msgstr "Recebido ${ symbol }" -#: src/models.js:84 +#: src/models.js:90 msgid "Sent ${ symbol }" msgstr "Enviado ${ symbol }" -#: src/models.js:86 +#: src/models.js:92 #, javascript-format msgid "You sent ${ symbol } to yourself" msgstr "Você enviou ${ symbol } para você mesmo" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:96 +#: src/models.js:102 msgid "[Today •] HH:mm" msgstr "[Hoje •] HH:mm" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:97 +#: src/models.js:103 msgid "[Tomorrow •] HH:mm" msgstr "[Amanhã •] HH:mm" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:98 +#: src/models.js:104 msgid "dddd [•] HH:mm" msgstr "dddd [•] HH:mm" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:99 +#: src/models.js:105 msgid "[Yesterday •] HH:mm" msgstr "[Ontem •] HH:mm" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:100 +#: src/models.js:106 msgid "[Last] dddd [•] HH:mm" msgstr "[Última] dddd [•] HH:mm" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:101 +#: src/models.js:107 msgid "DD MMM YYYY [•] HH:mm" msgstr "DD MMM YYYY [•] HH:mm" @@ -502,11 +502,11 @@ msgstr "Você recebeu **${ amount } ${ symbol }**" msgid "PAYMENT REQUEST" msgstr "REQUISIÇÃO DE PAGAMENTO" -#: src/components/TxDetailsModal.js:99 src/screens/PaymentRequestDetail.js:123 +#: src/components/TxDetailsModal.js:101 src/screens/PaymentRequestDetail.js:123 msgid "Token" msgstr "Token" -#: src/components/TxDetailsModal.js:152 src/screens/PaymentRequestDetail.js:127 +#: src/components/TxDetailsModal.js:166 src/screens/PaymentRequestDetail.js:127 msgid "Amount" msgstr "Quantidade" @@ -1120,7 +1120,7 @@ msgstr "Sem conexão com a internet" msgid "Public Explorer" msgstr "Explorer Público" -#: src/components/PushTxDetailsModal.js:69 src/components/TxDetailsModal.js:101 +#: src/components/PushTxDetailsModal.js:69 src/components/TxDetailsModal.js:103 msgid "Date & Time" msgstr "Data & Hora" @@ -1180,27 +1180,31 @@ msgstr "Processando" msgid "Voided" msgstr "Inválida" -#: src/components/TxDetailsModal.js:100 +#: src/components/TxDetailsModal.js:102 msgid "Description" msgstr "Descrição" -#: src/components/TxDetailsModal.js:102 +#: src/components/TxDetailsModal.js:104 msgid "Transaction ID" msgstr "ID da Transação" -#: src/components/TxDetailsModal.js:103 +#: src/components/TxDetailsModal.js:105 msgid "Blueprint Method" msgstr "Método do Blueprint" -#: src/components/TxDetailsModal.js:104 +#: src/components/TxDetailsModal.js:106 msgid "Nano Contract ID" msgstr "ID do Nano Contract" -#: src/components/TxDetailsModal.js:105 +#: src/components/TxDetailsModal.js:107 msgid "Nano Contract Caller" msgstr "Endereço de assinatura do Nano Contract" -#: src/components/TxDetailsModal.js:106 +#: src/components/TxDetailsModal.js:111 +msgid "Nano Contract Status" +msgstr "Status do Nano Contract" + +#: src/components/TxDetailsModal.js:120 msgid "Nano Contract" msgstr "Nano Contract" diff --git a/locale/ru-ru/texts.po b/locale/ru-ru/texts.po index 307436867..79aa9eeca 100644 --- a/locale/ru-ru/texts.po +++ b/locale/ru-ru/texts.po @@ -14,51 +14,51 @@ msgstr "" "X-Generator: Poedit 3.3.1\n" #. This should never happen! -#: src/models.js:79 +#: src/models.js:85 msgid "Unknown" msgstr "Неизвестно" -#: src/models.js:82 +#: src/models.js:88 #, javascript-format msgid "Received ${ symbol }" msgstr "Получено ${ symbol }" -#: src/models.js:84 +#: src/models.js:90 msgid "Sent ${ symbol }" msgstr "Отправлено ${ symbol }" -#: src/models.js:86 +#: src/models.js:92 #, javascript-format msgid "You sent ${ symbol } to yourself" msgstr "Вы отправили себе ${ symbol }" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:96 +#: src/models.js:102 msgid "[Today •] HH:mm" msgstr "[Сегодня •] HH:mm" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:97 +#: src/models.js:103 msgid "[Tomorrow •] HH:mm" msgstr "[Завтра •] HH:mm" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:98 +#: src/models.js:104 msgid "dddd [•] HH:mm" msgstr "dddd [•] HH:mm" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:99 +#: src/models.js:105 msgid "[Yesterday •] HH:mm" msgstr "[Вчера •] HH:mm" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:100 +#: src/models.js:106 msgid "[Last] dddd [•] HH:mm" msgstr "[Последний] dddd [•] HH:mm" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:101 +#: src/models.js:107 msgid "DD MMM YYYY [•] HH:mm" msgstr "DD MMM YYYY [•] HH:mm" @@ -491,11 +491,11 @@ msgstr "Вы только что получили **${ amount } ${ symbol }**" msgid "PAYMENT REQUEST" msgstr "ЗАПРОС СРЕДСТВ" -#: src/components/TxDetailsModal.js:99 src/screens/PaymentRequestDetail.js:123 +#: src/components/TxDetailsModal.js:101 src/screens/PaymentRequestDetail.js:123 msgid "Token" msgstr "Токен" -#: src/components/TxDetailsModal.js:152 src/screens/PaymentRequestDetail.js:127 +#: src/components/TxDetailsModal.js:166 src/screens/PaymentRequestDetail.js:127 msgid "Amount" msgstr "Количество" @@ -1080,7 +1080,7 @@ msgstr "Нет соединения с интернетом" msgid "Public Explorer" msgstr "Public Explorer" -#: src/components/PushTxDetailsModal.js:69 src/components/TxDetailsModal.js:101 +#: src/components/PushTxDetailsModal.js:69 src/components/TxDetailsModal.js:103 msgid "Date & Time" msgstr "Дата и Время" @@ -1140,27 +1140,31 @@ msgstr "" msgid "Voided" msgstr "" -#: src/components/TxDetailsModal.js:100 +#: src/components/TxDetailsModal.js:102 msgid "Description" msgstr "Описание" -#: src/components/TxDetailsModal.js:102 +#: src/components/TxDetailsModal.js:104 msgid "Transaction ID" msgstr "" -#: src/components/TxDetailsModal.js:103 +#: src/components/TxDetailsModal.js:105 msgid "Blueprint Method" msgstr "" -#: src/components/TxDetailsModal.js:104 +#: src/components/TxDetailsModal.js:106 msgid "Nano Contract ID" msgstr "" -#: src/components/TxDetailsModal.js:105 +#: src/components/TxDetailsModal.js:107 msgid "Nano Contract Caller" msgstr "" -#: src/components/TxDetailsModal.js:106 +#: src/components/TxDetailsModal.js:111 +msgid "Nano Contract Status" +msgstr "" + +#: src/components/TxDetailsModal.js:120 msgid "Nano Contract" msgstr "" diff --git a/locale/texts.pot b/locale/texts.pot index de3cb6fdf..7234fe558 100644 --- a/locale/texts.pot +++ b/locale/texts.pot @@ -3,51 +3,51 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Plural-Forms: nplurals=2; plural=(n!=1);\n" -#: src/models.js:79 +#: src/models.js:85 #. This should never happen! msgid "Unknown" msgstr "" -#: src/models.js:82 +#: src/models.js:88 #, javascript-format msgid "Received ${ symbol }" msgstr "" -#: src/models.js:84 +#: src/models.js:90 msgid "Sent ${ symbol }" msgstr "" -#: src/models.js:86 +#: src/models.js:92 #, javascript-format msgid "You sent ${ symbol } to yourself" msgstr "" -#: src/models.js:96 +#: src/models.js:102 #. See https://momentjs.com/docs/#/displaying/calendar-time/ msgid "[Today •] HH:mm" msgstr "" -#: src/models.js:97 +#: src/models.js:103 #. See https://momentjs.com/docs/#/displaying/calendar-time/ msgid "[Tomorrow •] HH:mm" msgstr "" -#: src/models.js:98 +#: src/models.js:104 #. See https://momentjs.com/docs/#/displaying/calendar-time/ msgid "dddd [•] HH:mm" msgstr "" -#: src/models.js:99 +#: src/models.js:105 #. See https://momentjs.com/docs/#/displaying/calendar-time/ msgid "[Yesterday •] HH:mm" msgstr "" -#: src/models.js:100 +#: src/models.js:106 #. See https://momentjs.com/docs/#/displaying/calendar-time/ msgid "[Last] dddd [•] HH:mm" msgstr "" -#: src/models.js:101 +#: src/models.js:107 #. See https://momentjs.com/docs/#/displaying/calendar-time/ msgid "DD MMM YYYY [•] HH:mm" msgstr "" @@ -479,12 +479,12 @@ msgstr "" msgid "PAYMENT REQUEST" msgstr "" -#: src/components/TxDetailsModal.js:99 +#: src/components/TxDetailsModal.js:101 #: src/screens/PaymentRequestDetail.js:123 msgid "Token" msgstr "" -#: src/components/TxDetailsModal.js:152 +#: src/components/TxDetailsModal.js:166 #: src/screens/PaymentRequestDetail.js:127 msgid "Amount" msgstr "" @@ -1072,7 +1072,7 @@ msgid "Public Explorer" msgstr "" #: src/components/PushTxDetailsModal.js:69 -#: src/components/TxDetailsModal.js:101 +#: src/components/TxDetailsModal.js:103 msgid "Date & Time" msgstr "" @@ -1131,27 +1131,31 @@ msgstr "" msgid "Voided" msgstr "" -#: src/components/TxDetailsModal.js:100 +#: src/components/TxDetailsModal.js:102 msgid "Description" msgstr "" -#: src/components/TxDetailsModal.js:102 +#: src/components/TxDetailsModal.js:104 msgid "Transaction ID" msgstr "" -#: src/components/TxDetailsModal.js:103 +#: src/components/TxDetailsModal.js:105 msgid "Blueprint Method" msgstr "" -#: src/components/TxDetailsModal.js:104 +#: src/components/TxDetailsModal.js:106 msgid "Nano Contract ID" msgstr "" -#: src/components/TxDetailsModal.js:105 +#: src/components/TxDetailsModal.js:107 msgid "Nano Contract Caller" msgstr "" -#: src/components/TxDetailsModal.js:106 +#: src/components/TxDetailsModal.js:111 +msgid "Nano Contract Status" +msgstr "" + +#: src/components/TxDetailsModal.js:120 msgid "Nano Contract" msgstr "" diff --git a/src/components/TxDetailsModal.js b/src/components/TxDetailsModal.js index ed67c7865..4e2a3aac3 100644 --- a/src/components/TxDetailsModal.js +++ b/src/components/TxDetailsModal.js @@ -16,6 +16,7 @@ import SlideIndicatorBar from './SlideIndicatorBar'; import CopyClipboard from './CopyClipboard'; import { PublicExplorerListButton } from './PublicExplorerListButton'; import { COLORS } from '../styles/themes'; +import { TransactionStatusLabel } from './TransactionStatusLabel'; class TxDetailsModal extends Component { style = StyleSheet.create({ @@ -55,7 +56,7 @@ class TxDetailsModal extends Component { * }} TxDetailsModal properties */ const { token, tx, isNFT } = this.props; - const { txId, ncId, ncMethod, ncCaller } = tx; + const { txId, ncId, ncMethod, ncCaller, isVoided } = tx; const ncCallerAddr = ncCaller && ncCaller.base58; const fullTokenStr = getTokenLabel(token); @@ -77,6 +78,7 @@ class TxDetailsModal extends Component { copyText: ncCallerAddr }); const isNc = tx.isNanoContract(); + const hasFirstBlock = tx.hasFirstBlock(); return ( } {isNc && } {isNc && } + {isNc + && ( + + )} + /> + )} {isNc && } diff --git a/src/models.js b/src/models.js index 16aa5f5ae..7425c3199 100644 --- a/src/models.js +++ b/src/models.js @@ -21,6 +21,7 @@ export class TxHistory { * ncId?: string; * ncMethod?: string; * ncCaller?: Address; + * firstBlock?: string; * }} */ constructor({ @@ -32,7 +33,8 @@ export class TxHistory { version, ncId, ncMethod, - ncCaller + ncCaller, + firstBlock, }) { /** * @type {string} @@ -70,6 +72,10 @@ export class TxHistory { * @type {Address?} */ this.ncCaller = ncCaller; + /** + * @type {string?} + */ + this.firstBlock = firstBlock; } getDescription(token) { @@ -110,6 +116,11 @@ export class TxHistory { return this.version === constants.NANO_CONTRACTS_VERSION; } + hasFirstBlock() { + // not null, not undefined and not string empty + return this.firstBlock != null && this.firstBlock.trim() !== ''; + } + /** * Creates a TxHistory instance from raw transaction history data. * @@ -122,6 +133,7 @@ export class TxHistory { * ncId?: string; * ncMethod?: string; * ncCaller?: Address; + * firstBlock?: string; * }} rawTxHistory - The raw transaction history data. * @param {string} tokenUid - The UID of the token associated with the transaction. * @@ -136,6 +148,7 @@ export class TxHistory { ncId, ncMethod, ncCaller, + firstBlock, } = rawTxHistory; return new TxHistory({ txId, @@ -148,6 +161,7 @@ export class TxHistory { tokenUid, // in wallet service this comes as 0/1 and in the full node comes with true/false isVoided: Boolean(rawTxHistory.voided), + firstBlock, }); } } diff --git a/src/sagas/tokens.js b/src/sagas/tokens.js index f2d3f43f2..7881da664 100644 --- a/src/sagas/tokens.js +++ b/src/sagas/tokens.js @@ -136,7 +136,7 @@ function* fetchTokenHistory(action) { return; } - const response = yield call(wallet.getTxHistory.bind(wallet), { token_id: tokenId }); + const response = yield call([wallet, wallet.getTxHistory], { token_id: tokenId }); const data = response.map(mapToTxHistory(tokenId)); log.debug('Success fetching token history.'); diff --git a/src/screens/MainScreen.js b/src/screens/MainScreen.js index d0ff54cf0..b3ab69ae1 100644 --- a/src/screens/MainScreen.js +++ b/src/screens/MainScreen.js @@ -455,7 +455,7 @@ class TxListItem extends React.Component { {description} {timestamp} - {item.getVersionInfo().label} + {item.getVersionInfo()} {balanceStr} From 0dbdd376f2af7e3124fad20d64b779db50a23a77 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Fri, 24 May 2024 14:34:53 +0100 Subject: [PATCH 14/51] feat: nano contract list component [7] (#435) * feat: add nano contract list component * feat: add nano contract feature toggle to dashboard * refactor: TwoOptionsToggle component to simplify its mechanics --- locale/da/texts.po | 56 +++-- locale/pt-br/texts.po | 61 +++-- locale/ru-ru/texts.po | 56 +++-- locale/texts.pot | 58 +++-- src/components/AskForPushNotification.js | 7 + .../AskForPushNotificationRefresh.js | 7 + src/components/HathorHeader.js | 143 +++++++---- .../NanoContract/NanoContractIcon.icon.js | 29 +++ .../NanoContract/NanoContractsList.js | 111 +++++++++ .../NanoContract/NanoContractsListItem.js | 106 ++++++++ .../NanoContract/NoNanoContracts.js | 62 +++++ .../RegisterNewNanoContractButton.js | 26 ++ src/components/PushTxDetailsModal.js | 7 + .../ShowPushNotificationTxDetails.js | 7 + src/components/SimpleButton.js | 26 +- src/components/TwoOptionsToggle.js | 86 +++++++ src/components/WalletConnect/ModalButton.js | 7 + src/models.js | 2 +- src/screens/Dashboard.js | 233 +++++++++++++----- src/styles/themes.js | 4 + src/utils.js | 13 +- 21 files changed, 913 insertions(+), 194 deletions(-) create mode 100644 src/components/NanoContract/NanoContractIcon.icon.js create mode 100644 src/components/NanoContract/NanoContractsList.js create mode 100644 src/components/NanoContract/NanoContractsListItem.js create mode 100644 src/components/NanoContract/NoNanoContracts.js create mode 100644 src/components/NanoContract/RegisterNewNanoContractButton.js create mode 100644 src/components/TwoOptionsToggle.js diff --git a/locale/da/texts.po b/locale/da/texts.po index 987506761..d5cb9eac0 100644 --- a/locale/da/texts.po +++ b/locale/da/texts.po @@ -335,14 +335,21 @@ msgstr "Symbolet er en kortere version af token-navnet" msgid "E.g. HTR" msgstr "F.eks. HTR" -#: src/screens/Dashboard.js:67 src/screens/RegisterTokenManual.js:147 +#. Only show the toggle button when Nano Contract is enabled to the wallet +#: src/screens/Dashboard.js:130 src/screens/Dashboard.js:187 +msgid "Tokens" +msgstr "Tokens" + +#. Only show the toggle button when Nano Contract is enabled to the wallet +#: src/components/NanoContract/NanoContractsList.js:71 +#: src/screens/Dashboard.js:131 +msgid "Nano Contracts" +msgstr "" + +#: src/screens/Dashboard.js:178 src/screens/RegisterTokenManual.js:147 msgid "Register token" msgstr "Registrer token" -#: src/screens/Dashboard.js:74 -msgid "TOKENS" -msgstr "TOKENS" - #: src/screens/InitWallet.js:61 msgid "Welcome to Hathor Wallet!" msgstr "Velkommen til Hathor Wallet!" @@ -987,29 +994,29 @@ msgstr "Transaktion" msgid "Open" msgstr "Åben" -#: src/components/AskForPushNotification.js:22 +#: src/components/AskForPushNotification.js:29 msgid "Do you want to enable push notifications for this wallet?" msgstr "" -#: src/components/AskForPushNotification.js:23 +#: src/components/AskForPushNotification.js:30 msgid "You can always change this later in the settings menu" msgstr "" -#: src/components/AskForPushNotification.js:24 +#: src/components/AskForPushNotification.js:31 msgid "Yes, enable" msgstr "" -#: src/components/AskForPushNotificationRefresh.js:28 +#: src/components/AskForPushNotificationRefresh.js:35 msgid "Refresh your push notification registration" msgstr "" -#: src/components/AskForPushNotificationRefresh.js:29 +#: src/components/AskForPushNotificationRefresh.js:36 msgid "" "In order to keep receiving push notifications, you need to refresh your " "registration. Do you want to do it now?" msgstr "" -#: src/components/AskForPushNotificationRefresh.js:30 +#: src/components/AskForPushNotificationRefresh.js:37 msgid "Refresh" msgstr "" @@ -1090,15 +1097,15 @@ msgstr "Ingen internetforbindelse" msgid "Public Explorer" msgstr "Public Explorer" -#: src/components/PushTxDetailsModal.js:69 src/components/TxDetailsModal.js:103 +#: src/components/PushTxDetailsModal.js:76 src/components/TxDetailsModal.js:101 msgid "Date & Time" msgstr "Dato & Tid" -#: src/components/PushTxDetailsModal.js:70 +#: src/components/PushTxDetailsModal.js:77 msgid "ID" msgstr "ID" -#: src/components/PushTxDetailsModal.js:94 +#: src/components/PushTxDetailsModal.js:101 msgid "New Transaction" msgstr "" @@ -1119,16 +1126,16 @@ msgstr "Del" msgid "Propagating transaction to the network." msgstr "" -#: src/components/ShowPushNotificationTxDetails.js:8 +#: src/components/ShowPushNotificationTxDetails.js:15 msgid "Transation not found" msgstr "" -#: src/components/ShowPushNotificationTxDetails.js:9 +#: src/components/ShowPushNotificationTxDetails.js:16 msgid "" "The transaction has not arrived yet in your wallet. Do you want to retry?" msgstr "" -#: src/components/ShowPushNotificationTxDetails.js:10 +#: src/components/ShowPushNotificationTxDetails.js:17 msgid "Retry" msgstr "" @@ -1212,5 +1219,16 @@ msgstr "" msgid "Custom network" msgstr "" -#~ msgid "I understand the risks of using a mobile wallet" -#~ msgstr "Jeg forstår risikoen ved at bruge en mobil wallet" +#: src/components/NanoContract/NoNanoContracts.js:16 +msgid "No Nano Contracts" +msgstr "" + +#: src/components/NanoContract/NoNanoContracts.js:18 +msgid "" +"You can keep track of your registered Nano Contracts here once you have " +"registered them." +msgstr "" + +#: src/components/NanoContract/RegisterNewNanoContractButton.js:22 +msgid "Register new" +msgstr "" diff --git a/locale/pt-br/texts.po b/locale/pt-br/texts.po index 9a44a469a..dae12e7e5 100644 --- a/locale/pt-br/texts.po +++ b/locale/pt-br/texts.po @@ -344,14 +344,21 @@ msgstr "O símbolo é a versão reduzida do nome do seu token" msgid "E.g. HTR" msgstr "Por exemplo, HTR" -#: src/screens/Dashboard.js:67 src/screens/RegisterTokenManual.js:147 +#. Only show the toggle button when Nano Contract is enabled to the wallet +#: src/screens/Dashboard.js:130 src/screens/Dashboard.js:187 +msgid "Tokens" +msgstr "Tokens" + +#. Only show the toggle button when Nano Contract is enabled to the wallet +#: src/components/NanoContract/NanoContractsList.js:71 +#: src/screens/Dashboard.js:131 +msgid "Nano Contracts" +msgstr "Nano Contracts" + +#: src/screens/Dashboard.js:178 src/screens/RegisterTokenManual.js:147 msgid "Register token" msgstr "Registrar um token" -#: src/screens/Dashboard.js:74 -msgid "TOKENS" -msgstr "TOKENS" - #: src/screens/InitWallet.js:61 msgid "Welcome to Hathor Wallet!" msgstr "Bem vindo à Hathor Wallet!" @@ -1014,23 +1021,23 @@ msgstr "Transação" msgid "Open" msgstr "Abrir" -#: src/components/AskForPushNotification.js:22 +#: src/components/AskForPushNotification.js:29 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:23 +#: src/components/AskForPushNotification.js:30 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:24 +#: src/components/AskForPushNotification.js:31 msgid "Yes, enable" msgstr "Sim, habilitar" -#: src/components/AskForPushNotificationRefresh.js:28 +#: src/components/AskForPushNotificationRefresh.js:35 msgid "Refresh your push notification registration" msgstr "Atualize sua configuração de notificação" -#: src/components/AskForPushNotificationRefresh.js:29 +#: src/components/AskForPushNotificationRefresh.js:36 msgid "" "In order to keep receiving push notifications, you need to refresh your " "registration. Do you want to do it now?" @@ -1038,7 +1045,7 @@ msgstr "" "Para continuar recebendo notificações, é necessário atualizar sua " "configuração de notificação. Deseja atualizar agora?" -#: src/components/AskForPushNotificationRefresh.js:30 +#: src/components/AskForPushNotificationRefresh.js:37 msgid "Refresh" msgstr "Atualizar" @@ -1120,15 +1127,15 @@ msgstr "Sem conexão com a internet" msgid "Public Explorer" msgstr "Explorer Público" -#: src/components/PushTxDetailsModal.js:69 src/components/TxDetailsModal.js:103 +#: src/components/PushTxDetailsModal.js:76 src/components/TxDetailsModal.js:101 msgid "Date & Time" msgstr "Data & Hora" -#: src/components/PushTxDetailsModal.js:70 +#: src/components/PushTxDetailsModal.js:77 msgid "ID" msgstr "ID" -#: src/components/PushTxDetailsModal.js:94 +#: src/components/PushTxDetailsModal.js:101 msgid "New Transaction" msgstr "Transação" @@ -1149,16 +1156,16 @@ msgstr "Compartilhar" msgid "Propagating transaction to the network." msgstr "Propagando a transação para a rede." -#: src/components/ShowPushNotificationTxDetails.js:8 +#: src/components/ShowPushNotificationTxDetails.js:15 msgid "Transation not found" msgstr "Transação não encontrada" -#: src/components/ShowPushNotificationTxDetails.js:9 +#: src/components/ShowPushNotificationTxDetails.js:16 msgid "" "The transaction has not arrived yet in your wallet. Do you want to retry?" msgstr "A transação ainda não chegou na sua carteira. Deseja tentar novamente?" -#: src/components/ShowPushNotificationTxDetails.js:10 +#: src/components/ShowPushNotificationTxDetails.js:17 msgid "Retry" msgstr "Tentar novamente" @@ -1248,14 +1255,16 @@ msgstr "" msgid "Custom network" msgstr "Rede personalizada" -#~ msgid "There has been an error upgrading your wallet." -#~ msgstr "Ocorreu um erro ao atualizar sua wallet." - -#~ msgid "Please reset it and restore it with your seed." -#~ msgstr "Por favor resete sua wallet e restaure utilizando sua seed." +#: src/components/NanoContract/NoNanoContracts.js:16 +msgid "No Nano Contracts" +msgstr "Nenhum Nano Contract" -#~ msgid "Upgrading your wallet, please hang on." -#~ msgstr "Atualizando sua wallet, por favor aguarde." +#: src/components/NanoContract/NoNanoContracts.js:18 +msgid "" +"You can keep track of your registered Nano Contracts here once you have " +"registered them." +msgstr "Você pode acompanhar os Nano Contracts registrados nesta tela." -#~ msgid "I understand the risks of using a mobile wallet" -#~ msgstr "Eu entendo os riscos de usar uma wallet de celular" +#: src/components/NanoContract/RegisterNewNanoContractButton.js:22 +msgid "Register new" +msgstr "Registrar Nano Contract" diff --git a/locale/ru-ru/texts.po b/locale/ru-ru/texts.po index 79aa9eeca..d06c178fe 100644 --- a/locale/ru-ru/texts.po +++ b/locale/ru-ru/texts.po @@ -336,14 +336,21 @@ msgstr "Символ является сокращенной версией им msgid "E.g. HTR" msgstr "Например, HTR" -#: src/screens/Dashboard.js:67 src/screens/RegisterTokenManual.js:147 +#. Only show the toggle button when Nano Contract is enabled to the wallet +#: src/screens/Dashboard.js:130 src/screens/Dashboard.js:187 +msgid "Tokens" +msgstr "ТокенЫ" + +#. Only show the toggle button when Nano Contract is enabled to the wallet +#: src/components/NanoContract/NanoContractsList.js:71 +#: src/screens/Dashboard.js:131 +msgid "Nano Contracts" +msgstr "" + +#: src/screens/Dashboard.js:178 src/screens/RegisterTokenManual.js:147 msgid "Register token" msgstr "Зарегистрировать токен" -#: src/screens/Dashboard.js:74 -msgid "TOKENS" -msgstr "ТОКЕНЫ" - #: src/screens/InitWallet.js:61 msgid "Welcome to Hathor Wallet!" msgstr "Добро пожаловать в Hathor Wallet!" @@ -991,29 +998,29 @@ msgstr "" msgid "Open" msgstr "Открыть" -#: src/components/AskForPushNotification.js:22 +#: src/components/AskForPushNotification.js:29 msgid "Do you want to enable push notifications for this wallet?" msgstr "" -#: src/components/AskForPushNotification.js:23 +#: src/components/AskForPushNotification.js:30 msgid "You can always change this later in the settings menu" msgstr "" -#: src/components/AskForPushNotification.js:24 +#: src/components/AskForPushNotification.js:31 msgid "Yes, enable" msgstr "" -#: src/components/AskForPushNotificationRefresh.js:28 +#: src/components/AskForPushNotificationRefresh.js:35 msgid "Refresh your push notification registration" msgstr "" -#: src/components/AskForPushNotificationRefresh.js:29 +#: src/components/AskForPushNotificationRefresh.js:36 msgid "" "In order to keep receiving push notifications, you need to refresh your " "registration. Do you want to do it now?" msgstr "" -#: src/components/AskForPushNotificationRefresh.js:30 +#: src/components/AskForPushNotificationRefresh.js:37 msgid "Refresh" msgstr "" @@ -1080,15 +1087,15 @@ msgstr "Нет соединения с интернетом" msgid "Public Explorer" msgstr "Public Explorer" -#: src/components/PushTxDetailsModal.js:69 src/components/TxDetailsModal.js:103 +#: src/components/PushTxDetailsModal.js:76 src/components/TxDetailsModal.js:101 msgid "Date & Time" msgstr "Дата и Время" -#: src/components/PushTxDetailsModal.js:70 +#: src/components/PushTxDetailsModal.js:77 msgid "ID" msgstr "ID" -#: src/components/PushTxDetailsModal.js:94 +#: src/components/PushTxDetailsModal.js:101 msgid "New Transaction" msgstr "" @@ -1109,16 +1116,16 @@ msgstr "Поделиться" msgid "Propagating transaction to the network." msgstr "" -#: src/components/ShowPushNotificationTxDetails.js:8 +#: src/components/ShowPushNotificationTxDetails.js:15 msgid "Transation not found" msgstr "" -#: src/components/ShowPushNotificationTxDetails.js:9 +#: src/components/ShowPushNotificationTxDetails.js:16 msgid "" "The transaction has not arrived yet in your wallet. Do you want to retry?" msgstr "" -#: src/components/ShowPushNotificationTxDetails.js:10 +#: src/components/ShowPushNotificationTxDetails.js:17 msgid "Retry" msgstr "" @@ -1201,5 +1208,16 @@ msgstr "" msgid "Custom network" msgstr "" -#~ msgid "I understand the risks of using a mobile wallet" -#~ msgstr "Я осознаю риски использования мобильного кошелька" +#: src/components/NanoContract/NoNanoContracts.js:16 +msgid "No Nano Contracts" +msgstr "" + +#: src/components/NanoContract/NoNanoContracts.js:18 +msgid "" +"You can keep track of your registered Nano Contracts here once you have " +"registered them." +msgstr "" + +#: src/components/NanoContract/RegisterNewNanoContractButton.js:22 +msgid "Register new" +msgstr "" diff --git a/locale/texts.pot b/locale/texts.pot index 7234fe558..ffc40232d 100644 --- a/locale/texts.pot +++ b/locale/texts.pot @@ -328,13 +328,21 @@ msgstr "" msgid "E.g. HTR" msgstr "" -#: src/screens/Dashboard.js:67 -#: src/screens/RegisterTokenManual.js:147 -msgid "Register token" +#: src/screens/Dashboard.js:130 +#: src/screens/Dashboard.js:187 +#. Only show the toggle button when Nano Contract is enabled to the wallet +msgid "Tokens" msgstr "" -#: src/screens/Dashboard.js:74 -msgid "TOKENS" +#: src/components/NanoContract/NanoContractsList.js:71 +#: src/screens/Dashboard.js:131 +#. Only show the toggle button when Nano Contract is enabled to the wallet +msgid "Nano Contracts" +msgstr "" + +#: src/screens/Dashboard.js:178 +#: src/screens/RegisterTokenManual.js:147 +msgid "Register token" msgstr "" #: src/screens/InitWallet.js:61 @@ -982,29 +990,29 @@ msgstr "" msgid "Open" msgstr "" -#: src/components/AskForPushNotification.js:22 +#: src/components/AskForPushNotification.js:29 msgid "Do you want to enable push notifications for this wallet?" msgstr "" -#: src/components/AskForPushNotification.js:23 +#: src/components/AskForPushNotification.js:30 msgid "You can always change this later in the settings menu" msgstr "" -#: src/components/AskForPushNotification.js:24 +#: src/components/AskForPushNotification.js:31 msgid "Yes, enable" msgstr "" -#: src/components/AskForPushNotificationRefresh.js:28 +#: src/components/AskForPushNotificationRefresh.js:35 msgid "Refresh your push notification registration" msgstr "" -#: src/components/AskForPushNotificationRefresh.js:29 +#: src/components/AskForPushNotificationRefresh.js:36 msgid "" "In order to keep receiving push notifications, you need to refresh your " "registration. Do you want to do it now?" msgstr "" -#: src/components/AskForPushNotificationRefresh.js:30 +#: src/components/AskForPushNotificationRefresh.js:37 msgid "Refresh" msgstr "" @@ -1071,16 +1079,16 @@ msgstr "" msgid "Public Explorer" msgstr "" -#: src/components/PushTxDetailsModal.js:69 -#: src/components/TxDetailsModal.js:103 +#: src/components/PushTxDetailsModal.js:76 +#: src/components/TxDetailsModal.js:101 msgid "Date & Time" msgstr "" -#: src/components/PushTxDetailsModal.js:70 +#: src/components/PushTxDetailsModal.js:77 msgid "ID" msgstr "" -#: src/components/PushTxDetailsModal.js:94 +#: src/components/PushTxDetailsModal.js:101 msgid "New Transaction" msgstr "" @@ -1102,15 +1110,15 @@ msgstr "" msgid "Propagating transaction to the network." msgstr "" -#: src/components/ShowPushNotificationTxDetails.js:8 +#: src/components/ShowPushNotificationTxDetails.js:15 msgid "Transation not found" msgstr "" -#: src/components/ShowPushNotificationTxDetails.js:9 +#: src/components/ShowPushNotificationTxDetails.js:16 msgid "The transaction has not arrived yet in your wallet. Do you want to retry?" msgstr "" -#: src/components/ShowPushNotificationTxDetails.js:10 +#: src/components/ShowPushNotificationTxDetails.js:17 msgid "Retry" msgstr "" @@ -1191,3 +1199,17 @@ msgstr "" #: src/components/NetworkSettings/NetworkStatusBar.js:14 msgid "Custom network" msgstr "" + +#: src/components/NanoContract/NoNanoContracts.js:16 +msgid "No Nano Contracts" +msgstr "" + +#: src/components/NanoContract/NoNanoContracts.js:18 +msgid "" +"You can keep track of your registered Nano Contracts here once you have " +"registered them." +msgstr "" + +#: src/components/NanoContract/RegisterNewNanoContractButton.js:22 +msgid "Register new" +msgstr "" diff --git a/src/components/AskForPushNotification.js b/src/components/AskForPushNotification.js index f1a468d89..b091c30a9 100644 --- a/src/components/AskForPushNotification.js +++ b/src/components/AskForPushNotification.js @@ -1,3 +1,10 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + import React from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { t } from 'ttag'; diff --git a/src/components/AskForPushNotificationRefresh.js b/src/components/AskForPushNotificationRefresh.js index a613ccf88..e8cbf0173 100644 --- a/src/components/AskForPushNotificationRefresh.js +++ b/src/components/AskForPushNotificationRefresh.js @@ -1,3 +1,10 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + import React from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { t } from 'ttag'; diff --git a/src/components/HathorHeader.js b/src/components/HathorHeader.js index d6952d7e3..493a9e404 100644 --- a/src/components/HathorHeader.js +++ b/src/components/HathorHeader.js @@ -16,59 +16,102 @@ import chevronLeft from '../assets/icons/chevron-left.png'; import closeIcon from '../assets/icons/icCloseActive.png'; import { COLORS, STYLE } from '../styles/themes'; -const HathorHeader = (props) => { - const renderBackButton = () => { - if (props.onBackPress) { - return ( - - - - - - ); - } - return ; - }; +const HathorHeader = ({ + title, + rightElement, + withLogo, + withBorder, + onBackPress, + onCancel, + wrapperStyle, + children, +}) => { + const hasChildren = children != null; + const left = React.Children.toArray(children).find( + (child) => child.type.displayName === HathorHeaderLeft.displayName + ); + const right = React.Children.toArray(children).find( + (child) => child.type.displayName === HathorHeaderRight.displayName + ); - const CancelButton = () => ( - + return ( + + {hasChildren + && ( + + {left} + {right} + + )} + {!hasChildren + && ( + + + + + + )} + ); +}; + +const Wrapper = ({ withBorder, style, children }) => ( + + {children} + +); + +const InnerWrapper = ({ children }) => ( + + {children} + +); - const renderHeaderRight = () => { - const element = (props.onCancel ? : props.rightElement); +const HathorHeaderLeft = ({ children }) => ({children}); +HathorHeaderLeft.displayName = 'HathorHeaderLeft'; + +const HathorHeaderRight = ({ children }) => {children}; +HathorHeaderRight.displayName = 'HathorHeaderRight'; + +HathorHeader.Left = HathorHeaderLeft; +HathorHeader.Right = HathorHeaderRight; + +const CancelButton = ({ onCancel }) => ( + +); + +const LeftComponent = ({ onBackPress }) => { + if (onBackPress) { return ( - - {element} + + + + ); - }; - - const renderHeaderCentral = () => { - if (props.withLogo) { - return ( - - ); - } - return {props.title}; - }; + } + return ; +}; - let extraStyle = {}; - if (props.withBorder) { - extraStyle = { borderBottomWidth: 1 }; +const CentralComponent = ({ title, withLogo }) => { + if (withLogo) { + return ( + + ); } + return {title}; +}; +const RightComponent = ({ rightElement, onCancel }) => { + const element = (onCancel ? : rightElement); return ( - - - {renderBackButton()} - {renderHeaderCentral()} - {renderHeaderRight()} - + + {element} ); }; @@ -81,6 +124,9 @@ const styles = StyleSheet.create({ borderColor: COLORS.borderColor, paddingHorizontal: 16, }, + wrapperWithBorder: { + borderBottomWidth: 1, + }, innerWrapper: { flex: 1, flexDirection: 'row', @@ -93,5 +139,16 @@ const styles = StyleSheet.create({ flexDirection: 'row', alignItems: 'center', }, + iconWrapperStart: { + justifyContent: 'flex-start', + }, + iconWrapperEnd: { + justifyContent: 'flex-end', + }, + centralComponentLogo: { + height: 22, + width: 100, + }, }); + export default HathorHeader; diff --git a/src/components/NanoContract/NanoContractIcon.icon.js b/src/components/NanoContract/NanoContractIcon.icon.js new file mode 100644 index 000000000..4a3737bd4 --- /dev/null +++ b/src/components/NanoContract/NanoContractIcon.icon.js @@ -0,0 +1,29 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import * as React from 'react' +import Svg, { Path } from 'react-native-svg' + +/** + * @param {SvgProps} props + * + * @description + * Svg converted from Figma using transaformer at https://react-svgr.com/playground/?native=true + */ +export const NanoContractIcon = (props) => ( + + + +); diff --git a/src/components/NanoContract/NanoContractsList.js b/src/components/NanoContract/NanoContractsList.js new file mode 100644 index 000000000..a150f3569 --- /dev/null +++ b/src/components/NanoContract/NanoContractsList.js @@ -0,0 +1,111 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import { StyleSheet, View, Text } from 'react-native'; +import { t } from 'ttag'; +import { useNavigation } from '@react-navigation/native'; + +import { useSelector } from 'react-redux'; +import { COLORS } from '../../styles/themes'; +import HathorHeader from '../HathorHeader'; +import { NoNanoContracts } from './NoNanoContracts'; +import { RegisterNanoContract } from './RegisterNewNanoContractButton'; +import { NanoContractsListItem } from './NanoContractsListItem'; +import { HathorFlatList } from '../HathorFlatList'; + +/** + * @param {Object} state Redux root state + * @returns {Object} Array of registered Nano Contract with basic information + */ +const getRegisteredNanoContracts = (state) => { + const { registered } = state.nanoContract; + return Object.values(registered); +} + +export const NanoContractsList = () => { + const registeredNanoContracts = useSelector(getRegisteredNanoContracts); + const navigation = useNavigation(); + + const navigatesToNanoContractTransactions = () => { + navigation.navigate('NanoContractTransactions'); + }; + const isEmpty = () => registeredNanoContracts.length === 0; + const notEmpty = () => !isEmpty(); + + return ( + +
+ {isEmpty() + && } + {notEmpty() + && ( + ( + + )} + keyExtractor={(nc) => nc.ncId} + /> + )} + + ); +}; + +const Wrapper = ({ children }) => ( + + {children} + +); + +const Header = () => ( + + + {t`Nano Contracts`} + + + + + +); + +const ListWrapper = ({ children }) => ( + + {children} + +); + +const styles = StyleSheet.create({ + wrapper: { + flex: 1, + justifyContent: 'flex-start', + alignItems: 'center', + backgroundColor: COLORS.lowContrastDetail, // Defines an outer area on the main list content + }, + listWrapper: { + flex: 1, + justifyContent: 'center', + alignSelf: 'stretch', + marginTop: 16, + marginBottom: 45, + backgroundColor: COLORS.backgroundColor, + marginHorizontal: 16, + borderRadius: 16, + shadowOffset: { height: 2, width: 0 }, + shadowRadius: 4, + shadowColor: COLORS.textColor, + shadowOpacity: 0.08, + }, + headerTitle: { + fontSize: 24, + lineHeight: 24, + fontWeight: 'bold', + }, +}); diff --git a/src/components/NanoContract/NanoContractsListItem.js b/src/components/NanoContract/NanoContractsListItem.js new file mode 100644 index 000000000..dde25bac4 --- /dev/null +++ b/src/components/NanoContract/NanoContractsListItem.js @@ -0,0 +1,106 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import { TouchableHighlight, StyleSheet, View, Text, Image } from 'react-native'; + +import chevronRight from '../../assets/icons/chevron-right.png'; +import { COLORS } from '../../styles/themes'; +import { getShortHash } from '../../utils'; +import { NanoContractIcon } from './NanoContractIcon.icon'; + +/** + * Renders each item of Nano Contract List. + * + * @param {Object} ncItem + * @property {Object} ncItem.item registered Nano Contract data + * @property {() => {}} ncItem.onPress A void function to be called when item is pressed. + */ +export const NanoContractsListItem = ({ item, onPress }) => ( + + + + + +); + +const Wrapper = ({ onPress, children }) => ( + + {children} + +); + +const Icon = () => ( + + + +); + +/** + * Renders item core content. + * + * @param {Object} ncItem + * @property {Obeject} ncItem.nc registered Nano Contract data + */ +const ContentWrapper = ({ nc }) => ( + + Nano Contract ID + {getShortHash(nc.ncId, 7)} + Blueprint Name + {nc.blueprintName} + +); + +const ArrowRight = () => ( + + + +); + +const styles = StyleSheet.create({ + wrapper: { + paddingVertical: 16, + paddingHorizontal: 16, + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + }, + contentWrapper: { + maxWidth: '80%', + flexDirection: 'column', + alignItems: 'flex-start', + justifyContent: 'space-between', + marginRight: 'auto', + paddingHorizontal: 16, + }, + icon: { + paddingVertical: 6, + paddingHorizontal: 8, + backgroundColor: COLORS.primary, + alignSelf: 'flex-start', + alignItems: 'center', + justifyContent: 'center', + borderRadius: 8, + }, + text: { + fontSize: 14, + lineHeight: 20, + paddingBottom: 6, + color: COLORS.textLabel, + }, + property: { + paddingBottom: 4, + fontWeight: 'bold', + color: 'black', + }, + padding0: { + paddingBottom: 0, + }, +}); diff --git a/src/components/NanoContract/NoNanoContracts.js b/src/components/NanoContract/NoNanoContracts.js new file mode 100644 index 000000000..a8e18dcab --- /dev/null +++ b/src/components/NanoContract/NoNanoContracts.js @@ -0,0 +1,62 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import { StyleSheet, View, Text } from 'react-native'; +import { t } from 'ttag'; + +import { RegisterNanoContract } from './RegisterNewNanoContractButton'; + +export const NoNanoContracts = () => ( + + {t`No Nano Contracts`} + + {t`You can keep track of your registered Nano Contracts here once you have registered them.`} + + + +); + +const styles = StyleSheet.create({ + wrapper: { + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + paddingHorizontal: 45, + /* Play the role of a minimum vertical padding for small screens */ + paddingVertical: 90, + }, + title: { + fontSize: 16, + lineHeight: 20, + fontWeight: 'bold', + paddingBottom: 16, + }, + content: { + fontSize: 14, + lineHeight: 20, + paddingBottom: 16, + textAlign: 'center', + }, + learnMoreWrapper: { + display: 'inline-block', + /* We are using negative margin here to correct the text position + * and create an optic effect of alignment. */ + marginBottom: -4, + paddingLeft: 2, + }, + learnMoreContainer: { + justifyContent: 'flex-start', + borderBottomWidth: 1, + }, + learnMoreText: { + fontSize: 14, + lineHeight: 20, + fontWeight: 'bold', + color: 'black', + }, +}); diff --git a/src/components/NanoContract/RegisterNewNanoContractButton.js b/src/components/NanoContract/RegisterNewNanoContractButton.js new file mode 100644 index 000000000..a924dbced --- /dev/null +++ b/src/components/NanoContract/RegisterNewNanoContractButton.js @@ -0,0 +1,26 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import { t } from 'ttag'; +import { useNavigation } from '@react-navigation/native'; + +import SimpleButton from '../SimpleButton'; + +export const RegisterNanoContract = () => { + const navigation = useNavigation(); + const navigatesToRegisterNanoContract = () => { + navigation.navigate('RegisterNanoContract'); + }; + + return ( + + ); +}; diff --git a/src/components/PushTxDetailsModal.js b/src/components/PushTxDetailsModal.js index 6a8eee367..2b9bfdb53 100644 --- a/src/components/PushTxDetailsModal.js +++ b/src/components/PushTxDetailsModal.js @@ -1,3 +1,10 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + import React from 'react'; import { Text, StyleSheet, View } from 'react-native'; import { t } from 'ttag'; diff --git a/src/components/ShowPushNotificationTxDetails.js b/src/components/ShowPushNotificationTxDetails.js index b16819901..fe9a87243 100644 --- a/src/components/ShowPushNotificationTxDetails.js +++ b/src/components/ShowPushNotificationTxDetails.js @@ -1,3 +1,10 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + import React from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { t } from 'ttag'; diff --git a/src/components/SimpleButton.js b/src/components/SimpleButton.js index 9dfef6f11..c7adc79f1 100644 --- a/src/components/SimpleButton.js +++ b/src/components/SimpleButton.js @@ -11,14 +11,29 @@ import { } from 'react-native'; import { PRIMARY_COLOR } from '../constants'; +/** + * Simple button component. + * + * @typedef {Object} Props + * @property {string} [title] - The title text of the button. + * @property {Object} [textStyle] - The style object for the text of the button. + * @property {string} [color] - The color of button's text. + * @property {string} [icon] - The icon component to be displayed in the button. + * @property {Object} [iconStyle] - The style object for the icon component. + * @property {Object} [containerStyle] - The style object for the container of the button. + * @property {Function} onPress - The function to be called when the button is pressed. + * @property {Object} children - The children component to be rendered. + * + * @param {Props} props - The props for the SimpleButton component. + */ const SimpleButton = ({ title, + textStyle, color, icon, - onPress, - containerStyle, - textStyle, iconStyle, + containerStyle, + onPress, children }) => { const renderTitle = () => { @@ -46,10 +61,7 @@ const SimpleButton = ({ }; return ( - + {renderTitle()} {renderIcon()} {children} diff --git a/src/components/TwoOptionsToggle.js b/src/components/TwoOptionsToggle.js new file mode 100644 index 000000000..af02df398 --- /dev/null +++ b/src/components/TwoOptionsToggle.js @@ -0,0 +1,86 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React, { useState } from 'react'; +import { StyleSheet, View, Text, TouchableOpacity } from 'react-native'; +import { COLORS } from '../styles/themes'; + +/** + * @param {{ + * options: Map<'first'|'second', { value: string; onTap: Function; }>; + * defaultOption: 'first'|'second'; + * }} + */ +export const TwoOptionsToggle = ({ options, defaultOption }) => { + const [currOption, setCurrOption] = useState(defaultOption); + const isFirst = currOption === 'first'; + const isSecond = currOption === 'second'; + + const onTapFirst = () => onTap('first'); + const onTapSecond = () => onTap('second'); + const onTap = (option) => { + if (option === currOption) { + // do nothing and halt. + return; + } + setCurrOption(option); + // Execute the callback assigned to the option + options[option].onTap(); + }; + + return ( + + + ); +}; + +/** + * @param {{ + * optionValue: string; + * isActive: boolean; + * onTap: (option: string) => void; + * }} + */ +const Option = ({ optionValue, isActive, onTap }) => ( + + {optionValue} + +); + +const styles = StyleSheet.create({ + wrapper: { + display: 'flex', + flexDirection: 'row', + width: '80%', + marginTop: 16, + borderRadius: 24, + backgroundColor: 'hsla(220, 10%, 94%, 1)', + }, + button: { + width: '50%', + borderRadius: 24, + paddingTop: 9, + paddingBottom: 10, + color: COLORS.textColor, + }, + buttonFocus: { + backgroundColor: COLORS.backgroundColor, + }, + text: { + fontSize: 14, + lineHeight: 20, + textAlign: 'center', + }, + textFocus: { + fontWeight: 'bold', + }, +}); diff --git a/src/components/WalletConnect/ModalButton.js b/src/components/WalletConnect/ModalButton.js index 85c9222b9..c78b19695 100644 --- a/src/components/WalletConnect/ModalButton.js +++ b/src/components/WalletConnect/ModalButton.js @@ -1,3 +1,10 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + import React from 'react'; import { Text, TouchableOpacity, StyleSheet } from 'react-native'; import { COLORS } from '../../styles/themes'; diff --git a/src/models.js b/src/models.js index 7425c3199..dec798466 100644 --- a/src/models.js +++ b/src/models.js @@ -16,7 +16,7 @@ export class TxHistory { * timestamp: number; * tokenUid: string; * balance: number; - * voided: boolean; + * isVoided: boolean; * version: number; * ncId?: string; * ncMethod?: string; diff --git a/src/screens/Dashboard.js b/src/screens/Dashboard.js index d6b8143c3..7f3326a5e 100644 --- a/src/screens/Dashboard.js +++ b/src/screens/Dashboard.js @@ -5,95 +5,208 @@ * LICENSE file in the root directory of this source tree. */ -import React from 'react'; -import { View } from 'react-native'; -import { connect } from 'react-redux'; +import React, { useState } from 'react'; +import { StyleSheet, View, Text } from 'react-native'; +import { useDispatch, useSelector } from 'react-redux'; import { t } from 'ttag'; import { get } from 'lodash'; +import { useNavigation } from '@react-navigation/native'; import AskForPushNotification from '../components/AskForPushNotification'; import HathorHeader from '../components/HathorHeader'; import TokenSelect from '../components/TokenSelect'; import SimpleButton from '../components/SimpleButton'; import OfflineBar from '../components/OfflineBar'; +import { TwoOptionsToggle } from '../components/TwoOptionsToggle'; import { tokenFetchBalanceRequested, updateSelectedToken } from '../actions'; import ShowPushNotificationTxDetails from '../components/ShowPushNotificationTxDetails'; import AskForPushNotificationRefresh from '../components/AskForPushNotificationRefresh'; +import { COLORS } from '../styles/themes'; +import { TOKEN_DOWNLOAD_STATUS } from '../sagas/tokens'; +import { NanoContractsList } from '../components/NanoContract/NanoContractsList'; +import { getNanoContractFeatureToggle } from '../utils'; /** - * tokens {Array} array with all added tokens on this wallet - * tokensBalance {Object} dict with balance for each token - * selectedToken {Object} token currently selected by the user - * tokenMetadata {Object} metadata of tokens + * State filter to retrieve token-related data from root state. + * + * @typedef {Object} TokenData + * @property {string} selectedToken - Current token selected. + * @property {string[]} tokens - Array containing all the tokens registered on the wallet. + * @property {{ [uid: string]: Object }} tokensBalance - Map of balance per token. + * @property {{ [uid: string]: Object }} tokensMetadata - Map of token's metadata per token. + * + * @returns {TokenData} Token-related data obtained from the root state. */ -const mapStateToProps = (state) => ({ +const getTokensState = (state) => ({ + selectedToken: state.selectedToken, tokens: state.tokens, tokensBalance: state.tokensBalance, - tokensHistory: state.tokensHistory, - selectedToken: state.selectedToken, - tokenMetadata: state.tokenMetadata, - tokenLoadingState: state.tokenLoadingState, + tokensMetadata: state.tokenMetadata, }); -const mapDispatchToProps = (dispatch) => ({ - updateSelectedToken: (token) => dispatch(updateSelectedToken(token)), - getBalance: (token) => dispatch(tokenFetchBalanceRequested(token)), -}); +// Check if the token balance is already loaded +/** + * @param {{ [uid: string]: Object }} tokensBalance a map of token's balance per token uid + * @param {{ uid: string }} token the token data + * @returns {string} the status of the current tokens balance loading process. + */ +const getTokensBalanceStatus = (tokensBalance, token) => get(tokensBalance, `${token.uid}.status`, TOKEN_DOWNLOAD_STATUS.LOADING); + +/** + * @param {string} status the current status from tokens balance loading process. + * @returns {boolean} `true` if loading, `false` otherwise. + */ +const isTokensBalanceLoading = (status) => status === TOKEN_DOWNLOAD_STATUS.LOADING; + +/** + * @param {string} status the current status from tokens balance loading process. + * @returns {boolean} `true` if failed, `false` otherwise. + */ +const isTokensBalanceFailed = (status) => status === TOKEN_DOWNLOAD_STATUS.FAILED; + +/** + * Enum for the list component that can be selected to render on Dashboard. + * @readonly + * @enum {string} + */ +const listOption = { + tokens: 'tokens', + nanoContracts: 'nanoContracts', +}; + +/** + * @param {listOption} currList the list component selected to be rendered. + * @returns {boolean} `true` if tokens list is selected, `false` otherwise. + */ +const isTokensSelected = (currList) => currList === listOption.tokens; + +/** + * @param {listOption} currList the list component selected to be rendered. + * @returns {boolean} `true` if nanoContracts list is selected, `false` otherwise. + */ +const isNanoContractsSelected = (currList) => currList === listOption.nanoContracts; + +export const Dashboard = () => { + const { + tokens, + tokensBalance, + selectedToken, + tokensMetadata, + } = useSelector(getTokensState); + const isNanoContractEnabled = useSelector(getNanoContractFeatureToggle); -class Dashboard extends React.Component { - static navigatorStyle = { tabBarVisible: false } + const [currList, selectList] = useState(listOption.tokens); + const navigation = useNavigation(); + const dispatch = useDispatch(); - onItemPress = (item) => { - // Check if the token balance is already loaded - const tokenBalanceStatus = get(this.props.tokensBalance, `${item.uid}.status`, 'loading'); + const onTokenPress = (token) => { + const status = getTokensBalanceStatus(tokensBalance, token); - if (tokenBalanceStatus === 'loading') { + if (isTokensBalanceLoading(status)) { return; } - if (tokenBalanceStatus === 'failed') { + if (isTokensBalanceFailed(status)) { // If the token balance status is failed, we should try again - this.props.getBalance(item.uid); + dispatch(tokenFetchBalanceRequested(token.uid)) return; } - this.props.updateSelectedToken(item); - this.props.navigation.navigate('MainScreen'); + dispatch(updateSelectedToken(token)); + navigation.navigate('MainScreen'); } - render() { - const ManualInfoButton = () => ( - this.props.navigation.navigate('RegisterToken')} - /> - ); - - const Header = () => ( - } - /> - ); - - return ( - - - - - } - renderArrow - onItemPress={this.onItemPress} - selectedToken={this.props.selectedToken} - tokens={this.props.tokens} - tokensBalance={this.props.tokensBalance} - tokenMetadata={this.props.tokenMetadata} - /> - - - ); - } + return ( + + + + + { // Only show the toggle button when Nano Contract is enabled to the wallet + isNanoContractEnabled + && ( + + selectList(listOption.tokens) }, + second: { value: t`Nano Contracts`, onTap: () => selectList(listOption.nanoContracts) } + }} + defaultOption='first' + /> + + ) + } + { // Default behavior is to show tokens list + isTokensSelected(currList) + && ( + } + renderArrow + onItemPress={onTokenPress} + selectedToken={selectedToken} + tokens={tokens} + tokensBalance={tokensBalance} + tokenMetadata={tokensMetadata} + /> + ) + } + { // Only show if Nano Contract is enabled in the wallet + isNanoContractEnabled + && isNanoContractsSelected(currList) + && + } + + + ); } -export default connect(mapStateToProps, mapDispatchToProps)(Dashboard); +const Wrapper = ({ children }) => ( + + {children} + +); + +const DashBoardHeader = ({ children }) => ( + + {children} + +); + +const RegisterToken = () => { + const navigation = useNavigation(); + return ( + navigation.navigate('RegisterToken')} + /> + ); +}; + +const TokensHeader = () => ( + + + {t`Tokens`} + + + + + +); + +const styles = StyleSheet.create({ + wrapper: { + flex: 1, + }, + headerWrapper: { + display: 'flex', + flexDirection: 'row', + justifyContent: 'center', + backgroundColor: COLORS.lowContrastDetail, + }, + headerTitle: { + fontSize: 24, + lineHeight: 24, + fontWeight: 'bold', + }, +}); + +export default Dashboard; diff --git a/src/styles/themes.js b/src/styles/themes.js index e574f2278..ca13a8bf9 100644 --- a/src/styles/themes.js +++ b/src/styles/themes.js @@ -92,6 +92,10 @@ export const COLORS = { feedbackError600: 'hsla(7, 100%, 30%, 1)', freeze100: 'hsla(0, 0%, 90%, 1)', freeze300: 'hsla(0, 0%, 45%, 1)', + /** + * @type {string} Black with 38% of light and full opaque + */ + textLabel: 'hsla(0, 0%, 38%, 1)', }; /** diff --git a/src/utils.js b/src/utils.js index 9ec88fcb6..e950f1354 100644 --- a/src/utils.js +++ b/src/utils.js @@ -12,7 +12,7 @@ import { t } from 'ttag'; import { Linking, Platform, Text } from 'react-native'; import { getStatusBarHeight } from 'react-native-status-bar-height'; import baseStyle from './styles/init'; -import { KEYCHAIN_USER } from './constants'; +import { KEYCHAIN_USER, NANO_CONTRACT_FEATURE_TOGGLE } from './constants'; import { STORE } from './store'; import { TxHistory } from './models'; import { COLORS, STYLE } from './styles/themes'; @@ -392,3 +392,14 @@ export function combineURLs(baseURL, relativeURL) { export const isPushNotificationAvailableForUser = (state) => ( state.pushNotification.available && state.pushNotification.deviceRegistered ); + +/** + * Get Nano Contract feature toggle state from redux. + * + * @param {Object} state Redux store state + * + * @returns {boolean} the Nano Contract feature toggle state. + */ +export const getNanoContractFeatureToggle = (state) => ( + state.featureToggles[NANO_CONTRACT_FEATURE_TOGGLE] +); From ad96b958bc0b9a348e85b6957e6e6b620033a16c Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Tue, 28 May 2024 01:44:06 +0100 Subject: [PATCH 15/51] fix: remove registered tokens when changing network settings (#475) * fix: remove registered tokens when changing network settings * feat: it extends HibridStore cleanStorage to also clean the cache * feat: remove cleanStorage and add cleanTokens when calling wallet.stop --- src/sagas/networkSettings.js | 6 +++--- src/store.js | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/sagas/networkSettings.js b/src/sagas/networkSettings.js index ea02c3ef8..60eeddcbe 100644 --- a/src/sagas/networkSettings.js +++ b/src/sagas/networkSettings.js @@ -12,7 +12,7 @@ import { types, reloadWalletRequested, onExceptionCaptured, - networkSettingsUpdateReady + networkSettingsUpdateReady, } from '../actions'; import { NETWORK_MAINNET, @@ -23,7 +23,7 @@ import { STAGE_DEV_PRIVNET, STAGE_TESTNET, WALLET_SERVICE_REQUEST_TIMEOUT, - NETWORK_PRIVATENET + NETWORK_PRIVATENET, } from '../constants'; import { getFullnodeNetwork, @@ -300,7 +300,7 @@ export function* persistNetworkSettings(action) { } // Stop wallet and clean its storage without clean its access data. - wallet.stop({ cleanStorage: true, cleanAddresses: true }); + wallet.stop({ cleanStorage: true, cleanAddresses: true, cleanTokens: true }); // This action should clean the tokens history on redux. // In addition, the reload also clean the inmemory storage. yield put(reloadWalletRequested()); diff --git a/src/store.js b/src/store.js index 88d6fcb93..9bf8cd037 100644 --- a/src/store.js +++ b/src/store.js @@ -143,6 +143,22 @@ class HybridStore extends MemoryStore { contracts[ncId] = ncValue; STORE.setItem(REGISTERED_NANO_CONTRACTS_KEY, contracts) } + + /** + * Clean the storage. + * @param {boolean} cleanHistory if we should clean the transaction history. + * @param {boolean} cleanAddresses if we should clean the addresses. + * @param {boolean} cleanTokens if we should clean the registered tokens. + * @async + * @returns {Promise} + */ + async cleanStorage(cleanHistory = false, cleanAddresses = false, cleanTokens = false) { + super.cleanStorage(cleanHistory, cleanAddresses, cleanTokens); + if (cleanTokens) { + // Remove from the cache + STORE.removeItem(REGISTERED_TOKENS_KEY); + } + } } /* eslint-enable class-methods-use-this */ From 432676078997fb7d64abbbf3e10eaae383c0eb84 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Tue, 28 May 2024 15:24:23 +0100 Subject: [PATCH 16/51] feat: add icons for nano contract transactions [8] (#455) * feat(nc): add icons * refactor: move icons to /Icons and add typed properties --- src/components/Icons/ActionDot.icon.js | 5 +++ src/components/Icons/ArrowDown.icon.js | 34 ++++++++++++++ src/components/Icons/ArrowUp.icon.js | 34 ++++++++++++++ src/components/Icons/Base.icon.js | 41 +++++++++++++++++ src/components/Icons/CircleCheck.icon.js | 8 ++++ src/components/Icons/CircleClock.icon.js | 8 ++++ src/components/Icons/CircleError.icon.js | 8 ++++ src/components/Icons/CircleInfo.icon.js | 41 +++++++++++++++++ src/components/Icons/NanoContract.icon.js | 40 +++++++++++++++++ src/components/Icons/Oracle.icon.js | 44 +++++++++++++++++++ src/components/Icons/Pen.icon.js | 37 ++++++++++++++++ src/components/Icons/RandomTx.icon.js | 40 +++++++++++++++++ src/components/Icons/Registered.icon.js | 44 +++++++++++++++++++ src/components/Icons/Wallet.icon.js | 40 +++++++++++++++++ .../NanoContract/NanoContractIcon.icon.js | 29 ------------ .../NanoContract/NanoContractsListItem.js | 2 +- src/styles/themes.js | 1 + 17 files changed, 426 insertions(+), 30 deletions(-) create mode 100644 src/components/Icons/ArrowDown.icon.js create mode 100644 src/components/Icons/ArrowUp.icon.js create mode 100644 src/components/Icons/Base.icon.js create mode 100644 src/components/Icons/CircleInfo.icon.js create mode 100644 src/components/Icons/NanoContract.icon.js create mode 100644 src/components/Icons/Oracle.icon.js create mode 100644 src/components/Icons/Pen.icon.js create mode 100644 src/components/Icons/RandomTx.icon.js create mode 100644 src/components/Icons/Registered.icon.js create mode 100644 src/components/Icons/Wallet.icon.js delete mode 100644 src/components/NanoContract/NanoContractIcon.icon.js diff --git a/src/components/Icons/ActionDot.icon.js b/src/components/Icons/ActionDot.icon.js index a27bd4fee..5680cbb8f 100644 --- a/src/components/Icons/ActionDot.icon.js +++ b/src/components/Icons/ActionDot.icon.js @@ -11,6 +11,11 @@ import { COLORS } from '../../styles/themes' import { DEFAULT_ICON_SIZE } from './constants' import { getScale, getViewBox } from './helper' +/** + * @param {object} props + * @property {number} props.size + * @property {StyleSheet} props.color + */ export const ActionDot = ({ size = DEFAULT_ICON_SIZE, color = COLORS.black }) => ( ( + + + +); + +const styles = StyleSheet.create({ + /* This wrapper adjusts the icon's size to 24x24, otherwise it would be 12x7. */ + wrapper: { + paddingVertical: 8.5, + paddingHorizontal: 6, + }, +}); diff --git a/src/components/Icons/ArrowUp.icon.js b/src/components/Icons/ArrowUp.icon.js new file mode 100644 index 000000000..1be5fcd89 --- /dev/null +++ b/src/components/Icons/ArrowUp.icon.js @@ -0,0 +1,34 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import * as React from 'react' +import { View, Image, StyleSheet } from 'react-native'; +import chevronUp from '../../assets/icons/chevron-up.png'; +import { DEFAULT_ICON_SIZE } from './constants'; + +/** + * @param {object} props + * @property {number} props.size + * @property {StyleSheet} props.style + */ +export const ArrowUpIcon = ({ size = DEFAULT_ICON_SIZE, style }) => ( + + + +); + +const styles = StyleSheet.create({ + /* This wrapper adjusts the icon's size to 24x24, otherwise it would be 12x7. */ + wrapper: { + paddingVertical: 8.5, + paddingHorizontal: 6, + }, +}); diff --git a/src/components/Icons/Base.icon.js b/src/components/Icons/Base.icon.js new file mode 100644 index 000000000..c505a5c5c --- /dev/null +++ b/src/components/Icons/Base.icon.js @@ -0,0 +1,41 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { StyleSheet, View } from 'react-native'; +import { COLORS } from '../../styles/themes'; + +/** + * @param {object} props + * @property {'default'|'outline'|'fill'} props.type + * @property {StyleSheet} props.style + * @property {ReactNode} props.children + */ +export const BaseIcon = ({ type = 'default', style, children }) => ( + + {children} + +); + +const styles = StyleSheet.create({ + default: {}, + outline: { + borderRadius: 8, + borderWidth: 1.3, + padding: 4, + }, + fill: { + borderRadius: 8, + borderWidth: 1.3, + borderColor: COLORS.primary, + padding: 4, + backgroundColor: COLORS.primary, + }, +}); diff --git a/src/components/Icons/CircleCheck.icon.js b/src/components/Icons/CircleCheck.icon.js index 09a125a5d..48f6895d7 100644 --- a/src/components/Icons/CircleCheck.icon.js +++ b/src/components/Icons/CircleCheck.icon.js @@ -11,6 +11,14 @@ import { COLORS } from '../../styles/themes' import { DEFAULT_ICON_SIZE } from './constants' import { getScale, getViewBox } from './helper' +/** + * @param {object} props + * @property {number} props.size + * @property {StyleSheet} props.color + * + * @description + * Svg converted from Figma using transaformer at https://react-svgr.com/playground/?native=true + */ export const CircleCheck = ({ size = DEFAULT_ICON_SIZE, color = COLORS.black }) => ( ( ( ( + + + + + + + + + + +); diff --git a/src/components/Icons/NanoContract.icon.js b/src/components/Icons/NanoContract.icon.js new file mode 100644 index 000000000..570fec539 --- /dev/null +++ b/src/components/Icons/NanoContract.icon.js @@ -0,0 +1,40 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import * as React from 'react' +import Svg, { Path } from 'react-native-svg' +import { COLORS } from '../../styles/themes'; +import { BaseIcon } from './Base.icon'; +import { DEFAULT_ICON_SIZE } from './constants'; +import { getScale, getViewBox } from './helper'; + +/** + * @param {object} props + * @property {SvgProps|{type: 'default'|'outline'|'fill'}} props.type + * @property {number} props.size + * @property {StyleSheet} props.color + * + * @description + * Svg converted from Figma using transaformer at https://react-svgr.com/playground/?native=true + */ +export const NanoContractIcon = ({ type = 'default', size = DEFAULT_ICON_SIZE, color = COLORS.black }) => ( + + + + + +); diff --git a/src/components/Icons/Oracle.icon.js b/src/components/Icons/Oracle.icon.js new file mode 100644 index 000000000..500177a53 --- /dev/null +++ b/src/components/Icons/Oracle.icon.js @@ -0,0 +1,44 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import * as React from 'react' +import Svg, { Path } from 'react-native-svg' +import { COLORS } from '../../styles/themes'; +import { BaseIcon } from './Base.icon'; +import { DEFAULT_ICON_SIZE } from './constants'; +import { getScale, getViewBox } from './helper'; + +/** + * @param {object} props + * @property {SvgProps|{type: 'default'|'outline'|'fill'}} props.type + * @property {number} props.size + * @property {StyleSheet} props.color + * + * @description + * Svg converted from Figma using transaformer at https://react-svgr.com/playground/?native=true + */ +export const OracleIcon = ({ type = 'default', size = DEFAULT_ICON_SIZE, color = COLORS.black }) => ( + + + + + + +); diff --git a/src/components/Icons/Pen.icon.js b/src/components/Icons/Pen.icon.js new file mode 100644 index 000000000..03d4204c8 --- /dev/null +++ b/src/components/Icons/Pen.icon.js @@ -0,0 +1,37 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import * as React from 'react' +import Svg, { Path } from 'react-native-svg' +import { COLORS } from '../../styles/themes'; +import { BaseIcon } from './Base.icon'; +import { DEFAULT_ICON_SIZE } from './constants'; + +/** + * @param {object} props + * @property {SvgProps|{type: 'default'|'outline'|'fill'}} props.type + * @property {number} props.size + * @property {StyleSheet} props.color + * + * @description + * Svg converted from Figma using transaformer at https://react-svgr.com/playground/?native=true + */ +export const PenIcon = ({ type = 'default', size = DEFAULT_ICON_SIZE, color = COLORS.black }) => ( + + + + + +); diff --git a/src/components/Icons/RandomTx.icon.js b/src/components/Icons/RandomTx.icon.js new file mode 100644 index 000000000..8cc228334 --- /dev/null +++ b/src/components/Icons/RandomTx.icon.js @@ -0,0 +1,40 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import * as React from 'react' +import Svg, { Path } from 'react-native-svg' +import { COLORS } from '../../styles/themes'; +import { BaseIcon } from './Base.icon'; +import { DEFAULT_ICON_SIZE } from './constants'; +import { getScale, getViewBox } from './helper'; + +/** + * @param {object} props + * @property {SvgProps|{type: 'default'|'outline'|'fill'}} props.type + * @property {number} props.size + * @property {StyleSheet} props.color + * + * @description + * Svg converted from Figma using transaformer at https://react-svgr.com/playground/?native=true + */ +export const RandomTxIcon = ({ type = 'default', size = DEFAULT_ICON_SIZE, color = COLORS.black }) => ( + + + + + +); diff --git a/src/components/Icons/Registered.icon.js b/src/components/Icons/Registered.icon.js new file mode 100644 index 000000000..31f741588 --- /dev/null +++ b/src/components/Icons/Registered.icon.js @@ -0,0 +1,44 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import * as React from 'react' +import Svg, { Path } from 'react-native-svg' +import { COLORS } from '../../styles/themes'; +import { BaseIcon } from './Base.icon'; +import { DEFAULT_ICON_SIZE } from './constants'; +import { getScale, getViewBox } from './helper'; + +/** + * @param {object} props + * @property {SvgProps|{type: 'default'|'outline'|'fill'}} props.type + * @property {number} props.size + * @property {StyleSheet} props.color + * + * @description + * Svg converted from Figma using transaformer at https://react-svgr.com/playground/?native=true + */ +export const RegisteredIcon = ({ type = 'default', size = DEFAULT_ICON_SIZE, color = COLORS.black }) => ( + + + + + + +); diff --git a/src/components/Icons/Wallet.icon.js b/src/components/Icons/Wallet.icon.js new file mode 100644 index 000000000..a769fbce0 --- /dev/null +++ b/src/components/Icons/Wallet.icon.js @@ -0,0 +1,40 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import * as React from 'react' +import Svg, { Path } from 'react-native-svg' +import { COLORS } from '../../styles/themes'; +import { BaseIcon } from './Base.icon'; +import { DEFAULT_ICON_SIZE } from './constants'; +import { getScale, getViewBox } from './helper'; + +/** + * @param {object} props + * @property {SvgProps|{type: 'default'|'outline'|'fill'}} props.type + * @property {number} props.size + * @property {StyleSheet} props.color + * + * @description + * Svg converted from Figma using transaformer at https://react-svgr.com/playground/?native=true + */ +export const WalletIcon = ({ type = 'default', size = DEFAULT_ICON_SIZE, color = COLORS.black }) => ( + + + + + +); diff --git a/src/components/NanoContract/NanoContractIcon.icon.js b/src/components/NanoContract/NanoContractIcon.icon.js deleted file mode 100644 index 4a3737bd4..000000000 --- a/src/components/NanoContract/NanoContractIcon.icon.js +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (c) Hathor Labs and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -import * as React from 'react' -import Svg, { Path } from 'react-native-svg' - -/** - * @param {SvgProps} props - * - * @description - * Svg converted from Figma using transaformer at https://react-svgr.com/playground/?native=true - */ -export const NanoContractIcon = (props) => ( - - - -); diff --git a/src/components/NanoContract/NanoContractsListItem.js b/src/components/NanoContract/NanoContractsListItem.js index dde25bac4..06b5150be 100644 --- a/src/components/NanoContract/NanoContractsListItem.js +++ b/src/components/NanoContract/NanoContractsListItem.js @@ -11,7 +11,7 @@ import { TouchableHighlight, StyleSheet, View, Text, Image } from 'react-native' import chevronRight from '../../assets/icons/chevron-right.png'; import { COLORS } from '../../styles/themes'; import { getShortHash } from '../../utils'; -import { NanoContractIcon } from './NanoContractIcon.icon'; +import { NanoContractIcon } from '../Icons/NanoContract.icon'; /** * Renders each item of Nano Contract List. diff --git a/src/styles/themes.js b/src/styles/themes.js index ca13a8bf9..6a2f44159 100644 --- a/src/styles/themes.js +++ b/src/styles/themes.js @@ -13,6 +13,7 @@ import { HslColor } from '../HslColor'; * Light theme color scheme */ export const COLORS = { + white: '#fff', black: '#000', /** * @type {string} The main background color From c744c9324dec0a60eb66385e18664efb17574c68 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Tue, 28 May 2024 16:59:32 +0100 Subject: [PATCH 17/51] feat: add components for nano contract transactions [9] (#456) * feat(nc): add components --- locale/da/texts.po | 2 +- locale/pt-br/texts.po | 2 +- locale/ru-ru/texts.po | 2 +- locale/texts.pot | 2 +- src/components/EditInfoContainer.js | 72 +++++++++++++++++ src/components/HathorHeader.js | 10 ++- src/components/ModalBase.js | 116 ++++++++++++++++++++++++++++ src/components/NewHathorButton.js | 24 ++++++ src/components/TextLabel.js | 35 +++++++++ src/components/TextValue.js | 35 +++++++++ src/utils.js | 10 +++ 11 files changed, 305 insertions(+), 5 deletions(-) create mode 100644 src/components/EditInfoContainer.js create mode 100644 src/components/ModalBase.js create mode 100644 src/components/TextLabel.js create mode 100644 src/components/TextValue.js diff --git a/locale/da/texts.po b/locale/da/texts.po index d5cb9eac0..c42056faa 100644 --- a/locale/da/texts.po +++ b/locale/da/texts.po @@ -61,7 +61,7 @@ msgstr "[Sidste] dddd [•] HH:mm" msgid "DD MMM YYYY [•] HH:mm" msgstr "DD MMM YYYY [•] HH:mm" -#: src/utils.js:159 +#: src/utils.js:160 msgid "Invalid address" msgstr "Ugyldig adresse" diff --git a/locale/pt-br/texts.po b/locale/pt-br/texts.po index dae12e7e5..c502c4226 100644 --- a/locale/pt-br/texts.po +++ b/locale/pt-br/texts.po @@ -61,7 +61,7 @@ msgstr "[Última] dddd [•] HH:mm" msgid "DD MMM YYYY [•] HH:mm" msgstr "DD MMM YYYY [•] HH:mm" -#: src/utils.js:159 +#: src/utils.js:160 msgid "Invalid address" msgstr "Endereço inválido" diff --git a/locale/ru-ru/texts.po b/locale/ru-ru/texts.po index d06c178fe..ae00bf6ee 100644 --- a/locale/ru-ru/texts.po +++ b/locale/ru-ru/texts.po @@ -62,7 +62,7 @@ msgstr "[Последний] dddd [•] HH:mm" msgid "DD MMM YYYY [•] HH:mm" msgstr "DD MMM YYYY [•] HH:mm" -#: src/utils.js:159 +#: src/utils.js:160 msgid "Invalid address" msgstr "Неправильный адрес" diff --git a/locale/texts.pot b/locale/texts.pot index ffc40232d..2cb15ab4b 100644 --- a/locale/texts.pot +++ b/locale/texts.pot @@ -52,7 +52,7 @@ msgstr "" msgid "DD MMM YYYY [•] HH:mm" msgstr "" -#: src/utils.js:159 +#: src/utils.js:160 msgid "Invalid address" msgstr "" diff --git a/src/components/EditInfoContainer.js b/src/components/EditInfoContainer.js new file mode 100644 index 000000000..202e49ccc --- /dev/null +++ b/src/components/EditInfoContainer.js @@ -0,0 +1,72 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import { + StyleSheet, + View, + TouchableOpacity +} from 'react-native'; +import { COLORS } from '../styles/themes'; + +import { PenIcon } from './Icons/Pen.icon'; + +export const EditInfoContainer = ({ center, onPress, children }) => ( + + + + {children} + + + + + + +); + +const styles = StyleSheet.create({ + editInfoWrapperCentered: { + alignSelf: 'center', + }, + editInfoContainer: { + paddingVertical: 8, + paddingLeft: 8, + paddingRight: 40, + borderRadius: 8, + backgroundColor: COLORS.white, + }, + editInfoContainerCentered: { + paddingLeft: 40, + }, + alignCenter: { + alignItems: 'center', + }, + editIcon: { + position: 'absolute', + display: 'flex', + justifyContent: 'center', + top: 0, + right: 0, + bottom: 0, + width: 24, + height: '100%', + marginVertical: 8, + marginRight: 8, + }, +}); diff --git a/src/components/HathorHeader.js b/src/components/HathorHeader.js index 493a9e404..f6a174b2a 100644 --- a/src/components/HathorHeader.js +++ b/src/components/HathorHeader.js @@ -30,6 +30,9 @@ const HathorHeader = ({ const left = React.Children.toArray(children).find( (child) => child.type.displayName === HathorHeaderLeft.displayName ); + const central = React.Children.toArray(children).find( + (child) => child.type.displayName === HathorHeaderCentral.displayName + ); const right = React.Children.toArray(children).find( (child) => child.type.displayName === HathorHeaderRight.displayName ); @@ -40,6 +43,7 @@ const HathorHeader = ({ && ( {left} + {central} {right} )} @@ -70,10 +74,14 @@ const InnerWrapper = ({ children }) => ( const HathorHeaderLeft = ({ children }) => ({children}); HathorHeaderLeft.displayName = 'HathorHeaderLeft'; +const HathorHeaderCentral = ({ style, children }) => {children}; +HathorHeaderCentral.displayName = 'HathorHeaderCentral'; + const HathorHeaderRight = ({ children }) => {children}; HathorHeaderRight.displayName = 'HathorHeaderRight'; HathorHeader.Left = HathorHeaderLeft; +HathorHeader.Central = HathorHeaderCentral; HathorHeader.Right = HathorHeaderRight; const CancelButton = ({ onCancel }) => ( @@ -118,7 +126,7 @@ const RightComponent = ({ rightElement, onCancel }) => { const styles = StyleSheet.create({ wrapper: { - height: STYLE.headerHeight, + minHeight: STYLE.headerHeight, flexDirection: 'row', alignItems: 'flex-end', borderColor: COLORS.borderColor, diff --git a/src/components/ModalBase.js b/src/components/ModalBase.js new file mode 100644 index 000000000..2b24b7acf --- /dev/null +++ b/src/components/ModalBase.js @@ -0,0 +1,116 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import { + StyleSheet, + View, + Text, +} from 'react-native'; +import Modal from 'react-native-modal'; + +import { COLORS } from '../styles/themes'; +import NewHathorButton from './NewHathorButton'; + +const ModalBase = ({ styleModal, styleWrapper, show, onDismiss, children }) => { + const hasChildren = children != null; + + const title = hasChildren && React.Children.toArray(children).find( + (child) => child.type.displayName === Title.displayName + ); + const body = hasChildren && React.Children.toArray(children).find( + (child) => child.type.displayName === Body.displayName + ); + const button = hasChildren && React.Children.toArray(children).find( + (child) => child.type.displayName === Button.displayName + ); + const discreteButton = hasChildren && React.Children.toArray(children).find( + (child) => child.type.displayName === DiscreteButton.displayName + ); + + return ( + + + {title && title} + {body && body} + {button && button} + {discreteButton && discreteButton} + + + ); +}; + +const Title = ({ children }) => ( + + + {children} + + +); +Title.displayName = 'ModalBaseTitle'; + +/** + * @param {Object} props + * @property {ReactNode} props.children + * @property {StyleProp} props.style + */ +const Body = ({ style, children }) => ( + + {children} + +); +Body.displayName = 'ModalBaseBody'; + +const Button = ({ title, disabled, secondary, danger, onPress }) => ( + +); +Button.displayName = 'ModalBaseButton'; + +const DiscreteButton = ({ title, onPress }) => ( + +); +DiscreteButton.displayName = 'ModalBaseDiscreteButton'; + +ModalBase.Title = Title; +ModalBase.Body = Body; +ModalBase.Button = Button; +ModalBase.DiscreteButton = DiscreteButton; + +const styles = StyleSheet.create({ + wrapper: { + borderRadius: 8, + paddingVertical: 24, + paddingHorizontal: 16, + backgroundColor: COLORS.white, + }, + titleWrapper: { + paddingBottom: 20, + }, + title: { + color: 'black', + fontSize: 18, + lineHeight: 20, + }, + discreteButton: { + marginTop: 8, + }, +}); + +export { ModalBase } diff --git a/src/components/NewHathorButton.js b/src/components/NewHathorButton.js index a1603e949..1afe5e5d1 100644 --- a/src/components/NewHathorButton.js +++ b/src/components/NewHathorButton.js @@ -21,6 +21,11 @@ const NewHathorButton = (props) => { textStyle.push(style.textDisabled); } + if (props.discrete) { + wrapperViewStyle.push(style.wrapperDiscrete); + textStyle.push(style.textDiscrete); + } + if (props.secondary) { wrapperViewStyle.push(style.wrapperSecondary); textStyle.push(style.textSecondary); @@ -34,6 +39,11 @@ const NewHathorButton = (props) => { } } + if (props.danger) { + wrapperViewStyle.push(style.wrapperSecondaryDanger); + textStyle.push(style.textSecondaryDanger); + } + return ( @@ -79,6 +89,14 @@ const style = StyleSheet.create({ wrapperSecondaryDisabled: { borderColor: COLORS.textColorShadow, }, + wrapperDiscrete: { + backgroundColor: COLORS.backgroundColor, + borderColor: COLORS.backgroundColor, + borderWidth: 1.5, + }, + wrapperSecondaryDanger: { + borderColor: COLORS.errorBgColor, + }, touchable: { flex: 1, flexDirection: 'row', @@ -101,6 +119,12 @@ const style = StyleSheet.create({ textDisabled: { color: COLORS.textColorShadow, }, + textDiscrete: { + color: COLORS.freeze300, + }, + textSecondaryDanger: { + color: COLORS.errorBgColor, + }, }); export default NewHathorButton; diff --git a/src/components/TextLabel.js b/src/components/TextLabel.js new file mode 100644 index 000000000..702e17d2d --- /dev/null +++ b/src/components/TextLabel.js @@ -0,0 +1,35 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import { + StyleSheet, + Text, +} from 'react-native'; + +export const TextLabel = ({ pb8, bold, children }) => ( + {children} +); + +const styles = StyleSheet.create({ + textLabel: { + fontSize: 12, + lineHeight: 20, + color: 'hsla(0, 0%, 38%, 1)', + }, + pb8: { + paddingBottom: 8, + }, + bold: { + fontWeight: 'bold', + }, +}); diff --git a/src/components/TextValue.js b/src/components/TextValue.js new file mode 100644 index 000000000..952616385 --- /dev/null +++ b/src/components/TextValue.js @@ -0,0 +1,35 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import { + StyleSheet, + Text, +} from 'react-native'; + +export const TextValue = ({ bold, pb4, children }) => ( + {children} +); + +const styles = StyleSheet.create({ + textValue: { + fontSize: 14, + lineHeight: 20, + color: 'black', + }, + pb4: { + paddingBottom: 4, + }, + bold: { + fontWeight: 'bold', + }, +}); diff --git a/src/utils.js b/src/utils.js index e950f1354..281c5d790 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 moment from 'moment'; import baseStyle from './styles/init'; import { KEYCHAIN_USER, NANO_CONTRACT_FEATURE_TOGGLE } from './constants'; import { STORE } from './store'; @@ -403,3 +404,12 @@ export const isPushNotificationAvailableForUser = (state) => ( export const getNanoContractFeatureToggle = (state) => ( state.featureToggles[NANO_CONTRACT_FEATURE_TOGGLE] ); + +/** + * Get timestamp in specific format. + * + * @param {number} timestamp + * + * @returns {string} formatted timestamp + */ +export const getTimestampFormat = (timestamp) => moment.unix(timestamp).format(t`DD MMM YYYY [•] HH:mm`) From b3cb6a48230a1f44d670928e25f34406bd053528 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Carneiro?= Date: Tue, 28 May 2024 16:42:22 -0300 Subject: [PATCH 18/51] chore: call super method on write (#492) --- src/store.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/store.js b/src/store.js index 9bf8cd037..937f65605 100644 --- a/src/store.js +++ b/src/store.js @@ -41,6 +41,7 @@ class HybridStore extends MemoryStore { * @returns {Promise} */ async saveAccessData(data) { + await super.saveAccessData(data); STORE.setItem(ACCESS_DATA_KEY, data); } @@ -75,6 +76,7 @@ class HybridStore extends MemoryStore { * @returns {Promise} */ async registerToken(token) { + await super.registerToken(token); const registeredTokens = STORE.getItem(REGISTERED_TOKENS_KEY) || {}; registeredTokens[token.uid] = token; STORE.setItem(REGISTERED_TOKENS_KEY, registeredTokens); @@ -88,6 +90,7 @@ class HybridStore extends MemoryStore { * @returns {Promise} */ async unregisterToken(tokenUid) { + await super.unregisterToken(tokenUid); const registeredTokens = STORE.getItem(REGISTERED_TOKENS_KEY) || {}; if (tokenUid in registeredTokens) { delete registeredTokens[tokenUid]; @@ -139,6 +142,7 @@ class HybridStore extends MemoryStore { * @async */ async registerNanoContract(ncId, ncValue) { + await super.registerNanoContract(ncId, ncValue); const contracts = STORE.getItem(REGISTERED_NANO_CONTRACTS_KEY) || {}; contracts[ncId] = ncValue; STORE.setItem(REGISTERED_NANO_CONTRACTS_KEY, contracts) @@ -153,7 +157,7 @@ class HybridStore extends MemoryStore { * @returns {Promise} */ async cleanStorage(cleanHistory = false, cleanAddresses = false, cleanTokens = false) { - super.cleanStorage(cleanHistory, cleanAddresses, cleanTokens); + await super.cleanStorage(cleanHistory, cleanAddresses, cleanTokens); if (cleanTokens) { // Remove from the cache STORE.removeItem(REGISTERED_TOKENS_KEY); From f884a85470abf8a269e6780c5dbb44df8dbe0e1a Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Thu, 30 May 2024 00:29:51 +0100 Subject: [PATCH 19/51] feat: add unregister nano contract saga effect [10] (#457) * feat(nc): implement unregister nano contract saga effect * feat: remove registered nano contracts on cleanTokens * feat: add unregisterNanoContract to HybridStore --- __tests__/sagas/nanoContracts/fixtures.js | 1 + .../nanoContracts/historyNanoContract.test.js | 8 +-- .../registerNanoContract.test.js | 1 + src/actions.js | 40 ++++++++++++ src/reducers/reducer.js | 61 +++++++++++++++++++ src/sagas/nanoContract.js | 32 +++++++++- src/store.js | 17 +++++- 7 files changed, 152 insertions(+), 8 deletions(-) diff --git a/__tests__/sagas/nanoContracts/fixtures.js b/__tests__/sagas/nanoContracts/fixtures.js index 0944f7716..fa6b6e4b4 100644 --- a/__tests__/sagas/nanoContracts/fixtures.js +++ b/__tests__/sagas/nanoContracts/fixtures.js @@ -81,6 +81,7 @@ export const fixtures = { ncState: { success: true, nc_id: '3cb032600bdf7db784800e4ea911b10676fa2f67591f82bb62628c234e771595', + blueprint_id: '000001342d3c5b858a4d4835baea93fcc683fa615ff5892bd044459621a0340a', blueprint_name: 'Bet', fields: { token_uid: { value: '00' }, diff --git a/__tests__/sagas/nanoContracts/historyNanoContract.test.js b/__tests__/sagas/nanoContracts/historyNanoContract.test.js index c7d3c277c..a234b0863 100644 --- a/__tests__/sagas/nanoContracts/historyNanoContract.test.js +++ b/__tests__/sagas/nanoContracts/historyNanoContract.test.js @@ -27,9 +27,7 @@ describe('sagas/nanoContract/fetchHistory', () => { // arrange wallet mock const mockedWallet = { getNetworkObject: jest.fn(), - storage: { - isAddressMine: jest.fn(), - }, + isAddressMine: jest.fn(), }; // arrange ncApi mock const mockedNcApi = jest.mocked(ncApi); @@ -115,10 +113,10 @@ describe('sagas/nanoContract/requestHistoryNanoContract', () => { // call effect to request history const gen = requestHistoryNanoContract(nanoContractHistoryRequest({ ncId })); - // select wallet + // select historyMeta gen.next(); // feed back historyMeta - gen.next({}); + gen.next({ [ncId]: { isLoading: false, after: null } }); // feed back wallet gen.next(fixtures.wallet.readyAndMine); // feed back isNanoContractRegistered diff --git a/__tests__/sagas/nanoContracts/registerNanoContract.test.js b/__tests__/sagas/nanoContracts/registerNanoContract.test.js index 2a2eb385d..a35e1571d 100644 --- a/__tests__/sagas/nanoContracts/registerNanoContract.test.js +++ b/__tests__/sagas/nanoContracts/registerNanoContract.test.js @@ -123,6 +123,7 @@ describe('sagas/nanoContract/registerNanoContract', () => { expect(actionResult.payload.action.payload.entryValue).toEqual(expect.objectContaining({ address, ncId, + blueprintId: expect.any(String), blueprintName: expect.any(String), })); // assert termination diff --git a/src/actions.js b/src/actions.js index 2347b759f..decb0476f 100644 --- a/src/actions.js +++ b/src/actions.js @@ -149,6 +149,12 @@ export const types = { NANOCONTRACT_HISTORY_SUCCESS: 'NANOCONTRACT_HISTORY_SUCCESS', /* It indicates a Nano Contract history failed to load. */ NANOCONTRACT_HISTORY_FAILURE: 'NANOCONTRACT_HISTORY_FAILURE', + /* It initiates an unregistration process of a Nano Contract. */ + NANOCONTRACT_UNREGISTER_REQUEST: 'NANOCONTRACT_UNREGISTER_REQUEST', + /* It signals a successful completion of unregistration process. */ + NANOCONTRACT_UNREGISTER_SUCCESS: 'NANOCONTRACT_UNREGISTER_SUCCESS', + /* It initiates a process to change the address on registered Nano Contract. */ + NANOCONTRACT_ADDRESS_CHANGE_REQUEST: 'NANOCONTRACT_ADDRESS_CHANGE_REQUEST', }; export const featureToggleInitialized = () => ({ @@ -1055,3 +1061,37 @@ export const nanoContractHistoryFailure = (payload) => ({ type: types.NANOCONTRACT_HISTORY_FAILURE, payload, }); + +/** + * Request unregistration of a Nano Contract by its key. + * @param {{ + * ncId: string, + * }} Nano Contract ID to unregister. + */ +export const nanoContractUnregisterRequest = (unregisterRequest) => ({ + type: types.NANOCONTRACT_UNREGISTER_REQUEST, + payload: unregisterRequest, +}); + +/** + * Unregistration of a Nano Contract has finished with success. + * @param {{ + * ncId: string, + * }} Nano Contract ID unregistered. + */ +export const nanoContractUnregisterSuccess = (unregistered) => ({ + type: types.NANOCONTRACT_UNREGISTER_SUCCESS, + payload: unregistered, +}); + +/** + * Request a change on the Nano Contract registered. + * @param {{ + * ncId: string; + * newAddress: string; + * }} changeAddressRequest + */ +export const nanoContractAddressChangeRequest = (changeAddressRequest) => ({ + type: types.NANOCONTRACT_ADDRESS_CHANGE_REQUEST, + payload: changeAddressRequest, +}); diff --git a/src/reducers/reducer.js b/src/reducers/reducer.js index f3537535d..16a7908d4 100644 --- a/src/reducers/reducer.js +++ b/src/reducers/reducer.js @@ -477,6 +477,10 @@ export const reducer = (state = initialState, action) => { return onNanoContractHistoryFailure(state, action); case types.NANOCONTRACT_HISTORY_SUCCESS: return onNanoContractHistorySuccess(state, action); + case types.NANOCONTRACT_UNREGISTER_SUCCESS: + return onNanoContractUnregisterSuccess(state, action); + case types.NANOCONTRACT_ADDRESS_CHANGE_REQUEST: + return onNanoContractAddressChangeRequest(state, action); default: return state; } @@ -1329,6 +1333,37 @@ export const onNanoContractRegisterSuccess = (state, { payload }) => ({ }, }); +/** + * @param {Object} state + * @param {{ + * payload: { + * ncId: string, + * } + * }} action + */ +export const onNanoContractUnregisterSuccess = (state, { payload }) => { + const { ncId } = payload; + + const newRegisteredContracts = { ...state.nanoContract.registered }; + delete newRegisteredContracts[ncId]; + + const newContractsHistory = { ...state.nanoContract.history }; + delete newContractsHistory[ncId]; + + const newContractsHistoryMeta = { ...state.nanoContract.historyMeta }; + delete newContractsHistoryMeta[ncId]; + + return ({ + ...state, + nanoContract: { + ...state.nanoContract, + registered: newRegisteredContracts, + history: newContractsHistory, + historyMeta: newContractsHistoryMeta, + }, + }); +}; + /** * @param {Object} state * @param {{ @@ -1407,3 +1442,29 @@ export const onNanoContractHistorySuccess = (state, { payload }) => ({ }, }, }); + +/** + * @param {Object} state + * @param {{ + * payload: { + * ncId: string; + * newAddress: string; + * } + * }} action + */ +export const onNanoContractAddressChangeRequest = (state, { payload }) => { + const newRegisteredNc = { + ...state.nanoContract.registered[payload.ncId], + address: payload.newAddress, + }; + return { + ...state, + nanoContract: { + ...state.nanoContract, + registered: { + ...state.nanoContract.registered, + [payload.ncId]: newRegisteredNc, + }, + }, + }; +}; diff --git a/src/sagas/nanoContract.js b/src/sagas/nanoContract.js index 6e9fd3e27..f4ec6a366 100644 --- a/src/sagas/nanoContract.js +++ b/src/sagas/nanoContract.js @@ -24,6 +24,7 @@ import { nanoContractHistorySuccess, nanoContractRegisterFailure, nanoContractRegisterSuccess, + nanoContractUnregisterSuccess, onExceptionCaptured, types, } from '../actions'; @@ -80,7 +81,8 @@ export function* registerNanoContract({ payload }) { let ncState = null; try { - ncState = yield call(ncApi.getNanoContractState.bind(ncApi), ncId); + const response = yield call([ncApi, ncApi.getNanoContractState], ncId); + ncState = response.ncState; } catch (error) { if (error instanceof NanoRequest404Error) { yield put(nanoContractRegisterFailure(failureMessage.nanoContractStateNotFound)); @@ -95,7 +97,7 @@ export function* registerNanoContract({ payload }) { address, ncId, blueprintId: ncState.blueprint_id, - blueprintName: ncState.blueprint_name + blueprintName: ncState.blueprint_name, }; yield call(wallet.storage.registerNanoContract.bind(wallet.storage), ncId, nc); @@ -254,9 +256,35 @@ export function* requestHistoryNanoContract({ payload }) { } } +/** + * Process Nano Contract unregister request. + * @param {{ + * payload: { + * ncId: string; + * } + * }} action with request payload. + */ +export function* unregisterNanoContract({ payload }) { + const { ncId } = payload; + + const wallet = yield select((state) => state.wallet); + if (!wallet.isReady()) { + log.error('Fail unregistering Nano Contract because wallet is not ready yet.'); + // This will show user an error modal with the option to send the error to sentry. + yield put(onExceptionCaptured(new Error(failureMessage.walletNotReadyError), true)); + return; + } + + yield call(wallet.storage.unregisterNanoContract.bind(wallet.storage), ncId); + + log.debug(`Success unregistering Nano Contract. ncId = ${ncId}`); + yield put(nanoContractUnregisterSuccess({ ncId })); +} + export function* saga() { yield all([ takeEvery(types.NANOCONTRACT_REGISTER_REQUEST, registerNanoContract), takeEvery(types.NANOCONTRACT_HISTORY_REQUEST, requestHistoryNanoContract), + takeEvery(types.NANOCONTRACT_UNREGISTER_REQUEST, unregisterNanoContract), ]); } diff --git a/src/store.js b/src/store.js index 937f65605..f39675617 100644 --- a/src/store.js +++ b/src/store.js @@ -145,7 +145,21 @@ class HybridStore extends MemoryStore { await super.registerNanoContract(ncId, ncValue); const contracts = STORE.getItem(REGISTERED_NANO_CONTRACTS_KEY) || {}; contracts[ncId] = ncValue; - STORE.setItem(REGISTERED_NANO_CONTRACTS_KEY, contracts) + STORE.setItem(REGISTERED_NANO_CONTRACTS_KEY, contracts); + } + + /** + * Unregister a nano contract. + * + * @param {string} ncId Nano Contract ID. + * @returns {Promise} + * @async + */ + async unregisterNanoContract(ncId) { + await super.unregisterNanoContract(ncId); + const contracts = STORE.getItem(REGISTERED_NANO_CONTRACTS_KEY) || {}; + delete contracts[ncId]; + STORE.setItem(REGISTERED_NANO_CONTRACTS_KEY, contracts); } /** @@ -161,6 +175,7 @@ class HybridStore extends MemoryStore { if (cleanTokens) { // Remove from the cache STORE.removeItem(REGISTERED_TOKENS_KEY); + STORE.removeItem(REGISTERED_NANO_CONTRACTS_KEY); } } } From 95eb49052c45953672fb55111a10612bf1ae53aa Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Tue, 11 Jun 2024 22:10:13 +0100 Subject: [PATCH 20/51] bump: 0.27.1 (#496) --- 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 762d930d3..b78b6c662 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 70 - versionName "0.27.0" + versionCode 71 + versionName "0.27.1" missingDimensionStrategy "react-native-camera", "general" } signingConfigs { diff --git a/ios/HathorMobile.xcodeproj/project.pbxproj b/ios/HathorMobile.xcodeproj/project.pbxproj index 1c671c37d..670370d97 100644 --- a/ios/HathorMobile.xcodeproj/project.pbxproj +++ b/ios/HathorMobile.xcodeproj/project.pbxproj @@ -521,7 +521,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 0.27.0; + MARKETING_VERSION = 0.27.1; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -550,7 +550,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 0.27.0; + MARKETING_VERSION = 0.27.1; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", diff --git a/package-lock.json b/package-lock.json index fa0db62fa..df6c6b6f3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "HathorMobile", - "version": "0.27.0", + "version": "0.27.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "HathorMobile", - "version": "0.27.0", + "version": "0.27.1", "hasInstallScript": true, "dependencies": { "@fortawesome/fontawesome-svg-core": "1.2.36", diff --git a/package.json b/package.json index dcdfd631f..b2be42b10 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "HathorMobile", - "version": "0.27.0", + "version": "0.27.1", "engines": { "node": ">=18.0.0", "npm": ">=9.0.0" From efbf1f269484d5f53fb9f42f591ff94840877082 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Wed, 12 Jun 2024 22:23:53 +0100 Subject: [PATCH 21/51] feat: NanoContractTransactions component [11] (#438) * feat: add nano contract screen * feat: add nano contract components * feat: improve NanoContractTransactionsList component * feat: add init effect on sagas/nanoContract * refactor: rename NanoContractTransactions* to NanoContractDetails* * refactor: use consumeAsyncIterator from utils * refactor: change oneline style to use unicode no-break space * refactor: extract getAllAddresses as utility fn * feat: add "offcard" prop to FeedbackContent component * refactor: move fetch all wallet addresses to a saga effect on sagas/mixins * refactor: move effects from mixins to wallet * refactor: remove mixins from sagas/index.js --- locale/da/texts.po | 153 +++++++++- locale/pt-br/texts.po | 157 ++++++++-- locale/ru-ru/texts.po | 153 +++++++++- locale/texts.pot | 152 +++++++++- src/App.js | 2 + src/actions.js | 67 +++++ src/components/FeedbackContent.js | 90 ++++++ .../EditAddressModal.component.js | 137 +++++++++ .../NanoContractDetails.component.js | 210 ++++++++++++++ .../NanoContractDetailsHeader.component.js | 267 ++++++++++++++++++ ...oContractTransactionsListItem.component.js | 124 ++++++++ .../NanoContract/NanoContractsList.js | 42 ++- .../NanoContract/NanoContractsListItem.js | 33 +-- .../NanoContract/NoNanoContracts.js | 62 ---- .../SelectAddressModal.component.js | 233 +++++++++++++++ .../UnregisterNanoContractModal.component.js | 69 +++++ src/components/TextValue.js | 17 +- src/reducers/reducer.js | 102 ++++++- src/sagas/helpers.js | 11 + src/sagas/nanoContract.js | 75 ++++- src/sagas/wallet.js | 34 ++- .../NanoContract/NanoContractDetailsScreen.js | 60 ++++ src/store.js | 29 ++ src/utils.js | 41 +++ 24 files changed, 2143 insertions(+), 177 deletions(-) create mode 100644 src/components/FeedbackContent.js create mode 100644 src/components/NanoContract/EditAddressModal.component.js create mode 100644 src/components/NanoContract/NanoContractDetails.component.js create mode 100644 src/components/NanoContract/NanoContractDetailsHeader.component.js create mode 100644 src/components/NanoContract/NanoContractTransactionsListItem.component.js delete mode 100644 src/components/NanoContract/NoNanoContracts.js create mode 100644 src/components/NanoContract/SelectAddressModal.component.js create mode 100644 src/components/NanoContract/UnregisterNanoContractModal.component.js create mode 100644 src/screens/NanoContract/NanoContractDetailsScreen.js diff --git a/locale/da/texts.po b/locale/da/texts.po index c42056faa..78f9a492b 100644 --- a/locale/da/texts.po +++ b/locale/da/texts.po @@ -57,7 +57,7 @@ msgid "[Last] dddd [•] HH:mm" msgstr "[Sidste] dddd [•] HH:mm" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:107 +#: src/models.js:107 src/utils.js:415 msgid "DD MMM YYYY [•] HH:mm" msgstr "DD MMM YYYY [•] HH:mm" @@ -341,7 +341,7 @@ msgid "Tokens" msgstr "Tokens" #. Only show the toggle button when Nano Contract is enabled to the wallet -#: src/components/NanoContract/NanoContractsList.js:71 +#: src/components/NanoContract/NanoContractsList.js:75 #: src/screens/Dashboard.js:131 msgid "Nano Contracts" msgstr "" @@ -432,6 +432,7 @@ msgstr "Ord" msgid "Enter your seed words separated by space" msgstr "Indtast dine seed-ord adskilt med mellemrum" +#: src/components/NanoContract/NanoContractDetails.component.js:194 #: src/screens/LoadHistoryScreen.js:51 src/screens/LoadWalletErrorScreen.js:20 msgid "Try again" msgstr "" @@ -684,11 +685,11 @@ msgstr "SEND ${ tokenNameUpperCase }" msgid "Your transfer is being processed" msgstr "Din overførsel behandles" -#: src/sagas/helpers.js:139 src/screens/SendConfirmScreen.js:104 +#: src/sagas/helpers.js:140 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:140 src/screens/SendConfirmScreen.js:105 +#: src/sagas/helpers.js:141 src/screens/SendConfirmScreen.js:105 msgid "Authorize operation" msgstr "Autoriserer overførslen" @@ -696,6 +697,8 @@ msgstr "Autoriserer overførslen" msgid "Your transfer of **${ this.amountAndToken }** has been confirmed" msgstr "Din overførsel af **${ _this.amountAndToken }** er bekræftet" +#: src/components/NanoContract/EditAddressModal.component.js:60 +#: src/components/NanoContract/SelectAddressModal.component.js:117 #: src/screens/SendConfirmScreen.js:161 msgid "Address" msgstr "Adresse" @@ -924,32 +927,45 @@ msgid "" "this can potentially make you susceptible to fraudulent schemes." msgstr "" -#: src/sagas/nanoContract.js:36 +#: src/screens/NanoContract/NanoContractDetailsScreen.js:39 +msgid "Nano Contract Details" +msgstr "" + +#: src/sagas/mixins.js:31 +msgid "Wallet is not ready to load addresses." +msgstr "" + +#. This will show the message in the feedback content at SelectAddressModal +#: src/sagas/mixins.js:47 +msgid "There was an error while loading wallet addresses. Try again." +msgstr "" + +#: src/sagas/nanoContract.js:43 msgid "Nano Contract already registered." msgstr "" -#: src/sagas/nanoContract.js:37 +#: src/sagas/nanoContract.js:44 msgid "Wallet is not ready yet to register a Nano Contract." msgstr "" -#: src/sagas/nanoContract.js:38 +#: src/sagas/nanoContract.js:45 msgid "The informed address does not belong to the wallet." msgstr "" -#: src/sagas/nanoContract.js:39 +#: src/sagas/nanoContract.js:46 msgid "Nano Contract not found." msgstr "" -#: src/sagas/nanoContract.js:40 +#: src/sagas/nanoContract.js:47 msgid "Error while trying to get Nano Contract state." msgstr "" -#: src/sagas/nanoContract.js:41 +#: src/sagas/nanoContract.js:48 msgid "Nano Contract not registered." msgstr "" -#: src/sagas/nanoContract.js:42 -msgid "Error while trying to fetch Nano Contract history." +#: src/sagas/nanoContract.js:49 +msgid "Error while trying to download Nano Contract transactions history." msgstr "" #: src/sagas/networkSettings.js:91 @@ -1097,7 +1113,7 @@ msgstr "Ingen internetforbindelse" msgid "Public Explorer" msgstr "Public Explorer" -#: src/components/PushTxDetailsModal.js:76 src/components/TxDetailsModal.js:101 +#: src/components/PushTxDetailsModal.js:76 src/components/TxDetailsModal.js:103 msgid "Date & Time" msgstr "Dato & Tid" @@ -1170,6 +1186,8 @@ msgstr "" msgid "Blueprint Method" msgstr "" +#: src/components/NanoContract/NanoContractDetailsHeader.component.js:142 +#: src/components/NanoContract/NanoContractsListItem.js:57 #: src/components/TxDetailsModal.js:106 msgid "Nano Contract ID" msgstr "" @@ -1182,6 +1200,7 @@ msgstr "" msgid "Nano Contract Status" msgstr "" +#: src/components/NanoContract/NanoContractDetailsHeader.component.js:79 #: src/components/TxDetailsModal.js:120 msgid "Nano Contract" msgstr "" @@ -1219,11 +1238,75 @@ msgstr "" msgid "Custom network" msgstr "" -#: src/components/NanoContract/NoNanoContracts.js:16 +#: src/components/NanoContract/EditAddressModal.component.js:41 +msgid "New Nano Contract Address" +msgstr "" + +#: src/components/NanoContract/EditAddressModal.component.js:49 +msgid "" +"This address signs any transaction you create with Nano Contracts method. " +"Switching to a new one means" +msgstr "" + +#: src/components/NanoContract/EditAddressModal.component.js:51 +msgid "all future transactions will use this address." +msgstr "" + +#: src/components/NanoContract/EditAddressModal.component.js:57 +msgid "Selected Information" +msgstr "" + +#: src/components/NanoContract/EditAddressModal.component.js:67 +msgid "Index" +msgstr "" + +#: src/components/NanoContract/EditAddressModal.component.js:74 +msgid "Confirm new address" +msgstr "" + +#: src/components/NanoContract/EditAddressModal.component.js:78 +msgid "Go back" +msgstr "" + +#: src/components/NanoContract/NanoContractDetails.component.js:158 +#: src/components/NanoContract/SelectAddressModal.component.js:105 +msgid "Loading" +msgstr "" + +#: src/components/NanoContract/NanoContractDetails.component.js:159 +msgid "Loading Nano Contract transactions." +msgstr "" + +#: src/components/NanoContract/NanoContractDetails.component.js:173 +msgid "Nano Contract Transactions Error" +msgstr "" + +#: src/components/NanoContract/NanoContractDetailsHeader.component.js:146 +#: src/components/NanoContract/NanoContractsListItem.js:59 +msgid "Blueprint Name" +msgstr "" + +#: src/components/NanoContract/NanoContractDetailsHeader.component.js:150 +msgid "Registered Address" +msgstr "" + +#: src/components/NanoContract/NanoContractDetailsHeader.component.js:153 +msgid "See status details" +msgstr "" + +#: src/components/NanoContract/NanoContractDetailsHeader.component.js:154 +msgid "Unregister contract" +msgstr "" + +#: src/components/NanoContract/NanoContractTransactionsListItem.component.js:62 +msgid "From this wallet" +msgstr "" + +#: src/components/NanoContract/NanoContractsList.js:85 msgid "No Nano Contracts" msgstr "" -#: src/components/NanoContract/NoNanoContracts.js:18 +#: src/components/NanoContract/NanoContractsList.js:86 msgid "" "You can keep track of your registered Nano Contracts here once you have " "registered them." @@ -1232,3 +1315,43 @@ msgstr "" #: src/components/NanoContract/RegisterNewNanoContractButton.js:22 msgid "Register new" msgstr "" + +#: src/components/NanoContract/SelectAddressModal.component.js:90 +msgid "Choose New Wallet Address" +msgstr "" + +#: src/components/NanoContract/SelectAddressModal.component.js:97 +msgid "Load Addresses Error" +msgstr "" + +#: src/components/NanoContract/SelectAddressModal.component.js:106 +msgid "Loading wallet addresses." +msgstr "" + +#: src/components/NanoContract/SelectAddressModal.component.js:114 +msgid "Current Information" +msgstr "" + +#: src/components/NanoContract/SelectAddressModal.component.js:115 +msgid "To change, select other address on the list below." +msgstr "" + +#: src/components/NanoContract/SelectAddressModal.component.js:178 +msgid "index" +msgstr "" + +#: src/components/NanoContract/UnregisterNanoContractModal.component.js:39 +msgid "Unregister Nano Contract" +msgstr "" + +#: src/components/NanoContract/UnregisterNanoContractModal.component.js:41 +msgid "Are you sure you want to unregister this Nano Contract?" +msgstr "" + +#: src/components/NanoContract/UnregisterNanoContractModal.component.js:44 +msgid "Yes, unregister contract" +msgstr "" + +#: src/components/NanoContract/UnregisterNanoContractModal.component.js:50 +msgid "No, go back" +msgstr "" diff --git a/locale/pt-br/texts.po b/locale/pt-br/texts.po index c502c4226..538ee763a 100644 --- a/locale/pt-br/texts.po +++ b/locale/pt-br/texts.po @@ -57,7 +57,7 @@ msgid "[Last] dddd [•] HH:mm" msgstr "[Última] dddd [•] HH:mm" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:107 +#: src/models.js:107 src/utils.js:415 msgid "DD MMM YYYY [•] HH:mm" msgstr "DD MMM YYYY [•] HH:mm" @@ -350,7 +350,7 @@ msgid "Tokens" msgstr "Tokens" #. Only show the toggle button when Nano Contract is enabled to the wallet -#: src/components/NanoContract/NanoContractsList.js:71 +#: src/components/NanoContract/NanoContractsList.js:75 #: src/screens/Dashboard.js:131 msgid "Nano Contracts" msgstr "Nano Contracts" @@ -443,6 +443,7 @@ msgstr "Palavras" msgid "Enter your seed words separated by space" msgstr "Digite suas palavras separadas por espaços" +#: src/components/NanoContract/NanoContractDetails.component.js:194 #: src/screens/LoadHistoryScreen.js:51 src/screens/LoadWalletErrorScreen.js:20 msgid "Try again" msgstr "Tente novamente" @@ -700,11 +701,11 @@ msgstr "ENVIAR ${ tokenNameUpperCase }" msgid "Your transfer is being processed" msgstr "Sua transferência está sendo processada" -#: src/sagas/helpers.js:139 src/screens/SendConfirmScreen.js:104 +#: src/sagas/helpers.js:140 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:140 src/screens/SendConfirmScreen.js:105 +#: src/sagas/helpers.js:141 src/screens/SendConfirmScreen.js:105 msgid "Authorize operation" msgstr "Autorizar operação" @@ -712,6 +713,8 @@ msgstr "Autorizar operação" msgid "Your transfer of **${ this.amountAndToken }** has been confirmed" msgstr "Sua transferência de **${ this.amountAndToken }** foi confirmada" +#: src/components/NanoContract/EditAddressModal.component.js:60 +#: src/components/NanoContract/SelectAddressModal.component.js:117 #: src/screens/SendConfirmScreen.js:161 msgid "Address" msgstr "Endereço" @@ -950,32 +953,45 @@ 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/nanoContract.js:36 +#: src/screens/NanoContract/NanoContractDetailsScreen.js:39 +msgid "Nano Contract Details" +msgstr "Detalhes do Nano Contract" + +#: src/sagas/mixins.js:31 +msgid "Wallet is not ready to load addresses." +msgstr "A wallet não está pronta para carregar endereços." + +#. This will show the message in the feedback content at SelectAddressModal +#: src/sagas/mixins.js:47 +msgid "There was an error while loading wallet addresses. Try again." +msgstr "Ocorreu um erro durante download dos endereços da wallet. Tente novamente." + +#: src/sagas/nanoContract.js:43 msgid "Nano Contract already registered." msgstr "Nano Contract já registrado." -#: src/sagas/nanoContract.js:37 +#: src/sagas/nanoContract.js:44 msgid "Wallet is not ready yet to register a Nano Contract." msgstr "A wallet não está pronta ainda para registrar Nano Contracts." -#: src/sagas/nanoContract.js:38 +#: src/sagas/nanoContract.js:45 msgid "The informed address does not belong to the wallet." msgstr "O endereço informado não pertence a esta carteira." -#: src/sagas/nanoContract.js:39 +#: src/sagas/nanoContract.js:46 msgid "Nano Contract not found." msgstr "Nano Contract não encontrado" -#: src/sagas/nanoContract.js:40 +#: src/sagas/nanoContract.js:47 msgid "Error while trying to get Nano Contract state." msgstr "Erro ao obter o estado do Nano Contract." -#: src/sagas/nanoContract.js:41 +#: src/sagas/nanoContract.js:48 msgid "Nano Contract not registered." msgstr "Nano Contract não registrado." -#: src/sagas/nanoContract.js:42 -msgid "Error while trying to fetch Nano Contract history." +#: src/sagas/nanoContract.js:49 +msgid "Error while trying to download Nano Contract transactions history." msgstr "Error ao fazer download do histórico de transações do Nano Contract." #: src/sagas/networkSettings.js:91 @@ -1127,7 +1143,7 @@ msgstr "Sem conexão com a internet" msgid "Public Explorer" msgstr "Explorer Público" -#: src/components/PushTxDetailsModal.js:76 src/components/TxDetailsModal.js:101 +#: src/components/PushTxDetailsModal.js:76 src/components/TxDetailsModal.js:103 msgid "Date & Time" msgstr "Data & Hora" @@ -1199,6 +1215,8 @@ msgstr "ID da Transação" msgid "Blueprint Method" msgstr "Método do Blueprint" +#: src/components/NanoContract/NanoContractDetailsHeader.component.js:142 +#: src/components/NanoContract/NanoContractsListItem.js:57 #: src/components/TxDetailsModal.js:106 msgid "Nano Contract ID" msgstr "ID do Nano Contract" @@ -1211,6 +1229,7 @@ msgstr "Endereço de assinatura do Nano Contract" msgid "Nano Contract Status" msgstr "Status do Nano Contract" +#: src/components/NanoContract/NanoContractDetailsHeader.component.js:79 #: src/components/TxDetailsModal.js:120 msgid "Nano Contract" msgstr "Nano Contract" @@ -1255,11 +1274,77 @@ msgstr "" msgid "Custom network" msgstr "Rede personalizada" -#: src/components/NanoContract/NoNanoContracts.js:16 +#: src/components/NanoContract/EditAddressModal.component.js:41 +msgid "New Nano Contract Address" +msgstr "Novo endereço para o Nano Contract" + +#: src/components/NanoContract/EditAddressModal.component.js:49 +msgid "" +"This address signs any transaction you create with Nano Contracts method. " +"Switching to a new one means" +msgstr "" +"Este endereço assina toda transação criada com os métodos do Nano Contract. " +"Ao alterar para um novo endereço significa que" + +#: src/components/NanoContract/EditAddressModal.component.js:51 +msgid "all future transactions will use this address." +msgstr "todas as transações futuras usarão este endereço." + +#: src/components/NanoContract/EditAddressModal.component.js:57 +msgid "Selected Information" +msgstr "Informação Selecionada" + +#: src/components/NanoContract/EditAddressModal.component.js:67 +msgid "Index" +msgstr "Índice" + +#: src/components/NanoContract/EditAddressModal.component.js:74 +msgid "Confirm new address" +msgstr "Confirmar novo endereço" + +#: src/components/NanoContract/EditAddressModal.component.js:78 +msgid "Go back" +msgstr "Voltar" + +#: src/components/NanoContract/NanoContractDetails.component.js:158 +#: src/components/NanoContract/SelectAddressModal.component.js:105 +msgid "Loading" +msgstr "Carregando" + +#: src/components/NanoContract/NanoContractDetails.component.js:159 +msgid "Loading Nano Contract transactions." +msgstr "Carregando transações do Nano Contract" + +#: src/components/NanoContract/NanoContractDetails.component.js:173 +msgid "Nano Contract Transactions Error" +msgstr "Erro em Transações do Nano Contract" + +#: src/components/NanoContract/NanoContractDetailsHeader.component.js:146 +#: src/components/NanoContract/NanoContractsListItem.js:59 +msgid "Blueprint Name" +msgstr "Nome do Blueprint" + +#: src/components/NanoContract/NanoContractDetailsHeader.component.js:150 +msgid "Registered Address" +msgstr "Endereço Registrado" + +#: src/components/NanoContract/NanoContractDetailsHeader.component.js:153 +msgid "See status details" +msgstr "Ver detalhes" + +#: src/components/NanoContract/NanoContractDetailsHeader.component.js:154 +msgid "Unregister contract" +msgstr "Desregistrar contract" + +#: src/components/NanoContract/NanoContractTransactionsListItem.component.js:62 +msgid "From this wallet" +msgstr "Desta wallet" + +#: src/components/NanoContract/NanoContractsList.js:85 msgid "No Nano Contracts" msgstr "Nenhum Nano Contract" -#: src/components/NanoContract/NoNanoContracts.js:18 +#: src/components/NanoContract/NanoContractsList.js:86 msgid "" "You can keep track of your registered Nano Contracts here once you have " "registered them." @@ -1267,4 +1352,44 @@ msgstr "Você pode acompanhar os Nano Contracts registrados nesta tela." #: src/components/NanoContract/RegisterNewNanoContractButton.js:22 msgid "Register new" -msgstr "Registrar Nano Contract" +msgstr "Registrar novo" + +#: src/components/NanoContract/SelectAddressModal.component.js:90 +msgid "Choose New Wallet Address" +msgstr "Escolha o Novo Endereço" + +#: src/components/NanoContract/SelectAddressModal.component.js:97 +msgid "Load Addresses Error" +msgstr "Erro ao carregar endereços" + +#: src/components/NanoContract/SelectAddressModal.component.js:106 +msgid "Loading wallet addresses." +msgstr "Carregando endereços da wallet." + +#: src/components/NanoContract/SelectAddressModal.component.js:114 +msgid "Current Information" +msgstr "Informação Atual" + +#: src/components/NanoContract/SelectAddressModal.component.js:115 +msgid "To change, select other address on the list below." +msgstr "Para alterar, selecione outro endereço da lista abaixo." + +#: src/components/NanoContract/SelectAddressModal.component.js:178 +msgid "index" +msgstr "índice" + +#: src/components/NanoContract/UnregisterNanoContractModal.component.js:39 +msgid "Unregister Nano Contract" +msgstr "Desregistrar Nano Contract" + +#: src/components/NanoContract/UnregisterNanoContractModal.component.js:41 +msgid "Are you sure you want to unregister this Nano Contract?" +msgstr "Confirma que deseja desregistrar este Nano Contract?" + +#: src/components/NanoContract/UnregisterNanoContractModal.component.js:44 +msgid "Yes, unregister contract" +msgstr "Sim, desregistrar" + +#: src/components/NanoContract/UnregisterNanoContractModal.component.js:50 +msgid "No, go back" +msgstr "Não, voltar" diff --git a/locale/ru-ru/texts.po b/locale/ru-ru/texts.po index ae00bf6ee..8ee6e2dcd 100644 --- a/locale/ru-ru/texts.po +++ b/locale/ru-ru/texts.po @@ -58,7 +58,7 @@ msgid "[Last] dddd [•] HH:mm" msgstr "[Последний] dddd [•] HH:mm" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:107 +#: src/models.js:107 src/utils.js:415 msgid "DD MMM YYYY [•] HH:mm" msgstr "DD MMM YYYY [•] HH:mm" @@ -342,7 +342,7 @@ msgid "Tokens" msgstr "ТокенЫ" #. Only show the toggle button when Nano Contract is enabled to the wallet -#: src/components/NanoContract/NanoContractsList.js:71 +#: src/components/NanoContract/NanoContractsList.js:75 #: src/screens/Dashboard.js:131 msgid "Nano Contracts" msgstr "" @@ -433,6 +433,7 @@ msgstr "Слова" msgid "Enter your seed words separated by space" msgstr "Введите seed-фразу" +#: src/components/NanoContract/NanoContractDetails.component.js:194 #: src/screens/LoadHistoryScreen.js:51 src/screens/LoadWalletErrorScreen.js:20 msgid "Try again" msgstr "" @@ -686,11 +687,11 @@ msgstr "ОТПРАВИТЬ ${ tokenNameUpperCase }" msgid "Your transfer is being processed" msgstr "Ваш перевод обрабатывается" -#: src/sagas/helpers.js:139 src/screens/SendConfirmScreen.js:104 +#: src/sagas/helpers.js:140 src/screens/SendConfirmScreen.js:104 msgid "Enter your 6-digit pin to authorize operation" msgstr "Введите 6-значный PIN-код для авторизации операции" -#: src/sagas/helpers.js:140 src/screens/SendConfirmScreen.js:105 +#: src/sagas/helpers.js:141 src/screens/SendConfirmScreen.js:105 msgid "Authorize operation" msgstr "Авторизовать операцию" @@ -698,6 +699,8 @@ msgstr "Авторизовать операцию" msgid "Your transfer of **${ this.amountAndToken }** has been confirmed" msgstr "Ваш перевод **${ this.amountAndToken }** был подтвержден" +#: src/components/NanoContract/EditAddressModal.component.js:60 +#: src/components/NanoContract/SelectAddressModal.component.js:117 #: src/screens/SendConfirmScreen.js:161 msgid "Address" msgstr "Адрес" @@ -928,32 +931,45 @@ msgid "" "this can potentially make you susceptible to fraudulent schemes." msgstr "" -#: src/sagas/nanoContract.js:36 +#: src/screens/NanoContract/NanoContractDetailsScreen.js:39 +msgid "Nano Contract Details" +msgstr "" + +#: src/sagas/mixins.js:31 +msgid "Wallet is not ready to load addresses." +msgstr "" + +#. This will show the message in the feedback content at SelectAddressModal +#: src/sagas/mixins.js:47 +msgid "There was an error while loading wallet addresses. Try again." +msgstr "" + +#: src/sagas/nanoContract.js:43 msgid "Nano Contract already registered." msgstr "" -#: src/sagas/nanoContract.js:37 +#: src/sagas/nanoContract.js:44 msgid "Wallet is not ready yet to register a Nano Contract." msgstr "" -#: src/sagas/nanoContract.js:38 +#: src/sagas/nanoContract.js:45 msgid "The informed address does not belong to the wallet." msgstr "" -#: src/sagas/nanoContract.js:39 +#: src/sagas/nanoContract.js:46 msgid "Nano Contract not found." msgstr "" -#: src/sagas/nanoContract.js:40 +#: src/sagas/nanoContract.js:47 msgid "Error while trying to get Nano Contract state." msgstr "" -#: src/sagas/nanoContract.js:41 +#: src/sagas/nanoContract.js:48 msgid "Nano Contract not registered." msgstr "" -#: src/sagas/nanoContract.js:42 -msgid "Error while trying to fetch Nano Contract history." +#: src/sagas/nanoContract.js:49 +msgid "Error while trying to download Nano Contract transactions history." msgstr "" #: src/sagas/networkSettings.js:91 @@ -1087,7 +1103,7 @@ msgstr "Нет соединения с интернетом" msgid "Public Explorer" msgstr "Public Explorer" -#: src/components/PushTxDetailsModal.js:76 src/components/TxDetailsModal.js:101 +#: src/components/PushTxDetailsModal.js:76 src/components/TxDetailsModal.js:103 msgid "Date & Time" msgstr "Дата и Время" @@ -1159,6 +1175,8 @@ msgstr "" msgid "Blueprint Method" msgstr "" +#: src/components/NanoContract/NanoContractDetailsHeader.component.js:142 +#: src/components/NanoContract/NanoContractsListItem.js:57 #: src/components/TxDetailsModal.js:106 msgid "Nano Contract ID" msgstr "" @@ -1171,6 +1189,7 @@ msgstr "" msgid "Nano Contract Status" msgstr "" +#: src/components/NanoContract/NanoContractDetailsHeader.component.js:79 #: src/components/TxDetailsModal.js:120 msgid "Nano Contract" msgstr "" @@ -1208,11 +1227,75 @@ msgstr "" msgid "Custom network" msgstr "" -#: src/components/NanoContract/NoNanoContracts.js:16 +#: src/components/NanoContract/EditAddressModal.component.js:41 +msgid "New Nano Contract Address" +msgstr "" + +#: src/components/NanoContract/EditAddressModal.component.js:49 +msgid "" +"This address signs any transaction you create with Nano Contracts method. " +"Switching to a new one means" +msgstr "" + +#: src/components/NanoContract/EditAddressModal.component.js:51 +msgid "all future transactions will use this address." +msgstr "" + +#: src/components/NanoContract/EditAddressModal.component.js:57 +msgid "Selected Information" +msgstr "" + +#: src/components/NanoContract/EditAddressModal.component.js:67 +msgid "Index" +msgstr "" + +#: src/components/NanoContract/EditAddressModal.component.js:74 +msgid "Confirm new address" +msgstr "" + +#: src/components/NanoContract/EditAddressModal.component.js:78 +msgid "Go back" +msgstr "" + +#: src/components/NanoContract/NanoContractDetails.component.js:158 +#: src/components/NanoContract/SelectAddressModal.component.js:105 +msgid "Loading" +msgstr "" + +#: src/components/NanoContract/NanoContractDetails.component.js:159 +msgid "Loading Nano Contract transactions." +msgstr "" + +#: src/components/NanoContract/NanoContractDetails.component.js:173 +msgid "Nano Contract Transactions Error" +msgstr "" + +#: src/components/NanoContract/NanoContractDetailsHeader.component.js:146 +#: src/components/NanoContract/NanoContractsListItem.js:59 +msgid "Blueprint Name" +msgstr "" + +#: src/components/NanoContract/NanoContractDetailsHeader.component.js:150 +msgid "Registered Address" +msgstr "" + +#: src/components/NanoContract/NanoContractDetailsHeader.component.js:153 +msgid "See status details" +msgstr "" + +#: src/components/NanoContract/NanoContractDetailsHeader.component.js:154 +msgid "Unregister contract" +msgstr "" + +#: src/components/NanoContract/NanoContractTransactionsListItem.component.js:62 +msgid "From this wallet" +msgstr "" + +#: src/components/NanoContract/NanoContractsList.js:85 msgid "No Nano Contracts" msgstr "" -#: src/components/NanoContract/NoNanoContracts.js:18 +#: src/components/NanoContract/NanoContractsList.js:86 msgid "" "You can keep track of your registered Nano Contracts here once you have " "registered them." @@ -1221,3 +1304,43 @@ msgstr "" #: src/components/NanoContract/RegisterNewNanoContractButton.js:22 msgid "Register new" msgstr "" + +#: src/components/NanoContract/SelectAddressModal.component.js:90 +msgid "Choose New Wallet Address" +msgstr "" + +#: src/components/NanoContract/SelectAddressModal.component.js:97 +msgid "Load Addresses Error" +msgstr "" + +#: src/components/NanoContract/SelectAddressModal.component.js:106 +msgid "Loading wallet addresses." +msgstr "" + +#: src/components/NanoContract/SelectAddressModal.component.js:114 +msgid "Current Information" +msgstr "" + +#: src/components/NanoContract/SelectAddressModal.component.js:115 +msgid "To change, select other address on the list below." +msgstr "" + +#: src/components/NanoContract/SelectAddressModal.component.js:178 +msgid "index" +msgstr "" + +#: src/components/NanoContract/UnregisterNanoContractModal.component.js:39 +msgid "Unregister Nano Contract" +msgstr "" + +#: src/components/NanoContract/UnregisterNanoContractModal.component.js:41 +msgid "Are you sure you want to unregister this Nano Contract?" +msgstr "" + +#: src/components/NanoContract/UnregisterNanoContractModal.component.js:44 +msgid "Yes, unregister contract" +msgstr "" + +#: src/components/NanoContract/UnregisterNanoContractModal.component.js:50 +msgid "No, go back" +msgstr "" diff --git a/locale/texts.pot b/locale/texts.pot index 2cb15ab4b..7a93a0d41 100644 --- a/locale/texts.pot +++ b/locale/texts.pot @@ -48,6 +48,7 @@ msgid "[Last] dddd [•] HH:mm" msgstr "" #: src/models.js:107 +#: src/utils.js:415 #. See https://momentjs.com/docs/#/displaying/calendar-time/ msgid "DD MMM YYYY [•] HH:mm" msgstr "" @@ -334,7 +335,7 @@ msgstr "" msgid "Tokens" msgstr "" -#: src/components/NanoContract/NanoContractsList.js:71 +#: src/components/NanoContract/NanoContractsList.js:75 #: src/screens/Dashboard.js:131 #. Only show the toggle button when Nano Contract is enabled to the wallet msgid "Nano Contracts" @@ -420,6 +421,7 @@ msgstr "" msgid "Enter your seed words separated by space" msgstr "" +#: src/components/NanoContract/NanoContractDetails.component.js:194 #: src/screens/LoadHistoryScreen.js:51 #: src/screens/LoadWalletErrorScreen.js:20 msgid "Try again" @@ -679,12 +681,12 @@ msgstr "" msgid "Your transfer is being processed" msgstr "" -#: src/sagas/helpers.js:139 +#: src/sagas/helpers.js:140 #: src/screens/SendConfirmScreen.js:104 msgid "Enter your 6-digit pin to authorize operation" msgstr "" -#: src/sagas/helpers.js:140 +#: src/sagas/helpers.js:141 #: src/screens/SendConfirmScreen.js:105 msgid "Authorize operation" msgstr "" @@ -693,6 +695,8 @@ msgstr "" msgid "Your transfer of **${ this.amountAndToken }** has been confirmed" msgstr "" +#: src/components/NanoContract/EditAddressModal.component.js:60 +#: src/components/NanoContract/SelectAddressModal.component.js:117 #: src/screens/SendConfirmScreen.js:161 msgid "Address" msgstr "" @@ -920,32 +924,45 @@ msgid "" "this can potentially make you susceptible to fraudulent schemes." msgstr "" -#: src/sagas/nanoContract.js:36 +#: src/screens/NanoContract/NanoContractDetailsScreen.js:39 +msgid "Nano Contract Details" +msgstr "" + +#: src/sagas/mixins.js:31 +msgid "Wallet is not ready to load addresses." +msgstr "" + +#: src/sagas/mixins.js:47 +#. This will show the message in the feedback content at SelectAddressModal +msgid "There was an error while loading wallet addresses. Try again." +msgstr "" + +#: src/sagas/nanoContract.js:43 msgid "Nano Contract already registered." msgstr "" -#: src/sagas/nanoContract.js:37 +#: src/sagas/nanoContract.js:44 msgid "Wallet is not ready yet to register a Nano Contract." msgstr "" -#: src/sagas/nanoContract.js:38 +#: src/sagas/nanoContract.js:45 msgid "The informed address does not belong to the wallet." msgstr "" -#: src/sagas/nanoContract.js:39 +#: src/sagas/nanoContract.js:46 msgid "Nano Contract not found." msgstr "" -#: src/sagas/nanoContract.js:40 +#: src/sagas/nanoContract.js:47 msgid "Error while trying to get Nano Contract state." msgstr "" -#: src/sagas/nanoContract.js:41 +#: src/sagas/nanoContract.js:48 msgid "Nano Contract not registered." msgstr "" -#: src/sagas/nanoContract.js:42 -msgid "Error while trying to fetch Nano Contract history." +#: src/sagas/nanoContract.js:49 +msgid "Error while trying to download Nano Contract transactions history." msgstr "" #: src/sagas/networkSettings.js:91 @@ -1080,7 +1097,7 @@ msgid "Public Explorer" msgstr "" #: src/components/PushTxDetailsModal.js:76 -#: src/components/TxDetailsModal.js:101 +#: src/components/TxDetailsModal.js:103 msgid "Date & Time" msgstr "" @@ -1151,6 +1168,8 @@ msgstr "" msgid "Blueprint Method" msgstr "" +#: src/components/NanoContract/NanoContractDetailsHeader.component.js:142 +#: src/components/NanoContract/NanoContractsListItem.js:57 #: src/components/TxDetailsModal.js:106 msgid "Nano Contract ID" msgstr "" @@ -1163,6 +1182,7 @@ msgstr "" msgid "Nano Contract Status" msgstr "" +#: src/components/NanoContract/NanoContractDetailsHeader.component.js:79 #: src/components/TxDetailsModal.js:120 msgid "Nano Contract" msgstr "" @@ -1200,11 +1220,75 @@ msgstr "" msgid "Custom network" msgstr "" -#: src/components/NanoContract/NoNanoContracts.js:16 +#: src/components/NanoContract/EditAddressModal.component.js:41 +msgid "New Nano Contract Address" +msgstr "" + +#: src/components/NanoContract/EditAddressModal.component.js:49 +msgid "" +"This address signs any transaction you create with Nano Contracts method. " +"Switching to a new one means" +msgstr "" + +#: src/components/NanoContract/EditAddressModal.component.js:51 +msgid "all future transactions will use this address." +msgstr "" + +#: src/components/NanoContract/EditAddressModal.component.js:57 +msgid "Selected Information" +msgstr "" + +#: src/components/NanoContract/EditAddressModal.component.js:67 +msgid "Index" +msgstr "" + +#: src/components/NanoContract/EditAddressModal.component.js:74 +msgid "Confirm new address" +msgstr "" + +#: src/components/NanoContract/EditAddressModal.component.js:78 +msgid "Go back" +msgstr "" + +#: src/components/NanoContract/NanoContractDetails.component.js:158 +#: src/components/NanoContract/SelectAddressModal.component.js:105 +msgid "Loading" +msgstr "" + +#: src/components/NanoContract/NanoContractDetails.component.js:159 +msgid "Loading Nano Contract transactions." +msgstr "" + +#: src/components/NanoContract/NanoContractDetails.component.js:173 +msgid "Nano Contract Transactions Error" +msgstr "" + +#: src/components/NanoContract/NanoContractDetailsHeader.component.js:146 +#: src/components/NanoContract/NanoContractsListItem.js:59 +msgid "Blueprint Name" +msgstr "" + +#: src/components/NanoContract/NanoContractDetailsHeader.component.js:150 +msgid "Registered Address" +msgstr "" + +#: src/components/NanoContract/NanoContractDetailsHeader.component.js:153 +msgid "See status details" +msgstr "" + +#: src/components/NanoContract/NanoContractDetailsHeader.component.js:154 +msgid "Unregister contract" +msgstr "" + +#: src/components/NanoContract/NanoContractTransactionsListItem.component.js:62 +msgid "From this wallet" +msgstr "" + +#: src/components/NanoContract/NanoContractsList.js:85 msgid "No Nano Contracts" msgstr "" -#: src/components/NanoContract/NoNanoContracts.js:18 +#: src/components/NanoContract/NanoContractsList.js:86 msgid "" "You can keep track of your registered Nano Contracts here once you have " "registered them." @@ -1213,3 +1297,43 @@ msgstr "" #: src/components/NanoContract/RegisterNewNanoContractButton.js:22 msgid "Register new" msgstr "" + +#: src/components/NanoContract/SelectAddressModal.component.js:90 +msgid "Choose New Wallet Address" +msgstr "" + +#: src/components/NanoContract/SelectAddressModal.component.js:97 +msgid "Load Addresses Error" +msgstr "" + +#: src/components/NanoContract/SelectAddressModal.component.js:106 +msgid "Loading wallet addresses." +msgstr "" + +#: src/components/NanoContract/SelectAddressModal.component.js:114 +msgid "Current Information" +msgstr "" + +#: src/components/NanoContract/SelectAddressModal.component.js:115 +msgid "To change, select other address on the list below." +msgstr "" + +#: src/components/NanoContract/SelectAddressModal.component.js:178 +msgid "index" +msgstr "" + +#: src/components/NanoContract/UnregisterNanoContractModal.component.js:39 +msgid "Unregister Nano Contract" +msgstr "" + +#: src/components/NanoContract/UnregisterNanoContractModal.component.js:41 +msgid "Are you sure you want to unregister this Nano Contract?" +msgstr "" + +#: src/components/NanoContract/UnregisterNanoContractModal.component.js:44 +msgid "Yes, unregister contract" +msgstr "" + +#: src/components/NanoContract/UnregisterNanoContractModal.component.js:50 +msgid "No, go back" +msgstr "" diff --git a/src/App.js b/src/App.js index 29deff761..f86d6f8d8 100644 --- a/src/App.js +++ b/src/App.js @@ -86,6 +86,7 @@ import WalletConnectModal from './components/WalletConnect/WalletConnectModal'; import { COLORS, HathorTheme } from './styles/themes'; import { NetworkSettingsFlowNav, NetworkSettingsFlowStack } from './screens/NetworkSettings'; import { NetworkStatusBar } from './components/NetworkSettings/NetworkStatusBar'; +import { NanoContractDetailsScreen } from './screens/NanoContract/NanoContractDetailsScreen'; /** * This Stack Navigator is exhibited when there is no wallet initialized on the local storage. @@ -376,6 +377,7 @@ const AppStack = () => { initialParams={{ hName: 'Main' }} component={TabNavigator} /> + diff --git a/src/actions.js b/src/actions.js index decb0476f..61ef861fe 100644 --- a/src/actions.js +++ b/src/actions.js @@ -137,6 +137,8 @@ export const types = { NETWORKSETTINGS_UPDATE_FAILURE: 'NETWORK_SETTINGS_UPDATE_FAILURE', /* It updates the redux state of network settings status. */ NETWORKSETTINGS_UPDATE_READY: 'NETWORK_SETTINGS_UPDATE_READY', + /* It signals Nano Contract initialization. */ + NANOCONTRACT_INIT: 'NANOCONTRACT_INIT', /* It initiates a registration process of a Nano Contract. */ NANOCONTRACT_REGISTER_REQUEST: 'NANOCONTRACT_REGISTER_REQUEST', /* It indicates a Nano Contract registration is couldn't complete. */ @@ -145,16 +147,26 @@ export const types = { NANOCONTRACT_REGISTER_SUCCESS: 'NANOCONTRACT_REGISTER_SUCCESS', /* It indicates a Nano Contract hitory was requested to load. */ NANOCONTRACT_HISTORY_REQUEST: 'NANOCONTRACT_HISTORY_REQUEST', + /* It indicates a Nano Contract history is processing. */ + NANOCONTRACT_HISTORY_LOADING: 'NANOCONTRACT_HISTORY_LOADING', /* It indicates a Nano Contract history was successfully loaded. */ NANOCONTRACT_HISTORY_SUCCESS: 'NANOCONTRACT_HISTORY_SUCCESS', /* It indicates a Nano Contract history failed to load. */ NANOCONTRACT_HISTORY_FAILURE: 'NANOCONTRACT_HISTORY_FAILURE', + /* It indicates a Nano Contract history clean. */ + NANOCONTRACT_HISTORY_CLEAN: 'NANOCONTRACT_HISTORY_CLEAN', /* It initiates an unregistration process of a Nano Contract. */ NANOCONTRACT_UNREGISTER_REQUEST: 'NANOCONTRACT_UNREGISTER_REQUEST', /* It signals a successful completion of unregistration process. */ NANOCONTRACT_UNREGISTER_SUCCESS: 'NANOCONTRACT_UNREGISTER_SUCCESS', /* It initiates a process to change the address on registered Nano Contract. */ NANOCONTRACT_ADDRESS_CHANGE_REQUEST: 'NANOCONTRACT_ADDRESS_CHANGE_REQUEST', + /* It triggers a process to fetch all wallet addresses. */ + SELECTADDRESS_ADDRESSES_REQUEST: 'SELECTADDRESS_ADDRESSES_REQUEST', + /* It signals the fetch has loaded all the addresses with success. */ + SELECTADDRESS_ADDRESSES_SUCCESS: 'SELECTADDRESS_ADDRESSES_SUCCESS', + /* It signals a fetch failure due to an error. */ + SELECTADDRESS_ADDRESSES_FAILURE: 'SELECTADDRESS_ADDRESSES_FAILURE', }; export const featureToggleInitialized = () => ({ @@ -994,6 +1006,13 @@ export const networkSettingsUpdateReady = () => ({ type: types.NETWORKSETTINGS_UPDATE_READY, }); +/** + * It signals Nano Contract initialization. + */ +export const nanoContractInit = () => ({ + type: types.NANOCONTRACT_INIT, +}); + /** * Request a Nano Contract to be registered. * @param {{ @@ -1039,6 +1058,17 @@ export const nanoContractHistoryRequest = (ncEntry) => ({ payload: ncEntry, }); +/** + * Nano Contract fetch history is loading. + * @param {{ + * ncId: string; + * }} + */ +export const nanoContractHistoryLoading = (ncEntry) => ({ + type: types.NANOCONTRACT_HISTORY_LOADING, + payload: ncEntry, +}); + /** * Nano Contract history has loaded success. * @param {Object} payload @@ -1051,6 +1081,16 @@ export const nanoContractHistorySuccess = (payload) => ({ payload, }); +/** + * Nano Contract history clean signal. + * @param {Object} payload + * @param {string} payload.ncId Nano Contract ID. + */ +export const nanoContractHistoryClean = (payload) => ({ + type: types.NANOCONTRACT_HISTORY_CLEAN, + payload, +}); + /** * Nano Contract history has failed. * @param {Object} payload @@ -1095,3 +1135,30 @@ export const nanoContractAddressChangeRequest = (changeAddressRequest) => ({ type: types.NANOCONTRACT_ADDRESS_CHANGE_REQUEST, payload: changeAddressRequest, }); + +/** + * Request to load all wallet addresses. + */ +export const selectAddressAddressesRequest = () => ({ + type: types.SELECTADDRESS_ADDRESSES_REQUEST, +}); + +/** + * Signals all wallet addresses were loaded with success. + * @param {Object} successPayload + * @param {string[]} successPayload.addresses + */ +export const selectAddressAddressesSuccess = (successPayload) => ({ + type: types.SELECTADDRESS_ADDRESSES_SUCCESS, + payload: successPayload, +}); + +/** + * Signals a failure on wallet addresses loading due to an error. + * @param {Object} failurePayload + * @param {string} failurePayload.error + */ +export const selectAddressAddressesFailure = (failurePayload) => ({ + type: types.SELECTADDRESS_ADDRESSES_FAILURE, + payload: failurePayload, +}); diff --git a/src/components/FeedbackContent.js b/src/components/FeedbackContent.js new file mode 100644 index 000000000..30ab1bb37 --- /dev/null +++ b/src/components/FeedbackContent.js @@ -0,0 +1,90 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import { + StyleSheet, + View, + Text, +} from 'react-native'; +import { COLORS } from '../styles/themes'; + +/** + * It presents a feedback content to user with title, message, icon and a call to action + * if needed. + * + * @param {Objects} props + * @param {string} props.title A bold, centered text + * @param {string} props.message A regular text underneath title + * @param {Object?} props.icon A react component or react element containing an icon, + * if provided, it renders uppon the title + * @param {Object?} props.action A react component or react element containing a call to action, + * if provided, it renders underneath the content + * @param {boolean} props.offcard Renders a feedback without card style + * + * @example + * } + * action={} + * /> + */ +export const FeedbackContent = ({ title, message, icon, action, offcard }) => ( + + + {icon + && ({icon})} + {title} + {message} + {action && (action)} + + +); + +const styles = StyleSheet.create({ + wrapper: { + flex: 1, + justifyContent: 'center', + alignSelf: 'stretch', + marginTop: 16, + marginBottom: 45, + backgroundColor: COLORS.backgroundColor, + marginHorizontal: 16, + }, + card: { + borderRadius: 16, + shadowOffset: { height: 2, width: 0 }, + shadowRadius: 4, + shadowColor: COLORS.textColor, + shadowOpacity: 0.08, + }, + content: { + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + paddingHorizontal: 45, + /* Play the role of a minimum vertical padding for small screens */ + paddingVertical: 90, + }, + title: { + fontSize: 16, + lineHeight: 20, + fontWeight: 'bold', + paddingBottom: 16, + textAlign: 'center', + }, + message: { + fontSize: 14, + lineHeight: 20, + paddingBottom: 16, + textAlign: 'center', + }, + icon: { + paddingBottom: 16, + }, +}); diff --git a/src/components/NanoContract/EditAddressModal.component.js b/src/components/NanoContract/EditAddressModal.component.js new file mode 100644 index 000000000..736e5c0bf --- /dev/null +++ b/src/components/NanoContract/EditAddressModal.component.js @@ -0,0 +1,137 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import { + StyleSheet, + View, + Text, +} from 'react-native'; +import { t } from 'ttag'; +import { COLORS } from '../../styles/themes'; +import { CircleInfoIcon } from '../Icons/CircleInfo.icon'; +import { ModalBase } from '../ModalBase'; +import { TextValue } from '../TextValue'; + +/** + * Use this modal to edit an address. + * + * @param {Object} props + * @param {string} props.item It refers to the address selected + * @param {boolean} props.show It determines if modal must show or hide + * @param {(address:string) => {}} props.onAddressChange + * Function called when an address is selected + * @param {() => {}} props.onDismiss + * Function called to dismiss the modal on user's interaction + * + * @example + * + */ +export const EditAddressModal = ({ item, show, onAddressChange, onDismiss }) => ( + + {t`New Nano Contract Address`} + + + + + + + + {t`This address signs any transaction you create with Nano Contracts method. Switching to a new one means`} + + {' '}{t`all future transactions will use this address.`} + + + + + + {t`Selected Information`} + + + {t`Address`} + {/* the unicode character u00A0 means no-break space. */} + {`:${'\u00A0'}${item.address}`} + + + + + {t`Index`} + {`:${'\u00A0'}${item.index}`} + + + + + onAddressChange(item.address)} + /> + + +); + +/** + * Container for label and value pair components. + * + * @param {Object} props + * @param {object} props.last It determines bottom padding application. + * @param {object} props.children + */ +const FieldContainer = ({ last, children }) => ( + + {children} + +); + +const styles = StyleSheet.create({ + pd0: { + paddingBottom: 0, + }, + pd8: { + paddingBottom: 8, + }, + body: { + paddingBottom: 20, + }, + fieldContainer: { + width: '100%', + paddingBottom: 4, + }, + text: { + fontSize: 14, + lineHeight: 20, + }, + bold: { + fontWeight: 'bold', + }, + infoContainer: { + flexShrink: 1, + flexDirection: 'row', + alignItems: 'center', + marginBottom: 24, + borderRadius: 8, + paddingVertical: 8, + paddingHorizontal: 16, + backgroundColor: 'hsla(203, 100%, 93%, 1)', + }, + infoContent: { + paddingLeft: 8, + }, + selectionContainer: { + borderRadius: 8, + paddingVertical: 8, + paddingHorizontal: 16, + backgroundColor: COLORS.freeze100, + }, +}); diff --git a/src/components/NanoContract/NanoContractDetails.component.js b/src/components/NanoContract/NanoContractDetails.component.js new file mode 100644 index 000000000..9f42113f1 --- /dev/null +++ b/src/components/NanoContract/NanoContractDetails.component.js @@ -0,0 +1,210 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { useNavigation } from '@react-navigation/native'; +import React, { useEffect, useState } from 'react'; +import { + StyleSheet, + View, + Image, +} from 'react-native'; +import { useDispatch, useSelector } from 'react-redux'; +import { t } from 'ttag'; +import { NanoContractDetailsHeader } from './NanoContractDetailsHeader.component'; +import { NanoContractTransactionsListItem } from './NanoContractTransactionsListItem.component'; +import { COLORS } from '../../styles/themes'; +import { nanoContractAddressChangeRequest, nanoContractHistoryRequest } from '../../actions'; +import { HathorFlatList } from '../HathorFlatList'; +import Spinner from '../Spinner'; +import errorIcon from '../../assets/images/icErrorBig.png'; +import SimpleButton from '../SimpleButton'; +import { FeedbackContent } from '../FeedbackContent'; + +/** + * Retrieves Nano Contract details from Redux. + * + * @param {string} ncId Nano Contract ID + * + * @returns {{ + * txHistory: Object[]; + * isLoading: boolean; + * error: string; + * after: string; + * }} + */ +const getNanoContractDetails = (ncId) => (state) => { + /* Without this default the app breaks after the current Nano Contract unregistration. + * By having a default value the app can render the component normally after unregistration + * and let it step aside while coming back to Dashboard screen. This transition happens + * quickly, therefore the user will not have time to see the default state. + */ + const defaultMeta = { isLoading: false, error: null, after: null }; + const txHistory = state.nanoContract.history[ncId] || []; + const { isLoading, error, after } = state.nanoContract.historyMeta[ncId] || defaultMeta; + return { + txHistory, + isLoading, + error, + after, + }; +} + +/** + * It presents a list of transactions from selected Nano Contract. + * + * @param {Object} props + * @param {Object} props.nc Nano Contract data + * @param {string} props.nc.ncId Nano Contract ID + * @param {string} props.nc.address Default caller address for Nano Contract interaction + */ +export const NanoContractDetails = ({ nc }) => { + const dispatch = useDispatch(); + const navigation = useNavigation(); + + const { txHistory, isLoading, error, after } = useSelector(getNanoContractDetails(nc.ncId)); + const [ncAddress, changeNcAddress] = useState(nc.address); + + const onAddressChange = (newAddress) => { + changeNcAddress(newAddress); + dispatch(nanoContractAddressChangeRequest({ newAddress, ncId: nc.ncId })); + } + + const navigatesToNanoContractTransaction = (tx) => { + navigation.navigate('NanoContractTransaction', { tx }); + }; + + useEffect(() => { + if (txHistory.length === 0) { + /* The first time we load the Nano Contract details its transaction history is empty. + * The second time it is garanteed that its transaction history is not empty, + * because a Nano Contract must have at least the 'initialize' transaction to exists. + * For the first transaction history load we don't need to specify the `after` param, + * it will be set during the load. + */ + dispatch(nanoContractHistoryRequest({ ncId: nc.ncId, after: null })); + } + }, []); + + const handleMoreTransactions = () => { + if (after == null) { + /* This situation is unlikely to happen because on the first transactions history load + * the `after` is assigned with the hash of the last transaction in the list. + */ + return; + } + dispatch(nanoContractHistoryRequest({ ncId: nc.ncId, after })); + }; + + /* If an error happens on loading transactions history, an error feedback + * content must be presented to the user, and must have precedence over + * all other content. + */ + const hasError = () => error != null; + const isEmpty = () => txHistory.length === 0 && !hasError(); + const notEmpty = () => !isEmpty() && !hasError(); + + return ( + + + {isLoading && ( + + )} + {hasError() + && ()} + {isEmpty() + && ()} + {notEmpty() + && ( + ( + navigatesToNanoContractTransaction(item)} + /> + )} + keyExtractor={(item) => item.txId} + refreshing={isLoading} + onRefresh={handleMoreTransactions} + /> + )} + + ); +}; + +/** + * @param {Object} props + * @param {Object?} props.children Either a react component or a react element + */ +const Wrapper = ({ children }) => ( + + {children} + +); + +/** + * Renders a feedback for the lack of transactions. + */ +export const NoNanoContractTransaction = () => ( + +); + +/** + * Renders an error feedback for when the history load request goes wrong, + * and provides a call to action to try fetch the history again. + * + * @param {Object} props + * @param {string} props.ncId Nano Contract ID + * @param {string} props.error + */ +export const ErrorLoadingTransaction = ({ ncId, error }) => ( + } + action={} + /> +); + +/** + * Renders a call to action to request history loading again. + * + * @param {Object} props + * @param {string} props.ncId Nano Contract ID + */ +export const TryAgain = ({ ncId }) => { + const dispatch = useDispatch(); + const fetchTransactionsHistory = () => { + dispatch(nanoContractHistoryRequest({ ncId, after: null })); + }; + + return ( + + ); +}; + +const styles = StyleSheet.create({ + wrapper: { + flex: 1, + justifyContent: 'flex-start', + alignSelf: 'stretch', + backgroundColor: COLORS.lowContrastDetail, // Defines an outer area on the main list content + }, + center: { + alignSelf: 'center', + }, +}); diff --git a/src/components/NanoContract/NanoContractDetailsHeader.component.js b/src/components/NanoContract/NanoContractDetailsHeader.component.js new file mode 100644 index 000000000..1bcadad78 --- /dev/null +++ b/src/components/NanoContract/NanoContractDetailsHeader.component.js @@ -0,0 +1,267 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React, { useState } from 'react'; +import { + StyleSheet, + View, + Text, + TouchableWithoutFeedback, + Linking, +} from 'react-native'; +import { useSelector } from 'react-redux'; +import { t } from 'ttag'; +import { COLORS } from '../../styles/themes'; +import { combineURLs, getShortContent, getShortHash } from '../../utils'; +import SimpleButton from '../SimpleButton'; +import HathorHeader from '../HathorHeader'; +import { NanoContractIcon } from '../Icons/NanoContract.icon'; +import { ArrowDownIcon } from '../Icons/ArrowDown.icon'; +import { ArrowUpIcon } from '../Icons/ArrowUp.icon'; +import { TextValue } from '../TextValue'; +import { TextLabel } from '../TextLabel'; +import { EditInfoContainer } from '../EditInfoContainer'; +import { SelectAddressModal } from './SelectAddressModal.component'; +import { UnregisterNanoContractModal } from './UnregisterNanoContractModal.component'; + +/** + * It presents the header for Nano Contract Details screen and provides the following + * actions to users: + * - Open Nano Contract details at the Explorer + * - Edit the registered address for the Nano Contract + * - Unregister the Nano Contract + * + * @param {Object} props + * @param {Object} props.nc Nano Contract data + * @param {string} props.address Default address selected + * @param {(address:string) => {}} props.onAddressChange Function called when address changes + */ +export const NanoContractDetailsHeader = ({ nc, address, onAddressChange }) => { + const [isShrank, toggleShrank] = useState(true); + const [selectedAddress, setSelectedAddress] = useState(address); + const [showSelectAddressModal, setShowSelectAddressModal] = useState(false); + const [showUnregisterNanoContractModal, setShowUnregisterNanoContractModal] = useState(false); + + const isExpanded = () => !isShrank; + + const onEditAddress = () => { + setShowSelectAddressModal(true); + }; + + const toggleSelectAddressModal = () => { + setShowSelectAddressModal(!showSelectAddressModal); + }; + + const onUnregisterNanoContract = () => { + setShowUnregisterNanoContractModal(true); + }; + + const toggleUnregisterNanoContractModal = () => { + setShowUnregisterNanoContractModal(!showUnregisterNanoContractModal); + }; + + const handleSelectAddress = (pickedAddress) => { + setSelectedAddress(pickedAddress); + toggleSelectAddressModal(); + onAddressChange(pickedAddress); + }; + + return ( + + + toggleShrank(!isShrank)}> + + + {t`Nano Contract`} + {isShrank + && } + {isExpanded() + && ( + + )} + + + + + + + ) +}; + +const HeaderIcon = () => ( + + + +); + +const HeaderShrank = () => ( + +); + +/** + * Presents all the information. + * + * @param {Object} props + * @param {Object} props.nc Nano Contract data + * @param {string} props.address Current address + * @param {() => void} props.onEditAddress Function called on address edition trigger + * @param {() => void} props.onUnregisterNanoContract Function called on unregister trigger + */ +const HeaderExpanded = ({ nc, address, onEditAddress, onUnregisterNanoContract }) => { + const baseExplorerUrl = useSelector((state) => state.networkSettings.explorerUrl); + + const navigatesToExplorer = () => { + const txUrl = `transaction/${nc.ncId}`; + const explorerLink = combineURLs(baseExplorerUrl, txUrl); + Linking.openURL(explorerLink); + }; + + return ( + <> + + + {getShortHash(nc.ncId, 7)} + {t`Nano Contract ID`} + + + {nc.blueprintName} + {t`Blueprint Name`} + + + {getShortContent(address, 7)} + {t`Registered Address`} + + + + + + + + + ) +}; + +/** + * Container for value and label pair components. + * + * @param {Object} props + * @param {Object} props.children + */ +const InfoContainer = ({ children }) => ( + + {children} + +); + +/** + * It presents two button options inline. + * + * @param {Object} props + * @param {Object} props.children + */ +const TwoActionsWrapper = ({ children }) => ( + + {children} + +); + +/** + * Text button in primary color and style. + * + * @param {Object} props + * @param {string} props.title Text content + * @param {() => void} props.onPress Callback for interaction + */ +const PrimaryTextButton = ({ title, onPress }) => ( + +); + +/** + * Text button in red. + * + * @param {Object} props + * @param {string} props.title Text content + * @param {() => void} props.onPress Callback for interaction + */ +const DenyTextButton = ({ title, onPress }) => ( + +); + +const styles = StyleSheet.create({ + headerCentral: { + flex: 1, + alignItems: 'center', + paddingTop: 24, + }, + headerWrapper: { + alignItems: 'center', + }, + headerTitle: { + fontSize: 18, + lineHeight: 20, + fontWeight: 'bold', + paddingVertical: 16, + }, + wrapper: { + paddingHorizontal: 16, + paddingBottom: 16, + }, + infoContainer: { + alignItems: 'center', + paddingBottom: 16, + }, + lastElement: { + paddingBottom: 0, + }, + TwoActionsWrapper: { + flexDirection: 'row', + flexWrap: 'nowrap', + }, + buttonWrapper: { + /* It also increases touch area. */ + paddingTop: 24, + }, + buttonText: { + fontWeight: 'bold', + }, + buttonUnregister: { + marginStart: 24, + color: 'hsla(0, 100%, 41%, 1)', + }, + buttonDetails: { + display: 'inline-block', + /* We are using negative margin here to correct the text position + * and create an optic effect of alignment. */ + marginBottom: -2, + borderBottomWidth: 1, + borderColor: COLORS.primary, + }, +}); diff --git a/src/components/NanoContract/NanoContractTransactionsListItem.component.js b/src/components/NanoContract/NanoContractTransactionsListItem.component.js new file mode 100644 index 000000000..7f5cb67af --- /dev/null +++ b/src/components/NanoContract/NanoContractTransactionsListItem.component.js @@ -0,0 +1,124 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import { TouchableHighlight, StyleSheet, View, Text, Image } from 'react-native'; +import { t } from 'ttag'; + +import chevronRight from '../../assets/icons/chevron-right.png'; +import { COLORS } from '../../styles/themes'; +import { getShortHash, getTimestampFormat } from '../../utils'; + +/** + * It renders an item of Nano Contract Transactions list. + * + * @param {Object} props + * @param {Object} props.item Nano Contract transaction data + * @param {() => void} props.onPress Callback function called on press + */ +export const NanoContractTransactionsListItem = ({ item, onPress }) => ( + + + + +); + +const Wrapper = ({ onPress, children }) => ( + + {children} + +); + +/** + * It presents summarized transaction information. + * + * @param {Object} props + * @param {Object} props.tx Nano Contract transaction data + * @param {string} props.tx.txId + * @param {string} props.tx.ncMethod + * @param {boolean} props.tx.isMine + * @param {number} props.tx.timestamp + */ +const TransactionSummary = ({ tx }) => ( + + + + {getShortHash(tx.txId, 7)} + + {tx.isMine + && ( + + {t`From this wallet`} + + )} + + {tx.ncMethod} + {getTimestampFormat(tx.timestamp)} + +); + +const ArrowRight = () => ( + + + +); + +const styles = StyleSheet.create({ + wrapper: { + paddingVertical: 16, + paddingHorizontal: 16, + flexDirection: 'row', + alignItems: 'center', + }, + contentWrapper: { + flexShrink: 1, + maxWidth: '80%', + flexDirection: 'column', + alignItems: 'flex-start', + justifyContent: 'space-between', + marginRight: 'auto', + /* Add optical effect of simmetry with array icon */ + paddingLeft: 8, + }, + contentHeadline: { + flex: 1, + flexDirection: 'row', + alignItems: 'center', + paddingBottom: 4, + }, + headlineLabel: { + marginLeft: 8, + borderRadius: 20, + paddingHorizontal: 12, + paddingVertical: 2, + backgroundColor: COLORS.freeze100, + }, + isMineLabel: { + fontSize: 12, + lineHeight: 20, + }, + text: { + fontSize: 14, + lineHeight: 20, + paddingBottom: 6, + color: 'hsla(0, 0%, 38%, 1)', + }, + property: { + fontWeight: 'bold', + color: 'black', + }, + padding0: { + paddingBottom: 0, + }, +}); diff --git a/src/components/NanoContract/NanoContractsList.js b/src/components/NanoContract/NanoContractsList.js index a150f3569..418797083 100644 --- a/src/components/NanoContract/NanoContractsList.js +++ b/src/components/NanoContract/NanoContractsList.js @@ -9,16 +9,17 @@ import React from 'react'; import { StyleSheet, View, Text } from 'react-native'; import { t } from 'ttag'; import { useNavigation } from '@react-navigation/native'; - import { useSelector } from 'react-redux'; import { COLORS } from '../../styles/themes'; import HathorHeader from '../HathorHeader'; -import { NoNanoContracts } from './NoNanoContracts'; +import { HathorFlatList } from '../HathorFlatList'; import { RegisterNanoContract } from './RegisterNewNanoContractButton'; import { NanoContractsListItem } from './NanoContractsListItem'; -import { HathorFlatList } from '../HathorFlatList'; +import { FeedbackContent } from '../FeedbackContent'; /** + * It selects the list of registered Nano Contracts. + * * @param {Object} state Redux root state * @returns {Object} Array of registered Nano Contract with basic information */ @@ -27,12 +28,15 @@ const getRegisteredNanoContracts = (state) => { return Object.values(registered); } +/** + * It presents a list of Nano Contracts or an empty content. + */ export const NanoContractsList = () => { const registeredNanoContracts = useSelector(getRegisteredNanoContracts); const navigation = useNavigation(); - const navigatesToNanoContractTransactions = () => { - navigation.navigate('NanoContractTransactions'); + const navigatesToNanoContractTransactions = (nc) => { + navigation.navigate('NanoContractDetailsScreen', { ncId: nc.ncId }); }; const isEmpty = () => registeredNanoContracts.length === 0; const notEmpty = () => !isEmpty(); @@ -41,7 +45,7 @@ export const NanoContractsList = () => {
{isEmpty() - && } + && } {notEmpty() && ( { renderItem={({ item }) => ( navigatesToNanoContractTransactions(item)} /> )} keyExtractor={(nc) => nc.ncId} @@ -76,10 +80,12 @@ const Header = () => (
); -const ListWrapper = ({ children }) => ( - - {children} - +export const NoNanoContracts = () => ( + } + /> ); const styles = StyleSheet.create({ @@ -89,20 +95,6 @@ const styles = StyleSheet.create({ alignItems: 'center', backgroundColor: COLORS.lowContrastDetail, // Defines an outer area on the main list content }, - listWrapper: { - flex: 1, - justifyContent: 'center', - alignSelf: 'stretch', - marginTop: 16, - marginBottom: 45, - backgroundColor: COLORS.backgroundColor, - marginHorizontal: 16, - borderRadius: 16, - shadowOffset: { height: 2, width: 0 }, - shadowRadius: 4, - shadowColor: COLORS.textColor, - shadowOpacity: 0.08, - }, headerTitle: { fontSize: 24, lineHeight: 24, diff --git a/src/components/NanoContract/NanoContractsListItem.js b/src/components/NanoContract/NanoContractsListItem.js index 06b5150be..4506530b2 100644 --- a/src/components/NanoContract/NanoContractsListItem.js +++ b/src/components/NanoContract/NanoContractsListItem.js @@ -7,6 +7,7 @@ import React from 'react'; import { TouchableHighlight, StyleSheet, View, Text, Image } from 'react-native'; +import { t } from 'ttag'; import chevronRight from '../../assets/icons/chevron-right.png'; import { COLORS } from '../../styles/themes'; @@ -14,16 +15,16 @@ import { getShortHash } from '../../utils'; import { NanoContractIcon } from '../Icons/NanoContract.icon'; /** - * Renders each item of Nano Contract List. + * It renders an item of Nano Contracts list. * - * @param {Object} ncItem - * @property {Object} ncItem.item registered Nano Contract data - * @property {() => {}} ncItem.onPress A void function to be called when item is pressed. + * @param {Object} props + * @param {Object} props.item Registered Nano Contract data + * @param {() => {}} props.onPress Callback function called on press */ export const NanoContractsListItem = ({ item, onPress }) => ( - + ); @@ -39,21 +40,23 @@ const Wrapper = ({ onPress, children }) => ( const Icon = () => ( - + ); /** - * Renders item core content. + * It presents summarized Nano Contract information. * - * @param {Object} ncItem - * @property {Obeject} ncItem.nc registered Nano Contract data + * @param {Object} props + * @param {Object} props.nc Registered Nano Contract data + * @param {string} props.nc.ncId + * @param {string} props.nc.blueprintName */ -const ContentWrapper = ({ nc }) => ( +const NanoContractSummary = ({ nc }) => ( - Nano Contract ID + {t`Nano Contract ID`} {getShortHash(nc.ncId, 7)} - Blueprint Name + {t`Blueprint Name`} {nc.blueprintName} ); @@ -81,13 +84,7 @@ const styles = StyleSheet.create({ paddingHorizontal: 16, }, icon: { - paddingVertical: 6, - paddingHorizontal: 8, - backgroundColor: COLORS.primary, alignSelf: 'flex-start', - alignItems: 'center', - justifyContent: 'center', - borderRadius: 8, }, text: { fontSize: 14, diff --git a/src/components/NanoContract/NoNanoContracts.js b/src/components/NanoContract/NoNanoContracts.js deleted file mode 100644 index a8e18dcab..000000000 --- a/src/components/NanoContract/NoNanoContracts.js +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright (c) Hathor Labs and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -import React from 'react'; -import { StyleSheet, View, Text } from 'react-native'; -import { t } from 'ttag'; - -import { RegisterNanoContract } from './RegisterNewNanoContractButton'; - -export const NoNanoContracts = () => ( - - {t`No Nano Contracts`} - - {t`You can keep track of your registered Nano Contracts here once you have registered them.`} - - - -); - -const styles = StyleSheet.create({ - wrapper: { - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - paddingHorizontal: 45, - /* Play the role of a minimum vertical padding for small screens */ - paddingVertical: 90, - }, - title: { - fontSize: 16, - lineHeight: 20, - fontWeight: 'bold', - paddingBottom: 16, - }, - content: { - fontSize: 14, - lineHeight: 20, - paddingBottom: 16, - textAlign: 'center', - }, - learnMoreWrapper: { - display: 'inline-block', - /* We are using negative margin here to correct the text position - * and create an optic effect of alignment. */ - marginBottom: -4, - paddingLeft: 2, - }, - learnMoreContainer: { - justifyContent: 'flex-start', - borderBottomWidth: 1, - }, - learnMoreText: { - fontSize: 14, - lineHeight: 20, - fontWeight: 'bold', - color: 'black', - }, -}); diff --git a/src/components/NanoContract/SelectAddressModal.component.js b/src/components/NanoContract/SelectAddressModal.component.js new file mode 100644 index 000000000..e4e0e4138 --- /dev/null +++ b/src/components/NanoContract/SelectAddressModal.component.js @@ -0,0 +1,233 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React, { useEffect, useState } from 'react'; +import { + StyleSheet, + View, + FlatList, + TouchableHighlight, + Text, + Image, +} from 'react-native'; +import { t } from 'ttag'; +import { useDispatch, useSelector } from 'react-redux'; +import { COLORS } from '../../styles/themes'; +import { ModalBase } from '../ModalBase'; +import { TextValue } from '../TextValue'; +import { TextLabel } from '../TextLabel'; +import { EditAddressModal } from './EditAddressModal.component'; +import { FeedbackContent } from '../FeedbackContent'; +import errorIcon from '../../assets/images/icErrorBig.png'; +import { selectAddressAddressesRequest } from '../../actions'; + +/** + * Use this modal to select an address from the wallet. + * + * @param {Object} props + * @param {string} props.address It refers to the address selected + * @param {boolean} props.show It determines if modal must show or hide + * @param {(address:string) => {}} props.onSelectAddress + * Callback function called when an address is selected + * @param {() => {}} props.onDismiss + * Callback function called to dismiss the modal + * + * @example + * + */ +export const SelectAddressModal = ({ address, show, onSelectAddress, onDismiss }) => { + const dispatch = useDispatch(); + const { addresses, error } = useSelector((state) => state.selectAddressModal); + + const [selectedItem, setSelectedItem] = useState({ address }); + const [showEditAddressModal, setShowEditAddressModal] = useState(false); + + const toggleEditAddressModal = () => { + setShowEditAddressModal(!showEditAddressModal); + }; + + const onDismissEditAddressModal = () => { + setSelectedItem({ address }); + toggleEditAddressModal(); + }; + + // This method is only called by AddressItem if the selected address + // is different from the current one. + const onSelectItem = (item) => { + setSelectedItem(item); + toggleEditAddressModal(); + }; + + const hookAddressChange = (selectedAddress) => { + toggleEditAddressModal(); + onSelectAddress(selectedAddress); + }; + + useEffect(() => { + dispatch(selectAddressAddressesRequest()); + }, []); + + const hasFailed = () => error; + const isLoading = () => !error && addresses.length === 0; + const hasLoaded = () => !error && addresses.length > 0; + + return ( + + {t`Choose New Wallet Address`} + + + {hasFailed() + && ( + )} + title={t`Load Addresses Error`} + message={error} + offcard + /> + )} + {isLoading() + && ( + + )} + {hasLoaded() + && ( + <> + + {t`Current Information`} + {t`To change, select other address on the list below.`} + + {t`Address`} + {/* the unicode character u00A0 means no-break space. */} + {`:${'\u00A0'}${address}`} + + + ( + + )} + keyExtractor={(item) => item.address} + /> + + )} + + {showEditAddressModal + && ( + + )} + + + ); +}; + +/** + * It renders and address as an item of the list, also it indicates a match + * between the current address and the item's address. + * + * @param {Object} props + * @param {string} props.currentAddress It refers to the address selected + * @param {Object} props.item Address of the item + * @param {string} props.item.address + * @param {number} props.item.index + * @param {(address:string) => void} props.onSelectItem + * Callback function called when an address is selected + */ +const AddressItem = ({ currentAddress, item, onSelectItem }) => { + const onPress = () => { + if (currentAddress === item.address) { + return; + } + onSelectItem(item); + }; + return ( + + + + {item.address} + {t`index`} {item.index} + + + + ) +}; + +const styles = StyleSheet.create({ + modal: { + justifyContent: 'flex-end', + marginHorizontal: 0, + }, + wrapper: { + height: '90%', + }, + body: { + flex: 1, + paddingBottom: 20, + }, + bodyWrapper: { + flex: 1, + }, + infoWrapper: { + borderRadius: 8, + backgroundColor: COLORS.freeze100, + paddingVertical: 8, + paddingHorizontal: 16, + marginBottom: 16, + }, + infoText: { + fontSize: 14, + lineHeight: 20, + color: COLORS.black, + paddingBottom: 8, + }, + textBold: { + fontWeight: 'bold', + }, + feedbackContentIcon: { + height: 36, + width: 36, + }, +}); + +const addressItemStyle = StyleSheet.create({ + wrapper: { + paddingVertical: 16, + paddingHorizontal: 16, + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + }, + selected: { + backgroundColor: COLORS.freeze100, + }, +}); diff --git a/src/components/NanoContract/UnregisterNanoContractModal.component.js b/src/components/NanoContract/UnregisterNanoContractModal.component.js new file mode 100644 index 000000000..2ce5ba1c4 --- /dev/null +++ b/src/components/NanoContract/UnregisterNanoContractModal.component.js @@ -0,0 +1,69 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import { + StyleSheet, + Text, +} from 'react-native'; +import { useDispatch } from 'react-redux'; +import { t } from 'ttag'; +import { useNavigation } from '@react-navigation/native'; +import { nanoContractUnregisterRequest } from '../../actions'; +import { ModalBase } from '../ModalBase'; + +/** + * Presents a modal to confirm unregistration of the Nano Contract. + * + * @param {Object} props + * @param {Object} props.ncId Nano Contract ID + * @param {Object} props.show It determines if modal must show or hide + * @param {() => void} props.onDimiss Callback function called to dismiss the modal + */ +export const UnregisterNanoContractModal = ({ ncId, show, onDismiss }) => { + const dispatch = useDispatch(); + const navigation = useNavigation(); + + const onUnregisterContract = () => { + onDismiss(); + dispatch(nanoContractUnregisterRequest({ ncId })); + navigation.navigate('Dashboard'); + }; + + return ( + + {t`Unregister Nano Contract`} + + {t`Are you sure you want to unregister this Nano Contract?`} + + + + + ); +}; + +const styles = StyleSheet.create({ + body: { + paddingBottom: 20, + }, + fieldContainer: { + width: '100%', + paddingBottom: 4, + }, + text: { + fontSize: 14, + lineHeight: 20, + }, +}); diff --git a/src/components/TextValue.js b/src/components/TextValue.js index 952616385..98c74bf78 100644 --- a/src/components/TextValue.js +++ b/src/components/TextValue.js @@ -11,9 +11,18 @@ import { Text, } from 'react-native'; -export const TextValue = ({ bold, pb4, children }) => ( +/** + * @param {Object} props + * @param {boolean} props.bold It sets font weight to bold + * @param {boolean} props.oneline It sets numberOfLines to 1 + * @param {boolean} props.shrink It sets flexShrink to 1 + * @param {boolean} props.pb4 It sets padding bottom to 4 + */ +export const TextValue = ({ bold, oneline, shrink, pb4, children }) => ( { @@ -471,16 +481,24 @@ export const reducer = (state = initialState, action) => { return onNetworkSettingsUpdateInvalid(state, action); case types.NANOCONTRACT_REGISTER_SUCCESS: return onNanoContractRegisterSuccess(state, action); - case types.NANOCONTRACT_HISTORY_REQUEST: - return onNanoContractHistoryRequest(state, action); + case types.NANOCONTRACT_HISTORY_LOADING: + return onNanoContractHistoryLoading(state, action); case types.NANOCONTRACT_HISTORY_FAILURE: return onNanoContractHistoryFailure(state, action); case types.NANOCONTRACT_HISTORY_SUCCESS: return onNanoContractHistorySuccess(state, action); + case types.NANOCONTRACT_HISTORY_CLEAN: + return onNanoContractHistoryClean(state, action); case types.NANOCONTRACT_UNREGISTER_SUCCESS: return onNanoContractUnregisterSuccess(state, action); case types.NANOCONTRACT_ADDRESS_CHANGE_REQUEST: return onNanoContractAddressChangeRequest(state, action); + case types.SELECTADDRESS_ADDRESSES_REQUEST: + return onSelectAddressAddressesRequest(state); + case types.SELECTADDRESS_ADDRESSES_FAILURE: + return onSelectAddressAddressesFailure(state, action); + case types.SELECTADDRESS_ADDRESSES_SUCCESS: + return onSelectAddressAddressesSuccess(state, action); default: return state; } @@ -1369,19 +1387,19 @@ export const onNanoContractUnregisterSuccess = (state, { payload }) => { * @param {{ * payload: { * ncId: string; - * after: string; * } * }} action */ -export const onNanoContractHistoryRequest = (state, { payload }) => ({ +export const onNanoContractHistoryLoading = (state, { payload }) => ({ ...state, nanoContract: { ...state.nanoContract, historyMeta: { ...state.nanoContract.historyMeta, [payload.ncId]: { + ...(state.nanoContract.historyMeta[payload.ncId]), isLoading: true, - after: payload.after, + error: null, }, }, }, @@ -1411,6 +1429,34 @@ export const onNanoContractHistoryFailure = (state, { payload }) => ({ }, }); +/** + * @param {Object} state + * @param {{ + * payload: { + * ncId: string; + * } + * }} action + */ +export const onNanoContractHistoryClean = (state, { payload }) => ({ + ...state, + nanoContract: { + ...state.nanoContract, + history: { + ...state.nanoContract.history, + [payload.ncId]: [], + }, + historyMeta: { + ...state.nanoContract.historyMeta, + [payload.ncId]: { + ...(state.nanoContract.historyMeta[payload.ncId]), + isLoading: false, + after: null, + error: null, + }, + }, + }, +}); + /** * @param {Object} state * @param {{ @@ -1436,8 +1482,10 @@ export const onNanoContractHistorySuccess = (state, { payload }) => ({ historyMeta: { ...state.nanoContract.historyMeta, [payload.ncId]: { + ...(state.nanoContract.historyMeta[payload.ncId]), isLoading: false, after: payload.after, + error: null, }, }, }, @@ -1468,3 +1516,43 @@ export const onNanoContractAddressChangeRequest = (state, { payload }) => { }, }; }; + +/** + * @param {Object} state + */ +export const onSelectAddressAddressesRequest = (state) => ({ + ...state, + selectAddressModal: initialState.selectAddressModal, +}); + +/** + * @param {Object} state + * @param {{ + * payload: { + * error: string; + * } + * }} action + */ +export const onSelectAddressAddressesFailure = (state, { payload }) => ({ + ...state, + selectAddressModal: { + addresses: [], + error: payload.error, + }, +}); + +/** + * @param {Object} state + * @param {{ + * payload: { + * addresses: string[]; + * } + * }} action + */ +export const onSelectAddressAddressesSuccess = (state, { payload }) => ({ + ...state, + selectAddressModal: { + addresses: payload.addresses, + error: null, + }, +}); diff --git a/src/sagas/helpers.js b/src/sagas/helpers.js index d361b2d3a..5c006dfcd 100644 --- a/src/sagas/helpers.js +++ b/src/sagas/helpers.js @@ -35,6 +35,7 @@ import { } from '../constants'; import { STORE } from '../store'; import { logger } from '../logger'; +import { consumeAsyncIterator } from '../utils'; const log = logger('helper'); @@ -194,6 +195,16 @@ export async function getRegisteredTokens(wallet, excludeHTR = false) { return tokens; } +/** + * Retrieve registered Nano Contracts persisted on App's storage. + * @param {Object} wallet Wallet instance + * @return {Promise} + * @async + */ +export async function getRegisteredNanoContracts(wallet) { + return consumeAsyncIterator(wallet.storage.getRegisteredNanoContracts()); +} + /** * Flat registered tokens to uid. * @param {{ tokens: Object }} Map of registered tokens by uid diff --git a/src/sagas/nanoContract.js b/src/sagas/nanoContract.js index f4ec6a366..1ea559d0f 100644 --- a/src/sagas/nanoContract.js +++ b/src/sagas/nanoContract.js @@ -16,11 +16,15 @@ import { all, put, call, + delay, + debounce, } from 'redux-saga/effects'; import { t } from 'ttag'; import { NanoRequest404Error } from '@hathor/wallet-lib/lib/errors'; import { + nanoContractHistoryClean, nanoContractHistoryFailure, + nanoContractHistoryLoading, nanoContractHistorySuccess, nanoContractRegisterFailure, nanoContractRegisterSuccess, @@ -30,6 +34,8 @@ import { } from '../actions'; import { logger } from '../logger'; import { NANO_CONTRACT_TX_HISTORY_SIZE } from '../constants'; +import { getNanoContractFeatureToggle } from '../utils'; +import { getRegisteredNanoContracts } from './helpers'; const log = logger('nano-contract-saga'); @@ -40,9 +46,24 @@ export const failureMessage = { nanoContractStateNotFound: t`Nano Contract not found.`, nanoContractStateFailure: t`Error while trying to get Nano Contract state.`, notRegistered: t`Nano Contract not registered.`, - nanoContractHistoryFailure: t`Error while trying to fetch Nano Contract history.`, + nanoContractHistoryFailure: t`Error while trying to download Nano Contract transactions history.`, }; +export function* init() { + const isEnabled = yield select(getNanoContractFeatureToggle); + if (!isEnabled) { + log.debug('Halting nano contract initialization because the feature flag is disabled.'); + return; + } + + const wallet = yield select((state) => state.wallet); + const contracts = yield call(getRegisteredNanoContracts, wallet); + for (const contract of contracts) { + yield put(nanoContractRegisterSuccess({ entryKey: contract.ncId, entryValue: contract })); + } + log.debug('Registered Nano Contracts loaded.'); +} + /** * Process Nano Contract registration request. * @param {{ @@ -195,8 +216,8 @@ export async function fetchHistory(ncId, count, after, wallet) { history.push(tx); } - let next = null; - if (history && history.length === count) { + let next = after; + if (history && history.length > 0) { next = history[history.length - 1].txId; } @@ -215,12 +236,15 @@ export async function fetchHistory(ncId, count, after, wallet) { export function* requestHistoryNanoContract({ payload }) { const { ncId, after } = payload; const count = NANO_CONTRACT_TX_HISTORY_SIZE; + log.debug('Start processing request for nano contract transaction history...'); const historyMeta = yield select((state) => state.nanoContract.historyMeta); if (historyMeta[ncId] && historyMeta[ncId].isLoading) { // Do nothing if nano contract already loading... + log.debug('Halting processing for nano contract transaction history request while it is loading...'); return; } + yield put(nanoContractHistoryLoading({ ncId })); const wallet = yield select((state) => state.wallet); if (!wallet.isReady()) { @@ -239,10 +263,22 @@ export function* requestHistoryNanoContract({ payload }) { return; } + if (after == null) { + // it clean the history when starting load from the beginning + yield put(nanoContractHistoryClean({ ncId })); + } + try { // fetch from fullnode const { history, next } = yield call(fetchHistory, ncId, count, after, wallet); + if (after != null) { + // The first load has always `after` equals null. The first load means to be fast, + // but the subsequent ones are all request by user and we want slow down multiple + // calls to this effect. + yield delay(1000) + } + log.debug('Success fetching Nano Contract history.'); yield put(nanoContractHistorySuccess({ ncId, history, after: next })); } catch (error) { @@ -281,10 +317,43 @@ export function* unregisterNanoContract({ payload }) { yield put(nanoContractUnregisterSuccess({ ncId })); } +/** + * Process update on registered Nano Contract address to persist on store. + * @param {{ + * payload: { + * ncId: string; + * address: string; + * } + * }} + */ +export function* requestNanoContractAddressChange({ payload }) { + const { ncId, address } = payload; + + const wallet = yield select((state) => state.wallet); + if (!wallet.isReady()) { + log.error('Fail updating Nano Contract address because wallet is not ready yet.'); + // This will show user an error modal with the option to send the error to sentry. + yield put(onExceptionCaptured(new Error(failureMessage.walletNotReadyError), true)); + return; + } + + yield call( + [ + wallet.storage, + wallet.storage.updateNanoContractRegisteredAddress + ], + ncId, + address, + ); + log.debug(`Success persisting Nano Contract address update. ncId = ${ncId}`); +} + export function* saga() { yield all([ + debounce(500, [[types.START_WALLET_SUCCESS, types.NANOCONTRACT_INIT]], init), takeEvery(types.NANOCONTRACT_REGISTER_REQUEST, registerNanoContract), takeEvery(types.NANOCONTRACT_HISTORY_REQUEST, requestHistoryNanoContract), takeEvery(types.NANOCONTRACT_UNREGISTER_REQUEST, unregisterNanoContract), + takeEvery(types.NANOCONTRACT_ADDRESS_CHANGE_REQUEST, requestNanoContractAddressChange), ]); } diff --git a/src/sagas/wallet.js b/src/sagas/wallet.js index 068453f1a..a74215351 100644 --- a/src/sagas/wallet.js +++ b/src/sagas/wallet.js @@ -32,6 +32,7 @@ import { import { eventChannel } from 'redux-saga'; import { getUniqueId } from 'react-native-device-info'; import { get } from 'lodash'; +import { t } from 'ttag'; import { DEFAULT_TOKEN, WALLET_SERVICE_FEATURE_TOGGLE, @@ -63,6 +64,8 @@ import { resetWalletSuccess, setTokens, onExceptionCaptured, + selectAddressAddressesSuccess, + selectAddressAddressesFailure, } from '../actions'; import { fetchTokenData } from './tokens'; import { @@ -75,7 +78,10 @@ import { getRegisteredTokenUids, progressiveRetryRequest, } from './helpers'; -import { setKeychainPin } from '../utils'; +import { getAllAddresses, setKeychainPin } from '../utils'; +import { logger } from '../logger'; + +const log = logger('wallet'); export const WALLET_STATUS = { NOT_STARTED: 'not_started', @@ -724,6 +730,31 @@ export function* refreshSharedAddress() { yield put(sharedAddressUpdate(address, index)); } +export function* fetchAllWalletAddresses() { + const wallet = yield select((state) => state.wallet); + if (!wallet.isReady()) { + log.error('Fail fetching all wallet addresses because wallet is not ready yet.'); + const errorMsg = t`Wallet is not ready to load addresses.`; + // This will show the message in the feedback content at SelectAddressModal + yield put(selectAddressAddressesFailure({ error: errorMsg })); + // This will show user an error modal with the option to send the error to sentry. + yield put(onExceptionCaptured(new Error(errorMsg), false)); + return; + } + + try { + const addresses = yield call(getAllAddresses, wallet); + log.log('All wallet addresses loaded with success.'); + yield put(selectAddressAddressesSuccess({ addresses })); + } catch (error) { + log.error('Error while fetching all wallet addresses.', error); + // This will show the message in the feedback content at SelectAddressModal + yield put(selectAddressAddressesFailure({ + error: t`There was an error while loading wallet addresses. Try again.` + })); + } +} + export function* saga() { yield all([ takeLatest('START_WALLET_REQUESTED', errorHandler(startWallet, startWalletFailed())), @@ -735,6 +766,7 @@ export function* saga() { takeEvery('WALLET_UPDATE_TX', handleTx), takeEvery('WALLET_BEST_BLOCK_UPDATE', bestBlockUpdate), takeEvery('WALLET_PARTIAL_UPDATE', loadPartialUpdate), + takeEvery(types.SELECTADDRESS_ADDRESSES_REQUEST, fetchAllWalletAddresses), takeEvery(types.WALLET_REFRESH_SHARED_ADDRESS, refreshSharedAddress), ]); } diff --git a/src/screens/NanoContract/NanoContractDetailsScreen.js b/src/screens/NanoContract/NanoContractDetailsScreen.js new file mode 100644 index 000000000..b1e67c834 --- /dev/null +++ b/src/screens/NanoContract/NanoContractDetailsScreen.js @@ -0,0 +1,60 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import { + StyleSheet, + View, +} from 'react-native'; +import { useSelector } from 'react-redux'; +import { t } from 'ttag'; +import HathorHeader from '../../components/HathorHeader'; +import { NanoContractDetails } from '../../components/NanoContract/NanoContractDetails.component'; +import OfflineBar from '../../components/OfflineBar'; +import { COLORS } from '../../styles/themes'; + +/** + * Presents an information summary of the Nano Contract, a list of transactions + * and provides some actions to users, for instance: + * - Open Nano Contract details at the Explorer + * - Edit the registered address for the Nano Contract + * - Unregister the Nano Contract + */ +export function NanoContractDetailsScreen({ navigation, route }) { + /* Without this default the app breaks after the current Nano Contract unregistration. + * By having a default value the app can render the screen normally after unregistration + * and let it step aside while coming back to Dashboard screen. This transition happens + * quickly, therefore the user will not have time to see the default state. + */ + const defaultNc = { ncId: '', address: '' }; + const { ncId } = route.params; + const nc = useSelector((state) => state.nanoContract.registered[ncId]) || defaultNc; + return ( + + navigation.goBack()} + /> + + + + ); +} + +const Wrapper = ({ children }) => ( + + {children} + +); + +const styles = StyleSheet.create({ + wrapper: { + flex: 1, + alignItems: 'center', + backgroundColor: COLORS.lowContrastDetail, // Defines an outer area on the main list content + }, +}); diff --git a/src/store.js b/src/store.js index f39675617..e2e9e04bf 100644 --- a/src/store.js +++ b/src/store.js @@ -162,6 +162,19 @@ class HybridStore extends MemoryStore { STORE.setItem(REGISTERED_NANO_CONTRACTS_KEY, contracts); } + /** + * Iterate on registered nano contract. + * + * @async + * @returns {AsyncGenerator} + */ + async* registeredNanoContractsIter() { + const contracts = STORE.getItem(REGISTERED_NANO_CONTRACTS_KEY) || {}; + for (const contract of Object.values(contracts)) { + yield { ...contract }; + } + } + /** * Clean the storage. * @param {boolean} cleanHistory if we should clean the transaction history. @@ -178,6 +191,22 @@ class HybridStore extends MemoryStore { STORE.removeItem(REGISTERED_NANO_CONTRACTS_KEY); } } + + /** + * Update nano contract registered address. + * @param {string} ncId Nano Contract ID. + * @param {string} address Nano Contract registered address. + * @async + * @returns {Promise} + */ + async updateNanoContractRegisteredAddress(ncId, address) { + await super.updateNanoContractRegisteredAddress(ncId, address); + const contract = await this.getNanoContract(ncId); + if (contract) { + const newContract = { ...contract, address }; + await this.registerNanoContract(ncId, newContract); + } + } } /* eslint-enable class-methods-use-this */ diff --git a/src/utils.js b/src/utils.js index 281c5d790..4b369a6d4 100644 --- a/src/utils.js +++ b/src/utils.js @@ -413,3 +413,44 @@ export const getNanoContractFeatureToggle = (state) => ( * @returns {string} formatted timestamp */ export const getTimestampFormat = (timestamp) => moment.unix(timestamp).format(t`DD MMM YYYY [•] HH:mm`) + +/** + * Extract all the items of an async iterator/generator. + * + * @returns {Promise} A promise of an array of unkown object. + * @async + */ +export const consumeAsyncIterator = async (asyncIterator) => { + const list = []; + for (;;) { + /* eslint-disable no-await-in-loop */ + const objYielded = await asyncIterator.next(); + const { value, done } = objYielded; + + if (done) { + break; + } + + list.push(value); + } + return [...list]; +}; + +/** + * Return all addresses of the wallet with info of each of them. + * + * @param {Object} wallet + * + * @returns {Promise<{ + * address: string; + * index: number; + * transactions: number; + * }[]>} a list of addres info. + * + * @throws {Error} either wallet not ready or other http request error if using wallet service. + * @async + */ +export const getAllAddresses = async (wallet) => { + const iterator = await wallet.getAllAddresses(); + return consumeAsyncIterator(iterator); +} From 99f49971b2698a140a727d3e68ceb0247c09b932 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Fri, 14 Jun 2024 03:26:18 +0100 Subject: [PATCH 22/51] feat: add NanoContractTransaction screen [12] (#440) * feat: add nano contract transaction screen * feat: add nano contract transaction components * Add FeedbackContent for no input/output on NanoContractTransactionBalanceList * fix: navigation to NanoContractTransactionScreen --- locale/da/texts.po | 20 ++ locale/pt-br/texts.po | 20 ++ locale/ru-ru/texts.po | 20 ++ locale/texts.pot | 20 ++ src/App.js | 2 + src/components/FeedbackContent.js | 21 +- src/components/Icons/Received.icon.js | 53 +++++ src/components/Icons/Sent.icon.js | 53 +++++ .../NanoContractDetails.component.js | 2 +- ...NanoContractTransactionHeader.component.js | 215 ++++++++++++++++++ src/components/TextValue.js | 8 +- .../NanoContractTransactionScreen.js | 60 +++++ 12 files changed, 484 insertions(+), 10 deletions(-) create mode 100644 src/components/Icons/Received.icon.js create mode 100644 src/components/Icons/Sent.icon.js create mode 100644 src/components/NanoContract/NanoContractTransactionHeader.component.js create mode 100644 src/screens/NanoContract/NanoContractTransactionScreen.js diff --git a/locale/da/texts.po b/locale/da/texts.po index 78f9a492b..58cc85363 100644 --- a/locale/da/texts.po +++ b/locale/da/texts.po @@ -931,6 +931,10 @@ msgstr "" msgid "Nano Contract Details" msgstr "" +#: src/screens/NanoContract/NanoContractTransactionScreen.js:37 +msgid "Nano Contract Transaction" +msgstr "" + #: src/sagas/mixins.js:31 msgid "Wallet is not ready to load addresses." msgstr "" @@ -1178,15 +1182,18 @@ msgstr "" msgid "Description" msgstr "Beskrivelse" +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:44 #: src/components/TxDetailsModal.js:104 msgid "Transaction ID" msgstr "" +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:89 #: src/components/TxDetailsModal.js:105 msgid "Blueprint Method" msgstr "" #: src/components/NanoContract/NanoContractDetailsHeader.component.js:142 +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:85 #: src/components/NanoContract/NanoContractsListItem.js:57 #: src/components/TxDetailsModal.js:106 msgid "Nano Contract ID" @@ -1298,10 +1305,23 @@ msgstr "" msgid "Unregister contract" msgstr "" +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:93 +msgid "Date and Time" +msgstr "" + +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:100 #: src/components/NanoContract/NanoContractTransactionsListItem.component.js:62 msgid "From this wallet" msgstr "" +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:103 +msgid "Caller" +msgstr "" + +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:106 +msgid "See transaction details" +msgstr "" + #: src/components/NanoContract/NanoContractsList.js:85 msgid "No Nano Contracts" msgstr "" diff --git a/locale/pt-br/texts.po b/locale/pt-br/texts.po index 538ee763a..8181b97a4 100644 --- a/locale/pt-br/texts.po +++ b/locale/pt-br/texts.po @@ -957,6 +957,10 @@ msgstr "" msgid "Nano Contract Details" msgstr "Detalhes do Nano Contract" +#: src/screens/NanoContract/NanoContractTransactionScreen.js:37 +msgid "Nano Contract Transaction" +msgstr "Transação do Nano Contract" + #: src/sagas/mixins.js:31 msgid "Wallet is not ready to load addresses." msgstr "A wallet não está pronta para carregar endereços." @@ -1207,15 +1211,18 @@ msgstr "Inválida" msgid "Description" msgstr "Descrição" +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:44 #: src/components/TxDetailsModal.js:104 msgid "Transaction ID" msgstr "ID da Transação" +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:89 #: src/components/TxDetailsModal.js:105 msgid "Blueprint Method" msgstr "Método do Blueprint" #: src/components/NanoContract/NanoContractDetailsHeader.component.js:142 +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:85 #: src/components/NanoContract/NanoContractsListItem.js:57 #: src/components/TxDetailsModal.js:106 msgid "Nano Contract ID" @@ -1336,10 +1343,23 @@ msgstr "Ver detalhes" msgid "Unregister contract" msgstr "Desregistrar contract" +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:93 +msgid "Date and Time" +msgstr "Data & Hora" + +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:100 #: src/components/NanoContract/NanoContractTransactionsListItem.component.js:62 msgid "From this wallet" msgstr "Desta wallet" +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:103 +msgid "Caller" +msgstr "Caller" + +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:106 +msgid "See transaction details" +msgstr "Ver detalhes" + #: src/components/NanoContract/NanoContractsList.js:85 msgid "No Nano Contracts" msgstr "Nenhum Nano Contract" diff --git a/locale/ru-ru/texts.po b/locale/ru-ru/texts.po index 8ee6e2dcd..b1b1c2388 100644 --- a/locale/ru-ru/texts.po +++ b/locale/ru-ru/texts.po @@ -935,6 +935,10 @@ msgstr "" msgid "Nano Contract Details" msgstr "" +#: src/screens/NanoContract/NanoContractTransactionScreen.js:37 +msgid "Nano Contract Transaction" +msgstr "" + #: src/sagas/mixins.js:31 msgid "Wallet is not ready to load addresses." msgstr "" @@ -1167,15 +1171,18 @@ msgstr "" msgid "Description" msgstr "Описание" +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:44 #: src/components/TxDetailsModal.js:104 msgid "Transaction ID" msgstr "" +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:89 #: src/components/TxDetailsModal.js:105 msgid "Blueprint Method" msgstr "" #: src/components/NanoContract/NanoContractDetailsHeader.component.js:142 +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:85 #: src/components/NanoContract/NanoContractsListItem.js:57 #: src/components/TxDetailsModal.js:106 msgid "Nano Contract ID" @@ -1287,10 +1294,23 @@ msgstr "" msgid "Unregister contract" msgstr "" +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:93 +msgid "Date and Time" +msgstr "" + +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:100 #: src/components/NanoContract/NanoContractTransactionsListItem.component.js:62 msgid "From this wallet" msgstr "" +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:103 +msgid "Caller" +msgstr "" + +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:106 +msgid "See transaction details" +msgstr "" + #: src/components/NanoContract/NanoContractsList.js:85 msgid "No Nano Contracts" msgstr "" diff --git a/locale/texts.pot b/locale/texts.pot index 7a93a0d41..1b645b232 100644 --- a/locale/texts.pot +++ b/locale/texts.pot @@ -928,6 +928,10 @@ msgstr "" msgid "Nano Contract Details" msgstr "" +#: src/screens/NanoContract/NanoContractTransactionScreen.js:37 +msgid "Nano Contract Transaction" +msgstr "" + #: src/sagas/mixins.js:31 msgid "Wallet is not ready to load addresses." msgstr "" @@ -1160,15 +1164,18 @@ msgstr "" msgid "Description" msgstr "" +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:44 #: src/components/TxDetailsModal.js:104 msgid "Transaction ID" msgstr "" +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:89 #: src/components/TxDetailsModal.js:105 msgid "Blueprint Method" msgstr "" #: src/components/NanoContract/NanoContractDetailsHeader.component.js:142 +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:85 #: src/components/NanoContract/NanoContractsListItem.js:57 #: src/components/TxDetailsModal.js:106 msgid "Nano Contract ID" @@ -1280,10 +1287,23 @@ msgstr "" msgid "Unregister contract" msgstr "" +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:93 +msgid "Date and Time" +msgstr "" + +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:100 #: src/components/NanoContract/NanoContractTransactionsListItem.component.js:62 msgid "From this wallet" msgstr "" +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:103 +msgid "Caller" +msgstr "" + +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:106 +msgid "See transaction details" +msgstr "" + #: src/components/NanoContract/NanoContractsList.js:85 msgid "No Nano Contracts" msgstr "" diff --git a/src/App.js b/src/App.js index f86d6f8d8..93a324459 100644 --- a/src/App.js +++ b/src/App.js @@ -87,6 +87,7 @@ import { COLORS, HathorTheme } from './styles/themes'; import { NetworkSettingsFlowNav, NetworkSettingsFlowStack } from './screens/NetworkSettings'; import { NetworkStatusBar } from './components/NetworkSettings/NetworkStatusBar'; import { NanoContractDetailsScreen } from './screens/NanoContract/NanoContractDetailsScreen'; +import { NanoContractTransactionScreen } from './screens/NanoContract/NanoContractTransactionScreen'; /** * This Stack Navigator is exhibited when there is no wallet initialized on the local storage. @@ -378,6 +379,7 @@ const AppStack = () => { component={TabNavigator} /> + diff --git a/src/components/FeedbackContent.js b/src/components/FeedbackContent.js index 30ab1bb37..b50e5db1b 100644 --- a/src/components/FeedbackContent.js +++ b/src/components/FeedbackContent.js @@ -35,19 +35,21 @@ import { COLORS } from '../styles/themes'; * /> */ export const FeedbackContent = ({ title, message, icon, action, offcard }) => ( - - - {icon - && ({icon})} - {title} - {message} - {action && (action)} + + + + {icon + && ({icon})} + {title} + {message} + {action && (action)} + ); const styles = StyleSheet.create({ - wrapper: { + container: { flex: 1, justifyContent: 'center', alignSelf: 'stretch', @@ -63,6 +65,9 @@ const styles = StyleSheet.create({ shadowColor: COLORS.textColor, shadowOpacity: 0.08, }, + wrapper: { + overflow: 'scroll', + }, content: { display: 'flex', alignItems: 'center', diff --git a/src/components/Icons/Received.icon.js b/src/components/Icons/Received.icon.js new file mode 100644 index 000000000..cf71232b0 --- /dev/null +++ b/src/components/Icons/Received.icon.js @@ -0,0 +1,53 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import * as React from 'react' +import Svg, { G, Path, Defs, ClipPath } from 'react-native-svg' +import { COLORS } from '../../styles/themes'; +import { BaseIcon } from './Base.icon'; +import { DEFAULT_ICON_SIZE } from './constants'; +import { getScale, getViewBox } from './helper'; + +/** + * @param {object} props + * @param {'default'|'outline'|'fill'} props.type + * @property {number} props.size + * @property {string} props.color + * @property {string} props.backgroundColor + * + * @description + * Svg converted from Figma using transaformer at https://react-svgr.com/playground/?native=true + */ +export const ReceivedIcon = ({ type = 'default', size = DEFAULT_ICON_SIZE, color = 'hsla(180, 85%, 34%, 1)', backgroundColor = COLORS.white }) => ( + + + + + + + + + + + + +); diff --git a/src/components/Icons/Sent.icon.js b/src/components/Icons/Sent.icon.js new file mode 100644 index 000000000..ce8ce3129 --- /dev/null +++ b/src/components/Icons/Sent.icon.js @@ -0,0 +1,53 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import * as React from 'react' +import Svg, { G, Path, Defs, ClipPath } from 'react-native-svg' +import { COLORS } from '../../styles/themes'; +import { BaseIcon } from './Base.icon'; +import { DEFAULT_ICON_SIZE } from './constants'; +import { getScale, getViewBox } from './helper'; + +/** + * @param {object} props + * @param {'default'|'outline'|'fill'} props.type + * @property {number} props.size + * @property {string} props.color + * @property {string} props.backgroundColor + * + * @description + * Svg converted from Figma using transaformer at https://react-svgr.com/playground/?native=true + */ +export const SentIcon = ({ type = 'default', size = DEFAULT_ICON_SIZE, color = COLORS.black, backgroundColor = COLORS.white }) => ( + + + + + + + + + + + + +); diff --git a/src/components/NanoContract/NanoContractDetails.component.js b/src/components/NanoContract/NanoContractDetails.component.js index 9f42113f1..9ed30d241 100644 --- a/src/components/NanoContract/NanoContractDetails.component.js +++ b/src/components/NanoContract/NanoContractDetails.component.js @@ -74,7 +74,7 @@ export const NanoContractDetails = ({ nc }) => { } const navigatesToNanoContractTransaction = (tx) => { - navigation.navigate('NanoContractTransaction', { tx }); + navigation.navigate('NanoContractTransactionScreen', { tx }); }; useEffect(() => { diff --git a/src/components/NanoContract/NanoContractTransactionHeader.component.js b/src/components/NanoContract/NanoContractTransactionHeader.component.js new file mode 100644 index 000000000..57940c2d1 --- /dev/null +++ b/src/components/NanoContract/NanoContractTransactionHeader.component.js @@ -0,0 +1,215 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React, { useState } from 'react'; +import { + StyleSheet, + View, + TouchableWithoutFeedback, + Linking, + Text, +} from 'react-native'; +import { t } from 'ttag'; +import { useSelector } from 'react-redux'; +import HathorHeader from '../HathorHeader'; +import { COLORS } from '../../styles/themes'; +import { combineURLs, getShortContent, getShortHash, getTimestampFormat } from '../../utils'; +import SimpleButton from '../SimpleButton'; +import { ArrowDownIcon } from '../Icons/ArrowDown.icon'; +import { TextValue } from '../TextValue'; +import { TextLabel } from '../TextLabel'; +import { TransactionStatusLabel } from '../TransactionStatusLabel'; + +/** + * It presents the header of Nano Contract Transaction screen. + * + * @param {Obejct} props + * @param {Obejct} props.tx Transaction data + */ +export const NanoContractTransactionHeader = ({ tx }) => { + // XXX: the set function for the state is beeing ignored because we can't + // use the shrank format just yet. We need the actions component first. + // For his, we also need hathor-core support for actions in each nano contract + // transaction. + const [isShrank, _] = useState(false); + + return ( + + + {}}> + + + {getShortHash(tx.txId, 7)} + {t`Transaction ID`} + + {isShrank ? : } + + + + + ) +}; + +const HeaderShrank = () => ( + +); + +/** + * It presents the expanded header of Nano Contract Transaction screen + * containing contextual information about the Nano Contract and the transaction. + * + * @param {Obejct} props + * @param {Obejct} props.tx Transaction data + */ +const HeaderExpanded = ({ tx }) => { + const baseExplorerUrl = useSelector((state) => state.networkSettings.explorerUrl); + const ncId = getShortHash(tx.ncId, 7); + const callerAddr = getShortContent(tx.caller, 7); + const hasFirstBlock = tx.firstBlock != null; + + const navigatesToExplorer = () => { + const txUrl = `transaction/${tx.txId}`; + const explorerLink = combineURLs(baseExplorerUrl, txUrl); + Linking.openURL(explorerLink); + }; + + /* XXX: add when shrank component can be used. */ + return ( + + + + + + {ncId} + {t`Nano Contract ID`} + + + {tx.ncMethod} + {t`Blueprint Method`} + + + {getTimestampFormat(tx.timestamp)} + {t`Date and Time`} + + + {callerAddr} + {tx.isMine + && ( + + {t`From this wallet`} + + )} + {t`Caller`} + + + + + + ) +}; + +/** + * Container for value and label pair components. + * + * @param {Object} props + * @param {Object} props.children + */ +const InfoContainer = ({ lastElement, children }) => ( + + {children} + +); + +/** + * It presents the action button as inline text. It can contain at maximum two actions. + * + * @param {Object} props + * @param {Object} props.children + */ +const ActionsWrapper = ({ children }) => ( + + {children} + +); + +/** + * Text button in primary color and style. + * + * @param {Object} props + * @param {string} props.title Text content + * @param {() => void} props.onPress Callback for interaction + */ +const PrimaryTextButton = ({ title, onPress }) => ( + +); + +const styles = StyleSheet.create({ + headerCentral: { + flex: 1, + alignItems: 'center', + paddingTop: 24, + }, + headerWrapper: { + alignItems: 'center', + }, + headerTitle: { + fontSize: 18, + lineHeight: 20, + fontWeight: 'bold', + paddingVertical: 16, + }, + wrapper: { + paddingHorizontal: 16, + paddingBottom: 16, + }, + infoContainer: { + alignItems: 'center', + paddingBottom: 16, + }, + lastElement: { + paddingBottom: 0, + }, + actionsWrapper: { + flexDirection: 'row', + justifyContent: 'center', + flexWrap: 'nowrap', + }, + buttonWrapper: { + paddingTop: 24, + }, + buttonText: { + fontWeight: 'bold', + }, + buttonUnregister: { + marginStart: 24, + color: 'hsla(0, 100%, 41%, 1)', + }, + buttonDetails: { + display: 'inline-block', + /* We are using negative margin here to correct the text position + * and create an optic effect of alignment. */ + marginBottom: -2, + borderBottomWidth: 1, + borderColor: COLORS.primary, + }, + headlineLabel: { + marginVertical: 6, + borderRadius: 20, + paddingHorizontal: 12, + paddingVertical: 2, + backgroundColor: COLORS.freeze100, + }, + isMineLabel: { + fontSize: 12, + lineHeight: 20, + }, +}); diff --git a/src/components/TextValue.js b/src/components/TextValue.js index 98c74bf78..f377e069c 100644 --- a/src/components/TextValue.js +++ b/src/components/TextValue.js @@ -13,14 +13,16 @@ import { /** * @param {Object} props + * @param {boolean} props.title It sets font weight to bold and a larger font size * @param {boolean} props.bold It sets font weight to bold * @param {boolean} props.oneline It sets numberOfLines to 1 * @param {boolean} props.shrink It sets flexShrink to 1 * @param {boolean} props.pb4 It sets padding bottom to 4 */ -export const TextValue = ({ bold, oneline, shrink, pb4, children }) => ( +export const TextValue = ({ title, bold, oneline, shrink, pb4, children }) => ( + + + + + + + ); +} + +const NavigationHeader = ({ navigation }) => ( + navigation.goBack()} + /> +); + +const Wrapper = ({ children }) => ( + + {children} + +); + +const ContentWrapper = ({ children }) => ( + + {children} + +); + +const styles = StyleSheet.create({ + wrapper: { + flex: 1, + alignItems: 'center', + backgroundColor: COLORS.lowContrastDetail, // Defines an outer area on the main list content + }, +}); From 2c664328e8c29b053963617aaba814b40c5d62c2 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Tue, 18 Jun 2024 17:28:02 +0100 Subject: [PATCH 23/51] feat: add nano contract register screen [13] (#466) - Add firstBlock to nano contract transaction on history - Feat: improve NanoContractTransactionsList component - Add feedback content for empty list - Add feedback content for error on load - Create FeedbackContent component - Remove NoNanoContract component - Refactor NanoContractsList component to use FeedbackContent component - Refactor: use replace on navigation in place of navigate - Feat: add feedback content for the first address loading - Refactor: move fetch first wallet address to saga effect - Refactor: move fetchFirstWalletAddress to sagas/wallet - Refactor: extract hasError to utils - Fix: avoid show success feedback on register screen because of initialization load --- locale/da/texts.po | 112 ++++-- locale/pt-br/texts.po | 116 +++++-- locale/ru-ru/texts.po | 112 ++++-- locale/texts.pot | 112 ++++-- src/App.js | 2 + src/actions.js | 47 ++- src/components/FeedbackModal.js | 29 +- src/components/HathorModal.js | 8 +- ...NanoContractTransactionHeader.component.js | 2 +- .../RegisterNewNanoContractButton.js | 2 +- src/constants.js | 7 + src/reducers/reducer.js | 144 +++++++- src/sagas/nanoContract.js | 12 +- src/sagas/wallet.js | 44 ++- .../NanoContractRegisterScreen.js | 327 ++++++++++++++++++ src/screens/NanoContract/helper.js | 47 +++ src/screens/NetworkSettings/helper.js | 7 + src/utils.js | 21 ++ 18 files changed, 1004 insertions(+), 147 deletions(-) create mode 100644 src/screens/NanoContract/NanoContractRegisterScreen.js create mode 100644 src/screens/NanoContract/helper.js diff --git a/locale/da/texts.po b/locale/da/texts.po index 58cc85363..a0eff693a 100644 --- a/locale/da/texts.po +++ b/locale/da/texts.po @@ -434,6 +434,7 @@ msgstr "Indtast dine seed-ord adskilt med mellemrum" #: src/components/NanoContract/NanoContractDetails.component.js:194 #: src/screens/LoadHistoryScreen.js:51 src/screens/LoadWalletErrorScreen.js:20 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:167 msgid "Try again" msgstr "" @@ -852,17 +853,17 @@ msgid "" msgstr "" #: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:20 -#: src/screens/NetworkSettings/helper.js:4 +#: src/screens/NetworkSettings/helper.js:11 msgid "Updating custom network settings..." msgstr "" #: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:21 -#: src/screens/NetworkSettings/helper.js:5 +#: src/screens/NetworkSettings/helper.js:12 msgid "Network settings successfully customized." msgstr "" #: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:22 -#: src/screens/NetworkSettings/helper.js:6 +#: src/screens/NetworkSettings/helper.js:13 msgid "" "There was an error while customizing network settings. Please try again " "later." @@ -931,17 +932,62 @@ msgstr "" msgid "Nano Contract Details" msgstr "" -#: src/screens/NanoContract/NanoContractTransactionScreen.js:37 -msgid "Nano Contract Transaction" +#: src/screens/NanoContract/NanoContractRegisterScreen.js:67 +msgid "Nano Contract ID is required." msgstr "" -#: src/sagas/mixins.js:31 -msgid "Wallet is not ready to load addresses." +#: src/screens/NanoContract/NanoContractRegisterScreen.js:157 +msgid "See contract" msgstr "" -#. This will show the message in the feedback content at SelectAddressModal -#: src/sagas/mixins.js:47 -msgid "There was an error while loading wallet addresses. Try again." +#: src/screens/NanoContract/NanoContractRegisterScreen.js:175 +msgid "Load First Addresses Error" +msgstr "" + +#: src/components/NanoContract/NanoContractDetails.component.js:158 +#: src/components/NanoContract/SelectAddressModal.component.js:105 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:183 +msgid "Loading" +msgstr "" + +#: src/screens/NanoContract/NanoContractRegisterScreen.js:184 +msgid "Loading first wallet address." +msgstr "" + +#: src/components/NanoContract/NanoContractDetailsHeader.component.js:142 +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:88 +#: src/components/NanoContract/NanoContractsListItem.js:57 +#: src/components/TxDetailsModal.js:106 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:193 +msgid "Nano Contract ID" +msgstr "" + +#: src/screens/NanoContract/NanoContractRegisterScreen.js:201 +msgid "Wallet Address" +msgstr "" + +#: src/screens/NanoContract/NanoContractRegisterScreen.js:211 +msgid "If you want to change the wallet address, you will be able to do" +msgstr "" + +#: src/screens/NanoContract/NanoContractRegisterScreen.js:213 +msgid "after the contract is registered." +msgstr "" + +#: src/screens/NanoContract/NanoContractRegisterScreen.js:232 +msgid "Register Nano Contract" +msgstr "" + +#: src/screens/NanoContract/NanoContractRegisterScreen.js:250 +msgid "Nano Contract Registration" +msgstr "" + +#: src/screens/NanoContract/NanoContractTransactionScreen.js:37 +msgid "Nano Contract Transaction" +msgstr "" + +#: src/screens/NanoContract/helper.js:11 +msgid "Contract successfully registered." msgstr "" #: src/sagas/nanoContract.js:43 @@ -1014,6 +1060,24 @@ msgstr "Transaktion" msgid "Open" msgstr "Åben" +#: src/sagas/wallet.js:744 +msgid "Wallet is not ready to load addresses." +msgstr "" + +#. This will show the message in the feedback content at SelectAddressModal +#: src/sagas/wallet.js:760 +msgid "There was an error while loading wallet addresses. Try again." +msgstr "" + +#: src/sagas/wallet.js:770 +msgid "Wallet is not ready to load the first address." +msgstr "" + +#. This will show the message in the feedback content +#: src/sagas/wallet.js:786 +msgid "There was an error while loading first wallet address. Try again." +msgstr "" + #: src/components/AskForPushNotification.js:29 msgid "Do you want to enable push notifications for this wallet?" msgstr "" @@ -1182,23 +1246,16 @@ msgstr "" msgid "Description" msgstr "Beskrivelse" -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:44 +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:47 #: src/components/TxDetailsModal.js:104 msgid "Transaction ID" msgstr "" -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:89 +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:92 #: src/components/TxDetailsModal.js:105 msgid "Blueprint Method" msgstr "" -#: src/components/NanoContract/NanoContractDetailsHeader.component.js:142 -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:85 -#: src/components/NanoContract/NanoContractsListItem.js:57 -#: src/components/TxDetailsModal.js:106 -msgid "Nano Contract ID" -msgstr "" - #: src/components/TxDetailsModal.js:107 msgid "Nano Contract Caller" msgstr "" @@ -1275,11 +1332,6 @@ msgstr "" msgid "Go back" msgstr "" -#: src/components/NanoContract/NanoContractDetails.component.js:158 -#: src/components/NanoContract/SelectAddressModal.component.js:105 -msgid "Loading" -msgstr "" - #: src/components/NanoContract/NanoContractDetails.component.js:159 msgid "Loading Nano Contract transactions." msgstr "" @@ -1305,20 +1357,24 @@ msgstr "" msgid "Unregister contract" msgstr "" -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:93 +#. XXX: add when shrank component can be used. +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:96 msgid "Date and Time" msgstr "" -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:100 +#. XXX: add when shrank component can be used. +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:103 #: src/components/NanoContract/NanoContractTransactionsListItem.component.js:62 msgid "From this wallet" msgstr "" -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:103 +#. XXX: add when shrank component can be used. +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:106 msgid "Caller" msgstr "" -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:106 +#. XXX: add when shrank component can be used. +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:109 msgid "See transaction details" msgstr "" diff --git a/locale/pt-br/texts.po b/locale/pt-br/texts.po index 8181b97a4..8e5a2d442 100644 --- a/locale/pt-br/texts.po +++ b/locale/pt-br/texts.po @@ -445,6 +445,7 @@ msgstr "Digite suas palavras separadas por espaços" #: src/components/NanoContract/NanoContractDetails.component.js:194 #: src/screens/LoadHistoryScreen.js:51 src/screens/LoadWalletErrorScreen.js:20 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:167 msgid "Try again" msgstr "Tente novamente" @@ -873,17 +874,17 @@ msgstr "" "souber o que está fazendo." #: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:20 -#: src/screens/NetworkSettings/helper.js:4 +#: src/screens/NetworkSettings/helper.js:11 msgid "Updating custom network settings..." msgstr "Atualizando configurações de rede..." #: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:21 -#: src/screens/NetworkSettings/helper.js:5 +#: src/screens/NetworkSettings/helper.js:12 msgid "Network settings successfully customized." msgstr "Configuração de rede atualizada com sucesso." #: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:22 -#: src/screens/NetworkSettings/helper.js:6 +#: src/screens/NetworkSettings/helper.js:13 msgid "" "There was an error while customizing network settings. Please try again " "later." @@ -957,18 +958,63 @@ msgstr "" msgid "Nano Contract Details" msgstr "Detalhes do Nano Contract" +#: src/screens/NanoContract/NanoContractRegisterScreen.js:67 +msgid "Nano Contract ID is required." +msgstr "ID do Nano Contract é obrigatório." + +#: src/screens/NanoContract/NanoContractRegisterScreen.js:157 +msgid "See contract" +msgstr "Ver contrato" + +#: src/screens/NanoContract/NanoContractRegisterScreen.js:175 +msgid "Load First Addresses Error" +msgstr "Erro ao carregar primeiro endereço da wallet" + +#: src/components/NanoContract/NanoContractDetails.component.js:158 +#: src/components/NanoContract/SelectAddressModal.component.js:105 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:183 +msgid "Loading" +msgstr "Carregando" + +#: src/screens/NanoContract/NanoContractRegisterScreen.js:184 +msgid "Loading first wallet address." +msgstr "Carregando primeiro endereço da wallet." + +#: src/components/NanoContract/NanoContractDetailsHeader.component.js:142 +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:88 +#: src/components/NanoContract/NanoContractsListItem.js:57 +#: src/components/TxDetailsModal.js:106 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:193 +msgid "Nano Contract ID" +msgstr "ID do Nano Contract" + +#: src/screens/NanoContract/NanoContractRegisterScreen.js:201 +msgid "Wallet Address" +msgstr "Endereço da Carteira" + +#: src/screens/NanoContract/NanoContractRegisterScreen.js:211 +msgid "If you want to change the wallet address, you will be able to do" +msgstr "Se deseja alterar o endereço de assinatura, você pode alterar" + +#: src/screens/NanoContract/NanoContractRegisterScreen.js:213 +msgid "after the contract is registered." +msgstr "depois do contrato ser registrado." + +#: src/screens/NanoContract/NanoContractRegisterScreen.js:232 +msgid "Register Nano Contract" +msgstr "Registrar Nano Contract" + +#: src/screens/NanoContract/NanoContractRegisterScreen.js:250 +msgid "Nano Contract Registration" +msgstr "Registro do Nano Contract" + #: src/screens/NanoContract/NanoContractTransactionScreen.js:37 msgid "Nano Contract Transaction" msgstr "Transação do Nano Contract" -#: src/sagas/mixins.js:31 -msgid "Wallet is not ready to load addresses." -msgstr "A wallet não está pronta para carregar endereços." - -#. This will show the message in the feedback content at SelectAddressModal -#: src/sagas/mixins.js:47 -msgid "There was an error while loading wallet addresses. Try again." -msgstr "Ocorreu um erro durante download dos endereços da wallet. Tente novamente." +#: src/screens/NanoContract/helper.js:11 +msgid "Contract successfully registered." +msgstr "Nano Contract registrado com sucesso." #: src/sagas/nanoContract.js:43 msgid "Nano Contract already registered." @@ -1041,6 +1087,26 @@ msgstr "Transação" msgid "Open" msgstr "Abrir" +#: src/sagas/wallet.js:744 +msgid "Wallet is not ready to load addresses." +msgstr "A wallet não está pronta para carregar os endereços." + +#. This will show the message in the feedback content at SelectAddressModal +#: src/sagas/wallet.js:760 +msgid "There was an error while loading wallet addresses. Try again." +msgstr "" +"Ocorreu um erro ao carregar os endereços da wallet. Tente novamente." + +#: src/sagas/wallet.js:770 +msgid "Wallet is not ready to load the first address." +msgstr "A wallet não está pronta para carregar o primeiro endereço." + +#. This will show the message in the feedback content +#: src/sagas/wallet.js:786 +msgid "There was an error while loading first wallet address. Try again." +msgstr "" +"Ocorreu um erro ao carregar o primeiro endereço da wallet. Tente novamente." + #: src/components/AskForPushNotification.js:29 msgid "Do you want to enable push notifications for this wallet?" msgstr "Você deseja habilitar as notificações para esta wallet?" @@ -1211,23 +1277,16 @@ msgstr "Inválida" msgid "Description" msgstr "Descrição" -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:44 +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:47 #: src/components/TxDetailsModal.js:104 msgid "Transaction ID" msgstr "ID da Transação" -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:89 +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:92 #: src/components/TxDetailsModal.js:105 msgid "Blueprint Method" msgstr "Método do Blueprint" -#: src/components/NanoContract/NanoContractDetailsHeader.component.js:142 -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:85 -#: src/components/NanoContract/NanoContractsListItem.js:57 -#: src/components/TxDetailsModal.js:106 -msgid "Nano Contract ID" -msgstr "ID do Nano Contract" - #: src/components/TxDetailsModal.js:107 msgid "Nano Contract Caller" msgstr "Endereço de assinatura do Nano Contract" @@ -1313,11 +1372,6 @@ msgstr "Confirmar novo endereço" msgid "Go back" msgstr "Voltar" -#: src/components/NanoContract/NanoContractDetails.component.js:158 -#: src/components/NanoContract/SelectAddressModal.component.js:105 -msgid "Loading" -msgstr "Carregando" - #: src/components/NanoContract/NanoContractDetails.component.js:159 msgid "Loading Nano Contract transactions." msgstr "Carregando transações do Nano Contract" @@ -1343,20 +1397,24 @@ msgstr "Ver detalhes" msgid "Unregister contract" msgstr "Desregistrar contract" -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:93 +#. XXX: add when shrank component can be used. +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:96 msgid "Date and Time" msgstr "Data & Hora" -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:100 +#. XXX: add when shrank component can be used. +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:103 #: src/components/NanoContract/NanoContractTransactionsListItem.component.js:62 msgid "From this wallet" msgstr "Desta wallet" -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:103 +#. XXX: add when shrank component can be used. +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:106 msgid "Caller" msgstr "Caller" -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:106 +#. XXX: add when shrank component can be used. +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:109 msgid "See transaction details" msgstr "Ver detalhes" diff --git a/locale/ru-ru/texts.po b/locale/ru-ru/texts.po index b1b1c2388..95fb124b3 100644 --- a/locale/ru-ru/texts.po +++ b/locale/ru-ru/texts.po @@ -435,6 +435,7 @@ msgstr "Введите seed-фразу" #: src/components/NanoContract/NanoContractDetails.component.js:194 #: src/screens/LoadHistoryScreen.js:51 src/screens/LoadWalletErrorScreen.js:20 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:167 msgid "Try again" msgstr "" @@ -856,17 +857,17 @@ msgid "" msgstr "" #: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:20 -#: src/screens/NetworkSettings/helper.js:4 +#: src/screens/NetworkSettings/helper.js:11 msgid "Updating custom network settings..." msgstr "" #: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:21 -#: src/screens/NetworkSettings/helper.js:5 +#: src/screens/NetworkSettings/helper.js:12 msgid "Network settings successfully customized." msgstr "" #: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:22 -#: src/screens/NetworkSettings/helper.js:6 +#: src/screens/NetworkSettings/helper.js:13 msgid "" "There was an error while customizing network settings. Please try again " "later." @@ -935,17 +936,62 @@ msgstr "" msgid "Nano Contract Details" msgstr "" -#: src/screens/NanoContract/NanoContractTransactionScreen.js:37 -msgid "Nano Contract Transaction" +#: src/screens/NanoContract/NanoContractRegisterScreen.js:67 +msgid "Nano Contract ID is required." msgstr "" -#: src/sagas/mixins.js:31 -msgid "Wallet is not ready to load addresses." +#: src/screens/NanoContract/NanoContractRegisterScreen.js:157 +msgid "See contract" msgstr "" -#. This will show the message in the feedback content at SelectAddressModal -#: src/sagas/mixins.js:47 -msgid "There was an error while loading wallet addresses. Try again." +#: src/screens/NanoContract/NanoContractRegisterScreen.js:175 +msgid "Load First Addresses Error" +msgstr "" + +#: src/components/NanoContract/NanoContractDetails.component.js:158 +#: src/components/NanoContract/SelectAddressModal.component.js:105 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:183 +msgid "Loading" +msgstr "" + +#: src/screens/NanoContract/NanoContractRegisterScreen.js:184 +msgid "Loading first wallet address." +msgstr "" + +#: src/components/NanoContract/NanoContractDetailsHeader.component.js:142 +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:88 +#: src/components/NanoContract/NanoContractsListItem.js:57 +#: src/components/TxDetailsModal.js:106 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:193 +msgid "Nano Contract ID" +msgstr "" + +#: src/screens/NanoContract/NanoContractRegisterScreen.js:201 +msgid "Wallet Address" +msgstr "" + +#: src/screens/NanoContract/NanoContractRegisterScreen.js:211 +msgid "If you want to change the wallet address, you will be able to do" +msgstr "" + +#: src/screens/NanoContract/NanoContractRegisterScreen.js:213 +msgid "after the contract is registered." +msgstr "" + +#: src/screens/NanoContract/NanoContractRegisterScreen.js:232 +msgid "Register Nano Contract" +msgstr "" + +#: src/screens/NanoContract/NanoContractRegisterScreen.js:250 +msgid "Nano Contract Registration" +msgstr "" + +#: src/screens/NanoContract/NanoContractTransactionScreen.js:37 +msgid "Nano Contract Transaction" +msgstr "" + +#: src/screens/NanoContract/helper.js:11 +msgid "Contract successfully registered." msgstr "" #: src/sagas/nanoContract.js:43 @@ -1018,6 +1064,24 @@ msgstr "" msgid "Open" msgstr "Открыть" +#: src/sagas/wallet.js:744 +msgid "Wallet is not ready to load addresses." +msgstr "" + +#. This will show the message in the feedback content at SelectAddressModal +#: src/sagas/wallet.js:760 +msgid "There was an error while loading wallet addresses. Try again." +msgstr "" + +#: src/sagas/wallet.js:770 +msgid "Wallet is not ready to load the first address." +msgstr "" + +#. This will show the message in the feedback content +#: src/sagas/wallet.js:786 +msgid "There was an error while loading first wallet address. Try again." +msgstr "" + #: src/components/AskForPushNotification.js:29 msgid "Do you want to enable push notifications for this wallet?" msgstr "" @@ -1171,23 +1235,16 @@ msgstr "" msgid "Description" msgstr "Описание" -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:44 +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:47 #: src/components/TxDetailsModal.js:104 msgid "Transaction ID" msgstr "" -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:89 +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:92 #: src/components/TxDetailsModal.js:105 msgid "Blueprint Method" msgstr "" -#: src/components/NanoContract/NanoContractDetailsHeader.component.js:142 -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:85 -#: src/components/NanoContract/NanoContractsListItem.js:57 -#: src/components/TxDetailsModal.js:106 -msgid "Nano Contract ID" -msgstr "" - #: src/components/TxDetailsModal.js:107 msgid "Nano Contract Caller" msgstr "" @@ -1264,11 +1321,6 @@ msgstr "" msgid "Go back" msgstr "" -#: src/components/NanoContract/NanoContractDetails.component.js:158 -#: src/components/NanoContract/SelectAddressModal.component.js:105 -msgid "Loading" -msgstr "" - #: src/components/NanoContract/NanoContractDetails.component.js:159 msgid "Loading Nano Contract transactions." msgstr "" @@ -1294,20 +1346,24 @@ msgstr "" msgid "Unregister contract" msgstr "" -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:93 +#. XXX: add when shrank component can be used. +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:96 msgid "Date and Time" msgstr "" -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:100 +#. XXX: add when shrank component can be used. +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:103 #: src/components/NanoContract/NanoContractTransactionsListItem.component.js:62 msgid "From this wallet" msgstr "" -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:103 +#. XXX: add when shrank component can be used. +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:106 msgid "Caller" msgstr "" -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:106 +#. XXX: add when shrank component can be used. +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:109 msgid "See transaction details" msgstr "" diff --git a/locale/texts.pot b/locale/texts.pot index 1b645b232..f11693ef4 100644 --- a/locale/texts.pot +++ b/locale/texts.pot @@ -424,6 +424,7 @@ msgstr "" #: src/components/NanoContract/NanoContractDetails.component.js:194 #: src/screens/LoadHistoryScreen.js:51 #: src/screens/LoadWalletErrorScreen.js:20 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:167 msgid "Try again" msgstr "" @@ -849,17 +850,17 @@ msgid "" msgstr "" #: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:20 -#: src/screens/NetworkSettings/helper.js:4 +#: src/screens/NetworkSettings/helper.js:11 msgid "Updating custom network settings..." msgstr "" #: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:21 -#: src/screens/NetworkSettings/helper.js:5 +#: src/screens/NetworkSettings/helper.js:12 msgid "Network settings successfully customized." msgstr "" #: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:22 -#: src/screens/NetworkSettings/helper.js:6 +#: src/screens/NetworkSettings/helper.js:13 msgid "" "There was an error while customizing network settings. Please try again " "later." @@ -928,17 +929,62 @@ msgstr "" msgid "Nano Contract Details" msgstr "" -#: src/screens/NanoContract/NanoContractTransactionScreen.js:37 -msgid "Nano Contract Transaction" +#: src/screens/NanoContract/NanoContractRegisterScreen.js:67 +msgid "Nano Contract ID is required." msgstr "" -#: src/sagas/mixins.js:31 -msgid "Wallet is not ready to load addresses." +#: src/screens/NanoContract/NanoContractRegisterScreen.js:157 +msgid "See contract" msgstr "" -#: src/sagas/mixins.js:47 -#. This will show the message in the feedback content at SelectAddressModal -msgid "There was an error while loading wallet addresses. Try again." +#: src/screens/NanoContract/NanoContractRegisterScreen.js:175 +msgid "Load First Addresses Error" +msgstr "" + +#: src/components/NanoContract/NanoContractDetails.component.js:158 +#: src/components/NanoContract/SelectAddressModal.component.js:105 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:183 +msgid "Loading" +msgstr "" + +#: src/screens/NanoContract/NanoContractRegisterScreen.js:184 +msgid "Loading first wallet address." +msgstr "" + +#: src/components/NanoContract/NanoContractDetailsHeader.component.js:142 +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:88 +#: src/components/NanoContract/NanoContractsListItem.js:57 +#: src/components/TxDetailsModal.js:106 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:193 +msgid "Nano Contract ID" +msgstr "" + +#: src/screens/NanoContract/NanoContractRegisterScreen.js:201 +msgid "Wallet Address" +msgstr "" + +#: src/screens/NanoContract/NanoContractRegisterScreen.js:211 +msgid "If you want to change the wallet address, you will be able to do" +msgstr "" + +#: src/screens/NanoContract/NanoContractRegisterScreen.js:213 +msgid "after the contract is registered." +msgstr "" + +#: src/screens/NanoContract/NanoContractRegisterScreen.js:232 +msgid "Register Nano Contract" +msgstr "" + +#: src/screens/NanoContract/NanoContractRegisterScreen.js:250 +msgid "Nano Contract Registration" +msgstr "" + +#: src/screens/NanoContract/NanoContractTransactionScreen.js:37 +msgid "Nano Contract Transaction" +msgstr "" + +#: src/screens/NanoContract/helper.js:11 +msgid "Contract successfully registered." msgstr "" #: src/sagas/nanoContract.js:43 @@ -1011,6 +1057,24 @@ msgstr "" msgid "Open" msgstr "" +#: src/sagas/wallet.js:744 +msgid "Wallet is not ready to load addresses." +msgstr "" + +#: src/sagas/wallet.js:760 +#. This will show the message in the feedback content at SelectAddressModal +msgid "There was an error while loading wallet addresses. Try again." +msgstr "" + +#: src/sagas/wallet.js:770 +msgid "Wallet is not ready to load the first address." +msgstr "" + +#: src/sagas/wallet.js:786 +#. This will show the message in the feedback content +msgid "There was an error while loading first wallet address. Try again." +msgstr "" + #: src/components/AskForPushNotification.js:29 msgid "Do you want to enable push notifications for this wallet?" msgstr "" @@ -1164,23 +1228,16 @@ msgstr "" msgid "Description" msgstr "" -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:44 +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:47 #: src/components/TxDetailsModal.js:104 msgid "Transaction ID" msgstr "" -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:89 +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:92 #: src/components/TxDetailsModal.js:105 msgid "Blueprint Method" msgstr "" -#: src/components/NanoContract/NanoContractDetailsHeader.component.js:142 -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:85 -#: src/components/NanoContract/NanoContractsListItem.js:57 -#: src/components/TxDetailsModal.js:106 -msgid "Nano Contract ID" -msgstr "" - #: src/components/TxDetailsModal.js:107 msgid "Nano Contract Caller" msgstr "" @@ -1257,11 +1314,6 @@ msgstr "" msgid "Go back" msgstr "" -#: src/components/NanoContract/NanoContractDetails.component.js:158 -#: src/components/NanoContract/SelectAddressModal.component.js:105 -msgid "Loading" -msgstr "" - #: src/components/NanoContract/NanoContractDetails.component.js:159 msgid "Loading Nano Contract transactions." msgstr "" @@ -1287,20 +1339,24 @@ msgstr "" msgid "Unregister contract" msgstr "" -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:93 +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:96 +#. XXX: add when shrank component can be used. msgid "Date and Time" msgstr "" -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:100 +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:103 #: src/components/NanoContract/NanoContractTransactionsListItem.component.js:62 +#. XXX: add when shrank component can be used. msgid "From this wallet" msgstr "" -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:103 +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:106 +#. XXX: add when shrank component can be used. msgid "Caller" msgstr "" -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:106 +#: src/components/NanoContract/NanoContractTransactionHeader.component.js:109 +#. XXX: add when shrank component can be used. msgid "See transaction details" msgstr "" diff --git a/src/App.js b/src/App.js index 93a324459..16a6115ec 100644 --- a/src/App.js +++ b/src/App.js @@ -88,6 +88,7 @@ import { NetworkSettingsFlowNav, NetworkSettingsFlowStack } from './screens/Netw import { NetworkStatusBar } from './components/NetworkSettings/NetworkStatusBar'; import { NanoContractDetailsScreen } from './screens/NanoContract/NanoContractDetailsScreen'; import { NanoContractTransactionScreen } from './screens/NanoContract/NanoContractTransactionScreen'; +import { NanoContractRegisterScreen } from './screens/NanoContract/NanoContractRegisterScreen'; /** * This Stack Navigator is exhibited when there is no wallet initialized on the local storage. @@ -380,6 +381,7 @@ const AppStack = () => { /> + diff --git a/src/actions.js b/src/actions.js index 61ef861fe..f134c8fcc 100644 --- a/src/actions.js +++ b/src/actions.js @@ -145,6 +145,8 @@ export const types = { NANOCONTRACT_REGISTER_FAILURE: 'NANOCONTRACT_REGISTER_FAILURE', /* It indicates a Nano Contract registration is complete. */ NANOCONTRACT_REGISTER_SUCCESS: 'NANOCONTRACT_REGISTER_SUCCESS', + /* It updates the redux state of nano contract register status to ready. */ + NANOCONTRACT_REGISTER_READY: 'NANOCONTRACT_REGISTER_READY', /* It indicates a Nano Contract hitory was requested to load. */ NANOCONTRACT_HISTORY_REQUEST: 'NANOCONTRACT_HISTORY_REQUEST', /* It indicates a Nano Contract history is processing. */ @@ -167,6 +169,12 @@ export const types = { SELECTADDRESS_ADDRESSES_SUCCESS: 'SELECTADDRESS_ADDRESSES_SUCCESS', /* It signals a fetch failure due to an error. */ SELECTADDRESS_ADDRESSES_FAILURE: 'SELECTADDRESS_ADDRESSES_FAILURE', + /* It triggers a process to fetch the first wallet address. */ + FIRSTADDRESS_REQUEST: 'FIRSTADDRESS_REQUEST', + /* It signals the fetch has loaded the first address with success. */ + FIRSTADDRESS_SUCCESS: 'FIRSTADDRESS_SUCCESS', + /* It signals a fetch failure due to an error. */ + FIRSTADDRESS_FAILURE: 'FIRSTADDRESS_FAILURE', }; export const featureToggleInitialized = () => ({ @@ -1037,8 +1045,9 @@ export const nanoContractRegisterFailure = (error) => ({ /** * Nano Contract registration has finished with success. * @param {{ - * entryKey: string, - * entryValue: Object, + * entryKey: string; + * entryValue: Object; + * hasFeedback?: boolean; * }} ncEntry basic information of Nano Contract registered. */ export const nanoContractRegisterSuccess = (ncEntry) => ({ @@ -1046,6 +1055,13 @@ export const nanoContractRegisterSuccess = (ncEntry) => ({ payload: ncEntry, }); +/** + * Request a change on Nano Contract register status to ready. + */ +export const nanoContractRegisterReady = () => ({ + type: types.NANOCONTRACT_REGISTER_READY, +}); + /** * Nano Contract request fetch history. * @param {{ @@ -1162,3 +1178,30 @@ export const selectAddressAddressesFailure = (failurePayload) => ({ type: types.SELECTADDRESS_ADDRESSES_FAILURE, payload: failurePayload, }); + +/** + * Request to load first wallet address. + */ +export const firstAddressRequest = () => ({ + type: types.FIRSTADDRESS_REQUEST, +}); + +/** + * Signals first wallet address was loaded with success. + * @param {Object} successPayload + * @param {string} successPayload.address + */ +export const firstAddressSuccess = (successPayload) => ({ + type: types.FIRSTADDRESS_SUCCESS, + payload: successPayload, +}); + +/** + * Signals a failure on first wallet address loading due to an error. + * @param {Object} failurePayload + * @param {string} failurePayload.error + */ +export const firstAddressFailure = (failurePayload) => ({ + type: types.FIRSTADDRESS_FAILURE, + payload: failurePayload, +}); diff --git a/src/components/FeedbackModal.js b/src/components/FeedbackModal.js index f6b39202d..4f7b371d4 100644 --- a/src/components/FeedbackModal.js +++ b/src/components/FeedbackModal.js @@ -6,19 +6,40 @@ */ import React from 'react'; -import { Text } from 'react-native'; +import { Text, View } from 'react-native'; import PropTypes from 'prop-types'; import HathorModal from './HathorModal'; const FeedbackModal = (props) => ( - + {props.icon} - + {props.text} + {props.action + && ( + + {props.action} + + )} ); +const styles = { + content: { + fontSize: 18, + lineHeight: 21, + paddingTop: 36, + textAlign: 'center', + }, + action: { + width: '100%', + paddingTop: 8, + }, +}; + FeedbackModal.propTypes = { // Icon used on this modal. Usually an image or the Spinner component icon: PropTypes.element.isRequired, @@ -28,6 +49,8 @@ FeedbackModal.propTypes = { // Function to execute on dismissing the modal onDismiss: PropTypes.func, + + action: PropTypes.element, }; export default FeedbackModal; diff --git a/src/components/HathorModal.js b/src/components/HathorModal.js index 9ee9610e2..3f70cdca4 100644 --- a/src/components/HathorModal.js +++ b/src/components/HathorModal.js @@ -23,7 +23,7 @@ const HathorModal = (props) => ( onBackdropPress={props.onDismiss} style={styles.modal} > - + {props.children} @@ -38,9 +38,9 @@ const styles = StyleSheet.create({ borderRadius: 8, alignItems: 'center', paddingHorizontal: 16, - paddingBottom: 56, - paddingTop: 48, - height: 290, + paddingBottom: 24, + paddingTop: 42, + minHeight: 290, }, }); diff --git a/src/components/NanoContract/NanoContractTransactionHeader.component.js b/src/components/NanoContract/NanoContractTransactionHeader.component.js index 57940c2d1..b2302e93a 100644 --- a/src/components/NanoContract/NanoContractTransactionHeader.component.js +++ b/src/components/NanoContract/NanoContractTransactionHeader.component.js @@ -35,7 +35,7 @@ export const NanoContractTransactionHeader = ({ tx }) => { // use the shrank format just yet. We need the actions component first. // For his, we also need hathor-core support for actions in each nano contract // transaction. - const [isShrank, _] = useState(false); + const [isShrank] = useState(false); return ( diff --git a/src/components/NanoContract/RegisterNewNanoContractButton.js b/src/components/NanoContract/RegisterNewNanoContractButton.js index a924dbced..2c06ef533 100644 --- a/src/components/NanoContract/RegisterNewNanoContractButton.js +++ b/src/components/NanoContract/RegisterNewNanoContractButton.js @@ -14,7 +14,7 @@ import SimpleButton from '../SimpleButton'; export const RegisterNanoContract = () => { const navigation = useNavigation(); const navigatesToRegisterNanoContract = () => { - navigation.navigate('RegisterNanoContract'); + navigation.navigate('NanoContractRegisterScreen'); }; return ( diff --git a/src/constants.js b/src/constants.js index 45d672404..e607e9071 100644 --- a/src/constants.js +++ b/src/constants.js @@ -233,6 +233,13 @@ export const NETWORKSETTINGS_STATUS = { SUCCESSFUL: 'successful', }; +export const NANOCONTRACT_REGISTER_STATUS = { + READY: 'ready', + FAILED: 'failed', + LOADING: 'loading', + SUCCESSFUL: 'successful', +}; + /** * Timeout in miliseconds to call wallet-service. */ diff --git a/src/reducers/reducer.js b/src/reducers/reducer.js index 90a9c9dde..2af02a9d9 100644 --- a/src/reducers/reducer.js +++ b/src/reducers/reducer.js @@ -7,7 +7,15 @@ import hathorLib from '@hathor/wallet-lib'; import { get } from 'lodash'; -import { INITIAL_TOKENS, DEFAULT_TOKEN, PUSH_API_STATUS, FEATURE_TOGGLE_DEFAULTS, PRE_SETTINGS_MAINNET, NETWORKSETTINGS_STATUS } from '../constants'; +import { + INITIAL_TOKENS, + DEFAULT_TOKEN, + PUSH_API_STATUS, + FEATURE_TOGGLE_DEFAULTS, + PRE_SETTINGS_MAINNET, + NETWORKSETTINGS_STATUS, + NANOCONTRACT_REGISTER_STATUS +} from '../constants'; import { types } from '../actions'; import { TOKEN_DOWNLOAD_STATUS } from '../sagas/tokens'; import { WALLET_STATUS } from '../sagas/wallet'; @@ -235,6 +243,8 @@ const initialState = { networkSettingsInvalid: {}, networkSettingsStatus: NETWORKSETTINGS_STATUS.READY, nanoContract: { + registerStatus: NANOCONTRACT_REGISTER_STATUS.READY, + registerFailureMessage: null, /** * registered {{ * [ncId: string]: { @@ -323,6 +333,16 @@ const initialState = { addresses: [], error: null, }, + /** + * firstAddress {{ + * address?: string; + * error?: string; + * }} it holds the first wallet address or the error status. + */ + firstAddress: { + address: null, + error: null, + }, }; export const reducer = (state = initialState, action) => { @@ -479,8 +499,6 @@ export const reducer = (state = initialState, action) => { return onNetworkSettingsUpdateFailure(state); case types.NETWORKSETTINGS_UPDATE_INVALID: return onNetworkSettingsUpdateInvalid(state, action); - case types.NANOCONTRACT_REGISTER_SUCCESS: - return onNanoContractRegisterSuccess(state, action); case types.NANOCONTRACT_HISTORY_LOADING: return onNanoContractHistoryLoading(state, action); case types.NANOCONTRACT_HISTORY_FAILURE: @@ -493,12 +511,26 @@ export const reducer = (state = initialState, action) => { return onNanoContractUnregisterSuccess(state, action); case types.NANOCONTRACT_ADDRESS_CHANGE_REQUEST: return onNanoContractAddressChangeRequest(state, action); + case types.NANOCONTRACT_REGISTER_REQUEST: + return onNanoContractRegisterRequest(state); + case types.NANOCONTRACT_REGISTER_FAILURE: + return onNanoContractRegisterFailure(state, action); + case types.NANOCONTRACT_REGISTER_SUCCESS: + return onNanoContractRegisterSuccess(state, action); + case types.NANOCONTRACT_REGISTER_READY: + return onNanoContractRegisterReady(state); case types.SELECTADDRESS_ADDRESSES_REQUEST: return onSelectAddressAddressesRequest(state); case types.SELECTADDRESS_ADDRESSES_FAILURE: return onSelectAddressAddressesFailure(state, action); case types.SELECTADDRESS_ADDRESSES_SUCCESS: return onSelectAddressAddressesSuccess(state, action); + case types.FIRSTADDRESS_REQUEST: + return onFirstAddressRequest(state); + case types.FIRSTADDRESS_FAILURE: + return onFirstAddressFailure(state, action); + case types.FIRSTADDRESS_SUCCESS: + return onFirstAddressSuccess(state, action); default: return state; } @@ -560,9 +592,9 @@ const onNewTx = (state, action) => { } if (txout.decoded && txout.decoded.address - && txout.decoded.address === state.latestInvoice.address - && txout.value === state.latestInvoice.amount - && txout.token === state.latestInvoice.token.uid) { + && txout.decoded.address === state.latestInvoice.address + && txout.value === state.latestInvoice.amount + && txout.token === state.latestInvoice.token.uid) { invoicePayment = tx; break; } @@ -1326,17 +1358,41 @@ export const onNetworkSettingsUpdateInvalid = (state, { payload }) => ({ networkSettingsStatus: NETWORKSETTINGS_STATUS.READY, }); +export const onNanoContractRegisterRequest = (state) => ({ + ...state, + nanoContract: { + ...state.nanoContract, + registerStatus: NANOCONTRACT_REGISTER_STATUS.LOADING, + registerFailureMessage: null, + }, +}); + +/** + * @param {Object} state Redux store state + * @param {Object} action + * @param {Object} action.payload + * @param {string} action.payload.error Error message on failure + */ +export const onNanoContractRegisterFailure = (state, { payload: { error } }) => ({ + ...state, + nanoContract: { + ...state.nanoContract, + registerStatus: NANOCONTRACT_REGISTER_STATUS.FAILED, + registerFailureMessage: error, + }, +}); + /** * @param {Object} state * @param {{ * payload: { - * entryKey: string, + * entryKey: string; * entryValue: { - * address: string, - * ncId: string, - * blueprintId: string, - * blueprintName: string - * } + * address: string; + * ncId: string; + * blueprintName: string; + * }; + * hasFeedback?: boolean; * } * }} action */ @@ -1344,10 +1400,34 @@ export const onNanoContractRegisterSuccess = (state, { payload }) => ({ ...state, nanoContract: { ...state.nanoContract, + registerStatus: payload.hasFeedback + ? NANOCONTRACT_REGISTER_STATUS.SUCCESSFUL + : NANOCONTRACT_REGISTER_STATUS.READY, registered: { ...state.nanoContract.registered, [payload.entryKey]: payload.entryValue, }, + history: { + ...state.nanoContract.history, + [payload.entryKey]: [], + }, + historyMeta: { + ...state.nanoContract.historyMeta, + [payload.entryKey]: { + isLoading: false, + error: null, + after: null, + }, + }, + }, +}); + +export const onNanoContractRegisterReady = (state) => ({ + ...state, + nanoContract: { + ...state.nanoContract, + registerStatus: NANOCONTRACT_REGISTER_STATUS.READY, + registerFailureMessage: null, }, }); @@ -1556,3 +1636,43 @@ export const onSelectAddressAddressesSuccess = (state, { payload }) => ({ error: null, }, }); + +/** + * @param {Object} state + */ +export const onFirstAddressRequest = (state) => ({ + ...state, + firstAddress: initialState.firstAddress, +}); + +/** + * @param {Object} state + * @param {{ + * payload: { + * error: string; + * } + * }} action + */ +export const onFirstAddressFailure = (state, { payload }) => ({ + ...state, + firstAddress: { + address: null, + error: payload.error, + }, +}); + +/** + * @param {Object} state + * @param {{ + * payload: { + * address: string; + * } + * }} action + */ +export const onFirstAddressSuccess = (state, { payload }) => ({ + ...state, + firstAddress: { + address: payload.address, + error: null, + }, +}); diff --git a/src/sagas/nanoContract.js b/src/sagas/nanoContract.js index 1ea559d0f..e8ec59a5d 100644 --- a/src/sagas/nanoContract.js +++ b/src/sagas/nanoContract.js @@ -102,8 +102,7 @@ export function* registerNanoContract({ payload }) { let ncState = null; try { - const response = yield call([ncApi, ncApi.getNanoContractState], ncId); - ncState = response.ncState; + ncState = yield call([ncApi, ncApi.getNanoContractState], ncId); } catch (error) { if (error instanceof NanoRequest404Error) { yield put(nanoContractRegisterFailure(failureMessage.nanoContractStateNotFound)); @@ -117,14 +116,14 @@ export function* registerNanoContract({ payload }) { const nc = { address, ncId, - blueprintId: ncState.blueprint_id, blueprintName: ncState.blueprint_name, + blueprintId: ncState.blueprint_id, }; yield call(wallet.storage.registerNanoContract.bind(wallet.storage), ncId, nc); - log.debug('Success registering Nano Contract.'); - // emit action NANOCONTRACT_REGISTER_SUCCESS - yield put(nanoContractRegisterSuccess({ entryKey: ncId, entryValue: nc })); + log.debug(`Success registering Nano Contract. nc = ${nc}`); + // emit action NANOCONTRACT_REGISTER_SUCCESS with feedback to user + yield put(nanoContractRegisterSuccess({ entryKey: ncId, entryValue: nc, hasFeedback: true })); } /** @@ -209,6 +208,7 @@ export async function fetchHistory(ncId, count, after, wallet) { ncId: rawTx.nc_id, ncMethod: rawTx.nc_method, blueprintId: rawTx.nc_blueprint_id, + firstBlock: rawTx.first_block, caller, isMine, balance, diff --git a/src/sagas/wallet.js b/src/sagas/wallet.js index a74215351..e8fcf6437 100644 --- a/src/sagas/wallet.js +++ b/src/sagas/wallet.js @@ -66,6 +66,8 @@ import { onExceptionCaptured, selectAddressAddressesSuccess, selectAddressAddressesFailure, + firstAddressFailure, + firstAddressSuccess, } from '../actions'; import { fetchTokenData } from './tokens'; import { @@ -78,7 +80,11 @@ import { getRegisteredTokenUids, progressiveRetryRequest, } from './helpers'; -import { getAllAddresses, setKeychainPin } from '../utils'; +import { + getAllAddresses, + getFirstAddress, + setKeychainPin +} from '../utils'; import { logger } from '../logger'; const log = logger('wallet'); @@ -733,10 +739,11 @@ export function* refreshSharedAddress() { export function* fetchAllWalletAddresses() { const wallet = yield select((state) => state.wallet); if (!wallet.isReady()) { - log.error('Fail fetching all wallet addresses because wallet is not ready yet.'); - const errorMsg = t`Wallet is not ready to load addresses.`; + const errorMsg = 'Fail fetching all wallet addresses because wallet is not ready yet.'; + log.error(errorMsg); + const feedbackErrorMsg = t`Wallet is not ready to load addresses.`; // This will show the message in the feedback content at SelectAddressModal - yield put(selectAddressAddressesFailure({ error: errorMsg })); + yield put(selectAddressAddressesFailure({ error: feedbackErrorMsg })); // This will show user an error modal with the option to send the error to sentry. yield put(onExceptionCaptured(new Error(errorMsg), false)); return; @@ -755,6 +762,32 @@ export function* fetchAllWalletAddresses() { } } +export function* fetchFirstWalletAddress() { + const wallet = yield select((state) => state.wallet); + if (!wallet.isReady()) { + const errorMsg = 'Fail fetching first wallet address because wallet is not ready yet.'; + log.error(errorMsg); + const feedbackErrorMsg = t`Wallet is not ready to load the first address.`; + // This will show the message in the feedback content + yield put(firstAddressFailure({ error: feedbackErrorMsg })); + // This will show user an error modal with the option to send the error to sentry. + yield put(onExceptionCaptured(new Error(errorMsg), false)); + return; + } + + try { + const address = yield call(getFirstAddress, wallet); + log.log('First wallet address loaded with success.'); + yield put(firstAddressSuccess({ address })); + } catch (error) { + log.error('Error while fetching first wallet address.', error); + // This will show the message in the feedback content + yield put(firstAddressFailure({ + error: t`There was an error while loading first wallet address. Try again.` + })); + } +} + export function* saga() { yield all([ takeLatest('START_WALLET_REQUESTED', errorHandler(startWallet, startWalletFailed())), @@ -766,7 +799,8 @@ export function* saga() { takeEvery('WALLET_UPDATE_TX', handleTx), takeEvery('WALLET_BEST_BLOCK_UPDATE', bestBlockUpdate), takeEvery('WALLET_PARTIAL_UPDATE', loadPartialUpdate), - takeEvery(types.SELECTADDRESS_ADDRESSES_REQUEST, fetchAllWalletAddresses), takeEvery(types.WALLET_REFRESH_SHARED_ADDRESS, refreshSharedAddress), + takeEvery(types.SELECTADDRESS_ADDRESSES_REQUEST, fetchAllWalletAddresses), + takeEvery(types.FIRSTADDRESS_REQUEST, fetchFirstWalletAddress), ]); } diff --git a/src/screens/NanoContract/NanoContractRegisterScreen.js b/src/screens/NanoContract/NanoContractRegisterScreen.js new file mode 100644 index 000000000..3925b78d2 --- /dev/null +++ b/src/screens/NanoContract/NanoContractRegisterScreen.js @@ -0,0 +1,327 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React, { + useEffect, + useState, + useCallback +} from 'react'; +import { + StyleSheet, + View, + Text, + Image, +} from 'react-native'; +import { + useDispatch, + useSelector +} from 'react-redux'; +import { t } from 'ttag'; +import HathorHeader from '../../components/HathorHeader'; +import { CircleInfoIcon } from '../../components/Icons/CircleInfo.icon'; +import NewHathorButton from '../../components/NewHathorButton'; +import OfflineBar from '../../components/OfflineBar'; +import SimpleInput from '../../components/SimpleInput'; +import { TextLabel } from '../../components/TextLabel'; +import { TextValue } from '../../components/TextValue'; +import { COLORS } from '../../styles/themes'; +import { + firstAddressRequest, + nanoContractRegisterReady, + nanoContractRegisterRequest +} from '../../actions'; +import { + feedbackSucceedText, + hasFailed, + hasSucceeded, + isLoading +} from './helper'; +import Spinner from '../../components/Spinner'; +import FeedbackModal from '../../components/FeedbackModal'; +import errorIcon from '../../assets/images/icErrorBig.png'; +import checkIcon from '../../assets/images/icCheckBig.png'; +import { FeedbackContent } from '../../components/FeedbackContent'; +import { hasError } from '../../utils'; + +/** + * Validates the formModel, returning the invalidModel. + * If there is no error in the formModel, the invalidModel is returned empty. + */ +function validate(formModel) { + const invalidModel = {}; + + if (!formModel.ncId) { + invalidModel.ncId = t`Nano Contract ID is required.`; + } + + return invalidModel; +} + +export function NanoContractRegisterScreen({ navigation }) { + const dispatch = useDispatch(); + const { address, error } = useSelector((state) => state.firstAddress); + const registerState = useSelector((state) => ({ + registerStatus: state.nanoContract.registerStatus, + registerFailureMessage: state.nanoContract.registerFailureMessage, + })); + + const [isClean, setClean] = useState(true); + const [formModel, setFormModel] = useState({ + ncId: null, + }); + const [invalidModel, setInvalidModel] = useState({ + ncId: null, + }); + + /** + * It handles input change to perform validations. + * @param { ((name: 'ncId') => (value: string) => {}) => {} } + */ + const handleInputChange = useCallback( + (name) => (value) => { + if (isClean) { + setClean(false); + } + + // update invalid model + const invalidModelCopy = { ...invalidModel }; + delete invalidModelCopy[name]; + setInvalidModel(invalidModelCopy); + + // update form model + const form = { + ...formModel, + [name]: value, + }; + setFormModel(form); + + // validate form model and update invalid model + setInvalidModel(validate(form)); + }, + [isClean, invalidModel, formModel] + ); + + const handleSubmit = useCallback( + () => { + const newInvalidModel = validate(formModel); + if (hasError(newInvalidModel)) { + setInvalidModel(newInvalidModel); + return; + } + + const { ncId } = formModel; + dispatch(nanoContractRegisterRequest({ address, ncId })); + }, + [formModel] + ); + + const handleFeedbackModalDismiss = () => { + dispatch(nanoContractRegisterReady()); + }; + + const navigatesToNanoContractTransactions = () => { + dispatch(nanoContractRegisterReady()); + navigation.replace('NanoContractDetailsScreen', { ncId: formModel.ncId }); + }; + + useEffect(() => { + if (!address) { + dispatch(firstAddressRequest()); + } + }, []); + + const hasFirstAddressFailed = () => error; + const isFirstAddressLoading = () => !error && !address; + const hasFirstAddressLoaded = () => !error && address; + + return ( + + + + {hasSucceeded(registerState.registerStatus) + && ( + )} + text={feedbackSucceedText} + onDismiss={handleFeedbackModalDismiss} + action={()} + /> + )} + + {hasFailed(registerState.registerStatus) + && ( + )} + text={registerState.registerFailureMessage} + onDismiss={handleFeedbackModalDismiss} + action={()} + /> + )} + + {hasFirstAddressFailed() + && ( + )} + title={t`Load First Addresses Error`} + message={error} + /> + )} + + {isFirstAddressLoading() + && ( + + )} + + {hasFirstAddressLoaded() + && ( + + + + + {t`Wallet Address`} + {address} + + + + + + + + + {t`If you want to change the wallet address, you will be able to do`} + + {' '}{t`after the contract is registered.`} + + + + + {isLoading(registerState.registerStatus) + && ( + + + + )} + + + + + )} + + + ); +} + +const FieldContainer = ({ last, children }) => ( + + {children} + +); + +const NavigationHeader = ({ navigation }) => ( + navigation.goBack()} + /> +); + +const Wrapper = ({ children }) => ( + + {children} + +); + +const ContentWrapper = ({ children }) => ( + + {children} + +); + +const styles = StyleSheet.create({ + wrapper: { + flex: 1, + justifyContent: 'flex-start', + alignSelf: 'stretch', + backgroundColor: COLORS.lowContrastDetail, // Defines an outer area on the main list content + }, + contentWrapper: { + flex: 1, + alignSelf: 'stretch', + paddingTop: 16, + paddingBottom: 48, + paddingHorizontal: 16, + }, + infoContainer: { + flexShrink: 1, + flexDirection: 'row', + alignItems: 'center', + marginBottom: 24, + borderRadius: 8, + paddingVertical: 8, + paddingHorizontal: 16, + backgroundColor: 'hsla(203, 100%, 93%, 1)', + }, + infoContent: { + flex: 1, + paddingLeft: 8, + }, + selectionContainer: { + marginBottom: 16, + borderRadius: 8, + paddingVertical: 8, + paddingHorizontal: 16, + backgroundColor: COLORS.freeze100, + }, + loadingContainer: { + alignItems: 'center', + }, + input: { + marginBottom: 24, + }, + buttonContainer: { + alignSelf: 'stretch', + marginTop: 'auto', + }, + feedbackModalIcon: { + height: 105, + width: 105 + }, + feedbackContentIcon: { + height: 36, + width: 36, + }, + text: { + fontSize: 14, + lineHeight: 20, + }, + bold: { + fontWeight: 'bold', + }, + pd0: { + paddingBottom: 0, + }, + pd8: { + paddingBottom: 8, + }, +}); diff --git a/src/screens/NanoContract/helper.js b/src/screens/NanoContract/helper.js new file mode 100644 index 000000000..5a292f4a2 --- /dev/null +++ b/src/screens/NanoContract/helper.js @@ -0,0 +1,47 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { t } from 'ttag'; +import { NANOCONTRACT_REGISTER_STATUS } from '../../constants'; + +export const feedbackSucceedText = t`Contract successfully registered.`; + +/** + * Check if the nano contract register status is successful. + * @param {string} status - status from redux store + * @returns {boolean} - true if the status is successful, false otherwise + */ +export const hasSucceeded = (status) => ( + status === NANOCONTRACT_REGISTER_STATUS.SUCCESSFUL +); + +/** + * Check if the nano contract register status is failed. + * @param {string} status - status from redux store + * @returns {boolean} - true if the status is failed, false otherwise + */ +export const hasFailed = (status) => ( + status === NANOCONTRACT_REGISTER_STATUS.FAILED +); + +/** + * Check if the nano contract register status is loading. + * @param {string} status - status from redux store + * @returns {boolean} - true if the status is loading, false otherwise + */ +export const isLoading = (status) => ( + status === NANOCONTRACT_REGISTER_STATUS.LOADING +); + +/** + * Check if the nano contract register status is not ready. + * @param {string} status - status from redux store + * @returns {boolean} - true if the status is not ready, false otherwise + */ +export const notReady = (status) => ( + status !== NANOCONTRACT_REGISTER_STATUS.READY +); diff --git a/src/screens/NetworkSettings/helper.js b/src/screens/NetworkSettings/helper.js index 11b7ffd45..a0c92702e 100644 --- a/src/screens/NetworkSettings/helper.js +++ b/src/screens/NetworkSettings/helper.js @@ -1,3 +1,10 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + import { t } from 'ttag'; import { NETWORKSETTINGS_STATUS } from '../../constants'; diff --git a/src/utils.js b/src/utils.js index 4b369a6d4..ab3f11bf5 100644 --- a/src/utils.js +++ b/src/utils.js @@ -8,6 +8,7 @@ import hathorLib from '@hathor/wallet-lib'; import * as Keychain from 'react-native-keychain'; import React from 'react'; +import { isEmpty } from 'lodash'; import { t } from 'ttag'; import { Linking, Platform, Text } from 'react-native'; import { getStatusBarHeight } from 'react-native-status-bar-height'; @@ -454,3 +455,23 @@ export const getAllAddresses = async (wallet) => { const iterator = await wallet.getAllAddresses(); return consumeAsyncIterator(iterator); } + +/** + * Return the first wallet's address. + * + * @param {Object} wallet + * + * @returns {Promise} + * @throws {Error} either wallet not ready or other http request error if using wallet service. + * @async + */ +export const getFirstAddress = async (wallet) => wallet.getAddressAtIndex(0); + +/** + * Verifies if the invalidModel of the form has an error message. + */ +export function hasError(invalidModel) { + return Object + .values({ ...invalidModel }) + .reduce((_hasError, currValue) => _hasError || !isEmpty(currValue), false); +} From 062cbe000479a9469001f86d9854bf775f665d20 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Tue, 18 Jun 2024 17:32:27 +0100 Subject: [PATCH 24/51] refactor: remove .component suffix from component name (#498) --- .../{EditAddressModal.component.js => EditAddressModal.js} | 0 ...anoContractDetails.component.js => NanoContractDetails.js} | 4 ++-- ...etailsHeader.component.js => NanoContractDetailsHeader.js} | 4 ++-- ...onHeader.component.js => NanoContractTransactionHeader.js} | 0 ...tItem.component.js => NanoContractTransactionsListItem.js} | 0 ...{SelectAddressModal.component.js => SelectAddressModal.js} | 2 +- ...tractModal.component.js => UnregisterNanoContractModal.js} | 0 src/screens/NanoContract/NanoContractDetailsScreen.js | 2 +- src/screens/NanoContract/NanoContractTransactionScreen.js | 2 +- 9 files changed, 7 insertions(+), 7 deletions(-) rename src/components/NanoContract/{EditAddressModal.component.js => EditAddressModal.js} (100%) rename src/components/NanoContract/{NanoContractDetails.component.js => NanoContractDetails.js} (99%) rename src/components/NanoContract/{NanoContractDetailsHeader.component.js => NanoContractDetailsHeader.js} (98%) rename src/components/NanoContract/{NanoContractTransactionHeader.component.js => NanoContractTransactionHeader.js} (100%) rename src/components/NanoContract/{NanoContractTransactionsListItem.component.js => NanoContractTransactionsListItem.js} (100%) rename src/components/NanoContract/{SelectAddressModal.component.js => SelectAddressModal.js} (99%) rename src/components/NanoContract/{UnregisterNanoContractModal.component.js => UnregisterNanoContractModal.js} (100%) diff --git a/src/components/NanoContract/EditAddressModal.component.js b/src/components/NanoContract/EditAddressModal.js similarity index 100% rename from src/components/NanoContract/EditAddressModal.component.js rename to src/components/NanoContract/EditAddressModal.js diff --git a/src/components/NanoContract/NanoContractDetails.component.js b/src/components/NanoContract/NanoContractDetails.js similarity index 99% rename from src/components/NanoContract/NanoContractDetails.component.js rename to src/components/NanoContract/NanoContractDetails.js index 9ed30d241..8c5b9f9c8 100644 --- a/src/components/NanoContract/NanoContractDetails.component.js +++ b/src/components/NanoContract/NanoContractDetails.js @@ -14,8 +14,8 @@ import { } from 'react-native'; import { useDispatch, useSelector } from 'react-redux'; import { t } from 'ttag'; -import { NanoContractDetailsHeader } from './NanoContractDetailsHeader.component'; -import { NanoContractTransactionsListItem } from './NanoContractTransactionsListItem.component'; +import { NanoContractDetailsHeader } from './NanoContractDetailsHeader'; +import { NanoContractTransactionsListItem } from './NanoContractTransactionsListItem'; import { COLORS } from '../../styles/themes'; import { nanoContractAddressChangeRequest, nanoContractHistoryRequest } from '../../actions'; import { HathorFlatList } from '../HathorFlatList'; diff --git a/src/components/NanoContract/NanoContractDetailsHeader.component.js b/src/components/NanoContract/NanoContractDetailsHeader.js similarity index 98% rename from src/components/NanoContract/NanoContractDetailsHeader.component.js rename to src/components/NanoContract/NanoContractDetailsHeader.js index 1bcadad78..19e676623 100644 --- a/src/components/NanoContract/NanoContractDetailsHeader.component.js +++ b/src/components/NanoContract/NanoContractDetailsHeader.js @@ -25,8 +25,8 @@ import { ArrowUpIcon } from '../Icons/ArrowUp.icon'; import { TextValue } from '../TextValue'; import { TextLabel } from '../TextLabel'; import { EditInfoContainer } from '../EditInfoContainer'; -import { SelectAddressModal } from './SelectAddressModal.component'; -import { UnregisterNanoContractModal } from './UnregisterNanoContractModal.component'; +import { SelectAddressModal } from './SelectAddressModal'; +import { UnregisterNanoContractModal } from './UnregisterNanoContractModal'; /** * It presents the header for Nano Contract Details screen and provides the following diff --git a/src/components/NanoContract/NanoContractTransactionHeader.component.js b/src/components/NanoContract/NanoContractTransactionHeader.js similarity index 100% rename from src/components/NanoContract/NanoContractTransactionHeader.component.js rename to src/components/NanoContract/NanoContractTransactionHeader.js diff --git a/src/components/NanoContract/NanoContractTransactionsListItem.component.js b/src/components/NanoContract/NanoContractTransactionsListItem.js similarity index 100% rename from src/components/NanoContract/NanoContractTransactionsListItem.component.js rename to src/components/NanoContract/NanoContractTransactionsListItem.js diff --git a/src/components/NanoContract/SelectAddressModal.component.js b/src/components/NanoContract/SelectAddressModal.js similarity index 99% rename from src/components/NanoContract/SelectAddressModal.component.js rename to src/components/NanoContract/SelectAddressModal.js index e4e0e4138..ac98a902e 100644 --- a/src/components/NanoContract/SelectAddressModal.component.js +++ b/src/components/NanoContract/SelectAddressModal.js @@ -20,7 +20,7 @@ import { COLORS } from '../../styles/themes'; import { ModalBase } from '../ModalBase'; import { TextValue } from '../TextValue'; import { TextLabel } from '../TextLabel'; -import { EditAddressModal } from './EditAddressModal.component'; +import { EditAddressModal } from './EditAddressModal'; import { FeedbackContent } from '../FeedbackContent'; import errorIcon from '../../assets/images/icErrorBig.png'; import { selectAddressAddressesRequest } from '../../actions'; diff --git a/src/components/NanoContract/UnregisterNanoContractModal.component.js b/src/components/NanoContract/UnregisterNanoContractModal.js similarity index 100% rename from src/components/NanoContract/UnregisterNanoContractModal.component.js rename to src/components/NanoContract/UnregisterNanoContractModal.js diff --git a/src/screens/NanoContract/NanoContractDetailsScreen.js b/src/screens/NanoContract/NanoContractDetailsScreen.js index b1e67c834..a9b7e0ea2 100644 --- a/src/screens/NanoContract/NanoContractDetailsScreen.js +++ b/src/screens/NanoContract/NanoContractDetailsScreen.js @@ -13,7 +13,7 @@ import { import { useSelector } from 'react-redux'; import { t } from 'ttag'; import HathorHeader from '../../components/HathorHeader'; -import { NanoContractDetails } from '../../components/NanoContract/NanoContractDetails.component'; +import { NanoContractDetails } from '../../components/NanoContract/NanoContractDetails'; import OfflineBar from '../../components/OfflineBar'; import { COLORS } from '../../styles/themes'; diff --git a/src/screens/NanoContract/NanoContractTransactionScreen.js b/src/screens/NanoContract/NanoContractTransactionScreen.js index 5808e801c..25d9f0905 100644 --- a/src/screens/NanoContract/NanoContractTransactionScreen.js +++ b/src/screens/NanoContract/NanoContractTransactionScreen.js @@ -12,7 +12,7 @@ import { } from 'react-native'; import { t } from 'ttag'; import HathorHeader from '../../components/HathorHeader'; -import { NanoContractTransactionHeader } from '../../components/NanoContract/NanoContractTransactionHeader.component'; +import { NanoContractTransactionHeader } from '../../components/NanoContract/NanoContractTransactionHeader'; import OfflineBar from '../../components/OfflineBar'; import { COLORS } from '../../styles/themes'; From 24808935fb27b4245773265df3aa046a656f1406 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Tue, 18 Jun 2024 17:37:22 +0100 Subject: [PATCH 25/51] chore: bump wallet-lib to v1.6.0 (#499) --- package-lock.json | 38 +++++++++++++++++++++++++++++--------- package.json | 2 +- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index e933ecc8a..86a8dbd51 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@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.5.0", + "@hathor/wallet-lib": "1.6.0", "@notifee/react-native": "5.7.0", "@react-native-async-storage/async-storage": "1.19.0", "@react-native-firebase/app": "16.7.0", @@ -2550,20 +2550,20 @@ } }, "node_modules/@hathor/wallet-lib": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@hathor/wallet-lib/-/wallet-lib-1.5.0.tgz", - "integrity": "sha512-H0wOuzaS+iUhADGG8fxvNpLddkM0BFjCAjt1nLml5Oe6DkTj5VtA2a7+aT1DR4qxUcPRmCo9D+azbMDiwbXtIA==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@hathor/wallet-lib/-/wallet-lib-1.6.0.tgz", + "integrity": "sha512-gEsvbCFUwuj+kZya55oiEYzNWYSIWjrFQkqefET1Grtjs812i9dL7EueMFEwvqpm9yr9AlJuweGFj1lAkS8ucg==", "dependencies": { "axios": "1.6.8", "bitcore-lib": "8.25.10", "bitcore-mnemonic": "8.25.10", "buffer": "6.0.3", "crypto-js": "4.2.0", - "isomorphic-ws": "4.0.1", + "isomorphic-ws": "5.0.0", "level": "8.0.0", "lodash": "4.17.21", "long": "4.0.0", - "ws": "7.5.9" + "ws": "8.17.0" }, "engines": { "node": ">=20.0.0", @@ -2598,6 +2598,26 @@ "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" }, + "node_modules/@hathor/wallet-lib/node_modules/ws": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz", + "integrity": "sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.11", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz", @@ -11780,9 +11800,9 @@ } }, "node_modules/isomorphic-ws": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz", - "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", + "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==", "peerDependencies": { "ws": "*" } diff --git a/package.json b/package.json index 6c3f14ba8..f89f6381e 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "@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.5.0", + "@hathor/wallet-lib": "1.6.0", "@notifee/react-native": "5.7.0", "@react-native-async-storage/async-storage": "1.19.0", "@react-native-firebase/app": "16.7.0", From 300ee3133b2c07b11f719becf169fa61f8a00e9f Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Wed, 26 Jun 2024 17:31:14 +0100 Subject: [PATCH 26/51] chore: update QA docs (#490) * chore: update QA docs --- QA_CUSTOM_NETWORK.md | 156 +++++++++++++------- QA_PUSH_NOTIFICATION.md | 312 ++++++++++++++++++++++------------------ 2 files changed, 276 insertions(+), 192 deletions(-) diff --git a/QA_CUSTOM_NETWORK.md b/QA_CUSTOM_NETWORK.md index b9f90d104..c184d8ef8 100644 --- a/QA_CUSTOM_NETWORK.md +++ b/QA_CUSTOM_NETWORK.md @@ -1,80 +1,136 @@ # Custom Network 1. **Settings screen** - 1. Go to Settings - 1. Check you see "General Settings" as the title for the first collection of settings - 1. Check you see "Developer Settings" as the title for the second collection of settings + 1. [ ] Go to Settings + 1. [ ] Check you see "General Settings" as the title for the first collection of settings + 1. [ ] Check you see "Developer Settings" as the title for the second collection of settings 1. **Risk Disclaimer screen** - 1. Go to Network Settings - 1. Ensure you see the `RISK DISCLAIMER` screen - 1. Verify that there is a disclaimer text on a yellow background - 1. Confirm there is a button at the bottom of the page to acknowledge the disclaimer + 1. [ ] Go to Network Settings + 1. [ ] Ensure you see the `RISK DISCLAIMER` screen + 1. [ ] Verify that there is a disclaimer text on a yellow background + 1. [ ] Confirm there is a button at the bottom of the page to acknowledge the disclaimer 1. **Network Pre-Settings Screen** - 1. Click on "I UNDERSTAND" - 1. Check you see the `NETWORK PRE-SETTINGS` screen - 1. Check you see two pre-settings options: Mainnet and Testnet - 1. Check you see a button at the bottom of the page to customize the network + 1. [ ] Click on "I UNDERSTAND" + 1. [ ] Check you see the `NETWORK PRE-SETTINGS` screen + 1. [ ] Check you see two pre-settings options: Mainnet and Testnet + 1. [ ] Check you see a button at the bottom of the page to customize the network 1. **CUSTOM NETWORK SETTINGS Screen** - 1. Click on "CUSTOMIZE" - 1. Check you see the `CUSTOM NETWORK SETTINGS` screen - 1. Check you see a warning message text in a yellow background - 1. Check you see a form with the following values: + 1. [ ] Click on "CUSTOMIZE" + 1. [ ] Check you see the `CUSTOM NETWORK SETTINGS` screen + 1. [ ] Check you see a warning message text in a yellow background + 1. [ ] Check you see a form with the following values: - Node URL - Explorer URL - Explorer Service URL + - Transaction Mining Service URL - Wallet Service URL (optional) - Wallet Service WS URL (optional) - 1. Check you see a button at the bottom of the page to send the form + + >[!NOTE] + >Wallet Service fields only appear if the wallet is allowed on Wallet Service feature. + + 1. [ ] Check you see a button at the bottom of the page to send the form 1. **Send the default network settings** - 1. Click on "SEND" - 1. Verify that the wallet has successfully reloaded - 1. Ensure that a success feedback modal appears following the reload - 1. Dismiss the success feedback modal + 1. [ ] Click on "SEND" + 1. [ ] Verify that the wallet has successfully reloaded + 1. [ ] Ensure that a success feedback modal appears following the reload + 1. [ ] Dismiss the success feedback modal 1. **Send an invalid Node URL** - 1. Empty the Node URL field - 1. Check that an invalidation message appears under the field informing Node URL is required - 1. Fill the field with an invalid URL, e.g. "invalid" - 1. Check that an invalidation message appears under the field informing Node URL should be a valid URL + 1. [ ] Empty the Node URL field + 1. [ ] Check that an invalidation message appears under the field informing Node URL is required + 1. [ ] Fill the field with an invalid URL, e.g. "invalid" + 1. [ ] Check that an invalidation message appears under the field informing Node URL should be a valid URL 1. **Send a custom network** - 1. Navigate back to Network Pre-Settings screen - 1. Click on "CUSTOMIZE" again - 1. Fill the "Node URL" field with the value "https://node1.mainnet.hathor.network/v1a/" - 1. Click on "SEND" - 1. Wait the wallet reload - 1. Check if a success feedback modal has appeared - 1. Dismiss the success feedback modal - 1. Verify that a network status bar with a yellow background and the message "Custom network: mainnet" is visible at the top of the screen + 1. [ ] Navigate back to Network Pre-Settings screen + 1. [ ] Click on "CUSTOMIZE" again + 1. [ ] Fill the "Node URL" field with the value "https://node1.mainnet.hathor.network/v1a/" + 1. [ ] Click on "SEND" + 1. [ ] Wait the wallet reload + 1. [ ] Check if a success feedback modal has appeared + 1. [ ] Dismiss the success feedback modal + 1. [ ] Verify that a network status bar with a yellow background and the message "Custom network: mainnet" is visible at the top of the screen 1. **Restoring to mainnet** - 1. Navigate back to Network Pre-Settings screen - 1. Click on "Mainnet" option - 1. Wait for the wallet to reload - 1. Check a success feedback modal has appeared - 1. Dismiss the success feedback modal - 1. Ensure that the network status bar at the top of the screen has disappeared + 1. [ ] Navigate back to Network Pre-Settings screen + 1. [ ] Click on "Mainnet" option + 1. [ ] Wait for the wallet to reload + 1. [ ] Check a success feedback modal has appeared + 1. [ ] Dismiss the success feedback modal + 1. [ ] Ensure that the network status bar at the top of the screen has disappeared 1. **Check testnet balance** + >[!NOTE] >In this test we want to check the difference of transaction history and balance between mainnet and testnet of the same wallet. Therefore, you should have some transactions also in your testnet wallet. - 1. Click on "Testnet" option - 1. Wait for the wallet to reload - 1. Check a success feedback modal has appeared - 1. Dismiss the success feedback modal - 1. Verify that a network status bar with a yellow background and the message "Custom network: testnet" is visible at the top of the screen - 1. Go to the Home screen - 1. Check the balance of Hathor token - 1. Click on Hathor token - 1. Check you see different transactions from "mainnet" - 1. Click on a transaction - 1. Click on the "Public Explorer" item - 1. Verify that you were redirected to the transaction page on the testnet explorer + 1. [ ] Click on "Testnet" option + 1. [ ] Wait for the wallet to reload + 1. [ ] Check a success feedback modal has appeared + 1. [ ] Dismiss the success feedback modal + 1. [ ] Verify that a network status bar with a yellow background and the message "Custom network: testnet" is visible at the top of the screen + 1. [ ] Go to the Home screen + 1. [ ] Check the balance of Hathor token + 1. [ ] Click on Hathor token + 1. [ ] Check you see different transactions from "mainnet" + 1. [ ] Click on a transaction + 1. [ ] Click on the "Public Explorer" item + 1. [ ] Verify that you were redirected to the transaction page on the testnet explorer + +1. **Check registered tokens are cleaned when changing network settings** + 1. [ ] Register a token on Mainnet network + 1. [ ] Navigate to Network Pre-Settings screen + 1. [ ] Select the Testnet network + 1. [ ] Navigate to Mainscreen + 1. [ ] Check there is only the HTR token in the dashboard + +1. **Check Wallet Service fields didn't appear when Wallet Service is disabled for the wallet** + 1. [ ] Go to Unleash and disables the Wallet Service for the wallet + 1. You should set this configuration on `wallet-service-mobile.rollout` + 1. [ ] Close and reopens the App + 1. [ ] Navigate Custom Network Settings screen on Network Settings feature + 1. [ ] Check the Wallet Service fields do not appear + +1. **Check Wallet Service fields disable Push Notification when they are empty** + 1. [ ] Go to Unleash and enables the Wallet Service for the wallet + 1. You should set this configuration on `wallet-service-mobile.rollout` + 1. [ ] Close and reopens the App + 1. [ ] Navigate Custom Network Settings screen on Network Settings feature + 1. [ ] Check the Wallet Service fields do appear + 1. [ ] Make both Wallet Service fields empty + 1. [ ] Navigate to Settings screen + 1. [ ] Check the Push Notification option didn't appear +1. **Check Wallet Service fields can't be empty unilaterally** + 1. [ ] Go to Unleash and enables the Wallet Service for the wallet + 1. You should set this configuration on `wallet-service-mobile.rollout` + 1. [ ] Close and reopens the App + 1. [ ] Navigate Custom Network Settings screen on Network Settings feature + 1. [ ] Check the Wallet Service fields do appear + 1. [ ] Make one of the Wallet Service fields empty + 1. [ ] Send the custom settings + 1. You should see an error message under the field +1. **Check Push Notification aren't available on Testnet** + 1. [ ] Navigate to Network Pre-Settings screen + 1. [ ] Select Testnet pre-settings + 1. [ ] Navigate to Settings screen + 1. [ ] Check Push Notification didn't appear +1. **Check Push Notification aren't being received on Testnet** + 1. [ ] Go to Unleash and enables the Wallet Service for the wallet + 1. You should set this configuration on `wallet-service-mobile.rollout` + 1. [ ] Navigate to Network Pre-Settings screen + 1. [ ] Select Mainnet pre-settings + 1. [ ] Enables Push Notification + 1. [ ] Send a transaction to your wallet from another wallet + 1. You should receive a push notification + 1. [ ] Navigate to Network Pre-Settings screen + 1. [ ] Select Testnet pre-settings + 1. [ ] Send a transaction to your wallet from another wallet + 1. You should **not** receive a push notification diff --git a/QA_PUSH_NOTIFICATION.md b/QA_PUSH_NOTIFICATION.md index 4500d2bb9..543b93cbe 100644 --- a/QA_PUSH_NOTIFICATION.md +++ b/QA_PUSH_NOTIFICATION.md @@ -26,250 +26,278 @@ It's a second custom NFT token to test. ## Suggested test sequence 1. **Preparation** - 1. Clear the application storage - 1. Make sure the deviceId is not registered in the unleash **`push-notification.rollout`** feature toggle - 1. If testing the fullnode wallet, make sure the unleash wallet-service feature toggle is disabled + 1. [ ] Clear the application storage + 1. [ ] Make sure the deviceId is not registered in the unleash **`push-notification.rollout`** feature toggle + 1. [ ] If testing the fullnode wallet, make sure the unleash wallet-service feature toggle is disabled + 1. **Initialize a new wallet** - 1. You should **not** see a modal to opt-in the push notification yet - 1. Go to the **Settings** page + 1. [ ] You should **not** see a modal to opt-in the push notification yet + 1. [ ] Go to the **Settings** page 1. You should **not** see the **Push Notification** item yet + 1. **Turn on the `push-notification` feature toggle** - 1. Go to the settings page - 1. Get the `deviceId` and add it in the `UserIDs` for the stage and platform mobile in the unleash **`push-notification.rollout`** feature toggle. - 1. Wait until the **Push Notification** item shows up in the Settings page - 1. You should see a modal to opt-in the push notification - 1. Click on **Yes, enable** + 1. [ ] Go to the settings page + 1. [ ] Get the `deviceId` and add it in the `UserIDs` for the stage and platform mobile in the unleash **`push-notification.rollout`** feature toggle. + 1. [ ] Wait until the **Push Notification** item shows up in the Settings page + 1. [ ] You should see a modal to opt-in the push notification + 1. [ ] Click on **Yes, enable** 1. You should be redirected to **Push Notification** page 1. **Turn off the `push-notification` feature toggle** - 1. **Go to the Settings page** - 1. Remove your `deviceId` from the unleash **`push-notification.rollout`** feature toggle - 1. Wait until the Push Notification item disappears from the Settings page + 1. [ ] **Go to the Settings page** + 1. [ ] Remove your `deviceId` from the unleash **`push-notification.rollout`** feature toggle + 1. [ ] Wait until the Push Notification item disappears from the Settings page + 1. **Test push notification settings on/off** - 1. Turn on the `push-notification` feature toggle - 1. Go to the **Push Notification** page - 1. Turn on the `Enable Push Notification` - 1. Turn on the `Show amounts on notification` - 1. Turn off the `Enabled Push Notification` - 1. Try to turn on the `Show amounts on notification` + 1. [ ] Turn on the `push-notification` feature toggle + 1. [ ] Go to the **Push Notification** page + 1. [ ] Turn on the `Enable Push Notification` + 1. [ ] Turn on the `Show amounts on notification` + 1. [ ] Turn off the `Enabled Push Notification` + 1. [ ] Try to turn on the `Show amounts on notification` 1. it should not be possible - 1. Turn on the `Enabled Push Notification` + 1. [ ] Turn on the `Enabled Push Notification` 1. `Show amounts on notification` should be on - 1. Turn off the `Show amounts on notification` - 1. Turn off the `Enabled Push Notification` + 1. [ ] Turn off the `Show amounts on notification` + 1. [ ] Turn off the `Enabled Push Notification` + 1. **Try to send a notification with `push-notification` feature toggle turned off** - 1. Go to the **Settings** page - 1. Turn **off** the `push-notifiation` feature toggle - 1. Send HTR to this wallet + 1. [ ] Go to the **Settings** page + 1. [ ] Turn **off** the `push-notifiation` feature toggle + 1. [ ] Send HTR to this wallet 1. Wait some minutes to guarantee you won't receive any notifications for this tx + 1. **Try to send a notification with `push-notification` feature toggle turned on** - 1. Go to the **Settings** page - 1. Turn **on** the `push-notifiation` feature toggle - 1. Send HTR to this wallet + 1. [ ] Go to the **Settings** page + 1. [ ] Turn **on** the `push-notifiation` feature toggle + 1. [ ] Send HTR to this wallet 1. Wait some minutes to guarantee you won't receive any notifications for this tx (because the settings `Enable Push Notification` is disabled) + 1. **Send a token after turn on `Enable Push Notification` option** - 1. Turn on the `push-notification` feature toggle - 1. Go to the **Push Notification** page - 1. Turn **on** the `Enable Push Notification` option - 1. Send HTR to this wallet + 1. [ ] Turn on the `push-notification` feature toggle + 1. [ ] Go to the **Push Notification** page + 1. [ ] Turn **on** the `Enable Push Notification` option + 1. [ ] Send HTR to this wallet 1. Wait until the notification arrives 1. You should receive a notification of new transaction without show amounts > There is a new transaction in your wallet. - 1. Dismiss the notification + 1. [ ] Dismiss the notification + 1. **Send a token after turn on `Show amounts on notification` option** - 1. Turn on the `push-notification` feature toggle - 1. Go to the **Push Notification** page - 1. Turn **on** the `Enable Push Notification` option - 1. Turn **on** the `Show amounts on notification` option - 1. Send HTR to this wallet + 1. [ ] Turn on the `push-notification` feature toggle + 1. [ ] Go to the **Push Notification** page + 1. [ ] Turn **on** the `Enable Push Notification` option + 1. [ ] Turn **on** the `Show amounts on notification` option + 1. [ ] Send HTR to this wallet 1. Wait until the notification arrives 1. You should receive a notification of new transaction showing amounts in the message > You have received 0.04 HTR on a new transaction. - 1. Dismiss the notification + 1. [ ] Dismiss the notification + 1. **View the details of the transaction (foreground)** - 1. Send a token after turn on `Enable Push Notification` option - 1. **Keep the application open** - 1. Click on the notification - 1. Wait until the modal with tx details open + 1. [ ] Send a token after turn on `Enable Push Notification` option + 1. [ ] **Keep the application open** + 1. [ ] Click on the notification + 1. [ ] Wait until the modal with tx details open 1. The `HTR - HATHOR` name should be in the primary color (purple) - 1. Click on the `HTR - HATHOR` item + 1. [ ] Click on the `HTR - HATHOR` item 1. The **Balance** page should open + 1. **View the details of the transaction (quit)** - > INFO: Notifee v5.7.0 with Android API 32 has a [known issue regarding onBackgroundEvent](https://github.com/invertase/notifee/issues/404). - 1. Turn on the `push-notification` feature toggle - 1. Go to the **Push Notification** page - 1. Turn **on** the `Enable Push Notification` option - 1. **Close the application** - 1. Send HTR to this wallet + + >[!NOTE] + >Notifee v5.7.0 with Android API 32 has a [known issue regarding onBackgroundEvent](https://github.com/invertase/notifee/issues/404). + + 1. [ ] Turn on the `push-notification` feature toggle + 1. [ ] Go to the **Push Notification** page + 1. [ ] Turn **on** the `Enable Push Notification` option + 1. [ ] **Close the application** + 1. [ ] Send HTR to this wallet 1. Wait until the notification arrives 1. You should receive a notification of new transaction without show amounts > There is a new transaction in your wallet. - 1. Click on the notification - 1. Wait until the modal with tx details open + 1. [ ] Click on the notification + 1. [ ] Wait until the modal with tx details open 1. The `HTR - HATHOR` name should be in the primary color (purple) - 1. Click on the `HTR - HATHOR` item + 1. [ ] Click on the `HTR - HATHOR` item 1. The **Balance** page should open + 1. **View the details of the transaction while in foreground starting from lock screen** - 1. Send a token after turn on `Enable Push Notification` option - 1. **Keep the application open** - 1. Go to the Settings screen. - 1. Go to Security - 1. Click on Lock Wallet. - 1. Click on the notification - 1. Unlock the screen - 1. Wait until the modal with tx details open + 1. [ ] Send a token after turn on `Enable Push Notification` option + 1. [ ] **Keep the application open** + 1. [ ] Go to the Settings screen. + 1. [ ] Go to Security + 1. [ ] Click on Lock Wallet. + 1. [ ] Click on the notification + 1. [ ] Unlock the screen + 1. [ ] Wait until the modal with tx details open 1. The `HTR - HATHOR` name should be in the primary color (purple) - 1. Click on the `HTR - HATHOR` item + 1. [ ] Click on the `HTR - HATHOR` item 1. The **Balance** page should open + 1. **Reset wallet and send a token** - 1. Reset the wallet - 1. Send HTR to this wallet + 1. [ ] Reset the wallet + 1. [ ] Send HTR to this wallet 1. Wait until the notification arrives 1. You should **not** receive a notification - 1. Import the wallet - 1. Send HTR to this wallet + 1. [ ] Import the wallet + 1. [ ] Send HTR to this wallet 1. Wait until the notification arrives 1. You should **not** receive a notification - 1. Send a token after turn on `Enable Push Notification` option + 1. [ ] Send a token after turn on `Enable Push Notification` option 1. Wait until the notification arrives 1. You should receive a notification of new transaction without show amounts > There is a new transaction in your wallet. - 1. Dismiss the notification + 1. [ ] Dismiss the notification + 1. **Send 2 tokens after turn on `Show amounts on notification` option** - 1. Turn on the `push-notification` feature toggle - 1. Go to the **Push Notification** page - 1. Turn **on** the `Enable Push Notification` option - 1. Turn **on** the `Show amounts on notification` option - 1. Send HTR and TTT to this wallet + 1. [ ] Turn on the `push-notification` feature toggle + 1. [ ] Go to the **Push Notification** page + 1. [ ] Turn **on** the `Enable Push Notification` option + 1. [ ] Turn **on** the `Show amounts on notification` option + 1. [ ] Send HTR and TTT to this wallet 1. Wait until the notification arrives 1. You should receive a notification of new transaction showing 2 amounts in the message > You have received 0.09 HTR and 0.01 TTT on a new transaction. - 1. Click on the notification - 1. Wait until the modal with tx details open + 1. [ ] Click on the notification + 1. [ ] Wait until the modal with tx details open 1. The `HTR - HATHOR` name should be in the primary color (purple) 1. The `TTT - Test Token Test` name should be in gray - 1. Click on the `TTT - Test Token Test` item + 1. [ ] Click on the `TTT - Test Token Test` item 1. Nothing should happen - 1. Click on the `HTR - HATHOR` item + 1. [ ] Click on the `HTR - HATHOR` item 1. The **Balance** page should open + 1. **Send 3 tokens after turn on `Show amounts on notification` option** - 1. Turn on the `push-notification` feature toggle - 1. Go to the **Push Notification** page - 1. Turn **on** the `Enable Push Notification` option - 1. Turn **on** the `Show amounts on notification` option - 1. Send HTR, TTT and TN1 to this wallet + 1. [ ] Turn on the `push-notification` feature toggle + 1. [ ] Go to the **Push Notification** page + 1. [ ] Turn **on** the `Enable Push Notification` option + 1. [ ] Turn **on** the `Show amounts on notification` option + 1. [ ] Send HTR, TTT and TN1 to this wallet 1. Wait until the notification arrives 1. You should receive a notification of new transaction showing 2 amounts in the message > You have received 0.05 TN1, 0.03 TTT and 1 other token on a new transaction. - 1. Click on the notification - 1. Wait until the modal with tx details open + 1. [ ] Click on the notification + 1. [ ] Wait until the modal with tx details open 1. The `HTR - HATHOR` name should be in the primary color (purple) 1. The `TTT - Test Token Test` name should be in gray 1. The `TN1 - Test Nft` name should be in gray - 1. Click on the `TTT - Test Token Test` item + 1. [ ] Click on the `TTT - Test Token Test` item 1. Nothing should happen - 1. Click on the `TN1 - Test Nft` item + 1. [ ] Click on the `TN1 - Test Nft` item 1. Nothing should happen - 1. Click on the `HTR - HATHOR` item + 1. [ ] Click on the `HTR - HATHOR` item 1. The **Balance** page should open + 1. **Send 4 tokens after turn on `Show amounts on notification` option** - 1. Turn on the `push-notification` feature toggle - 1. Go to the **Push Notification** page - 1. Turn **on** the `Enable Push Notification` option - 1. Turn **on** the `Show amounts on notification` option - 1. Send HTR, TTT, TN1 and TNT to this wallet + 1. [ ] Turn on the `push-notification` feature toggle + 1. [ ] Go to the **Push Notification** page + 1. [ ] Turn **on** the `Enable Push Notification` option + 1. [ ] Turn **on** the `Show amounts on notification` option + 1. [ ] Send HTR, TTT, TN1 and TNT to this wallet 1. Wait until the notification arrives 1. You should receive a notification of new transaction showing 2 amounts in the message > You have received 0.08 TNT, 0.05 TN1 and 2 other tokens on a new transaction. - 1. Click on the notification - 1. Wait until the modal with tx details open + 1. [ ] Click on the notification + 1. [ ] Wait until the modal with tx details open 1. The `HTR - HATHOR` name should be in the primary color (purple) 1. The `TTT - Test Token Test` name should be in gray 1. The `TN1 - Test Nft` name should be in gray 1. The `TNT - Test Nft Test` name should be in gray - 1. Click on the `TTT - Test Token Test` item + 1. [ ] Click on the `TTT - Test Token Test` item 1. Nothing should happen - 1. Click on the `TN1 - Test Nft` item + 1. [ ] Click on the `TN1 - Test Nft` item 1. Nothing should happen - 1. Click on the `TNT - Test Nft Test` item + 1. [ ] Click on the `TNT - Test Nft Test` item 1. Nothing should happen - 1. Click on the `HTR - HATHOR` item + 1. [ ] Click on the `HTR - HATHOR` item 1. The **Balance** page should open + 1. **Register `TTT` token and send 2 tokens after turn on `Show amounts on notification` option** - > WARNING: Not possible using `wallet-service` in `testnet` due to a validation that consults the fullnode. - > Jump to **Test open the wallet 2 weeks later** (19) - 1. Register `TTT` token in the wallet - 1. Turn on the `push-notification` feature toggle - 1. Go to the **Push Notification** page - 1. Turn **on** the `Enable Push Notification` option - 1. Turn **on** the `Show amounts on notification` option - 1. Send HTR and TTT to this wallet + + >[!WARNING] + >Not possible using `wallet-service` in `testnet` due to a validation that consults the fullnode. + >Jump to **Test open the wallet 2 weeks later** + + 1. [ ] Register `TTT` token in the wallet + 1. [ ] Turn on the `push-notification` feature toggle + 1. [ ] Go to the **Push Notification** page + 1. [ ] Turn **on** the `Enable Push Notification` option + 1. [ ] Turn **on** the `Show amounts on notification` option + 1. [ ] Send HTR and TTT to this wallet 1. Wait until the notification arrives 1. You should receive a notification of new transaction showing 2 amounts in the message > You have received 0.02 TTT and 0.01 HTR on a new transaction. - 1. Click on the notification - 1. Wait until the modal with tx details open + 1. [ ] Click on the notification + 1. [ ] Wait until the modal with tx details open 1. The `HTR - HATHOR` name should be in the primary color (purple) 1. The `TTT - Test Token Test` name should be in the primary color (purple) - 1. Click on the `TTT - Test Token Test` item + 1. [ ] Click on the `TTT - Test Token Test` item 1. The **Balance** page for `TTT` token should open + 1. **Register `TN1` token and send 2 tokens after turn on `Show amounts on notification` option** - 1. Register `TTT` token in the wallet - 1. Turn on the `push-notification` feature toggle - 1. Go to the **Push Notification** page - 1. Turn **on** the `Enable Push Notification` option - 1. Turn **on** the `Show amounts on notification` option - 1. Send HTR and TN1 to this wallet + 1. [ ] Register `TTT` token in the wallet + 1. [ ] Turn on the `push-notification` feature toggle + 1. [ ] Go to the **Push Notification** page + 1. [ ] Turn **on** the `Enable Push Notification` option + 1. [ ] Turn **on** the `Show amounts on notification` option + 1. [ ] Send HTR and TN1 to this wallet 1. Wait until the notification arrives 1. You should receive a notification of new transaction showing 2 amounts in the message > You have received 0.03 TN1 and 0.02 HTR on a new transaction. - 1. Click on the notification - 1. Wait until the modal with tx details open + 1. [ ] Click on the notification + 1. [ ] Wait until the modal with tx details open 1. The `HTR - HATHOR` name should be in the primary color (purple) 1. The `TN1 - Test Nft` name should be in the primary color (purple) 1. The `TN1 - Test Nft` amount should be integer - 1. Click on the `TN1 - Test Nft` item + 1. [ ] Click on the `TN1 - Test Nft` item 1. The **Balance** page for `TN1` token should open -1. **Send token to self** + +1. [ ] **Send token to self** 1. Wait some minutes to guarantee you won't receive any notifications for this tx + 1. Test open the wallet 2 weeks later - > WARNING: Skip if running the wallet from store. - 1. Open the file `src/sagas/pushNotification.js` and search for the following assignment: + + >[!WARNING] + >Skip if running the wallet from store. + + 1. [ ] Open the file `src/sagas/pushNotification.js` and search for the following assignment: ```jsx const timeSinceLastRegistration = moment().diff(enabledAt, 'weeks'); ``` - 1. Assign the value `2` to `timeSinceLastRegistration` and save + 1. [ ] Assign the value `2` to `timeSinceLastRegistration` and save ```jsx const timeSinceLastRegistration = 2; ``` - 1. Reload the wallet - 1. You should see a modal asking for a registration refresh + 1. [ ] Reload the wallet + 1. You should see a modal asking for a registration refresh > This modal only shows up when the user is using the fullnode wallet. - 1. Click on **Refresh** - 1. Enter your pin - 1. Done! You will continue to receive the push notification. - 1. Reassign `timeSinceLastRegistration` with its previous expression: + 1. [ ] Click on **Refresh** + 1. [ ] Enter your pin + 1. [ ] Done! You will continue to receive the push notification. + 1. [ ] Reassign `timeSinceLastRegistration` with its previous expression: ```jsx const timeSinceLastRegistration = moment().diff(enabledAt, 'weeks'); ``` + 1. **Close test** - 1. Register TNT token - 1. Send back all the tokens to the source wallet - 1. Disable push notification settings - 1. Turn off push notification feature toggle - 1. Unregister the tokens - 1. Reset the wallet - 1. Close the app - 1. Clear the application storage + 1. [ ] Register TNT token + 1. [ ] Send back all the tokens to the source wallet + 1. [ ] Disable push notification settings + 1. [ ] Turn off push notification feature toggle + 1. [ ] Unregister the tokens + 1. [ ] Reset the wallet + 1. [ ] Close the app + 1. [ ] Clear the application storage ### Turn on the `wallet-service` feature toggle -1. Get the `deviceId` and add it in the `UserIDs` strategy in the unleash **`wallet-service-mobile-android-testnet.rollout`** feature toggle -1. Turn the feature toggle on +1. [ ] Get the `deviceId` and add it in the `UserIDs` strategy in the unleash **`wallet-service-mobile-android-testnet.rollout`** feature toggle +1. [ ] Turn the feature toggle on Run all the tests above with the wallet-service turned on. But as a quick test you can run the following test: 1. **Send token after turn on the `wallet-service` feature toggle** - 1. Turn **on** the `push-notification` feature toggle - 1. Turn **on** the `wallet-service` feature toggle - 1. View the details of the transaction (foreground) - 1. View the details of the transaction (quit) + 1. [ ] Turn **on** the `push-notification` feature toggle + 1. [ ] Turn **on** the `wallet-service` feature toggle + 1. [ ] View the details of the transaction (foreground) + 1. [ ] View the details of the transaction (quit) From 42ae16679afb40706c07a71cc5b43f6cf9c41a5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Abadesso?= Date: Wed, 3 Jul 2024 11:08:40 -0300 Subject: [PATCH 27/51] chore: updated flake and nodejs version (#501) --- flake.lock | 66 +++++++++++++++++++++++++++++++++++++++++------------- flake.nix | 4 ++-- 2 files changed, 53 insertions(+), 17 deletions(-) diff --git a/flake.lock b/flake.lock index 9095299ca..e6693c853 100644 --- a/flake.lock +++ b/flake.lock @@ -6,11 +6,11 @@ "nixpkgs": "nixpkgs" }, "locked": { - "lastModified": 1649691969, - "narHash": "sha256-nY1aUWIyh3TcGVo3sn+3vyCh+tOiEZL4JtMX3aOZSeY=", + "lastModified": 1717408969, + "narHash": "sha256-Q0OEFqe35fZbbRPPRdrjTUUChKVhhWXz3T9ZSKmaoVY=", "owner": "numtide", "repo": "devshell", - "rev": "e22633b05fec2fe196888c593d4d9b3f4f648a25", + "rev": "1ebbe68d57457c8cae98145410b164b5477761f4", "type": "github" }, "original": { @@ -20,12 +20,15 @@ } }, "flake-utils": { + "inputs": { + "systems": "systems" + }, "locked": { - "lastModified": 1642700792, - "narHash": "sha256-XqHrk7hFb+zBvRg6Ghl+AZDq03ov6OshJLiSWOoX5es=", + "lastModified": 1701680307, + "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", "owner": "numtide", "repo": "flake-utils", - "rev": "846b2ae0fc4cc943637d3d1def4454213e203cba", + "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", "type": "github" }, "original": { @@ -35,12 +38,15 @@ } }, "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, "locked": { - "lastModified": 1649676176, - "narHash": "sha256-OWKJratjt2RW151VUlJPRALb7OU2S5s+f0vLj4o1bHM=", + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", "owner": "numtide", "repo": "flake-utils", - "rev": "a4b154ebbdc88c8498a5c7b01589addc9e9cb678", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", "type": "github" }, "original": { @@ -51,11 +57,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1643381941, - "narHash": "sha256-pHTwvnN4tTsEKkWlXQ8JMY423epos8wUOhthpwJjtpc=", + "lastModified": 1704161960, + "narHash": "sha256-QGua89Pmq+FBAro8NriTuoO/wNaUtugt29/qqA8zeeM=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "5efc8ca954272c4376ac929f4c5ffefcc20551d5", + "rev": "63143ac2c9186be6d9da6035fa22620018c85932", "type": "github" }, "original": { @@ -67,11 +73,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1705403940, - "narHash": "sha256-bl7E3w35Bleiexg01WsN0RuAQEL23HaQeNBC2zjt+9w=", + "lastModified": 1719082008, + "narHash": "sha256-jHJSUH619zBQ6WdC21fFAlDxHErKVDJ5fpN0Hgx4sjs=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "f0326542989e1bdac955ad6269b334a8da4b0c95", + "rev": "9693852a2070b398ee123a329e68f0dab5526681", "type": "github" }, "original": { @@ -85,6 +91,36 @@ "flake-utils": "flake-utils_2", "nixpkgs": "nixpkgs_2" } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index 37bba0214..d633f94b6 100644 --- a/flake.nix +++ b/flake.nix @@ -11,7 +11,7 @@ let pkgs = import nixpkgs { inherit system; - overlays = [ devshell.overlay ]; + overlays = [ devshell.overlays.default ]; }; in pkgs.devshell.mkShell { @@ -31,7 +31,7 @@ ]; packages = with pkgs; [ nixpkgs-fmt - nodejs-18_x + nodejs_20 ruby gnumake gettext From d1183eece49a57d4bdc11e45ef3cd2962d347060 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Mon, 8 Jul 2024 19:32:57 +0100 Subject: [PATCH 28/51] fix: cleaning of tokens and nano-contract registered after reload (#506) * fix: cleaning of tokens and nano-contract registered after reload --- src/reducers/reducer.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/reducers/reducer.js b/src/reducers/reducer.js index 2af02a9d9..4c92d330a 100644 --- a/src/reducers/reducer.js +++ b/src/reducers/reducer.js @@ -1222,13 +1222,20 @@ export const onExceptionCaptured = (state, { payload }) => { }; /** - * On wallet reload, tokens data will be reloaded as well. + * On wallet reload, tokens and nano contract data will be + * reloaded as well. + * + * Some flows can request a wallet reload such as: + * - Disable wallet service + * - Change network settings + * - Loose network connection */ export const onReloadWalletRequested = (state) => ({ ...state, tokensHistory: initialState.tokensHistory, tokensBalance: initialState.tokensBalance, loadHistoryStatus: initialState.loadHistoryStatus, + nanoContract: initialState.nanoContract, }); const onWalletReloading = (state) => ({ From 08ed334b49996b881428636b4fb88e8fa34af717 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Carneiro?= Date: Wed, 10 Jul 2024 13:20:16 -0300 Subject: [PATCH 29/51] chore: upgrade wallet lib to v1.8.0 (#505) * feat: remove uses of HATHOR_TOKEN_CONFIG * chore: update package-lock * chore: use constants on config --- package-lock.json | 51 ++++++++++++++++---------------- package.json | 2 +- src/actions.js | 4 +-- src/config.js | 5 +++- src/sagas/helpers.js | 2 +- src/sagas/wallet.js | 8 ++--- src/screens/CreateTokenAmount.js | 2 +- src/screens/MainScreen.js | 4 +-- src/screens/UnregisterToken.js | 2 +- 9 files changed, 42 insertions(+), 38 deletions(-) diff --git a/package-lock.json b/package-lock.json index 86a8dbd51..bf5a0354a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@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.6.0", + "@hathor/wallet-lib": "1.8.0", "@notifee/react-native": "5.7.0", "@react-native-async-storage/async-storage": "1.19.0", "@react-native-firebase/app": "16.7.0", @@ -2550,19 +2550,19 @@ } }, "node_modules/@hathor/wallet-lib": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@hathor/wallet-lib/-/wallet-lib-1.6.0.tgz", - "integrity": "sha512-gEsvbCFUwuj+kZya55oiEYzNWYSIWjrFQkqefET1Grtjs812i9dL7EueMFEwvqpm9yr9AlJuweGFj1lAkS8ucg==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@hathor/wallet-lib/-/wallet-lib-1.8.0.tgz", + "integrity": "sha512-G1kLlxZ4Ev3S7hPiq/y9wUl4ns4pndOvuK37q+xqdxieZKCl2/O7lXiiHVOgRN9xOntL/TR66n2UZPpe0p29zQ==", "dependencies": { - "axios": "1.6.8", + "axios": "1.7.2", "bitcore-lib": "8.25.10", "bitcore-mnemonic": "8.25.10", "buffer": "6.0.3", "crypto-js": "4.2.0", "isomorphic-ws": "5.0.0", - "level": "8.0.0", + "level": "8.0.1", "lodash": "4.17.21", - "long": "4.0.0", + "long": "5.2.3", "ws": "8.17.0" }, "engines": { @@ -7001,9 +7001,9 @@ "integrity": "sha512-HQiug4c+/s3WOvEnDRxXVmNtSG5s2gJM9r19BTcqjp7BWcE48PB+Y2G6jE65kqI0LpsQeMZygt/b60Gi4KxGyA==" }, "node_modules/abstract-level": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/abstract-level/-/abstract-level-1.0.3.tgz", - "integrity": "sha512-t6jv+xHy+VYwc4xqZMn2Pa9DjcdzvzZmQGRjTFc8spIbRGHgBrEKbPq+rYXc7CCo0lxgYvSgKVg9qZAhpVQSjA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/abstract-level/-/abstract-level-1.0.4.tgz", + "integrity": "sha512-eUP/6pbXBkMbXFdx4IH2fVgvB7M0JvR7/lIL33zcs0IBcwjdzSSl31TOJsaCzmKSSDF9h8QYSOJux4Nd4YJqFg==", "dependencies": { "buffer": "^6.0.3", "catering": "^2.1.0", @@ -7390,9 +7390,9 @@ } }, "node_modules/axios": { - "version": "1.6.8", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", - "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", + "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", @@ -8361,9 +8361,9 @@ "dev": true }, "node_modules/classic-level": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/classic-level/-/classic-level-1.3.0.tgz", - "integrity": "sha512-iwFAJQYtqRTRM0F6L8h4JCt00ZSGdOyqh7yVrhhjrOpFhmBjNlRUey64MCiyo6UmQHMJ+No3c81nujPv+n9yrg==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/classic-level/-/classic-level-1.4.1.tgz", + "integrity": "sha512-qGx/KJl3bvtOHrGau2WklEZuXhS3zme+jf+fsu6Ej7W7IP/C49v7KNlWIsT1jZu0YnfzSIYDGcEWpCa1wKGWXQ==", "hasInstallScript": true, "dependencies": { "abstract-level": "^1.0.2", @@ -15105,10 +15105,11 @@ } }, "node_modules/level": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/level/-/level-8.0.0.tgz", - "integrity": "sha512-ypf0jjAk2BWI33yzEaaotpq7fkOPALKAgDBxggO6Q9HGX2MRXn0wbP1Jn/tJv1gtL867+YOjOB49WaUF3UoJNQ==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/level/-/level-8.0.1.tgz", + "integrity": "sha512-oPBGkheysuw7DmzFQYyFe8NAia5jFLAgEnkgWnK3OXAuJr8qFT+xBQIwokAZPME2bhPFzS8hlYcL16m8UZrtwQ==", "dependencies": { + "abstract-level": "^1.0.4", "browser-level": "^1.0.1", "classic-level": "^1.2.0" }, @@ -15340,9 +15341,9 @@ } }, "node_modules/long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" }, "node_modules/loose-envify": { "version": "1.4.0", @@ -16711,9 +16712,9 @@ } }, "node_modules/node-gyp-build": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz", - "integrity": "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==", + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz", + "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==", "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", diff --git a/package.json b/package.json index f89f6381e..2e3842549 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "@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.6.0", + "@hathor/wallet-lib": "1.8.0", "@notifee/react-native": "5.7.0", "@react-native-async-storage/async-storage": "1.19.0", "@react-native-firebase/app": "16.7.0", diff --git a/src/actions.js b/src/actions.js index f134c8fcc..0fb3f0c9c 100644 --- a/src/actions.js +++ b/src/actions.js @@ -420,7 +420,7 @@ export const fetchTokensMetadata = async (tokens, network) => { for (const tokenChunk of tokenChunks) { /* eslint-disable no-await-in-loop */ await Promise.all(tokenChunk.map(async (token) => { - if (token === hathorLibConstants.HATHOR_TOKEN_CONFIG.uid) { + if (token === hathorLibConstants.NATIVE_TOKEN_UID) { return; } @@ -491,7 +491,7 @@ export const fetchTokenBalance = async (wallet, uid) => { * wallet {HathorWallet | HathorWalletServiceWallet} wallet object */ export const fetchNewHTRBalance = async (wallet) => { - const { uid } = hathorLibConstants.HATHOR_TOKEN_CONFIG; + const uid = hathorLibConstants.NATIVE_TOKEN_UID; return fetchTokenBalance(wallet, uid); }; diff --git a/src/config.js b/src/config.js index 1a4fd5862..4a1f2182f 100644 --- a/src/config.js +++ b/src/config.js @@ -37,7 +37,10 @@ export const _IS_MULTI_TOKEN = true; * uid: '00' * } */ -export const _DEFAULT_TOKEN = hathorLib.constants.HATHOR_TOKEN_CONFIG; +export const _DEFAULT_TOKEN = { + uid: hathorLib.constants.NATIVE_TOKEN_UID, + ...hathorLib.constants.DEFAULT_NATIVE_TOKEN_CONFIG, +}; /** * App's primary color (Hathor purple) diff --git a/src/sagas/helpers.js b/src/sagas/helpers.js index 5c006dfcd..ba678281e 100644 --- a/src/sagas/helpers.js +++ b/src/sagas/helpers.js @@ -170,7 +170,7 @@ export function isUnlockScreen(action) { * }>} */ export async function getRegisteredTokens(wallet, excludeHTR = false) { - const htrUid = hathorLib.constants.HATHOR_TOKEN_CONFIG.uid; + const htrUid = hathorLib.constants.NATIVE_TOKEN_UID; const tokens = {}; // redux-saga generator magic does not work well with the "for await..of" syntax diff --git a/src/sagas/wallet.js b/src/sagas/wallet.js index e8fcf6437..f2afc1323 100644 --- a/src/sagas/wallet.js +++ b/src/sagas/wallet.js @@ -307,7 +307,7 @@ export function* startWallet(action) { */ export function* loadTokens() { const customTokenUid = DEFAULT_TOKEN.uid; - const htrUid = hathorLibConstants.HATHOR_TOKEN_CONFIG.uid; + const htrUid = hathorLibConstants.NATIVE_TOKEN_UID; // 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 @@ -533,7 +533,7 @@ export function* handleTx(action) { for (const tokenUid of tokensToDownload) { yield put(tokenFetchBalanceRequested(tokenUid, true)); - if (tokenUid === hathorLibConstants.HATHOR_TOKEN_CONFIG.uid + if (tokenUid === hathorLibConstants.NATIVE_TOKEN_UID || tokenUid === DEFAULT_TOKEN.uid) { yield put(tokenFetchHistoryRequested(tokenUid, true)); } else { @@ -626,7 +626,7 @@ export function* bestBlockUpdate({ payload }) { } if (currentHeight !== payload) { - yield put(tokenFetchBalanceRequested(hathorLibConstants.HATHOR_TOKEN_CONFIG.uid)); + yield put(tokenFetchBalanceRequested(hathorLibConstants.NATIVE_TOKEN_UID)); } } @@ -656,7 +656,7 @@ export function* onWalletReloadData() { const registeredTokens = yield call(loadTokens); const customTokenUid = DEFAULT_TOKEN.uid; - const htrUid = hathorLibConstants.HATHOR_TOKEN_CONFIG.uid; + const htrUid = hathorLibConstants.NATIVE_TOKEN_UID; // We might have lost transactions during the reload, so we must invalidate the // token histories: diff --git a/src/screens/CreateTokenAmount.js b/src/screens/CreateTokenAmount.js index 3f1575767..49dc9172c 100644 --- a/src/screens/CreateTokenAmount.js +++ b/src/screens/CreateTokenAmount.js @@ -27,7 +27,7 @@ import { COLORS } from '../styles/themes'; const mapStateToProps = (state) => ({ balance: get( state.tokensBalance, - `[${hathorLib.constants.HATHOR_TOKEN_CONFIG.uid}].data`, + `[${hathorLib.constants.NATIVE_TOKEN_UID}].data`, { available: 0, locked: 0, diff --git a/src/screens/MainScreen.js b/src/screens/MainScreen.js index b3ab69ae1..be897f115 100644 --- a/src/screens/MainScreen.js +++ b/src/screens/MainScreen.js @@ -110,7 +110,7 @@ class MainScreen extends React.Component { } tokenInfo = () => { - if (this.props.selectedToken.uid !== hathorConstants.HATHOR_TOKEN_CONFIG.uid) { + if (this.props.selectedToken.uid !== hathorConstants.NATIVE_TOKEN_UID) { this.props.navigation.navigate('TokenDetail'); } } @@ -199,7 +199,7 @@ class MainScreen extends React.Component { }; const renderRightElement = () => { - if (this.props.selectedToken.uid !== hathorConstants.HATHOR_TOKEN_CONFIG.uid) { + if (this.props.selectedToken.uid !== hathorConstants.NATIVE_TOKEN_UID) { return ( diff --git a/src/screens/UnregisterToken.js b/src/screens/UnregisterToken.js index c5384b5b5..fe3c15bfc 100644 --- a/src/screens/UnregisterToken.js +++ b/src/screens/UnregisterToken.js @@ -69,7 +69,7 @@ class UnregisterToken extends React.Component { unregisterConfirmed = () => { const tokenUnregister = this.props.selectedToken.uid; // Preventing unregistering HTR token, even if the user gets on this screen because of an error - if (tokenUnregister === hathorLib.constants.HATHOR_TOKEN_CONFIG.uid) { + if (tokenUnregister === hathorLib.constants.NATIVE_TOKEN_UID) { return; } From 03228e936f93e328c7e0842ae6401a46f589a7c0 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Wed, 10 Jul 2024 17:34:45 +0100 Subject: [PATCH 30/51] fix: txMiningServiceUrl lack of declaration on customNetwork (#503) * fix: txMiningServiceUrl lack of declaration on customNetwork --- src/constants.js | 2 +- src/sagas/networkSettings.js | 6 +++--- src/sagas/tokens.js | 10 +++++++--- src/sagas/wallet.js | 6 ++++++ 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/constants.js b/src/constants.js index e607e9071..63e206411 100644 --- a/src/constants.js +++ b/src/constants.js @@ -203,7 +203,7 @@ export const PRE_SETTINGS_TESTNET = { export const NODE_SERVER_MAINNET_URL = 'https://mobile.wallet.hathor.network/v1a/'; export const EXPLORER_MAINNET_URL = 'https://explorer.hathor.network/'; -export const EXPLORER_SERVICE_MAINNET_URL = 'https://explorer-service.hathor.network'; +export const EXPLORER_SERVICE_MAINNET_URL = 'https://explorer-service.hathor.network/'; export const TX_MINING_SERVICE_MAINNET_URL = 'https://txmining.mainnet.hathor.network/'; export const PRE_SETTINGS_MAINNET = { diff --git a/src/sagas/networkSettings.js b/src/sagas/networkSettings.js index 60eeddcbe..1bc49e952 100644 --- a/src/sagas/networkSettings.js +++ b/src/sagas/networkSettings.js @@ -202,12 +202,11 @@ export function* updateNetworkSettings(action) { // Validates the potential network and set the network accordingly if (potentialNetwork === NETWORK_MAINNET) { network = NETWORK_MAINNET; - } else if (potentialNetwork.startsWith(NETWORK_TESTNET)) { + } else if (potentialNetwork.includes(NETWORK_TESTNET)) { network = NETWORK_TESTNET; - } else if (potentialNetwork.startsWith(NETWORK_PRIVATENET)) { + } else if (potentialNetwork.includes(NETWORK_PRIVATENET)) { network = NETWORK_PRIVATENET; } else { - log.debug('The network informed is not allowed. Make sure your network is either "mainnet", "testnet" or "privatenet", or starts with "testnet" or "privatenet".'); yield put(networkSettingsUpdateFailure()); return; } @@ -227,6 +226,7 @@ export function* updateNetworkSettings(action) { nodeUrl, explorerUrl, explorerServiceUrl, + txMiningServiceUrl, walletServiceUrl, walletServiceWsUrl, }; diff --git a/src/sagas/tokens.js b/src/sagas/tokens.js index 7881da664..7e6774fa9 100644 --- a/src/sagas/tokens.js +++ b/src/sagas/tokens.js @@ -96,6 +96,8 @@ function* fetchTokenBalance(action) { const tokenBalance = yield select((state) => get(state.tokensBalance, tokenId)); if (!force && tokenBalance && tokenBalance.oldStatus === TOKEN_DOWNLOAD_STATUS.READY) { + log.debug(`Token download status READY.`); + log.debug(`Token balance already downloaded for token ${tokenId}. Skipping download.`); // The data is already loaded, we should dispatch success yield put(tokenFetchBalanceSuccess(tokenId, tokenBalance.data)); return; @@ -114,7 +116,7 @@ function* fetchTokenBalance(action) { locked: token.balance.locked, }; - log.debug('Success fetching token balance.'); + log.debug(`Success fetching token balance for token ${tokenId}.`); yield put(tokenFetchBalanceSuccess(tokenId, balance)); } catch (e) { log.error('Error while fetching token balance.', e); @@ -131,7 +133,7 @@ function* fetchTokenHistory(action) { if (!force && tokenHistory && tokenHistory.oldStatus === TOKEN_DOWNLOAD_STATUS.READY) { // The data is already loaded, we should dispatch success - log.debug('Success fetching token history from store.'); + log.debug(`Token history already downloaded for token ${tokenId}. Skipping download.`); yield put(tokenFetchHistorySuccess(tokenId, tokenHistory.data)); return; } @@ -139,7 +141,7 @@ function* fetchTokenHistory(action) { const response = yield call([wallet, wallet.getTxHistory], { token_id: tokenId }); const data = response.map(mapToTxHistory(tokenId)); - log.debug('Success fetching token history.'); + log.debug(`Success fetching token history for token ${tokenId}.`); yield put(tokenFetchHistorySuccess(tokenId, data)); } catch (e) { log.error('Error while fetching token history.', e); @@ -162,10 +164,12 @@ function* routeTokenChange(action) { switch (action.type) { case 'NEW_TOKEN': + log.debug('[routeTokenChange] fetching token balance on NEW_TOKEN event'); yield put({ type: types.TOKEN_FETCH_BALANCE_REQUESTED, tokenId: action.payload.uid }); break; case 'SET_TOKENS': default: + log.debug('[routeTokenChange] fetching token balance on SET_TOKENS event'); for (const uid of getRegisteredTokenUids({ tokens: action.payload })) { yield put({ type: types.TOKEN_FETCH_BALANCE_REQUESTED, tokenId: uid }); } diff --git a/src/sagas/wallet.js b/src/sagas/wallet.js index f2afc1323..2745f2c53 100644 --- a/src/sagas/wallet.js +++ b/src/sagas/wallet.js @@ -199,6 +199,10 @@ export function* startWallet(action) { wallet = new HathorWallet(walletConfig); } + // Extra wallet configuration based on customNetwork + config.setExplorerServiceBaseUrl(networkSettings.explorerServiceUrl); + config.setTxMiningUrl(networkSettings.txMiningServiceUrl); + yield put(setWallet(wallet)); // Setup listeners before starting the wallet so we don't lose messages @@ -338,6 +342,8 @@ export function* loadTokens() { // to asynchronously load the balances of each one. The `put` effect will just dispatch // and continue, loading the tokens asynchronously for (const token of registeredUids) { + // Skip the native token, once it has its balance loaded already + if (token === htrUid) continue; yield put(tokenFetchBalanceRequested(token)); } From b96ec18a0736f099c2f0a6dc596e8215962bd82b Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Mon, 15 Jul 2024 19:24:05 +0100 Subject: [PATCH 31/51] fix: Unable to send token by reading a QR code (#507) * fix: token registered condition after identify the qr code token --- src/screens/SendScanQRCode.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/screens/SendScanQRCode.js b/src/screens/SendScanQRCode.js index a4c1255ce..5a45c6c9c 100644 --- a/src/screens/SendScanQRCode.js +++ b/src/screens/SendScanQRCode.js @@ -57,10 +57,8 @@ class SendScanQRCode extends React.Component { if (!qrcode.isValid) { this.showAlertError(qrcode.error); } else if (qrcode.token && qrcode.amount) { - const isTokenRegistered = this.props.tokens.some( - (stateToken) => stateToken.uid === qrcode.token.uid - ); - if (isTokenRegistered) { + // If token is registered then navigates to confirmation screen + if (qrcode.token.uid in this.props.tokens) { const params = { address: qrcode.address, token: qrcode.token, From ca9676f61d3e5389e1e0a049d6d8c3dc64b537e5 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Tue, 30 Jul 2024 23:43:37 +0100 Subject: [PATCH 32/51] fix: unable to open push notification details modal over any screen (#508) * fix: tx details modal to show over any screen after unlock * lint: comply to rules * chore: improve documentation --- src/App.js | 2 + .../ShowPushNotificationTxDetails.js | 6 +- src/constants.js | 5 ++ src/sagas/pushNotification.js | 57 +++++++++++++++++-- src/screens/Dashboard.js | 2 - src/screens/SendScanQRCode.js | 2 +- 6 files changed, 65 insertions(+), 9 deletions(-) diff --git a/src/App.js b/src/App.js index 29deff761..7296d6e4d 100644 --- a/src/App.js +++ b/src/App.js @@ -86,6 +86,7 @@ import WalletConnectModal from './components/WalletConnect/WalletConnectModal'; import { COLORS, HathorTheme } from './styles/themes'; import { NetworkSettingsFlowNav, NetworkSettingsFlowStack } from './screens/NetworkSettings'; import { NetworkStatusBar } from './components/NetworkSettings/NetworkStatusBar'; +import ShowPushNotificationTxDetails from './components/ShowPushNotificationTxDetails'; /** * This Stack Navigator is exhibited when there is no wallet initialized on the local storage. @@ -725,6 +726,7 @@ const App = () => ( theme={HathorTheme} ref={navigationRef} > + diff --git a/src/components/ShowPushNotificationTxDetails.js b/src/components/ShowPushNotificationTxDetails.js index b16819901..7989465ec 100644 --- a/src/components/ShowPushNotificationTxDetails.js +++ b/src/components/ShowPushNotificationTxDetails.js @@ -1,3 +1,4 @@ +import { useNavigation } from '@react-navigation/native'; import React from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { t } from 'ttag'; @@ -9,7 +10,8 @@ const txNotFoundTitle = t`Transation not found`; const txNotFoundBody = t`The transaction has not arrived yet in your wallet. Do you want to retry?`; const txNotFoundButton = t`Retry`; -export default function ShowPushNotificationTxDetails(props) { +export default function ShowPushNotificationTxDetails() { + const navigation = useNavigation(); const txDetails = useSelector((state) => state.pushNotification.txDetails); const dispatch = useDispatch(); @@ -26,7 +28,7 @@ export default function ShowPushNotificationTxDetails(props) { const renderPushTxDetailsModal = () => ( dispatch(pushCleanTxDetails())} diff --git a/src/constants.js b/src/constants.js index 2bee3e091..7d9788cee 100644 --- a/src/constants.js +++ b/src/constants.js @@ -254,3 +254,8 @@ export const NETWORK_PRIVATENET = 'privatenet'; export const MAX_RETRIES = 8; export const INITIAL_RETRY_LATENCY = 300; // ms export const LATENCY_MULTIPLIER = 30; // multiplier per iteration +/** + * Timeout for await wallet load in the context of tx details loading. + * It awaits 5 minutes. + */ +export const TX_DETAILS_TIMEOUT = 5 * 60 * 1000; // 5 minutes diff --git a/src/sagas/pushNotification.js b/src/sagas/pushNotification.js index 09793ed30..d7679a908 100644 --- a/src/sagas/pushNotification.js +++ b/src/sagas/pushNotification.js @@ -46,6 +46,7 @@ import { pushNotificationKey, PUSH_CHANNEL_TRANSACTION, PUSH_ACTION, + TX_DETAILS_TIMEOUT, } from '../constants'; import { getPushNotificationSettings } from '../utils'; import { STORE } from '../store'; @@ -55,6 +56,17 @@ import { WALLET_STATUS } from './wallet'; import { logger } from '../logger'; const log = logger('push-notification-saga'); +log.haltingTxDetailsLoading = () => log.debug('Halting tx details loading.'); +log.timeoutTxDetailsLoading = () => { + const msg = 'Timeout during tx details loading.'; + log.error(msg); + return msg; +}; +log.errorTxDetailsLoading = () => { + const msg = 'Error loading tx details during wallet loading.' + log.error(msg); + return msg; +}; const TRANSACTION_CHANNEL_NAME = t`Transaction`; const PUSH_ACTION_TITLE = t`Open`; @@ -616,16 +628,53 @@ export function* loadTxDetails(action) { const { txId } = action.payload; const isLocked = yield select((state) => state.lockScreen); if (isLocked) { + log.debug('Awaiting wallet unlock to resume tx details loading.'); const { resetWallet } = yield race({ - // Wait for the unlock screen to be dismissed, and wallet to be loaded - unlockWallet: all([take(isUnlockScreen), take(types.START_WALLET_SUCCESS)]), + // Wait for the unlock screen to be dismissed + unlockWallet: take(isUnlockScreen), resetWallet: take(types.RESET_WALLET) }); + if (resetWallet) { - log.debug('Halting loadTxDetails.'); + log.debug('User has chosen to reset wallet during tx details loading.'); + log.haltingTxDetailsLoading(); return; } - log.debug('Continuing loadTxDetails after unlock screen.'); + + // Halt if wallet loading has already failed + const walletStatus = yield select((state) => state.walletStartState); + if (walletStatus === WALLET_STATUS.FAILED) { + // It shouldn't happen because this very effect is invoked only after + // wallet load succeeds. + log.error('Error loading tx details while wallet has failed.'); + log.haltingTxDetailsLoading(); + return; + } + + // Await wallet load if wallet is not ready yet, otherwise continue + if (walletStatus !== WALLET_STATUS.READY) { + // Await wallet load until timeout or error. + const { error, timeout } = yield race({ + ready: take(types.START_WALLET_SUCCESS), + error: take(types.WALLET_STATE_ERROR), + timeout: delay(TX_DETAILS_TIMEOUT), + }); + + // It doesn't make sense to await more than that for a notification. + if (timeout) { + yield put(onExceptionCaptured(log.timeoutTxDetailsLoading())); + log.haltingTxDetailsLoading(); + return; + } + + if (error) { + yield put(onExceptionCaptured(log.errorTxDetailsLoading())); + log.haltingTxDetailsLoading(); + return; + } + } + + log.debug('Continuing tx details loading after unlock screen.'); } try { diff --git a/src/screens/Dashboard.js b/src/screens/Dashboard.js index d6b8143c3..fb1ad736e 100644 --- a/src/screens/Dashboard.js +++ b/src/screens/Dashboard.js @@ -17,7 +17,6 @@ import TokenSelect from '../components/TokenSelect'; import SimpleButton from '../components/SimpleButton'; import OfflineBar from '../components/OfflineBar'; import { tokenFetchBalanceRequested, updateSelectedToken } from '../actions'; -import ShowPushNotificationTxDetails from '../components/ShowPushNotificationTxDetails'; import AskForPushNotificationRefresh from '../components/AskForPushNotificationRefresh'; /** @@ -78,7 +77,6 @@ class Dashboard extends React.Component { return ( - Date: Wed, 31 Jul 2024 13:30:29 +0100 Subject: [PATCH 33/51] bump: v0.27.2 (#513) --- 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 b78b6c662..f53d04b4e 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 71 - versionName "0.27.1" + versionCode 72 + versionName "0.27.2" missingDimensionStrategy "react-native-camera", "general" } signingConfigs { diff --git a/ios/HathorMobile.xcodeproj/project.pbxproj b/ios/HathorMobile.xcodeproj/project.pbxproj index 670370d97..d2a8656ef 100644 --- a/ios/HathorMobile.xcodeproj/project.pbxproj +++ b/ios/HathorMobile.xcodeproj/project.pbxproj @@ -521,7 +521,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 0.27.1; + MARKETING_VERSION = 0.27.2; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -550,7 +550,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 0.27.1; + MARKETING_VERSION = 0.27.2; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", diff --git a/package-lock.json b/package-lock.json index df6c6b6f3..af6cf78e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "HathorMobile", - "version": "0.27.1", + "version": "0.27.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "HathorMobile", - "version": "0.27.1", + "version": "0.27.2", "hasInstallScript": true, "dependencies": { "@fortawesome/fontawesome-svg-core": "1.2.36", diff --git a/package.json b/package.json index b2be42b10..c723ba9c1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "HathorMobile", - "version": "0.27.1", + "version": "0.27.2", "engines": { "node": ">=18.0.0", "npm": ">=9.0.0" From b011a4fd01147900138f1b0296bf6270632d6bb0 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Tue, 13 Aug 2024 20:22:13 +0100 Subject: [PATCH 34/51] lint: comply with rules --- src/screens/Dashboard.js | 1 + src/utils.js | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/screens/Dashboard.js b/src/screens/Dashboard.js index f70447ef4..0e7f1ada3 100644 --- a/src/screens/Dashboard.js +++ b/src/screens/Dashboard.js @@ -24,6 +24,7 @@ import { COLORS } from '../styles/themes'; import { TOKEN_DOWNLOAD_STATUS } from '../sagas/tokens'; import { NanoContractsList } from '../components/NanoContract/NanoContractsList'; import { getNanoContractFeatureToggle } from '../utils'; +import ShowPushNotificationTxDetails from '../components/ShowPushNotificationTxDetails'; /** * State filter to retrieve token-related data from root state. diff --git a/src/utils.js b/src/utils.js index d903b42d2..212540fa1 100644 --- a/src/utils.js +++ b/src/utils.js @@ -12,7 +12,6 @@ import { isEmpty } from 'lodash'; import { t } from 'ttag'; import { Linking, Platform, Text } from 'react-native'; import { getStatusBarHeight } from 'react-native-status-bar-height'; -import { isEmpty } from 'lodash'; import moment from 'moment'; import baseStyle from './styles/init'; import { KEYCHAIN_USER, NETWORK_MAINNET, NANO_CONTRACT_FEATURE_TOGGLE } from './constants'; From 87b5884eac4acb8abc8cd192055323f11c47f03f Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Fri, 16 Aug 2024 17:06:17 +0100 Subject: [PATCH 35/51] feat: nano contract integration with wallet connect (#495) * feat: implement Nano Contract integration with Wallet Connect * feat: adapt new nc tx to initialize, set result and withdrawal * feat: make ncAddress react to firstAddress change * feat: add name to method arguments * feat: fire sentry error notification for method info not found * feat: improve title of Arguments component * feat: retrieve fallback arg entries when blueprint is loading * feat: add nft check to action token on Actions component * feat: add blueprint download to the tx loading flag and a redundancy --- locale/da/texts.po | 303 ++++++++++++---- locale/pt-br/texts.po | 310 ++++++++++++---- locale/ru-ru/texts.po | 303 ++++++++++++---- locale/texts.pot | 302 ++++++++++++---- src/App.js | 4 + src/actions.js | 132 ++++++- src/components/FeedbackContent.js | 30 +- src/components/FeedbackModal.js | 13 + src/components/FrozenTextValue.js | 30 ++ src/components/HathorFlatList.js | 12 +- src/components/Icons/CircleInfo.icon.js | 2 +- src/components/TextValue.js | 11 +- .../NanoContract/DappContainer.js | 84 +++++ .../NanoContract/DeclineModal.js | 54 +++ .../NanoContract/NanoContractActions.js | 203 +++++++++++ .../NanoContract/NanoContractExecInfo.js | 170 +++++++++ .../NanoContract/NanoContractMethodArgs.js | 154 ++++++++ .../NewNanoContractTransactionModal.js | 163 +++++++++ .../NewNanoContractTransactionRequest.js | 332 ++++++++++++++++++ src/components/WalletConnect/theme.js | 74 ++++ src/components/WarnTextValue.js | 31 ++ src/constants.js | 18 + src/reducers/reducer.js | 313 ++++++++++++++++- src/sagas/nanoContract.js | 32 ++ src/sagas/tokens.js | 51 +++ src/sagas/wallet.js | 13 +- src/sagas/walletConnect.js | 1 + .../NewNanoContractTransactionScreen.js | 44 +++ src/styles/themes.js | 2 + 29 files changed, 2876 insertions(+), 315 deletions(-) create mode 100644 src/components/FrozenTextValue.js create mode 100644 src/components/WalletConnect/NanoContract/DappContainer.js create mode 100644 src/components/WalletConnect/NanoContract/DeclineModal.js create mode 100644 src/components/WalletConnect/NanoContract/NanoContractActions.js create mode 100644 src/components/WalletConnect/NanoContract/NanoContractExecInfo.js create mode 100644 src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js create mode 100644 src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js create mode 100644 src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js create mode 100644 src/components/WalletConnect/theme.js create mode 100644 src/components/WarnTextValue.js create mode 100644 src/screens/WalletConnect/NewNanoContractTransactionScreen.js diff --git a/locale/da/texts.po b/locale/da/texts.po index 2242db836..2f2f1ee00 100644 --- a/locale/da/texts.po +++ b/locale/da/texts.po @@ -57,11 +57,11 @@ msgid "[Last] dddd [•] HH:mm" msgstr "[Sidste] dddd [•] HH:mm" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:107 src/utils.js:415 +#: src/models.js:107 src/utils.js:416 msgid "DD MMM YYYY [•] HH:mm" msgstr "DD MMM YYYY [•] HH:mm" -#: src/utils.js:160 +#: src/utils.js:161 msgid "Invalid address" msgstr "Ugyldig adresse" @@ -432,9 +432,10 @@ msgstr "Ord" msgid "Enter your seed words separated by space" msgstr "Indtast dine seed-ord adskilt med mellemrum" -#: src/components/NanoContract/NanoContractDetails.component.js:194 +#: src/components/NanoContract/NanoContractDetails.js:194 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:282 #: src/screens/LoadHistoryScreen.js:51 src/screens/LoadWalletErrorScreen.js:20 -#: src/screens/NanoContract/NanoContractRegisterScreen.js:167 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:161 msgid "Try again" msgstr "" @@ -534,6 +535,7 @@ msgstr "Indtast din pinkode " msgid "Unlock Hathor Wallet" msgstr "Lås Hathor-wallet op" +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:84 #: src/screens/PinScreen.js:265 #: src/screens/WalletConnect/WalletConnectList.js:125 msgid "Cancel" @@ -698,8 +700,8 @@ msgstr "Autoriserer overførslen" msgid "Your transfer of **${ this.amountAndToken }** has been confirmed" msgstr "Din overførsel af **${ _this.amountAndToken }** er bekræftet" -#: src/components/NanoContract/EditAddressModal.component.js:60 -#: src/components/NanoContract/SelectAddressModal.component.js:117 +#: src/components/NanoContract/EditAddressModal.js:60 +#: src/components/NanoContract/SelectAddressModal.js:117 #: src/screens/SendConfirmScreen.js:161 msgid "Address" msgstr "Adresse" @@ -797,6 +799,11 @@ msgstr "Jeg vil afregistrere token **${ tokenLabel }**" msgid "Unregister token" msgstr "Afregistrer token" +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:68 +#: src/screens/WalletConnect/NewNanoContractTransactionScreen.js:24 +msgid "New Nano Contract Transaction" +msgstr "" + #: src/screens/WalletConnect/WalletConnectList.js:33 msgid "There was an error connecting. Please try again later." msgstr "" @@ -940,53 +947,55 @@ msgstr "" msgid "Nano Contract Details" msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:67 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:58 msgid "Nano Contract ID is required." msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:157 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:151 msgid "See contract" msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:175 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:169 msgid "Load First Addresses Error" msgstr "" -#: src/components/NanoContract/NanoContractDetails.component.js:158 -#: src/components/NanoContract/SelectAddressModal.component.js:105 -#: src/screens/NanoContract/NanoContractRegisterScreen.js:183 +#: src/components/NanoContract/NanoContractDetails.js:158 +#: src/components/NanoContract/SelectAddressModal.js:105 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:202 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:177 msgid "Loading" msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:184 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:178 msgid "Loading first wallet address." msgstr "" -#: src/components/NanoContract/NanoContractDetailsHeader.component.js:142 -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:88 +#: src/components/NanoContract/NanoContractDetailsHeader.js:142 +#: src/components/NanoContract/NanoContractTransactionHeader.js:88 #: src/components/NanoContract/NanoContractsListItem.js:57 #: src/components/TxDetailsModal.js:106 -#: src/screens/NanoContract/NanoContractRegisterScreen.js:193 +#: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:83 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:187 msgid "Nano Contract ID" msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:201 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:195 msgid "Wallet Address" msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:211 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:205 msgid "If you want to change the wallet address, you will be able to do" msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:213 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:207 msgid "after the contract is registered." msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:232 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:226 msgid "Register Nano Contract" msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:250 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:244 msgid "Nano Contract Registration" msgstr "" @@ -998,31 +1007,39 @@ msgstr "" msgid "Contract successfully registered." msgstr "" -#: src/sagas/nanoContract.js:43 +#: src/sagas/nanoContract.js:45 msgid "Nano Contract already registered." msgstr "" -#: src/sagas/nanoContract.js:44 +#: src/sagas/nanoContract.js:46 msgid "Wallet is not ready yet to register a Nano Contract." msgstr "" -#: src/sagas/nanoContract.js:45 +#: src/sagas/nanoContract.js:47 msgid "The informed address does not belong to the wallet." msgstr "" -#: src/sagas/nanoContract.js:46 +#: src/sagas/nanoContract.js:48 msgid "Nano Contract not found." msgstr "" -#: src/sagas/nanoContract.js:47 +#: src/sagas/nanoContract.js:49 msgid "Error while trying to get Nano Contract state." msgstr "" -#: src/sagas/nanoContract.js:48 +#: src/sagas/nanoContract.js:50 +msgid "Blueprint not found." +msgstr "" + +#: src/sagas/nanoContract.js:51 +msgid "Couldn't get Blueprint info." +msgstr "" + +#: src/sagas/nanoContract.js:52 msgid "Nano Contract not registered." msgstr "" -#: src/sagas/nanoContract.js:49 +#: src/sagas/nanoContract.js:53 msgid "Error while trying to download Nano Contract transactions history." msgstr "" @@ -1068,21 +1085,29 @@ msgstr "Transaktion" msgid "Open" msgstr "Åben" -#: src/sagas/wallet.js:744 +#: src/sagas/tokens.js:40 +msgid "Wallet is not ready yet." +msgstr "" + +#: src/sagas/tokens.js:41 +msgid "Error loading the details of some tokens." +msgstr "" + +#: src/sagas/wallet.js:749 msgid "Wallet is not ready to load addresses." msgstr "" #. This will show the message in the feedback content at SelectAddressModal -#: src/sagas/wallet.js:760 +#: src/sagas/wallet.js:765 msgid "There was an error while loading wallet addresses. Try again." msgstr "" -#: src/sagas/wallet.js:770 +#: src/sagas/wallet.js:775 msgid "Wallet is not ready to load the first address." msgstr "" #. This will show the message in the feedback content -#: src/sagas/wallet.js:786 +#: src/sagas/wallet.js:791 msgid "There was an error while loading first wallet address. Try again." msgstr "" @@ -1254,13 +1279,14 @@ msgstr "" msgid "Description" msgstr "Beskrivelse" -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:47 +#: src/components/NanoContract/NanoContractTransactionHeader.js:47 #: src/components/TxDetailsModal.js:104 msgid "Transaction ID" msgstr "" -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:92 +#: src/components/NanoContract/NanoContractTransactionHeader.js:92 #: src/components/TxDetailsModal.js:105 +#: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:116 msgid "Blueprint Method" msgstr "" @@ -1272,7 +1298,7 @@ msgstr "" msgid "Nano Contract Status" msgstr "" -#: src/components/NanoContract/NanoContractDetailsHeader.component.js:79 +#: src/components/NanoContract/NanoContractDetailsHeader.js:79 #: src/components/TxDetailsModal.js:120 msgid "Nano Contract" msgstr "" @@ -1306,83 +1332,216 @@ msgid "" "address derived from your root key on the m/44'/280'/0'/0/0 derivation path." msgstr "" +#: src/components/WalletConnect/NanoContract/DappContainer.js:41 +msgid "Review your transaction from this dApp" +msgstr "" + +#: src/components/WalletConnect/NanoContract/DappContainer.js:44 +msgid "Stay vigilant and protect your data from potential phishing attempts." +msgstr "" + +#: src/components/WalletConnect/NanoContract/DeclineModal.js:26 +msgid "Decline transaction" +msgstr "" + +#: src/components/WalletConnect/NanoContract/DeclineModal.js:29 +msgid "Are you sure you want to decline this transaction?" +msgstr "" + +#: src/components/WalletConnect/NanoContract/DeclineModal.js:33 +msgid "Yes, decline transaction" +msgstr "" + +#: src/components/NanoContract/UnregisterNanoContractModal.js:50 +#: src/components/WalletConnect/NanoContract/DeclineModal.js:39 +msgid "No, go back" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NanoContractActions.js:36 +#, javascript-format +msgid "${ tokenSymbol } Deposit" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NanoContractActions.js:37 +msgid "${ tokenSymbol } Withdrawal" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NanoContractActions.js:101 +msgid "Action List" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NanoContractActions.js:150 +msgid "To Address:" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:88 +msgid "Blueprint ID" +msgstr "" + +#: src/components/NanoContract/NanoContractDetailsHeader.js:146 +#: src/components/NanoContract/NanoContractsListItem.js:59 +#: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:93 +msgid "Blueprint Name" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:109 +#: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:140 +msgid "Loading..." +msgstr "" + +#: src/components/NanoContract/NanoContractTransactionHeader.js:106 +#: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:124 +msgid "Caller" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:143 +msgid "Couldn't determine address, select one" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:48 +#, javascript-format +msgid "Position ${ idx }" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:93 +msgid "Arguments" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:72 +msgid "You have received a new Nano Contract Transaction. Please" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:74 +msgid "carefully review the details" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:76 +msgid "before deciding to accept or decline." +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:80 +msgid "Review transaction details" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:98 +msgid "" +"Caution: There are risks associated with signing dapp transaction requests." +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:104 +msgid "Read More." +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:184 +msgid "Nano Contract Not Found" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:185 +msgid "" +"The Nano Contract requested is not registered. First register the Nano " +"Contract to interact with it." +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:188 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:235 +msgid "Decline Transaction" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:203 +msgid "Loading transaction information." +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:231 +msgid "Accept Transaction" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:245 +msgid "Sending transaction" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:246 +msgid "Please wait." +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:272 +msgid "Transaction successfully sent." +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:274 +msgid "Ok, close" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:280 +msgid "Error while sending transaction." +msgstr "" + #: src/components/NetworkSettings/NetworkStatusBar.js:14 msgid "Custom network" msgstr "" -#: src/components/NanoContract/EditAddressModal.component.js:41 +#: src/components/NanoContract/EditAddressModal.js:41 msgid "New Nano Contract Address" msgstr "" -#: src/components/NanoContract/EditAddressModal.component.js:49 +#: src/components/NanoContract/EditAddressModal.js:49 msgid "" "This address signs any transaction you create with Nano Contracts method. " "Switching to a new one means" msgstr "" -#: src/components/NanoContract/EditAddressModal.component.js:51 +#: src/components/NanoContract/EditAddressModal.js:51 msgid "all future transactions will use this address." msgstr "" -#: src/components/NanoContract/EditAddressModal.component.js:57 +#: src/components/NanoContract/EditAddressModal.js:57 msgid "Selected Information" msgstr "" -#: src/components/NanoContract/EditAddressModal.component.js:67 +#: src/components/NanoContract/EditAddressModal.js:67 msgid "Index" msgstr "" -#: src/components/NanoContract/EditAddressModal.component.js:74 +#: src/components/NanoContract/EditAddressModal.js:74 msgid "Confirm new address" msgstr "" -#: src/components/NanoContract/EditAddressModal.component.js:78 +#: src/components/NanoContract/EditAddressModal.js:78 msgid "Go back" msgstr "" -#: src/components/NanoContract/NanoContractDetails.component.js:159 +#: src/components/NanoContract/NanoContractDetails.js:159 msgid "Loading Nano Contract transactions." msgstr "" -#: src/components/NanoContract/NanoContractDetails.component.js:173 +#: src/components/NanoContract/NanoContractDetails.js:173 msgid "Nano Contract Transactions Error" msgstr "" -#: src/components/NanoContract/NanoContractDetailsHeader.component.js:146 -#: src/components/NanoContract/NanoContractsListItem.js:59 -msgid "Blueprint Name" -msgstr "" - -#: src/components/NanoContract/NanoContractDetailsHeader.component.js:150 +#: src/components/NanoContract/NanoContractDetailsHeader.js:150 msgid "Registered Address" msgstr "" -#: src/components/NanoContract/NanoContractDetailsHeader.component.js:153 +#: src/components/NanoContract/NanoContractDetailsHeader.js:153 msgid "See status details" msgstr "" -#: src/components/NanoContract/NanoContractDetailsHeader.component.js:154 +#: src/components/NanoContract/NanoContractDetailsHeader.js:154 msgid "Unregister contract" msgstr "" #. XXX: add when shrank component can be used. -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:96 +#: src/components/NanoContract/NanoContractTransactionHeader.js:96 msgid "Date and Time" msgstr "" #. XXX: add when shrank component can be used. -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:103 -#: src/components/NanoContract/NanoContractTransactionsListItem.component.js:62 +#: src/components/NanoContract/NanoContractTransactionHeader.js:103 +#: src/components/NanoContract/NanoContractTransactionsListItem.js:62 msgid "From this wallet" msgstr "" #. XXX: add when shrank component can be used. -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:106 -msgid "Caller" -msgstr "" - -#. XXX: add when shrank component can be used. -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:109 +#: src/components/NanoContract/NanoContractTransactionHeader.js:109 msgid "See transaction details" msgstr "" @@ -1400,42 +1559,38 @@ msgstr "" msgid "Register new" msgstr "" -#: src/components/NanoContract/SelectAddressModal.component.js:90 +#: src/components/NanoContract/SelectAddressModal.js:90 msgid "Choose New Wallet Address" msgstr "" -#: src/components/NanoContract/SelectAddressModal.component.js:97 +#: src/components/NanoContract/SelectAddressModal.js:97 msgid "Load Addresses Error" msgstr "" -#: src/components/NanoContract/SelectAddressModal.component.js:106 +#: src/components/NanoContract/SelectAddressModal.js:106 msgid "Loading wallet addresses." msgstr "" -#: src/components/NanoContract/SelectAddressModal.component.js:114 +#: src/components/NanoContract/SelectAddressModal.js:114 msgid "Current Information" msgstr "" -#: src/components/NanoContract/SelectAddressModal.component.js:115 +#: src/components/NanoContract/SelectAddressModal.js:115 msgid "To change, select other address on the list below." msgstr "" -#: src/components/NanoContract/SelectAddressModal.component.js:178 +#: src/components/NanoContract/SelectAddressModal.js:178 msgid "index" msgstr "" -#: src/components/NanoContract/UnregisterNanoContractModal.component.js:39 +#: src/components/NanoContract/UnregisterNanoContractModal.js:39 msgid "Unregister Nano Contract" msgstr "" -#: src/components/NanoContract/UnregisterNanoContractModal.component.js:41 +#: src/components/NanoContract/UnregisterNanoContractModal.js:41 msgid "Are you sure you want to unregister this Nano Contract?" msgstr "" -#: src/components/NanoContract/UnregisterNanoContractModal.component.js:44 +#: src/components/NanoContract/UnregisterNanoContractModal.js:44 msgid "Yes, unregister contract" msgstr "" - -#: src/components/NanoContract/UnregisterNanoContractModal.component.js:50 -msgid "No, go back" -msgstr "" diff --git a/locale/pt-br/texts.po b/locale/pt-br/texts.po index 1dbb3e1e0..58ae687d1 100644 --- a/locale/pt-br/texts.po +++ b/locale/pt-br/texts.po @@ -57,11 +57,11 @@ msgid "[Last] dddd [•] HH:mm" msgstr "[Última] dddd [•] HH:mm" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:107 src/utils.js:415 +#: src/models.js:107 src/utils.js:416 msgid "DD MMM YYYY [•] HH:mm" msgstr "DD MMM YYYY [•] HH:mm" -#: src/utils.js:160 +#: src/utils.js:161 msgid "Invalid address" msgstr "Endereço inválido" @@ -443,9 +443,10 @@ msgstr "Palavras" msgid "Enter your seed words separated by space" msgstr "Digite suas palavras separadas por espaços" -#: src/components/NanoContract/NanoContractDetails.component.js:194 +#: src/components/NanoContract/NanoContractDetails.js:194 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:282 #: src/screens/LoadHistoryScreen.js:51 src/screens/LoadWalletErrorScreen.js:20 -#: src/screens/NanoContract/NanoContractRegisterScreen.js:167 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:161 msgid "Try again" msgstr "Tente novamente" @@ -547,6 +548,7 @@ msgstr "Digite seu PIN " msgid "Unlock Hathor Wallet" msgstr "Desbloqueie sua Hathor Wallet" +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:84 #: src/screens/PinScreen.js:265 #: src/screens/WalletConnect/WalletConnectList.js:125 msgid "Cancel" @@ -714,8 +716,8 @@ msgstr "Autorizar operação" msgid "Your transfer of **${ this.amountAndToken }** has been confirmed" msgstr "Sua transferência de **${ this.amountAndToken }** foi confirmada" -#: src/components/NanoContract/EditAddressModal.component.js:60 -#: src/components/NanoContract/SelectAddressModal.component.js:117 +#: src/components/NanoContract/EditAddressModal.js:60 +#: src/components/NanoContract/SelectAddressModal.js:117 #: src/screens/SendConfirmScreen.js:161 msgid "Address" msgstr "Endereço" @@ -815,6 +817,11 @@ msgstr "Eu quero desregistrar o token **${ tokenLabel }**" msgid "Unregister token" msgstr "Desregistrar token" +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:68 +#: src/screens/WalletConnect/NewNanoContractTransactionScreen.js:24 +msgid "New Nano Contract Transaction" +msgstr "Nova Transação de Nano Contract" + #: src/screens/WalletConnect/WalletConnectList.js:33 msgid "There was an error connecting. Please try again later." msgstr "" @@ -966,53 +973,55 @@ msgstr "" msgid "Nano Contract Details" msgstr "Detalhes do Nano Contract" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:67 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:58 msgid "Nano Contract ID is required." msgstr "ID do Nano Contract é obrigatório." -#: src/screens/NanoContract/NanoContractRegisterScreen.js:157 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:151 msgid "See contract" msgstr "Ver contrato" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:175 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:169 msgid "Load First Addresses Error" msgstr "Erro ao carregar primeiro endereço da wallet" -#: src/components/NanoContract/NanoContractDetails.component.js:158 -#: src/components/NanoContract/SelectAddressModal.component.js:105 -#: src/screens/NanoContract/NanoContractRegisterScreen.js:183 +#: src/components/NanoContract/NanoContractDetails.js:158 +#: src/components/NanoContract/SelectAddressModal.js:105 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:202 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:177 msgid "Loading" msgstr "Carregando" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:184 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:178 msgid "Loading first wallet address." msgstr "Carregando primeiro endereço da wallet." -#: src/components/NanoContract/NanoContractDetailsHeader.component.js:142 -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:88 +#: src/components/NanoContract/NanoContractDetailsHeader.js:142 +#: src/components/NanoContract/NanoContractTransactionHeader.js:88 #: src/components/NanoContract/NanoContractsListItem.js:57 #: src/components/TxDetailsModal.js:106 -#: src/screens/NanoContract/NanoContractRegisterScreen.js:193 +#: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:83 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:187 msgid "Nano Contract ID" msgstr "ID do Nano Contract" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:201 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:195 msgid "Wallet Address" msgstr "Endereço da Carteira" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:211 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:205 msgid "If you want to change the wallet address, you will be able to do" msgstr "Se deseja alterar o endereço de assinatura, você pode alterar" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:213 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:207 msgid "after the contract is registered." msgstr "depois do contrato ser registrado." -#: src/screens/NanoContract/NanoContractRegisterScreen.js:232 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:226 msgid "Register Nano Contract" msgstr "Registrar Nano Contract" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:250 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:244 msgid "Nano Contract Registration" msgstr "Registro do Nano Contract" @@ -1024,31 +1033,39 @@ msgstr "Transação do Nano Contract" msgid "Contract successfully registered." msgstr "Nano Contract registrado com sucesso." -#: src/sagas/nanoContract.js:43 +#: src/sagas/nanoContract.js:45 msgid "Nano Contract already registered." msgstr "Nano Contract já registrado." -#: src/sagas/nanoContract.js:44 +#: src/sagas/nanoContract.js:46 msgid "Wallet is not ready yet to register a Nano Contract." msgstr "A wallet não está pronta ainda para registrar Nano Contracts." -#: src/sagas/nanoContract.js:45 +#: src/sagas/nanoContract.js:47 msgid "The informed address does not belong to the wallet." msgstr "O endereço informado não pertence a esta carteira." -#: src/sagas/nanoContract.js:46 +#: src/sagas/nanoContract.js:48 msgid "Nano Contract not found." msgstr "Nano Contract não encontrado" -#: src/sagas/nanoContract.js:47 +#: src/sagas/nanoContract.js:49 msgid "Error while trying to get Nano Contract state." msgstr "Erro ao obter o estado do Nano Contract." -#: src/sagas/nanoContract.js:48 +#: src/sagas/nanoContract.js:50 +msgid "Blueprint not found." +msgstr "Blueprint não encontrado." + +#: src/sagas/nanoContract.js:51 +msgid "Couldn't get Blueprint info." +msgstr "Não foi possível carregar informações do Blueprint." + +#: src/sagas/nanoContract.js:52 msgid "Nano Contract not registered." msgstr "Nano Contract não registrado." -#: src/sagas/nanoContract.js:49 +#: src/sagas/nanoContract.js:53 msgid "Error while trying to download Nano Contract transactions history." msgstr "Error ao fazer download do histórico de transações do Nano Contract." @@ -1095,22 +1112,29 @@ msgstr "Transação" msgid "Open" msgstr "Abrir" -#: src/sagas/wallet.js:744 +#: src/sagas/tokens.js:40 +msgid "Wallet is not ready yet." +msgstr "A wallet não está pronta ainda." + +#: src/sagas/tokens.js:41 +msgid "Error loading the details of some tokens." +msgstr "Ocorreu um erro durante o carregamento de detalhes de alguns tokens." + +#: src/sagas/wallet.js:749 msgid "Wallet is not ready to load addresses." msgstr "A wallet não está pronta para carregar os endereços." #. This will show the message in the feedback content at SelectAddressModal -#: src/sagas/wallet.js:760 +#: src/sagas/wallet.js:765 msgid "There was an error while loading wallet addresses. Try again." -msgstr "" -"Ocorreu um erro ao carregar os endereços da wallet. Tente novamente." +msgstr "Ocorreu um erro ao carregar os endereços da wallet. Tente novamente." -#: src/sagas/wallet.js:770 +#: src/sagas/wallet.js:775 msgid "Wallet is not ready to load the first address." msgstr "A wallet não está pronta para carregar o primeiro endereço." #. This will show the message in the feedback content -#: src/sagas/wallet.js:786 +#: src/sagas/wallet.js:791 msgid "There was an error while loading first wallet address. Try again." msgstr "" "Ocorreu um erro ao carregar o primeiro endereço da wallet. Tente novamente." @@ -1285,13 +1309,14 @@ msgstr "Inválida" msgid "Description" msgstr "Descrição" -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:47 +#: src/components/NanoContract/NanoContractTransactionHeader.js:47 #: src/components/TxDetailsModal.js:104 msgid "Transaction ID" msgstr "ID da Transação" -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:92 +#: src/components/NanoContract/NanoContractTransactionHeader.js:92 #: src/components/TxDetailsModal.js:105 +#: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:116 msgid "Blueprint Method" msgstr "Método do Blueprint" @@ -1303,7 +1328,7 @@ msgstr "Endereço de assinatura do Nano Contract" msgid "Nano Contract Status" msgstr "Status do Nano Contract" -#: src/components/NanoContract/NanoContractDetailsHeader.component.js:79 +#: src/components/NanoContract/NanoContractDetailsHeader.js:79 #: src/components/TxDetailsModal.js:120 msgid "Nano Contract" msgstr "Nano Contract" @@ -1344,15 +1369,162 @@ msgstr "" "endereço derivado da sua chave raiz no caminho de derivação " "m/44'/280'/0'/0/0." +#: src/components/WalletConnect/NanoContract/DappContainer.js:41 +msgid "Review your transaction from this dApp" +msgstr "Revise sua transação originada neste dApp" + +#: src/components/WalletConnect/NanoContract/DappContainer.js:44 +msgid "Stay vigilant and protect your data from potential phishing attempts." +msgstr "Fique atento e proteja seus dados de potenciais tentativas de phising." + +#: src/components/WalletConnect/NanoContract/DeclineModal.js:26 +msgid "Decline transaction" +msgstr "Recusar transação" + +#: src/components/WalletConnect/NanoContract/DeclineModal.js:29 +msgid "Are you sure you want to decline this transaction?" +msgstr "Tem certeza que deseja recusar esta transação?" + +#: src/components/WalletConnect/NanoContract/DeclineModal.js:33 +msgid "Yes, decline transaction" +msgstr "Sim, recusar transação" + +#: src/components/NanoContract/UnregisterNanoContractModal.js:50 +#: src/components/WalletConnect/NanoContract/DeclineModal.js:39 +msgid "No, go back" +msgstr "Não, voltar" + +#: src/components/WalletConnect/NanoContract/NanoContractActions.js:36 +#, javascript-format +msgid "${ tokenSymbol } Deposit" +msgstr "${ tokenSymbol } Depósito" + +#: src/components/WalletConnect/NanoContract/NanoContractActions.js:37 +msgid "${ tokenSymbol } Withdrawal" +msgstr "${ tokenSymbol } Saque" + +#: src/components/WalletConnect/NanoContract/NanoContractActions.js:101 +msgid "Action List" +msgstr "Lista de Actions" + +#: src/components/WalletConnect/NanoContract/NanoContractActions.js:150 +msgid "To Address:" +msgstr "Para o endereço:" + +#: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:88 +msgid "Blueprint ID" +msgstr "ID do Blueprint" + +#: src/components/NanoContract/NanoContractDetailsHeader.js:146 +#: src/components/NanoContract/NanoContractsListItem.js:59 +#: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:93 +msgid "Blueprint Name" +msgstr "Nome do Blueprint" + +#: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:109 +#: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:140 +msgid "Loading..." +msgstr "Carregando..." + +#: src/components/NanoContract/NanoContractTransactionHeader.js:106 +#: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:124 +msgid "Caller" +msgstr "Caller" + +#: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:143 +msgid "Couldn't determine address, select one" +msgstr "Não foi possível determinar um endereço, selecione um" + +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:48 +#, javascript-format +msgid "Position ${ idx }" +msgstr "Posição ${ idx }" + +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:93 +msgid "Arguments" +msgstr "Argumentos" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:72 +msgid "You have received a new Nano Contract Transaction. Please" +msgstr "" +"Você recebeu um pedido para criar uma transação de Nano Contract. Por favor," + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:74 +msgid "carefully review the details" +msgstr "revise os detalhes com cuidado" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:76 +msgid "before deciding to accept or decline." +msgstr "antes de decidir aceitar ou recusar." + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:80 +msgid "Review transaction details" +msgstr "Revisar detalhes da transação" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:98 +msgid "" +"Caution: There are risks associated with signing dapp transaction requests." +msgstr "" +"Cuidado: Há riscos associados a pedidos de assinatura de transação via dApp." + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:104 +msgid "Read More." +msgstr "Ler mais." + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:184 +msgid "Nano Contract Not Found" +msgstr "Nano Contract não encontrado" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:185 +msgid "" +"The Nano Contract requested is not registered. First register the Nano " +"Contract to interact with it." +msgstr "" +"O Nano Contract solicitado não está registrado. Primeiro registre o Nano " +"Contract para interagir com ele." + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:188 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:235 +msgid "Decline Transaction" +msgstr "Recusar transação" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:203 +msgid "Loading transaction information." +msgstr "Carregando informações da transação." + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:231 +msgid "Accept Transaction" +msgstr "Aceitar transação" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:245 +msgid "Sending transaction" +msgstr "Enviando transação" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:246 +msgid "Please wait." +msgstr "Por favor, espere." + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:272 +msgid "Transaction successfully sent." +msgstr "Transação enviada com sucesso." + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:274 +msgid "Ok, close" +msgstr "Ok, fechar" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:280 +msgid "Error while sending transaction." +msgstr "Ocorreu um erro durante o envio da transação." + #: src/components/NetworkSettings/NetworkStatusBar.js:14 msgid "Custom network" msgstr "Rede personalizada" -#: src/components/NanoContract/EditAddressModal.component.js:41 +#: src/components/NanoContract/EditAddressModal.js:41 msgid "New Nano Contract Address" msgstr "Novo endereço para o Nano Contract" -#: src/components/NanoContract/EditAddressModal.component.js:49 +#: src/components/NanoContract/EditAddressModal.js:49 msgid "" "This address signs any transaction you create with Nano Contracts method. " "Switching to a new one means" @@ -1360,69 +1532,59 @@ msgstr "" "Este endereço assina toda transação criada com os métodos do Nano Contract. " "Ao alterar para um novo endereço significa que" -#: src/components/NanoContract/EditAddressModal.component.js:51 +#: src/components/NanoContract/EditAddressModal.js:51 msgid "all future transactions will use this address." msgstr "todas as transações futuras usarão este endereço." -#: src/components/NanoContract/EditAddressModal.component.js:57 +#: src/components/NanoContract/EditAddressModal.js:57 msgid "Selected Information" msgstr "Informação Selecionada" -#: src/components/NanoContract/EditAddressModal.component.js:67 +#: src/components/NanoContract/EditAddressModal.js:67 msgid "Index" msgstr "Índice" -#: src/components/NanoContract/EditAddressModal.component.js:74 +#: src/components/NanoContract/EditAddressModal.js:74 msgid "Confirm new address" msgstr "Confirmar novo endereço" -#: src/components/NanoContract/EditAddressModal.component.js:78 +#: src/components/NanoContract/EditAddressModal.js:78 msgid "Go back" msgstr "Voltar" -#: src/components/NanoContract/NanoContractDetails.component.js:159 +#: src/components/NanoContract/NanoContractDetails.js:159 msgid "Loading Nano Contract transactions." msgstr "Carregando transações do Nano Contract" -#: src/components/NanoContract/NanoContractDetails.component.js:173 +#: src/components/NanoContract/NanoContractDetails.js:173 msgid "Nano Contract Transactions Error" msgstr "Erro em Transações do Nano Contract" -#: src/components/NanoContract/NanoContractDetailsHeader.component.js:146 -#: src/components/NanoContract/NanoContractsListItem.js:59 -msgid "Blueprint Name" -msgstr "Nome do Blueprint" - -#: src/components/NanoContract/NanoContractDetailsHeader.component.js:150 +#: src/components/NanoContract/NanoContractDetailsHeader.js:150 msgid "Registered Address" msgstr "Endereço Registrado" -#: src/components/NanoContract/NanoContractDetailsHeader.component.js:153 +#: src/components/NanoContract/NanoContractDetailsHeader.js:153 msgid "See status details" msgstr "Ver detalhes" -#: src/components/NanoContract/NanoContractDetailsHeader.component.js:154 +#: src/components/NanoContract/NanoContractDetailsHeader.js:154 msgid "Unregister contract" msgstr "Desregistrar contract" #. XXX: add when shrank component can be used. -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:96 +#: src/components/NanoContract/NanoContractTransactionHeader.js:96 msgid "Date and Time" msgstr "Data & Hora" #. XXX: add when shrank component can be used. -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:103 -#: src/components/NanoContract/NanoContractTransactionsListItem.component.js:62 +#: src/components/NanoContract/NanoContractTransactionHeader.js:103 +#: src/components/NanoContract/NanoContractTransactionsListItem.js:62 msgid "From this wallet" msgstr "Desta wallet" #. XXX: add when shrank component can be used. -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:106 -msgid "Caller" -msgstr "Caller" - -#. XXX: add when shrank component can be used. -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:109 +#: src/components/NanoContract/NanoContractTransactionHeader.js:109 msgid "See transaction details" msgstr "Ver detalhes" @@ -1440,42 +1602,38 @@ msgstr "Você pode acompanhar os Nano Contracts registrados nesta tela." msgid "Register new" msgstr "Registrar novo" -#: src/components/NanoContract/SelectAddressModal.component.js:90 +#: src/components/NanoContract/SelectAddressModal.js:90 msgid "Choose New Wallet Address" msgstr "Escolha o Novo Endereço" -#: src/components/NanoContract/SelectAddressModal.component.js:97 +#: src/components/NanoContract/SelectAddressModal.js:97 msgid "Load Addresses Error" msgstr "Erro ao carregar endereços" -#: src/components/NanoContract/SelectAddressModal.component.js:106 +#: src/components/NanoContract/SelectAddressModal.js:106 msgid "Loading wallet addresses." msgstr "Carregando endereços da wallet." -#: src/components/NanoContract/SelectAddressModal.component.js:114 +#: src/components/NanoContract/SelectAddressModal.js:114 msgid "Current Information" msgstr "Informação Atual" -#: src/components/NanoContract/SelectAddressModal.component.js:115 +#: src/components/NanoContract/SelectAddressModal.js:115 msgid "To change, select other address on the list below." msgstr "Para alterar, selecione outro endereço da lista abaixo." -#: src/components/NanoContract/SelectAddressModal.component.js:178 +#: src/components/NanoContract/SelectAddressModal.js:178 msgid "index" msgstr "índice" -#: src/components/NanoContract/UnregisterNanoContractModal.component.js:39 +#: src/components/NanoContract/UnregisterNanoContractModal.js:39 msgid "Unregister Nano Contract" msgstr "Desregistrar Nano Contract" -#: src/components/NanoContract/UnregisterNanoContractModal.component.js:41 +#: src/components/NanoContract/UnregisterNanoContractModal.js:41 msgid "Are you sure you want to unregister this Nano Contract?" msgstr "Confirma que deseja desregistrar este Nano Contract?" -#: src/components/NanoContract/UnregisterNanoContractModal.component.js:44 +#: src/components/NanoContract/UnregisterNanoContractModal.js:44 msgid "Yes, unregister contract" msgstr "Sim, desregistrar" - -#: src/components/NanoContract/UnregisterNanoContractModal.component.js:50 -msgid "No, go back" -msgstr "Não, voltar" diff --git a/locale/ru-ru/texts.po b/locale/ru-ru/texts.po index a7988691e..f3b11bb36 100644 --- a/locale/ru-ru/texts.po +++ b/locale/ru-ru/texts.po @@ -58,11 +58,11 @@ msgid "[Last] dddd [•] HH:mm" msgstr "[Последний] dddd [•] HH:mm" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:107 src/utils.js:415 +#: src/models.js:107 src/utils.js:416 msgid "DD MMM YYYY [•] HH:mm" msgstr "DD MMM YYYY [•] HH:mm" -#: src/utils.js:160 +#: src/utils.js:161 msgid "Invalid address" msgstr "Неправильный адрес" @@ -433,9 +433,10 @@ msgstr "Слова" msgid "Enter your seed words separated by space" msgstr "Введите seed-фразу" -#: src/components/NanoContract/NanoContractDetails.component.js:194 +#: src/components/NanoContract/NanoContractDetails.js:194 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:282 #: src/screens/LoadHistoryScreen.js:51 src/screens/LoadWalletErrorScreen.js:20 -#: src/screens/NanoContract/NanoContractRegisterScreen.js:167 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:161 msgid "Try again" msgstr "" @@ -536,6 +537,7 @@ msgstr "Введите свой PIN-код " msgid "Unlock Hathor Wallet" msgstr "Разблокировать Hathor Wallet" +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:84 #: src/screens/PinScreen.js:265 #: src/screens/WalletConnect/WalletConnectList.js:125 msgid "Cancel" @@ -700,8 +702,8 @@ msgstr "Авторизовать операцию" msgid "Your transfer of **${ this.amountAndToken }** has been confirmed" msgstr "Ваш перевод **${ this.amountAndToken }** был подтвержден" -#: src/components/NanoContract/EditAddressModal.component.js:60 -#: src/components/NanoContract/SelectAddressModal.component.js:117 +#: src/components/NanoContract/EditAddressModal.js:60 +#: src/components/NanoContract/SelectAddressModal.js:117 #: src/screens/SendConfirmScreen.js:161 msgid "Address" msgstr "Адрес" @@ -801,6 +803,11 @@ msgstr "Я хочу отменить регистрацию токена **${ to msgid "Unregister token" msgstr "Отменить регистрацию токена" +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:68 +#: src/screens/WalletConnect/NewNanoContractTransactionScreen.js:24 +msgid "New Nano Contract Transaction" +msgstr "" + #: src/screens/WalletConnect/WalletConnectList.js:33 msgid "There was an error connecting. Please try again later." msgstr "" @@ -944,53 +951,55 @@ msgstr "" msgid "Nano Contract Details" msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:67 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:58 msgid "Nano Contract ID is required." msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:157 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:151 msgid "See contract" msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:175 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:169 msgid "Load First Addresses Error" msgstr "" -#: src/components/NanoContract/NanoContractDetails.component.js:158 -#: src/components/NanoContract/SelectAddressModal.component.js:105 -#: src/screens/NanoContract/NanoContractRegisterScreen.js:183 +#: src/components/NanoContract/NanoContractDetails.js:158 +#: src/components/NanoContract/SelectAddressModal.js:105 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:202 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:177 msgid "Loading" msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:184 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:178 msgid "Loading first wallet address." msgstr "" -#: src/components/NanoContract/NanoContractDetailsHeader.component.js:142 -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:88 +#: src/components/NanoContract/NanoContractDetailsHeader.js:142 +#: src/components/NanoContract/NanoContractTransactionHeader.js:88 #: src/components/NanoContract/NanoContractsListItem.js:57 #: src/components/TxDetailsModal.js:106 -#: src/screens/NanoContract/NanoContractRegisterScreen.js:193 +#: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:83 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:187 msgid "Nano Contract ID" msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:201 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:195 msgid "Wallet Address" msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:211 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:205 msgid "If you want to change the wallet address, you will be able to do" msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:213 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:207 msgid "after the contract is registered." msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:232 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:226 msgid "Register Nano Contract" msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:250 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:244 msgid "Nano Contract Registration" msgstr "" @@ -1002,31 +1011,39 @@ msgstr "" msgid "Contract successfully registered." msgstr "" -#: src/sagas/nanoContract.js:43 +#: src/sagas/nanoContract.js:45 msgid "Nano Contract already registered." msgstr "" -#: src/sagas/nanoContract.js:44 +#: src/sagas/nanoContract.js:46 msgid "Wallet is not ready yet to register a Nano Contract." msgstr "" -#: src/sagas/nanoContract.js:45 +#: src/sagas/nanoContract.js:47 msgid "The informed address does not belong to the wallet." msgstr "" -#: src/sagas/nanoContract.js:46 +#: src/sagas/nanoContract.js:48 msgid "Nano Contract not found." msgstr "" -#: src/sagas/nanoContract.js:47 +#: src/sagas/nanoContract.js:49 msgid "Error while trying to get Nano Contract state." msgstr "" -#: src/sagas/nanoContract.js:48 +#: src/sagas/nanoContract.js:50 +msgid "Blueprint not found." +msgstr "" + +#: src/sagas/nanoContract.js:51 +msgid "Couldn't get Blueprint info." +msgstr "" + +#: src/sagas/nanoContract.js:52 msgid "Nano Contract not registered." msgstr "" -#: src/sagas/nanoContract.js:49 +#: src/sagas/nanoContract.js:53 msgid "Error while trying to download Nano Contract transactions history." msgstr "" @@ -1072,21 +1089,29 @@ msgstr "" msgid "Open" msgstr "Открыть" -#: src/sagas/wallet.js:744 +#: src/sagas/tokens.js:40 +msgid "Wallet is not ready yet." +msgstr "" + +#: src/sagas/tokens.js:41 +msgid "Error loading the details of some tokens." +msgstr "" + +#: src/sagas/wallet.js:749 msgid "Wallet is not ready to load addresses." msgstr "" #. This will show the message in the feedback content at SelectAddressModal -#: src/sagas/wallet.js:760 +#: src/sagas/wallet.js:765 msgid "There was an error while loading wallet addresses. Try again." msgstr "" -#: src/sagas/wallet.js:770 +#: src/sagas/wallet.js:775 msgid "Wallet is not ready to load the first address." msgstr "" #. This will show the message in the feedback content -#: src/sagas/wallet.js:786 +#: src/sagas/wallet.js:791 msgid "There was an error while loading first wallet address. Try again." msgstr "" @@ -1243,13 +1268,14 @@ msgstr "" msgid "Description" msgstr "Описание" -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:47 +#: src/components/NanoContract/NanoContractTransactionHeader.js:47 #: src/components/TxDetailsModal.js:104 msgid "Transaction ID" msgstr "" -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:92 +#: src/components/NanoContract/NanoContractTransactionHeader.js:92 #: src/components/TxDetailsModal.js:105 +#: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:116 msgid "Blueprint Method" msgstr "" @@ -1261,7 +1287,7 @@ msgstr "" msgid "Nano Contract Status" msgstr "" -#: src/components/NanoContract/NanoContractDetailsHeader.component.js:79 +#: src/components/NanoContract/NanoContractDetailsHeader.js:79 #: src/components/TxDetailsModal.js:120 msgid "Nano Contract" msgstr "" @@ -1295,83 +1321,216 @@ msgid "" "address derived from your root key on the m/44'/280'/0'/0/0 derivation path." msgstr "" +#: src/components/WalletConnect/NanoContract/DappContainer.js:41 +msgid "Review your transaction from this dApp" +msgstr "" + +#: src/components/WalletConnect/NanoContract/DappContainer.js:44 +msgid "Stay vigilant and protect your data from potential phishing attempts." +msgstr "" + +#: src/components/WalletConnect/NanoContract/DeclineModal.js:26 +msgid "Decline transaction" +msgstr "" + +#: src/components/WalletConnect/NanoContract/DeclineModal.js:29 +msgid "Are you sure you want to decline this transaction?" +msgstr "" + +#: src/components/WalletConnect/NanoContract/DeclineModal.js:33 +msgid "Yes, decline transaction" +msgstr "" + +#: src/components/NanoContract/UnregisterNanoContractModal.js:50 +#: src/components/WalletConnect/NanoContract/DeclineModal.js:39 +msgid "No, go back" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NanoContractActions.js:36 +#, javascript-format +msgid "${ tokenSymbol } Deposit" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NanoContractActions.js:37 +msgid "${ tokenSymbol } Withdrawal" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NanoContractActions.js:101 +msgid "Action List" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NanoContractActions.js:150 +msgid "To Address:" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:88 +msgid "Blueprint ID" +msgstr "" + +#: src/components/NanoContract/NanoContractDetailsHeader.js:146 +#: src/components/NanoContract/NanoContractsListItem.js:59 +#: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:93 +msgid "Blueprint Name" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:109 +#: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:140 +msgid "Loading..." +msgstr "" + +#: src/components/NanoContract/NanoContractTransactionHeader.js:106 +#: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:124 +msgid "Caller" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:143 +msgid "Couldn't determine address, select one" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:48 +#, javascript-format +msgid "Position ${ idx }" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:93 +msgid "Arguments" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:72 +msgid "You have received a new Nano Contract Transaction. Please" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:74 +msgid "carefully review the details" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:76 +msgid "before deciding to accept or decline." +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:80 +msgid "Review transaction details" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:98 +msgid "" +"Caution: There are risks associated with signing dapp transaction requests." +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:104 +msgid "Read More." +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:184 +msgid "Nano Contract Not Found" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:185 +msgid "" +"The Nano Contract requested is not registered. First register the Nano " +"Contract to interact with it." +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:188 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:235 +msgid "Decline Transaction" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:203 +msgid "Loading transaction information." +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:231 +msgid "Accept Transaction" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:245 +msgid "Sending transaction" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:246 +msgid "Please wait." +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:272 +msgid "Transaction successfully sent." +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:274 +msgid "Ok, close" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:280 +msgid "Error while sending transaction." +msgstr "" + #: src/components/NetworkSettings/NetworkStatusBar.js:14 msgid "Custom network" msgstr "" -#: src/components/NanoContract/EditAddressModal.component.js:41 +#: src/components/NanoContract/EditAddressModal.js:41 msgid "New Nano Contract Address" msgstr "" -#: src/components/NanoContract/EditAddressModal.component.js:49 +#: src/components/NanoContract/EditAddressModal.js:49 msgid "" "This address signs any transaction you create with Nano Contracts method. " "Switching to a new one means" msgstr "" -#: src/components/NanoContract/EditAddressModal.component.js:51 +#: src/components/NanoContract/EditAddressModal.js:51 msgid "all future transactions will use this address." msgstr "" -#: src/components/NanoContract/EditAddressModal.component.js:57 +#: src/components/NanoContract/EditAddressModal.js:57 msgid "Selected Information" msgstr "" -#: src/components/NanoContract/EditAddressModal.component.js:67 +#: src/components/NanoContract/EditAddressModal.js:67 msgid "Index" msgstr "" -#: src/components/NanoContract/EditAddressModal.component.js:74 +#: src/components/NanoContract/EditAddressModal.js:74 msgid "Confirm new address" msgstr "" -#: src/components/NanoContract/EditAddressModal.component.js:78 +#: src/components/NanoContract/EditAddressModal.js:78 msgid "Go back" msgstr "" -#: src/components/NanoContract/NanoContractDetails.component.js:159 +#: src/components/NanoContract/NanoContractDetails.js:159 msgid "Loading Nano Contract transactions." msgstr "" -#: src/components/NanoContract/NanoContractDetails.component.js:173 +#: src/components/NanoContract/NanoContractDetails.js:173 msgid "Nano Contract Transactions Error" msgstr "" -#: src/components/NanoContract/NanoContractDetailsHeader.component.js:146 -#: src/components/NanoContract/NanoContractsListItem.js:59 -msgid "Blueprint Name" -msgstr "" - -#: src/components/NanoContract/NanoContractDetailsHeader.component.js:150 +#: src/components/NanoContract/NanoContractDetailsHeader.js:150 msgid "Registered Address" msgstr "" -#: src/components/NanoContract/NanoContractDetailsHeader.component.js:153 +#: src/components/NanoContract/NanoContractDetailsHeader.js:153 msgid "See status details" msgstr "" -#: src/components/NanoContract/NanoContractDetailsHeader.component.js:154 +#: src/components/NanoContract/NanoContractDetailsHeader.js:154 msgid "Unregister contract" msgstr "" #. XXX: add when shrank component can be used. -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:96 +#: src/components/NanoContract/NanoContractTransactionHeader.js:96 msgid "Date and Time" msgstr "" #. XXX: add when shrank component can be used. -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:103 -#: src/components/NanoContract/NanoContractTransactionsListItem.component.js:62 +#: src/components/NanoContract/NanoContractTransactionHeader.js:103 +#: src/components/NanoContract/NanoContractTransactionsListItem.js:62 msgid "From this wallet" msgstr "" #. XXX: add when shrank component can be used. -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:106 -msgid "Caller" -msgstr "" - -#. XXX: add when shrank component can be used. -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:109 +#: src/components/NanoContract/NanoContractTransactionHeader.js:109 msgid "See transaction details" msgstr "" @@ -1389,42 +1548,38 @@ msgstr "" msgid "Register new" msgstr "" -#: src/components/NanoContract/SelectAddressModal.component.js:90 +#: src/components/NanoContract/SelectAddressModal.js:90 msgid "Choose New Wallet Address" msgstr "" -#: src/components/NanoContract/SelectAddressModal.component.js:97 +#: src/components/NanoContract/SelectAddressModal.js:97 msgid "Load Addresses Error" msgstr "" -#: src/components/NanoContract/SelectAddressModal.component.js:106 +#: src/components/NanoContract/SelectAddressModal.js:106 msgid "Loading wallet addresses." msgstr "" -#: src/components/NanoContract/SelectAddressModal.component.js:114 +#: src/components/NanoContract/SelectAddressModal.js:114 msgid "Current Information" msgstr "" -#: src/components/NanoContract/SelectAddressModal.component.js:115 +#: src/components/NanoContract/SelectAddressModal.js:115 msgid "To change, select other address on the list below." msgstr "" -#: src/components/NanoContract/SelectAddressModal.component.js:178 +#: src/components/NanoContract/SelectAddressModal.js:178 msgid "index" msgstr "" -#: src/components/NanoContract/UnregisterNanoContractModal.component.js:39 +#: src/components/NanoContract/UnregisterNanoContractModal.js:39 msgid "Unregister Nano Contract" msgstr "" -#: src/components/NanoContract/UnregisterNanoContractModal.component.js:41 +#: src/components/NanoContract/UnregisterNanoContractModal.js:41 msgid "Are you sure you want to unregister this Nano Contract?" msgstr "" -#: src/components/NanoContract/UnregisterNanoContractModal.component.js:44 +#: src/components/NanoContract/UnregisterNanoContractModal.js:44 msgid "Yes, unregister contract" msgstr "" - -#: src/components/NanoContract/UnregisterNanoContractModal.component.js:50 -msgid "No, go back" -msgstr "" diff --git a/locale/texts.pot b/locale/texts.pot index 5e4b032b8..b7d76f44e 100644 --- a/locale/texts.pot +++ b/locale/texts.pot @@ -48,12 +48,12 @@ msgid "[Last] dddd [•] HH:mm" msgstr "" #: src/models.js:107 -#: src/utils.js:415 +#: src/utils.js:416 #. See https://momentjs.com/docs/#/displaying/calendar-time/ msgid "DD MMM YYYY [•] HH:mm" msgstr "" -#: src/utils.js:160 +#: src/utils.js:161 msgid "Invalid address" msgstr "" @@ -421,10 +421,11 @@ msgstr "" msgid "Enter your seed words separated by space" msgstr "" -#: src/components/NanoContract/NanoContractDetails.component.js:194 +#: src/components/NanoContract/NanoContractDetails.js:194 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:282 #: src/screens/LoadHistoryScreen.js:51 #: src/screens/LoadWalletErrorScreen.js:20 -#: src/screens/NanoContract/NanoContractRegisterScreen.js:167 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:161 msgid "Try again" msgstr "" @@ -529,6 +530,7 @@ msgstr "" msgid "Unlock Hathor Wallet" msgstr "" +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:84 #: src/screens/PinScreen.js:265 #: src/screens/WalletConnect/WalletConnectList.js:125 msgid "Cancel" @@ -696,8 +698,8 @@ msgstr "" msgid "Your transfer of **${ this.amountAndToken }** has been confirmed" msgstr "" -#: src/components/NanoContract/EditAddressModal.component.js:60 -#: src/components/NanoContract/SelectAddressModal.component.js:117 +#: src/components/NanoContract/EditAddressModal.js:60 +#: src/components/NanoContract/SelectAddressModal.js:117 #: src/screens/SendConfirmScreen.js:161 msgid "Address" msgstr "" @@ -794,6 +796,11 @@ msgstr "" msgid "Unregister token" msgstr "" +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:68 +#: src/screens/WalletConnect/NewNanoContractTransactionScreen.js:24 +msgid "New Nano Contract Transaction" +msgstr "" + #: src/screens/WalletConnect/WalletConnectList.js:33 msgid "There was an error connecting. Please try again later." msgstr "" @@ -937,53 +944,55 @@ msgstr "" msgid "Nano Contract Details" msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:67 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:58 msgid "Nano Contract ID is required." msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:157 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:151 msgid "See contract" msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:175 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:169 msgid "Load First Addresses Error" msgstr "" -#: src/components/NanoContract/NanoContractDetails.component.js:158 -#: src/components/NanoContract/SelectAddressModal.component.js:105 -#: src/screens/NanoContract/NanoContractRegisterScreen.js:183 +#: src/components/NanoContract/NanoContractDetails.js:158 +#: src/components/NanoContract/SelectAddressModal.js:105 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:202 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:177 msgid "Loading" msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:184 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:178 msgid "Loading first wallet address." msgstr "" -#: src/components/NanoContract/NanoContractDetailsHeader.component.js:142 -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:88 +#: src/components/NanoContract/NanoContractDetailsHeader.js:142 +#: src/components/NanoContract/NanoContractTransactionHeader.js:88 #: src/components/NanoContract/NanoContractsListItem.js:57 #: src/components/TxDetailsModal.js:106 -#: src/screens/NanoContract/NanoContractRegisterScreen.js:193 +#: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:83 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:187 msgid "Nano Contract ID" msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:201 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:195 msgid "Wallet Address" msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:211 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:205 msgid "If you want to change the wallet address, you will be able to do" msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:213 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:207 msgid "after the contract is registered." msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:232 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:226 msgid "Register Nano Contract" msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:250 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:244 msgid "Nano Contract Registration" msgstr "" @@ -995,31 +1004,39 @@ msgstr "" msgid "Contract successfully registered." msgstr "" -#: src/sagas/nanoContract.js:43 +#: src/sagas/nanoContract.js:45 msgid "Nano Contract already registered." msgstr "" -#: src/sagas/nanoContract.js:44 +#: src/sagas/nanoContract.js:46 msgid "Wallet is not ready yet to register a Nano Contract." msgstr "" -#: src/sagas/nanoContract.js:45 +#: src/sagas/nanoContract.js:47 msgid "The informed address does not belong to the wallet." msgstr "" -#: src/sagas/nanoContract.js:46 +#: src/sagas/nanoContract.js:48 msgid "Nano Contract not found." msgstr "" -#: src/sagas/nanoContract.js:47 +#: src/sagas/nanoContract.js:49 msgid "Error while trying to get Nano Contract state." msgstr "" -#: src/sagas/nanoContract.js:48 +#: src/sagas/nanoContract.js:50 +msgid "Blueprint not found." +msgstr "" + +#: src/sagas/nanoContract.js:51 +msgid "Couldn't get Blueprint info." +msgstr "" + +#: src/sagas/nanoContract.js:52 msgid "Nano Contract not registered." msgstr "" -#: src/sagas/nanoContract.js:49 +#: src/sagas/nanoContract.js:53 msgid "Error while trying to download Nano Contract transactions history." msgstr "" @@ -1065,20 +1082,28 @@ msgstr "" msgid "Open" msgstr "" -#: src/sagas/wallet.js:744 +#: src/sagas/tokens.js:40 +msgid "Wallet is not ready yet." +msgstr "" + +#: src/sagas/tokens.js:41 +msgid "Error loading the details of some tokens." +msgstr "" + +#: src/sagas/wallet.js:749 msgid "Wallet is not ready to load addresses." msgstr "" -#: src/sagas/wallet.js:760 +#: src/sagas/wallet.js:765 #. This will show the message in the feedback content at SelectAddressModal msgid "There was an error while loading wallet addresses. Try again." msgstr "" -#: src/sagas/wallet.js:770 +#: src/sagas/wallet.js:775 msgid "Wallet is not ready to load the first address." msgstr "" -#: src/sagas/wallet.js:786 +#: src/sagas/wallet.js:791 #. This will show the message in the feedback content msgid "There was an error while loading first wallet address. Try again." msgstr "" @@ -1236,13 +1261,14 @@ msgstr "" msgid "Description" msgstr "" -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:47 +#: src/components/NanoContract/NanoContractTransactionHeader.js:47 #: src/components/TxDetailsModal.js:104 msgid "Transaction ID" msgstr "" -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:92 +#: src/components/NanoContract/NanoContractTransactionHeader.js:92 #: src/components/TxDetailsModal.js:105 +#: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:116 msgid "Blueprint Method" msgstr "" @@ -1254,7 +1280,7 @@ msgstr "" msgid "Nano Contract Status" msgstr "" -#: src/components/NanoContract/NanoContractDetailsHeader.component.js:79 +#: src/components/NanoContract/NanoContractDetailsHeader.js:79 #: src/components/TxDetailsModal.js:120 msgid "Nano Contract" msgstr "" @@ -1288,82 +1314,214 @@ msgid "" "address derived from your root key on the m/44'/280'/0'/0/0 derivation path." msgstr "" +#: src/components/WalletConnect/NanoContract/DappContainer.js:41 +msgid "Review your transaction from this dApp" +msgstr "" + +#: src/components/WalletConnect/NanoContract/DappContainer.js:44 +msgid "Stay vigilant and protect your data from potential phishing attempts." +msgstr "" + +#: src/components/WalletConnect/NanoContract/DeclineModal.js:26 +msgid "Decline transaction" +msgstr "" + +#: src/components/WalletConnect/NanoContract/DeclineModal.js:29 +msgid "Are you sure you want to decline this transaction?" +msgstr "" + +#: src/components/WalletConnect/NanoContract/DeclineModal.js:33 +msgid "Yes, decline transaction" +msgstr "" + +#: src/components/NanoContract/UnregisterNanoContractModal.js:50 +#: src/components/WalletConnect/NanoContract/DeclineModal.js:39 +msgid "No, go back" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NanoContractActions.js:36 +#, javascript-format +msgid "${ tokenSymbol } Deposit" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NanoContractActions.js:37 +msgid "${ tokenSymbol } Withdrawal" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NanoContractActions.js:101 +msgid "Action List" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NanoContractActions.js:150 +msgid "To Address:" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:88 +msgid "Blueprint ID" +msgstr "" + +#: src/components/NanoContract/NanoContractDetailsHeader.js:146 +#: src/components/NanoContract/NanoContractsListItem.js:59 +#: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:93 +msgid "Blueprint Name" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:109 +#: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:140 +msgid "Loading..." +msgstr "" + +#: src/components/NanoContract/NanoContractTransactionHeader.js:106 +#: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:124 +msgid "Caller" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:143 +msgid "Couldn't determine address, select one" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:48 +#, javascript-format +msgid "Position ${ idx }" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:93 +msgid "Arguments" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:72 +msgid "You have received a new Nano Contract Transaction. Please" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:74 +msgid "carefully review the details" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:76 +msgid "before deciding to accept or decline." +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:80 +msgid "Review transaction details" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:98 +msgid "Caution: There are risks associated with signing dapp transaction requests." +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:104 +msgid "Read More." +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:184 +msgid "Nano Contract Not Found" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:185 +msgid "" +"The Nano Contract requested is not registered. First register the Nano " +"Contract to interact with it." +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:188 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:235 +msgid "Decline Transaction" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:203 +msgid "Loading transaction information." +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:231 +msgid "Accept Transaction" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:245 +msgid "Sending transaction" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:246 +msgid "Please wait." +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:272 +msgid "Transaction successfully sent." +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:274 +msgid "Ok, close" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:280 +msgid "Error while sending transaction." +msgstr "" + #: src/components/NetworkSettings/NetworkStatusBar.js:14 msgid "Custom network" msgstr "" -#: src/components/NanoContract/EditAddressModal.component.js:41 +#: src/components/NanoContract/EditAddressModal.js:41 msgid "New Nano Contract Address" msgstr "" -#: src/components/NanoContract/EditAddressModal.component.js:49 +#: src/components/NanoContract/EditAddressModal.js:49 msgid "" "This address signs any transaction you create with Nano Contracts method. " "Switching to a new one means" msgstr "" -#: src/components/NanoContract/EditAddressModal.component.js:51 +#: src/components/NanoContract/EditAddressModal.js:51 msgid "all future transactions will use this address." msgstr "" -#: src/components/NanoContract/EditAddressModal.component.js:57 +#: src/components/NanoContract/EditAddressModal.js:57 msgid "Selected Information" msgstr "" -#: src/components/NanoContract/EditAddressModal.component.js:67 +#: src/components/NanoContract/EditAddressModal.js:67 msgid "Index" msgstr "" -#: src/components/NanoContract/EditAddressModal.component.js:74 +#: src/components/NanoContract/EditAddressModal.js:74 msgid "Confirm new address" msgstr "" -#: src/components/NanoContract/EditAddressModal.component.js:78 +#: src/components/NanoContract/EditAddressModal.js:78 msgid "Go back" msgstr "" -#: src/components/NanoContract/NanoContractDetails.component.js:159 +#: src/components/NanoContract/NanoContractDetails.js:159 msgid "Loading Nano Contract transactions." msgstr "" -#: src/components/NanoContract/NanoContractDetails.component.js:173 +#: src/components/NanoContract/NanoContractDetails.js:173 msgid "Nano Contract Transactions Error" msgstr "" -#: src/components/NanoContract/NanoContractDetailsHeader.component.js:146 -#: src/components/NanoContract/NanoContractsListItem.js:59 -msgid "Blueprint Name" -msgstr "" - -#: src/components/NanoContract/NanoContractDetailsHeader.component.js:150 +#: src/components/NanoContract/NanoContractDetailsHeader.js:150 msgid "Registered Address" msgstr "" -#: src/components/NanoContract/NanoContractDetailsHeader.component.js:153 +#: src/components/NanoContract/NanoContractDetailsHeader.js:153 msgid "See status details" msgstr "" -#: src/components/NanoContract/NanoContractDetailsHeader.component.js:154 +#: src/components/NanoContract/NanoContractDetailsHeader.js:154 msgid "Unregister contract" msgstr "" -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:96 +#: src/components/NanoContract/NanoContractTransactionHeader.js:96 #. XXX: add when shrank component can be used. msgid "Date and Time" msgstr "" -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:103 -#: src/components/NanoContract/NanoContractTransactionsListItem.component.js:62 +#: src/components/NanoContract/NanoContractTransactionHeader.js:103 +#: src/components/NanoContract/NanoContractTransactionsListItem.js:62 #. XXX: add when shrank component can be used. msgid "From this wallet" msgstr "" -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:106 -#. XXX: add when shrank component can be used. -msgid "Caller" -msgstr "" - -#: src/components/NanoContract/NanoContractTransactionHeader.component.js:109 +#: src/components/NanoContract/NanoContractTransactionHeader.js:109 #. XXX: add when shrank component can be used. msgid "See transaction details" msgstr "" @@ -1382,42 +1540,38 @@ msgstr "" msgid "Register new" msgstr "" -#: src/components/NanoContract/SelectAddressModal.component.js:90 +#: src/components/NanoContract/SelectAddressModal.js:90 msgid "Choose New Wallet Address" msgstr "" -#: src/components/NanoContract/SelectAddressModal.component.js:97 +#: src/components/NanoContract/SelectAddressModal.js:97 msgid "Load Addresses Error" msgstr "" -#: src/components/NanoContract/SelectAddressModal.component.js:106 +#: src/components/NanoContract/SelectAddressModal.js:106 msgid "Loading wallet addresses." msgstr "" -#: src/components/NanoContract/SelectAddressModal.component.js:114 +#: src/components/NanoContract/SelectAddressModal.js:114 msgid "Current Information" msgstr "" -#: src/components/NanoContract/SelectAddressModal.component.js:115 +#: src/components/NanoContract/SelectAddressModal.js:115 msgid "To change, select other address on the list below." msgstr "" -#: src/components/NanoContract/SelectAddressModal.component.js:178 +#: src/components/NanoContract/SelectAddressModal.js:178 msgid "index" msgstr "" -#: src/components/NanoContract/UnregisterNanoContractModal.component.js:39 +#: src/components/NanoContract/UnregisterNanoContractModal.js:39 msgid "Unregister Nano Contract" msgstr "" -#: src/components/NanoContract/UnregisterNanoContractModal.component.js:41 +#: src/components/NanoContract/UnregisterNanoContractModal.js:41 msgid "Are you sure you want to unregister this Nano Contract?" msgstr "" -#: src/components/NanoContract/UnregisterNanoContractModal.component.js:44 +#: src/components/NanoContract/UnregisterNanoContractModal.js:44 msgid "Yes, unregister contract" msgstr "" - -#: src/components/NanoContract/UnregisterNanoContractModal.component.js:50 -msgid "No, go back" -msgstr "" diff --git a/src/App.js b/src/App.js index 82e90bcea..e679bebd1 100644 --- a/src/App.js +++ b/src/App.js @@ -90,6 +90,8 @@ import ShowPushNotificationTxDetails from './components/ShowPushNotificationTxDe import { NanoContractDetailsScreen } from './screens/NanoContract/NanoContractDetailsScreen'; import { NanoContractTransactionScreen } from './screens/NanoContract/NanoContractTransactionScreen'; import { NanoContractRegisterScreen } from './screens/NanoContract/NanoContractRegisterScreen'; +import { NewNanoContractTransactionScreen } from './screens/WalletConnect/NewNanoContractTransactionScreen'; +import { NewNanoContractTransactionModal } from './components/WalletConnect/NanoContract/NewNanoContractTransactionModal'; /** * This Stack Navigator is exhibited when there is no wallet initialized on the local storage. @@ -388,6 +390,7 @@ const AppStack = () => { + ( + diff --git a/src/actions.js b/src/actions.js index f742095a8..4e8b70e96 100644 --- a/src/actions.js +++ b/src/actions.js @@ -11,7 +11,7 @@ import { metadataApi, } from '@hathor/wallet-lib'; import { - METADATA_CONCURRENT_DOWNLOAD, + METADATA_CONCURRENT_DOWNLOAD, WALLETCONNECT_NEW_NANOCONTRACT_TX_STATUS, } from './constants'; import { mapToTxHistory } from './utils'; @@ -113,6 +113,9 @@ export const types = { SET_WALLET_CONNECT: 'SET_WALLET_CONNECT', SET_WALLET_CONNECT_MODAL: 'SET_WALLET_CONNECT_MODAL', SET_WALLET_CONNECT_SESSIONS: 'SET_WALLET_CONNECT_SESSIONS', + WALLET_CONNECT_ACCEPT: 'WALLET_CONNECT_ACCEPT', + WALLET_CONNECT_REJECT: 'WALLET_CONNECT_REJECT', + SET_NEW_NANO_CONTRACT_TRANSACTION: 'SET_NEW_NANO_CONTRACT_TRANSACTION', SET_UNLEASH_CLIENT: 'SET_UNLEASH_CLIENT', WC_URI_INPUTTED: 'WC_URI_INPUTTED', WC_CANCEL_SESSION: 'WC_CANCEL_SESSION', @@ -163,6 +166,12 @@ export const types = { NANOCONTRACT_UNREGISTER_SUCCESS: 'NANOCONTRACT_UNREGISTER_SUCCESS', /* It initiates a process to change the address on registered Nano Contract. */ NANOCONTRACT_ADDRESS_CHANGE_REQUEST: 'NANOCONTRACT_ADDRESS_CHANGE_REQUEST', + /* It triggers a process to fetch blueprint info. */ + NANOCONTRACT_BLUEPRINTINFO_REQUEST: 'NANOCONTRACT_BLUEPRINTINFO_REQUEST', + /* It signals a failure on fetch blueprint info. */ + NANOCONTRACT_BLUEPRINTINFO_FAILURE: 'NANOCONTRACT_BLUEPRINTINFO_FAILURE', + /* It signals a success on fetch blueprint info. */ + NANOCONTRACT_BLUEPRINTINFO_SUCCESS: 'NANOCONTRACT_BLUEPRINTINFO_SUCCESS', /* It triggers a process to fetch all wallet addresses. */ SELECTADDRESS_ADDRESSES_REQUEST: 'SELECTADDRESS_ADDRESSES_REQUEST', /* It signals the fetch has loaded all the addresses with success. */ @@ -175,6 +184,10 @@ export const types = { FIRSTADDRESS_SUCCESS: 'FIRSTADDRESS_SUCCESS', /* It signals a fetch failure due to an error. */ FIRSTADDRESS_FAILURE: 'FIRSTADDRESS_FAILURE', + /* It updates the redux state of new nano contract transaction status on wallet connect register. */ + WALLETCONNECT_NEW_NANOCONTRACT_STATUS: 'WALLETCONNECT_NEW_NANOCONTRACT_STATUS', + UNREGISTEREDTOKENS_REQUEST: 'UNREGISTEREDTOKENS_REQUEST', + UNREGISTEREDTOKENS_UPDATE: 'UNREGISTEREDTOKENS_UPDATE', }; export const featureToggleInitialized = () => ({ @@ -235,6 +248,30 @@ export const walletConnectCancelSession = (sessionKey) => ({ payload: sessionKey, }); +/** + * @param {Object} data Data that the user has accepted. + */ +export const walletConnectAccept = (data) => ({ + type: types.WALLET_CONNECT_ACCEPT, + payload: data, +}); + +export const walletConnectReject = () => ({ + type: types.WALLET_CONNECT_REJECT, +}); + +/** + * @param {Object} ncRequest + * @param {boolean} ncRequest.show + * @param {Object} ncRequest.data + * @param {Object} ncRequest.data.nc + * @param {Object} ncRequest.data.dapp + */ +export const setNewNanoContractTransaction = (ncRequest) => ({ + type: types.SET_NEW_NANO_CONTRACT_TRANSACTION, + payload: ncRequest +}); + /** * isShowingPinScreen {bool} * */ @@ -1205,3 +1242,96 @@ export const firstAddressFailure = (failurePayload) => ({ type: types.FIRSTADDRESS_FAILURE, payload: failurePayload, }); + +/** + * Signals update on new nano contract status to ready. + */ +export const setNewNanoContractStatusReady = () => ({ + type: types.WALLETCONNECT_NEW_NANOCONTRACT_STATUS, + payload: WALLETCONNECT_NEW_NANOCONTRACT_TX_STATUS.READY, +}); + +/** + * Signals update on new nano contract status to loading. + */ +export const setNewNanoContractStatusLoading = () => ({ + type: types.WALLETCONNECT_NEW_NANOCONTRACT_STATUS, + payload: WALLETCONNECT_NEW_NANOCONTRACT_TX_STATUS.LOADING, +}); + +/** + * Signals update on new nano contract status to failed. + */ +export const setNewNanoContractStatusFailure = () => ({ + type: types.WALLETCONNECT_NEW_NANOCONTRACT_STATUS, + payload: WALLETCONNECT_NEW_NANOCONTRACT_TX_STATUS.FAILED, +}); + +/** + * Signals update on new nano contract status to successful. + */ +export const setNewNanoContractStatusSuccess = () => ({ + type: types.WALLETCONNECT_NEW_NANOCONTRACT_STATUS, + payload: WALLETCONNECT_NEW_NANOCONTRACT_TX_STATUS.SUCCESSFUL, +}); + +/** + * Blueprint Info request in the context of a Nano Contract. + * @param {string} id Blueprint ID. + */ +export const nanoContractBlueprintInfoRequest = (id) => ({ + type: types.NANOCONTRACT_BLUEPRINTINFO_REQUEST, + payload: { id }, +}); + +/** + * Signals the bluprint info request has failed. + * @param {string} id Blueprint ID. + * @param {string} error Request failure reason. + */ +export const nanoContractBlueprintInfoFailure = (id, error) => ({ + type: types.NANOCONTRACT_BLUEPRINTINFO_FAILURE, + payload: { id, error }, +}); + +/** + * Signals the blueprint info was fetched with success. + * @param {string} id Blueprint ID. + * @param {{ + * id: string; + * name: string; + * public_methods: { + * [methodName: string]: { + * args: { + * type: string; + * name: string; + * }[]; + * }; + * }; + * }} blueprintInfo Raw data response from fullnode. + */ +export const nanoContractBlueprintInfoSuccess = (id, blueprintInfo) => ({ + type: types.NANOCONTRACT_BLUEPRINTINFO_SUCCESS, + payload: { id, data: { ...blueprintInfo } }, +}); + +/** + * Signals a request to load a collection of token data by a collection of token UID. + * @param {Object} payload + * @param {string[]} payload.uids A list of token UID. + */ +export const unregisteredTokensRequest = (payload) => ({ + type: types.UNREGISTEREDTOKENS_REQUEST, + payload, +}); + +/** + * Signals an update to unregistered tokens state. + * @param {Object} payload + * @param {Object} payload.tokens A map of token data by its UID. + * @param {string} payload.error The error message as feedback to user + */ +export const unregisteredTokensUpdate = (payload) => ({ + type: types.UNREGISTEREDTOKENS_UPDATE, + payload, +}); diff --git a/src/components/FeedbackContent.js b/src/components/FeedbackContent.js index b50e5db1b..87c47f200 100644 --- a/src/components/FeedbackContent.js +++ b/src/components/FeedbackContent.js @@ -25,6 +25,7 @@ import { COLORS } from '../styles/themes'; * @param {Object?} props.action A react component or react element containing a call to action, * if provided, it renders underneath the content * @param {boolean} props.offcard Renders a feedback without card style + * @param {boolean} props.offmargin Renders a feedback without margins * * @example * } * /> */ -export const FeedbackContent = ({ title, message, icon, action, offcard }) => ( - +export const FeedbackContent = ({ + title, + message, + icon, + action, + offcard, + offmargin, + offbackground +}) => ( + {icon @@ -55,8 +70,8 @@ const styles = StyleSheet.create({ alignSelf: 'stretch', marginTop: 16, marginBottom: 45, - backgroundColor: COLORS.backgroundColor, marginHorizontal: 16, + backgroundColor: COLORS.backgroundColor, }, card: { borderRadius: 16, @@ -65,6 +80,15 @@ const styles = StyleSheet.create({ shadowColor: COLORS.textColor, shadowOpacity: 0.08, }, + offMargin: { + marginTop: 0, + marginBottom: 0, + marginRight: 0, + marginLeft: 0, + }, + offBackground: { + backgroundColor: COLORS.lowContrastDetail, + }, wrapper: { overflow: 'scroll', }, diff --git a/src/components/FeedbackModal.js b/src/components/FeedbackModal.js index 4f7b371d4..4509bb187 100644 --- a/src/components/FeedbackModal.js +++ b/src/components/FeedbackModal.js @@ -10,6 +10,19 @@ import { Text, View } from 'react-native'; import PropTypes from 'prop-types'; import HathorModal from './HathorModal'; +/** + * It renders a modal with some feedback to user. + * + * @param {Object} props + * + * @example + * )} + * text={t`Error while sending transaction.`} + * onDismiss={handleFeedbackModalDismiss} + * action={()} + * /> + */ const FeedbackModal = (props) => ( ( + + {children} + +); diff --git a/src/components/HathorFlatList.js b/src/components/HathorFlatList.js index 043e2dcec..2d7cf5aac 100644 --- a/src/components/HathorFlatList.js +++ b/src/components/HathorFlatList.js @@ -5,17 +5,23 @@ * LICENSE file in the root directory of this source tree. */ +/* eslint no-unused-vars: ["warn", { "varsIgnorePattern": "Props" }] -- + * Properties like `FlatListProps` are not counted as used, + * however they are important for coding completion during development. + */ + import React from 'react'; import { - FlatList, StyleSheet, View, + FlatList, StyleSheet, View, FlatListProps } from 'react-native'; import { COLORS } from '../styles/themes'; /** - * param {FlatListProps} props + * @param {FlatListProps} props + * @param {Object} props.wrapperStyle A custom style object used to control the wrapper component. */ export const HathorFlatList = (props) => ( - + ( > diff --git a/src/components/TextValue.js b/src/components/TextValue.js index f377e069c..efcf00c71 100644 --- a/src/components/TextValue.js +++ b/src/components/TextValue.js @@ -10,23 +10,28 @@ import { StyleSheet, Text, } from 'react-native'; +import { commonStyles } from './WalletConnect/theme'; /** * @param {Object} props * @param {boolean} props.title It sets font weight to bold and a larger font size + * @param {boolean} props.label It sets font weight to bold and a bottom margin * @param {boolean} props.bold It sets font weight to bold * @param {boolean} props.oneline It sets numberOfLines to 1 * @param {boolean} props.shrink It sets flexShrink to 1 * @param {boolean} props.pb4 It sets padding bottom to 4 + * @param {string} props.color It sets text color */ -export const TextValue = ({ title, bold, oneline, shrink, pb4, children }) => ( +export const TextValue = ({ title, label, bold, oneline, shrink, pb4, color, children }) => ( {children} ); @@ -41,6 +46,10 @@ const styles = StyleSheet.create({ fontSize: 18, fontWeight: 'bold', }, + label: [ + commonStyles.bold, + commonStyles.mb4, + ], pb4: { paddingBottom: 4, }, diff --git a/src/components/WalletConnect/NanoContract/DappContainer.js b/src/components/WalletConnect/NanoContract/DappContainer.js new file mode 100644 index 000000000..7e46f6828 --- /dev/null +++ b/src/components/WalletConnect/NanoContract/DappContainer.js @@ -0,0 +1,84 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import { + StyleSheet, + View, + Text, + Image +} from 'react-native'; +import { t } from 'ttag'; +import { commonStyles } from '../theme'; + +/** + * Renders DApp information. + * + * @param {Object} props + * @param {Object} props.dapp + */ +export const DappContainer = ({ dapp }) => ( + + + + + + + {dapp.proposer} + {'• '}{dapp.chain} + + + + {t`Review your transaction from this dApp`} + + + {t`Stay vigilant and protect your data from potential phishing attempts.`} + + +); + +const styles = StyleSheet.create({ + container: { + gap: 16, + paddingVertical: 16, + paddingHorizontal: 16, + }, + header: { + flexDirection: 'row', + gap: 16, + }, + avatar: { + flexShrink: 1, + alignSelf: 'flex-start', + maxWidth: 48, + maxHeight: 48, + }, + avatarIcon: { + width: 48, + height: 48, + backgroundColor: 'hsla(0, 0%, 85%, 1)', + borderRadius: 24, + }, + proposer: [ + commonStyles.text, + commonStyles.bold, + commonStyles.mb4, + ], + network: [ + commonStyles.text, + { color: 'hsla(263, 100%, 64%, 1)', } + ], + emphasis: [ + commonStyles.text, + commonStyles.bold + ] +}); diff --git a/src/components/WalletConnect/NanoContract/DeclineModal.js b/src/components/WalletConnect/NanoContract/DeclineModal.js new file mode 100644 index 000000000..f93959237 --- /dev/null +++ b/src/components/WalletConnect/NanoContract/DeclineModal.js @@ -0,0 +1,54 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import { + StyleSheet, + Text, +} from 'react-native'; +import { t } from 'ttag'; +import { ModalBase } from '../../ModalBase'; + +/** + * It renders a confirmation modal to decline the tranaction creation. + * + * @param {Object} props + * @param {boolean} props.show Flag that determines the if the modal should appear or not. + * @param {() => void} props.onDecline Callback fn for decline action. + * @param {() => void} props.onDismiss Callback fn for dismiss action. + */ +export const DeclineModal = ({ show, onDecline, onDismiss }) => ( + + {t`Decline transaction`} + + + {t`Are you sure you want to decline this transaction?`} + + + + + +); + +const styles = StyleSheet.create({ + declineModalBody: { + paddingBottom: 24, + }, + text: { + fontSize: 16, + lineHeight: 20, + textAlign: 'center', + }, +}); diff --git a/src/components/WalletConnect/NanoContract/NanoContractActions.js b/src/components/WalletConnect/NanoContract/NanoContractActions.js new file mode 100644 index 000000000..9b38d0d70 --- /dev/null +++ b/src/components/WalletConnect/NanoContract/NanoContractActions.js @@ -0,0 +1,203 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React, { useCallback } from 'react'; +import { + StyleSheet, + View, + Text, +} from 'react-native'; +import { t } from 'ttag'; +import { useSelector } from 'react-redux'; +import { HathorFlatList } from '../../HathorFlatList'; +import { commonStyles } from '../theme'; +import { getShortHash, isTokenNFT, renderValue } from '../../../utils'; +import { ReceivedIcon } from '../../Icons/Received.icon'; +import { SentIcon } from '../../Icons/Sent.icon'; +import { AlertUI, COLORS } from '../../../styles/themes'; +import { DEFAULT_TOKEN } from '../../../constants'; +import { WarnTextValue } from '../../WarnTextValue'; +import { CircleError } from '../../Icons/CircleError.icon'; + +/** + * It returns the title template for each action type, + * which is either 'deposit' or 'withdrawal'. + * + * @param {string} tokenSymbol The token symbol fetched from metadata, + * or a shortened token hash. + * + * @returns {string} A title template by action type. + */ +const actionTitleMap = (tokenSymbol) => ({ + deposit: t`${tokenSymbol} Deposit`, + withdrawal: t`${tokenSymbol} Withdrawal`, +}); + +/** + * Get action title depending on the action type. + * @param {Object} tokens A map of token metadata by token uid + * @param {Object} action An action object + * + * @returns {string} A formatted title to be used in the action card + * + * @example + * getActionTitle({ '123': { ..., symbol: 'STR' }}, { ..., token: '123', type: 'deposit' }) + * >>> 'STR Deposit' + * + * @example + * getActionTitle({}, { ..., token: '1234...5678', type: 'deposit' }) + * >>> '1234...5678 Deposit' + */ +const getActionTitle = (tokens, action) => { + const tokenMetadata = tokens[action.token]; + if (tokenMetadata) { + return actionTitleMap(tokenMetadata.symbol)[action.type]; + } + + if (action.token === DEFAULT_TOKEN.uid) { + return actionTitleMap(DEFAULT_TOKEN.symbol)[action.type] + } + + return actionTitleMap(getShortHash(action.token))[action.type]; +}; + +/** + * It renders a list of actions with a proper title for each one. + * It renders nothing if there aren't actions to render. + * + * @param {Object} props + * @param {Object[]} props.ncActions A list of Nano Contract actions. + * @param {Object} props.tokens A map of token metadata by token uid. + * @param {string} props.error A feedback error for tokens not loaded. + */ +export const NanoContractActions = ({ ncActions, tokens, error }) => { + if (!ncActions || ncActions.length < 1) { + return null; + } + + const tokenMetadata = useSelector((state) => state.tokenMetadata); + // A callback to check if the action token is an NFT. + const isNft = useCallback( + (token) => isTokenNFT(token, tokenMetadata), + [tokenMetadata] + ); + // A callback to retrieve the action title by its token symbol of hash. + const getTitle = useCallback( + (action) => getActionTitle(tokens, action), + [tokens] + ); + + const styles = StyleSheet.create({ + wrapper: { marginTop: 0, marginBottom: 0, marginHorizontal: 0 }, + }); + + return ( + + + {t`Action List`} + + ( + + )} + // If has error, shows the feedback error message in the list header. + ListHeaderComponent={error && ( + + + + {error} + + + )} + /> + + ); +}; + +/** + * @param {Object} props + * @param {{ + * type: 'deposit'|'withdrawal'; + * token: string; + * amount: number; + * address: string; + * }} props.action A transaction's action object + * @param {boolean} props.isNft A flag to inform if the token is an NFT or not + * @param {string} props.title The card title for the action + */ +const ActionItem = ({ action, title, isNft }) => { + const styles = StyleSheet.create({ + action: [commonStyles.text, commonStyles.bold], + valueLabel: [commonStyles.text, commonStyles.field, commonStyles.bold, commonStyles.mb4], + value: [commonStyles.text, commonStyles.field], + }); + + return ( + + + + {title} + {action.address + && ( + + {t`To Address:`} + {action.address} + + )} + + + + ) +} + +/** + * It renders an icon by action type, either 'deposit' or 'withdrawal'. + * + * @param {Object} props + * @param {'deposit'|'withdrawal'} props.type Action type. + */ +const Icon = ({ type }) => { + const iconMap = { + deposit: SentIcon({ type: 'default' }), + withdrawal: ReceivedIcon({ type: 'default' }), + }; + + return (iconMap[type]); +}; + +/** + * It renders an amount with the right format. + * + * @param {Object} props + * @param {number} props.amount + * @param {boolean} props.isNft + */ +const Amount = ({ amount, isNft }) => { + const amountToRender = renderValue(amount, isNft); + + const styles = StyleSheet.create({ + wrapper: { + marginLeft: 'auto', + }, + amount: { + fontSize: 16, + lineHeight: 20, + color: COLORS.black, + }, + }); + + return ( + + + {amountToRender} + + + ) +}; diff --git a/src/components/WalletConnect/NanoContract/NanoContractExecInfo.js b/src/components/WalletConnect/NanoContract/NanoContractExecInfo.js new file mode 100644 index 000000000..c06577d27 --- /dev/null +++ b/src/components/WalletConnect/NanoContract/NanoContractExecInfo.js @@ -0,0 +1,170 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React, { useEffect, useMemo } from 'react'; +import { + StyleSheet, + View, + TouchableOpacity, +} from 'react-native'; +import { useDispatch, useSelector } from 'react-redux'; +import { t } from 'ttag'; +import { firstAddressRequest } from '../../../actions'; +import { NANOCONTRACT_BLUEPRINTINFO_STATUS as STATUS } from '../../../constants'; +import { COLORS } from '../../../styles/themes'; +import { FrozenTextValue } from '../../FrozenTextValue'; +import { CircleError } from '../../Icons/CircleError.icon'; +import { NanoContractIcon } from '../../Icons/NanoContract.icon'; +import { PenIcon } from '../../Icons/Pen.icon'; +import Spinner from '../../Spinner'; +import { TextValue } from '../../TextValue'; +import { WarnTextValue } from '../../WarnTextValue'; +import { commonStyles } from '../theme'; + +/** + * It renders a card with basic information to execute the Nano Contract creation. + * + * @param {Object} props + * @param {Object} props.nc Nano Contract info. + * @param {() => void} props.onSelectAddress Callback fn for tap on caller address component. + */ +export const NanoContractExecInfo = ({ nc, onSelectAddress }) => { + const dispatch = useDispatch(); + const registeredNc = useSelector((state) => state.nanoContract.registered[nc.ncId]); + const blueprintInfo = useSelector((state) => state.nanoContract.blueprint[nc.blueprintId]); + const firstAddress = useSelector((state) => state.firstAddress); + + const isInitialize = nc.method === 'initialize'; + const notInitialize = !isInitialize; + + const blueprintName = useMemo(() => { + if (notInitialize && registeredNc) { + return registeredNc.blueprintName; + } + + if (blueprintInfo?.status === STATUS.SUCCESSFUL) { + return blueprintInfo.data.name; + } + return null; + }, [blueprintInfo]); + + useEffect(() => { + if (isInitialize) { + // Load firstAddress if not loaded + if (!firstAddress.address) { + dispatch(firstAddressRequest()); + } + } + }, []); + + const isBlueprintInfoLoading = !registeredNc + && blueprintInfo?.status === STATUS.LOADING; + const hasBlueprintInfoFailed = !registeredNc + && blueprintInfo?.status === STATUS.FAILED; + + const hasCaller = nc.caller != null; + const hasFirstAddressFailed = !hasCaller && isInitialize && firstAddress.error; + const isFirstAddressLoading = !hasCaller + && isInitialize + && !hasFirstAddressFailed; + + return ( + + + + + + {notInitialize && ( + + {t`Nano Contract ID`} + {nc.ncId} + + )} + + {t`Blueprint ID`} + {nc.blueprintId} + + + + {t`Blueprint Name`} + {isBlueprintInfoLoading && ( + + {' '} + + )} + {hasBlueprintInfoFailed && ( + + {' '} + + )} + + {blueprintName && ( + {blueprintName} + )} + {isBlueprintInfoLoading && ( + {t`Loading...`} + )} + {hasBlueprintInfoFailed && ( + {blueprintInfo.error} + )} + + + {t`Blueprint Method`} + {nc.method} + + + + + + + {t`Caller`} + {isFirstAddressLoading && ( + + {' '} + + )} + {(hasFirstAddressFailed) && ( + + {' '} + + )} + + {hasCaller && ( + {nc.caller || firstAddress.address} + )} + {isFirstAddressLoading && ( + {t`Loading...`} + )} + {hasFirstAddressFailed && ( + {t`Couldn't determine address, select one`} + )} + + + + + + + + + ) +}; + +const styles = StyleSheet.create({ + contentEditable: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + }, + contentEditableValue: { + flexShrink: 1, + paddingRight: 8, + }, + contentEditableIcon: { + width: 24, + paddingRight: 2, + }, +}); diff --git a/src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js b/src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js new file mode 100644 index 000000000..1c7b945d8 --- /dev/null +++ b/src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js @@ -0,0 +1,154 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React, { useMemo } from 'react'; +import { + StyleSheet, + View, + Text, +} from 'react-native'; +import { useDispatch, useSelector } from 'react-redux'; +import { t } from 'ttag'; +import { get } from 'lodash'; +import { COLORS } from '../../../styles/themes'; +import { commonStyles } from '../theme'; +import { onExceptionCaptured } from '../../../actions'; +import { NANOCONTRACT_BLUEPRINTINFO_STATUS as STATUS } from '../../../constants'; +import { FeedbackContent } from '../../FeedbackContent'; +import Spinner from '../../Spinner'; + +/** + * Get method info from registered blueprint data. + * + * @param {{ + * data: Object; + * }} blueprint The blueprint info object + * @param {string} method The method name to get info from blueprint public methods + * + * @returns {Object} + */ +function getMethodInfoFromBlueprint(blueprint, method) { + return get(blueprint.data, `public_methods.${method}`, null); +} + +/** + * Get the fallback entries for the method arguments. + * + * @param {string[]} args A list of argument value + * + * @returns {[argName: string, value: string][]} + * + * @example + * getFallbackArgEntries([...argValues]) + * >>> [['Position 0', 'abc'], ['Position 1', '00'], ['Position 2', 123]] + */ +function getFallbackArgEntries(args) { + return args.map((arg, idx) => [t`Position ${idx}`, arg]); +} + +/** + * It renders a list of method arguments for when the Nano Contract executes. + * + * @param {Object} props + * @param {string} props.blueprintId ID of blueprint. + * @param {string} props.method Method's name. + * @param {string[]} props.ncArgs A list of method's argument. + */ +export const NanoContractMethodArgs = ({ blueprintId, method, ncArgs }) => { + if (!ncArgs.length) { + return null; + } + const dispatch = useDispatch(); + + const blueprintInfo = useSelector((state) => state.nanoContract.blueprint[blueprintId]); + // It results a in a list of entries like: + // >>> [['oracle_script', 'abc'], ['token_uid', '00'], ['date_last_bet', 123]] + // or a fallback like: + // >>> [['Position 0', 'abc'], ['Position 1', '00'], ['Position 2', 123]] + const argEntries = useMemo(() => { + if (blueprintInfo == null || blueprintInfo.status === STATUS.LOADING) { + return []; + } + + const methodInfo = getMethodInfoFromBlueprint(blueprintInfo, method); + if (methodInfo) { + return ncArgs.map((arg, idx) => [methodInfo.args[idx].name, arg]); + } + + // Send this condition to sentry because it should never happen. + // Check any change in the lib or in the fullnode that could cause an impact here. + const errMsg = 'Error while getting the argument names of public_methods on blueprint'; + dispatch(onExceptionCaptured(new Error(errMsg), false)); + + // Still render a fallback + return getFallbackArgEntries(ncArgs); + }, [method, ncArgs, blueprintInfo]); + + // Empty while downloading the bleuprint details + const isEmpty = argEntries.length === 0; + const notEmpty = !isEmpty; + + return ( + + + {t`Arguments`} + + {isEmpty /* This is a redundancy to the general loading */ + && ( + } + offmargin + /> + )} + {notEmpty + && ( + + + {argEntries.map(([argName, argValue]) => ( + + + {argName} + + + {argValue} + + + ))} + + + )} + + ) +}; + +const styles = StyleSheet.create({ + argPosition: { + flexShrink: 10, + width: '30%', + paddingRight: 8, + }, + argPositionText: [ + commonStyles.text, + commonStyles.bold + ], + argValue: { + maxWidth: '70%', + backgroundColor: 'hsla(0, 0%, 96%, 1)', + paddingVertical: 2, + paddingHorizontal: 8, + borderRadius: 4, + }, + argValueText: { + fontSize: 12, + lineHeight: 16, + color: COLORS.black, + }, +}); diff --git a/src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js b/src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js new file mode 100644 index 000000000..204026dc7 --- /dev/null +++ b/src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js @@ -0,0 +1,163 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import { + StyleSheet, + View, + Text, + Linking, +} from 'react-native'; +import { t } from 'ttag'; +import { useNavigation } from '@react-navigation/native'; +import { useDispatch, useSelector } from 'react-redux'; +import { COLORS } from '../../../styles/themes'; +import { CircleInfoIcon } from '../../Icons/CircleInfo.icon'; +import { ModalBase } from '../../ModalBase'; +import SimpleButton from '../../SimpleButton'; +import { setNewNanoContractTransaction, walletConnectReject } from '../../../actions'; +import { WALLET_STATUS } from '../../../sagas/wallet'; +import { NANO_CONTRACT_INFO_URL } from '../../../constants'; + +export const NewNanoContractTransactionModal = () => { + const navigation = useNavigation(); + const dispatch = useDispatch(); + const readMoreUrl = NANO_CONTRACT_INFO_URL; + + const { + showModal, + ncTxRequest, + } = useSelector((state) => { + const { + walletStartState, + walletConnect: { + newNanoContractTransaction: { + showModal: showNcTxModal, + data, + } + }, + } = state; + const isWalletReady = walletStartState === WALLET_STATUS.READY; + + return { + showModal: showNcTxModal && isWalletReady, + ncTxRequest: data, + }; + }); + + const onDismiss = () => { + dispatch(walletConnectReject()); + dispatch(setNewNanoContractTransaction({ show: false, data: null })); + }; + + const navigatesToNewNanoContractScreen = () => { + dispatch(setNewNanoContractTransaction({ show: false, data: null })); + navigation.navigate('NewNanoContractTransactionScreen', { ncTxRequest }); + }; + + const onReadMore = () => { + Linking.openURL(readMoreUrl) + }; + + return ( + + {t`New Nano Contract Transaction`} + + + + {t`You have received a new Nano Contract Transaction. Please`} + + {' '}{t`carefully review the details`}{' '} + + {t`before deciding to accept or decline.`} + + + + + + ); +}; + +const WarnDisclaimer = ({ onReadMore }) => ( + + + + + + + {t`Caution: There are risks associated with signing dapp transaction requests.`} + + + + + + +); + +const styles = StyleSheet.create({ + body: { + paddingBottom: 24, + }, + text: { + fontSize: 14, + lineHeight: 20, + }, + bold: { + fontWeight: 'bold', + }, + warnContainer: { + flexShrink: 1, + flexDirection: 'row', + alignItems: 'center', + marginBottom: 24, + borderRadius: 8, + paddingTop: 12, + /* It should have been 12 but it is adjusted to compensate the negative + * margin on learnMoreWrapper and the difference between the font size + * and the line height, which amounts to 8 points of compensation. + */ + paddingBottom: 20, + paddingHorizontal: 16, + backgroundColor: COLORS.cardWarning100, + }, + warnContent: { + paddingLeft: 8, + }, + warnMessage: { + fontSize: 12, + lineHeight: 16, + }, + learnMoreWrapper: { + display: 'inline-block', + /* We are using negative margin here to correct the text position + * and create an optic effect of alignment. */ + marginBottom: -4, + paddingLeft: 2, + marginRight: 'auto', + }, + learnMoreContainer: { + justifyContent: 'flex-start', + borderBottomWidth: 1, + }, + learnMoreText: { + fontSize: 12, + lineHeight: 16, + fontWeight: 'bold', + color: 'hsla(0, 0%, 25%, 1)', + }, +}); diff --git a/src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js b/src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js new file mode 100644 index 000000000..238cceded --- /dev/null +++ b/src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js @@ -0,0 +1,332 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React, { + useEffect, + useMemo, + useState +} from 'react'; +import { + StyleSheet, + View, + ScrollView, + TouchableWithoutFeedback, + Image +} from 'react-native'; +import { useDispatch, useSelector } from 'react-redux'; +import { useNavigation } from '@react-navigation/native'; +import { t } from 'ttag'; +import { + nanoContractBlueprintInfoRequest, + setNewNanoContractStatusReady, + walletConnectAccept, + walletConnectReject, + unregisteredTokensRequest +} from '../../../actions'; +import { COLORS } from '../../../styles/themes'; +import NewHathorButton from '../../NewHathorButton'; +import { SelectAddressModal } from '../../NanoContract/SelectAddressModal'; +import { FeedbackContent } from '../../FeedbackContent'; +import { DEFAULT_TOKEN, NANOCONTRACT_BLUEPRINTINFO_STATUS, WALLETCONNECT_NEW_NANOCONTRACT_TX_STATUS } from '../../../constants'; +import Spinner from '../../Spinner'; +import FeedbackModal from '../../FeedbackModal'; +import errorIcon from '../../../assets/images/icErrorBig.png'; +import checkIcon from '../../../assets/images/icCheckBig.png'; +import { DappContainer } from './DappContainer'; +import { NanoContractExecInfo } from './NanoContractExecInfo'; +import { NanoContractActions } from './NanoContractActions'; +import { NanoContractMethodArgs } from './NanoContractMethodArgs'; +import { DeclineModal } from './DeclineModal'; + +/** + * @param {Object} props + * @param {Object} props.ncTxRequest + * @param {Object} props.ncTxRequest.nc + * @param {string} props.ncTxRequest.nc.ncId + * @param {string} props.ncTxRequest.nc.blueprintId + * @param {Object[]} props.ncTxRequest.nc.actions + * @param {string} props.ncTxRequest.nc.method + * @param {string[]} props.ncTxRequest.nc.args + * @param {Object} props.ncTxRequest.dapp + * @param {string} props.ncTxRequest.dapp.icon + * @param {string} props.ncTxRequest.dapp.proposer + * @param {string} props.ncTxRequest.dapp.url + * @param {string} props.ncTxRequest.dapp.description + */ +export const NewNanoContractTransactionRequest = ({ ncTxRequest }) => { + const { nc, dapp } = ncTxRequest; + const dispatch = useDispatch(); + const navigation = useNavigation(); + const newTxStatus = useSelector((state) => state.walletConnect.newNanoContractTransaction.status); + const firstAddress = useSelector((state) => state.firstAddress); + // Nullable if the nano contract method is 'initialize' + const registeredNc = useSelector((state) => state.nanoContract.registered[nc.ncId]); + const knownTokens = useSelector((state) => ({ ...state.tokens, ...state.unregisteredTokens })); + const blueprintInfo = useSelector((state) => state.nanoContract.blueprint[nc.blueprintId]); + + const [showSelectAddressModal, setShowSelectAddressModal] = useState(false); + const [showDeclineModal, setShowDeclineModal] = useState(false); + /** + * If nano-contract's method is 'initialize' then the expression + * should be resolved to firstAddress value by default. + * + * In case of failure to load the first address the user will see + * a feedback message instruction it to select an address for the + * transaction. + */ + const [ncAddress, setNcAddress] = useState(registeredNc?.address || firstAddress.address); + const ncToAccept = useMemo(() => ({ + ...nc, + caller: ncAddress, + }), [ncAddress]) + + const toggleSelectAddressModal = () => setShowSelectAddressModal(!showSelectAddressModal); + const handleAddressSelection = (newAddress) => { + setNcAddress(newAddress); + toggleSelectAddressModal(); + }; + + // Accepts the Nano Contract data preseted. + const onAcceptTransaction = () => { + // Update the caller with the address selected by the user. + const acceptedNc = { ...nc, caller: ncAddress }; + // Signal the user has accepted the current request and pass the accepted data. + dispatch(walletConnectAccept(acceptedNc)); + }; + + const onDeclineTransaction = () => { + setShowDeclineModal(true); + }; + const onDeclineConfirmation = () => { + setShowDeclineModal(false); + dispatch(walletConnectReject()); + navigation.goBack(); + }; + const onDismissDeclineModal = () => { + setShowDeclineModal(false); + }; + + // Control which content to show, if the nano contract is not registered + // a feedback content should tell user the nano contract must be registered first + // and only let user decline the transaction to get out the page, otherwise interaction + // content is showed. + const notInitialize = ncToAccept.method !== 'initialize'; + const notRegistered = notInitialize && registeredNc == null; + // It results in true for registered nc and initialize request + const showRequest = !notRegistered; + + // This effect should run at most twice: + // 1. when in the construct phase + // 2. after firstAddress is set on store after a request to load it + // The mentioned load request at (2) can happen for 'initialize' transaction, + // it is requested from a child component, NanoContractExecInfo. + useEffect(() => { + if (ncToAccept.method === 'initialize' && firstAddress.address) { + setNcAddress(firstAddress.address); + } + }, [firstAddress]); + + // This effect runs only once in the construct phase + useEffect(() => { + // Do nothing if nano contract is not registered and don't call initialize method. + if (notRegistered) return; + + // Request blueprint info if not present to feed the components: + // - NanoContractExecInfo, and + // - NanoContractMethodArgs + if (!blueprintInfo) { + dispatch(nanoContractBlueprintInfoRequest(nc.blueprintId)); + } + + // Request token data for each unknown token present in actions + const unknownTokensUid = []; + const actionTokensUid = nc.actions?.map((each) => each.token) || []; + actionTokensUid.forEach((uid) => { + if (uid !== DEFAULT_TOKEN.uid && !(uid in knownTokens)) { + unknownTokensUid.push(uid); + } + }); + dispatch(unregisteredTokensRequest({ uids: unknownTokensUid })); + }, []); + + const onFeedbackModalDismiss = () => { + dispatch(setNewNanoContractStatusReady()); + navigation.goBack(); + }; + + const onNavigateToDashboard = () => { + dispatch(setNewNanoContractStatusReady()); + navigation.navigate('Dashboard'); + }; + + const onTryAgain = () => { + dispatch(setNewNanoContractStatusReady()); + }; + + // Loading while downloading: + // 1. each token details + // 2. the blueprint details + const isTxInfoLoading = () => ( + knownTokens.isLoading + || blueprintInfo == null + || blueprintInfo.status === NANOCONTRACT_BLUEPRINTINFO_STATUS.LOADING + ); + const isTxInfoLoaded = () => ( + !isTxInfoLoading() && newTxStatus !== WALLETCONNECT_NEW_NANOCONTRACT_TX_STATUS.LOADING + ); + const isTxProcessing = () => ( + !isTxInfoLoading() && newTxStatus === WALLETCONNECT_NEW_NANOCONTRACT_TX_STATUS.LOADING + ); + const isTxSuccessful = () => newTxStatus === WALLETCONNECT_NEW_NANOCONTRACT_TX_STATUS.SUCCESSFUL; + const isTxFailed = () => newTxStatus === WALLETCONNECT_NEW_NANOCONTRACT_TX_STATUS.FAILED; + + return ( + <> + {notRegistered && ( + + )} + /> + )} + {showRequest && ( + + + + {isTxInfoLoading() && ( + } + offmargin + offcard + offbackground + /> + )} + {isTxInfoLoaded() && ( + + + + + + + {/* User actions */} + + + + + + )} + {isTxProcessing() && ( + } + offmargin + offcard + offbackground + /> + )} + + + + )} + + + + {isTxSuccessful() && ( + )} + text={t`Transaction successfully sent.`} + onDismiss={onFeedbackModalDismiss} + action={()} + /> + )} + {isTxFailed() && ( + )} + text={t`Error while sending transaction.`} + onDismiss={onFeedbackModalDismiss} + action={()} + /> + )} + + ); +}; + +const styles = StyleSheet.create({ + wide: { + width: '100%' + }, + wrapper: { + flex: 1, + paddingHorizontal: 16, + backgroundColor: COLORS.lowContrastDetail, // Defines an outer area on the main list content + }, + content: { + flex: 1, + rowGap: 24, + width: '100%', + paddingVertical: 16, + }, + balanceReceived: { + color: 'hsla(180, 85%, 34%, 1)', + fontWeight: 'bold', + }, + actionContainer: { + flexDirection: 'column', + gap: 8, + paddingBottom: 48, + }, + declineModalBody: { + paddingBottom: 24, + }, + text: { + fontSize: 16, + lineHeight: 20, + textAlign: 'center', + }, + feedbackModalIcon: { + height: 105, + width: 105 + }, +}); diff --git a/src/components/WalletConnect/theme.js b/src/components/WalletConnect/theme.js new file mode 100644 index 000000000..bab0f09ee --- /dev/null +++ b/src/components/WalletConnect/theme.js @@ -0,0 +1,74 @@ +import { StyleSheet } from 'react-native'; +import { COLORS } from '../../styles/themes'; + +export const commonStyles = StyleSheet.create({ + // Card + card: { + paddingVertical: 16, + paddingHorizontal: 16, + backgroundColor: COLORS.backgroundColor, + borderTopLeftRadius: 16, + borderTopRightRadius: 16, + borderBottomLeftRadius: 16, + borderBottomRightRadius: 16, + shadowOffset: { height: 2, width: 0 }, + shadowRadius: 4, + shadowColor: COLORS.textColor, + shadowOpacity: 0.08, + }, + cardSplit: { + flexDirection: 'row', + gap: 16, + }, + cardSplitIcon: { + flexShrink: 1, + alignSelf: 'flex-start', + }, + cardSplitContent: { + maxWidth: '80%', + flexDirection: 'column', + gap: 8, + }, + cardSeparator: { + width: '100%', + height: 1, + backgroundColor: COLORS.borderColor + }, + cardStack: { + flexDirection: 'column', + }, + cardStackItem: { + flexDirection: 'row', + paddingVertical: 16, + }, + listItem: { + paddingVertical: 24, + paddingHorizontal: 16, + }, + feedbackItem: { + paddingHorizontal: 16, + }, + + // General + text: { + fontSize: 14, + lineHeight: 20, + color: COLORS.black, + }, + bold: { + fontWeight: 'bold', + }, + field: { + color: 'hsla(0, 0%, 38%, 1)', + }, + mb4: { + marginBottom: 4, + }, + sectionTitle: { + fontSize: 16, + fontWeight: 'bold', + lineHeight: 20, + color: COLORS.black, + marginBottom: 24, + }, +}); diff --git a/src/components/WarnTextValue.js b/src/components/WarnTextValue.js new file mode 100644 index 000000000..4ddc85f94 --- /dev/null +++ b/src/components/WarnTextValue.js @@ -0,0 +1,31 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import { AlertUI } from '../styles/themes'; +import { TextValue } from './TextValue'; + +/** + * @param {Object} props + * @param {boolean} props.title It sets font weight to bold and a larger font size + * @param {boolean} props.bold It sets font weight to bold + * @param {boolean} props.oneline It sets numberOfLines to 1 + * @param {boolean} props.shrink It sets flexShrink to 1 + * @param {boolean} props.pb4 It sets padding bottom to 4 + */ +export const WarnTextValue = ({ title, bold, oneline, shrink, pb4, children }) => ( + + {children} + +); diff --git a/src/constants.js b/src/constants.js index 165e599ee..0a23a61da 100644 --- a/src/constants.js +++ b/src/constants.js @@ -240,6 +240,19 @@ export const NANOCONTRACT_REGISTER_STATUS = { SUCCESSFUL: 'successful', }; +export const WALLETCONNECT_NEW_NANOCONTRACT_TX_STATUS = { + READY: 'ready', + FAILED: 'failed', + LOADING: 'loading', + SUCCESSFUL: 'successful', +}; + +export const NANOCONTRACT_BLUEPRINTINFO_STATUS = { + FAILED: 'failed', + LOADING: 'loading', + SUCCESSFUL: 'successful', +}; + /** * Timeout in miliseconds to call wallet-service. */ @@ -262,6 +275,7 @@ export const NETWORK_PRIVATENET = 'privatenet'; export const MAX_RETRIES = 8; export const INITIAL_RETRY_LATENCY = 300; // ms export const LATENCY_MULTIPLIER = 30; // multiplier per iteration + /** * Timeout for await wallet load in the context of tx details loading. * It awaits 5 minutes. @@ -272,3 +286,7 @@ export const TX_DETAILS_TIMEOUT = 5 * 60 * 1000; // 5 minutes * Nano Contract's transaction history list size. */ export const NANO_CONTRACT_TX_HISTORY_SIZE = 20; +/** + * Nano Contract documentation URL. + */ +export const NANO_CONTRACT_INFO_URL = 'https://docs.hathor.network/explanations/features/nano-contracts/'; diff --git a/src/reducers/reducer.js b/src/reducers/reducer.js index 07619da52..431ff74d6 100644 --- a/src/reducers/reducer.js +++ b/src/reducers/reducer.js @@ -14,7 +14,9 @@ import { FEATURE_TOGGLE_DEFAULTS, PRE_SETTINGS_MAINNET, NETWORKSETTINGS_STATUS, - NANOCONTRACT_REGISTER_STATUS + NANOCONTRACT_REGISTER_STATUS, + WALLETCONNECT_NEW_NANOCONTRACT_TX_STATUS, + NANOCONTRACT_BLUEPRINTINFO_STATUS } from '../constants'; import { types } from '../actions'; import { TOKEN_DOWNLOAD_STATUS } from '../sagas/tokens'; @@ -97,6 +99,26 @@ const initialState = { * @see {@link INITIAL_TOKENS} */ tokens: INITIAL_TOKENS, + /** + * Remarks + * We use the map of tokens to collect token details for tokens + * used in actions but not registered by the user. + * + * @example + * { + * '000003a3b261e142d3dfd84970d3a50a93b5bc3a66a3b6ba973956148a3eb824': { + * name: 'YanCoin', + * symbol: 'YAN', + * uid: '000003a3b261e142d3dfd84970d3a50a93b5bc3a66a3b6ba973956148a3eb824', + * }, + * isLoading: false, + * error: null, + * } + */ + unregisteredTokens: { + isLoading: false, + error: null, + }, /** * selectedToken {{ * uid: string; @@ -231,6 +253,38 @@ const initialState = { modal: { show: false, }, + /** + * newNanoContractTransaction {{ + * showModal: boolean; + * data: { + * nc: { + * network: string; + * ncId: string; + * blueprintId: string; + * method: string; + * caller: string; + * actions: { + * type: string; + * token: string; + * amount: number; + * address?: string; + * }[]; + * args: string[]; + * }; + * dapp: { + * icon: string; + * proposer: string; + * url: string; + * description: string; + * }; + * }; + * }} + */ + newNanoContractTransaction: { + status: WALLETCONNECT_NEW_NANOCONTRACT_TX_STATUS.READY, + showModal: false, + data: null, + }, connectionFailed: false, sessions: {}, }, @@ -334,6 +388,82 @@ const initialState = { * } */ historyMeta: {}, + /** + * blueprint { + * [blueprintId: string]: { + * status: string; + * data?: { + * id: string; + * name: string; + * public_methods: { + * [methodName: string]: { + * args: { + * type: string; + * name: string; + * }[]; + * }; + * }; + * }; + * error?: string; + * } + * } + * @example + * { + * '3cb032600bdf7db784800e4ea911b10676fa2f67591f82bb62628c234e771595': { + * status: 'success', + * data: { + * id: '3cb032600bdf7db784800e4ea911b10676fa2f67591f82bb62628c234e771595', + * name: 'Bet', + * public_methods: { + * bet: { + * args: [ + * { + * name: "address", + * type: "bytes" + * }, + * { + * name: "score", + * type: "str" + * } + * ], + * return_type: "null" + * }, + * initialize: { + * args: [ + * { + * name: "oracle_script", + * type: "bytes" + * }, + * { + * name: "token_uid", + * type: "bytes" + * }, + * { + * name: "date_last_bet", + * type: "int" + * } + * ], + * return_type: "null" + * }, + * set_result: { + * args: [ + * { + * name: "result", + * type: "SignedData[str]" + * } + * ], + * return_type: "null" + * }, + * withdraw: { + * args: [], + * return_type: "null" + * } + * }, + * }, + * }, + * } + */ + blueprint: {}, }, /** * selectAddressModal {{ @@ -543,6 +673,20 @@ export const reducer = (state = initialState, action) => { return onFirstAddressFailure(state, action); case types.FIRSTADDRESS_SUCCESS: return onFirstAddressSuccess(state, action); + case types.SET_NEW_NANO_CONTRACT_TRANSACTION: + return onSetNewNanoContractTransaction(state, action); + case types.WALLETCONNECT_NEW_NANOCONTRACT_STATUS: + return onSetNewNanoContractTransactionStatus(state, action); + case types.NANOCONTRACT_BLUEPRINTINFO_REQUEST: + return onNanoContractBlueprintInfoRequest(state, action); + case types.NANOCONTRACT_BLUEPRINTINFO_FAILURE: + return onNanoContractBlueprintInfoFailure(state, action); + case types.NANOCONTRACT_BLUEPRINTINFO_SUCCESS: + return onNanoContractBlueprintInfoSuccess(state, action); + case types.UNREGISTEREDTOKENS_REQUEST: + return onUnregisteredTokensRequest(state); + case types.UNREGISTEREDTOKENS_UPDATE: + return onUnregisteredTokensUpdate(state, action); default: return state; } @@ -1695,3 +1839,170 @@ export const onFirstAddressSuccess = (state, { payload }) => ({ error: null, }, }); + +/** + * @param {Object} state + * @param {{ + * payload: { + * showModal: boolean; + * data: { + * nc: { + * network: string; + * ncId: string; + * blueprintId: string; + * method: string; + * caller: string; + * actions: { + * type: string; + * token: string; + * amount: number; + * address?: string; + * }[]; + * args: string[]; + * }; + * dapp: { + * icon: string; + * proposer: string; + * url: string; + * description: string; + * }; + * }; + * }; + * }} action + */ +export const onSetNewNanoContractTransaction = (state, { payload }) => ({ + ...state, + walletConnect: { + ...state.walletConnect, + newNanoContractTransaction: { + ...payload, + status: WALLETCONNECT_NEW_NANOCONTRACT_TX_STATUS.READY, + }, + }, +}); + +export const onSetNewNanoContractTransactionStatus = (state, { payload }) => ({ + ...state, + walletConnect: { + ...state.walletConnect, + newNanoContractTransaction: { + ...state.walletConnect.newNanoContractTransaction, + status: payload, + }, + }, +}); + +/** + * @param {Object} state + * @param {{ + * payload: { + * id: string; + * }; + * }} action + */ +export const onNanoContractBlueprintInfoRequest = (state, { payload }) => ({ + ...state, + nanoContract: { + ...state.nanoContract, + blueprint: { + ...state.nanoContract.blueprint, + [payload.id]: { + status: NANOCONTRACT_BLUEPRINTINFO_STATUS.LOADING, + data: null, + error: null, + }, + }, + }, +}); + +/** + * @param {Object} state + * @param {{ + * payload: { + * id: string; + * error: string; + * }; + * }} action + */ +export const onNanoContractBlueprintInfoFailure = (state, { payload }) => ({ + ...state, + nanoContract: { + ...state.nanoContract, + blueprint: { + ...state.nanoContract.blueprint, + [payload.id]: { + status: NANOCONTRACT_BLUEPRINTINFO_STATUS.FAILED, + data: null, + error: payload.error, + }, + }, + }, +}); + +/** + * @param {Object} state + * @param {{ + * payload: { + * id: string; + * data: { + * id: string; + * name: string; + * public_methods: { + * [methodName: string]: { + * args: { + * type: string; + * name: string; + * }[]; + * }; + * }; + * }; + * }; + * }} action + */ +export const onNanoContractBlueprintInfoSuccess = (state, { payload }) => ({ + ...state, + nanoContract: { + ...state.nanoContract, + blueprint: { + ...state.nanoContract.blueprint, + [payload.id]: { + status: NANOCONTRACT_BLUEPRINTINFO_STATUS.SUCCESSFUL, + data: payload.data, + error: null, + }, + }, + }, +}); + +/** + * Remarks + * This reducer aims to clean error feedback message before processing the request. + */ +export const onUnregisteredTokensRequest = (state) => ({ + ...state, + unregisteredTokens: { + ...state.unregisteredTokens, + isLoading: true, + error: null, + }, +}); + +/** + * Update walletConnect.tokens with some tokens data needed to feed UI components + * without the need to register them, also update an error feedback message if present. + * + * @param {Object} state + * @param {Object} action + * @param {Object} action.payload + * @param {Object} action.payload.tokens A map of token data by its UID. + * @param {string} action.payload.error The error message as feedback to user + */ +export const onUnregisteredTokensUpdate = (state, { payload }) => ({ + ...state, + unregisteredTokens: { + ...state.unregisteredTokens, + ...payload.tokens, + isLoading: false, + error: payload.error || null, + }, +}) diff --git a/src/sagas/nanoContract.js b/src/sagas/nanoContract.js index e8ec59a5d..230f43582 100644 --- a/src/sagas/nanoContract.js +++ b/src/sagas/nanoContract.js @@ -22,6 +22,8 @@ import { import { t } from 'ttag'; import { NanoRequest404Error } from '@hathor/wallet-lib/lib/errors'; import { + nanoContractBlueprintInfoFailure, + nanoContractBlueprintInfoSuccess, nanoContractHistoryClean, nanoContractHistoryFailure, nanoContractHistoryLoading, @@ -45,6 +47,8 @@ export const failureMessage = { addressNotMine: t`The informed address does not belong to the wallet.`, nanoContractStateNotFound: t`Nano Contract not found.`, nanoContractStateFailure: t`Error while trying to get Nano Contract state.`, + blueprintInfoNotFound: t`Blueprint not found.`, + blueprintInfoFailure: t`Couldn't get Blueprint info.`, notRegistered: t`Nano Contract not registered.`, nanoContractHistoryFailure: t`Error while trying to download Nano Contract transactions history.`, }; @@ -348,6 +352,33 @@ export function* requestNanoContractAddressChange({ payload }) { log.debug(`Success persisting Nano Contract address update. ncId = ${ncId}`); } +/** + * Process request to fetch blueprint info. + * @param {{ + * payload: { + * id: string; + * } + * }} + */ +export function* requestBlueprintInfo({ payload }) { + const { id } = payload; + let blueprintInfo = null; + try { + blueprintInfo = yield call([ncApi, ncApi.getBlueprintInformation], id); + } catch (error) { + if (error instanceof NanoRequest404Error) { + yield put(nanoContractBlueprintInfoFailure(id, failureMessage.blueprintInfoNotFound)); + } else { + log.error('Error while fetching blueprint info.', error); + yield put(nanoContractBlueprintInfoFailure(id, failureMessage.blueprintInfoFailure)); + } + return; + } + + log.debug(`Success fetching blueprint info. id = ${id}`); + yield put(nanoContractBlueprintInfoSuccess(id, blueprintInfo)); +} + export function* saga() { yield all([ debounce(500, [[types.START_WALLET_SUCCESS, types.NANOCONTRACT_INIT]], init), @@ -355,5 +386,6 @@ export function* saga() { takeEvery(types.NANOCONTRACT_HISTORY_REQUEST, requestHistoryNanoContract), takeEvery(types.NANOCONTRACT_UNREGISTER_REQUEST, unregisterNanoContract), takeEvery(types.NANOCONTRACT_ADDRESS_CHANGE_REQUEST, requestNanoContractAddressChange), + takeEvery(types.NANOCONTRACT_BLUEPRINTINFO_REQUEST, requestBlueprintInfo), ]); } diff --git a/src/sagas/tokens.js b/src/sagas/tokens.js index 7e6774fa9..55e6e2fdd 100644 --- a/src/sagas/tokens.js +++ b/src/sagas/tokens.js @@ -15,6 +15,7 @@ import { all, put, } from 'redux-saga/effects'; +import { t } from 'ttag'; import { metadataApi } from '@hathor/wallet-lib'; import { channel } from 'redux-saga'; import { get } from 'lodash'; @@ -28,11 +29,18 @@ import { tokenFetchHistoryRequested, tokenFetchHistorySuccess, tokenFetchHistoryFailed, + onExceptionCaptured, + unregisteredTokensUpdate, } from '../actions'; import { logger } from '../logger'; const log = logger('tokens-saga'); +const failureMessage = { + walletNotReadyError: t`Wallet is not ready yet.`, + someTokensNotLoaded: t`Error loading the details of some tokens.`, +}; + /** * @readonly * @enum {string} @@ -287,6 +295,48 @@ export function* fetchTokenData(tokenId, force = false) { } } +/** + * Request tokens data to feed walletConnect's tokens. + * @param {Object} action + * @param {Object} action.payload + * @param {string[]} action.payload.uids + */ +export function* requestUnregisteredTokens(action) { + const { uids } = action.payload; + + const wallet = yield select((state) => state.wallet); + if (!wallet.isReady()) { + log.error('Fail updating loading tokens data because wallet is not ready yet.'); + // This will show user an error modal with the option to send the error to sentry. + yield put(onExceptionCaptured(new Error(failureMessage.walletNotReadyError), true)); + return; + } + + const tokens = {}; + let someError = false; + for (const uid of uids) { + try { + const { tokenInfo: { symbol, name } } = yield call([wallet, wallet.getTokenDetails], uid); + const token = { uid, symbol, name }; + tokens[uid] = token; + } catch (e) { + log.error(`Fail getting token data for token ${uid}.`, e); + someError = true; + // continue download of tokens + } + } + + if (someError) { + log.log('There was a failure while getting tokens data to feed unregisteredTokens.'); + yield put( + unregisteredTokensUpdate({ tokens, error: failureMessage.someTokensNotLoaded }) + ); + return; + } + log.log('Success getting tokens data to feed unregisteredTokens.'); + yield put(unregisteredTokensUpdate({ tokens })); +} + export function* saga() { yield all([ fork(fetchTokenBalanceQueue), @@ -294,5 +344,6 @@ export function* saga() { takeEvery(types.TOKEN_FETCH_HISTORY_REQUESTED, fetchTokenHistory), takeEvery(types.NEW_TOKEN, routeTokenChange), takeEvery(types.SET_TOKENS, routeTokenChange), + takeEvery(types.UNREGISTEREDTOKENS_REQUEST, requestUnregisteredTokens), ]); } diff --git a/src/sagas/wallet.js b/src/sagas/wallet.js index 19490b0d0..c5168c9b1 100644 --- a/src/sagas/wallet.js +++ b/src/sagas/wallet.js @@ -126,7 +126,7 @@ export function* isWalletServiceEnabled() { 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`); + log.log(`Still ignoring wallet-service, will expire in ${EXPIRE_WS_IGNORE_FLAG - delta}ms`); return false; } } else { @@ -303,7 +303,7 @@ export function* startWallet(action) { try { yield call(loadTokens); } catch (e) { - console.error('Tokens load failed: ', e); + log.error('Tokens load failed: ', e); yield put(onExceptionCaptured(e, false)); yield put(startWalletFailed()); return; @@ -387,7 +387,7 @@ export function* loadTokens() { * So we fetch the tokens metadata and store on redux */ export function* fetchTokensMetadata(tokens) { - // No tokens to load + // No tokens to load, set metadata as loaded if (!tokens.length) { return; } @@ -413,8 +413,7 @@ export function* fetchTokensMetadata(tokens) { const tokenMetadatas = {}; for (const response of responses) { if (response.type === types.TOKEN_FETCH_METADATA_FAILED) { - // eslint-disable-next-line - console.log('Error downloading metadata of token', response.tokenId); + log.log(`Error downloading metadata of token ${response.tokenId}.`); } else if (response.type === types.TOKEN_FETCH_METADATA_SUCCESS) { // When the request returns null, it means that we have no metadata for this token if (response.data) { @@ -427,7 +426,7 @@ export function* fetchTokensMetadata(tokens) { } export function* onWalletServiceDisabled() { - console.debug('We are currently in the wallet-service and the feature-flag is disabled, reloading.'); + log.debug('We are currently in the wallet-service and the feature-flag is disabled, reloading.'); yield put(reloadWalletRequested()); } @@ -722,7 +721,7 @@ export function* onWalletReloadData() { // Finally, set the wallet to READY by dispatching startWalletSuccess yield put(startWalletSuccess()); } catch (e) { - console.log('Wallet reload data failed: ', e); + log.error('Wallet reload data failed: ', e); yield put(onExceptionCaptured(e, false)); yield put(startWalletFailed()); } diff --git a/src/sagas/walletConnect.js b/src/sagas/walletConnect.js index ea897affa..338798f62 100644 --- a/src/sagas/walletConnect.js +++ b/src/sagas/walletConnect.js @@ -243,6 +243,7 @@ export function* onSessionRequest(action) { proposer: get(requestSession.peer, 'metadata.name', ''), url: get(requestSession.peer, 'metadata.url', ''), description: get(requestSession.peer, 'metadata.description', ''), + chain: get(requestSession.namespaces, 'hathor.chains[0]', ''), }; switch (params.request.method) { diff --git a/src/screens/WalletConnect/NewNanoContractTransactionScreen.js b/src/screens/WalletConnect/NewNanoContractTransactionScreen.js new file mode 100644 index 000000000..666996548 --- /dev/null +++ b/src/screens/WalletConnect/NewNanoContractTransactionScreen.js @@ -0,0 +1,44 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import { + StyleSheet, + View, +} from 'react-native'; +import { t } from 'ttag'; +import HathorHeader from '../../components/HathorHeader'; +import OfflineBar from '../../components/OfflineBar'; +import { NewNanoContractTransactionRequest } from '../../components/WalletConnect/NanoContract/NewNanoContractTransactionRequest'; +import { COLORS } from '../../styles/themes'; + +export function NewNanoContractTransactionScreen({ route }) { + const { ncTxRequest } = route.params; + return ( + + + + + + ); +} + +const Wrapper = ({ children }) => ( + + {children} + +); + +const styles = StyleSheet.create({ + wrapper: { + flex: 1, + alignItems: 'center', + backgroundColor: COLORS.lowContrastDetail, // Defines an outer area on the main list content + }, +}); diff --git a/src/styles/themes.js b/src/styles/themes.js index 6a2f44159..970658496 100644 --- a/src/styles/themes.js +++ b/src/styles/themes.js @@ -93,6 +93,8 @@ export const COLORS = { feedbackError600: 'hsla(7, 100%, 30%, 1)', freeze100: 'hsla(0, 0%, 90%, 1)', freeze300: 'hsla(0, 0%, 45%, 1)', + cardWarning100: 'hsla(46, 100%, 95%, 1)', + cardWarning200: 'hsla(46, 100%, 58%, 1)', /** * @type {string} Black with 38% of light and full opaque */ From 6d7b7135f94e78abf0f950f32bf9c0ec8a73d21f Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Mon, 19 Aug 2024 16:48:42 +0100 Subject: [PATCH 36/51] feat: add nano-contract transaction actions (#530) * feat: add tx actions --- .../NanoContractTransactionActionList.js | 61 ++++++ .../NanoContractTransactionActionListItem.js | 184 ++++++++++++++++++ .../NanoContractTransactionHeader.js | 83 ++++---- src/constants.js | 8 + src/reducers/reducer.js | 17 +- src/sagas/nanoContract.js | 14 +- .../NanoContractTransactionScreen.js | 2 + 7 files changed, 310 insertions(+), 59 deletions(-) create mode 100644 src/components/NanoContract/NanoContractTransactionActionList.js create mode 100644 src/components/NanoContract/NanoContractTransactionActionListItem.js diff --git a/src/components/NanoContract/NanoContractTransactionActionList.js b/src/components/NanoContract/NanoContractTransactionActionList.js new file mode 100644 index 000000000..2fee5092c --- /dev/null +++ b/src/components/NanoContract/NanoContractTransactionActionList.js @@ -0,0 +1,61 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { StyleSheet, View } from 'react-native'; +import { t } from 'ttag'; +import { COLORS } from '../../styles/themes'; +import { NanoContractTransactionActionListItem } from './NanoContractTransactionActionListItem'; +import { HathorFlatList } from '../HathorFlatList'; +import { FeedbackContent } from '../FeedbackContent'; + +/** + * It presents a list of actions of a transaction. + * + * @param {Object} props + * @param {Object} props.tx Transaction data + */ +export const NanoContractTransactionActionList = ({ tx }) => { + const isEmpty = () => tx.actions.length === 0; + const notEmpty = () => !isEmpty(); + + return ( + + {isEmpty() + && ()} + {notEmpty() + && ( + ( + + )} + /> + )} + + ); +}; + +const NoActions = () => ( + +); + +const Wrapper = ({ children }) => ( + + {children} + +); + +const styles = StyleSheet.create({ + wrapper: { + flex: 1, + alignSelf: 'stretch', + backgroundColor: COLORS.lowContrastDetail, // Defines an outer area on the main list content + }, +}); diff --git a/src/components/NanoContract/NanoContractTransactionActionListItem.js b/src/components/NanoContract/NanoContractTransactionActionListItem.js new file mode 100644 index 000000000..747ef1608 --- /dev/null +++ b/src/components/NanoContract/NanoContractTransactionActionListItem.js @@ -0,0 +1,184 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import { + TouchableHighlight, + StyleSheet, + View, + Text, +} from 'react-native'; +import { useSelector } from 'react-redux'; +import { t } from 'ttag'; +import { NANO_CONTRACT_ACTION } from '../../constants'; +import { COLORS } from '../../styles/themes'; +import { getShortHash, isTokenNFT, renderValue } from '../../utils'; +import { ReceivedIcon } from '../Icons/Received.icon'; +import { SentIcon } from '../Icons/Sent.icon'; + +/** + * Retrieves token symbol, otherwise returns a shortened token hash. + * + * @param {string} tokenUid Token hash + * @returns {(state) => boolean} Callback that takes state as input + * + * Remarks: + * This function should be used combined with `useSelector`. + */ +function getTokenSymbol(tokenUid) { + return (state) => { + const tokens = state.tokens || {}; + if (tokenUid in tokens) { + return tokens[tokenUid].symbol; + } + return getShortHash(tokenUid, 7); + }; +} + +/** + * Checks if the referred token is an NFT. + * + * @param {string} tokenUid Token hash + * @returns {(state) => boolean} Callback that takes state as input + * + * Remarks: + * This function should be used combined with `useSelector`. + */ +function checkIsTokenNft(tokenUid) { + return (state) => isTokenNFT(tokenUid, state.tokenMetadata || {}); +} + +/** + * It renders the item of actions list of a Nano Contract transaction. + * + * @param {Object} props + * @param {{ + * type: string; + * uid: string; + * amount: number; + * }} props.item A transaction action + */ +export const NanoContractTransactionActionListItem = ({ item }) => { + const tokenSymbol = useSelector(getTokenSymbol(item.uid)); + const isNft = useSelector(checkIsTokenNft(item.uid)); + + return ( + + + + + + ); +}; + +const Wrapper = ({ children }) => ( + + {children} + +); + +/** + * It renders the balance icon, either sent or received. + * + * @param {Object} props + * @param {'deposit'|'withdrawal'} props.type An action type + */ +const Icon = ({ type }) => { + const iconMap = { + deposit: SentIcon({ type: 'default' }), + withdrawal: ReceivedIcon({ type: 'default' }), + }; + + return (iconMap[type]); +}; + +/** + * Renders item core content. + * + * @param {Object} props + * @property {string} props.tokenSymbol The symbol that represents a token + * @property {'deposit'|'withdrawal'} props.type An action type + */ +const ContentWrapper = ({ tokenSymbol, type }) => { + const contentMap = { + deposit: t`Deposit ${tokenSymbol}`, + withdrawal: t`Withdrawal ${tokenSymbol}`, + }; + + return ( + + {contentMap[type]} + + ); +}; + +/** + * It presents the token's amount using the right style. + * + * @param {Object} props + * @param {number} props.amount Action amount as integer + * @param {boolean} props.isNft True when it is an NFT, false otherwise + * @param {'deposit'|'withdrawal'} props.type An action type + */ +const TokenAmount = ({ amount, isNft, type }) => { + const isReceivingToken = type === NANO_CONTRACT_ACTION.withdrawal; + const amountToRender = renderValue(amount, isNft); + + return ( + + + {amountToRender} + + + ) +}; + +const styles = StyleSheet.create({ + wrapper: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + flexWrap: 'wrap', + width: '100%', + paddingVertical: 24, + paddingHorizontal: 16, + }, + contentWrapper: { + maxWidth: '80%', + flexDirection: 'column', + alignItems: 'flex-start', + justifyContent: 'space-between', + marginRight: 'auto', + paddingHorizontal: 16, + }, + text: { + fontSize: 14, + lineHeight: 20, + paddingBottom: 6, + color: 'hsla(0, 0%, 38%, 1)', + }, + property: { + paddingBottom: 4, + fontWeight: 'bold', + color: 'black', + }, + amountWrapper: { + marginLeft: 'auto', + }, + amount: { + fontSize: 16, + lineHeight: 20, + }, + amountReceived: { + color: 'hsla(180, 85%, 34%, 1)', + fontWeight: 'bold', + }, +}); diff --git a/src/components/NanoContract/NanoContractTransactionHeader.js b/src/components/NanoContract/NanoContractTransactionHeader.js index b2302e93a..c9453e0f9 100644 --- a/src/components/NanoContract/NanoContractTransactionHeader.js +++ b/src/components/NanoContract/NanoContractTransactionHeader.js @@ -23,6 +23,7 @@ import { ArrowDownIcon } from '../Icons/ArrowDown.icon'; import { TextValue } from '../TextValue'; import { TextLabel } from '../TextLabel'; import { TransactionStatusLabel } from '../TransactionStatusLabel'; +import { ArrowUpIcon } from '../Icons/ArrowUp.icon'; /** * It presents the header of Nano Contract Transaction screen. @@ -31,16 +32,12 @@ import { TransactionStatusLabel } from '../TransactionStatusLabel'; * @param {Obejct} props.tx Transaction data */ export const NanoContractTransactionHeader = ({ tx }) => { - // XXX: the set function for the state is beeing ignored because we can't - // use the shrank format just yet. We need the actions component first. - // For his, we also need hathor-core support for actions in each nano contract - // transaction. - const [isShrank] = useState(false); + const [isShrank, toggleShrank] = useState(false); // shows expanded header by default return ( - {}}> + toggleShrank(!isShrank)}> {getShortHash(tx.txId, 7)} @@ -77,38 +74,40 @@ const HeaderExpanded = ({ tx }) => { Linking.openURL(explorerLink); }; - /* XXX: add when shrank component can be used. */ return ( - - - - - - {ncId} - {t`Nano Contract ID`} - - - {tx.ncMethod} - {t`Blueprint Method`} - - - {getTimestampFormat(tx.timestamp)} - {t`Date and Time`} - - - {callerAddr} - {tx.isMine - && ( - - {t`From this wallet`} - - )} - {t`Caller`} - - - - - + <> + + + + + + {ncId} + {t`Nano Contract ID`} + + + {tx.ncMethod} + {t`Blueprint Method`} + + + {getTimestampFormat(tx.timestamp)} + {t`Date and Time`} + + + {callerAddr} + {tx.isMine + && ( + + {t`From this wallet`} + + )} + {t`Caller`} + + + + + + + ) }; @@ -161,12 +160,6 @@ const styles = StyleSheet.create({ headerWrapper: { alignItems: 'center', }, - headerTitle: { - fontSize: 18, - lineHeight: 20, - fontWeight: 'bold', - paddingVertical: 16, - }, wrapper: { paddingHorizontal: 16, paddingBottom: 16, @@ -189,10 +182,6 @@ const styles = StyleSheet.create({ buttonText: { fontWeight: 'bold', }, - buttonUnregister: { - marginStart: 24, - color: 'hsla(0, 100%, 41%, 1)', - }, buttonDetails: { display: 'inline-block', /* We are using negative margin here to correct the text position diff --git a/src/constants.js b/src/constants.js index 0a23a61da..27de995a9 100644 --- a/src/constants.js +++ b/src/constants.js @@ -290,3 +290,11 @@ export const NANO_CONTRACT_TX_HISTORY_SIZE = 20; * Nano Contract documentation URL. */ export const NANO_CONTRACT_INFO_URL = 'https://docs.hathor.network/explanations/features/nano-contracts/'; + +/** + * Nano Contract Action Enum + */ +export const NANO_CONTRACT_ACTION = { + withdrawal: 'withdrawal', + deposit: 'deposit', +}; diff --git a/src/reducers/reducer.js b/src/reducers/reducer.js index 431ff74d6..a5b399c6f 100644 --- a/src/reducers/reducer.js +++ b/src/reducers/reducer.js @@ -341,9 +341,14 @@ const initialState = { * ncId: string; * ncMethod: string; * blueprintId: string; + * firstBlock: string; * caller: string; * isMine: boolean; - * balance: {[uid: string]: Object}; + * actions: { + * type: 'withdrawal'|'deposit'; + * uid: string; + * amount: number; + * }[]; * }[]; * }} history of Nano Contracts registered per wallet address. * @example @@ -362,9 +367,13 @@ const initialState = { * blueprintId: '0025dadebe337a79006f181c05e4799ce98639aedfbd26335806790bdea4b1d4'; * caller: 'HTeZeYTCv7cZ8u7pBGHkWsPwhZAuoq5j3V', * isMine: true, - * balance: { - * '00': 300, - * }, + * actions: [ + * { + * type: 'withdrawal', + * uid: '00', + * amount: 100, + * } + * ] * }, * ], * } diff --git a/src/sagas/nanoContract.js b/src/sagas/nanoContract.js index 230f43582..08b034d4d 100644 --- a/src/sagas/nanoContract.js +++ b/src/sagas/nanoContract.js @@ -7,7 +7,6 @@ import { ncApi, - transactionUtils, addressUtils, } from '@hathor/wallet-lib'; import { @@ -198,12 +197,11 @@ export async function fetchHistory(ncId, count, after, wallet) { // throwing an exception. // eslint-disable-next-line no-await-in-loop const isMine = await wallet.isAddressMine(caller); - const getTxBalanceFn = transactionUtils.getTxBalance.bind(transactionUtils); - // XXX: Wallet Service Wallet doesn't support getTxBalanceFn. - // It means this method can't run under wallet-service without - // throwing an exception. - // eslint-disable-next-line no-await-in-loop - const balance = await getTxBalanceFn(rawTx, wallet.storage); + const actions = rawTx.nc_context.actions.map((each) => ({ + type: each.type, // 'deposit' or 'withdrawal' + uid: each.token_uid, + amount: each.amount, + })); const tx = { txId: rawTx.hash, timestamp: rawTx.timestamp, @@ -215,7 +213,7 @@ export async function fetchHistory(ncId, count, after, wallet) { firstBlock: rawTx.first_block, caller, isMine, - balance, + actions, }; history.push(tx); } diff --git a/src/screens/NanoContract/NanoContractTransactionScreen.js b/src/screens/NanoContract/NanoContractTransactionScreen.js index 25d9f0905..db6db6cfa 100644 --- a/src/screens/NanoContract/NanoContractTransactionScreen.js +++ b/src/screens/NanoContract/NanoContractTransactionScreen.js @@ -13,6 +13,7 @@ import { import { t } from 'ttag'; import HathorHeader from '../../components/HathorHeader'; import { NanoContractTransactionHeader } from '../../components/NanoContract/NanoContractTransactionHeader'; +import { NanoContractTransactionActionList } from '../../components/NanoContract/NanoContractTransactionActionList'; import OfflineBar from '../../components/OfflineBar'; import { COLORS } from '../../styles/themes'; @@ -26,6 +27,7 @@ export function NanoContractTransactionScreen({ navigation, route }) { + From 3eab776f3df9c09cdbcd2b258d42583fd8bd54c7 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Thu, 22 Aug 2024 17:00:30 +0100 Subject: [PATCH 37/51] refactor: replace network identification logic by the lib method (#534) * refactor: replace network identification logic by the lib method --- package-lock.json | 19 +++++++++++-------- package.json | 2 +- src/sagas/networkSettings.js | 16 ++-------------- 3 files changed, 14 insertions(+), 23 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7bd731fa0..b2fa08760 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@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.8.0", + "@hathor/wallet-lib": "1.9.0", "@notifee/react-native": "5.7.0", "@react-native-async-storage/async-storage": "1.19.0", "@react-native-firebase/app": "16.7.0", @@ -2550,10 +2550,12 @@ } }, "node_modules/@hathor/wallet-lib": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@hathor/wallet-lib/-/wallet-lib-1.8.0.tgz", - "integrity": "sha512-G1kLlxZ4Ev3S7hPiq/y9wUl4ns4pndOvuK37q+xqdxieZKCl2/O7lXiiHVOgRN9xOntL/TR66n2UZPpe0p29zQ==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@hathor/wallet-lib/-/wallet-lib-1.9.0.tgz", + "integrity": "sha512-8m6sr/PObnRoCsmalV2AqKO+tMPyzI/Lw/eJ0KgtaZSn7drBeFlDRhYyEilxpFqq9BCzvx18i8tXAudFoW2OUQ==", + "license": "MIT", "dependencies": { + "abstract-level": "1.0.4", "axios": "1.7.2", "bitcore-lib": "8.25.10", "bitcore-mnemonic": "8.25.10", @@ -2563,7 +2565,7 @@ "level": "8.0.1", "lodash": "4.17.21", "long": "5.2.3", - "ws": "8.17.0" + "ws": "8.17.1" }, "engines": { "node": ">=20.0.0", @@ -2599,9 +2601,10 @@ "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" }, "node_modules/@hathor/wallet-lib/node_modules/ws": { - "version": "8.17.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz", - "integrity": "sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", "engines": { "node": ">=10.0.0" }, diff --git a/package.json b/package.json index 860be7e8d..fc695b30a 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "@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.8.0", + "@hathor/wallet-lib": "1.9.0", "@notifee/react-native": "5.7.0", "@react-native-async-storage/async-storage": "1.19.0", "@react-native-firebase/app": "16.7.0", diff --git a/src/sagas/networkSettings.js b/src/sagas/networkSettings.js index 6b9b7bced..69dcd53dc 100644 --- a/src/sagas/networkSettings.js +++ b/src/sagas/networkSettings.js @@ -1,5 +1,5 @@ import { all, takeEvery, put, call, race, delay, select } from 'redux-saga/effects'; -import { config } from '@hathor/wallet-lib'; +import { config, helpersUtils } from '@hathor/wallet-lib'; import { isEmpty } from 'lodash'; import { t } from 'ttag'; import { @@ -22,7 +22,6 @@ import { STAGE_DEV_PRIVNET, STAGE_TESTNET, WALLET_SERVICE_REQUEST_TIMEOUT, - NETWORK_PRIVATENET, } from '../constants'; import { getFullnodeNetwork, @@ -154,7 +153,6 @@ export function* updateNetworkSettings(action) { // - walletServiceUrl has precedence // - nodeUrl as fallback let potentialNetwork; - let network; if (useWalletService && !isEmpty(walletServiceUrl)) { log.debug('Configuring wallet-service on custom network settings.'); config.setWalletServiceBaseUrl(walletServiceUrl); @@ -194,17 +192,7 @@ export function* updateNetworkSettings(action) { return; } - // Validates the potential network and set the network accordingly - if (potentialNetwork === NETWORK_MAINNET) { - network = NETWORK_MAINNET; - } else if (potentialNetwork.includes(NETWORK_TESTNET)) { - network = NETWORK_TESTNET; - } else if (potentialNetwork.includes(NETWORK_PRIVATENET)) { - network = NETWORK_PRIVATENET; - } else { - yield put(networkSettingsUpdateFailure()); - return; - } + const network = helpersUtils.getNetworkFromFullNodeNetwork(potentialNetwork); let stage; if (network === NETWORK_MAINNET) { From 9103a9ec0426372a1140d171bb5d38fc0e8c8c8c Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Thu, 22 Aug 2024 17:04:36 +0100 Subject: [PATCH 38/51] fix: nano-contract history pagination and list order (#512) * fix: nano-contract history pagination and list order * refactor: requestHistoryNanoContract to allow get newer and older transactions * chore: remove delay from saga * feat: default isAddressMine or isMine to false in case user is using wallet service * feat: add load more button to load older transactions * fix: callback reference by using an appropriate hook * fix: call isWalletServiceEnabled to return a generator to consume --- locale/da/texts.po | 182 +++++++++-------- locale/pt-br/texts.po | 189 ++++++++++-------- locale/ru-ru/texts.po | 182 +++++++++-------- locale/texts.pot | 182 +++++++++-------- src/actions.js | 8 +- .../NanoContract/NanoContractDetails.js | 76 +++++-- src/reducers/reducer.js | 15 +- src/sagas/nanoContract.js | 114 +++++++---- src/utils.js | 12 ++ 9 files changed, 573 insertions(+), 387 deletions(-) diff --git a/locale/da/texts.po b/locale/da/texts.po index 2f2f1ee00..ae8dcd634 100644 --- a/locale/da/texts.po +++ b/locale/da/texts.po @@ -57,7 +57,7 @@ msgid "[Last] dddd [•] HH:mm" msgstr "[Sidste] dddd [•] HH:mm" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:107 src/utils.js:416 +#: src/models.js:107 src/utils.js:426 msgid "DD MMM YYYY [•] HH:mm" msgstr "DD MMM YYYY [•] HH:mm" @@ -432,8 +432,8 @@ msgstr "Ord" msgid "Enter your seed words separated by space" msgstr "Indtast dine seed-ord adskilt med mellemrum" -#: src/components/NanoContract/NanoContractDetails.js:194 -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:282 +#: src/components/NanoContract/NanoContractDetails.js:238 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:289 #: src/screens/LoadHistoryScreen.js:51 src/screens/LoadWalletErrorScreen.js:20 #: src/screens/NanoContract/NanoContractRegisterScreen.js:161 msgid "Try again" @@ -458,7 +458,7 @@ msgid "There's been an error connecting to the server." msgstr "" #: src/screens/LoadWalletErrorScreen.js:21 src/screens/PinScreen.js:268 -#: src/screens/Settings.js:154 +#: src/screens/Settings.js:161 msgid "Reset wallet" msgstr "Nulstil wallet" @@ -541,7 +541,7 @@ msgstr "Lås Hathor-wallet op" msgid "Cancel" msgstr "Annuller" -#: src/screens/PushNotification.js:58 src/screens/Settings.js:128 +#: src/screens/PushNotification.js:58 src/screens/Settings.js:135 msgid "Push Notification" msgstr "" @@ -576,7 +576,7 @@ msgstr "MODTAG" #. translator: Used when the QR Code Scanner is opened, and user will manually #. enter the information. -#: src/screens/RegisterToken.js:27 src/screens/SendScanQRCode.js:89 +#: src/screens/RegisterToken.js:27 src/screens/SendScanQRCode.js:87 msgid "Manual info" msgstr "Manuel information" @@ -660,7 +660,7 @@ msgstr "Skift pinkode" msgid "Lock wallet" msgstr "Lås wallet" -#: src/screens/SendAddressInput.js:53 src/screens/SendScanQRCode.js:97 +#: src/screens/SendAddressInput.js:53 src/screens/SendScanQRCode.js:95 msgid "SEND" msgstr "SEND" @@ -706,7 +706,7 @@ msgstr "Din overførsel af **${ _this.amountAndToken }** er bekræftet" msgid "Address" msgstr "Adresse" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:288 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:291 #: src/screens/SendConfirmScreen.js:168 msgid "Send" msgstr "Send" @@ -719,53 +719,53 @@ msgstr "Ugyldig QR-kode" msgid "OK" msgstr "Okay" -#: src/screens/SendScanQRCode.js:73 +#: src/screens/SendScanQRCode.js:71 #, javascript-format msgid "You don't have the requested token [${ tokenLabel }]" msgstr "Du har ikke den anmodede token [${ tokenLabel }]" -#: src/screens/SendScanQRCode.js:105 +#: src/screens/SendScanQRCode.js:103 #: src/screens/WalletConnect/WalletConnectScan.js:49 msgid "Scan the QR code" msgstr "Scan QR-koden" -#: src/screens/Settings.js:97 +#: src/screens/Settings.js:104 msgid "You are connected to" msgstr "Du er tilsluttet til" -#: src/screens/Settings.js:105 +#: src/screens/Settings.js:112 msgid "General Settings" msgstr "" -#: src/screens/Settings.js:109 +#: src/screens/Settings.js:116 msgid "Connected to" msgstr "Forbundet til" -#: src/screens/Settings.js:122 +#: src/screens/Settings.js:129 msgid "Security" msgstr "Sikkerhed" -#: src/screens/Settings.js:135 +#: src/screens/Settings.js:142 msgid "Create a new token" msgstr "Opret en ny token" -#: src/screens/Settings.js:142 +#: src/screens/Settings.js:149 msgid "Register a token" msgstr "Registrer en token" -#: src/screens/Settings.js:158 +#: src/screens/Settings.js:165 msgid "About" msgstr "Om" -#: src/screens/Settings.js:165 +#: src/screens/Settings.js:172 msgid "Unique app identifier" msgstr "" -#: src/screens/Settings.js:179 +#: src/screens/Settings.js:186 msgid "Developer Settings" msgstr "" -#: src/screens/Settings.js:181 +#: src/screens/Settings.js:188 msgid "Network Settings" msgstr "" @@ -900,27 +900,27 @@ msgstr "" msgid "walletServiceWsUrl is required when walletServiceUrl is filled." msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:229 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:232 msgid "Node URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:239 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:242 msgid "Explorer URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:248 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:251 msgid "Explorer Service URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:257 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:260 msgid "Transaction Mining Service URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:268 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:271 msgid "Wallet Service URL (optional)" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:277 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:280 msgid "Wallet Service WS URL (optional)" msgstr "" @@ -959,9 +959,9 @@ msgstr "" msgid "Load First Addresses Error" msgstr "" -#: src/components/NanoContract/NanoContractDetails.js:158 +#: src/components/NanoContract/NanoContractDetails.js:202 #: src/components/NanoContract/SelectAddressModal.js:105 -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:202 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:209 #: src/screens/NanoContract/NanoContractRegisterScreen.js:177 msgid "Loading" msgstr "" @@ -971,7 +971,7 @@ msgid "Loading first wallet address." msgstr "" #: src/components/NanoContract/NanoContractDetailsHeader.js:142 -#: src/components/NanoContract/NanoContractTransactionHeader.js:88 +#: src/components/NanoContract/NanoContractTransactionHeader.js:85 #: src/components/NanoContract/NanoContractsListItem.js:57 #: src/components/TxDetailsModal.js:106 #: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:83 @@ -999,7 +999,7 @@ msgstr "" msgid "Nano Contract Registration" msgstr "" -#: src/screens/NanoContract/NanoContractTransactionScreen.js:37 +#: src/screens/NanoContract/NanoContractTransactionScreen.js:39 msgid "Nano Contract Transaction" msgstr "" @@ -1007,81 +1007,81 @@ msgstr "" msgid "Contract successfully registered." msgstr "" -#: src/sagas/nanoContract.js:45 +#: src/sagas/nanoContract.js:44 msgid "Nano Contract already registered." msgstr "" -#: src/sagas/nanoContract.js:46 +#: src/sagas/nanoContract.js:45 msgid "Wallet is not ready yet to register a Nano Contract." msgstr "" -#: src/sagas/nanoContract.js:47 +#: src/sagas/nanoContract.js:46 msgid "The informed address does not belong to the wallet." msgstr "" -#: src/sagas/nanoContract.js:48 +#: src/sagas/nanoContract.js:47 msgid "Nano Contract not found." msgstr "" -#: src/sagas/nanoContract.js:49 +#: src/sagas/nanoContract.js:48 msgid "Error while trying to get Nano Contract state." msgstr "" -#: src/sagas/nanoContract.js:50 +#: src/sagas/nanoContract.js:49 msgid "Blueprint not found." msgstr "" -#: src/sagas/nanoContract.js:51 +#: src/sagas/nanoContract.js:50 msgid "Couldn't get Blueprint info." msgstr "" -#: src/sagas/nanoContract.js:52 +#: src/sagas/nanoContract.js:51 msgid "Nano Contract not registered." msgstr "" -#: src/sagas/nanoContract.js:53 +#: src/sagas/nanoContract.js:52 msgid "Error while trying to download Nano Contract transactions history." msgstr "" -#: src/sagas/networkSettings.js:91 +#: src/sagas/networkSettings.js:86 msgid "Custom Network Settings cannot be empty." msgstr "" -#: src/sagas/networkSettings.js:98 +#: src/sagas/networkSettings.js:93 msgid "explorerUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:105 +#: src/sagas/networkSettings.js:100 msgid "explorerServiceUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:112 +#: src/sagas/networkSettings.js:107 msgid "txMiningServiceUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:119 +#: src/sagas/networkSettings.js:114 msgid "nodeUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:126 +#: src/sagas/networkSettings.js:121 msgid "walletServiceUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:133 +#: src/sagas/networkSettings.js:128 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:296 +#: src/sagas/networkSettings.js:291 msgid "Wallet not found while trying to persist the custom network settings." msgstr "" -#: src/sagas/pushNotification.js:59 +#: src/sagas/pushNotification.js:71 msgid "Transaction" msgstr "Transaktion" -#: src/sagas/pushNotification.js:60 +#: src/sagas/pushNotification.js:72 msgid "Open" msgstr "Åben" @@ -1093,21 +1093,21 @@ msgstr "" msgid "Error loading the details of some tokens." msgstr "" -#: src/sagas/wallet.js:749 +#: src/sagas/wallet.js:781 msgid "Wallet is not ready to load addresses." msgstr "" #. This will show the message in the feedback content at SelectAddressModal -#: src/sagas/wallet.js:765 +#: src/sagas/wallet.js:797 msgid "There was an error while loading wallet addresses. Try again." msgstr "" -#: src/sagas/wallet.js:775 +#: src/sagas/wallet.js:807 msgid "Wallet is not ready to load the first address." msgstr "" #. This will show the message in the feedback content -#: src/sagas/wallet.js:791 +#: src/sagas/wallet.js:823 msgid "There was an error while loading first wallet address. Try again." msgstr "" @@ -1243,16 +1243,16 @@ msgstr "Del" msgid "Propagating transaction to the network." msgstr "" -#: src/components/ShowPushNotificationTxDetails.js:15 +#: src/components/ShowPushNotificationTxDetails.js:16 msgid "Transation not found" msgstr "" -#: src/components/ShowPushNotificationTxDetails.js:16 +#: src/components/ShowPushNotificationTxDetails.js:17 msgid "" "The transaction has not arrived yet in your wallet. Do you want to retry?" msgstr "" -#: src/components/ShowPushNotificationTxDetails.js:17 +#: src/components/ShowPushNotificationTxDetails.js:18 msgid "Retry" msgstr "" @@ -1279,12 +1279,12 @@ msgstr "" msgid "Description" msgstr "Beskrivelse" -#: src/components/NanoContract/NanoContractTransactionHeader.js:47 +#: src/components/NanoContract/NanoContractTransactionHeader.js:44 #: src/components/TxDetailsModal.js:104 msgid "Transaction ID" msgstr "" -#: src/components/NanoContract/NanoContractTransactionHeader.js:92 +#: src/components/NanoContract/NanoContractTransactionHeader.js:89 #: src/components/TxDetailsModal.js:105 #: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:116 msgid "Blueprint Method" @@ -1389,7 +1389,7 @@ msgstr "" msgid "Loading..." msgstr "" -#: src/components/NanoContract/NanoContractTransactionHeader.js:106 +#: src/components/NanoContract/NanoContractTransactionHeader.js:103 #: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:124 msgid "Caller" msgstr "" @@ -1398,15 +1398,19 @@ msgstr "" msgid "Couldn't determine address, select one" msgstr "" -#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:48 +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:50 #, javascript-format msgid "Position ${ idx }" msgstr "" -#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:93 +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:98 msgid "Arguments" msgstr "" +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:103 +msgid "Loading arguments." +msgstr "" + #: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:72 msgid "You have received a new Nano Contract Transaction. Please" msgstr "" @@ -1432,46 +1436,46 @@ msgstr "" msgid "Read More." msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:184 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:191 msgid "Nano Contract Not Found" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:185 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:192 msgid "" "The Nano Contract requested is not registered. First register the Nano " "Contract to interact with it." msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:188 -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:235 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:195 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:242 msgid "Decline Transaction" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:203 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:210 msgid "Loading transaction information." msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:231 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:238 msgid "Accept Transaction" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:245 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:252 msgid "Sending transaction" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:246 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:253 msgid "Please wait." msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:272 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:279 msgid "Transaction successfully sent." msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:274 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:281 msgid "Ok, close" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:280 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:287 msgid "Error while sending transaction." msgstr "" @@ -1509,11 +1513,15 @@ msgstr "" msgid "Go back" msgstr "" -#: src/components/NanoContract/NanoContractDetails.js:159 +#: src/components/NanoContract/NanoContractDetails.js:177 +msgid "Load More" +msgstr "" + +#: src/components/NanoContract/NanoContractDetails.js:203 msgid "Loading Nano Contract transactions." msgstr "" -#: src/components/NanoContract/NanoContractDetails.js:173 +#: src/components/NanoContract/NanoContractDetails.js:217 msgid "Nano Contract Transactions Error" msgstr "" @@ -1529,19 +1537,33 @@ msgstr "" msgid "Unregister contract" msgstr "" -#. XXX: add when shrank component can be used. -#: src/components/NanoContract/NanoContractTransactionHeader.js:96 +#: src/components/NanoContract/NanoContractTransactionActionList.js:44 +msgid "No Actions" +msgstr "" + +#: src/components/NanoContract/NanoContractTransactionActionList.js:45 +msgid "See full transaction details on Public Explorer." +msgstr "" + +#: src/components/NanoContract/NanoContractTransactionActionListItem.js:108 +#, javascript-format +msgid "Deposit ${ tokenSymbol }" +msgstr "" + +#: src/components/NanoContract/NanoContractTransactionActionListItem.js:109 +msgid "Withdrawal ${ tokenSymbol }" +msgstr "" + +#: src/components/NanoContract/NanoContractTransactionHeader.js:93 msgid "Date and Time" msgstr "" -#. XXX: add when shrank component can be used. -#: src/components/NanoContract/NanoContractTransactionHeader.js:103 +#: src/components/NanoContract/NanoContractTransactionHeader.js:100 #: src/components/NanoContract/NanoContractTransactionsListItem.js:62 msgid "From this wallet" msgstr "" -#. XXX: add when shrank component can be used. -#: src/components/NanoContract/NanoContractTransactionHeader.js:109 +#: src/components/NanoContract/NanoContractTransactionHeader.js:106 msgid "See transaction details" msgstr "" diff --git a/locale/pt-br/texts.po b/locale/pt-br/texts.po index 58ae687d1..b120c96b6 100644 --- a/locale/pt-br/texts.po +++ b/locale/pt-br/texts.po @@ -57,7 +57,7 @@ msgid "[Last] dddd [•] HH:mm" msgstr "[Última] dddd [•] HH:mm" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:107 src/utils.js:416 +#: src/models.js:107 src/utils.js:426 msgid "DD MMM YYYY [•] HH:mm" msgstr "DD MMM YYYY [•] HH:mm" @@ -443,8 +443,8 @@ msgstr "Palavras" msgid "Enter your seed words separated by space" msgstr "Digite suas palavras separadas por espaços" -#: src/components/NanoContract/NanoContractDetails.js:194 -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:282 +#: src/components/NanoContract/NanoContractDetails.js:238 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:289 #: src/screens/LoadHistoryScreen.js:51 src/screens/LoadWalletErrorScreen.js:20 #: src/screens/NanoContract/NanoContractRegisterScreen.js:161 msgid "Try again" @@ -469,7 +469,7 @@ msgid "There's been an error connecting to the server." msgstr "Ocorreu um erro ao conectar com o servidor." #: src/screens/LoadWalletErrorScreen.js:21 src/screens/PinScreen.js:268 -#: src/screens/Settings.js:154 +#: src/screens/Settings.js:161 msgid "Reset wallet" msgstr "Resetar Wallet" @@ -554,7 +554,7 @@ msgstr "Desbloqueie sua Hathor Wallet" msgid "Cancel" msgstr "Cancelar" -#: src/screens/PushNotification.js:58 src/screens/Settings.js:128 +#: src/screens/PushNotification.js:58 src/screens/Settings.js:135 msgid "Push Notification" msgstr "Notificação" @@ -591,7 +591,7 @@ msgstr "RECEBER" #. translator: Used when the QR Code Scanner is opened, and user will manually #. enter the information. -#: src/screens/RegisterToken.js:27 src/screens/SendScanQRCode.js:89 +#: src/screens/RegisterToken.js:27 src/screens/SendScanQRCode.js:87 msgid "Manual info" msgstr "Digitar" @@ -676,7 +676,7 @@ msgstr "Mudar PIN" msgid "Lock wallet" msgstr "Bloquear a wallet" -#: src/screens/SendAddressInput.js:53 src/screens/SendScanQRCode.js:97 +#: src/screens/SendAddressInput.js:53 src/screens/SendScanQRCode.js:95 msgid "SEND" msgstr "ENVIAR" @@ -722,7 +722,7 @@ msgstr "Sua transferência de **${ this.amountAndToken }** foi confirmada" msgid "Address" msgstr "Endereço" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:288 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:291 #: src/screens/SendConfirmScreen.js:168 msgid "Send" msgstr "Enviar" @@ -735,53 +735,53 @@ msgstr "QR code inválido" msgid "OK" msgstr "OK" -#: src/screens/SendScanQRCode.js:73 +#: src/screens/SendScanQRCode.js:71 #, javascript-format msgid "You don't have the requested token [${ tokenLabel }]" msgstr "Você não tem o token requisitado [${ tokenLabel }]" -#: src/screens/SendScanQRCode.js:105 +#: src/screens/SendScanQRCode.js:103 #: src/screens/WalletConnect/WalletConnectScan.js:49 msgid "Scan the QR code" msgstr "Leia o QR code" -#: src/screens/Settings.js:97 +#: src/screens/Settings.js:104 msgid "You are connected to" msgstr "Você está conectado à" -#: src/screens/Settings.js:105 +#: src/screens/Settings.js:112 msgid "General Settings" msgstr "Configurações Gerais" -#: src/screens/Settings.js:109 +#: src/screens/Settings.js:116 msgid "Connected to" msgstr "Conectado ao servidor" -#: src/screens/Settings.js:122 +#: src/screens/Settings.js:129 msgid "Security" msgstr "Segurança" -#: src/screens/Settings.js:135 +#: src/screens/Settings.js:142 msgid "Create a new token" msgstr "Criar um novo token" -#: src/screens/Settings.js:142 +#: src/screens/Settings.js:149 msgid "Register a token" msgstr "Registrar um token" -#: src/screens/Settings.js:158 +#: src/screens/Settings.js:165 msgid "About" msgstr "Sobre" -#: src/screens/Settings.js:165 +#: src/screens/Settings.js:172 msgid "Unique app identifier" msgstr "Identificador único do aplicativo" -#: src/screens/Settings.js:179 +#: src/screens/Settings.js:186 msgid "Developer Settings" msgstr "Configurações do Desenvolvedor" -#: src/screens/Settings.js:181 +#: src/screens/Settings.js:188 msgid "Network Settings" msgstr "Configurações de Rede" @@ -917,33 +917,35 @@ msgstr "txMiningServiceUrl é obrigatório." #: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:62 msgid "walletServiceUrl is required when walletServiceWsUrl is filled." -msgstr "walletServiceUrl é obrigatório quando walletServiceWsUrl está preenchido." +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" +msgstr "" +"walletServiceWsUrl é obrigatório quando walletServiceUrl está preenchido" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:229 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:232 msgid "Node URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:239 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:242 msgid "Explorer URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:248 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:251 msgid "Explorer Service URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:257 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:260 msgid "Transaction Mining Service URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:268 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:271 msgid "Wallet Service URL (optional)" msgstr "Wallet Service URL (opcional)" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:277 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:280 msgid "Wallet Service WS URL (optional)" msgstr "Wallet Service WS URL (opcional)" @@ -985,9 +987,9 @@ msgstr "Ver contrato" msgid "Load First Addresses Error" msgstr "Erro ao carregar primeiro endereço da wallet" -#: src/components/NanoContract/NanoContractDetails.js:158 +#: src/components/NanoContract/NanoContractDetails.js:202 #: src/components/NanoContract/SelectAddressModal.js:105 -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:202 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:209 #: src/screens/NanoContract/NanoContractRegisterScreen.js:177 msgid "Loading" msgstr "Carregando" @@ -997,7 +999,7 @@ msgid "Loading first wallet address." msgstr "Carregando primeiro endereço da wallet." #: src/components/NanoContract/NanoContractDetailsHeader.js:142 -#: src/components/NanoContract/NanoContractTransactionHeader.js:88 +#: src/components/NanoContract/NanoContractTransactionHeader.js:85 #: src/components/NanoContract/NanoContractsListItem.js:57 #: src/components/TxDetailsModal.js:106 #: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:83 @@ -1025,7 +1027,7 @@ msgstr "Registrar Nano Contract" msgid "Nano Contract Registration" msgstr "Registro do Nano Contract" -#: src/screens/NanoContract/NanoContractTransactionScreen.js:37 +#: src/screens/NanoContract/NanoContractTransactionScreen.js:39 msgid "Nano Contract Transaction" msgstr "Transação do Nano Contract" @@ -1033,82 +1035,82 @@ msgstr "Transação do Nano Contract" msgid "Contract successfully registered." msgstr "Nano Contract registrado com sucesso." -#: src/sagas/nanoContract.js:45 +#: src/sagas/nanoContract.js:44 msgid "Nano Contract already registered." msgstr "Nano Contract já registrado." -#: src/sagas/nanoContract.js:46 +#: src/sagas/nanoContract.js:45 msgid "Wallet is not ready yet to register a Nano Contract." msgstr "A wallet não está pronta ainda para registrar Nano Contracts." -#: src/sagas/nanoContract.js:47 +#: src/sagas/nanoContract.js:46 msgid "The informed address does not belong to the wallet." msgstr "O endereço informado não pertence a esta carteira." -#: src/sagas/nanoContract.js:48 +#: src/sagas/nanoContract.js:47 msgid "Nano Contract not found." msgstr "Nano Contract não encontrado" -#: src/sagas/nanoContract.js:49 +#: src/sagas/nanoContract.js:48 msgid "Error while trying to get Nano Contract state." msgstr "Erro ao obter o estado do Nano Contract." -#: src/sagas/nanoContract.js:50 +#: src/sagas/nanoContract.js:49 msgid "Blueprint not found." msgstr "Blueprint não encontrado." -#: src/sagas/nanoContract.js:51 +#: src/sagas/nanoContract.js:50 msgid "Couldn't get Blueprint info." msgstr "Não foi possível carregar informações do Blueprint." -#: src/sagas/nanoContract.js:52 +#: src/sagas/nanoContract.js:51 msgid "Nano Contract not registered." msgstr "Nano Contract não registrado." -#: src/sagas/nanoContract.js:53 +#: src/sagas/nanoContract.js:52 msgid "Error while trying to download Nano Contract transactions history." msgstr "Error ao fazer download do histórico de transações do Nano Contract." -#: src/sagas/networkSettings.js:91 +#: src/sagas/networkSettings.js:86 msgid "Custom Network Settings cannot be empty." msgstr "As Configurações de Rede não podem estar vazias." -#: src/sagas/networkSettings.js:98 +#: src/sagas/networkSettings.js:93 msgid "explorerUrl should be a valid URL." msgstr "explorerUrl deve ser uma URL válida." -#: src/sagas/networkSettings.js:105 +#: src/sagas/networkSettings.js:100 msgid "explorerServiceUrl should be a valid URL." msgstr "explorerServiceUrl deve ser uma URL válida." -#: src/sagas/networkSettings.js:112 +#: src/sagas/networkSettings.js:107 msgid "txMiningServiceUrl should be a valid URL." msgstr "txMiningServiceUrl deve ser uma URL válida." -#: src/sagas/networkSettings.js:119 +#: src/sagas/networkSettings.js:114 msgid "nodeUrl should be a valid URL." msgstr "nodeUrl deve ser uma URL válida." -#: src/sagas/networkSettings.js:126 +#: src/sagas/networkSettings.js:121 msgid "walletServiceUrl should be a valid URL." msgstr "walletServiceUrl deve ser uma URL válida." -#: src/sagas/networkSettings.js:133 +#: src/sagas/networkSettings.js:128 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:296 +#: src/sagas/networkSettings.js:291 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:59 +#: src/sagas/pushNotification.js:71 msgid "Transaction" msgstr "Transação" -#: src/sagas/pushNotification.js:60 +#: src/sagas/pushNotification.js:72 msgid "Open" msgstr "Abrir" @@ -1120,21 +1122,21 @@ msgstr "A wallet não está pronta ainda." msgid "Error loading the details of some tokens." msgstr "Ocorreu um erro durante o carregamento de detalhes de alguns tokens." -#: src/sagas/wallet.js:749 +#: src/sagas/wallet.js:781 msgid "Wallet is not ready to load addresses." msgstr "A wallet não está pronta para carregar os endereços." #. This will show the message in the feedback content at SelectAddressModal -#: src/sagas/wallet.js:765 +#: src/sagas/wallet.js:797 msgid "There was an error while loading wallet addresses. Try again." msgstr "Ocorreu um erro ao carregar os endereços da wallet. Tente novamente." -#: src/sagas/wallet.js:775 +#: src/sagas/wallet.js:807 msgid "Wallet is not ready to load the first address." msgstr "A wallet não está pronta para carregar o primeiro endereço." #. This will show the message in the feedback content -#: src/sagas/wallet.js:791 +#: src/sagas/wallet.js:823 msgid "There was an error while loading first wallet address. Try again." msgstr "" "Ocorreu um erro ao carregar o primeiro endereço da wallet. Tente novamente." @@ -1274,16 +1276,16 @@ msgstr "Compartilhar" msgid "Propagating transaction to the network." msgstr "Propagando a transação para a rede." -#: src/components/ShowPushNotificationTxDetails.js:15 +#: src/components/ShowPushNotificationTxDetails.js:16 msgid "Transation not found" msgstr "Transação não encontrada" -#: src/components/ShowPushNotificationTxDetails.js:16 +#: src/components/ShowPushNotificationTxDetails.js:17 msgid "" "The transaction has not arrived yet in your wallet. Do you want to retry?" msgstr "A transação ainda não chegou na sua carteira. Deseja tentar novamente?" -#: src/components/ShowPushNotificationTxDetails.js:17 +#: src/components/ShowPushNotificationTxDetails.js:18 msgid "Retry" msgstr "Tentar novamente" @@ -1309,12 +1311,12 @@ msgstr "Inválida" msgid "Description" msgstr "Descrição" -#: src/components/NanoContract/NanoContractTransactionHeader.js:47 +#: src/components/NanoContract/NanoContractTransactionHeader.js:44 #: src/components/TxDetailsModal.js:104 msgid "Transaction ID" msgstr "ID da Transação" -#: src/components/NanoContract/NanoContractTransactionHeader.js:92 +#: src/components/NanoContract/NanoContractTransactionHeader.js:89 #: src/components/TxDetailsModal.js:105 #: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:116 msgid "Blueprint Method" @@ -1426,7 +1428,7 @@ msgstr "Nome do Blueprint" msgid "Loading..." msgstr "Carregando..." -#: src/components/NanoContract/NanoContractTransactionHeader.js:106 +#: src/components/NanoContract/NanoContractTransactionHeader.js:103 #: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:124 msgid "Caller" msgstr "Caller" @@ -1435,15 +1437,19 @@ msgstr "Caller" msgid "Couldn't determine address, select one" msgstr "Não foi possível determinar um endereço, selecione um" -#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:48 +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:50 #, javascript-format msgid "Position ${ idx }" msgstr "Posição ${ idx }" -#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:93 +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:98 msgid "Arguments" msgstr "Argumentos" +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:103 +msgid "Loading arguments." +msgstr "Carregando argumentos." + #: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:72 msgid "You have received a new Nano Contract Transaction. Please" msgstr "" @@ -1471,11 +1477,11 @@ msgstr "" msgid "Read More." msgstr "Ler mais." -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:184 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:191 msgid "Nano Contract Not Found" msgstr "Nano Contract não encontrado" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:185 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:192 msgid "" "The Nano Contract requested is not registered. First register the Nano " "Contract to interact with it." @@ -1483,36 +1489,36 @@ msgstr "" "O Nano Contract solicitado não está registrado. Primeiro registre o Nano " "Contract para interagir com ele." -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:188 -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:235 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:195 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:242 msgid "Decline Transaction" msgstr "Recusar transação" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:203 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:210 msgid "Loading transaction information." msgstr "Carregando informações da transação." -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:231 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:238 msgid "Accept Transaction" msgstr "Aceitar transação" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:245 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:252 msgid "Sending transaction" msgstr "Enviando transação" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:246 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:253 msgid "Please wait." msgstr "Por favor, espere." -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:272 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:279 msgid "Transaction successfully sent." msgstr "Transação enviada com sucesso." -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:274 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:281 msgid "Ok, close" msgstr "Ok, fechar" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:280 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:287 msgid "Error while sending transaction." msgstr "Ocorreu um erro durante o envio da transação." @@ -1552,11 +1558,15 @@ msgstr "Confirmar novo endereço" msgid "Go back" msgstr "Voltar" -#: src/components/NanoContract/NanoContractDetails.js:159 +#: src/components/NanoContract/NanoContractDetails.js:177 +msgid "Load More" +msgstr "Carregar mais" + +#: src/components/NanoContract/NanoContractDetails.js:203 msgid "Loading Nano Contract transactions." msgstr "Carregando transações do Nano Contract" -#: src/components/NanoContract/NanoContractDetails.js:173 +#: src/components/NanoContract/NanoContractDetails.js:217 msgid "Nano Contract Transactions Error" msgstr "Erro em Transações do Nano Contract" @@ -1572,19 +1582,34 @@ msgstr "Ver detalhes" msgid "Unregister contract" msgstr "Desregistrar contract" -#. XXX: add when shrank component can be used. -#: src/components/NanoContract/NanoContractTransactionHeader.js:96 +#: src/components/NanoContract/NanoContractTransactionActionList.js:44 +msgid "No Actions" +msgstr "Nenhuma Action" + +#: src/components/NanoContract/NanoContractTransactionActionList.js:45 +msgid "See full transaction details on Public Explorer." +msgstr "Ver todos os detalhes da transação no Explorer Público." + +#: src/components/NanoContract/NanoContractTransactionActionListItem.js:108 +#, javascript-format +msgid "Deposit ${ tokenSymbol }" +msgstr "Depósito ${ tokenSymbol }" + +#: src/components/NanoContract/NanoContractTransactionActionListItem.js:109 +#, javascript-format +msgid "Withdrawal ${ tokenSymbol }" +msgstr "Saque ${ tokenSymbol }" + +#: src/components/NanoContract/NanoContractTransactionHeader.js:93 msgid "Date and Time" msgstr "Data & Hora" -#. XXX: add when shrank component can be used. -#: src/components/NanoContract/NanoContractTransactionHeader.js:103 +#: src/components/NanoContract/NanoContractTransactionHeader.js:100 #: src/components/NanoContract/NanoContractTransactionsListItem.js:62 msgid "From this wallet" msgstr "Desta wallet" -#. XXX: add when shrank component can be used. -#: src/components/NanoContract/NanoContractTransactionHeader.js:109 +#: src/components/NanoContract/NanoContractTransactionHeader.js:106 msgid "See transaction details" msgstr "Ver detalhes" diff --git a/locale/ru-ru/texts.po b/locale/ru-ru/texts.po index f3b11bb36..7c3654954 100644 --- a/locale/ru-ru/texts.po +++ b/locale/ru-ru/texts.po @@ -58,7 +58,7 @@ msgid "[Last] dddd [•] HH:mm" msgstr "[Последний] dddd [•] HH:mm" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:107 src/utils.js:416 +#: src/models.js:107 src/utils.js:426 msgid "DD MMM YYYY [•] HH:mm" msgstr "DD MMM YYYY [•] HH:mm" @@ -433,8 +433,8 @@ msgstr "Слова" msgid "Enter your seed words separated by space" msgstr "Введите seed-фразу" -#: src/components/NanoContract/NanoContractDetails.js:194 -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:282 +#: src/components/NanoContract/NanoContractDetails.js:238 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:289 #: src/screens/LoadHistoryScreen.js:51 src/screens/LoadWalletErrorScreen.js:20 #: src/screens/NanoContract/NanoContractRegisterScreen.js:161 msgid "Try again" @@ -459,7 +459,7 @@ msgid "There's been an error connecting to the server." msgstr "" #: src/screens/LoadWalletErrorScreen.js:21 src/screens/PinScreen.js:268 -#: src/screens/Settings.js:154 +#: src/screens/Settings.js:161 msgid "Reset wallet" msgstr "Сбросить кошелек" @@ -543,7 +543,7 @@ msgstr "Разблокировать Hathor Wallet" msgid "Cancel" msgstr "Отмена" -#: src/screens/PushNotification.js:58 src/screens/Settings.js:128 +#: src/screens/PushNotification.js:58 src/screens/Settings.js:135 msgid "Push Notification" msgstr "" @@ -578,7 +578,7 @@ msgstr "ПОЛУЧИТЬ" #. translator: Used when the QR Code Scanner is opened, and user will manually #. enter the information. -#: src/screens/RegisterToken.js:27 src/screens/SendScanQRCode.js:89 +#: src/screens/RegisterToken.js:27 src/screens/SendScanQRCode.js:87 msgid "Manual info" msgstr "Ручной ввод" @@ -661,7 +661,7 @@ msgstr "Изменить PIN-код" msgid "Lock wallet" msgstr "Заблокировать кошелек" -#: src/screens/SendAddressInput.js:53 src/screens/SendScanQRCode.js:97 +#: src/screens/SendAddressInput.js:53 src/screens/SendScanQRCode.js:95 msgid "SEND" msgstr "ОТПРАВИТЬ" @@ -708,7 +708,7 @@ msgstr "Ваш перевод **${ this.amountAndToken }** был подтвер msgid "Address" msgstr "Адрес" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:288 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:291 #: src/screens/SendConfirmScreen.js:168 msgid "Send" msgstr "Отправить" @@ -721,53 +721,53 @@ msgstr "Неверный QR-код" msgid "OK" msgstr "OK" -#: src/screens/SendScanQRCode.js:73 +#: src/screens/SendScanQRCode.js:71 #, javascript-format msgid "You don't have the requested token [${ tokenLabel }]" msgstr "У вас нет запрошенного токена [${ tokenLabel }]" -#: src/screens/SendScanQRCode.js:105 +#: src/screens/SendScanQRCode.js:103 #: src/screens/WalletConnect/WalletConnectScan.js:49 msgid "Scan the QR code" msgstr "Сканировать QR-код" -#: src/screens/Settings.js:97 +#: src/screens/Settings.js:104 msgid "You are connected to" msgstr "Вы подключены к" -#: src/screens/Settings.js:105 +#: src/screens/Settings.js:112 msgid "General Settings" msgstr "" -#: src/screens/Settings.js:109 +#: src/screens/Settings.js:116 msgid "Connected to" msgstr "Подключены к" -#: src/screens/Settings.js:122 +#: src/screens/Settings.js:129 msgid "Security" msgstr "Безопасность" -#: src/screens/Settings.js:135 +#: src/screens/Settings.js:142 msgid "Create a new token" msgstr "Создать новый токен" -#: src/screens/Settings.js:142 +#: src/screens/Settings.js:149 msgid "Register a token" msgstr "Зарегистрировать токен" -#: src/screens/Settings.js:158 +#: src/screens/Settings.js:165 msgid "About" msgstr "О нас" -#: src/screens/Settings.js:165 +#: src/screens/Settings.js:172 msgid "Unique app identifier" msgstr "" -#: src/screens/Settings.js:179 +#: src/screens/Settings.js:186 msgid "Developer Settings" msgstr "" -#: src/screens/Settings.js:181 +#: src/screens/Settings.js:188 msgid "Network Settings" msgstr "" @@ -904,27 +904,27 @@ msgstr "" msgid "walletServiceWsUrl is required when walletServiceUrl is filled." msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:229 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:232 msgid "Node URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:239 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:242 msgid "Explorer URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:248 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:251 msgid "Explorer Service URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:257 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:260 msgid "Transaction Mining Service URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:268 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:271 msgid "Wallet Service URL (optional)" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:277 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:280 msgid "Wallet Service WS URL (optional)" msgstr "" @@ -963,9 +963,9 @@ msgstr "" msgid "Load First Addresses Error" msgstr "" -#: src/components/NanoContract/NanoContractDetails.js:158 +#: src/components/NanoContract/NanoContractDetails.js:202 #: src/components/NanoContract/SelectAddressModal.js:105 -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:202 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:209 #: src/screens/NanoContract/NanoContractRegisterScreen.js:177 msgid "Loading" msgstr "" @@ -975,7 +975,7 @@ msgid "Loading first wallet address." msgstr "" #: src/components/NanoContract/NanoContractDetailsHeader.js:142 -#: src/components/NanoContract/NanoContractTransactionHeader.js:88 +#: src/components/NanoContract/NanoContractTransactionHeader.js:85 #: src/components/NanoContract/NanoContractsListItem.js:57 #: src/components/TxDetailsModal.js:106 #: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:83 @@ -1003,7 +1003,7 @@ msgstr "" msgid "Nano Contract Registration" msgstr "" -#: src/screens/NanoContract/NanoContractTransactionScreen.js:37 +#: src/screens/NanoContract/NanoContractTransactionScreen.js:39 msgid "Nano Contract Transaction" msgstr "" @@ -1011,81 +1011,81 @@ msgstr "" msgid "Contract successfully registered." msgstr "" -#: src/sagas/nanoContract.js:45 +#: src/sagas/nanoContract.js:44 msgid "Nano Contract already registered." msgstr "" -#: src/sagas/nanoContract.js:46 +#: src/sagas/nanoContract.js:45 msgid "Wallet is not ready yet to register a Nano Contract." msgstr "" -#: src/sagas/nanoContract.js:47 +#: src/sagas/nanoContract.js:46 msgid "The informed address does not belong to the wallet." msgstr "" -#: src/sagas/nanoContract.js:48 +#: src/sagas/nanoContract.js:47 msgid "Nano Contract not found." msgstr "" -#: src/sagas/nanoContract.js:49 +#: src/sagas/nanoContract.js:48 msgid "Error while trying to get Nano Contract state." msgstr "" -#: src/sagas/nanoContract.js:50 +#: src/sagas/nanoContract.js:49 msgid "Blueprint not found." msgstr "" -#: src/sagas/nanoContract.js:51 +#: src/sagas/nanoContract.js:50 msgid "Couldn't get Blueprint info." msgstr "" -#: src/sagas/nanoContract.js:52 +#: src/sagas/nanoContract.js:51 msgid "Nano Contract not registered." msgstr "" -#: src/sagas/nanoContract.js:53 +#: src/sagas/nanoContract.js:52 msgid "Error while trying to download Nano Contract transactions history." msgstr "" -#: src/sagas/networkSettings.js:91 +#: src/sagas/networkSettings.js:86 msgid "Custom Network Settings cannot be empty." msgstr "" -#: src/sagas/networkSettings.js:98 +#: src/sagas/networkSettings.js:93 msgid "explorerUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:105 +#: src/sagas/networkSettings.js:100 msgid "explorerServiceUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:112 +#: src/sagas/networkSettings.js:107 msgid "txMiningServiceUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:119 +#: src/sagas/networkSettings.js:114 msgid "nodeUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:126 +#: src/sagas/networkSettings.js:121 msgid "walletServiceUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:133 +#: src/sagas/networkSettings.js:128 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:296 +#: src/sagas/networkSettings.js:291 msgid "Wallet not found while trying to persist the custom network settings." msgstr "" -#: src/sagas/pushNotification.js:59 +#: src/sagas/pushNotification.js:71 msgid "Transaction" msgstr "" -#: src/sagas/pushNotification.js:60 +#: src/sagas/pushNotification.js:72 msgid "Open" msgstr "Открыть" @@ -1097,21 +1097,21 @@ msgstr "" msgid "Error loading the details of some tokens." msgstr "" -#: src/sagas/wallet.js:749 +#: src/sagas/wallet.js:781 msgid "Wallet is not ready to load addresses." msgstr "" #. This will show the message in the feedback content at SelectAddressModal -#: src/sagas/wallet.js:765 +#: src/sagas/wallet.js:797 msgid "There was an error while loading wallet addresses. Try again." msgstr "" -#: src/sagas/wallet.js:775 +#: src/sagas/wallet.js:807 msgid "Wallet is not ready to load the first address." msgstr "" #. This will show the message in the feedback content -#: src/sagas/wallet.js:791 +#: src/sagas/wallet.js:823 msgid "There was an error while loading first wallet address. Try again." msgstr "" @@ -1233,16 +1233,16 @@ msgstr "Поделиться" msgid "Propagating transaction to the network." msgstr "" -#: src/components/ShowPushNotificationTxDetails.js:15 +#: src/components/ShowPushNotificationTxDetails.js:16 msgid "Transation not found" msgstr "" -#: src/components/ShowPushNotificationTxDetails.js:16 +#: src/components/ShowPushNotificationTxDetails.js:17 msgid "" "The transaction has not arrived yet in your wallet. Do you want to retry?" msgstr "" -#: src/components/ShowPushNotificationTxDetails.js:17 +#: src/components/ShowPushNotificationTxDetails.js:18 msgid "Retry" msgstr "" @@ -1268,12 +1268,12 @@ msgstr "" msgid "Description" msgstr "Описание" -#: src/components/NanoContract/NanoContractTransactionHeader.js:47 +#: src/components/NanoContract/NanoContractTransactionHeader.js:44 #: src/components/TxDetailsModal.js:104 msgid "Transaction ID" msgstr "" -#: src/components/NanoContract/NanoContractTransactionHeader.js:92 +#: src/components/NanoContract/NanoContractTransactionHeader.js:89 #: src/components/TxDetailsModal.js:105 #: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:116 msgid "Blueprint Method" @@ -1378,7 +1378,7 @@ msgstr "" msgid "Loading..." msgstr "" -#: src/components/NanoContract/NanoContractTransactionHeader.js:106 +#: src/components/NanoContract/NanoContractTransactionHeader.js:103 #: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:124 msgid "Caller" msgstr "" @@ -1387,15 +1387,19 @@ msgstr "" msgid "Couldn't determine address, select one" msgstr "" -#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:48 +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:50 #, javascript-format msgid "Position ${ idx }" msgstr "" -#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:93 +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:98 msgid "Arguments" msgstr "" +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:103 +msgid "Loading arguments." +msgstr "" + #: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:72 msgid "You have received a new Nano Contract Transaction. Please" msgstr "" @@ -1421,46 +1425,46 @@ msgstr "" msgid "Read More." msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:184 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:191 msgid "Nano Contract Not Found" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:185 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:192 msgid "" "The Nano Contract requested is not registered. First register the Nano " "Contract to interact with it." msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:188 -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:235 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:195 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:242 msgid "Decline Transaction" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:203 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:210 msgid "Loading transaction information." msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:231 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:238 msgid "Accept Transaction" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:245 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:252 msgid "Sending transaction" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:246 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:253 msgid "Please wait." msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:272 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:279 msgid "Transaction successfully sent." msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:274 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:281 msgid "Ok, close" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:280 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:287 msgid "Error while sending transaction." msgstr "" @@ -1498,11 +1502,15 @@ msgstr "" msgid "Go back" msgstr "" -#: src/components/NanoContract/NanoContractDetails.js:159 +#: src/components/NanoContract/NanoContractDetails.js:177 +msgid "Load More" +msgstr "" + +#: src/components/NanoContract/NanoContractDetails.js:203 msgid "Loading Nano Contract transactions." msgstr "" -#: src/components/NanoContract/NanoContractDetails.js:173 +#: src/components/NanoContract/NanoContractDetails.js:217 msgid "Nano Contract Transactions Error" msgstr "" @@ -1518,19 +1526,33 @@ msgstr "" msgid "Unregister contract" msgstr "" -#. XXX: add when shrank component can be used. -#: src/components/NanoContract/NanoContractTransactionHeader.js:96 +#: src/components/NanoContract/NanoContractTransactionActionList.js:44 +msgid "No Actions" +msgstr "" + +#: src/components/NanoContract/NanoContractTransactionActionList.js:45 +msgid "See full transaction details on Public Explorer." +msgstr "" + +#: src/components/NanoContract/NanoContractTransactionActionListItem.js:108 +#, javascript-format +msgid "Deposit ${ tokenSymbol }" +msgstr "" + +#: src/components/NanoContract/NanoContractTransactionActionListItem.js:109 +msgid "Withdrawal ${ tokenSymbol }" +msgstr "" + +#: src/components/NanoContract/NanoContractTransactionHeader.js:93 msgid "Date and Time" msgstr "" -#. XXX: add when shrank component can be used. -#: src/components/NanoContract/NanoContractTransactionHeader.js:103 +#: src/components/NanoContract/NanoContractTransactionHeader.js:100 #: src/components/NanoContract/NanoContractTransactionsListItem.js:62 msgid "From this wallet" msgstr "" -#. XXX: add when shrank component can be used. -#: src/components/NanoContract/NanoContractTransactionHeader.js:109 +#: src/components/NanoContract/NanoContractTransactionHeader.js:106 msgid "See transaction details" msgstr "" diff --git a/locale/texts.pot b/locale/texts.pot index b7d76f44e..e0dc3880e 100644 --- a/locale/texts.pot +++ b/locale/texts.pot @@ -48,7 +48,7 @@ msgid "[Last] dddd [•] HH:mm" msgstr "" #: src/models.js:107 -#: src/utils.js:416 +#: src/utils.js:426 #. See https://momentjs.com/docs/#/displaying/calendar-time/ msgid "DD MMM YYYY [•] HH:mm" msgstr "" @@ -421,8 +421,8 @@ msgstr "" msgid "Enter your seed words separated by space" msgstr "" -#: src/components/NanoContract/NanoContractDetails.js:194 -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:282 +#: src/components/NanoContract/NanoContractDetails.js:238 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:289 #: src/screens/LoadHistoryScreen.js:51 #: src/screens/LoadWalletErrorScreen.js:20 #: src/screens/NanoContract/NanoContractRegisterScreen.js:161 @@ -449,7 +449,7 @@ msgstr "" #: src/screens/LoadWalletErrorScreen.js:21 #: src/screens/PinScreen.js:268 -#: src/screens/Settings.js:154 +#: src/screens/Settings.js:161 msgid "Reset wallet" msgstr "" @@ -537,7 +537,7 @@ msgid "Cancel" msgstr "" #: src/screens/PushNotification.js:58 -#: src/screens/Settings.js:128 +#: src/screens/Settings.js:135 msgid "Push Notification" msgstr "" @@ -571,7 +571,7 @@ msgid "RECEIVE" msgstr "" #: src/screens/RegisterToken.js:27 -#: src/screens/SendScanQRCode.js:89 +#: src/screens/SendScanQRCode.js:87 #. translator: Used when the QR Code Scanner is opened, and user will manually #. enter the information. msgid "Manual info" @@ -654,7 +654,7 @@ msgid "Lock wallet" msgstr "" #: src/screens/SendAddressInput.js:53 -#: src/screens/SendScanQRCode.js:97 +#: src/screens/SendScanQRCode.js:95 msgid "SEND" msgstr "" @@ -704,7 +704,7 @@ msgstr "" msgid "Address" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:288 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:291 #: src/screens/SendConfirmScreen.js:168 msgid "Send" msgstr "" @@ -717,53 +717,53 @@ msgstr "" msgid "OK" msgstr "" -#: src/screens/SendScanQRCode.js:73 +#: src/screens/SendScanQRCode.js:71 #, javascript-format msgid "You don't have the requested token [${ tokenLabel }]" msgstr "" -#: src/screens/SendScanQRCode.js:105 +#: src/screens/SendScanQRCode.js:103 #: src/screens/WalletConnect/WalletConnectScan.js:49 msgid "Scan the QR code" msgstr "" -#: src/screens/Settings.js:97 +#: src/screens/Settings.js:104 msgid "You are connected to" msgstr "" -#: src/screens/Settings.js:105 +#: src/screens/Settings.js:112 msgid "General Settings" msgstr "" -#: src/screens/Settings.js:109 +#: src/screens/Settings.js:116 msgid "Connected to" msgstr "" -#: src/screens/Settings.js:122 +#: src/screens/Settings.js:129 msgid "Security" msgstr "" -#: src/screens/Settings.js:135 +#: src/screens/Settings.js:142 msgid "Create a new token" msgstr "" -#: src/screens/Settings.js:142 +#: src/screens/Settings.js:149 msgid "Register a token" msgstr "" -#: src/screens/Settings.js:158 +#: src/screens/Settings.js:165 msgid "About" msgstr "" -#: src/screens/Settings.js:165 +#: src/screens/Settings.js:172 msgid "Unique app identifier" msgstr "" -#: src/screens/Settings.js:179 +#: src/screens/Settings.js:186 msgid "Developer Settings" msgstr "" -#: src/screens/Settings.js:181 +#: src/screens/Settings.js:188 msgid "Network Settings" msgstr "" @@ -897,27 +897,27 @@ msgstr "" msgid "walletServiceWsUrl is required when walletServiceUrl is filled." msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:229 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:232 msgid "Node URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:239 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:242 msgid "Explorer URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:248 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:251 msgid "Explorer Service URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:257 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:260 msgid "Transaction Mining Service URL" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:268 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:271 msgid "Wallet Service URL (optional)" msgstr "" -#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:277 +#: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:280 msgid "Wallet Service WS URL (optional)" msgstr "" @@ -956,9 +956,9 @@ msgstr "" msgid "Load First Addresses Error" msgstr "" -#: src/components/NanoContract/NanoContractDetails.js:158 +#: src/components/NanoContract/NanoContractDetails.js:202 #: src/components/NanoContract/SelectAddressModal.js:105 -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:202 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:209 #: src/screens/NanoContract/NanoContractRegisterScreen.js:177 msgid "Loading" msgstr "" @@ -968,7 +968,7 @@ msgid "Loading first wallet address." msgstr "" #: src/components/NanoContract/NanoContractDetailsHeader.js:142 -#: src/components/NanoContract/NanoContractTransactionHeader.js:88 +#: src/components/NanoContract/NanoContractTransactionHeader.js:85 #: src/components/NanoContract/NanoContractsListItem.js:57 #: src/components/TxDetailsModal.js:106 #: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:83 @@ -996,7 +996,7 @@ msgstr "" msgid "Nano Contract Registration" msgstr "" -#: src/screens/NanoContract/NanoContractTransactionScreen.js:37 +#: src/screens/NanoContract/NanoContractTransactionScreen.js:39 msgid "Nano Contract Transaction" msgstr "" @@ -1004,81 +1004,81 @@ msgstr "" msgid "Contract successfully registered." msgstr "" -#: src/sagas/nanoContract.js:45 +#: src/sagas/nanoContract.js:44 msgid "Nano Contract already registered." msgstr "" -#: src/sagas/nanoContract.js:46 +#: src/sagas/nanoContract.js:45 msgid "Wallet is not ready yet to register a Nano Contract." msgstr "" -#: src/sagas/nanoContract.js:47 +#: src/sagas/nanoContract.js:46 msgid "The informed address does not belong to the wallet." msgstr "" -#: src/sagas/nanoContract.js:48 +#: src/sagas/nanoContract.js:47 msgid "Nano Contract not found." msgstr "" -#: src/sagas/nanoContract.js:49 +#: src/sagas/nanoContract.js:48 msgid "Error while trying to get Nano Contract state." msgstr "" -#: src/sagas/nanoContract.js:50 +#: src/sagas/nanoContract.js:49 msgid "Blueprint not found." msgstr "" -#: src/sagas/nanoContract.js:51 +#: src/sagas/nanoContract.js:50 msgid "Couldn't get Blueprint info." msgstr "" -#: src/sagas/nanoContract.js:52 +#: src/sagas/nanoContract.js:51 msgid "Nano Contract not registered." msgstr "" -#: src/sagas/nanoContract.js:53 +#: src/sagas/nanoContract.js:52 msgid "Error while trying to download Nano Contract transactions history." msgstr "" -#: src/sagas/networkSettings.js:91 +#: src/sagas/networkSettings.js:86 msgid "Custom Network Settings cannot be empty." msgstr "" -#: src/sagas/networkSettings.js:98 +#: src/sagas/networkSettings.js:93 msgid "explorerUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:105 +#: src/sagas/networkSettings.js:100 msgid "explorerServiceUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:112 +#: src/sagas/networkSettings.js:107 msgid "txMiningServiceUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:119 +#: src/sagas/networkSettings.js:114 msgid "nodeUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:126 +#: src/sagas/networkSettings.js:121 msgid "walletServiceUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:133 +#: src/sagas/networkSettings.js:128 msgid "walletServiceWsUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:296 +#: src/sagas/networkSettings.js:291 #. 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:59 +#: src/sagas/pushNotification.js:71 msgid "Transaction" msgstr "" -#: src/sagas/pushNotification.js:60 +#: src/sagas/pushNotification.js:72 msgid "Open" msgstr "" @@ -1090,20 +1090,20 @@ msgstr "" msgid "Error loading the details of some tokens." msgstr "" -#: src/sagas/wallet.js:749 +#: src/sagas/wallet.js:781 msgid "Wallet is not ready to load addresses." msgstr "" -#: src/sagas/wallet.js:765 +#: src/sagas/wallet.js:797 #. This will show the message in the feedback content at SelectAddressModal msgid "There was an error while loading wallet addresses. Try again." msgstr "" -#: src/sagas/wallet.js:775 +#: src/sagas/wallet.js:807 msgid "Wallet is not ready to load the first address." msgstr "" -#: src/sagas/wallet.js:791 +#: src/sagas/wallet.js:823 #. This will show the message in the feedback content msgid "There was an error while loading first wallet address. Try again." msgstr "" @@ -1228,15 +1228,15 @@ msgstr "" msgid "Propagating transaction to the network." msgstr "" -#: src/components/ShowPushNotificationTxDetails.js:15 +#: src/components/ShowPushNotificationTxDetails.js:16 msgid "Transation not found" msgstr "" -#: src/components/ShowPushNotificationTxDetails.js:16 +#: src/components/ShowPushNotificationTxDetails.js:17 msgid "The transaction has not arrived yet in your wallet. Do you want to retry?" msgstr "" -#: src/components/ShowPushNotificationTxDetails.js:17 +#: src/components/ShowPushNotificationTxDetails.js:18 msgid "Retry" msgstr "" @@ -1261,12 +1261,12 @@ msgstr "" msgid "Description" msgstr "" -#: src/components/NanoContract/NanoContractTransactionHeader.js:47 +#: src/components/NanoContract/NanoContractTransactionHeader.js:44 #: src/components/TxDetailsModal.js:104 msgid "Transaction ID" msgstr "" -#: src/components/NanoContract/NanoContractTransactionHeader.js:92 +#: src/components/NanoContract/NanoContractTransactionHeader.js:89 #: src/components/TxDetailsModal.js:105 #: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:116 msgid "Blueprint Method" @@ -1371,7 +1371,7 @@ msgstr "" msgid "Loading..." msgstr "" -#: src/components/NanoContract/NanoContractTransactionHeader.js:106 +#: src/components/NanoContract/NanoContractTransactionHeader.js:103 #: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:124 msgid "Caller" msgstr "" @@ -1380,15 +1380,19 @@ msgstr "" msgid "Couldn't determine address, select one" msgstr "" -#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:48 +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:50 #, javascript-format msgid "Position ${ idx }" msgstr "" -#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:93 +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:98 msgid "Arguments" msgstr "" +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:103 +msgid "Loading arguments." +msgstr "" + #: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:72 msgid "You have received a new Nano Contract Transaction. Please" msgstr "" @@ -1413,46 +1417,46 @@ msgstr "" msgid "Read More." msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:184 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:191 msgid "Nano Contract Not Found" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:185 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:192 msgid "" "The Nano Contract requested is not registered. First register the Nano " "Contract to interact with it." msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:188 -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:235 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:195 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:242 msgid "Decline Transaction" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:203 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:210 msgid "Loading transaction information." msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:231 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:238 msgid "Accept Transaction" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:245 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:252 msgid "Sending transaction" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:246 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:253 msgid "Please wait." msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:272 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:279 msgid "Transaction successfully sent." msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:274 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:281 msgid "Ok, close" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:280 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:287 msgid "Error while sending transaction." msgstr "" @@ -1490,11 +1494,15 @@ msgstr "" msgid "Go back" msgstr "" -#: src/components/NanoContract/NanoContractDetails.js:159 +#: src/components/NanoContract/NanoContractDetails.js:177 +msgid "Load More" +msgstr "" + +#: src/components/NanoContract/NanoContractDetails.js:203 msgid "Loading Nano Contract transactions." msgstr "" -#: src/components/NanoContract/NanoContractDetails.js:173 +#: src/components/NanoContract/NanoContractDetails.js:217 msgid "Nano Contract Transactions Error" msgstr "" @@ -1510,19 +1518,33 @@ msgstr "" msgid "Unregister contract" msgstr "" -#: src/components/NanoContract/NanoContractTransactionHeader.js:96 -#. XXX: add when shrank component can be used. +#: src/components/NanoContract/NanoContractTransactionActionList.js:44 +msgid "No Actions" +msgstr "" + +#: src/components/NanoContract/NanoContractTransactionActionList.js:45 +msgid "See full transaction details on Public Explorer." +msgstr "" + +#: src/components/NanoContract/NanoContractTransactionActionListItem.js:108 +#, javascript-format +msgid "Deposit ${ tokenSymbol }" +msgstr "" + +#: src/components/NanoContract/NanoContractTransactionActionListItem.js:109 +msgid "Withdrawal ${ tokenSymbol }" +msgstr "" + +#: src/components/NanoContract/NanoContractTransactionHeader.js:93 msgid "Date and Time" msgstr "" -#: src/components/NanoContract/NanoContractTransactionHeader.js:103 +#: src/components/NanoContract/NanoContractTransactionHeader.js:100 #: src/components/NanoContract/NanoContractTransactionsListItem.js:62 -#. XXX: add when shrank component can be used. msgid "From this wallet" msgstr "" -#: src/components/NanoContract/NanoContractTransactionHeader.js:109 -#. XXX: add when shrank component can be used. +#: src/components/NanoContract/NanoContractTransactionHeader.js:106 msgid "See transaction details" msgstr "" diff --git a/src/actions.js b/src/actions.js index 4e8b70e96..f358d7c58 100644 --- a/src/actions.js +++ b/src/actions.js @@ -1126,8 +1126,12 @@ export const nanoContractHistoryLoading = (ncEntry) => ({ * Nano Contract history has loaded success. * @param {Object} payload * @param {string} payload.ncId Nano Contract ID. - * @param {Object[]} payload.history Nano Contract's history chunk as array. - * @param {string} payload.after A new history chunk will be fetched after this hash. + * @param {Object[]?} payload.history A chunk of txs to initialize history + * @param {Object[]?} payload.beforeHistory A chunk of newer txs. + * @param {Object[]?} payload.afterHistory A chunk of older txs. + * + * @description + * The history options are mutually exclusive. */ export const nanoContractHistorySuccess = (payload) => ({ type: types.NANOCONTRACT_HISTORY_SUCCESS, diff --git a/src/components/NanoContract/NanoContractDetails.js b/src/components/NanoContract/NanoContractDetails.js index 8c5b9f9c8..df3a2dafe 100644 --- a/src/components/NanoContract/NanoContractDetails.js +++ b/src/components/NanoContract/NanoContractDetails.js @@ -6,7 +6,7 @@ */ import { useNavigation } from '@react-navigation/native'; -import React, { useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import { StyleSheet, View, @@ -23,6 +23,7 @@ import Spinner from '../Spinner'; import errorIcon from '../../assets/images/icErrorBig.png'; import SimpleButton from '../SimpleButton'; import { FeedbackContent } from '../FeedbackContent'; +import NewHathorButton from '../NewHathorButton'; /** * Retrieves Nano Contract details from Redux. @@ -33,7 +34,6 @@ import { FeedbackContent } from '../FeedbackContent'; * txHistory: Object[]; * isLoading: boolean; * error: string; - * after: string; * }} */ const getNanoContractDetails = (ncId) => (state) => { @@ -42,14 +42,13 @@ const getNanoContractDetails = (ncId) => (state) => { * and let it step aside while coming back to Dashboard screen. This transition happens * quickly, therefore the user will not have time to see the default state. */ - const defaultMeta = { isLoading: false, error: null, after: null }; + const defaultMeta = { isLoading: false, error: null }; const txHistory = state.nanoContract.history[ncId] || []; - const { isLoading, error, after } = state.nanoContract.historyMeta[ncId] || defaultMeta; + const { isLoading, error } = state.nanoContract.historyMeta[ncId] || defaultMeta; return { txHistory, isLoading, error, - after, }; } @@ -65,7 +64,11 @@ export const NanoContractDetails = ({ nc }) => { const dispatch = useDispatch(); const navigation = useNavigation(); - const { txHistory, isLoading, error, after } = useSelector(getNanoContractDetails(nc.ncId)); + const { + txHistory, + isLoading, + error, + } = useSelector(getNanoContractDetails(nc.ncId)); const [ncAddress, changeNcAddress] = useState(nc.address); const onAddressChange = (newAddress) => { @@ -77,6 +80,7 @@ export const NanoContractDetails = ({ nc }) => { navigation.navigate('NanoContractTransactionScreen', { tx }); }; + // This effect runs only once when the component is first built. useEffect(() => { if (txHistory.length === 0) { /* The first time we load the Nano Contract details its transaction history is empty. @@ -85,18 +89,18 @@ export const NanoContractDetails = ({ nc }) => { * For the first transaction history load we don't need to specify the `after` param, * it will be set during the load. */ - dispatch(nanoContractHistoryRequest({ ncId: nc.ncId, after: null })); + dispatch(nanoContractHistoryRequest({ ncId: nc.ncId })); + } else { + // Fetch new transactions when there are some transactions in the history. + dispatch(nanoContractHistoryRequest({ ncId: nc.ncId, before: txHistory[0].txId })); } }, []); - const handleMoreTransactions = () => { - if (after == null) { - /* This situation is unlikely to happen because on the first transactions history load - * the `after` is assigned with the hash of the last transaction in the list. - */ - return; - } - dispatch(nanoContractHistoryRequest({ ncId: nc.ncId, after })); + /** + * Triggered when a user makes the pull gesture on the transaction history content. + */ + const handleNewerTransactions = () => { + dispatch(nanoContractHistoryRequest({ ncId: nc.ncId, before: txHistory[0].txId })); }; /* If an error happens on loading transactions history, an error feedback @@ -133,13 +137,53 @@ export const NanoContractDetails = ({ nc }) => { )} keyExtractor={(item) => item.txId} refreshing={isLoading} - onRefresh={handleMoreTransactions} + // Enables the pull gesture to get newer transactions + onRefresh={handleNewerTransactions} + // Enables a button to load more of older transactions until the end + // By reaching the end, the button ceases to render + ListFooterComponent={} + extraData={[isLoading, error]} /> )} ); }; +/** + * It shows a button to 'Load More' transactions after the last one. + * It hides the button when the last transaction is the initialize. + * + * @param {Object} prop Properties object + * @param {{ + * ncId: string; + * txId: string; + * }} prop.lastTx A transaction item from transaction history + */ +const LoadMoreButton = ({ lastTx }) => { + const dispatch = useDispatch(); + const isInitializeTx = lastTx.ncMethod === 'initialize'; + + /** + * This handling will dispatch an action to request for + * older transactions after a txId. + */ + const handleLoadMore = () => useCallback(dispatch(nanoContractHistoryRequest({ + ncId: lastTx.ncId, + after: lastTx.txId, + })), [lastTx]); + + return !isInitializeTx && ( + + ) +}; + /** * @param {Object} props * @param {Object?} props.children Either a react component or a react element diff --git a/src/reducers/reducer.js b/src/reducers/reducer.js index a5b399c6f..eaea546d2 100644 --- a/src/reducers/reducer.js +++ b/src/reducers/reducer.js @@ -384,7 +384,6 @@ const initialState = { * [ncId: string]: { * isLoading: boolean; * error: string; - * after: string; * }; * }} holds the load state for each nano contract, including the after hash * from which a new history chunk should be fetched, exclusively. @@ -392,7 +391,6 @@ const initialState = { * { * '000001342d3c5b858a4d4835baea93fcc683fa615ff5892bd044459621a0340a': { * isLoading: false, - * after: '000075e15f015dc768065763acd9b563ec002e37182869965ff2c712bed83e1e', * }, * } */ @@ -1702,7 +1700,6 @@ export const onNanoContractHistoryClean = (state, { payload }) => ({ [payload.ncId]: { ...(state.nanoContract.historyMeta[payload.ncId]), isLoading: false, - after: null, error: null, }, }, @@ -1714,8 +1711,9 @@ export const onNanoContractHistoryClean = (state, { payload }) => ({ * @param {{ * payload: { * ncId: string; - * history: Object[]; - * after: string; + * history?: Object[]; + * beforeHistory?: Object[]; + * afterHistory?: Object[]; * } * }} action */ @@ -1726,9 +1724,9 @@ export const onNanoContractHistorySuccess = (state, { payload }) => ({ history: { ...state.nanoContract.history, [payload.ncId]: [ - ...(state.nanoContract.history[payload.ncId] || []), - // we are putting at the bottom because we expect an array with descending order. - ...payload.history, + ...(payload.beforeHistory || []), + ...(payload.history || state.nanoContract.history[payload.ncId] || []), + ...(payload.afterHistory || []), ], }, historyMeta: { @@ -1736,7 +1734,6 @@ export const onNanoContractHistorySuccess = (state, { payload }) => ({ [payload.ncId]: { ...(state.nanoContract.historyMeta[payload.ncId]), isLoading: false, - after: payload.after, error: null, }, }, diff --git a/src/sagas/nanoContract.js b/src/sagas/nanoContract.js index 08b034d4d..7f7df2a0b 100644 --- a/src/sagas/nanoContract.js +++ b/src/sagas/nanoContract.js @@ -15,7 +15,6 @@ import { all, put, call, - delay, debounce, } from 'redux-saga/effects'; import { t } from 'ttag'; @@ -35,8 +34,9 @@ import { } from '../actions'; import { logger } from '../logger'; import { NANO_CONTRACT_TX_HISTORY_SIZE } from '../constants'; -import { getNanoContractFeatureToggle } from '../utils'; +import { consumeGenerator, getNanoContractFeatureToggle } from '../utils'; import { getRegisteredNanoContracts } from './helpers'; +import { isWalletServiceEnabled } from './wallet'; const log = logger('nano-contract-saga'); @@ -96,7 +96,15 @@ export function* registerNanoContract({ payload }) { return; } - const isAddressMine = yield call(wallet.isAddressMine.bind(wallet), address); + // XXX: Wallet Service doesn't implement isAddressMine. + // See issue: https://github.com/HathorNetwork/hathor-wallet-lib/issues/732 + // Default to `false` if using Wallet Service. + let isAddressMine = false; + const useWalletService = yield call(isWalletServiceEnabled); + if (!useWalletService) { + isAddressMine = yield call([wallet, wallet.isAddressMine], address); + } + if (!isAddressMine) { log.debug('Fail registering Nano Contract because address do not belongs to this wallet.'); yield put(nanoContractRegisterFailure(failureMessage.addressNotMine)); @@ -150,7 +158,6 @@ export function* registerNanoContract({ payload }) { * * @typedef {Object} RawNcTxHistoryResponse * @property {boolean} success - * @property {number|null} after * @property {RawNcTxHistory} history */ @@ -170,38 +177,65 @@ export function* registerNanoContract({ payload }) { /** * Fetch history from Nano Contract wallet's API. - * @param {string} ncId Nano Contract ID - * @param {number} count Maximum quantity of history items - * @param {string} after Transaction hash to start to get items - * @param {Object} wallet Wallet instance from redux state + * @req {Object} req + * @param {string} req.ncId Nano Contract ID + * @param {number} req.count Maximum quantity of items to fetch + * @param {string} req.before Transaction hash to start to get newer items + * @param {string} req.after Transaction hash to start to get older items + * @param {Object} req.wallet Wallet instance from redux state + * + * @returns {{ + * history: {}; + * }} * * @throws {Error} when request code is greater then 399 or when response's success is false */ -export async function fetchHistory(ncId, count, after, wallet) { +export async function fetchHistory(req) { + const { + wallet, + ncId, + count, + after, + before, + } = req; /** * @type {RawNcTxHistoryResponse} response */ - const response = await ncApi.getNanoContractHistory(ncId, count, after); + const response = await ncApi.getNanoContractHistory( + ncId, + count, + after || null, + before || null, + ); const { success, history: rawHistory } = response; if (!success) { throw new Error('Failed to fetch nano contract history'); } - const history = []; - for (const rawTx of rawHistory) { + /* TODO: Make it run concurrently while guaranting the order. + /* see https://github.com/HathorNetwork/hathor-wallet-mobile/issues/514 + */ + const historyNewestToOldest = new Array(rawHistory.length) + for (let idx = 0; idx < rawHistory.length; idx += 1) { + const rawTx = rawHistory[idx]; const network = wallet.getNetworkObject(); const caller = addressUtils.getAddressFromPubkey(rawTx.nc_pubkey, network).base58; - // XXX: Wallet Service Wallet doesn't implement isAddressMine. - // It means this method can't run under wallet-service without - // throwing an exception. - // eslint-disable-next-line no-await-in-loop - const isMine = await wallet.isAddressMine(caller); + // XXX: Wallet Service doesn't implement isAddressMine. + // See issue: https://github.com/HathorNetwork/hathor-wallet-lib/issues/732 + // Default to `false` if using Wallet Service. + let isMine = false; + const useWalletService = consumeGenerator(isWalletServiceEnabled()); + if (!useWalletService) { + // eslint-disable-next-line no-await-in-loop + isMine = await wallet.isAddressMine(caller); + } const actions = rawTx.nc_context.actions.map((each) => ({ type: each.type, // 'deposit' or 'withdrawal' uid: each.token_uid, amount: each.amount, })); + const tx = { txId: rawTx.hash, timestamp: rawTx.timestamp, @@ -215,15 +249,10 @@ export async function fetchHistory(ncId, count, after, wallet) { isMine, actions, }; - history.push(tx); - } - - let next = after; - if (history && history.length > 0) { - next = history[history.length - 1].txId; + historyNewestToOldest[idx] = tx; } - return { history, next }; + return { history: historyNewestToOldest }; } /** @@ -231,13 +260,13 @@ export async function fetchHistory(ncId, count, after, wallet) { * @param {{ * payload: { * ncId: string; - * after: string; + * before?: string; + * after?: string; * } * }} action with request payload. */ export function* requestHistoryNanoContract({ payload }) { - const { ncId, after } = payload; - const count = NANO_CONTRACT_TX_HISTORY_SIZE; + const { ncId, before, after } = payload; log.debug('Start processing request for nano contract transaction history...'); const historyMeta = yield select((state) => state.nanoContract.historyMeta); @@ -265,24 +294,33 @@ export function* requestHistoryNanoContract({ payload }) { return; } - if (after == null) { + if (before == null && after == null) { // it clean the history when starting load from the beginning + log.debug('Cleaning previous history to start over.'); yield put(nanoContractHistoryClean({ ncId })); } try { - // fetch from fullnode - const { history, next } = yield call(fetchHistory, ncId, count, after, wallet); - - if (after != null) { - // The first load has always `after` equals null. The first load means to be fast, - // but the subsequent ones are all request by user and we want slow down multiple - // calls to this effect. - yield delay(1000) - } + const req = { + wallet, + ncId, + before, + after, + count: NANO_CONTRACT_TX_HISTORY_SIZE, + }; + const { history } = yield call(fetchHistory, req); log.debug('Success fetching Nano Contract history.'); - yield put(nanoContractHistorySuccess({ ncId, history, after: next })); + if (before != null) { + log.debug('Adding beforeHistory.'); + yield put(nanoContractHistorySuccess({ ncId, beforeHistory: history })); + } else if (after != null) { + log.debug('Adding afterHistory.'); + yield put(nanoContractHistorySuccess({ ncId, afterHistory: history })); + } else { + log.debug('Initializing history.'); + yield put(nanoContractHistorySuccess({ ncId, history })); + } } catch (error) { log.error('Error while fetching Nano Contract history.', error); // break loading process and give feedback to user diff --git a/src/utils.js b/src/utils.js index 212540fa1..52a9a4230 100644 --- a/src/utils.js +++ b/src/utils.js @@ -425,6 +425,18 @@ export const getNanoContractFeatureToggle = (state) => ( */ export const getTimestampFormat = (timestamp) => moment.unix(timestamp).format(t`DD MMM YYYY [•] HH:mm`) +/** + * Extract the result of a generator function when it is done. + */ +export const consumeGenerator = (generator) => { + for (;;) { + const { value, done } = generator.next(); + if (done) { + return value; + } + } +} + /** * Extract all the items of an async iterator/generator. * From 6ba53d99af932c81748acbe53745f054321835d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Carneiro?= Date: Fri, 23 Aug 2024 09:39:57 -0300 Subject: [PATCH 39/51] chore: upgrade actions to a version that use node20 (LTS) (#535) * chore: upgrade actions to a version that use node20 (LTS) * chore: update tests to run on node20 --- .github/workflows/main.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a71681774..3d13c786c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -18,11 +18,11 @@ jobs: timeout-minutes: 40 # default is 360 strategy: matrix: - node-version: [14.x, 16.x] + node-version: [20.x] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - name: Install dependencies From c3223ce34653031f19e68118a05b20d0acb7862e Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Mon, 26 Aug 2024 19:00:46 +0100 Subject: [PATCH 40/51] feat: add qr code to register nano contract (#533) * feat: add qr code to register nano contract --- locale/da/texts.po | 62 ++++++++-------- locale/pt-br/texts.po | 63 ++++++++-------- locale/ru-ru/texts.po | 62 ++++++++-------- locale/texts.pot | 64 +++++++++-------- src/App.js | 68 ++++++++++++++++++ .../RegisterNewNanoContractButton.js | 2 +- .../NanoContractRegisterScreen.js | 11 ++- .../NanoContractRegisterQrCodeScreen.js | 71 +++++++++++++++++++ 8 files changed, 286 insertions(+), 117 deletions(-) create mode 100644 src/screens/NanoContractRegisterQrCodeScreen.js diff --git a/locale/da/texts.po b/locale/da/texts.po index ae8dcd634..82d5593ec 100644 --- a/locale/da/texts.po +++ b/locale/da/texts.po @@ -435,7 +435,7 @@ msgstr "Indtast dine seed-ord adskilt med mellemrum" #: src/components/NanoContract/NanoContractDetails.js:238 #: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:289 #: src/screens/LoadHistoryScreen.js:51 src/screens/LoadWalletErrorScreen.js:20 -#: src/screens/NanoContract/NanoContractRegisterScreen.js:161 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:168 msgid "Try again" msgstr "" @@ -490,6 +490,22 @@ msgstr "Tilgængelig saldo" msgid "Locked" msgstr "Låst" +#. translator: Used when the QR Code Scanner is opened, and user will manually +#. enter the information. +#: src/screens/NanoContractRegisterQrCodeScreen.js:26 +#: src/screens/RegisterToken.js:27 src/screens/SendScanQRCode.js:87 +msgid "Manual info" +msgstr "Manuel information" + +#: src/screens/NanoContract/NanoContractRegisterScreen.js:251 +#: src/screens/NanoContractRegisterQrCodeScreen.js:48 +msgid "Nano Contract Registration" +msgstr "" + +#: src/screens/NanoContractRegisterQrCodeScreen.js:63 +msgid "Scan the nano contract ID QR code" +msgstr "" + #: src/screens/PaymentRequestDetail.js:88 #, javascript-format msgid "You've just received **${ amount } ${ symbol }**" @@ -574,12 +590,6 @@ msgstr "Betalingsanmodning" msgid "RECEIVE" msgstr "MODTAG" -#. translator: Used when the QR Code Scanner is opened, and user will manually -#. enter the information. -#: src/screens/RegisterToken.js:27 src/screens/SendScanQRCode.js:87 -msgid "Manual info" -msgstr "Manuel information" - #: src/screens/RegisterToken.js:42 src/screens/RegisterTokenManual.js:126 msgid "REGISTER TOKEN" msgstr "REGISTRER TOKEN" @@ -951,22 +961,22 @@ msgstr "" msgid "Nano Contract ID is required." msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:151 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:158 msgid "See contract" msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:169 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:176 msgid "Load First Addresses Error" msgstr "" #: src/components/NanoContract/NanoContractDetails.js:202 #: src/components/NanoContract/SelectAddressModal.js:105 #: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:209 -#: src/screens/NanoContract/NanoContractRegisterScreen.js:177 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:184 msgid "Loading" msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:178 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:185 msgid "Loading first wallet address." msgstr "" @@ -975,30 +985,26 @@ msgstr "" #: src/components/NanoContract/NanoContractsListItem.js:57 #: src/components/TxDetailsModal.js:106 #: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:83 -#: src/screens/NanoContract/NanoContractRegisterScreen.js:187 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:194 msgid "Nano Contract ID" msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:195 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:202 msgid "Wallet Address" msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:205 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:212 msgid "If you want to change the wallet address, you will be able to do" msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:207 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:214 msgid "after the contract is registered." msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:226 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:233 msgid "Register Nano Contract" msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:244 -msgid "Nano Contract Registration" -msgstr "" - #: src/screens/NanoContract/NanoContractTransactionScreen.js:39 msgid "Nano Contract Transaction" msgstr "" @@ -1043,37 +1049,37 @@ msgstr "" msgid "Error while trying to download Nano Contract transactions history." msgstr "" -#: src/sagas/networkSettings.js:86 +#: src/sagas/networkSettings.js:85 msgid "Custom Network Settings cannot be empty." msgstr "" -#: src/sagas/networkSettings.js:93 +#: src/sagas/networkSettings.js:92 msgid "explorerUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:100 +#: src/sagas/networkSettings.js:99 msgid "explorerServiceUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:107 +#: src/sagas/networkSettings.js:106 msgid "txMiningServiceUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:114 +#: src/sagas/networkSettings.js:113 msgid "nodeUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:121 +#: src/sagas/networkSettings.js:120 msgid "walletServiceUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:128 +#: src/sagas/networkSettings.js:127 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:291 +#: src/sagas/networkSettings.js:279 msgid "Wallet not found while trying to persist the custom network settings." msgstr "" diff --git a/locale/pt-br/texts.po b/locale/pt-br/texts.po index b120c96b6..2b8484689 100644 --- a/locale/pt-br/texts.po +++ b/locale/pt-br/texts.po @@ -446,7 +446,7 @@ msgstr "Digite suas palavras separadas por espaços" #: src/components/NanoContract/NanoContractDetails.js:238 #: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:289 #: src/screens/LoadHistoryScreen.js:51 src/screens/LoadWalletErrorScreen.js:20 -#: src/screens/NanoContract/NanoContractRegisterScreen.js:161 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:168 msgid "Try again" msgstr "Tente novamente" @@ -503,6 +503,22 @@ msgstr "Saldo Disponível" msgid "Locked" msgstr "Bloqueado" +#. translator: Used when the QR Code Scanner is opened, and user will manually +#. enter the information. +#: src/screens/NanoContractRegisterQrCodeScreen.js:26 +#: src/screens/RegisterToken.js:27 src/screens/SendScanQRCode.js:87 +msgid "Manual info" +msgstr "Digitar" + +#: src/screens/NanoContract/NanoContractRegisterScreen.js:251 +#: src/screens/NanoContractRegisterQrCodeScreen.js:48 +msgid "Nano Contract Registration" +msgstr "Registro do Nano Contract" + +#: src/screens/NanoContractRegisterQrCodeScreen.js:63 +msgid "Scan the nano contract ID QR code" +msgstr "Leia o QR code do Nano Contract ID" + #: src/screens/PaymentRequestDetail.js:88 #, javascript-format msgid "You've just received **${ amount } ${ symbol }**" @@ -589,12 +605,6 @@ msgstr "Requisição de Pagamento" msgid "RECEIVE" msgstr "RECEBER" -#. translator: Used when the QR Code Scanner is opened, and user will manually -#. enter the information. -#: src/screens/RegisterToken.js:27 src/screens/SendScanQRCode.js:87 -msgid "Manual info" -msgstr "Digitar" - #: src/screens/RegisterToken.js:42 src/screens/RegisterTokenManual.js:126 msgid "REGISTER TOKEN" msgstr "REGISTRAR UM TOKEN" @@ -979,22 +989,22 @@ msgstr "Detalhes do Nano Contract" msgid "Nano Contract ID is required." msgstr "ID do Nano Contract é obrigatório." -#: src/screens/NanoContract/NanoContractRegisterScreen.js:151 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:158 msgid "See contract" msgstr "Ver contrato" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:169 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:176 msgid "Load First Addresses Error" msgstr "Erro ao carregar primeiro endereço da wallet" #: src/components/NanoContract/NanoContractDetails.js:202 #: src/components/NanoContract/SelectAddressModal.js:105 #: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:209 -#: src/screens/NanoContract/NanoContractRegisterScreen.js:177 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:184 msgid "Loading" msgstr "Carregando" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:178 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:185 msgid "Loading first wallet address." msgstr "Carregando primeiro endereço da wallet." @@ -1003,30 +1013,26 @@ msgstr "Carregando primeiro endereço da wallet." #: src/components/NanoContract/NanoContractsListItem.js:57 #: src/components/TxDetailsModal.js:106 #: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:83 -#: src/screens/NanoContract/NanoContractRegisterScreen.js:187 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:194 msgid "Nano Contract ID" msgstr "ID do Nano Contract" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:195 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:202 msgid "Wallet Address" msgstr "Endereço da Carteira" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:205 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:212 msgid "If you want to change the wallet address, you will be able to do" msgstr "Se deseja alterar o endereço de assinatura, você pode alterar" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:207 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:214 msgid "after the contract is registered." msgstr "depois do contrato ser registrado." -#: src/screens/NanoContract/NanoContractRegisterScreen.js:226 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:233 msgid "Register Nano Contract" msgstr "Registrar Nano Contract" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:244 -msgid "Nano Contract Registration" -msgstr "Registro do Nano Contract" - #: src/screens/NanoContract/NanoContractTransactionScreen.js:39 msgid "Nano Contract Transaction" msgstr "Transação do Nano Contract" @@ -1071,37 +1077,37 @@ msgstr "Nano Contract não registrado." msgid "Error while trying to download Nano Contract transactions history." msgstr "Error ao fazer download do histórico de transações do Nano Contract." -#: src/sagas/networkSettings.js:86 +#: src/sagas/networkSettings.js:85 msgid "Custom Network Settings cannot be empty." msgstr "As Configurações de Rede não podem estar vazias." -#: src/sagas/networkSettings.js:93 +#: src/sagas/networkSettings.js:92 msgid "explorerUrl should be a valid URL." msgstr "explorerUrl deve ser uma URL válida." -#: src/sagas/networkSettings.js:100 +#: src/sagas/networkSettings.js:99 msgid "explorerServiceUrl should be a valid URL." msgstr "explorerServiceUrl deve ser uma URL válida." -#: src/sagas/networkSettings.js:107 +#: src/sagas/networkSettings.js:106 msgid "txMiningServiceUrl should be a valid URL." msgstr "txMiningServiceUrl deve ser uma URL válida." -#: src/sagas/networkSettings.js:114 +#: src/sagas/networkSettings.js:113 msgid "nodeUrl should be a valid URL." msgstr "nodeUrl deve ser uma URL válida." -#: src/sagas/networkSettings.js:121 +#: src/sagas/networkSettings.js:120 msgid "walletServiceUrl should be a valid URL." msgstr "walletServiceUrl deve ser uma URL válida." -#: src/sagas/networkSettings.js:128 +#: src/sagas/networkSettings.js:127 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:291 +#: src/sagas/networkSettings.js:279 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." @@ -1596,7 +1602,6 @@ msgid "Deposit ${ tokenSymbol }" msgstr "Depósito ${ tokenSymbol }" #: src/components/NanoContract/NanoContractTransactionActionListItem.js:109 -#, javascript-format msgid "Withdrawal ${ tokenSymbol }" msgstr "Saque ${ tokenSymbol }" diff --git a/locale/ru-ru/texts.po b/locale/ru-ru/texts.po index 7c3654954..f38b2fa8a 100644 --- a/locale/ru-ru/texts.po +++ b/locale/ru-ru/texts.po @@ -436,7 +436,7 @@ msgstr "Введите seed-фразу" #: src/components/NanoContract/NanoContractDetails.js:238 #: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:289 #: src/screens/LoadHistoryScreen.js:51 src/screens/LoadWalletErrorScreen.js:20 -#: src/screens/NanoContract/NanoContractRegisterScreen.js:161 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:168 msgid "Try again" msgstr "" @@ -492,6 +492,22 @@ msgstr "Доступный Баланс" msgid "Locked" msgstr "Заблокированный" +#. translator: Used when the QR Code Scanner is opened, and user will manually +#. enter the information. +#: src/screens/NanoContractRegisterQrCodeScreen.js:26 +#: src/screens/RegisterToken.js:27 src/screens/SendScanQRCode.js:87 +msgid "Manual info" +msgstr "Ручной ввод" + +#: src/screens/NanoContract/NanoContractRegisterScreen.js:251 +#: src/screens/NanoContractRegisterQrCodeScreen.js:48 +msgid "Nano Contract Registration" +msgstr "" + +#: src/screens/NanoContractRegisterQrCodeScreen.js:63 +msgid "Scan the nano contract ID QR code" +msgstr "" + #: src/screens/PaymentRequestDetail.js:88 #, javascript-format msgid "You've just received **${ amount } ${ symbol }**" @@ -576,12 +592,6 @@ msgstr "Запрос Средств" msgid "RECEIVE" msgstr "ПОЛУЧИТЬ" -#. translator: Used when the QR Code Scanner is opened, and user will manually -#. enter the information. -#: src/screens/RegisterToken.js:27 src/screens/SendScanQRCode.js:87 -msgid "Manual info" -msgstr "Ручной ввод" - #: src/screens/RegisterToken.js:42 src/screens/RegisterTokenManual.js:126 msgid "REGISTER TOKEN" msgstr "РЕГИСТРАЦИЯ ТОКЕНА" @@ -955,22 +965,22 @@ msgstr "" msgid "Nano Contract ID is required." msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:151 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:158 msgid "See contract" msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:169 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:176 msgid "Load First Addresses Error" msgstr "" #: src/components/NanoContract/NanoContractDetails.js:202 #: src/components/NanoContract/SelectAddressModal.js:105 #: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:209 -#: src/screens/NanoContract/NanoContractRegisterScreen.js:177 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:184 msgid "Loading" msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:178 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:185 msgid "Loading first wallet address." msgstr "" @@ -979,30 +989,26 @@ msgstr "" #: src/components/NanoContract/NanoContractsListItem.js:57 #: src/components/TxDetailsModal.js:106 #: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:83 -#: src/screens/NanoContract/NanoContractRegisterScreen.js:187 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:194 msgid "Nano Contract ID" msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:195 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:202 msgid "Wallet Address" msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:205 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:212 msgid "If you want to change the wallet address, you will be able to do" msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:207 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:214 msgid "after the contract is registered." msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:226 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:233 msgid "Register Nano Contract" msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:244 -msgid "Nano Contract Registration" -msgstr "" - #: src/screens/NanoContract/NanoContractTransactionScreen.js:39 msgid "Nano Contract Transaction" msgstr "" @@ -1047,37 +1053,37 @@ msgstr "" msgid "Error while trying to download Nano Contract transactions history." msgstr "" -#: src/sagas/networkSettings.js:86 +#: src/sagas/networkSettings.js:85 msgid "Custom Network Settings cannot be empty." msgstr "" -#: src/sagas/networkSettings.js:93 +#: src/sagas/networkSettings.js:92 msgid "explorerUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:100 +#: src/sagas/networkSettings.js:99 msgid "explorerServiceUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:107 +#: src/sagas/networkSettings.js:106 msgid "txMiningServiceUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:114 +#: src/sagas/networkSettings.js:113 msgid "nodeUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:121 +#: src/sagas/networkSettings.js:120 msgid "walletServiceUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:128 +#: src/sagas/networkSettings.js:127 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:291 +#: src/sagas/networkSettings.js:279 msgid "Wallet not found while trying to persist the custom network settings." msgstr "" diff --git a/locale/texts.pot b/locale/texts.pot index e0dc3880e..5e738173f 100644 --- a/locale/texts.pot +++ b/locale/texts.pot @@ -425,7 +425,7 @@ msgstr "" #: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:289 #: src/screens/LoadHistoryScreen.js:51 #: src/screens/LoadWalletErrorScreen.js:20 -#: src/screens/NanoContract/NanoContractRegisterScreen.js:161 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:168 msgid "Try again" msgstr "" @@ -482,6 +482,23 @@ msgstr "" msgid "Locked" msgstr "" +#: src/screens/NanoContractRegisterQrCodeScreen.js:26 +#: src/screens/RegisterToken.js:27 +#: src/screens/SendScanQRCode.js:87 +#. translator: Used when the QR Code Scanner is opened, and user will manually +#. enter the information. +msgid "Manual info" +msgstr "" + +#: src/screens/NanoContract/NanoContractRegisterScreen.js:251 +#: src/screens/NanoContractRegisterQrCodeScreen.js:48 +msgid "Nano Contract Registration" +msgstr "" + +#: src/screens/NanoContractRegisterQrCodeScreen.js:63 +msgid "Scan the nano contract ID QR code" +msgstr "" + #: src/screens/PaymentRequestDetail.js:88 #, javascript-format msgid "You've just received **${ amount } ${ symbol }**" @@ -570,13 +587,6 @@ msgstr "" msgid "RECEIVE" msgstr "" -#: src/screens/RegisterToken.js:27 -#: src/screens/SendScanQRCode.js:87 -#. translator: Used when the QR Code Scanner is opened, and user will manually -#. enter the information. -msgid "Manual info" -msgstr "" - #: src/screens/RegisterToken.js:42 #: src/screens/RegisterTokenManual.js:126 msgid "REGISTER TOKEN" @@ -948,22 +958,22 @@ msgstr "" msgid "Nano Contract ID is required." msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:151 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:158 msgid "See contract" msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:169 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:176 msgid "Load First Addresses Error" msgstr "" #: src/components/NanoContract/NanoContractDetails.js:202 #: src/components/NanoContract/SelectAddressModal.js:105 #: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:209 -#: src/screens/NanoContract/NanoContractRegisterScreen.js:177 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:184 msgid "Loading" msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:178 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:185 msgid "Loading first wallet address." msgstr "" @@ -972,30 +982,26 @@ msgstr "" #: src/components/NanoContract/NanoContractsListItem.js:57 #: src/components/TxDetailsModal.js:106 #: src/components/WalletConnect/NanoContract/NanoContractExecInfo.js:83 -#: src/screens/NanoContract/NanoContractRegisterScreen.js:187 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:194 msgid "Nano Contract ID" msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:195 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:202 msgid "Wallet Address" msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:205 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:212 msgid "If you want to change the wallet address, you will be able to do" msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:207 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:214 msgid "after the contract is registered." msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:226 +#: src/screens/NanoContract/NanoContractRegisterScreen.js:233 msgid "Register Nano Contract" msgstr "" -#: src/screens/NanoContract/NanoContractRegisterScreen.js:244 -msgid "Nano Contract Registration" -msgstr "" - #: src/screens/NanoContract/NanoContractTransactionScreen.js:39 msgid "Nano Contract Transaction" msgstr "" @@ -1040,35 +1046,35 @@ msgstr "" msgid "Error while trying to download Nano Contract transactions history." msgstr "" -#: src/sagas/networkSettings.js:86 +#: src/sagas/networkSettings.js:85 msgid "Custom Network Settings cannot be empty." msgstr "" -#: src/sagas/networkSettings.js:93 +#: src/sagas/networkSettings.js:92 msgid "explorerUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:100 +#: src/sagas/networkSettings.js:99 msgid "explorerServiceUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:107 +#: src/sagas/networkSettings.js:106 msgid "txMiningServiceUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:114 +#: src/sagas/networkSettings.js:113 msgid "nodeUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:121 +#: src/sagas/networkSettings.js:120 msgid "walletServiceUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:128 +#: src/sagas/networkSettings.js:127 msgid "walletServiceWsUrl should be a valid URL." msgstr "" -#: src/sagas/networkSettings.js:291 +#: src/sagas/networkSettings.js:279 #. 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." diff --git a/src/App.js b/src/App.js index e679bebd1..9dbddc2b0 100644 --- a/src/App.js +++ b/src/App.js @@ -92,6 +92,7 @@ import { NanoContractTransactionScreen } from './screens/NanoContract/NanoContra import { NanoContractRegisterScreen } from './screens/NanoContract/NanoContractRegisterScreen'; import { NewNanoContractTransactionScreen } from './screens/WalletConnect/NewNanoContractTransactionScreen'; import { NewNanoContractTransactionModal } from './components/WalletConnect/NanoContract/NewNanoContractTransactionModal'; +import { NanoContractRegisterQrCodeScreen } from './screens/NanoContractRegisterQrCodeScreen'; /** * This Stack Navigator is exhibited when there is no wallet initialized on the local storage. @@ -288,6 +289,72 @@ const RegisterTokenStack = ({ navigation }) => { ); }; +/** + * Stack of screens dedicated to the nano contract registration process + */ +const RegisterNanoContractStack = ({ navigation }) => { + const Stack = createStackNavigator(); + const dispatch = useDispatch(); + const isCameraAvailable = useSelector((state) => state.isCameraAvailable); + + /** + * Defines which screen will be the initial one, according to app camera permissions + * @param {null|boolean} cameraStatus + * @returns {string} Route name + */ + const decideRouteByCameraAvailablity = (cameraStatus) => { + switch (isCameraAvailable) { + case true: + return 'NanoContractRegisterQrCodeScreen'; + case false: + return 'NanoContractRegisterScreen'; + default: + return 'RegisterCameraPermissionScreen'; + } + }; + + // Initial screen set on component initial rendering + const [initialRoute, setInitialRoute] = useState( + decideRouteByCameraAvailablity(isCameraAvailable) + ); + + /* + * Request camera permission on initialization only if permission is not already set + */ + useEffect(() => { + if (isCameraAvailable === null) { + dispatch(requestCameraPermission()); + } + }, []); + + // Listen to camera permission changes from user input and navigate to the relevant screen + useEffect(() => { + const newScreenName = decideRouteByCameraAvailablity(isCameraAvailable); + + // Navigator screen already correct: no further action. + if (initialRoute === newScreenName) { + return; + } + + // Set initial route and navigate there according to new permission set + setInitialRoute(newScreenName); + navigation.replace(newScreenName); + }, [isCameraAvailable]); + + return ( + + + + + + ); +}; + const tabBarIconMap = { Home: 'icDashboard', Send: 'icSend', @@ -400,6 +467,7 @@ const AppStack = () => { /> + { const navigation = useNavigation(); const navigatesToRegisterNanoContract = () => { - navigation.navigate('NanoContractRegisterScreen'); + navigation.navigate('RegisterNanoContract'); }; return ( diff --git a/src/screens/NanoContract/NanoContractRegisterScreen.js b/src/screens/NanoContract/NanoContractRegisterScreen.js index 3925b78d2..d5bed315c 100644 --- a/src/screens/NanoContract/NanoContractRegisterScreen.js +++ b/src/screens/NanoContract/NanoContractRegisterScreen.js @@ -61,7 +61,9 @@ function validate(formModel) { return invalidModel; } -export function NanoContractRegisterScreen({ navigation }) { +export function NanoContractRegisterScreen({ navigation, route }) { + const ncIdFromQrCode = route.params?.ncId; + const dispatch = useDispatch(); const { address, error } = useSelector((state) => state.firstAddress); const registerState = useSelector((state) => ({ @@ -116,7 +118,7 @@ export function NanoContractRegisterScreen({ navigation }) { const { ncId } = formModel; dispatch(nanoContractRegisterRequest({ address, ncId })); }, - [formModel] + [formModel, address] ); const handleFeedbackModalDismiss = () => { @@ -129,6 +131,11 @@ export function NanoContractRegisterScreen({ navigation }) { }; useEffect(() => { + if (ncIdFromQrCode) { + // Set ncId in the input when given + handleInputChange('ncId')(ncIdFromQrCode); + } + if (!address) { dispatch(firstAddressRequest()); } diff --git a/src/screens/NanoContractRegisterQrCodeScreen.js b/src/screens/NanoContractRegisterQrCodeScreen.js new file mode 100644 index 000000000..004b8fcfe --- /dev/null +++ b/src/screens/NanoContractRegisterQrCodeScreen.js @@ -0,0 +1,71 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import { View } from 'react-native'; +import { t } from 'ttag'; + +import HathorHeader from '../components/HathorHeader'; +import QRCodeReader from '../components/QRCodeReader'; +import SimpleButton from '../components/SimpleButton'; +import { COLORS } from '../styles/themes'; + +export const NanoContractRegisterQrCodeScreen = ({ navigation }) => { + const onSuccess = (e) => { + navigation.navigate('NanoContractRegisterScreen', { ncId: e.data }); + } + + const renderHeaderRightElement = () => ( + navigation.navigate('NanoContractRegisterScreen')} + /> + ); + + return ( + + + navigation.pop()} + rightElement={renderHeaderRightElement()} + /> + + + + + + ); +} + +export default NanoContractRegisterQrCodeScreen; From 8814c63034df77fe69b1a6f6e94c9856f193e897 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Tue, 27 Aug 2024 16:08:51 +0100 Subject: [PATCH 41/51] feat: improve text input type (#548) --- src/screens/InitWallet.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/screens/InitWallet.js b/src/screens/InitWallet.js index 6e60202ba..d921b84c9 100644 --- a/src/screens/InitWallet.js +++ b/src/screens/InitWallet.js @@ -326,6 +326,14 @@ class LoadWordsScreen extends React.Component { autoFocus onSubmitEditing={this.loadClicked} blurOnSubmit + inputMode='text' + secureTextEntry + autoCorrect={false} + autoComplete='off' + // ios only + spellCheck={false} + // android only + importantForAutofill='no' /> {this.state.words.length} From 74520c635a58e0e5e36dc2ef7e7e36d56ac3e702 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Tue, 27 Aug 2024 22:47:40 +0100 Subject: [PATCH 42/51] refactor: replace nano contract state call for history call (#549) * refactor: replace nano contract state call for history call * refactor: replace nano history call to getFullTxById --- locale/da/texts.po | 24 ++++++++++++++---------- locale/pt-br/texts.po | 26 +++++++++++++++----------- locale/ru-ru/texts.po | 24 ++++++++++++++---------- locale/texts.pot | 24 ++++++++++++++---------- src/sagas/nanoContract.js | 36 +++++++++++++++++++++++++++++------- 5 files changed, 86 insertions(+), 48 deletions(-) diff --git a/locale/da/texts.po b/locale/da/texts.po index 82d5593ec..613800025 100644 --- a/locale/da/texts.po +++ b/locale/da/texts.po @@ -1013,39 +1013,43 @@ msgstr "" msgid "Contract successfully registered." msgstr "" -#: src/sagas/nanoContract.js:44 +#: src/sagas/nanoContract.js:45 msgid "Nano Contract already registered." msgstr "" -#: src/sagas/nanoContract.js:45 +#: src/sagas/nanoContract.js:46 msgid "Wallet is not ready yet to register a Nano Contract." msgstr "" -#: src/sagas/nanoContract.js:46 +#: src/sagas/nanoContract.js:47 msgid "The informed address does not belong to the wallet." msgstr "" -#: src/sagas/nanoContract.js:47 +#: src/sagas/nanoContract.js:48 msgid "Nano Contract not found." msgstr "" -#: src/sagas/nanoContract.js:48 -msgid "Error while trying to get Nano Contract state." +#: src/sagas/nanoContract.js:49 +msgid "Error while trying to register Nano Contract." msgstr "" -#: src/sagas/nanoContract.js:49 +#: src/sagas/nanoContract.js:50 +msgid "Invalid transaction to register as Nano Contract." +msgstr "" + +#: src/sagas/nanoContract.js:51 msgid "Blueprint not found." msgstr "" -#: src/sagas/nanoContract.js:50 +#: src/sagas/nanoContract.js:52 msgid "Couldn't get Blueprint info." msgstr "" -#: src/sagas/nanoContract.js:51 +#: src/sagas/nanoContract.js:53 msgid "Nano Contract not registered." msgstr "" -#: src/sagas/nanoContract.js:52 +#: src/sagas/nanoContract.js:54 msgid "Error while trying to download Nano Contract transactions history." msgstr "" diff --git a/locale/pt-br/texts.po b/locale/pt-br/texts.po index 2b8484689..855443bbf 100644 --- a/locale/pt-br/texts.po +++ b/locale/pt-br/texts.po @@ -1041,39 +1041,43 @@ msgstr "Transação do Nano Contract" msgid "Contract successfully registered." msgstr "Nano Contract registrado com sucesso." -#: src/sagas/nanoContract.js:44 +#: src/sagas/nanoContract.js:45 msgid "Nano Contract already registered." msgstr "Nano Contract já registrado." -#: src/sagas/nanoContract.js:45 +#: src/sagas/nanoContract.js:46 msgid "Wallet is not ready yet to register a Nano Contract." msgstr "A wallet não está pronta ainda para registrar Nano Contracts." -#: src/sagas/nanoContract.js:46 +#: src/sagas/nanoContract.js:47 msgid "The informed address does not belong to the wallet." msgstr "O endereço informado não pertence a esta carteira." -#: src/sagas/nanoContract.js:47 +#: src/sagas/nanoContract.js:48 msgid "Nano Contract not found." msgstr "Nano Contract não encontrado" -#: src/sagas/nanoContract.js:48 -msgid "Error while trying to get Nano Contract state." -msgstr "Erro ao obter o estado do Nano Contract." - #: src/sagas/nanoContract.js:49 +msgid "Error while trying to register Nano Contract." +msgstr "Erro ao registrar Nano Contract." + +#: src/sagas/nanoContract.js:50 +msgid "Invalid transaction to register as Nano Contract." +msgstr "Transação de Nano Contract inválida." + +#: src/sagas/nanoContract.js:51 msgid "Blueprint not found." msgstr "Blueprint não encontrado." -#: src/sagas/nanoContract.js:50 +#: src/sagas/nanoContract.js:52 msgid "Couldn't get Blueprint info." msgstr "Não foi possível carregar informações do Blueprint." -#: src/sagas/nanoContract.js:51 +#: src/sagas/nanoContract.js:53 msgid "Nano Contract not registered." msgstr "Nano Contract não registrado." -#: src/sagas/nanoContract.js:52 +#: src/sagas/nanoContract.js:54 msgid "Error while trying to download Nano Contract transactions history." msgstr "Error ao fazer download do histórico de transações do Nano Contract." diff --git a/locale/ru-ru/texts.po b/locale/ru-ru/texts.po index f38b2fa8a..dbd40697f 100644 --- a/locale/ru-ru/texts.po +++ b/locale/ru-ru/texts.po @@ -1017,39 +1017,43 @@ msgstr "" msgid "Contract successfully registered." msgstr "" -#: src/sagas/nanoContract.js:44 +#: src/sagas/nanoContract.js:45 msgid "Nano Contract already registered." msgstr "" -#: src/sagas/nanoContract.js:45 +#: src/sagas/nanoContract.js:46 msgid "Wallet is not ready yet to register a Nano Contract." msgstr "" -#: src/sagas/nanoContract.js:46 +#: src/sagas/nanoContract.js:47 msgid "The informed address does not belong to the wallet." msgstr "" -#: src/sagas/nanoContract.js:47 +#: src/sagas/nanoContract.js:48 msgid "Nano Contract not found." msgstr "" -#: src/sagas/nanoContract.js:48 -msgid "Error while trying to get Nano Contract state." +#: src/sagas/nanoContract.js:49 +msgid "Error while trying to register Nano Contract." msgstr "" -#: src/sagas/nanoContract.js:49 +#: src/sagas/nanoContract.js:50 +msgid "Invalid transaction to register as Nano Contract." +msgstr "" + +#: src/sagas/nanoContract.js:51 msgid "Blueprint not found." msgstr "" -#: src/sagas/nanoContract.js:50 +#: src/sagas/nanoContract.js:52 msgid "Couldn't get Blueprint info." msgstr "" -#: src/sagas/nanoContract.js:51 +#: src/sagas/nanoContract.js:53 msgid "Nano Contract not registered." msgstr "" -#: src/sagas/nanoContract.js:52 +#: src/sagas/nanoContract.js:54 msgid "Error while trying to download Nano Contract transactions history." msgstr "" diff --git a/locale/texts.pot b/locale/texts.pot index 5e738173f..573f758d2 100644 --- a/locale/texts.pot +++ b/locale/texts.pot @@ -1010,39 +1010,43 @@ msgstr "" msgid "Contract successfully registered." msgstr "" -#: src/sagas/nanoContract.js:44 +#: src/sagas/nanoContract.js:45 msgid "Nano Contract already registered." msgstr "" -#: src/sagas/nanoContract.js:45 +#: src/sagas/nanoContract.js:46 msgid "Wallet is not ready yet to register a Nano Contract." msgstr "" -#: src/sagas/nanoContract.js:46 +#: src/sagas/nanoContract.js:47 msgid "The informed address does not belong to the wallet." msgstr "" -#: src/sagas/nanoContract.js:47 +#: src/sagas/nanoContract.js:48 msgid "Nano Contract not found." msgstr "" -#: src/sagas/nanoContract.js:48 -msgid "Error while trying to get Nano Contract state." +#: src/sagas/nanoContract.js:49 +msgid "Error while trying to register Nano Contract." msgstr "" -#: src/sagas/nanoContract.js:49 +#: src/sagas/nanoContract.js:50 +msgid "Invalid transaction to register as Nano Contract." +msgstr "" + +#: src/sagas/nanoContract.js:51 msgid "Blueprint not found." msgstr "" -#: src/sagas/nanoContract.js:50 +#: src/sagas/nanoContract.js:52 msgid "Couldn't get Blueprint info." msgstr "" -#: src/sagas/nanoContract.js:51 +#: src/sagas/nanoContract.js:53 msgid "Nano Contract not registered." msgstr "" -#: src/sagas/nanoContract.js:52 +#: src/sagas/nanoContract.js:54 msgid "Error while trying to download Nano Contract transactions history." msgstr "" diff --git a/src/sagas/nanoContract.js b/src/sagas/nanoContract.js index 7f7df2a0b..46615029a 100644 --- a/src/sagas/nanoContract.js +++ b/src/sagas/nanoContract.js @@ -8,6 +8,7 @@ import { ncApi, addressUtils, + nanoUtils, } from '@hathor/wallet-lib'; import { takeEvery, @@ -45,7 +46,8 @@ export const failureMessage = { walletNotReadyError: t`Wallet is not ready yet to register a Nano Contract.`, addressNotMine: t`The informed address does not belong to the wallet.`, nanoContractStateNotFound: t`Nano Contract not found.`, - nanoContractStateFailure: t`Error while trying to get Nano Contract state.`, + nanoContractFailure: t`Error while trying to register Nano Contract.`, + nanoContractInvalid: t`Invalid transaction to register as Nano Contract.`, blueprintInfoNotFound: t`Blueprint not found.`, blueprintInfoFailure: t`Couldn't get Blueprint info.`, notRegistered: t`Nano Contract not registered.`, @@ -111,15 +113,35 @@ export function* registerNanoContract({ payload }) { return; } - let ncState = null; + let tx; try { - ncState = yield call([ncApi, ncApi.getNanoContractState], ncId); + const response = yield call([wallet, wallet.getFullTxById], ncId); + tx = response.tx; + } catch (error) { + log.debug('Fail registering Nano Contract while getting full transaction by ID.'); + yield put(nanoContractRegisterFailure(failureMessage.nanoContractFailure)); + return; + } + + if (!nanoUtils.isNanoContractCreateTx(tx)) { + log.debug('Fail registering Nano Contract because transaction is not calling initialize.'); + yield put(nanoContractRegisterFailure(failureMessage.nanoContractInvalid)); + return; + } + const { nc_blueprint_id: blueprintId } = tx; + + let blueprintName = null; + try { + const blueprintInfo = yield call([ncApi, ncApi.getBlueprintInformation], blueprintId); + blueprintName = blueprintInfo.name; + // Also set blueprint on store + yield put(nanoContractBlueprintInfoSuccess(blueprintId, blueprintInfo)); } catch (error) { if (error instanceof NanoRequest404Error) { - yield put(nanoContractRegisterFailure(failureMessage.nanoContractStateNotFound)); + yield put(nanoContractRegisterFailure(failureMessage.blueprintInfoNotFound)); } else { log.error('Error while registering Nano Contract.', error); - yield put(nanoContractRegisterFailure(failureMessage.nanoContractStateFailure)); + yield put(nanoContractRegisterFailure(failureMessage.blueprintInfoFailure)); } return; } @@ -127,8 +149,8 @@ export function* registerNanoContract({ payload }) { const nc = { address, ncId, - blueprintName: ncState.blueprint_name, - blueprintId: ncState.blueprint_id, + blueprintId, + blueprintName, }; yield call(wallet.storage.registerNanoContract.bind(wallet.storage), ncId, nc); From 5a76d57550620c3986ca50b654d533dfcb4ad56d Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Sat, 31 Aug 2024 22:08:02 +0100 Subject: [PATCH 43/51] feat: parse argument value by custom type (#537) * feat: parse argument value by custom type * chore: update wallet-lib to v1.10.0 * feat: show registered token symbol for TokenUid arg type * feat: add support to Address type when parsing arguments * refactor: extract dependencies to parent component * chore: update package-lock.json * feat: add support to extra argument and remove Address parse * refactor: remove fallback for extra arguments * review: apply suggestions * chore: add logger to utils --- package-lock.json | 8 +- package.json | 2 +- .../NanoContract/NanoContractMethodArgs.js | 79 +++++++++++++++++-- src/utils.js | 30 +++++++ 4 files changed, 109 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index b2fa08760..7a4f1f312 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@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.9.0", + "@hathor/wallet-lib": "1.10.0", "@notifee/react-native": "5.7.0", "@react-native-async-storage/async-storage": "1.19.0", "@react-native-firebase/app": "16.7.0", @@ -2550,9 +2550,9 @@ } }, "node_modules/@hathor/wallet-lib": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@hathor/wallet-lib/-/wallet-lib-1.9.0.tgz", - "integrity": "sha512-8m6sr/PObnRoCsmalV2AqKO+tMPyzI/Lw/eJ0KgtaZSn7drBeFlDRhYyEilxpFqq9BCzvx18i8tXAudFoW2OUQ==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@hathor/wallet-lib/-/wallet-lib-1.10.0.tgz", + "integrity": "sha512-oxQoxnwZNDrgjnvblx0ipVOxMNeM8W2PJkQpyzZJyl1QpRyhqHqqyYD0iSn1bhQEq8C1IRoSrvjsd3Af5eHdWQ==", "license": "MIT", "dependencies": { "abstract-level": "1.0.4", diff --git a/package.json b/package.json index fc695b30a..ad37671ec 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "@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.9.0", + "@hathor/wallet-lib": "1.10.0", "@notifee/react-native": "5.7.0", "@react-native-async-storage/async-storage": "1.19.0", "@react-native-firebase/app": "16.7.0", diff --git a/src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js b/src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js index 1c7b945d8..818fbf3ca 100644 --- a/src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js +++ b/src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js @@ -14,12 +14,14 @@ import { import { useDispatch, useSelector } from 'react-redux'; import { t } from 'ttag'; import { get } from 'lodash'; +import { Network } from '@hathor/wallet-lib'; import { COLORS } from '../../../styles/themes'; import { commonStyles } from '../theme'; import { onExceptionCaptured } from '../../../actions'; -import { NANOCONTRACT_BLUEPRINTINFO_STATUS as STATUS } from '../../../constants'; +import { DEFAULT_TOKEN, NANOCONTRACT_BLUEPRINTINFO_STATUS as STATUS } from '../../../constants'; import { FeedbackContent } from '../../FeedbackContent'; import Spinner from '../../Spinner'; +import { getTimestampFormat, parseScriptData, renderValue } from '../../../utils'; /** * Get method info from registered blueprint data. @@ -63,10 +65,16 @@ export const NanoContractMethodArgs = ({ blueprintId, method, ncArgs }) => { return null; } const dispatch = useDispatch(); + const network = useSelector((state) => new Network(state.networkSettings.network)); + const tokens = useSelector((state) => state.tokens); const blueprintInfo = useSelector((state) => state.nanoContract.blueprint[blueprintId]); // It results a in a list of entries like: - // >>> [['oracle_script', 'abc'], ['token_uid', '00'], ['date_last_bet', 123]] + // >>>[ + // >>> ['oracle_script', 'abc', 'TxOutputScript'], + // >>> ['token_uid', '00', 'TokenUid'], + // >>> ['date_last_bet', 123, 'Timestamp'] + // >>>] // or a fallback like: // >>> [['Position 0', 'abc'], ['Position 1', '00'], ['Position 2', 123]] const argEntries = useMemo(() => { @@ -76,7 +84,7 @@ export const NanoContractMethodArgs = ({ blueprintId, method, ncArgs }) => { const methodInfo = getMethodInfoFromBlueprint(blueprintInfo, method); if (methodInfo) { - return ncArgs.map((arg, idx) => [methodInfo.args[idx].name, arg]); + return ncArgs.map((arg, idx) => [methodInfo.args[idx].name, arg, methodInfo.args[idx].type]); } // Send this condition to sentry because it should never happen. @@ -109,7 +117,7 @@ export const NanoContractMethodArgs = ({ blueprintId, method, ncArgs }) => { && ( - {argEntries.map(([argName, argValue]) => ( + {argEntries.map(([argName, argValue, argType]) => ( { {argName} - {argValue} + + + ))} @@ -129,6 +144,60 @@ export const NanoContractMethodArgs = ({ blueprintId, method, ncArgs }) => { ) }; +/** + * Component responsible to render the appropriate format for the value + * taking in consideration the type. + * + * Remarks + * The values received here when derived from 'byte' type like + * 'TxOutputScript', 'TokenUid' and 'VertexId' are already in their + * hexadecimal format. + * + * Values of type 'Address', which also derives from 'byte' are + * in base58 format. + * + * Values of type 'SignedData[Result]' arrives here in presentation + * format. + * + * @param {Object} props + * @param {string} props.type An argument type + * @param {string} props.value An argument value + * @param {Object} props.network A network object + * @param {Object} props.tokens A map of registered tokens + */ +const ArgValue = ({ type, value, network, tokens }) => { + if (type === 'Amount') { + return renderValue(value); + } + + if (type === 'Timestamp') { + return getTimestampFormat(value); + } + + if (type === 'TxOutputScript') { + const parsedScript = parseScriptData(value, network); + if (parsedScript && parsedScript.getType() === 'data') { + return parsedScript.data; + } + + if (parsedScript) { + return parsedScript.address.base58; + } + } + + if (type === 'TokenUid') { + if (value === DEFAULT_TOKEN.uid) { + return DEFAULT_TOKEN.symbol; + } + + if (value in tokens) { + return tokens[value].symbol; + } + } + + return value; +}; + const styles = StyleSheet.create({ argPosition: { flexShrink: 10, diff --git a/src/utils.js b/src/utils.js index 52a9a4230..51c316f97 100644 --- a/src/utils.js +++ b/src/utils.js @@ -18,6 +18,9 @@ import { KEYCHAIN_USER, NETWORK_MAINNET, NANO_CONTRACT_FEATURE_TOGGLE } from './ import { STORE } from './store'; import { TxHistory } from './models'; import { COLORS, STYLE } from './styles/themes'; +import { logger } from './logger'; + +const log = logger('utils'); export const Strong = (props) => {props.children}; @@ -497,3 +500,30 @@ export function hasError(invalidModel) { .values({ ...invalidModel }) .reduce((_hasError, currValue) => _hasError || !isEmpty(currValue), false); } + +/** + * Parses a script data to return an instance of script type. + * + * @example + * parseScriptData('P2PKH or P2SH script', networkObj); + * >>> { address, timelock } + * + * @example + * parseScriptData('Data script', networkObj); + * >>> { data } + * + * @param {string} scriptData A script in its hexadecimal format + * @param {Object} network A network object + * + * @return {P2PKH | P2SH | ScriptData | null} Parsed script object + */ +export const parseScriptData = (scriptData, network) => { + try { + const script = hathorLib.bufferUtils.hexToBuffer(scriptData); + return hathorLib.scriptsUtils.parseScript(script, network); + } catch (error) { + log.error('Error parsing script data.', error); + // Avoid throwing exception when we can't parse the script no matter the reason + return null; + } +} From c306c254e114cd6388d7ef42fce879f98f915f61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Abadesso?= Date: Tue, 3 Sep 2024 13:01:18 -0300 Subject: [PATCH 44/51] feat: rpc lib integration (#500) * feat: implement Nano Contract integration with Wallet Connect * feat: adapt new nc tx to initialize, set result and withdrawal feat: make ncAddress react to firstAddress change * feat: make ncAddress react to firstAddress the right way * feat: add name to method arguments * i18n: udpate files * i18n: update translations and fix some typos * review: apply suggestions and do some fixes * refactor: load of token details not refistered but used in actions * lint: comply with rules * chore: remove unused action * chore: add undeclared style * refactor: walletConnect.tokens to unregisteredTokens * chore: remove unused actions, reducer and saga effect * chore: improving feedback message * refactor: move requestTokens from walletConnect saga to tokens saga * refactor: rename effect requestTokens to requestUnregisteredTokens * feat: fire sentry error notification for method info not found * feat: improve title of Arguments component * chore: remove blueprint info action and reducer * feat: retrieve fallback arg entries when blueprint is loading * feat: add nft check to action token on Actions component * chore: apply suggestions and improve documentation on new nc tx request * lint: comply with rules * fix: check if blueprintInfo is not null * refactor: reanem style value to field * type: recursar * i18n: update translation files * feat: add blueprint download to the tx loading flag and a redundancy * fix: FeedbackContent margin style Remarks: The `marginVertical` and `marginHorizontal` are not being applied. Probably this is a bug in ReactNative. * lint: comply with rules * fix: typo * chore: added dependencies and shims needed by walletconnect * refactor: using object destructuring in shims * chore: rebase me * refactor: rootSaga now restarts failed sagas and sends errors to sentry * feat: added actions and reducers * refactor: NewNanoContract modal handling in wallet connect global modal * feat: handling send nano contract tx triggers * refactor: removed unused import * refactor: typo * refactor: unused log * refactor: refactored SignMessageRequest to use new modal format * chore: rebase me, adding mocks to dashboard * refactor: removed function from redux * refactor: convert PinScreen to a function component * refactor: pin screen now dispatches an action (as a param) * fix: invalid feedback modal on pin screen * Revert "fix: invalid feedback modal on pin screen" This reverts commit 3a1615531e8df180a13fd5696fc785e55822e6b1. * Revert "refactor: pin screen now dispatches an action (as a param)" This reverts commit 9aebf8382ad2a470f2c4589c34cbe161ee6fbf1c. * Revert "fix: invalid feedback modal on pin screen" This reverts commit 3a1615531e8df180a13fd5696fc785e55822e6b1. * Revert "refactor: convert PinScreen to a function component" This reverts commit 69473153c57efed4451c43d4e517508d43203841. * refactor: removed mocks from dashboard * chore: removed useless log * fix: added onDismiss as a dependency to useCallback as it might change * refactor: re-enable wallet-service, stop initializing wallet-connect when its enabled and removed cyclic dependency * docs: added docstring to promptHandler * docs: added docstring to requests * refactor: removed unused imports on Dashboard * chore: update locale * chore: fix broken rebase * refactor: removed navigation from useEffect deps * docs: added hathor header and comment explaining createStore deprecation * feat: added log when wallet-service is enabled and wallet-connect tries to init * refactor: added retry strategy * refactor: wallet-connect will always fork the init, so it restarts successfully * refactor: nano contract saga is critical * feat: added log for sagas crash and max retries * fix: log being called incorrectly * feat: logging when wallet-connect is initialzed * fix: added missing import * fix: broken rebase fixes * chore: updated translations * refactor: removed fuzzy translations * refactor: removed unused import on NanoContactTransactionHeader * feat: logging error on root saga * chore: removed walletconnect libraries from package * feat: added hathor-rpc-handler from the deployed lib * chore: changed CI version to 18x and 20x * chore: updated enable walletconnect script * refactor: removed error logging on NanoContractMethodArgs * chore: removed local library from package-lock.json * refactor: removed unused option on PinScreen * fix: added missing import * chore: removed comment on pt-br texts * chore: updated enable-walletconnect.patch * refactor: added missing attribute from metadata * chore: updated package-lock with wallet-lib version * chore: updated locale * chore: restored package-lock.json * refactor: NanoContact -> NanoContract * refactor: removed baseStyles prop from WalletConnectModal * refactor: removed package.json changes in walletconnect diff * refactor: reseting nano contract state on unmount * refactor: acceptedNc -> acceptedReq * refactor: onReadMore is now on warn component * refactor: .. -> . * refactor: removed unused styles from NewNanoContractTransctionModal * chore: fix inconsistent return * refactor: added missing translations * chore: removed .mo * refactor: updated patch to remove duplicated shim and podfile.lock --------- Co-authored-by: Alex Ruzenhack --- .github/workflows/main.yml | 2 +- enable-walletconnect.patch | 78 ++-- ios/Podfile.lock | 2 +- locale/da/texts.po | 121 +++--- locale/pt-br/texts.po | 154 ++++---- locale/ru-ru/texts.po | 121 +++--- locale/texts.pot | 120 +++--- package-lock.json | 81 ++++ package.json | 5 +- src/App.js | 6 +- src/actions.js | 38 ++ .../NanoContractTransactionHeader.js | 2 +- .../NanoContract/NanoContractExecInfo.js | 2 +- .../NanoContract/NanoContractMethodArgs.js | 12 +- .../NewNanoContractTransactionModal.js | 129 ++----- .../NewNanoContractTransactionRequest.js | 14 +- .../WalletConnect/SignMessageModal.js | 80 ++-- .../WalletConnect/SignMessageRequest.js | 126 +++++++ .../WalletConnect/WalletConnectModal.js | 10 +- .../WalletConnect/WarnDisclaimer.js | 83 ++++ src/reducers/reducer.init.js | 15 +- src/reducers/reducer.js | 28 ++ src/sagas/featureToggle.js | 1 - src/sagas/index.js | 66 +++- src/sagas/wallet.js | 3 +- src/sagas/walletConnect.js | 355 +++++++++++++----- src/screens/PinScreen.js | 1 + .../WalletConnect/SignMessageRequestScreen.js | 45 +++ walletconnect.sh | 13 + 29 files changed, 1200 insertions(+), 513 deletions(-) create mode 100644 src/components/WalletConnect/SignMessageRequest.js create mode 100644 src/components/WalletConnect/WarnDisclaimer.js create mode 100644 src/screens/WalletConnect/SignMessageRequestScreen.js diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3d13c786c..08237ff46 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -26,7 +26,7 @@ jobs: with: node-version: ${{ matrix.node-version }} - name: Install dependencies - run: npm install + run: npm ci - name: Install gettext run: sudo apt-get install gettext - name: Check version diff --git a/enable-walletconnect.patch b/enable-walletconnect.patch index 0b8141763..a28e7a5fe 100644 --- a/enable-walletconnect.patch +++ b/enable-walletconnect.patch @@ -1,53 +1,52 @@ -diff --git a/package.json b/package.json -index b5a16f3..8c13347 100644 ---- a/package.json -+++ b/package.json -@@ -26,6 +26,8 @@ - "@react-navigation/native": "6.1.7", - "@react-navigation/stack": "6.3.17", - "@sentry/react-native": "^5.6.0", -+ "@walletconnect/core": "^2.10.2", -+ "@walletconnect/web3wallet": "^1.9.2", - "assert": "^2.0.0", - "buffer": "^4.9.2", - "console-browserify": "^1.2.0", -@@ -67,6 +69,7 @@ - "redux-saga": "^1.2.0", - "redux-thunk": "^2.4.1", - "stream-browserify": "^1.0.0", -+ "text-encoding": "^0.7.0", - "ttag": "^1.7.24", - "unleash-proxy-client": "^1.11.0", - "url": "^0.11.0" diff --git a/shim.js b/shim.js -index 73c130d..10d894b 100644 +index 73c130d..deae975 100644 --- a/shim.js +++ b/shim.js -@@ -26,3 +26,8 @@ if (typeof localStorage !== 'undefined') { +@@ -26,3 +26,20 @@ if (typeof localStorage !== 'undefined') { // If using the crypto shim, uncomment the following line to ensure // crypto is loaded first, so it can populate global.crypto require('crypto') -+const TextEncoder = require('text-encoding').TextEncoder; -+const TextDecoder = require('text-encoding').TextDecoder; ++ ++const { TextEncoder, TextDecoder } = require('text-encoding'); + +global.TextDecoder = TextDecoder; +global.TextEncoder = TextEncoder; ++ ++if (typeof btoa === 'undefined') { ++ global.btoa = function (str) { ++ return Buffer.from(str, 'binary').toString('base64'); ++ }; ++} ++ ++if (typeof atob === 'undefined') { ++ global.atob = function (b64Encoded) { ++ return Buffer.from(b64Encoded, 'base64').toString('binary'); ++ }; ++} diff --git a/src/sagas/walletConnect.js b/src/sagas/walletConnect.js -index 5e954de..39ff15b 100644 +index 160cb11..07ccb34 100644 --- a/src/sagas/walletConnect.js +++ b/src/sagas/walletConnect.js -@@ -61,6 +61,8 @@ import { +@@ -45,6 +45,7 @@ + * loaded. + */ + ++import '@walletconnect/react-native-compat'; + import { + call, + fork, +@@ -62,6 +63,8 @@ import { } from 'redux-saga/effects'; import { eventChannel } from 'redux-saga'; import { get, values } from 'lodash'; +import { Core } from '@walletconnect/core'; +import { Web3Wallet } from '@walletconnect/web3wallet'; - - import { WalletConnectModalTypes } from '../components/WalletConnect/WalletConnectModal'; import { -@@ -95,12 +97,6 @@ const ERROR_CODES = { - INVALID_PAYLOAD: 5003, + TriggerTypes, + TriggerResponseTypes, +@@ -100,12 +103,6 @@ const AVAILABLE_METHODS = { }; + const AVAILABLE_EVENTS = []; -// We're mocking it here because we don't want to add the walletconnect -// libraries in our production build. If you really want to add it, just run the @@ -55,6 +54,21 @@ index 5e954de..39ff15b 100644 -const Core = class {}; -const Web3Wallet = class {}; - - function* isWalletConnectEnabled() { + /** + * Those are the only ones we are currently using, extracted from + * https://docs.walletconnect.com/2.0/specs/clients/sign/error-codes +@@ -118,13 +115,10 @@ const ERROR_CODES = { + INVALID_PAYLOAD: 5003, + }; + +-function isWalletConnectEnabled() { +- return false; +- /* ++function* isWalletConnectEnabled() { const walletConnectEnabled = yield call(checkForFeatureFlag, WALLET_CONNECT_FEATURE_TOGGLE); + return walletConnectEnabled; +- */ + } + + function* init() { diff --git a/ios/Podfile.lock b/ios/Podfile.lock index a92cc1d68..676a0a0a5 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -874,4 +874,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: c422aedd0c3b236578801088c8afef28030c3c83 -COCOAPODS: 1.13.0 +COCOAPODS: 1.15.2 diff --git a/locale/da/texts.po b/locale/da/texts.po index 613800025..82101d472 100644 --- a/locale/da/texts.po +++ b/locale/da/texts.po @@ -228,7 +228,7 @@ msgstr "Du har ${ amountAvailableText } HTR tilgængelig" #: 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/InitWallet.js:349 src/screens/SendAddressInput.js:66 #: src/screens/SendAmountInput.js:185 msgid "Next" msgstr "Næste" @@ -433,7 +433,7 @@ msgid "Enter your seed words separated by space" msgstr "Indtast dine seed-ord adskilt med mellemrum" #: src/components/NanoContract/NanoContractDetails.js:238 -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:289 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:293 #: src/screens/LoadHistoryScreen.js:51 src/screens/LoadWalletErrorScreen.js:20 #: src/screens/NanoContract/NanoContractRegisterScreen.js:168 msgid "Try again" @@ -551,7 +551,8 @@ msgstr "Indtast din pinkode " msgid "Unlock Hathor Wallet" msgstr "Lås Hathor-wallet op" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:84 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:72 +#: src/components/WalletConnect/SignMessageModal.js:75 #: src/screens/PinScreen.js:265 #: src/screens/WalletConnect/WalletConnectList.js:125 msgid "Cancel" @@ -712,6 +713,7 @@ msgstr "Din overførsel af **${ _this.amountAndToken }** er bekræftet" #: src/components/NanoContract/EditAddressModal.js:60 #: src/components/NanoContract/SelectAddressModal.js:117 +#: src/components/WalletConnect/SignMessageRequest.js:40 #: src/screens/SendConfirmScreen.js:161 msgid "Address" msgstr "Adresse" @@ -809,11 +811,15 @@ msgstr "Jeg vil afregistrere token **${ tokenLabel }**" msgid "Unregister token" msgstr "Afregistrer token" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:68 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:56 #: src/screens/WalletConnect/NewNanoContractTransactionScreen.js:24 msgid "New Nano Contract Transaction" msgstr "" +#: src/screens/WalletConnect/SignMessageRequestScreen.js:25 +msgid "Sign Message Request" +msgstr "" + #: src/screens/WalletConnect/WalletConnectList.js:33 msgid "There was an error connecting. Please try again later." msgstr "" @@ -971,7 +977,7 @@ msgstr "" #: src/components/NanoContract/NanoContractDetails.js:202 #: src/components/NanoContract/SelectAddressModal.js:105 -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:209 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:213 #: src/screens/NanoContract/NanoContractRegisterScreen.js:184 msgid "Loading" msgstr "" @@ -1103,21 +1109,21 @@ msgstr "" msgid "Error loading the details of some tokens." msgstr "" -#: src/sagas/wallet.js:781 +#: src/sagas/wallet.js:780 msgid "Wallet is not ready to load addresses." msgstr "" #. This will show the message in the feedback content at SelectAddressModal -#: src/sagas/wallet.js:797 +#: src/sagas/wallet.js:796 msgid "There was an error while loading wallet addresses. Try again." msgstr "" -#: src/sagas/wallet.js:807 +#: src/sagas/wallet.js:806 msgid "Wallet is not ready to load the first address." msgstr "" #. This will show the message in the feedback content -#: src/sagas/wallet.js:823 +#: src/sagas/wallet.js:822 msgid "There was an error while loading first wallet address. Try again." msgstr "" @@ -1332,14 +1338,51 @@ msgid "" "security step to protect your data from potential phishing risks." msgstr "" -#: src/components/WalletConnect/SignMessageModal.js:50 -msgid "Sign this message?" +#: src/components/WalletConnect/SignMessageModal.js:59 +msgid "New Sign Message Request" +msgstr "" + +#: src/components/WalletConnect/SignMessageModal.js:63 +msgid "You have received a new Sign Message Request. Please" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:62 +#: src/components/WalletConnect/SignMessageModal.js:65 +msgid "carefully review the details" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:64 +#: src/components/WalletConnect/SignMessageModal.js:67 +msgid "before deciding to accept or decline." msgstr "" -#: src/components/WalletConnect/SignMessageModal.js:57 +#: src/components/WalletConnect/SignMessageModal.js:71 +msgid "Review Sign Message Request details" +msgstr "" + +#: src/components/WalletConnect/SignMessageRequest.js:35 +msgid "Message to sign" +msgstr "" + +#: src/components/WalletConnect/SignMessageRequest.js:45 +msgid "Address Path" +msgstr "" + +#: src/components/WalletConnect/SignMessageRequest.js:81 +msgid "Accept Request" +msgstr "" + +#: src/components/WalletConnect/SignMessageRequest.js:85 +msgid "Decline Request" +msgstr "" + +#: src/components/WalletConnect/WarnDisclaimer.js:21 msgid "" -"By clicking approve, you will sign the requested message using the first " -"address derived from your root key on the m/44'/280'/0'/0/0 derivation path." +"Caution: There are risks associated with signing dapp transaction requests." +msgstr "" + +#: src/components/WalletConnect/WarnDisclaimer.js:27 +msgid "Read More." msgstr "" #: src/components/WalletConnect/NanoContract/DappContainer.js:41 @@ -1408,84 +1451,67 @@ msgstr "" msgid "Couldn't determine address, select one" msgstr "" -#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:50 +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:49 #, javascript-format msgid "Position ${ idx }" msgstr "" -#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:98 +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:91 msgid "Arguments" msgstr "" -#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:103 +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:96 msgid "Loading arguments." msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:72 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:60 msgid "You have received a new Nano Contract Transaction. Please" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:74 -msgid "carefully review the details" -msgstr "" - -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:76 -msgid "before deciding to accept or decline." -msgstr "" - -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:80 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:68 msgid "Review transaction details" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:98 -msgid "" -"Caution: There are risks associated with signing dapp transaction requests." -msgstr "" - -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:104 -msgid "Read More." -msgstr "" - -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:191 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:195 msgid "Nano Contract Not Found" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:192 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:196 msgid "" "The Nano Contract requested is not registered. First register the Nano " "Contract to interact with it." msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:195 -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:242 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:199 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:246 msgid "Decline Transaction" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:210 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:214 msgid "Loading transaction information." msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:238 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:242 msgid "Accept Transaction" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:252 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:256 msgid "Sending transaction" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:253 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:257 msgid "Please wait." msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:279 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:283 msgid "Transaction successfully sent." msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:281 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:285 msgid "Ok, close" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:287 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:291 msgid "Error while sending transaction." msgstr "" @@ -1556,7 +1582,6 @@ msgid "See full transaction details on Public Explorer." msgstr "" #: src/components/NanoContract/NanoContractTransactionActionListItem.js:108 -#, javascript-format msgid "Deposit ${ tokenSymbol }" msgstr "" diff --git a/locale/pt-br/texts.po b/locale/pt-br/texts.po index 855443bbf..fd571ad06 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.3.1\n" +"X-Generator: Poedit 3.4.4\n" #. This should never happen! #: src/models.js:85 @@ -68,7 +68,7 @@ msgstr "Endereço inválido" #. * #. * @example #. * You have received 10 T2, 5 T1 and 2 other token on a new transaction. -#. +#. #: src/workers/pushNotificationHandler.js:92 #, javascript-format msgid "" @@ -87,7 +87,7 @@ msgstr[1] "" #. * #. * @example #. * You have received 10 T2 and 5 T1 on a new transaction. -#. +#. #: src/workers/pushNotificationHandler.js:105 #, javascript-format msgid "" @@ -99,7 +99,7 @@ msgstr "" #. * #. * @example #. * You have received 10 T2 on a new transaction. -#. +#. #: src/workers/pushNotificationHandler.js:113 msgid "You have received ${ _firstToken2 } on a new transaction." msgstr "Você recebeu ${ _firstToken2 } em uma nova transação." @@ -236,7 +236,7 @@ msgstr "Você tem ${ amountAvailableText } HTR disponíveis" #: 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/InitWallet.js:349 src/screens/SendAddressInput.js:66 #: src/screens/SendAmountInput.js:185 msgid "Next" msgstr "Próximo" @@ -309,7 +309,7 @@ msgstr "DETALHES DO TOKEN" #. * #. * name {string} token name -#. +#. #: src/screens/CreateTokenName.js:26 msgid "0/30 characters" msgstr "0/30 caracteres" @@ -444,7 +444,7 @@ msgid "Enter your seed words separated by space" msgstr "Digite suas palavras separadas por espaços" #: src/components/NanoContract/NanoContractDetails.js:238 -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:289 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:293 #: src/screens/LoadHistoryScreen.js:51 src/screens/LoadWalletErrorScreen.js:20 #: src/screens/NanoContract/NanoContractRegisterScreen.js:168 msgid "Try again" @@ -517,7 +517,7 @@ msgstr "Registro do Nano Contract" #: src/screens/NanoContractRegisterQrCodeScreen.js:63 msgid "Scan the nano contract ID QR code" -msgstr "Leia o QR code do Nano Contract ID" +msgstr "Escaneie o QR code do ID do Nano Contract" #: src/screens/PaymentRequestDetail.js:88 #, javascript-format @@ -564,7 +564,8 @@ msgstr "Digite seu PIN " msgid "Unlock Hathor Wallet" msgstr "Desbloqueie sua Hathor Wallet" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:84 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:72 +#: src/components/WalletConnect/SignMessageModal.js:75 #: src/screens/PinScreen.js:265 #: src/screens/WalletConnect/WalletConnectList.js:125 msgid "Cancel" @@ -594,7 +595,7 @@ msgstr "" #. * #. * index {number} Selected index of the tab bar -#. +#. #. eslint thinks routes is not used, but TabView uses it #. eslint-disable-next-line react/no-unused-state #: src/screens/Receive.js:31 @@ -728,6 +729,7 @@ msgstr "Sua transferência de **${ this.amountAndToken }** foi confirmada" #: src/components/NanoContract/EditAddressModal.js:60 #: src/components/NanoContract/SelectAddressModal.js:117 +#: src/components/WalletConnect/SignMessageRequest.js:40 #: src/screens/SendConfirmScreen.js:161 msgid "Address" msgstr "Endereço" @@ -827,11 +829,15 @@ msgstr "Eu quero desregistrar o token **${ tokenLabel }**" msgid "Unregister token" msgstr "Desregistrar token" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:68 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:56 #: src/screens/WalletConnect/NewNanoContractTransactionScreen.js:24 msgid "New Nano Contract Transaction" msgstr "Nova Transação de Nano Contract" +#: src/screens/WalletConnect/SignMessageRequestScreen.js:25 +msgid "Sign Message Request" +msgstr "Solicitação de Assinatura de Mensagem" + #: src/screens/WalletConnect/WalletConnectList.js:33 msgid "There was an error connecting. Please try again later." msgstr "" @@ -933,7 +939,7 @@ msgstr "" #: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:65 msgid "walletServiceWsUrl is required when walletServiceUrl is filled." msgstr "" -"walletServiceWsUrl é obrigatório quando walletServiceUrl está preenchido" +"walletServiceWsUrl é obrigatório quando walletServiceUrl está preenchido." #: src/screens/NetworkSettings/CustomNetworkSettingsScreen.js:232 msgid "Node URL" @@ -999,7 +1005,7 @@ msgstr "Erro ao carregar primeiro endereço da wallet" #: src/components/NanoContract/NanoContractDetails.js:202 #: src/components/NanoContract/SelectAddressModal.js:105 -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:209 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:213 #: src/screens/NanoContract/NanoContractRegisterScreen.js:184 msgid "Loading" msgstr "Carregando" @@ -1055,15 +1061,15 @@ msgstr "O endereço informado não pertence a esta carteira." #: src/sagas/nanoContract.js:48 msgid "Nano Contract not found." -msgstr "Nano Contract não encontrado" +msgstr "Nano Contract não encontrado." #: src/sagas/nanoContract.js:49 msgid "Error while trying to register Nano Contract." -msgstr "Erro ao registrar Nano Contract." +msgstr "Erro ao tentar registrar o Nano Contract." #: src/sagas/nanoContract.js:50 msgid "Invalid transaction to register as Nano Contract." -msgstr "Transação de Nano Contract inválida." +msgstr "Nano Contract inválido." #: src/sagas/nanoContract.js:51 msgid "Blueprint not found." @@ -1132,21 +1138,21 @@ msgstr "A wallet não está pronta ainda." msgid "Error loading the details of some tokens." msgstr "Ocorreu um erro durante o carregamento de detalhes de alguns tokens." -#: src/sagas/wallet.js:781 +#: src/sagas/wallet.js:780 msgid "Wallet is not ready to load addresses." msgstr "A wallet não está pronta para carregar os endereços." #. This will show the message in the feedback content at SelectAddressModal -#: src/sagas/wallet.js:797 +#: src/sagas/wallet.js:796 msgid "There was an error while loading wallet addresses. Try again." msgstr "Ocorreu um erro ao carregar os endereços da wallet. Tente novamente." -#: src/sagas/wallet.js:807 +#: src/sagas/wallet.js:806 msgid "Wallet is not ready to load the first address." msgstr "A wallet não está pronta para carregar o primeiro endereço." #. This will show the message in the feedback content -#: src/sagas/wallet.js:823 +#: src/sagas/wallet.js:822 msgid "There was an error while loading first wallet address. Try again." msgstr "" "Ocorreu um erro ao carregar o primeiro endereço da wallet. Tente novamente." @@ -1368,18 +1374,54 @@ msgstr "" "importante de segurança para proteger seus dados de potenciais riscos de " "phishing." -#: src/components/WalletConnect/SignMessageModal.js:50 -msgid "Sign this message?" -msgstr "Assinar essa mensagem?" +#: src/components/WalletConnect/SignMessageModal.js:59 +msgid "New Sign Message Request" +msgstr "Nova Solicitação de Assinatura de Mensagem" + +#: src/components/WalletConnect/SignMessageModal.js:63 +msgid "You have received a new Sign Message Request. Please" +msgstr "" +"Você recebeu uma nova Solicitação de Assinatura de Mensagem. Por favor," -#: src/components/WalletConnect/SignMessageModal.js:57 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:62 +#: src/components/WalletConnect/SignMessageModal.js:65 +msgid "carefully review the details" +msgstr "revise os detalhes com cuidado" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:64 +#: src/components/WalletConnect/SignMessageModal.js:67 +msgid "before deciding to accept or decline." +msgstr "antes de decidir aceitar ou recusar." + +#: src/components/WalletConnect/SignMessageModal.js:71 +msgid "Review Sign Message Request details" +msgstr "Revisar detalhes da Solicitação de Assinatura de Mensagem" + +#: src/components/WalletConnect/SignMessageRequest.js:35 +msgid "Message to sign" +msgstr "Mensagem para assinar" + +#: src/components/WalletConnect/SignMessageRequest.js:45 +msgid "Address Path" +msgstr "Path do Endereço" + +#: src/components/WalletConnect/SignMessageRequest.js:81 +msgid "Accept Request" +msgstr "Aceitar Solicitação" + +#: src/components/WalletConnect/SignMessageRequest.js:85 +msgid "Decline Request" +msgstr "Recusar Solicitação" + +#: src/components/WalletConnect/WarnDisclaimer.js:21 msgid "" -"By clicking approve, you will sign the requested message using the first " -"address derived from your root key on the m/44'/280'/0'/0/0 derivation path." +"Caution: There are risks associated with signing dapp transaction requests." msgstr "" -"Ao clicar em aprovar, você assinará a mensagem solicitada usando o primeiro " -"endereço derivado da sua chave raiz no caminho de derivação " -"m/44'/280'/0'/0/0." +"Cuidado: Há riscos associados a pedidos de assinatura de transação via dApp." + +#: src/components/WalletConnect/WarnDisclaimer.js:27 +msgid "Read More." +msgstr "Ler mais." #: src/components/WalletConnect/NanoContract/DappContainer.js:41 msgid "Review your transaction from this dApp" @@ -1447,51 +1489,33 @@ msgstr "Caller" msgid "Couldn't determine address, select one" msgstr "Não foi possível determinar um endereço, selecione um" -#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:50 +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:49 #, javascript-format msgid "Position ${ idx }" msgstr "Posição ${ idx }" -#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:98 +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:91 msgid "Arguments" msgstr "Argumentos" -#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:103 +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:96 msgid "Loading arguments." msgstr "Carregando argumentos." -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:72 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:60 msgid "You have received a new Nano Contract Transaction. Please" msgstr "" "Você recebeu um pedido para criar uma transação de Nano Contract. Por favor," -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:74 -msgid "carefully review the details" -msgstr "revise os detalhes com cuidado" - -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:76 -msgid "before deciding to accept or decline." -msgstr "antes de decidir aceitar ou recusar." - -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:80 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:68 msgid "Review transaction details" msgstr "Revisar detalhes da transação" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:98 -msgid "" -"Caution: There are risks associated with signing dapp transaction requests." -msgstr "" -"Cuidado: Há riscos associados a pedidos de assinatura de transação via dApp." - -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:104 -msgid "Read More." -msgstr "Ler mais." - -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:191 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:195 msgid "Nano Contract Not Found" msgstr "Nano Contract não encontrado" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:192 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:196 msgid "" "The Nano Contract requested is not registered. First register the Nano " "Contract to interact with it." @@ -1499,36 +1523,36 @@ msgstr "" "O Nano Contract solicitado não está registrado. Primeiro registre o Nano " "Contract para interagir com ele." -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:195 -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:242 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:199 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:246 msgid "Decline Transaction" msgstr "Recusar transação" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:210 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:214 msgid "Loading transaction information." msgstr "Carregando informações da transação." -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:238 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:242 msgid "Accept Transaction" msgstr "Aceitar transação" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:252 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:256 msgid "Sending transaction" msgstr "Enviando transação" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:253 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:257 msgid "Please wait." msgstr "Por favor, espere." -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:279 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:283 msgid "Transaction successfully sent." msgstr "Transação enviada com sucesso." -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:281 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:285 msgid "Ok, close" msgstr "Ok, fechar" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:287 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:291 msgid "Error while sending transaction." msgstr "Ocorreu um erro durante o envio da transação." @@ -1574,7 +1598,7 @@ msgstr "Carregar mais" #: src/components/NanoContract/NanoContractDetails.js:203 msgid "Loading Nano Contract transactions." -msgstr "Carregando transações do Nano Contract" +msgstr "Carregando transações do Nano Contract." #: src/components/NanoContract/NanoContractDetails.js:217 msgid "Nano Contract Transactions Error" @@ -1598,7 +1622,7 @@ msgstr "Nenhuma Action" #: src/components/NanoContract/NanoContractTransactionActionList.js:45 msgid "See full transaction details on Public Explorer." -msgstr "Ver todos os detalhes da transação no Explorer Público." +msgstr "Ver detalhes completos da transação no Explorer Público." #: src/components/NanoContract/NanoContractTransactionActionListItem.js:108 #, javascript-format diff --git a/locale/ru-ru/texts.po b/locale/ru-ru/texts.po index dbd40697f..72e211152 100644 --- a/locale/ru-ru/texts.po +++ b/locale/ru-ru/texts.po @@ -229,7 +229,7 @@ msgstr "У вас ${ amountAvailableText } HTR" #: 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/InitWallet.js:349 src/screens/SendAddressInput.js:66 #: src/screens/SendAmountInput.js:185 msgid "Next" msgstr "Далее" @@ -434,7 +434,7 @@ msgid "Enter your seed words separated by space" msgstr "Введите seed-фразу" #: src/components/NanoContract/NanoContractDetails.js:238 -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:289 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:293 #: src/screens/LoadHistoryScreen.js:51 src/screens/LoadWalletErrorScreen.js:20 #: src/screens/NanoContract/NanoContractRegisterScreen.js:168 msgid "Try again" @@ -553,7 +553,8 @@ msgstr "Введите свой PIN-код " msgid "Unlock Hathor Wallet" msgstr "Разблокировать Hathor Wallet" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:84 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:72 +#: src/components/WalletConnect/SignMessageModal.js:75 #: src/screens/PinScreen.js:265 #: src/screens/WalletConnect/WalletConnectList.js:125 msgid "Cancel" @@ -714,6 +715,7 @@ msgstr "Ваш перевод **${ this.amountAndToken }** был подтвер #: src/components/NanoContract/EditAddressModal.js:60 #: src/components/NanoContract/SelectAddressModal.js:117 +#: src/components/WalletConnect/SignMessageRequest.js:40 #: src/screens/SendConfirmScreen.js:161 msgid "Address" msgstr "Адрес" @@ -813,11 +815,15 @@ msgstr "Я хочу отменить регистрацию токена **${ to msgid "Unregister token" msgstr "Отменить регистрацию токена" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:68 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:56 #: src/screens/WalletConnect/NewNanoContractTransactionScreen.js:24 msgid "New Nano Contract Transaction" msgstr "" +#: src/screens/WalletConnect/SignMessageRequestScreen.js:25 +msgid "Sign Message Request" +msgstr "" + #: src/screens/WalletConnect/WalletConnectList.js:33 msgid "There was an error connecting. Please try again later." msgstr "" @@ -975,7 +981,7 @@ msgstr "" #: src/components/NanoContract/NanoContractDetails.js:202 #: src/components/NanoContract/SelectAddressModal.js:105 -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:209 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:213 #: src/screens/NanoContract/NanoContractRegisterScreen.js:184 msgid "Loading" msgstr "" @@ -1107,21 +1113,21 @@ msgstr "" msgid "Error loading the details of some tokens." msgstr "" -#: src/sagas/wallet.js:781 +#: src/sagas/wallet.js:780 msgid "Wallet is not ready to load addresses." msgstr "" #. This will show the message in the feedback content at SelectAddressModal -#: src/sagas/wallet.js:797 +#: src/sagas/wallet.js:796 msgid "There was an error while loading wallet addresses. Try again." msgstr "" -#: src/sagas/wallet.js:807 +#: src/sagas/wallet.js:806 msgid "Wallet is not ready to load the first address." msgstr "" #. This will show the message in the feedback content -#: src/sagas/wallet.js:823 +#: src/sagas/wallet.js:822 msgid "There was an error while loading first wallet address. Try again." msgstr "" @@ -1321,14 +1327,51 @@ msgid "" "security step to protect your data from potential phishing risks." msgstr "" -#: src/components/WalletConnect/SignMessageModal.js:50 -msgid "Sign this message?" +#: src/components/WalletConnect/SignMessageModal.js:59 +msgid "New Sign Message Request" +msgstr "" + +#: src/components/WalletConnect/SignMessageModal.js:63 +msgid "You have received a new Sign Message Request. Please" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:62 +#: src/components/WalletConnect/SignMessageModal.js:65 +msgid "carefully review the details" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:64 +#: src/components/WalletConnect/SignMessageModal.js:67 +msgid "before deciding to accept or decline." msgstr "" -#: src/components/WalletConnect/SignMessageModal.js:57 +#: src/components/WalletConnect/SignMessageModal.js:71 +msgid "Review Sign Message Request details" +msgstr "" + +#: src/components/WalletConnect/SignMessageRequest.js:35 +msgid "Message to sign" +msgstr "" + +#: src/components/WalletConnect/SignMessageRequest.js:45 +msgid "Address Path" +msgstr "" + +#: src/components/WalletConnect/SignMessageRequest.js:81 +msgid "Accept Request" +msgstr "" + +#: src/components/WalletConnect/SignMessageRequest.js:85 +msgid "Decline Request" +msgstr "" + +#: src/components/WalletConnect/WarnDisclaimer.js:21 msgid "" -"By clicking approve, you will sign the requested message using the first " -"address derived from your root key on the m/44'/280'/0'/0/0 derivation path." +"Caution: There are risks associated with signing dapp transaction requests." +msgstr "" + +#: src/components/WalletConnect/WarnDisclaimer.js:27 +msgid "Read More." msgstr "" #: src/components/WalletConnect/NanoContract/DappContainer.js:41 @@ -1397,84 +1440,67 @@ msgstr "" msgid "Couldn't determine address, select one" msgstr "" -#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:50 +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:49 #, javascript-format msgid "Position ${ idx }" msgstr "" -#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:98 +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:91 msgid "Arguments" msgstr "" -#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:103 +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:96 msgid "Loading arguments." msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:72 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:60 msgid "You have received a new Nano Contract Transaction. Please" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:74 -msgid "carefully review the details" -msgstr "" - -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:76 -msgid "before deciding to accept or decline." -msgstr "" - -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:80 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:68 msgid "Review transaction details" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:98 -msgid "" -"Caution: There are risks associated with signing dapp transaction requests." -msgstr "" - -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:104 -msgid "Read More." -msgstr "" - -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:191 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:195 msgid "Nano Contract Not Found" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:192 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:196 msgid "" "The Nano Contract requested is not registered. First register the Nano " "Contract to interact with it." msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:195 -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:242 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:199 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:246 msgid "Decline Transaction" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:210 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:214 msgid "Loading transaction information." msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:238 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:242 msgid "Accept Transaction" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:252 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:256 msgid "Sending transaction" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:253 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:257 msgid "Please wait." msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:279 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:283 msgid "Transaction successfully sent." msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:281 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:285 msgid "Ok, close" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:287 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:291 msgid "Error while sending transaction." msgstr "" @@ -1545,7 +1571,6 @@ msgid "See full transaction details on Public Explorer." msgstr "" #: src/components/NanoContract/NanoContractTransactionActionListItem.js:108 -#, javascript-format msgid "Deposit ${ tokenSymbol }" msgstr "" diff --git a/locale/texts.pot b/locale/texts.pot index 573f758d2..0cbb994bc 100644 --- a/locale/texts.pot +++ b/locale/texts.pot @@ -223,7 +223,7 @@ msgstr "" #: src/screens/CreateTokenName.js:64 #: src/screens/CreateTokenSymbol.js:84 #: src/screens/InitWallet.js:220 -#: src/screens/InitWallet.js:341 +#: src/screens/InitWallet.js:349 #: src/screens/SendAddressInput.js:66 #: src/screens/SendAmountInput.js:185 msgid "Next" @@ -422,7 +422,7 @@ msgid "Enter your seed words separated by space" msgstr "" #: src/components/NanoContract/NanoContractDetails.js:238 -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:289 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:293 #: src/screens/LoadHistoryScreen.js:51 #: src/screens/LoadWalletErrorScreen.js:20 #: src/screens/NanoContract/NanoContractRegisterScreen.js:168 @@ -547,7 +547,8 @@ msgstr "" msgid "Unlock Hathor Wallet" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:84 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:72 +#: src/components/WalletConnect/SignMessageModal.js:75 #: src/screens/PinScreen.js:265 #: src/screens/WalletConnect/WalletConnectList.js:125 msgid "Cancel" @@ -710,6 +711,7 @@ msgstr "" #: src/components/NanoContract/EditAddressModal.js:60 #: src/components/NanoContract/SelectAddressModal.js:117 +#: src/components/WalletConnect/SignMessageRequest.js:40 #: src/screens/SendConfirmScreen.js:161 msgid "Address" msgstr "" @@ -806,11 +808,15 @@ msgstr "" msgid "Unregister token" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:68 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:56 #: src/screens/WalletConnect/NewNanoContractTransactionScreen.js:24 msgid "New Nano Contract Transaction" msgstr "" +#: src/screens/WalletConnect/SignMessageRequestScreen.js:25 +msgid "Sign Message Request" +msgstr "" + #: src/screens/WalletConnect/WalletConnectList.js:33 msgid "There was an error connecting. Please try again later." msgstr "" @@ -968,7 +974,7 @@ msgstr "" #: src/components/NanoContract/NanoContractDetails.js:202 #: src/components/NanoContract/SelectAddressModal.js:105 -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:209 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:213 #: src/screens/NanoContract/NanoContractRegisterScreen.js:184 msgid "Loading" msgstr "" @@ -1100,20 +1106,20 @@ msgstr "" msgid "Error loading the details of some tokens." msgstr "" -#: src/sagas/wallet.js:781 +#: src/sagas/wallet.js:780 msgid "Wallet is not ready to load addresses." msgstr "" -#: src/sagas/wallet.js:797 +#: src/sagas/wallet.js:796 #. This will show the message in the feedback content at SelectAddressModal msgid "There was an error while loading wallet addresses. Try again." msgstr "" -#: src/sagas/wallet.js:807 +#: src/sagas/wallet.js:806 msgid "Wallet is not ready to load the first address." msgstr "" -#: src/sagas/wallet.js:823 +#: src/sagas/wallet.js:822 #. This will show the message in the feedback content msgid "There was an error while loading first wallet address. Try again." msgstr "" @@ -1314,14 +1320,50 @@ msgid "" "security step to protect your data from potential phishing risks." msgstr "" -#: src/components/WalletConnect/SignMessageModal.js:50 -msgid "Sign this message?" +#: src/components/WalletConnect/SignMessageModal.js:59 +msgid "New Sign Message Request" msgstr "" -#: src/components/WalletConnect/SignMessageModal.js:57 -msgid "" -"By clicking approve, you will sign the requested message using the first " -"address derived from your root key on the m/44'/280'/0'/0/0 derivation path." +#: src/components/WalletConnect/SignMessageModal.js:63 +msgid "You have received a new Sign Message Request. Please" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:62 +#: src/components/WalletConnect/SignMessageModal.js:65 +msgid "carefully review the details" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:64 +#: src/components/WalletConnect/SignMessageModal.js:67 +msgid "before deciding to accept or decline." +msgstr "" + +#: src/components/WalletConnect/SignMessageModal.js:71 +msgid "Review Sign Message Request details" +msgstr "" + +#: src/components/WalletConnect/SignMessageRequest.js:35 +msgid "Message to sign" +msgstr "" + +#: src/components/WalletConnect/SignMessageRequest.js:45 +msgid "Address Path" +msgstr "" + +#: src/components/WalletConnect/SignMessageRequest.js:81 +msgid "Accept Request" +msgstr "" + +#: src/components/WalletConnect/SignMessageRequest.js:85 +msgid "Decline Request" +msgstr "" + +#: src/components/WalletConnect/WarnDisclaimer.js:21 +msgid "Caution: There are risks associated with signing dapp transaction requests." +msgstr "" + +#: src/components/WalletConnect/WarnDisclaimer.js:27 +msgid "Read More." msgstr "" #: src/components/WalletConnect/NanoContract/DappContainer.js:41 @@ -1390,83 +1432,67 @@ msgstr "" msgid "Couldn't determine address, select one" msgstr "" -#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:50 +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:49 #, javascript-format msgid "Position ${ idx }" msgstr "" -#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:98 +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:91 msgid "Arguments" msgstr "" -#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:103 +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:96 msgid "Loading arguments." msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:72 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:60 msgid "You have received a new Nano Contract Transaction. Please" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:74 -msgid "carefully review the details" -msgstr "" - -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:76 -msgid "before deciding to accept or decline." -msgstr "" - -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:80 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:68 msgid "Review transaction details" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:98 -msgid "Caution: There are risks associated with signing dapp transaction requests." -msgstr "" - -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:104 -msgid "Read More." -msgstr "" - -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:191 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:195 msgid "Nano Contract Not Found" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:192 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:196 msgid "" "The Nano Contract requested is not registered. First register the Nano " "Contract to interact with it." msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:195 -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:242 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:199 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:246 msgid "Decline Transaction" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:210 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:214 msgid "Loading transaction information." msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:238 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:242 msgid "Accept Transaction" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:252 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:256 msgid "Sending transaction" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:253 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:257 msgid "Please wait." msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:279 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:283 msgid "Transaction successfully sent." msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:281 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:285 msgid "Ok, close" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:287 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:291 msgid "Error while sending transaction." msgstr "" diff --git a/package-lock.json b/package-lock.json index 7a4f1f312..56c5d85b7 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/hathor-rpc-handler": "0.0.1-experimental-alpha", "@hathor/unleash-client": "0.1.0", "@hathor/wallet-lib": "1.10.0", "@notifee/react-native": "5.7.0", @@ -2541,6 +2542,86 @@ "@hapi/hoek": "^9.0.0" } }, + "node_modules/@hathor/hathor-rpc-handler": { + "version": "0.0.1-experimental-alpha", + "resolved": "https://registry.npmjs.org/@hathor/hathor-rpc-handler/-/hathor-rpc-handler-0.0.1-experimental-alpha.tgz", + "integrity": "sha512-/8zz7HUFYOYd1k0ZIyJMyNBB1mdhZ0VoU09yAVkYFtJFJlScCPD8gNvlWmtjC8CdBnQAAk7ozKW4k0UbYd6/zQ==", + "dependencies": { + "@hathor/wallet-lib": "1.8.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@hathor/hathor-rpc-handler/node_modules/@hathor/wallet-lib": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@hathor/wallet-lib/-/wallet-lib-1.8.0.tgz", + "integrity": "sha512-G1kLlxZ4Ev3S7hPiq/y9wUl4ns4pndOvuK37q+xqdxieZKCl2/O7lXiiHVOgRN9xOntL/TR66n2UZPpe0p29zQ==", + "dependencies": { + "axios": "1.7.2", + "bitcore-lib": "8.25.10", + "bitcore-mnemonic": "8.25.10", + "buffer": "6.0.3", + "crypto-js": "4.2.0", + "isomorphic-ws": "5.0.0", + "level": "8.0.1", + "lodash": "4.17.21", + "long": "5.2.3", + "ws": "8.17.0" + }, + "engines": { + "node": ">=20.0.0", + "npm": ">=10.0.0" + } + }, + "node_modules/@hathor/hathor-rpc-handler/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/@hathor/hathor-rpc-handler/node_modules/crypto-js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" + }, + "node_modules/@hathor/hathor-rpc-handler/node_modules/ws": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz", + "integrity": "sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/@hathor/unleash-client": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/@hathor/unleash-client/-/unleash-client-0.1.0.tgz", diff --git a/package.json b/package.json index ad37671ec..551a502b1 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/hathor-rpc-handler": "0.0.1-experimental-alpha", "@hathor/unleash-client": "0.1.0", "@hathor/wallet-lib": "1.10.0", "@notifee/react-native": "5.7.0", @@ -132,6 +133,8 @@ "/jestMockSetup.js" ], "cacheDirectory": ".jest/cache", - "testMatch": ["**/*.test.js"] + "testMatch": [ + "**/*.test.js" + ] } } diff --git a/src/App.js b/src/App.js index 9dbddc2b0..b3dd47c14 100644 --- a/src/App.js +++ b/src/App.js @@ -91,8 +91,8 @@ import { NanoContractDetailsScreen } from './screens/NanoContract/NanoContractDe import { NanoContractTransactionScreen } from './screens/NanoContract/NanoContractTransactionScreen'; import { NanoContractRegisterScreen } from './screens/NanoContract/NanoContractRegisterScreen'; import { NewNanoContractTransactionScreen } from './screens/WalletConnect/NewNanoContractTransactionScreen'; -import { NewNanoContractTransactionModal } from './components/WalletConnect/NanoContract/NewNanoContractTransactionModal'; import { NanoContractRegisterQrCodeScreen } from './screens/NanoContractRegisterQrCodeScreen'; +import { SignMessageRequestScreen } from './screens/WalletConnect/SignMessageRequestScreen'; /** * This Stack Navigator is exhibited when there is no wallet initialized on the local storage. @@ -458,6 +458,7 @@ const AppStack = () => { + ( - + - diff --git a/src/actions.js b/src/actions.js index f358d7c58..f1966c338 100644 --- a/src/actions.js +++ b/src/actions.js @@ -188,6 +188,10 @@ export const types = { WALLETCONNECT_NEW_NANOCONTRACT_STATUS: 'WALLETCONNECT_NEW_NANOCONTRACT_STATUS', UNREGISTEREDTOKENS_REQUEST: 'UNREGISTEREDTOKENS_REQUEST', UNREGISTEREDTOKENS_UPDATE: 'UNREGISTEREDTOKENS_UPDATE', + WALLETCONNECT_NEW_NANOCONTRACT_RETRY: 'WALLETCONNECT_NEW_NANOCONTRACT_RETRY', + WALLETCONNECT_NEW_NANOCONTRACT_RETRY_DISMISS: 'WALLETCONNECT_NEW_NANOCONTRACT_RETRY_DISMISS', + SHOW_SIGN_MESSAGE_REQUEST_MODAL: 'SHOW_SIGN_MESSAGE_REQUEST_MODAL', + SHOW_NANO_CONTRACT_SEND_TX_MODAL: 'SHOW_NANO_CONTRACT_SEND_TX_MODAL' }; export const featureToggleInitialized = () => ({ @@ -1247,6 +1251,20 @@ export const firstAddressFailure = (failurePayload) => ({ payload: failurePayload, }); +/** + * Signals that the user wants to attempt to retry the nano contract tx send + */ +export const newNanoContractRetry = () => ({ + type: types.WALLETCONNECT_NEW_NANOCONTRACT_RETRY, +}); + +/** + * Signals that the user doesn't want to retry the new nano contract tx send + */ +export const newNanoContractRetryDismiss = () => ({ + type: types.WALLETCONNECT_NEW_NANOCONTRACT_RETRY_DISMISS, +}); + /** * Signals update on new nano contract status to ready. */ @@ -1339,3 +1357,23 @@ export const unregisteredTokensUpdate = (payload) => ({ type: types.UNREGISTEREDTOKENS_UPDATE, payload, }); + +export const showSignMessageWithAddressModal = (accept, deny, data, dapp) => ({ + type: types.SHOW_SIGN_MESSAGE_REQUEST_MODAL, + payload: { + accept, + deny, + data, + dapp, + }, +}); + +export const showNanoContractSendTxModal = (accept, deny, nc, dapp) => ({ + type: types.SHOW_NANO_CONTRACT_SEND_TX_MODAL, + payload: { + accept, + deny, + nc, + dapp, + }, +}); diff --git a/src/components/NanoContract/NanoContractTransactionHeader.js b/src/components/NanoContract/NanoContractTransactionHeader.js index c9453e0f9..f662638ce 100644 --- a/src/components/NanoContract/NanoContractTransactionHeader.js +++ b/src/components/NanoContract/NanoContractTransactionHeader.js @@ -20,10 +20,10 @@ import { COLORS } from '../../styles/themes'; import { combineURLs, getShortContent, getShortHash, getTimestampFormat } from '../../utils'; import SimpleButton from '../SimpleButton'; import { ArrowDownIcon } from '../Icons/ArrowDown.icon'; +import { ArrowUpIcon } from '../Icons/ArrowUp.icon'; import { TextValue } from '../TextValue'; import { TextLabel } from '../TextLabel'; import { TransactionStatusLabel } from '../TransactionStatusLabel'; -import { ArrowUpIcon } from '../Icons/ArrowUp.icon'; /** * It presents the header of Nano Contract Transaction screen. diff --git a/src/components/WalletConnect/NanoContract/NanoContractExecInfo.js b/src/components/WalletConnect/NanoContract/NanoContractExecInfo.js index c06577d27..564d38802 100644 --- a/src/components/WalletConnect/NanoContract/NanoContractExecInfo.js +++ b/src/components/WalletConnect/NanoContract/NanoContractExecInfo.js @@ -59,7 +59,7 @@ export const NanoContractExecInfo = ({ nc, onSelectAddress }) => { dispatch(firstAddressRequest()); } } - }, []); + }, [nc]); const isBlueprintInfoLoading = !registeredNc && blueprintInfo?.status === STATUS.LOADING; diff --git a/src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js b/src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js index 818fbf3ca..eb5635b58 100644 --- a/src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js +++ b/src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js @@ -11,13 +11,12 @@ import { View, Text, } from 'react-native'; -import { useDispatch, useSelector } from 'react-redux'; +import { useSelector } from 'react-redux'; import { t } from 'ttag'; import { get } from 'lodash'; import { Network } from '@hathor/wallet-lib'; import { COLORS } from '../../../styles/themes'; import { commonStyles } from '../theme'; -import { onExceptionCaptured } from '../../../actions'; import { DEFAULT_TOKEN, NANOCONTRACT_BLUEPRINTINFO_STATUS as STATUS } from '../../../constants'; import { FeedbackContent } from '../../FeedbackContent'; import Spinner from '../../Spinner'; @@ -61,10 +60,10 @@ function getFallbackArgEntries(args) { * @param {string[]} props.ncArgs A list of method's argument. */ export const NanoContractMethodArgs = ({ blueprintId, method, ncArgs }) => { - if (!ncArgs.length) { + if (!ncArgs || ncArgs.length <= 0) { return null; } - const dispatch = useDispatch(); + const network = useSelector((state) => new Network(state.networkSettings.network)); const tokens = useSelector((state) => state.tokens); @@ -87,11 +86,6 @@ export const NanoContractMethodArgs = ({ blueprintId, method, ncArgs }) => { return ncArgs.map((arg, idx) => [methodInfo.args[idx].name, arg, methodInfo.args[idx].type]); } - // Send this condition to sentry because it should never happen. - // Check any change in the lib or in the fullnode that could cause an impact here. - const errMsg = 'Error while getting the argument names of public_methods on blueprint'; - dispatch(onExceptionCaptured(new Error(errMsg), false)); - // Still render a fallback return getFallbackArgEntries(ncArgs); }, [method, ncArgs, blueprintInfo]); diff --git a/src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js b/src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js index 204026dc7..94ca30cb0 100644 --- a/src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js +++ b/src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js @@ -5,69 +5,49 @@ * LICENSE file in the root directory of this source tree. */ -import React from 'react'; +import React, { useCallback, useEffect } from 'react'; import { StyleSheet, - View, Text, - Linking, } from 'react-native'; import { t } from 'ttag'; import { useNavigation } from '@react-navigation/native'; import { useDispatch, useSelector } from 'react-redux'; -import { COLORS } from '../../../styles/themes'; -import { CircleInfoIcon } from '../../Icons/CircleInfo.icon'; import { ModalBase } from '../../ModalBase'; -import SimpleButton from '../../SimpleButton'; -import { setNewNanoContractTransaction, walletConnectReject } from '../../../actions'; -import { WALLET_STATUS } from '../../../sagas/wallet'; -import { NANO_CONTRACT_INFO_URL } from '../../../constants'; +import { walletConnectReject } from '../../../actions'; +import { WarnDisclaimer } from '../WarnDisclaimer'; -export const NewNanoContractTransactionModal = () => { +export const NewNanoContractTransactionModal = ({ + onDismiss, + data, +}) => { + const isRetrying = useSelector(({ walletConnect }) => ( + walletConnect.newNanoContractTransaction.retrying + )); const navigation = useNavigation(); const dispatch = useDispatch(); - const readMoreUrl = NANO_CONTRACT_INFO_URL; - const { - showModal, - ncTxRequest, - } = useSelector((state) => { - const { - walletStartState, - walletConnect: { - newNanoContractTransaction: { - showModal: showNcTxModal, - data, - } - }, - } = state; - const isWalletReady = walletStartState === WALLET_STATUS.READY; - - return { - showModal: showNcTxModal && isWalletReady, - ncTxRequest: data, - }; - }); - - const onDismiss = () => { + const onModalDismiss = useCallback(() => { dispatch(walletConnectReject()); - dispatch(setNewNanoContractTransaction({ show: false, data: null })); - }; + onDismiss(); + }, [onDismiss]); const navigatesToNewNanoContractScreen = () => { - dispatch(setNewNanoContractTransaction({ show: false, data: null })); - navigation.navigate('NewNanoContractTransactionScreen', { ncTxRequest }); + onDismiss(); + navigation.navigate('NewNanoContractTransactionScreen', { ncTxRequest: data }); }; - const onReadMore = () => { - Linking.openURL(readMoreUrl) - }; + useEffect(() => { + if (isRetrying) { + navigatesToNewNanoContractScreen(); + } + }, [isRetrying]); return ( - + {t`New Nano Contract Transaction`} - + {t`You have received a new Nano Contract Transaction. Please`} @@ -82,33 +62,12 @@ export const NewNanoContractTransactionModal = () => { /> ); }; -const WarnDisclaimer = ({ onReadMore }) => ( - - - - - - - {t`Caution: There are risks associated with signing dapp transaction requests.`} - - - - - - -); - const styles = StyleSheet.create({ body: { paddingBottom: 24, @@ -119,45 +78,5 @@ const styles = StyleSheet.create({ }, bold: { fontWeight: 'bold', - }, - warnContainer: { - flexShrink: 1, - flexDirection: 'row', - alignItems: 'center', - marginBottom: 24, - borderRadius: 8, - paddingTop: 12, - /* It should have been 12 but it is adjusted to compensate the negative - * margin on learnMoreWrapper and the difference between the font size - * and the line height, which amounts to 8 points of compensation. - */ - paddingBottom: 20, - paddingHorizontal: 16, - backgroundColor: COLORS.cardWarning100, - }, - warnContent: { - paddingLeft: 8, - }, - warnMessage: { - fontSize: 12, - lineHeight: 16, - }, - learnMoreWrapper: { - display: 'inline-block', - /* We are using negative margin here to correct the text position - * and create an optic effect of alignment. */ - marginBottom: -4, - paddingLeft: 2, - marginRight: 'auto', - }, - learnMoreContainer: { - justifyContent: 'flex-start', - borderBottomWidth: 1, - }, - learnMoreText: { - fontSize: 12, - lineHeight: 16, - fontWeight: 'bold', - color: 'hsla(0, 0%, 25%, 1)', - }, + } }); diff --git a/src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js b/src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js index 238cceded..8ab54b0c1 100644 --- a/src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js +++ b/src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js @@ -22,6 +22,8 @@ import { useNavigation } from '@react-navigation/native'; import { t } from 'ttag'; import { nanoContractBlueprintInfoRequest, + newNanoContractRetry, + newNanoContractRetryDismiss, setNewNanoContractStatusReady, walletConnectAccept, walletConnectReject, @@ -58,7 +60,7 @@ import { DeclineModal } from './DeclineModal'; * @param {string} props.ncTxRequest.dapp.description */ export const NewNanoContractTransactionRequest = ({ ncTxRequest }) => { - const { nc, dapp } = ncTxRequest; + const { data: nc, dapp } = ncTxRequest; const dispatch = useDispatch(); const navigation = useNavigation(); const newTxStatus = useSelector((state) => state.walletConnect.newNanoContractTransaction.status); @@ -133,7 +135,7 @@ export const NewNanoContractTransactionRequest = ({ ncTxRequest }) => { // This effect runs only once in the construct phase useEffect(() => { // Do nothing if nano contract is not registered and don't call initialize method. - if (notRegistered) return; + if (notRegistered) return undefined; // Request blueprint info if not present to feed the components: // - NanoContractExecInfo, and @@ -151,20 +153,24 @@ export const NewNanoContractTransactionRequest = ({ ncTxRequest }) => { } }); dispatch(unregisteredTokensRequest({ uids: unknownTokensUid })); + + return () => { + dispatch(setNewNanoContractStatusReady()); + dispatch(newNanoContractRetryDismiss()); + }; }, []); const onFeedbackModalDismiss = () => { - dispatch(setNewNanoContractStatusReady()); navigation.goBack(); }; const onNavigateToDashboard = () => { - dispatch(setNewNanoContractStatusReady()); navigation.navigate('Dashboard'); }; const onTryAgain = () => { dispatch(setNewNanoContractStatusReady()); + dispatch(newNanoContractRetry()); }; // Loading while downloading: diff --git a/src/components/WalletConnect/SignMessageModal.js b/src/components/WalletConnect/SignMessageModal.js index c78eff940..e1770470e 100644 --- a/src/components/WalletConnect/SignMessageModal.js +++ b/src/components/WalletConnect/SignMessageModal.js @@ -7,61 +7,71 @@ import React from 'react'; import { useDispatch } from 'react-redux'; +import { useNavigation } from '@react-navigation/native'; import { t } from 'ttag'; import { StyleSheet, Text } from 'react-native'; -import ApproveRejectModal from './ApproveRejectModal'; import { COLORS } from '../../styles/themes'; +import { ModalBase } from '../ModalBase'; +import { WarnDisclaimer } from './WarnDisclaimer'; +import { walletConnectReject } from '../../actions'; -const modalStyle = StyleSheet.create({ - signMessageText: { - backgroundColor: COLORS.textColorShadowLighter, - width: '100%', - height: 100, - borderRadius: 15, - padding: 8, - marginBottom: 12, - marginTop: 12, +const styles = StyleSheet.create({ + body: { + paddingBottom: 24, + }, + text: { + fontSize: 14, + lineHeight: 20, + }, + bold: { + fontWeight: 'bold', + }, + selectionContainer: { + borderRadius: 8, + paddingVertical: 8, + paddingHorizontal: 16, + backgroundColor: COLORS.freeze100, }, }); export default ({ - onAcceptAction, - onRejectAction, onDismiss, data, - baseStyles, }) => { - const styles = { ...baseStyles, modalStyle }; + const navigation = useNavigation(); const dispatch = useDispatch(); - const { message, } = data; - const onAccept = () => { + const onReject = () => { onDismiss(); - dispatch(onAcceptAction); + dispatch(walletConnectReject()); }; - const onReject = () => { + const navigateToSignMessageRequestScreen = () => { onDismiss(); - dispatch(onRejectAction); + navigation.navigate('SignMessageRequest', { signMessageRequest: data }); }; return ( - - - { message } - - - { t`By clicking approve, you will sign the requested message using the first address derived from your root key on the m/44'/280'/0'/0/0 derivation path.` } + + {t`New Sign Message Request`} + + + + {t`You have received a new Sign Message Request. Please`} + + {' '}{t`carefully review the details`}{' '} - - )} - onAccept={onAccept} - onReject={onReject} - data={data} - baseStyles={styles} - /> + {t`before deciding to accept or decline.`} + + + + + ); }; diff --git a/src/components/WalletConnect/SignMessageRequest.js b/src/components/WalletConnect/SignMessageRequest.js new file mode 100644 index 000000000..cb140cf5c --- /dev/null +++ b/src/components/WalletConnect/SignMessageRequest.js @@ -0,0 +1,126 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { + StyleSheet, + View, + ScrollView, + TouchableWithoutFeedback, + Text, +} from 'react-native'; +import { useDispatch } from 'react-redux'; +import { useNavigation } from '@react-navigation/native'; +import { t } from 'ttag'; +import { + walletConnectAccept, + walletConnectReject +} from '../../actions'; +import { COLORS } from '../../styles/themes'; +import NewHathorButton from '../NewHathorButton'; +import { DappContainer } from './NanoContract/DappContainer'; +import { commonStyles } from './theme'; +import { NanoContractIcon } from '../Icons/NanoContract.icon'; + +export const SignMessageRequestData = ({ data }) => ( + + + + + + + {t`Message to sign`} + {data.message} + + + + {t`Address`} + {data.address.address} + + + + {t`Address Path`} + {data.address.addressPath} + + + +); + +export const SignMessageRequest = ({ signMessageRequest }) => { + const { dapp, data } = signMessageRequest; + const { message, address } = data; + const dispatch = useDispatch(); + const navigation = useNavigation(); + + const onAcceptSignMessageRequest = () => { + const acceptedReq = { address, message }; + + // Signal the user has accepted the current request and pass the accepted data. + dispatch(walletConnectAccept(acceptedReq)); + navigation.goBack(); + }; + + const onDeclineTransaction = () => { + dispatch(walletConnectReject()); + navigation.goBack(); + }; + + return ( + + + + + + + {/* User actions */} + + + + + + + + + ); +}; + +const styles = StyleSheet.create({ + wide: { + width: '100%' + }, + wrapper: { + flex: 1, + paddingHorizontal: 16, + backgroundColor: COLORS.lowContrastDetail, // Defines an outer area on the main list content + }, + content: { + flex: 1, + rowGap: 24, + width: '100%', + paddingVertical: 16, + }, + balanceReceived: { + color: 'hsla(180, 85%, 34%, 1)', + fontWeight: 'bold', + }, + actionContainer: { + flexDirection: 'column', + gap: 8, + paddingBottom: 48, + }, + declineModalBody: { + paddingBottom: 24, + }, + value: [commonStyles.text, commonStyles.value], +}); diff --git a/src/components/WalletConnect/WalletConnectModal.js b/src/components/WalletConnect/WalletConnectModal.js index 4019ce1c9..91d80593e 100644 --- a/src/components/WalletConnect/WalletConnectModal.js +++ b/src/components/WalletConnect/WalletConnectModal.js @@ -12,6 +12,7 @@ import { hideWalletConnectModal } from '../../actions'; import SignMessageModal from './SignMessageModal'; import ConnectModal from './ConnectModal'; import { COLORS } from '../../styles/themes'; +import { NewNanoContractTransactionModal } from './NanoContract/NewNanoContractTransactionModal'; export default () => { const dispatch = useDispatch(); @@ -40,7 +41,13 @@ export default () => { + ); + case WalletConnectModalTypes.SEND_NANO_CONTRACT_TX: + return ( + ); default: @@ -100,4 +107,5 @@ const baseStyles = StyleSheet.create({ export const WalletConnectModalTypes = { CONNECT: 'CONNECT', SIGN_MESSAGE: 'SIGN_MESSAGE', + SEND_NANO_CONTRACT_TX: 'SEND_NANO_CONTRACT_TX', }; diff --git a/src/components/WalletConnect/WarnDisclaimer.js b/src/components/WalletConnect/WarnDisclaimer.js new file mode 100644 index 000000000..396082cbd --- /dev/null +++ b/src/components/WalletConnect/WarnDisclaimer.js @@ -0,0 +1,83 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { t } from 'ttag'; +import { StyleSheet, View, Text, Linking } from 'react-native'; +import { COLORS } from '../../styles/themes'; +import { CircleInfoIcon } from '../Icons/CircleInfo.icon'; +import SimpleButton from '../SimpleButton'; +import { NANO_CONTRACT_INFO_URL } from '../../constants'; + +export const WarnDisclaimer = () => { + const onReadMore = () => { + Linking.openURL(NANO_CONTRACT_INFO_URL) + }; + + return ( + + + + + + + {t`Caution: There are risks associated with signing dapp transaction requests.`} + + + + + + + ); +}; + +const styles = StyleSheet.create({ + warnContainer: { + flexShrink: 1, + flexDirection: 'row', + alignItems: 'center', + marginBottom: 24, + borderRadius: 8, + paddingTop: 12, + /* It should have been 12 but it is adjusted to compensate the negative + * margin on learnMoreWrapper and the difference between the font size + * and the line height, which amounts to 8 points of compensation. + */ + paddingBottom: 20, + paddingHorizontal: 16, + backgroundColor: COLORS.cardWarning100, + }, + warnContent: { + paddingLeft: 8, + }, + warnMessage: { + fontSize: 12, + lineHeight: 16, + }, + learnMoreWrapper: { + display: 'inline-block', + /* We are using negative margin here to correct the text position + * and create an optic effect of alignment. */ + marginBottom: -4, + paddingLeft: 2, + marginRight: 'auto', + }, + learnMoreContainer: { + justifyContent: 'flex-start', + borderBottomWidth: 1, + }, + learnMoreText: { + fontSize: 12, + lineHeight: 16, + fontWeight: 'bold', + color: 'hsla(0, 0%, 25%, 1)', + }, +}); diff --git a/src/reducers/reducer.init.js b/src/reducers/reducer.init.js index d9288d42b..7829383ac 100644 --- a/src/reducers/reducer.init.js +++ b/src/reducers/reducer.init.js @@ -1,4 +1,17 @@ -import { applyMiddleware, createStore } from 'redux'; +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { + applyMiddleware, + // createStore is deprecated, the new recommended way to use redux is by using + // redux-toolkit, more on this at + // https://redux.js.org/introduction/why-rtk-is-redux-today + legacy_createStore as createStore, +} from 'redux'; import createSagaMiddleware from 'redux-saga'; import thunk from 'redux-thunk'; import { reducer } from './reducer'; diff --git a/src/reducers/reducer.js b/src/reducers/reducer.js index eaea546d2..43294ea1e 100644 --- a/src/reducers/reducer.js +++ b/src/reducers/reducer.js @@ -256,6 +256,7 @@ const initialState = { /** * newNanoContractTransaction {{ * showModal: boolean; + * retrying: boolean; * data: { * nc: { * network: string; @@ -283,6 +284,7 @@ const initialState = { newNanoContractTransaction: { status: WALLETCONNECT_NEW_NANOCONTRACT_TX_STATUS.READY, showModal: false, + retrying: false, data: null, }, connectionFailed: false, @@ -694,6 +696,10 @@ export const reducer = (state = initialState, action) => { return onUnregisteredTokensRequest(state); case types.UNREGISTEREDTOKENS_UPDATE: return onUnregisteredTokensUpdate(state, action); + case types.WALLETCONNECT_NEW_NANOCONTRACT_RETRY: + return onNewNanoContractTransactionRetry(state); + case types.WALLETCONNECT_NEW_NANOCONTRACT_RETRY_DISMISS: + return onNewNanoContractTransactionRetryDismiss(state); default: return state; } @@ -1887,6 +1893,28 @@ export const onSetNewNanoContractTransaction = (state, { payload }) => ({ }, }); +export const onNewNanoContractTransactionRetry = (state) => ({ + ...state, + walletConnect: { + ...state.walletConnect, + newNanoContractTransaction: { + ...state.walletConnect.newNanoContractTransaction, + retrying: true, + }, + }, +}); + +export const onNewNanoContractTransactionRetryDismiss = (state) => ({ + ...state, + walletConnect: { + ...state.walletConnect, + newNanoContractTransaction: { + ...state.walletConnect.newNanoContractTransaction, + retrying: false, + }, + }, +}); + export const onSetNewNanoContractTransactionStatus = (state, { payload }) => ({ ...state, walletConnect: { diff --git a/src/sagas/featureToggle.js b/src/sagas/featureToggle.js index 9300bf984..67fefd829 100644 --- a/src/sagas/featureToggle.js +++ b/src/sagas/featureToggle.js @@ -79,7 +79,6 @@ export function* fetchTogglesRoutine() { } export function* handleToggleUpdate() { - console.log('Handling feature toggle update'); const unleashClient = yield select((state) => state.unleashClient); const networkSettings = yield select((state) => state.networkSettings); diff --git a/src/sagas/index.js b/src/sagas/index.js index 9506ad358..a6864c805 100644 --- a/src/sagas/index.js +++ b/src/sagas/index.js @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import { all, fork } from 'redux-saga/effects'; +import { all, call, put, spawn } from 'redux-saga/effects'; import { saga as walletSagas } from './wallet'; import { saga as tokensSagas } from './tokens'; import { saga as pushNotificationSaga } from './pushNotification'; @@ -15,23 +15,53 @@ import { saga as permissionsSagas } from './permissions'; import { saga as walletConnectSagas } from './walletConnect'; import { saga as networkSettingsSagas } from './networkSettings'; import { saga as nanoContractSagas } from './nanoContract'; +import { onExceptionCaptured } from '../actions'; +import { logger } from '../logger'; -const sagas = [ - walletSagas, - tokensSagas, - pushNotificationSaga, - networkSettingsSagas, - errorHandlerSagas, - featureToggleSagas, - permissionsSagas, - walletConnectSagas, - nanoContractSagas, -]; - -function* defaultSaga() { - yield all( - sagas.map((saga) => fork(saga)) - ); +const MAX_RETRIES = 5; + +const log = logger('rootSaga'); + +const sagas = { + walletSagas: { saga: walletSagas, retryCount: 0, critical: true }, + tokensSagas: { saga: tokensSagas, retryCount: 0, critical: true }, + pushNotificationSaga: { saga: pushNotificationSaga, retryCount: 0, critical: true }, + networkSettingsSagas: { saga: networkSettingsSagas, retryCount: 0, critical: true }, + errorHandlerSagas: { saga: errorHandlerSagas, retryCount: 0, critical: true }, + featureToggleSagas: { saga: featureToggleSagas, retryCount: 0, critical: true }, + permissionsSagas: { saga: permissionsSagas, retryCount: 0, critical: true }, + walletConnectSagas: { saga: walletConnectSagas, retryCount: 0, critical: false }, + nanoContractSagas: { saga: nanoContractSagas, retryCount: 0, critical: true }, +}; + +function* rootSaga() { + yield all(Object.keys(sagas).map((name) => spawn(function* supervisor() { + while (true) { + const { saga, retryCount, critical } = sagas[name]; + + try { + if (retryCount > MAX_RETRIES && !critical) { + continue; + } + + yield call(saga); + + break + } catch (e) { + log.error(`Saga ${name} crashed, restarting. [${retryCount}/${MAX_RETRIES}]`); + sagas[name].retryCount = retryCount + 1; + + if (retryCount >= MAX_RETRIES) { + log.error(`Max retries reached for saga ${name}`); + yield put(onExceptionCaptured(e, critical)); + break; + } + + yield put(onExceptionCaptured(e, false)); + log.error(e); + } + } + }))); } -export default defaultSaga; +export default rootSaga; diff --git a/src/sagas/wallet.js b/src/sagas/wallet.js index c5168c9b1..1b4191a18 100644 --- a/src/sagas/wallet.js +++ b/src/sagas/wallet.js @@ -387,7 +387,6 @@ export function* loadTokens() { * So we fetch the tokens metadata and store on redux */ export function* fetchTokensMetadata(tokens) { - // No tokens to load, set metadata as loaded if (!tokens.length) { return; } @@ -413,7 +412,7 @@ export function* fetchTokensMetadata(tokens) { const tokenMetadatas = {}; for (const response of responses) { if (response.type === types.TOKEN_FETCH_METADATA_FAILED) { - log.log(`Error downloading metadata of token ${response.tokenId}.`); + log.error(`Error downloading metadata of token ${response.tokenId}.`); } else if (response.type === types.TOKEN_FETCH_METADATA_SUCCESS) { // When the request returns null, it means that we have no metadata for this token if (response.data) { diff --git a/src/sagas/walletConnect.js b/src/sagas/walletConnect.js index 338798f62..160cb11c8 100644 --- a/src/sagas/walletConnect.js +++ b/src/sagas/walletConnect.js @@ -58,10 +58,18 @@ import { takeEvery, select, race, + spawn, } from 'redux-saga/effects'; import { eventChannel } from 'redux-saga'; import { get, values } from 'lodash'; - +import { + TriggerTypes, + TriggerResponseTypes, + RpcResponseTypes, + SendNanoContractTxFailure, + handleRpcRequest, +} from '@hathor/hathor-rpc-handler'; +import { isWalletServiceEnabled, WALLET_STATUS } from './wallet'; import { WalletConnectModalTypes } from '../components/WalletConnect/WalletConnectModal'; import { WALLET_CONNECT_PROJECT_ID, @@ -74,14 +82,30 @@ import { setWalletConnectSessions, onExceptionCaptured, setWCConnectionFailed, + showSignMessageWithAddressModal, + showNanoContractSendTxModal, + setNewNanoContractStatusLoading, + setNewNanoContractStatusReady, + setNewNanoContractStatusFailure, + setNewNanoContractStatusSuccess, } from '../actions'; import { checkForFeatureFlag, getNetworkSettings, showPinScreenForResult } from './helpers'; +import { logger } from '../logger'; + +const log = logger('walletConnect'); const AVAILABLE_METHODS = { - HATHOR_SIGN_MESSAGE: 'hathor_signMessage', + HATHOR_SIGN_MESSAGE: 'htr_signWithAddress', + HATHOR_SEND_NANO_TX: 'htr_sendNanoContractTx', }; const AVAILABLE_EVENTS = []; +// We're mocking it here because we don't want to add the walletconnect +// libraries in our production build. If you really want to add it, just run the +// src/walletconnect.sh script +const Core = class {}; +const Web3Wallet = class {}; + /** * Those are the only ones we are currently using, extracted from * https://docs.walletconnect.com/2.0/specs/clients/sign/error-codes @@ -94,22 +118,33 @@ const ERROR_CODES = { INVALID_PAYLOAD: 5003, }; -// We're mocking it here because we don't want to add the walletconnect -// libraries in our production build. If you really want to add it, just run the -// src/walletconnect.sh script -const Core = class {}; -const Web3Wallet = class {}; - -function* isWalletConnectEnabled() { +function isWalletConnectEnabled() { + return false; + /* const walletConnectEnabled = yield call(checkForFeatureFlag, WALLET_CONNECT_FEATURE_TOGGLE); return walletConnectEnabled; + */ } function* init() { + const walletStartState = yield select((state) => state.walletStartState); + + if (walletStartState !== WALLET_STATUS.READY) { + log.debug('Wallet not ready yet, waiting for START_WALLET_SUCCESS.'); + yield take(types.START_WALLET_SUCCESS); + log.debug('Starting wallet-connect.'); + } + try { + const walletServiceEnabled = yield call(isWalletServiceEnabled); const walletConnectEnabled = yield call(isWalletConnectEnabled); + if (walletServiceEnabled) { + log.debug('Wallet Service enabled, skipping wallet-connect init.'); + return; + } + if (!walletConnectEnabled) { return; } @@ -230,9 +265,12 @@ export function* onSessionRequest(action) { const { payload } = action; const { params } = payload; + const wallet = yield select((state) => state.wallet); + const { web3wallet } = yield select((state) => state.walletConnect.client); const activeSessions = yield call(() => web3wallet.getActiveSessions()); const requestSession = activeSessions[payload.topic]; + if (!requestSession) { console.error('Could not identify the request session, ignoring request..'); return; @@ -246,16 +284,59 @@ export function* onSessionRequest(action) { chain: get(requestSession.namespaces, 'hathor.chains[0]', ''), }; - switch (params.request.method) { - case AVAILABLE_METHODS.HATHOR_SIGN_MESSAGE: - yield call(onSignMessageRequest, { - ...data, - requestId: payload.id, - topic: payload.topic, - message: get(params, 'request.params.message'), - }); - break; - default: + try { + let dispatch; + yield put((_dispatch) => { + dispatch = _dispatch; + }); + + const response = yield call( + handleRpcRequest, + params.request, + wallet, + data, + promptHandler(dispatch), + ); + + switch (response.type) { + case RpcResponseTypes.SendNanoContractTxResponse: + yield put(setNewNanoContractStatusSuccess()); + break; + default: + break; + } + + yield call(() => web3wallet.respondSessionRequest({ + topic: payload.topic, + response: { + id: payload.id, + jsonrpc: '2.0', + result: response, + } + })); + } catch (e) { + let shouldAnswer = true; + switch (e.constructor) { + case SendNanoContractTxFailure: { + yield put(setNewNanoContractStatusFailure()); + + // User might try again, wait for it. + const { retry } = yield race({ + retry: take(types.WALLETCONNECT_NEW_NANOCONTRACT_RETRY), + dismiss: take(types.WALLETCONNECT_NEW_NANOCONTRACT_RETRY_DISMISS), + }); + + if (retry) { + shouldAnswer = false; + // Retry the action, exactly as it came: + yield spawn(onSessionRequest, action); + } + } break; + default: + break; + } + + if (shouldAnswer) { yield call(() => web3wallet.respondSessionRequest({ topic: payload.topic, response: { @@ -267,104 +348,198 @@ export function* onSessionRequest(action) { }, }, })); - break; + } } } +/** + * Handles various types of prompt requests by dispatching appropriate actions + * and resolving with the corresponding responses. + * + * @param {function} dispatch - The dispatch function to send actions to the store. + * @returns {function} - A function that receives Trigger requests from the rpc + * library and dispatches actions + * + * The returned function performs the following: + * + * - Depending on the `request.type`, it will: + * - `TriggerTypes.SignMessageWithAddressConfirmationPrompt`: + * - Dispatches `showSignMessageWithAddressModal` with acceptance/rejection handlers. + * - Resolves with `TriggerResponseTypes.SignMessageWithAddressConfirmationResponse`. + * - `TriggerTypes.SendNanoContractTxConfirmationPrompt`: + * - Dispatches `showNanoContractSendTxModal` with acceptance/rejection handlers. + * - Resolves with `TriggerResponseTypes.SendNanoContractTxConfirmationResponse`. + * - `TriggerTypes.SendNanoContractTxLoadingTrigger`: + * - Dispatches `setNewNanoContractStatusLoading`. + * - Resolves immediately. + * - `TriggerTypes.LoadingFinishedTrigger`: + * - Dispatches `setNewNanoContractStatusReady`. + * - Resolves immediately. + * - `TriggerTypes.PinConfirmationPrompt`: + * - Awaits `showPinScreenForResult` to get the PIN code. + * - Resolves with `TriggerResponseTypes.PinRequestResponse`. + * - For any other `request.type`, this method will reject with an error. + * + * @param {Object} request - The request object containing type and data. + * @param {Object} requestMetadata - Additional metadata for the request. + * + * @returns {Promise} - A Promise that resolves with the appropriate + * response based on the request type. + * + * @example + * const handler = promptHandler(dispatch); + */ +const promptHandler = (dispatch) => (request, requestMetadata) => + // eslint-disable-next-line + new Promise(async (resolve, reject) => { + switch (request.type) { + case TriggerTypes.SignMessageWithAddressConfirmationPrompt: { + const signMessageResponseTemplate = (accepted) => () => resolve({ + type: TriggerResponseTypes.SignMessageWithAddressConfirmationResponse, + data: accepted, + }); + dispatch(showSignMessageWithAddressModal( + signMessageResponseTemplate(true), + signMessageResponseTemplate(false), + request.data, + requestMetadata, + )) + } break; + case TriggerTypes.SendNanoContractTxConfirmationPrompt: { + const sendNanoContractTxResponseTemplate = (accepted) => (data) => resolve({ + type: TriggerResponseTypes.SendNanoContractTxConfirmationResponse, + data: { + accepted, + nc: data?.payload, + } + }); + + dispatch(showNanoContractSendTxModal( + sendNanoContractTxResponseTemplate(true), + sendNanoContractTxResponseTemplate(false), + request.data, + requestMetadata, + )); + } break; + case TriggerTypes.SendNanoContractTxLoadingTrigger: + dispatch(setNewNanoContractStatusLoading()); + resolve(); + break; + case TriggerTypes.LoadingFinishedTrigger: + dispatch(setNewNanoContractStatusReady()); + resolve(); + break; + case TriggerTypes.PinConfirmationPrompt: { + const pinCode = await showPinScreenForResult(dispatch); + + resolve({ + type: TriggerResponseTypes.PinRequestResponse, + data: { + accepted: true, + pinCode, + } + }); + } break; + default: reject(new Error('Invalid request')); + } + }); + /** * This saga will be called (dispatched from the event listener) when a sign * message RPC is published from a dApp * - * @param {String} data.requestId Unique identifier of the request - * @param {String} data.topic Unique identifier of the connected session - * @param {String} data.message Message the dApp requested a signature for + * @param {String} payload.data.requestId Unique identifier of the request + * @param {String} payload.data.topic Unique identifier of the connected session + * @param {String} payload.data.message Message the dApp requested a signature for + * @param {String} payload.dapp.icon The icon sent by the dApp + * @param {String} payload.dapp.proposer The proposer name sent by the dapp + * @param {String} payload.dapp.url The url sent by the dApp + * @param {String} payload.dapp.description The description sent by the dApp + * @param {String} payload.accept A callback function to indicate that the + * request has been accepted. + * @param {String} payload.deny A callback function to indicate that the request + * has been denied. */ -export function* onSignMessageRequest(data) { - const { web3wallet } = yield select((state) => state.walletConnect.client); - - const onAcceptAction = { type: 'WALLET_CONNECT_ACCEPT' }; - const onRejectAction = { type: 'WALLET_CONNECT_REJECT' }; +export function* onSignMessageRequest({ payload }) { + const { accept, deny: denyCb, data, dapp } = payload; const wallet = yield select((state) => state.wallet); if (!wallet.isReady()) { - console.error('Got a session request but wallet is not ready, ignoring..'); + log.error('Got a session request but wallet is not ready, ignoring.'); return; } yield put(setWalletConnectModal({ show: true, type: WalletConnectModalTypes.SIGN_MESSAGE, - data, - onAcceptAction, - onRejectAction, + data: { + data, + dapp, + }, })); - const { reject } = yield race({ - accept: take(onAcceptAction.type), - reject: take(onRejectAction.type), + const { deny } = yield race({ + accept: take(types.WALLET_CONNECT_ACCEPT), + deny: take(types.WALLET_CONNECT_REJECT), }); - try { - if (reject) { - yield call(() => web3wallet.respondSessionRequest({ - topic: data.topic, - response: { - id: data.requestId, - jsonrpc: '2.0', - error: { - code: ERROR_CODES.USER_REJECTED, - message: 'Rejected by the user', - }, - }, - })); - return; - } + if (deny) { + denyCb(); - if (!data.message) { - yield call(() => web3wallet.respondSessionRequest({ - topic: data.topic, - response: { - id: data.requestId, - jsonrpc: '2.0', - error: { - code: ERROR_CODES.INVALID_PAYLOAD, - message: 'Missing message to sign', - }, - }, - })); + return; + } - return; - } + accept(); +} - const { message } = data; +/** + * This saga will be called (dispatched from the event listener) when a + * sendNanoContractTx message RPC is published from a dApp + * + * @param {String} payload.data.requestId Unique identifier of the request + * @param {String} payload.data.topic Unique identifier of the connected session + * @param {String} payload.data.message Message the dApp requested a signature for + * @param {String} payload.dapp.icon The icon sent by the dApp + * @param {String} payload.dapp.proposer The proposer name sent by the dapp + * @param {String} payload.dapp.url The url sent by the dApp + * @param {String} payload.dapp.description The description sent by the dApp + * @param {String} payload.accept A callback function to indicate that the + * request has been accepted. + * @param {String} payload.deny A callback function to indicate that the request + * has been denied. + */ +export function* onSendNanoContractTxRequest({ payload }) { + const { accept: acceptCb, deny: denyCb, nc, dapp } = payload; - let dispatch; - yield put((_dispatch) => { - dispatch = _dispatch; - }); + const wallet = yield select((state) => state.wallet); - const pinCode = yield call(() => showPinScreenForResult(dispatch)); - const signedMessage = yield call(() => wallet.signMessageWithAddress( - message, - 0, // First address - pinCode, - )); - - const response = { - id: data.requestId, - jsonrpc: '2.0', - result: signedMessage, - }; + if (!wallet.isReady()) { + console.error('Got a session request but wallet is not ready, ignoring..'); + return; + } - yield call(() => web3wallet.respondSessionRequest({ - topic: data.topic, - response, - })); - } catch (error) { - console.log('Captured error on signMessage: ', error); - yield put(onExceptionCaptured(error)); + yield put(setWalletConnectModal({ + show: true, + type: WalletConnectModalTypes.SEND_NANO_CONTRACT_TX, + data: { + dapp, + data: nc, + }, + })); + + const { deny, accept } = yield race({ + accept: take(types.WALLET_CONNECT_ACCEPT), + deny: take(types.WALLET_CONNECT_REJECT), + }); + + if (deny) { + denyCb(); + + return; } + + acceptCb(accept); } /** @@ -520,7 +695,9 @@ export function* onSessionDelete(action) { export function* saga() { yield all([ fork(featureToggleUpdateListener), - takeLatest(types.START_WALLET_SUCCESS, init), + fork(init), + takeLatest(types.SHOW_NANO_CONTRACT_SEND_TX_MODAL, onSendNanoContractTxRequest), + takeLatest(types.SHOW_SIGN_MESSAGE_REQUEST_MODAL, onSignMessageRequest), takeLeading('WC_SESSION_REQUEST', onSessionRequest), takeEvery('WC_SESSION_PROPOSAL', onSessionProposal), takeEvery('WC_SESSION_DELETE', onSessionDelete), diff --git a/src/screens/PinScreen.js b/src/screens/PinScreen.js index 65a4eb214..96bca2061 100644 --- a/src/screens/PinScreen.js +++ b/src/screens/PinScreen.js @@ -268,6 +268,7 @@ class PinScreen extends React.Component { title = t`Reset wallet`; onPress = () => this.goToReset(); } + return ( + + + + + ); +} + +const Wrapper = ({ children }) => ( + + {children} + +); + +const styles = StyleSheet.create({ + wrapper: { + flex: 1, + alignItems: 'center', + backgroundColor: COLORS.lowContrastDetail, + }, +}); diff --git a/walletconnect.sh b/walletconnect.sh index 1b5a960fb..6eabbe9b9 100644 --- a/walletconnect.sh +++ b/walletconnect.sh @@ -2,4 +2,17 @@ git apply enable-walletconnect.patch +npm install \ + @walletconnect/core@2.15.1\ + @walletconnect/web3wallet@1.14.1 \ + @ethersproject/shims@5.7.0 \ + @json-rpc-tools/utils@1.7.6 \ + @react-native-community/netinfo@11.3.1 \ + @walletconnect/react-native-compat@2.12.2 \ + ethers@6.13.2 \ + events@3.3.0 \ + fast-text-encoding@1.0.6 \ + react-native-get-random-values@1.11.0 \ + text-encoding@0.7.0 + npm install && cd ios && pod install From ee76a2de799595720b940414112f77e4a238109f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Abadesso?= Date: Sun, 8 Sep 2024 21:07:48 -0300 Subject: [PATCH 45/51] feat: create token RPC (#528) * feat: create token retry/dismiss, better formatting and fixes * refactor: console.error -> log.error * refactor: renderBoolean -> renderBooleanFormatter * refactor: only show 'allowMint and Melt' when a mint/melt address is sent * refactor: added missing create token handling on saga and actions * refactor: removed 3 dots * refactor: removed unused styles * refactor: setting create token to ready on component unmount * refactor: removed touchable opacity * refactor: improved error on root saga * refactor: better imports * refactor: using commonStyles * refactor: removed onReadMore * refactor: retrying -> false on onSetCreateTokenRetryDismiss * refactor: missing code and general fixes * feat: added address to create token request and changed order of create mint and melt * feat: updated translations * refactor: removed fuzzy translations * chore: removed .mo * refactor: apply review suggestions * refactor: title -> name --- locale/da/texts.po | 187 +++++++++++---- locale/pt-br/texts.po | 211 +++++++++++----- locale/ru-ru/texts.po | 187 +++++++++++---- locale/texts.pot | 183 ++++++++++---- src/App.js | 2 + src/actions.js | 66 ++++- .../WalletConnect/CreateTokenModal.js | 65 +++++ .../WalletConnect/CreateTokenRequest.js | 225 ++++++++++++++++++ .../WalletConnect/WalletConnectModal.js | 9 + src/constants.js | 26 +- src/reducers/reducer.js | 47 +++- src/sagas/helpers.js | 9 + src/sagas/index.js | 1 + src/sagas/walletConnect.js | 101 +++++++- .../WalletConnect/CreateTokenScreen.js | 45 ++++ 15 files changed, 1132 insertions(+), 232 deletions(-) create mode 100644 src/components/WalletConnect/CreateTokenModal.js create mode 100644 src/components/WalletConnect/CreateTokenRequest.js create mode 100644 src/screens/WalletConnect/CreateTokenScreen.js diff --git a/locale/da/texts.po b/locale/da/texts.po index 82101d472..6022603fa 100644 --- a/locale/da/texts.po +++ b/locale/da/texts.po @@ -57,11 +57,11 @@ msgid "[Last] dddd [•] HH:mm" msgstr "[Sidste] dddd [•] HH:mm" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:107 src/utils.js:426 +#: src/models.js:107 src/utils.js:429 msgid "DD MMM YYYY [•] HH:mm" msgstr "DD MMM YYYY [•] HH:mm" -#: src/utils.js:161 +#: src/utils.js:164 msgid "Invalid address" msgstr "Ugyldig adresse" @@ -433,7 +433,8 @@ msgid "Enter your seed words separated by space" msgstr "Indtast dine seed-ord adskilt med mellemrum" #: src/components/NanoContract/NanoContractDetails.js:238 -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:293 +#: src/components/WalletConnect/CreateTokenRequest.js:197 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:295 #: src/screens/LoadHistoryScreen.js:51 src/screens/LoadWalletErrorScreen.js:20 #: src/screens/NanoContract/NanoContractRegisterScreen.js:168 msgid "Try again" @@ -519,7 +520,9 @@ msgstr "BETALINGSANMODNING" msgid "Token" msgstr "Token" -#: src/components/TxDetailsModal.js:166 src/screens/PaymentRequestDetail.js:127 +#: src/components/TxDetailsModal.js:166 +#: src/components/WalletConnect/CreateTokenRequest.js:82 +#: src/screens/PaymentRequestDetail.js:127 msgid "Amount" msgstr "Antal" @@ -551,8 +554,9 @@ msgstr "Indtast din pinkode " msgid "Unlock Hathor Wallet" msgstr "Lås Hathor-wallet op" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:72 -#: src/components/WalletConnect/SignMessageModal.js:75 +#: src/components/WalletConnect/CreateTokenModal.js:60 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:64 +#: src/components/WalletConnect/SignMessageModal.js:72 #: src/screens/PinScreen.js:265 #: src/screens/WalletConnect/WalletConnectList.js:125 msgid "Cancel" @@ -811,7 +815,11 @@ msgstr "Jeg vil afregistrere token **${ tokenLabel }**" msgid "Unregister token" msgstr "Afregistrer token" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:56 +#: src/screens/WalletConnect/CreateTokenScreen.js:25 +msgid "Create Token Request" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:48 #: src/screens/WalletConnect/NewNanoContractTransactionScreen.js:24 msgid "New Nano Contract Transaction" msgstr "" @@ -977,7 +985,7 @@ msgstr "" #: src/components/NanoContract/NanoContractDetails.js:202 #: src/components/NanoContract/SelectAddressModal.js:105 -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:213 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:215 #: src/screens/NanoContract/NanoContractRegisterScreen.js:184 msgid "Loading" msgstr "" @@ -1338,50 +1346,142 @@ msgid "" "security step to protect your data from potential phishing risks." msgstr "" -#: src/components/WalletConnect/SignMessageModal.js:59 -msgid "New Sign Message Request" +#: src/components/WalletConnect/CreateTokenModal.js:44 +msgid "New Create Token Request" msgstr "" -#: src/components/WalletConnect/SignMessageModal.js:63 -msgid "You have received a new Sign Message Request. Please" +#: src/components/WalletConnect/CreateTokenModal.js:48 +msgid "You have received a new Create Token Request. Please" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:62 -#: src/components/WalletConnect/SignMessageModal.js:65 +#: src/components/WalletConnect/CreateTokenModal.js:50 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:54 +#: src/components/WalletConnect/SignMessageModal.js:62 msgid "carefully review the details" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:64 -#: src/components/WalletConnect/SignMessageModal.js:67 +#: src/components/WalletConnect/CreateTokenModal.js:52 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:56 +#: src/components/WalletConnect/SignMessageModal.js:64 msgid "before deciding to accept or decline." msgstr "" -#: src/components/WalletConnect/SignMessageModal.js:71 -msgid "Review Sign Message Request details" +#: src/components/WalletConnect/CreateTokenModal.js:56 +msgid "Review Create Token Request details" msgstr "" -#: src/components/WalletConnect/SignMessageRequest.js:35 -msgid "Message to sign" +#: src/components/WalletConnect/CreateTokenRequest.js:74 +msgid "Yes" msgstr "" -#: src/components/WalletConnect/SignMessageRequest.js:45 -msgid "Address Path" +#: src/components/WalletConnect/CreateTokenRequest.js:74 +msgid "No" +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:80 +msgid "Name" +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:81 +msgid "Symbol" +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:83 +#, javascript-format +msgid "Address to send newly minted ${ data.symbol }" msgstr "" +#: src/components/WalletConnect/CreateTokenRequest.js:84 +msgid "Address to send change ${ DEFAULT_TOKEN.uid }" +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:85 +msgid "Create mint authority?" +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:86 +msgid "Create melt authority?" +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:87 +msgid "Address to send the mint authority" +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:88 +msgid "Address to send the melt authority" +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:92 +msgid "Allow external mint authority addresses?" +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:99 +msgid "Allow external melt authority addresses?" +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:103 +msgid "Token data" +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:158 #: src/components/WalletConnect/SignMessageRequest.js:81 msgid "Accept Request" msgstr "" +#: src/components/WalletConnect/CreateTokenRequest.js:162 #: src/components/WalletConnect/SignMessageRequest.js:85 msgid "Decline Request" msgstr "" -#: src/components/WalletConnect/WarnDisclaimer.js:21 +#: src/components/WalletConnect/CreateTokenRequest.js:172 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:258 +msgid "Sending transaction" +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:173 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:259 +msgid "Please wait." +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:186 +msgid "Create Token Transaction successfully sent." +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:188 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:287 +msgid "Ok, close" +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:195 +msgid "Error while sending create token transaction." +msgstr "" + +#: src/components/WalletConnect/SignMessageModal.js:56 +msgid "New Sign Message Request" +msgstr "" + +#: src/components/WalletConnect/SignMessageModal.js:60 +msgid "You have received a new Sign Message Request. Please" +msgstr "" + +#: src/components/WalletConnect/SignMessageModal.js:68 +msgid "Review Sign Message Request details" +msgstr "" + +#: src/components/WalletConnect/SignMessageRequest.js:35 +msgid "Message to sign" +msgstr "" + +#: src/components/WalletConnect/SignMessageRequest.js:45 +msgid "Address Path" +msgstr "" + +#: src/components/WalletConnect/WarnDisclaimer.js:27 msgid "" "Caution: There are risks associated with signing dapp transaction requests." msgstr "" -#: src/components/WalletConnect/WarnDisclaimer.js:27 +#: src/components/WalletConnect/WarnDisclaimer.js:33 msgid "Read More." msgstr "" @@ -1451,67 +1551,55 @@ msgstr "" msgid "Couldn't determine address, select one" msgstr "" -#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:49 +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:51 #, javascript-format msgid "Position ${ idx }" msgstr "" -#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:91 +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:100 msgid "Arguments" msgstr "" -#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:96 +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:105 msgid "Loading arguments." msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:60 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:52 msgid "You have received a new Nano Contract Transaction. Please" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:68 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:60 msgid "Review transaction details" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:195 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:197 msgid "Nano Contract Not Found" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:196 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:198 msgid "" "The Nano Contract requested is not registered. First register the Nano " "Contract to interact with it." msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:199 -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:246 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:201 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:248 msgid "Decline Transaction" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:214 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:216 msgid "Loading transaction information." msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:242 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:244 msgid "Accept Transaction" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:256 -msgid "Sending transaction" -msgstr "" - -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:257 -msgid "Please wait." -msgstr "" - -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:283 -msgid "Transaction successfully sent." -msgstr "" - #: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:285 -msgid "Ok, close" +msgid "Transaction successfully sent." msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:291 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:293 msgid "Error while sending transaction." msgstr "" @@ -1582,6 +1670,7 @@ msgid "See full transaction details on Public Explorer." msgstr "" #: src/components/NanoContract/NanoContractTransactionActionListItem.js:108 +#, javascript-format msgid "Deposit ${ tokenSymbol }" msgstr "" diff --git a/locale/pt-br/texts.po b/locale/pt-br/texts.po index fd571ad06..9634cc78b 100644 --- a/locale/pt-br/texts.po +++ b/locale/pt-br/texts.po @@ -57,18 +57,18 @@ msgid "[Last] dddd [•] HH:mm" msgstr "[Última] dddd [•] HH:mm" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:107 src/utils.js:426 +#: src/models.js:107 src/utils.js:429 msgid "DD MMM YYYY [•] HH:mm" msgstr "DD MMM YYYY [•] HH:mm" -#: src/utils.js:161 +#: src/utils.js:164 msgid "Invalid address" msgstr "Endereço inválido" #. * #. * @example #. * You have received 10 T2, 5 T1 and 2 other token on a new transaction. -#. +#. #: src/workers/pushNotificationHandler.js:92 #, javascript-format msgid "" @@ -87,7 +87,7 @@ msgstr[1] "" #. * #. * @example #. * You have received 10 T2 and 5 T1 on a new transaction. -#. +#. #: src/workers/pushNotificationHandler.js:105 #, javascript-format msgid "" @@ -99,7 +99,7 @@ msgstr "" #. * #. * @example #. * You have received 10 T2 on a new transaction. -#. +#. #: src/workers/pushNotificationHandler.js:113 msgid "You have received ${ _firstToken2 } on a new transaction." msgstr "Você recebeu ${ _firstToken2 } em uma nova transação." @@ -309,7 +309,7 @@ msgstr "DETALHES DO TOKEN" #. * #. * name {string} token name -#. +#. #: src/screens/CreateTokenName.js:26 msgid "0/30 characters" msgstr "0/30 caracteres" @@ -444,7 +444,8 @@ msgid "Enter your seed words separated by space" msgstr "Digite suas palavras separadas por espaços" #: src/components/NanoContract/NanoContractDetails.js:238 -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:293 +#: src/components/WalletConnect/CreateTokenRequest.js:197 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:295 #: src/screens/LoadHistoryScreen.js:51 src/screens/LoadWalletErrorScreen.js:20 #: src/screens/NanoContract/NanoContractRegisterScreen.js:168 msgid "Try again" @@ -532,7 +533,9 @@ msgstr "REQUISIÇÃO DE PAGAMENTO" msgid "Token" msgstr "Token" -#: src/components/TxDetailsModal.js:166 src/screens/PaymentRequestDetail.js:127 +#: src/components/TxDetailsModal.js:166 +#: src/components/WalletConnect/CreateTokenRequest.js:82 +#: src/screens/PaymentRequestDetail.js:127 msgid "Amount" msgstr "Quantidade" @@ -564,8 +567,9 @@ msgstr "Digite seu PIN " msgid "Unlock Hathor Wallet" msgstr "Desbloqueie sua Hathor Wallet" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:72 -#: src/components/WalletConnect/SignMessageModal.js:75 +#: src/components/WalletConnect/CreateTokenModal.js:60 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:64 +#: src/components/WalletConnect/SignMessageModal.js:72 #: src/screens/PinScreen.js:265 #: src/screens/WalletConnect/WalletConnectList.js:125 msgid "Cancel" @@ -595,7 +599,7 @@ msgstr "" #. * #. * index {number} Selected index of the tab bar -#. +#. #. eslint thinks routes is not used, but TabView uses it #. eslint-disable-next-line react/no-unused-state #: src/screens/Receive.js:31 @@ -829,7 +833,11 @@ msgstr "Eu quero desregistrar o token **${ tokenLabel }**" msgid "Unregister token" msgstr "Desregistrar token" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:56 +#: src/screens/WalletConnect/CreateTokenScreen.js:25 +msgid "Create Token Request" +msgstr "Requisição de Criação de Token" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:48 #: src/screens/WalletConnect/NewNanoContractTransactionScreen.js:24 msgid "New Nano Contract Transaction" msgstr "Nova Transação de Nano Contract" @@ -1005,7 +1013,7 @@ msgstr "Erro ao carregar primeiro endereço da wallet" #: src/components/NanoContract/NanoContractDetails.js:202 #: src/components/NanoContract/SelectAddressModal.js:105 -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:213 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:215 #: src/screens/NanoContract/NanoContractRegisterScreen.js:184 msgid "Loading" msgstr "Carregando" @@ -1374,52 +1382,144 @@ msgstr "" "importante de segurança para proteger seus dados de potenciais riscos de " "phishing." -#: src/components/WalletConnect/SignMessageModal.js:59 -msgid "New Sign Message Request" -msgstr "Nova Solicitação de Assinatura de Mensagem" +#: src/components/WalletConnect/CreateTokenModal.js:44 +msgid "New Create Token Request" +msgstr "Nova Requisição de Criação de Token" -#: src/components/WalletConnect/SignMessageModal.js:63 -msgid "You have received a new Sign Message Request. Please" -msgstr "" -"Você recebeu uma nova Solicitação de Assinatura de Mensagem. Por favor," +#: src/components/WalletConnect/CreateTokenModal.js:48 +msgid "You have received a new Create Token Request. Please" +msgstr "Você recebeu uma nova Solicitação de Criação de Token. Por favor," -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:62 -#: src/components/WalletConnect/SignMessageModal.js:65 +#: src/components/WalletConnect/CreateTokenModal.js:50 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:54 +#: src/components/WalletConnect/SignMessageModal.js:62 msgid "carefully review the details" msgstr "revise os detalhes com cuidado" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:64 -#: src/components/WalletConnect/SignMessageModal.js:67 +#: src/components/WalletConnect/CreateTokenModal.js:52 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:56 +#: src/components/WalletConnect/SignMessageModal.js:64 msgid "before deciding to accept or decline." msgstr "antes de decidir aceitar ou recusar." -#: src/components/WalletConnect/SignMessageModal.js:71 -msgid "Review Sign Message Request details" -msgstr "Revisar detalhes da Solicitação de Assinatura de Mensagem" +#: src/components/WalletConnect/CreateTokenModal.js:56 +msgid "Review Create Token Request details" +msgstr "Revisar detalhes da Solicitação de Criação de Token" -#: src/components/WalletConnect/SignMessageRequest.js:35 -msgid "Message to sign" -msgstr "Mensagem para assinar" +#: src/components/WalletConnect/CreateTokenRequest.js:74 +msgid "Yes" +msgstr "Sim" -#: src/components/WalletConnect/SignMessageRequest.js:45 -msgid "Address Path" -msgstr "Path do Endereço" +#: src/components/WalletConnect/CreateTokenRequest.js:74 +msgid "No" +msgstr "Não" + +#: src/components/WalletConnect/CreateTokenRequest.js:80 +msgid "Name" +msgstr "Nome" +#: src/components/WalletConnect/CreateTokenRequest.js:81 +msgid "Symbol" +msgstr "Símbolo" + +#: src/components/WalletConnect/CreateTokenRequest.js:83 +#, javascript-format +msgid "Address to send newly minted ${ data.symbol }" +msgstr "Endereço para enviar os tokens ${ data.symbol } criados" + +#: src/components/WalletConnect/CreateTokenRequest.js:84 +msgid "Address to send change ${ DEFAULT_TOKEN.uid }" +msgstr "Endereço para enviar o ${ DEFAULT_TOKEN.uid } de troco" + +#: src/components/WalletConnect/CreateTokenRequest.js:85 +msgid "Create mint authority?" +msgstr "Criar mint authority?" + +#: src/components/WalletConnect/CreateTokenRequest.js:86 +msgid "Create melt authority?" +msgstr "Criar melt authority?" + +#: src/components/WalletConnect/CreateTokenRequest.js:87 +msgid "Address to send the mint authority" +msgstr "Endereço para enviar o mint authority" + +#: src/components/WalletConnect/CreateTokenRequest.js:88 +msgid "Address to send the melt authority" +msgstr "Endereço para enviar o melt authority" + +#: src/components/WalletConnect/CreateTokenRequest.js:92 +msgid "Allow external mint authority addresses?" +msgstr "Permitir endereços externos de mint authority?" + +#: src/components/WalletConnect/CreateTokenRequest.js:99 +msgid "Allow external melt authority addresses?" +msgstr "Permitir endereços externos de melt authority?" + +#: src/components/WalletConnect/CreateTokenRequest.js:103 +msgid "Token data" +msgstr "Dados do Token" + +#: src/components/WalletConnect/CreateTokenRequest.js:158 #: src/components/WalletConnect/SignMessageRequest.js:81 msgid "Accept Request" msgstr "Aceitar Solicitação" +#: src/components/WalletConnect/CreateTokenRequest.js:162 #: src/components/WalletConnect/SignMessageRequest.js:85 msgid "Decline Request" msgstr "Recusar Solicitação" -#: src/components/WalletConnect/WarnDisclaimer.js:21 +#: src/components/WalletConnect/CreateTokenRequest.js:172 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:258 +msgid "Sending transaction" +msgstr "Enviando transação" + +#: src/components/WalletConnect/CreateTokenRequest.js:173 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:259 +msgid "Please wait." +msgstr "Por favor, espere." + +#: src/components/WalletConnect/CreateTokenRequest.js:186 +msgid "Create Token Transaction successfully sent." +msgstr "Transação de Criação de Token enviada com sucesso." + +#: src/components/WalletConnect/CreateTokenRequest.js:188 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:287 +msgid "Ok, close" +msgstr "Ok, fechar" + +#: src/components/WalletConnect/CreateTokenRequest.js:195 +msgid "Error while sending create token transaction." +msgstr "Erro ao enviar transação de criação de token." + +#: src/components/WalletConnect/SignMessageModal.js:56 +msgid "New Sign Message Request" +msgstr "Nova Solicitação de Assinatura de Mensagem" + +#: src/components/WalletConnect/SignMessageModal.js:60 +msgid "You have received a new Sign Message Request. Please" +msgstr "" +"Você recebeu uma nova Solicitação de Assinatura de Mensagem. Por favor," + +#: src/components/WalletConnect/SignMessageModal.js:68 +msgid "Review Sign Message Request details" +msgstr "Revisar detalhes da Solicitação de Assinatura de Mensagem" + +#: src/components/WalletConnect/SignMessageRequest.js:35 +msgid "Message to sign" +msgstr "Mensagem para assinar" + +#: src/components/WalletConnect/SignMessageRequest.js:45 +msgid "Address Path" +msgstr "Path do Endereço" + +#: src/components/WalletConnect/WarnDisclaimer.js:27 msgid "" "Caution: There are risks associated with signing dapp transaction requests." msgstr "" "Cuidado: Há riscos associados a pedidos de assinatura de transação via dApp." -#: src/components/WalletConnect/WarnDisclaimer.js:27 +#: src/components/WalletConnect/WarnDisclaimer.js:33 msgid "Read More." msgstr "Ler mais." @@ -1489,33 +1589,33 @@ msgstr "Caller" msgid "Couldn't determine address, select one" msgstr "Não foi possível determinar um endereço, selecione um" -#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:49 +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:51 #, javascript-format msgid "Position ${ idx }" msgstr "Posição ${ idx }" -#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:91 +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:100 msgid "Arguments" msgstr "Argumentos" -#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:96 +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:105 msgid "Loading arguments." msgstr "Carregando argumentos." -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:60 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:52 msgid "You have received a new Nano Contract Transaction. Please" msgstr "" "Você recebeu um pedido para criar uma transação de Nano Contract. Por favor," -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:68 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:60 msgid "Review transaction details" msgstr "Revisar detalhes da transação" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:195 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:197 msgid "Nano Contract Not Found" msgstr "Nano Contract não encontrado" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:196 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:198 msgid "" "The Nano Contract requested is not registered. First register the Nano " "Contract to interact with it." @@ -1523,36 +1623,24 @@ msgstr "" "O Nano Contract solicitado não está registrado. Primeiro registre o Nano " "Contract para interagir com ele." -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:199 -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:246 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:201 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:248 msgid "Decline Transaction" msgstr "Recusar transação" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:214 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:216 msgid "Loading transaction information." msgstr "Carregando informações da transação." -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:242 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:244 msgid "Accept Transaction" msgstr "Aceitar transação" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:256 -msgid "Sending transaction" -msgstr "Enviando transação" - -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:257 -msgid "Please wait." -msgstr "Por favor, espere." - -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:283 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:285 msgid "Transaction successfully sent." msgstr "Transação enviada com sucesso." -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:285 -msgid "Ok, close" -msgstr "Ok, fechar" - -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:291 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:293 msgid "Error while sending transaction." msgstr "Ocorreu um erro durante o envio da transação." @@ -1695,3 +1783,6 @@ msgstr "Confirma que deseja desregistrar este Nano Contract?" #: src/components/NanoContract/UnregisterNanoContractModal.js:44 msgid "Yes, unregister contract" msgstr "Sim, desregistrar" + +#~ msgid "Title" +#~ msgstr "Título" diff --git a/locale/ru-ru/texts.po b/locale/ru-ru/texts.po index 72e211152..1fb48d314 100644 --- a/locale/ru-ru/texts.po +++ b/locale/ru-ru/texts.po @@ -58,11 +58,11 @@ msgid "[Last] dddd [•] HH:mm" msgstr "[Последний] dddd [•] HH:mm" #. See https://momentjs.com/docs/#/displaying/calendar-time/ -#: src/models.js:107 src/utils.js:426 +#: src/models.js:107 src/utils.js:429 msgid "DD MMM YYYY [•] HH:mm" msgstr "DD MMM YYYY [•] HH:mm" -#: src/utils.js:161 +#: src/utils.js:164 msgid "Invalid address" msgstr "Неправильный адрес" @@ -434,7 +434,8 @@ msgid "Enter your seed words separated by space" msgstr "Введите seed-фразу" #: src/components/NanoContract/NanoContractDetails.js:238 -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:293 +#: src/components/WalletConnect/CreateTokenRequest.js:197 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:295 #: src/screens/LoadHistoryScreen.js:51 src/screens/LoadWalletErrorScreen.js:20 #: src/screens/NanoContract/NanoContractRegisterScreen.js:168 msgid "Try again" @@ -521,7 +522,9 @@ msgstr "ЗАПРОС СРЕДСТВ" msgid "Token" msgstr "Токен" -#: src/components/TxDetailsModal.js:166 src/screens/PaymentRequestDetail.js:127 +#: src/components/TxDetailsModal.js:166 +#: src/components/WalletConnect/CreateTokenRequest.js:82 +#: src/screens/PaymentRequestDetail.js:127 msgid "Amount" msgstr "Количество" @@ -553,8 +556,9 @@ msgstr "Введите свой PIN-код " msgid "Unlock Hathor Wallet" msgstr "Разблокировать Hathor Wallet" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:72 -#: src/components/WalletConnect/SignMessageModal.js:75 +#: src/components/WalletConnect/CreateTokenModal.js:60 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:64 +#: src/components/WalletConnect/SignMessageModal.js:72 #: src/screens/PinScreen.js:265 #: src/screens/WalletConnect/WalletConnectList.js:125 msgid "Cancel" @@ -815,7 +819,11 @@ msgstr "Я хочу отменить регистрацию токена **${ to msgid "Unregister token" msgstr "Отменить регистрацию токена" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:56 +#: src/screens/WalletConnect/CreateTokenScreen.js:25 +msgid "Create Token Request" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:48 #: src/screens/WalletConnect/NewNanoContractTransactionScreen.js:24 msgid "New Nano Contract Transaction" msgstr "" @@ -981,7 +989,7 @@ msgstr "" #: src/components/NanoContract/NanoContractDetails.js:202 #: src/components/NanoContract/SelectAddressModal.js:105 -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:213 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:215 #: src/screens/NanoContract/NanoContractRegisterScreen.js:184 msgid "Loading" msgstr "" @@ -1327,50 +1335,142 @@ msgid "" "security step to protect your data from potential phishing risks." msgstr "" -#: src/components/WalletConnect/SignMessageModal.js:59 -msgid "New Sign Message Request" +#: src/components/WalletConnect/CreateTokenModal.js:44 +msgid "New Create Token Request" msgstr "" -#: src/components/WalletConnect/SignMessageModal.js:63 -msgid "You have received a new Sign Message Request. Please" +#: src/components/WalletConnect/CreateTokenModal.js:48 +msgid "You have received a new Create Token Request. Please" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:62 -#: src/components/WalletConnect/SignMessageModal.js:65 +#: src/components/WalletConnect/CreateTokenModal.js:50 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:54 +#: src/components/WalletConnect/SignMessageModal.js:62 msgid "carefully review the details" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:64 -#: src/components/WalletConnect/SignMessageModal.js:67 +#: src/components/WalletConnect/CreateTokenModal.js:52 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:56 +#: src/components/WalletConnect/SignMessageModal.js:64 msgid "before deciding to accept or decline." msgstr "" -#: src/components/WalletConnect/SignMessageModal.js:71 -msgid "Review Sign Message Request details" +#: src/components/WalletConnect/CreateTokenModal.js:56 +msgid "Review Create Token Request details" msgstr "" -#: src/components/WalletConnect/SignMessageRequest.js:35 -msgid "Message to sign" +#: src/components/WalletConnect/CreateTokenRequest.js:74 +msgid "Yes" msgstr "" -#: src/components/WalletConnect/SignMessageRequest.js:45 -msgid "Address Path" +#: src/components/WalletConnect/CreateTokenRequest.js:74 +msgid "No" +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:80 +msgid "Name" +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:81 +msgid "Symbol" +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:83 +#, javascript-format +msgid "Address to send newly minted ${ data.symbol }" msgstr "" +#: src/components/WalletConnect/CreateTokenRequest.js:84 +msgid "Address to send change ${ DEFAULT_TOKEN.uid }" +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:85 +msgid "Create mint authority?" +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:86 +msgid "Create melt authority?" +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:87 +msgid "Address to send the mint authority" +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:88 +msgid "Address to send the melt authority" +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:92 +msgid "Allow external mint authority addresses?" +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:99 +msgid "Allow external melt authority addresses?" +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:103 +msgid "Token data" +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:158 #: src/components/WalletConnect/SignMessageRequest.js:81 msgid "Accept Request" msgstr "" +#: src/components/WalletConnect/CreateTokenRequest.js:162 #: src/components/WalletConnect/SignMessageRequest.js:85 msgid "Decline Request" msgstr "" -#: src/components/WalletConnect/WarnDisclaimer.js:21 +#: src/components/WalletConnect/CreateTokenRequest.js:172 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:258 +msgid "Sending transaction" +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:173 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:259 +msgid "Please wait." +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:186 +msgid "Create Token Transaction successfully sent." +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:188 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:287 +msgid "Ok, close" +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:195 +msgid "Error while sending create token transaction." +msgstr "" + +#: src/components/WalletConnect/SignMessageModal.js:56 +msgid "New Sign Message Request" +msgstr "" + +#: src/components/WalletConnect/SignMessageModal.js:60 +msgid "You have received a new Sign Message Request. Please" +msgstr "" + +#: src/components/WalletConnect/SignMessageModal.js:68 +msgid "Review Sign Message Request details" +msgstr "" + +#: src/components/WalletConnect/SignMessageRequest.js:35 +msgid "Message to sign" +msgstr "" + +#: src/components/WalletConnect/SignMessageRequest.js:45 +msgid "Address Path" +msgstr "" + +#: src/components/WalletConnect/WarnDisclaimer.js:27 msgid "" "Caution: There are risks associated with signing dapp transaction requests." msgstr "" -#: src/components/WalletConnect/WarnDisclaimer.js:27 +#: src/components/WalletConnect/WarnDisclaimer.js:33 msgid "Read More." msgstr "" @@ -1440,67 +1540,55 @@ msgstr "" msgid "Couldn't determine address, select one" msgstr "" -#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:49 +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:51 #, javascript-format msgid "Position ${ idx }" msgstr "" -#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:91 +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:100 msgid "Arguments" msgstr "" -#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:96 +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:105 msgid "Loading arguments." msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:60 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:52 msgid "You have received a new Nano Contract Transaction. Please" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:68 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:60 msgid "Review transaction details" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:195 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:197 msgid "Nano Contract Not Found" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:196 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:198 msgid "" "The Nano Contract requested is not registered. First register the Nano " "Contract to interact with it." msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:199 -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:246 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:201 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:248 msgid "Decline Transaction" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:214 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:216 msgid "Loading transaction information." msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:242 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:244 msgid "Accept Transaction" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:256 -msgid "Sending transaction" -msgstr "" - -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:257 -msgid "Please wait." -msgstr "" - -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:283 -msgid "Transaction successfully sent." -msgstr "" - #: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:285 -msgid "Ok, close" +msgid "Transaction successfully sent." msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:291 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:293 msgid "Error while sending transaction." msgstr "" @@ -1571,6 +1659,7 @@ msgid "See full transaction details on Public Explorer." msgstr "" #: src/components/NanoContract/NanoContractTransactionActionListItem.js:108 +#, javascript-format msgid "Deposit ${ tokenSymbol }" msgstr "" diff --git a/locale/texts.pot b/locale/texts.pot index 0cbb994bc..72c4316a2 100644 --- a/locale/texts.pot +++ b/locale/texts.pot @@ -48,12 +48,12 @@ msgid "[Last] dddd [•] HH:mm" msgstr "" #: src/models.js:107 -#: src/utils.js:426 +#: src/utils.js:429 #. See https://momentjs.com/docs/#/displaying/calendar-time/ msgid "DD MMM YYYY [•] HH:mm" msgstr "" -#: src/utils.js:161 +#: src/utils.js:164 msgid "Invalid address" msgstr "" @@ -422,7 +422,8 @@ msgid "Enter your seed words separated by space" msgstr "" #: src/components/NanoContract/NanoContractDetails.js:238 -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:293 +#: src/components/WalletConnect/CreateTokenRequest.js:197 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:295 #: src/screens/LoadHistoryScreen.js:51 #: src/screens/LoadWalletErrorScreen.js:20 #: src/screens/NanoContract/NanoContractRegisterScreen.js:168 @@ -514,6 +515,7 @@ msgid "Token" msgstr "" #: src/components/TxDetailsModal.js:166 +#: src/components/WalletConnect/CreateTokenRequest.js:82 #: src/screens/PaymentRequestDetail.js:127 msgid "Amount" msgstr "" @@ -547,8 +549,9 @@ msgstr "" msgid "Unlock Hathor Wallet" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:72 -#: src/components/WalletConnect/SignMessageModal.js:75 +#: src/components/WalletConnect/CreateTokenModal.js:60 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:64 +#: src/components/WalletConnect/SignMessageModal.js:72 #: src/screens/PinScreen.js:265 #: src/screens/WalletConnect/WalletConnectList.js:125 msgid "Cancel" @@ -808,7 +811,11 @@ msgstr "" msgid "Unregister token" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:56 +#: src/screens/WalletConnect/CreateTokenScreen.js:25 +msgid "Create Token Request" +msgstr "" + +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:48 #: src/screens/WalletConnect/NewNanoContractTransactionScreen.js:24 msgid "New Nano Contract Transaction" msgstr "" @@ -974,7 +981,7 @@ msgstr "" #: src/components/NanoContract/NanoContractDetails.js:202 #: src/components/NanoContract/SelectAddressModal.js:105 -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:213 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:215 #: src/screens/NanoContract/NanoContractRegisterScreen.js:184 msgid "Loading" msgstr "" @@ -1320,49 +1327,141 @@ msgid "" "security step to protect your data from potential phishing risks." msgstr "" -#: src/components/WalletConnect/SignMessageModal.js:59 -msgid "New Sign Message Request" +#: src/components/WalletConnect/CreateTokenModal.js:44 +msgid "New Create Token Request" msgstr "" -#: src/components/WalletConnect/SignMessageModal.js:63 -msgid "You have received a new Sign Message Request. Please" +#: src/components/WalletConnect/CreateTokenModal.js:48 +msgid "You have received a new Create Token Request. Please" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:62 -#: src/components/WalletConnect/SignMessageModal.js:65 +#: src/components/WalletConnect/CreateTokenModal.js:50 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:54 +#: src/components/WalletConnect/SignMessageModal.js:62 msgid "carefully review the details" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:64 -#: src/components/WalletConnect/SignMessageModal.js:67 +#: src/components/WalletConnect/CreateTokenModal.js:52 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:56 +#: src/components/WalletConnect/SignMessageModal.js:64 msgid "before deciding to accept or decline." msgstr "" -#: src/components/WalletConnect/SignMessageModal.js:71 -msgid "Review Sign Message Request details" +#: src/components/WalletConnect/CreateTokenModal.js:56 +msgid "Review Create Token Request details" msgstr "" -#: src/components/WalletConnect/SignMessageRequest.js:35 -msgid "Message to sign" +#: src/components/WalletConnect/CreateTokenRequest.js:74 +msgid "Yes" msgstr "" -#: src/components/WalletConnect/SignMessageRequest.js:45 -msgid "Address Path" +#: src/components/WalletConnect/CreateTokenRequest.js:74 +msgid "No" +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:80 +msgid "Name" +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:81 +msgid "Symbol" +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:83 +#, javascript-format +msgid "Address to send newly minted ${ data.symbol }" msgstr "" +#: src/components/WalletConnect/CreateTokenRequest.js:84 +msgid "Address to send change ${ DEFAULT_TOKEN.uid }" +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:85 +msgid "Create mint authority?" +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:86 +msgid "Create melt authority?" +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:87 +msgid "Address to send the mint authority" +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:88 +msgid "Address to send the melt authority" +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:92 +msgid "Allow external mint authority addresses?" +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:99 +msgid "Allow external melt authority addresses?" +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:103 +msgid "Token data" +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:158 #: src/components/WalletConnect/SignMessageRequest.js:81 msgid "Accept Request" msgstr "" +#: src/components/WalletConnect/CreateTokenRequest.js:162 #: src/components/WalletConnect/SignMessageRequest.js:85 msgid "Decline Request" msgstr "" -#: src/components/WalletConnect/WarnDisclaimer.js:21 -msgid "Caution: There are risks associated with signing dapp transaction requests." +#: src/components/WalletConnect/CreateTokenRequest.js:172 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:258 +msgid "Sending transaction" +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:173 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:259 +msgid "Please wait." +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:186 +msgid "Create Token Transaction successfully sent." +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:188 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:287 +msgid "Ok, close" +msgstr "" + +#: src/components/WalletConnect/CreateTokenRequest.js:195 +msgid "Error while sending create token transaction." +msgstr "" + +#: src/components/WalletConnect/SignMessageModal.js:56 +msgid "New Sign Message Request" +msgstr "" + +#: src/components/WalletConnect/SignMessageModal.js:60 +msgid "You have received a new Sign Message Request. Please" +msgstr "" + +#: src/components/WalletConnect/SignMessageModal.js:68 +msgid "Review Sign Message Request details" +msgstr "" + +#: src/components/WalletConnect/SignMessageRequest.js:35 +msgid "Message to sign" +msgstr "" + +#: src/components/WalletConnect/SignMessageRequest.js:45 +msgid "Address Path" msgstr "" #: src/components/WalletConnect/WarnDisclaimer.js:27 +msgid "Caution: There are risks associated with signing dapp transaction requests." +msgstr "" + +#: src/components/WalletConnect/WarnDisclaimer.js:33 msgid "Read More." msgstr "" @@ -1432,67 +1531,55 @@ msgstr "" msgid "Couldn't determine address, select one" msgstr "" -#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:49 +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:51 #, javascript-format msgid "Position ${ idx }" msgstr "" -#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:91 +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:100 msgid "Arguments" msgstr "" -#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:96 +#: src/components/WalletConnect/NanoContract/NanoContractMethodArgs.js:105 msgid "Loading arguments." msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:60 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:52 msgid "You have received a new Nano Contract Transaction. Please" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:68 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionModal.js:60 msgid "Review transaction details" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:195 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:197 msgid "Nano Contract Not Found" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:196 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:198 msgid "" "The Nano Contract requested is not registered. First register the Nano " "Contract to interact with it." msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:199 -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:246 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:201 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:248 msgid "Decline Transaction" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:214 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:216 msgid "Loading transaction information." msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:242 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:244 msgid "Accept Transaction" msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:256 -msgid "Sending transaction" -msgstr "" - -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:257 -msgid "Please wait." -msgstr "" - -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:283 -msgid "Transaction successfully sent." -msgstr "" - #: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:285 -msgid "Ok, close" +msgid "Transaction successfully sent." msgstr "" -#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:291 +#: src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js:293 msgid "Error while sending transaction." msgstr "" diff --git a/src/App.js b/src/App.js index b3dd47c14..20e93ac41 100644 --- a/src/App.js +++ b/src/App.js @@ -93,6 +93,7 @@ import { NanoContractRegisterScreen } from './screens/NanoContract/NanoContractR import { NewNanoContractTransactionScreen } from './screens/WalletConnect/NewNanoContractTransactionScreen'; import { NanoContractRegisterQrCodeScreen } from './screens/NanoContractRegisterQrCodeScreen'; import { SignMessageRequestScreen } from './screens/WalletConnect/SignMessageRequestScreen'; +import { CreateTokenRequestScreen } from './screens/WalletConnect/CreateTokenScreen'; /** * This Stack Navigator is exhibited when there is no wallet initialized on the local storage. @@ -459,6 +460,7 @@ const AppStack = () => { + ({ @@ -1297,6 +1303,52 @@ export const setNewNanoContractStatusSuccess = () => ({ payload: WALLETCONNECT_NEW_NANOCONTRACT_TX_STATUS.SUCCESSFUL, }); +/** + * Signals that the user wants to attempt to retry the create token request + */ +export const createTokenRetry = () => ({ + type: types.WALLETCONNECT_CREATE_TOKEN_RETRY, +}); + +/** + * Signals that the user doesn't want to retry the create token request + */ +export const createTokenRetryDismiss = () => ({ + type: types.WALLETCONNECT_CREATE_TOKEN_RETRY_DISMISS, +}); + +/** + * Signals update on create token status to ready. + */ +export const setCreateTokenStatusReady = () => ({ + type: types.WALLETCONNECT_CREATE_TOKEN_STATUS, + payload: WALLETCONNECT_CREATE_TOKEN_STATUS.READY, +}); + +/** + * Signals update on create token status to loading. + */ +export const setCreateTokenStatusLoading = () => ({ + type: types.WALLETCONNECT_CREATE_TOKEN_STATUS, + payload: WALLETCONNECT_CREATE_TOKEN_STATUS.LOADING, +}); + +/** + * Signals update on create token status to failed. + */ +export const setCreateTokenStatusFailed = () => ({ + type: types.WALLETCONNECT_CREATE_TOKEN_STATUS, + payload: WALLETCONNECT_CREATE_TOKEN_STATUS.FAILED, +}); + +/** + * Signals update on create token status to successful. + */ +export const setCreateTokenStatusSuccessful = () => ({ + type: types.WALLETCONNECT_CREATE_TOKEN_STATUS, + payload: WALLETCONNECT_CREATE_TOKEN_STATUS.SUCCESSFUL, +}); + /** * Blueprint Info request in the context of a Nano Contract. * @param {string} id Blueprint ID. @@ -1377,3 +1429,13 @@ export const showNanoContractSendTxModal = (accept, deny, nc, dapp) => ({ dapp, }, }); + +export const showCreateTokenModal = (accept, deny, data, dapp) => ({ + type: types.SHOW_CREATE_TOKEN_REQUEST_MODAL, + payload: { + accept, + deny, + data, + dapp, + }, +}); diff --git a/src/components/WalletConnect/CreateTokenModal.js b/src/components/WalletConnect/CreateTokenModal.js new file mode 100644 index 000000000..98afb2ada --- /dev/null +++ b/src/components/WalletConnect/CreateTokenModal.js @@ -0,0 +1,65 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React, { useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { useNavigation } from '@react-navigation/native'; +import { t } from 'ttag'; +import { Text } from 'react-native'; +import { ModalBase } from '../ModalBase'; +import { WarnDisclaimer } from './WarnDisclaimer'; +import { walletConnectReject } from '../../actions'; +import { commonStyles } from './theme'; + +export default ({ + onDismiss, + data, +}) => { + const navigation = useNavigation(); + const dispatch = useDispatch(); + const isRetrying = useSelector((state) => state.walletConnect.createToken.retrying); + + const onReject = () => { + onDismiss(); + dispatch(walletConnectReject()); + }; + + const navigateToCreateTokenRequestScreen = () => { + onDismiss(); + navigation.navigate('CreateTokenRequest', { createTokenRequest: data }); + }; + + useEffect(() => { + if (isRetrying) { + navigateToCreateTokenRequestScreen(); + } + }, [isRetrying]); + + return ( + + {t`New Create Token Request`} + + + + {t`You have received a new Create Token Request. Please`} + + {' '}{t`carefully review the details`}{' '} + + {t`before deciding to accept or decline.`} + + + + + + ); +}; diff --git a/src/components/WalletConnect/CreateTokenRequest.js b/src/components/WalletConnect/CreateTokenRequest.js new file mode 100644 index 000000000..087610aed --- /dev/null +++ b/src/components/WalletConnect/CreateTokenRequest.js @@ -0,0 +1,225 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { useEffect } from 'react'; +import { + StyleSheet, + View, + ScrollView, + Text, + Image, +} from 'react-native'; +import { useDispatch, useSelector } from 'react-redux'; +import { useNavigation } from '@react-navigation/native'; +import { numberUtils } from '@hathor/wallet-lib'; +import { t } from 'ttag'; +import { + createTokenRetry, + createTokenRetryDismiss, + setCreateTokenStatusReady, + walletConnectAccept, + walletConnectReject +} from '../../actions'; +import { COLORS } from '../../styles/themes'; +import NewHathorButton from '../NewHathorButton'; +import { DappContainer } from './NanoContract/DappContainer'; +import { commonStyles } from './theme'; +import { FeedbackContent } from '../FeedbackContent'; +import { DEFAULT_TOKEN, WALLETCONNECT_CREATE_TOKEN_STATUS } from '../../constants'; +import FeedbackModal from '../FeedbackModal'; +import Spinner from '../Spinner'; +import errorIcon from '../../assets/images/icErrorBig.png'; +import checkIcon from '../../assets/images/icCheckBig.png'; + +const condRenderData = ( + attribute, + title, + separator, + formatter = (val) => val, +) => { + if (attribute != null) { + return ( + <> + { separator && } + + + { title } + + + { formatter(attribute) } + + + + ); + } + + return null; +}; + +/** + * Renders translated values for boolean inputs + * @param {boolean} bool + */ +function renderBooleanFormatter(bool) { + return bool ? t`Yes` : t`No`; +} + +export const CreateTokenRequestData = ({ data }) => ( + + + { condRenderData(data.name, t`Name`, false) } + { condRenderData(data.symbol, t`Symbol`, true) } + { condRenderData(data.amount, t`Amount`, true, numberUtils.prettyValue) } + { condRenderData(data.address, t`Address to send newly minted ${data.symbol}`, true) } + { condRenderData(data.changeAddress, t`Address to send change ${DEFAULT_TOKEN.uid}`, true) } + { condRenderData(data.createMint, t`Create mint authority?`, true, renderBooleanFormatter) } + { condRenderData(data.createMelt, t`Create melt authority?`, true, renderBooleanFormatter) } + { condRenderData(data.mintAuthorityAddress, t`Address to send the mint authority`, true) } + { condRenderData(data.meltAuthorityAddress, t`Address to send the melt authority`, true) } + { data.mintAuthorityAddress != null + && condRenderData( + data.allowExternalMintAuthorityAddress, + t`Allow external mint authority addresses?`, + true, + renderBooleanFormatter, + )} + { data.meltAuthorityAddress != null + && condRenderData( + data.allowExternalMeltAuthorityAddress, + t`Allow external melt authority addresses?`, + true, + renderBooleanFormatter, + )} + { condRenderData(data.data, t`Token data`, true, (tokenData) => tokenData.join('\n')) } + + +); + +export const CreateTokenRequest = ({ createTokenRequest }) => { + const { dapp, data } = createTokenRequest; + const { status } = useSelector((state) => state.walletConnect.createToken); + const dispatch = useDispatch(); + const navigation = useNavigation(); + + useEffect(() => () => { + dispatch(setCreateTokenStatusReady()); + }, []); + + const onAcceptCreateTokenRequest = () => { + const acceptedCreateToken = data; + + dispatch(walletConnectAccept(acceptedCreateToken)); + }; + + const onDeclineTransaction = () => { + dispatch(walletConnectReject()); + navigation.goBack(); + }; + + const isTxReady = status === WALLETCONNECT_CREATE_TOKEN_STATUS.READY; + const isTxProcessing = status === WALLETCONNECT_CREATE_TOKEN_STATUS.LOADING; + const isTxSuccessful = status === WALLETCONNECT_CREATE_TOKEN_STATUS.SUCCESSFUL; + const isTxFailed = status === WALLETCONNECT_CREATE_TOKEN_STATUS.FAILED; + + const onFeedbackModalDismiss = () => { + dispatch(createTokenRetryDismiss()); + navigation.goBack(); + }; + + const onNavigateToDashboard = () => { + navigation.navigate('Dashboard'); + }; + + const onTryAgain = () => { + dispatch(createTokenRetry()); + }; + + return ( + <> + + + {isTxReady && ( + + + + {/* User actions */} + + + + + + )} + {isTxProcessing && ( + } + offmargin + offcard + offbackground + /> + )} + + + + {isTxSuccessful && ( + )} + text={t`Create Token Transaction successfully sent.`} + onDismiss={onFeedbackModalDismiss} + action={()} + /> + )} + + {isTxFailed && ( + )} + text={t`Error while sending create token transaction.`} + onDismiss={onFeedbackModalDismiss} + action={()} + /> + )} + + ); +}; + +const styles = StyleSheet.create({ + wide: { + width: '100%' + }, + wrapper: { + flex: 1, + paddingHorizontal: 16, + backgroundColor: COLORS.lowContrastDetail, // Defines an outer area on the main list content + }, + content: { + flex: 1, + rowGap: 24, + width: '100%', + paddingVertical: 16, + }, + actionContainer: { + flexDirection: 'column', + gap: 8, + paddingBottom: 48, + }, + value: [commonStyles.text, commonStyles.value], +}); diff --git a/src/components/WalletConnect/WalletConnectModal.js b/src/components/WalletConnect/WalletConnectModal.js index 91d80593e..84ef1b59c 100644 --- a/src/components/WalletConnect/WalletConnectModal.js +++ b/src/components/WalletConnect/WalletConnectModal.js @@ -13,6 +13,7 @@ import SignMessageModal from './SignMessageModal'; import ConnectModal from './ConnectModal'; import { COLORS } from '../../styles/themes'; import { NewNanoContractTransactionModal } from './NanoContract/NewNanoContractTransactionModal'; +import CreateTokenModal from './CreateTokenModal'; export default () => { const dispatch = useDispatch(); @@ -50,6 +51,13 @@ export default () => { onDismiss={onDismiss} /> ); + case WalletConnectModalTypes.CREATE_TOKEN: + return ( + + ); default: return null; } @@ -108,4 +116,5 @@ export const WalletConnectModalTypes = { CONNECT: 'CONNECT', SIGN_MESSAGE: 'SIGN_MESSAGE', SEND_NANO_CONTRACT_TX: 'SEND_NANO_CONTRACT_TX', + CREATE_TOKEN: 'CREATE_TOKEN', }; diff --git a/src/constants.js b/src/constants.js index 27de995a9..2e56e2e19 100644 --- a/src/constants.js +++ b/src/constants.js @@ -225,32 +225,32 @@ export const networkSettingsKeyMap = { networkSettings: 'networkSettings:networkSettings' }; -export const NETWORKSETTINGS_STATUS = { +export const BASE_STATUS = { READY: 'ready', FAILED: 'failed', LOADING: 'loading', - WAITING: 'waiting', SUCCESSFUL: 'successful', }; +export const NETWORKSETTINGS_STATUS = { + ...BASE_STATUS, + WAITING: 'waiting', +}; + +export const WALLETCONNECT_CREATE_TOKEN_STATUS = { + ...BASE_STATUS, +}; + export const NANOCONTRACT_REGISTER_STATUS = { - READY: 'ready', - FAILED: 'failed', - LOADING: 'loading', - SUCCESSFUL: 'successful', + ...BASE_STATUS, }; export const WALLETCONNECT_NEW_NANOCONTRACT_TX_STATUS = { - READY: 'ready', - FAILED: 'failed', - LOADING: 'loading', - SUCCESSFUL: 'successful', + ...BASE_STATUS, }; export const NANOCONTRACT_BLUEPRINTINFO_STATUS = { - FAILED: 'failed', - LOADING: 'loading', - SUCCESSFUL: 'successful', + ...BASE_STATUS, }; /** diff --git a/src/reducers/reducer.js b/src/reducers/reducer.js index 43294ea1e..7fe2ee610 100644 --- a/src/reducers/reducer.js +++ b/src/reducers/reducer.js @@ -16,7 +16,8 @@ import { NETWORKSETTINGS_STATUS, NANOCONTRACT_REGISTER_STATUS, WALLETCONNECT_NEW_NANOCONTRACT_TX_STATUS, - NANOCONTRACT_BLUEPRINTINFO_STATUS + NANOCONTRACT_BLUEPRINTINFO_STATUS, + WALLETCONNECT_CREATE_TOKEN_STATUS } from '../constants'; import { types } from '../actions'; import { TOKEN_DOWNLOAD_STATUS } from '../sagas/tokens'; @@ -287,6 +288,10 @@ const initialState = { retrying: false, data: null, }, + createToken: { + status: WALLETCONNECT_CREATE_TOKEN_STATUS.READY, + retrying: false, + }, connectionFailed: false, sessions: {}, }, @@ -686,6 +691,12 @@ export const reducer = (state = initialState, action) => { return onSetNewNanoContractTransaction(state, action); case types.WALLETCONNECT_NEW_NANOCONTRACT_STATUS: return onSetNewNanoContractTransactionStatus(state, action); + case types.WALLETCONNECT_CREATE_TOKEN_STATUS: + return onSetCreateTokenStatus(state, action); + case types.WALLETCONNECT_CREATE_TOKEN_RETRY: + return onSetCreateTokenRetry(state, action); + case types.WALLETCONNECT_CREATE_TOKEN_RETRY_DISMISS: + return onSetCreateTokenRetryDismiss(state, action); case types.NANOCONTRACT_BLUEPRINTINFO_REQUEST: return onNanoContractBlueprintInfoRequest(state, action); case types.NANOCONTRACT_BLUEPRINTINFO_FAILURE: @@ -1926,6 +1937,40 @@ export const onSetNewNanoContractTransactionStatus = (state, { payload }) => ({ }, }); +export const onSetCreateTokenRetry = (state) => ({ + ...state, + walletConnect: { + ...state.walletConnect, + createToken: { + ...state.walletConnect.createToken, + retrying: true, + }, + }, +}); + +export const onSetCreateTokenRetryDismiss = (state) => ({ + ...state, + walletConnect: { + ...state.walletConnect, + createToken: { + ...state.walletConnect.createToken, + retrying: false, + }, + }, +}); + +export const onSetCreateTokenStatus = (state, { payload }) => ({ + ...state, + walletConnect: { + ...state.walletConnect, + createToken: { + ...state.walletConnect.createToken, + status: payload, + retrying: false, + }, + }, +}); + /** * @param {Object} state * @param {{ diff --git a/src/sagas/helpers.js b/src/sagas/helpers.js index ba678281e..b95350e65 100644 --- a/src/sagas/helpers.js +++ b/src/sagas/helpers.js @@ -344,3 +344,12 @@ export function* progressiveRetryRequest(request, maxRetries = MAX_RETRIES) { // throw last error after retries exhausted throw lastError; } + +export function* retryHandler(retryAction, dismissAction) { + const { retry } = yield race({ + retry: take(retryAction), + dismiss: take(dismissAction), + }); + + return retry != null; +} diff --git a/src/sagas/index.js b/src/sagas/index.js index a6864c805..ed340924b 100644 --- a/src/sagas/index.js +++ b/src/sagas/index.js @@ -48,6 +48,7 @@ function* rootSaga() { break } catch (e) { + log.error('Error on root saga', e); log.error(`Saga ${name} crashed, restarting. [${retryCount}/${MAX_RETRIES}]`); sagas[name].retryCount = retryCount + 1; diff --git a/src/sagas/walletConnect.js b/src/sagas/walletConnect.js index 160cb11c8..ebeadba6c 100644 --- a/src/sagas/walletConnect.js +++ b/src/sagas/walletConnect.js @@ -68,6 +68,7 @@ import { RpcResponseTypes, SendNanoContractTxFailure, handleRpcRequest, + CreateTokenError, } from '@hathor/hathor-rpc-handler'; import { isWalletServiceEnabled, WALLET_STATUS } from './wallet'; import { WalletConnectModalTypes } from '../components/WalletConnect/WalletConnectModal'; @@ -84,12 +85,17 @@ import { setWCConnectionFailed, showSignMessageWithAddressModal, showNanoContractSendTxModal, + showCreateTokenModal, setNewNanoContractStatusLoading, setNewNanoContractStatusReady, setNewNanoContractStatusFailure, setNewNanoContractStatusSuccess, + setCreateTokenStatusLoading, + setCreateTokenStatusReady, + setCreateTokenStatusSuccessful, + setCreateTokenStatusFailed, } from '../actions'; -import { checkForFeatureFlag, getNetworkSettings, showPinScreenForResult } from './helpers'; +import { checkForFeatureFlag, getNetworkSettings, retryHandler, showPinScreenForResult } from './helpers'; import { logger } from '../logger'; const log = logger('walletConnect'); @@ -179,7 +185,7 @@ function* init() { yield cancel(); } catch (error) { - console.error('Error loading wallet connect', error); + log.error('Error loading wallet connect', error); yield put(onExceptionCaptured(error)); } } @@ -272,7 +278,7 @@ export function* onSessionRequest(action) { const requestSession = activeSessions[payload.topic]; if (!requestSession) { - console.error('Could not identify the request session, ignoring request..'); + log.error('Could not identify the request session, ignoring request.'); return; } @@ -302,6 +308,9 @@ export function* onSessionRequest(action) { case RpcResponseTypes.SendNanoContractTxResponse: yield put(setNewNanoContractStatusSuccess()); break; + case RpcResponseTypes.CreateTokenResponse: + yield put(setCreateTokenStatusSuccessful()); + break; default: break; } @@ -320,11 +329,27 @@ export function* onSessionRequest(action) { case SendNanoContractTxFailure: { yield put(setNewNanoContractStatusFailure()); + const retry = yield call( + retryHandler, + types.WALLETCONNECT_CREATE_TOKEN_RETRY, + types.WALLETCONNECT_CREATE_TOKEN_RETRY_DISMISS, + ); + + if (retry) { + shouldAnswer = false; + // Retry the action, exactly as it came: + yield spawn(onSessionRequest, action); + } + } break; + case CreateTokenError: { + yield put(setCreateTokenStatusFailed()); + // User might try again, wait for it. - const { retry } = yield race({ - retry: take(types.WALLETCONNECT_NEW_NANOCONTRACT_RETRY), - dismiss: take(types.WALLETCONNECT_NEW_NANOCONTRACT_RETRY_DISMISS), - }); + const retry = yield call( + retryHandler, + types.WALLETCONNECT_CREATE_TOKEN_RETRY, + types.WALLETCONNECT_CREATE_TOKEN_RETRY_DISMISS, + ); if (retry) { shouldAnswer = false; @@ -393,6 +418,21 @@ const promptHandler = (dispatch) => (request, requestMetadata) => // eslint-disable-next-line new Promise(async (resolve, reject) => { switch (request.type) { + case TriggerTypes.CreateTokenConfirmationPrompt: { + const createTokenResponseTemplate = (accepted) => (data) => resolve({ + type: TriggerResponseTypes.CreateTokenConfirmationResponse, + data: { + accepted, + token: data?.payload, + } + }); + dispatch(showCreateTokenModal( + createTokenResponseTemplate(true), + createTokenResponseTemplate(false), + request.data, + requestMetadata, + )) + } break; case TriggerTypes.SignMessageWithAddressConfirmationPrompt: { const signMessageResponseTemplate = (accepted) => () => resolve({ type: TriggerResponseTypes.SignMessageWithAddressConfirmationResponse, @@ -425,7 +465,15 @@ const promptHandler = (dispatch) => (request, requestMetadata) => dispatch(setNewNanoContractStatusLoading()); resolve(); break; - case TriggerTypes.LoadingFinishedTrigger: + case TriggerTypes.CreateTokenLoadingTrigger: + dispatch(setCreateTokenStatusLoading()); + resolve(); + break; + case TriggerTypes.CreateTokenLoadingFinishedTrigger: + dispatch(setCreateTokenStatusReady()); + resolve(); + break; + case TriggerTypes.SendNanoContractTxLoadingFinishedTrigger: dispatch(setNewNanoContractStatusReady()); resolve(); break; @@ -515,7 +563,7 @@ export function* onSendNanoContractTxRequest({ payload }) { const wallet = yield select((state) => state.wallet); if (!wallet.isReady()) { - console.error('Got a session request but wallet is not ready, ignoring..'); + log.error('Got a session request but wallet is not ready, ignoring.'); return; } @@ -542,6 +590,38 @@ export function* onSendNanoContractTxRequest({ payload }) { acceptCb(accept); } +export function* onCreateTokenRequest({ payload }) { + const { accept: acceptCb, deny: denyCb, data, dapp } = payload; + + const wallet = yield select((state) => state.wallet); + + if (!wallet.isReady()) { + log.error('Got a session request but wallet is not ready, ignoring.'); + return; + } + + yield put(setWalletConnectModal({ + show: true, + type: WalletConnectModalTypes.CREATE_TOKEN, + data: { + dapp, + data, + }, + })); + + const { deny, accept } = yield race({ + accept: take(types.WALLET_CONNECT_ACCEPT), + deny: take(types.WALLET_CONNECT_REJECT), + }); + + if (deny) { + denyCb(); + + return; + } + + acceptCb(accept); +} /** * Listens for the wallet reset action, dispatched from the wallet sagas so we * can clear all current sessions. @@ -619,7 +699,7 @@ export function* onSessionProposal(action) { yield call(refreshActiveSessions); } catch (error) { - console.error('Error on sessionProposal: ', error); + log.error('Error on sessionProposal: ', error); yield put(onExceptionCaptured(error)); } } @@ -698,6 +778,7 @@ export function* saga() { fork(init), takeLatest(types.SHOW_NANO_CONTRACT_SEND_TX_MODAL, onSendNanoContractTxRequest), takeLatest(types.SHOW_SIGN_MESSAGE_REQUEST_MODAL, onSignMessageRequest), + takeLatest(types.SHOW_CREATE_TOKEN_REQUEST_MODAL, onCreateTokenRequest), takeLeading('WC_SESSION_REQUEST', onSessionRequest), takeEvery('WC_SESSION_PROPOSAL', onSessionProposal), takeEvery('WC_SESSION_DELETE', onSessionDelete), diff --git a/src/screens/WalletConnect/CreateTokenScreen.js b/src/screens/WalletConnect/CreateTokenScreen.js new file mode 100644 index 000000000..cb1b4344a --- /dev/null +++ b/src/screens/WalletConnect/CreateTokenScreen.js @@ -0,0 +1,45 @@ +/** + * Copyright (c) Hathor Labs and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import { + StyleSheet, + View, +} from 'react-native'; +import { t } from 'ttag'; +import HathorHeader from '../../components/HathorHeader'; +import OfflineBar from '../../components/OfflineBar'; +import { CreateTokenRequest } from '../../components/WalletConnect/CreateTokenRequest'; +import { COLORS } from '../../styles/themes'; + +export function CreateTokenRequestScreen({ route }) { + const { createTokenRequest } = route.params; + + return ( + + + + + + ); +} + +const Wrapper = ({ children }) => ( + + {children} + +); + +const styles = StyleSheet.create({ + wrapper: { + flex: 1, + alignItems: 'center', + backgroundColor: COLORS.lowContrastDetail, // Defines an outer area on the main list content + }, +}); From 93d6c08be9bb19852f1f6be41a8896617ad74533 Mon Sep 17 00:00:00 2001 From: Marcelo Salhab Brogliato Date: Tue, 10 Sep 2024 14:23:18 -0500 Subject: [PATCH 46/51] chore: Add git commit information to the pre-release script output --- pre_release.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pre_release.sh b/pre_release.sh index 5c22eb3fd..ffbc6ae05 100755 --- a/pre_release.sh +++ b/pre_release.sh @@ -10,6 +10,8 @@ set -e # Exit on any command failure. set -u # Exit on unset variables. set -v # Verbose mode for better debugging +git log -n1 + rm -rf node_modules/ rm -f ./android/app/google-services.json From 57b782c1b77b03668b273180b8318fb9455cfd10 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Thu, 12 Sep 2024 18:24:17 +0100 Subject: [PATCH 47/51] feat: add safe effect helper (#552) * feat: add safeEffect as a saga helper --- src/sagas/helpers.js | 20 ++++++++++++++++++++ src/sagas/nanoContract.js | 17 +++++++++++++++-- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/sagas/helpers.js b/src/sagas/helpers.js index b95350e65..63f7710be 100644 --- a/src/sagas/helpers.js +++ b/src/sagas/helpers.js @@ -353,3 +353,23 @@ export function* retryHandler(retryAction, dismissAction) { return retry != null; } + +/** + * A wrapper effect to catch unexpected error and provide a binding + * for the desired handling behavior. + * + * @param {Object} effect The targeted effect. + * @param {(error) => void} onError The error handling effect, + * which receives the error object as first argument. + * + * @returns An anonymous effect. + */ +export function safeEffect(effect, onError) { + return function* _safeEffect(payload) { + try { + yield call(effect, payload); + } catch (error) { + yield call(onError, error); + } + } +} diff --git a/src/sagas/nanoContract.js b/src/sagas/nanoContract.js index 46615029a..369fceced 100644 --- a/src/sagas/nanoContract.js +++ b/src/sagas/nanoContract.js @@ -36,7 +36,7 @@ import { import { logger } from '../logger'; import { NANO_CONTRACT_TX_HISTORY_SIZE } from '../constants'; import { consumeGenerator, getNanoContractFeatureToggle } from '../utils'; -import { getRegisteredNanoContracts } from './helpers'; +import { getRegisteredNanoContracts, safeEffect } from './helpers'; import { isWalletServiceEnabled } from './wallet'; const log = logger('nano-contract-saga'); @@ -158,6 +158,16 @@ export function* registerNanoContract({ payload }) { // emit action NANOCONTRACT_REGISTER_SUCCESS with feedback to user yield put(nanoContractRegisterSuccess({ entryKey: ncId, entryValue: nc, hasFeedback: true })); } +/** + * Effect invoked by safeEffect if an unexpected error occurs. + * + * @param {Object} error The error captured. + */ +function* registerNanoContractOnError(error) { + log.error('Unexpected error while registering Nano Contract.', error); + yield put(nanoContractRegisterFailure(failureMessage.nanoContractFailure)); + yield put(onExceptionCaptured(error, false)); +} /** * @typedef {Object} RawNcTxHistory @@ -440,7 +450,10 @@ export function* requestBlueprintInfo({ payload }) { export function* saga() { yield all([ debounce(500, [[types.START_WALLET_SUCCESS, types.NANOCONTRACT_INIT]], init), - takeEvery(types.NANOCONTRACT_REGISTER_REQUEST, registerNanoContract), + takeEvery( + types.NANOCONTRACT_REGISTER_REQUEST, + safeEffect(registerNanoContract, registerNanoContractOnError) + ), takeEvery(types.NANOCONTRACT_HISTORY_REQUEST, requestHistoryNanoContract), takeEvery(types.NANOCONTRACT_UNREGISTER_REQUEST, unregisterNanoContract), takeEvery(types.NANOCONTRACT_ADDRESS_CHANGE_REQUEST, requestNanoContractAddressChange), From 088a8345664681aff3e09a844a219b3c3d273e31 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Wed, 25 Sep 2024 23:25:39 +0100 Subject: [PATCH 48/51] feat: make requestUnregisteredTokens concurrent (#565) * feat: make requestUnregisteredTokens request concurrent * feat: add action to set an end to unregistered tokens request * feat: optimize function to one group and the last group * refactor: restructure actions to improve state update * refactor: add download to actions name --- src/actions.js | 36 ++++++-- .../NewNanoContractTransactionRequest.js | 4 +- src/constants.js | 8 ++ src/reducers/reducer.js | 51 ++++++++--- src/sagas/tokens.js | 86 +++++++++++++------ src/utils.js | 24 ++++++ 6 files changed, 164 insertions(+), 45 deletions(-) diff --git a/src/actions.js b/src/actions.js index 1538b6993..707650670 100644 --- a/src/actions.js +++ b/src/actions.js @@ -188,8 +188,14 @@ export const types = { FIRSTADDRESS_FAILURE: 'FIRSTADDRESS_FAILURE', /* It updates the redux state of new nano contract transaction status on wallet connect register. */ WALLETCONNECT_NEW_NANOCONTRACT_STATUS: 'WALLETCONNECT_NEW_NANOCONTRACT_STATUS', - UNREGISTEREDTOKENS_REQUEST: 'UNREGISTEREDTOKENS_REQUEST', - UNREGISTEREDTOKENS_UPDATE: 'UNREGISTEREDTOKENS_UPDATE', + /* It triggers a process to fetch token details for a list of unregistered tokens. */ + UNREGISTEREDTOKENS_DOWNLOAD_REQUEST: 'UNREGISTEREDTOKENS_DOWNLOAD_REQUEST', + /* It signals the process has loaded at least one token details with success. */ + UNREGISTEREDTOKENS_DOWNLOAD_SUCCESS: 'UNREGISTEREDTOKENS_DOWNLOAD_SUCCESS', + /* It signals the process has failed to load at least one token details. */ + UNREGISTEREDTOKENS_DOWNLOAD_FAILURE: 'UNREGISTEREDTOKENS_DOWNLOAD_FAILURE', + /* It signals the end of the process. */ + UNREGISTEREDTOKENS_DOWNLOAD_END: 'UNREGISTEREDTOKENS_DOWNLOAD_END', WALLETCONNECT_NEW_NANOCONTRACT_RETRY: 'WALLETCONNECT_NEW_NANOCONTRACT_RETRY', WALLETCONNECT_NEW_NANOCONTRACT_RETRY_DISMISS: 'WALLETCONNECT_NEW_NANOCONTRACT_RETRY_DISMISS', SHOW_SIGN_MESSAGE_REQUEST_MODAL: 'SHOW_SIGN_MESSAGE_REQUEST_MODAL', @@ -1394,22 +1400,38 @@ export const nanoContractBlueprintInfoSuccess = (id, blueprintInfo) => ({ * @param {Object} payload * @param {string[]} payload.uids A list of token UID. */ -export const unregisteredTokensRequest = (payload) => ({ - type: types.UNREGISTEREDTOKENS_REQUEST, +export const unregisteredTokensDownloadRequest = (payload) => ({ + type: types.UNREGISTEREDTOKENS_DOWNLOAD_REQUEST, payload, }); /** - * Signals an update to unregistered tokens state. + * Signals the success of unregistered tokens request. * @param {Object} payload * @param {Object} payload.tokens A map of token data by its UID. + */ +export const unregisteredTokensDownloadSuccess = (payload) => ({ + type: types.UNREGISTEREDTOKENS_DOWNLOAD_SUCCESS, + payload, +}); + +/** + * Signals a failure on unregistered tokens request. + * @param {Object} payload * @param {string} payload.error The error message as feedback to user */ -export const unregisteredTokensUpdate = (payload) => ({ - type: types.UNREGISTEREDTOKENS_UPDATE, +export const unregisteredTokensDownloadFailure = (payload) => ({ + type: types.UNREGISTEREDTOKENS_DOWNLOAD_FAILURE, payload, }); +/** + * Signals the unregistered tokens request has ended. + */ +export const unregisteredTokensDownloadEnd = () => ({ + type: types.UNREGISTEREDTOKENS_DOWNLOAD_END, +}); + export const showSignMessageWithAddressModal = (accept, deny, data, dapp) => ({ type: types.SHOW_SIGN_MESSAGE_REQUEST_MODAL, payload: { diff --git a/src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js b/src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js index 8ab54b0c1..b6c8eaa61 100644 --- a/src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js +++ b/src/components/WalletConnect/NanoContract/NewNanoContractTransactionRequest.js @@ -27,7 +27,7 @@ import { setNewNanoContractStatusReady, walletConnectAccept, walletConnectReject, - unregisteredTokensRequest + unregisteredTokensDownloadRequest } from '../../../actions'; import { COLORS } from '../../../styles/themes'; import NewHathorButton from '../../NewHathorButton'; @@ -152,7 +152,7 @@ export const NewNanoContractTransactionRequest = ({ ncTxRequest }) => { unknownTokensUid.push(uid); } }); - dispatch(unregisteredTokensRequest({ uids: unknownTokensUid })); + dispatch(unregisteredTokensDownloadRequest({ uids: unknownTokensUid })); return () => { dispatch(setNewNanoContractStatusReady()); diff --git a/src/constants.js b/src/constants.js index 2e56e2e19..a7bae3637 100644 --- a/src/constants.js +++ b/src/constants.js @@ -298,3 +298,11 @@ export const NANO_CONTRACT_ACTION = { withdrawal: 'withdrawal', deposit: 'deposit', }; + +export const NODE_RATE_LIMIT_CONF = { + thin_wallet_token: { + perSecond: 3, + burst: 10, + delay: 3, + } +}; diff --git a/src/reducers/reducer.js b/src/reducers/reducer.js index 7fe2ee610..058249383 100644 --- a/src/reducers/reducer.js +++ b/src/reducers/reducer.js @@ -703,10 +703,14 @@ export const reducer = (state = initialState, action) => { return onNanoContractBlueprintInfoFailure(state, action); case types.NANOCONTRACT_BLUEPRINTINFO_SUCCESS: return onNanoContractBlueprintInfoSuccess(state, action); - case types.UNREGISTEREDTOKENS_REQUEST: - return onUnregisteredTokensRequest(state); - case types.UNREGISTEREDTOKENS_UPDATE: - return onUnregisteredTokensUpdate(state, action); + case types.UNREGISTEREDTOKENS_DOWNLOAD_REQUEST: + return onUnregisteredTokensDownloadRequest(state); + case types.UNREGISTEREDTOKENS_DOWNLOAD_SUCCESS: + return onUnregisteredTokensDownloadSuccess(state, action); + case types.UNREGISTEREDTOKENS_DOWNLOAD_FAILURE: + return onUnregisteredTokensDownloadFailure(state, action); + case types.UNREGISTEREDTOKENS_DOWNLOAD_END: + return onUnregisteredTokensDownloadEnd(state); case types.WALLETCONNECT_NEW_NANOCONTRACT_RETRY: return onNewNanoContractTransactionRetry(state); case types.WALLETCONNECT_NEW_NANOCONTRACT_RETRY_DISMISS: @@ -2057,7 +2061,7 @@ export const onNanoContractBlueprintInfoSuccess = (state, { payload }) => ({ * Remarks * This reducer aims to clean error feedback message before processing the request. */ -export const onUnregisteredTokensRequest = (state) => ({ +export const onUnregisteredTokensDownloadRequest = (state) => ({ ...state, unregisteredTokens: { ...state.unregisteredTokens, @@ -2067,21 +2071,46 @@ export const onUnregisteredTokensRequest = (state) => ({ }); /** - * Update walletConnect.tokens with some tokens data needed to feed UI components - * without the need to register them, also update an error feedback message if present. + * Update tokens state as the request was successful. * * @param {Object} state * @param {Object} action * @param {Object} action.payload - * @param {Object} action.payload.tokens A map of token data by its UID. * @param {string} action.payload.error The error message as feedback to user */ -export const onUnregisteredTokensUpdate = (state, { payload }) => ({ +export const onUnregisteredTokensDownloadSuccess = (state, { payload }) => ({ ...state, unregisteredTokens: { ...state.unregisteredTokens, ...payload.tokens, - isLoading: false, + }, +}); + +/** + * Set error message as a user feedback. + * + * @param {Object} state + * @param {Object} action + * @param {Object} action.payload + * @param {string} action.payload.error The error message as feedback to user + */ +export const onUnregisteredTokensDownloadFailure = (state, { payload }) => ({ + ...state, + unregisteredTokens: { + ...state.unregisteredTokens, error: payload.error || null, }, -}) +}); + +/** + * Change state of isLoading to false while keeping tokens state. + * + * @param {Object} state + */ +export const onUnregisteredTokensDownloadEnd = (state) => ({ + ...state, + unregisteredTokens: { + ...state.unregisteredTokens, + isLoading: false, + }, +}); diff --git a/src/sagas/tokens.js b/src/sagas/tokens.js index 55e6e2fdd..acdc6056d 100644 --- a/src/sagas/tokens.js +++ b/src/sagas/tokens.js @@ -14,13 +14,14 @@ import { take, all, put, + join, } from 'redux-saga/effects'; import { t } from 'ttag'; import { metadataApi } from '@hathor/wallet-lib'; import { channel } from 'redux-saga'; import { get } from 'lodash'; import { specificTypeAndPayload, dispatchAndWait, getRegisteredTokenUids } from './helpers'; -import { mapToTxHistory } from '../utils'; +import { mapToTxHistory, splitInGroups } from '../utils'; import { types, tokenFetchBalanceRequested, @@ -30,9 +31,12 @@ import { tokenFetchHistorySuccess, tokenFetchHistoryFailed, onExceptionCaptured, - unregisteredTokensUpdate, + unregisteredTokensDownloadSuccess, + unregisteredTokensDownloadEnd, + unregisteredTokensDownloadFailure, } from '../actions'; import { logger } from '../logger'; +import { NODE_RATE_LIMIT_CONF } from '../constants'; const log = logger('tokens-saga'); @@ -296,14 +300,46 @@ export function* fetchTokenData(tokenId, force = false) { } /** - * Request tokens data to feed walletConnect's tokens. + * Get token details from wallet. + * + * @param {Object} wallet The application wallet. + * @param {string} uid Token UID. + * + * @description + * The token endpoint has 3r/s with 10r of burst and 3s of delay. + */ +export function* getTokenDetails(wallet, uid) { + try { + const { tokenInfo: { symbol, name } } = yield call([wallet, wallet.getTokenDetails], uid); + yield put(unregisteredTokensDownloadSuccess({ tokens: { [uid]: { uid, symbol, name } } })); + } catch (e) { + log.error(`Fail getting token data for token ${uid}.`, e); + yield put(unregisteredTokensDownloadFailure({ error: failureMessage.someTokensNotLoaded })); + } +} + +/** + * Request token details of unregistered tokens to feed new + * nano contract request actions. + * + * @description + * It optimizes for burst because we need the data as soon as possible, + * at the same time we should avoid request denials from the endpoint, + * which justifies a delay from burst to burst. + * * @param {Object} action * @param {Object} action.payload * @param {string[]} action.payload.uids */ -export function* requestUnregisteredTokens(action) { +export function* requestUnregisteredTokensDownload(action) { const { uids } = action.payload; + if (uids.length === 0) { + log.debug('No uids to request token details.'); + yield put(unregisteredTokensDownloadEnd()); + return; + } + const wallet = yield select((state) => state.wallet); if (!wallet.isReady()) { log.error('Fail updating loading tokens data because wallet is not ready yet.'); @@ -312,29 +348,29 @@ export function* requestUnregisteredTokens(action) { return; } - const tokens = {}; - let someError = false; - for (const uid of uids) { - try { - const { tokenInfo: { symbol, name } } = yield call([wallet, wallet.getTokenDetails], uid); - const token = { uid, symbol, name }; - tokens[uid] = token; - } catch (e) { - log.error(`Fail getting token data for token ${uid}.`, e); - someError = true; - // continue download of tokens + /** + * NOTE: We can improve the follwoing strategy by adopting a more robust + * rate-limit algorithm, like the sliding window or token bucket. + */ + + // These are the default values configured in the nginx conf public nodes. + const perBurst = NODE_RATE_LIMIT_CONF.thin_wallet_token.burst; + const burstDelay = NODE_RATE_LIMIT_CONF.thin_wallet_token.delay; + const uidGroups = splitInGroups(uids, perBurst); + for (const group of uidGroups) { + // Fork is a non-blocking effect, it doesn't cause the caller suspension. + const tasks = yield all(group.map((uid) => fork(getTokenDetails, wallet, uid))); + // Awaits a group to finish before burst the next group + yield join(tasks); + // Skip delay if there is only one group or is the last group + if (uidGroups.length === 1 || group === uidGroups.at(-1)) { + continue; } - } - - if (someError) { - log.log('There was a failure while getting tokens data to feed unregisteredTokens.'); - yield put( - unregisteredTokensUpdate({ tokens, error: failureMessage.someTokensNotLoaded }) - ); - return; + // This is a quick request, we should give a break before next burst + yield delay(burstDelay * 1000); } log.log('Success getting tokens data to feed unregisteredTokens.'); - yield put(unregisteredTokensUpdate({ tokens })); + yield put(unregisteredTokensDownloadEnd()); } export function* saga() { @@ -344,6 +380,6 @@ export function* saga() { takeEvery(types.TOKEN_FETCH_HISTORY_REQUESTED, fetchTokenHistory), takeEvery(types.NEW_TOKEN, routeTokenChange), takeEvery(types.SET_TOKENS, routeTokenChange), - takeEvery(types.UNREGISTEREDTOKENS_REQUEST, requestUnregisteredTokens), + takeEvery(types.UNREGISTEREDTOKENS_DOWNLOAD_REQUEST, requestUnregisteredTokensDownload), ]); } diff --git a/src/utils.js b/src/utils.js index 51c316f97..156ea08bf 100644 --- a/src/utils.js +++ b/src/utils.js @@ -527,3 +527,27 @@ export const parseScriptData = (scriptData, network) => { return null; } } + +/** + * Split a list in a list of groups defined by group size. + * + * @param {[]} list An array object. + * @param {number} groupSize The size of a group, which determines the final number of groups. + * + * @returns {[][]} Returns an array of grouped itens in array. + * + * @example + * splitInGroups(['a','b'], 1); + * // outputs: [['a'], ['b']] + */ +export function splitInGroups(list, groupSize) { + if (groupSize === 0) { + return list; + } + + const groups = []; + for (let i = 0; i < list.length; i += groupSize) { + groups.push(list.slice(i, i + groupSize)); + } + return groups; +} From 2a972d2f1fb5375111bbd2b5a186ca76576435e4 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Thu, 26 Sep 2024 17:36:17 +0100 Subject: [PATCH 49/51] fix: change nc address by renaming the param to newAddress (#571) --- src/sagas/nanoContract.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sagas/nanoContract.js b/src/sagas/nanoContract.js index 369fceced..16234ba9e 100644 --- a/src/sagas/nanoContract.js +++ b/src/sagas/nanoContract.js @@ -394,12 +394,12 @@ export function* unregisterNanoContract({ payload }) { * @param {{ * payload: { * ncId: string; - * address: string; + * newAddress: string; * } * }} */ export function* requestNanoContractAddressChange({ payload }) { - const { ncId, address } = payload; + const { ncId, newAddress } = payload; const wallet = yield select((state) => state.wallet); if (!wallet.isReady()) { @@ -415,7 +415,7 @@ export function* requestNanoContractAddressChange({ payload }) { wallet.storage.updateNanoContractRegisteredAddress ], ncId, - address, + newAddress, ); log.debug(`Success persisting Nano Contract address update. ncId = ${ncId}`); } From 122b033f117faeafac00d14a38eb8d77895bacc8 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Thu, 26 Sep 2024 22:08:31 +0100 Subject: [PATCH 50/51] refactor: fetchHistory on sagas/nanoContract to avoid iteration suspension (#569) * refactor: fetchHistory on sagas/nanoContract to avoid iteration suspension * docs: improve docstring --- src/sagas/nanoContract.js | 67 +++++++++++++++++++++++---------------- src/utils.js | 12 ------- 2 files changed, 40 insertions(+), 39 deletions(-) diff --git a/src/sagas/nanoContract.js b/src/sagas/nanoContract.js index 16234ba9e..cca332e59 100644 --- a/src/sagas/nanoContract.js +++ b/src/sagas/nanoContract.js @@ -20,6 +20,8 @@ import { } from 'redux-saga/effects'; import { t } from 'ttag'; import { NanoRequest404Error } from '@hathor/wallet-lib/lib/errors'; +import { getRegisteredNanoContracts, safeEffect } from './helpers'; +import { isWalletServiceEnabled } from './wallet'; import { nanoContractBlueprintInfoFailure, nanoContractBlueprintInfoSuccess, @@ -35,9 +37,7 @@ import { } from '../actions'; import { logger } from '../logger'; import { NANO_CONTRACT_TX_HISTORY_SIZE } from '../constants'; -import { consumeGenerator, getNanoContractFeatureToggle } from '../utils'; -import { getRegisteredNanoContracts, safeEffect } from './helpers'; -import { isWalletServiceEnabled } from './wallet'; +import { getNanoContractFeatureToggle } from '../utils'; const log = logger('nano-contract-saga'); @@ -54,6 +54,25 @@ export const failureMessage = { nanoContractHistoryFailure: t`Error while trying to download Nano Contract transactions history.`, }; +/** + * Call the async wallet method `isAddressMine` considering the type of wallet. + * + * @param {Object} wallet A wallet instance + * @param {string} address A wallet address to check + * @param {boolean} useWalletService A flag that determines if wallet service is in use + */ +export const isAddressMine = async (wallet, address, useWalletService) => { + // XXX: Wallet Service doesn't implement isAddressMine. + // See issue: https://github.com/HathorNetwork/hathor-wallet-lib/issues/732 + // Default to `false` if using Wallet Service. + if (useWalletService) { + return false; + } + + const isMine = await wallet.isAddressMine(address); + return isMine; +}; + export function* init() { const isEnabled = yield select(getNanoContractFeatureToggle); if (!isEnabled) { @@ -101,13 +120,13 @@ export function* registerNanoContract({ payload }) { // XXX: Wallet Service doesn't implement isAddressMine. // See issue: https://github.com/HathorNetwork/hathor-wallet-lib/issues/732 // Default to `false` if using Wallet Service. - let isAddressMine = false; + let isAddrMine = false; const useWalletService = yield call(isWalletServiceEnabled); if (!useWalletService) { - isAddressMine = yield call([wallet, wallet.isAddressMine], address); + isAddrMine = yield call([wallet, wallet.isAddressMine], address); } - if (!isAddressMine) { + if (!isAddrMine) { log.debug('Fail registering Nano Contract because address do not belongs to this wallet.'); yield put(nanoContractRegisterFailure(failureMessage.addressNotMine)); return; @@ -158,6 +177,7 @@ export function* registerNanoContract({ payload }) { // emit action NANOCONTRACT_REGISTER_SUCCESS with feedback to user yield put(nanoContractRegisterSuccess({ entryKey: ncId, entryValue: nc, hasFeedback: true })); } + /** * Effect invoked by safeEffect if an unexpected error occurs. * @@ -215,9 +235,10 @@ function* registerNanoContractOnError(error) { * @param {string} req.before Transaction hash to start to get newer items * @param {string} req.after Transaction hash to start to get older items * @param {Object} req.wallet Wallet instance from redux state + * @param {boolean} req.useWalletService A flag that determines if wallet service is in use * * @returns {{ - * history: {}; + * history: NcTxHistory; * }} * * @throws {Error} when request code is greater then 399 or when response's success is false @@ -225,6 +246,7 @@ function* registerNanoContractOnError(error) { export async function fetchHistory(req) { const { wallet, + useWalletService, ncId, count, after, @@ -245,28 +267,17 @@ export async function fetchHistory(req) { throw new Error('Failed to fetch nano contract history'); } - /* TODO: Make it run concurrently while guaranting the order. - /* see https://github.com/HathorNetwork/hathor-wallet-mobile/issues/514 - */ - const historyNewestToOldest = new Array(rawHistory.length) - for (let idx = 0; idx < rawHistory.length; idx += 1) { - const rawTx = rawHistory[idx]; - const network = wallet.getNetworkObject(); + const network = wallet.getNetworkObject(); + // Translate rawNcTxHistory to NcTxHistory + // Prouce a list ordered from newest to oldest + const transformedTxHistory = rawHistory.map(async (rawTx) => { const caller = addressUtils.getAddressFromPubkey(rawTx.nc_pubkey, network).base58; - // XXX: Wallet Service doesn't implement isAddressMine. - // See issue: https://github.com/HathorNetwork/hathor-wallet-lib/issues/732 - // Default to `false` if using Wallet Service. - let isMine = false; - const useWalletService = consumeGenerator(isWalletServiceEnabled()); - if (!useWalletService) { - // eslint-disable-next-line no-await-in-loop - isMine = await wallet.isAddressMine(caller); - } const actions = rawTx.nc_context.actions.map((each) => ({ type: each.type, // 'deposit' or 'withdrawal' uid: each.token_uid, amount: each.amount, })); + const isMine = await isAddressMine(wallet, caller, useWalletService); const tx = { txId: rawTx.hash, @@ -278,13 +289,13 @@ export async function fetchHistory(req) { blueprintId: rawTx.nc_blueprint_id, firstBlock: rawTx.first_block, caller, - isMine, actions, + isMine, }; - historyNewestToOldest[idx] = tx; - } + return tx; + }); - return { history: historyNewestToOldest }; + return { history: await Promise.all(transformedTxHistory) }; } /** @@ -332,9 +343,11 @@ export function* requestHistoryNanoContract({ payload }) { yield put(nanoContractHistoryClean({ ncId })); } + const useWalletService = yield select((state) => state.useWalletService); try { const req = { wallet, + useWalletService, ncId, before, after, diff --git a/src/utils.js b/src/utils.js index 156ea08bf..ef137eaf4 100644 --- a/src/utils.js +++ b/src/utils.js @@ -428,18 +428,6 @@ export const getNanoContractFeatureToggle = (state) => ( */ export const getTimestampFormat = (timestamp) => moment.unix(timestamp).format(t`DD MMM YYYY [•] HH:mm`) -/** - * Extract the result of a generator function when it is done. - */ -export const consumeGenerator = (generator) => { - for (;;) { - const { value, done } = generator.next(); - if (done) { - return value; - } - } -} - /** * Extract all the items of an async iterator/generator. * From fdf8b2820f710c83eb7796ca1ed18b5e00c9e760 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Mon, 30 Sep 2024 15:47:35 +0100 Subject: [PATCH 51/51] feat: add QA for Nano Contract (#572) * feat: add QA for Nano Contract --- QA_NANO_CONTRACT.md | 175 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 QA_NANO_CONTRACT.md diff --git a/QA_NANO_CONTRACT.md b/QA_NANO_CONTRACT.md new file mode 100644 index 000000000..7496ce16c --- /dev/null +++ b/QA_NANO_CONTRACT.md @@ -0,0 +1,175 @@ +# QA Nano Contract + +- CTA: call-to-action + +### Activation +- [ ] Make sure the wallet-service is disabled for the device +- [ ] Enable the nano contract feature for the device +- Close the wallet and reopen +- [ ] Check a toggle button with two options appear in the dashboard head + - Tokens + - Nano + +### Nano Contract Details +- [ ] Check the option "Tokens" is selected +- Select the option "Nano Contracts" +- [ ] Check the Nano Contract component is in focus with a title "No Nano Contracts" +- [ ] Check the *Nano Contract Details* component has: + - a message, and + - a call-to-action (CTA) "Register new" + +### Register Nano Contract +- Register a new Nano Contract by tapping over "Register new" on *Nano Contract Details* + - It may focus on a component of loading +- [ ] Check if the Nano Contract Registration screen is in focus +- [ ] Check if the Nano Contract ID is an input +- [ ] Check if the Wallet Address is loaded +- [ ] Check if the "REGISTER NANO CONTRACT" button is disabled +- Type the following Nano Contract ID `00004f1a3b07f0ffc7ced77a19ae6d4407991a15a281c0ddaf3d6e04a56eba78` + - It represents a Nano Contract in the `nano-testnet` network +- [ ] Check the "REGISTER NANO CONTRACT" button is now actionable +- Tap on "REGISTER NANO CONTRACT" button +- [ ] Check for a failure feedback modal to appear + - You can't register a Nano Contract from another network + - If the message "The informed address does not belong to the wallet" is the failure message, it happens when you are using the wallet service. Make sure to disable it and reload the wallet. +- Dismiss the modal by tapping outside its area +- Remove the input +- [ ] Check if a validation message of error appeared under the input: + - It should informe the field is required + - it should be in color red +- [ ] Check if the "REGISTER NANO CONTRACT" button is disabled +- Type any input +- [ ] Check if the validation error message disappeared + +### Change network +- Navigate to the *Settings* screen +- Go to "Network Settings" > Understand disclaimer > Customize +- [ ] Check if the following fields are hidden, you should not be able see them if Wallet Service is disabled: + - "Wallet Service URL (optional)", and + - "Wallet Service WS URL (optional)" +- [NOTE] If you see the mentioned fields because you have Wallet Service enabled, check they are empty or do it by your own before following the next instructions. +- Set "Node URL" the input "https://node1.nano-testnet.hathor.network/v1a/" +- Set "Explorer URL" the input "https://explorer.alpha.nano-testnet.hathor.network/" +- Set "Explorer Service URL" the input "https://explorer-service.nano-testnet.hathor.network/" +- Set "Transaction Mining Service URL" the input "https://txmining.nano-testnet.hathor.network/" +- Tap on "SEND" button +- [ ] Check for a success feedback modal message +- Dismiss the feedback modal +- [ ] Check there is a yellow bar at the head informing the custom network as *testnet* +- Go back to *Dashboard* screen + +### Register Nano Contract using the right network +- Select the option "Nano Contracts" of the toggle button +- Tap on "Register new" CTA +- Type `00004f1a3b07f0ffc7ced77a19ae6d4407991a15a281c0ddaf3d6e04a56eba78` as input to Nano Contract ID +- Tap on "REGISTER NANO CONTRACT" button +- [ ] Check for a success feedback modal to appear + - If a failure feedback modal appear with the message "The informed address does not belong to the wallet", it happens when you are using the wallet service. Make sure to disable it and reload the wallet +- [ ] Check the feedback modal has: + - A message informing the registration has happen with success, and + - A "SEE CONTRACT" button +- Tap on "SEE CONTRACT" button +- [ ] Check *Nano Contract Details* screen is in focus + +### Inspect Nano Contract Details +- From the *Nano Contract Details* screen follow the instructions +- [ ] Check there is at least one transaction listed, the "initialize" +- Tap on "Nano Contract" header to expand it +- Tap on "Nano Contract" header again to shrink it +- Tap on "Nano Contract" header to expand it again +- [ ] Check there is a "Nano Contract ID" field with eclipsed value +- [ ] Check there is a "Blueprint Name" field and value +- [ ] Check there ia a "Registered Address" field and value +- [ ] Check the field "Registered Address" is actionable +- [ ] Check there are two CTAs side-by-side: + - "See status details", and + - "Unregister contract" +- Tap on "See status details" CTA: + - A browser app should open in the explorer page of the Nano Contract and it should contains all Nano Contract details information, including the arguments of the method call +- Go back to the wallet +- Tap on "Registered Address" +- [ ] Check for a "Choose New Wallet Address" modal appears +- [ ] Check the address of *index 0* is selected +- Select any other indexed address +- [ ] Check for a "New Nano Contract Address" modal appears +- [ ] Check "New Nano Contract Address" modal has: + - An information content informing user this address is used to sign a transaction within the Nano Contract + - A mirror content informing the selected address and its index + - A "CONFIRM NEW ADDRESS" button + - A "GO BACK" button +- Tap on "GO BACK" button +- [ ] Check the modal disappear +- Select any other indexed address +- [ ] Check the modal appear +- Tap outside modal area +- [ ] Check the modal disappear +- Select any other indexed address +- Tap on "CONFIRM NEW ADDRESS" +- [ ] Check for the new address is set on "Registered Address" field +- Tap on "Registered Address" +- [ ] Check for the correct indexed address is selected +- [ ] Dismiss the modal +- Tap on "Unregister contract" CTA +- [ ] Check for a new "Unregister Nano Contract" modal appear +- [ ] Check "Unregister Nano Contract" modal has: + - A message asking the user to be sure of unregistration, + - A button "YES, UNREGISTER CONTRACT", and + - A button "NO, GO BACK" +- Tap on "NO, GO BACK" button +- [ ] Check the modal disappear +- Tap on "Unregister contract" CTA again +- Tap on "YES, UNREGISTER CONTRACT" +- [ ] Check the *Dashboard* screen is on focus +- [ ] Check the Nano Contract list is empty + - It shows title "No Nano Contracts" + +### Inspect Nano Contract Transaction +- [ ] Register the Nano Contract `00004f1a3b07f0ffc7ced77a19ae6d4407991a15a281c0ddaf3d6e04a56eba78` +- Go the *Nano Contract Details* screen +- [ ] Check there are many transactions +- [ ] Check a transaction item has: + - An eclipsed Transaction ID value in bold format + - A method name for the transaction + - A humanized timestamp +- [ ] Check any item is actionable +- [ ] Check the transaction list is ordered from newest to oldest +- [ ] Check the oldest transaction is the `initialize` +- Tap on `initialize` transaction +- [ ] Check the *Nano Contract Transaction* screen is on focus +- [ ] Check there is a "Transaction ID" field with eclipsed value as a header +- [ ] Check the header is expanded +- [ ] Check there is a label with value "Executed" in green +- [ ] Check there is a "Transaction ID" field with eclipsed value +- [ ] Check there is a "Nano Contract ID" field with eclipsed value +- [ ] Check there is a "Blueprint Method" field and value +- [ ] Check there ia a "Date and Time" field and value +- [ ] Check there is a "Caller" field with eclipsed value +- [ ] Check there is a "See transaction details" CTA +- Tap on any area of the header to shrink it +- Tap on "Transaction ID" header to expand it +- Tap on "See transaction details" CTA + - A browser app should open in the explorer page of the Transaction and it should contains all Transaction details information, including the arguments of the method call +- Go back to the wallet +- [ ] Check there is a content area under the header with title "No Actions" +- Go back to *Nano Contract Details* screen +- Tap on any bet +- [ ] Check the *Nano Contract Transaction* screen is on focus +- [ ] Check there is an action as a content that has: + - A "Deposit" text prefix + - An Input ID with eclipsed value + - An action amount + +### Change network and reset registered Nano Contracts +- Go to *Network Settings* screen +- Customize it to `testnet` network +- Go back to *Dashboard* screen +- Select "Nano Contracts" option if not selected +- [ ] Check there is no Nano Contract registered +- Customize network to `nano-testnet` network +- Set "Node URL" to value `https://node1.nano-testnet.hathor.network/v1a/` +- Set "Transaction Mining Service URL" to value `https://txmining.nano-testnet.hathor.network/` +- Clean "Wallet Service URL (optional)" and "Wallet Service WS URL (optional)" if present +- Tap on "SEND" +- Go back to *Dashboard* screen +- Select "Nano Contracts" option if not selected +- [ ] Check there is no Nano Contract registered