diff --git a/frontend/pages/communication/chat.vue b/frontend/pages/communication/chat.vue index b9b0878..29ebc17 100644 --- a/frontend/pages/communication/chat.vue +++ b/frontend/pages/communication/chat.vue @@ -17,6 +17,7 @@ const matrixMembers = ref([]) const matrixMessageDraft = ref("") const matrixMessagesViewport = ref(null) const matrixAttachmentInput = ref(null) +const matrixAttachmentObjectUrls = ref({}) const roomCreateOpen = ref(false) const collapsedRoomGroups = ref({}) const matrixCallOpen = ref(false) @@ -56,6 +57,7 @@ let matrixMessagesRequestActive = false let matrixMembersRequestActive = false let matrixLiveKitRoom = null const matrixCallVideoElements = new Map() +const matrixAttachmentPreviewRequests = new Set() const canUseMatrixChat = computed(() => Boolean(status.value?.reachable && status.value?.provisioningConfigured) @@ -286,6 +288,7 @@ const mergeMatrixMessages = (incomingMessages) => { matrixMessages.value = Array.from(byId.values()) .sort((a, b) => (a.timestamp || 0) - (b.timestamp || 0)) + loadAttachmentPreviews() } const scrollMessagesToBottom = async () => { @@ -845,6 +848,7 @@ const sendMatrixMessage = async () => { matrixMessages.value = matrixMessages.value.map((item) => item.id === optimisticId ? message : item ) + loadAttachmentPreviews() } catch (error) { matrixMessages.value = matrixMessages.value.map((item) => item.id === optimisticId ? { ...item, pending: false, failed: true } : item @@ -967,6 +971,37 @@ const matrixMediaProxyUrl = (attachment) => { return `/api/communication/matrix/media?${params.toString()}` } +const attachmentObjectUrl = (attachment) => + attachment?.url ? matrixAttachmentObjectUrls.value[attachment.url] || "" : "" + +const ensureAttachmentObjectUrl = async (attachment) => { + if (!attachment?.url || matrixAttachmentObjectUrls.value[attachment.url] || matrixAttachmentPreviewRequests.has(attachment.url)) return + + matrixAttachmentPreviewRequests.add(attachment.url) + try { + const blob = await $api(matrixMediaProxyUrl(attachment), { + responseType: "blob" + }) + const objectUrl = URL.createObjectURL(blob) + matrixAttachmentObjectUrls.value = { + ...matrixAttachmentObjectUrls.value, + [attachment.url]: objectUrl + } + } catch (error) { + // Fällt nur auf den Link zurück; der Chat selbst soll weiter funktionieren. + } finally { + matrixAttachmentPreviewRequests.delete(attachment.url) + } +} + +const loadAttachmentPreviews = () => { + for (const message of matrixMessages.value) { + if (message.attachment?.isImage && message.attachment.url) { + ensureAttachmentObjectUrl(message.attachment) + } + } +} + const formatLastUpdated = computed(() => { if (!lastUpdated.value) return "Noch nicht aktualisiert" @@ -990,6 +1025,7 @@ onBeforeUnmount(() => { stopMatrixAutoRefresh() stopMatrixCallDurationTimer() leaveMatrixCall() + Object.values(matrixAttachmentObjectUrls.value).forEach((objectUrl) => URL.revokeObjectURL(objectUrl)) }) @@ -1404,8 +1440,8 @@ onBeforeUnmount(() => { :class="message.own ? 'border-white/20' : 'border-default'" >