mirror of
https://github.com/pezkuwichain/pezkuwi-apps.git
synced 2026-11-02 06:31:07 +00:00
- dev ^0.86.4 at the root and in packages/apps; directives renamed by pezkuwi-dev-migrate-eslint10. - eslint --fix with the 0.86.4 config; one fix broke syntax (typescript-eslint prefer-optional-chain turned `(isStarted && onStart) && onStart()` into `(isStarted && onStart?.();` in TxButton) and is corrected by hand. - The remaining findings fixed rather than disabled: optional chains (each suggestion reviewed), `cause` on rethrown errors, `if` instead of `a && promise` in effects, parallel instead of sequential awaits in Promise.all, Error rejections, no `delete` on arrays (the storage query cache is a Map; an upload's file list no longer keeps a hole), no `[object Object]` stringification (TFunction's second argument is text or options), dead code removed (referenda's never-shown expand button, react-query's unused CoreDescriptor).
119 lines
3.9 KiB
TypeScript
119 lines
3.9 KiB
TypeScript
// Copyright 2017-2026 @pezkuwi/app-accounts authors & contributors
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
import type { ActionStatus } from '@pezkuwi/react-components/Status/types';
|
|
import type { AccountId, AccountIndex, Address } from '@pezkuwi/types/interfaces';
|
|
import type { CreateResult } from '@pezkuwi/ui-keyring/types';
|
|
import type { AccountBalance, SortedAccount } from './types.js';
|
|
|
|
import FileSaver from 'file-saver';
|
|
import React from 'react';
|
|
|
|
import { getEnvironment } from '@pezkuwi/react-api/util';
|
|
import { InputAddress, Menu } from '@pezkuwi/react-components';
|
|
import { getAddressMeta } from '@pezkuwi/react-components/util';
|
|
import { BN_ZERO } from '@pezkuwi/util';
|
|
|
|
export function createMenuGroup (key: string, items: (React.ReactNode | false | undefined | null)[], header?: string): React.ReactNode | null {
|
|
const filtered = items.filter((e): e is React.ReactNode => !!e);
|
|
|
|
return filtered.length
|
|
? (
|
|
<React.Fragment key={key}>
|
|
<Menu.Divider />
|
|
{header && (
|
|
<Menu.Header>{header}</Menu.Header>
|
|
)}
|
|
{filtered}
|
|
</React.Fragment>
|
|
)
|
|
: null;
|
|
}
|
|
|
|
export type AccountIdIsh = AccountId | AccountIndex | Address | string | Uint8Array | null;
|
|
|
|
export function downloadAccount ({ json, pair }: CreateResult): void {
|
|
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
FileSaver.saveAs(
|
|
new Blob([JSON.stringify(json)], { type: 'application/json; charset=utf-8' }),
|
|
`${pair.address}.json`
|
|
);
|
|
}
|
|
|
|
export function tryCreateAccount (commitAccount: () => CreateResult, success: string): ActionStatus {
|
|
const status: ActionStatus = {
|
|
action: 'create',
|
|
message: success,
|
|
status: 'success'
|
|
};
|
|
|
|
try {
|
|
const result = commitAccount();
|
|
const address = result.pair.address;
|
|
|
|
status.account = address;
|
|
|
|
if (getEnvironment() === 'web') {
|
|
downloadAccount(result);
|
|
}
|
|
|
|
downloadAccount(result);
|
|
InputAddress.setLastValue('account', address);
|
|
} catch (error) {
|
|
status.status = 'error';
|
|
status.message = (error as Error).message;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
export const SORT_CATEGORY = ['parent', 'name', 'date', 'balances'] as const;
|
|
|
|
export type SortCategory = typeof SORT_CATEGORY[number];
|
|
|
|
function comparator (accountsMap: Record<string, SortedAccount>, balances: Record<string, AccountBalance | undefined>, category: SortCategory, fromMax: boolean): (a: SortedAccount, b: SortedAccount) => number {
|
|
function accountQualifiedName (account: SortedAccount | undefined): string {
|
|
if (account) {
|
|
const parent = (account.account?.meta.parentAddress || '');
|
|
|
|
return accountQualifiedName(accountsMap[parent]) + account.address;
|
|
} else {
|
|
return '';
|
|
}
|
|
}
|
|
|
|
function make <T> (getValue: (acc: SortedAccount) => T, compare: (a: T, b: T) => number) {
|
|
const mult = fromMax ? 1 : -1;
|
|
|
|
return (a: SortedAccount, b: SortedAccount) =>
|
|
mult * compare(getValue(a), getValue(b));
|
|
}
|
|
|
|
switch (category) {
|
|
case 'parent':
|
|
// We need to ensure that parent will be adjacent to its children
|
|
// so we use format '...<grand-parent-addr><parent-addr><account-addr>'
|
|
return make(accountQualifiedName, (a, b) => a.localeCompare(b));
|
|
|
|
case 'name':
|
|
return make((acc) => getAddressMeta(acc.address).name ?? '', (a, b) => a.localeCompare(b));
|
|
|
|
case 'date':
|
|
return make((acc) => acc.account?.meta.whenCreated ?? 0, (a, b) => a - b);
|
|
|
|
case 'balances':
|
|
return make((acc) => balances[acc.address]?.total ?? BN_ZERO, (a, b) => a.cmp(b));
|
|
}
|
|
}
|
|
|
|
export function sortAccounts (accountsList: SortedAccount[], accountsMap: Record<string, SortedAccount>, balances: Record<string, AccountBalance>, by: SortCategory, fromMax: boolean): SortedAccount[] {
|
|
return [...accountsList]
|
|
.sort(comparator(accountsMap, balances, by, fromMax))
|
|
.sort((a, b) =>
|
|
a.isFavorite === b.isFavorite
|
|
? 0
|
|
: b.isFavorite
|
|
? 1
|
|
: -1);
|
|
}
|