Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev #138

Merged
merged 4 commits into from
Nov 15, 2024
Merged

Dev #138

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ Install dependencies
npm install
```

Install chain descriptors

```bash
npm run add-descriptors
```

Start the app

```bash
Expand Down
37 changes: 36 additions & 1 deletion src/app/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { lazy } from 'react';

import { LayoutBasic } from '@components/layouts/basic';
import { LayoutCodeEditor } from '@components/layouts/codeEditor';
import { NotFound } from '@views/notFound';

const Home = lazy(() => import('../views/home'));
const CodeEditor = lazy(() => import('../views/codeEditor'));
Expand All @@ -12,8 +11,16 @@ const BlockDetails = lazy(() => import('../views/blockDetails'));
const Explorer = lazy(() => import('../views/explorer'));
const SignedExtrinsics = lazy(() => import('../views/signedExtrinsics'));
const Forks = lazy(() => import('../views/forks'));
const Extrinsics = lazy(() => import('../views/extrinsics'));
const ChainState = lazy(() => import('../views/chainState'));
const Constants = lazy(() => import('../views/constants'));
const RuntimeCalls = lazy(() => import('../views/runtimeCalls'));
const Onboarding = lazy(() => import('../views/onboarding'));
const LatestBlocks = lazy(() => import('../views/latestBlocks'));
const Decoder = lazy(() => import('../views/decoder'));
const DecoderDynamic = lazy(() => import('../views/decoderDynamic'));
const RpcCalls = lazy(() => import('../views/rpcCalls'));
const NotFound = lazy(() => import('../views/notFound'));

export const routes = () => ([
{
Expand All @@ -39,6 +46,34 @@ export const routes = () => ([
path: 'login-callback',
element: <Callback />,
},
{
path: 'extrinsics',
element: <Extrinsics />,
},
{
path: 'chain-state',
element: <ChainState />,
},
{
path: 'constants',
element: <Constants />,
},
{
path: 'runtime-calls',
element: <RuntimeCalls />,
},
{
path: 'rpc-calls',
element: <RpcCalls />,
},
{
path: 'decoder',
element: <Decoder />,
},
{
path: 'decoder-dynamic',
element: <DecoderDynamic />,
},
],
},
{
Expand Down
42 changes: 42 additions & 0 deletions src/components/callDocs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Icon } from '@components/icon';
import { cn } from '@utils/helpers';

interface ICallDocs {
docs: string[];
className?: string;
}

export const CallDocs = ({
docs,
className,
}: ICallDocs) => {
if (docs.length <= 0) {
return null;
} else {
return (
<div className={cn(
'p-3 font-body1-regular',
'grid grid-cols-[24px_1fr] gap-2',
'bg-dev-black-800 dark:bg-dev-purple-300',
'border border-dev-purple-700 dark:border-dev-purple-200',
'text-dev-purple-50 dark:text-dev-black-1000',
className,
)}
>
<Icon
name="icon-info"
size={[24]}
/>
<div className="flex flex-col gap-2">
{
docs?.map((doc, i) => (
<p key={`doc-${i}`}>
{doc}
</p>
))
}
</div>
</div>
);
}
};
1 change: 1 addition & 0 deletions src/components/chainState/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as InvocationStorageArgs } from './invocationStorageArgs';
37 changes: 37 additions & 0 deletions src/components/chainState/invocationStorageArgs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { NotImplemented } from '@components/invocationArgsMapper/notImplemented';
import { useStoreChain } from '@stores';

import { InvocationMapper } from '../invocationArgsMapper/invocationMapper';

import type { InvocationStorageArgs as Type } from '@components/invocationArgsMapper/types';
import type { TMetaDataStorageItem } from '@custom-types/papi';
import type { MetadataLookup } from '@polkadot-api/metadata-builders';

const shouldSkipRendering = (storage: TMetaDataStorageItem, lookup: MetadataLookup | null): boolean => {
return storage.type.tag === 'plain' || !lookup;
};

const InvocationStorageArgs = ({ args, onChange }: Type) => {
const lookup = useStoreChain?.use?.lookup?.();
if (!lookup) {
return null;
}

try {
if (!shouldSkipRendering(args, lookup)) {
return (
<InvocationMapper
invokationVar={lookup!((args.type.value as { key: number }).key)}
onChange={onChange}
/>
);
} else {
return null;
}
} catch (error) {
console.error(error);
return <NotImplemented />;
}
};

export default InvocationStorageArgs;
1 change: 1 addition & 0 deletions src/components/decoder/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as InvocationDecoder } from './invocationDecoder';
52 changes: 52 additions & 0 deletions src/components/decoder/invocationDecoder.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/* eslint-disable react/jsx-no-bind */
/* eslint-disable react-hooks/exhaustive-deps */
import { useCallback } from 'react';

