-
Notifications
You must be signed in to change notification settings - Fork 19
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
Optimize: evm params #244
base: master
Are you sure you want to change the base?
Optimize: evm params #244
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) 环境变量配置正确且安全! 环境变量的添加方式符合最佳实践,使用了 GitHub secrets 来存储敏感信息。建议在项目文档中说明此 API 密钥的用途,以便其他开发者理解。 |
||
|
||
- name: Build Example Web | ||
run: | | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -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 | ||||||||||||||||||||||||||||||||||
Comment on lines
+5
to
+7
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) 建议:改进环境变量的文档说明和安全性 建议对每个 API 密钥添加更详细的说明,包括:
同时建议:
# wallet connect v2 project id
NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID=xxxx
NEXT_PUBLIC_WALLET_CONNECT_RELAY_URL=wss://relay.walletconnect.com
# Blockfrost API key for Cardano integration
# Required for Cardano network operations
# Get your key at: https://blockfrost.io
NEXT_PUBLIC_BLOCKFROST_CARDANO_PROJECT_ID=xxxx
# Oklink API key for blockchain data
# Required for asset management features
# Get your key at: https://www.oklink.com
NEXT_PUBLIC_OKLINK_API_KEY=xxxx 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||
NEXT_PUBLIC_OKLINK_API_KEY=xxxx |
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'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+21
to
+23
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) 建议优化错误提示信息 当前的错误信息过于简单。建议添加更多上下文信息,以帮助开发者快速定位问题。 - if (!context) throw new Error('ApiField must be used within ApiForm');
+ if (!context) throw new Error('ApiAutoTextArea 组件必须在 ApiForm 组件内使用。请检查组件层级结构。'); 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+32
to
+39
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 需要修复类型安全问题 onChange 事件处理器中的值需要明确类型定义。 - onChange={(e) => setField({ ...field, value: e.target.value })}
+ onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => setField({ ...field, value: e.target.value })} 📝 Committable suggestion
Suggested change
🧰 Tools🪛 eslint[error] 36-36: Unsafe return of an (@typescript-eslint/no-unsafe-return) |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{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> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+50
to
+67
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) 建议增加无障碍访问支持 组件缺少必要的 ARIA 属性,这可能影响屏幕阅读器用户的使用体验。 <Label htmlFor={id}>
{label}
- {required && <span className="text-red-500">*</span>}
+ {required && <span className="text-red-500" aria-label="必填字段">*</span>}
</Label> 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ApiAutoTextArea.displayName = 'ApiAutoTextArea'; |
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)); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 修复在循环中使用 Hook 的问题。 不能在循环或条件中调用 提供修复建议: - const dependencyStates = availableDependencyFields?.map(fieldId => {
- const [field] = useAtom(store.fieldsAtom(fieldId));
- return {
- id: fieldId,
- value: field.value,
- name: field.name
- };
- }) ?? [];
+ const dependencyAtoms = availableDependencyFields?.map(fieldId => store.fieldsAtom(fieldId)) ?? [];
+ const dependencyFields = dependencyAtoms.map(atom => {
+ const [field] = useAtom(atom);
+ return {
+ id: field.id,
+ value: field.value,
+ name: field.name
+ };
+ });
🧰 Tools🪛 eslint[error] 41-41: React Hook "useAtom" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function. (react-hooks/rules-of-hooks) |
||||||
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(', ')}`; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 修复返回未安全类型值的问题。 第 55 行可能返回 提供修复建议: - return `请填写 ${filterFields.map(field => field.name ?? field.id).join(', ')}`;
+ return `请填写 ${filterFields.map((field: { name: string; id: string }) => field.name ?? field.id).join(', ')}`; 📝 Committable suggestion
Suggested change
🧰 Tools🪛 eslint[error] 55-55: Unsafe return of an (@typescript-eslint/no-unsafe-return) |
||||||
} | ||||||
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} | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 避免将异步函数直接赋给 onClick。
提供修复建议: - onClick={handleClick}
+ onClick={() => { void handleClick(); }} 📝 Committable suggestion
Suggested change
🧰 Tools🪛 eslint[error] 102-102: Promise-returning function provided to attribute where a void return was expected. (@typescript-eslint/no-misused-promises) |
||||||
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'; |
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; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+6
to
+10
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) 建议增强类型安全性 建议对
export interface ApiCheckboxProps {
id: string;
- label?: string;
+ label: string;
defaultChecked?: boolean;
} 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+20
to
+21
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 需要修复类型安全问题
+ type FieldState = {
+ name?: string;
+ value: boolean;
+ error?: string;
+ required?: boolean;
+ disabled?: boolean;
+ };
- const [field, setField] = useAtom(store.fieldsAtom(id));
+ const [field, setField] = useAtom<FieldState>(store.fieldsAtom(id));
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
useEffect(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
field.name = label; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, []); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
useEffect(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (defaultChecked) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setField({ ...field, value: defaultChecked }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, []); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+23
to
+31
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 建议优化 useEffect 逻辑 当前实现存在以下问题:
- useEffect(() => {
- field.name = label;
- }, []);
-
- useEffect(() => {
- if (defaultChecked) {
- setField({ ...field, value: defaultChecked });
- }
- }, []);
+ useEffect(() => {
+ setField(prev => ({
+ ...prev,
+ name: label,
+ value: defaultChecked ?? prev.value
+ }));
+ }, [label, defaultChecked]); 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+33
to
+53
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) 建议改进可访问性和用户体验 当前实现可以通过以下方式改进:
<div className="flex items-center gap-2">
<Checkbox
id={id}
+ aria-describedby={`${id}-error`}
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}
+ {field.required && <span className="text-red-500 ml-1">*</span>}
</label>
{field.error && (
- <div className="text-sm text-red-500">{field.error}</div>
+ <div id={`${id}-error`} className="text-sm text-red-500 mt-1" role="alert">
+ {field.error}
+ </div>
)}
</div> 📝 Committable suggestion
Suggested change
🧰 Tools🪛 eslint[error] 39-39: Unsafe return of an (@typescript-eslint/no-unsafe-return) |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ApiCheckbox.displayName = 'ApiCheckbox'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick (assertive)
环境变量添加正确且安全!
环境变量的添加遵循了最佳实践:
建议在代码注释中说明这个 API key 的用途,方便其他开发者理解。
echo "NEXT_PUBLIC_BLOCKFROST_CARDANO_PROJECT_ID=${{ secrets.BLOCKFROST_CARDANO_PROJECT_ID }}" >> ./packages/example/.env +# OKLink API key for blockchain data queries echo "NEXT_PUBLIC_OKLINK_API_KEY=${{ secrets.OKLINK_API_KEY }}" >> ./packages/example/.env
📝 Committable suggestion