Something went wrong. Try again.
Monorepo for Tangled
Something went wrong. Try again.
TypeScript
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192import { didFromUri, rkeyFromUri } from "$lib/api/uri";import type { ReactionRecord } from "$lib/api/reaction";import type { RecordView } from "$lib/api/records";
export const ORDERED_REACTION_KINDS = ["๐", "๐", "๐", "๐", "๐ซค", "โค๏ธ", "๐", "๐"] as const;export type ReactionKind = (typeof ORDERED_REACTION_KINDS)[number];
const KIND_SET = new Set<string>(ORDERED_REACTION_KINDS);
export interface ReactionGroup { kind: ReactionKind; count: number; users: string[]; isReacted: boolean; viewerRkey?: string;}
// groups flat reaction records by emoji, keeping OrderedReactionKinds order and// dropping kinds nobody used. handleOf resolves a reactor did to a display handle.export function buildReactions( items: RecordView<ReactionRecord>[], viewerDid?: string, handleOf?: (did: string) => string): ReactionGroup[] { const groups = new Map<ReactionKind, ReactionGroup>();
for (const item of items) { const kind = item.value.reaction; if (!KIND_SET.has(kind)) continue; const did = didFromUri(item.uri); let group = groups.get(kind as ReactionKind); if (!group) { group = { kind: kind as ReactionKind, count: 0, users: [], isReacted: false }; groups.set(kind as ReactionKind, group); } group.count++; group.users.push(handleOf ? handleOf(did) : did); if (viewerDid && did === viewerDid) { group.isReacted = true; group.viewerRkey = rkeyFromUri(item.uri); } }
return ORDERED_REACTION_KINDS.filter((k) => groups.has(k)).map((k) => groups.get(k)!);}
// optimistic addition for a reaction groupexport function withViewerReaction( group: ReactionGroup, viewerHandle: string, rkey: string): ReactionGroup { return { ...group, count: group.count + 1, users: [...group.users, viewerHandle], isReacted: true, viewerRkey: rkey };}
// optimistic removal for a reaction groupexport function withoutViewerReaction(group: ReactionGroup, viewerHandle: string): ReactionGroup { // drop one occurrence of the viewer; count never dips below zero const idx = group.users.indexOf(viewerHandle); const users = idx === -1 ? group.users : group.users.filter((_, i) => i !== idx); return { ...group, count: Math.max(0, group.count - 1), users, isReacted: false, viewerRkey: undefined };}
// optimistic add of a fresh reaction (e.g. from the picker): bumps the group if the// kind is already present, otherwise inserts a new one, keeping canonical emoji order.export function upsertViewerReaction( groups: ReactionGroup[], kind: ReactionKind, viewerHandle: string, rkey: string): ReactionGroup[] { const exists = groups.some((g) => g.kind === kind); const next = exists ? groups.map((g) => (g.kind === kind ? withViewerReaction(g, viewerHandle, rkey) : g)) : [...groups, { kind, count: 1, users: [viewerHandle], isReacted: true, viewerRkey: rkey }]; return ORDERED_REACTION_KINDS.filter((k) => next.some((g) => g.kind === k)).map((k) => next.find((g) => g.kind === k)! );}