<script setup lang="ts"> import type { Status } from 'masto' const { status: _status, details } = defineProps<{ status: Status details?: boolean }>() let status = $ref<Status>({ ..._status }) watch(() => _status, (val) => { status = { ...val } }, { deep: true, immediate: true }) const clipboard = useClipboard() const router = useRouter() const route = useRoute() const isAuthor = $computed(() => status.account.id === currentUser.value?.account.id) // Use different states to let the user press different actions right after the other const isLoading = $ref({ reblogged: false, favourited: false, bookmarked: false, pinned: false, translation: false, }) type Action = 'reblogged' | 'favourited' | 'bookmarked' | 'pinned' type CountField = 'reblogsCount' | 'favouritesCount' async function toggleStatusAction(action: Action, newStatus: Promise<Status>, countField?: CountField) { // Optimistic update status[action] = !status[action] if (countField) status[countField] += status[action] ? 1 : -1 try { isLoading[action] = true Object.assign(status, await newStatus) } finally { isLoading[action] = false } } const toggleReblog = () => toggleStatusAction( 'reblogged', useMasto().statuses[status.reblogged ? 'unreblog' : 'reblog'](status.id).then((res) => { if (status.reblogged) // returns the original status return res.reblog! return res }), 'reblogsCount', ) const toggleFavourite = () => toggleStatusAction( 'favourited', useMasto().statuses[status.favourited ? 'unfavourite' : 'favourite'](status.id), 'favouritesCount', ) const toggleBookmark = () => toggleStatusAction( 'bookmarked', useMasto().statuses[status.bookmarked ? 'unbookmark' : 'bookmark'](status.id), ) const togglePin = async () => toggleStatusAction( 'pinned', useMasto().statuses[status.pinned ? 'unpin' : 'pin'](status.id), ) const { toggle: _toggleTranslation, translation, enabled: isTranslationEnabled } = useTranslation(_status) const toggleTranslation = async () => { isLoading.translation = true await _toggleTranslation() isLoading.translation = false } const copyLink = async () => { await clipboard.copy(`${location.origin}${getStatusPath(status)}`) } const deleteStatus = async () => { // TODO confirm to delete if (process.dev) { // eslint-disable-next-line no-alert const result = confirm('[DEV] Are you sure you want to delete this post?') if (!result) return } await useMasto().statuses.remove(status.id) if (route.name === '@account-status') router.back() // TODO when timeline, remove this item } const deleteAndRedraft = async () => { // TODO confirm to delete const { text } = await useMasto().statuses.remove(status.id) openPublishDialog('dialog', getDraftFromStatus(status, text), true) } const reply = () => { if (details) { // TODO focus to editor } else { const { key, draft } = getReplyDraft(status) openPublishDialog(key, draft()) } } function editStatus() { openPublishDialog(`edit-${status.id}`, { ...getDraftFromStatus(status), editingStatus: status, }) } </script> <template> <div flex justify-between> <div flex-1> <StatusActionButton content="Reply" :text="status.repliesCount" color="text-blue" hover="text-blue" group-hover="bg-blue/10" icon="i-ri:chat-3-line" @click="reply" /> </div> <div flex-1> <StatusActionButton content="Boost" :text="status.reblogsCount" color="text-green" hover="text-green" group-hover="bg-green/10" icon="i-ri:repeat-line" active-icon="i-ri:repeat-fill" :active="status.reblogged" :disabled="isLoading.reblogged" @click="toggleReblog()" /> </div> <div flex-1> <StatusActionButton content="Favourite" :text="status.favouritesCount" color="text-rose" hover="text-rose" group-hover="bg-rose/10" icon="i-ri:heart-3-line" active-icon="i-ri:heart-3-fill" :active="status.favourited" :disabled="isLoading.favourited" @click="toggleFavourite()" /> </div> <div flex-none> <StatusActionButton content="Bookmark" color="text-yellow" hover="text-yellow" group-hover="bg-yellow/10" icon="i-ri:bookmark-line" active-icon="i-ri:bookmark-fill" :active="status.bookmarked" :disabled="isLoading.bookmarked" @click="toggleBookmark()" /> </div> <CommonDropdown flex-none ml3 placement="bottom"> <StatusActionButton content="More" color="text-purple" hover="text-purple" group-hover="bg-purple/10" icon="i-ri:more-line" /> <template #popper> <div flex="~ col"> <CommonDropdownItem icon="i-ri:link" @click="copyLink"> Copy link to this post </CommonDropdownItem> <NuxtLink :to="status.url" target="_blank"> <CommonDropdownItem v-if="status.url" icon="i-ri:arrow-right-up-line"> Open in original site </CommonDropdownItem> </NuxtLink> <CommonDropdownItem v-if="isTranslationEnabled && status.language !== languageCode" icon="i-ri:translate" @click="toggleTranslation"> <template v-if="!translation.visible"> Translate post </template> <template v-else> Show untranslated </template> </CommonDropdownItem> <template v-if="isAuthor"> <CommonDropdownItem icon="i-ri:pushpin-line" @click="togglePin" > {{ status.pinned ? 'Unpin on profile' : 'Pin on profile' }} </CommonDropdownItem> <CommonDropdownItem icon="i-ri:edit-line" @click="editStatus"> Edit </CommonDropdownItem> <CommonDropdownItem icon="i-ri:delete-bin-line" text-red-600 @click="deleteStatus" > Delete </CommonDropdownItem> <CommonDropdownItem icon="i-ri:eraser-line" text-red-600 @click="deleteAndRedraft" > Delete & re-draft </CommonDropdownItem> </template> <!-- TODO not available when not the same server --> <template v-else> <CommonDropdownItem icon="i-ri:at-line" @click="mentionUser(status.account)" > Mention @{{ status.account.acct }} </CommonDropdownItem> </template> </div> </template> </CommonDropdown> </div> </template>