feat: add comments
This commit is contained in:
@@ -85,6 +85,32 @@ export type Post = {
|
||||
viewer_has_liked?: boolean;
|
||||
/** How many users are tagged on this post (drives the mentions icon). */
|
||||
mentions_count?: number;
|
||||
/** The post's pinned comment, if any — hydrated so the card footer can show
|
||||
* it without a separate fetch. Null/absent when nothing is pinned. */
|
||||
pinned_comment?: Comment | null;
|
||||
};
|
||||
|
||||
/** A comment or reply on a post. Threading is one level deep: `parent_id` is
|
||||
* null for a top-level comment and otherwise the thread root. `reply_to_handle`
|
||||
* is who a reply was addressed to (for the "Replying to @handle" prefix). */
|
||||
export type Comment = {
|
||||
id: string;
|
||||
post_id: string;
|
||||
user_id: string;
|
||||
parent_id?: string | null;
|
||||
body: string;
|
||||
likes_count: number;
|
||||
/** Non-null when this comment is the post's pinned comment. */
|
||||
pinned_at?: string | null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
author_name: string;
|
||||
author_handle: string;
|
||||
author_avatar_url?: string | null;
|
||||
reply_to_handle?: string | null;
|
||||
viewer_has_liked: boolean;
|
||||
/** Number of replies under a top-level comment (0 on replies). */
|
||||
reply_count: number;
|
||||
};
|
||||
|
||||
/** Result of a like / unlike toggle: the new viewer state + effective total. */
|
||||
@@ -282,6 +308,68 @@ export const posts = {
|
||||
},
|
||||
};
|
||||
|
||||
// ─── Comments ────────────────────────────────────────────────────────────
|
||||
|
||||
export type CommentPageParams = { limit?: number; offset?: number };
|
||||
|
||||
function commentPageQuery({ limit = 20, offset = 0 }: CommentPageParams) {
|
||||
return new URLSearchParams({
|
||||
limit: String(limit),
|
||||
offset: String(offset),
|
||||
}).toString();
|
||||
}
|
||||
|
||||
export const comments = {
|
||||
/** A post's top-level comments — pinned first, then newest. Public. */
|
||||
list(postId: string, params: CommentPageParams = {}) {
|
||||
return call<Comment[]>(
|
||||
`/posts/${postId}/comments?${commentPageQuery(params)}`,
|
||||
);
|
||||
},
|
||||
/** A thread's replies, oldest first. Public. */
|
||||
replies(commentId: string, params: CommentPageParams = {}) {
|
||||
return call<Comment[]>(
|
||||
`/comments/${commentId}/replies?${commentPageQuery(params)}`,
|
||||
);
|
||||
},
|
||||
/** Post a comment, or a reply when `parentId` is set. */
|
||||
create(postId: string, body: string, parentId?: string) {
|
||||
return call<Comment>(`/posts/${postId}/comments`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ body, parent_id: parentId ?? null }),
|
||||
});
|
||||
},
|
||||
/** Like / unlike a comment; returns the new like count. */
|
||||
like(commentId: string) {
|
||||
return call<{ likes_count: number }>(`/comments/${commentId}/like`, {
|
||||
method: "POST",
|
||||
});
|
||||
},
|
||||
unlike(commentId: string) {
|
||||
return call<{ likes_count: number }>(`/comments/${commentId}/like`, {
|
||||
method: "DELETE",
|
||||
});
|
||||
},
|
||||
/** Pin / unpin a comment (post author only; top-level only). */
|
||||
pin(commentId: string) {
|
||||
return call<void>(`/comments/${commentId}/pin`, { method: "POST" });
|
||||
},
|
||||
unpin(commentId: string) {
|
||||
return call<void>(`/comments/${commentId}/pin`, { method: "DELETE" });
|
||||
},
|
||||
/** Delete a comment (author or post owner). Removes the whole thread. */
|
||||
delete(commentId: string) {
|
||||
return call<void>(`/comments/${commentId}`, { method: "DELETE" });
|
||||
},
|
||||
/** Report a comment. */
|
||||
report(commentId: string, category: PostReportCategory, reason: string) {
|
||||
return call<void>(`/comments/${commentId}/report`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ category, reason }),
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
// ─── Bookmarks ───────────────────────────────────────────────────────────
|
||||
|
||||
export const bookmarks = {
|
||||
@@ -486,6 +574,10 @@ export type AppNotification = {
|
||||
* row represents (renders "X and N-1 others liked your post"). */
|
||||
post_id?: string | null;
|
||||
actor_count?: number;
|
||||
/** Handle of the post's author (when the row references a post), so comment
|
||||
* / reply notifications can deep-link to the post even though the actor
|
||||
* isn't its owner. */
|
||||
post_author_handle?: string | null;
|
||||
};
|
||||
|
||||
export const notifications = {
|
||||
|
||||
Reference in New Issue
Block a user