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,74 @@
// Copyright 2017-2025 @pezkuwi/react-components authors & contributors
// SPDX-License-Identifier: Apache-2.0
import React from 'react';
import AccountName from '../AccountName.js';
import IdentityIcon from '../IdentityIcon/index.js';
import { styled } from '../styled.js';
interface Props {
address: string;
className?: string;
isUppercase: boolean;
name: string;
style?: Record<string, string>;
}
function KeyPair ({ address, className = '' }: Props): React.ReactElement<Props> {
return (
<StyledDiv className={`${className} ui--KeyPair`}>
<IdentityIcon
className='icon'
value={address}
/>
<div className='name'>
<AccountName value={address} />
</div>
<div className='address'>
{address}
</div>
</StyledDiv>
);
}
const StyledDiv = styled.div`
display: flex;
flex-wrap: nowrap;
justify-content: space-between;
position: relative;
white-space: nowrap;
> .address {
display: inline-block;
flex: 1;
font-size: var(--font-size-small);
margin-left: 1rem;
max-width: var(--width-shortaddr);
min-width: var(--width-shortaddr);
opacity: var(--opacity-light);
overflow: hidden;
text-align: right;
text-overflow: ellipsis;
}
> .icon {
position: absolute;
top: -5px;
left: 0;
}
> .name {
display: inline-block;
flex: 1 0;
margin-left: 3rem;
overflow: hidden;
text-overflow: ellipsis;
&.uppercase {
text-transform: uppercase;
}
}
`;
export default React.memo(KeyPair);
@@ -0,0 +1,17 @@
// Copyright 2017-2025 @pezkuwi/react-components authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { KeyringSectionOption } from '@pezkuwi/ui-keyring/options/types';
import React from 'react';
import Dropdown from '../Dropdown.js';
export default function createHeader (option: KeyringSectionOption): React.ReactNode {
return (
<Dropdown.Header
content={option.name}
key={option.key || option.name}
/>
);
}
@@ -0,0 +1,37 @@
// Copyright 2017-2025 @pezkuwi/react-components authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { KeyringSectionOption } from '@pezkuwi/ui-keyring/options/types';
import type { Option } from './types.js';
import React from 'react';
import { keyring } from '@pezkuwi/ui-keyring';
import { decodeAddress } from '@pezkuwi/util-crypto';
import KeyPair from './KeyPair.js';
export default function createItem (option: KeyringSectionOption, isUppercase = true): Option | null {
const allowedLength = keyring.keyring.type === 'ethereum'
? 20
: 32;
try {
if (decodeAddress(option.key).length >= allowedLength) {
return {
...option,
text: (
<KeyPair
address={option.key || ''}
isUppercase={isUppercase}
name={option.name}
/>
)
};
}
} catch {
// ignore
}
return null;
}
@@ -0,0 +1,419 @@
// Copyright 2017-2025 @pezkuwi/react-components authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { DropdownItemProps } from 'semantic-ui-react';
import type { KeyringOption$Type, KeyringOptions, KeyringSectionOption } from '@pezkuwi/ui-keyring/options/types';
import type { Option } from './types.js';
import React from 'react';
import store from 'store';
import { withMulti, withObservable } from '@pezkuwi/react-api/hoc';
import { keyring } from '@pezkuwi/ui-keyring';
import { createOptionItem } from '@pezkuwi/ui-keyring/options/item';
import { isNull, isUndefined } from '@pezkuwi/util';
import { isAddress } from '@pezkuwi/util-crypto';
import Dropdown from '../Dropdown.js';
import Static from '../Static.js';
import { styled } from '../styled.js';
import { getAddressName, toAddress } from '../util/index.js';
import createHeader from './createHeader.js';
import createItem from './createItem.js';
interface Props {
className?: string;
defaultValue?: Uint8Array | string | null;
filter?: string[] | null;
hideAddress?: boolean;
isDisabled?: boolean;
isError?: boolean;
isInput?: boolean;
isMultiple?: boolean;
label?: React.ReactNode;
labelExtra?: React.ReactNode;
onChange?: (value: string | null) => void;
onChangeMulti?: (value: string[]) => void;
options?: KeyringSectionOption[] | null;
optionsAll?: Record<string, Option[]>;
placeholder?: string;
type?: KeyringOption$Type;
value?: string | Uint8Array | string[] | null;
withEllipsis?: boolean;
withExclude?: boolean;
withLabel?: boolean;
}
type ExportedType = React.ComponentType<Props> & {
createOption: (option: KeyringSectionOption, isUppercase?: boolean) => Option | null;
setLastValue: (type: KeyringOption$Type, value: string) => void;
};
interface State {
lastValue?: string;
value?: string | string[];
}
const STORAGE_KEY = 'options:InputAddress';
const DEFAULT_TYPE = 'all';
const MULTI_DEFAULT: string[] = [];
function transformToAddress (value?: string | Uint8Array | null): string | null {
try {
return toAddress(value, false, keyring.keyring.type === 'ethereum' ? 20 : 32) || null;
} catch {
// noop, handled by return
}
return null;
}
function transformToAccountId (value: string): string | null {
if (!value) {
return null;
}
const accountId = transformToAddress(value);
return !accountId
? null
: accountId;
}
function createOption (address: string): Option | null {
let isRecent: boolean | undefined;
const pair = keyring.getAccount(address);
let name: string | undefined;
if (pair) {
name = pair.meta.name;
} else {
const addr = keyring.getAddress(address);
if (addr) {
name = addr.meta.name;
isRecent = addr.meta.isRecent;
} else {
isRecent = true;
}
}
return createItem(createOptionItem(address, name), !isRecent);
}
function readOptions (): Record<string, Record<string, string>> {
return store.get(STORAGE_KEY) as Record<string, Record<string, string>> || { defaults: {} };
}
function getLastValue (type: KeyringOption$Type = DEFAULT_TYPE): string {
const options = readOptions();
return options.defaults[type];
}
function setLastValue (type: KeyringOption$Type = DEFAULT_TYPE, value: string): void {
const options = readOptions();
options.defaults[type] = value;
store.set(STORAGE_KEY, options);
}
function dedupe (options: Option[]): Option[] {
return options.reduce<Option[]>((all, o, index) => {
const hasDupe = all.some(({ key }, eindex) =>
eindex !== index &&
key === o.key
);
if (!hasDupe) {
all.push(o);
}
return all;
}, []);
}
class InputAddress extends React.PureComponent<Props, State> {
public override state: State = {};
public static getDerivedStateFromProps ({ type, value }: Props, { lastValue }: State): Pick<State, never> | null {
try {
return {
lastValue: lastValue || getLastValue(type),
value: Array.isArray(value)
? value.map((v) => toAddress(v))
: (toAddress(value) || undefined)
};
} catch {
return null;
}
}
public override render (): React.ReactNode {
const { className = '', defaultValue, hideAddress = false, isDisabled = false, isError, isMultiple, label, labelExtra, options, optionsAll, placeholder, type = DEFAULT_TYPE, withEllipsis, withLabel } = this.props;
const hasOptions = (options && options.length !== 0) || (optionsAll && Object.keys(optionsAll[type]).length !== 0);
// the options could be delayed, don't render without
if (!hasOptions && !isDisabled && !['allPlus'].includes(type)) {
// This is nasty, but since this things is non-functional, there is not much
// we can do (well, wrap it, however that approach is deprecated here)
return (
<Static
className={className}
label={label}
>
No accounts are available for selection.
</Static>
);
}
const { lastValue, value } = this.state;
const lastOption = this.getLastOptionValue();
const actualValue = transformToAddress(
isDisabled || (defaultValue && defaultValue !== '0x' && (this.hasValue(defaultValue) || type === 'allPlus'))
? defaultValue
: this.hasValue(lastValue)
? lastValue
: lastOption?.value
);
const actualOptions: Option[] = options
? dedupe(
options
.map((o) => createItem(o))
.filter((o): o is Option => !!o)
)
: isDisabled && actualValue
? [createOption(actualValue)].filter((o): o is Option => !!o)
: actualValue
? this.addActual(actualValue)
: this.getFiltered();
const _defaultValue = (isMultiple || !isUndefined(value))
? undefined
: actualValue;
return (
<StyledDropdown
className={`${className} ui--InputAddress ${hideAddress ? 'hideAddress' : ''}`}
defaultValue={_defaultValue}
isDisabled={isDisabled}
isError={isError}
isMultiple={isMultiple}
label={label}
labelExtra={labelExtra}
onChange={
isMultiple
? this.onChangeMulti
: this.onChange
}
onSearch={this.onSearch}
options={
// FIXME: this is a "bit" of a HACK - the issue is that the "null"
// value from Option is not correct for the supplied type. (This
// originates in the ui repo for the KeyringOption)
actualOptions as unknown as React.ReactNode[]
}
placeholder={placeholder}
renderLabel={
isMultiple
? this.renderLabel
: undefined
}
value={
isMultiple && !value
? MULTI_DEFAULT
: value
}
withEllipsis={withEllipsis}
withLabel={withLabel}
/>
);
}
private addActual (actualValue: string): Option[] {
const base = this.getFiltered();
return this.hasValue(actualValue)
? base
: base.concat(...[createOption(actualValue)].filter((o): o is Option => !!o));
}
private renderLabel = ({ value }: KeyringSectionOption): React.ReactNode => {
if (!value) {
return undefined;
}
return getAddressName(value);
};
private getLastOptionValue (): KeyringSectionOption | undefined {
const available = this.getFiltered();
return available.length
? available[available.length - 1]
: undefined;
}
private hasValue (test?: Uint8Array | string | null): boolean {
const address = test?.toString();
return this.getFiltered().some(({ value }) => value === address);
}
private getFiltered (): Option[] {
const { filter, optionsAll, type = DEFAULT_TYPE, withExclude = false } = this.props;
return !optionsAll
? []
: dedupe(optionsAll[type]).filter(({ value }) =>
!filter || (
!!value && (
withExclude
? !filter.includes(value)
: filter.includes(value)
)
)
);
}
private onChange = (address: string): void => {
const { filter, onChange, type } = this.props;
!filter && setLastValue(type, address);
onChange && onChange(
!!address && (this.hasValue(address) || (type === 'allPlus' && isAddress(address)))
? transformToAccountId(address)
: null
);
};
private onChangeMulti = (addresses: string[]): void => {
const { onChangeMulti } = this.props;
if (onChangeMulti) {
onChangeMulti(
addresses
.map(transformToAccountId)
.filter((address): address is string => !!address)
);
}
};
private onSearch = (filteredOptions: DropdownItemProps[], _query: string): DropdownItemProps[] => {
const { isInput = true } = this.props;
const query = _query.trim();
const queryLower = query.toLowerCase();
const matches = filteredOptions.filter((item): boolean =>
!!item.value && typeof item.name === 'string' && (
(item.name.toLowerCase?.().includes(queryLower)) ||
item.value.toString().toLowerCase().includes(queryLower)
)
);
if (isInput && matches.length === 0) {
const accountId = transformToAccountId(query);
if (accountId) {
const account = keyring.getAccount(accountId);
if (account) {
matches.push({
key: account.address,
name: account.meta.name,
value: account.address
});
} else {
const item = keyring.saveRecent(
accountId.toString()
).option;
matches.push({
key: item.key,
name: item.name,
value: item.value || undefined
});
}
}
}
// FIXME The return here is _very_ suspect, but it actually does exactly
// what it is meant to do... filter and return the options for clicking
//
// (effectively it seems to be the value type that should allow undefined
// instead of null in there...)
return matches.filter((item, index): boolean => {
const isLast = index === matches.length - 1;
const nextItem = matches[index + 1];
const hasNext = nextItem?.value;
return !(isNull(item.value) || isUndefined(item.value)) || (!isLast && !!hasNext);
});
};
}
const StyledDropdown = styled(Dropdown)`
.ui.dropdown .text {
width: 100%;
}
.ui.disabled.search {
pointer-events: all;
}
.ui.search.selection.dropdown {
> .text > .ui--KeyPair {
.ui--IdentityIcon {
left: -2.75rem;
top: -1.05rem;
> div,
img,
svg {
height: 32px !important;
width: 32px !important;
}
}
.name {
margin-left: 0;
> .ui--AccountName {
height: auto;
}
}
}
> .menu > div.item > .ui--KeyPair > .name > .ui--AccountName {
height: auto;
}
}
&.hideAddress .ui.search.selection.dropdown > .text > .ui--KeyPair .address {
flex: 0;
max-width: 0;
}
`;
const ExportedComponent = withMulti(
InputAddress,
withObservable(keyring.keyringOption.optionsSubject, {
propName: 'optionsAll',
transform: (optionsAll: KeyringOptions): Record<string, (Option | React.ReactNode)[]> =>
Object.entries(optionsAll).reduce((result: Record<string, (Option | React.ReactNode)[]>, [type, options]): Record<string, (Option | React.ReactNode)[]> => {
result[type] = options
.map((option): Option | React.ReactNode | null =>
option.value === null
? createHeader(option)
: createItem(option)
)
.filter((o): o is Option | React.ReactNode => !!o);
return result;
}, {})
})
) as ExportedType;
ExportedComponent.createOption = createItem;
ExportedComponent.setLastValue = setLastValue;
export default ExportedComponent;
@@ -0,0 +1,31 @@
// Copyright 2017-2025 @pezkuwi/react-components authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type React from 'react';
import type { KeyringOption$Type, KeyringSectionOption } from '@pezkuwi/ui-keyring/options/types';
export interface Option extends KeyringSectionOption {
className?: string;
text: React.ReactNode;
}
export interface InputAddressProps {
className?: string;
defaultValue?: Uint8Array | string | null;
hideAddress?: boolean;
isDisabled?: boolean;
isError?: boolean;
isInput?: boolean;
isMultiple?: boolean;
label?: React.ReactNode;
labelExtra?: React.ReactNode;
onChange?: (value: string | null) => void;
onChangeMulti?: (value: string[]) => void;
options?: KeyringSectionOption[];
optionsAll?: Record<string, Option[]>;
placeholder?: string;
type?: KeyringOption$Type;
value?: string | Uint8Array | string[] | null;
withEllipsis?: boolean;
withLabel?: boolean;
}