mirror of
https://github.com/pezkuwichain/pezkuwi-apps.git
synced 2026-06-13 09:21: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,126 @@
|
||||
// Copyright 2017-2025 @pezkuwi/app-gilt authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { u32, u128 } from '@pezkuwi/types';
|
||||
import type { BN } from '@pezkuwi/util';
|
||||
|
||||
import React, { useMemo, useState } from 'react';
|
||||
|
||||
import { Button, InputAddress, InputBalance, InputNumber, Modal, TxButton } from '@pezkuwi/react-components';
|
||||
import { useApi, useToggle } from '@pezkuwi/react-hooks';
|
||||
import { Available } from '@pezkuwi/react-query';
|
||||
import { BN_ONE } from '@pezkuwi/util';
|
||||
|
||||
import { useTranslation } from '../translate.js';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
isDisabled: boolean;
|
||||
proxies: Record<string, string[]>;
|
||||
}
|
||||
|
||||
function Bid ({ className, isDisabled, proxies }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const [isOpen, toggleOpen] = useToggle();
|
||||
const [accountId, setAccountId] = useState<string | null>(null);
|
||||
const [proxyId, setProxyId] = useState<string | null>(null);
|
||||
const [amount, setAmount] = useState<BN | undefined>();
|
||||
const [duration, setDuration] = useState<BN | undefined>();
|
||||
|
||||
const tx = useMemo(
|
||||
() => accountId && amount && duration
|
||||
? api.tx.proxy.proxy(accountId, null, api.tx.gilt.placeBid(amount, duration))
|
||||
: null,
|
||||
[api, accountId, amount, duration]
|
||||
);
|
||||
|
||||
const proxiedAccounts = Object.keys(proxies);
|
||||
const isAmountError = !amount || amount.isZero() || amount.lt(api.consts.gilt.minFreeze as u128);
|
||||
const isDurationError = !duration || !duration.gte(BN_ONE) || duration.gt(api.consts.gilt.queueCount as u128);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
icon='plus'
|
||||
isDisabled={!proxiedAccounts.length || isDisabled}
|
||||
label={t('Submit Bid')}
|
||||
onClick={toggleOpen}
|
||||
/>
|
||||
{isOpen && (
|
||||
<Modal
|
||||
className={className}
|
||||
header={t('submit gilt bid')}
|
||||
onClose={toggleOpen}
|
||||
size='large'
|
||||
>
|
||||
<Modal.Content>
|
||||
<Modal.Columns hint={t('This account will make the bid for the gilt and pay all associated fees.')}>
|
||||
<InputAddress
|
||||
filter={proxiedAccounts}
|
||||
label={t('use proxied account')}
|
||||
labelExtra={
|
||||
<Available
|
||||
label={<span className='label'>{t('transferable')}</span>}
|
||||
params={accountId}
|
||||
/>
|
||||
}
|
||||
onChange={setAccountId}
|
||||
type='account'
|
||||
/>
|
||||
{accountId && (
|
||||
<InputAddress
|
||||
filter={proxies[accountId]}
|
||||
label={t('send via proxy')}
|
||||
onChange={setProxyId}
|
||||
type='account'
|
||||
/>
|
||||
)}
|
||||
</Modal.Columns>
|
||||
<Modal.Columns hint={t('The amount you wish to lock for the duration. It needs to be more than the gilt minimum.')}>
|
||||
<InputBalance
|
||||
autoFocus
|
||||
defaultValue={api.consts.gilt.minFreeze as u128}
|
||||
isError={isAmountError}
|
||||
isZeroable={false}
|
||||
label={t('bid amount')}
|
||||
onChange={setAmount}
|
||||
/>
|
||||
<InputBalance
|
||||
defaultValue={api.consts.gilt.minFreeze as u128}
|
||||
isDisabled
|
||||
label={t('minimum freeze amount')}
|
||||
/>
|
||||
</Modal.Columns>
|
||||
<Modal.Columns hint={t('The number of periods this bid is to be freezed for, less than the maximum period')}>
|
||||
<InputNumber
|
||||
defaultValue={BN_ONE}
|
||||
isError={isDurationError}
|
||||
isZeroable={false}
|
||||
label={t('lock periods')}
|
||||
onChange={setDuration}
|
||||
/>
|
||||
<InputNumber
|
||||
defaultValue={api.consts.gilt.queueCount as u32}
|
||||
isDisabled
|
||||
label={t('maximum lock periods')}
|
||||
/>
|
||||
</Modal.Columns>
|
||||
</Modal.Content>
|
||||
<Modal.Actions>
|
||||
<TxButton
|
||||
accountId={proxyId}
|
||||
extrinsic={tx}
|
||||
icon='check'
|
||||
isDisabled={isAmountError || isDurationError || !accountId}
|
||||
label={t('Bid')}
|
||||
onStart={toggleOpen}
|
||||
/>
|
||||
</Modal.Actions>
|
||||
</Modal>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Bid);
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright 2017-2025 @pezkuwi/app-gilt authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { QueueTotal } from './types.js';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { Table } from '@pezkuwi/react-components';
|
||||
import { FormatBalance } from '@pezkuwi/react-query';
|
||||
import { formatNumber } from '@pezkuwi/util';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
value: QueueTotal;
|
||||
}
|
||||
|
||||
function Queue ({ className, value: { balance, index, numItems } }: Props): React.ReactElement<Props> {
|
||||
return (
|
||||
<tr className={className}>
|
||||
<Table.Column.Id value={index} />
|
||||
<td className='number all'>
|
||||
{formatNumber(numItems)}
|
||||
</td>
|
||||
<td className='all'>
|
||||
<FormatBalance value={balance} />
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Queue);
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright 2017-2025 @pezkuwi/app-gilt authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { QueueTotal } from './types.js';
|
||||
|
||||
import React, { useRef } from 'react';
|
||||
|
||||
import { Table } from '@pezkuwi/react-components';
|
||||
|
||||
import { useTranslation } from '../translate.js';
|
||||
import Queue from './Queue.js';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
queueTotals?: QueueTotal[];
|
||||
}
|
||||
|
||||
function Queues ({ className, queueTotals }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const headerRef = useRef<([React.ReactNode?, string?, number?] | false)[]>([
|
||||
[t('queues'), 'start'],
|
||||
[t('participants'), 'number'],
|
||||
[t('balance'), 'number']
|
||||
]);
|
||||
|
||||
return (
|
||||
<Table
|
||||
className={className}
|
||||
empty={queueTotals && t('No active gilt queues found.')}
|
||||
header={headerRef.current}
|
||||
>
|
||||
{queueTotals?.map((value) => (
|
||||
<Queue
|
||||
key={value.index}
|
||||
value={value}
|
||||
/>
|
||||
))}
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Queues);
|
||||
@@ -0,0 +1,67 @@
|
||||
// Copyright 2017-2025 @pezkuwi/app-gilt authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { u128 } from '@pezkuwi/types';
|
||||
import type { ActiveGiltsTotal } from '@pezkuwi/types/interfaces';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { CardSummary, SummaryBox } from '@pezkuwi/react-components';
|
||||
import { useApi, useBestNumber } from '@pezkuwi/react-hooks';
|
||||
import { BN, BN_HUNDRED, BN_QUINTILL, formatNumber } from '@pezkuwi/util';
|
||||
|
||||
import { useTranslation } from '../translate.js';
|
||||
|
||||
interface Props {
|
||||
activeTotal?: ActiveGiltsTotal;
|
||||
className?: string;
|
||||
isDisabled: boolean;
|
||||
}
|
||||
|
||||
const DIVIDOR_NU = 10_000;
|
||||
const DIVISOR_BN = new BN(10_000);
|
||||
|
||||
function Summary ({ activeTotal, className, isDisabled }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const bestNumber = useBestNumber();
|
||||
|
||||
return (
|
||||
<SummaryBox className={className}>
|
||||
<section>
|
||||
<CardSummary label={t('active')}>
|
||||
{isDisabled ? t('no') : t('yes')}
|
||||
</CardSummary>
|
||||
{activeTotal && (
|
||||
<CardSummary label={t('index')}>
|
||||
{formatNumber(activeTotal.index)}
|
||||
</CardSummary>
|
||||
)}
|
||||
</section>
|
||||
{activeTotal && (
|
||||
<section>
|
||||
<CardSummary label={t('proportion')}>
|
||||
{(activeTotal.proportion.mul(DIVISOR_BN).imul(BN_HUNDRED).div(BN_QUINTILL).toNumber() / DIVIDOR_NU).toFixed(2)}%
|
||||
</CardSummary>
|
||||
<CardSummary label={t('target')}>
|
||||
{(activeTotal.target.mul(DIVISOR_BN).imul(BN_HUNDRED).div(BN_QUINTILL).toNumber() / DIVIDOR_NU).toFixed(2)}%
|
||||
</CardSummary>
|
||||
</section>
|
||||
)}
|
||||
<section>
|
||||
{bestNumber && (
|
||||
<CardSummary
|
||||
label={t('intake')}
|
||||
progress={{
|
||||
total: api.consts.gilt.intakePeriod as u128,
|
||||
value: bestNumber.mod(api.consts.gilt.intakePeriod as u128),
|
||||
withTime: true
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</section>
|
||||
</SummaryBox>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Summary);
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright 2017-2025 @pezkuwi/app-gilt authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
import { Button } from '@pezkuwi/react-components';
|
||||
|
||||
import { useProxies } from '../useProxies.js';
|
||||
import BidAdd from './BidAdd.js';
|
||||
import Queues from './Queues.js';
|
||||
import Summary from './Summary.js';
|
||||
import useInfo from './useInfo.js';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
function Overview ({ className }: Props): React.ReactElement<Props> {
|
||||
const proxies = useProxies();
|
||||
const { info } = useInfo();
|
||||
|
||||
const isDisabled = useMemo(
|
||||
() => !info?.activeTotal || info.activeTotal.target.isZero(),
|
||||
[info]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<Summary
|
||||
activeTotal={info?.activeTotal}
|
||||
isDisabled={isDisabled}
|
||||
/>
|
||||
<Button.Group>
|
||||
<BidAdd
|
||||
isDisabled={isDisabled}
|
||||
proxies={proxies}
|
||||
/>
|
||||
</Button.Group>
|
||||
<Queues queueTotals={info?.queueTotals} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Overview);
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright 2017-2025 @pezkuwi/app-gilt authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { ActiveGiltsTotal, BalanceOf } from '@pezkuwi/types/interfaces';
|
||||
import type { BN } from '@pezkuwi/util';
|
||||
|
||||
export interface QueueTotal {
|
||||
balance: BalanceOf;
|
||||
index: number;
|
||||
numItems: BN;
|
||||
}
|
||||
|
||||
export interface GiltInfo {
|
||||
activeIndex?: BN | null;
|
||||
activeTotal?: ActiveGiltsTotal;
|
||||
queueTotals?: QueueTotal[];
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright 2017-2025 @pezkuwi/app-gilt authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { u32 } from '@pezkuwi/types';
|
||||
import type { ActiveGiltsTotal, BalanceOf } from '@pezkuwi/types/interfaces';
|
||||
import type { GiltInfo, QueueTotal } from './types.js';
|
||||
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { createNamedHook, useApi, useCallMulti } from '@pezkuwi/react-hooks';
|
||||
import { BN_ONE } from '@pezkuwi/util';
|
||||
|
||||
interface State {
|
||||
info?: GiltInfo;
|
||||
}
|
||||
|
||||
const OPT_GILT = {
|
||||
defaultValue: {} as GiltInfo,
|
||||
transform: ([activeTotal, queueTotals]: [ActiveGiltsTotal, [u32, BalanceOf][]]): GiltInfo => ({
|
||||
activeIndex: activeTotal.index.isZero()
|
||||
? null
|
||||
: activeTotal.index.sub(BN_ONE),
|
||||
activeTotal,
|
||||
queueTotals: queueTotals
|
||||
.map(([numItems, balance], index): QueueTotal => ({ balance, index: index + 1, numItems }))
|
||||
.filter(({ balance }) => !balance.isZero())
|
||||
})
|
||||
};
|
||||
|
||||
function useInfoImpl (): State {
|
||||
const { api } = useApi();
|
||||
const info = useCallMulti<GiltInfo>([
|
||||
api.query.gilt.activeTotal,
|
||||
api.query.gilt.queueTotals
|
||||
], OPT_GILT);
|
||||
|
||||
// useEffect((): void => {
|
||||
// info.activeIndex &&
|
||||
// api.query.gilt.active
|
||||
// .entries()
|
||||
// .then((value) => console.log(JSON.stringify(value)))
|
||||
// .catch(console.error);
|
||||
// }, [api, info?.activeIndex]);
|
||||
|
||||
return useMemo(
|
||||
() => ({ info }),
|
||||
[info]
|
||||
);
|
||||
}
|
||||
|
||||
export default createNamedHook('useInfo', useInfoImpl);
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright 2017-2025 @pezkuwi/app-gilt authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import React, { useRef } from 'react';
|
||||
import { Route, Routes } from 'react-router';
|
||||
|
||||
import { Tabs } from '@pezkuwi/react-components';
|
||||
|
||||
import Overview from './Overview/index.js';
|
||||
import { useTranslation } from './translate.js';
|
||||
|
||||
interface Props {
|
||||
basePath: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
function GiltApp ({ basePath, className }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const tabsRef = useRef([
|
||||
{
|
||||
isRoot: true,
|
||||
name: 'overview',
|
||||
text: t('Overview')
|
||||
}
|
||||
]);
|
||||
|
||||
return (
|
||||
<main className={className}>
|
||||
<Tabs
|
||||
basePath={basePath}
|
||||
items={tabsRef.current}
|
||||
/>
|
||||
<Routes>
|
||||
<Route path={basePath}>
|
||||
<Route
|
||||
element={
|
||||
<Overview />
|
||||
}
|
||||
index
|
||||
/>
|
||||
</Route>
|
||||
</Routes>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(GiltApp);
|
||||
@@ -0,0 +1,8 @@
|
||||
// Copyright 2017-2025 @pezkuwi/app-gilt 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-gilt');
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// Copyright 2017-2025 @pezkuwi/app-gilt authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Vec } from '@pezkuwi/types';
|
||||
import type { BalanceOf, ProxyDefinition } from '@pezkuwi/types/interfaces';
|
||||
import type { ITuple } from '@pezkuwi/types/types';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { createNamedHook, useAccounts, useApi, useIsMountedRef } from '@pezkuwi/react-hooks';
|
||||
|
||||
type ProxyResult = ITuple<[Vec<ProxyDefinition>, BalanceOf]>;
|
||||
|
||||
function useProxiesImpl (): Record<string, string[]> {
|
||||
const { api } = useApi();
|
||||
const { allAccounts } = useAccounts();
|
||||
const mountedRef = useIsMountedRef();
|
||||
const [state, setState] = useState<Record<string, string[]>>({});
|
||||
|
||||
useEffect((): void => {
|
||||
if (allAccounts.length) {
|
||||
api.query.proxy.proxies
|
||||
.multi<ProxyResult>(allAccounts)
|
||||
.then((result) =>
|
||||
mountedRef.current && setState(
|
||||
result
|
||||
.map(([p], index): [string, string[]] => [
|
||||
allAccounts[index],
|
||||
p.map(({ delegate }) => delegate.toString())
|
||||
])
|
||||
.filter(([, p]) => p.length)
|
||||
.reduce((all, [a, p]) => ({ ...all, [a]: p }), {})
|
||||
)
|
||||
)
|
||||
.catch(console.error);
|
||||
}
|
||||
}, [allAccounts, api, mountedRef]);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
export const useProxies = createNamedHook('useProxies', useProxiesImpl);
|
||||
Reference in New Issue
Block a user