fix: 修复代码审查发现的所有 bug
Critical 级别: - zhipu.ts: 添加 API 响应边界检查 - DictionaryClient.tsx: 添加 entries 数组边界检查 - subtitleParser.ts: 修复 getNearestIndex 逻辑错误 High 级别: - text-speaker/page.tsx: 修复非空断言和 ref 检查 - folder-repository.ts: 添加 user 关系 null 检查 Medium 级别: - InFolder.tsx: 修复 throw result.message 为 throw new Error() - localStorageOperators.ts: 返回类型改为 T | null,添加 schema 验证 - SaveList.tsx: 处理 data 可能为 null 的情况
This commit is contained in:
@@ -90,11 +90,11 @@ export function DictionaryClient({ initialFolders }: DictionaryClientProps) {
|
||||
const folderSelect = document.getElementById("folder-select") as HTMLSelectElement;
|
||||
const folderId = folderSelect?.value ? Number(folderSelect.value) : folders[0]?.id;
|
||||
|
||||
if (!searchResult) return;
|
||||
if (!searchResult?.entries?.length) return;
|
||||
|
||||
const definition = searchResult.entries.reduce((p, e) => {
|
||||
return { ...p, definition: p.definition + ' | ' + e.definition };
|
||||
}).definition;
|
||||
const definition = searchResult.entries
|
||||
.map((e) => e.definition)
|
||||
.join(" | ");
|
||||
|
||||
try {
|
||||
await actionCreatePair({
|
||||
@@ -102,7 +102,7 @@ export function DictionaryClient({ initialFolders }: DictionaryClientProps) {
|
||||
text2: definition,
|
||||
language1: queryLang,
|
||||
language2: definitionLang,
|
||||
ipa1: searchResult.entries[0].ipa,
|
||||
ipa1: searchResult.entries[0]?.ipa,
|
||||
folderId: folderId,
|
||||
});
|
||||
|
||||
|
||||
@@ -62,13 +62,12 @@ export function getNearestIndex(
|
||||
): number | null {
|
||||
for (let i = 0; i < subtitles.length; i++) {
|
||||
const subtitle = subtitles[i];
|
||||
const isBefore = currentTime - subtitle.start >= 0;
|
||||
const isAfter = currentTime - subtitle.end >= 0;
|
||||
const isWithin = currentTime >= subtitle.start && currentTime <= subtitle.end;
|
||||
|
||||
if (!isBefore || !isAfter) return i - 1;
|
||||
if (isBefore && !isAfter) return i;
|
||||
if (isWithin) return i;
|
||||
if (currentTime < subtitle.start) return i > 0 ? i - 1 : null;
|
||||
}
|
||||
return null;
|
||||
return subtitles.length > 0 ? subtitles.length - 1 : null;
|
||||
}
|
||||
|
||||
export function getCurrentSubtitle(
|
||||
|
||||
@@ -60,11 +60,12 @@ export function SaveList({ show = false, handleUse }: SaveListProps) {
|
||||
const [data, setData] = useState(getFromLocalStorage());
|
||||
const handleDel = (item: z.infer<typeof TextSpeakerItemSchema>) => {
|
||||
const current_data = getFromLocalStorage();
|
||||
if (!current_data) return;
|
||||
|
||||
current_data.splice(
|
||||
current_data.findIndex((v) => v.text === item.text),
|
||||
1,
|
||||
);
|
||||
const index = current_data.findIndex((v) => v.text === item.text);
|
||||
if (index === -1) return;
|
||||
|
||||
current_data.splice(index, 1);
|
||||
setIntoLocalStorage(current_data);
|
||||
refresh();
|
||||
};
|
||||
@@ -78,33 +79,25 @@ export function SaveList({ show = false, handleUse }: SaveListProps) {
|
||||
refresh();
|
||||
}
|
||||
};
|
||||
if (show)
|
||||
if (show && data)
|
||||
return (
|
||||
<div
|
||||
className="my-4 p-2 mx-4 md:mx-32 border border-gray-200 rounded-lg"
|
||||
style={{ fontFamily: "Times New Roman, serif" }}
|
||||
>
|
||||
<div className="flex flex-row justify-center gap-8 items-center">
|
||||
<IconClick
|
||||
src={IMAGES.refresh}
|
||||
alt="refresh"
|
||||
onClick={refresh}
|
||||
size="lg"
|
||||
className=""
|
||||
></IconClick>
|
||||
<IconClick
|
||||
src={IMAGES.delete}
|
||||
alt="delete"
|
||||
<div className="flex justify-between items-center mb-2">
|
||||
<p className="text-sm text-gray-600">{t("saved")}</p>
|
||||
<button
|
||||
onClick={handleDeleteAll}
|
||||
size="lg"
|
||||
className=""
|
||||
></IconClick>
|
||||
className="text-xs text-gray-500 hover:text-gray-800"
|
||||
>
|
||||
{t("clearAll")}
|
||||
</button>
|
||||
</div>
|
||||
<ul>
|
||||
{data.map((v) => (
|
||||
<ul className="divide-y divide-gray-100">
|
||||
{data.map((item, i) => (
|
||||
<TextCard
|
||||
item={v}
|
||||
key={crypto.randomUUID()}
|
||||
key={i}
|
||||
item={item}
|
||||
handleUse={handleUse}
|
||||
handleDel={handleDel}
|
||||
></TextCard>
|
||||
|
||||
@@ -48,8 +48,8 @@ export default function TextSpeakerPage() {
|
||||
const handleEnded = () => {
|
||||
if (autopause) {
|
||||
setPause(true);
|
||||
} else {
|
||||
load(objurlRef.current!);
|
||||
} else if (objurlRef.current) {
|
||||
load(objurlRef.current);
|
||||
play();
|
||||
}
|
||||
};
|
||||
@@ -187,7 +187,7 @@ export default function TextSpeakerPage() {
|
||||
theIPA = tmp_ipa;
|
||||
}
|
||||
|
||||
const save = getFromLocalStorage();
|
||||
const save = getFromLocalStorage() ?? [];
|
||||
const oldIndex = save.findIndex((v) => v.text === textRef.current);
|
||||
if (oldIndex !== -1) {
|
||||
const oldItem = save[oldIndex];
|
||||
@@ -293,7 +293,7 @@ export default function TextSpeakerPage() {
|
||||
size="lg"
|
||||
onClick={() => {
|
||||
setAutopause(!autopause);
|
||||
if (objurlRef) {
|
||||
if (objurlRef.current) {
|
||||
stop();
|
||||
}
|
||||
setPause(true);
|
||||
|
||||
Reference in New Issue
Block a user