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
@@ -0,0 +1,47 @@
// Copyright 2017-2025 @pezkuwi/app-referenda authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { BN } from '@pezkuwi/util';
import type { PalletReferenda, TrackDescription } from '../../types.js';
import React from 'react';
import { Dropdown, styled } from '@pezkuwi/react-components';
import { useTranslation } from '../../translate.js';
import useTrackOptions from './useTrackOptions.js';
interface Props {
className?: string;
exclude?: (BN | number)[];
include?: (BN | number)[];
onChange: (trackId: number) => void;
palletReferenda: PalletReferenda;
tracks: TrackDescription[];
}
function TrackDropdown ({ className, exclude, include, onChange, palletReferenda, tracks }: Props): React.ReactElement<Props> | null {
const { t } = useTranslation();
const trackOpts = useTrackOptions(palletReferenda, tracks, include, exclude);
return (
<Dropdown
className={className}
defaultValue={trackOpts[0].value}
label={t('submission track')}
onChange={onChange}
options={trackOpts}
/>
);
}
export default React.memo(styled(TrackDropdown)`
.trackOption {
.faded {
font-size: var(--font-size-small);
font-weight: var(--font-weight-normal);
margin-top: 0.125rem;
opacity: 0.6;
}
}
`);
@@ -0,0 +1,307 @@
// Copyright 2017-2025 @pezkuwi/app-referenda authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { RawParam } from '@pezkuwi/react-params/types';
import type { BN } from '@pezkuwi/util';
import type { HexString } from '@pezkuwi/util/types';
import type { PalletReferenda, TrackDescription } from '../../types.js';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { Button, Dropdown, Input, InputAddress, InputBalance, InputNumber, Modal, styled, ToggleGroup, TxButton } from '@pezkuwi/react-components';
import { useApi, useBestNumber, usePreimage, useToggle } from '@pezkuwi/react-hooks';
import Params from '@pezkuwi/react-params';
import { Available } from '@pezkuwi/react-query';
import { getTypeDef } from '@pezkuwi/types/create';
import { BN_HUNDRED, BN_ONE, BN_THOUSAND, BN_ZERO, isHex } from '@pezkuwi/util';
import { useTranslation } from '../../translate.js';
import { getTrackInfo } from '../../util.js';
import TrackDropdown from './TrackDropdown.js';
interface Props {
className?: string;
isMember: boolean;
members?: string[];
palletReferenda: PalletReferenda;
tracks: TrackDescription[];
}
interface HashState {
imageHash?: HexString | null;
isImageHashValid: boolean;
}
interface ImageState {
imageLen: BN;
imageLenDefault?: BN;
isImageLenValid: boolean;
}
function Submit ({ className = '', isMember, members, palletReferenda, tracks }: Props): React.ReactElement<Props> | null {
const { t } = useTranslation();
const { api, specName } = useApi();
const bestNumber = useBestNumber();
const [isOpen, toggleOpen] = useToggle();
const [accountId, setAccountId] = useState<string | null>(null);
const [trackId, setTrack] = useState<number | undefined>(undefined);
const [origin, setOrigin] = useState<RawParam['value']>(null);
const [{ imageHash, isImageHashValid }, setImageHash] = useState<HashState>({ imageHash: null, isImageHashValid: false });
const [{ imageLen, imageLenDefault, isImageLenValid }, setImageLen] = useState<ImageState>({ imageLen: BN_ZERO, isImageLenValid: false });
const [enactIndex, setEnactIndex] = useState(0);
const [afterBlocks, setAfterBlocks] = useState<BN | undefined>(BN_HUNDRED);
const [atBlock, setAtBlock] = useState<BN | undefined>(BN_ONE);
const [initialAt, setInitialAt] = useState<BN | undefined>();
const preimage = usePreimage(imageHash);
useEffect((): void => {
bestNumber && setInitialAt((prev) =>
prev || bestNumber.add(BN_THOUSAND)
);
}, [bestNumber]);
useEffect((): void => {
preimage?.proposalLength && setImageLen((prev) => ({
imageLen: prev.imageLen,
imageLenDefault: preimage.proposalLength,
isImageLenValid: prev.isImageLenValid
}));
}, [preimage]);
const trackInfo = useMemo(
() => getTrackInfo(api, specName, palletReferenda, tracks, trackId),
[api, palletReferenda, specName, trackId, tracks]
);
const isInvalidAt = useMemo(
() => !bestNumber || (
enactIndex === 0
? afterBlocks?.lt(BN_ONE)
: atBlock?.lt(bestNumber)
),
[afterBlocks, atBlock, bestNumber, enactIndex]
);
const originType = useMemo(
() => [{
name: 'origin',
type: getTypeDef(api.tx[palletReferenda as 'referenda'].submit.meta.args[0].type.toString())
}],
[api, palletReferenda]
);
const originOptions = useMemo(
() => trackInfo && Array.isArray(trackInfo.origin)
? trackInfo.origin.map((records, index) => ({
text: Object.values(records)[0],
value: index + 1
}))
: null,
[trackInfo]
);
const selectedOrigin = useMemo(
() => !trackInfo?.origin || Array.isArray(trackInfo?.origin)
? origin
: trackInfo.origin,
[origin, trackInfo]
);
const enactOpts = useMemo(
() => [
{ text: t('After delay'), value: 'after' },
{ text: t('At block'), value: 'at' }
],
[t]
);
const _onChangeOriginMulti = useCallback(
(value: number) => setOrigin(
trackInfo && Array.isArray(trackInfo.origin)
? trackInfo.origin[value - 1]
: null
),
[trackInfo]
);
const _onChangeOrigin = useCallback(
([{ isValid, value }]: RawParam[]) =>
setOrigin(isValid ? value : null),
[]
);
const _onChangeImageHash = useCallback(
(h?: string) =>
setImageHash({
imageHash: h as HexString,
isImageHashValid: isHex(h, 256)
}),
[]
);
const _onChangeImageLen = useCallback(
(value?: BN): void => {
value && setImageLen((prev) => ({
imageLen: value,
imageLenDefault: prev.imageLenDefault,
isImageLenValid: !value.isZero()
}));
},
[]
);
return (
<>
{isOpen && (
<StyledModal
className={className}
header={t('Submit proposal')}
onClose={toggleOpen}
size='large'
>
<Modal.Content>
<Modal.Columns hint={t('The proposal will be registered from this account and the balance lock will be applied here.')}>
<InputAddress
filter={members}
label={t('propose from account')}
labelExtra={
<Available
label={<span className='label'>{t('transferable')}</span>}
params={accountId}
/>
}
onChange={setAccountId}
type='account'
/>
</Modal.Columns>
<Modal.Columns hint={t('The origin (and by extension track) that you wish to submit for, each has a different period, different root and acceptance criteria.')}>
<TrackDropdown
onChange={setTrack}
palletReferenda={palletReferenda}
tracks={tracks}
/>
{!trackInfo?.origin && (
<Params
className='originSelect'
onChange={_onChangeOrigin}
params={originType}
/>
)}
{originOptions && (
<Dropdown
defaultValue={originOptions[0].value}
label={t('track origin')}
onChange={_onChangeOriginMulti}
options={originOptions}
/>
)}
</Modal.Columns>
<Modal.Columns
hint={
<>
<p>{t('The hash of the preimage for the proposal as previously submitted or intended.')}</p>
<p>{t('The length value will be auto-populated from the on-chain value if it is found.')}</p>
</>
}
>
<Input
autoFocus
isError={!isImageHashValid}
label={t('preimage hash')}
onChange={_onChangeImageHash}
value={imageHash || ''}
/>
<InputNumber
defaultValue={imageLenDefault}
isDisabled={!!preimage?.proposalLength && !preimage?.proposalLength.isZero() && isImageHashValid && isImageLenValid}
isError={!isImageLenValid}
key='inputLength'
label={t('preimage length')}
onChange={_onChangeImageLen}
value={imageLen}
/>
</Modal.Columns>
<Modal.Columns
align='center'
hint={t('The moment of enactment, either at a specific block, or after a specific number of blocks.')}
>
<ToggleGroup
onChange={setEnactIndex}
options={enactOpts}
value={enactIndex}
/>
</Modal.Columns>
{enactIndex === 0
? (
<Modal.Columns hint={t('The number of blocks to delay enactment after proposal approval.')}>
<InputNumber
defaultValue={BN_HUNDRED}
isError={isInvalidAt}
label={t('after number of blocks')}
onChange={setAfterBlocks}
value={afterBlocks}
/>
</Modal.Columns>
)
: (
<Modal.Columns hint={t('A specific block to enact the proposal at.')}>
<InputNumber
defaultValue={initialAt}
isError={isInvalidAt}
label={t('at specific block')}
onChange={setAtBlock}
value={atBlock}
/>
</Modal.Columns>
)
}
<Modal.Columns hint={t('The deposit for this proposal will be locked for the referendum duration.')}>
<InputBalance
defaultValue={api.consts[palletReferenda as 'referenda'].submissionDeposit}
isDisabled
label={t('submission deposit')}
/>
</Modal.Columns>
</Modal.Content>
<Modal.Actions>
<TxButton
accountId={accountId}
icon='plus'
isDisabled={!selectedOrigin || !isImageHashValid || !isImageLenValid || !accountId || isInvalidAt || !preimage?.proposalHash}
label={t('Submit proposal')}
onStart={toggleOpen}
params={[
selectedOrigin,
{
Lookup: preimage
? { hash: preimage.proposalHash, len: imageLen }
: { hash: imageHash, len: imageLen }
},
enactIndex === 0
? { After: afterBlocks }
: { At: atBlock }
]}
tx={api.tx[palletReferenda as 'referenda'].submit}
/>
</Modal.Actions>
</StyledModal>
)}
<Button
icon='plus'
isDisabled={!isMember}
label={t('Submit proposal')}
onClick={toggleOpen}
/>
</>
);
}
const StyledModal = styled(Modal)`
.originSelect, .timeSelect {
> .ui--Params-Content {
padding-left: 0;
}
}
`;
export default React.memo(Submit);
@@ -0,0 +1,9 @@
// Copyright 2017-2025 @pezkuwi/app-referenda authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type React from 'react';
export interface TrackOption {
text: React.ReactNode;
value: number;
}
@@ -0,0 +1,57 @@
// Copyright 2017-2025 @pezkuwi/app-referenda authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { ApiPromise } from '@pezkuwi/api';
import type { BN } from '@pezkuwi/util';
import type { TrackDescription } from '../../types.js';
import type { TrackOption } from './types.js';
import React, { useMemo } from 'react';
import { createNamedHook, useApi } from '@pezkuwi/react-hooks';
import { bnToBn } from '@pezkuwi/util';
import { getTrackInfo, getTrackName } from '../../util.js';
function getTrackOptions (api: ApiPromise, specName: string, palletReferenda: string, tracks: TrackDescription[], include?: (BN | number)[], exclude?: (BN | number)[]): TrackOption[] {
const includeBn = include?.map((v) => bnToBn(v));
const excludeBn = exclude?.map((v) => bnToBn(v));
return tracks
.filter(({ id }) =>
(
!includeBn ||
includeBn.some((v) => v.eq(id))
) && (
!excludeBn ||
!excludeBn.some((v) => v.eq(id))
)
)
.map(({ id, info }): TrackOption => {
const trackInfo = getTrackInfo(api, specName, palletReferenda, tracks, id.toNumber());
const trackName = getTrackName(id, info);
return {
text: trackInfo?.text
? (
<div className='trackOption'>
<div className='normal'>{trackName}</div>
<div className='faded'>{trackInfo.text}</div>
</div>
)
: trackName,
value: id.toNumber()
};
});
}
function useTrackOptionsImpl (palletReferenda: string, tracks: TrackDescription[], include?: (BN | number)[], exclude?: (BN | number)[]): TrackOption[] {
const { api, specName } = useApi();
return useMemo(
() => getTrackOptions(api, specName, palletReferenda, tracks, include, exclude),
[api, exclude, include, palletReferenda, specName, tracks]
);
}
export default createNamedHook('useTrackOptions', useTrackOptionsImpl);