mirror of
https://github.com/pezkuwichain/pezkuwi-apps.git
synced 2026-04-22 12:28:01 +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,111 @@
|
||||
// Copyright 2017-2025 @pezkuwi/react-components authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// we use augmented types in this tsconfig
|
||||
import '@pezkuwi/api-augment/bizinikiwi';
|
||||
|
||||
import type { DispatchError, DispatchResult, Event, EventRecord } from '@pezkuwi/types/interfaces';
|
||||
import type { StagingXcmV5TraitsOutcome } from '@pezkuwi/types/lookup';
|
||||
|
||||
type EventCheck = (event: Event) => string | null;
|
||||
|
||||
const INCOMPLETE = 'incomplete execution';
|
||||
|
||||
function extractError (result: DispatchResult): string | null {
|
||||
if (!result) {
|
||||
return INCOMPLETE;
|
||||
} else if (result.isErr) {
|
||||
return `error: ${getDispatchError(result.asErr)}`;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function batchInterrupted ({ data: [index, error] }: Event): string | null {
|
||||
return `error: ${index.toString()}: ${getDispatchError(error as DispatchError)}`;
|
||||
}
|
||||
|
||||
function dispatchResult ({ data: [result] }: Event): string | null {
|
||||
return extractError(result as DispatchResult);
|
||||
}
|
||||
|
||||
function dispatchResultCouncil ({ data: [, result] }: Event): string | null {
|
||||
return extractError(result as DispatchResult);
|
||||
}
|
||||
|
||||
// [approving, timepoint, multisig, callHash, result]
|
||||
function dispatchResultMulti ({ data: [,,,, result] }: Event): string | null {
|
||||
return extractError(result as DispatchResult);
|
||||
}
|
||||
|
||||
function xcmAttempted ({ data: [outcome] }: Event): string | null {
|
||||
if (!outcome) {
|
||||
return INCOMPLETE;
|
||||
} else if ((outcome as StagingXcmV5TraitsOutcome).isIncomplete) {
|
||||
const error = (outcome as StagingXcmV5TraitsOutcome).asIncomplete.error;
|
||||
|
||||
return `error: ${error.error.type}`;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const collective: Record<string, EventCheck> = {
|
||||
Executed: dispatchResultCouncil
|
||||
};
|
||||
|
||||
const xcmPallet: Record<string, EventCheck> = {
|
||||
Attempted: xcmAttempted
|
||||
};
|
||||
|
||||
const CHECKS: Record<string, Record<string, EventCheck>> = {
|
||||
allianceMotion: collective,
|
||||
council: collective,
|
||||
membership: collective,
|
||||
multisig: {
|
||||
MultisigExecuted: dispatchResultMulti
|
||||
},
|
||||
pezkuwiXcm: xcmPallet,
|
||||
proxy: {
|
||||
ProxyExecuted: dispatchResult
|
||||
},
|
||||
sudo: {
|
||||
Sudid: dispatchResult,
|
||||
SudoAsDone: dispatchResult
|
||||
},
|
||||
technicalCommittee: collective,
|
||||
utility: {
|
||||
BatchInterrupted: batchInterrupted,
|
||||
DispatchedAs: dispatchResult
|
||||
},
|
||||
xcmPallet
|
||||
};
|
||||
|
||||
export function getDispatchError (dispatchError: DispatchError): string {
|
||||
let message: string = dispatchError.type;
|
||||
|
||||
if (dispatchError.isModule) {
|
||||
try {
|
||||
const mod = dispatchError.asModule;
|
||||
const error = dispatchError.registry.findMetaError(mod);
|
||||
|
||||
message = `${error.section}.${error.name}`;
|
||||
} catch {
|
||||
// swallow
|
||||
}
|
||||
} else if (dispatchError.isToken) {
|
||||
message = `${dispatchError.type}.${dispatchError.asToken.type}`;
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
export function getIncompleteMessage ({ event }: EventRecord): string | null {
|
||||
const { method, section } = event;
|
||||
|
||||
return (
|
||||
!!CHECKS[section] &&
|
||||
!!CHECKS[section][method] &&
|
||||
CHECKS[section][method](event)
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright 2017-2025 @pezkuwi/react-components authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { QueueTxStatus } from './types.js';
|
||||
|
||||
export const STATUS_COMPLETE: QueueTxStatus[] = [
|
||||
// status from subscription
|
||||
'finalitytimeout', 'finalized', 'inblock', 'usurped', 'dropped', 'invalid',
|
||||
// normal completion
|
||||
'cancelled', 'error', 'sent'
|
||||
];
|
||||
@@ -0,0 +1,312 @@
|
||||
// Copyright 2017-2025 @pezkuwi/react-components authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { IconName } from '@fortawesome/fontawesome-svg-core';
|
||||
import type { QueueStatus, QueueTx, QueueTxStatus } from './types.js';
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import { useQueue } from '@pezkuwi/react-hooks';
|
||||
|
||||
import AddressMini from '../AddressMini.js';
|
||||
import Icon from '../Icon.js';
|
||||
import Spinner from '../Spinner.js';
|
||||
import { styled } from '../styled.js';
|
||||
import { STATUS_COMPLETE } from './constants.js';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
function iconName (status: string): IconName {
|
||||
switch (status) {
|
||||
case 'error':
|
||||
return 'ban';
|
||||
|
||||
case 'event':
|
||||
case 'eventWarn':
|
||||
return 'assistive-listening-systems';
|
||||
|
||||
case 'received':
|
||||
return 'telegram-plane';
|
||||
|
||||
default:
|
||||
return 'check';
|
||||
}
|
||||
}
|
||||
|
||||
function signerIconName (status: QueueTxStatus): IconName {
|
||||
switch (status) {
|
||||
case 'cancelled':
|
||||
return 'ban';
|
||||
|
||||
case 'completed':
|
||||
case 'inblock':
|
||||
case 'finalized':
|
||||
case 'sent':
|
||||
return 'check';
|
||||
|
||||
case 'dropped':
|
||||
case 'invalid':
|
||||
case 'usurped':
|
||||
return 'arrow-down';
|
||||
|
||||
case 'error':
|
||||
case 'finalitytimeout':
|
||||
return 'exclamation-triangle';
|
||||
|
||||
case 'queued':
|
||||
// case 'retracted':
|
||||
return 'random';
|
||||
|
||||
default:
|
||||
return 'spinner';
|
||||
}
|
||||
}
|
||||
|
||||
function renderStatus ({ account, action, id, message, removeItem, status }: QueueStatus): React.ReactNode {
|
||||
return (
|
||||
<div
|
||||
className={`item ${status}`}
|
||||
key={id}
|
||||
>
|
||||
<div className='wrapper'>
|
||||
<div className='container'>
|
||||
<Icon
|
||||
icon='times'
|
||||
onClick={removeItem}
|
||||
/>
|
||||
<div className='short'>
|
||||
<Icon icon={iconName(status)} />
|
||||
</div>
|
||||
<div className='desc'>
|
||||
<div className='header'>
|
||||
{Array.isArray(action)
|
||||
? action.map((action, index) => <div key={index}>{action}</div>)
|
||||
: action}
|
||||
</div>
|
||||
{account && (
|
||||
<AddressMini value={account} />
|
||||
)}
|
||||
<div className='status'>
|
||||
{message}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function renderItem ({ error, extrinsic, id, removeItem, rpc, status }: QueueTx): React.ReactNode {
|
||||
let { method, section } = rpc;
|
||||
|
||||
if (extrinsic) {
|
||||
const found = extrinsic.registry.findMetaCall(extrinsic.callIndex);
|
||||
|
||||
if (found.section !== 'unknown') {
|
||||
method = found.method;
|
||||
section = found.section;
|
||||
}
|
||||
}
|
||||
|
||||
const icon = signerIconName(status) as 'ban' | 'spinner';
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`item ${status}`}
|
||||
key={id}
|
||||
>
|
||||
<div className='wrapper'>
|
||||
<div className='container'>
|
||||
{STATUS_COMPLETE.includes(status) && (
|
||||
<Icon
|
||||
icon='times'
|
||||
onClick={removeItem}
|
||||
/>
|
||||
)}
|
||||
<div className='short'>
|
||||
{icon === 'spinner'
|
||||
? <Spinner variant='push' />
|
||||
: <Icon icon={icon} />
|
||||
}
|
||||
</div>
|
||||
<div className='desc'>
|
||||
<div className='header'>
|
||||
{section}.{method}
|
||||
</div>
|
||||
<div className='status'>
|
||||
{error ? (error.message || error.toString()) : status}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function filterSt (stqueue?: QueueStatus[]): QueueStatus[] {
|
||||
return (stqueue || []).filter(({ isCompleted }) => !isCompleted);
|
||||
}
|
||||
|
||||
function filterTx (txqueue?: QueueTx[]): QueueTx[] {
|
||||
return (txqueue || []).filter(({ status }) => !['completed', 'incomplete'].includes(status));
|
||||
}
|
||||
|
||||
function Status ({ className = '' }: Props): React.ReactElement<Props> | null {
|
||||
const { stqueue, txqueue } = useQueue();
|
||||
const [allSt, setAllSt] = useState<QueueStatus[]>([]);
|
||||
const [allTx, setAllTx] = useState<QueueTx[]>([]);
|
||||
|
||||
useEffect((): void => {
|
||||
setAllSt(filterSt(stqueue));
|
||||
}, [stqueue]);
|
||||
|
||||
useEffect((): void => {
|
||||
setAllTx(filterTx(txqueue));
|
||||
}, [txqueue]);
|
||||
|
||||
if (!allSt.length && !allTx.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledDiv className={`${className} ui--Status`}>
|
||||
{allTx.map(renderItem)}
|
||||
{allSt.map(renderStatus)}
|
||||
</StyledDiv>
|
||||
);
|
||||
}
|
||||
|
||||
const StyledDiv = styled.div`
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
position: fixed;
|
||||
right: 0.75rem;
|
||||
top: 0.75rem;
|
||||
transition-property: width;
|
||||
transition-duration: 0.75s;
|
||||
width: 4.5rem;
|
||||
z-index: 1001;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1);
|
||||
width: 23rem;
|
||||
|
||||
.item .desc {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.item {
|
||||
display: block;
|
||||
|
||||
.desc {
|
||||
display: none;
|
||||
}
|
||||
|
||||
> .wrapper > .container {
|
||||
align-items: top;
|
||||
background: #00688b;
|
||||
border-radius: 0.25rem;
|
||||
color: white;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 0.25rem;
|
||||
padding: 0 0.5rem;
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
|
||||
.desc {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
padding: 0.75rem 1rem 0.5rem;
|
||||
width: 19rem;
|
||||
|
||||
.status {
|
||||
font-weight: var(--font-weight-normal);
|
||||
}
|
||||
|
||||
.ui--AddressMini {
|
||||
.ui--AddressMini-address {
|
||||
min-width: 0;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.header {
|
||||
opacity: 0.66;
|
||||
}
|
||||
|
||||
.short {
|
||||
font-size: 2.5rem;
|
||||
min-width: 3rem;
|
||||
opacity: 0.75;
|
||||
padding: 0.5rem 0 0.5rem 0.5rem;
|
||||
|
||||
.ui--Icon {
|
||||
color: white !important;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.ui--Spinner {
|
||||
display: inline-block;
|
||||
height: 1em;
|
||||
line-height: 1;
|
||||
vertical-align: -0.125em;
|
||||
|
||||
img {
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.padded {
|
||||
padding: 0.25rem 0 0 0 !important;
|
||||
}
|
||||
|
||||
.ui--Icon.isClickable {
|
||||
position: absolute;
|
||||
top: 0.5rem;
|
||||
right: 0.5rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
&.cancelled > .wrapper > .container {
|
||||
background: #cd9b1d
|
||||
}
|
||||
|
||||
&.event > .wrapper > .container {
|
||||
background: teal;
|
||||
}
|
||||
|
||||
&.eventWarn > .wrapper > .container {
|
||||
background: darkorange;
|
||||
}
|
||||
|
||||
&.completed,
|
||||
&.finalized,
|
||||
&.inblock,
|
||||
&.sent,
|
||||
&.success {
|
||||
& > .wrapper > .container {
|
||||
background: green;
|
||||
}
|
||||
}
|
||||
|
||||
&.dropped,
|
||||
&.error,
|
||||
&.finalitytimeout,
|
||||
&.invalid,
|
||||
&.usurped {
|
||||
& > .wrapper > .container {
|
||||
background: red;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default React.memo(Status);
|
||||
@@ -0,0 +1,115 @@
|
||||
// Copyright 2017-2025 @pezkuwi/react-components authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { SubmittableResult } from '@pezkuwi/api';
|
||||
import type { SubmittableExtrinsic } from '@pezkuwi/api/promise/types';
|
||||
import type { SignerResult } from '@pezkuwi/api/types';
|
||||
import type { AccountId, Address } from '@pezkuwi/types/interfaces';
|
||||
import type { DefinitionRpcExt, Registry, SignerPayloadJSON } from '@pezkuwi/types/types';
|
||||
|
||||
export type Actions = 'create' | 'edit' | 'restore' | 'forget' | 'backup' | 'changePassword' | 'transfer';
|
||||
|
||||
export interface ActionStatusBase {
|
||||
account?: AccountId | Address | string;
|
||||
message?: string;
|
||||
status: 'error' | 'event' | 'eventWarn' | 'queued' | 'received' | 'success';
|
||||
}
|
||||
|
||||
export interface ActionStatusPartial extends ActionStatusBase {
|
||||
action: string;
|
||||
}
|
||||
|
||||
export interface ActionStatus extends ActionStatusBase {
|
||||
action: string | string[];
|
||||
}
|
||||
|
||||
export interface AccountInfo {
|
||||
accountId?: string | null;
|
||||
}
|
||||
|
||||
export type QueueTxStatus = 'future' | 'ready' | 'finalized' | 'finalitytimeout' | 'usurped' | 'dropped' | 'inblock' | 'invalid' | 'broadcast' | 'cancelled' | 'completed' | 'error' | 'incomplete' | 'queued' | 'qr' | 'retracted' | 'sending' | 'signing' | 'sent' | 'blocked';
|
||||
|
||||
export type SignerCallback = (id: number, result: SignerResult | null) => void;
|
||||
|
||||
export type TxCallback = (status: SubmittableResult) => void;
|
||||
|
||||
export type TxFailedCallback = (status: Error | SubmittableResult | null) => void;
|
||||
|
||||
export interface QueueTx extends AccountInfo {
|
||||
error?: Error;
|
||||
extrinsic?: SubmittableExtrinsic;
|
||||
id: number;
|
||||
isUnsigned?: boolean;
|
||||
payload?: SignerPayloadJSON;
|
||||
result?: any;
|
||||
removeItem: () => void;
|
||||
rpc: DefinitionRpcExt;
|
||||
signerCb?: SignerCallback;
|
||||
txFailedCb?: TxFailedCallback;
|
||||
txSuccessCb?: TxCallback;
|
||||
txStartCb?: () => void;
|
||||
txUpdateCb?: TxCallback;
|
||||
values?: unknown[];
|
||||
status: QueueTxStatus;
|
||||
}
|
||||
|
||||
export interface QueueStatus extends ActionStatus {
|
||||
id: number;
|
||||
isCompleted: boolean;
|
||||
removeItem: () => void;
|
||||
}
|
||||
|
||||
export interface QueueTxResult {
|
||||
error?: Error;
|
||||
result?: any;
|
||||
status: QueueTxStatus;
|
||||
}
|
||||
|
||||
export interface QueueTxExtrinsic extends AccountInfo {
|
||||
extrinsic?: SubmittableExtrinsic;
|
||||
}
|
||||
|
||||
export interface QueueTxRpc extends AccountInfo {
|
||||
rpc: DefinitionRpcExt;
|
||||
values: unknown[];
|
||||
}
|
||||
|
||||
export interface PartialAccountInfo {
|
||||
accountId?: string | null;
|
||||
}
|
||||
|
||||
export interface PartialQueueTxExtrinsic extends PartialAccountInfo {
|
||||
extrinsic?: SubmittableExtrinsic;
|
||||
payload?: SignerPayloadJSON;
|
||||
signerCb?: SignerCallback;
|
||||
txFailedCb?: TxFailedCallback;
|
||||
txSuccessCb?: TxCallback;
|
||||
txStartCb?: () => void;
|
||||
txUpdateCb?: TxCallback;
|
||||
isUnsigned?: boolean;
|
||||
}
|
||||
|
||||
export interface PartialQueueTxRpc extends PartialAccountInfo {
|
||||
rpc: DefinitionRpcExt;
|
||||
values: unknown[];
|
||||
}
|
||||
|
||||
export type QueueTxRpcAdd = (value: PartialQueueTxRpc) => void;
|
||||
|
||||
export type QueueTxExtrinsicAdd = (value: PartialQueueTxExtrinsic) => void;
|
||||
|
||||
export type QueueTxPayloadAdd = (registry: Registry, payload: SignerPayloadJSON, signerCb: SignerCallback) => void;
|
||||
|
||||
export type QueueTxMessageSetStatus = (id: number, status: QueueTxStatus, result?: any, error?: Error) => void;
|
||||
|
||||
export type QueueAction$Add = (status: ActionStatus | ActionStatus[]) => void;
|
||||
|
||||
export interface QueueProps {
|
||||
stqueue: QueueStatus[];
|
||||
txqueue: QueueTx[];
|
||||
queueAction: QueueAction$Add;
|
||||
queueExtrinsic: QueueTxExtrinsicAdd;
|
||||
queuePayload: QueueTxPayloadAdd;
|
||||
queueRpc: QueueTxRpcAdd;
|
||||
queueSetTxStatus: QueueTxMessageSetStatus;
|
||||
}
|
||||
Reference in New Issue
Block a user