diff --git a/src/pages/chat/chat.ts b/src/pages/chat/chat.ts index 82acb6b..a466ef6 100755 --- a/src/pages/chat/chat.ts +++ b/src/pages/chat/chat.ts @@ -382,7 +382,6 @@ class ChatElement extends HTMLElement { throw new Error('No paired member found'); } - // Formater la date pour n'avoir que l'heure et la date const now = new Date(); const formattedTime = now.toLocaleString('fr-FR', { day: '2-digit', @@ -392,7 +391,6 @@ class ChatElement extends HTMLElement { minute: '2-digit' }); - // Créer le message au format attendu par messageStore const newMessage = { id: Date.now(), sender: myAddresses[0], @@ -401,9 +399,10 @@ class ChatElement extends HTMLElement { type: 'text' as const }; - - messageStore.addMessage(this.selectedMemberId!, newMessage); - this.messagesMock = messageStore.getMessages(); + if (this.selectedMemberId) { + messageStore.addMessage(this.selectedMemberId!, newMessage); + this.messagesMock = messageStore.getMessages(); + } // Récupérer le process_id du parent (conversation) const groupItem = this.shadowRoot?.querySelector('[data-process-id]'); @@ -541,15 +540,33 @@ class ChatElement extends HTMLElement { const messageContent = document.createElement('div'); messageContent.className = 'message'; - if (message.sender === myAddresses[0]) { - messageContent.classList.add('user'); + if (message.type === 'file') { messageContent.innerHTML = ` - ${message.text} + ${await addressToEmoji(message.sender)}: + + 📎 ${message.fileName} + ${message.time} `; + + // Ajouter le gestionnaire de clic pour le téléchargement + const fileSpan = messageContent.querySelector('.file-message'); + fileSpan?.addEventListener('click', () => { + const fileKey = `file_${message.id}`; + const fileData = localStorage.getItem(fileKey); + if (fileData) { + // Créer un lien de téléchargement + const link = document.createElement('a'); + link.href = fileData; + link.download = message.fileName; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + } + }); } else { messageContent.innerHTML = ` - ${await addressToEmoji(memberAddress)}: ${message.text} + ${await addressToEmoji(message.sender)}: ${message.text} ${message.time} `; } @@ -844,45 +861,187 @@ class ChatElement extends HTMLElement { // Send a file private async sendFile(file: File) { - if (!this.selectedMemberId) return; - - const MAX_FILE_SIZE = 5 * 1024 * 1024; + // Ajouter une vérification de la taille avant la conversion en base64 + const MAX_FILE_SIZE = 1 * 1024 * 1024; // Réduire à 1MB pour éviter les problèmes de quota if (file.size > MAX_FILE_SIZE) { - alert('File is too large. Maximum size is 5MB'); + alert('Le fichier est trop volumineux. Taille maximum : 1MB'); return; } - const reader = new FileReader(); - reader.readAsDataURL(file); - - reader.onload = () => { - const fileMessage = { + try { + const myAddresses = await this.getMemberFromDevice(); + if (!myAddresses) { + throw new Error('No paired member found'); + } + + // Compresser l'image si c'est une image + let fileData: string; + if (file.type.startsWith('image/')) { + fileData = await this.compressImage(file); + } else { + fileData = await this.readFileAsBase64(file); + } + + // Créer un message avec un texte descriptif au lieu du fileData + const newMessage = { id: Date.now(), - sender: "4NK", + sender: myAddresses[0], + text: `Fichier envoyé: ${file.name}`, // Ajouter un texte descriptif fileName: file.name, - fileData: reader.result, - time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }), + time: new Date().toLocaleString('fr-FR'), type: 'file' as const }; - messageStore.addMessage(this.selectedMemberId!, fileMessage); - this.messagesMock = messageStore.getMessages(); - this.loadMemberChat(this.selectedMemberId!); + // Stocker le fileData séparément + try { + const fileKey = `file_${newMessage.id}`; + localStorage.setItem(fileKey, fileData); + } catch (storageError) { + console.error('Erreur de stockage du fichier:', storageError); + alert('Erreur lors du stockage du fichier. Essayez avec un fichier plus petit.'); + return; + } + + if (this.selectedMemberId) { + messageStore.addMessage(this.selectedMemberId, newMessage); + this.messagesMock = messageStore.getMessages(); + } + + // Récupérer le process_id du parent (conversation) + const groupItem = this.shadowRoot?.querySelector('[data-process-id]'); + const parentProcessId = groupItem?.getAttribute('data-process-id'); + + if (!parentProcessId) { + throw new Error('Parent process ID not found'); + } + + const messageTemplate = { + process_id: parentProcessId, + parent_id: null, + description: 'file_message', + messages: { + state: 'initial', + object: { + type: 'file', + content: fileData, + metadata: { + created_at: newMessage.time, + last_updated: newMessage.time, + sender: myAddresses[0], + recipient: this.selectedMemberId, + fileName: file.name, + fileType: file.type + } + } + }, + roles: { + public: { + members: [ + { sp_addresses: myAddresses }, + { sp_addresses: [this.selectedMemberId] } + ], + validation_rules: [ + { + quorum: 0.0, + fields: ['description', 'messages'], + min_sig_member: 0.0, + }, + ], + storages: [storageUrl] + }, + owner: { + members: [ + { sp_addresses: myAddresses }, + { sp_addresses: [this.selectedMemberId] } + ], + validation_rules: [ + { + quorum: 1.0, + fields: ['description', 'messages'], + min_sig_member: 1.0, + }, + ], + storages: [storageUrl] + } + } + }; + + const result = await this.createMessagingProcess( + [{ sp_addresses: [this.selectedMemberId!] }], + 'relay_address', + 1 + ); + + console.log('Final file message process:', { + template: messageTemplate, + result: result, + timestamp: new Date().toISOString() + }); const fileInput = this.shadowRoot?.querySelector('#file-input') as HTMLInputElement; if (fileInput) fileInput.value = ''; - // Générer la réponse automatique + this.loadMemberChat(this.selectedMemberId!); + setTimeout(() => { const autoReply = this.generateAutoReply(this.selectedMemberId!); messageStore.addMessage(this.selectedMemberId!, autoReply); this.messagesMock = messageStore.getMessages(); this.loadMemberChat(this.selectedMemberId!); - // Ajouter la notification UNIQUEMENT pour la réponse reçue this.addNotification(this.selectedMemberId!, autoReply); }, 1000); - }; + + } catch (error) { + console.error('Error sending file:', error); + } + } + + private async readFileAsBase64(file: File): Promise { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = () => resolve(reader.result as string); + reader.onerror = reject; + reader.readAsDataURL(file); + }); + } + + private async compressImage(file: File): Promise { + return new Promise((resolve, reject) => { + const img = new Image(); + const canvas = document.createElement('canvas'); + const ctx = canvas.getContext('2d'); + + img.onload = () => { + // Calculer les nouvelles dimensions + let width = img.width; + let height = img.height; + const MAX_WIDTH = 800; + const MAX_HEIGHT = 600; + + if (width > height) { + if (width > MAX_WIDTH) { + height *= MAX_WIDTH / width; + width = MAX_WIDTH; + } + } else { + if (height > MAX_HEIGHT) { + width *= MAX_HEIGHT / height; + height = MAX_HEIGHT; + } + } + + canvas.width = width; + canvas.height = height; + ctx?.drawImage(img, 0, 0, width, height); + + // Compression avec qualité réduite + resolve(canvas.toDataURL('image/jpeg', 0.7)); + }; + + img.onerror = reject; + img.src = URL.createObjectURL(file); + }); } connectedCallback() {