Files
BRETT/app/components/ThreadSubscriptionButton.vue
FliesentischKingandDevin f57fffb414 Thread page UX improvements
- 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>
2026-10-04 03:06:32 +02:00

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>