diff --git a/packages/app-accounts/src/Account.tsx b/packages/app-accounts/src/Account.tsx index 198361f1bc36..6e4f3f019f87 100644 --- a/packages/app-accounts/src/Account.tsx +++ b/packages/app-accounts/src/Account.tsx @@ -6,8 +6,9 @@ import { ActionStatus } from '@polkadot/react-components/Status/types'; import { I18nProps } from '@polkadot/react-components/types'; import React, { useState, useEffect } from 'react'; +import { Label } from 'semantic-ui-react'; import styled from 'styled-components'; -import { AddressCard, AddressInfo, Button, ChainLock, Forget, Menu, Popup } from '@polkadot/react-components'; +import { AddressInfo, AddressSmall, Button, ChainLock, Forget, Icon, InputTags, LinkPolkascan, Menu, Popup, Input } from '@polkadot/react-components'; import { useApi } from '@polkadot/react-hooks'; import keyring from '@polkadot/ui-keyring'; @@ -19,19 +20,27 @@ import translate from './translate'; interface Props extends I18nProps { address: string; + allowTags: string[]; className?: string; + isFavorite: boolean; + toggleFavorite: (address: string) => void; } -function Account ({ address, className, t }: Props): React.ReactElement { +function Account ({ address, allowTags, className, isFavorite, t, toggleFavorite }: Props): React.ReactElement | null { const api = useApi(); + const [tags, setTags] = useState([]); + const [accName, setAccName] = useState(''); const [genesisHash, setGenesisHash] = useState(null); const [isBackupOpen, setIsBackupOpen] = useState(false); + const [isEditingName, setIsEditingName] = useState(false); + const [isEditingTags, setIsEditingTags] = useState(false); const [{ isDevelopment, isEditable, isExternal }, setFlags] = useState({ isDevelopment: false, isEditable: false, isExternal: false }); const [isDeriveOpen, setIsDeriveOpen] = useState(false); const [isForgetOpen, setIsForgetOpen] = useState(false); const [isPasswordOpen, setIsPasswordOpen] = useState(false); const [isSettingPopupOpen, setIsSettingPopupOpen] = useState(false); const [isTransferOpen, setIsTransferOpen] = useState(false); + const [isVisible, setIsVisible] = useState(true); useEffect((): void => { const account = keyring.getAccount(address); @@ -42,14 +51,64 @@ function Account ({ address, className, t }: Props): React.ReactElement { isEditable: (account && !(account.meta.isInjected || account.meta.isHardware)) || false, isExternal: (account && account.meta.isExternal) || false }); + setTags(account?.meta?.tags || []); + setAccName(account?.meta?.name || ''); }, [address]); + useEffect((): void => { + if (allowTags.length === 0) { + setIsVisible(true); + } else { + setIsVisible( + allowTags.reduce((result: boolean, tag: string): boolean => { + return result || tags.includes(tag); + }, false) + ); + } + }, [allowTags, tags]); + + if (!isVisible) { + return null; + } + + const _toggleEditName = (): void => setIsEditingName(!isEditingName); + const _toggleEditTags = (): void => setIsEditingTags(!isEditingTags); const _toggleBackup = (): void => setIsBackupOpen(!isBackupOpen); const _toggleDerive = (): void => setIsDeriveOpen(!isDeriveOpen); const _toggleForget = (): void => setIsForgetOpen(!isForgetOpen); const _togglePass = (): void => setIsPasswordOpen(!isPasswordOpen); const _toggleSettingPopup = (): void => setIsSettingPopupOpen(!isSettingPopupOpen); const _toggleTransfer = (): void => setIsTransferOpen(!isTransferOpen); + const _saveName = (): void => { + _toggleEditName(); + + const meta = { name: accName, whenEdited: Date.now() }; + + if (address) { + try { + const currentKeyring = keyring.getPair(address); + + currentKeyring && keyring.saveAccountMeta(currentKeyring, meta); + } catch (error) { + keyring.saveAddress(address, meta); + } + } + }; + const _saveTags = (): void => { + _toggleEditTags(); + + const meta = { tags, whenEdited: Date.now() }; + + if (address) { + try { + const currentKeyring = keyring.getPair(address); + + currentKeyring && keyring.saveAccountMeta(currentKeyring, meta); + } catch (error) { + keyring.saveAddress(address, meta); + } + } + }; const _onForget = (): void => { if (!address) { return; @@ -76,137 +135,193 @@ function Account ({ address, className, t }: Props): React.ReactElement { setGenesisHash(genesisHash); }; + const _onFavorite = (): void => toggleFavorite(address); - // FIXME It is a bit heavy-handled switching of being editable here completely - // (and removing the tags, however the keyring cannot save these) return ( - -
+ + + + + + + ) + : undefined + } + onClickName={_toggleEditName} + toggle={isEditingName} + value={address} + /> + {isBackupOpen && ( + + )} + {isDeriveOpen && ( + + )} + {isForgetOpen && ( + + )} + {isPasswordOpen && ( + + )} + {isTransferOpen && ( + + )} + + + {isEditingTags + ? ( + + ) + : ( +
+ {tags.length + ? tags.map((tag): React.ReactNode => ( + + )) + : + } +
+ ) + } + + + + + + + + +
- - } - className={className} - isEditable={isEditable} - type='account' - value={address} - withExplorer - withIndexOrAddress={false} - withTags - > - {address && ( - <> - {isBackupOpen && ( - - )} - {isDeriveOpen && ( - - )} - {isForgetOpen && ( - - )} - {isPasswordOpen && ( - - )} - {isTransferOpen && ( - - )} - - )} - -
+ {t('Derive account from source')} + + + {t('Change on-chain nickname')} + + + {t('Create a backup file for this account')} + + + {t("Change this account's password")} + + + {t('Forget this account')} + + {!api.isDevelopment && ( + <> + + + + )} + + + + + + + ); } @@ -215,5 +330,19 @@ export default translate( .accounts--Account-buttons { text-align: right; } + + .tags--toggle { + cursor: pointer; + width: 100%; + min-height: 1.5rem; + + label { + cursor: pointer; + } + } + + .name--input { + width: 16rem; + } ` ); diff --git a/packages/app-accounts/src/Overview.tsx b/packages/app-accounts/src/Overview.tsx index dfb611eb7ed4..62723e39a0d6 100644 --- a/packages/app-accounts/src/Overview.tsx +++ b/packages/app-accounts/src/Overview.tsx @@ -5,11 +5,12 @@ import { I18nProps } from '@polkadot/react-components/types'; import { ComponentProps } from './types'; -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; +import styled from 'styled-components'; import keyring from '@polkadot/ui-keyring'; import { getLedger, isLedger } from '@polkadot/react-api'; -import { useAccounts } from '@polkadot/react-hooks'; -import { Button, CardGrid } from '@polkadot/react-components'; +import { useAccounts, useFavorites } from '@polkadot/react-hooks'; +import { Button, InputTags, Table } from '@polkadot/react-components'; import CreateModal from './modals/Create'; import ImportModal from './modals/Import'; @@ -21,6 +22,10 @@ import translate from './translate'; interface Props extends ComponentProps, I18nProps { } +type SortedAccount = { address: string; isFavorite: boolean }; + +const STORE_FAVS = 'accounts:favorites'; + // query the ledger for the address, adding it to the keyring async function queryLedger (): Promise { const ledger = getLedger(); @@ -34,58 +39,36 @@ async function queryLedger (): Promise { } } -function Overview ({ onStatusChange, t }: Props): React.ReactElement { +function Overview ({ className, onStatusChange, t }: Props): React.ReactElement { const { allAccounts, hasAccounts } = useAccounts(); const [isCreateOpen, setIsCreateOpen] = useState(false); const [isImportOpen, setIsImportOpen] = useState(false); const [isQrOpen, setIsQrOpen] = useState(false); - const emptyScreen = !(isCreateOpen || isImportOpen || isQrOpen) && !hasAccounts; + const [favorites, toggleFavorite] = useFavorites(STORE_FAVS); + const [sortedAccounts, setSortedAccounts] = useState([]); + const [tags, setTags] = useState([]); + + useEffect((): void => { + setSortedAccounts( + allAccounts + .map((address): SortedAccount => ({ address, isFavorite: favorites.includes(address) })) + .sort((a, b): number => + a.isFavorite === b.isFavorite + ? 0 + : b.isFavorite + ? 1 + : -1 + ) + ); + }, [allAccounts, favorites]); const _toggleCreate = (): void => setIsCreateOpen(!isCreateOpen); const _toggleImport = (): void => setIsImportOpen(!isImportOpen); const _toggleQr = (): void => setIsQrOpen(!isQrOpen); return ( - } - buttons={ - -