import { InvocationDecoderArgs } from '@components/decoder/invocationDecoderArgs';
import { NotImplemented } from '@components/invocationArgsMapper/notImplemented';

import styles from '../invocationArgsMapper/styles.module.css';

import type { InvocationDecoder as Type } from '@components/invocationArgsMapper/types';

const InvocationDecoder = ({ fields, onChange }: Type) => {
const handleOnChange = useCallback((index: number, args: unknown) => {
onChange(index, args);
}, []);

if (!fields) {
return null;
} else {
return (
<div className="flex flex-col gap-6 empty:hidden">
{
fields.map((field, index) => {
const { name, type, description } = field;
if (!type) {
return <NotImplemented key={`rpc-field-not-implemented-${name}`} />;
} else {
return (
<div key={`rpc-field-${name}-${type}`}>
<span className="block pb-1 font-geist capitalize font-body1-regular">
{name}
</span>
<div className={styles['invocationContainer']}>
<div className={styles['invocationGroup']}>
<InvocationDecoderArgs
decoder={field}
onChange={(args) => handleOnChange(index, args)}
placeholder={description}
/>
</div>
</div>
</div>
);
}
})
}
</div>
);
}
};

export default InvocationDecoder;
49 changes: 49 additions & 0 deletions src/components/decoder/invocationDecoderArgs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { NotImplemented } from '@components/invocationArgsMapper/notImplemented';
import { OrderBuilder } from '@components/metadataBuilders/orderBuilder';
import { PrimitiveBuilder } from '@components/metadataBuilders/primitiveBuilder';

import type { IDecoderBuilderProps } from '@components/invocationArgsMapper/types';
import type { InvocationDecoderArgs as Type } from '@constants/decoders/types';

const mapperCore: Record<Type['type'], (props: IDecoderBuilderProps) => JSX.Element> = {
array: (props) => (
<OrderBuilder
{...props}
sequence={{
type: 'sequence',
value: {
type: 'primitive',
value: 'str',
id: 1, /* hardcoding it does not cause a problem */
},
}}
/>
),
string: (props) => (
<PrimitiveBuilder
{...props}
primitive={{ value: 'str', type: 'primitive' }}
/>
),
hex: (props) => (
<PrimitiveBuilder
{...props}
primitive={{ value: 'str', type: 'primitive' }}
/>
),
};
export const InvocationDecoderArgs = (props: IDecoderBuilderProps) => {
if (!props) {
return null;
} else {
try {
const decoderType = props.decoder.type;
const InvocationComponent = mapperCore[decoderType] ?? NotImplemented;

return <InvocationComponent {...props} />;
} catch (error) {
console.error(error);
return <NotImplemented />;
}
}
};
1 change: 1 addition & 0 deletions src/components/decoderDynamic/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as InvocationDecoderDynamic } from './invocationDecoderDynamic';
28 changes: 28 additions & 0 deletions src/components/decoderDynamic/invocationDecoderDynamic.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { PrimitiveBuilder } from '@components/metadataBuilders/primitiveBuilder';

import styles from '../invocationArgsMapper/styles.module.css';

import type { InvocationDecoderDynamic as Type } from '@components/invocationArgsMapper/types';

const InvocationDecoderDynamic = ({ onChange }: Type) => {
return (
<div className="flex flex-col gap-6 empty:hidden">
<div>
<span className="block pb-1 font-geist capitalize font-body1-regular">
SCALE-encoded value
</span>
<div className={styles['invocationContainer']}>
<div className={styles['invocationGroup']}>
<PrimitiveBuilder
onChange={onChange}
placeholder="hex"
primitive={{ value: 'str', type: 'primitive' }}
/>
</div>
</div>
</div>
</div>
);
};

export default InvocationDecoderDynamic;
39 changes: 39 additions & 0 deletions src/components/invocationArgsMapper/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { NotImplemented } from '@components/invocationArgsMapper/notImplemented';
import { useStoreChain } from '@stores';

import { InvocationMapper } from './invocationMapper';

import type { InvocationArgsMapper as Type } from '@components/invocationArgsMapper/types';

export const InvocationArgsMapper = ({ invocationVar, onChange }: Type) => {
const lookup = useStoreChain?.use?.lookup?.();

try {
if (!lookup) {
return null;
} else {
if (!invocationVar?.type) {
return <NotImplemented />;
} else {
if (invocationVar.type !== 'lookupEntry') {
return (
<InvocationMapper
invokationVar={invocationVar}
onChange={onChange}
/>
);
} else {
return (
<InvocationMapper
invokationVar={lookup(invocationVar.value.id)}
onChange={onChange}
/>
);
}
}
}
} catch (error) {
console.error(error);
return <NotImplemented />;
}
};
Loading