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:
parent
158ba41670
commit
560048a0cd
@ -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;
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user