From a238dac00dab502c1d256c36e96e24b540f77e54 Mon Sep 17 00:00:00 2001 From: classicalliu Date: Tue, 26 Mar 2019 18:56:02 +0800 Subject: [PATCH 001/119] chore: mock some interface in cell module --- packages/neuron-wallet/src/cell.ts | 43 ++++++++++++++ packages/neuron-wallet/src/mock_rpc.ts | 81 ++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 packages/neuron-wallet/src/mock_rpc.ts diff --git a/packages/neuron-wallet/src/cell.ts b/packages/neuron-wallet/src/cell.ts index 1f84e529a3..9299a330f5 100644 --- a/packages/neuron-wallet/src/cell.ts +++ b/packages/neuron-wallet/src/cell.ts @@ -1,5 +1,6 @@ import asw from './wallets/asw' import ckbCore from './core' +import { getCellChanges, storeCells } from './mock_rpc' export interface OutPoint { hash: string @@ -21,6 +22,8 @@ export interface Cell { lock: string type?: Script | null outPoint?: OutPoint + state?: string + stateChange?: string } export const getUnspentCells = async () => { @@ -33,6 +36,46 @@ export const getLiveCell = async (outPoint: OutPoint) => { return liveCell } +/* eslint @typescript-eslint/no-unused-vars: "warn" */ + +// save one Cell to db +// check cell exists and status +// if exists, and status right, ignore +// if exists, and status not right, update or throw(something wrong) +// if not exist, insert +export const saveCell = async (cell: Cell) => { + return cell +} + +// marked a cell used +// update state = 'spent' +export const markCellSpent = async (cell: Cell) => { + return cell +} + +// get cells changes from chain and save to db +export const loadCellsFromChain = async () => { + const cells = await getCellChanges() + cells.forEach(cell => { + if (cell.stateChange === 'created') { + saveCell(cell) + } else if (cell.stateChange === 'spent') { + markCellSpent(cell) + } + // any stateChange else would be ignored + }) +} + +// different wallet has different cells and txs +export const getCellsByWallet = async (page: number, perPage: number, walletID: string) => { + const cells = storeCells + + return { + totalCount: cells.length, + cells, + } +} + export default { getUnspentCells, getLiveCell, diff --git a/packages/neuron-wallet/src/mock_rpc.ts b/packages/neuron-wallet/src/mock_rpc.ts new file mode 100644 index 0000000000..df5a0e5cf7 --- /dev/null +++ b/packages/neuron-wallet/src/mock_rpc.ts @@ -0,0 +1,81 @@ +import { Cell } from './cell' + +// mock as cells in db +export const storeCells: Cell[] = [ + { + outPoint: { + hash: '0x3abd21e6e51674bb961bb4c5f3cee9faa5da30e64be10628dc1cef292cbae324', + index: 0, + }, + state: 'live', + // origin cell infos + data: '0x', + capacity: 10, + type: null, + lock: '0x84aa24c0f4e70ca4fb559a31abdb237a74f3106ed57510579043348984fc1478', + }, + { + outPoint: { + hash: '0xb22b53a7613f5754850f118eae16caf867107d72a9b125ca596855583e712c97', + index: 0, + }, + state: 'dead', + // origin cell infos + data: '0x', + capacity: 20, + type: null, + lock: '0xa4ce51d3c7e26701d4249179546f405d7a5ac24ffb3f2f6b8bef15017161e2e5', + }, +] + +// mock an interface: get cell change info from chain +// stateChange should be 'created' or 'spent' +// params: [addresses, beginBlockNumber, endBlockNumber] +export const getCellChanges = async () => { + const cells: Cell[] = [ + { + outPoint: { + hash: '0x3abd21e6e51674bb961bb4c5f3cee9faa5da30e64be10628dc1cef292cbae324', + index: 0, + }, + stateChange: 'created', + // origin cell infos + data: '0x', + capacity: 10, + type: null, + lock: '0x84aa24c0f4e70ca4fb559a31abdb237a74f3106ed57510579043348984fc1478', + }, + { + outPoint: { + hash: '0xb22b53a7613f5754850f118eae16caf867107d72a9b125ca596855583e712c97', + index: 0, + }, + stateChange: 'spent', + // origin cell infos + data: '0x', + capacity: 20, + type: null, + lock: '0xa4ce51d3c7e26701d4249179546f405d7a5ac24ffb3f2f6b8bef15017161e2e5', + }, + ] + + return cells +} + +// WIP +// mock an interface: get history transactions from chain +// related address may in inputs or outputs(such as input and return change) +// params: [addresses] +export const getHistoryTransactions = async () => { + return [ + { + deps: [], + hash: '0x03027d3cec6ba03a5c363879b20af806bdf955e17d75bc81cde5a91b56c13f17', + inputs: [], + outputs: [], + version: 0, + blockHash: '0xdbe33b04110a87ad72d1ba8aaada764bbdbc635a0944debd3c1c2fedde1685d1', + timestamp: '1545992487397', + }, + ] +} From e13be1780d61f2da91706477a08a2d8e20876f05 Mon Sep 17 00:00:00 2001 From: Keith Date: Tue, 26 Mar 2019 20:25:20 +0800 Subject: [PATCH 002/119] refactor: refactor channels into channels, controllers, services, move network management to neuron --- .gitignore | 3 + .../src/components/NetworkEditor/index.tsx | 60 +- .../neuron-ui/src/components/Router/index.tsx | 4 +- .../src/components/Settings/Networks.tsx | 10 +- .../Settings/RemoveNetworkDialog.tsx | 11 +- .../MainContent/actionCreators/networks.ts | 120 ++-- .../src/containers/MainContent/actions.ts | 2 +- .../src/containers/Providers/index.tsx | 164 +++--- packages/neuron-ui/src/contexts/Chain.ts | 1 + packages/neuron-ui/src/contexts/Settings.ts | 12 +- packages/neuron-ui/src/services/UILayer.ts | 23 +- .../src/utils/SyntheticEventEmitter.ts | 15 +- packages/neuron-ui/src/utils/const.ts | 19 +- packages/neuron-wallet/src/channel/index.ts | 526 ------------------ .../neuron-wallet/src/channel/listeners.ts | 315 +++++++++++ .../neuron-wallet/src/channel/transactions.ts | 0 packages/neuron-wallet/src/channel/wallet.ts | 98 ++++ .../neuron-wallet/src/commands/commands.ts | 1 + .../neuron-wallet/src/commands/dispatcher.ts | 1 + .../neuron-wallet/src/commands/handlers.ts | 8 +- .../neuron-wallet/src/controllers/index.ts | 12 + .../neuron-wallet/src/controllers/netowrks.ts | 136 +++++ .../src/controllers/transactions.ts | 69 +++ packages/neuron-wallet/src/main.ts | 23 +- packages/neuron-wallet/src/menu.ts | 2 +- packages/neuron-wallet/src/monitor.ts | 50 +- packages/neuron-wallet/src/preload.ts | 7 +- .../neuron-wallet/src/services/networks.ts | 82 +++ .../src/services/transactions.ts | 33 ++ packages/neuron-wallet/src/utils/const.ts | 7 +- .../neuron-wallet/src/utils/windowManage.ts | 25 + 31 files changed, 1074 insertions(+), 765 deletions(-) delete mode 100644 packages/neuron-wallet/src/channel/index.ts create mode 100644 packages/neuron-wallet/src/channel/listeners.ts create mode 100644 packages/neuron-wallet/src/channel/transactions.ts create mode 100644 packages/neuron-wallet/src/channel/wallet.ts create mode 100644 packages/neuron-wallet/src/controllers/index.ts create mode 100644 packages/neuron-wallet/src/controllers/netowrks.ts create mode 100644 packages/neuron-wallet/src/controllers/transactions.ts create mode 100644 packages/neuron-wallet/src/services/networks.ts create mode 100644 packages/neuron-wallet/src/services/transactions.ts create mode 100644 packages/neuron-wallet/src/utils/windowManage.ts diff --git a/.gitignore b/.gitignore index f60053cc23..18eae68aa2 100755 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,6 @@ lerna-debug.log #docs docs + +# vscode config +./packages/neuron-wallet/.vscode diff --git a/packages/neuron-ui/src/components/NetworkEditor/index.tsx b/packages/neuron-ui/src/components/NetworkEditor/index.tsx index 5074a05070..52f1bb455d 100644 --- a/packages/neuron-ui/src/components/NetworkEditor/index.tsx +++ b/packages/neuron-ui/src/components/NetworkEditor/index.tsx @@ -3,11 +3,9 @@ import { RouteComponentProps } from 'react-router-dom' import { Card, Form, Button, Alert } from 'react-bootstrap' import { useTranslation } from 'react-i18next' -import { MainActions, actionCreators } from '../../containers/MainContent/reducer' +import { MainActions, actionCreators, initState } from '../../containers/MainContent/reducer' import { ContentProps } from '../../containers/MainContent' import InlineInput, { InputProps } from '../../widgets/InlineInput' - -import ChainContext from '../../contexts/Chain' import SettingsContext from '../../contexts/Settings' enum PlaceHolder { @@ -19,40 +17,36 @@ enum TooltipText { URL = 'Address of the node', } -export default (props: React.PropsWithoutRef>) => { +export default (props: React.PropsWithoutRef>) => { const { networkEditor, dispatch, errorMsgs, match } = props - const settings = useContext(SettingsContext) - const chain = useContext(ChainContext) const { params } = match const [t] = useTranslation() - - // idx of the network to update, -1 means create - const idx = settings.networks.map(n => n.name).indexOf(params.name) + const settings = useContext(SettingsContext) useEffect(() => { - if (idx > -1) { + if (params.id === 'new') { dispatch({ type: MainActions.UpdateNetworkEditor, - payload: settings.networks[idx], + payload: initState.networkEditor, }) + } else { + const network = settings.networks.find(n => n.id === params.id) + if (network) { + dispatch({ + type: MainActions.UpdateNetworkEditor, + payload: { + name: network.name, + remote: network.remote, + }, + }) + } else { + // TODO: handle error + } } return () => { - // clean props of editor - dispatch({ - type: MainActions.UpdateNetworkEditor, - payload: { - name: '', - remote: '', - }, - }) - dispatch({ - type: MainActions.ErrorMessage, - payload: { - networks: '', - }, - }) + // TODO: clean } - }, [params.name]) + }, [params.id]) const inputs: InputProps[] = [ { @@ -85,8 +79,8 @@ export default (props: React.PropsWithoutRef - {idx === -1 ? t('settings.network.editnetwork.title') : params.name} - {errorMsgs.networks ? {errorMsgs.networks} : null} + {params.id === 'new' ? t('settings.network.editnetwork.title') : 'name'} + {errorMsgs.networks ? {t(`messages.${errorMsgs.networks}`)} : null}
{inputs.map(inputProps => ( @@ -99,10 +93,12 @@ export default (props: React.PropsWithoutRef { - if (chain.network.name === params.name) { - dispatch(actionCreators.setNetwork(networkEditor)) - } - dispatch(actionCreators.saveNetworks(idx, settings.networks, networkEditor, props.history.push)) + dispatch( + actionCreators.createOrUpdateNetowrk({ + id: params.id, + ...networkEditor, + }), + ) }} > Save diff --git a/packages/neuron-ui/src/components/Router/index.tsx b/packages/neuron-ui/src/components/Router/index.tsx index 1893f6d117..dfcb3ec04a 100644 --- a/packages/neuron-ui/src/components/Router/index.tsx +++ b/packages/neuron-ui/src/components/Router/index.tsx @@ -127,8 +127,8 @@ export const mainContents: CustomRoute[] = [ { name: `NetorkEditor`, path: Routes.NetworkEditor, - params: '/:name', - exact: true, + params: '/:id', + exact: false, component: NetworkEditor, }, { diff --git a/packages/neuron-ui/src/components/Settings/Networks.tsx b/packages/neuron-ui/src/components/Settings/Networks.tsx index a94a48559b..d0417cc42a 100644 --- a/packages/neuron-ui/src/components/Settings/Networks.tsx +++ b/packages/neuron-ui/src/components/Settings/Networks.tsx @@ -6,17 +6,15 @@ import { Configure } from 'grommet-icons' import { useTranslation } from 'react-i18next' import ChainContext, { Network } from '../../contexts/Chain' -import SettingsContext, { defaultNetworks } from '../../contexts/Settings' +import SettingsContext from '../../contexts/Settings' import { ContentProps } from '../../containers/MainContent' -import { Routes } from '../../utils/const' +import { Routes, UnremovableNetwork } from '../../utils/const' import { MainActions, actionCreators } from '../../containers/MainContent/reducer' import Dialog from '../../widgets/Dialog' import RemoveNetworkDialog from './RemoveNetworkDialog' import Dropdown, { DropDownItem } from '../../widgets/Dropdown' -const Testnet = defaultNetworks[0].name - const Popover = styled.div` position: relative; &:hover { @@ -68,7 +66,7 @@ const Networks = (props: React.PropsWithoutRef { - props.history.push(`${Routes.NetworkEditor}/${network.name}`) + props.history.push(`${Routes.NetworkEditor}/${network.id}`) }, disabled: isDefault, }, @@ -95,7 +93,7 @@ const Networks = (props: React.PropsWithoutRef{`${t('Remove Address')}: ${network.name || network.remote}`} {`Network of name: ${network.name}, address: ${network.remote} will be removed.`} - {isChecked ?

{`It's the active network, removing it will make reconnect to ${Testnet}`}

: null} + {isChecked ? ( +

{`It's the active network, removing it will make reconnect to ${UnremovableNetwork}`}

+ ) : null}
{ - dispatch(actionCreators.deleteNetwork(network.name)) - dispatch(actionCreators.setNetwork(defaultNetworks[0])) + dispatch(actionCreators.deleteNetwork(network.id)) }} > {t('Confirm')} diff --git a/packages/neuron-ui/src/containers/MainContent/actionCreators/networks.ts b/packages/neuron-ui/src/containers/MainContent/actionCreators/networks.ts index ed99146a97..eb92064085 100644 --- a/packages/neuron-ui/src/containers/MainContent/actionCreators/networks.ts +++ b/packages/neuron-ui/src/containers/MainContent/actionCreators/networks.ts @@ -1,98 +1,96 @@ -import { setNetwork } from '../../../services/UILayer' +import { setNetwork, networks } from '../../../services/UILayer' import { Network } from '../../../contexts/Chain' -import { defaultNetworks } from '../../../contexts/Settings' import { MainActions } from '../reducer' -import { Routes, Message, MAX_NETWORK_NAME_LENGTH } from '../../../utils/const' -import { saveNetworks, loadNetworks } from '../../../utils/localStorage' +import { Message, MAX_NETWORK_NAME_LENGTH, UnremovableNetworkId } from '../../../utils/const' import i18n from '../../../utils/i18n' -const Testnet = defaultNetworks[0].name - export default { - setNetwork: (network: Network) => { - setNetwork(network) + getNetwork: (id: string) => { + networks('show', id) return { - type: MainActions.SetNetwork, - payload: network, + type: MainActions.UpdateLoading, + payload: { + networks: true, + }, + } + }, + createOrUpdateNetowrk: ({ id, name, remote }: { id?: string; name: string; remote: string }) => { + if (id === 'new') { + networks('create', { + name, + remote, + }) + } else { + networks('update', { + id, + name, + remote, + }) + } + return { + type: MainActions.UpdateLoading, + payload: { + network: true, + }, } }, - saveNetworks: (idx: number, networks: Network[], editorNetwork: Network, navTo: (path: string) => void) => { - if (!editorNetwork.name) { + deleteNetwork: (id?: string) => { + if (id === undefined) throw new Error('No network id found') + if (id === UnremovableNetworkId) { return { type: MainActions.ErrorMessage, payload: { - networks: i18n.t(`messages.${Message.NameIsRequired}`), + networks: `This netowrk is unremovable`, }, } } - if (editorNetwork.name.length > MAX_NETWORK_NAME_LENGTH) { + networks('delete', id) + return { + type: MainActions.SetDialog, + payload: { + open: false, + }, + } + }, + // + // + // + setNetwork: (network: Network) => { + setNetwork(network) + return { + type: MainActions.Netowrks, + payload: network, + } + }, + saveNetwork: (params: { id: string; name: string; remote: string }) => { + if (!params.name) { return { type: MainActions.ErrorMessage, payload: { - networks: i18n.t(`messages.${Message.LengthOfNameShouldBeLessThanOrEqualTo}`, { - length: MAX_NETWORK_NAME_LENGTH, - }), + networks: Message.NameIsRequired, }, } } - if (!editorNetwork.remote) { + if (params.name.length > 28) { return { type: MainActions.ErrorMessage, payload: { - networks: i18n.t(`messages.${Message.URLIsRequired}`), + networks: `${i18n.t(Message.LengthOfNameShouldBeLessThanOrEqualTo)} ${MAX_NETWORK_NAME_LENGTH}`, }, } } - const ns = [...networks] - - if (idx === -1) { - // create - if (ns.map(n => n.name).indexOf(editorNetwork.name) > -1) { - // exist - return { - type: MainActions.ErrorMessage, - payload: { - networks: i18n.t(`messages.${Message.NetworkNameExist}`), - }, - } - } - ns.push(editorNetwork) - } else { - // edit - ns[idx] = editorNetwork - } - - // temp solution, better to remove - saveNetworks(ns) - window.dispatchEvent(new Event('NetworksUpdate')) - navTo(Routes.SettingsNetworks) - return { - type: MainActions.SaveNetworks, - payload: ns, - } - }, - - deleteNetwork: (name: string) => { - if (name === Testnet) { + if (!params.remote) { return { type: MainActions.ErrorMessage, payload: { - networks: i18n.t(`messages.is-unremovable`, { - target: Testnet, - }), + networks: Message.URLIsRequired, }, } } - const networks = loadNetworks() - const newNetworks = networks.filter((n: Network) => n.name !== name) - saveNetworks(newNetworks) - window.dispatchEvent(new Event('NetworksUpdate')) return { - type: MainActions.SetDialog, - payload: { - open: false, - }, + type: MainActions.Netowrks, + payload: params, } }, } diff --git a/packages/neuron-ui/src/containers/MainContent/actions.ts b/packages/neuron-ui/src/containers/MainContent/actions.ts index e93bb8ad47..db69510d31 100644 --- a/packages/neuron-ui/src/containers/MainContent/actions.ts +++ b/packages/neuron-ui/src/containers/MainContent/actions.ts @@ -7,7 +7,6 @@ export enum MainActions { GetTransaction, GetTransactions, SetPage, - SetNetwork, UpdateNetworkEditor, SaveNetworks, DeleteNetwork, @@ -19,6 +18,7 @@ export enum MainActions { UpdateTransfer, UpdatePassword, UpdateLoading, + Netowrks, } export default MainActions diff --git a/packages/neuron-ui/src/containers/Providers/index.tsx b/packages/neuron-ui/src/containers/Providers/index.tsx index f70d5027ed..aac4876198 100644 --- a/packages/neuron-ui/src/containers/Providers/index.tsx +++ b/packages/neuron-ui/src/containers/Providers/index.tsx @@ -1,13 +1,12 @@ import React, { useReducer, useEffect } from 'react' import { useTranslation } from 'react-i18next' -import ChainContext, { initChain, Cell, Transaction } from '../../contexts/Chain' +import ChainContext from '../../contexts/Chain' import WalletContext from '../../contexts/Wallet' import SettingsContext from '../../contexts/Settings' import { reducer, initProviders, ProviderActions, ProviderDispatch } from './reducer' import UILayer from '../../services/UILayer' -import { Channel, NetworkStatus } from '../../utils/const' -import { loadNetworks } from '../../utils/localStorage' +import { Channel } from '../../utils/const' const withProviders = (Comp: React.ComponentType<{ providerDispatch: ProviderDispatch }>) => ( props: React.Props, @@ -56,24 +55,6 @@ const withProviders = (Comp: React.ComponentType<{ providerDispatch: ProviderDis } }) - UILayer.on( - Channel.GetNetwork, - (_e: Event, args: Response<{ name: string; remote: string; connected: boolean }>) => { - if (args.status) { - dispatch({ - type: ProviderActions.Chain, - payload: { - network: { - name: args.result.name, - remote: args.result.remote, - status: args.result.connected ? NetworkStatus.Online : NetworkStatus.Offline, - }, - }, - }) - } - }, - ) - UILayer.on(Channel.GetBalance, (_e: Event, args: Response) => { if (args.status) { dispatch({ @@ -85,70 +66,113 @@ const withProviders = (Comp: React.ComponentType<{ providerDispatch: ProviderDis } }) - UILayer.on(Channel.SendCapacity, () => { - // TODO - }) - - UILayer.on(Channel.GetCellsByTypeHash, (_e: Event, args: Response) => { - // TODO: + UILayer.on(Channel.Wallet, (_e: Event, method: 'activeWallet', args: Response) => { if (args.status) { - dispatch({ - type: ProviderActions.Chain, - payload: { - cells: args.result, - }, - }) + switch (method) { + case 'activeWallet': { + dispatch({ + type: ProviderActions.Wallet, + payload: { + ...args.result, + }, + }) + break + } + default: { + break + } + } + } else { + // TODO: handle error } }) - UILayer.on(Channel.GetTransaction, (_e: Event, args: Response) => { + UILayer.on(Channel.Transactions, (_e: Event, method: 'index' | 'show', args: Response) => { if (args.status) { - dispatch({ - type: ProviderActions.Chain, - payload: { - transaction: args.result, - }, - }) + switch (method) { + case 'index': { + dispatch({ + type: ProviderActions.Chain, + payload: { + transactions: args.result, + }, + }) + break + } + case 'show': { + break + } + default: { + break + } + } } else { - dispatch({ - type: ProviderActions.Chain, - payload: { - transaction: initChain.transaction, - }, - }) + // TODO: handle error } }) UILayer.on( - Channel.GetTransactions, - (_e: Event, args: Response<{ totalCount: number; items: Transaction[]; pageNo: number; pageSize: number }>) => { - // TODO: + Channel.Networks, + ( + _e: Event, + method: 'index' | 'show' | 'create' | 'delete' | 'update' | 'activeNetwork' | 'setActive', + args: Response, + ) => { if (args.status) { - dispatch({ - type: ProviderActions.Chain, - payload: { - transactions: args.result, - }, - }) + switch (method) { + case 'index': { + // handle new network list + dispatch({ + type: ProviderActions.Settings, + payload: { + networks: args.result, + }, + }) + break + } + // case 'show': { + // // handle single network + // dispatch({ + // type: ProviderActions.Settings, + // payload: { + // transaction: args.result, + // }, + // }) + // break + // } + // case 'create': { + // break + // } + // case 'delete': { + // break + // } + case 'activeNetwork': { + dispatch({ + type: ProviderActions.Chain, + payload: { + network: args.result, + }, + }) + break + } + case 'setActive': { + dispatch({ + type: ProviderActions.Chain, + payload: { + network: args.result, + }, + }) + break + } + default: { + break + } + } } else { - dispatch({ - type: ProviderActions.Chain, - payload: { - transactions: initChain.transactions, - }, - }) + // TODO: handle error } }, ) - UILayer.addEventListener('NetworksUpdate', () => { - const networks = loadNetworks() - dispatch({ - type: ProviderActions.Settings, - payload: { - networks, - }, - }) - }) }, []) return ( diff --git a/packages/neuron-ui/src/contexts/Chain.ts b/packages/neuron-ui/src/contexts/Chain.ts index 026584f5be..af4038b2eb 100644 --- a/packages/neuron-ui/src/contexts/Chain.ts +++ b/packages/neuron-ui/src/contexts/Chain.ts @@ -27,6 +27,7 @@ export interface Transaction { hash: string } export interface Network { + id?: string name: string remote: string status?: NetworkStatus diff --git a/packages/neuron-ui/src/contexts/Settings.ts b/packages/neuron-ui/src/contexts/Settings.ts index 783562c6f8..05f112ac35 100644 --- a/packages/neuron-ui/src/contexts/Settings.ts +++ b/packages/neuron-ui/src/contexts/Settings.ts @@ -1,18 +1,8 @@ import { createContext } from 'react' -import { loadNetworks } from '../utils/localStorage' -import { DEFAULT_NETWORKS } from '../utils/const' import { Network } from './Chain' import { Wallet } from './Wallet' -export const defaultNetworks = (() => { - const cachedNetworks = loadNetworks() - if (cachedNetworks.length) { - return cachedNetworks - } - return DEFAULT_NETWORKS -})() - interface WalletSettings { seeds: string name: string @@ -27,7 +17,7 @@ export const initSettings: WalletSettings = { name: '', seedsValid: false, passwordValid: false, - networks: defaultNetworks, + networks: [], wallets: [], } diff --git a/packages/neuron-ui/src/services/UILayer.ts b/packages/neuron-ui/src/services/UILayer.ts index f7fc83fd42..70ecd73f3b 100644 --- a/packages/neuron-ui/src/services/UILayer.ts +++ b/packages/neuron-ui/src/services/UILayer.ts @@ -25,15 +25,18 @@ const UILayer = (() => { return new SyntheticEventEmitter(window.bridge.ipcRenderer) } return { - send: (channel: string, msg: any = '') => { + send: (channel: string, ...msg: any[]) => { console.warn(`Message: ${msg} to channel ${channel} failed due to Electron not loaded`) }, - sendSync: (channel: string, msg: any = '') => { + sendSync: (channel: string, ...msg: any[]) => { console.warn(`Message: ${msg} to channel ${channel} failed due to Electron not loaded`) }, on: (channel: string, cb: Function) => { console.warn(`Channel ${channel} and Function ${cb.toString()} failed due to Electron not loaded`) }, + once: (channel: string, cb: Function) => { + console.warn(`Channel ${channel} and Function ${cb.toString()} failed due to Electron not loaded`) + }, removeAllListeners: (channel?: string) => { console.warn(`Channel ${channel} cannot removed due to Electron not loaded`) }, @@ -101,7 +104,7 @@ export const sendCapacity = (items: TransferItem[], password: string) => { }) } export const setNetwork = (network: Network) => { - UILayer.send(Channel.SetNetwork, network) + UILayer.send(Channel.Networks, 'setActive', network.id) } export const getTransactions = ({ pageNo = 0, pageSize = 15, addresses = [] }: GetTransactionsParams) => { @@ -128,4 +131,18 @@ export const checkPassword = (walletID: string, password: string, handleResult: }) } +// promise style channel +export const networks = (method: string, params: any) => { + // return new Promise((resolve: Function, reject: Function) => { + UILayer.send(Channel.Networks, method, params) + // UILayer.once(Channel.Networks, (_e: Event, args: Response) => { + // if (args.status) { + // resolve(args.result) + // } else { + // reject(args.msg) + // } + // }) + // }) +} + export default UILayer diff --git a/packages/neuron-ui/src/utils/SyntheticEventEmitter.ts b/packages/neuron-ui/src/utils/SyntheticEventEmitter.ts index b83afbbe49..63ebd9ddfb 100644 --- a/packages/neuron-ui/src/utils/SyntheticEventEmitter.ts +++ b/packages/neuron-ui/src/utils/SyntheticEventEmitter.ts @@ -5,12 +5,12 @@ class SyntheticEventEmitter { this.handlers = handlers } - send = (channel: string, args: any = '') => { - this.handlers.forEach(handler => handler.send(channel, args)) + send = (channel: string, ...args: any[]) => { + this.handlers.forEach(handler => handler.send(channel, ...args)) } - sendSync = (channel: string, args: any = '') => { - return this.handlers.map(handler => handler.sendSync && handler.sendSync(channel, args)) + sendSync = (channel: string, ...args: any[]) => { + return this.handlers.map(handler => handler.sendSync && handler.sendSync(channel, ...args)) } on = (channel: string, cb: Function) => { @@ -20,6 +20,13 @@ class SyntheticEventEmitter { }) } + once = (channel: string, cb: Function) => { + this.removeAllListeners(channel) + return this.handlers.map(handler => { + return handler.once(channel, cb) + }) + } + removeAllListeners = (channel: string) => { this.handlers.forEach(handler => { if ('removeAllListeners' in handler) { diff --git a/packages/neuron-ui/src/utils/const.ts b/packages/neuron-ui/src/utils/const.ts index 6c0a355ce1..e63682811d 100644 --- a/packages/neuron-ui/src/utils/const.ts +++ b/packages/neuron-ui/src/utils/const.ts @@ -1,5 +1,7 @@ export const MAX_NETWORK_NAME_LENGTH = 28 export const EXPLORER = 'http://localhost:3000' +export const UnremovableNetwork = 'Testnet' +export const UnremovableNetworkId = '0' export const DEFAULT_NETWORKS = [ { @@ -20,16 +22,7 @@ export enum NetworkStatus { export enum Channel { // App SetLanguage = 'setLanguage', - // Chain - GetBlock = 'getBlock', GetTransaction = 'getTransaction', - GetLiveCell = 'getLiveCell', - GetTipHeader = 'getTipHeader', - GetTipBlockNumber = 'getTipBlockNumber', - GetLocalNodeId = 'getLocalNodeId', - GetNetwork = 'getNetwork', - SetNetwork = 'setNetwork', - SwitchNetwork = 'switchNetwork', // Wallet CreateWallet = 'createWallet', @@ -40,19 +33,21 @@ export enum Channel { SwitchWallet = 'switchWallet', GetBalance = 'getBalance', GetCellsByTypeHash = 'getCellsByTypeHash', - GetUnspentCells = 'getUnspentCells', GetTransactions = 'getTransactions', GetWallet = 'getWallet', CheckWalletPassword = 'checkWalletPassword', GetWallets = 'getWallets', SendCapacity = 'sendCapacity', - SendTransaction = 'sendTransaction', - Sign = 'sign', // Page NavTo = 'navTo', // Terminal Terminal = 'terminal', + // promise style channel + Networks = 'networks', + // + Transactions = 'transactions', + Wallet = 'wallet', } export enum Routes { diff --git a/packages/neuron-wallet/src/channel/index.ts b/packages/neuron-wallet/src/channel/index.ts deleted file mode 100644 index 0197c8db5c..0000000000 --- a/packages/neuron-wallet/src/channel/index.ts +++ /dev/null @@ -1,526 +0,0 @@ -import { ipcMain, Notification, BrowserWindow } from 'electron' - -import { Channel } from '../utils/const' -import { transactions, transactionCount, wallets, Wallet, updateWallets, validatePassword } from '../mock' -import asw from '../wallets/asw' -import ckbCore from '../core' -import Key from '../keys/key' -import WalletStore from '../store/WalletStore' - -enum ResponseStatus { - Fail, - Success, -} - -const checkPassword = (walletID: string, password: string) => { - const myWallet = wallets().find(wallet => wallet.id === walletID) - if (!myWallet) { - return { - status: ResponseStatus.Success, - result: false, - msg: 'Wallet not found', - } - } - if (validatePassword(myWallet, password)) { - return { - status: ResponseStatus.Success, - result: true, - } - } - return { - status: ResponseStatus.Success, - result: false, - msg: 'Wrong password', - } -} - -export class Listeners { - static start = ( - methods: string[] = [ - 'getLiveCell', - 'createWallet', - 'deleteWallet', - 'editWallet', - 'importWallet', - 'exportWallet', - 'switchWallet', - 'getBalance', - 'getCellsByTypeHash', - 'asw', - 'getUnspentCells', - 'getTransaction', - 'getTransactions', - 'getWallets', - 'checkWalletPassword', - 'sendCapacity', - 'sendTransaction', - 'sign', - 'setNetwork', - ], - ) => { - methods.forEach(method => { - const descriptor = Object.getOwnPropertyDescriptor(Listeners, method) - if (descriptor) { - descriptor.value() - } - }) - } - - /** - * @static getLiveCell - * @memberof ChannelListeners - * @description listen to get live cell channel - */ - static getLiveCell = () => { - return ipcMain.on(Channel.GetLiveCell, (e: Electron.Event, ...args: string[]) => { - e.sender.send(Channel.GetLiveCell, args) - }) - } - - // wallet - /** - * @static createWallet - * @memberof ChannelListeners - * @description channel to create wallet - */ - static createWallet = () => { - return ipcMain.on( - Channel.CreateWallet, - (e: Electron.Event, { walletName, password }: { walletName: string; password: string }) => { - const walletStore = new WalletStore() - const walletID = walletStore.saveWallet(walletName, Key.generateKey(password).getKeystore()) - e.sender.send(Channel.CreateWallet, { - status: ResponseStatus.Success, - result: walletID, - }) - }, - ) - } - - /** - * @static checkWalletPassword - * @memberof ChannelListeners - * @description channel to check wallets password - */ - static checkWalletPassword = () => { - return ipcMain.on( - Channel.CheckWalletPassword, - (e: Electron.Event, { walletID, password }: { walletID: string; password: string }) => { - e.sender.send(Channel.CheckWalletPassword, checkPassword(walletID, password)) - }, - ) - } - - /** - * @static deleteWallet - * @memberof ChannelListeners - * @description channel to delete wallet - */ - static deleteWallet = () => { - return ipcMain.on( - Channel.DeleteWallet, - (e: Electron.Event, { walletID, password }: { walletID: string; password: string }) => { - const args = checkPassword(walletID, password) - if (args.result) { - const walletList = wallets() - const index = walletList.findIndex(wallet => wallet.id === walletID) - walletList.splice(index, 1) - updateWallets(walletList) - } - e.sender.send(Channel.DeleteWallet, args) - }, - ) - } - - /** - * @static editWallet - * @memberof ChannelListeners - * @description channel to edit wallet - */ - static editWallet = () => { - return ipcMain.on( - Channel.EditWallet, - ( - e: Electron.Event, - { - walletID, - walletName, - password, - newPassword, - }: { walletID: string; walletName: string; password: string; newPassword: string }, - ) => { - const args = checkPassword(walletID, password) - if (args.result) { - const wallet = wallets().find(item => item.id === walletID) - if (wallet) { - wallet.name = walletName - wallet.password = newPassword - } - } - e.sender.send(Channel.EditWallet, args) - }, - ) - } - - /** - * @static importWallet - * @memberof ChannelListeners - * @description channel to import a wallet - */ - static importWallet = () => { - return ipcMain.on( - Channel.ImportWallet, - ( - e: Electron.Event, - { - walletName, - password, - mnemonic, - keystore, - }: { walletName: string; password: string; mnemonic: string; keystore: string }, - ) => { - try { - const walletStore = new WalletStore() - let storedKeystore - if (mnemonic) { - storedKeystore = Key.fromMnemonic(mnemonic, true, password).getKeystore() - } else if (keystore) { - storedKeystore = Key.fromKeystoreString(keystore, password).getKeystore() - } - if (storedKeystore) { - walletStore.saveWallet(walletName, storedKeystore) - e.sender.send(Channel.ImportWallet, { - status: ResponseStatus.Success, - result: true, - }) - } else { - e.sender.send(Channel.ImportWallet, { - status: ResponseStatus.Success, - result: false, - msg: 'Error', - }) - } - } catch (error) { - e.sender.send(Channel.ImportWallet, { - status: ResponseStatus.Success, - result: false, - msg: error.message, - }) - } - }, - ) - } - - /** - * @static exportWallet - * @memberof ChannelListeners - * @description channel to export wallet - */ - static exportWallet = () => { - return ipcMain.on(Channel.ExportWallet, (e: Electron.Event) => { - e.sender.send(Channel.ExportWallet, { - status: ResponseStatus.Success, - result: `wallet exported`, - }) - }) - } - - /** - * @static switchWallet - * @memberof ChannelListeners - * @description channel to switch wallet - */ - static switchWallet = () => { - return ipcMain.on(Channel.SwitchWallet, (e: Electron.Event, wallet: Wallet) => { - e.sender.send(Channel.SwitchWallet, { - status: ResponseStatus.Success, - result: wallet.name, - }) - }) - } - - /** - * @static getBalance - * @memberof ChannelListeners - * @description channel to get balance - */ - static getBalance = () => { - return ipcMain.on(Channel.GetBalance, (e: Electron.Event) => { - e.sender.send(Channel.GetBalance, { - status: ResponseStatus.Success, - result: `balance`, - }) - }) - } - - /** - * @static getCellsByTypeHash - * @memberof ChannelListeners - * @description channel to get cells by type hash - */ - static getCellsByTypeHash = () => { - return ipcMain.on(Channel.GetCellsByTypeHash, (e: Electron.Event) => { - e.sender.send(Channel.GetCellsByTypeHash, { - status: ResponseStatus.Success, - result: [`cell`], - }) - }) - } - - /** - * @static asw - * @memberof ChannelListeners - * @description channel to get asw - */ - static asw = () => { - return ipcMain.on(`ASW`, (e: Electron.Event) => { - e.sender.send(`ASW`, { - status: ResponseStatus.Success, - result: asw, - }) - }) - } - - /** - * @static getUnspentCells - * @memberof ChannelListeners - * @description channel to get unspent cells - */ - static getUnspentCells = () => { - return ipcMain.on(Channel.GetUnspentCells, (e: Electron.Event) => { - e.sender.send(Channel.GetUnspentCells, { - status: ResponseStatus.Success, - result: [`cell`], - }) - }) - } - - /** - * @static getTransaction - * @memberof ChannelListeners - * @description get transaction by hash - */ - static getTransaction = () => { - return ipcMain.on(Channel.GetTransaction, (e: Electron.Event, { hash }: { hash: string }) => { - const transaction = transactions.find(tx => `${tx.hash}` === hash) - if (transaction) { - e.sender.send(Channel.GetTransaction, { - status: ResponseStatus.Success, - result: transaction, - }) - } else { - e.sender.send(Channel.GetTransaction, { - status: ResponseStatus.Fail, - msg: `Transaction of ${hash} is not found`, - }) - } - }) - } - - /** - * @static getTransactions - * @memberof ChannelListeners - * @description get transactions - */ - static getTransactions = () => { - return ipcMain.on( - Channel.GetTransactions, - ( - e: Electron.Event, - { pageNo, pageSize, addresses }: { pageNo: number; pageSize: number; addresses: string[] }, - ) => { - e.sender.send(Channel.GetTransactions, { - status: ResponseStatus.Success, - result: { - addresses, - pageNo, - pageSize, - totalCount: transactionCount, - items: transactions.map(tx => ({ - ...tx, - value: tx.value * pageNo * pageSize, - })), - }, - }) - }, - ) - } - - /** - * @static getWallets - * @memberof ChannelListeners - * @description channel to get wallets - */ - static getWallets = () => { - return ipcMain.on(Channel.GetWallets, (e: Electron.Event) => { - e.sender.send(Channel.GetWallets, { - status: ResponseStatus.Success, - result: wallets(), - }) - }) - } - - /** - * @static sendCapacity - * @memberof ChannelListeners - * @description channel to send capacity - */ - static sendCapacity = () => { - return ipcMain.on( - Channel.SendCapacity, - ( - e: Electron.Event, - { items, password }: { items: { address: string; capacity: string; unit: string }[]; password: string }, - ) => { - setTimeout(() => { - if (!items.length || !items[0].address) { - e.returnValue = { - status: ResponseStatus.Fail, - msg: 'Address not specified', - } - return - } - // TODO: verify password - // TODO: verify capacity - const notification = new Notification({ - title: `Send Capacity`, - body: `Send Capacity to CKB with ${JSON.stringify( - { - items, - password, - }, - null, - 2, - )}`, - }) - notification.show() - e.returnValue = { - status: ResponseStatus.Success, - msg: `Send Successfully`, - } - }, 3000) - }, - ) - } - - /** - * @static sendTransaction - * @memberof ChannelListeners - * @description channel to send transaction - */ - static sendTransaction = () => { - return ipcMain.on(Channel.SendTransaction, (e: Electron.Event) => { - const notification = new Notification({ - title: `Send Transaction`, - body: `transaction detail`, - }) - notification.show() - e.sender.send(Channel.SendTransaction, { - status: ResponseStatus.Success, - result: { - hash: 'transaction hash', - }, - }) - }) - } - - /** - * @static sign - * @memberof ChannelListeners - * @description channel to sign msg - */ - static sign = () => { - return ipcMain.on(Channel.Sign, (e: Electron.Event) => { - e.sender.send(Channel.Sign, { - status: ResponseStatus.Success, - result: `signed msg`, - }) - }) - } - - /** - * @static setNetwork - * @memberof ChannelListeners - * @description channel to set network - */ - static setNetwork = () => { - return ipcMain.on(Channel.SetNetwork, (e: Electron.Event, network: { name: string; remote: string }) => { - // TODO: - ckbCore.setNode({ - url: network.remote, - }) - Object.defineProperty(ckbCore.node, 'name', { - value: network.name, - }) - e.sender.send(Channel.GetNetwork, { - status: ResponseStatus.Success, - result: { - ...network, - connected: false, - }, - }) - }) - } -} - -export default class WalletChannel extends Listeners { - public win: BrowserWindow - - constructor(window: BrowserWindow) { - super() - this.win = window - } - - public sendWallet = ( - wallet: any = { - name: 'asw', - address: asw.address, - publicKey: asw.publicKey, - }, - ) => { - this.win.webContents.send(Channel.GetWallet, { - status: ResponseStatus.Success, - result: wallet, - }) - } - - public setUILocale = (locale: string) => { - this.win.webContents.send(Channel.SetLanguage, { - status: ResponseStatus.Success, - result: locale, - }) - } - - public navTo = (route: string) => { - this.win.webContents.send(Channel.NavTo, { - status: ResponseStatus.Success, - result: { - router: route, - }, - }) - } - - public sendTransactionHistory = ({ - pageNo, - pageSize, - addresses, - }: { - pageNo: number - pageSize: number - addresses: string[] - }) => { - this.win.webContents.send(Channel.GetTransactions, { - status: ResponseStatus.Success, - result: { - addresses, - pageNo, - pageSize, - totalCount: transactionCount, - items: transactions.map(tx => ({ - ...tx, - value: tx.value * pageNo * pageSize, - })), - }, - }) - } -} diff --git a/packages/neuron-wallet/src/channel/listeners.ts b/packages/neuron-wallet/src/channel/listeners.ts new file mode 100644 index 0000000000..fb6050fc95 --- /dev/null +++ b/packages/neuron-wallet/src/channel/listeners.ts @@ -0,0 +1,315 @@ +import { ipcMain, Notification } from 'electron' + +import { Channel } from '../utils/const' +import { transactions, transactionCount, wallets, validatePassword, updateWallets, Wallet } from '../mock' +import asw from '../wallets/asw' +import { ResponseCode } from './wallet' +import NetworksController from '../controllers/netowrks' +import TransactionsController from '../controllers/transactions' + +const checkPassword = (walletID: string, password: string) => { + const myWallet = wallets().find(wallet => wallet.id === walletID) + if (!myWallet) { + return { + status: ResponseCode.Success, + result: false, + msg: 'Wallet not found', + } + } + if (validatePassword(myWallet, password)) { + return { + status: ResponseCode.Success, + result: true, + } + } + return { + status: ResponseCode.Success, + result: false, + msg: 'Wrong password', + } +} + +// controll styled code +export default class Listeners { + static start = ( + methods: string[] = [ + 'deleteWallet', + 'editWallet', + 'switchWallet', + 'getBalance', + 'asw', + 'getUnspentCells', + 'getTransaction', + 'getTransactions', + 'getWallets', + 'checkWalletPassword', + 'sendCapacity', + // controller style code + 'networks', + ], + ) => { + methods.forEach(method => { + const descriptor = Object.getOwnPropertyDescriptor(Listeners, method) + if (descriptor) { + descriptor.value() + } + }) + } + + // wallet + + /** + * @static checkWalletPassword + * @memberof ChannelListeners + * @description channel to check wallets password + */ + static checkWalletPassword = () => { + return ipcMain.on( + Channel.CheckWalletPassword, + (e: Electron.Event, { walletID, password }: { walletID: string; password: string }) => { + e.sender.send(Channel.CheckWalletPassword, checkPassword(walletID, password)) + }, + ) + } + + /** + * @static deleteWallet + * @memberof ChannelListeners + * @description channel to delete wallet + */ + static deleteWallet = () => { + return ipcMain.on( + Channel.DeleteWallet, + (e: Electron.Event, { walletID, password }: { walletID: string; password: string }) => { + const args = checkPassword(walletID, password) + if (args.result) { + const walletList = wallets() + const index = walletList.findIndex(wallet => wallet.id === walletID) + walletList.splice(index, 1) + updateWallets(walletList) + } + e.sender.send(Channel.DeleteWallet, args) + }, + ) + } + + /** + * @static editWallet + * @memberof ChannelListeners + * @description channel to edit wallet + */ + static editWallet = () => { + return ipcMain.on( + Channel.EditWallet, + ( + e: Electron.Event, + { + walletID, + walletName, + password, + newPassword, + }: { walletID: string; walletName: string; password: string; newPassword: string }, + ) => { + const args = checkPassword(walletID, password) + if (args.result) { + const wallet = wallets().find(item => item.id === walletID) + if (wallet) { + wallet.name = walletName + wallet.password = newPassword + } + } + e.sender.send(Channel.EditWallet, args) + }, + ) + } + + /** + * @static switchWallet + * @memberof ChannelListeners + * @description channel to switch wallet + */ + static switchWallet = () => { + return ipcMain.on(Channel.SwitchWallet, (e: Electron.Event, wallet: Wallet) => { + e.sender.send(Channel.SwitchWallet, { + status: ResponseCode.Success, + result: wallet.name, + }) + }) + } + + /** + * @static getBalance + * @memberof ChannelListeners + * @description channel to get balance + */ + static getBalance = () => { + return ipcMain.on(Channel.GetBalance, (e: Electron.Event) => { + e.sender.send(Channel.GetBalance, { + status: ResponseCode.Success, + result: `balance`, + }) + }) + } + + /** + * @static asw + * @memberof ChannelListeners + * @description channel to get asw + */ + static asw = () => { + return ipcMain.on(`ASW`, (e: Electron.Event) => { + e.sender.send(`ASW`, { + status: ResponseCode.Success, + result: asw, + }) + }) + } + + /** + * @static getUnspentCells + * @memberof ChannelListeners + * @description channel to get unspent cells + */ + static getUnspentCells = () => { + return ipcMain.on(Channel.GetUnspentCells, (e: Electron.Event) => { + e.sender.send(Channel.GetUnspentCells, { + status: ResponseCode.Success, + result: [`cell`], + }) + }) + } + + /** + * @static getTransaction + * @memberof ChannelListeners + * @description get transaction by hash + */ + static getTransaction = () => { + return ipcMain.on(Channel.GetTransaction, (e: Electron.Event, { hash }: { hash: string }) => { + const transaction = transactions.find(tx => `${tx.hash}` === hash) + if (transaction) { + e.sender.send(Channel.GetTransaction, { + status: ResponseCode.Success, + result: transaction, + }) + } else { + e.sender.send(Channel.GetTransaction, { + status: ResponseCode.Fail, + msg: `Transaction of ${hash} is not found`, + }) + } + }) + } + + /** + * @static getTransactions + * @memberof ChannelListeners + * @description get transactions + */ + static getTransactions = () => { + return ipcMain.on( + Channel.GetTransactions, + ( + e: Electron.Event, + { pageNo, pageSize, addresses }: { pageNo: number; pageSize: number; addresses: string[] }, + ) => { + e.sender.send(Channel.GetTransactions, { + status: ResponseCode.Success, + result: { + addresses, + pageNo, + pageSize, + totalCount: transactionCount, + items: transactions.map(tx => ({ + ...tx, + value: tx.value * pageNo * pageSize, + })), + }, + }) + }, + ) + } + + /** + * @static getWallets + * @memberof ChannelListeners + * @description channel to get wallets + */ + static getWallets = () => { + return ipcMain.on(Channel.GetWallets, (e: Electron.Event) => { + e.sender.send(Channel.GetWallets, { + status: ResponseCode.Success, + result: wallets(), + }) + }) + } + + /** + * @static sendCapacity + * @memberof ChannelListeners + * @description channel to send capacity + */ + static sendCapacity = () => { + return ipcMain.on( + Channel.SendCapacity, + ( + e: Electron.Event, + { items, password }: { items: { address: string; capacity: string; unit: string }[]; password: string }, + ) => { + setTimeout(() => { + if (!items.length || !items[0].address) { + e.returnValue = { + status: ResponseCode.Fail, + msg: 'Address not specified', + } + return + } + // TODO: verify password + // TODO: verify capacity + const notification = new Notification({ + title: `Send Capacity`, + body: `Send Capacity to CKB with ${JSON.stringify( + { + items, + password, + }, + null, + 2, + )}`, + }) + notification.show() + e.returnValue = { + status: ResponseCode.Success, + msg: `Send Successfully`, + } + }, 3000) + }, + ) + } + + // controller style code + public static networks = () => { + return ipcMain.on(Channel.Networks, (e: Electron.Event, method: keyof typeof NetworksController, params: any) => { + e.sender.send(Channel.Networks, method, (NetworksController[method] as Function)(params)) + }) + } + + public static transactions = () => { + return ipcMain.on( + Channel.Transactions, + (e: Electron.Event, method: keyof typeof TransactionsController, params: any) => { + e.sender.send(Channel.Transactions, method, (TransactionsController[method] as Function)(params)) + }, + ) + } + + // TODO: add wallet controller and service + // public wallet = () => { + // return ipcMain.on( + // Channel.Wallet, + // (e: Electron.Event, { method, params }: { method: keyof typeof NetworksController; params: any }) => { + // e.sender.send(Channel.Networks, (NetworksController[method] as Function)(params)) + // }, + // ) + // } +} diff --git a/packages/neuron-wallet/src/channel/transactions.ts b/packages/neuron-wallet/src/channel/transactions.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/neuron-wallet/src/channel/wallet.ts b/packages/neuron-wallet/src/channel/wallet.ts new file mode 100644 index 0000000000..68f5917dda --- /dev/null +++ b/packages/neuron-wallet/src/channel/wallet.ts @@ -0,0 +1,98 @@ +import { BrowserWindow } from 'electron' +import { Network } from '../services/networks' +import { Response } from '../controllers' + +import { Channel } from '../utils/const' +import { transactions, transactionCount } from '../mock' +import asw from '../wallets/asw' +import Listeners from './listeners' + +// controll styled code +import NetworksController from '../controllers/netowrks' + +export enum ResponseCode { + Fail, + Success, +} + +export default class WalletChannel extends Listeners { + public win: BrowserWindow + + public netowrksController: any + + constructor(window: BrowserWindow) { + super() + this.win = window + this.netowrksController = new NetworksController(this) + } + + public sendWallet = ( + wallet: any = { + name: 'asw', + address: asw.address, + publicKey: asw.publicKey, + }, + ) => { + this.win.webContents.send(Channel.Wallet, 'activeWallet', { + status: ResponseCode.Success, + result: wallet, + }) + } + + public setUILocale = (locale: string) => { + this.win.webContents.send(Channel.SetLanguage, { + status: ResponseCode.Success, + result: locale, + }) + } + + public navTo = (route: string) => { + this.win.webContents.send(Channel.NavTo, { + status: ResponseCode.Success, + result: { + router: route, + }, + }) + } + + public sendTransactionHistory = ({ + pageNo, + pageSize, + addresses, + }: { + pageNo: number + pageSize: number + addresses: string[] + }) => { + this.win.webContents.send(Channel.GetTransactions, { + status: ResponseCode.Success, + result: { + addresses, + pageNo, + pageSize, + totalCount: transactionCount, + items: transactions.map(tx => ({ + ...tx, + value: tx.value * pageNo * pageSize, + })), + }, + }) + } + + public syncNetworks = (params: { + active?: Response + networks?: Response + status?: Response + }) => { + if (!this.win) return + if (params.networks) { + this.win.webContents.send(Channel.Networks, 'index', params.networks) + } + if (params.active) { + this.win.webContents.send(Channel.Networks, 'activeNetwork', params.active) + } + if (params.status) { + this.win.webContents.send(Channel.Networks, 'status', params.status) + } + } +} diff --git a/packages/neuron-wallet/src/commands/commands.ts b/packages/neuron-wallet/src/commands/commands.ts index 23e864a87b..7069d127f9 100644 --- a/packages/neuron-wallet/src/commands/commands.ts +++ b/packages/neuron-wallet/src/commands/commands.ts @@ -2,6 +2,7 @@ // In many cases a command comes from the app shell and is sent to renderer process with channel. enum Command { SendWallet = 'send-wallet', // default to asw for now + SyncNetworks = 'sync-network', SendTransactionHistory = 'send-transaction-history', ShowAbout = 'show-about', ShowPreferences = 'show-preferences', diff --git a/packages/neuron-wallet/src/commands/dispatcher.ts b/packages/neuron-wallet/src/commands/dispatcher.ts index eb01b955ff..005b5a8069 100644 --- a/packages/neuron-wallet/src/commands/dispatcher.ts +++ b/packages/neuron-wallet/src/commands/dispatcher.ts @@ -5,6 +5,7 @@ const maps = { [Command.ShowAbout as string]: handlers.aboutHandler, [Command.ShowPreferences as string]: handlers.rendererMessageHandler, [Command.SendWallet as string]: handlers.rendererMessageHandler, + [Command.SyncNetworks as string]: handlers.rendererMessageHandler, [Command.ShowTerminal as string]: handlers.rendererMessageHandler, [Command.SendTransactionHistory as string]: handlers.rendererMessageHandler, [Command.SetUILocale as string]: handlers.rendererMessageHandler, diff --git a/packages/neuron-wallet/src/commands/handlers.ts b/packages/neuron-wallet/src/commands/handlers.ts index 08f6bbe384..5f33f79e2a 100644 --- a/packages/neuron-wallet/src/commands/handlers.ts +++ b/packages/neuron-wallet/src/commands/handlers.ts @@ -1,6 +1,6 @@ import { app, dialog, shell, BrowserWindow } from 'electron' import Command from './commands' -import WalletChannel from '../channel' +import WalletChannel from '../channel/wallet' export interface CommandInfo { channel: WalletChannel @@ -63,6 +63,12 @@ const rendererMessageHandler: Handler = (command, info) => { } break } + case Command.SyncNetworks: { + if (info) { + info.channel.syncNetworks(info.extra! as any) + } + break + } case Command.SendTransactionHistory: { if (info && info.extra && info.extra.pageNo && info.extra.pageSize && info.extra.addresses) { info.channel.sendTransactionHistory({ diff --git a/packages/neuron-wallet/src/controllers/index.ts b/packages/neuron-wallet/src/controllers/index.ts new file mode 100644 index 0000000000..1e5012842c --- /dev/null +++ b/packages/neuron-wallet/src/controllers/index.ts @@ -0,0 +1,12 @@ +export enum ResponseCode { + Fail, + Success, +} +export interface Response { + status: ResponseCode + msg?: string + result?: T +} +class Controller {} + +export default Controller diff --git a/packages/neuron-wallet/src/controllers/netowrks.ts b/packages/neuron-wallet/src/controllers/netowrks.ts new file mode 100644 index 0000000000..4fd6ebf335 --- /dev/null +++ b/packages/neuron-wallet/src/controllers/netowrks.ts @@ -0,0 +1,136 @@ +import { Channel } from '../utils/const' +import { ResponseCode, Response } from '.' +import NetowrkService, { Network } from '../services/networks' +import WalletChannel from '../channel/wallet' +import windowManage from '../main' + +class NetworksController { + public channel: WalletChannel + + static service = new NetowrkService() + + constructor(channel: WalletChannel) { + this.channel = channel + NetworksController.service = new NetowrkService() + } + + public static index = (): Response => { + const networks = NetworksController.service.index() + if (networks) { + return { + status: ResponseCode.Success, + result: networks, + } + } + + return { + status: ResponseCode.Fail, + msg: 'networks not found', + } + } + + public static show = (id: string): Response => { + const network = NetworksController.service.show(id) + if (network) { + return { + status: ResponseCode.Success, + result: network, + } + } + return { + status: ResponseCode.Fail, + msg: 'Network found found', + } + } + + public static create = (network: Partial): Response => { + // TODO: validation + if (network.name && network.remote) { + const newNetwork = NetworksController.service.create(network.name, network.remote) + // TODO: sync + windowManage.broad(Channel.Networks, 'index', NetworksController.index()) + return { + status: ResponseCode.Success, + result: newNetwork, + } + } + return { + status: ResponseCode.Fail, + msg: 'Invalid network', + } + } + + public static update = (network: Network): Response => { + // TODO: verification + const success = NetworksController.service.update(network) + if (success) { + // TODO: sync + windowManage.broad(Channel.Networks, 'index', NetworksController.index()) + return { + status: ResponseCode.Success, + result: true, + } + } + return { + status: ResponseCode.Fail, + msg: 'Network not found', + } + } + + public static delete = (id: string): Response => { + // regard the first network as the default one, which is not allowed to be deleted + const defaultNetwork = NetworksController.service.index()[0] + const activeNetwork = NetworksController.service.active + if (id === defaultNetwork.id) { + return { + status: ResponseCode.Fail, + msg: 'Default network is unremovable', + } + } + const success = NetworksController.service.delete(id) + if (success) { + // check if deleted network is current network, switch to default network if true + if (activeNetwork && activeNetwork.id === id) { + NetworksController.service.setActive(defaultNetwork.id) + windowManage.broad(Channel.Networks, 'activeNetwork', NetworksController.activeNetwork()) + } + // TODO: sync + windowManage.broad(Channel.Networks, 'index', NetworksController.index()) + return { + status: ResponseCode.Success, + result: true, + } + } + return { + status: ResponseCode.Fail, + msg: 'Network not found', + } + } + + public static activeNetwork = () => ({ + status: ResponseCode.Success, + result: NetworksController.service.active, + }) + + public static setActive = (id: string): Response => { + const success = NetworksController.service.setActive(id) + + if (success) { + return { + status: ResponseCode.Success, + result: NetworksController.service.active, + } + } + + return { + status: ResponseCode.Fail, + msg: 'Network not found', + } + } + + public static status = () => { + return false + } +} + +export default NetworksController diff --git a/packages/neuron-wallet/src/controllers/transactions.ts b/packages/neuron-wallet/src/controllers/transactions.ts new file mode 100644 index 0000000000..d993e97f95 --- /dev/null +++ b/packages/neuron-wallet/src/controllers/transactions.ts @@ -0,0 +1,69 @@ +import TransactionsService, { Transaction } from '../services/transactions' +import { ResponseCode, Response } from '.' +import WalletChannel from '../channel/wallet' + +export default class TransactionsController { + public channel: WalletChannel + + static service = new TransactionsService() + + constructor(channel: WalletChannel) { + this.channel = channel + } + + public static index = (): Response => { + const transactions = TransactionsService.index() + if (transactions) { + return { + status: ResponseCode.Success, + result: transactions, + } + } + return { + status: ResponseCode.Fail, + msg: 'Transactions not found', + } + } + + public static show = (hash: string): Response => { + const network = TransactionsService.show(hash) + if (network) { + return { + status: ResponseCode.Success, + result: network, + } + } + return { + status: ResponseCode.Fail, + msg: 'Transaction not found', + } + } + + public static create = (transaction: Transaction): Response => { + const success = TransactionsService.create(transaction) + if (success) { + return { + status: ResponseCode.Success, + result: transaction, + } + } + return { + status: ResponseCode.Fail, + msg: 'Fail to create transaction', + } + } + + public static delete = (hash: string): Response => { + const success = TransactionsService.delete(hash) + if (success) { + return { + status: ResponseCode.Success, + result: true, + } + } + return { + status: ResponseCode.Fail, + msg: 'Fail to delete transaction', + } + } +} diff --git a/packages/neuron-wallet/src/main.ts b/packages/neuron-wallet/src/main.ts index 5d6ce6882e..ca4c37fbf3 100644 --- a/packages/neuron-wallet/src/main.ts +++ b/packages/neuron-wallet/src/main.ts @@ -3,12 +3,16 @@ import windowStateKeeper from 'electron-window-state' import path from 'path' import env from './env' -import WalletChannel from './channel' +import WalletChannel from './channel/wallet' import TerminalChannel from './channel/terminal' import monitorChain from './monitor' import i18n from './i18n' import mainmenu from './menu' import dispatch, { Command } from './commands/dispatcher' +import NetowrksController from './controllers/netowrks' +import WindowManage from './utils/windowManage' + +const windowManage = new WindowManage() let mainWindow: Electron.BrowserWindow | null // start listening @@ -16,11 +20,24 @@ WalletChannel.start() const initUILayer = (win: BrowserWindow) => { const channel = new WalletChannel(win) + // const netowrksController = new NetowrksController(channel) dispatch(Command.SendWallet, { channel, }) + dispatch(Command.SyncNetworks, { + channel, + extra: { + active: NetowrksController.activeNetwork(), + networks: NetowrksController.index(), + connected: { + status: 1, + result: 1, + }, + }, + }) + dispatch(Command.SetUILocale, { channel, extra: { @@ -80,6 +97,8 @@ function createWindow() { monitorChain(mainWindow.webContents) const terminalChannel = new TerminalChannel(mainWindow.webContents) terminalChannel.start() + + windowManage.add(mainWindow) } app.on('ready', createWindow) @@ -95,3 +114,5 @@ app.on('activate', () => { createWindow() } }) + +export default windowManage diff --git a/packages/neuron-wallet/src/menu.ts b/packages/neuron-wallet/src/menu.ts index f113f99aa1..0d5eb031c9 100644 --- a/packages/neuron-wallet/src/menu.ts +++ b/packages/neuron-wallet/src/menu.ts @@ -2,7 +2,7 @@ import { app, Menu, MenuItem, MenuItemConstructorOptions, BrowserWindow } from ' import env from './env' import dispatch, { Command } from './commands/dispatcher' import i18n from './i18n' -import WalletChannel from './channel' +import WalletChannel from './channel/wallet' const separator: MenuItemConstructorOptions = { type: 'separator', diff --git a/packages/neuron-wallet/src/monitor.ts b/packages/neuron-wallet/src/monitor.ts index 00662ce332..631ad502a6 100644 --- a/packages/neuron-wallet/src/monitor.ts +++ b/packages/neuron-wallet/src/monitor.ts @@ -1,5 +1,9 @@ import { interval } from 'rxjs' -import { map, distinctUntilChanged, flatMap } from 'rxjs/operators' +import { + // map, + distinctUntilChanged, + flatMap, +} from 'rxjs/operators' import { Channel } from './utils/const' import ckbCore from './core' import asw from './wallets/asw' @@ -17,28 +21,28 @@ const monitors = { } const monitorChain = (webContents: Electron.WebContents) => { - numbers - .pipe(map(() => monitors.network())) - .pipe( - distinctUntilChanged((x, y) => { - return x.connected === y.connected && x.remote === y.remote - }), - ) - .subscribe( - result => { - if (!webContents) return - webContents.send(Channel.GetNetwork, { - status: 1, - result, - }) - }, - (err: Error) => { - logger.log({ - level: 'error', - message: err.message, - }) - }, - ) + // numbers + // .pipe(map(() => monitors.network())) + // .pipe( + // distinctUntilChanged((x, y) => { + // return x.connected === y.connected && x.remote === y.remote + // }), + // ) + // .subscribe( + // result => { + // if (!webContents) return + // webContents.send(Channel.Networks, { + // status: 1, + // result, + // }) + // }, + // (err: Error) => { + // logger.log({ + // level: 'error', + // message: err.message, + // }) + // }, + // ) numbers .pipe(flatMap(monitors.tipBlockNumber)) diff --git a/packages/neuron-wallet/src/preload.ts b/packages/neuron-wallet/src/preload.ts index 921b54a516..20d040a98b 100644 --- a/packages/neuron-wallet/src/preload.ts +++ b/packages/neuron-wallet/src/preload.ts @@ -9,11 +9,14 @@ declare global { const bridge = { ipcRenderer: { - send: (channel: string, args: any = '') => ipcRenderer.send(channel, args), - sendSync: (channel: string, args: any = '') => ipcRenderer.sendSync(channel, args), + send: (channel: string, ...args: any[]) => ipcRenderer.send(channel, ...args), + sendSync: (channel: string, ...args: any[]) => ipcRenderer.sendSync(channel, ...args), on: (channel: string, cb: Function) => { ipcRenderer.on(channel, cb) }, + once: (channel: string, cb: Function) => { + ipcRenderer.on(channel, cb) + }, removeAllListeners: (channel: string) => ipcRenderer.removeAllListeners(channel), }, } diff --git a/packages/neuron-wallet/src/services/networks.ts b/packages/neuron-wallet/src/services/networks.ts new file mode 100644 index 0000000000..b0e8474593 --- /dev/null +++ b/packages/neuron-wallet/src/services/networks.ts @@ -0,0 +1,82 @@ +import { v4 } from 'uuid' + +export interface Network { + id: string + name: string + remote: string +} + +export const defaultNetowrks: Network[] = [ + { + id: '0', + name: 'Testnet', + remote: 'http://localhost:8114', + }, + { + id: '1', + name: 'Local', + remote: 'http://localhost:8114', + }, +] + +export default class NetworkService { + public networks: Network[] = [] + + public active: Network | undefined = undefined + + constructor() { + defaultNetowrks.forEach(network => this.create(network.name, network.remote)) + this.setActive(this.networks[0].id) + } + + public index = (): Network[] => { + return this.networks + } + + public show = (id: string): Network | undefined => { + return this.networks.find(network => network.id === id) + } + + public update = ({ id, name, remote }: Network): boolean => { + const network = this.show(id) + if (network) { + if (name) { + network.name = name + } + if (remote) { + network.remote = remote + } + return true + } + return false + } + + public create = (name: string, remote: string): Network => { + // TODO: verification + const network = { + id: v4(), + name, + remote, + } + this.networks.push(network) + return network + } + + public delete = (id: string): boolean => { + const network = this.show(id) + if (network) { + this.networks = this.networks.filter(n => n.id !== id) + return true + } + return false + } + + public setActive = (id: string): boolean => { + const network = this.show(id) + if (network) { + this.active = network + return true + } + return false + } +} diff --git a/packages/neuron-wallet/src/services/transactions.ts b/packages/neuron-wallet/src/services/transactions.ts new file mode 100644 index 0000000000..6378bd462d --- /dev/null +++ b/packages/neuron-wallet/src/services/transactions.ts @@ -0,0 +1,33 @@ +export interface Transaction { + hash: string + type: number + value: string +} +let currentTransaction: Transaction[] = [] +export default class TransactionsService { + public static index = (): Transaction[] => { + return currentTransaction + } + + public static show = (hash: string): Transaction | undefined => { + return currentTransaction.find(transaction => transaction.hash === hash) + } + + public static create = (transaction: Transaction): Transaction => { + const sameTransaction = TransactionsService.show(transaction.hash) + if (sameTransaction) { + throw new Error('Transaction exists') + } + currentTransaction.push(transaction) + return transaction + } + + public static delete = (hash: string): boolean => { + const transaction = TransactionsService.show(hash) + if (transaction) { + currentTransaction = currentTransaction.filter(tx => tx.hash !== hash) + return true + } + return false + } +} diff --git a/packages/neuron-wallet/src/utils/const.ts b/packages/neuron-wallet/src/utils/const.ts index 0226b61b37..06cf1a092a 100644 --- a/packages/neuron-wallet/src/utils/const.ts +++ b/packages/neuron-wallet/src/utils/const.ts @@ -8,9 +8,6 @@ export enum Channel { GetTipHeader = 'getTipHeader', GetTipBlockNumber = 'getTipBlockNumber', GetLocalNodeId = 'getLocalNodeId', - GetNetwork = 'getNetwork', - SetNetwork = 'setNetwork', - SwitchNetwork = 'switchNetwork', // Wallet CreateWallet = 'createWallet', @@ -34,6 +31,10 @@ export enum Channel { NavTo = 'navTo', // Terminal Terminal = 'terminal', + // controller style code + Networks = 'networks', + Wallet = 'wallet', + Transactions = 'transactions', } export default { diff --git a/packages/neuron-wallet/src/utils/windowManage.ts b/packages/neuron-wallet/src/utils/windowManage.ts new file mode 100644 index 0000000000..34a1defc7b --- /dev/null +++ b/packages/neuron-wallet/src/utils/windowManage.ts @@ -0,0 +1,25 @@ +import { BrowserWindow } from 'electron' +import { Channel } from './const' + +class WindowManage { + public windows: BrowserWindow[] + + constructor() { + this.windows = [] + } + + public add = (win: BrowserWindow) => { + this.windows.push(win) + } + + // public remove = () + public broad = (channel: Channel, method: string, params: any) => { + this.windows.forEach(window => { + if (window) { + window.webContents.send(channel, method, params) + } + }) + } +} + +export default WindowManage From a6de4d13a271ea724bc25ced228ead8045be84b8 Mon Sep 17 00:00:00 2001 From: Keith Date: Tue, 26 Mar 2019 17:19:09 +0800 Subject: [PATCH 003/119] refactor: refactor transaction history channel[wip] --- .../src/components/Transaction/index.tsx | 10 ++++----- .../src/containers/Providers/index.tsx | 22 +++++-------------- packages/neuron-ui/src/services/UILayer.ts | 16 ++------------ packages/neuron-ui/src/utils/const.ts | 1 - .../neuron-wallet/src/channel/listeners.ts | 3 ++- packages/neuron-wallet/src/channel/wallet.ts | 2 +- .../src/controllers/transactions.ts | 20 +++++++++++++---- packages/neuron-wallet/src/mock.ts | 4 ++-- .../src/services/transactions.ts | 15 +++++++++++-- 9 files changed, 46 insertions(+), 47 deletions(-) diff --git a/packages/neuron-ui/src/components/Transaction/index.tsx b/packages/neuron-ui/src/components/Transaction/index.tsx index b44490fac2..60c81c4cb9 100644 --- a/packages/neuron-ui/src/components/Transaction/index.tsx +++ b/packages/neuron-ui/src/components/Transaction/index.tsx @@ -10,7 +10,7 @@ import { ProviderActions } from '../../containers/Providers/reducer' import ChainContext from '../../contexts/Chain' const Transaction = (props: React.PropsWithoutRef>) => { - const { match, errorMsgs, dispatch, providerDispatch, loadings, history } = props + const { match, errorMsgs, dispatch, providerDispatch, history } = props const chain = useContext(ChainContext) const [t] = useTranslation() const { transaction } = chain @@ -35,25 +35,23 @@ const Transaction = (props: React.PropsWithoutRef {`${t('history.transaction-hash')}: `} - {loading ? 'Loading' : transaction.hash} + {transaction.hash} {errorMsgs.transaction ? {t(`messages.${errorMsgs.transaction}`)} : null} {`${t('history.amount')}: `} - {loading ? 'Loading' : transaction.value} + {transaction.value} {`${t('history.date')}: `} - {loading ? 'Loading' : new Date(transaction.date).toLocaleString()} + {new Date(transaction.date).toLocaleString()} diff --git a/packages/neuron-ui/src/containers/Providers/index.tsx b/packages/neuron-ui/src/containers/Providers/index.tsx index aac4876198..48ae2ff684 100644 --- a/packages/neuron-ui/src/containers/Providers/index.tsx +++ b/packages/neuron-ui/src/containers/Providers/index.tsx @@ -100,6 +100,12 @@ const withProviders = (Comp: React.ComponentType<{ providerDispatch: ProviderDis break } case 'show': { + dispatch({ + type: ProviderActions.Chain, + payload: { + transaction: args.result, + }, + }) break } default: { @@ -130,22 +136,6 @@ const withProviders = (Comp: React.ComponentType<{ providerDispatch: ProviderDis }) break } - // case 'show': { - // // handle single network - // dispatch({ - // type: ProviderActions.Settings, - // payload: { - // transaction: args.result, - // }, - // }) - // break - // } - // case 'create': { - // break - // } - // case 'delete': { - // break - // } case 'activeNetwork': { dispatch({ type: ProviderActions.Chain, diff --git a/packages/neuron-ui/src/services/UILayer.ts b/packages/neuron-ui/src/services/UILayer.ts index 70ecd73f3b..8ef5f85411 100644 --- a/packages/neuron-ui/src/services/UILayer.ts +++ b/packages/neuron-ui/src/services/UILayer.ts @@ -108,7 +108,7 @@ export const setNetwork = (network: Network) => { } export const getTransactions = ({ pageNo = 0, pageSize = 15, addresses = [] }: GetTransactionsParams) => { - UILayer.send(Channel.GetTransactions, { + UILayer.send(Channel.Transactions, 'index', { pageNo, pageSize, addresses, @@ -116,9 +116,7 @@ export const getTransactions = ({ pageNo = 0, pageSize = 15, addresses = [] }: G } export const getTransaction = (hash: string) => { - UILayer.send(Channel.GetTransaction, { - hash, - }) + UILayer.send(Channel.Transactions, 'show', hash) } export const checkPassword = (walletID: string, password: string, handleResult: any) => { @@ -131,18 +129,8 @@ export const checkPassword = (walletID: string, password: string, handleResult: }) } -// promise style channel export const networks = (method: string, params: any) => { - // return new Promise((resolve: Function, reject: Function) => { UILayer.send(Channel.Networks, method, params) - // UILayer.once(Channel.Networks, (_e: Event, args: Response) => { - // if (args.status) { - // resolve(args.result) - // } else { - // reject(args.msg) - // } - // }) - // }) } export default UILayer diff --git a/packages/neuron-ui/src/utils/const.ts b/packages/neuron-ui/src/utils/const.ts index e63682811d..c211bcc60c 100644 --- a/packages/neuron-ui/src/utils/const.ts +++ b/packages/neuron-ui/src/utils/const.ts @@ -22,7 +22,6 @@ export enum NetworkStatus { export enum Channel { // App SetLanguage = 'setLanguage', - GetTransaction = 'getTransaction', // Wallet CreateWallet = 'createWallet', diff --git a/packages/neuron-wallet/src/channel/listeners.ts b/packages/neuron-wallet/src/channel/listeners.ts index fb6050fc95..0a29367c75 100644 --- a/packages/neuron-wallet/src/channel/listeners.ts +++ b/packages/neuron-wallet/src/channel/listeners.ts @@ -46,6 +46,7 @@ export default class Listeners { 'sendCapacity', // controller style code 'networks', + 'transactions', ], ) => { methods.forEach(method => { @@ -222,7 +223,7 @@ export default class Listeners { totalCount: transactionCount, items: transactions.map(tx => ({ ...tx, - value: tx.value * pageNo * pageSize, + value: +tx.value * pageNo * pageSize, })), }, }) diff --git a/packages/neuron-wallet/src/channel/wallet.ts b/packages/neuron-wallet/src/channel/wallet.ts index 68f5917dda..89c360c322 100644 --- a/packages/neuron-wallet/src/channel/wallet.ts +++ b/packages/neuron-wallet/src/channel/wallet.ts @@ -73,7 +73,7 @@ export default class WalletChannel extends Listeners { totalCount: transactionCount, items: transactions.map(tx => ({ ...tx, - value: tx.value * pageNo * pageSize, + value: +tx.value * pageNo * pageSize, })), }, }) diff --git a/packages/neuron-wallet/src/controllers/transactions.ts b/packages/neuron-wallet/src/controllers/transactions.ts index d993e97f95..078e38c466 100644 --- a/packages/neuron-wallet/src/controllers/transactions.ts +++ b/packages/neuron-wallet/src/controllers/transactions.ts @@ -1,4 +1,4 @@ -import TransactionsService, { Transaction } from '../services/transactions' +import TransactionsService, { Transaction, TransactionsParams } from '../services/transactions' import { ResponseCode, Response } from '.' import WalletChannel from '../channel/wallet' @@ -11,12 +11,24 @@ export default class TransactionsController { this.channel = channel } - public static index = (): Response => { - const transactions = TransactionsService.index() + public static index = ( + params?: TransactionsParams, + ): Response<{ pageNo: number; pageSize: number; totalCount: number; items: Transaction[] } | Transaction[]> => { + const transactions = TransactionsService.index(params) if (transactions) { + if (!params) { + return { + status: ResponseCode.Success, + result: transactions, + } + } return { status: ResponseCode.Success, - result: transactions, + result: { + ...params, + totalCount: TransactionsService.index().length, + items: transactions, + }, } } return { diff --git a/packages/neuron-wallet/src/mock.ts b/packages/neuron-wallet/src/mock.ts index edd941b642..717fe7df0a 100644 --- a/packages/neuron-wallet/src/mock.ts +++ b/packages/neuron-wallet/src/mock.ts @@ -5,8 +5,8 @@ export const transactions = Array.from({ }) .map(() => ({ date: new Date().getTime() - Math.round(Math.random() * 100000000), - value: Math.random(), - hash: Math.round(Math.random() * 10000000000000000), + value: `${Math.random()}`, + hash: `${Math.round(Math.random() * 10000000000000000)}`, type: Math.round(Math.random() * 2), })) .sort((p, n) => +n.date - +p.date) diff --git a/packages/neuron-wallet/src/services/transactions.ts b/packages/neuron-wallet/src/services/transactions.ts index 6378bd462d..7f2775f497 100644 --- a/packages/neuron-wallet/src/services/transactions.ts +++ b/packages/neuron-wallet/src/services/transactions.ts @@ -1,11 +1,22 @@ +import { transactions } from '../mock' + export interface Transaction { hash: string type: number + date: number value: string } -let currentTransaction: Transaction[] = [] +export interface TransactionsParams { + pageNo: number + pageSize: number + addresses: string[] +} +let currentTransaction: Transaction[] = transactions export default class TransactionsService { - public static index = (): Transaction[] => { + public static index = (params?: TransactionsParams): Transaction[] => { + if (params) { + // TODO + } return currentTransaction } From f16a1416ab86f261725cdc3660dc7e33fdcf31e0 Mon Sep 17 00:00:00 2001 From: Keith Date: Tue, 26 Mar 2019 19:27:07 +0800 Subject: [PATCH 004/119] refactor: clean unused code and add enumeration of controller method --- .../src/components/NetworkEditor/index.tsx | 25 +++++- .../src/containers/Header/reducer.ts | 4 +- .../MainContent/actionCreators/networks.ts | 47 +++++++--- .../actionCreators/transactions.ts | 6 +- .../src/containers/Providers/index.tsx | 88 +++++++++---------- packages/neuron-ui/src/services/UILayer.ts | 38 ++++---- packages/neuron-ui/src/utils/const.ts | 10 --- .../neuron-wallet/src/channel/listeners.ts | 83 +++-------------- .../neuron-wallet/src/channel/transactions.ts | 0 packages/neuron-wallet/src/channel/wallet.ts | 23 ++--- .../neuron-wallet/src/controllers/index.ts | 3 +- .../neuron-wallet/src/controllers/netowrks.ts | 23 +++-- .../src/controllers/transactions.ts | 2 +- packages/neuron-wallet/src/main.ts | 1 - .../neuron-wallet/src/services/networks.ts | 1 + packages/neuron-wallet/src/utils/const.ts | 20 ----- 16 files changed, 163 insertions(+), 211 deletions(-) delete mode 100644 packages/neuron-wallet/src/channel/transactions.ts diff --git a/packages/neuron-ui/src/components/NetworkEditor/index.tsx b/packages/neuron-ui/src/components/NetworkEditor/index.tsx index 52f1bb455d..4e45496ad1 100644 --- a/packages/neuron-ui/src/components/NetworkEditor/index.tsx +++ b/packages/neuron-ui/src/components/NetworkEditor/index.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useContext } from 'react' +import React, { useEffect, useContext, useRef } from 'react' import { RouteComponentProps } from 'react-router-dom' import { Card, Form, Button, Alert } from 'react-bootstrap' import { useTranslation } from 'react-i18next' @@ -18,11 +18,18 @@ enum TooltipText { } export default (props: React.PropsWithoutRef>) => { - const { networkEditor, dispatch, errorMsgs, match } = props + const { networkEditor, dispatch, errorMsgs, match, history } = props const { params } = match const [t] = useTranslation() const settings = useContext(SettingsContext) + const currentNetwork = settings.networks.find(n => n.id === params.id) + + const ref = useRef({ + count: settings.networks.length, + detail: currentNetwork, + }) + useEffect(() => { if (params.id === 'new') { dispatch({ @@ -48,6 +55,18 @@ export default (props: React.PropsWithoutRef { + if ( + ref.current.count !== settings.networks.length || + JSON.stringify(ref.current.detail) !== JSON.stringify(currentNetwork) + ) { + history.goBack() + } + return () => { + // TODO: clean + } + }, [settings.networks.length, JSON.stringify(currentNetwork)]) + const inputs: InputProps[] = [ { label: t('settings.network.editnetwork.rpcurl'), @@ -80,7 +99,7 @@ export default (props: React.PropsWithoutRef {params.id === 'new' ? t('settings.network.editnetwork.title') : 'name'} - {errorMsgs.networks ? {t(`messages.${errorMsgs.networks}`)} : null} + {errorMsgs.networks ? {errorMsgs.networks} : null} {inputs.map(inputProps => ( diff --git a/packages/neuron-ui/src/containers/Header/reducer.ts b/packages/neuron-ui/src/containers/Header/reducer.ts index f3dfc0523c..872773208d 100644 --- a/packages/neuron-ui/src/containers/Header/reducer.ts +++ b/packages/neuron-ui/src/containers/Header/reducer.ts @@ -1,4 +1,4 @@ -import { setNetwork } from '../../services/UILayer' +import { networks, NetworksMethod } from '../../services/UILayer' import { Network } from '../../contexts/Chain' export enum HeaderActions { @@ -28,7 +28,7 @@ export const reducer = (state: any, action: { type: HeaderActions; payload?: any export const actionCreators = { setNetwork: (network: Network) => { - setNetwork(network) + networks(NetworksMethod.SetActive, network.id) return { type: HeaderActions.SetNetwork, payload: network, diff --git a/packages/neuron-ui/src/containers/MainContent/actionCreators/networks.ts b/packages/neuron-ui/src/containers/MainContent/actionCreators/networks.ts index eb92064085..efe70d54e3 100644 --- a/packages/neuron-ui/src/containers/MainContent/actionCreators/networks.ts +++ b/packages/neuron-ui/src/containers/MainContent/actionCreators/networks.ts @@ -1,13 +1,13 @@ -import { setNetwork, networks } from '../../../services/UILayer' +import { networks, NetworksMethod } from '../../../services/UILayer' import { Network } from '../../../contexts/Chain' import { MainActions } from '../reducer' -import { Message, MAX_NETWORK_NAME_LENGTH, UnremovableNetworkId } from '../../../utils/const' +import { Message, MAX_NETWORK_NAME_LENGTH, UnremovableNetworkId, UnremovableNetwork } from '../../../utils/const' import i18n from '../../../utils/i18n' export default { getNetwork: (id: string) => { - networks('show', id) + networks(NetworksMethod.Show, id) return { type: MainActions.UpdateLoading, payload: { @@ -16,13 +16,39 @@ export default { } }, createOrUpdateNetowrk: ({ id, name, remote }: { id?: string; name: string; remote: string }) => { + if (!name) { + return { + type: MainActions.ErrorMessage, + payload: { + networks: i18n.t(`messages.${Message.NameIsRequired}`), + }, + } + } + if (name.length > MAX_NETWORK_NAME_LENGTH) { + return { + type: MainActions.ErrorMessage, + payload: { + networks: i18n.t(`messages.${Message.LengthOfNameShouldBeLessThanOrEqualTo}`, { + length: MAX_NETWORK_NAME_LENGTH, + }), + }, + } + } + if (!remote) { + return { + type: MainActions.ErrorMessage, + payload: { + networks: i18n.t(`messages.${Message.URLIsRequired}`), + }, + } + } if (id === 'new') { - networks('create', { + networks(NetworksMethod.Create, { name, remote, }) } else { - networks('update', { + networks(NetworksMethod.Update, { id, name, remote, @@ -41,11 +67,13 @@ export default { return { type: MainActions.ErrorMessage, payload: { - networks: `This netowrk is unremovable`, + networks: i18n.t(`messages.is-unremovable`, { + target: UnremovableNetwork, + }), }, } } - networks('delete', id) + networks(NetworksMethod.Delete, id) return { type: MainActions.SetDialog, payload: { @@ -53,11 +81,8 @@ export default { }, } }, - // - // - // setNetwork: (network: Network) => { - setNetwork(network) + networks(NetworksMethod.SetActive, network.id) return { type: MainActions.Netowrks, payload: network, diff --git a/packages/neuron-ui/src/containers/MainContent/actionCreators/transactions.ts b/packages/neuron-ui/src/containers/MainContent/actionCreators/transactions.ts index 1ed2a26a73..94ef08a8ba 100644 --- a/packages/neuron-ui/src/containers/MainContent/actionCreators/transactions.ts +++ b/packages/neuron-ui/src/containers/MainContent/actionCreators/transactions.ts @@ -1,9 +1,9 @@ import { MainActions } from '../reducer' -import { getTransactions, GetTransactionsParams, getTransaction } from '../../../services/UILayer' +import { transactions, GetTransactionsParams, TransactionsMethod } from '../../../services/UILayer' export default { getTransaction: (hash: string) => { - getTransaction(hash) + transactions(TransactionsMethod.Show, hash) return { type: MainActions.UpdateLoading, payload: { @@ -13,7 +13,7 @@ export default { }, getTransactions: (params: GetTransactionsParams) => { - getTransactions(params) + transactions(TransactionsMethod.Index, params) return { type: MainActions.UpdateLoading, payload: { diff --git a/packages/neuron-ui/src/containers/Providers/index.tsx b/packages/neuron-ui/src/containers/Providers/index.tsx index 48ae2ff684..928ad68df2 100644 --- a/packages/neuron-ui/src/containers/Providers/index.tsx +++ b/packages/neuron-ui/src/containers/Providers/index.tsx @@ -5,7 +5,7 @@ import WalletContext from '../../contexts/Wallet' import SettingsContext from '../../contexts/Settings' import { reducer, initProviders, ProviderActions, ProviderDispatch } from './reducer' -import UILayer from '../../services/UILayer' +import UILayer, { NetworksMethod, TransactionsMethod } from '../../services/UILayer' import { Channel } from '../../utils/const' const withProviders = (Comp: React.ComponentType<{ providerDispatch: ProviderDispatch }>) => ( @@ -87,10 +87,10 @@ const withProviders = (Comp: React.ComponentType<{ providerDispatch: ProviderDis } }) - UILayer.on(Channel.Transactions, (_e: Event, method: 'index' | 'show', args: Response) => { + UILayer.on(Channel.Transactions, (_e: Event, method: TransactionsMethod, args: Response) => { if (args.status) { switch (method) { - case 'index': { + case TransactionsMethod.Index: { dispatch({ type: ProviderActions.Chain, payload: { @@ -99,7 +99,7 @@ const withProviders = (Comp: React.ComponentType<{ providerDispatch: ProviderDis }) break } - case 'show': { + case TransactionsMethod.Show: { dispatch({ type: ProviderActions.Chain, payload: { @@ -117,52 +117,44 @@ const withProviders = (Comp: React.ComponentType<{ providerDispatch: ProviderDis } }) - UILayer.on( - Channel.Networks, - ( - _e: Event, - method: 'index' | 'show' | 'create' | 'delete' | 'update' | 'activeNetwork' | 'setActive', - args: Response, - ) => { - if (args.status) { - switch (method) { - case 'index': { - // handle new network list - dispatch({ - type: ProviderActions.Settings, - payload: { - networks: args.result, - }, - }) - break - } - case 'activeNetwork': { - dispatch({ - type: ProviderActions.Chain, - payload: { - network: args.result, - }, - }) - break - } - case 'setActive': { - dispatch({ - type: ProviderActions.Chain, - payload: { - network: args.result, - }, - }) - break - } - default: { - break - } + UILayer.on(Channel.Networks, (_e: Event, method: NetworksMethod, args: Response) => { + if (args.status) { + switch (method) { + case NetworksMethod.Index: { + dispatch({ + type: ProviderActions.Settings, + payload: { + networks: args.result, + }, + }) + break + } + case NetworksMethod.ActiveNetwork: { + dispatch({ + type: ProviderActions.Chain, + payload: { + network: args.result, + }, + }) + break + } + case NetworksMethod.SetActive: { + dispatch({ + type: ProviderActions.Chain, + payload: { + network: args.result, + }, + }) + break + } + default: { + break } - } else { - // TODO: handle error } - }, - ) + } else { + // TODO: handle error + } + }) }, []) return ( diff --git a/packages/neuron-ui/src/services/UILayer.ts b/packages/neuron-ui/src/services/UILayer.ts index 8ef5f85411..0079fc3825 100644 --- a/packages/neuron-ui/src/services/UILayer.ts +++ b/packages/neuron-ui/src/services/UILayer.ts @@ -1,6 +1,5 @@ import { Channel, CapacityUnit } from '../utils/const' import SyntheticEventEmitter from '../utils/SyntheticEventEmitter' -import { Network } from '../contexts/Chain' declare global { interface Window { @@ -9,6 +8,21 @@ declare global { } } +export enum NetworksMethod { + Index = 'index', + Show = 'show', + Create = 'create', + Update = 'update', + Delete = 'delete', + ActiveNetwork = 'activeNetwork', + SetActive = 'setActive', +} + +export enum TransactionsMethod { + Index = 'index', + Show = 'show', +} + export interface TransferItem { address: string capacity: string @@ -92,10 +106,6 @@ export const importWallet = (wallet: { walletName: string; password: string; mne UILayer.send(Channel.ImportWallet, wallet) export const exportWallet = () => UILayer.send(Channel.ExportWallet) -export const getLiveCell = (outpoint: any) => UILayer.send('getLiveCell', outpoint) -export const getCellsByTypeHash = (typeHash: string) => { - UILayer.send(Channel.GetCellsByTypeHash, typeHash) -} export const sendCapacity = (items: TransferItem[], password: string) => { return UILayer.sendSync(Channel.SendCapacity, { @@ -103,21 +113,6 @@ export const sendCapacity = (items: TransferItem[], password: string) => { password, }) } -export const setNetwork = (network: Network) => { - UILayer.send(Channel.Networks, 'setActive', network.id) -} - -export const getTransactions = ({ pageNo = 0, pageSize = 15, addresses = [] }: GetTransactionsParams) => { - UILayer.send(Channel.Transactions, 'index', { - pageNo, - pageSize, - addresses, - }) -} - -export const getTransaction = (hash: string) => { - UILayer.send(Channel.Transactions, 'show', hash) -} export const checkPassword = (walletID: string, password: string, handleResult: any) => { UILayer.on(Channel.CheckWalletPassword, (_e: any, args: Response) => { @@ -132,5 +127,8 @@ export const checkPassword = (walletID: string, password: string, handleResult: export const networks = (method: string, params: any) => { UILayer.send(Channel.Networks, method, params) } +export const transactions = (method: string, params: GetTransactionsParams | string) => { + UILayer.send(Channel.Transactions, method, params) +} export default UILayer diff --git a/packages/neuron-ui/src/utils/const.ts b/packages/neuron-ui/src/utils/const.ts index c211bcc60c..7460eb7518 100644 --- a/packages/neuron-ui/src/utils/const.ts +++ b/packages/neuron-ui/src/utils/const.ts @@ -20,31 +20,21 @@ export enum NetworkStatus { } export enum Channel { - // App SetLanguage = 'setLanguage', - - // Wallet CreateWallet = 'createWallet', DeleteWallet = 'deleteWallet', EditWallet = 'editWallet', ImportWallet = 'importWallet', ExportWallet = 'exportWallet', - SwitchWallet = 'switchWallet', GetBalance = 'getBalance', - GetCellsByTypeHash = 'getCellsByTypeHash', - GetTransactions = 'getTransactions', GetWallet = 'getWallet', CheckWalletPassword = 'checkWalletPassword', GetWallets = 'getWallets', SendCapacity = 'sendCapacity', - // Page NavTo = 'navTo', - // Terminal Terminal = 'terminal', - // promise style channel Networks = 'networks', - // Transactions = 'transactions', Wallet = 'wallet', } diff --git a/packages/neuron-wallet/src/channel/listeners.ts b/packages/neuron-wallet/src/channel/listeners.ts index 0a29367c75..089d2366c7 100644 --- a/packages/neuron-wallet/src/channel/listeners.ts +++ b/packages/neuron-wallet/src/channel/listeners.ts @@ -1,7 +1,7 @@ import { ipcMain, Notification } from 'electron' import { Channel } from '../utils/const' -import { transactions, transactionCount, wallets, validatePassword, updateWallets, Wallet } from '../mock' +import { wallets, validatePassword, updateWallets, Wallet } from '../mock' import asw from '../wallets/asw' import { ResponseCode } from './wallet' import NetworksController from '../controllers/netowrks' @@ -29,7 +29,6 @@ const checkPassword = (walletID: string, password: string) => { } } -// controll styled code export default class Listeners { static start = ( methods: string[] = [ @@ -38,13 +37,9 @@ export default class Listeners { 'switchWallet', 'getBalance', 'asw', - 'getUnspentCells', - 'getTransaction', - 'getTransactions', 'getWallets', 'checkWalletPassword', 'sendCapacity', - // controller style code 'networks', 'transactions', ], @@ -166,71 +161,6 @@ export default class Listeners { }) } - /** - * @static getUnspentCells - * @memberof ChannelListeners - * @description channel to get unspent cells - */ - static getUnspentCells = () => { - return ipcMain.on(Channel.GetUnspentCells, (e: Electron.Event) => { - e.sender.send(Channel.GetUnspentCells, { - status: ResponseCode.Success, - result: [`cell`], - }) - }) - } - - /** - * @static getTransaction - * @memberof ChannelListeners - * @description get transaction by hash - */ - static getTransaction = () => { - return ipcMain.on(Channel.GetTransaction, (e: Electron.Event, { hash }: { hash: string }) => { - const transaction = transactions.find(tx => `${tx.hash}` === hash) - if (transaction) { - e.sender.send(Channel.GetTransaction, { - status: ResponseCode.Success, - result: transaction, - }) - } else { - e.sender.send(Channel.GetTransaction, { - status: ResponseCode.Fail, - msg: `Transaction of ${hash} is not found`, - }) - } - }) - } - - /** - * @static getTransactions - * @memberof ChannelListeners - * @description get transactions - */ - static getTransactions = () => { - return ipcMain.on( - Channel.GetTransactions, - ( - e: Electron.Event, - { pageNo, pageSize, addresses }: { pageNo: number; pageSize: number; addresses: string[] }, - ) => { - e.sender.send(Channel.GetTransactions, { - status: ResponseCode.Success, - result: { - addresses, - pageNo, - pageSize, - totalCount: transactionCount, - items: transactions.map(tx => ({ - ...tx, - value: +tx.value * pageNo * pageSize, - })), - }, - }) - }, - ) - } - /** * @static getWallets * @memberof ChannelListeners @@ -288,13 +218,22 @@ export default class Listeners { ) } - // controller style code + /** + * @method networks + * @memberof ChannelListeners + * @description listen to Channel.Networks and invoke corresponding method of networksController + */ public static networks = () => { return ipcMain.on(Channel.Networks, (e: Electron.Event, method: keyof typeof NetworksController, params: any) => { e.sender.send(Channel.Networks, method, (NetworksController[method] as Function)(params)) }) } + /** + * @method transactions + * @memberof ChannelListeners + * @description listen to Channel.Transactions and invoke corresponding method of transactionsController + */ public static transactions = () => { return ipcMain.on( Channel.Transactions, diff --git a/packages/neuron-wallet/src/channel/transactions.ts b/packages/neuron-wallet/src/channel/transactions.ts deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/neuron-wallet/src/channel/wallet.ts b/packages/neuron-wallet/src/channel/wallet.ts index 89c360c322..52ca01c40d 100644 --- a/packages/neuron-wallet/src/channel/wallet.ts +++ b/packages/neuron-wallet/src/channel/wallet.ts @@ -1,14 +1,14 @@ import { BrowserWindow } from 'electron' -import { Network } from '../services/networks' + +import Listeners from './listeners' import { Response } from '../controllers' +import NetworksController, { NetworksMethod } from '../controllers/netowrks' +import { Network } from '../services/networks' -import { Channel } from '../utils/const' -import { transactions, transactionCount } from '../mock' import asw from '../wallets/asw' -import Listeners from './listeners' -// controll styled code -import NetworksController from '../controllers/netowrks' +import { Channel } from '../utils/const' +import { transactions, transactionCount } from '../mock' export enum ResponseCode { Fail, @@ -86,13 +86,14 @@ export default class WalletChannel extends Listeners { }) => { if (!this.win) return if (params.networks) { - this.win.webContents.send(Channel.Networks, 'index', params.networks) + this.win.webContents.send(Channel.Networks, NetworksMethod.Index, params.networks) } if (params.active) { - this.win.webContents.send(Channel.Networks, 'activeNetwork', params.active) - } - if (params.status) { - this.win.webContents.send(Channel.Networks, 'status', params.status) + this.win.webContents.send(Channel.Networks, NetworksMethod.ActiveNetwork, params.active) } + // TODO: status handler + // if (params.status) { + // this.win.webContents.send(Channel.Networks, 'status', params.status) + // } } } diff --git a/packages/neuron-wallet/src/controllers/index.ts b/packages/neuron-wallet/src/controllers/index.ts index 1e5012842c..ab22916554 100644 --- a/packages/neuron-wallet/src/controllers/index.ts +++ b/packages/neuron-wallet/src/controllers/index.ts @@ -7,6 +7,5 @@ export interface Response { msg?: string result?: T } -class Controller {} -export default Controller +export default undefined diff --git a/packages/neuron-wallet/src/controllers/netowrks.ts b/packages/neuron-wallet/src/controllers/netowrks.ts index 4fd6ebf335..45559b7997 100644 --- a/packages/neuron-wallet/src/controllers/netowrks.ts +++ b/packages/neuron-wallet/src/controllers/netowrks.ts @@ -1,9 +1,18 @@ -import { Channel } from '../utils/const' import { ResponseCode, Response } from '.' -import NetowrkService, { Network } from '../services/networks' -import WalletChannel from '../channel/wallet' import windowManage from '../main' +import WalletChannel from '../channel/wallet' +import NetowrkService, { Network } from '../services/networks' +import { Channel } from '../utils/const' +export enum NetworksMethod { + Index = 'index', + Show = 'show', + Create = 'create', + Update = 'update', + Delete = 'delete', + ActiveNetwork = 'activeNetwork', + SetActive = 'setActive', +} class NetworksController { public channel: WalletChannel @@ -48,7 +57,7 @@ class NetworksController { if (network.name && network.remote) { const newNetwork = NetworksController.service.create(network.name, network.remote) // TODO: sync - windowManage.broad(Channel.Networks, 'index', NetworksController.index()) + windowManage.broad(Channel.Networks, NetworksMethod.Index, NetworksController.index()) return { status: ResponseCode.Success, result: newNetwork, @@ -65,7 +74,7 @@ class NetworksController { const success = NetworksController.service.update(network) if (success) { // TODO: sync - windowManage.broad(Channel.Networks, 'index', NetworksController.index()) + windowManage.broad(Channel.Networks, NetworksMethod.Index, NetworksController.index()) return { status: ResponseCode.Success, result: true, @@ -92,10 +101,10 @@ class NetworksController { // check if deleted network is current network, switch to default network if true if (activeNetwork && activeNetwork.id === id) { NetworksController.service.setActive(defaultNetwork.id) - windowManage.broad(Channel.Networks, 'activeNetwork', NetworksController.activeNetwork()) + windowManage.broad(Channel.Networks, NetworksMethod.ActiveNetwork, NetworksController.activeNetwork()) } // TODO: sync - windowManage.broad(Channel.Networks, 'index', NetworksController.index()) + windowManage.broad(Channel.Networks, NetworksMethod.Index, NetworksController.index()) return { status: ResponseCode.Success, result: true, diff --git a/packages/neuron-wallet/src/controllers/transactions.ts b/packages/neuron-wallet/src/controllers/transactions.ts index 078e38c466..df12deef67 100644 --- a/packages/neuron-wallet/src/controllers/transactions.ts +++ b/packages/neuron-wallet/src/controllers/transactions.ts @@ -1,6 +1,6 @@ -import TransactionsService, { Transaction, TransactionsParams } from '../services/transactions' import { ResponseCode, Response } from '.' import WalletChannel from '../channel/wallet' +import TransactionsService, { Transaction, TransactionsParams } from '../services/transactions' export default class TransactionsController { public channel: WalletChannel diff --git a/packages/neuron-wallet/src/main.ts b/packages/neuron-wallet/src/main.ts index ca4c37fbf3..4d24920f24 100644 --- a/packages/neuron-wallet/src/main.ts +++ b/packages/neuron-wallet/src/main.ts @@ -20,7 +20,6 @@ WalletChannel.start() const initUILayer = (win: BrowserWindow) => { const channel = new WalletChannel(win) - // const netowrksController = new NetowrksController(channel) dispatch(Command.SendWallet, { channel, diff --git a/packages/neuron-wallet/src/services/networks.ts b/packages/neuron-wallet/src/services/networks.ts index b0e8474593..02735e910a 100644 --- a/packages/neuron-wallet/src/services/networks.ts +++ b/packages/neuron-wallet/src/services/networks.ts @@ -6,6 +6,7 @@ export interface Network { remote: string } +// this should come from config or db export const defaultNetowrks: Network[] = [ { id: '0', diff --git a/packages/neuron-wallet/src/utils/const.ts b/packages/neuron-wallet/src/utils/const.ts index 06cf1a092a..2fa164bcf5 100644 --- a/packages/neuron-wallet/src/utils/const.ts +++ b/packages/neuron-wallet/src/utils/const.ts @@ -1,37 +1,17 @@ export enum Channel { - // App SetLanguage = 'setLanguage', - // Chain - GetBlock = 'getBlock', - GetTransaction = 'getTransaction', - GetLiveCell = 'getLiveCell', - GetTipHeader = 'getTipHeader', GetTipBlockNumber = 'getTipBlockNumber', - GetLocalNodeId = 'getLocalNodeId', - // Wallet - CreateWallet = 'createWallet', DeleteWallet = 'deleteWallet', EditWallet = 'editWallet', - ImportWallet = 'importWallet', - ExportWallet = 'exportWallet', SwitchWallet = 'switchWallet', GetBalance = 'getBalance', - GetCellsByTypeHash = 'getCellsByTypeHash', - GetUnspentCells = 'getUnspentCells', GetTransactions = 'getTransactions', - GetWallet = 'getWallet', CheckWalletPassword = 'checkWalletPassword', GetWallets = 'getWallets', SendCapacity = 'sendCapacity', - SendTransaction = 'sendTransaction', - Sign = 'sign', - - // Page NavTo = 'navTo', - // Terminal Terminal = 'terminal', - // controller style code Networks = 'networks', Wallet = 'wallet', Transactions = 'transactions', From 8b523cdbd033536de57c89ca1e993aec92910abb Mon Sep 17 00:00:00 2001 From: Keith Date: Wed, 27 Mar 2019 03:21:18 +0800 Subject: [PATCH 005/119] refactor: refactor wallet channel into channel, controller, service --- .../neuron-ui/src/components/Router/index.tsx | 2 +- .../Settings/InputWalletPasswordDialog.tsx | 16 +- .../src/components/Settings/Wallets.tsx | 44 ++-- .../components/WalletWizard/createWallet.tsx | 12 +- .../src/containers/Header/reducer.ts | 2 +- .../MainContent/actionCreators/index.ts | 4 +- .../MainContent/actionCreators/networks.ts | 3 +- .../MainContent/actionCreators/wallet.ts | 23 -- .../MainContent/actionCreators/wallets.ts | 66 ++++++ .../src/containers/MainContent/actions.ts | 1 + .../src/containers/MainContent/reducer.ts | 19 +- .../src/containers/Providers/index.tsx | 39 ++-- packages/neuron-ui/src/services/UILayer.ts | 75 +++--- packages/neuron-ui/src/utils/const.ts | 2 +- .../neuron-wallet/src/channel/listeners.ts | 24 +- packages/neuron-wallet/src/channel/wallet.ts | 15 +- .../neuron-wallet/src/commands/commands.ts | 1 + .../neuron-wallet/src/commands/dispatcher.ts | 1 + .../neuron-wallet/src/commands/handlers.ts | 4 +- .../neuron-wallet/src/controllers/netowrks.ts | 19 +- .../neuron-wallet/src/controllers/wallets.ts | 220 ++++++++++++++++++ packages/neuron-wallet/src/main.ts | 10 +- packages/neuron-wallet/src/mock.ts | 2 +- .../neuron-wallet/src/services/networks.ts | 2 +- .../neuron-wallet/src/services/wallets.ts | 116 +++++++++ packages/neuron-wallet/src/utils/const.ts | 2 +- .../neuron-wallet/src/utils/validators.ts | 2 +- .../neuron-wallet/src/utils/windowManage.ts | 3 +- 28 files changed, 562 insertions(+), 167 deletions(-) delete mode 100644 packages/neuron-ui/src/containers/MainContent/actionCreators/wallet.ts create mode 100644 packages/neuron-ui/src/containers/MainContent/actionCreators/wallets.ts create mode 100644 packages/neuron-wallet/src/controllers/wallets.ts create mode 100644 packages/neuron-wallet/src/services/wallets.ts diff --git a/packages/neuron-ui/src/components/Router/index.tsx b/packages/neuron-ui/src/components/Router/index.tsx index dfcb3ec04a..3c1e424ee7 100644 --- a/packages/neuron-ui/src/components/Router/index.tsx +++ b/packages/neuron-ui/src/components/Router/index.tsx @@ -134,7 +134,7 @@ export const mainContents: CustomRoute[] = [ { name: `WalletEditor`, path: Routes.WalletEditor, - params: '/:wallet', + params: '/:id', exact: true, component: WalletEditor, }, diff --git a/packages/neuron-ui/src/components/Settings/InputWalletPasswordDialog.tsx b/packages/neuron-ui/src/components/Settings/InputWalletPasswordDialog.tsx index 169977344a..5ac2834fd0 100644 --- a/packages/neuron-ui/src/components/Settings/InputWalletPasswordDialog.tsx +++ b/packages/neuron-ui/src/components/Settings/InputWalletPasswordDialog.tsx @@ -3,8 +3,8 @@ import styled from 'styled-components' import { useTranslation } from 'react-i18next' import { Card, Button, Form, Row, Col } from 'react-bootstrap' -import { MainActions } from '../../containers/MainContent/reducer' -import { checkPassword, deleteWallet, editWallet } from '../../services/UILayer' +import { MainActions, actionCreators } from '../../containers/MainContent/reducer' +import { checkPassword } from '../../services/UILayer' import { Wallet } from '../../contexts/Wallet' export enum CheckType { @@ -64,11 +64,19 @@ const InputWalletPasswordDialog = ({ switch (checkType) { case CheckType.EditWallet: if (newWalletName && newPassword) { - editWallet(wallet.id, newWalletName, password, newPassword, handleResult) + dispatch( + actionCreators.createOrUpdateWallet( + { + id: wallet.id, + name: newWalletName, + }, + password, + ), + ) } break case CheckType.DeleteWallet: - deleteWallet(wallet.id, password, handleResult) + dispatch(actionCreators.deleteWallet(wallet.id, password)) break case CheckType.CheckPassword: default: diff --git a/packages/neuron-ui/src/components/Settings/Wallets.tsx b/packages/neuron-ui/src/components/Settings/Wallets.tsx index d5eda40408..6f2ba3935e 100644 --- a/packages/neuron-ui/src/components/Settings/Wallets.tsx +++ b/packages/neuron-ui/src/components/Settings/Wallets.tsx @@ -1,14 +1,15 @@ -import React, { useState, useContext } from 'react' -import styled from 'styled-components' +import React, { useContext } from 'react' import { Link, RouteComponentProps } from 'react-router-dom' import { ListGroup, Form, Container } from 'react-bootstrap' -import { Configure } from 'grommet-icons' import { useTranslation } from 'react-i18next' +import styled from 'styled-components' +import { Configure } from 'grommet-icons' + import { Routes } from '../../utils/const' -import WalletContext from '../../contexts/Settings' +import WalletContext from '../../contexts/Wallet' +import SettingsContext from '../../contexts/Settings' import { ContentProps } from '../../containers/MainContent' -import { MainActions } from '../../containers/MainContent/reducer' -import { getWallets } from '../../services/UILayer' +import { actionCreators, MainActions } from '../../containers/MainContent/reducer' import InputWalletPasswordDialog, { CheckType } from './InputWalletPasswordDialog' import Dialog from '../../widgets/Dialog' import Dropdown, { DropDownItem } from '../../widgets/Dropdown' @@ -44,32 +45,28 @@ const WalletActions = ({ isDefault, actionItems }: { isDefault: boolean; actionI } const Wallets = (props: React.PropsWithoutRef) => { - const { wallets } = useContext(WalletContext) - const { dispatch, dialog } = props + const activeWallet = useContext(WalletContext) + const { wallets } = useContext(SettingsContext) + const { dispatch, dialog, history } = props const [t] = useTranslation() - const [walletSelected, setWalletSelected] = useState(() => { - getWallets() - return 0 - }) - - const actionItems = (index: number) => [ + const actionItems = (id: string) => [ { label: t('menuitem.select'), onClick: () => { - setWalletSelected(index) + dispatch(actionCreators.setActiveWallet(id)) }, }, { label: t('menuitem.backup'), onClick: () => { - // props.history.push(`${Routes.NetworkEditor}/${network.name}`) + dispatch(actionCreators.backupWallet(id)) }, }, { label: t('menuitem.edit'), onClick: () => { - props.history.push(`${Routes.WalletEditor}/${JSON.stringify(wallets[index])}`) + history.push(`${Routes.WalletEditor}/${id}}`) }, }, { @@ -79,7 +76,7 @@ const Wallets = (props: React.PropsWithoutRef wallet.id === id), }, }) }, @@ -89,11 +86,11 @@ const Wallets = (props: React.PropsWithoutRef - {wallets.map((wallet, index) => { - const isChecked = index === walletSelected + {wallets.map(wallet => { + const isChecked = wallet.id === activeWallet.id return ( { - setWalletSelected(index) + // setWalletSelected(index) + dispatch(actionCreators.setActiveWallet(wallet.id)) }} /> - + ) })} diff --git a/packages/neuron-ui/src/components/WalletWizard/createWallet.tsx b/packages/neuron-ui/src/components/WalletWizard/createWallet.tsx index 5f8cd4b720..e9be523a79 100644 --- a/packages/neuron-ui/src/components/WalletWizard/createWallet.tsx +++ b/packages/neuron-ui/src/components/WalletWizard/createWallet.tsx @@ -55,10 +55,14 @@ export default (props: React.PropsWithoutRef onAfterNext={() => { // temp logic for simulate creation props.dispatch( - actionCreators.createWallet({ - walletName: settings.name, - password: '', - }), + actionCreators.createOrUpdateWallet( + { + name: settings.name, + address: 'temp address', + publicKey: new Uint8Array([1]), + }, + '', + ), ) }} > diff --git a/packages/neuron-ui/src/containers/Header/reducer.ts b/packages/neuron-ui/src/containers/Header/reducer.ts index 872773208d..d3dc7d33c7 100644 --- a/packages/neuron-ui/src/containers/Header/reducer.ts +++ b/packages/neuron-ui/src/containers/Header/reducer.ts @@ -28,7 +28,7 @@ export const reducer = (state: any, action: { type: HeaderActions; payload?: any export const actionCreators = { setNetwork: (network: Network) => { - networks(NetworksMethod.SetActive, network.id) + networks(NetworksMethod.SetActive, network.id!) return { type: HeaderActions.SetNetwork, payload: network, diff --git a/packages/neuron-ui/src/containers/MainContent/actionCreators/index.ts b/packages/neuron-ui/src/containers/MainContent/actionCreators/index.ts index 6a15df5437..43b7b02522 100644 --- a/packages/neuron-ui/src/containers/MainContent/actionCreators/index.ts +++ b/packages/neuron-ui/src/containers/MainContent/actionCreators/index.ts @@ -1,10 +1,10 @@ -import wallet from './wallet' +import wallets from './wallets' import networks from './networks' import transfer from './transfer' import transactions from './transactions' export const actionCreators = { - ...wallet, + ...wallets, ...networks, ...transfer, ...transactions, diff --git a/packages/neuron-ui/src/containers/MainContent/actionCreators/networks.ts b/packages/neuron-ui/src/containers/MainContent/actionCreators/networks.ts index efe70d54e3..c2fde3a6b6 100644 --- a/packages/neuron-ui/src/containers/MainContent/actionCreators/networks.ts +++ b/packages/neuron-ui/src/containers/MainContent/actionCreators/networks.ts @@ -82,7 +82,8 @@ export default { } }, setNetwork: (network: Network) => { - networks(NetworksMethod.SetActive, network.id) + // TODO: verification + networks(NetworksMethod.SetActive, network.id!) return { type: MainActions.Netowrks, payload: network, diff --git a/packages/neuron-ui/src/containers/MainContent/actionCreators/wallet.ts b/packages/neuron-ui/src/containers/MainContent/actionCreators/wallet.ts deleted file mode 100644 index 99ba9c049b..0000000000 --- a/packages/neuron-ui/src/containers/MainContent/actionCreators/wallet.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { initState, MainActions } from '../reducer' -import { createWallet, importWallet, exportWallet } from '../../../services/UILayer' - -export default { - createWallet: (wallet: typeof initState.createWallet) => { - createWallet(wallet) - return { - type: MainActions.CreateWallet, - } - }, - importWallet: (wallet: typeof initState.tempWallet) => { - importWallet(wallet) - return { - type: MainActions.ImportWallet, - } - }, - exportWallet: () => { - exportWallet() - return { - type: MainActions.ExportWallet, - } - }, -} diff --git a/packages/neuron-ui/src/containers/MainContent/actionCreators/wallets.ts b/packages/neuron-ui/src/containers/MainContent/actionCreators/wallets.ts new file mode 100644 index 0000000000..f54ff839e7 --- /dev/null +++ b/packages/neuron-ui/src/containers/MainContent/actionCreators/wallets.ts @@ -0,0 +1,66 @@ +import { initState, MainActions } from '../reducer' +import { wallets, WalletsMethod } from '../../../services/UILayer' + +export default { + setActiveWallet: (id: string) => { + wallets(WalletsMethod.SetActive, id) + return { + type: MainActions.Wallet, + payload: id, + } + }, + backupWallet: (id: string) => { + wallets(WalletsMethod.Backup, id) + return { + type: MainActions.Wallet, + payload: id, + } + }, + createOrUpdateWallet: ( + params: + | { name: string; address: string; publicKey: Uint8Array } + | { id: string; name?: string; address?: string; publicKey?: Uint8Array }, + password: string, + ) => { + // verification + if ('id' in params) { + // update + wallets(WalletsMethod.Update, { + ...params, + password, + }) + } else { + // create + wallets(WalletsMethod.Create, { + ...params, + password, + }) + } + return { + type: MainActions.Wallet, + payload: params, + } + }, + deleteWallet: (id: string, password: string) => { + wallets(WalletsMethod.Delete, { + id, + password, + }) + return { + type: MainActions.Wallet, + payload: id, + } + }, + importWallet: (params: typeof initState.tempWallet) => { + wallets(WalletsMethod.Import, params) + return { + type: MainActions.Wallet, + } + }, + exportWallet: (id: string) => { + wallets(WalletsMethod.Export, id) + return { + type: MainActions.Wallet, + } + }, +} diff --git a/packages/neuron-ui/src/containers/MainContent/actions.ts b/packages/neuron-ui/src/containers/MainContent/actions.ts index db69510d31..a86115d080 100644 --- a/packages/neuron-ui/src/containers/MainContent/actions.ts +++ b/packages/neuron-ui/src/containers/MainContent/actions.ts @@ -19,6 +19,7 @@ export enum MainActions { UpdatePassword, UpdateLoading, Netowrks, + Wallet, } export default MainActions diff --git a/packages/neuron-ui/src/containers/MainContent/reducer.ts b/packages/neuron-ui/src/containers/MainContent/reducer.ts index 4819a1b491..0c66837e88 100644 --- a/packages/neuron-ui/src/containers/MainContent/reducer.ts +++ b/packages/neuron-ui/src/containers/MainContent/reducer.ts @@ -1,4 +1,3 @@ - import { CapacityUnit } from '../../utils/const' import actionCreators from './actionCreators' import MainActions from './actions' @@ -14,15 +13,15 @@ export type InitState = typeof initState export const reducer = (state: typeof initState, action: { type: MainActions; payload: any }) => { switch (action.type) { // wallet - case MainActions.UpdateTempWallet: { - return { - ...state, - tempWallet: { - ...state.tempWallet, - ...action.payload, - }, - } - } + // case MainActions.UpdateTempWallet: { + // return { + // ...state, + // tempWallet: { + // ...state.tempWallet, + // ...action.payload, + // }, + // } + // } // network case MainActions.UpdateNetworkEditor: { return { diff --git a/packages/neuron-ui/src/containers/Providers/index.tsx b/packages/neuron-ui/src/containers/Providers/index.tsx index 928ad68df2..004cba3189 100644 --- a/packages/neuron-ui/src/containers/Providers/index.tsx +++ b/packages/neuron-ui/src/containers/Providers/index.tsx @@ -5,7 +5,7 @@ import WalletContext from '../../contexts/Wallet' import SettingsContext from '../../contexts/Settings' import { reducer, initProviders, ProviderActions, ProviderDispatch } from './reducer' -import UILayer, { NetworksMethod, TransactionsMethod } from '../../services/UILayer' +import UILayer, { NetworksMethod, TransactionsMethod, WalletsMethod } from '../../services/UILayer' import { Channel } from '../../utils/const' const withProviders = (Comp: React.ComponentType<{ providerDispatch: ProviderDispatch }>) => ( @@ -66,14 +66,23 @@ const withProviders = (Comp: React.ComponentType<{ providerDispatch: ProviderDis } }) - UILayer.on(Channel.Wallet, (_e: Event, method: 'activeWallet', args: Response) => { + UILayer.on(Channel.Transactions, (_e: Event, method: TransactionsMethod, args: Response) => { if (args.status) { switch (method) { - case 'activeWallet': { + case TransactionsMethod.Index: { dispatch({ - type: ProviderActions.Wallet, + type: ProviderActions.Chain, + payload: { + transactions: args.result, + }, + }) + break + } + case TransactionsMethod.Show: { + dispatch({ + type: ProviderActions.Chain, payload: { - ...args.result, + transaction: args.result, }, }) break @@ -87,24 +96,22 @@ const withProviders = (Comp: React.ComponentType<{ providerDispatch: ProviderDis } }) - UILayer.on(Channel.Transactions, (_e: Event, method: TransactionsMethod, args: Response) => { + UILayer.on(Channel.Wallets, (_e: Event, method: WalletsMethod, args: Response) => { if (args.status) { switch (method) { - case TransactionsMethod.Index: { + case WalletsMethod.Index: { dispatch({ - type: ProviderActions.Chain, + type: ProviderActions.Settings, payload: { - transactions: args.result, + wallets: args.result, }, }) break } - case TransactionsMethod.Show: { + case WalletsMethod.Active: { dispatch({ - type: ProviderActions.Chain, - payload: { - transaction: args.result, - }, + type: ProviderActions.Wallet, + payload: args.result, }) break } @@ -112,8 +119,6 @@ const withProviders = (Comp: React.ComponentType<{ providerDispatch: ProviderDis break } } - } else { - // TODO: handle error } }) @@ -129,7 +134,7 @@ const withProviders = (Comp: React.ComponentType<{ providerDispatch: ProviderDis }) break } - case NetworksMethod.ActiveNetwork: { + case NetworksMethod.Active: { dispatch({ type: ProviderActions.Chain, payload: { diff --git a/packages/neuron-ui/src/services/UILayer.ts b/packages/neuron-ui/src/services/UILayer.ts index 0079fc3825..a9b961ddf2 100644 --- a/packages/neuron-ui/src/services/UILayer.ts +++ b/packages/neuron-ui/src/services/UILayer.ts @@ -1,3 +1,4 @@ +import { Network } from '../contexts/Chain' import { Channel, CapacityUnit } from '../utils/const' import SyntheticEventEmitter from '../utils/SyntheticEventEmitter' @@ -14,7 +15,7 @@ export enum NetworksMethod { Create = 'create', Update = 'update', Delete = 'delete', - ActiveNetwork = 'activeNetwork', + Active = 'active', SetActive = 'setActive', } @@ -22,6 +23,17 @@ export enum TransactionsMethod { Index = 'index', Show = 'show', } +export enum WalletsMethod { + Index = 'index', + Create = 'create', + Import = 'import', + Update = 'update', + Delete = 'delete', + Export = 'export', + Active = 'active', + SetActive = 'setActive', + Backup = 'backup', +} export interface TransferItem { address: string @@ -64,49 +76,6 @@ export const getWallets = () => { UILayer.send(Channel.GetWallets) } -export const createWallet = (wallet: { walletName: string; password: string }) => - UILayer.send(Channel.CreateWallet, wallet) - -export const deleteWallet = (walletID: string, password: string, handleResult: any) => { - UILayer.on(Channel.DeleteWallet, (_e: any, args: Response) => { - if (args.result) { - getWallets() - } - handleResult(args) - }) - UILayer.send(Channel.DeleteWallet, { - walletID, - password, - }) -} - -export const editWallet = ( - walletID: string, - walletName: string, - password: string, - newPassword: string, - handleResult: any, -) => { - UILayer.on(Channel.EditWallet, (_e: any, args: Response) => { - if (args.result) { - getWallets() - } - handleResult(args) - }) - - UILayer.send(Channel.EditWallet, { - walletID, - walletName, - password, - newPassword, - }) -} - -export const importWallet = (wallet: { walletName: string; password: string; mnemonic: string; keystore: string }) => - UILayer.send(Channel.ImportWallet, wallet) - -export const exportWallet = () => UILayer.send(Channel.ExportWallet) - export const sendCapacity = (items: TransferItem[], password: string) => { return UILayer.sendSync(Channel.SendCapacity, { items, @@ -124,11 +93,25 @@ export const checkPassword = (walletID: string, password: string, handleResult: }) } -export const networks = (method: string, params: any) => { +export const networks = (method: NetworksMethod, params: string | Network) => { UILayer.send(Channel.Networks, method, params) } -export const transactions = (method: string, params: GetTransactionsParams | string) => { +export const transactions = (method: TransactionsMethod, params: GetTransactionsParams | string) => { UILayer.send(Channel.Transactions, method, params) } +export const wallets = ( + method: WalletsMethod, + params: + | undefined + | string + | { name: string; password: string } + | { keystore: string; password: string } + | { mnemonic: string; password: string } + | { id: string; password: string } + | { id: string; name?: string; password: string; newPassword?: string }, +) => { + UILayer.send(Channel.Wallets, method, params) +} + export default UILayer diff --git a/packages/neuron-ui/src/utils/const.ts b/packages/neuron-ui/src/utils/const.ts index 7460eb7518..528a6e5ffb 100644 --- a/packages/neuron-ui/src/utils/const.ts +++ b/packages/neuron-ui/src/utils/const.ts @@ -36,7 +36,7 @@ export enum Channel { Terminal = 'terminal', Networks = 'networks', Transactions = 'transactions', - Wallet = 'wallet', + Wallets = 'wallets', } export enum Routes { diff --git a/packages/neuron-wallet/src/channel/listeners.ts b/packages/neuron-wallet/src/channel/listeners.ts index 089d2366c7..f8d6c9b23b 100644 --- a/packages/neuron-wallet/src/channel/listeners.ts +++ b/packages/neuron-wallet/src/channel/listeners.ts @@ -1,11 +1,12 @@ import { ipcMain, Notification } from 'electron' import { Channel } from '../utils/const' -import { wallets, validatePassword, updateWallets, Wallet } from '../mock' +import { wallets, verifyPassword, updateWallets, Wallet } from '../mock' import asw from '../wallets/asw' import { ResponseCode } from './wallet' import NetworksController from '../controllers/netowrks' import TransactionsController from '../controllers/transactions' +import WalletsController from '../controllers/wallets' const checkPassword = (walletID: string, password: string) => { const myWallet = wallets().find(wallet => wallet.id === walletID) @@ -16,7 +17,7 @@ const checkPassword = (walletID: string, password: string) => { msg: 'Wallet not found', } } - if (validatePassword(myWallet, password)) { + if (verifyPassword(myWallet, password)) { return { status: ResponseCode.Success, result: true, @@ -243,13 +244,14 @@ export default class Listeners { ) } - // TODO: add wallet controller and service - // public wallet = () => { - // return ipcMain.on( - // Channel.Wallet, - // (e: Electron.Event, { method, params }: { method: keyof typeof NetworksController; params: any }) => { - // e.sender.send(Channel.Networks, (NetworksController[method] as Function)(params)) - // }, - // ) - // } + /** + * @method wallet + * @memberof ChannelListeners + * @description listen to Channel.Wallet and invoke corresponding method of WalletsController + */ + public static wallet = () => { + return ipcMain.on(Channel.Wallets, (e: Electron.Event, method: keyof typeof WalletsController, params: any) => { + e.sender.send(Channel.Wallets, method, (WalletsController[method] as Function)(params)) + }) + } } diff --git a/packages/neuron-wallet/src/channel/wallet.ts b/packages/neuron-wallet/src/channel/wallet.ts index 52ca01c40d..20f077c33d 100644 --- a/packages/neuron-wallet/src/channel/wallet.ts +++ b/packages/neuron-wallet/src/channel/wallet.ts @@ -4,6 +4,7 @@ import Listeners from './listeners' import { Response } from '../controllers' import NetworksController, { NetworksMethod } from '../controllers/netowrks' import { Network } from '../services/networks' +import { WalletsMethod } from '../controllers/wallets' import asw from '../wallets/asw' @@ -33,7 +34,7 @@ export default class WalletChannel extends Listeners { publicKey: asw.publicKey, }, ) => { - this.win.webContents.send(Channel.Wallet, 'activeWallet', { + this.win.webContents.send(Channel.Wallets, 'activeWallet', { status: ResponseCode.Success, result: wallet, }) @@ -79,6 +80,16 @@ export default class WalletChannel extends Listeners { }) } + public syncWallets = (params: { active?: Response; wallets?: Response }) => { + if (!this.win) return + if (params.active) { + this.win.webContents.send(Channel.Wallets, WalletsMethod.Active, params.active) + } + if (params.wallets) { + this.win.webContents.send(Channel.Wallets, WalletsMethod.Index, params.wallets) + } + } + public syncNetworks = (params: { active?: Response networks?: Response @@ -89,7 +100,7 @@ export default class WalletChannel extends Listeners { this.win.webContents.send(Channel.Networks, NetworksMethod.Index, params.networks) } if (params.active) { - this.win.webContents.send(Channel.Networks, NetworksMethod.ActiveNetwork, params.active) + this.win.webContents.send(Channel.Networks, NetworksMethod.Active, params.active) } // TODO: status handler // if (params.status) { diff --git a/packages/neuron-wallet/src/commands/commands.ts b/packages/neuron-wallet/src/commands/commands.ts index 7069d127f9..248c546f91 100644 --- a/packages/neuron-wallet/src/commands/commands.ts +++ b/packages/neuron-wallet/src/commands/commands.ts @@ -2,6 +2,7 @@ // In many cases a command comes from the app shell and is sent to renderer process with channel. enum Command { SendWallet = 'send-wallet', // default to asw for now + SyncWallets = 'sync-wallets', SyncNetworks = 'sync-network', SendTransactionHistory = 'send-transaction-history', ShowAbout = 'show-about', diff --git a/packages/neuron-wallet/src/commands/dispatcher.ts b/packages/neuron-wallet/src/commands/dispatcher.ts index 005b5a8069..7ec353aef2 100644 --- a/packages/neuron-wallet/src/commands/dispatcher.ts +++ b/packages/neuron-wallet/src/commands/dispatcher.ts @@ -5,6 +5,7 @@ const maps = { [Command.ShowAbout as string]: handlers.aboutHandler, [Command.ShowPreferences as string]: handlers.rendererMessageHandler, [Command.SendWallet as string]: handlers.rendererMessageHandler, + [Command.SyncWallets as string]: handlers.rendererMessageHandler, [Command.SyncNetworks as string]: handlers.rendererMessageHandler, [Command.ShowTerminal as string]: handlers.rendererMessageHandler, [Command.SendTransactionHistory as string]: handlers.rendererMessageHandler, diff --git a/packages/neuron-wallet/src/commands/handlers.ts b/packages/neuron-wallet/src/commands/handlers.ts index 5f33f79e2a..fd6dce4237 100644 --- a/packages/neuron-wallet/src/commands/handlers.ts +++ b/packages/neuron-wallet/src/commands/handlers.ts @@ -57,9 +57,9 @@ const rendererMessageHandler: Handler = (command, info) => { } break } - case Command.SendWallet: { + case Command.SyncWallets: { if (info) { - info.channel.sendWallet() + info.channel.syncWallets(info.extra! as any) } break } diff --git a/packages/neuron-wallet/src/controllers/netowrks.ts b/packages/neuron-wallet/src/controllers/netowrks.ts index 45559b7997..19b0bab364 100644 --- a/packages/neuron-wallet/src/controllers/netowrks.ts +++ b/packages/neuron-wallet/src/controllers/netowrks.ts @@ -1,7 +1,7 @@ import { ResponseCode, Response } from '.' import windowManage from '../main' import WalletChannel from '../channel/wallet' -import NetowrkService, { Network } from '../services/networks' +import NetowrksService, { Network } from '../services/networks' import { Channel } from '../utils/const' export enum NetworksMethod { @@ -10,17 +10,16 @@ export enum NetworksMethod { Create = 'create', Update = 'update', Delete = 'delete', - ActiveNetwork = 'activeNetwork', + Active = 'active', SetActive = 'setActive', } class NetworksController { public channel: WalletChannel - static service = new NetowrkService() + static service = new NetowrksService() constructor(channel: WalletChannel) { this.channel = channel - NetworksController.service = new NetowrkService() } public static index = (): Response => { @@ -34,7 +33,7 @@ class NetworksController { return { status: ResponseCode.Fail, - msg: 'networks not found', + msg: 'Networks not found', } } @@ -57,7 +56,7 @@ class NetworksController { if (network.name && network.remote) { const newNetwork = NetworksController.service.create(network.name, network.remote) // TODO: sync - windowManage.broad(Channel.Networks, NetworksMethod.Index, NetworksController.index()) + windowManage.broadcast(Channel.Networks, NetworksMethod.Index, NetworksController.index()) return { status: ResponseCode.Success, result: newNetwork, @@ -74,7 +73,7 @@ class NetworksController { const success = NetworksController.service.update(network) if (success) { // TODO: sync - windowManage.broad(Channel.Networks, NetworksMethod.Index, NetworksController.index()) + windowManage.broadcast(Channel.Networks, NetworksMethod.Index, NetworksController.index()) return { status: ResponseCode.Success, result: true, @@ -101,10 +100,10 @@ class NetworksController { // check if deleted network is current network, switch to default network if true if (activeNetwork && activeNetwork.id === id) { NetworksController.service.setActive(defaultNetwork.id) - windowManage.broad(Channel.Networks, NetworksMethod.ActiveNetwork, NetworksController.activeNetwork()) + windowManage.broadcast(Channel.Networks, NetworksMethod.Active, NetworksController.active()) } // TODO: sync - windowManage.broad(Channel.Networks, NetworksMethod.Index, NetworksController.index()) + windowManage.broadcast(Channel.Networks, NetworksMethod.Index, NetworksController.index()) return { status: ResponseCode.Success, result: true, @@ -116,7 +115,7 @@ class NetworksController { } } - public static activeNetwork = () => ({ + public static active = () => ({ status: ResponseCode.Success, result: NetworksController.service.active, }) diff --git a/packages/neuron-wallet/src/controllers/wallets.ts b/packages/neuron-wallet/src/controllers/wallets.ts new file mode 100644 index 0000000000..d76ff39392 --- /dev/null +++ b/packages/neuron-wallet/src/controllers/wallets.ts @@ -0,0 +1,220 @@ +import WalletChannel from '../channel/wallet' +import WalletsService, { Wallet } from '../services/wallets' +import { verifyPassword } from '../utils/validators' +import { Response, ResponseCode } from '.' +import asw from '../wallets/asw' +import windowManage from '../main' +import { Channel } from '../utils/const' + +const activeWallet = asw + +export enum WalletsMethod { + Index = 'index', + Create = 'create', + Import = 'import', + Update = 'update', + Delete = 'delete', + Active = 'active', + SetActive = 'setActive', +} + +class WalletsController { + public channel: WalletChannel + + static service = new WalletsService() + + constructor(channel: WalletChannel) { + this.channel = channel + } + + public static index = (): Response => { + const wallets = WalletsController.service.index() + if (wallets) { + return { + status: ResponseCode.Success, + result: wallets, + } + } + return { + status: ResponseCode.Fail, + msg: 'Wallets not found', + } + } + + public static show = (id: string): Response => { + const wallet = WalletsController.service.show(id) + if (wallet) { + return { + status: ResponseCode.Success, + result: wallet, + } + } + return { + status: ResponseCode.Fail, + msg: 'Wallet not found', + } + } + + public static create = ({ + name, + address, + publicKey, + password, + }: { + name: string + address: string + publicKey: Uint8Array + password: string + }): Response => { + const wallet = WalletsController.service.create( + { + name, + address, + publicKey, + }, + password, + ) + if (wallet) { + return { + status: ResponseCode.Success, + result: wallet, + } + } + return { + status: ResponseCode.Fail, + msg: 'Failed to create wallet', + } + } + + // TODO: import wallet + // public static import = ({ + // name, + // password, + // mnemonic, + // keystore, + // }: { + // name: string + // password: string + // mnemonic: string + // keystore: string + // }): Response => { + // // generate wallet for service + // const walletStore = new WalletStore() + // let storedKeystore + // if (mnemonic) { + // storedKeystore = Key.fromMnemonic(mnemonic, true, password).getKeystore() + // } else if (keystore) { + // storedKeystore = Key.fromKeystoreString(keystore, password).getKeystore() + // } + // if (storedKeystore) { + // walletStore.saveWallet(name, storedKeystore) + // return { + // status: ResponseCode.Success, + // result: true, + // } + // } + // return { + // status: ResponseCode.Fail, + // msg: 'Failed to import wallet', + // } + // } + + // TODO: implement service.update + // public static update = ({ + // id, + // name, + // address, + // publicKey, + // password, + // }: { + // id: string + // name?: string + // address?: string + // publicKey?: Uint8Array + // password: string + // }): Response => { + // const wallet = WalletsController.service.show(id) + // const isPermitted = verifyPassword(wallet, password) + // if (!isPermitted) { + // return { + // status: ResponseCode.Fail, + // msg: 'Incorrect password', + // } + // } + // const success = WalletsController.service.update({ + // id, + // name, + // address, + // publicKey, + // }) + // if (success) { + // windowManage.broadcast(Channel.Wallets, WalletsMethod.Index, WalletsController.index()) + // return { + // status: ResponseCode.Success, + // result: true, + // } + // } + // return { + // status: ResponseCode.Fail, + // msg: 'Failed to update wallet', + // } + // } + + public static delete = ({ id, password }: { id: string; password: string }): Response => { + const wallet = WalletsController.service.show(id) + const isPermitted = verifyPassword(wallet, password) + if (!isPermitted) { + return { + status: ResponseCode.Fail, + msg: 'Incorrect password', + } + } + const success = WalletsController.service.delete(id) + if (success) { + // TODO: details, what to do when active wallet deleted + windowManage.broadcast(Channel.Wallets, WalletsMethod.Index, WalletsController.index()) + return { + status: ResponseCode.Success, + result: true, + } + } + return { + status: ResponseCode.Fail, + msg: 'Failed to delete wallet', + } + } + + public static active = () => { + if (activeWallet) { + return { + status: ResponseCode.Success, + result: { + name: 'active wallet', + address: activeWallet.address, + publicKey: activeWallet.publicKey, + }, + } + } + return { + status: ResponseCode.Fail, + msg: 'No active wallet', + } + } + + public static setActive = (id: string) => { + const success = WalletsController.service.setActive(id) + if (success) { + return { + status: ResponseCode.Success, + result: WalletsController.service.active, + } + } + return { + status: ResponseCode.Fail, + msg: 'Failed to activate wallet', + } + // TODO: verification + } +} + +export default WalletsController diff --git a/packages/neuron-wallet/src/main.ts b/packages/neuron-wallet/src/main.ts index 4d24920f24..af954946ea 100644 --- a/packages/neuron-wallet/src/main.ts +++ b/packages/neuron-wallet/src/main.ts @@ -11,24 +11,28 @@ import mainmenu from './menu' import dispatch, { Command } from './commands/dispatcher' import NetowrksController from './controllers/netowrks' import WindowManage from './utils/windowManage' +import WalletsController from './controllers/wallets' const windowManage = new WindowManage() let mainWindow: Electron.BrowserWindow | null -// start listening WalletChannel.start() const initUILayer = (win: BrowserWindow) => { const channel = new WalletChannel(win) - dispatch(Command.SendWallet, { + dispatch(Command.SyncWallets, { channel, + extra: { + active: WalletsController.active(), + wallets: WalletsController.index(), + }, }) dispatch(Command.SyncNetworks, { channel, extra: { - active: NetowrksController.activeNetwork(), + active: NetowrksController.active(), networks: NetowrksController.index(), connected: { status: 1, diff --git a/packages/neuron-wallet/src/mock.ts b/packages/neuron-wallet/src/mock.ts index 717fe7df0a..fd49e5ddb3 100644 --- a/packages/neuron-wallet/src/mock.ts +++ b/packages/neuron-wallet/src/mock.ts @@ -52,7 +52,7 @@ export const updateWallets = (newWallets: Wallet[]) => { list = newWallets } -export const validatePassword = (wallet: Wallet, password: string) => { +export const verifyPassword = (wallet: Wallet, password: string) => { if (wallet.password === password) { return true } diff --git a/packages/neuron-wallet/src/services/networks.ts b/packages/neuron-wallet/src/services/networks.ts index 02735e910a..9b08618147 100644 --- a/packages/neuron-wallet/src/services/networks.ts +++ b/packages/neuron-wallet/src/services/networks.ts @@ -20,7 +20,7 @@ export const defaultNetowrks: Network[] = [ }, ] -export default class NetworkService { +export default class NetworksService { public networks: Network[] = [] public active: Network | undefined = undefined diff --git a/packages/neuron-wallet/src/services/wallets.ts b/packages/neuron-wallet/src/services/wallets.ts new file mode 100644 index 0000000000..f395f606df --- /dev/null +++ b/packages/neuron-wallet/src/services/wallets.ts @@ -0,0 +1,116 @@ +import asw from '../wallets/asw' +import WalletStore from '../store/WalletStore' +import Key from '../keys/key' + +const walletStore = new WalletStore() + +export interface Wallet { + id: string + name: string + address: string + publicKey: Uint8Array +} + +// this should come from config or db +export const defaultWallet: Wallet = { + id: '0', + name: 'asw', + address: asw.address, + publicKey: asw.publicKey, +} + +const defaultPassword = '0' + +export default class WalletService { + public wallets: Wallet[] = [] + + public active: Wallet | undefined = undefined + + constructor() { + this.create( + { + name: 'asw', + address: asw.address, + publicKey: asw.publicKey, + }, + defaultPassword, + ) + this.setActive(walletStore.getAllWallets()[0].id) + } + + public index = (): Wallet[] => { + return walletStore.getAllWallets().map(w => ({ + id: w.id, + name: w.name, + address: `address of ${w.id}`, + publicKey: Buffer.from(`address of ${w.id}`), + })) + } + + public show = (id: string): Wallet | undefined => { + return this.wallets.find(wallet => wallet.id === id) + } + + public create = ( + { name, address, publicKey }: { name: string; address: string; publicKey: Uint8Array }, + password: string, + ): Wallet => { + const id = walletStore.saveWallet(name, Key.generateKey(password).getKeystore()) + if (id) { + const storedWallet = walletStore.getWallet(id) + return { + id, + name: storedWallet.name, + address, + publicKey, + } + } + throw new Error('Failed to create wallet') + } + + // TODO: update wallet + // public update = ({ + // id, + // name, + // address, + // publicKey, + // }: { + // id: string + // name?: string + // address?: string + // publicKey?: Uint8Array + // }): boolean => { + // const wallet = this.show(id) + // if (wallet) { + // if (name) { + // wallet.name = name + // } + // if (address) { + // wallet.address = address + // } + // if (publicKey) { + // wallet.publicKey = publicKey + // } + // return true + // } + // return false + // } + + public delete = (id: string): boolean => { + const wallet = this.show(id) + if (wallet) { + this.wallets = this.wallets.filter(w => w.id !== id) + return true + } + return false + } + + public setActive = (id: string): boolean => { + const wallet = this.show(id) + if (wallet) { + this.active = wallet + return true + } + return false + } +} diff --git a/packages/neuron-wallet/src/utils/const.ts b/packages/neuron-wallet/src/utils/const.ts index 2fa164bcf5..ec712ab5d6 100644 --- a/packages/neuron-wallet/src/utils/const.ts +++ b/packages/neuron-wallet/src/utils/const.ts @@ -13,7 +13,7 @@ export enum Channel { NavTo = 'navTo', Terminal = 'terminal', Networks = 'networks', - Wallet = 'wallet', + Wallets = 'wallets', Transactions = 'transactions', } diff --git a/packages/neuron-wallet/src/utils/validators.ts b/packages/neuron-wallet/src/utils/validators.ts index 39b41bf3bb..aecfe42472 100644 --- a/packages/neuron-wallet/src/utils/validators.ts +++ b/packages/neuron-wallet/src/utils/validators.ts @@ -1,4 +1,4 @@ -export const verifyPassword = (_address: string, password: string): boolean => { +export const verifyPassword = (_wallet: any, password: string): boolean => { if (password === '123456') { return true } diff --git a/packages/neuron-wallet/src/utils/windowManage.ts b/packages/neuron-wallet/src/utils/windowManage.ts index 34a1defc7b..124b80e278 100644 --- a/packages/neuron-wallet/src/utils/windowManage.ts +++ b/packages/neuron-wallet/src/utils/windowManage.ts @@ -12,8 +12,7 @@ class WindowManage { this.windows.push(win) } - // public remove = () - public broad = (channel: Channel, method: string, params: any) => { + public broadcast = (channel: Channel, method: string, params: any) => { this.windows.forEach(window => { if (window) { window.webContents.send(channel, method, params) From 38a9b880b6c8b330f57e552f28bcda506222589e Mon Sep 17 00:00:00 2001 From: Keith Date: Thu, 28 Mar 2019 02:23:19 +0800 Subject: [PATCH 006/119] refactor(neuron-ui): refactor wallet wizard and set its state in MainContent --- .eslintrc.js | 6 +- .../src/components/ActionFlow/index.tsx | 118 ------------------ .../src/components/Mnemonic/index.tsx | 107 ++++++++++++++++ .../neuron-ui/src/components/Prompt/index.tsx | 29 +++++ .../neuron-ui/src/components/Router/index.tsx | 49 +++++--- .../src/components/WalletSubmission/index.tsx | 95 ++++++++++++++ .../src/components/WalletWizard/actionA.tsx | 17 --- .../src/components/WalletWizard/actionB.tsx | 26 ---- .../src/components/WalletWizard/actionC.tsx | 75 ----------- .../src/components/WalletWizard/actionD.tsx | 18 --- .../components/WalletWizard/createWallet.tsx | 83 ------------ .../components/WalletWizard/importWallet.tsx | 80 ------------ .../src/components/WalletWizard/index.tsx | 114 +++++------------ .../src/containers/MainContent/actions.ts | 1 + .../src/containers/MainContent/index.tsx | 10 +- .../src/containers/MainContent/reducer.ts | 14 ++- .../src/containers/MainContent/state.ts | 12 +- packages/neuron-ui/src/locales/en.ts | 18 ++- packages/neuron-ui/src/locales/zh.ts | 18 ++- packages/neuron-ui/src/utils/const.ts | 11 ++ packages/neuron-ui/src/utils/hooks.tsx | 15 +++ packages/neuron-ui/src/utils/mnemonic.ts | 12 ++ packages/neuron-ui/src/utils/parser.ts | 26 ++-- packages/neuron-ui/src/utils/validators.ts | 21 +++- .../neuron-ui/src/widgets/Screen/index.tsx | 17 +++ .../src/widgets/ScreenButtonRow/index.tsx | 7 ++ packages/neuron-wallet/src/main.ts | 6 +- 27 files changed, 449 insertions(+), 556 deletions(-) delete mode 100644 packages/neuron-ui/src/components/ActionFlow/index.tsx create mode 100644 packages/neuron-ui/src/components/Mnemonic/index.tsx create mode 100644 packages/neuron-ui/src/components/Prompt/index.tsx create mode 100644 packages/neuron-ui/src/components/WalletSubmission/index.tsx delete mode 100644 packages/neuron-ui/src/components/WalletWizard/actionA.tsx delete mode 100644 packages/neuron-ui/src/components/WalletWizard/actionB.tsx delete mode 100644 packages/neuron-ui/src/components/WalletWizard/actionC.tsx delete mode 100644 packages/neuron-ui/src/components/WalletWizard/actionD.tsx delete mode 100644 packages/neuron-ui/src/components/WalletWizard/createWallet.tsx delete mode 100644 packages/neuron-ui/src/components/WalletWizard/importWallet.tsx create mode 100644 packages/neuron-ui/src/utils/hooks.tsx create mode 100644 packages/neuron-ui/src/utils/mnemonic.ts create mode 100644 packages/neuron-ui/src/widgets/Screen/index.tsx create mode 100644 packages/neuron-ui/src/widgets/ScreenButtonRow/index.tsx diff --git a/.eslintrc.js b/.eslintrc.js index a8e077f1ac..7d87ec6193 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -36,9 +36,11 @@ module.exports = { "ignoreRegExpLiterals": true, }], "object-curly-newline": ["error", { - "ObjectExpression": "always", + "ObjectExpression": { + "consistent": true + }, "ObjectPattern": { - "multiline": true + "consistent": true }, "ImportDeclaration": { "consistent": true, diff --git a/packages/neuron-ui/src/components/ActionFlow/index.tsx b/packages/neuron-ui/src/components/ActionFlow/index.tsx deleted file mode 100644 index 5ea4fe85a2..0000000000 --- a/packages/neuron-ui/src/components/ActionFlow/index.tsx +++ /dev/null @@ -1,118 +0,0 @@ -import React, { useState, useEffect } from 'react' -import styled from 'styled-components' - -const ActionFlowStyle = styled.div` - display: flex; - flex-direction: column; - padding-top: 100px; - width: 100%; - min-height: 500px; - .actionFlowHeader { - height: 80px; - display: flex; - flex-direction: row; - justify-content: space-around; - >div: nth-child(n + ${(props: { step: number }) => props.step + 1}) { - color: lightgrey; - > div:nth-child(1) { - background-color: white; - } - } - } - .actionFlowBody { - flex: 1; - display: flex; - justify-content: center; - & > div { - display: flex; - flex-direction: column; - align-items: center; - width: 100%; - } - } - .actionFlowFooter { - border-top: 2px solid lightgrey; - height: 60px; - padding: 20px 20px 0 20px; - button { - height: 40px; - width: 100px; - } - } -` - -const ActionStep: React.SFC<{ - title: string - onBeforeBack?: Function - onBeforeNext?: Function - onAfterNext?: Function - onAfterBack?: Function - children: any -}> = ({ children }: { children: any }) =>
{children}
- -ActionStep.defaultProps = { - title: 'StepName', -} - -ActionStep.displayName = 'ActionStep' - -const ActionFlow = ({ children }: { children: Array }) => { - const [step, setStep] = useState(1) - useEffect(() => {}, [step]) - - return ( - -
{children[step - 1]}
-
- - -
-
- ) -} -export { ActionStep } -export default ActionFlow diff --git a/packages/neuron-ui/src/components/Mnemonic/index.tsx b/packages/neuron-ui/src/components/Mnemonic/index.tsx new file mode 100644 index 0000000000..ec9dc380de --- /dev/null +++ b/packages/neuron-ui/src/components/Mnemonic/index.tsx @@ -0,0 +1,107 @@ +import React, { useContext, useEffect, useCallback } from 'react' +import { RouteComponentProps } from 'react-router-dom' +import { Alert, Button, FormControl } from 'react-bootstrap' +import { useTranslation } from 'react-i18next' + +import Screen from '../../widgets/Screen' +import ScreenButtonRow from '../../widgets/ScreenButtonRow' + +import WalletContext from '../../contexts/Wallet' +import { ContentProps } from '../../containers/MainContent' +import { MainActions } from '../../containers/MainContent/reducer' +import mnemonicUtils from '../../utils/mnemonic' +import { MnemonicAction, Routes } from '../../utils/const' + +const Mnemonic = (props: React.PropsWithoutRef>) => { + const { + match: { + params: { type }, + }, + mnemonic: { generated, imported }, + errorMsgs: { wizard }, + dispatch, + history, + } = props + + const { address } = useContext(WalletContext) + const [t] = useTranslation() + + const isCreate = type === MnemonicAction.Create + + useEffect(() => { + if (type === MnemonicAction.Verify && !generated) { + history.push(`${Routes.Mnemonic}/${MnemonicAction.Create}`) + } else if (!isCreate) { + dispatch({ + type: MainActions.UpdateMnemonic, + payload: { imported: '' }, + }) + } else { + dispatch({ + type: MainActions.UpdateMnemonic, + payload: { + generated: mnemonicUtils.generateMnemonic(), + imported: '', + }, + }) + } + }, [type]) + + const onChange = useCallback( + (e: React.FormEvent<{ value: string }>) => { + if (isCreate) return + const { value } = e.currentTarget + dispatch({ + type: MainActions.UpdateMnemonic, + payload: { imported: value }, + }) + }, + [type], + ) + + const onBack = useCallback(() => { + history.goBack() + }, []) + + const onNext = useCallback(() => { + switch (type) { + case MnemonicAction.Create: { + history.push(`${Routes.Mnemonic}/${MnemonicAction.Verify}`) + break + } + case MnemonicAction.Verify: + case MnemonicAction.Import: { + history.push(Routes.WalletSubmission) + break + } + default: { + break + } + } + }, [type]) + + const disableNext = type === MnemonicAction.Verify && !mnemonicUtils.verifyMnemonic(generated, imported) + const message = isCreate ? 'wizard.your-wallet-seed-is' : 'wizard.input-your-seed' + + return ( + +
+

{t(message)}

+ + {wizard ? {t(wizard)} : null} + + + + +
+
+ ) +} + +Mnemonic.displayName = 'Mnemonic' + +export default Mnemonic diff --git a/packages/neuron-ui/src/components/Prompt/index.tsx b/packages/neuron-ui/src/components/Prompt/index.tsx new file mode 100644 index 0000000000..adb8dd9b73 --- /dev/null +++ b/packages/neuron-ui/src/components/Prompt/index.tsx @@ -0,0 +1,29 @@ +import React, { useContext } from 'react' +import { RouteComponentProps } from 'react-router-dom' +import { useTranslation } from 'react-i18next' + +import Screen from '../../widgets/Screen' + +import WalletContext from '../../contexts/Wallet' +import { queryParsers } from '../../utils/parser' + +const Prompt = ({ + match: { + params: { event }, + }, + location: { search }, +}: RouteComponentProps<{ event: string }>) => { + const [t] = useTranslation() + const { address } = useContext(WalletContext) + const params = queryParsers.prompt(search) + + return ( + +
{t(`messages.${event}`, params)}
+
+ ) +} + +Prompt.displayName = 'Prompt' + +export default Prompt diff --git a/packages/neuron-ui/src/components/Router/index.tsx b/packages/neuron-ui/src/components/Router/index.tsx index 3c1e424ee7..39febacd4a 100644 --- a/packages/neuron-ui/src/components/Router/index.tsx +++ b/packages/neuron-ui/src/components/Router/index.tsx @@ -1,35 +1,38 @@ import React, { useContext } from 'react' import { BrowserRouter as Router, Route, RouteComponentProps } from 'react-router-dom' -import { Routes, Channel } from '../../utils/const' - -import UILayer from '../../services/UILayer' import RoutesWithProps from './RoutesWithProps' +import Header from '../../containers/Header' +import Sidebar from '../../containers/Sidebar' import MainContent from '../../containers/MainContent' import Notification from '../../containers/Notification' -import Sidebar from '../../containers/Sidebar' -import Header from '../../containers/Header' import Home from '../Home' +import WalletWizard from '../WalletWizard' +import Mnemonic from '../Mnemonic' +import WalletSubmission from '../WalletSubmission' import WalletDetail from '../WalletDetail' import Send from '../Transfer' import Receive from '../Receive' import History from '../History' import Transaction from '../Transaction' -import Addresses from '../Addresses' import Settings from '../Settings' -import WalletWizard, { ImportWallet, CreateWallet } from '../WalletWizard' import General from '../Settings/General' +import Addresses from '../Addresses' import Wallets from '../Settings/Wallets' import Network from '../Settings/Networks' import NetworkEditor from '../NetworkEditor' import WalletEditor from '../WalletEditor' import Terminal from '../Terminal' +import Prompt from '../Prompt' import WalletContext from '../../contexts/Wallet' +import UILayer from '../../services/UILayer' +import { Routes, Channel } from '../../utils/const' + export interface CustomRoute { - path: string name: string + path: string params?: string exact?: boolean component: React.FunctionComponent @@ -90,7 +93,7 @@ export const mainContents: CustomRoute[] = [ { name: `Transaction`, path: Routes.Transaction, - params: '/:hash', + params: `/:hash`, exact: false, component: Transaction, }, @@ -139,22 +142,23 @@ export const mainContents: CustomRoute[] = [ component: WalletEditor, }, { - name: `CreateWallet`, - path: Routes.CreateWallet, + name: `WalletWizard`, + path: Routes.WalletWizard, exact: false, - component: CreateWallet, + component: WalletWizard, }, { - name: `ImportWallet`, - path: Routes.ImportWallet, + name: `Mnemonic`, + path: Routes.Mnemonic, + params: `/:type`, exact: false, - component: ImportWallet, + component: Mnemonic, }, { - name: `WalletWizard`, - path: Routes.WalletWizard, - exact: false, - component: WalletWizard, + name: `WalletSubmission`, + path: Routes.WalletSubmission, + exact: true, + component: WalletSubmission, }, { name: `Terminal`, @@ -162,6 +166,13 @@ export const mainContents: CustomRoute[] = [ exact: true, component: Terminal, }, + { + name: `Prompt`, + path: Routes.Prompt, + params: '/:event', + exact: false, + component: Prompt, + }, ] const CustomRouter = (appProps: any) => { diff --git a/packages/neuron-ui/src/components/WalletSubmission/index.tsx b/packages/neuron-ui/src/components/WalletSubmission/index.tsx new file mode 100644 index 0000000000..7abf566956 --- /dev/null +++ b/packages/neuron-ui/src/components/WalletSubmission/index.tsx @@ -0,0 +1,95 @@ +import React, { useCallback, useEffect, useContext } from 'react' +import { RouteComponentProps } from 'react-router-dom' +import { Button, InputGroup, FormControl } from 'react-bootstrap' +import { useTranslation } from 'react-i18next' + +import Screen from '../../widgets/Screen' +import ScreenButtonRow from '../../widgets/ScreenButtonRow' + +import WalletContext from '../../contexts/Wallet' +import initState from '../../containers/MainContent/state' +import { ContentProps } from '../../containers/MainContent' +import { MainActions } from '../../containers/MainContent/reducer' + +import { verifyWalletSubmission } from '../../utils/validators' +import { Routes } from '../../utils/const' + +const inptus = [ + { label: 'password', key: 'password' }, + { label: 'confirm-password', key: 'confirmPassword' }, + { label: 'name', key: 'name' }, +] + +const WalletSubmission = (props: React.PropsWithoutRef) => { + const { address } = useContext(WalletContext) + const [t] = useTranslation() + + const { dispatch, mnemonic, history } = props + const { name } = mnemonic + + useEffect(() => { + dispatch({ + type: MainActions.UpdateMnemonic, + payload: { name: `wallet @${Math.round(Math.random() * 100)}` }, + }) + }, []) + + const onChange = useCallback( + (field: keyof typeof mnemonic) => (e: React.FormEvent<{ value: string }>) => { + const { value } = e.currentTarget + dispatch({ + type: MainActions.UpdateMnemonic, + payload: { [field]: value }, + }) + }, + [], + ) + + const onBack = useCallback(() => { + history.goBack() + }, []) + + const onNext = useCallback( + (walletName: string) => () => { + dispatch({ + type: MainActions.UpdateMnemonic, + payload: initState.mnemonic, + }) + // TODO: send message to neuron-wallet and listen to response + history.push(`${Routes.Prompt}/create-wallet-success?name=${walletName.replace(/\s/g, '%20')}`) + }, + [], + ) + + const message = 'wizard.set-a-strong-password-to-protect-your-wallet' + type MnemonicKey = keyof typeof mnemonic + const disableNext = !verifyWalletSubmission(mnemonic) + + return ( + +
+

{t(message)}

+ {inptus.map(input => ( + + + {t(`wizard.${input.label}`)} + + + + ))} + + + + + +
+
+ ) +} + +WalletSubmission.displayName = 'WalletSubmission' +export default WalletSubmission diff --git a/packages/neuron-ui/src/components/WalletWizard/actionA.tsx b/packages/neuron-ui/src/components/WalletWizard/actionA.tsx deleted file mode 100644 index e4edb53011..0000000000 --- a/packages/neuron-ui/src/components/WalletWizard/actionA.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import React from 'react' - -export default () => ( - <> -

You wallet seed is

-