fix: type fix
This commit is contained in:
+21
-11
@@ -14,7 +14,7 @@
|
|||||||
* would just no-op).
|
* would just no-op).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { useEffect } from "react";
|
import { Suspense, useEffect } from "react";
|
||||||
import { useRouter, useSearchParams } from "next/navigation";
|
import { useRouter, useSearchParams } from "next/navigation";
|
||||||
import { useAuth } from "@/components/auth/auth-provider";
|
import { useAuth } from "@/components/auth/auth-provider";
|
||||||
|
|
||||||
@@ -23,6 +23,24 @@ export default function AuthLayout({
|
|||||||
}: {
|
}: {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}) {
|
}) {
|
||||||
|
// useSearchParams forces a CSR bailout during prerender, so Next.js
|
||||||
|
// requires a Suspense boundary anywhere it's read. The gate is the
|
||||||
|
// only consumer here — isolate it so the rest of the layout can still
|
||||||
|
// SSR normally.
|
||||||
|
return (
|
||||||
|
<main className="flex items-center justify-center px-4 py-12 min-h-[90vh]">
|
||||||
|
<Suspense fallback={<AuthShell>{children}</AuthShell>}>
|
||||||
|
<AuthGate>{children}</AuthGate>
|
||||||
|
</Suspense>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function AuthShell({ children }: { children: React.ReactNode }) {
|
||||||
|
return <div className="w-full max-w-md">{children}</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function AuthGate({ children }: { children: React.ReactNode }) {
|
||||||
const { user, loading } = useAuth();
|
const { user, loading } = useAuth();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const params = useSearchParams();
|
const params = useSearchParams();
|
||||||
@@ -35,16 +53,8 @@ export default function AuthLayout({
|
|||||||
|
|
||||||
// Don't flash the form for an instant before the redirect fires.
|
// Don't flash the form for an instant before the redirect fires.
|
||||||
if (!loading && user) {
|
if (!loading && user) {
|
||||||
return (
|
return <p className="text-sm text-muted-foreground">Redirecting…</p>;
|
||||||
<main className="flex items-center justify-center px-4 py-12 min-h-[90vh]">
|
|
||||||
<p className="text-sm text-muted-foreground">Redirecting…</p>
|
|
||||||
</main>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return <AuthShell>{children}</AuthShell>;
|
||||||
<main className="flex items-center justify-center px-4 py-12 min-h-[90vh]">
|
|
||||||
<div className="w-full max-w-md">{children}</div>
|
|
||||||
</main>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,22 +54,23 @@ export function CollectionScrollCard({
|
|||||||
}) {
|
}) {
|
||||||
const scrollRef = useRef<HTMLDivElement>(null);
|
const scrollRef = useRef<HTMLDivElement>(null);
|
||||||
const cardRef = useRef<HTMLElement>(null);
|
const cardRef = useRef<HTMLElement>(null);
|
||||||
const [posts, setPosts] = useState<Post[]>(initialPosts ?? []);
|
// `fetched` only holds posts pulled by the lazy IntersectionObserver
|
||||||
|
// fetch. When the parent supplies `initialPosts`, we derive `posts`
|
||||||
|
// from it directly — no mirror-into-state effect required, which means
|
||||||
|
// fresh data from the parent (e.g. after an edit) shows up on the very
|
||||||
|
// next render without a setState-cascade.
|
||||||
|
const [fetched, setFetched] = useState<Post[]>([]);
|
||||||
const [fetchState, setFetchState] = useState<FetchState>(
|
const [fetchState, setFetchState] = useState<FetchState>(
|
||||||
initialPosts ? "loaded" : "idle",
|
initialPosts ? "loaded" : "idle",
|
||||||
);
|
);
|
||||||
const [activeIndex, setActiveIndex] = useState(0);
|
const [activeIndex, setActiveIndex] = useState(0);
|
||||||
|
|
||||||
// Reconcile when the parent supplies fresh posts (e.g. after editing).
|
const posts = initialPosts ?? fetched;
|
||||||
useEffect(() => {
|
|
||||||
if (!initialPosts) return;
|
|
||||||
setPosts(initialPosts);
|
|
||||||
setFetchState("loaded");
|
|
||||||
}, [initialPosts]);
|
|
||||||
|
|
||||||
// Fetch posts the first time the carousel enters the viewport (with a
|
// Fetch posts the first time the carousel enters the viewport (with a
|
||||||
// 200px rootMargin so we kick it off just before it's visible). Skipped
|
// 200px rootMargin so we kick it off just before it's visible). Skipped
|
||||||
// entirely when `initialPosts` was provided.
|
// entirely when `initialPosts` was provided (fetchState starts at
|
||||||
|
// "loaded" in that case so the early-return below fires).
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (fetchState !== "idle") return;
|
if (fetchState !== "idle") return;
|
||||||
const el = cardRef.current;
|
const el = cardRef.current;
|
||||||
@@ -83,7 +84,7 @@ export function CollectionScrollCard({
|
|||||||
collections
|
collections
|
||||||
.get(c.id)
|
.get(c.id)
|
||||||
.then(({ posts: rows }) => {
|
.then(({ posts: rows }) => {
|
||||||
setPosts(rows ?? []);
|
setFetched(rows ?? []);
|
||||||
setFetchState("loaded");
|
setFetchState("loaded");
|
||||||
})
|
})
|
||||||
.catch(() => setFetchState("error"));
|
.catch(() => setFetchState("error"));
|
||||||
|
|||||||
Reference in New Issue
Block a user