diff --git a/src/renderer/hooks/useKeystoreAccounts.ts b/src/renderer/hooks/useKeystoreAccounts.ts new file mode 100644 index 000000000..3b7a411b0 --- /dev/null +++ b/src/renderer/hooks/useKeystoreAccounts.ts @@ -0,0 +1,21 @@ +import * as RD from '@devexperts/remote-data-ts' +import * as FP from 'fp-ts/lib/function' +import { useObservableState } from 'observable-hooks' + +import { useWalletContext } from '../contexts/WalletContext' +import { KeystoreAccountsRD, KeystoreAccountsUI } from '../services/wallet/types' + +export const useKeystoreAccounts = (): { + keystoreAccounts: KeystoreAccountsRD + reloadKeystoreAccounts: FP.Lazy + keystoreAccountsUI: KeystoreAccountsUI +} => { + const { + keystoreService: { reloadKeystoreAccounts, keystoreAccounts$, keystoreAccountsUI$ } + } = useWalletContext() + + const keystoreAccounts = useObservableState(keystoreAccounts$, RD.initial) + const keystoreAccountsUI = useObservableState(keystoreAccountsUI$, []) + + return { keystoreAccounts, keystoreAccountsUI, reloadKeystoreAccounts } +} diff --git a/src/renderer/hooks/useKeystoreState.ts b/src/renderer/hooks/useKeystoreState.ts new file mode 100644 index 000000000..bc09031a3 --- /dev/null +++ b/src/renderer/hooks/useKeystoreState.ts @@ -0,0 +1,29 @@ +import * as FP from 'fp-ts/lib/function' +import * as O from 'fp-ts/lib/Option' +import { useObservableState } from 'observable-hooks' +import * as RxOp from 'rxjs/operators' + +import { useWalletContext } from '../contexts/WalletContext' +import { INITIAL_KEYSTORE_STATE } from '../services/wallet/const' +import { KeystoreState, Phrase } from '../services/wallet/types' +import { getPhrase, getWalletName } from '../services/wallet/util' + +export const useKeystoreState = (): { + state: KeystoreState + phrase: O.Option + walletName: O.Option + remove: () => Promise + unlock: (password: string) => Promise + lock: FP.Lazy +} => { + const { + keystoreService: { keystore$, unlock, lock, removeKeystoreAccount: remove } + } = useWalletContext() + + const state = useObservableState(keystore$, INITIAL_KEYSTORE_STATE) + + const [phrase] = useObservableState(() => FP.pipe(keystore$, RxOp.map(FP.flow(getPhrase))), O.none) + const [walletName] = useObservableState(() => FP.pipe(keystore$, RxOp.map(FP.flow(getWalletName))), O.none) + + return { state, phrase, walletName, unlock, lock, remove } +} diff --git a/src/renderer/services/wallet/keystore.ts b/src/renderer/services/wallet/keystore.ts index d3451551c..800dec5d1 100644 --- a/src/renderer/services/wallet/keystore.ts +++ b/src/renderer/services/wallet/keystore.ts @@ -19,7 +19,8 @@ import { LoadKeystoreLD, ImportKeystoreParams, AddKeystoreParams, - KeystoreAccountsLD + KeystoreAccountsLD, + KeystoreAccountsUI$ } from './types' import { getKeystore, @@ -39,6 +40,9 @@ const { set: setKeystoreState } = observableState(INITIAL_KEYSTORE_STATE) +/** + * Internal state of keystore accounts - not shared to outside world + */ const { get$: getKeystoreAccounts$, get: getKeystoreAccounts, @@ -215,6 +219,14 @@ const keystoreAccounts$: KeystoreAccountsLD = FP.pipe( RxOp.startWith(RD.pending) ) +// Simplified `KeystoreAccounts` (w/o loading state, w/o `keystore`) to display data at UIs +const keystoreAccountsUI$: KeystoreAccountsUI$ = FP.pipe( + getKeystoreAccounts$, + // Transform `KeystoreAccounts` -> `KeystoreAccountsUI` + RxOp.map(FP.flow(A.map(({ id, name, selected }) => ({ id, name, selected })))), + RxOp.shareReplay(1) +) + const id = FP.pipe(getKeystoreState(), getKeystoreId) if (!id) { throw Error(`Can't export keystore - keystore id is missing in KeystoreState`) @@ -251,6 +263,7 @@ export const keystoreService: KeystoreService = { unlock, validatePassword$, reloadKeystoreAccounts, + keystoreAccountsUI$, keystoreAccounts$ } diff --git a/src/renderer/services/wallet/types.ts b/src/renderer/services/wallet/types.ts index 1840169ab..0d8e16da6 100644 --- a/src/renderer/services/wallet/types.ts +++ b/src/renderer/services/wallet/types.ts @@ -8,7 +8,7 @@ import { NonEmptyArray } from 'fp-ts/lib/NonEmptyArray' import * as O from 'fp-ts/lib/Option' import * as Rx from 'rxjs' -import { KeystoreAccounts } from '../../../shared/api/io' +import { KeystoreAccount, KeystoreAccounts } from '../../../shared/api/io' import { KeystoreId, LedgerError, Network } from '../../../shared/api/types' import { WalletAddress, WalletBalanceType, WalletType } from '../../../shared/wallet/types' import { LiveData } from '../../helpers/rx/liveData' @@ -72,6 +72,7 @@ export type KeystoreService = { validatePassword$: ValidatePasswordHandler reloadKeystoreAccounts: FP.Lazy keystoreAccounts$: KeystoreAccountsLD + keystoreAccountsUI$: KeystoreAccountsUI$ } export type WalletAddressAsync = { address: RD.RemoteData; type: WalletType } @@ -207,4 +208,9 @@ export type LedgerAddressMap$ = Rx.Observable export type LedgerAddressesMap = Record export type LedgerAddressesMap$ = Rx.Observable +export type KeystoreAccountsRD = RD.RemoteData export type KeystoreAccountsLD = LiveData + +export type KeystoreAccountUI = Omit +export type KeystoreAccountsUI = KeystoreAccountUI[] +export type KeystoreAccountsUI$ = Rx.Observable diff --git a/src/renderer/views/app/AppView.tsx b/src/renderer/views/app/AppView.tsx index ef7380916..3ad8736d2 100644 --- a/src/renderer/views/app/AppView.tsx +++ b/src/renderer/views/app/AppView.tsx @@ -16,9 +16,9 @@ import { Header } from '../../components/header' import { Button } from '../../components/uielements/button' import { useI18nContext } from '../../contexts/I18nContext' import { useMidgardContext } from '../../contexts/MidgardContext' -import { useWalletContext } from '../../contexts/WalletContext' import { unionChains } from '../../helpers/fp/array' import { rdAltOnPending } from '../../helpers/fpHelpers' +import { useKeystoreAccounts } from '../../hooks/useKeystoreAccounts' import { useMimirHalt } from '../../hooks/useMimirHalt' import { useTheme } from '../../hooks/useTheme' import { DEFAULT_MIMIR_HALT } from '../../services/thorchain/const' @@ -72,11 +72,7 @@ export const AppView: React.FC = (): JSX.Element => { const prevHaltedChains = useRef([]) const prevMimirHalt = useRef(DEFAULT_MIMIR_HALT) - const { - keystoreService: { reloadKeystoreAccounts, keystoreAccounts$ } - } = useWalletContext() - - const keystoreAccounts = useObservableState(keystoreAccounts$, RD.initial) + const { keystoreAccounts, reloadKeystoreAccounts } = useKeystoreAccounts() const { mimirHaltRD } = useMimirHalt() diff --git a/src/renderer/views/wallet/UnlockView.tsx b/src/renderer/views/wallet/UnlockView.tsx index 5633755f9..fa510719a 100644 --- a/src/renderer/views/wallet/UnlockView.tsx +++ b/src/renderer/views/wallet/UnlockView.tsx @@ -1,16 +1,9 @@ import React from 'react' -import { useObservableState } from 'observable-hooks' - import { UnlockForm } from '../../components/wallet/unlock' -import { useWalletContext } from '../../contexts/WalletContext' -import { INITIAL_KEYSTORE_STATE } from '../../services/wallet/const' +import { useKeystoreState } from '../../hooks/useKeystoreState' export const UnlockView: React.FC = (): JSX.Element => { - const { - keystoreService: { keystore$, removeKeystoreAccount, unlock } - } = useWalletContext() - const keystore = useObservableState(keystore$, INITIAL_KEYSTORE_STATE) - - return + const { state: keystore, unlock, remove } = useKeystoreState() + return }