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:
2026-01-07 13:05:27 +03:00
commit d21bfb1320
5867 changed files with 329019 additions and 0 deletions
View File
View File
+1
View File
@@ -0,0 +1 @@
# @pezkuwi/app-collator
+23
View File
@@ -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": "*"
}
}
+50
View File
@@ -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);
+49
View File
@@ -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);
+36
View File
@@ -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);
+38
View File
@@ -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);
+8
View File
@@ -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');
}
+11
View File
@@ -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;
}
+100
View File
@@ -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" }
]
}