Files
FEDEO/frontend/components/ChatReactions.vue

40 lines
2.2 KiB
Vue

<script setup>
const props = defineProps({ api: { type: Function, required: true }, endpoint: { type: String, required: true }, messages: { type: Array, required: true }, disabled: Boolean })
const emit = defineEmits(['error'])
const reactions = ref({}), busy = ref(false)
let timer, disposed = false, fetching = false, revision = 0
const ids = computed(() => props.messages.filter(m => !m.pending && !m.failed).map(m => m.id))
async function refresh() {
if (disposed || fetching || busy.value || document.hidden) return
const version = ++revision, endpoint = props.endpoint, list = [...ids.value]
fetching = true
try {
const next = {}
for (let index = 0; index < list.length; index += 100) {
const result = await props.api(`${endpoint}/reactions?ids=${encodeURIComponent(list.slice(index, index + 100).join(','))}`)
Object.assign(next, result.reactions || {})
if (disposed || version !== revision || props.endpoint !== endpoint) return
}
if (!disposed && version === revision) reactions.value = next
} catch { /* Temporary polling errors must not obscure messages. */ }
finally { fetching = false }
}
async function react(messageId, emoji) {
if (disposed || busy.value || props.disabled || !ids.value.includes(messageId)) return
const version = ++revision, endpoint = props.endpoint
const active = !(reactions.value[messageId] || []).some(r => r.emoji === emoji && r.own)
busy.value = true
try {
const result = await props.api(`${endpoint}/reactions`, { method: 'PUT', body: { messageId, emoji, active } })
if (!disposed && version === revision && endpoint === props.endpoint) reactions.value = { ...reactions.value, [messageId]: result.reactions?.[messageId] || [] }
} catch (error) { if (!disposed && version === revision) emit('error', error) }
finally { if (!disposed) busy.value = false }
}
watch(() => props.endpoint, () => { ++revision; reactions.value = {} })
watch(ids, () => { void refresh() }, { immediate: true })
onMounted(() => { timer = setInterval(refresh, 3000) })
onBeforeUnmount(() => { disposed = true; ++revision; clearInterval(timer) })
</script>
<template><slot :reactions="reactions" :react="react" :busy="busy" /></template>