From 8d625507753517899927eb1fe54663aab9149c77 Mon Sep 17 00:00:00 2001 From: DJ2LS Date: Tue, 6 Feb 2024 20:09:20 +0100 Subject: [PATCH] first attempt with adding and downloading message attachments --- gui/src/components/chat_messages_received.vue | 76 +++++------ gui/src/components/chat_messages_sent.vue | 80 +++++------ gui/src/components/chat_new_message.vue | 128 ++++++++++++++---- gui/src/js/api.js | 8 +- gui/src/js/messagesHandler.ts | 10 +- modem/api_validations.py | 1 + modem/message_system_db_attachments.py | 17 ++- modem/message_system_db_model.py | 2 +- modem/server.py | 5 + 9 files changed, 206 insertions(+), 121 deletions(-) diff --git a/gui/src/components/chat_messages_received.vue b/gui/src/components/chat_messages_received.vue index e452502d..d641eb82 100644 --- a/gui/src/components/chat_messages_received.vue +++ b/gui/src/components/chat_messages_received.vue @@ -2,13 +2,13 @@
-
-

- {{ getFileContent["filename"] }} | - {{ getFileContent["filesize"] }} Bytes | - {{ getFileContent["filetype"] }} -

+ +
+
+ +
+

{{ message.body }}

@@ -33,14 +33,7 @@ - +
-
-
-

- {{ getFileContent["filename"] }} | - {{ getFileContent["filesize"] }} Bytes | - {{ getFileContent["filetype"] }} -

+
+
+ +
+
+ +

{{ message.body }}

@@ -101,7 +93,6 @@ import { import { setActivePinia } from "pinia"; import pinia from "../store/index"; setActivePinia(pinia); -import { saveAs } from "file-saver"; import { useChatStore } from "../store/chatStore.js"; const chat = useChatStore(pinia); @@ -126,42 +117,41 @@ export default { //console.log(this.infoModal) //this.infoModal.show() }, - async downloadAttachment() { + async downloadAttachment(hash_sha512, fileName) { try { - // reset file store - chat.downloadFileFromDB = []; + const jsondata = await getMessageAttachment(hash_sha512); + const byteCharacters = atob(jsondata.data); + const byteArrays = []; - const attachment = await getMessageAttachment(this.message.id); - const blob = new Blob([atob_FD(attachment[2])], { - type: `${attachment[1]};charset=utf-8`, - }); - saveAs(blob, attachment[0]); + for (let offset = 0; offset < byteCharacters.length; offset += 512) { + const slice = byteCharacters.slice(offset, offset + 512); + const byteNumbers = new Array(slice.length); + for (let i = 0; i < slice.length; i++) { + byteNumbers[i] = slice.charCodeAt(i); + } + const byteArray = new Uint8Array(byteNumbers); + byteArrays.push(byteArray); + } + + const blob = new Blob(byteArrays, { type: jsondata.type }); + const url = URL.createObjectURL(blob); + + // Creating a temporary anchor element to download the file + const anchor = document.createElement('a'); + anchor.href = url; + anchor.download = fileName; + document.body.appendChild(anchor); + anchor.click(); + + // Cleanup + document.body.removeChild(anchor); + URL.revokeObjectURL(url); } catch (error) { - console.error("Failed to download attachment:", error); + console.error("Failed to download the attachment:", error); } }, }, computed: { - getFileContent() { - if (this.message.attachments.length <= 0) { - return { filename: "", filesize: 0, filetype: "" }; - } - - var filename = Object.keys(this.message._attachments)[0]; - var filesize = this.message._attachments[filename]["length"]; - var filetype = filename.split(".")[1]; - - // ensure filesize is 0 for hiding message header if no data is available - if ( - typeof filename === "undefined" || - filename === "" || - filename === "-" - ) { - filesize = 0; - } - - return { filename: filename, filesize: filesize, filetype: filetype }; - }, messageWidthClass() { // Calculate a Bootstrap grid class based on message length // Adjust the logic as needed to fit your requirements diff --git a/gui/src/components/chat_new_message.vue b/gui/src/components/chat_new_message.vue index e704f704..def6ce1d 100644 --- a/gui/src/components/chat_new_message.vue +++ b/gui/src/components/chat_new_message.vue @@ -44,34 +44,72 @@ chat.inputText += detail.unicode const chatModuleMessage=ref(null); +// Function to trigger the hidden file input +function triggerFileInput() { + fileInput.value.click(); +} -function transmitNewMessage(){ +// Use a ref for storing multiple files +const selectedFiles = ref([]); +const fileInput = ref(null); +function handleFileSelection(event) { + // Reset previously selected files + selectedFiles.value = []; - // if no callsign is selected, assume we are using the first one.. - if(typeof(chat.selectedCallsign) == 'undefined'){ + // Process each file + for (let file of event.target.files) { + const reader = new FileReader(); + reader.onload = () => { + // Convert file content to base64 + const base64Content = btoa(reader.result); // Adjust this line if necessary + selectedFiles.value.push({ + name: file.name, + size: file.size, + type: file.type, + content: base64Content, // Store base64 encoded content + }); + }; + reader.readAsBinaryString(file); // Read the file content as binary string + } +}function transmitNewMessage() { + // Check if a callsign is selected, default to the first one if not + if (typeof(chat.selectedCallsign) === 'undefined') { chat.selectedCallsign = Object.keys(chat.callsign_list)[0]; } - chat.inputText = chat.inputText.trim(); - if (chat.inputText.length==0 && chat.inputFileName == "-") - return; + + // Proceed only if there is text or files selected + if (chat.inputText.length === 0 && selectedFiles.value.length === 0) return; + + const attachments = selectedFiles.value.map(file => { + return { + name: file.name, + type: file.type, + data: file.content + }; + }); + if (chat.selectedCallsign.startsWith("BC-")) { - - return "new broadcast" - + // Handle broadcast message differently if needed + return "new broadcast"; } else { - //newMessage(chat.selectedCallsign, chat.inputText, chat.inputFile, chat.inputFileName, chat.inputFileSize, chat.inputFileType) - newMessage(chat.selectedCallsign, chat.inputText) + // If there are attachments, send them along with the message + if (attachments.length > 0) { + newMessage(chat.selectedCallsign, chat.inputText, attachments); + } else { + // Send text only if no attachments are selected + newMessage(chat.selectedCallsign, chat.inputText); + } } - // finally do a cleanup - //chatModuleMessage.reset(); + + // Cleanup after sending message chat.inputText = ''; - chatModuleMessage.value=""; - // @ts-expect-error - resetFile() + chatModuleMessage.value = ""; + selectedFiles.value = []; // Clear selected files after sending + // Reset any other states or UI elements as necessary } function resetFile(event){ @@ -140,9 +178,9 @@ function calculateTimeNeeded(){ return obj.snr === snrList[i].snr }) - calculatedSpeedPerMinutePER0.push(chat.inputFileSize / result.bpm) - calculatedSpeedPerMinutePER25.push(chat.inputFileSize / (result.bpm * 0.75)) - calculatedSpeedPerMinutePER75.push(chat.inputFileSize / (result.bpm * 0.25)) + calculatedSpeedPerMinutePER0.push(totalSize / result.bpm) + calculatedSpeedPerMinutePER25.push(totalSize / (result.bpm * 0.75)) + calculatedSpeedPerMinutePER75.push(totalSize / (result.bpm * 0.25)) } @@ -202,11 +240,11 @@ const speedChartData = computed(() => ({ - +