4wswt4ex2: Add like and unlike for post cards
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
// Browser-side like / unlike calls and the pure card update they drive.
|
||||
|
||||
import type { CardPost } from "./feed-model";
|
||||
|
||||
export interface LikeOutcome {
|
||||
isLiked: boolean;
|
||||
likesCount: number | null;
|
||||
}
|
||||
|
||||
export class LikeError extends Error {
|
||||
constructor(public readonly signedOut: boolean) {
|
||||
super(signedOut ? "sign_in" : "like_failed");
|
||||
}
|
||||
}
|
||||
|
||||
export async function sendLike(
|
||||
postId: string,
|
||||
liked: boolean,
|
||||
fetchImpl: typeof fetch = fetch,
|
||||
): Promise<LikeOutcome> {
|
||||
let res: Response;
|
||||
try {
|
||||
res = await fetchImpl(`/api/posts/${encodeURIComponent(postId)}/like`, {
|
||||
method: liked ? "POST" : "DELETE",
|
||||
credentials: "same-origin",
|
||||
});
|
||||
} catch {
|
||||
throw new LikeError(false);
|
||||
}
|
||||
if (res.status === 401) throw new LikeError(true);
|
||||
if (!res.ok) throw new LikeError(false);
|
||||
try {
|
||||
const b = (await res.json()) as { isLiked?: unknown; likesCount?: unknown };
|
||||
return {
|
||||
isLiked: typeof b.isLiked === "boolean" ? b.isLiked : liked,
|
||||
likesCount:
|
||||
typeof b.likesCount === "number" && Number.isInteger(b.likesCount) && b.likesCount >= 0
|
||||
? b.likesCount
|
||||
: null,
|
||||
};
|
||||
} catch {
|
||||
throw new LikeError(false);
|
||||
}
|
||||
}
|
||||
|
||||
/** The card after a successful call: the server count when present, else flip and adjust by one. */
|
||||
export function applyLikeOutcome(post: CardPost, wanted: boolean, out: LikeOutcome): CardPost {
|
||||
if (out.likesCount !== null) return { ...post, isLiked: out.isLiked, likesCount: out.likesCount };
|
||||
const changed = post.isLiked !== wanted;
|
||||
const delta = changed ? (wanted ? 1 : -1) : 0;
|
||||
return { ...post, isLiked: wanted, likesCount: Math.max(0, post.likesCount + delta) };
|
||||
}
|
||||
Reference in New Issue
Block a user