Messages update & deletion

This commit is contained in:
LittleSheep 2024-03-31 20:35:36 +08:00
parent fc1aef6eb7
commit 09154f1359
10 changed files with 209 additions and 27 deletions

View File

@ -1,5 +1,33 @@
<template> <template>
<v-form class="flex-grow-1" ref="chat" @submit.prevent="sendMessage"> <v-form class="flex-grow-1" ref="chat" @submit.prevent="sendMessage">
<v-expand-transition>
<v-alert
v-show="channels.related?.messages?.edit_to"
class="mb-3 text-sm"
variant="tonal"
density="compact"
type="info"
>
You are about editing a message #{{ channels.related?.messages?.edit_to?.id }}
<template #prepend>
<div class="h-[30px] flex items-center justify-center">
<v-icon icon="mdi-pencil" size="small" />
</div>
</template>
<template #append>
<v-btn
icon="mdi-close"
size="x-small"
color="info"
variant="text"
@click="channels.related.messages.edit_to = null"
/>
</template>
</v-alert>
</v-expand-transition>
<v-textarea <v-textarea
auto-grow auto-grow
hide-details hide-details
@ -54,7 +82,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { reactive, ref } from "vue" import { reactive, ref, watch } from "vue"
import { request } from "@/scripts/request" import { request } from "@/scripts/request"
import { getAtk } from "@/stores/userinfo" import { getAtk } from "@/stores/userinfo"
import { useChannels } from "@/stores/channels" import { useChannels } from "@/stores/channels"
@ -82,9 +110,16 @@ const data = ref<any>({
}) })
async function sendMessage() { async function sendMessage() {
if (!data.value.content) return
const url = channels.related.messages.edit_to
? `/api/channels/${channels.current.alias}/messages/${channels.related.messages.edit_to?.id}`
: `/api/channels/${channels.current.alias}/messages`
const method = channels.related.messages.edit_to ? "PUT" : "POST"
loading.value = true loading.value = true
const res = await request("messaging", `/api/channels/${channels.current.alias}/messages`, { const res = await request("messaging", url, {
method: "POST", method: method,
headers: { Authorization: `Bearer ${await getAtk()}`, "Content-Type": "application/json" }, headers: { Authorization: `Bearer ${await getAtk()}`, "Content-Type": "application/json" },
body: JSON.stringify(data.value) body: JSON.stringify(data.value)
}) })
@ -98,8 +133,19 @@ async function sendMessage() {
loading.value = false loading.value = false
} }
watch(
() => channels.related.messages.edit_to,
(val) => {
if (val) {
data.value = val
}
}
)
function resetEditor() { function resetEditor() {
chat.value?.reset() chat.value?.reset()
channels.related.messages.edit_to = null
channels.related.messages.delete_to = null
data.value = { data.value = {
content: "", content: "",
attachments: [] attachments: []

View File

@ -4,7 +4,7 @@
:onLoad="props.loader" :onLoad="props.loader"
> >
<template v-for="item in props.messages" :key="item"> <template v-for="item in props.messages" :key="item">
<chat-message class="mb-4" :item="item" /> <chat-message class="px-6 py-2" :item="item" />
</template> </template>
<template #empty> <template #empty>

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="flex gap-2"> <div class="flex gap-2 relative transition-colors transition-300 message-item">
<div> <div>
<v-avatar <v-avatar
color="grey-lighten-2" color="grey-lighten-2"
@ -19,17 +19,54 @@
:attachments="props.item?.attachments" :attachments="props.item?.attachments"
/> />
</div> </div>
<div class="transition-opacity transition-300 message-action">
<v-card>
<div class="flex px-2 py-0.5">
<v-btn icon="mdi-pencil" size="x-small" variant="text" @click="editMessage" />
<v-btn icon="mdi-delete" size="x-small" variant="text" color="error" @click="deleteMessage" />
</div>
</v-card>
</div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { useChannels } from "@/stores/channels"
import MessageAttachment from "@/components/chat/renderer/MessageAttachment.vue" import MessageAttachment from "@/components/chat/renderer/MessageAttachment.vue"
const channels = useChannels()
const props = defineProps<{ item: any }>() const props = defineProps<{ item: any }>()
function editMessage() {
channels.related.messages.edit_to = JSON.parse(JSON.stringify(props.item))
}
function deleteMessage() {
channels.related.messages.delete_to = JSON.parse(JSON.stringify(props.item))
channels.related.messages.delete_to.channel = channels.current
channels.show.messages.delete = true
}
</script> </script>
<style scoped> <style scoped>
.rounded-card { .rounded-card {
border-radius: 8px; border-radius: 8px;
} }
.message-action {
position: absolute;
right: 8px;
top: -25%;
opacity: 0;
}
.message-item:hover {
background-color: rgba(0, 0, 0, .15);
}
.message-item:hover .message-action {
opacity: 100%;
}
</style> </style>

View File

@ -0,0 +1,52 @@
<template>
<v-card title="Delete a message" class="min-h-[540px]" :loading="loading">
<template #text>
You are deleting a message
<b>#{{ channels.related?.messages?.delete_to?.id }}</b> <br />
This message will gone and never appear again. But the replies won't affected. Are you confirm?
</template>
<template #actions>
<div class="w-full flex justify-end">
<v-btn color="grey-darken-3" @click="channels.show.messages.delete = false">Not really</v-btn>
<v-btn color="error" :disabled="loading" @click="deleteMessage">Yes</v-btn>
</div>
</template>
</v-card>
<v-snackbar v-model="success" :timeout="3000">The realm has been deleted.</v-snackbar>
<!-- @vue-ignore -->
<v-snackbar v-model="error" :timeout="5000">Something went wrong... {{ error }}</v-snackbar>
</template>
<script setup lang="ts">
import { request } from "@/scripts/request"
import { getAtk } from "@/stores/userinfo"
import { useChannels } from "@/stores/channels"
import { ref } from "vue"
const channels = useChannels()
const error = ref<string | null>(null)
const success = ref(false)
const loading = ref(false)
async function deleteMessage() {
const target = channels.related.messages.delete_to
const url = `/api/channels/${target.channel.alias}/messages/${target.id}`
loading.value = true
const res = await request("messaging", url, {
method: "DELETE",
headers: { Authorization: `Bearer ${await getAtk()}` }
})
if (res.status !== 200) {
error.value = await res.text()
} else {
success.value = true
channels.show.messages.delete = false
channels.related.messages.delete_to = null
}
loading.value = false
}
</script>

View File

@ -8,7 +8,7 @@
<template #actions> <template #actions>
<div class="w-full flex justify-end"> <div class="w-full flex justify-end">
<v-btn color="grey-darken-3" @click="channels.show.delete = false">Not really</v-btn> <v-btn color="grey-darken-3" @click="channels.show.delete = false">Not really</v-btn>
<v-btn color="error" :disabled="loading" @click="deletePost">Yes</v-btn> <v-btn color="error" :disabled="loading" @click="deleteChannel">Yes</v-btn>
</div> </div>
</template> </template>
</v-card> </v-card>
@ -36,7 +36,7 @@ const error = ref<string | null>(null)
const success = ref(false) const success = ref(false)
const loading = ref(false) const loading = ref(false)
async function deletePost() { async function deleteChannel() {
const target = channels.related.delete_to const target = channels.related.delete_to
const url = `/api/channels/${target.id}` const url = `/api/channels/${target.id}`
@ -52,8 +52,8 @@ async function deletePost() {
channels.show.delete = false channels.show.delete = false
channels.related.delete_to = null channels.related.delete_to = null
emits("relist") emits("relist")
if (route.name?.toString()?.startsWith("realm")) { if (route.name?.toString()?.includes("channel")) {
router.push({ name: "explore" }) await router.push({ name: "explore" })
} }
} }
loading.value = false loading.value = false

View File

@ -8,6 +8,10 @@
<v-bottom-sheet class="max-w-[480px]" v-model="channels.show.delete"> <v-bottom-sheet class="max-w-[480px]" v-model="channels.show.delete">
<channel-deletion @relist="channels.list" /> <channel-deletion @relist="channels.list" />
</v-bottom-sheet> </v-bottom-sheet>
<v-bottom-sheet class="max-w-[480px]" v-model="channels.show.messages.delete">
<message-deletion />
</v-bottom-sheet>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@ -15,6 +19,7 @@ import { useChannels } from "@/stores/channels"
import ChannelEditor from "@/components/chat/channels/ChannelEditor.vue" import ChannelEditor from "@/components/chat/channels/ChannelEditor.vue"
import ChannelMembers from "@/components/chat/channels/ChannelMembers.vue" import ChannelMembers from "@/components/chat/channels/ChannelMembers.vue"
import ChannelDeletion from "@/components/chat/channels/ChannelDeletion.vue" import ChannelDeletion from "@/components/chat/channels/ChannelDeletion.vue"
import MessageDeletion from "@/components/chat/MessageDeletion.vue"
const channels = useChannels() const channels = useChannels()
</script> </script>

View File

@ -8,7 +8,7 @@
<template #actions> <template #actions>
<div class="w-full flex justify-end"> <div class="w-full flex justify-end">
<v-btn color="grey-darken-3" @click="realms.show.delete = false">Not really</v-btn> <v-btn color="grey-darken-3" @click="realms.show.delete = false">Not really</v-btn>
<v-btn color="error" :disabled="loading" @click="deletePost">Yes</v-btn> <v-btn color="error" :disabled="loading" @click="deleteRealm">Yes</v-btn>
</div> </div>
</template> </template>
</v-card> </v-card>
@ -36,7 +36,7 @@ const error = ref<string | null>(null)
const success = ref(false) const success = ref(false)
const loading = ref(false) const loading = ref(false)
async function deletePost() { async function deleteRealm() {
const target = realms.related.delete_to const target = realms.related.delete_to
const url = `/api/realms/${target.id}` const url = `/api/realms/${target.id}`
@ -52,8 +52,8 @@ async function deletePost() {
realms.show.delete = false realms.show.delete = false
realms.related.delete_to = null realms.related.delete_to = null
emits("relist") emits("relist")
if (route.name?.toString()?.startsWith("realm")) { if (route.name?.toString()?.includes("realm")) {
router.push({ name: "explore" }) await router.push({ name: "explore" })
} }
} }
loading.value = false loading.value = false

View File

@ -1,10 +1,10 @@
<template> <template>
<v-app-bar :order="5" color="grey-lighten-3"> <v-app-bar :order="5" color="grey-lighten-3">
<div class="max-md:px-5 md:px-12 flex flex-grow-1 items-center"> <div class="max-md:px-5 md:px-12 flex flex-grow-1 items-center max-w-full">
<v-app-bar-nav-icon icon="mdi-chat" :loading="loading" /> <v-app-bar-nav-icon icon="mdi-chat" :loading="loading" />
<h2 class="ml-2 text-lg font-500">{{ channels.current?.name }}</h2> <h2 class="ml-2 text-lg font-500 overflow-hidden ws-nowrap text-clip">{{ channels.current?.name }}</h2>
<p class="ml-3 text-xs opacity-80">{{ channels.current?.description }}</p> <p class="ml-3 text-xs opacity-80 overflow-hidden ws-nowrap text-clip">{{ channels.current?.description }}</p>
<v-spacer /> <v-spacer />
@ -23,7 +23,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { request } from "@/scripts/request" import { request } from "@/scripts/request"
import { useRoute } from "vue-router" import { useRoute } from "vue-router"
import { ref, watch } from "vue" import { onMounted, ref, watch } from "vue"
import { useChannels } from "@/stores/channels" import { useChannels } from "@/stores/channels"
import ChannelAction from "@/components/chat/channels/ChannelAction.vue" import ChannelAction from "@/components/chat/channels/ChannelAction.vue"
@ -47,9 +47,11 @@ async function readMetadata() {
watch( watch(
() => route.params.channel, () => route.params.channel,
() => { (val) => {
if (val) {
channels.messages = [] channels.messages = []
readMetadata() readMetadata()
}
}, },
{ immediate: true } { immediate: true }
) )
@ -62,4 +64,9 @@ watch(() => channels.done, (val) => {
}) })
} }
}, { immediate: true }) }, { immediate: true })
onMounted(() => {
channels.current = null
channels.messages = []
})
</script> </script>

