mirror of
https://github.com/pezkuwichain/pezkuwi-apps.git
synced 2026-06-13 18:41:07 +00:00
feat: initial Pezkuwi Apps rebrand from polkadot-apps
Rebranded terminology: - Polkadot → Pezkuwi - Kusama → Dicle - Westend → Zagros - Rococo → PezkuwiChain - Substrate → Bizinikiwi - parachain → teyrchain Custom logos with Kurdistan brand colors (#e6007a → #86e62a): - bizinikiwi-hexagon.svg - sora-bizinikiwi.svg - hezscanner.svg - heztreasury.svg - pezkuwiscan.svg - pezkuwistats.svg - pezkuwiassembly.svg - pezkuwiholic.svg
This commit is contained in:
@@ -0,0 +1 @@
|
||||
# @pezkuwi/app-collator
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"bugs": "https://github.com/pezkuwichain/pezkuwi-apps/issues",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"homepage": "https://github.com/pezkuwichain/pezkuwi-apps/tree/master/packages/page-collator#readme",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@pezkuwi/app-collator",
|
||||
"private": true,
|
||||
"repository": {
|
||||
"directory": "packages/page-collator",
|
||||
"type": "git",
|
||||
"url": "https://github.com/pezkuwichain/pezkuwi-apps.git"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"type": "module",
|
||||
"version": "0.168.2-4-x",
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
"react-dom": "*",
|
||||
"react-is": "*"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copyright 2017-2025 @pezkuwi/app-collator authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Collator as CollatorType } from './types.js';
|
||||
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
import { AddressSmall, Badge, Table } from '@pezkuwi/react-components';
|
||||
import { BalanceFree } from '@pezkuwi/react-query';
|
||||
import { formatNumber } from '@pezkuwi/util';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
info: CollatorType;
|
||||
lastBlock?: string;
|
||||
}
|
||||
|
||||
function Collator ({ className, info: { accountId, deposit, isInvulnerable, lastBlock: lastStateBlock }, lastBlock: lastGlobalBlock }: Props): React.ReactElement<Props> {
|
||||
const lastBlock = useMemo(
|
||||
() => lastStateBlock
|
||||
? formatNumber(lastStateBlock)
|
||||
: lastGlobalBlock,
|
||||
[lastStateBlock, lastGlobalBlock]
|
||||
);
|
||||
|
||||
return (
|
||||
<tr className={className}>
|
||||
<td className='badge number'>
|
||||
{isInvulnerable && (
|
||||
<Badge
|
||||
color='green'
|
||||
icon='shield'
|
||||
/>
|
||||
)}
|
||||
</td>
|
||||
<td className='address all'>
|
||||
<AddressSmall value={accountId} />
|
||||
</td>
|
||||
<Table.Column.Balance value={deposit} />
|
||||
<td className='number'>
|
||||
<BalanceFree params={accountId} />
|
||||
</td>
|
||||
<td className='number'>
|
||||
{lastBlock}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Collator);
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright 2017-2025 @pezkuwi/app-collator authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import React, { useRef } from 'react';
|
||||
|
||||
import { Table } from '@pezkuwi/react-components';
|
||||
import { useBlockAuthors } from '@pezkuwi/react-hooks';
|
||||
|
||||
import Collator from './Collator.js';
|
||||
import Summary from './Summary.js';
|
||||
import { useTranslation } from './translate.js';
|
||||
import useCollators from './useCollators.js';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
function Collators ({ className }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const collators = useCollators();
|
||||
const { byAuthor } = useBlockAuthors();
|
||||
|
||||
const hdrRef = useRef<([React.ReactNode?, string?, number?] | false)[]>([
|
||||
[t('collators'), 'start', 2],
|
||||
[t('deposit'), 'number'],
|
||||
[t('balance'), 'number'],
|
||||
[t('last #'), 'number']
|
||||
]);
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<Summary />
|
||||
<Table
|
||||
empty={collators && t('No running collators')}
|
||||
header={hdrRef.current}
|
||||
>
|
||||
{collators?.map((c) => (
|
||||
<Collator
|
||||
info={c}
|
||||
key={c.accountId}
|
||||
lastBlock={byAuthor[c.accountId]}
|
||||
/>
|
||||
))}
|
||||
</Table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Collators);
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright 2017-2025 @pezkuwi/app-collator authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { BN } from '@pezkuwi/util';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { CardSummary, SummaryBox } from '@pezkuwi/react-components';
|
||||
import { useApi, useCall } from '@pezkuwi/react-hooks';
|
||||
import { formatNumber } from '@pezkuwi/util';
|
||||
|
||||
import { useTranslation } from './translate.js';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
function Summary ({ className }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const desiredCandidates = useCall<BN>(api.query.collatorSelection.desiredCandidates);
|
||||
|
||||
return (
|
||||
<SummaryBox className={className}>
|
||||
<section>
|
||||
{desiredCandidates && (
|
||||
<CardSummary label={t('desired')}>
|
||||
{formatNumber(desiredCandidates)}
|
||||
</CardSummary>
|
||||
)}
|
||||
</section>
|
||||
</SummaryBox>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Summary);
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright 2017-2025 @pezkuwi/app-collator authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import React, { useRef } from 'react';
|
||||
|
||||
import { Tabs } from '@pezkuwi/react-components';
|
||||
|
||||
import Collators from './Collators.js';
|
||||
import { useTranslation } from './translate.js';
|
||||
|
||||
interface Props {
|
||||
basePath: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
function CollatorApp ({ basePath, className }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const itemsRef = useRef([
|
||||
{
|
||||
isRoot: true,
|
||||
name: 'index',
|
||||
text: t('Overview')
|
||||
}
|
||||
]);
|
||||
|
||||
return (
|
||||
<main className={className}>
|
||||
<Tabs
|
||||
basePath={basePath}
|
||||
items={itemsRef.current}
|
||||
/>
|
||||
<Collators />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(CollatorApp);
|
||||
@@ -0,0 +1,8 @@
|
||||
// Copyright 2017-2025 @pezkuwi/app-collator authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { useTranslation as useTranslationBase } from 'react-i18next';
|
||||
|
||||
export function useTranslation (): { t: (key: string, options?: { replace: Record<string, unknown> }) => string } {
|
||||
return useTranslationBase('app-collator');
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright 2017-2025 @pezkuwi/app-collator authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { BN } from '@pezkuwi/util';
|
||||
|
||||
export interface Collator {
|
||||
accountId: string;
|
||||
deposit?: BN;
|
||||
isInvulnerable: boolean;
|
||||
lastBlock?: BN;
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
// Copyright 2017-2025 @pezkuwi/app-collator authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { BTreeSet } from '@pezkuwi/types';
|
||||
import type { AccountId32, Balance } from '@pezkuwi/types/interfaces';
|
||||
import type { BN } from '@pezkuwi/util';
|
||||
import type { Collator } from './types.js';
|
||||
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import { createNamedHook, useApi, useCall } from '@pezkuwi/react-hooks';
|
||||
|
||||
type Authored = Record<string, BN>;
|
||||
|
||||
interface PalletCollatorSelectionCandidateInfo {
|
||||
deposit: Balance;
|
||||
who: AccountId32;
|
||||
}
|
||||
|
||||
const OPT_INV = {
|
||||
transform: (invulnerables: AccountId32[]) =>
|
||||
invulnerables.map((accountId32): Collator => ({
|
||||
accountId: accountId32.toString(),
|
||||
isInvulnerable: true
|
||||
}))
|
||||
};
|
||||
|
||||
const OPT_CAN = {
|
||||
transform: (candidates: PalletCollatorSelectionCandidateInfo[] | BTreeSet) =>
|
||||
Array.isArray(candidates)
|
||||
? candidates.map(({ deposit, who }): Collator => ({
|
||||
accountId: who.toString(),
|
||||
deposit,
|
||||
isInvulnerable: false
|
||||
}))
|
||||
: candidates.strings.map((accountId): Collator => ({
|
||||
accountId,
|
||||
isInvulnerable: false
|
||||
}))
|
||||
};
|
||||
|
||||
const OPT_AUT = {
|
||||
transform: ([[accountIds], lastAuthoredBlocks]: [[string[]], BN[]]) =>
|
||||
accountIds.reduce<Authored>((map, accountId, index): Authored => ({
|
||||
...map,
|
||||
[accountId]: lastAuthoredBlocks[index]
|
||||
}), {}),
|
||||
withParamsTransform: true
|
||||
};
|
||||
|
||||
function includeAuthors (prev: Collator[], authored: Authored): Collator[] {
|
||||
let hasDiff = false;
|
||||
const adjusted = prev.map((c): Collator => {
|
||||
if (authored[c.accountId] && (!c.lastBlock || !authored[c.accountId].eq(c.lastBlock))) {
|
||||
hasDiff = true;
|
||||
|
||||
return { ...c, lastBlock: authored[c.accountId] };
|
||||
}
|
||||
|
||||
return c;
|
||||
});
|
||||
|
||||
return hasDiff
|
||||
? adjusted
|
||||
: prev;
|
||||
}
|
||||
|
||||
function useCollatorImpl (): Collator[] | undefined {
|
||||
const { api } = useApi();
|
||||
const [state, setState] = useState<Collator[] | undefined>();
|
||||
|
||||
const accountIds = useMemo(
|
||||
() => state?.map(({ accountId }) => accountId),
|
||||
[state]
|
||||
);
|
||||
|
||||
// candidates was replaced by candidateList. This is for compatibility.
|
||||
const candidateCall = api.query.collatorSelection.candidates || api.query.collatorSelection.candidateList;
|
||||
const invulnerables = useCall<Collator[]>(api.query.collatorSelection.invulnerables, [], OPT_INV);
|
||||
const candidates = useCall<Collator[]>(candidateCall, [], OPT_CAN);
|
||||
const lastBlocks = useCall<Authored>(accountIds && api.query.collatorSelection.lastAuthoredBlock?.multi, [accountIds], OPT_AUT);
|
||||
|
||||
useEffect(
|
||||
() => invulnerables && candidates && setState(() =>
|
||||
[...invulnerables, ...candidates]
|
||||
),
|
||||
[candidates, invulnerables]
|
||||
);
|
||||
|
||||
useEffect(
|
||||
() => lastBlocks && setState((prev) =>
|
||||
prev && includeAuthors(prev, lastBlocks)
|
||||
),
|
||||
[lastBlocks]
|
||||
);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
export default createNamedHook('useCollatorImpl', useCollatorImpl);
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": "..",
|
||||
"outDir": "./build",
|
||||
"rootDir": "./src"
|
||||
},
|
||||
"references": [
|
||||
{ "path": "../react-components/tsconfig.build.json" }
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user