Filter accounts/addresses by tag or name (#2102)

* Filter accounts/addresses by tag or name

* Filter identity names (as available)

* Adjust filter input label
This commit is contained in:
Jaco Greeff 2020-01-09 17:54:22 +01:00 committed by GitHub
parent 158ba41670
commit 560048a0cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 78 additions and 37 deletions

View File

@ -2,13 +2,14 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { DeriveAccountInfo } from '@polkadot/api-derive/types';
import { ActionStatus } from '@polkadot/react-components/Status/types';
import React, { useState, useEffect } from 'react';
import { Label } from 'semantic-ui-react';
import styled from 'styled-components';
import { AddressInfo, AddressSmall, Button, ChainLock, Forget, Icon, InputTags, LinkPolkascan, Menu, Popup, Input } from '@polkadot/react-components';
import { useApi, useToggle } from '@polkadot/react-hooks';
import { useApi, useCall, useToggle } from '@polkadot/react-hooks';
import keyring from '@polkadot/ui-keyring';
import Backup from './modals/Backup';
@ -20,15 +21,16 @@ import { useTranslation } from './translate';
interface Props {
address: string;
allowTags: string[];
className?: string;
filter: string;
isFavorite: boolean;
toggleFavorite: (address: string) => void;
}
function Account ({ address, allowTags, className, isFavorite, toggleFavorite }: Props): React.ReactElement<Props> | null {
function Account ({ address, className, filter, isFavorite, toggleFavorite }: Props): React.ReactElement<Props> | null {
const { t } = useTranslation();
const api = useApi();
const info = useCall<DeriveAccountInfo>(api.api.derive.accounts.info as any, [address]);
const [tags, setTags] = useState<string[]>([]);
const [accName, setAccName] = useState('');
const [genesisHash, setGenesisHash] = useState<string | null>(null);
@ -46,6 +48,18 @@ function Account ({ address, allowTags, className, isFavorite, toggleFavorite }:
const _setTags = (tags: string[]): void => setTags(tags.sort());
useEffect((): void => {
const { identity, nickname } = info || {};
if (api.api.query.identity?.identityOf) {
if (identity?.display) {
setAccName(identity.display);
}
} else if (nickname) {
setAccName(nickname);
}
}, [info]);
useEffect((): void => {
const account = keyring.getAccount(address);
@ -60,16 +74,18 @@ function Account ({ address, allowTags, className, isFavorite, toggleFavorite }:
}, [address]);
useEffect((): void => {
if (allowTags.length === 0) {
if (filter.length === 0) {
setIsVisible(true);
} else {
const _filter = filter.toLowerCase();
setIsVisible(
allowTags.reduce((result: boolean, tag: string): boolean => {
return result || tags.includes(tag);
}, false)
tags.reduce((result: boolean, tag: string): boolean => {
return result || tag.toLowerCase().includes(_filter);
}, accName.toLowerCase().includes(_filter))
);
}
}, [allowTags, tags]);
}, [accName, filter, tags]);
if (!isVisible) {
return null;

View File

@ -9,7 +9,7 @@ import styled from 'styled-components';
import keyring from '@polkadot/ui-keyring';
import { getLedger, isLedger } from '@polkadot/react-api';
import { useAccounts, useFavorites } from '@polkadot/react-hooks';
import { Button, InputTags, Table } from '@polkadot/react-components';
import { Button, Input, Table } from '@polkadot/react-components';
import CreateModal from './modals/Create';
import ImportModal from './modals/Import';
@ -43,7 +43,7 @@ function Overview ({ className, onStatusChange }: Props): React.ReactElement<Pro
const [isQrOpen, setIsQrOpen] = useState(false);
const [favorites, toggleFavorite] = useFavorites(STORE_FAVS);
const [sortedAccounts, setSortedAccounts] = useState<SortedAccount[]>([]);
const [tags, setTags] = useState<string[]>([]);
const [filter, setFilter] = useState<string>('');
useEffect((): void => {
setSortedAccounts(
@ -121,12 +121,11 @@ function Overview ({ className, onStatusChange }: Props): React.ReactElement<Pro
? (
<>
<div className='filter--tags'>
<InputTags
allowAdd={false}
label={t('filter by tags')}
onChange={setTags}
defaultValue={tags}
value={tags}
<Input
isFull
label={t('filter by name or tags')}
onChange={setFilter}
value={filter}
/>
</div>
<Table>
@ -134,7 +133,7 @@ function Overview ({ className, onStatusChange }: Props): React.ReactElement<Pro
{sortedAccounts.map(({ address, isFavorite }): React.ReactNode => (
<Account
address={address}
allowTags={tags}
filter={filter}
isFavorite={isFavorite}
key={address}
toggleFavorite={toggleFavorite}

View File

@ -2,6 +2,7 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { DeriveAccountInfo } from '@polkadot/api-derive/types';
import { KeyringAddress } from '@polkadot/ui-keyring/types';
import { ActionStatus } from '@polkadot/react-components/Status/types';
@ -9,7 +10,7 @@ import React, { useEffect, useState } from 'react';
import { Label } from 'semantic-ui-react';
import styled from 'styled-components';
import { AddressSmall, AddressInfo, Button, ChainLock, Icon, InputTags, Input, LinkPolkascan, Forget, Menu, Popup } from '@polkadot/react-components';
import { useApi } from '@polkadot/react-hooks';
import { useApi, useCall } from '@polkadot/react-hooks';
import keyring from '@polkadot/ui-keyring';
import Transfer from '@polkadot/app-accounts/modals/Transfer';
@ -17,8 +18,8 @@ import { useTranslation } from './translate';
interface Props {
address: string;
allowTags: string[];
className?: string;
filter: string;
isFavorite: boolean;
toggleFavorite: (address: string) => void;
}
@ -28,9 +29,10 @@ const WITH_EXTENDED = { nonce: true };
const isEditable = true;
function Address ({ address, allowTags, className, isFavorite, toggleFavorite }: Props): React.ReactElement<Props> | null {
function Address ({ address, className, filter, isFavorite, toggleFavorite }: Props): React.ReactElement<Props> | null {
const { t } = useTranslation();
const api = useApi();
const info = useCall<DeriveAccountInfo>(api.api.derive.accounts.info as any, [address]);
const [tags, setTags] = useState<string[]>([]);
const [accName, setAccName] = useState('');
const [current, setCurrent] = useState<KeyringAddress | null>(null);
@ -44,6 +46,18 @@ function Address ({ address, allowTags, className, isFavorite, toggleFavorite }:
const _setTags = (tags: string[]): void => setTags(tags.sort());
useEffect((): void => {
const { identity, nickname } = info || {};
if (api.api.query.identity?.identityOf) {
if (identity?.display) {
setAccName(identity.display);
}
} else if (nickname) {
setAccName(nickname);
}
}, [info]);
useEffect((): void => {
const current = keyring.getAddress(address);
@ -59,16 +73,18 @@ function Address ({ address, allowTags, className, isFavorite, toggleFavorite }:
}, [address]);
useEffect((): void => {
if (allowTags.length === 0) {
if (filter.length === 0) {
setIsVisible(true);
} else {
const _filter = filter.toLowerCase();
setIsVisible(
allowTags.reduce((result: boolean, tag: string): boolean => {
return result || tags.includes(tag);
}, false)
tags.reduce((result: boolean, tag: string): boolean => {
return result || tag.toLowerCase().includes(_filter);
}, accName.toLowerCase().includes(_filter))
);
}
}, [allowTags, tags]);
}, [accName, filter, tags]);
if (!isVisible) {
return null;

View File

@ -6,7 +6,7 @@ import { ComponentProps as Props } from './types';
import React, { useEffect, useState } from 'react';
import styled from 'styled-components';
import { Button, InputTags, Table } from '@polkadot/react-components';
import { Button, Input, Table } from '@polkadot/react-components';
import { useAddresses, useFavorites } from '@polkadot/react-hooks';
import CreateModal from './modals/Create';
@ -23,7 +23,7 @@ function Overview ({ className, onStatusChange }: Props): React.ReactElement<Pro
const [isCreateOpen, setIsCreateOpen] = useState(false);
const [favorites, toggleFavorite] = useFavorites(STORE_FAVS);
const [sortedAddresses, setSortedAddresses] = useState<SortedAddress[]>([]);
const [tags, setTags] = useState<string[]>([]);
const [filter, setFilter] = useState<string>('');
useEffect((): void => {
setSortedAddresses(
@ -61,12 +61,11 @@ function Overview ({ className, onStatusChange }: Props): React.ReactElement<Pro
? (
<>
<div className='filter--tags'>
<InputTags
allowAdd={false}
label={t('filter by tags')}
onChange={setTags}
defaultValue={tags}
value={tags}
<Input
isFull
label={t('filter by name or tags')}
onChange={setFilter}
value={filter}
/>
</div>
<Table>
@ -74,7 +73,7 @@ function Overview ({ className, onStatusChange }: Props): React.ReactElement<Pro
{sortedAddresses.map(({ address, isFavorite }): React.ReactNode => (
<Address
address={address}
allowTags={tags}
filter={filter}
isFavorite={isFavorite}
key={address}
toggleFavorite={toggleFavorite}

View File

@ -22,6 +22,7 @@ interface Props extends BareProps {
isDisabled?: boolean;
isEditable?: boolean;
isError?: boolean;
isFull?: boolean;
isHidden?: boolean;
isReadOnly?: boolean;
label?: React.ReactNode;
@ -87,7 +88,7 @@ const isSelectAll = (key: string, isPreKeyDown: boolean): boolean =>
let counter = 0;
export default function Input ({ autoFocus = false, children, className, defaultValue, help, icon, isEditable = false, isAction = false, isDisabled = false, isError = false, isHidden = false, isReadOnly = false, label, labelExtra, max, maxLength, min, name, onBlur, onChange, onEnter, onEscape, onKeyDown, onKeyUp, onPaste, placeholder, style, tabIndex, type = 'text', value, withEllipsis, withLabel }: Props): React.ReactElement<Props> {
export default function Input ({ autoFocus = false, children, className, defaultValue, help, icon, isEditable = false, isAction = false, isDisabled = false, isError = false, isFull = false, isHidden = false, isReadOnly = false, label, labelExtra, max, maxLength, min, name, onBlur, onChange, onEnter, onEscape, onKeyDown, onKeyUp, onPaste, placeholder, style, tabIndex, type = 'text', value, withEllipsis, withLabel }: Props): React.ReactElement<Props> {
const [stateName] = useState(`in_${counter++}_at_${Date.now()}`);
const _onBlur = (): void => {
@ -120,6 +121,7 @@ export default function Input ({ autoFocus = false, children, className, default
<Labelled
className={className}
help={help}
isFull={isFull}
label={label}
labelExtra={labelExtra}
style={style}

View File

@ -13,6 +13,7 @@ import { classes } from './util';
interface Props extends BareProps {
help?: React.ReactNode;
isHidden?: boolean;
isFull?: boolean;
isOuter?: boolean;
isSmall?: boolean;
label?: React.ReactNode;
@ -64,6 +65,14 @@ const Wrapper = styled.div`
}
}
&.label-full {
padding-left: 0;
> label {
left: 1.55rem;
}
}
.labelExtra {
color: rgba(78, 78, 78, .85);
font-weight: 100;
@ -123,7 +132,7 @@ const Wrapper = styled.div`
}
`;
export default function Labelled ({ className, children, help, isHidden, isOuter, isSmall, label = defaultLabel, labelExtra, style, withEllipsis, withLabel = true }: Props): React.ReactElement<Props> | null {
export default function Labelled ({ className, children, help, isFull, isHidden, isOuter, isSmall, label = defaultLabel, labelExtra, style, withEllipsis, withLabel = true }: Props): React.ReactElement<Props> | null {
if (isHidden) {
return null;
} else if (!withLabel) {
@ -134,7 +143,7 @@ export default function Labelled ({ className, children, help, isHidden, isOuter
return (
<Wrapper
className={classes('ui--Labelled', isSmall && 'label-small', isOuter && 'label-outer', className)}
className={classes('ui--Labelled', isSmall && 'label-small', isFull && 'label-full', isOuter && 'label-outer', className)}
style={style}
>
<label>