Skip to content

Commit

Permalink
Optimize: evm params (#244)
Browse files Browse the repository at this point in the history
* chore: optimize example

* chore: optimize evm example

* chore: optimize sui、tron、cfx example

* chore: optimize sui example

* chore: optimize evm example
  • Loading branch information
ByteZhang1024 authored Dec 9, 2024
1 parent d780526 commit e57d01c
Show file tree
Hide file tree
Showing 50 changed files with 5,447 additions and 182 deletions.
1 change: 1 addition & 0 deletions .github/workflows/deploy-dapp-example-web.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ jobs:
# Next.js environment variables
echo "NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID=${{ secrets.WALLET_CONNECT_PROJECT_ID }}" > ./packages/example/.env
echo "NEXT_PUBLIC_BLOCKFROST_CARDANO_PROJECT_ID=${{ secrets.BLOCKFROST_CARDANO_PROJECT_ID }}" >> ./packages/example/.env
echo "NEXT_PUBLIC_OKLINK_API_KEY=${{ secrets.EXAMPLE_OKLINK_API_KEY }}" >> ./packages/example/.env
- name: Install Dependency
env:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/publish-npm-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ jobs:
# Next.js environment variables
echo "NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID=${{ secrets.WALLET_CONNECT_PROJECT_ID }}" > ./packages/example/.env
echo "NEXT_PUBLIC_BLOCKFROST_CARDANO_PROJECT_ID=${{ secrets.BLOCKFROST_CARDANO_PROJECT_ID }}" >> ./packages/example/.env
echo "NEXT_PUBLIC_OKLINK_API_KEY=${{ secrets.OKLINK_API_KEY }}" >> ./packages/example/.env
- name: Build Example Web
run: |
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/versionInfo.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

const version = '2.1.20';
const version = '2.1.21';
const versionBuild = '2020-0101-1';

export default {
Expand Down
4 changes: 3 additions & 1 deletion packages/example/.env.simple
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@
NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID=xxxx
NEXT_PUBLIC_WALLET_CONNECT_RELAY_URL=wss://relay.walletconnect.com
# block frost project id
NEXT_PUBLIC_BLOCKFROST_CARDANO_PROJECT_ID=xxxx
NEXT_PUBLIC_BLOCKFROST_CARDANO_PROJECT_ID=xxxx
# oklink api key
NEXT_PUBLIC_OKLINK_API_KEY=xxxx
6 changes: 6 additions & 0 deletions packages/example/chains.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ export const registeredChains: IChainInfo[] = [
href: '/aptosMartian',
icon: 'https://uni.onekey-asset.com/static/chain/apt.png',
},
{
id: uuid.v4(),
name: 'Benfen',
href: '/benfen',
icon: 'https://uni.onekey-asset.com/static/chain/bfc.png',
},
{
id: uuid.v4(),
name: 'BTC',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ type ApiPayloadAction =
| { type: 'SET_PRESUPPOSE_PARAMS'; payload: IPresupposeParam[] };

// 优化 JSON 格式化函数
const tryFormatJson = (json: string) => {
export const tryFormatJson = (json: string) => {
try {
return JSON.stringify(JSON.parse(json), null, 2);
} catch {
Expand Down
69 changes: 69 additions & 0 deletions packages/example/components/ApiForm/ApiAutoTextArea.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import React, { memo, useContext, useEffect } from 'react';
import { useAtom } from 'jotai';
import { Label } from '../ui/label';
import { ApiFormContext } from './ApiForm';
import { AutoHeightTextarea } from '../ui/textarea';


interface AutoTextAreaProps {
id: string;
placeholder?: string;
label?: string;
required?: boolean;
}

const TextArea = memo(({
id,
placeholder,
label,
required
}: AutoTextAreaProps) => {
const context = useContext(ApiFormContext);
if (!context) throw new Error('ApiField must be used within ApiForm');

const { store } = context;
const [field, setField] = useAtom(store.fieldsAtom(id));

useEffect(() => {
field.name = label;
field.required = required;
}, []);

return <>
<AutoHeightTextarea
id={id}
value={field.value}
onChange={(e) => setField({ ...field, value: e.target.value })}
placeholder={placeholder}
disabled={field.disabled}
/>
{field.error && (
<div className="text-sm text-red-500">{field.error}</div>
)}
</>
});

export interface ApiAutoTextAreaProps extends AutoTextAreaProps {
id: string;
}

export const ApiAutoTextArea = memo(({
id,
label,
placeholder,
required
}: ApiAutoTextAreaProps) => {
return (
<div>
{label && (
<Label htmlFor={id}>
{label}
{required && <span className="text-red-500">*</span>}
</Label>
)}
<TextArea id={id} placeholder={placeholder} label={label} required={required} />
</div>
);
});

ApiAutoTextArea.displayName = 'ApiAutoTextArea';
114 changes: 114 additions & 0 deletions packages/example/components/ApiForm/ApiButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import React, { memo, useContext, useCallback, useMemo, useEffect } from 'react';
import { useAtom } from 'jotai';
import { Button } from '../ui/button';
import { ApiFormContext } from './ApiForm';
import { useValidation } from './hooks/useValidation';
import { get, isEmpty } from 'lodash';
import { toast } from '../ui/use-toast';

export interface ApiButtonProps {
id: string;
label: string;
onClick: () => Promise<void>;
validation?: {
fields: string[];
validator?: (values: Record<string, { id: string; value: string; required: boolean }>) => string | undefined;
};
availableDependencyFields?: string[];
}

export const ApiButton = memo(({
id,
label,
onClick,
validation,
availableDependencyFields,
}: ApiButtonProps) => {
const context = useContext(ApiFormContext);
if (!context) throw new Error('ApiButton must be used within ApiForm');

const { store } = context;
const [field, setField] = useAtom(store.fieldsAtom(id));

const loading = field.extra?.loading ?? false;
const result = field.extra?.result;

useEffect(() => {
field.name = label;
}, []);

const dependencyStates = availableDependencyFields?.map(fieldId => {
const [field] = useAtom(store.fieldsAtom(fieldId));
return {
id: fieldId,
value: field.value,
name: field.name
};
}) ?? [];

const disabledTooltip = useMemo(() => {
const filterFields = dependencyStates.filter(field =>
(field.value == null || isEmpty(field.value))
);

if (filterFields.length > 0) {
return `请填写 ${filterFields.map(field => field.name ?? field.id).join(', ')}`;
}
return null;
}, [dependencyStates]);

const setResult = (value: string) => {
setField({ ...field, extra: { ...field.extra, result: value } });
}

const setLoading = (value: boolean) => {
setField({ ...field, extra: { ...field.extra, loading: value } });
}

const { validate } = useValidation({
store,
validation
});

const handleClick = useCallback(async () => {
setResult(undefined);

const { isValid, error } = validate();
if (!isValid) {
setResult(error || '验证失败');
return;
}

try {
setLoading(true);
await onClick();
} catch (error) {
const errorMessage = get(error, 'message', 'error') ?? JSON.stringify(error);
toast({
title: '执行失败',
description: errorMessage,
variant: 'destructive',
});
setResult(errorMessage);
} finally {
setLoading(false);
}
}, [onClick, validate, setLoading, setResult]);

return (
<div className="flex flex-col gap-1">
<Button
key={id}
onClick={handleClick}
disabled={disabledTooltip != null}
loading={loading}
>
{label}
</Button>
{disabledTooltip && <div className="text-red-500 text-sm">{disabledTooltip}</div>}
{result && <div className="text-red-500 text-sm">{result}</div>}
</div>
);
});

ApiButton.displayName = 'ApiButton';
56 changes: 56 additions & 0 deletions packages/example/components/ApiForm/ApiCheckbox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React, { memo, useContext, useEffect } from 'react';
import { useAtom } from 'jotai';
import { ApiFormContext } from './ApiForm';
import { Checkbox } from '../ui/checkbox';

export interface ApiCheckboxProps {
id: string;
label?: string;
defaultChecked?: boolean;
}

export const ApiCheckbox = memo(({
id,
label,
defaultChecked
}: ApiCheckboxProps) => {
const context = useContext(ApiFormContext);
if (!context) throw new Error('ApiField must be used within ApiForm');

const { store } = context;
const [field, setField] = useAtom(store.fieldsAtom(id));

useEffect(() => {
field.name = label;
}, []);

useEffect(() => {
if (defaultChecked) {
setField({ ...field, value: defaultChecked });
}
}, []);

return <div className="flex items-center gap-2">
<Checkbox
id={id}
defaultChecked={defaultChecked}
required={field.required}
checked={field.value}
onCheckedChange={(e) => setField({ ...field, value: e })}
disabled={field.disabled}
/>

<label
htmlFor={id}
className="p-0 m-0 text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
{label}
</label>

{field.error && (
<div className="text-sm text-red-500">{field.error}</div>
)}
</div>
});

ApiCheckbox.displayName = 'ApiCheckbox';
Loading

0 comments on commit e57d01c

Please sign in to comment.