From cd7d7e5ef2a695e0a919dbe26b2eae62d201267b Mon Sep 17 00:00:00 2001 From: Keith Date: Tue, 30 Jul 2019 01:25:07 +0800 Subject: [PATCH] feat(neuron-ui): add popping messages on copying and updating --- .../src/components/Overview/index.tsx | 6 +-- .../src/components/Receive/index.tsx | 15 ++++-- .../Notification/Notification.module.scss | 33 +++++++++++++ .../src/containers/Notification/index.tsx | 47 +++++++++++-------- packages/neuron-ui/src/index.tsx | 2 + packages/neuron-ui/src/locales/en.json | 12 ++++- packages/neuron-ui/src/locales/zh.json | 12 ++++- .../neuron-ui/src/states/initStates/app.ts | 1 + .../stateProvider/actionCreators/app.ts | 14 ++++++ .../stateProvider/actionCreators/settings.ts | 8 ++-- .../stateProvider/actionCreators/wallets.ts | 20 ++------ .../src/states/stateProvider/reducer.ts | 21 +++++++++ packages/neuron-ui/src/types/App/index.d.ts | 6 +++ .../neuron-ui/src/types/global/index.d.ts | 2 + .../neuron-ui/src/widgets/QRCode/index.tsx | 7 ++- 15 files changed, 156 insertions(+), 50 deletions(-) create mode 100644 packages/neuron-ui/src/containers/Notification/Notification.module.scss diff --git a/packages/neuron-ui/src/components/Overview/index.tsx b/packages/neuron-ui/src/components/Overview/index.tsx index 1b99a0f414..3a5b1a6acc 100644 --- a/packages/neuron-ui/src/components/Overview/index.tsx +++ b/packages/neuron-ui/src/components/Overview/index.tsx @@ -21,7 +21,7 @@ import { } from 'office-ui-fabric-react' import { StateWithDispatch } from 'states/stateProvider/reducer' -import { updateTransactionList } from 'states/stateProvider/actionCreators' +import { updateTransactionList, addPopup } from 'states/stateProvider/actionCreators' import { showErrorMessage } from 'services/remote' @@ -281,11 +281,11 @@ const Overview = ({ if (defaultAddress) { window.navigator.clipboard.writeText(defaultAddress.identifier) hideMinerInfo() - // TODO: Add notification + addPopup('lock-arg-copid')(dispatch) } else { showErrorMessage(t('messages.error'), t('messages.can-not-find-the-default-address')) } - }, [defaultAddress, t, hideMinerInfo]) + }, [defaultAddress, t, hideMinerInfo, dispatch]) return ( diff --git a/packages/neuron-ui/src/components/Receive/index.tsx b/packages/neuron-ui/src/components/Receive/index.tsx index 2d08248ace..845641fb37 100644 --- a/packages/neuron-ui/src/components/Receive/index.tsx +++ b/packages/neuron-ui/src/components/Receive/index.tsx @@ -5,10 +5,12 @@ import { Stack, Text, TextField, TooltipHost, Modal, FontSizes, IconButton } fro import { StateWithDispatch } from 'states/stateProvider/reducer' import QRCode from 'widgets/QRCode' +import { addPopup } from 'states/stateProvider/actionCreators' const Receive = ({ wallet: { addresses = [] }, match: { params }, + dispatch, }: React.PropsWithoutRef>) => { const [t] = useTranslation() const [showLargeQRCode, setShowLargeQRCode] = useState(false) @@ -23,7 +25,8 @@ const Receive = ({ const copyAddress = useCallback(() => { window.navigator.clipboard.writeText(accountAddress) - }, [accountAddress]) + addPopup('addr-copied')(dispatch) + }, [accountAddress, dispatch]) if (!accountAddress) { return
{t('receive.address-not-found')}
@@ -55,7 +58,13 @@ const Receive = ({
- setShowLargeQRCode(true)} size={256} exportable /> + setShowLargeQRCode(true)} + size={256} + exportable + dispatch={dispatch} + /> @@ -78,7 +87,7 @@ const Receive = ({ - + diff --git a/packages/neuron-ui/src/containers/Notification/Notification.module.scss b/packages/neuron-ui/src/containers/Notification/Notification.module.scss new file mode 100644 index 0000000000..36cfba1bc3 --- /dev/null +++ b/packages/neuron-ui/src/containers/Notification/Notification.module.scss @@ -0,0 +1,33 @@ +.autoDismissMessages { + display: flex; + flex-direction: column; + align-items: flex-end; + position: absolute; + right: 0; + + &>div { + max-width: auto; + margin: 3px; + animation: autoDismiss 2.5s ease-out forwards; + transform-origin: center top; + box-sizing: border-box; + box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.25); + } +} + +@keyframes autoDismiss { + from { + transform: translateX(110%) + } + + 15%, + 85% { + + transform: translateX(0) + } + + + 100% { + transform: translateX(110%) + } +} diff --git a/packages/neuron-ui/src/containers/Notification/index.tsx b/packages/neuron-ui/src/containers/Notification/index.tsx index 40c97aab95..4be4198f60 100644 --- a/packages/neuron-ui/src/containers/Notification/index.tsx +++ b/packages/neuron-ui/src/containers/Notification/index.tsx @@ -5,6 +5,7 @@ import { useTranslation } from 'react-i18next' import { MessageBar, MessageBarType, IconButton } from 'office-ui-fabric-react' import { NeuronWalletContext } from 'states/stateProvider' import { StateWithDispatch, AppActions } from 'states/stateProvider/reducer' +import styles from './Notification.module.scss' const notificationType = (type: 'success' | 'warning' | 'alert') => { switch (type) { @@ -29,7 +30,7 @@ const DismissButton = ({ onDismiss }: { onDismiss: React.MouseEventHandler) => { const { - app: { notifications = [] }, + app: { notifications = [], popups = [] }, } = useContext(NeuronWalletContext) const [t] = useTranslation() const onDismiss = useCallback(() => { @@ -38,28 +39,36 @@ const NoticeContent = ({ dispatch }: React.PropsWithoutRef} - > - {t(notification.content)} - +
+ {notifications.length ? ( + } + > + {t(notification.content)} + + ) : null} +
+ {popups.map(popup => ( + + {t(popup.text)} + + ))} +
+
) } diff --git a/packages/neuron-ui/src/index.tsx b/packages/neuron-ui/src/index.tsx index da87bfd142..1cc1fd40e4 100755 --- a/packages/neuron-ui/src/index.tsx +++ b/packages/neuron-ui/src/index.tsx @@ -5,6 +5,7 @@ import { loadTheme, getTheme } from 'office-ui-fabric-react' import { AddCircle as AddIcon, Alert as AlertIcon, + Checkmark as SuccessIcon, Close as DismissIcon, Copy as CopyIcon, Down as ArrowDownIcon, @@ -58,6 +59,7 @@ const { semanticColors } = theme registerIcons({ icons: { errorbadge: , + completed: , MiniCopy: , Search: , FirstPage: , diff --git a/packages/neuron-ui/src/locales/en.json b/packages/neuron-ui/src/locales/en.json index 2dc3683459..8701c5ac38 100644 --- a/packages/neuron-ui/src/locales/en.json +++ b/packages/neuron-ui/src/locales/en.json @@ -217,7 +217,17 @@ "invalid-mnemonic": "Invalid mnemonic words", "camera-not-available-or-disabled": "Camera is unavailable or disabled", "can-not-find-the-default-address": "Cannot find the default address", - "amount-too-small": "Amount should not be less than 61 CKB" + "amount-too-small": "Amount should not be less than 61 CKB", + "create-wallet-successfully": "Create a wallet successfully", + "import-wallet-successfully": "Import a wallet successfully", + "update-wallet-successfully": "Update the wallet successfully", + "delete-wallet-successfully": "Delete the wallet successfully", + "create-network-successfully": "Create a network successfully", + "update-network-successfully": "Update the network successfully", + "delete-network-successfully": "Delete the network successfully", + "addr-copied": "Address has been copied to the clipboard", + "qrcode-copied": "QR Code has been copied to the clipboard", + "lock-arg-copid": "Lock Arg has been copied to the clipboard" }, "sync": { "syncing": "Syncing", diff --git a/packages/neuron-ui/src/locales/zh.json b/packages/neuron-ui/src/locales/zh.json index 6d78df2623..c73b0ca1f9 100644 --- a/packages/neuron-ui/src/locales/zh.json +++ b/packages/neuron-ui/src/locales/zh.json @@ -217,7 +217,17 @@ "invalid-mnemonic": "助记词不合法", "camera-not-available-or-disabled": "摄像头不可用或被禁用", "can-not-find-the-default-address": "未获得默认地址", - "amount-too-small": "金额应不小于 61 CKB" + "amount-too-small": "金额应不小于 61 CKB", + "create-wallet-successfully": "新建钱包成功", + "import-wallet-successfully": "导入钱包成功", + "update-wallet-successfully": "已更新钱包信息", + "delete-wallet-successfully": "已删除钱包", + "create-network-successfully": "新节点已添加", + "update-network-successfully": "已更新节点信息", + "delete-network-successfully": "节点已删除", + "addr-copied": "地址已复制到剪贴板", + "qrcode-copied": "二维码已复制到剪贴板", + "lock-arg-copid": "Lock Arg 已复制到剪贴板" }, "sync": { "syncing": "同步中", diff --git a/packages/neuron-ui/src/states/initStates/app.ts b/packages/neuron-ui/src/states/initStates/app.ts index cf39544917..60c013c9cf 100644 --- a/packages/neuron-ui/src/states/initStates/app.ts +++ b/packages/neuron-ui/src/states/initStates/app.ts @@ -31,6 +31,7 @@ const appState: State.App = { transactions: null, wizard: null, }, + popups: [], notifications: [], loadings: { sending: false, diff --git a/packages/neuron-ui/src/states/stateProvider/actionCreators/app.ts b/packages/neuron-ui/src/states/stateProvider/actionCreators/app.ts index 43f0d76a85..ad6d14afd3 100644 --- a/packages/neuron-ui/src/states/stateProvider/actionCreators/app.ts +++ b/packages/neuron-ui/src/states/stateProvider/actionCreators/app.ts @@ -85,7 +85,21 @@ export const addNotification = ({ type, content }: { type: 'alert'; content: str }) } +export const addPopup = (text: string) => (dispatch: StateDispatch) => { + dispatch({ + type: AppActions.PopIn, + payload: { text: `messages.${text}`, timestamp: Date.now() }, + }) + setTimeout(() => { + dispatch({ + type: AppActions.PopOut, + payload: null, + }) + }, 3000) +} + export default { initAppState, addNotification, + addPopup, } diff --git a/packages/neuron-ui/src/states/stateProvider/actionCreators/settings.ts b/packages/neuron-ui/src/states/stateProvider/actionCreators/settings.ts index 95a06310c6..2fc4283507 100644 --- a/packages/neuron-ui/src/states/stateProvider/actionCreators/settings.ts +++ b/packages/neuron-ui/src/states/stateProvider/actionCreators/settings.ts @@ -1,7 +1,7 @@ import { createNetwork as createRemoteNetwork, updateNetwork as updateRemoteNetwork } from 'services/remote' import { addressBook } from 'utils/localCache' import { Routes } from 'utils/const' -import { addNotification } from './app' +import { addNotification, addPopup } from './app' import { AppActions, StateDispatch } from '../reducer' @@ -20,6 +20,7 @@ export const createNetwork = (params: Controller.CreateNetworkParams) => (dispat type: AppActions.Ignore, payload: null, }) + addPopup('create-network-successfully')(dispatch) history.push(Routes.SettingsNetworks) } else { addNotification({ type: 'alert', content: res.message.title })(dispatch) @@ -30,10 +31,7 @@ export const createNetwork = (params: Controller.CreateNetworkParams) => (dispat export const updateNetwork = (params: Controller.UpdateNetworkParams) => (dispatch: StateDispatch, history: any) => { updateRemoteNetwork(params).then(res => { if (res.status) { - dispatch({ - type: AppActions.Ignore, - payload: null, - }) + addPopup('update-network-successfully')(dispatch) history.push(Routes.SettingsNetworks) } else { addNotification({ type: 'alert', content: res.message.title })(dispatch) diff --git a/packages/neuron-ui/src/states/stateProvider/actionCreators/wallets.ts b/packages/neuron-ui/src/states/stateProvider/actionCreators/wallets.ts index bb7e7dda7c..84f2af8089 100644 --- a/packages/neuron-ui/src/states/stateProvider/actionCreators/wallets.ts +++ b/packages/neuron-ui/src/states/stateProvider/actionCreators/wallets.ts @@ -16,7 +16,7 @@ import { wallets as walletsCache, currentWallet as currentWalletCache } from 'ut import { Routes } from 'utils/const' import addressesToBalance from 'utils/addressesToBalance' import { NeuronWalletActions } from '../reducer' -import { addNotification } from './app' +import { addNotification, addPopup } from './app' export const updateCurrentWallet = () => (dispatch: StateDispatch) => { getCurrentWallet().then(res => { @@ -39,10 +39,6 @@ export const createWalletWithMnemonic = (params: Controller.ImportMnemonicParams ) => { importMnemonic(params).then(res => { if (res.status) { - dispatch({ - type: AppActions.Ignore, - payload: null, - }) history.push(Routes.Overview) } else { addNotification({ type: 'alert', content: res.message.title })(dispatch) @@ -56,10 +52,6 @@ export const importWalletWithMnemonic = (params: Controller.ImportMnemonicParams ) => { importMnemonic(params).then(res => { if (res.status) { - dispatch({ - type: AppActions.Ignore, - payload: null, - }) history.push(Routes.Overview) } else { addNotification({ type: 'alert', content: res.message.title })(dispatch) @@ -87,10 +79,7 @@ export const updateWalletProperty = (params: Controller.UpdateWalletParams) => ( ) => { updateWallet(params).then(res => { if (res.status) { - dispatch({ - type: AppActions.Ignore, - payload: null, - }) + addPopup('update-wallet-successfully')(dispatch) if (history) { history.push(Routes.SettingsWallets) } @@ -195,10 +184,7 @@ export const deleteWallet = (params: Controller.DeleteWalletParams) => (dispatch }) deleteRemoteWallet(params).then(res => { if (res.status) { - dispatch({ - type: AppActions.Ignore, - payload: null, - }) + addPopup('delete-wallet-successfully')(dispatch) } else { addNotification({ type: 'alert', content: res.message.title })(dispatch) } diff --git a/packages/neuron-ui/src/states/stateProvider/reducer.ts b/packages/neuron-ui/src/states/stateProvider/reducer.ts index 5492b68238..e031c830f6 100644 --- a/packages/neuron-ui/src/states/stateProvider/reducer.ts +++ b/packages/neuron-ui/src/states/stateProvider/reducer.ts @@ -40,6 +40,9 @@ export enum AppActions { UpdateTipBlockNumber = 'updateTipBlockNumber', UpdateChainInfo = 'updateChainInfo', UpdateLoadings = 'updateLoadings', + + PopIn = 'popIn', + PopOut = 'popOut', Ignore = 'ignore', } @@ -453,6 +456,24 @@ export const reducer = ( }, } } + case AppActions.PopIn: { + return { + ...state, + app: { + ...app, + popups: [...app.popups, payload], + }, + } + } + case AppActions.PopOut: { + return { + ...state, + app: { + ...app, + popups: app.popups.slice(1), + }, + } + } default: { return state } diff --git a/packages/neuron-ui/src/types/App/index.d.ts b/packages/neuron-ui/src/types/App/index.d.ts index 156abd1575..95b77c5bc6 100644 --- a/packages/neuron-ui/src/types/App/index.d.ts +++ b/packages/neuron-ui/src/types/App/index.d.ts @@ -60,6 +60,11 @@ declare namespace State { loading: boolean } + interface Popup { + timestamp: Date + text: string + } + interface App { tipBlockNumber: string chain: string @@ -74,6 +79,7 @@ declare namespace State { messages: { [index: string]: Message | null } + popups: Popup[] notifications: Message[] loadings: { sending: boolean diff --git a/packages/neuron-ui/src/types/global/index.d.ts b/packages/neuron-ui/src/types/global/index.d.ts index 8b0d1e71b0..2148d10ccc 100644 --- a/packages/neuron-ui/src/types/global/index.d.ts +++ b/packages/neuron-ui/src/types/global/index.d.ts @@ -19,3 +19,5 @@ declare module '*.json' { const value: any export default value } + +declare module '*.scss' diff --git a/packages/neuron-ui/src/widgets/QRCode/index.tsx b/packages/neuron-ui/src/widgets/QRCode/index.tsx index 6b77bd126f..fd30af1eb6 100644 --- a/packages/neuron-ui/src/widgets/QRCode/index.tsx +++ b/packages/neuron-ui/src/widgets/QRCode/index.tsx @@ -3,6 +3,8 @@ import React, { useEffect, useRef, useCallback } from 'react' import canvg from 'canvg' import { Stack, DefaultButton } from 'office-ui-fabric-react' import { useTranslation } from 'react-i18next' +import { addPopup } from 'states/stateProvider/actionCreators' +import { StateDispatch } from 'states/stateProvider/reducer' const QRCodeImpl = require('qr.js/lib/QRCode') @@ -80,6 +82,7 @@ const QRCode = ({ onQRCodeClick, includeMargin = false, exportable = false, + dispatch, }: { value: string size: number @@ -90,6 +93,7 @@ const QRCode = ({ onQRCodeClick?: React.MouseEventHandler includeMargin?: boolean exportable?: boolean + dispatch: StateDispatch }) => { const [t] = useTranslation() const qrcode = new QRCodeImpl(-1, level) @@ -125,7 +129,8 @@ const QRCode = ({ const dataURL = canvasRef.current.toDataURL('image/png') const img = window.nativeImage.createFromDataURL(dataURL) window.clipboard.writeImage(img) - }, []) + addPopup('qrcode-copied')(dispatch) + }, [dispatch]) useEffect(() => { if (canvasRef.current !== null) {