feat: more actions in status card

zio/stable
三咲智子 2022-11-24 16:34:05 +08:00
parent 2422c809e0
commit e9c9ecefc8
No known key found for this signature in database
GPG Key ID: 69992F2250DFD93E
7 changed files with 157 additions and 87 deletions

View File

@ -1,26 +0,0 @@
<script setup lang="ts">
const { modelValue } = defineModel<{
modelValue: boolean
}>()
const el = ref<HTMLDivElement>()
onClickOutside(el, () => {
if (modelValue)
modelValue.value = false
})
</script>
<template>
<div
v-show="modelValue"
ref="el"
absolute
bg-base
rounded
shadow-xl
dark="border border-base"
>
<slot />
</div>
</template>

View File

@ -1,5 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
defineProps<{
content?: string
}>()
</script> </script>
<template> <template>
@ -9,7 +11,9 @@
<slot /> <slot />
<template #popper> <template #popper>
<div text-3> <div text-3>
<slot name="popper" /> <slot name="popper">
{{ content }}
</slot>
</div> </div>
</template> </template>
</VTooltip> </VTooltip>

View File

@ -0,0 +1,18 @@
<script setup lang="ts">
import { dropdownContextKey } from './ctx'
const dropdown = $ref<any>()
provide(dropdownContextKey, {
hide: () => dropdown.hide(),
})
</script>
<template>
<VDropdown v-bind="$attrs" ref="dropdown">
<slot />
<template #popper="scope">
<slot name="popper" v-bind="scope" />
</template>
</VDropdown>
</template>

View File

@ -0,0 +1,22 @@
<script setup lang="ts">
import { dropdownContextKey } from './ctx'
defineProps<{
icon?: string
}>()
const emit = defineEmits(['click'])
const { hide } = inject(dropdownContextKey)!
const handleClick = (evt: MouseEvent) => {
hide()
emit('click', evt)
}
</script>
<template>
<div flex gap-2 items-center cursor-pointer px4 py3 font-700 hover="bg-active" @click="handleClick">
<div v-if="icon" :class="icon" />
<span text-15px><slot /></span>
</div>
</template>

View File

@ -0,0 +1,5 @@
import type { InjectionKey } from 'vue'
export const dropdownContextKey: InjectionKey<{
hide: () => void
}> = Symbol('dropdownContextKey')

View File

