profile editor bug fixes

This commit is contained in:
makearmy 2025-09-30 20:36:26 -04:00
parent 0cbeca833f
commit 16ae6d9c1c
4 changed files with 194 additions and 122 deletions

View file

@ -1,7 +1,7 @@
// components/account/ProfileEditor.tsx
"use client";
import { useEffect, useMemo, useState } from "react";
import { useEffect, useState } from "react";
type Me = {
id: string;
@ -24,33 +24,38 @@ export default function ProfileEditor({
const [first_name, setFirst] = useState("");
const [last_name, setLast] = useState("");
const [email, setEmail] = useState("");
const [profileLocation, setProfileLocation] = useState(""); // renamed to avoid shadowing window.location
const [profileLocation, setProfileLocation] = useState("");
const [msg, setMsg] = useState<string | null>(null);
const [busy, setBusy] = useState(false);
const [loading, setLoading] = useState(!meProp);
const nextAccount = "/portal/account";
// Load profile if not provided via props
useEffect(() => {
if (meProp) {
setMe(meProp);
setLoading(false);
} else {
(async () => {
try {
const r = await fetch("/api/account", { credentials: "include", cache: "no-store" });
if (!r.ok) throw new Error(String(r.status));
const j = await r.json();
const user: Me | undefined = j?.user ?? j?.data ?? undefined;
if (!user) throw new Error("Bad response");
setMe(user);
} catch (e: any) {
setMsg(`Failed to load: ${e?.message || e}`);
} finally {
setLoading(false);
}
})();
return;
}
let alive = true;
(async () => {
try {
const r = await fetch("/api/account", { credentials: "include", cache: "no-store" });
if (!r.ok) throw new Error(String(r.status));
const j = await r.json();
const user: Me | undefined = j?.user ?? j?.data ?? undefined;
if (!user) throw new Error("Bad response");
if (alive) setMe(user);
} catch (e: any) {
if (alive) setMsg(`Failed to load: ${e?.message || e}`);
} finally {
if (alive) setLoading(false);
}
})();
return () => {
alive = false;
};
}, [meProp]);
useEffect(() => {
@ -61,6 +66,41 @@ export default function ProfileEditor({
setProfileLocation(me.location || "");
}, [me]);
// Auto-retry a pending sensitive update after coming back from reauth
useEffect(() => {
const raw = typeof window !== "undefined" ? sessionStorage.getItem("pendingProfileUpdate") : null;
if (!raw) return;
let pending: Record<string, any> | null = null;
try {
pending = JSON.parse(raw);
} catch {
pending = null;
}
if (!pending) {
sessionStorage.removeItem("pendingProfileUpdate");
return;
}
(async () => {
try {
const r = await fetch("/api/account/profile", {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(pending),
});
const j = await r.json().catch(() => ({}));
if (!r.ok) {
setMsg(j?.error || "Update after re-auth failed");
} else {
setMsg("Saved after re-authentication.");
onUpdated?.();
}
} finally {
sessionStorage.removeItem("pendingProfileUpdate");
}
})();
}, [onUpdated]);
const onSave = async () => {
setMsg(null);
setBusy(true);
@ -76,9 +116,12 @@ export default function ProfileEditor({
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
});
if (r.status === 428) {
// Need reauth for sensitive change (email)
if (typeof window !== "undefined") {
// Stash the pending payload so we can retry after reauth
sessionStorage.setItem("pendingProfileUpdate", JSON.stringify(payload));
window.location.assign(
`/auth/sign-in?reauth=1&next=${encodeURIComponent(nextAccount + "#security")}`
);
@ -91,6 +134,7 @@ export default function ProfileEditor({
}
return;
}
const j = await r.json().catch(() => ({}));
if (!r.ok) {
setMsg(j?.error || "Update failed");