From ec9a4f5657b12c277bacccad3d0b60211411d1a9 Mon Sep 17 00:00:00 2001 From: Keith Date: Thu, 11 Jul 2019 16:51:50 +0800 Subject: [PATCH 1/6] feat: move the password dialog to the main window --- .../src/components/PasswordRequest/index.tsx | 96 ++++++++++++++ .../neuron-ui/src/components/Send/hooks.ts | 102 +++++++++++---- .../neuron-ui/src/components/Send/index.tsx | 10 +- .../src/components/WalletSetting/index.tsx | 1 + .../neuron-ui/src/containers/Main/hooks.ts | 21 ++-- .../neuron-ui/src/containers/Main/index.tsx | 7 ++ packages/neuron-ui/src/locales/en.json | 13 ++ packages/neuron-ui/src/locales/zh.json | 13 ++ packages/neuron-ui/src/services/UILayer.ts | 16 ++- .../neuron-ui/src/states/initStates/app.ts | 6 + .../stateProvider/actionCreators/send.ts | 47 ++----- .../stateProvider/actionCreators/wallets.ts | 18 +++ .../src/states/stateProvider/reducer.ts | 48 ++++++- .../src/stories/PasswordRequest.stories.tsx | 102 +++++++++++++++ packages/neuron-ui/src/stories/index.tsx | 1 + packages/neuron-ui/src/types/App/index.d.ts | 6 + .../scripts/copy-renderer-html.sh | 1 - .../src/controllers/app/index.ts | 40 +----- .../src/controllers/app/options.ts | 55 +++++--- .../src/controllers/wallets/index.ts | 117 ++++++------------ .../src/models/window-manager.ts | 2 +- .../neuron-wallet/src/services/wallets.ts | 28 +++-- .../src/types/controller/index.d.ts | 8 ++ .../src/utils/application-menu.ts | 23 ++-- .../neuron-wallet/src/utils/prompt/index.ts | 86 ------------- .../src/utils/prompt/password/index.html | 53 -------- .../src/utils/prompt/password/index.ts | 25 ---- .../tests/services/wallets.test.ts | 2 - 28 files changed, 542 insertions(+), 405 deletions(-) create mode 100644 packages/neuron-ui/src/components/PasswordRequest/index.tsx create mode 100644 packages/neuron-ui/src/stories/PasswordRequest.stories.tsx delete mode 100644 packages/neuron-wallet/src/utils/prompt/index.ts delete mode 100644 packages/neuron-wallet/src/utils/prompt/password/index.html delete mode 100644 packages/neuron-wallet/src/utils/prompt/password/index.ts diff --git a/packages/neuron-ui/src/components/PasswordRequest/index.tsx b/packages/neuron-ui/src/components/PasswordRequest/index.tsx new file mode 100644 index 0000000000..cd782252ff --- /dev/null +++ b/packages/neuron-ui/src/components/PasswordRequest/index.tsx @@ -0,0 +1,96 @@ +import React, { useCallback, useMemo } from 'react' +import { useTranslation } from 'react-i18next' +import { Stack, Text, Label, Modal, TextField, PrimaryButton, DefaultButton } from 'office-ui-fabric-react' +import { StateWithDispatch, AppActions } from 'states/stateProvider/reducer' +import actionCreators from 'states/stateProvider/actionCreators' + +const PasswordRequest = ({ + app: { + send: { txID, outputs, description }, + passwordRequest: { walletID = '', actionType = null, password = '' }, + }, + settings: { wallets = [] }, + dispatch, +}: React.PropsWithoutRef) => { + const [t] = useTranslation() + const wallet = useMemo(() => wallets.find(w => w.id === walletID), [walletID, wallets]) + const onDismiss = useCallback(() => { + dispatch({ + type: AppActions.DismissPasswordRequest, + payload: null, + }) + }, [dispatch]) + + const onConfirm = useCallback(() => { + const params = { id: walletID, password } + switch (actionType) { + case 'delete': { + dispatch(actionCreators.deleteWallet(params)) + break + } + case 'backup': { + dispatch(actionCreators.backupWallet(params)) + break + } + case 'send': { + dispatch(actionCreators.submitTransaction(txID, walletID, outputs, description, password)) + break + } + default: { + break + } + } + }, [dispatch, walletID, password, actionType, txID, description, outputs]) + + const onChange = useCallback( + (_e, value?: string) => { + if (undefined !== value) { + dispatch({ + type: AppActions.UpdatePassword, + payload: value, + }) + } + }, + [dispatch] + ) + const onKeyPress = useCallback( + (e: React.KeyboardEvent) => { + if (e.key === 'Enter') { + onConfirm() + } + }, + [onConfirm] + ) + if (!wallet) { + return null + } + return ( + + { + + {t(`password-request.${actionType}.title`, { name: wallet ? wallet.name : '' })} + + + + {t('common.cancel')} + + {t('common.confirm')} + + + + } + + ) +} + +PasswordRequest.displayName = 'PasswordRequest' +export default PasswordRequest diff --git a/packages/neuron-ui/src/components/Send/hooks.ts b/packages/neuron-ui/src/components/Send/hooks.ts index bc835948f3..e8ae2a211b 100644 --- a/packages/neuron-ui/src/components/Send/hooks.ts +++ b/packages/neuron-ui/src/components/Send/hooks.ts @@ -1,11 +1,50 @@ -import React, { useCallback, useEffect, useMemo } from 'react' +import React, { useCallback, useEffect } from 'react' import { IDropdownOption } from 'office-ui-fabric-react' import { AppActions, StateDispatch } from 'states/stateProvider/reducer' -import actionCreators from 'states/stateProvider/actionCreators' +import { Message } from 'utils/const' +import { verifyAddress } from 'utils/validators' import { TransactionOutput } from '.' +const validateTransactionParams = ({ items, dispatch }: { items: TransactionOutput[]; dispatch: StateDispatch }) => { + const errorAction = { + type: AppActions.AddNotification, + payload: { + type: 'warning', + timestamp: Date.now(), + content: Message.AtLeastOneAddressNeeded, + }, + } + if (!items.length || !items[0].address) { + dispatch(errorAction) + return false + } + const invalid = items.some( + (item): boolean => { + if (!verifyAddress(item.address)) { + errorAction.payload.content = Message.InvalidAddress + return true + } + if (Number.isNaN(+item.amount) || +item.amount < 0) { + errorAction.payload.content = Message.InvalidAmount + return true + } + const [, decimal = ''] = item.amount.split('.') + if (decimal.length > 8) { + errorAction.payload.content = Message.InvalidAmount + return true + } + return false + } + ) + if (invalid) { + dispatch(errorAction) + return false + } + return true +} + const useUpdateTransactionOutput = (dispatch: StateDispatch) => useCallback( (field: string) => (idx: number) => (value: string) => { @@ -14,7 +53,7 @@ const useUpdateTransactionOutput = (dispatch: StateDispatch) => payload: { idx, item: { - [field]: value, + [field]: value.trim(), }, }, }) @@ -41,24 +80,38 @@ const useRemoveTransactionOutput = (dispatch: StateDispatch) => [dispatch] ) -const useOnSubmit = (dispatch: StateDispatch) => +const useOnSubmit = (items: TransactionOutput[], dispatch: StateDispatch) => useCallback( - (id: string = '', walletID: string = '', items: TransactionOutput[] = [], description: string = '') => () => { - setTimeout(() => { - dispatch(actionCreators.submitTransaction(id, walletID, items, description)) - }, 10) + (walletID: string = '') => () => { + if (validateTransactionParams({ items, dispatch })) { + dispatch({ + type: AppActions.UpdateTransactionID, + payload: null, + }) + dispatch({ + type: AppActions.RequestPassword, + payload: { + walletID, + actionType: 'send', + }, + }) + } }, - [dispatch] + [dispatch, items] ) -const useOnItemChange = (updateTransactionOutput: Function) => (field: string = '', idx: number = -1) => ( - _e: React.FormEvent, - value?: string -) => { - if (undefined !== value) { - updateTransactionOutput(field)(idx)(value) - } -} +const useOnItemChange = (updateTransactionOutput: Function) => + useCallback( + (field: string = '', idx: number = -1) => ( + _e: React.FormEvent, + value?: string + ) => { + if (undefined !== value) { + updateTransactionOutput(field)(idx)(value) + } + }, + [updateTransactionOutput] + ) const useCapacityUnitChange = (updateTransactionOutput: Function) => useCallback( @@ -76,7 +129,7 @@ const useUpdateTransactionPrice = (dispatch: StateDispatch) => if (undefined !== value) { dispatch({ type: AppActions.UpdateSendPrice, - payload: value, + payload: value.trim(), }) } }, @@ -105,11 +158,16 @@ const clear = (dispatch: StateDispatch) => { const useClear = (dispatch: StateDispatch) => useCallback(() => clear(dispatch), [dispatch]) -export const useInitialize = (address: string, dispatch: React.Dispatch, history: any) => { +export const useInitialize = ( + address: string, + items: TransactionOutput[], + dispatch: React.Dispatch, + history: any +) => { const updateTransactionOutput = useUpdateTransactionOutput(dispatch) const onItemChange = useOnItemChange(updateTransactionOutput) const onCapacityUnitChange = useCapacityUnitChange(updateTransactionOutput) - const onSubmit = useOnSubmit(dispatch) + const onSubmit = useOnSubmit(items, dispatch) const addTransactionOutput = useAddTransactionOutput(dispatch) const removeTransactionOutput = useRemoveTransactionOutput(dispatch) const updateTransactionPrice = useUpdateTransactionPrice(dispatch) @@ -125,11 +183,7 @@ export const useInitialize = (address: string, dispatch: React.Dispatch, hi } }, [address, dispatch, history, updateTransactionOutput]) - // TODO: generate new id on every submission - const id = useMemo(() => Math.round(Math.random() * 1000).toString(), []) - return { - id, updateTransactionOutput, onItemChange, onCapacityUnitChange, diff --git a/packages/neuron-ui/src/components/Send/index.tsx b/packages/neuron-ui/src/components/Send/index.tsx index 2d81c28afb..a4c81d9277 100644 --- a/packages/neuron-ui/src/components/Send/index.tsx +++ b/packages/neuron-ui/src/components/Send/index.tsx @@ -40,7 +40,6 @@ const Send = ({ }: React.PropsWithoutRef>) => { const { t } = useTranslation() const { - id, updateTransactionOutput, onItemChange, onSubmit, @@ -49,7 +48,7 @@ const Send = ({ updateTransactionPrice, onDescriptionChange, onClear, - } = useInitialize(address, dispatch, history) + } = useInitialize(address, send.outputs, dispatch, history) return ( @@ -153,12 +152,7 @@ const Send = ({ {sending ? ( ) : ( - + )} diff --git a/packages/neuron-ui/src/components/WalletSetting/index.tsx b/packages/neuron-ui/src/components/WalletSetting/index.tsx index fe2c74a9e4..697fb3b3f6 100644 --- a/packages/neuron-ui/src/components/WalletSetting/index.tsx +++ b/packages/neuron-ui/src/components/WalletSetting/index.tsx @@ -59,6 +59,7 @@ const WalletSetting = ({ key: wallet.id, text: wallet.name, checked: wallet.id === currentID, + disabled: wallet.id === currentID, onRenderLabel: ({ text }: IChoiceGroupOption) => { return ( diff --git a/packages/neuron-ui/src/containers/Main/hooks.ts b/packages/neuron-ui/src/containers/Main/hooks.ts index 39a5b2d80f..df0a5da315 100644 --- a/packages/neuron-ui/src/containers/Main/hooks.ts +++ b/packages/neuron-ui/src/containers/Main/hooks.ts @@ -229,17 +229,6 @@ export const useChannelListeners = (i18n: any, history: any, chain: State.Chain, }) break } - case WalletsMethod.Delete: { - dispatch({ - type: NeuronWalletActions.Settings, - payload: { wallets: args.result.allWallets }, - }) - dispatch({ - type: NeuronWalletActions.Wallet, - payload: args.result.currentWallet, - }) - break - } case WalletsMethod.SendCapacity: { if (args.result) { history.push(Routes.History) @@ -266,6 +255,16 @@ export const useChannelListeners = (i18n: any, history: any, chain: State.Chain, }) break } + case WalletsMethod.RequestPassword: { + dispatch({ + type: AppActions.RequestPassword, + payload: { + walletID: args.result.walletID || '', + actionType: args.result.actionType || '', + }, + }) + break + } default: { break } diff --git a/packages/neuron-ui/src/containers/Main/index.tsx b/packages/neuron-ui/src/containers/Main/index.tsx index 49e083e41a..c3cc3db494 100644 --- a/packages/neuron-ui/src/containers/Main/index.tsx +++ b/packages/neuron-ui/src/containers/Main/index.tsx @@ -16,6 +16,7 @@ import Addresses from 'components/Addresses' import NetworkEditor from 'components/NetworkEditor' import WalletEditor from 'components/WalletEditor' import LaunchScreen from 'components/LaunchScreen' +import PasswordRequest from 'components/PasswordRequest' import { Routes } from 'utils/const' @@ -93,6 +94,12 @@ export const mainContents: CustomRouter.Route[] = [ exact: false, comp: WalletWizard, }, + { + name: `PasswordRequest`, + path: '/', + exact: false, + comp: PasswordRequest, + }, ] const MainContent = ({ diff --git a/packages/neuron-ui/src/locales/en.json b/packages/neuron-ui/src/locales/en.json index 319bf66fdb..eabe475c37 100644 --- a/packages/neuron-ui/src/locales/en.json +++ b/packages/neuron-ui/src/locales/en.json @@ -143,6 +143,19 @@ } } }, + "password-request": { + "wallet-not-found": "The wallet is not found", + "password": "Password", + "send": { + "title": "Submit the transaction" + }, + "delete": { + "title": "Delete the {{name}} wallet" + }, + "backup": { + "title": "Backup the {{name}} wallet" + } + }, "menuitem": { "add": "Add", "update": "Update", diff --git a/packages/neuron-ui/src/locales/zh.json b/packages/neuron-ui/src/locales/zh.json index e92629f351..f90f01d995 100644 --- a/packages/neuron-ui/src/locales/zh.json +++ b/packages/neuron-ui/src/locales/zh.json @@ -143,6 +143,19 @@ } } }, + "password-request": { + "wallet-not-found": "找不到该钱包", + "password": "密码", + "send": { + "title": "发起交易" + }, + "delete": { + "title": "删除钱包 {{name}}" + }, + "backup": { + "title": "备份钱包 {{name}}" + } + }, "menuitem": { "add": "添加", "update": "更新", diff --git a/packages/neuron-ui/src/services/UILayer.ts b/packages/neuron-ui/src/services/UILayer.ts index b2a489c524..4b9c8164c4 100644 --- a/packages/neuron-ui/src/services/UILayer.ts +++ b/packages/neuron-ui/src/services/UILayer.ts @@ -31,6 +31,7 @@ export enum WalletsMethod { SendingStatus = 'sendingStatus', AllAddresses = 'allAddresses', UpdateAddressDescription = 'updateAddressDescription', + RequestPassword = 'requestPassword', } export enum NetworksMethod { @@ -127,7 +128,14 @@ export const wallets = ( | { mnemonic: string; password: string } | { id: string; password: string } | { id: string; name?: string; password: string; newPassword?: string } - | { id: string; walletID: string; items: { address: string; capacity: string }[]; fee: string; description: string } + | { + id: string + walletID: string + items: { address: string; capacity: string }[] + password: string + fee: string + description: string + } ) => { UILayer.send(Channel.Wallets, method, params) } @@ -140,11 +148,10 @@ export const walletsCall = instantiateMethodCall(wallets) as { importMnemonic: (params: { name: string; mnemonic: string; password: string }) => void create: (params: { name: string; mnemonic: string; password: string }) => void update: (params: { id: string; password?: string; newPassword?: string; name?: string }) => void - delete: (id: string) => void - export: (id: string) => void + delete: (params: { id: string; password: string }) => void getCurrent: () => void activate: (id: string) => void - backup: (id: string) => void + backup: (params: { id: string; password: string }) => void sendCapacity: (params: { id: string walletID: string @@ -152,6 +159,7 @@ export const walletsCall = instantiateMethodCall(wallets) as { address: string capacity: string }[] + password: string fee: string description: string }) => void diff --git a/packages/neuron-ui/src/states/initStates/app.ts b/packages/neuron-ui/src/states/initStates/app.ts index 9afb3387b6..016db0bf96 100644 --- a/packages/neuron-ui/src/states/initStates/app.ts +++ b/packages/neuron-ui/src/states/initStates/app.ts @@ -2,6 +2,7 @@ import { CapacityUnit } from 'utils/const' const appState: State.App = { send: { + txID: '', outputs: [ { address: '', @@ -13,6 +14,11 @@ const appState: State.App = { description: '', loading: false, }, + passwordRequest: { + actionType: null, + walletID: '', + password: '', + }, messages: { networks: null, send: null, diff --git a/packages/neuron-ui/src/states/stateProvider/actionCreators/send.ts b/packages/neuron-ui/src/states/stateProvider/actionCreators/send.ts index 8e4fea0e44..747c4e9fc7 100644 --- a/packages/neuron-ui/src/states/stateProvider/actionCreators/send.ts +++ b/packages/neuron-ui/src/states/stateProvider/actionCreators/send.ts @@ -1,46 +1,18 @@ import { walletsCall } from 'services/UILayer' -import { Message } from 'utils/const' -import { verifyAddress } from 'utils/validators' import { CKBToShannonFormatter } from 'utils/formatters' import { TransactionOutput } from 'components/Send' import { AppActions } from '../reducer' export default { - submitTransaction: (id: string, walletID: string, items: TransactionOutput[], description: string) => { - const errorAction = { - type: AppActions.AddNotification, - payload: { - type: 'warning', - timestamp: Date.now(), - content: Message.AtLeastOneAddressNeeded, - }, - } - if (!items.length || !items[0].address) { - return errorAction - } - const invalid = items.some( - (item): boolean => { - if (!verifyAddress(item.address)) { - errorAction.payload.content = Message.InvalidAddress - return true - } - if (Number.isNaN(+item.amount) || +item.amount < 0) { - errorAction.payload.content = Message.InvalidAmount - return true - } - const [, decimal = ''] = item.amount.split('.') - if (decimal.length > 8) { - errorAction.payload.content = Message.InvalidAmount - return true - } - return false - } - ) - if (invalid) { - return errorAction - } + submitTransaction: ( + id: string = '', + walletID: string = '', + items: TransactionOutput[] = [], + description: string = '', + password: string = '' + ) => { walletsCall.sendCapacity({ id, walletID, @@ -48,12 +20,13 @@ export default { address: item.address, capacity: CKBToShannonFormatter(item.amount, item.unit), })), + password, fee: '0', description, }) return { - type: AppActions.UpdateSendLoading, - payload: true, + type: AppActions.DismissPasswordRequest, + payload: null, } }, } diff --git a/packages/neuron-ui/src/states/stateProvider/actionCreators/wallets.ts b/packages/neuron-ui/src/states/stateProvider/actionCreators/wallets.ts index accc88feb9..938147ffaf 100644 --- a/packages/neuron-ui/src/states/stateProvider/actionCreators/wallets.ts +++ b/packages/neuron-ui/src/states/stateProvider/actionCreators/wallets.ts @@ -23,4 +23,22 @@ export default { payload: null, } }, + deleteWallet: (params: { id: string; password: string }) => { + setTimeout(() => { + walletsCall.delete(params) + }, 100) + return { + type: AppActions.DismissPasswordRequest, + payload: null, + } + }, + backupWallet: (params: { id: string; password: string }) => { + setTimeout(() => { + walletsCall.backup(params) + }, 100) + return { + type: AppActions.DismissPasswordRequest, + payload: null, + } + }, } diff --git a/packages/neuron-ui/src/states/stateProvider/reducer.ts b/packages/neuron-ui/src/states/stateProvider/reducer.ts index dc5e86921c..1b7f57211e 100644 --- a/packages/neuron-ui/src/states/stateProvider/reducer.ts +++ b/packages/neuron-ui/src/states/stateProvider/reducer.ts @@ -8,6 +8,7 @@ export enum NeuronWalletActions { Settings = 'settings', } export enum AppActions { + UpdateTransactionID = 'updateTransactionID', AddSendOutput = 'addSendOutput', RemoveSendOutput = 'removeSendOutput', UpdateSendOutput = 'updateSendOutput', @@ -15,14 +16,15 @@ export enum AppActions { UpdateSendDescription = 'updateSendDescription', ClearSendState = 'clearSendState', UpdateSendLoading = 'updateSendLoading', - UpdatePassword = 'updatePassword', - UpdateConfirmPassword = 'updateConfirmPassword', UpdateMessage = 'updateMessage', AddNotification = 'addNotification', RemoveNotification = 'removeNotification', ClearNotifications = 'clearNotifications', CleanTransaction = 'cleanTransaction', CleanTransactions = 'cleanTransactions', + RequestPassword = 'requestPassword', + DismissPasswordRequest = 'dismissPasswordRequest', + UpdatePassword = 'updatePassword', Ignore = 'ignore', } @@ -120,6 +122,18 @@ export const reducer = ( } } // Actions of App + case AppActions.UpdateTransactionID: { + return { + ...state, + app: { + ...app, + send: { + ...app.send, + txID: Math.round(Math.random() * 100000).toString(), + }, + }, + } + } case AppActions.AddSendOutput: { return { ...state, @@ -236,6 +250,36 @@ export const reducer = ( }, } } + case AppActions.RequestPassword: { + return { + ...state, + app: { + ...app, + passwordRequest: payload, + }, + } + } + case AppActions.DismissPasswordRequest: { + return { + ...state, + app: { + ...app, + passwordRequest: initStates.app.passwordRequest, + }, + } + } + case AppActions.UpdatePassword: { + return { + ...state, + app: { + ...app, + passwordRequest: { + ...app.passwordRequest, + password: payload, + }, + }, + } + } case AppActions.UpdateMessage: { /** * payload: {type,content, timestamp} diff --git a/packages/neuron-ui/src/stories/PasswordRequest.stories.tsx b/packages/neuron-ui/src/stories/PasswordRequest.stories.tsx new file mode 100644 index 0000000000..894b16b849 --- /dev/null +++ b/packages/neuron-ui/src/stories/PasswordRequest.stories.tsx @@ -0,0 +1,102 @@ +import React from 'react' +import { storiesOf } from '@storybook/react' +import { action } from '@storybook/addon-actions' +import PasswordRquest from 'components/PasswordRequest' +import initStates from 'states/initStates' + +const states: { [title: string]: State.AppWithNeuronWallet } = { + 'Wallet not Found': { + ...initStates, + app: { + ...initStates.app, + passwordRequest: { + walletID: '1', + actionType: 'delete', + password: '', + }, + }, + }, + 'Empty Password of Delete Wallet': { + ...initStates, + settings: { + ...initStates.settings, + wallets: [{ id: '1', name: 'test wallet' }], + }, + app: { + ...initStates.app, + passwordRequest: { + walletID: '1', + actionType: 'delete', + password: '', + }, + }, + }, + 'Content Password of Delete Wallet': { + ...initStates, + settings: { + ...initStates.settings, + wallets: [{ id: '1', name: 'test wallet' }], + }, + app: { + ...initStates.app, + passwordRequest: { + walletID: '1', + actionType: 'delete', + password: '123456', + }, + }, + }, + 'Empty Password of Backup Wallet': { + ...initStates, + settings: { + ...initStates.settings, + wallets: [{ id: '1', name: 'test wallet' }], + }, + app: { + ...initStates.app, + passwordRequest: { + walletID: '1', + actionType: 'backup', + password: '', + }, + }, + }, + 'Content Password of Backup Wallet': { + ...initStates, + settings: { + ...initStates.settings, + wallets: [{ id: '1', name: 'test wallet' }], + }, + app: { + ...initStates.app, + passwordRequest: { + walletID: '1', + actionType: 'backup', + password: '123456', + }, + }, + }, + 'Send Transaction': { + ...initStates, + settings: { + ...initStates.settings, + wallets: [{ id: '1', name: 'test wallet' }], + }, + app: { + ...initStates.app, + passwordRequest: { + walletID: '1', + actionType: 'send', + password: '123456', + }, + }, + }, +} + +const stories = storiesOf('PasswordRequest', module) + +Object.entries(states).forEach(([title, props]) => { + stories.add(title, () => ( + action('Dispatch')(JSON.stringify(reducerAction, null, 2))} /> + )) +}) diff --git a/packages/neuron-ui/src/stories/index.tsx b/packages/neuron-ui/src/stories/index.tsx index 751b809d45..24857a4103 100644 --- a/packages/neuron-ui/src/stories/index.tsx +++ b/packages/neuron-ui/src/stories/index.tsx @@ -3,3 +3,4 @@ import './TransactionList.stories' import './GeneralSetting.stories' import './WalletSetting.stories' import './NetworkSetting.stories' +import './PasswordRequest.stories' diff --git a/packages/neuron-ui/src/types/App/index.d.ts b/packages/neuron-ui/src/types/App/index.d.ts index 929bda92bc..b2e68a8baf 100644 --- a/packages/neuron-ui/src/types/App/index.d.ts +++ b/packages/neuron-ui/src/types/App/index.d.ts @@ -52,6 +52,7 @@ declare namespace State { content: string } interface Send { + txID: string outputs: Output[] price: string description: string @@ -60,6 +61,11 @@ declare namespace State { interface App { send: Send + passwordRequest: { + actionType: 'send' | 'backup' | 'delete' | null + walletID: string + password: string + } messages: { [index: string]: Message | null } diff --git a/packages/neuron-wallet/scripts/copy-renderer-html.sh b/packages/neuron-wallet/scripts/copy-renderer-html.sh index 1ab83fd30b..e77cc798a6 100755 --- a/packages/neuron-wallet/scripts/copy-renderer-html.sh +++ b/packages/neuron-wallet/scripts/copy-renderer-html.sh @@ -1,4 +1,3 @@ #!/bin/bash ncp ./src/startup/sync-block-task/index.html ./dist/startup/sync-block-task/index.html -ncp ./src/utils/prompt/password/index.html ./dist/utils/prompt/password/index.html diff --git a/packages/neuron-wallet/src/controllers/app/index.ts b/packages/neuron-wallet/src/controllers/app/index.ts index 0c588aba52..a1490ea043 100644 --- a/packages/neuron-wallet/src/controllers/app/index.ts +++ b/packages/neuron-wallet/src/controllers/app/index.ts @@ -16,13 +16,13 @@ import windowManager from '../../models/window-manager' import i18n from '../../utils/i18n' import env from '../../env' -const walletsService = WalletsService.getInstance() const networksService = NetworksService.getInstance() const nodeService = NodeService.getInstance() @ControllerDecorator(Channel.App) export default class AppController { public static initWindow = async (win: BrowserWindow) => { + const walletsService = WalletsService.getInstance() const [ currentWallet = null, wallets = [], @@ -172,32 +172,6 @@ export default class AppController { AppController.navTo(URL.ImportWallet) } - public static async backupCurrentWallet() { - const currentWallet = AppController.getCurrentWallet() - if (currentWallet) { - const res = await WalletsController.backup(currentWallet.id) - if (!res.status) { - AppController.showMessageBox({ - type: 'error', - message: res.msg!, - }) - } - } - } - - public static async deleteCurrentWallet() { - const currentWallet = AppController.getCurrentWallet() - if (currentWallet) { - const res = await WalletsController.delete(currentWallet.id) - if (!res.status) { - AppController.showMessageBox({ - type: 'error', - message: res.msg!, - }) - } - } - } - public static async showTransactionDetails(hash: string) { const win = new BrowserWindow({ width: 1200, @@ -214,18 +188,6 @@ export default class AppController { AppController.initWindow(win) }) } - - private static getCurrentWallet() { - const currentWallet = walletsService.getCurrent() - if (!currentWallet) { - AppController.showMessageBox({ - type: 'error', - message: i18n.t('messages.not-found', { field: i18n.t('keywords.wallet') }), - }) - return null - } - return currentWallet - } } /* eslint-disable */ diff --git a/packages/neuron-wallet/src/controllers/app/options.ts b/packages/neuron-wallet/src/controllers/app/options.ts index d8a92974a2..b5e6874eec 100644 --- a/packages/neuron-wallet/src/controllers/app/options.ts +++ b/packages/neuron-wallet/src/controllers/app/options.ts @@ -1,8 +1,7 @@ import { MenuItemConstructorOptions, clipboard, dialog } from 'electron' import { bech32Address } from '@nervosnetwork/ckb-sdk-utils' -import NetworksController from '../networks' -import WalletsController from '../wallets' +import WalletsService from '../../services/wallets' import NetworksService from '../../services/networks' import AppController from '.' import i18n from '../../utils/i18n' @@ -30,8 +29,19 @@ export const contextMenuTemplate: { [key: string]: (id: string) => Promise } = { networkList: async (id: string) => { - const { result: network } = await NetworksController.get(id) - const { result: currentNetworkID } = await NetworksController.currentID() + const [network, currentNetworkID] = await Promise.all([ + networksService.get(id).catch(() => null), + networksService.getCurrentID().catch(() => null), + ]) + + if (!network) { + AppController.showMessageBox({ + type: 'error', + message: i18n.t('messages.network-not-found', { id }), + }) + return [] + } + const isCurrent = currentNetworkID === id const isDefault = network.type === 0 @@ -40,7 +50,12 @@ export const contextMenuTemplate: { label: i18n.t('contextMenu.select'), enabled: !isCurrent, click: () => { - NetworksController.activate(id) + networksService.activate(id).catch((err: Error) => { + AppController.showMessageBox({ + type: 'error', + message: err.message, + }) + }) }, }, { @@ -84,23 +99,29 @@ export const contextMenuTemplate: { ] }, walletList: async (id: string) => { + const walletsService = WalletsService.getInstance() + const wallet = walletsService.get(id) + if (!wallet) { + AppController.showMessageBox({ + type: 'error', + message: i18n.t('messages.wallet-not-found', { id }), + }) + } return [ { label: i18n.t('contextMenu.select'), click: () => { - WalletsController.activate(id) + try { + walletsService.setCurrent(id) + } catch (err) { + AppController.showMessageBox({ type: 'error', message: err.message }) + } }, }, { label: i18n.t('contextMenu.backup'), click: async () => { - const res = await WalletsController.backup(id) - if (!res.status) { - AppController.showMessageBox({ - type: 'error', - message: res.msg!, - }) - } + walletsService.requestPassword(id, 'backup') }, }, { @@ -112,13 +133,7 @@ export const contextMenuTemplate: { { label: i18n.t('contextMenu.delete'), click: async () => { - const res = await WalletsController.delete(id) - if (!res.status) { - AppController.showMessageBox({ - type: 'error', - message: res.msg!, - }) - } + walletsService.requestPassword(id, 'delete') }, }, ] diff --git a/packages/neuron-wallet/src/controllers/wallets/index.ts b/packages/neuron-wallet/src/controllers/wallets/index.ts index aaeb911eb9..9730a203fc 100644 --- a/packages/neuron-wallet/src/controllers/wallets/index.ts +++ b/packages/neuron-wallet/src/controllers/wallets/index.ts @@ -11,19 +11,16 @@ import { CurrentWalletNotSet, IsRequired, WalletNotFound, - IncorrectPassword, InvalidMnemonic, - EmptyPassword, ServiceHasNoResponse, + EmptyPassword, + IncorrectPassword, } from '../../exceptions' -import prompt from '../../utils/prompt' import i18n from '../../utils/i18n' import windowManager from '../../models/window-manager' import AddressService from '../../services/addresses' import WalletCreatedSubject from '../../models/subjects/wallet-created-subject' -const walletsService = WalletsService.getInstance() - /** * @class WalletsController * @description handle messages from wallets channel @@ -32,6 +29,7 @@ const walletsService = WalletsService.getInstance() export default class WalletsController { @CatchControllerError public static async getAll(): Promise[]>> { + const walletsService = WalletsService.getInstance() const wallets = walletsService.getAll() if (!wallets) { throw new ServiceHasNoResponse('Wallet') @@ -44,6 +42,7 @@ export default class WalletsController { @CatchControllerError public static async get(id: string): Promise> { + const walletsService = WalletsService.getInstance() if (typeof id === 'undefined') { throw new IsRequired('ID') } @@ -130,6 +129,7 @@ export default class WalletsController { accountKeychain.chainCode.toString('hex') ) + const walletsService = WalletsService.getInstance() const wallet = walletsService.create({ id: '', name, @@ -174,6 +174,7 @@ export default class WalletsController { accountKeychain.chainCode.toString('hex') ) + const walletsService = WalletsService.getInstance() const wallet = walletsService.create({ id: '', name, @@ -199,6 +200,7 @@ export default class WalletsController { name: string newPassword?: string }): Promise> { + const walletsService = WalletsService.getInstance() const wallet = walletsService.get(id) if (!wallet) { throw new WalletNotFound(id) @@ -222,22 +224,17 @@ export default class WalletsController { } @CatchControllerError - public static async delete(id: string): Promise> { - const password = await WalletsController.requestPassword(i18n.t('messageBox.remove-wallet.title')) - if (password === null) { - return { - status: ResponseCode.Success, - result: null, - } - } - + public static async delete({ + id = '', + password = '', + }: Controller.Params.DeleteWallet): Promise> { if (password === '') { throw new EmptyPassword() } + const walletsService = WalletsService.getInstance() if (!walletsService.validate({ id, password })) { throw new IncorrectPassword() } - walletsService.delete(id) return { @@ -246,30 +243,40 @@ export default class WalletsController { } @CatchControllerError - public static async export({ id, password }: { id: string; password: string }): Promise> { + public static async backup({ + id = '', + password = '', + }: Controller.Params.BackupWallet): Promise> { + const walletsService = WalletsService.getInstance() + const wallet = walletsService.get(id) + if (!walletsService.validate({ id, password })) { throw new IncorrectPassword() } - return { - status: ResponseCode.Success, - result: JSON.stringify(walletsService.get(id)), - } - } - @CatchControllerError - public static async backup(id: string): Promise> { - const password = await WalletsController.requestPassword(i18n.t('messageBox.backup-keystore.title')) - if (password === null) { - return { - status: ResponseCode.Success, - result: false, - } - } - return WalletsController.downloadKeystore(id, password) + const keystore = wallet.loadKeystore() + return new Promise(resolve => { + AppController.showSaveDialog( + { + title: i18n.t('messages.save-keystore'), + defaultPath: wallet.name, + }, + (filename?: string) => { + if (filename) { + fs.writeFileSync(filename, JSON.stringify(keystore)) + resolve({ + status: ResponseCode.Success, + result: true, + }) + } + } + ) + }) } @CatchControllerError public static async activate(id: string) { + const walletsService = WalletsService.getInstance() walletsService.setCurrent(id) const currentWallet = walletsService.getCurrent() as FileKeystoreWallet if (!currentWallet || id !== currentWallet.id) { @@ -307,16 +314,10 @@ export default class WalletsController { address: string capacity: string }[] + password: string fee: string description?: string }) { - const password = await WalletsController.requestPassword(i18n.t('messageBox.send-capacity.title')) - if (password === null) { - return { - status: ResponseCode.Success, - result: '', - } - } if (!params) { throw new IsRequired('Parameters') } @@ -325,10 +326,11 @@ export default class WalletsController { status: ResponseCode.Success, result: true, }) + const walletsService = WalletsService.getInstance() const hash = await walletsService.sendCapacity( params.walletID, params.items, - password, + params.password, params.fee, params.description ) @@ -376,43 +378,6 @@ export default class WalletsController { }, } } - - private static async requestPassword(title: string): Promise { - const password = (await prompt('password', { - title, - })) as string | null - return password - } - - private static async downloadKeystore(id: string, password: string): Promise> { - if (password === '') { - throw new EmptyPassword() - } - const wallet = await walletsService.get(id) - - if (!walletsService.validate({ id, password })) { - throw new IncorrectPassword() - } - - const keystore = wallet.loadKeystore() - return new Promise(resolve => { - AppController.showSaveDialog( - { - title: i18n.t('messages.save-keystore'), - defaultPath: wallet.name, - }, - (filename?: string) => { - if (filename) { - fs.writeFileSync(filename, JSON.stringify(keystore)) - resolve({ - status: ResponseCode.Success, - result: true, - }) - } - } - ) - }) - } } /* eslint-disable */ diff --git a/packages/neuron-wallet/src/models/window-manager.ts b/packages/neuron-wallet/src/models/window-manager.ts index 66a6fd8f39..5cd1c692cd 100644 --- a/packages/neuron-wallet/src/models/window-manager.ts +++ b/packages/neuron-wallet/src/models/window-manager.ts @@ -8,7 +8,7 @@ interface SendMessage { (channel: Channel.App, method: Controller.AppMethod, params: any): void ( channel: Channel.Wallets, - method: Controller.WalletsMethod | 'allAddresses' | 'sendingStatus' | 'getCurrent', + method: Controller.WalletsMethod | 'allAddresses' | 'sendingStatus' | 'getCurrent' | 'requestPassword', params: any ): void (channel: Channel.Transactions, method: Controller.TransactionsMethod | 'transactionUpdated', params: any): void diff --git a/packages/neuron-wallet/src/services/wallets.ts b/packages/neuron-wallet/src/services/wallets.ts index 23a8144945..2916d4dc55 100644 --- a/packages/neuron-wallet/src/services/wallets.ts +++ b/packages/neuron-wallet/src/services/wallets.ts @@ -18,6 +18,8 @@ import AddressDbChangedSubject from '../models/subjects/address-db-changed-subje import AddressesUsedSubject from '../models/subjects/addresses-used-subject' import { WalletListSubject, CurrentWalletSubject } from '../models/subjects/wallets' import { broadcastAddressList } from '../utils/broadcast' +import { Channel, ResponseCode } from '../utils/const' +import windowManager from '../models/window-manager' const { core } = NodeService.getInstance() const fileService = FileService.getInstance() @@ -229,17 +231,17 @@ export default class WalletService { public delete = (id: string) => { const wallets = this.getAll() const walletJSON = wallets.find(w => w.id === id) - const current = this.getCurrent() - const currentID = current ? current.id : '' if (!walletJSON) { throw new WalletNotFound(id) } const wallet = FileKeystoreWallet.fromJSON(walletJSON) - const newWallets = wallets.filter(w => w.id !== id) + const current = this.getCurrent() + const currentID = current ? current.id : '' + if (currentID === id) { if (newWallets.length > 0) { this.setCurrent(newWallets[0].id) @@ -292,21 +294,21 @@ export default class WalletService { } public sendCapacity = async ( - walletID: string, + walletID: string = '', items: { address: string capacity: string - }[], - password: string, + }[] = [], + password: string = '', fee: string = '0', - description?: string + description: string = '' ) => { const wallet = await this.get(walletID) if (!wallet) { throw new CurrentWalletNotSet() } - if (password === undefined || password === '') { + if (password === '') { throw new IsRequired('Password') } @@ -409,4 +411,14 @@ export default class WalletService { privateKey: `0x${masterKeychain.derivePath(path).privateKey.toString('hex')}`, })) } + + public requestPassword = (walletID: string, actionType: 'delete' | 'backup') => { + windowManager.sendToMainWindow(Channel.Wallets, 'requestPassword', { + status: ResponseCode.Success, + result: { + walletID, + actionType, + }, + }) + } } diff --git a/packages/neuron-wallet/src/types/controller/index.d.ts b/packages/neuron-wallet/src/types/controller/index.d.ts index a28c929b8d..c6d318a9c0 100644 --- a/packages/neuron-wallet/src/types/controller/index.d.ts +++ b/packages/neuron-wallet/src/types/controller/index.d.ts @@ -17,6 +17,14 @@ declare module Controller { pageSize: number addresses: string } + interface BackupWallet { + id: string + password: string + } + interface DeleteWallet { + id: string + password: string + } } interface Wallet { diff --git a/packages/neuron-wallet/src/utils/application-menu.ts b/packages/neuron-wallet/src/utils/application-menu.ts index c75d27918b..5862ab82bc 100644 --- a/packages/neuron-wallet/src/utils/application-menu.ts +++ b/packages/neuron-wallet/src/utils/application-menu.ts @@ -3,7 +3,7 @@ import app from '../app' import env from '../env' import i18n from './i18n' import AppController from '../controllers/app' -import WalletsController from '../controllers/wallets' +import WalletsService from '../services/wallets' const separator: MenuItemConstructorOptions = { type: 'separator', @@ -73,18 +73,26 @@ export const walletMenuItem: MenuItemConstructorOptions = { id: 'backup', label: i18n.t('application-menu.wallet.backup'), click: () => { - if (AppController) { - AppController.backupCurrentWallet() + const walletsService = WalletsService.getInstance() + const currentWallet = walletsService.getCurrent() + if (!currentWallet) { + // TODO: show the error message + return } + walletsService.requestPassword(currentWallet.id, 'backup') }, }, { id: 'delete', label: i18n.t('application-menu.wallet.delete'), click: () => { - if (AppController) { - AppController.deleteCurrentWallet() + const walletsService = WalletsService.getInstance() + const currentWallet = walletsService.getCurrent() + if (!currentWallet) { + // TODO: show the error message + return } + walletsService.requestPassword(currentWallet.id, 'delete') }, }, /** @@ -222,9 +230,8 @@ export const updateApplicationMenu = (wallets: Controller.Wallet[], id: string | type: 'radio', checked: wallet.id === id, click: () => { - if (WalletsController) { - WalletsController.activate(wallet.id) - } + const walletsService = WalletsService.getInstance() + walletsService.setCurrent(wallet.id) }, }) ) diff --git a/packages/neuron-wallet/src/utils/prompt/index.ts b/packages/neuron-wallet/src/utils/prompt/index.ts deleted file mode 100644 index f587881ff0..0000000000 --- a/packages/neuron-wallet/src/utils/prompt/index.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { BrowserWindow, ipcMain, BrowserWindowConstructorOptions } from 'electron' -import url from 'url' -import path from 'path' -import i18n from '../i18n' - -const cleanup = (win: BrowserWindow | null) => { - if (win) { - win.close() - } -} - -const passwordLabels = { - label: i18n.t('prompt.password.label'), - submit: i18n.t('prompt.password.submit'), - cancel: i18n.t('prompt.password.cancel'), -} - -const prompt = (type = 'password', browserOptions: BrowserWindowConstructorOptions = {}) => { - let win: BrowserWindow | null = null - - const id = `${Date.now()}${Math.round(Math.random() * 100)}` - const labels = passwordLabels - return new Promise(resolve => { - const options = { - width: 500, - height: 200, - resizable: false, - title: type, - label: 'Please input a value', - alwaysOnTop: false, - value: null, - type: 'inptu', - selectOptions: null, - useHtmlLabel: false, - customStylesheet: null, - show: false, - ...browserOptions, - } - - win = new BrowserWindow({ - ...options, - webPreferences: { - nodeIntegration: true, - }, - }) - - win.on(`ready-to-show`, () => { - if (win) { - win.show() - win.focus() - win.webContents.send(`prompt-init`, labels) - } - }) - - win.setMenu(null) - - const dataHandler = (_e: Event, data: string) => { - cleanup(win) - resolve(data) - } - - const cancelHandler = () => { - cleanup(win) - resolve(null) - } - - ipcMain.on(`prompt-data-${id}`, dataHandler) - ipcMain.on(`prompt-cancel-${id}`, cancelHandler) - - win.on('closed', () => { - ipcMain.removeListener(`prompt-data-${id}`, dataHandler) - ipcMain.removeListener(`prompt-cancel-${id}`, cancelHandler) - }) - - const promptUrl = url.format({ - protocol: 'file', - slashes: true, - pathname: path.join(__dirname, type, 'index.html'), - hash: id, - }) - - win.loadURL(promptUrl) - }) -} - -export default prompt diff --git a/packages/neuron-wallet/src/utils/prompt/password/index.html b/packages/neuron-wallet/src/utils/prompt/password/index.html deleted file mode 100644 index 34fd8ed393..0000000000 --- a/packages/neuron-wallet/src/utils/prompt/password/index.html +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - -
- - -
- - -
-
- - - - diff --git a/packages/neuron-wallet/src/utils/prompt/password/index.ts b/packages/neuron-wallet/src/utils/prompt/password/index.ts deleted file mode 100644 index c7bd6fc397..0000000000 --- a/packages/neuron-wallet/src/utils/prompt/password/index.ts +++ /dev/null @@ -1,25 +0,0 @@ -const { ipcRenderer } = require('electron') - -const form = document.querySelector('form') as any -const id = window.location.hash.slice(1) - -form.addEventListener('submit', (event: any) => { - event.preventDefault() - if (event && event.target) { - const password = event.target.password.value - ipcRenderer.send(`prompt-data-${id}`, password) - } - return false -}) - -form.cancel.addEventListener('click', (event: Event) => { - event.preventDefault() - ipcRenderer.send(`prompt-cancel-${id}`) - return false -}) - -ipcRenderer.on(`prompt-init`, (_e: Event, labels: any) => { - form.password.labels[0].innerText = labels.label - form.submit.value = labels.submit - form.cancel.value = labels.cancel -}) diff --git a/packages/neuron-wallet/tests/services/wallets.test.ts b/packages/neuron-wallet/tests/services/wallets.test.ts index 3c2462e977..e99faa8353 100644 --- a/packages/neuron-wallet/tests/services/wallets.test.ts +++ b/packages/neuron-wallet/tests/services/wallets.test.ts @@ -1,5 +1,3 @@ -import '../../src/controllers/wallets' -import '../../src/controllers/app' import WalletService, { WalletProperties } from '../../src/services/wallets' import { Witness } from '../../src/types/cell-types' import Keystore from '../../src/models/keys/keystore' From 03a3ea71dee7b9271517f4db91ab8d32af7700bc Mon Sep 17 00:00:00 2001 From: James Chen Date: Fri, 12 Jul 2019 10:30:32 +0900 Subject: [PATCH 2/6] chore(deps): Add electron-notarize as dev depencency --- packages/neuron-wallet/package.json | 1 + yarn.lock | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/packages/neuron-wallet/package.json b/packages/neuron-wallet/package.json index f36fa7c18c..549b406fe4 100644 --- a/packages/neuron-wallet/package.json +++ b/packages/neuron-wallet/package.json @@ -58,6 +58,7 @@ "electron": "5.0.6", "electron-builder": "21.0.14", "electron-devtools-installer": "2.2.4", + "electron-notarize": "0.1.1", "lint-staged": "8.1.7", "rimraf": "2.6.3", "spectron": "7.0.0" diff --git a/yarn.lock b/yarn.lock index 602bc77306..fe17c221c3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6936,6 +6936,14 @@ electron-download@^4.1.0, electron-download@^4.1.1: semver "^5.4.1" sumchecker "^2.0.2" +electron-notarize@0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/electron-notarize/-/electron-notarize-0.1.1.tgz#c3563d70c5e7b3315f44e8495b30050a8c408b91" + integrity sha512-TpKfJcz4LXl5jiGvZTs5fbEx+wUFXV5u8voeG5WCHWfY/cdgdD8lDZIZRqLVOtR3VO+drgJ9aiSHIO9TYn/fKg== + dependencies: + debug "^4.1.1" + fs-extra "^8.0.1" + electron-osx-sign@0.4.11: version "0.4.11" resolved "https://registry.yarnpkg.com/electron-osx-sign/-/electron-osx-sign-0.4.11.tgz#8377732fe7b207969f264b67582ee47029ce092f" From 4d92af444616548dfc9999f9a9cf7219c158b8d7 Mon Sep 17 00:00:00 2001 From: James Chen Date: Fri, 12 Jul 2019 11:30:35 +0900 Subject: [PATCH 3/6] chore(package): Set up macOS signing and notarizing Follow guide at https://kilianvalkhof.com/2019/electron/notarizing-your-electron-application/. --- azure-pipelines.yml | 2 ++ packages/neuron-wallet/.eslintignore | 1 + .../neuron-wallet/assets/entitlements.plist | 8 ++++++++ packages/neuron-wallet/electron-builder.yml | 9 +++++++++ packages/neuron-wallet/scripts/notarize.js | 17 +++++++++++++++++ 5 files changed, 37 insertions(+) create mode 100644 packages/neuron-wallet/assets/entitlements.plist create mode 100644 packages/neuron-wallet/scripts/notarize.js diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 1d4970fbde..8712de6bad 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -134,6 +134,8 @@ jobs: CSC_KEY_PASSWORD: $(macSiginingCertificatePassword) WIN_CSC_LINK: $(winSiginingCertificate.secureFilePath) WIN_CSC_KEY_PASSWORD: $(winSiginingCertificatePassword) + APPLE_ID: $(appleId) + APPLE_ID_PASSWORD: $(appleIdPassword) - task: GitHubRelease@0 inputs: gitHubConnection: nervos-bot diff --git a/packages/neuron-wallet/.eslintignore b/packages/neuron-wallet/.eslintignore index 9914c64b73..ed6c873a2d 100644 --- a/packages/neuron-wallet/.eslintignore +++ b/packages/neuron-wallet/.eslintignore @@ -3,3 +3,4 @@ dist src/database/**/migrations jest.config.js jest.e2e.config.js +scripts/notarize.js diff --git a/packages/neuron-wallet/assets/entitlements.plist b/packages/neuron-wallet/assets/entitlements.plist new file mode 100644 index 0000000000..bb87459e76 --- /dev/null +++ b/packages/neuron-wallet/assets/entitlements.plist @@ -0,0 +1,8 @@ + + + + + com.apple.security.cs.allow-unsigned-executable-memory + + + \ No newline at end of file diff --git a/packages/neuron-wallet/electron-builder.yml b/packages/neuron-wallet/electron-builder.yml index 4d38a4a7f2..522ea86ab6 100644 --- a/packages/neuron-wallet/electron-builder.yml +++ b/packages/neuron-wallet/electron-builder.yml @@ -8,6 +8,8 @@ directories: buildResources: . output: ../../release +afterSign: scripts/notarize.js + files: - from: "../.." to: "." @@ -31,6 +33,9 @@ nsis: createDesktopShortcut: always createStartMenuShortcut: true +dmg: + sign: false + win: artifactName: "${productName}-v${version}-${os}-${arch}-installer.${ext}" icon: assets/images/icon.ico @@ -43,6 +48,10 @@ mac: artifactName: "${productName}-v${version}-${os}.${ext}" category: public.app-category.finance icon: assets/images/icon.icns + hardenedRuntime: true + gatekeeperAssess: false + entitlements: assets/entitlements.plist + entitlementsInherit: assets/entitlements.plist target: - dmg - zip diff --git a/packages/neuron-wallet/scripts/notarize.js b/packages/neuron-wallet/scripts/notarize.js new file mode 100644 index 0000000000..0ceebcdd11 --- /dev/null +++ b/packages/neuron-wallet/scripts/notarize.js @@ -0,0 +1,17 @@ +const { notarize } = require('electron-notarize'); + +exports.default = async function notarizing(context) { + const { electronPlatformName, appOutDir } = context; + if (electronPlatformName !== 'darwin') { + return; + } + + const appName = context.packager.appInfo.productFilename; + + return await notarize({ + appBundleId: 'com.nervos.neuron', + appPath: `${appOutDir}/${appName}.app`, + appleId: process.env.APPLE_ID, + appleIdPassword: process.env.APPLE_ID_PASSWORD, + }); +}; \ No newline at end of file From 8f07e61879f437eed199009c1585cf72f1a965d0 Mon Sep 17 00:00:00 2001 From: Keith Date: Fri, 12 Jul 2019 11:04:59 +0800 Subject: [PATCH 4/6] feat(neuron-ui): nav to the networks setting on clicking on the network status --- .../neuron-ui/src/containers/Footer/index.tsx | 44 ++++++++++++------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/packages/neuron-ui/src/containers/Footer/index.tsx b/packages/neuron-ui/src/containers/Footer/index.tsx index c90a25f60f..950b24cdd6 100644 --- a/packages/neuron-ui/src/containers/Footer/index.tsx +++ b/packages/neuron-ui/src/containers/Footer/index.tsx @@ -1,4 +1,4 @@ -import React, { useContext } from 'react' +import React, { useCallback, useContext } from 'react' import { createPortal } from 'react-dom' import { RouteComponentProps } from 'react-router-dom' import { useTranslation } from 'react-i18next' @@ -6,10 +6,21 @@ import { Stack, getTheme, Text, ProgressIndicator } from 'office-ui-fabric-react import { Alert as AlertIcon, Nodes as ConnectIcon } from 'grommet-icons' import { StateWithDispatch } from 'states/stateProvider/reducer' -import { ConnectStatus, FULL_SCREENS } from 'utils/const' +import { ConnectStatus, FULL_SCREENS, Routes } from 'utils/const' import { NeuronWalletContext } from 'states/stateProvider' const theme = getTheme() +const stackStyles = { + root: [ + { + width: '100%', + background: theme.palette.neutralLighter, + }, + ], +} +const stackItemStyles = { + root: [theme.fonts.small], +} // TODO: Listen to sync progress report and update const SyncStatus = () => { @@ -34,27 +45,24 @@ const NetworkStatus = ({ name, online }: { name: string; online: boolean }) => { } // Status bar -const Footer = ({ location: { pathname } }: React.PropsWithoutRef) => { +const Footer = ({ + history, + location: { pathname }, +}: React.PropsWithoutRef) => { const { chain: { networkID, connectStatus }, settings: { networks }, } = useContext(NeuronWalletContext) + const [t] = useTranslation() + + const goToNetworksSetting = useCallback(() => { + history.push(Routes.SettingsNetworks) + }, [history]) if (FULL_SCREENS.find(url => pathname.startsWith(url))) { return null } const currentNetwork = networks.find(network => network.id === networkID) - const stackStyles = { - root: [ - { - width: '100%', - background: theme.palette.neutralLighter, - }, - ], - } - const stackItemStyles = { - root: [theme.fonts.small], - } return ( - + {currentNetwork ? ( - ) : null} - + ) : ( + {t('settings.setting-tabs.network')} + )} + ) } From 122d057a6d0ffd0979573d5273727a7033b6f97c Mon Sep 17 00:00:00 2001 From: classicalliu Date: Fri, 12 Jul 2019 11:14:41 +0800 Subject: [PATCH 5/6] chore: only include pending and success tx count in address --- .../neuron-wallet/src/database/address/dao.ts | 6 ++- .../src/database/chain/ormconfig.ts | 2 +- .../src/services/transactions.ts | 42 ++++++++----------- 3 files changed, 23 insertions(+), 27 deletions(-) diff --git a/packages/neuron-wallet/src/database/address/dao.ts b/packages/neuron-wallet/src/database/address/dao.ts index f64ad177a6..058446f70d 100644 --- a/packages/neuron-wallet/src/database/address/dao.ts +++ b/packages/neuron-wallet/src/database/address/dao.ts @@ -5,6 +5,7 @@ import { getConnection } from './ormconfig' import TransactionsService, { OutputStatus } from '../../services/transactions' import CellsService from '../../services/cells' import LockUtils from '../../models/lock-utils' +import { TransactionStatus } from '../../types/cell-types' export interface Address { walletId: string @@ -55,7 +56,10 @@ export default class AddressDao { address, }) - const txCount: number = await TransactionsService.getCountByAddress(address) + const txCount: number = await TransactionsService.getCountByAddressAndStatus(address, [ + TransactionStatus.Pending, + TransactionStatus.Success, + ]) const entities = await Promise.all( addressEntities.map(async entity => { const addressEntity = entity diff --git a/packages/neuron-wallet/src/database/chain/ormconfig.ts b/packages/neuron-wallet/src/database/chain/ormconfig.ts index c6003ebff2..8c1f67a3d8 100644 --- a/packages/neuron-wallet/src/database/chain/ormconfig.ts +++ b/packages/neuron-wallet/src/database/chain/ormconfig.ts @@ -39,7 +39,7 @@ export const initConnection = async (genesisBlockHash: string) => { try { await getConnection().close() } catch (err) { - logger.log({ level: 'error', message: err.message }) + // do nothing } const connectionOptions = await connectOptions(genesisBlockHash) diff --git a/packages/neuron-wallet/src/services/transactions.ts b/packages/neuron-wallet/src/services/transactions.ts index 9d90c0370d..e7a2590b06 100644 --- a/packages/neuron-wallet/src/services/transactions.ts +++ b/packages/neuron-wallet/src/services/transactions.ts @@ -585,36 +585,28 @@ export default class TransactionsService { return [...new Set(inputBlake160s.concat(outputBlake160s))] } - // tx count with one lockHash - public static getCountByLockHash = async (lockHash: string): Promise => { - const outputs: OutputEntity[] = await getConnection() - .getRepository(OutputEntity) - .createQueryBuilder('output') - .where(`output.lockHash = :lockHash`, { lockHash }) - .select('DISTINCT output.outPointTxHash', 'outPointTxHash') - .getRawMany() - - const outputTxHashes: string[] = outputs.map(output => output.outPointTxHash) - - const inputs: InputEntity[] = await getConnection() - .getRepository(InputEntity) - .createQueryBuilder('input') - .where(`input.lockHash = :lockHash`, { lockHash }) - .select(`DISTINCT input.transactionHash`, 'transactionHash') - .getRawMany() - - const inputTxHashes: string[] = inputs.map((input: any) => input.transactionHash) - - const hashes: string[] = [...new Set(outputTxHashes.concat(inputTxHashes))] - - const count: number = hashes.length + // tx count with one lockHash and status + public static getCountByLockHashAndStatus = async ( + lockHash: string, + status: TransactionStatus[] + ): Promise => { + const count: number = await getConnection() + .getRepository(TransactionEntity) + .createQueryBuilder('tx') + .leftJoinAndSelect('tx.inputs', 'input') + .leftJoinAndSelect('tx.outputs', 'output') + .where(`(input.lockHash = :lockHash OR output.lockHash = :lockHash) AND tx.status IN (:...status)`, { + lockHash, + status, + }) + .getCount() return count } - public static getCountByAddress = async (address: string): Promise => { + public static getCountByAddressAndStatus = async (address: string, status: TransactionStatus[]): Promise => { const lockHash: string = await LockUtils.addressToLockHash(address) - return TransactionsService.getCountByLockHash(lockHash) + return TransactionsService.getCountByLockHashAndStatus(lockHash, status) } public static pendings = async (): Promise => { From 45fab10302e861cefc252e580807bf8fc8c63635 Mon Sep 17 00:00:00 2001 From: James Chen Date: Fri, 12 Jul 2019 12:55:10 +0900 Subject: [PATCH 6/6] chore: Releasing 0.1.0-alpha.6 --- lerna.json | 2 +- package.json | 2 +- packages/neuron-ui/package.json | 2 +- packages/neuron-wallet/package.json | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lerna.json b/lerna.json index 92350e4781..430d6ac6a1 100644 --- a/lerna.json +++ b/lerna.json @@ -2,7 +2,7 @@ "packages": [ "packages/*" ], - "version": "0.1.0-alpha.5", + "version": "0.1.0-alpha.6", "npmClient": "yarn", "useWorkspaces": true } diff --git a/package.json b/package.json index 5a162a9734..b063184689 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "@nervosnetwork/neuron", "productName": "Neuron", "description": "CKB Neuron Wallet", - "version": "0.1.0-alpha.5", + "version": "0.1.0-alpha.6", "private": true, "author": { "name": "Nervos Core Dev", diff --git a/packages/neuron-ui/package.json b/packages/neuron-ui/package.json index 73cfd744d2..18d5d8f4e1 100644 --- a/packages/neuron-ui/package.json +++ b/packages/neuron-ui/package.json @@ -1,6 +1,6 @@ { "name": "@nervosnetwork/neuron-ui", - "version": "0.1.0-alpha.5", + "version": "0.1.0-alpha.6", "private": true, "author": { "name": "Nervos Core Dev", diff --git a/packages/neuron-wallet/package.json b/packages/neuron-wallet/package.json index 549b406fe4..61fa450f33 100644 --- a/packages/neuron-wallet/package.json +++ b/packages/neuron-wallet/package.json @@ -3,7 +3,7 @@ "productName": "Neuron", "description": "CKB Neuron Wallet", "homepage": "https://www.nervos.org/", - "version": "0.1.0-alpha.5", + "version": "0.1.0-alpha.6", "private": true, "author": { "name": "Nervos Core Dev", @@ -48,7 +48,7 @@ }, "devDependencies": { "@nervosnetwork/ckb-types": "0.15.0", - "@nervosnetwork/neuron-ui": "0.1.0-alpha.5", + "@nervosnetwork/neuron-ui": "0.1.0-alpha.6", "@types/async": "3.0.0", "@types/electron-devtools-installer": "2.2.0", "@types/elliptic": "6.4.8",