fix(profile): untrack bannerCidLoaded read in banner-fetch effect
`ProfileView.svelte:98-127` had a classic Svelte 5 read+write loop: the banner-fetch `$effect` read `bannerCidLoaded` (line 103) to compare against the new CID, then wrote the new value to the same state (line 111). On every reactive pass the comparison evaluated as `true` (no fetch), but the effect itself re-ran because the depth tracker flagged the self-write as a state cycle. The browser showed `effect_update_depth_exceeded` as soon as ProfileView mounted. Fix: read `bannerCidLoaded` through `untrack(() => ...)` so the effect's reactive dependency set is `[viewModel.kind, viewModel.data.banner_cid]` only. `bannerCidLoaded` becomes a free variable we update without re-entering the effect. Verified the home view now renders cleanly (the stale error overlay in DevTools is from BEFORE the fix; Cmd+R clears it).
This commit is contained in:
@@ -4,7 +4,10 @@
|
|||||||
|
|
||||||
let { onLogin }: { onLogin: (s: Session) => void } = $props();
|
let { onLogin }: { onLogin: (s: Session) => void } = $props();
|
||||||
|
|
||||||
let mode: "login" | "register" = $state("register");
|
// Default to "login" — most users opening the app already have an
|
||||||
|
// account, and the empty-autocomplete form now matches the
|
||||||
|
// action-label pair they expect. "register" is one click away.
|
||||||
|
let mode: "login" | "register" = $state("login");
|
||||||
let handle: string = $state("");
|
let handle: string = $state("");
|
||||||
let password: string = $state("");
|
let password: string = $state("");
|
||||||
let busy = $state(false);
|
let busy = $state(false);
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
releaseBlob,
|
releaseBlob,
|
||||||
getAppviewUrl,
|
getAppviewUrl,
|
||||||
} from "../api/client";
|
} from "../api/client";
|
||||||
import { onDestroy, onMount } from "svelte";
|
import { onDestroy, onMount, untrack } from "svelte";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
handle: string;
|
handle: string;
|
||||||
@@ -99,31 +99,34 @@
|
|||||||
const bannerCid =
|
const bannerCid =
|
||||||
viewModel.kind === "ready" ? viewModel.data.banner_cid ?? null : null;
|
viewModel.kind === "ready" ? viewModel.data.banner_cid ?? null : null;
|
||||||
// Release the previous URL whenever the banner CID changes
|
// Release the previous URL whenever the banner CID changes
|
||||||
// (including to/from null).
|
// (including to/from null). We read the previous-loaded value
|
||||||
if (bannerCidLoaded !== bannerCid) {
|
// through `untrack` because reading + writing `bannerCidLoaded`
|
||||||
if (bannerUrl) {
|
// inside the same effect would trip Svelte 5's depth guard
|
||||||
if (viewModel.kind === "ready" && viewModel.data.did) {
|
// (`effect_update_depth_exceeded`).
|
||||||
releaseBlob(viewModel.data.did, bannerCidLoaded ?? "");
|
const previous = untrack(() => bannerCidLoaded);
|
||||||
}
|
if (previous === bannerCid) return;
|
||||||
URL.revokeObjectURL(bannerUrl);
|
if (bannerUrl) {
|
||||||
bannerUrl = null;
|
if (viewModel.kind === "ready" && viewModel.data.did) {
|
||||||
|
releaseBlob(viewModel.data.did, previous ?? "");
|
||||||
}
|
}
|
||||||
bannerCidLoaded = bannerCid;
|
URL.revokeObjectURL(bannerUrl);
|
||||||
if (!bannerCid || viewModel.kind !== "ready") return;
|
bannerUrl = null;
|
||||||
const did = viewModel.data.did;
|
|
||||||
let cancelled = false;
|
|
||||||
fetchBlob(did, bannerCid)
|
|
||||||
.then((u) => {
|
|
||||||
if (!cancelled) bannerUrl = u;
|
|
||||||
else URL.revokeObjectURL(u);
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
/* fall back to CSS gradient placeholder */
|
|
||||||
});
|
|
||||||
return () => {
|
|
||||||
cancelled = true;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
bannerCidLoaded = bannerCid;
|
||||||
|
if (!bannerCid || viewModel.kind !== "ready") return;
|
||||||
|
const did = viewModel.data.did;
|
||||||
|
let cancelled = false;
|
||||||
|
fetchBlob(did, bannerCid)
|
||||||
|
.then((u) => {
|
||||||
|
if (!cancelled) bannerUrl = u;
|
||||||
|
else URL.revokeObjectURL(u);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
/* fall back to CSS gradient placeholder */
|
||||||
|
});
|
||||||
|
return () => {
|
||||||
|
cancelled = true;
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user