优化体验,修复 bug
This commit is contained in:
@@ -365,12 +365,15 @@
|
|||||||
"shareCopied": "Code copied",
|
"shareCopied": "Code copied",
|
||||||
"shareGenerating": "Generating…",
|
"shareGenerating": "Generating…",
|
||||||
"shareCreateFail": "Failed to generate share code",
|
"shareCreateFail": "Failed to generate share code",
|
||||||
|
"shareActiveExists": "You already have an active share code. Please try again later.",
|
||||||
|
"shareInvalidParams": "Invalid request. Please check and try again.",
|
||||||
|
"shareSystemError": "System error. Please try again later.",
|
||||||
"shareExpireAt": "Expires at",
|
"shareExpireAt": "Expires at",
|
||||||
"importTab": "Import EQ",
|
"importTab": "Import EQ",
|
||||||
"importCodeHint": "Enter or paste the 5-character share code",
|
"importCodeHint": "Enter or paste the 5-character share code",
|
||||||
"importQuery": "Query",
|
"importQuery": "Query",
|
||||||
"importQuerying": "Querying…",
|
"importQuerying": "Querying…",
|
||||||
"importCodeNotFound": "Share code not found",
|
"importCodeNotFound": "Share code not found or expired",
|
||||||
"importEqName": "EQ Name",
|
"importEqName": "EQ Name",
|
||||||
"importButton": "Import",
|
"importButton": "Import",
|
||||||
"importSaving": "Importing…",
|
"importSaving": "Importing…",
|
||||||
@@ -381,8 +384,7 @@
|
|||||||
"mySharesEmpty": "No shared EQ yet",
|
"mySharesEmpty": "No shared EQ yet",
|
||||||
"mySharesCode": "Code",
|
"mySharesCode": "Code",
|
||||||
"mySharesName": "EQ Name",
|
"mySharesName": "EQ Name",
|
||||||
"mySharesLoadFail": "Failed to load share list",
|
"mySharesLoadFail": "Failed to load share list"
|
||||||
"importCodeNotFound": "Share code not found or expired"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dac": {
|
"dac": {
|
||||||
|
|||||||
@@ -301,6 +301,9 @@
|
|||||||
"shareCopied": "已複製分享碼",
|
"shareCopied": "已複製分享碼",
|
||||||
"shareGenerating": "產生中…",
|
"shareGenerating": "產生中…",
|
||||||
"shareCreateFail": "產生分享碼失敗",
|
"shareCreateFail": "產生分享碼失敗",
|
||||||
|
"shareActiveExists": "已有未過期的分享碼,請稍後再試",
|
||||||
|
"shareInvalidParams": "請求參數無效,請檢查後重試",
|
||||||
|
"shareSystemError": "系統錯誤,請稍後重試",
|
||||||
"shareExpireAt": "有效期至",
|
"shareExpireAt": "有效期至",
|
||||||
"importTab": "匯入 EQ",
|
"importTab": "匯入 EQ",
|
||||||
"importCodeHint": "輸入或貼上 5 位分享碼",
|
"importCodeHint": "輸入或貼上 5 位分享碼",
|
||||||
|
|||||||
@@ -301,6 +301,9 @@
|
|||||||
"shareCopied": "已复制分享码",
|
"shareCopied": "已复制分享码",
|
||||||
"shareGenerating": "生成中…",
|
"shareGenerating": "生成中…",
|
||||||
"shareCreateFail": "生成分享码失败",
|
"shareCreateFail": "生成分享码失败",
|
||||||
|
"shareActiveExists": "已有未过期的分享码,请稍后再试",
|
||||||
|
"shareInvalidParams": "请求参数无效,请检查后重试",
|
||||||
|
"shareSystemError": "系统错误,请稍后重试",
|
||||||
"shareExpireAt": "有效期至",
|
"shareExpireAt": "有效期至",
|
||||||
"importTab": "导入 EQ",
|
"importTab": "导入 EQ",
|
||||||
"importCodeHint": "输入或粘贴 5 位分享码",
|
"importCodeHint": "输入或粘贴 5 位分享码",
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import {
|
|||||||
type PeqChangePayload,
|
type PeqChangePayload,
|
||||||
type PeqState,
|
type PeqState,
|
||||||
buildPeqPresetBody,
|
buildPeqPresetBody,
|
||||||
acceptShareCode,
|
|
||||||
} from "@/lib/luxsinApi";
|
} from "@/lib/luxsinApi";
|
||||||
import {
|
import {
|
||||||
getFilterType,
|
getFilterType,
|
||||||
@@ -719,16 +718,6 @@ export default function EQPage() {
|
|||||||
|
|
||||||
skipPeqAutoSyncRef.current = true;
|
skipPeqAutoSyncRef.current = true;
|
||||||
try {
|
try {
|
||||||
const mac = deviceState?.mac ?? "";
|
|
||||||
if (!isDemoMode && mac) {
|
|
||||||
const acceptRes = await acceptShareCode(mac, shareCode.trim());
|
|
||||||
if (acceptRes.code !== 200) {
|
|
||||||
toast.error(eqUi.importFail ?? eqUi.importCodeNotFound);
|
|
||||||
skipPeqAutoSyncRef.current = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await upgradePeqChange(payload);
|
await upgradePeqChange(payload);
|
||||||
if (api && !isDemoMode) {
|
if (api && !isDemoMode) {
|
||||||
const latest = await fetchEqSyncPeq(api, "importSharedEq");
|
const latest = await fetchEqSyncPeq(api, "importSharedEq");
|
||||||
@@ -1850,6 +1839,7 @@ export default function EQPage() {
|
|||||||
eqUi={eqUi}
|
eqUi={eqUi}
|
||||||
peqCardLabels={peqCardLabels}
|
peqCardLabels={peqCardLabels}
|
||||||
mac={deviceState?.mac ?? ""}
|
mac={deviceState?.mac ?? ""}
|
||||||
|
isDemoMode={isDemoMode}
|
||||||
onImportEq={handleImportSharedEq}
|
onImportEq={handleImportSharedEq}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,9 @@ import { useState } from "react";
|
|||||||
import { X, Copy, Loader2 } from "lucide-react";
|
import { X, Copy, Loader2 } from "lucide-react";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { createShareCode, listShareCodes, queryShareCode } from "@/lib/luxsinApi";
|
import { acceptShareCode, createShareCode, listShareCodes, queryShareCode } from "@/lib/luxsinApi";
|
||||||
import type { PeqEqUi } from "../constants";
|
import type { PeqEqUi } from "../constants";
|
||||||
|
import { resolveShareApiMessage } from "../shareMessages";
|
||||||
|
|
||||||
type PeqItem = {
|
type PeqItem = {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -39,6 +40,7 @@ export function ShareDialog({
|
|||||||
eqUi,
|
eqUi,
|
||||||
peqCardLabels,
|
peqCardLabels,
|
||||||
mac,
|
mac,
|
||||||
|
isDemoMode,
|
||||||
onImportEq,
|
onImportEq,
|
||||||
}: {
|
}: {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@@ -48,6 +50,7 @@ export function ShareDialog({
|
|||||||
eqUi: PeqEqUi;
|
eqUi: PeqEqUi;
|
||||||
peqCardLabels: PeqCardLabels;
|
peqCardLabels: PeqCardLabels;
|
||||||
mac: string;
|
mac: string;
|
||||||
|
isDemoMode: boolean;
|
||||||
onImportEq: (data: ImportedEqData, shareCode: string) => void | Promise<void>;
|
onImportEq: (data: ImportedEqData, shareCode: string) => void | Promise<void>;
|
||||||
}) {
|
}) {
|
||||||
const [shareDialogTab, setShareDialogTab] = useState<"share" | "import" | "myShares">("share");
|
const [shareDialogTab, setShareDialogTab] = useState<"share" | "import" | "myShares">("share");
|
||||||
@@ -64,6 +67,26 @@ export function ShareDialog({
|
|||||||
const [mySharesList, setMySharesList] = useState<Array<{ share_code: string; expire_at: string; eq_data: Record<string, unknown> }>>([]);
|
const [mySharesList, setMySharesList] = useState<Array<{ share_code: string; expire_at: string; eq_data: Record<string, unknown> }>>([]);
|
||||||
const [mySharesLoading, setMySharesLoading] = useState(false);
|
const [mySharesLoading, setMySharesLoading] = useState(false);
|
||||||
|
|
||||||
|
const handleImportClick = async () => {
|
||||||
|
if (importSaving || !importedEqData || !queriedShareCode) return;
|
||||||
|
const nextName = importPresetName.trim();
|
||||||
|
if (!nextName) return;
|
||||||
|
|
||||||
|
setImportSaving(true);
|
||||||
|
try {
|
||||||
|
if (!isDemoMode && mac) {
|
||||||
|
const acceptRes = await acceptShareCode(mac, queriedShareCode);
|
||||||
|
if (acceptRes.code !== 200) {
|
||||||
|
toast.error(resolveShareApiMessage("accept", acceptRes, eqUi));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await onImportEq({ ...importedEqData, name: nextName }, queriedShareCode);
|
||||||
|
} finally {
|
||||||
|
setImportSaving(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (!open) return null;
|
if (!open) return null;
|
||||||
|
|
||||||
/* ── clipboard helper ── */
|
/* ── clipboard helper ── */
|
||||||
@@ -87,7 +110,9 @@ export function ShareDialog({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="fixed inset-0 z-[120] flex items-center justify-center bg-black/65 px-4"
|
className="fixed inset-0 z-[120] flex items-center justify-center bg-black/65 px-4"
|
||||||
onClick={onClose}
|
onClick={() => {
|
||||||
|
if (!importSaving) onClose();
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="w-full max-w-[420px] rounded-[14px] p-5"
|
className="w-full max-w-[420px] rounded-[14px] p-5"
|
||||||
@@ -142,12 +167,12 @@ export function ShareDialog({
|
|||||||
try {
|
try {
|
||||||
const res = await listShareCodes(mac);
|
const res = await listShareCodes(mac);
|
||||||
if (res.code !== 200) {
|
if (res.code !== 200) {
|
||||||
toast.error(eqUi.mySharesLoadFail);
|
toast.error(resolveShareApiMessage("list", res, eqUi));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setMySharesList(res.share_codes ?? []);
|
setMySharesList(res.share_codes ?? []);
|
||||||
} catch {
|
} catch {
|
||||||
toast.error(eqUi.mySharesLoadFail);
|
toast.error(resolveShareApiMessage("list", { code: 500 }, eqUi));
|
||||||
} finally {
|
} finally {
|
||||||
setMySharesLoading(false);
|
setMySharesLoading(false);
|
||||||
}
|
}
|
||||||
@@ -160,7 +185,8 @@ export function ShareDialog({
|
|||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="rounded-full p-1.5 text-white/45 transition-colors hover:bg-white/10 hover:text-white/80"
|
className="rounded-full p-1.5 text-white/45 transition-colors hover:bg-white/10 hover:text-white/80 disabled:opacity-40 disabled:pointer-events-none"
|
||||||
|
disabled={importSaving}
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
>
|
>
|
||||||
<X size={20} />
|
<X size={20} />
|
||||||
@@ -236,13 +262,13 @@ export function ShareDialog({
|
|||||||
};
|
};
|
||||||
const res = await createShareCode(mac, eqData);
|
const res = await createShareCode(mac, eqData);
|
||||||
if (res.code !== 200) {
|
if (res.code !== 200) {
|
||||||
toast.error(res.msg || eqUi.shareCreateFail);
|
toast.error(resolveShareApiMessage("create", res, eqUi));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setShareCode(res.share_code ?? "");
|
setShareCode(res.share_code ?? "");
|
||||||
setShareCodeExpireAt(res.expire_at ?? null);
|
setShareCodeExpireAt(res.expire_at ?? null);
|
||||||
} catch {
|
} catch {
|
||||||
toast.error(eqUi.shareCreateFail);
|
toast.error(resolveShareApiMessage("create", { code: 500 }, eqUi));
|
||||||
} finally {
|
} finally {
|
||||||
setShareGenerating(false);
|
setShareGenerating(false);
|
||||||
}
|
}
|
||||||
@@ -391,12 +417,12 @@ export function ShareDialog({
|
|||||||
try {
|
try {
|
||||||
const res = await queryShareCode(code);
|
const res = await queryShareCode(code);
|
||||||
if (res.code !== 200) {
|
if (res.code !== 200) {
|
||||||
toast.error(eqUi.importCodeNotFound);
|
toast.error(resolveShareApiMessage("query", res, eqUi));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const eq = res.eq_data;
|
const eq = res.eq_data;
|
||||||
if (!eq) {
|
if (!eq) {
|
||||||
toast.error(eqUi.importCodeNotFound);
|
toast.error(resolveShareApiMessage("query", { code: 0 }, eqUi));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const fetchedName = (eq.name as string) || "";
|
const fetchedName = (eq.name as string) || "";
|
||||||
@@ -413,7 +439,7 @@ export function ShareDialog({
|
|||||||
setImportPresetName(fetchedName);
|
setImportPresetName(fetchedName);
|
||||||
setQueriedShareCode(code);
|
setQueriedShareCode(code);
|
||||||
} catch {
|
} catch {
|
||||||
toast.error(eqUi.importCodeNotFound);
|
toast.error(resolveShareApiMessage("query", { code: 500 }, eqUi));
|
||||||
} finally {
|
} finally {
|
||||||
setImportQuerying(false);
|
setImportQuerying(false);
|
||||||
}
|
}
|
||||||
@@ -451,17 +477,7 @@ export function ShareDialog({
|
|||||||
? "bg-[#00FFF6]/40 cursor-not-allowed"
|
? "bg-[#00FFF6]/40 cursor-not-allowed"
|
||||||
: "bg-[#00FFF6] hover:brightness-95",
|
: "bg-[#00FFF6] hover:brightness-95",
|
||||||
)}
|
)}
|
||||||
onClick={() => {
|
onClick={() => void handleImportClick()}
|
||||||
if (importSaving || !importedEqData || !queriedShareCode) return;
|
|
||||||
const nextName = importPresetName.trim();
|
|
||||||
if (!nextName) return;
|
|
||||||
setImportSaving(true);
|
|
||||||
void Promise.resolve(
|
|
||||||
onImportEq({ ...importedEqData, name: nextName }, queriedShareCode),
|
|
||||||
).finally(() => {
|
|
||||||
setImportSaving(false);
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{importSaving ? (
|
{importSaving ? (
|
||||||
<span className="flex items-center justify-center gap-1.5">
|
<span className="flex items-center justify-center gap-1.5">
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import type { PeqEqUi } from "./constants";
|
||||||
|
|
||||||
|
export type ShareApiAction = "create" | "query" | "accept" | "list";
|
||||||
|
|
||||||
|
export function resolveShareApiMessage(
|
||||||
|
action: ShareApiAction,
|
||||||
|
res: { code: number; msg?: string },
|
||||||
|
eqUi: PeqEqUi,
|
||||||
|
): string {
|
||||||
|
if (res.code === 200) return "";
|
||||||
|
|
||||||
|
if (action === "list") {
|
||||||
|
return eqUi.mySharesLoadFail ?? "Failed to load share list";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res.code === 0) {
|
||||||
|
if (action === "create") {
|
||||||
|
return eqUi.shareActiveExists ?? eqUi.shareCreateFail ?? "Failed to generate share code";
|
||||||
|
}
|
||||||
|
return eqUi.importCodeNotFound ?? "Share code not found or expired";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res.code === 400) {
|
||||||
|
return eqUi.shareInvalidParams ?? eqUi.shareCreateFail ?? "Invalid request";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res.code === 500) {
|
||||||
|
return eqUi.shareSystemError ?? eqUi.shareCreateFail ?? "System error";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action === "create") {
|
||||||
|
return eqUi.shareCreateFail ?? "Failed to generate share code";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action === "accept") {
|
||||||
|
return eqUi.importFail ?? eqUi.importCodeNotFound ?? "Failed to import EQ";
|
||||||
|
}
|
||||||
|
|
||||||
|
return eqUi.importCodeNotFound ?? "Share code not found or expired";
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user