Skip to content

Commit

Permalink
feat: loading spinner for assets table (#1641)
Browse files Browse the repository at this point in the history
* feat: loading spinner for assets table

* feat: streaming for assets table

* feat: update mock in test

* chore: lint

* fix: fix eslint import rule compat

# Conflicts:
#	eslint.config.js
#	pnpm-lock.yaml

* feat: show indicator on assets table until stream has finished, change indicator icon

* feat: enable non-null assertions and ignore existing errors

* fix: import path and lints
  • Loading branch information
vacekj authored Aug 28, 2024
1 parent a8a5f41 commit ee2e34d
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 24 deletions.
1 change: 1 addition & 0 deletions apps/minifront/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
},
"devDependencies": {
"@chain-registry/types": "^0.45.38",
"@eslint/compat": "^1.1.0",
"@types/lodash": "^4.17.4",
"@types/react": "^18.3.2",
"@types/react-dom": "^18.3.0",
Expand Down
35 changes: 28 additions & 7 deletions apps/minifront/src/components/dashboard/assets-table/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import { BalancesByAccount, groupByAccount, useBalancesResponses } from '../../.
import { AbridgedZQueryState } from '@penumbra-zone/zquery/src/types';
import { shouldDisplay } from '../../../fetchers/balances/should-display';
import { sortByPriorityScore } from '../../../fetchers/balances/by-priority-score';
import { LineWave } from 'react-loader-spinner';
import { cn } from '@penumbra-zone/ui/lib/utils';

const getTradeLink = (balance: BalancesResponse): string => {
const metadata = getMetadataFromBalancesResponseOptional(balance);
Expand All @@ -34,18 +36,26 @@ const byAccountIndex = (a: BalancesByAccount, b: BalancesByAccount) => {

const filteredBalancesByAccountSelector = (
zQueryState: AbridgedZQueryState<BalancesResponse[]>,
): BalancesByAccount[] =>
zQueryState.data
?.filter(shouldDisplay)
.sort(sortByPriorityScore)
.reduce(groupByAccount, [])
.sort(byAccountIndex) ?? [];
): AbridgedZQueryState<BalancesByAccount[]> => {
const data =
zQueryState.data
?.filter(shouldDisplay)
.sort(sortByPriorityScore)
.reduce(groupByAccount, [])
.sort(byAccountIndex) ?? [];
return {
...zQueryState,
data,
};
};

export default function AssetsTable() {
const balancesByAccount = useBalancesResponses({
const balances = useBalancesResponses({
select: filteredBalancesByAccountSelector,
shouldReselect: (before, after) => before?.data !== after.data,
});
const balancesByAccount = balances?.data;
const loading = balances?.loading;

if (balancesByAccount?.length === 0) {
return (
Expand Down Expand Up @@ -114,6 +124,17 @@ export default function AssetsTable() {
</Fragment>
))}
</Table>
{(loading ?? balancesByAccount === undefined) && (
<div className='mt-5 flex w-full flex-col items-center justify-center'>
<LineWave
visible={true}
height='70'
width='70'
color='white'
wrapperClass={cn('mb-5 transition-all duration-300')}
/>
</div>
)}
</div>
);
}
19 changes: 17 additions & 2 deletions apps/minifront/src/fetchers/balances/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ import { AddressIndex } from '@penumbra-zone/protobuf/penumbra/core/keys/v1/keys
import { ViewService } from '@penumbra-zone/protobuf';
import { penumbra } from '../../prax';

interface BalancesProps {
export interface BalancesProps {
accountFilter?: AddressIndex;
assetIdFilter?: AssetId;
}

export const getBalances = ({ accountFilter, assetIdFilter }: BalancesProps = {}): Promise<
BalancesResponse[]
> => {
const req = new BalancesRequest();
const req = new BalancesRequest({});
if (accountFilter) {
req.accountFilter = accountFilter;
}
Expand All @@ -26,3 +26,18 @@ export const getBalances = ({ accountFilter, assetIdFilter }: BalancesProps = {}
const iterable = penumbra.service(ViewService).balances(req);
return Array.fromAsync(iterable);
};

export const getBalancesStream = ({
accountFilter,
assetIdFilter,
}: BalancesProps = {}): AsyncIterable<BalancesResponse> => {
const req = new BalancesRequest();
if (accountFilter) {
req.accountFilter = accountFilter;
}
if (assetIdFilter) {
req.assetIdFilter = assetIdFilter;
}

return penumbra.service(ViewService).balances(req);
};
37 changes: 33 additions & 4 deletions apps/minifront/src/state/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import { BalancesResponse } from '@penumbra-zone/protobuf/penumbra/view/v1/view_
import { ZQueryState, createZQuery } from '@penumbra-zone/zquery';
import { AbridgedZQueryState } from '@penumbra-zone/zquery/src/types';
import { SliceCreator, useStore } from '.';
import { getAllAssets } from '../fetchers/assets';
import { getBalances } from '../fetchers/balances';
import { getStakingTokenMetadata } from '../fetchers/registry';
import { getBalancesStream } from '../fetchers/balances';
import { getAllAssets } from '../fetchers/assets';
import { uint8ArrayToHex } from '@penumbra-zone/types/hex';

/**
* For Noble specifically we need to use a Bech32 encoding rather than Bech32m,
Expand All @@ -33,9 +34,37 @@ export const { stakingTokenMetadata, useStakingTokenMetadata } = createZQuery({
},
});

const getHash = (bal: BalancesResponse) => uint8ArrayToHex(bal.toBinary());

export const { balancesResponses, useBalancesResponses } = createZQuery({
name: 'balancesResponses',
fetch: getBalances,
fetch: getBalancesStream,
stream: () => {
const balanceResponseIdsToKeep = new Set<string>();

return {
onValue: (
prevState: BalancesResponse[] | undefined = [],
balanceResponse: BalancesResponse,
) => {
balanceResponseIdsToKeep.add(getHash(balanceResponse));

const existingIndex = prevState.findIndex(bal => getHash(bal) === getHash(balanceResponse));

// Update any existing items in place, rather than appending
// duplicates.
if (existingIndex >= 0) {
return prevState.toSpliced(existingIndex, 1, balanceResponse);
} else {
return [...prevState, balanceResponse];
}
},

onEnd: (prevState = []) =>
// Discard any balances from a previous stream.
prevState.filter(balanceResponse => balanceResponseIdsToKeep.has(getHash(balanceResponse))),
};
},
getUseStore: () => useStore,
get: state => state.shared.balancesResponses,
set: setter => {
Expand All @@ -61,7 +90,7 @@ export const { assets, useAssets } = createZQuery({

export interface SharedSlice {
assets: ZQueryState<Metadata[]>;
balancesResponses: ZQueryState<BalancesResponse[]>;
balancesResponses: ZQueryState<BalancesResponse[], Parameters<typeof getBalancesStream>>;
stakingTokenMetadata: ZQueryState<Metadata>;
}

Expand Down
21 changes: 11 additions & 10 deletions apps/minifront/src/state/staking/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,10 @@ vi.mock('../../fetchers/registry', async () => ({
}));

vi.mock('../../fetchers/balances', () => ({
getBalances: vi.fn(async () =>
Promise.resolve([
{
getBalancesStream: vi.fn(() => ({
[Symbol.asyncIterator]: async function* () {
await new Promise(resolve => void setTimeout(resolve, 0));
yield {
balanceView: new ValueView({
valueView: {
case: 'knownAssetId',
Expand All @@ -93,8 +94,8 @@ vi.mock('../../fetchers/balances', () => ({
},
},
}),
},
{
};
yield {
balanceView: new ValueView({
valueView: {
case: 'knownAssetId',
Expand All @@ -118,8 +119,8 @@ vi.mock('../../fetchers/balances', () => ({
},
},
}),
},
{
};
yield {
balanceView: new ValueView({
valueView: {
case: 'knownAssetId',
Expand All @@ -142,9 +143,9 @@ vi.mock('../../fetchers/balances', () => ({
},
},
}),
},
]),
),
};
},
})),
}));

vi.mock('../../prax', () => ({
Expand Down
2 changes: 1 addition & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export default tseslint.config(

{
name: 'custom:import-enabled',
plugins: { import: import_ },
plugins: { import: fixupPluginRules(import_) },
settings: { 'import/resolver': { typescript: true } },
rules: {
// be aware this rule doesn't always provide correct fixes. its bad fixes
Expand Down
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit ee2e34d

Please sign in to comment.