From 5b16276bbcfe778c6f637ba5b86c95f16aa8f94b Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Mon, 23 Oct 2023 15:43:30 +0100 Subject: [PATCH 01/23] chore: remove legacy comment --- src/sagas/networkSettings.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sagas/networkSettings.js b/src/sagas/networkSettings.js index 9521e0597..00fe6de4f 100644 --- a/src/sagas/networkSettings.js +++ b/src/sagas/networkSettings.js @@ -138,7 +138,6 @@ export function* updateNetworkSettings(action) { try { network = yield call(getFullnodeNetwork); } catch (err) { - // NOTE: Keep the console? console.error('Error calling the fullnode while trying to get network details in updateNetworkSettings effect..', err); rollbackConfigUrls(backupUrl); yield put(networkSettingsUpdateFailure()); From 300e4b4494cc701042bc9b5b84e5950bb489684a Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Mon, 23 Oct 2023 15:42:21 +0100 Subject: [PATCH 02/23] refactor: rename action to NETWORKSETTINGS_UPDATE_REQUEST --- __tests__/sagas/networkSettings.test.ts | 46 +++++++++---------- src/actions.js | 7 +-- src/reducers/reducer.js | 6 +-- src/sagas/networkSettings.js | 12 ++++- .../CustomNetworkSettingsScreen.js | 4 +- 5 files changed, 42 insertions(+), 33 deletions(-) diff --git a/__tests__/sagas/networkSettings.test.ts b/__tests__/sagas/networkSettings.test.ts index 41f5e707c..33ca7839d 100644 --- a/__tests__/sagas/networkSettings.test.ts +++ b/__tests__/sagas/networkSettings.test.ts @@ -4,7 +4,7 @@ import { all, effectTypes, fork } from 'redux-saga/effects'; import createSagaMiddleware, { END, runSaga } from 'redux-saga'; import { applyMiddleware, createStore } from 'redux'; import { reducer } from '../../src/reducers/reducer'; -import { networkSettingsUpdate, networkSettingsUpdateSuccess, reloadWalletRequested, types } from '../../src/actions'; +import { networkSettingsUpdateRequest, networkSettingsUpdateSuccess, reloadWalletRequested, types } from '../../src/actions'; import AsyncStorage from '@react-native-async-storage/async-storage'; import { networkSettingsKeyMap } from '../../src/constants'; import { STORE } from '../../src/store'; @@ -46,47 +46,47 @@ describe('updateNetworkSettings', () => { const task = middleware.run(defaultSaga); Promise.resolve() - .then(() => store.dispatch(networkSettingsUpdate(null))) - .then(() => store.dispatch(networkSettingsUpdate(undefined))) - .then(() => store.dispatch(networkSettingsUpdate({}))) - .then(() => store.dispatch(networkSettingsUpdate({ explorerUrl: undefined }))) - .then(() => store.dispatch(networkSettingsUpdate({ explorerUrl: null }))) - .then(() => store.dispatch(networkSettingsUpdate({ explorerUrl: '' }))) - .then(() => store.dispatch(networkSettingsUpdate({ explorerUrl: 1 }))) - .then(() => store.dispatch(networkSettingsUpdate({ explorerUrl: 'invalid.url.com' }))) + .then(() => store.dispatch(networkSettingsUpdateRequest(null))) + .then(() => store.dispatch(networkSettingsUpdateRequest(undefined))) + .then(() => store.dispatch(networkSettingsUpdateRequest({}))) + .then(() => store.dispatch(networkSettingsUpdateRequest({ explorerUrl: undefined }))) + .then(() => store.dispatch(networkSettingsUpdateRequest({ explorerUrl: null }))) + .then(() => store.dispatch(networkSettingsUpdateRequest({ explorerUrl: '' }))) + .then(() => store.dispatch(networkSettingsUpdateRequest({ explorerUrl: 1 }))) + .then(() => store.dispatch(networkSettingsUpdateRequest({ explorerUrl: 'invalid.url.com' }))) // explorerUrl is valid, however it must have at least nodeUrl - .then(() => store.dispatch(networkSettingsUpdate({ explorerUrl: 'http://localhost:8081/' }))) + .then(() => store.dispatch(networkSettingsUpdateRequest({ explorerUrl: 'http://localhost:8081/' }))) // explorerUrl is valid, but explorerServiceUrl is empty - .then(() => store.dispatch(networkSettingsUpdate({ + .then(() => store.dispatch(networkSettingsUpdateRequest({ explorerUrl: 'http://localhost:8081/', explorerServiceUrl: '', }))) // explorerUrl is valid, but explorerServiceUrl is invalid - .then(() => store.dispatch(networkSettingsUpdate({ + .then(() => store.dispatch(networkSettingsUpdateRequest({ explorerUrl: 'http://localhost:8081/', explorerServiceUrl: 'invalid.url.com', }))) // explorer urls are valid, but nodeUrl is empty - .then(() => store.dispatch(networkSettingsUpdate({ + .then(() => store.dispatch(networkSettingsUpdateRequest({ explorerUrl: 'http://localhost:8081/', explorerServiceUrl: 'http://localhost:8082/', nodeUrl: '', }))) // explorer urls are valid, but nodeUrl is invalid - .then(() => store.dispatch(networkSettingsUpdate({ + .then(() => store.dispatch(networkSettingsUpdateRequest({ explorerUrl: 'http://localhost:8081/', explorerServiceUrl: 'http://localhost:8082/', nodeUrl: 'invalid.url.com' }))) // explorer and node urls are valid, but waletServiceUrl is invalid - .then(() => store.dispatch(networkSettingsUpdate({ + .then(() => store.dispatch(networkSettingsUpdateRequest({ explorerUrl: 'http://localhost:8081/', explorerServiceUrl: 'http://localhost:8082/', nodeUrl: 'http://localhost:3000/', walletServiceUrl: 'invalid.url.com' }))) // explorer, node, and wallet service urls are valid, but walletServiceWsUrl is empty - .then(() => store.dispatch(networkSettingsUpdate({ + .then(() => store.dispatch(networkSettingsUpdateRequest({ explorerUrl: 'http://localhost:8081/', explorerServiceUrl: 'http://localhost:8082/', nodeUrl: 'http://localhost:3000/', @@ -94,7 +94,7 @@ describe('updateNetworkSettings', () => { walletServiceWsUrl: '' }))) // explorer, node, and wallet service urls are valid, but walletServiceWsUrl is invalid - .then(() => store.dispatch(networkSettingsUpdate({ + .then(() => store.dispatch(networkSettingsUpdateRequest({ explorerUrl: 'http://localhost:8081/', explorerServiceUrl: 'http://localhost:8082/', nodeUrl: 'http://localhost:3000/', @@ -102,7 +102,7 @@ describe('updateNetworkSettings', () => { walletServiceWsUrl: 'invalid.url.com' }))) // all urls are valid, except nodeUrl - .then(() => store.dispatch(networkSettingsUpdate({ + .then(() => store.dispatch(networkSettingsUpdateRequest({ explorerUrl: 'http://localhost:8081/', explorerServiceUrl: 'http://localhost:8082/', nodeUrl: 'invalid.url.com', @@ -171,21 +171,21 @@ describe('updateNetworkSettings', () => { Promise.resolve() // calls getFullnodeNetwork - .then(() => store.dispatch(networkSettingsUpdate({ + .then(() => store.dispatch(networkSettingsUpdateRequest({ explorerUrl: 'http://localhost:8081/', explorerServiceUrl: 'http://localhost:8082/', nodeUrl: 'http://localhost:3000/', }))) // calls getWalletServiceNetwork // it will fail because is lacking the walletServiceWsUrl - .then(() => store.dispatch(networkSettingsUpdate({ + .then(() => store.dispatch(networkSettingsUpdateRequest({ explorerUrl: 'http://localhost:8081/', explorerServiceUrl: 'http://localhost:8082/', nodeUrl: 'http://localhost:3000/', walletServiceUrl: 'http://localhost:8080/' }))) // calls getWalletServiceNetwork - .then(() => store.dispatch(networkSettingsUpdate({ + .then(() => store.dispatch(networkSettingsUpdateRequest({ explorerUrl: 'http://localhost:8081/', explorerServiceUrl: 'http://localhost:8082/', nodeUrl: 'http://localhost:3000/', @@ -193,14 +193,14 @@ describe('updateNetworkSettings', () => { walletServiceWsUrl: 'ws://ws.localhost:4040/' }))) // calls getFullnodeNetwork - .then(() => store.dispatch(networkSettingsUpdate({ + .then(() => store.dispatch(networkSettingsUpdateRequest({ explorerUrl: 'http://localhost:8081/', explorerServiceUrl: 'http://localhost:8082/', nodeUrl: 'http://localhost:3000/', }))) // calls getFullnodeNetwork // here the getFullnodeNetwork rejects throwing an error - .then(() => store.dispatch(networkSettingsUpdate({ + .then(() => store.dispatch(networkSettingsUpdateRequest({ explorerUrl: 'http://localhost:8081/', explorerServiceUrl: 'http://localhost:8082/', nodeUrl: 'http://localhost:3000/', diff --git a/src/actions.js b/src/actions.js index 89574e30a..48c0d3f7d 100644 --- a/src/actions.js +++ b/src/actions.js @@ -120,7 +120,8 @@ export const types = { // NOTE: These actions follows a taxonomy that should be applied // to all other actions. // See: https://github.com/HathorNetwork/hathor-wallet-mobile/issues/334 - NETWORKSETTINGS_UPDATE: 'NETWORK_SETTINGS_UPDATE', + /* It delivers the user's network settings input from the form. */ + NETWORKSETTINGS_UPDATE_REQUEST: 'NETWORK_SETTINGS_UPDATE_REQUEST', NETWORKSETTINGS_UPDATE_SUCCESS: 'NETWORK_SETTINGS_UPDATE_SUCCESS', NETWORKSETTINGS_UPDATE_READY: 'NETWORK_SETTINGS_UPDATE_READY', NETWORKSETTINGS_UPDATE_FAILURE: 'NETWORK_SETTINGS_UPDATE_FAILURE', @@ -861,8 +862,8 @@ export const setWCConnectionFailed = (failed) => ({ * walletServiceWsUrl?: string * }} customNetworkRequest Request input */ -export const networkSettingsUpdate = (customNetworkRequest) => ({ - type: types.NETWORKSETTINGS_UPDATE, +export const networkSettingsUpdateRequest = (customNetworkRequest) => ({ + type: types.NETWORKSETTINGS_UPDATE_REQUEST, payload: customNetworkRequest, }); diff --git a/src/reducers/reducer.js b/src/reducers/reducer.js index 9dd21c39c..82a66a570 100644 --- a/src/reducers/reducer.js +++ b/src/reducers/reducer.js @@ -345,8 +345,8 @@ export const reducer = (state = initialState, action) => { return onSetWalletConnectSessions(state, action); case types.WC_SET_CONNECTION_FAILED: return onSetWCConnectionFailed(state, action); - case types.NETWORKSETTINGS_UPDATE: - return onNetworkSettingsUpdate(state); + case types.NETWORKSETTINGS_UPDATE_REQUEST: + return onNetworkSettingsUpdateRequest(state); case types.NETWORKSETTINGS_UPDATE_SUCCESS: return onNetworkSettingsUpdateSucess(state, action); case types.NETWORKSETTINGS_UPDATE_READY: @@ -1086,7 +1086,7 @@ export const onSetWCConnectionFailed = (state, { payload }) => ({ * @param {Object} action.payload The network settings emitted in saga * @see updateNetworkSettings */ -export const onNetworkSettingsUpdate = (state) => ({ +export const onNetworkSettingsUpdateRequest = (state) => ({ ...state, networkSettingsStatus: NETWORKSETTINGS_STATUS.LOADING, }); diff --git a/src/sagas/networkSettings.js b/src/sagas/networkSettings.js index 00fe6de4f..f8939d764 100644 --- a/src/sagas/networkSettings.js +++ b/src/sagas/networkSettings.js @@ -3,7 +3,15 @@ import { config } from '@hathor/wallet-lib'; import { isEmpty } from 'lodash'; import AsyncStorage from '@react-native-async-storage/async-storage'; import { t } from 'ttag'; -import { featureToggleUpdate, networkSettingsUpdateErrors, networkSettingsUpdateFailure, networkSettingsUpdateReady, networkSettingsUpdateSuccess, reloadWalletRequested, types } from '../actions'; +import { + featureToggleUpdate, + networkSettingsUpdateErrors, + networkSettingsUpdateFailure, + networkSettingsUpdateReady, + networkSettingsUpdateSuccess, + reloadWalletRequested, + types +} from '../actions'; import { HTTP_REQUEST_TIMEOUT, NETWORK, networkSettingsKeyMap, NETWORK_TESTNET, STAGE, STAGE_DEV_PRIVNET, STAGE_TESTNET, WALLET_SERVICE_REQUEST_TIMEOUT } from '../constants'; import { getFullnodeNetwork, getWalletServiceNetwork } from './helpers'; import { STORE } from '../store'; @@ -250,7 +258,7 @@ export function* cleanNetworkSettings() { export function* saga() { yield all([ takeEvery(types.START_WALLET_SUCCESS, initNetworkSettings), - takeEvery(types.NETWORKSETTINGS_UPDATE, updateNetworkSettings), + takeEvery(types.NETWORKSETTINGS_UPDATE_REQUEST, updateNetworkSettings), takeEvery(types.NETWORKSETTINGS_UPDATE_SUCCESS, persistNetworkSettings), takeEvery(types.RESET_WALLET, cleanNetworkSettings), ]); diff --git a/src/screens/NetworkSettings/CustomNetworkSettingsScreen.js b/src/screens/NetworkSettings/CustomNetworkSettingsScreen.js index ef7f810ef..34b362bba 100644 --- a/src/screens/NetworkSettings/CustomNetworkSettingsScreen.js +++ b/src/screens/NetworkSettings/CustomNetworkSettingsScreen.js @@ -3,7 +3,7 @@ import { View, Text, StyleSheet, Image } from 'react-native'; import { useDispatch, useSelector } from 'react-redux'; import { t } from 'ttag'; import { isEmpty } from 'lodash'; -import { networkSettingsUpdate, networkSettingsUpdateErrors, networkSettingsUpdateReady } from '../../actions'; +import { networkSettingsUpdateRequest, networkSettingsUpdateErrors, networkSettingsUpdateReady } from '../../actions'; import FeedbackModal from '../../components/FeedbackModal'; import HathorHeader from '../../components/HathorHeader'; import NewHathorButton from '../../components/NewHathorButton'; @@ -152,7 +152,7 @@ export const CustomNetworkSettingsScreen = ({ navigation }) => { return; } - dispatch(networkSettingsUpdate(formModel)); + dispatch(networkSettingsUpdateRequest(formModel)); }; useEffect(() => { From d14be82a1605cd7d84c84bd4b708d9cf71e90d26 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Mon, 23 Oct 2023 18:49:29 +0100 Subject: [PATCH 03/23] feat: add action NETWORKSETTINGS_UPDATE_STATE --- src/actions.js | 19 +++++++++++++++++++ src/reducers/reducer.js | 11 +++++++++++ src/sagas/networkSettings.js | 3 ++- 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/actions.js b/src/actions.js index 48c0d3f7d..3112630ad 100644 --- a/src/actions.js +++ b/src/actions.js @@ -122,6 +122,8 @@ export const types = { // See: https://github.com/HathorNetwork/hathor-wallet-mobile/issues/334 /* It delivers the user's network settings input from the form. */ NETWORKSETTINGS_UPDATE_REQUEST: 'NETWORK_SETTINGS_UPDATE_REQUEST', + /* It updates the redux state */ + NETWORKSETTINGS_UPDATE_STATE: 'NETWORKSETTINGS_UPDATE_STATE', NETWORKSETTINGS_UPDATE_SUCCESS: 'NETWORK_SETTINGS_UPDATE_SUCCESS', NETWORKSETTINGS_UPDATE_READY: 'NETWORK_SETTINGS_UPDATE_READY', NETWORKSETTINGS_UPDATE_FAILURE: 'NETWORK_SETTINGS_UPDATE_FAILURE', @@ -867,6 +869,23 @@ export const networkSettingsUpdateRequest = (customNetworkRequest) => ({ payload: customNetworkRequest, }); +/** + * Emits the custom network settings to update the redux store. + * @param {{ + * stage: string, + * network: string, + * nodeUrl: string, + * explorerUrl: string, + * explorerServiceUrl: string, + * walletServiceUrl?: string + * walletServiceWsUrl?: string + * }} customNetwork Settings to persist + */ +export const networkSettingsUpdateState = (customNetwork) => ({ + type: types.NETWORKSETTINGS_UPDATE_STATE, + payload: customNetwork, +}); + /** * Emits the custom network settings to be stored and persisted. * @param {{ diff --git a/src/reducers/reducer.js b/src/reducers/reducer.js index 82a66a570..7e694e891 100644 --- a/src/reducers/reducer.js +++ b/src/reducers/reducer.js @@ -347,6 +347,8 @@ export const reducer = (state = initialState, action) => { return onSetWCConnectionFailed(state, action); case types.NETWORKSETTINGS_UPDATE_REQUEST: return onNetworkSettingsUpdateRequest(state); + case types.NETWORKSETTINGS_UPDATE_STATE: + return onNetworkSettingsUpdateState(state, action); case types.NETWORKSETTINGS_UPDATE_SUCCESS: return onNetworkSettingsUpdateSucess(state, action); case types.NETWORKSETTINGS_UPDATE_READY: @@ -1091,6 +1093,15 @@ export const onNetworkSettingsUpdateRequest = (state) => ({ networkSettingsStatus: NETWORKSETTINGS_STATUS.LOADING, }); +/** + * @param {Object} action.payload The network settings emitted in saga + * @see networkSettingsUpdateState customNetwork + */ +export const onNetworkSettingsUpdateState = (state, { payload }) => ({ + ...state, + networkSettings: payload, +}); + /** * @param {Object} action.payload The network settings emitted in saga * @see updateNetworkSettings diff --git a/src/sagas/networkSettings.js b/src/sagas/networkSettings.js index f8939d764..863756d6c 100644 --- a/src/sagas/networkSettings.js +++ b/src/sagas/networkSettings.js @@ -8,6 +8,7 @@ import { networkSettingsUpdateErrors, networkSettingsUpdateFailure, networkSettingsUpdateReady, + networkSettingsUpdateState, networkSettingsUpdateSuccess, reloadWalletRequested, types @@ -24,7 +25,7 @@ import { STORE } from '../store'; export function* initNetworkSettings() { const customNetwork = STORE.getItem(networkSettingsKeyMap.networkSettings); if (customNetwork) { - yield put(networkSettingsUpdateSuccess(customNetwork)); + yield put(networkSettingsUpdateState(customNetwork)); } } From 7ffd6eb93a8a03191dcc63f5e64424a15020d8f8 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Mon, 23 Oct 2023 19:27:20 +0100 Subject: [PATCH 04/23] feat: add action NETWORKSETTINGS_PERSIST_STORE --- src/actions.js | 19 +++++++++++++++++++ src/reducers/reducer.js | 13 +++++++++++++ src/sagas/networkSettings.js | 14 ++++++++++---- .../NetworkPreSettingsScreen.js | 6 +++--- 4 files changed, 45 insertions(+), 7 deletions(-) diff --git a/src/actions.js b/src/actions.js index 3112630ad..7b78a2717 100644 --- a/src/actions.js +++ b/src/actions.js @@ -124,6 +124,8 @@ export const types = { NETWORKSETTINGS_UPDATE_REQUEST: 'NETWORK_SETTINGS_UPDATE_REQUEST', /* It updates the redux state */ NETWORKSETTINGS_UPDATE_STATE: 'NETWORKSETTINGS_UPDATE_STATE', + /* It persists the complete structure of network settings in the app storage and updates the redux store. */ + NETWORKSETTINGS_PERSIST_STORE: 'NETWORKSETTINGS_PERSIST_STORE', NETWORKSETTINGS_UPDATE_SUCCESS: 'NETWORK_SETTINGS_UPDATE_SUCCESS', NETWORKSETTINGS_UPDATE_READY: 'NETWORK_SETTINGS_UPDATE_READY', NETWORKSETTINGS_UPDATE_FAILURE: 'NETWORK_SETTINGS_UPDATE_FAILURE', @@ -886,6 +888,23 @@ export const networkSettingsUpdateState = (customNetwork) => ({ payload: customNetwork, }); +/** + * Emits the custom network settings to persist in the app storage and update the redux store. + * @param {{ + * stage: string, + * network: string, + * nodeUrl: string, + * explorerUrl: string, + * explorerServiceUrl: string, + * walletServiceUrl?: string + * walletServiceWsUrl?: string + * }} customNetwork Settings to persist + */ +export const networkSettingsPersistStore = (customNetwork) => ({ + type: types.NETWORKSETTINGS_PERSIST_STORE, + payload: customNetwork, +}); + /** * Emits the custom network settings to be stored and persisted. * @param {{ diff --git a/src/reducers/reducer.js b/src/reducers/reducer.js index 7e694e891..18064a8d0 100644 --- a/src/reducers/reducer.js +++ b/src/reducers/reducer.js @@ -349,6 +349,8 @@ export const reducer = (state = initialState, action) => { return onNetworkSettingsUpdateRequest(state); case types.NETWORKSETTINGS_UPDATE_STATE: return onNetworkSettingsUpdateState(state, action); + case types.NETWORKSETTINGS_PERSIST_STORE: + return onNetworkSettingsPersistStore(state, action); case types.NETWORKSETTINGS_UPDATE_SUCCESS: return onNetworkSettingsUpdateSucess(state, action); case types.NETWORKSETTINGS_UPDATE_READY: @@ -1102,6 +1104,17 @@ export const onNetworkSettingsUpdateState = (state, { payload }) => ({ networkSettings: payload, }); +/** + * @param {Object} action.payload The network settings emitted in saga + * @see networkSettingsPersistStore customNetwork + */ +export const onNetworkSettingsPersistStore = (state, { payload }) => ({ + ...state, + networkSettings: payload, + networkSettingsStatus: NETWORKSETTINGS_STATUS.LOADING, +}); + + /** * @param {Object} action.payload The network settings emitted in saga * @see updateNetworkSettings diff --git a/src/sagas/networkSettings.js b/src/sagas/networkSettings.js index 863756d6c..0f937136f 100644 --- a/src/sagas/networkSettings.js +++ b/src/sagas/networkSettings.js @@ -5,6 +5,7 @@ import AsyncStorage from '@react-native-async-storage/async-storage'; import { t } from 'ttag'; import { featureToggleUpdate, + networkSettingsPersistStore, networkSettingsUpdateErrors, networkSettingsUpdateFailure, networkSettingsUpdateReady, @@ -179,7 +180,7 @@ export function* updateNetworkSettings(action) { walletServiceWsUrl, }; - yield put(networkSettingsUpdateSuccess(customNetwork)); + yield put(networkSettingsPersistStore(customNetwork)); } /** @@ -225,8 +226,13 @@ function invalidUrl(tryUrl) { export function* persistNetworkSettings(action) { // persists after reducer being updated const networkSettings = action.payload; - const strNetworkSettings = JSON.stringify(networkSettings); - yield call(AsyncStorage.setItem, networkSettingsKeyMap.networkSettings, strNetworkSettings); + try { + STORE.setItem(networkSettingsKeyMap.networkSettings, networkSettings); + } catch (err) { + console.error('Error while persisting the custom network settings.', err); + yield put(networkSettingsUpdateFailure()); + return; + } // trigger toggle update to be managed by featureToggle saga yield put(featureToggleUpdate()); @@ -260,7 +266,7 @@ export function* saga() { yield all([ takeEvery(types.START_WALLET_SUCCESS, initNetworkSettings), takeEvery(types.NETWORKSETTINGS_UPDATE_REQUEST, updateNetworkSettings), - takeEvery(types.NETWORKSETTINGS_UPDATE_SUCCESS, persistNetworkSettings), + takeEvery(types.NETWORKSETTINGS_PERSIST_STORE, persistNetworkSettings), takeEvery(types.RESET_WALLET, cleanNetworkSettings), ]); } diff --git a/src/screens/NetworkSettings/NetworkPreSettingsScreen.js b/src/screens/NetworkSettings/NetworkPreSettingsScreen.js index df7f1df63..1793fc7e5 100644 --- a/src/screens/NetworkSettings/NetworkPreSettingsScreen.js +++ b/src/screens/NetworkSettings/NetworkPreSettingsScreen.js @@ -18,7 +18,7 @@ import HathorHeader from '../../components/HathorHeader'; import NewHathorButton from '../../components/NewHathorButton'; import Spinner from '../../components/Spinner'; import FeedbackModal from '../../components/FeedbackModal'; -import { networkSettingsUpdateReady, networkSettingsUpdateSuccess } from '../../actions'; +import { networkSettingsPersistStore, networkSettingsUpdateReady } from '../../actions'; import { PRE_SETTINGS_MAINNET, PRE_SETTINGS_TESTNET } from '../../constants'; import { CustomNetworkSettingsNav } from './CustomNetworkSettingsScreen'; import { feedbackFailedText, feedbackLoadingText, hasFailed, isLoading } from './helper'; @@ -76,8 +76,8 @@ export const NetworkPreSettingsNav = Symbol('NetworkPreSettings').toString(); export function NetworkPreSettingsScreen({ navigation }) { const dispatch = useDispatch(); const networkSettingsStatus = useSelector((state) => state.networkSettingsStatus); - const setMainnetNetwork = () => dispatch(networkSettingsUpdateSuccess(PRE_SETTINGS_MAINNET)); - const setTestnetNetwork = () => dispatch(networkSettingsUpdateSuccess(PRE_SETTINGS_TESTNET)); + const setMainnetNetwork = () => dispatch(networkSettingsPersistStore(PRE_SETTINGS_MAINNET)); + const setTestnetNetwork = () => dispatch(networkSettingsPersistStore(PRE_SETTINGS_TESTNET)); const setCustomNetwork = () => { navigation.push(CustomNetworkSettingsNav); }; From 461f1d40302d6df08dd1dcaff821d76139bc0ed2 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Mon, 23 Oct 2023 20:28:17 +0100 Subject: [PATCH 05/23] feat: add action NETWORKSETTINGS_UPDATE_WAITING --- src/actions.js | 11 +++++++++++ src/constants.js | 1 + src/reducers/reducer.js | 9 +++++++++ src/sagas/networkSettings.js | 3 ++- 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/actions.js b/src/actions.js index 7b78a2717..dc94c9d9f 100644 --- a/src/actions.js +++ b/src/actions.js @@ -126,6 +126,8 @@ export const types = { NETWORKSETTINGS_UPDATE_STATE: 'NETWORKSETTINGS_UPDATE_STATE', /* It persists the complete structure of network settings in the app storage and updates the redux store. */ NETWORKSETTINGS_PERSIST_STORE: 'NETWORKSETTINGS_PERSIST_STORE', + /* It indicates the persistence is complete and the wallet will be reloaded. */ + NETWORKSETTINGS_UPDATE_WAITING: 'NETWORKSETTINGS_UPDATE_WAITING', NETWORKSETTINGS_UPDATE_SUCCESS: 'NETWORK_SETTINGS_UPDATE_SUCCESS', NETWORKSETTINGS_UPDATE_READY: 'NETWORK_SETTINGS_UPDATE_READY', NETWORKSETTINGS_UPDATE_FAILURE: 'NETWORK_SETTINGS_UPDATE_FAILURE', @@ -905,6 +907,15 @@ export const networkSettingsPersistStore = (customNetwork) => ({ payload: customNetwork, }); + +/** + * Emits the waiting signal after persist the custom network. + * It means the wallet will reload. + */ +export const networkSettingsUpdateWaiting = () => ({ + type: types.NETWORKSETTINGS_UPDATE_WAITING, +}); + /** * Emits the custom network settings to be stored and persisted. * @param {{ diff --git a/src/constants.js b/src/constants.js index 6d50c7055..644912f50 100644 --- a/src/constants.js +++ b/src/constants.js @@ -209,6 +209,7 @@ export const NETWORKSETTINGS_STATUS = { READY: 'ready', FAILED: 'failed', LOADING: 'loading', + WAITING: 'waiting', }; /** diff --git a/src/reducers/reducer.js b/src/reducers/reducer.js index 18064a8d0..7a4541b08 100644 --- a/src/reducers/reducer.js +++ b/src/reducers/reducer.js @@ -351,6 +351,8 @@ export const reducer = (state = initialState, action) => { return onNetworkSettingsUpdateState(state, action); case types.NETWORKSETTINGS_PERSIST_STORE: return onNetworkSettingsPersistStore(state, action); + case types.NETWORKSETTINGS_UPDATE_WAITING: + return onNetworkSettingsUpdateWaiting(state); case types.NETWORKSETTINGS_UPDATE_SUCCESS: return onNetworkSettingsUpdateSucess(state, action); case types.NETWORKSETTINGS_UPDATE_READY: @@ -1114,6 +1116,13 @@ export const onNetworkSettingsPersistStore = (state, { payload }) => ({ networkSettingsStatus: NETWORKSETTINGS_STATUS.LOADING, }); +/** + * Set `WAITING` state on network settings status. + */ +export const onNetworkSettingsUpdateWaiting = (state) => ({ + ...state, + networkSettingsStatus: NETWORKSETTINGS_STATUS.WAITING, +}); /** * @param {Object} action.payload The network settings emitted in saga diff --git a/src/sagas/networkSettings.js b/src/sagas/networkSettings.js index 0f937136f..008d9edc8 100644 --- a/src/sagas/networkSettings.js +++ b/src/sagas/networkSettings.js @@ -11,6 +11,7 @@ import { networkSettingsUpdateReady, networkSettingsUpdateState, networkSettingsUpdateSuccess, + networkSettingsUpdateWaiting, reloadWalletRequested, types } from '../actions'; @@ -248,7 +249,7 @@ export function* persistNetworkSettings(action) { yield put(reloadWalletRequested()); } - yield put(networkSettingsUpdateReady()); + yield put(networkSettingsUpdateWaiting()); } /** From e938aeab83272fe21241727db4ac95fa899f7d4b Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Mon, 23 Oct 2023 20:44:44 +0100 Subject: [PATCH 06/23] refactor: action NETWORKSETTINGS_UPDATE_SUCCESS --- src/actions.js | 16 ++++------------ src/constants.js | 1 + src/reducers/reducer.js | 10 ++++------ src/sagas/networkSettings.js | 11 +++++++---- .../CustomNetworkSettingsScreen.js | 19 +++++++++++++++++++ .../NetworkPreSettingsScreen.js | 12 +++++++++++- src/screens/NetworkSettings/helper.js | 10 ++++++++++ 7 files changed, 56 insertions(+), 23 deletions(-) diff --git a/src/actions.js b/src/actions.js index dc94c9d9f..5d195758d 100644 --- a/src/actions.js +++ b/src/actions.js @@ -128,6 +128,7 @@ export const types = { NETWORKSETTINGS_PERSIST_STORE: 'NETWORKSETTINGS_PERSIST_STORE', /* It indicates the persistence is complete and the wallet will be reloaded. */ NETWORKSETTINGS_UPDATE_WAITING: 'NETWORKSETTINGS_UPDATE_WAITING', + /* It indicates the update is complete after wallet reloads. */ NETWORKSETTINGS_UPDATE_SUCCESS: 'NETWORK_SETTINGS_UPDATE_SUCCESS', NETWORKSETTINGS_UPDATE_READY: 'NETWORK_SETTINGS_UPDATE_READY', NETWORKSETTINGS_UPDATE_FAILURE: 'NETWORK_SETTINGS_UPDATE_FAILURE', @@ -917,20 +918,11 @@ export const networkSettingsUpdateWaiting = () => ({ }); /** - * Emits the custom network settings to be stored and persisted. - * @param {{ - * stage: string, - * network: string, - * nodeUrl: string, - * explorerUrl: string, - * explorerServiceUrl: string, - * walletServiceUrl?: string - * walletServiceWsUrl?: string - * }} customNetwork Settings to persist + * Emits the success signal after wallet reloads. + * It servers as hook for the frontend to provide feedback for the user. */ -export const networkSettingsUpdateSuccess = (customNetwork) => ({ +export const networkSettingsUpdateSuccess = () => ({ type: types.NETWORKSETTINGS_UPDATE_SUCCESS, - payload: customNetwork, }); /** diff --git a/src/constants.js b/src/constants.js index 644912f50..302eb4e6b 100644 --- a/src/constants.js +++ b/src/constants.js @@ -210,6 +210,7 @@ export const NETWORKSETTINGS_STATUS = { FAILED: 'failed', LOADING: 'loading', WAITING: 'waiting', + SUCCESSFUL: 'successful', }; /** diff --git a/src/reducers/reducer.js b/src/reducers/reducer.js index 7a4541b08..045a4be17 100644 --- a/src/reducers/reducer.js +++ b/src/reducers/reducer.js @@ -354,7 +354,7 @@ export const reducer = (state = initialState, action) => { case types.NETWORKSETTINGS_UPDATE_WAITING: return onNetworkSettingsUpdateWaiting(state); case types.NETWORKSETTINGS_UPDATE_SUCCESS: - return onNetworkSettingsUpdateSucess(state, action); + return onNetworkSettingsUpdateSuccess(state); case types.NETWORKSETTINGS_UPDATE_READY: return onNetworkSettingsUpdateReady(state); case types.NETWORKSETTINGS_UPDATE_FAILURE: @@ -1125,13 +1125,11 @@ export const onNetworkSettingsUpdateWaiting = (state) => ({ }); /** - * @param {Object} action.payload The network settings emitted in saga - * @see updateNetworkSettings + * Set `SUCCESSFUL` state on network settings status. */ -export const onNetworkSettingsUpdateSucess = (state, { payload }) => ({ +export const onNetworkSettingsUpdateSuccess = (state) => ({ ...state, - networkSettings: payload, - networkSettingsStatus: NETWORKSETTINGS_STATUS.LOADING, + networkSettingsStatus: NETWORKSETTINGS_STATUS.SUCCESSFUL, }); /** diff --git a/src/sagas/networkSettings.js b/src/sagas/networkSettings.js index 008d9edc8..2276070e2 100644 --- a/src/sagas/networkSettings.js +++ b/src/sagas/networkSettings.js @@ -15,20 +15,23 @@ import { reloadWalletRequested, types } from '../actions'; -import { HTTP_REQUEST_TIMEOUT, NETWORK, networkSettingsKeyMap, NETWORK_TESTNET, STAGE, STAGE_DEV_PRIVNET, STAGE_TESTNET, WALLET_SERVICE_REQUEST_TIMEOUT } from '../constants'; +import { HTTP_REQUEST_TIMEOUT, NETWORK, networkSettingsKeyMap, NETWORKSETTINGS_STATUS, NETWORK_TESTNET, STAGE, STAGE_DEV_PRIVNET, STAGE_TESTNET, WALLET_SERVICE_REQUEST_TIMEOUT } from '../constants'; import { getFullnodeNetwork, getWalletServiceNetwork } from './helpers'; import { STORE } from '../store'; /** - * Initialize network settings saga. - * - * It looks up a stored network settings to update the redux state. + * Initialize the network settings saga when the wallet starts successfully. */ export function* initNetworkSettings() { const customNetwork = STORE.getItem(networkSettingsKeyMap.networkSettings); if (customNetwork) { yield put(networkSettingsUpdateState(customNetwork)); } + + const status = yield select((state) => state.networkSettingsStatus); + if (status === NETWORKSETTINGS_STATUS.WAITING) { + yield put(networkSettingsUpdateSuccess()); + } } /** diff --git a/src/screens/NetworkSettings/CustomNetworkSettingsScreen.js b/src/screens/NetworkSettings/CustomNetworkSettingsScreen.js index 34b362bba..623b863ad 100644 --- a/src/screens/NetworkSettings/CustomNetworkSettingsScreen.js +++ b/src/screens/NetworkSettings/CustomNetworkSettingsScreen.js @@ -10,13 +10,24 @@ import NewHathorButton from '../../components/NewHathorButton'; import SimpleInput from '../../components/SimpleInput'; import { NETWORKSETTINGS_STATUS } from '../../constants'; import errorIcon from '../../assets/images/icErrorBig.png'; +import checkIcon from '../../assets/images/icCheckBig.png'; import Spinner from '../../components/Spinner'; const customNetworkSettingsTitleText = t`Custom Network Settings`.toUpperCase(); const warningText = t`Any token outside mainnet network bear no value. Only change if you know what you are doing.`; const feedbackLoadingText = t`Updating custom network settings...`; +const feedbackSucceedText = t`Network settings customized with success.`; const feedbackFailedText = t`There was an error while customizing network settings. Please try again later.`; +/** + * Check if the network settings status is successful. + * @param {object} networkSettingsStatus - status from redux store + * @returns {boolean} - true if the status is successful, false otherwise + */ +const hasSucceed = (networkSettingsStatus) => { + return networkSettingsStatus === NETWORKSETTINGS_STATUS.SUCCESSFUL; +}; + /** * Check if the network settings status is failed. * @param {object} networkSettingsStatus - status from redux store @@ -183,6 +194,14 @@ export const CustomNetworkSettingsScreen = ({ navigation }) => { /> )} + {hasSucceed(networkSettingsStatus) && ( + )} + text={feedbackSucceedText} + onDismiss={handleFeedbackModalDismiss} + /> + )} + {hasFailed(networkSettingsStatus) && ( )} diff --git a/src/screens/NetworkSettings/NetworkPreSettingsScreen.js b/src/screens/NetworkSettings/NetworkPreSettingsScreen.js index 1793fc7e5..f87064a22 100644 --- a/src/screens/NetworkSettings/NetworkPreSettingsScreen.js +++ b/src/screens/NetworkSettings/NetworkPreSettingsScreen.js @@ -21,10 +21,12 @@ import FeedbackModal from '../../components/FeedbackModal'; import { networkSettingsPersistStore, networkSettingsUpdateReady } from '../../actions'; import { PRE_SETTINGS_MAINNET, PRE_SETTINGS_TESTNET } from '../../constants'; import { CustomNetworkSettingsNav } from './CustomNetworkSettingsScreen'; -import { feedbackFailedText, feedbackLoadingText, hasFailed, isLoading } from './helper'; +import { feedbackSucceedText, feedbackFailedText, feedbackLoadingText, hasFailed, isLoading, hasSucceed } from './helper'; import errorIcon from '../../assets/images/icErrorBig.png'; +import checkIcon from '../../assets/images/icCheckBig.png'; const presettingsTitleText = t`Network Pre-Settings`.toUpperCase(); + const styles = StyleSheet.create({ container: { flex: 1, @@ -100,6 +102,14 @@ export function NetworkPreSettingsScreen({ navigation }) { /> )} + {hasSucceed(networkSettingsStatus) && ( + )} + text={feedbackSucceedText} + onDismiss={handleFeedbackModalDismiss} + /> + )} + {hasFailed(networkSettingsStatus) && ( )} diff --git a/src/screens/NetworkSettings/helper.js b/src/screens/NetworkSettings/helper.js index 229d386dd..fd839906e 100644 --- a/src/screens/NetworkSettings/helper.js +++ b/src/screens/NetworkSettings/helper.js @@ -2,8 +2,18 @@ import { t } from 'ttag'; import { NETWORKSETTINGS_STATUS } from '../../constants'; export const feedbackLoadingText = t`Updating custom network settings...`; +export const feedbackSucceedText = t`Network settings customized with success.`; export const feedbackFailedText = t`There was an error while customizing network settings. Please try again later.`; +/** + * Check if the network settings status is successful. + * @param {object} networkSettingsStatus - status from redux store + * @returns {boolean} - true if the status is successful, false otherwise + */ +export const hasSucceed = (networkSettingsStatus) => { + return networkSettingsStatus === NETWORKSETTINGS_STATUS.SUCCESSFUL; +}; + /** * Check if the network settings status is failed. * @param {object} networkSettingsStatus - status from redux store From 46de5afee8c47ee5f752998fa7304069323e4ab4 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Tue, 24 Oct 2023 00:20:24 +0100 Subject: [PATCH 07/23] refactor: action NETWORKSETTINGS_UPDATE_INVALID - This action replaces the NETWORK_UPDATE_ERRORS --- src/actions.js | 11 +- src/reducers/reducer.js | 14 ++- src/sagas/networkSettings.js | 23 ++-- .../CustomNetworkSettingsScreen.js | 102 +++++++++--------- 4 files changed, 77 insertions(+), 73 deletions(-) diff --git a/src/actions.js b/src/actions.js index 5d195758d..f319990fa 100644 --- a/src/actions.js +++ b/src/actions.js @@ -130,9 +130,10 @@ export const types = { NETWORKSETTINGS_UPDATE_WAITING: 'NETWORKSETTINGS_UPDATE_WAITING', /* It indicates the update is complete after wallet reloads. */ NETWORKSETTINGS_UPDATE_SUCCESS: 'NETWORK_SETTINGS_UPDATE_SUCCESS', + /* It indicates the update request has invalid inputs. */ + NETWORKSETTINGS_UPDATE_INVALID: 'NETWORKSETTINGS_UPDATE_INVALID', NETWORKSETTINGS_UPDATE_READY: 'NETWORK_SETTINGS_UPDATE_READY', NETWORKSETTINGS_UPDATE_FAILURE: 'NETWORK_SETTINGS_UPDATE_FAILURE', - NETWORKSETTINGS_UPDATE_ERRORS: 'NETWORK_SETTINGS_UPDATE_ERRORS', }; export const featureToggleInitialized = () => ({ @@ -934,8 +935,8 @@ export const networkSettingsUpdateFailure = () => ({ }); /** - * Emits errors signal for custom network settings form representing - * invalid inputs. + * Emits invalid signal for custom network settings request inputs. + * It means the form should present the invalid message on the corresponding inputs. * @param {{ * message: string, * nodeUrl: string, @@ -945,8 +946,8 @@ export const networkSettingsUpdateFailure = () => ({ * walletServiceWsUrl?: string * }} errors The validation errors from custom network settings form */ -export const networkSettingsUpdateErrors = (errors) => ({ - type: types.NETWORKSETTINGS_UPDATE_ERRORS, +export const networkSettingsUpdateInvalid = (errors) => ({ + type: types.NETWORKSETTINGS_UPDATE_INVALID, payload: errors, }); diff --git a/src/reducers/reducer.js b/src/reducers/reducer.js index 045a4be17..2dff8cbce 100644 --- a/src/reducers/reducer.js +++ b/src/reducers/reducer.js @@ -207,7 +207,7 @@ const initialState = { ...FEATURE_TOGGLE_DEFAULTS, }, networkSettings: PRE_SETTINGS_MAINNET, - networkSettingsErrors: {}, + networkSettingsInvalid: {}, networkSettingsStatus: NETWORKSETTINGS_STATUS.READY, }; @@ -359,8 +359,8 @@ export const reducer = (state = initialState, action) => { return onNetworkSettingsUpdateReady(state); case types.NETWORKSETTINGS_UPDATE_FAILURE: return onNetworkSettingsUpdateFailure(state); - case types.NETWORKSETTINGS_UPDATE_ERRORS: - return onNetworkSettingsUpdateErrors(state, action); + case types.NETWORKSETTINGS_UPDATE_INVALID: + return onNetworkSettingsUpdateInvalid(state, action); default: return state; } @@ -1150,8 +1150,12 @@ export const onNetworkSettingsUpdateFailure = (state) => ({ networkSettingsStatus: NETWORKSETTINGS_STATUS.FAILED }); -export const onNetworkSettingsUpdateErrors = (state, { payload }) => ({ +/** + * @param {Object} action.payload The errors from network settings input validation + * @see networkSettingsUpdateInvalid errors + */ +export const onNetworkSettingsUpdateInvalid = (state, { payload }) => ({ ...state, - networkSettingsErrors: payload, + networkSettingsInvalid: payload, networkSettingsStatus: NETWORKSETTINGS_STATUS.READY, }); diff --git a/src/sagas/networkSettings.js b/src/sagas/networkSettings.js index 2276070e2..f8475d034 100644 --- a/src/sagas/networkSettings.js +++ b/src/sagas/networkSettings.js @@ -6,7 +6,7 @@ import { t } from 'ttag'; import { featureToggleUpdate, networkSettingsPersistStore, - networkSettingsUpdateErrors, + networkSettingsUpdateInvalid, networkSettingsUpdateFailure, networkSettingsUpdateReady, networkSettingsUpdateState, @@ -61,53 +61,50 @@ export function* updateNetworkSettings(action) { walletServiceWsUrl, } = action.payload || {}; - const errors = {}; + const invalidPayload = {}; // validates input emptyness if (isEmpty(action.payload)) { - errors.message = t`Custom Network Settings cannot be empty.`; + invalidPayload.message = t`Custom Network Settings cannot be empty.`; } // validates explorerUrl // - required // - should have a valid URL if (isEmpty(explorerUrl) || invalidUrl(explorerUrl)) { - errors.explorerUrl = t`explorerUrl should be a valid URL.`; + invalidPayload.explorerUrl = t`explorerUrl should be a valid URL.`; } // validates explorerServiceUrl // - required // - should have a valid URL if (isEmpty(explorerServiceUrl) || invalidUrl(explorerServiceUrl)) { - errors.explorerServiceUrl = t`explorerServiceUrl should be a valid URL.`; + invalidPayload.explorerServiceUrl = t`explorerServiceUrl should be a valid URL.`; } // validates nodeUrl // - required // - should have a valid URl if (isEmpty(nodeUrl) || invalidUrl(nodeUrl)) { - errors.nodeUrl = t`nodeUrl should be a valid URL.`; + invalidPayload.nodeUrl = t`nodeUrl should be a valid URL.`; } // validates walletServiceUrl // - optional // - should have a valid URL, if given if (walletServiceUrl && invalidUrl(walletServiceUrl)) { - errors.walletServiceUrl = t`walletServiceUrl should be a valid URL.`; + invalidPayload.walletServiceUrl = t`walletServiceUrl should be a valid URL.`; } // validates walletServiceWsUrl // - conditionally required // - should have a valid URL, if walletServiceUrl is given if (walletServiceUrl && invalidUrl(walletServiceWsUrl)) { - errors.walletServiceWsUrl = t`walletServiceWsUrl should be a valid URL.`; + invalidPayload.walletServiceWsUrl = t`walletServiceWsUrl should be a valid URL.`; } - // TODO: Refactor by segregating Failure from Errors - // - create networkSettingsUpdateErrors - // - implement reaction to networkSettingsUpdateFailure - yield put(networkSettingsUpdateErrors(errors)); - if (Object.keys(errors).length > 0) { + yield put(networkSettingsUpdateInvalid(invalidPayload)); + if (Object.keys(invalidPayload).length > 0) { return; } diff --git a/src/screens/NetworkSettings/CustomNetworkSettingsScreen.js b/src/screens/NetworkSettings/CustomNetworkSettingsScreen.js index 623b863ad..ae54cd23d 100644 --- a/src/screens/NetworkSettings/CustomNetworkSettingsScreen.js +++ b/src/screens/NetworkSettings/CustomNetworkSettingsScreen.js @@ -3,7 +3,7 @@ import { View, Text, StyleSheet, Image } from 'react-native'; import { useDispatch, useSelector } from 'react-redux'; import { t } from 'ttag'; import { isEmpty } from 'lodash'; -import { networkSettingsUpdateRequest, networkSettingsUpdateErrors, networkSettingsUpdateReady } from '../../actions'; +import { networkSettingsUpdateRequest, networkSettingsUpdateInvalid, networkSettingsUpdateReady } from '../../actions'; import FeedbackModal from '../../components/FeedbackModal'; import HathorHeader from '../../components/HathorHeader'; import NewHathorButton from '../../components/NewHathorButton'; @@ -45,34 +45,34 @@ const hasFailed = (networkSettingsStatus) => networkSettingsStatus === NETWORKSE const isLoading = (networkSettingsStatus) => networkSettingsStatus === NETWORKSETTINGS_STATUS.LOADING; /** - * Verifies if the errorModel of the form has an error message. + * Verifies if the invalidModel of the form has an error message. */ -function hasError(errorModel) { +function hasError(invalidModel) { return Object - .values({ ...errorModel }) + .values({ ...invalidModel }) .reduce((_hasError, currValue) => _hasError || !isEmpty(currValue), false); -} +}; /** - * Validates the formModel, returning the errorModel. - * If there is no error in the formModel, the errorModel is returned empty. + * Validates the formModel, returning the invalidModel. + * If there is no error in the formModel, the invalidModel is returned empty. */ function validate(formModel) { - const errorModel = {}; + const invalidModel = {}; if (!formModel.nodeUrl) { - errorModel.nodeUrl = t`nodeUrl is required.`; + invalidModel.nodeUrl = t`nodeUrl is required.`; } if (!formModel.explorerUrl) { - errorModel.explorerUrl = t`explorerUrl is required.`; + invalidModel.explorerUrl = t`explorerUrl is required.`; } if (!formModel.explorerServiceUrl) { - errorModel.explorerServiceUrl = t`explorerServiceUrl is required.`; + invalidModel.explorerServiceUrl = t`explorerServiceUrl is required.`; } - return errorModel; + return invalidModel; } const styles = StyleSheet.create({ @@ -114,7 +114,7 @@ export const CustomNetworkSettingsNav = Symbol('CustomNetworkSettings').toString export const CustomNetworkSettingsScreen = ({ navigation }) => { const dispatch = useDispatch(); const networkSettings = useSelector((state) => state.networkSettings); - const networkSettingsErrors = useSelector((state) => state.networkSettingsErrors); + const networkSettingsInvalid = useSelector((state) => state.networkSettingsInvalid); const networkSettingsStatus = useSelector((state) => state.networkSettingsStatus); const [formModel, setFormModel] = useState({ @@ -125,31 +125,33 @@ export const CustomNetworkSettingsScreen = ({ navigation }) => { walletServiceWsUrl: networkSettings.walletServiceWsUrl || '', }); - const [errorModel, setErrorModel] = useState({ - nodeUrl: networkSettingsErrors?.nodeUrl || '', - explorerUrl: networkSettingsErrors?.explorerUrl || '', - explorerServiceUrl: networkSettingsErrors?.explorerServiceUrl || '', - walletServiceUrl: networkSettingsErrors?.walletServiceUrl || '', - walletServiceWsUrl: networkSettingsErrors?.walletServiceWsUrl || '', + const [invalidModel, setInvalidModel] = useState({ + nodeUrl: networkSettingsInvalid?.nodeUrl || '', + explorerUrl: networkSettingsInvalid?.explorerUrl || '', + explorerServiceUrl: networkSettingsInvalid?.explorerServiceUrl || '', + walletServiceUrl: networkSettingsInvalid?.walletServiceUrl || '', + walletServiceWsUrl: networkSettingsInvalid?.walletServiceWsUrl || '', }); // eslint-disable-next-line max-len /* @param {'nodeUrl' | 'explorerUrl' | 'explorerServiceUrl' | 'walletServiceUrl' | 'walletServiceWsUrl' } name */ - const handleInputChange = (name) => (value) => { - // update error model - const errors = { ...errorModel }; - delete errors[name]; - setErrorModel(errors); - - // update form model - const form = { - ...formModel, - [name]: value, + const handleInputChange = (name) => { + return (value) => { + // update inalid 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)); }; - setFormModel(form); - - // validate form model and update error model - setErrorModel(validate(form)); }; const handleFeedbackModalDismiss = () => { @@ -157,9 +159,9 @@ export const CustomNetworkSettingsScreen = ({ navigation }) => { }; const handleSubmit = () => { - const errors = validate(formModel); - if (hasError(errors)) { - setErrorModel(errors); + const newInvalidModel = validate(formModel); + if (hasError(newInvalidModel)) { + setInvalidModel(newInvalidModel); return; } @@ -167,17 +169,17 @@ export const CustomNetworkSettingsScreen = ({ navigation }) => { }; useEffect(() => { - setErrorModel({ - nodeUrl: networkSettingsErrors?.nodeUrl || '', - explorerUrl: networkSettingsErrors?.explorerUrl || '', - explorerServiceUrl: networkSettingsErrors?.explorerServiceUrl || '', - walletServiceUrl: networkSettingsErrors?.walletServiceUrl || '', - walletServiceWsUrl: networkSettingsErrors?.walletServiceWsUrl || '', + setInvalidModel({ + nodeUrl: networkSettingsInvalid?.nodeUrl || '', + explorerUrl: networkSettingsInvalid?.explorerUrl || '', + explorerServiceUrl: networkSettingsInvalid?.explorerServiceUrl || '', + walletServiceUrl: networkSettingsInvalid?.walletServiceUrl || '', + walletServiceWsUrl: networkSettingsInvalid?.walletServiceWsUrl || '', }); - }, [networkSettingsErrors]); + }, [networkSettingsInvalid]); useEffect(() => function cleanUp() { - dispatch(networkSettingsUpdateErrors({})); + dispatch(networkSettingsUpdateInvalid({})); }, []); return ( @@ -219,7 +221,7 @@ export const CustomNetworkSettingsScreen = ({ navigation }) => { label={t`Node URL`} autoFocus onChangeText={handleInputChange('nodeUrl')} - error={errorModel.nodeUrl} + error={invalidModel.nodeUrl} value={formModel.nodeUrl} /> @@ -228,7 +230,7 @@ export const CustomNetworkSettingsScreen = ({ navigation }) => { label={t`Explorer URL`} autoFocus onChangeText={handleInputChange('explorerUrl')} - error={errorModel.explorerUrl} + error={invalidModel.explorerUrl} value={formModel.explorerUrl} /> @@ -237,7 +239,7 @@ export const CustomNetworkSettingsScreen = ({ navigation }) => { label={t`Explorer Service URL`} autoFocus onChangeText={handleInputChange('explorerServiceUrl')} - error={errorModel.explorerServiceUrl} + error={invalidModel.explorerServiceUrl} value={formModel.explorerServiceUrl} /> @@ -246,7 +248,7 @@ export const CustomNetworkSettingsScreen = ({ navigation }) => { label={t`Wallet Service URL (optional)`} autoFocus onChangeText={handleInputChange('walletServiceUrl')} - error={errorModel.walletServiceUrl} + error={invalidModel.walletServiceUrl} value={formModel.walletServiceUrl} /> @@ -255,13 +257,13 @@ export const CustomNetworkSettingsScreen = ({ navigation }) => { label={t`Wallet Service WS URL (optional)`} autoFocus onChangeText={handleInputChange('walletServiceWsUrl')} - error={errorModel.walletServiceWsUrl} + error={invalidModel.walletServiceWsUrl} value={formModel.walletServiceWsUrl} /> From 03ba63fc6a80a54f266466bdf0349336cf4c5bce Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Tue, 24 Oct 2023 00:44:16 +0100 Subject: [PATCH 08/23] refactor: action NETWORKSETTINGS_UPDATE_FAILURE --- src/actions.js | 4 +++- src/reducers/reducer.js | 3 +-- src/sagas/networkSettings.js | 9 ++++++++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/actions.js b/src/actions.js index f319990fa..6b679a566 100644 --- a/src/actions.js +++ b/src/actions.js @@ -132,8 +132,9 @@ export const types = { NETWORKSETTINGS_UPDATE_SUCCESS: 'NETWORK_SETTINGS_UPDATE_SUCCESS', /* It indicates the update request has invalid inputs. */ NETWORKSETTINGS_UPDATE_INVALID: 'NETWORKSETTINGS_UPDATE_INVALID', - NETWORKSETTINGS_UPDATE_READY: 'NETWORK_SETTINGS_UPDATE_READY', + /* It indicates the update request has failed. */ NETWORKSETTINGS_UPDATE_FAILURE: 'NETWORK_SETTINGS_UPDATE_FAILURE', + NETWORKSETTINGS_UPDATE_READY: 'NETWORK_SETTINGS_UPDATE_READY', }; export const featureToggleInitialized = () => ({ @@ -929,6 +930,7 @@ export const networkSettingsUpdateSuccess = () => ({ /** * Emits the failure signal for custom network settings request. * It means the request couldn't be processed due to internal error. + * It servers as hook for the frontend to provide feedback for the user. */ export const networkSettingsUpdateFailure = () => ({ type: types.NETWORKSETTINGS_UPDATE_FAILURE, diff --git a/src/reducers/reducer.js b/src/reducers/reducer.js index 2dff8cbce..5de8a7e14 100644 --- a/src/reducers/reducer.js +++ b/src/reducers/reducer.js @@ -1142,8 +1142,7 @@ export const onNetworkSettingsUpdateReady = (state) => ({ }); /** - * @param {Object} action.payload The errors from network settings input validation - * @see updateNetworkSettings + * Set `FAILED` state on network settings status. */ export const onNetworkSettingsUpdateFailure = (state) => ({ ...state, diff --git a/src/sagas/networkSettings.js b/src/sagas/networkSettings.js index f8475d034..4de41f50e 100644 --- a/src/sagas/networkSettings.js +++ b/src/sagas/networkSettings.js @@ -158,6 +158,7 @@ export function* updateNetworkSettings(action) { // Fail after try get network from fullnode if (!network) { + console.warn('The network could not be found.'); yield put(networkSettingsUpdateFailure()); return; } @@ -256,7 +257,13 @@ export function* persistNetworkSettings(action) { * Deletes the network settings from the application storage. */ export function* cleanNetworkSettings() { - STORE.removeItem(networkSettingsKeyMap.networkSettings); + try { + STORE.removeItem(networkSettingsKeyMap.networkSettings); + } + catch (err) { + console.error('Error while deleting the custom network settings from app storage.', err); + yield 1; + } yield 0; } From 61d88a536588a692082eb090175887c96af95add Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Tue, 24 Oct 2023 04:03:54 +0100 Subject: [PATCH 09/23] chore: add docstring to action NETWORKSETTINGS_UPDATE_READY --- src/actions.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/actions.js b/src/actions.js index 6b679a566..00cc7869e 100644 --- a/src/actions.js +++ b/src/actions.js @@ -134,6 +134,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 */ NETWORKSETTINGS_UPDATE_READY: 'NETWORK_SETTINGS_UPDATE_READY', }; From 0352e196067f403c401448dd3337b4538371936c Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Tue, 24 Oct 2023 03:40:17 +0100 Subject: [PATCH 10/23] refactor: NetworkStatusBar - Improve readability --- .../NetworkSettings/NetworkStatusBar.js | 26 +++++++++---------- src/sagas/networkSettings.js | 1 - 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/components/NetworkSettings/NetworkStatusBar.js b/src/components/NetworkSettings/NetworkStatusBar.js index 64717833c..1b6733e62 100644 --- a/src/components/NetworkSettings/NetworkStatusBar.js +++ b/src/components/NetworkSettings/NetworkStatusBar.js @@ -4,8 +4,8 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ -import { useSelector } from 'react-redux'; -import { eq } from 'lodash'; +import { useSelector } from "react-redux"; +import { isEqual } from "lodash"; import { t } from 'ttag'; import { AlertUI } from '../../styles/themes'; import { ToplineBar } from '../ToplineBar'; @@ -13,18 +13,18 @@ import { PRE_SETTINGS_MAINNET } from '../../constants'; const customNetworkText = t`Custom network`; +function notMainnet(networkSettings) { + return !isEqual(networkSettings , PRE_SETTINGS_MAINNET); +} + +const style = { + backgroundColor: AlertUI.primaryColor, + color: AlertUI.dark40Color, +}; + export const NetworkStatusBar = () => { + const getStatusText = (networkSettings) => customNetworkText+': '+networkSettings.network; const networkSettings = useSelector((state) => state.networkSettings); - if (eq(networkSettings, PRE_SETTINGS_MAINNET)) { - return null; - } - const style = { - backgroundColor: AlertUI.primaryColor, - color: AlertUI.dark40Color, - }; - const text = `${customNetworkText}: ${networkSettings.network}`; - return ( - - ); + return notMainnet(networkSettings) && (); }; diff --git a/src/sagas/networkSettings.js b/src/sagas/networkSettings.js index 4de41f50e..809390fcb 100644 --- a/src/sagas/networkSettings.js +++ b/src/sagas/networkSettings.js @@ -8,7 +8,6 @@ import { networkSettingsPersistStore, networkSettingsUpdateInvalid, networkSettingsUpdateFailure, - networkSettingsUpdateReady, networkSettingsUpdateState, networkSettingsUpdateSuccess, networkSettingsUpdateWaiting, From c70ac9750e69c30a8ee4f4e62a6e507c4e1db2ca Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Tue, 24 Oct 2023 18:34:32 +0100 Subject: [PATCH 11/23] refactor: saga effect persistNetworkSettings - change network settings status to `WAITING` just after persist the custom network settings - throw a fatal exception message to the user if the wallet is not found - stop the wallet and clean its storage and addresses - add a reducer for RELOAD_WALLET_REQUESTED action to clean the tokens history and balance on redux store --- src/reducers/reducer.js | 12 ++++++++ src/sagas/networkSettings.js | 55 +++++++++++++++++++++++------------- 2 files changed, 47 insertions(+), 20 deletions(-) diff --git a/src/reducers/reducer.js b/src/reducers/reducer.js index 5de8a7e14..81211ec56 100644 --- a/src/reducers/reducer.js +++ b/src/reducers/reducer.js @@ -327,6 +327,8 @@ export const reducer = (state = initialState, action) => { return onPushReset(state); case types.EXCEPTION_CAPTURED: return onExceptionCaptured(state, action); + case types.RELOAD_WALLET_REQUESTED: + return onReloadWalletRequested(state); case types.WALLET_RELOADING: return onWalletReloading(state); case types.SHARED_ADDRESS_UPDATE: @@ -1032,6 +1034,16 @@ export const onExceptionCaptured = (state, { payload }) => { }; }; +/** + * On wallet reload, tokens data will be reloaded as well. + */ +export const onReloadWalletRequested = (state) => ({ + ...state, + tokensHistory: initialState.tokensHistory, + tokensBalance: initialState.tokensBalance, + loadHistoryStatus: initialState.loadHistoryStatus, +}); + const onWalletReloading = (state) => ({ ...state, walletStartState: WALLET_STATUS.LOADING, diff --git a/src/sagas/networkSettings.js b/src/sagas/networkSettings.js index 809390fcb..b96135259 100644 --- a/src/sagas/networkSettings.js +++ b/src/sagas/networkSettings.js @@ -1,21 +1,36 @@ -import { all, takeEvery, put, call, race, take, delay, select } from 'redux-saga/effects'; -import { config } from '@hathor/wallet-lib'; +import { all, takeEvery, put, call, race, delay, select } from 'redux-saga/effects'; +import { config, wallet as oldWalletUtil } from '@hathor/wallet-lib'; import { isEmpty } from 'lodash'; -import AsyncStorage from '@react-native-async-storage/async-storage'; import { t } from 'ttag'; import { - featureToggleUpdate, networkSettingsPersistStore, networkSettingsUpdateInvalid, networkSettingsUpdateFailure, networkSettingsUpdateState, networkSettingsUpdateSuccess, networkSettingsUpdateWaiting, + types, + startWalletRequested, reloadWalletRequested, - types + networkSettingsPersistComplete, + onExceptionCaptured } from '../actions'; -import { HTTP_REQUEST_TIMEOUT, NETWORK, networkSettingsKeyMap, NETWORKSETTINGS_STATUS, NETWORK_TESTNET, STAGE, STAGE_DEV_PRIVNET, STAGE_TESTNET, WALLET_SERVICE_REQUEST_TIMEOUT } from '../constants'; -import { getFullnodeNetwork, getWalletServiceNetwork } from './helpers'; +import { + HTTP_REQUEST_TIMEOUT, + NETWORK, + networkSettingsKeyMap, + NETWORKSETTINGS_STATUS, + NETWORK_TESTNET, + STAGE, + STAGE_DEV_PRIVNET, + STAGE_TESTNET, + WALLET_SERVICE_REQUEST_TIMEOUT +} from '../constants'; +import { + getFullnodeNetwork, + getWalletServiceNetwork, + showPinScreenForResult +} from './helpers'; import { STORE } from '../store'; /** @@ -229,27 +244,27 @@ export function* persistNetworkSettings(action) { const networkSettings = action.payload; try { STORE.setItem(networkSettingsKeyMap.networkSettings, networkSettings); + yield put(networkSettingsUpdateWaiting()); } catch (err) { console.error('Error while persisting the custom network settings.', err); yield put(networkSettingsUpdateFailure()); return; } - // trigger toggle update to be managed by featureToggle saga - yield put(featureToggleUpdate()); - - // if wallet-service is being deactivated, it will trigger the reload, - // otherwise we should trigger by ourselves - const { timeout } = yield race({ - reload: take(types.RELOAD_WALLET_REQUESTED), - timeout: delay(HTTP_REQUEST_TIMEOUT), - }); - - if (timeout) { - yield put(reloadWalletRequested()); + const wallet = yield select((state) => state.wallet); + if (!wallet) { + // If we fall into this situation, the app should be killed for custom new network settings take effect. + const errMsg = 'Wallet not found while trying to persist the custom network settings.'; + console.warn(errMsg) + yield put(onExceptionCaptured(errMsg, /* isFatal */ true)); + return; } - yield put(networkSettingsUpdateWaiting()); + // Stop wallet and clean its storage without clean its access data. + wallet.stop({ cleanStorage: true, cleanAddresses: true }); + // This action should clean the tokens history on redux. + // In addition, the reload also clean the inmemory storage. + yield put(reloadWalletRequested()); } /** From 02acec26d982e594e057425965cdc9de48badc8b Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Tue, 24 Oct 2023 19:12:29 +0100 Subject: [PATCH 12/23] lint: resolve all the lint issues --- src/actions.js | 1 - .../NetworkSettings/NetworkStatusBar.js | 12 +++--- src/sagas/helpers.js | 6 ++- src/sagas/networkSettings.js | 14 +++---- .../CustomNetworkSettingsScreen.js | 39 +++++++++---------- .../NetworkSettingsFlowStack.js | 10 ++++- src/screens/NetworkSettings/helper.js | 4 +- 7 files changed, 45 insertions(+), 41 deletions(-) diff --git a/src/actions.js b/src/actions.js index 00cc7869e..7eae801c0 100644 --- a/src/actions.js +++ b/src/actions.js @@ -911,7 +911,6 @@ export const networkSettingsPersistStore = (customNetwork) => ({ payload: customNetwork, }); - /** * Emits the waiting signal after persist the custom network. * It means the wallet will reload. diff --git a/src/components/NetworkSettings/NetworkStatusBar.js b/src/components/NetworkSettings/NetworkStatusBar.js index 1b6733e62..e5d55164c 100644 --- a/src/components/NetworkSettings/NetworkStatusBar.js +++ b/src/components/NetworkSettings/NetworkStatusBar.js @@ -4,8 +4,8 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ -import { useSelector } from "react-redux"; -import { isEqual } from "lodash"; +import { useSelector } from 'react-redux'; +import { isEqual } from 'lodash'; import { t } from 'ttag'; import { AlertUI } from '../../styles/themes'; import { ToplineBar } from '../ToplineBar'; @@ -14,7 +14,7 @@ import { PRE_SETTINGS_MAINNET } from '../../constants'; const customNetworkText = t`Custom network`; function notMainnet(networkSettings) { - return !isEqual(networkSettings , PRE_SETTINGS_MAINNET); + return !isEqual(networkSettings, PRE_SETTINGS_MAINNET); } const style = { @@ -23,8 +23,10 @@ const style = { }; export const NetworkStatusBar = () => { - const getStatusText = (networkSettings) => customNetworkText+': '+networkSettings.network; + const getStatusText = (networkSettings) => `${customNetworkText}: ${networkSettings.network}`; const networkSettings = useSelector((state) => state.networkSettings); - return notMainnet(networkSettings) && (); + return notMainnet(networkSettings) && ( + + ); }; diff --git a/src/sagas/helpers.js b/src/sagas/helpers.js index 7487c12ce..b87221f20 100644 --- a/src/sagas/helpers.js +++ b/src/sagas/helpers.js @@ -251,5 +251,9 @@ export function disableFeaturesIfNeeded(customNetworkSettings, currentFeatureTog export function getNetworkSettings(state) { // The state is always present, but the stored network settings // has precedence, once it indicates a custom network. - return STORE.getItem(networkSettingsKeyMap.networkSettings) ?? state.networkSettings; + let networkSettings = STORE.getItem(networkSettingsKeyMap.networkSettings); + if (!networkSettings) { + networkSettings = state.networkSettings; + } + return networkSettings; } diff --git a/src/sagas/networkSettings.js b/src/sagas/networkSettings.js index b96135259..e57936bbb 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, wallet as oldWalletUtil } from '@hathor/wallet-lib'; +import { config } from '@hathor/wallet-lib'; import { isEmpty } from 'lodash'; import { t } from 'ttag'; import { @@ -10,13 +10,10 @@ import { networkSettingsUpdateSuccess, networkSettingsUpdateWaiting, types, - startWalletRequested, reloadWalletRequested, - networkSettingsPersistComplete, onExceptionCaptured } from '../actions'; import { - HTTP_REQUEST_TIMEOUT, NETWORK, networkSettingsKeyMap, NETWORKSETTINGS_STATUS, @@ -29,7 +26,6 @@ import { import { getFullnodeNetwork, getWalletServiceNetwork, - showPinScreenForResult } from './helpers'; import { STORE } from '../store'; @@ -253,9 +249,10 @@ export function* persistNetworkSettings(action) { const wallet = yield select((state) => state.wallet); if (!wallet) { - // If we fall into this situation, the app should be killed for custom new network settings take effect. + // If we fall into this situation, the app should be killed + // for the custom new network settings take effect. const errMsg = 'Wallet not found while trying to persist the custom network settings.'; - console.warn(errMsg) + console.warn(errMsg); yield put(onExceptionCaptured(errMsg, /* isFatal */ true)); return; } @@ -273,8 +270,7 @@ export function* persistNetworkSettings(action) { export function* cleanNetworkSettings() { try { STORE.removeItem(networkSettingsKeyMap.networkSettings); - } - catch (err) { + } catch (err) { console.error('Error while deleting the custom network settings from app storage.', err); yield 1; } diff --git a/src/screens/NetworkSettings/CustomNetworkSettingsScreen.js b/src/screens/NetworkSettings/CustomNetworkSettingsScreen.js index ae54cd23d..965e0ef8e 100644 --- a/src/screens/NetworkSettings/CustomNetworkSettingsScreen.js +++ b/src/screens/NetworkSettings/CustomNetworkSettingsScreen.js @@ -24,9 +24,8 @@ const feedbackFailedText = t`There was an error while customizing network settin * @param {object} networkSettingsStatus - status from redux store * @returns {boolean} - true if the status is successful, false otherwise */ -const hasSucceed = (networkSettingsStatus) => { - return networkSettingsStatus === NETWORKSETTINGS_STATUS.SUCCESSFUL; -}; +// eslint-disable-next-line max-len +const hasSucceed = (networkSettingsStatus) => networkSettingsStatus === NETWORKSETTINGS_STATUS.SUCCESSFUL; /** * Check if the network settings status is failed. @@ -45,13 +44,13 @@ const hasFailed = (networkSettingsStatus) => networkSettingsStatus === NETWORKSE const isLoading = (networkSettingsStatus) => networkSettingsStatus === NETWORKSETTINGS_STATUS.LOADING; /** - * Verifies if the invalidModel of the form has an error message. + * Verifies if the invalidModel of the form has an error message. */ function hasError(invalidModel) { return Object .values({ ...invalidModel }) .reduce((_hasError, currValue) => _hasError || !isEmpty(currValue), false); -}; +} /** * Validates the formModel, returning the invalidModel. @@ -135,23 +134,21 @@ export const CustomNetworkSettingsScreen = ({ navigation }) => { // eslint-disable-next-line max-len /* @param {'nodeUrl' | 'explorerUrl' | 'explorerServiceUrl' | 'walletServiceUrl' | 'walletServiceWsUrl' } name */ - const handleInputChange = (name) => { - return (value) => { - // update inalid 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)); + const handleInputChange = (name) => (value) => { + // update inalid 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)); }; const handleFeedbackModalDismiss = () => { diff --git a/src/screens/NetworkSettings/NetworkSettingsFlowStack.js b/src/screens/NetworkSettings/NetworkSettingsFlowStack.js index ac7589054..61c5c44c9 100644 --- a/src/screens/NetworkSettings/NetworkSettingsFlowStack.js +++ b/src/screens/NetworkSettings/NetworkSettingsFlowStack.js @@ -24,8 +24,14 @@ export const NetworkSettingsFlowStack = () => { name={NetworkSettingsDisclaimerNav} component={NetworkSettingsDisclaimerScreen} /> - - + + ); }; diff --git a/src/screens/NetworkSettings/helper.js b/src/screens/NetworkSettings/helper.js index fd839906e..7ccdd622b 100644 --- a/src/screens/NetworkSettings/helper.js +++ b/src/screens/NetworkSettings/helper.js @@ -10,9 +10,9 @@ export const feedbackFailedText = t`There was an error while customizing network * @param {object} networkSettingsStatus - status from redux store * @returns {boolean} - true if the status is successful, false otherwise */ -export const hasSucceed = (networkSettingsStatus) => { +export function hasSucceed(networkSettingsStatus) { return networkSettingsStatus === NETWORKSETTINGS_STATUS.SUCCESSFUL; -}; +} /** * Check if the network settings status is failed. From 67fa7356437819ed1600ed6a4bda96d60332c45b Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Mon, 30 Oct 2023 15:50:00 +0000 Subject: [PATCH 13/23] review: apply msg suggestions --- src/screens/NetworkSettings/CustomNetworkSettingsScreen.js | 2 +- src/screens/NetworkSettings/helper.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/screens/NetworkSettings/CustomNetworkSettingsScreen.js b/src/screens/NetworkSettings/CustomNetworkSettingsScreen.js index 965e0ef8e..c125aeae5 100644 --- a/src/screens/NetworkSettings/CustomNetworkSettingsScreen.js +++ b/src/screens/NetworkSettings/CustomNetworkSettingsScreen.js @@ -16,7 +16,7 @@ import Spinner from '../../components/Spinner'; const customNetworkSettingsTitleText = t`Custom Network Settings`.toUpperCase(); const warningText = t`Any token outside mainnet network bear no value. Only change if you know what you are doing.`; const feedbackLoadingText = t`Updating custom network settings...`; -const feedbackSucceedText = t`Network settings customized with success.`; +const feedbackSucceedText = t`Network settings successfully customized.`; const feedbackFailedText = t`There was an error while customizing network settings. Please try again later.`; /** diff --git a/src/screens/NetworkSettings/helper.js b/src/screens/NetworkSettings/helper.js index 7ccdd622b..11b7ffd45 100644 --- a/src/screens/NetworkSettings/helper.js +++ b/src/screens/NetworkSettings/helper.js @@ -2,7 +2,7 @@ import { t } from 'ttag'; import { NETWORKSETTINGS_STATUS } from '../../constants'; export const feedbackLoadingText = t`Updating custom network settings...`; -export const feedbackSucceedText = t`Network settings customized with success.`; +export const feedbackSucceedText = t`Network settings successfully customized.`; export const feedbackFailedText = t`There was an error while customizing network settings. Please try again later.`; /** From 1be1450b176c506251c1081549d239a99e7ca407 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Tue, 31 Oct 2023 20:23:57 +0000 Subject: [PATCH 14/23] refactor: inline retrieve of network settings --- src/sagas/helpers.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/sagas/helpers.js b/src/sagas/helpers.js index b87221f20..7487c12ce 100644 --- a/src/sagas/helpers.js +++ b/src/sagas/helpers.js @@ -251,9 +251,5 @@ export function disableFeaturesIfNeeded(customNetworkSettings, currentFeatureTog export function getNetworkSettings(state) { // The state is always present, but the stored network settings // has precedence, once it indicates a custom network. - let networkSettings = STORE.getItem(networkSettingsKeyMap.networkSettings); - if (!networkSettings) { - networkSettings = state.networkSettings; - } - return networkSettings; + return STORE.getItem(networkSettingsKeyMap.networkSettings) ?? state.networkSettings; } From 0f97f75cb5e2827cc0eea5d11c4475f17064fbb2 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Tue, 31 Oct 2023 20:25:24 +0000 Subject: [PATCH 15/23] fix: typos --- src/actions.js | 2 +- src/screens/NetworkSettings/CustomNetworkSettingsScreen.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/actions.js b/src/actions.js index 7eae801c0..4f181b332 100644 --- a/src/actions.js +++ b/src/actions.js @@ -930,7 +930,7 @@ export const networkSettingsUpdateSuccess = () => ({ /** * Emits the failure signal for custom network settings request. * It means the request couldn't be processed due to internal error. - * It servers as hook for the frontend to provide feedback for the user. + * It serves as hook for the frontend to provide feedback for the user. */ export const networkSettingsUpdateFailure = () => ({ type: types.NETWORKSETTINGS_UPDATE_FAILURE, diff --git a/src/screens/NetworkSettings/CustomNetworkSettingsScreen.js b/src/screens/NetworkSettings/CustomNetworkSettingsScreen.js index c125aeae5..fdcbecf0c 100644 --- a/src/screens/NetworkSettings/CustomNetworkSettingsScreen.js +++ b/src/screens/NetworkSettings/CustomNetworkSettingsScreen.js @@ -135,7 +135,7 @@ export const CustomNetworkSettingsScreen = ({ navigation }) => { // eslint-disable-next-line max-len /* @param {'nodeUrl' | 'explorerUrl' | 'explorerServiceUrl' | 'walletServiceUrl' | 'walletServiceWsUrl' } name */ const handleInputChange = (name) => (value) => { - // update inalid model + // update invalid model const invalidModelCopy = { ...invalidModel }; delete invalidModelCopy[name]; setInvalidModel(invalidModelCopy); From e0ad2a137d10d424c16d5600df5678261d3ae0b7 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Tue, 31 Oct 2023 20:26:30 +0000 Subject: [PATCH 16/23] refactor: replace to helper's functions --- .../CustomNetworkSettingsScreen.js | 26 +------------------ 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/src/screens/NetworkSettings/CustomNetworkSettingsScreen.js b/src/screens/NetworkSettings/CustomNetworkSettingsScreen.js index fdcbecf0c..e0f59ec9d 100644 --- a/src/screens/NetworkSettings/CustomNetworkSettingsScreen.js +++ b/src/screens/NetworkSettings/CustomNetworkSettingsScreen.js @@ -8,10 +8,10 @@ import FeedbackModal from '../../components/FeedbackModal'; import HathorHeader from '../../components/HathorHeader'; import NewHathorButton from '../../components/NewHathorButton'; import SimpleInput from '../../components/SimpleInput'; -import { NETWORKSETTINGS_STATUS } from '../../constants'; import errorIcon from '../../assets/images/icErrorBig.png'; import checkIcon from '../../assets/images/icCheckBig.png'; import Spinner from '../../components/Spinner'; +import { hasSucceed, hasFailed, isLoading } from './helper'; const customNetworkSettingsTitleText = t`Custom Network Settings`.toUpperCase(); const warningText = t`Any token outside mainnet network bear no value. Only change if you know what you are doing.`; @@ -19,30 +19,6 @@ const feedbackLoadingText = t`Updating custom network settings...`; const feedbackSucceedText = t`Network settings successfully customized.`; const feedbackFailedText = t`There was an error while customizing network settings. Please try again later.`; -/** - * Check if the network settings status is successful. - * @param {object} networkSettingsStatus - status from redux store - * @returns {boolean} - true if the status is successful, false otherwise - */ -// eslint-disable-next-line max-len -const hasSucceed = (networkSettingsStatus) => networkSettingsStatus === NETWORKSETTINGS_STATUS.SUCCESSFUL; - -/** - * Check if the network settings status is failed. - * @param {object} networkSettingsStatus - status from redux store - * @returns {boolean} - true if the status is failed, false otherwise - */ -// eslint-disable-next-line max-len -const hasFailed = (networkSettingsStatus) => networkSettingsStatus === NETWORKSETTINGS_STATUS.FAILED; - -/** - * Check if the network settings status is loading. - * @param {object} networkSettingsStatus - status from redux store - * @returns {boolean} - true if the status is loading, false otherwise - */ -// eslint-disable-next-line max-len -const isLoading = (networkSettingsStatus) => networkSettingsStatus === NETWORKSETTINGS_STATUS.LOADING; - /** * Verifies if the invalidModel of the form has an error message. */ From 393eb4be8cda595460ada6bc15bdfdfe037368ac Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Tue, 31 Oct 2023 20:27:34 +0000 Subject: [PATCH 17/23] feat: add fallback for the ready status --- src/sagas/networkSettings.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/sagas/networkSettings.js b/src/sagas/networkSettings.js index e57936bbb..44c01d312 100644 --- a/src/sagas/networkSettings.js +++ b/src/sagas/networkSettings.js @@ -11,7 +11,8 @@ import { networkSettingsUpdateWaiting, types, reloadWalletRequested, - onExceptionCaptured + onExceptionCaptured, + networkSettingsUpdateReady } from '../actions'; import { NETWORK, @@ -41,6 +42,8 @@ export function* initNetworkSettings() { const status = yield select((state) => state.networkSettingsStatus); if (status === NETWORKSETTINGS_STATUS.WAITING) { yield put(networkSettingsUpdateSuccess()); + } else { + yield put(networkSettingsUpdateReady()); } } From 185670b8814f0a9737fd21fb99d492768ea84ec3 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Tue, 31 Oct 2023 20:31:03 +0000 Subject: [PATCH 18/23] chore: wrap text in localization mechanism --- src/sagas/networkSettings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sagas/networkSettings.js b/src/sagas/networkSettings.js index 44c01d312..f54785332 100644 --- a/src/sagas/networkSettings.js +++ b/src/sagas/networkSettings.js @@ -254,7 +254,7 @@ export function* persistNetworkSettings(action) { if (!wallet) { // If we fall into this situation, the app should be killed // for the custom new network settings take effect. - const errMsg = 'Wallet not found while trying to persist the custom network settings.'; + const errMsg = t`Wallet not found while trying to persist the custom network settings.`; console.warn(errMsg); yield put(onExceptionCaptured(errMsg, /* isFatal */ true)); return; From 51609ad2c25538dff76d21b84addb3151fca275c Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Tue, 31 Oct 2023 23:15:54 +0000 Subject: [PATCH 19/23] feat: ignore wallet service URLs for mainnet check on network status bar --- .../NetworkSettings/NetworkStatusBar.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/components/NetworkSettings/NetworkStatusBar.js b/src/components/NetworkSettings/NetworkStatusBar.js index e5d55164c..697e688e7 100644 --- a/src/components/NetworkSettings/NetworkStatusBar.js +++ b/src/components/NetworkSettings/NetworkStatusBar.js @@ -14,7 +14,21 @@ import { PRE_SETTINGS_MAINNET } from '../../constants'; const customNetworkText = t`Custom network`; function notMainnet(networkSettings) { - return !isEqual(networkSettings, PRE_SETTINGS_MAINNET); + const currNetwork = { + stage: networkSettings.stage, + network: networkSettings.network, + nodeUrl: networkSettings.nodeUrl, + explorerUrl: networkSettings.explorerUrl, + explorerServiceUrl: networkSettings.explorerServiceUrl, + }; + const mainnet = { + stage: PRE_SETTINGS_MAINNET.stage, + network: PRE_SETTINGS_MAINNET.network, + nodeUrl: PRE_SETTINGS_MAINNET.nodeUrl, + explorerUrl: PRE_SETTINGS_MAINNET.explorerUrl, + explorerServiceUrl: PRE_SETTINGS_MAINNET.explorerServiceUrl, + }; + return !isEqual(currNetwork, mainnet); } const style = { From bbb8f5d5a0e75b53f7517ea5b8481bb221f7b699 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Wed, 1 Nov 2023 00:16:39 +0000 Subject: [PATCH 20/23] feat: add full check of custom network against mainnet pre-settings - The full check will happen when the custom network contain a valid URL for wallet-service. This behavior is to avoid a bypass on the network status bar when the wallet-service is given. This is important because the wallet-service has precedence over fullnode. --- src/components/NetworkSettings/NetworkStatusBar.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/NetworkSettings/NetworkStatusBar.js b/src/components/NetworkSettings/NetworkStatusBar.js index 697e688e7..fb2bd7ea1 100644 --- a/src/components/NetworkSettings/NetworkStatusBar.js +++ b/src/components/NetworkSettings/NetworkStatusBar.js @@ -14,6 +14,10 @@ import { PRE_SETTINGS_MAINNET } from '../../constants'; const customNetworkText = t`Custom network`; function notMainnet(networkSettings) { + if (networkSettings.walletServiceUrl) { + return !isEqual(networkSettings, PRE_SETTINGS_MAINNET); + } + const currNetwork = { stage: networkSettings.stage, network: networkSettings.network, From f31c263e2c8a64ee37f553dcd1dca0908b155267 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Wed, 1 Nov 2023 13:28:59 +0000 Subject: [PATCH 21/23] chore: add comments explaining the two checks on notMainnet --- src/components/NetworkSettings/NetworkStatusBar.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/components/NetworkSettings/NetworkStatusBar.js b/src/components/NetworkSettings/NetworkStatusBar.js index fb2bd7ea1..05329248b 100644 --- a/src/components/NetworkSettings/NetworkStatusBar.js +++ b/src/components/NetworkSettings/NetworkStatusBar.js @@ -14,10 +14,17 @@ import { PRE_SETTINGS_MAINNET } from '../../constants'; const customNetworkText = t`Custom network`; function notMainnet(networkSettings) { + // If the networkSettings has a walletServiceUrl, then + // we should run a full check against the mainnet presettings. + // This is important because the wallet service has precedence + // over fullnode. if (networkSettings.walletServiceUrl) { return !isEqual(networkSettings, PRE_SETTINGS_MAINNET); } + // In the absence of walletServiceUrl we can remove wallet + // service URLs from the equality check against the mainnet + // presettings. const currNetwork = { stage: networkSettings.stage, network: networkSettings.network, From 3825b2e3978e12bc1900b0797d7f6c7d08f6aa95 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Wed, 1 Nov 2023 13:34:49 +0000 Subject: [PATCH 22/23] chore: add comment to explain set READY fallback behavior --- src/sagas/networkSettings.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sagas/networkSettings.js b/src/sagas/networkSettings.js index f54785332..2fead2b7d 100644 --- a/src/sagas/networkSettings.js +++ b/src/sagas/networkSettings.js @@ -41,8 +41,12 @@ export function* initNetworkSettings() { const status = yield select((state) => state.networkSettingsStatus); if (status === NETWORKSETTINGS_STATUS.WAITING) { + // This branch completes the network update by delivering + // a success feedback to the user. yield put(networkSettingsUpdateSuccess()); } else { + // This branch is a fallback to set network status to READY + // after wallet initialization. yield put(networkSettingsUpdateReady()); } } From d994fdb5854da31ac757b9b8425af9eea380d2f0 Mon Sep 17 00:00:00 2001 From: Alex Ruzenhack Date: Wed, 1 Nov 2023 13:44:04 +0000 Subject: [PATCH 23/23] chore: update docstrings of action methods --- src/actions.js | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/actions.js b/src/actions.js index 4f181b332..72e80a852 100644 --- a/src/actions.js +++ b/src/actions.js @@ -120,7 +120,7 @@ export const types = { // NOTE: These actions follows a taxonomy that should be applied // to all other actions. // See: https://github.com/HathorNetwork/hathor-wallet-mobile/issues/334 - /* It delivers the user's network settings input from the form. */ + /* It initiates an update of the network settings based on user input from a form. */ NETWORKSETTINGS_UPDATE_REQUEST: 'NETWORK_SETTINGS_UPDATE_REQUEST', /* It updates the redux state */ NETWORKSETTINGS_UPDATE_STATE: 'NETWORKSETTINGS_UPDATE_STATE', @@ -128,7 +128,7 @@ export const types = { NETWORKSETTINGS_PERSIST_STORE: 'NETWORKSETTINGS_PERSIST_STORE', /* It indicates the persistence is complete and the wallet will be reloaded. */ NETWORKSETTINGS_UPDATE_WAITING: 'NETWORKSETTINGS_UPDATE_WAITING', - /* It indicates the update is complete after wallet reloads. */ + /* It indicates the update is complete after the wallet reloads. */ NETWORKSETTINGS_UPDATE_SUCCESS: 'NETWORK_SETTINGS_UPDATE_SUCCESS', /* It indicates the update request has invalid inputs. */ NETWORKSETTINGS_UPDATE_INVALID: 'NETWORKSETTINGS_UPDATE_INVALID', @@ -912,32 +912,34 @@ export const networkSettingsPersistStore = (customNetwork) => ({ }); /** - * Emits the waiting signal after persist the custom network. - * It means the wallet will reload. + * Action indicating that the network settings update process + * is in a waiting state. + * This is used after persisting custom network configurations, + * resulting in a wallet reload. */ export const networkSettingsUpdateWaiting = () => ({ type: types.NETWORKSETTINGS_UPDATE_WAITING, }); /** - * Emits the success signal after wallet reloads. - * It servers as hook for the frontend to provide feedback for the user. + * Action indicating that the network settings update was successful. + * This serves as a hook for the frontend to provide feedback to the user. */ export const networkSettingsUpdateSuccess = () => ({ type: types.NETWORKSETTINGS_UPDATE_SUCCESS, }); /** - * Emits the failure signal for custom network settings request. + * Action indicating a failure state for the custom network settings request. * It means the request couldn't be processed due to internal error. - * It serves as hook for the frontend to provide feedback for the user. + * This serves as a hook for the frontend to provide feedback to the user. */ export const networkSettingsUpdateFailure = () => ({ type: types.NETWORKSETTINGS_UPDATE_FAILURE, }); /** - * Emits invalid signal for custom network settings request inputs. + * Action indicating an invalid state for the custom network settings request inputs. * It means the form should present the invalid message on the corresponding inputs. * @param {{ * message: string,