forked from BRETT/BRETT
- Skeleton comment cards while the thread loads; comment count only rendered once data arrives - Subscribe button updates optimistically (fixes shallowRef useFetch data not being reactive on in-place mutation) - Add-tag control restyled as a tag chip, tag icon removed; recording indicator bar dropped in favor of the mic button state - Nav arrows and audio controls follow the theme border radius; arrow z-index no longer overlays sticky header/MOTD - Thread notifications are marked read on visit and on live arrival - Audio upload player uses the shared media volume Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
57 lines
1.6 KiB
Vue
57 lines
1.6 KiB
Vue
<script setup lang="ts">
|
|
import { toast } from 'vue-sonner'
|
|
import { Button } from '@/components/ui/button'
|
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
|
|
|
|
const props = defineProps<{ uploadId: string, enabled: boolean }>()
|
|
const emit = defineEmits<{ change: [uploadId: string, enabled: boolean] }>()
|
|
const busy = shallowRef(false)
|
|
const { t } = useI18n()
|
|
const label = computed(() => props.enabled
|
|
? t('thread.subscribeOff')
|
|
: t('thread.subscribeOn'))
|
|
|
|
async function toggle() {
|
|
if (busy.value) return
|
|
busy.value = true
|
|
const uploadId = props.uploadId
|
|
const next = !props.enabled
|
|
// Optimistisch umschalten — wie bei den Reactions; der Server korrigiert nur noch
|
|
emit('change', uploadId, next)
|
|
try {
|
|
const result = await $fetch<{ enabled: boolean }>(`/api/uploads/${uploadId}/subscription`, {
|
|
method: 'PUT',
|
|
body: { enabled: next },
|
|
})
|
|
if (result.enabled !== next) emit('change', uploadId, result.enabled)
|
|
toast.success(result.enabled ? t('thread.subOn') : t('thread.subOff'))
|
|
}
|
|
catch {
|
|
emit('change', uploadId, !next)
|
|
toast.error(t('thread.subFailed'))
|
|
}
|
|
finally {
|
|
busy.value = false
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<Tooltip>
|
|
<TooltipTrigger as-child>
|
|
<Button
|
|
type="button"
|
|
:variant="enabled ? 'secondary' : 'ghost'"
|
|
size="icon"
|
|
:disabled="busy"
|
|
:aria-label="label"
|
|
:aria-pressed="enabled"
|
|
@click="toggle"
|
|
>
|
|
<Icon :name="enabled ? 'ph:bell-fill' : 'ph:bell-slash-fill'" />
|
|
</Button>
|
|
</TooltipTrigger>
|
|
<TooltipContent>{{ label }}</TooltipContent>
|
|
</Tooltip>
|
|
</template>
|