85 lines
2.8 KiB
TypeScript
85 lines
2.8 KiB
TypeScript
"use client";
|
|
|
|
/**
|
|
* /u/me — owner Posts grid (the default tab).
|
|
*
|
|
* Window-virtualised, infinite-scroll 2-up grid (usePostScrollFeed keyset-pages
|
|
* the owner's posts; VirtualPostGrid renders + windows them). Tapping a
|
|
* thumbnail jumps to /u/me/posts#post-<id> where the scroll feed renders the
|
|
* full passages.
|
|
*
|
|
* Editing the profile lives on its own page (/settings/profile), reached
|
|
* from the header's "Edit profile" button.
|
|
*/
|
|
|
|
import { useState } from "react";
|
|
import { useAuth } from "@/components/auth/auth-provider";
|
|
import { VirtualPostGrid } from "@/components/social/post/virtual-post-grid";
|
|
import { usePostScrollFeed } from "@/components/social/post/use-post-scroll-feed";
|
|
import { ConfirmModal } from "@/components/ui/confirm-modal";
|
|
import { posts, SocialError, type Post } from "@/lib/social";
|
|
|
|
export default function MyProfilePage() {
|
|
const { user } = useAuth();
|
|
// Defer the authed /posts/mine fetch until we have a user.
|
|
if (!user) return null;
|
|
return <OwnerGrid />;
|
|
}
|
|
|
|
function OwnerGrid() {
|
|
const { feed, setFeed, loading, hasMore, error, setError, sentinelRef } =
|
|
usePostScrollFeed(
|
|
(before, limit) => posts.mine({ limit, before: before ?? undefined }),
|
|
24,
|
|
);
|
|
const [pendingDelete, setPendingDelete] = useState<string | null>(null);
|
|
|
|
// Card delete buttons stage an id; ConfirmModal runs `performDelete` when the
|
|
// user confirms. We throw on failure so the modal stays open with an error.
|
|
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)));
|
|
}
|
|
|
|
return (
|
|
<section>
|
|
{error ? <p className="mb-3 text-sm text-destructive">{error}</p> : null}
|
|
|
|
<VirtualPostGrid
|
|
posts={feed}
|
|
loading={loading}
|
|
hasMore={hasMore}
|
|
sentinelRef={sentinelRef}
|
|
hrefFor={(p) => `/u/me/posts#post-${p.id}`}
|
|
onDelete={(id) => setPendingDelete(id)}
|
|
onUpdated={onPostUpdated}
|
|
emptyMessage="You haven't posted anything yet. Select any passage in the knowledgebase and use the toolbar to share it to your feed."
|
|
/>
|
|
|
|
<ConfirmModal
|
|
open={!!pendingDelete}
|
|
onClose={() => 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}
|
|
/>
|
|
</section>
|
|
);
|
|
}
|