From e5bb2d720253a7108120884b13041969ba6cf9a0 Mon Sep 17 00:00:00 2001 From: N-Pex Date: Thu, 3 Jul 2025 10:54:31 +0200 Subject: [PATCH] Use thumbnail view when sending attachments So we can preview videos etc. --- .../file_mode/SendAttachmentsLayout.vue | 15 +- src/components/file_mode/ThumbnailView.vue | 163 +++++++++--------- .../messages/composition/useThumbnail.ts | 74 ++++++++ src/models/eventAttachment.ts | 5 +- 4 files changed, 164 insertions(+), 93 deletions(-) create mode 100644 src/components/messages/composition/useThumbnail.ts diff --git a/src/components/file_mode/SendAttachmentsLayout.vue b/src/components/file_mode/SendAttachmentsLayout.vue index ac29a57..175c857 100644 --- a/src/components/file_mode/SendAttachmentsLayout.vue +++ b/src/components/file_mode/SendAttachmentsLayout.vue @@ -20,17 +20,14 @@ @dragleave.prevent="dropTarget = false" @dragenter.prevent="dropTarget = true" > - -
+
{{ currentAttachment.file.name }}
-
+
{{ $t("message.preparing_to_upload") }}
+  
import { defineComponent, reactive } from "vue"; import messageMixin from "../messages/messageMixin"; -import prettyBytes from "pretty-bytes"; import { Attachment } from "../../models/attachment"; import C2PABadge from "../c2pa/C2PABadge.vue"; import { createUploadBatch } from "../../models/attachmentManager"; import AttachmentInfo from "./AttachmentInfo.vue"; +import ThumbnailView from "./ThumbnailView.vue"; export default defineComponent({ mixins: [messageMixin], - components: { C2PABadge, AttachmentInfo }, + components: { C2PABadge, AttachmentInfo, ThumbnailView }, emits: ["pick-file", "close"], props: { defaultRootMessageText: { diff --git a/src/components/file_mode/ThumbnailView.vue b/src/components/file_mode/ThumbnailView.vue index e73fdee..a3f7f58 100644 --- a/src/components/file_mode/ThumbnailView.vue +++ b/src/components/file_mode/ThumbnailView.vue @@ -1,108 +1,107 @@ - diff --git a/src/components/messages/composition/useThumbnail.ts b/src/components/messages/composition/useThumbnail.ts new file mode 100644 index 0000000..c841377 --- /dev/null +++ b/src/components/messages/composition/useThumbnail.ts @@ -0,0 +1,74 @@ +import { computed, ref } from "vue"; +import { KeanuEvent } from "../../../models/eventAttachment"; +import utils from "@/plugins/utils"; +import prettyBytes from "pretty-bytes"; + +export const useThumbnail = (source: KeanuEvent | File | undefined) => { + const isVideo = ref(false); + const isImage = ref(false); + const isAPK = ref(false); + const isIPA = ref(false); + const isPDF = ref(false); + const isZip = ref(false); + const isChannelMessage = ref(false); + const fileName = ref(""); + const fileSize = ref(""); + + function isEvent(source: KeanuEvent | File | undefined): source is KeanuEvent { + return (source as KeanuEvent).getId !== undefined; + } + + if (isEvent(source)) { + const event = source; + isVideo.value = event.getContent().msgtype == "m.video"; + isImage.value = event.getContent().msgtype == "m.image"; + isAPK.value = utils.isFileTypeAPK(event); + isIPA.value = utils.isFileTypeIPA(event); + isPDF.value = utils.isFileTypePDF(event); + isZip.value = utils.isFileTypeZip(event); + isChannelMessage.value = event.isChannelMessage ?? false; + fileName.value = utils.getFileName(event); + fileSize.value = utils.getFileSizeFormatted(event); + } else if (source) { + const file = source as File; + isVideo.value = file.type.startsWith("video/"); + isImage.value = file.type.startsWith("image/"); + fileName.value = file.name; + fileSize.value = prettyBytes(file.size); + } + + const fileTypeIcon = computed(() => { + if (isAPK.value) { + if (isChannelMessage.value) { + return "$vuetify.icons.ic_channel_apk"; + } + return "$vuetify.icons.ic_apk"; + } else if (isIPA.value) { + return "$vuetify.icons.ic_ipa"; + } else if (isPDF.value) { + if (isChannelMessage.value) { + return "$vuetify.icons.ic_channel_pdf"; + } + return "$vuetify.icons.ic_pdf"; + } else if (isZip.value) { + return "$vuetify.icons.ic_zip"; + } + return "description"; + }); + + const fileTypeIconClass = computed(() => { + if (isZip.value) { + return "zip"; + } + return undefined; + }); + + return { + isVideo, + isImage, + fileTypeIcon, + fileTypeIconClass, + fileName, + fileSize, + }; +}; diff --git a/src/models/eventAttachment.ts b/src/models/eventAttachment.ts index 4c95497..0635501 100644 --- a/src/models/eventAttachment.ts +++ b/src/models/eventAttachment.ts @@ -8,11 +8,13 @@ export type KeanuEventExtension = { replyEvent?: MatrixEvent & KeanuEventExtension; } +export type KeanuEvent = MatrixEvent & KeanuEventExtension; + export type EventAttachmentUrlType = "src" | "thumbnail"; export type EventAttachmentUrlData = {data: string, type: EventAttachmentUrlType}; export type EventAttachment = { - event: MatrixEvent & KeanuEventExtension; + event: KeanuEvent; name: string; src?: string; srcSize: number; @@ -28,7 +30,6 @@ export type EventAttachment = { release: (src: boolean, thumbnail: boolean) => void; }; -export type KeanuEvent = MatrixEvent & KeanuEventExtension; export type KeanuRoom = Room & { displayType: "im.keanu.room_type_default" | "im.keanu.room_type_voice" | "im.keanu.room_type_file" | "im.keanu.room_type_channel" | undefined;