export interface CommentReaction { emoji: string count: number me: boolean names: string[] } export interface ReactionEventPayload { commentId: number emoji: string actorId: number actorName: string active: boolean count: number } export const REACTION_EMOJIS = ['๐Ÿ‘', 'โค๏ธ', '๐Ÿ˜‚', '๐Ÿ˜ฎ', '๐Ÿ˜ข', '๐Ÿ”ฅ', '๐ŸŽ‰', '๐Ÿ‘', '๐Ÿ˜', '๐Ÿค”', '๐Ÿ‘Ž', '๐Ÿ˜…', '๐Ÿ™', '๐Ÿ’ฏ', '๐Ÿš€', '๐Ÿ’€'] export function useCommentReactions() { const map = useState>('reactions:by-comment', () => ({})) const { user } = useAuth() function seed(commentId: number, reactions: CommentReaction[]) { map.value = { ...map.value, [commentId]: reactions } } function reactionsFor(commentId: number): CommentReaction[] { return map.value[commentId] ?? [] } function applyEvent(e: ReactionEventPayload) { const list = (map.value[e.commentId] ??= []) let entry = list.find(r => r.emoji === e.emoji) if (e.active) { if (!entry) { entry = { emoji: e.emoji, count: 0, me: false, names: [] } list.push(entry) } if (!entry.names.includes(e.actorName)) { entry.names.push(e.actorName) } } else if (entry) { entry.names = entry.names.filter(n => n !== e.actorName) } if (!entry) return entry.count = e.count if (e.actorId === user.value?.id) { entry.me = e.active } if (entry.count <= 0) { list.splice(list.indexOf(entry), 1) } } async function toggle(commentId: number, emoji: string) { const me = user.value const before = map.value[commentId]?.find(r => r.emoji === emoji) // Optimistisch umschalten โ€” die Reaktion ist sofort sichtbar, // die Server-Antwort korrigiert nur noch Zรคhler/Status if (me) { applyEvent({ commentId, emoji, actorId: me.id, actorName: me.name, active: !before?.me, count: (before?.count ?? 0) + (before?.me ? -1 : 1), }) } try { const res = await $fetch<{ active: boolean, count: number }>(`/api/comments/${commentId}/reactions`, { method: 'POST', body: { emoji }, }) if (me) { applyEvent({ commentId, emoji, actorId: me.id, actorName: me.name, active: res.active, count: res.count }) } } catch (err) { if (me) { applyEvent({ commentId, emoji, actorId: me.id, actorName: me.name, active: !!before?.me, count: before?.count ?? 0 }) } throw err } } return { reactionsFor, seed, applyEvent, toggle } }