✨ Messages update & deletion
This commit is contained in:
		@@ -1,5 +1,33 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <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
 | 
			
		||||
      auto-grow
 | 
			
		||||
      hide-details
 | 
			
		||||
@@ -54,7 +82,7 @@
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { reactive, ref } from "vue"
 | 
			
		||||
import { reactive, ref, watch } from "vue"
 | 
			
		||||
import { request } from "@/scripts/request"
 | 
			
		||||
import { getAtk } from "@/stores/userinfo"
 | 
			
		||||
import { useChannels } from "@/stores/channels"
 | 
			
		||||
@@ -82,9 +110,16 @@ const data = ref<any>({
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
  const res = await request("messaging", `/api/channels/${channels.current.alias}/messages`, {
 | 
			
		||||
    method: "POST",
 | 
			
		||||
  const res = await request("messaging", url, {
 | 
			
		||||
    method: method,
 | 
			
		||||
    headers: { Authorization: `Bearer ${await getAtk()}`, "Content-Type": "application/json" },
 | 
			
		||||
    body: JSON.stringify(data.value)
 | 
			
		||||
  })
 | 
			
		||||
@@ -98,8 +133,19 @@ async function sendMessage() {
 | 
			
		||||
  loading.value = false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
watch(
 | 
			
		||||
  () => channels.related.messages.edit_to,
 | 
			
		||||
  (val) => {
 | 
			
		||||
    if (val) {
 | 
			
		||||
      data.value = val
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
function resetEditor() {
 | 
			
		||||
  chat.value?.reset()
 | 
			
		||||
  channels.related.messages.edit_to = null
 | 
			
		||||
  channels.related.messages.delete_to = null
 | 
			
		||||
  data.value = {
 | 
			
		||||
    content: "",
 | 
			
		||||
    attachments: []
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@
 | 
			
		||||
    :onLoad="props.loader"
 | 
			
		||||
  >
 | 
			
		||||
    <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 #empty>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="flex gap-2">
 | 
			
		||||
  <div class="flex gap-2 relative transition-colors transition-300 message-item">
 | 
			
		||||
    <div>
 | 
			
		||||
      <v-avatar
 | 
			
		||||
        color="grey-lighten-2"
 | 
			
		||||
@@ -19,17 +19,54 @@
 | 
			
		||||
        :attachments="props.item?.attachments"
 | 
			
		||||
      />
 | 
			
		||||
    </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>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { useChannels } from "@/stores/channels"
 | 
			
		||||
import MessageAttachment from "@/components/chat/renderer/MessageAttachment.vue"
 | 
			
		||||
 | 
			
		||||
const channels = useChannels()
 | 
			
		||||
 | 
			
		||||
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>
 | 
			
		||||
 | 
			
		||||
<style scoped>
 | 
			
		||||
.rounded-card {
 | 
			
		||||
  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>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										52
									
								
								src/components/chat/MessageDeletion.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/components/chat/MessageDeletion.vue
									
									
									
									
									
										Normal 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>
 | 
			
		||||
@@ -8,7 +8,7 @@
 | 
			
		||||
    <template #actions>
 | 
			
		||||
      <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="error" :disabled="loading" @click="deletePost">Yes</v-btn>
 | 
			
		||||
        <v-btn color="error" :disabled="loading" @click="deleteChannel">Yes</v-btn>
 | 
			
		||||
      </div>
 | 
			
		||||
    </template>
 | 
			
		||||
  </v-card>
 | 
			
		||||
@@ -36,7 +36,7 @@ const error = ref<string | null>(null)
 | 
			
		||||
const success = ref(false)
 | 
			
		||||
const loading = ref(false)
 | 
			
		||||
 | 
			
		||||
async function deletePost() {
 | 
			
		||||
async function deleteChannel() {
 | 
			
		||||
  const target = channels.related.delete_to
 | 
			
		||||
  const url = `/api/channels/${target.id}`
 | 
			
		||||
 | 
			
		||||
@@ -52,8 +52,8 @@ async function deletePost() {
 | 
			
		||||
    channels.show.delete = false
 | 
			
		||||
    channels.related.delete_to = null
 | 
			
		||||
    emits("relist")
 | 
			
		||||
    if (route.name?.toString()?.startsWith("realm")) {
 | 
			
		||||
      router.push({ name: "explore" })
 | 
			
		||||
    if (route.name?.toString()?.includes("channel")) {
 | 
			
		||||
      await router.push({ name: "explore" })
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  loading.value = false
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,10 @@
 | 
			
		||||
  <v-bottom-sheet class="max-w-[480px]" v-model="channels.show.delete">
 | 
			
		||||
    <channel-deletion @relist="channels.list" />
 | 
			
		||||
  </v-bottom-sheet>
 | 
			
		||||
 | 
			
		||||
  <v-bottom-sheet class="max-w-[480px]" v-model="channels.show.messages.delete">
 | 
			
		||||
    <message-deletion />
 | 
			
		||||
  </v-bottom-sheet>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
@@ -15,6 +19,7 @@ import { useChannels } from "@/stores/channels"
 | 
			
		||||
import ChannelEditor from "@/components/chat/channels/ChannelEditor.vue"
 | 
			
		||||
import ChannelMembers from "@/components/chat/channels/ChannelMembers.vue"
 | 
			
		||||
import ChannelDeletion from "@/components/chat/channels/ChannelDeletion.vue"
 | 
			
		||||
import MessageDeletion from "@/components/chat/MessageDeletion.vue"
 | 
			
		||||
 | 
			
		||||
const channels = useChannels()
 | 
			
		||||
</script>
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,7 @@
 | 
			
		||||
    <template #actions>
 | 
			
		||||
      <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="error" :disabled="loading" @click="deletePost">Yes</v-btn>
 | 
			
		||||
        <v-btn color="error" :disabled="loading" @click="deleteRealm">Yes</v-btn>
 | 
			
		||||
      </div>
 | 
			
		||||
    </template>
 | 
			
		||||
  </v-card>
 | 
			
		||||
@@ -36,7 +36,7 @@ const error = ref<string | null>(null)
 | 
			
		||||
const success = ref(false)
 | 
			
		||||
const loading = ref(false)
 | 
			
		||||
 | 
			
		||||
async function deletePost() {
 | 
			
		||||
async function deleteRealm() {
 | 
			
		||||
  const target = realms.related.delete_to
 | 
			
		||||
  const url = `/api/realms/${target.id}`
 | 
			
		||||
 | 
			
		||||
@@ -52,8 +52,8 @@ async function deletePost() {
 | 
			
		||||
    realms.show.delete = false
 | 
			
		||||
    realms.related.delete_to = null
 | 
			
		||||
    emits("relist")
 | 
			
		||||
    if (route.name?.toString()?.startsWith("realm")) {
 | 
			
		||||
      router.push({ name: "explore" })
 | 
			
		||||
    if (route.name?.toString()?.includes("realm")) {
 | 
			
		||||
      await router.push({ name: "explore" })
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  loading.value = false
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,10 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <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" />
 | 
			
		||||
 | 
			
		||||
      <h2 class="ml-2 text-lg font-500">{{ channels.current?.name }}</h2>
 | 
			
		||||
      <p class="ml-3 text-xs opacity-80">{{ channels.current?.description }}</p>
 | 
			
		||||
      <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 overflow-hidden ws-nowrap text-clip">{{ channels.current?.description }}</p>
 | 
			
		||||
 | 
			
		||||
      <v-spacer />
 | 
			
		||||
 | 
			
		||||
@@ -23,7 +23,7 @@
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { request } from "@/scripts/request"
 | 
			
		||||
import { useRoute } from "vue-router"
 | 
			
		||||
import { ref, watch } from "vue"
 | 
			
		||||
import { onMounted, ref, watch } from "vue"
 | 
			
		||||
import { useChannels } from "@/stores/channels"
 | 
			
		||||
import ChannelAction from "@/components/chat/channels/ChannelAction.vue"
 | 
			
		||||
 | 
			
		||||
@@ -47,9 +47,11 @@ async function readMetadata() {
 | 
			
		||||
 | 
			
		||||
watch(
 | 
			
		||||
  () => route.params.channel,
 | 
			
		||||
  () => {
 | 
			
		||||
    channels.messages = []
 | 
			
		||||
    readMetadata()
 | 
			
		||||
  (val) => {
 | 
			
		||||
    if (val) {
 | 
			
		||||
      channels.messages = []
 | 
			
		||||
      readMetadata()
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  { immediate: true }
 | 
			
		||||
)
 | 
			
		||||
@@ -62,4 +64,9 @@ watch(() => channels.done, (val) => {
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
}, { immediate: true })
 | 
			
		||||
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
  channels.current = null
 | 
			
		||||
  channels.messages = []
 | 
			
		||||
})
 | 
			
		||||
</script>
 | 
			
		||||
@@ -1,7 +1,8 @@
 | 
			
		||||
import { defineStore } from "pinia"
 | 
			
		||||
import { reactive, ref } from "vue"
 | 
			
		||||
import { reactive, ref, watch } from "vue"
 | 
			
		||||
import { checkLoggedIn, getAtk } from "@/stores/userinfo"
 | 
			
		||||
import { buildRequestUrl, request } from "@/scripts/request"
 | 
			
		||||
import { useRoute } from "vue-router"
 | 
			
		||||
 | 
			
		||||
export const useChannels = defineStore("channels", () => {
 | 
			
		||||
  let socket: WebSocket
 | 
			
		||||
@@ -11,19 +12,40 @@ export const useChannels = defineStore("channels", () => {
 | 
			
		||||
  const show = reactive({
 | 
			
		||||
    members: false,
 | 
			
		||||
    editor: false,
 | 
			
		||||
    delete: false
 | 
			
		||||
    delete: false,
 | 
			
		||||
 | 
			
		||||
    messages: {
 | 
			
		||||
      delete: false
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  const related = reactive<{ edit_to: any; manage_to: any; delete_to: any }>({
 | 
			
		||||
  const related = reactive<{ [id: string]: any }>({
 | 
			
		||||
    edit_to: null,
 | 
			
		||||
    manage_to: null,
 | 
			
		||||
    delete_to: null
 | 
			
		||||
    delete_to: null,
 | 
			
		||||
 | 
			
		||||
    messages: {
 | 
			
		||||
      edit_to: null,
 | 
			
		||||
      delete_to: null
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  const available = ref<any[]>([])
 | 
			
		||||
  const current = ref<any>(null)
 | 
			
		||||
  const messages = ref<any[]>([])
 | 
			
		||||
 | 
			
		||||
  const route = useRoute()
 | 
			
		||||
  watch(
 | 
			
		||||
    () => route.params.channel,
 | 
			
		||||
    (val) => {
 | 
			
		||||
      if (!val) {
 | 
			
		||||
        messages.value = []
 | 
			
		||||
        current.value = null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    { immediate: true }
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
  async function list() {
 | 
			
		||||
    if (!(await checkLoggedIn())) return
 | 
			
		||||
 | 
			
		||||
@@ -57,6 +79,17 @@ export const useChannels = defineStore("channels", () => {
 | 
			
		||||
        switch (data["w"]) {
 | 
			
		||||
          case "messages.new":
 | 
			
		||||
            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
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <v-container fluid class="px-6">
 | 
			
		||||
  <v-container fluid class="px-0">
 | 
			
		||||
    <div class="message-list">
 | 
			
		||||
      <chat-list :loader="readMore" :messages="channels.messages" />
 | 
			
		||||
    </div>
 | 
			
		||||
@@ -22,7 +22,7 @@
 | 
			
		||||
import { useChannels } from "@/stores/channels"
 | 
			
		||||
import { request } from "@/scripts/request"
 | 
			
		||||
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 ChatList from "@/components/chat/ChatList.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) {
 | 
			
		||||
      pagination.page = 1
 | 
			
		||||
      pagination.total = 0
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user