View File

@ -1,7 +1,8 @@
import { defineStore } from "pinia" import { defineStore } from "pinia"
import { reactive, ref } from "vue" import { reactive, ref, watch } from "vue"
import { checkLoggedIn, getAtk } from "@/stores/userinfo" import { checkLoggedIn, getAtk } from "@/stores/userinfo"
import { buildRequestUrl, request } from "@/scripts/request" import { buildRequestUrl, request } from "@/scripts/request"
import { useRoute } from "vue-router"
export const useChannels = defineStore("channels", () => { export const useChannels = defineStore("channels", () => {
let socket: WebSocket let socket: WebSocket
@ -11,19 +12,40 @@ export const useChannels = defineStore("channels", () => {
const show = reactive({ const show = reactive({
members: false, members: false,
editor: false, editor: false,
delete: false,
messages: {
delete: false delete: false
}
}) })
const related = reactive<{ edit_to: any; manage_to: any; delete_to: any }>({ const related = reactive<{ [id: string]: any }>({
edit_to: null, edit_to: null,
manage_to: null, manage_to: null,
delete_to: null,
messages: {
edit_to: null,
delete_to: null delete_to: null
}
}) })
const available = ref<any[]>([]) const available = ref<any[]>([])
const current = ref<any>(null) const current = ref<any>(null)
const messages = ref<any[]>([]) const messages = ref<any[]>([])
const route = useRoute()
watch(
() => route.params.channel,
(val) => {
if (!val) {
messages.value = []
current.value = null
}
},
{ immediate: true }
)
async function list() { async function list() {
if (!(await checkLoggedIn())) return if (!(await checkLoggedIn())) return
@ -57,6 +79,17 @@ export const useChannels = defineStore("channels", () => {
switch (data["w"]) { switch (data["w"]) {
case "messages.new": case "messages.new":
messages.value.unshift(payload) messages.value.unshift(payload)
break
case "messages.update":
messages.value = messages.value.map((x) => {
if (x.id === payload.id) return payload
else return x
})
break
case "messages.burnt":
messages.value = messages.value.filter((x) => {
return x.id !== payload.id
})
} }
} }
}) })

View File

@ -1,5 +1,5 @@
<template> <template>
<v-container fluid class="px-6"> <v-container fluid class="px-0">
<div class="message-list"> <div class="message-list">
<chat-list :loader="readMore" :messages="channels.messages" /> <chat-list :loader="readMore" :messages="channels.messages" />
</div> </div>
@ -22,7 +22,7 @@
import { useChannels } from "@/stores/channels" import { useChannels } from "@/stores/channels"
import { request } from "@/scripts/request" import { request } from "@/scripts/request"
import { useUI } from "@/stores/ui" import { useUI } from "@/stores/ui"
import { computed, reactive, ref, watch } from "vue" import { computed, onUnmounted, reactive, ref, watch } from "vue"
import { useRoute } from "vue-router" import { useRoute } from "vue-router"
import ChatList from "@/components/chat/ChatList.vue" import ChatList from "@/components/chat/ChatList.vue"
import ChatEditor from "@/components/chat/ChatEditor.vue" import ChatEditor from "@/components/chat/ChatEditor.vue"
@ -83,7 +83,9 @@ async function readMore({ done }: any) {
} }
} }
watch(() => channels.current, (val) => { watch(
() => channels.current,
(val) => {
if (val) { if (val) {
pagination.page = 1 pagination.page = 1
pagination.total = 0 pagination.total = 0