@ -3,8 +3,7 @@ defineProps<{
text?: string | number text?: string | number
color: string color: string
icon: string icon: string
activeIcon: string activeIcon?: string
tooltip: string
hover: string hover: string
groupHover: string groupHover: string
active?: boolean active?: boolean
@ -17,7 +16,6 @@ defineOptions({
</script> </script>
<template> <template>
<CommonTooltip placement="bottom">
<button <button
flex gap-1 items-center rounded :hover="`op100 ${hover}`" group flex gap-1 items-center rounded :hover="`op100 ${hover}`" group
:class="active ? [color, 'op100'] : 'op50'" :class="active ? [color, 'op100'] : 'op50'"
@ -29,9 +27,4 @@ defineOptions({
<span v-if="text">{{ text }}</span> <span v-if="text">{{ text }}</span>
</button> </button>
<template #popper>
{{ tooltip }}
</template>
</CommonTooltip>
</template> </template>

View File

@ -5,6 +5,12 @@ const { status } = defineProps<{
status: Status status: Status
}>() }>()
const isAuthor = $computed(() => status.account.id === currentUser.value?.account?.id)
const clipboard = useClipboard()
const router = useRouter()
const route = useRoute()
// Use different states to let the user press different actions right after the other // Use different states to let the user press different actions right after the other
const isLoading = $ref({ reblogged: false, favourited: false, bookmarked: false }) const isLoading = $ref({ reblogged: false, favourited: false, bookmarked: false })
async function toggleStatusAction(action: 'reblogged' | 'favourited' | 'bookmarked', newStatus: Promise<Status>) { async function toggleStatusAction(action: 'reblogged' | 'favourited' | 'bookmarked', newStatus: Promise<Status>) {
@ -33,19 +39,36 @@ const toggleBookmark = () => toggleStatusAction(
'bookmarked', 'bookmarked',
masto.statuses[status.bookmarked ? 'unbookmark' : 'bookmark'](status.id), masto.statuses[status.bookmarked ? 'unbookmark' : 'bookmark'](status.id),
) )
const copyLink = async () => {
await clipboard.copy(location.href)
}
const openInOriginal = () => {
window.open(status.url!, '_blank')
}
const deleteStatus = async () => {
// TODO confirm to delete
await masto.statuses.remove(status.id)
if (route.name === '@user-post')
router.back()
// TODO when timeline, remove this item
}
</script> </script>
<template> <template>
<div flex justify-between gap-8> <div flex justify-between gap-8>
<CommonTooltip placement="bottom" content="Replay">
<RouterLink :to="getStatusPath(status)"> <RouterLink :to="getStatusPath(status)">
<StatusActionButton <StatusActionButton
:text="status.repliesCount" :text="status.repliesCount"
color="text-blue" hover="text-blue" group-hover="bg-blue/10" color="text-blue" hover="text-blue" group-hover="bg-blue/10"
icon="i-ri:chat-3-line" icon="i-ri:chat-3-line"
tooltip="Replay"
/> />
</RouterLink> </RouterLink>
</CommonTooltip>
<CommonTooltip placement="bottom" content="Boost">
<StatusActionButton <StatusActionButton
:text="status.reblogsCount" :text="status.reblogsCount"
color="text-green" hover="text-green" group-hover="bg-green/10" color="text-green" hover="text-green" group-hover="bg-green/10"
@ -53,10 +76,11 @@ const toggleBookmark = () => toggleStatusAction(
active-icon="i-ri:repeat-fill" active-icon="i-ri:repeat-fill"
:active="status.reblogged" :active="status.reblogged"
:disabled="isLoading.reblogged" :disabled="isLoading.reblogged"
tooltip="Boost"
@click="toggleReblog()" @click="toggleReblog()"
/> />
</CommonTooltip>
<CommonTooltip placement="bottom" content="Favourite">
<StatusActionButton <StatusActionButton
:text="status.favouritesCount" :text="status.favouritesCount"
color="text-rose" hover="text-rose" group-hover="bg-rose/10" color="text-rose" hover="text-rose" group-hover="bg-rose/10"
@ -64,26 +88,56 @@ const toggleBookmark = () => toggleStatusAction(
active-icon="i-ri:heart-3-fill" active-icon="i-ri:heart-3-fill"
:active="status.favourited" :active="status.favourited"
:disabled="isLoading.favourited" :disabled="isLoading.favourited"
tooltip="Favourite"
@click="toggleFavourite()" @click="toggleFavourite()"
/> />
</CommonTooltip>
<CommonTooltip placement="bottom" content="Bookmark">
<StatusActionButton <StatusActionButton
color="text-yellow" hover="text-yellow" group-hover="bg-yellow/10" color="text-yellow" hover="text-yellow" group-hover="bg-yellow/10"
icon="i-ri:bookmark-line" icon="i-ri:bookmark-line"
active-icon="i-ri:bookmark-fill" active-icon="i-ri:bookmark-fill"
:active="status.bookmarked" :active="status.bookmarked"
:disabled="isLoading.bookmarked" :disabled="isLoading.bookmarked"
tooltip="Bookmark"
@click="toggleBookmark()" @click="toggleBookmark()"
/> />
</CommonTooltip>
<!-- <VDropdown> <CommonDropdown placement="bottom">
<CommonTooltip placement="bottom" content="More">
<button flex gap-1 items-center rounded op50 hover="op100 text-purple" group> <button flex gap-1 items-center rounded op50 hover="op100 text-purple" group>
<div rounded-full p2 group-hover="bg-purple/10"> <div rounded-full p2 group-hover="bg-purple/10">
<div i-ri:share-circle-line /> <div i-ri:more-line />
</div> </div>
</button> </button>
</VDropdown> --> </CommonTooltip>
<template #popper>
<div flex="~ col">
<CommonDropdownItem icon="i-ri:link" @click="copyLink">
Copy link to this post
</CommonDropdownItem>
<CommonDropdownItem v-if="status.url" icon="i-ri:arrow-right-up-line" @click="openInOriginal">
Open in original site
</CommonDropdownItem>
<!-- TODO -->
<template v-if="isAuthor">
<CommonDropdownItem v-if="isAuthor" icon="i-ri:edit-line">
Edit
</CommonDropdownItem>
<CommonDropdownItem
v-if="isAuthor" icon="i-ri:delete-bin-line" text-red-600
@click="deleteStatus"
>
Delete
</CommonDropdownItem>
</template>
</div>
</template>
</CommonDropdown>
</div> </div>
</template> </template>