forked from BRETT/BRETT
Initial Commit
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
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 }
|
||||
}
|
||||
Reference in New Issue
Block a user