40 lines
2.2 KiB
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>
|