forked from BRETT/BRETT
89 lines
2.4 KiB
TypeScript
89 lines
2.4 KiB
TypeScript
export interface UploadReaction {
|
|
emoji: string
|
|
count: number
|
|
me: boolean
|
|
names: string[]
|
|
}
|
|
|
|
export interface UploadReactionEventPayload {
|
|
uploadId: string
|
|
emoji: string
|
|
actorId: number
|
|
actorName: string
|
|
active: boolean
|
|
count: number
|
|
}
|
|
|
|
export function useUploadReactions() {
|
|
const map = useState<Record<string, UploadReaction[]>>('reactions:by-upload', () => ({}))
|
|
const { user } = useAuth()
|
|
|
|
function seed(uploadId: string, reactions: UploadReaction[]) {
|
|
map.value = { ...map.value, [uploadId]: reactions }
|
|
}
|
|
|
|
function reactionsFor(uploadId: string): UploadReaction[] {
|
|
return map.value[uploadId] ?? []
|
|
}
|
|
|
|
function applyEvent(e: UploadReactionEventPayload) {
|
|
const list = (map.value[e.uploadId] ??= [])
|
|
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(uploadId: string, emoji: string) {
|
|
const me = user.value
|
|
const before = map.value[uploadId]?.find(r => r.emoji === emoji)
|
|
// Optimistisch umschalten — die Reaktion ist sofort sichtbar,
|
|
// die Server-Antwort korrigiert nur noch Zähler/Status
|
|
if (me) {
|
|
applyEvent({
|
|
uploadId,
|
|
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, isSubscribed: boolean }>(`/api/uploads/${uploadId}/reactions`, {
|
|
method: 'POST',
|
|
body: { emoji },
|
|
})
|
|
if (me) {
|
|
applyEvent({ uploadId, emoji, actorId: me.id, actorName: me.name, active: res.active, count: res.count })
|
|
}
|
|
return res
|
|
}
|
|
catch (err) {
|
|
if (me) {
|
|
applyEvent({ uploadId, emoji, actorId: me.id, actorName: me.name, active: !!before?.me, count: before?.count ?? 0 })
|
|
}
|
|
throw err
|
|
}
|
|
}
|
|
|
|
return { reactionsFor, seed, applyEvent, toggle }
|
|
}
|