0.3.5 共享功能修复

This commit is contained in:
liaibo
2026-01-24 12:32:51 +08:00
parent 25923f308c
commit 3c3f407f4b
44 changed files with 3754 additions and 420 deletions
+55 -14
View File
@@ -6,6 +6,7 @@ import { useState, useCallback, useEffect } from "react";
import { useRouter } from "next/navigation";
import { isConvexEnabled } from "@/lib/convex/enabled";
import { api } from "@/lib/convex/api";
import { getUserFacingErrorMessage } from "@/lib/auth/errors";
type AuthStep = "signIn" | "signUp";
@@ -43,11 +44,47 @@ export default function AuthPage() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [username, setUsername] = useState("");
const [pendingUsernameToSave, setPendingUsernameToSave] = useState<string | null>(null);
const [isSavingUsername, setIsSavingUsername] = useState(false);
const [message, setMessage] = useState<{
type: "success" | "error" | "info";
text: string;
} | null>(null);
// 注册完成后,等待登录态同步完成再写入用户名,避免出现“未登录”的竞态
useEffect(() => {
if (!pendingUsernameToSave) return;
if (!isAuthenticated) return;
if (currentUser === undefined || currentUser === null) return;
if (currentUser.name) {
setPendingUsernameToSave(null);
return;
}
let cancelled = false;
setIsSavingUsername(true);
(async () => {
try {
await setMyUsername({ username: pendingUsernameToSave });
if (cancelled) return;
setPendingUsernameToSave(null);
setMessage({ type: "success", text: "注册成功!" });
router.replace("/");
} catch (err: unknown) {
if (cancelled) return;
setPendingUsernameToSave(null);
setMessage({ type: "error", text: getUserFacingErrorMessage(err, "用户名设置失败,请重试") });
} finally {
if (!cancelled) setIsSavingUsername(false);
}
})();
return () => {
cancelled = true;
};
}, [currentUser, isAuthenticated, pendingUsernameToSave, router, setMyUsername]);
// 执行登录的核心逻辑
const performSignIn = useCallback(async (email: string, password: string, flow: AuthStep) => {
setMessage(null);
@@ -67,13 +104,11 @@ export default function AuthPage() {
if (result.signingIn) {
if (flow === "signUp") {
try {
await setMyUsername({ username });
} catch (e: any) {
// 说明:注册已成功,但用户名可能重复;此时保持登录状态,转入“设置用户名”步骤继续处理。
setMessage({ type: "error", text: e?.message ?? "用户名设置失败,请重试" });
return;
}
// 说明:注册已成功,但此刻登录态可能尚未同步到 Convex functions。
// 这里先进入“待写入用户名”状态,等 currentUser 可用后再写,避免出现“未登录”的竞态。
setMessage({ type: "info", text: "注册成功,正在保存用户名..." });
setPendingUsernameToSave(username.trim());
return;
}
setMessage({ type: "success", text: flow === "signIn" ? "登录成功!" : "注册成功!" });
setTimeout(() => router.push("/"), 500);
@@ -89,9 +124,9 @@ export default function AuthPage() {
setMessage({ type: "error", text: "操作失败,请重试" });
} catch (error: any) {
setMessage({ type: "error", text: error.message || "操作失败,请重试" });
setMessage({ type: "error", text: getUserFacingErrorMessage(error, "操作失败,请重试") });
}
}, [router, setMyUsername, signIn, username]);
}, [router, signIn, username]);
const handleSubmit = useCallback(async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
@@ -134,12 +169,12 @@ export default function AuthPage() {
}
if (isAuthenticated) {
if (currentUser === undefined) {
if (currentUser === undefined || currentUser === null) {
return (
<div className="min-h-screen flex items-center justify-center bg-gray-50">
<div className="text-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto mb-4"></div>
<p className="text-gray-600">...</p>
<p className="text-gray-600">...</p>
</div>
</div>
);
@@ -148,11 +183,15 @@ export default function AuthPage() {
const saveUsername = async () => {
setMessage(null);
try {
await setMyUsername({ username });
if (isSavingUsername) return;
setIsSavingUsername(true);
await setMyUsername({ username: username.trim() });
setMessage({ type: "success", text: "用户名已保存!" });
router.replace("/");
} catch (error: any) {
setMessage({ type: "error", text: error.message || "保存失败,请重试" });
setMessage({ type: "error", text: getUserFacingErrorMessage(error, "保存失败,请重试") });
} finally {
setIsSavingUsername(false);
}
};
@@ -185,6 +224,7 @@ export default function AuthPage() {
required
value={username}
onChange={(e) => setUsername(e.target.value)}
disabled={isSavingUsername}
className="appearance-none rounded-md relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
placeholder="用户名(2-32 位,不含空格)"
/>
@@ -192,9 +232,10 @@ export default function AuthPage() {
<button
type="button"
onClick={() => void saveUsername()}
disabled={isSavingUsername}
className="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
{isSavingUsername ? "保存中..." : "保存并继续"}
</button>
</div>
</div>