From 4662bcdc7df7f25683ddad69df3a8d00c67ac466 Mon Sep 17 00:00:00 2001 From: N-Pex Date: Wed, 5 Nov 2025 16:56:17 +0100 Subject: [PATCH] Introduce a list of image mime types we can support as images E.g. TIFF will now be handled as a generic file, not image, even though it has mime prefix "image/" --- src/components/CreateRoom.vue | 2 +- src/components/Join.vue | 2 +- src/components/Profile.vue | 2 +- src/components/RoomAvatarPicker.vue | 2 +- src/components/RoomExport.vue | 2 +- src/components/create/CreateRoomAvatar.vue | 2 +- .../messages/composition/useThumbnail.ts | 2 +- src/main.js | 5 +++++ src/models/attachmentManager.ts | 2 +- src/plugins/utils.js | 19 +++++++++++++++++-- 10 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/components/CreateRoom.vue b/src/components/CreateRoom.vue index a34dd22..e85e773 100644 --- a/src/components/CreateRoom.vue +++ b/src/components/CreateRoom.vue @@ -68,7 +68,7 @@ + @change="handlePickedUserAvatar($event)" :accept="supportedImageTypes" class="d-none" /> diff --git a/src/components/Join.vue b/src/components/Join.vue index 103c245..c12d547 100644 --- a/src/components/Join.vue +++ b/src/components/Join.vue @@ -114,7 +114,7 @@ + :accept="supportedImageTypes" class="d-none" />

{{ $t("profile.select_language") }}

diff --git a/src/components/Profile.vue b/src/components/Profile.vue index 09a6ac5..cf2d62a 100644 --- a/src/components/Profile.vue +++ b/src/components/Profile.vue @@ -33,7 +33,7 @@ type="file" name="avatar" @change="handlePickedAvatar($event)" - accept="image/*" + :accept="supportedImageTypes" class="d-none" /> diff --git a/src/components/RoomAvatarPicker.vue b/src/components/RoomAvatarPicker.vue index a2e7990..e7c4c40 100644 --- a/src/components/RoomAvatarPicker.vue +++ b/src/components/RoomAvatarPicker.vue @@ -10,7 +10,7 @@ type="file" name="roomAvatar" @change="handleRoomPickedAvatar($event)" - accept="image/*" + :accept="supportedImageTypes" class="d-none" /> diff --git a/src/components/RoomExport.vue b/src/components/RoomExport.vue index eda85df..790c68e 100644 --- a/src/components/RoomExport.vue +++ b/src/components/RoomExport.vue @@ -384,7 +384,7 @@ export default { const mime = blob.type; - if (mime.startsWith("image/")) { + if (util.isSupportedImageType(mime)) { var extension = ".png"; switch (mime) { case "image/jpeg": diff --git a/src/components/create/CreateRoomAvatar.vue b/src/components/create/CreateRoomAvatar.vue index ec66588..bad15ce 100644 --- a/src/components/create/CreateRoomAvatar.vue +++ b/src/components/create/CreateRoomAvatar.vue @@ -4,7 +4,7 @@ $vuetify.icons.room_avatar_placeholder $vuetify.icons.ic_camera + @change="handlePickedRoomAvatar($event)" :accept="supportedImageTypes" class="d-none" />
diff --git a/src/components/messages/composition/useThumbnail.ts b/src/components/messages/composition/useThumbnail.ts index 73d0c8a..f1513b6 100644 --- a/src/components/messages/composition/useThumbnail.ts +++ b/src/components/messages/composition/useThumbnail.ts @@ -32,7 +32,7 @@ export const useThumbnail = (source: KeanuEvent | File | undefined) => { } else if (source) { const file = source as File; isVideo.value = file.type.startsWith("video/"); - isImage.value = file.type.startsWith("image/"); + isImage.value = utils.isSupportedImageType(file.type); fileName.value = file.name; fileSize.value = prettyBytes(file.size); } diff --git a/src/main.js b/src/main.js index e1b9423..d5be7dd 100644 --- a/src/main.js +++ b/src/main.js @@ -16,6 +16,8 @@ import Vue3Sanitize from "vue-3-sanitize"; import vuetify from './plugins/vuetify'; import { Buffer } from 'buffer/' import { createApp, h } from 'vue'; +import { supportedImageTypes } from '@/plugins/utils'; + globalThis.Buffer = Buffer; var defaultOptions = Vue3Sanitize.defaults; @@ -25,6 +27,9 @@ defaultOptions.allowedTags = []; const app = createApp({ render: () => h(App) }); +app.config.globalProperties.supportedImageTypes = supportedImageTypes; + + app.use(Vue3Sanitize, defaultOptions); app.config.productionTip = false diff --git a/src/models/attachmentManager.ts b/src/models/attachmentManager.ts index abbb79d..dfe1bd1 100644 --- a/src/models/attachmentManager.ts +++ b/src/models/attachmentManager.ts @@ -77,7 +77,7 @@ export class AttachmentManager { private async prepareUpload(attachment: Attachment, room: KeanuRoom): Promise { const file = attachment.file; - if (file.type.startsWith("image/")) { + if (utils.isSupportedImageType(file.type)) { let url = URL.createObjectURL(file); attachment.src = url; if (attachment.src) { diff --git a/src/plugins/utils.js b/src/plugins/utils.js index e780f14..ef2c615 100644 --- a/src/plugins/utils.js +++ b/src/plugins/utils.js @@ -30,6 +30,17 @@ export const CLIENT_EVENT_MEDIA_INTERVENTION_FLAGS = "im.keanu.proof_hint"; export const THUMBNAIL_MAX_WIDTH = 640; export const THUMBNAIL_MAX_HEIGHT = 640; +export const supportedImageTypes = [ + "image/bmp", + "image/gif", + "image/jpg", + "image/jpeg", + "image/png", + "image/svg", + "image/webp", + "image/x-icon" +]; + // Install extended localized format dayjs.extend(localizedFormat); dayjs.extend(duration); @@ -437,7 +448,7 @@ class Util { }; } - if (file.type.startsWith("image/")) { + if (this.isSupportedImageType(file.type)) { msgtype = "m.image"; // Generate thumbnail? @@ -940,7 +951,7 @@ class Util { var reader = new FileReader(); reader.onload = (e) => { const file = event.target.files[0]; - if (file.type.startsWith("image/")) { + if (this.isSupportedImageType(file.type)) { try { var image = e.target.result; @@ -1236,6 +1247,10 @@ class Util { return false; } + isSupportedImageType(mime) { + return supportedImageTypes.some(prefix => mime.startsWith(prefix)); + } + isMobileOrTabletBrowser() { // Regular expression to match common mobile and tablet browser user agent strings const mobileTabletPattern = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|Tablet|Mobile|CriOS/i;