"use client";
/**
* /u/me — owner Posts grid (the default tab).
*
* Grid-only — no Grid/Scroll toggle. Tapping a thumbnail jumps to
* /u/me/posts#post- where the Instagram-style scroll feed renders
* the full passages.
*
* `?edit=1` (set by the header's Edit profile button) swaps the grid
* for the inline edit form. Saving returns to /u/me. Keeps the URL
* shareable while avoiding a separate route + layout.
*/
import { Suspense, useEffect, useState } from "react";
import { useRouter, useSearchParams } from "next/navigation";
import { LayoutGrid } from "lucide-react";
import { useAuth } from "@/components/auth/auth-provider";
import { ProfileEditForm } from "@/components/profile/profile-edit-form";
import { PostGridCard } from "@/components/social/post/post-grid-card";
import { ConfirmModal } from "@/components/ui/confirm-modal";
import { posts, SocialError, type Post } from "@/lib/social";
export default function MyProfilePage() {
// useSearchParams() forces a CSR bailout during prerender; the
// Suspense boundary is required by Next.js so the build doesn't fail
// (the same wrapping is in app/u/me/layout.tsx for its own check).
// The fallback matches the page's idle loading state so the brief
// moment before search params resolve doesn't flash an empty section.
return (
Loading…
}
>
);
}
function MyProfilePageInner() {
const { user } = useAuth();
const router = useRouter();
const searchParams = useSearchParams();
const editMode = searchParams.get("edit") === "1";
const [feed, setFeed] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
// Post id pending confirmation — modal opens when non-null and runs
// the actual delete on confirm.
const [pendingDelete, setPendingDelete] = useState(null);
useEffect(() => {
if (!user) return;
posts
.mine()
.then((rows) => setFeed(rows ?? []))
.catch((err) =>
setError(
err instanceof SocialError ? err.message : "Could not load posts.",
),
)
.finally(() => setLoading(false));
}, [user]);
// Card delete buttons stage an id; ConfirmModal runs `performDelete`
// when the user confirms. We throw on failure so the modal stays open
// with an inline error message.
async function performDelete() {
const id = pendingDelete;
if (!id) return;
const prev = feed;
setFeed((rows) => rows.filter((p) => p.id !== id));
try {
await posts.delete(id);
} catch (err) {
setFeed(prev);
const msg =
err instanceof SocialError ? err.message : "Could not delete.";
setError(msg);
throw new Error(msg);
}
}
function onPostUpdated(updated: Post) {
setFeed((rows) => rows.map((p) => (p.id === updated.id ? updated : p)));
}
if (!user) return null;
if (editMode) {
return router.replace("/u/me")} />;
}
return (
{error ? (
{error}
) : loading ? (
Loading…
) : feed.length === 0 ? (
) : (
{feed.map((p) => (
setPendingDelete(id)}
onUpdated={onPostUpdated}
href={`/u/me/posts#post-${p.id}`}
/>
))}
)}
setPendingDelete(null)}
title="Delete this post?"
message="The post will be removed permanently from your feed and from any collections it's in. This can't be undone."
confirmLabel="Delete post"
onConfirm={performDelete}
/>
);
}
function EmptyPosts() {
return (
You haven't posted anything yet. Select any passage in the
knowledgebase and use the toolbar to share it to your feed.
);
}