mirror of
https://github.com/versia-pub/frontend.git
synced 2025-12-06 08:28:20 +01:00
feat: ✨ Reimplement like, reblog and delete functionality
This commit is contained in:
parent
db4cb78f02
commit
7862757da7
|
|
@ -4,18 +4,18 @@
|
||||||
<Reply class="size-5 text-primary" />
|
<Reply class="size-5 text-primary" />
|
||||||
{{ numberFormat(replyCount) }}
|
{{ numberFormat(replyCount) }}
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="ghost">
|
<Button variant="ghost" @click="liked ? unlike() : like()">
|
||||||
<Heart class="size-5 text-primary" />
|
<Heart class="size-5 text-primary" />
|
||||||
{{ numberFormat(likeCount) }}
|
{{ numberFormat(likeCount) }}
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="ghost">
|
<Button variant="ghost" @click="reblogged ? unreblog() : reblog()">
|
||||||
<Repeat class="size-5 text-primary" />
|
<Repeat class="size-5 text-primary" />
|
||||||
{{ numberFormat(reblogCount) }}
|
{{ numberFormat(reblogCount) }}
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="ghost" @click="emit('quote')">
|
<Button variant="ghost" @click="emit('quote')">
|
||||||
<Quote class="size-5 text-primary" />
|
<Quote class="size-5 text-primary" />
|
||||||
</Button>
|
</Button>
|
||||||
<Menu :api-note-string="apiNoteString" :url="url" :remote-url="remoteUrl" :is-remote="isRemote" :author-id="authorId" @edit="emit('edit')">
|
<Menu :api-note-string="apiNoteString" :url="url" :remote-url="remoteUrl" :is-remote="isRemote" :author-id="authorId" @edit="emit('edit')" :note-id="noteId" @delete="emit('delete')">
|
||||||
<Button variant="ghost">
|
<Button variant="ghost">
|
||||||
<Ellipsis class="size-5 text-primary" />
|
<Ellipsis class="size-5 text-primary" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -25,26 +25,63 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { Ellipsis, Heart, Quote, Repeat, Reply } from "lucide-vue-next";
|
import { Ellipsis, Heart, Quote, Repeat, Reply } from "lucide-vue-next";
|
||||||
|
import { toast } from "vue-sonner";
|
||||||
import { Button } from "~/components/ui/button";
|
import { Button } from "~/components/ui/button";
|
||||||
import Menu from "./menu.vue";
|
import Menu from "./menu.vue";
|
||||||
|
|
||||||
defineProps<{
|
const { noteId } = defineProps<{
|
||||||
replyCount: number;
|
replyCount: number;
|
||||||
likeCount: number;
|
likeCount: number;
|
||||||
reblogCount: number;
|
reblogCount: number;
|
||||||
apiNoteString: string;
|
apiNoteString: string;
|
||||||
|
noteId: string;
|
||||||
isRemote: boolean;
|
isRemote: boolean;
|
||||||
url: string;
|
url: string;
|
||||||
remoteUrl: string;
|
remoteUrl: string;
|
||||||
authorId: string;
|
authorId: string;
|
||||||
|
liked: boolean;
|
||||||
|
reblogged: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
edit: [];
|
edit: [];
|
||||||
reply: [];
|
reply: [];
|
||||||
quote: [];
|
quote: [];
|
||||||
|
delete: [];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const like = async () => {
|
||||||
|
const id = toast.loading("Liking status...");
|
||||||
|
const { data } = await client.value.favouriteStatus(noteId);
|
||||||
|
toast.dismiss(id);
|
||||||
|
toast.success("Status liked");
|
||||||
|
useEvent("note:edit", data);
|
||||||
|
};
|
||||||
|
|
||||||
|
const unlike = async () => {
|
||||||
|
const id = toast.loading("Unliking status...");
|
||||||
|
const { data } = await client.value.unfavouriteStatus(noteId);
|
||||||
|
toast.dismiss(id);
|
||||||
|
toast.success("Status unliked");
|
||||||
|
useEvent("note:edit", data);
|
||||||
|
};
|
||||||
|
|
||||||
|
const reblog = async () => {
|
||||||
|
const id = toast.loading("Reblogging status...");
|
||||||
|
const { data } = await client.value.reblogStatus(noteId);
|
||||||
|
toast.dismiss(id);
|
||||||
|
toast.success("Status reblogged");
|
||||||
|
useEvent("note:edit", data.reblog || data);
|
||||||
|
};
|
||||||
|
|
||||||
|
const unreblog = async () => {
|
||||||
|
const id = toast.loading("Unreblogging status...");
|
||||||
|
const { data } = await client.value.unreblogStatus(noteId);
|
||||||
|
toast.dismiss(id);
|
||||||
|
toast.success("Status unreblogged");
|
||||||
|
useEvent("note:edit", data);
|
||||||
|
};
|
||||||
|
|
||||||
const numberFormat = (number = 0) =>
|
const numberFormat = (number = 0) =>
|
||||||
number !== 0
|
number !== 0
|
||||||
? new Intl.NumberFormat(undefined, {
|
? new Intl.NumberFormat(undefined, {
|
||||||
|
|
|
||||||
|
|
@ -20,17 +20,20 @@ import {
|
||||||
Trash,
|
Trash,
|
||||||
} from "lucide-vue-next";
|
} from "lucide-vue-next";
|
||||||
import { toast } from "vue-sonner";
|
import { toast } from "vue-sonner";
|
||||||
|
import { confirmModalService } from "~/components/modals/composable.ts";
|
||||||
|
|
||||||
const { authorId } = defineProps<{
|
const { authorId, noteId } = defineProps<{
|
||||||
apiNoteString: string;
|
apiNoteString: string;
|
||||||
isRemote: boolean;
|
isRemote: boolean;
|
||||||
url: string;
|
url: string;
|
||||||
remoteUrl: string;
|
remoteUrl: string;
|
||||||
authorId: string;
|
authorId: string;
|
||||||
|
noteId: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
edit: [];
|
edit: [];
|
||||||
|
delete: [];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const { copy } = useClipboard();
|
const { copy } = useClipboard();
|
||||||
|
|
@ -42,11 +45,33 @@ const copyText = (text: string) => {
|
||||||
toast.success("Copied to clipboard");
|
toast.success("Copied to clipboard");
|
||||||
};
|
};
|
||||||
|
|
||||||
const blockUser = async (id: string) => {
|
const blockUser = async (userId: string) => {
|
||||||
await client.value.blockAccount(id);
|
const id = toast.loading("Blocking user...");
|
||||||
|
await client.value.blockAccount(userId);
|
||||||
|
toast.dismiss(id);
|
||||||
|
|
||||||
toast.success("User blocked");
|
toast.success("User blocked");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const _delete = async () => {
|
||||||
|
const confirmation = await confirmModalService.confirm({
|
||||||
|
title: "Delete status",
|
||||||
|
message: "Are you sure you want to delete this status?",
|
||||||
|
confirmText: "Delete",
|
||||||
|
inputType: "none",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!confirmation.confirmed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const id = toast.loading("Deleting status...");
|
||||||
|
await client.value.deleteStatus(noteId);
|
||||||
|
toast.dismiss(id);
|
||||||
|
|
||||||
|
toast.success("Status deleted");
|
||||||
|
emit("delete");
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -86,11 +111,11 @@ const blockUser = async (id: string) => {
|
||||||
</DropdownMenuGroup>
|
</DropdownMenuGroup>
|
||||||
<DropdownMenuSeparator v-if="authorIsMe" />
|
<DropdownMenuSeparator v-if="authorIsMe" />
|
||||||
<DropdownMenuGroup v-if="authorIsMe">
|
<DropdownMenuGroup v-if="authorIsMe">
|
||||||
<DropdownMenuItem as="button">
|
<DropdownMenuItem as="button" :disabled="true">
|
||||||
<Delete class="mr-2 size-4" />
|
<Delete class="mr-2 size-4" />
|
||||||
<span>Delete and redraft</span>
|
<span>Delete and redraft</span>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem as="button">
|
<DropdownMenuItem as="button" @click="_delete">
|
||||||
<Trash class="mr-2 size-4" />
|
<Trash class="mr-2 size-4" />
|
||||||
<span>Delete</span>
|
<span>Delete</span>
|
||||||
<DropdownMenuShortcut>⌘D</DropdownMenuShortcut>
|
<DropdownMenuShortcut>⌘D</DropdownMenuShortcut>
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
</CardContent>
|
</CardContent>
|
||||||
<CardFooter v-if="!hideActions">
|
<CardFooter v-if="!hideActions">
|
||||||
<Actions :reply-count="noteToUse.replies_count" :like-count="noteToUse.favourites_count" :url="url"
|
<Actions :reply-count="noteToUse.replies_count" :like-count="noteToUse.favourites_count" :url="url"
|
||||||
:api-note-string="JSON.stringify(note, null, 4)" :reblog-count="noteToUse.reblogs_count" :remote-url="noteToUse.url" :is-remote="isRemote" :author-id="noteToUse.account.id" @edit="useEvent('composer:edit', note)" @reply="useEvent('composer:reply', note)" @quote="useEvent('composer:quote', note)" />
|
:api-note-string="JSON.stringify(note, null, 4)" :reblog-count="noteToUse.reblogs_count" :remote-url="noteToUse.url" :is-remote="isRemote" :author-id="noteToUse.account.id" @edit="useEvent('composer:edit', note)" @reply="useEvent('composer:reply', note)" @quote="useEvent('composer:quote', note)" @delete="useEvent('note:delete', note)" :note-id="noteToUse.id" :liked="noteToUse.favourited ?? false" :reblogged="noteToUse.reblogged ?? false" />
|
||||||
</CardFooter>
|
</CardFooter>
|
||||||
</Card>
|
</Card>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -33,10 +33,10 @@ const { note } = defineProps<{
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
// Notes can be reblogs, in which case the actual thing to render is inside the reblog property
|
// Notes can be reblogs, in which case the actual thing to render is inside the reblog property
|
||||||
const noteToUse = note.reblog ? note.reblog : note;
|
const noteToUse = computed(() => (note.reblog ? note.reblog : note));
|
||||||
|
|
||||||
const url = wrapUrl(`/@${noteToUse.account.acct}/${noteToUse.id}`);
|
const url = wrapUrl(`/@${noteToUse.value.account.acct}/${noteToUse.value.id}`);
|
||||||
const accountUrl = wrapUrl(`/@${noteToUse.account.acct}`);
|
const accountUrl = wrapUrl(`/@${noteToUse.value.account.acct}`);
|
||||||
const reblogAccountUrl = wrapUrl(`/@${note.account.acct}`);
|
const reblogAccountUrl = wrapUrl(`/@${note.account.acct}`);
|
||||||
const isRemote = noteToUse.account.acct.includes("@");
|
const isRemote = noteToUse.value.account.acct.includes("@");
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue