Skip to content

Commit 3b68429

Browse files
authored
Merge pull request #561 from Concordium/x-account-selector
Hook up account selector on main page
2 parents 7e47281 + 9b6f443 commit 3b68429

File tree

4 files changed

+123
-84
lines changed

4 files changed

+123
-84
lines changed

packages/browser-wallet/src/popup/popupX/page-layouts/MainLayout/Header/Header.scss

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -161,9 +161,7 @@
161161
&_search-form {
162162
display: flex;
163163

164-
.account-search__form {
165-
width: 100%;
166-
164+
.form-search {
167165
.form-input__field {
168166
height: rem(32px);
169167
padding: rem(10px);
@@ -211,7 +209,9 @@
211209
display: flex;
212210
padding: rem(12px) 0;
213211
align-items: center;
212+
border: unset;
214213
border-bottom: 1px solid $color-grey-3;
214+
background: none;
215215

216216
&:last-child {
217217
border-bottom: none;
@@ -229,22 +229,28 @@
229229

230230
svg {
231231
display: unset;
232+
position: absolute;
233+
left: rem(12px);
232234
}
233235
}
234236
}
235237

236238
.account {
239+
flex: 1;
237240
display: flex;
238241
align-items: center;
239242

243+
.text__additional_small {
244+
text-align: left;
245+
}
246+
240247
svg {
241248
display: none;
242249
margin-right: rem(4px);
243250
}
244251
}
245252

246253
.balance {
247-
margin-left: auto;
248254
min-width: rem(65px);
249255
}
250256

@@ -254,7 +260,6 @@
254260
width: unset;
255261
min-width: rem(100px);
256262
justify-content: flex-end;
257-
margin-left: rem(16px);
258263

259264
.token-icon {
260265
display: flex;
@@ -264,6 +269,11 @@
264269
height: rem(24px);
265270
border-radius: rem(5px);
266271
background-color: $color-main-bg;
272+
273+
img {
274+
max-width: rem(24px);
275+
max-height: rem(24px);
276+
}
267277
}
268278
}
269279
}

packages/browser-wallet/src/popup/popupX/page-layouts/MainLayout/Header/Header.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export default function Header({
4545
<MenuButton setMenuOpen={setMenuOpen} menuOpen={menuOpen} hideMenu={hideMenu} />
4646
</div>
4747
<MenuTiles menuOpen={menuOpen} setMenuOpen={setMenuOpen} />
48-
<AccountSelector showAccountSelector={accountOpen} />
48+
<AccountSelector showAccountSelector={accountOpen} onUpdateSelectedAccount={() => setAccountOpen(false)} />
4949
</div>
5050
);
5151
}

packages/browser-wallet/src/popup/popupX/page-layouts/MainLayout/Header/components/AccountSelector.tsx

Lines changed: 102 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,120 @@
1-
import React from 'react';
1+
import React, { ChangeEvent, useMemo, useState } from 'react';
22
import CarretRight from '@assets/svgX/caret-right.svg';
3-
import Form from '@popup/popupX/shared/Form/Form';
4-
import EthLogo from '@assets/svgX/eth-logo.svg';
5-
import DnwLogo from '@assets/svgX/dnw-logo.svg';
6-
import EureLogo from '@assets/svgX/eure-logo.svg';
7-
import BtcLogo from '@assets/svgX/btc-logo.svg';
83
import ArrowsUpDown from '@assets/svgX/arrows-down-up.svg';
9-
import FormSearch from '@popup/popupX/shared/Form/Search';
4+
import { Search } from '@popup/popupX/shared/Form/Search';
105
import Button from '@popup/popupX/shared/Button';
6+
import { useAtom, useAtomValue } from 'jotai';
7+
import { credentialsAtomWithLoading, selectedAccountAtom } from '@popup/store/account';
8+
import { displayNameAndSplitAddress } from '@popup/shared/utils/account-helpers';
9+
import { useAccountInfo } from '@popup/shared/AccountInfoListenerContext';
10+
import { displayAsCcd } from 'wallet-common-helpers';
11+
import { WalletCredential } from '@shared/storage/types';
12+
import { useTranslation } from 'react-i18next';
13+
import clsx from 'clsx';
14+
import { tokensAtom } from '@popup/store/token';
15+
import Img from '@popup/shared/Img';
1116

12-
type Props = { showAccountSelector: boolean };
17+
function CcdBalance({ credential }: { credential: WalletCredential }) {
18+
const accountInfo = useAccountInfo(credential);
19+
const balance =
20+
accountInfo === undefined ? '' : displayAsCcd(accountInfo.accountAmount.microCcdAmount, false, true);
21+
// eslint-disable-next-line react/jsx-no-useless-fragment
22+
return <>{balance}</>;
23+
}
24+
25+
type Props = { showAccountSelector: boolean; onUpdateSelectedAccount: () => void };
26+
27+
function compareAsc(left: WalletCredential, right: WalletCredential): number {
28+
if (left.credName === '' && right.credName !== '') {
29+
return 1;
30+
}
31+
if (right.credName === '' && left.credName !== '') {
32+
return -1;
33+
}
34+
return left.credName.localeCompare(right.credName) || left.address.localeCompare(right.address);
35+
}
36+
37+
function compareDesc(left: WalletCredential, right: WalletCredential): number {
38+
return compareAsc(right, left);
39+
}
40+
41+
export default function AccountSelector({ showAccountSelector, onUpdateSelectedAccount }: Props) {
42+
const { t } = useTranslation('x', { keyPrefix: 'header.accountSelector' });
43+
const credentialsLoading = useAtomValue(credentialsAtomWithLoading);
44+
const [selectedAccount, setSelectedAccount] = useAtom(selectedAccountAtom);
45+
const [search, setSearch] = useState('');
46+
const [ascSort, setAscSort] = useState(true);
47+
const credentials = credentialsLoading.value ?? [];
48+
const tokens = useAtomValue(tokensAtom);
49+
const filtered = useMemo(
50+
() =>
51+
credentials.filter(
52+
(credential) =>
53+
credential.credName.toLowerCase().includes(search.toLowerCase()) ||
54+
credential.address.toLowerCase().includes(search.toLowerCase())
55+
),
56+
[search, credentials]
57+
);
58+
const sorted = useMemo(() => filtered.sort(ascSort ? compareAsc : compareDesc), [filtered, ascSort]);
59+
const onAccountClick = (address: string) => () => {
60+
setSelectedAccount(address);
61+
onUpdateSelectedAccount();
62+
};
1363

14-
export default function AccountSelector({ showAccountSelector }: Props) {
1564
if (!showAccountSelector) return null;
1665
return (
1766
<div className="main-header__account-selector fade-menu-bg">
1867
<div className="main-header__account-selector_group">
1968
<div className="main-header__account-selector_search-form">
20-
<Form
21-
onSubmit={() => {}}
22-
// formMethods={}
23-
className="account-search__form"
24-
>
25-
{(f) => {
26-
return (
27-
<FormSearch
28-
control={f.control}
29-
name="network"
30-
placeholder="Search by name"
31-
autoFocus
32-
defaultValue="https://whatevertheaddressIs.com"
33-
/>
34-
);
35-
}}
36-
</Form>
37-
<Button.IconText icon={<ArrowsUpDown />} label="Sort A-Z" />
69+
<Search
70+
autoFocus
71+
placeholder={t('searchBy')}
72+
value={search}
73+
onChange={(e: ChangeEvent<HTMLInputElement>) => setSearch(e.target.value)}
74+
/>
75+
<Button.IconText
76+
icon={<ArrowsUpDown />}
77+
label={ascSort ? t('sortAsc') : t('sortDesc')}
78+
onClick={() => setAscSort((a) => !a)}
79+
/>
3880
</div>
3981
<div className="main-header__account-selector_list">
40-
<div className="main-header__account-selector_list-item active">
41-
<div className="account">
42-
<CarretRight />
43-
<span className="text__additional_small">Accout 1 / 6gk...k7o</span>
44-
</div>
45-
<div className="balance">
46-
<span className="text__additional_small">1.2M CCD</span>
47-
</div>
48-
<div className="tokens">
49-
<div className="token-icon">
50-
<EthLogo />
51-
</div>
52-
<div className="token-icon">
53-
<DnwLogo />
54-
</div>
55-
<div className="token-icon">
56-
<EureLogo />
57-
</div>
58-
<div className="token-icon">
59-
<BtcLogo />
60-
</div>
61-
</div>
62-
</div>
63-
<div className="main-header__account-selector_list-item">
64-
<div className="account">
65-
<CarretRight />
66-
<span className="text__additional_small">Accout 2 / 6gk...k7o</span>
67-
</div>
68-
<div className="balance">
69-
<span className="text__additional_small">0.4M CCD</span>
70-
</div>
71-
<div className="tokens">
72-
<div className="token-icon">
73-
<EthLogo />
82+
{sorted.map((credential) => (
83+
<Button.Base
84+
className={clsx('main-header__account-selector_list-item', {
85+
active: credential.address === selectedAccount,
86+
})}
87+
onClick={onAccountClick(credential.address)}
88+
>
89+
<div className="account">
90+
{credential.address === selectedAccount && <CarretRight />}
91+
<span className="text__additional_small">{displayNameAndSplitAddress(credential)}</span>
7492
</div>
75-
<div className="token-icon">
76-
<DnwLogo />
93+
<div className="balance">
94+
<span className="text__additional_small">
95+
<CcdBalance credential={credential} />
96+
</span>
7797
</div>
78-
</div>
79-
</div>
80-
<div className="main-header__account-selector_list-item">
81-
<div className="account">
82-
<CarretRight />
83-
<span className="text__additional_small">Accout 3 / 6gk...k7o</span>
84-
</div>
85-
<div className="balance">
86-
<span className="text__additional_small">200k CCD</span>
87-
</div>
88-
<div className="tokens">
89-
<div className="token-icon">
90-
<EthLogo />
98+
<div className="tokens">
99+
{tokens.loading ||
100+
Object.values(tokens.value[credential.address]).flatMap((contractTokens) =>
101+
contractTokens.flatMap((token) =>
102+
token.metadata.thumbnail?.url === undefined
103+
? []
104+
: [
105+
<div className="token-icon">
106+
<Img
107+
src={token.metadata.thumbnail.url}
108+
alt={token.metadata.symbol ?? '?'}
109+
withDefaults
110+
/>
111+
</div>,
112+
]
113+
)
114+
)}
91115
</div>
92-
</div>
93-
</div>
116+
</Button.Base>
117+
))}
94118
</div>
95119
</div>
96120
</div>

packages/browser-wallet/src/popup/popupX/page-layouts/MainLayout/Header/i18n/en.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ const t = {
1111
restore: 'Restore',
1212
oldUI: 'Old UI',
1313
},
14+
accountSelector: {
15+
sortAsc: 'Sort A-Z',
16+
sortDesc: 'Sort Z-A',
17+
searchBy: 'Search by name or address',
18+
},
1419
};
1520

1621
export default t;

0 commit comments

Comments
 (0)