declare global { interface Window { toggleUserList: () => void; switchUser: (userId: string | number) => void; loadMemberChat: (memberId: string | number) => void; } } import { groupsMock } from '../../mocks/mock-signature/groupsMock'; import { messagesMock as initialMessagesMock, messagesMock } from '../../mocks/mock-signature/messagesMock'; import { membersMock } from '../../mocks/mock-signature/membersMocks'; import { ApiReturn, Device, Member } from '../../../pkg/sdk_client'; import { DocumentSignature, } from '../../models/signature.models'; import { messageStore } from '../../utils/messageMock'; import { Group } from '../../interface/groupInterface'; import { getCorrectDOM } from '../../utils/document.utils'; import chatStyle from '../../../public/style/chat.css?inline'; import { addressToEmoji } from '../../utils/sp-address.utils'; import Database from '../../services/database.service'; const storageUrl = `/storage`; interface LocalNotification { memberId: string; text: string; time: string; } interface Message { content: string; content_type: "text" | "file"; created_at: number; sender: string []; recipient: string; } export function initChat() { const chatElement = document.createElement('chat-element'); const container = document.querySelector('.container'); if (container) { container.appendChild(chatElement); } } class ChatElement extends HTMLElement { static get observedAttributes() { return ['process-id']; } private sdkClient: any; private processId: string | null = null; private selectedMemberId: string | null = null; private messagesMock: any[] = []; private dom: Node; private notifications: LocalNotification[] = []; private notificationBadge = document.querySelector('.notification-badge'); private notificationBoard = document.getElementById('notification-board'); private notificationBell = document.getElementById('notification-bell'); private selectedSignatories: DocumentSignature[] = []; private allMembers = membersMock.map(member => ({ id: member.id, name: member.name, roleName: 'Default Role' })); constructor() { super(); this.attachShadow({ mode: 'open' }); this.messagesMock = messageStore.getMessages(); this.dom = getCorrectDOM('signature-element'); this.processId = this.getAttribute('process-id'); // Initialiser sdkClient this.initSDKClient(); // Récupérer le processId depuis l'attribut du composant console.log('🔍 Constructor - Process ID from element:', this.processId); this.shadowRoot!.innerHTML = `
`; window.toggleUserList = this.toggleUserList.bind(this); window.loadMemberChat = this.loadMemberChat.bind(this); this.notificationBadge = document.querySelector('.notification-badge'); this.notificationBoard = document.getElementById('notification-board'); this.notificationBell = document.getElementById('notification-bell'); if (!this.notificationBadge || !this.notificationBoard || !this.notificationBell) { console.error('Notification elements not found'); } // Initialiser les événements de notification document.addEventListener('click', (event: Event): void => { if (this.notificationBoard && this.notificationBoard.style.display === 'block' && !this.notificationBoard.contains(event.target as Node) && this.notificationBell && !this.notificationBell.contains(event.target as Node)) { this.notificationBoard.style.display = 'none'; } }); this.initMessageEvents(); document.addEventListener('newMessagingProcess', ((event: CustomEvent) => { console.log('🎯 Received newMessagingProcess event:', event.detail); this.addNewMessagingProcess(event.detail.processId, event.detail.processName); }) as EventListener); } attributeChangedCallback(name: string, oldValue: string, newValue: string) { console.log(`🔄 Attribute ${name} changed from ${oldValue} to ${newValue}`); if (name === 'process-id' && newValue) { console.log('🔍 Loading chat with new process ID:', newValue); this.loadGroupList(newValue); } } private initMessageEvents() { const sendButton = this.shadowRoot?.querySelector('#send-button'); if (sendButton) { sendButton.addEventListener('click', () => this.sendMessage()); } const messageInput = this.shadowRoot?.querySelector('#message-input'); if (messageInput) { messageInput.addEventListener('keypress', (event: Event) => { const keyEvent = event as KeyboardEvent; if (keyEvent.key === 'Enter' && !keyEvent.shiftKey) { event.preventDefault(); this.sendMessage(); } }); } const fileInput = this.shadowRoot?.querySelector('#file-input') as HTMLInputElement; if (fileInput) { fileInput.addEventListener('change', (event: Event) => { const target = event.target as HTMLInputElement; if (target.files && target.files.length > 0) { this.sendFile(target.files[0]); } }); } } ///////////////////// Notification module ///////////////////// // Delete a notification private removeNotification(index: number) { this.notifications?.splice(index, 1); // Ajout de ?. this.renderNotifications(); this.updateNotificationBadge(); } // Show notifications private renderNotifications() { if (!this.notificationBoard) return; // Reset the interface this.notificationBoard.innerHTML = ''; // Displays "No notifications available" if there are no notifications if (this.notifications.length === 0) { this.notificationBoard.innerHTML = '
No notifications available
'; return; } // Add each notification to the list this.notifications.forEach((notif, index) => { const notifElement = document.createElement('div'); notifElement.className = 'notification-item'; notifElement.textContent = `${notif.text} at ${notif.time}`; notifElement.onclick = () => { this.loadMemberChat(notif.memberId); this.removeNotification(index); }; this.notificationBoard?.appendChild(notifElement); }); } private updateNotificationBadge() { if (!this.notificationBadge) return; const count = this.notifications.length; this.notificationBadge.textContent = count > 99 ? '+99' : count.toString(); (this.notificationBadge as HTMLElement).style.display = count > 0 ? 'block' : 'none'; } // Add notification private async addNotification(memberId: string, message: any) { try { // Obtenir l'emoji de l'adresse const memberEmoji = await addressToEmoji(memberId); // Obtenir le processus et le rôle const groupItem = this.shadowRoot?.querySelector('[data-process-id]'); const processId = groupItem?.getAttribute('data-process-id'); const processEmoji = processId ? await addressToEmoji(processId) : '📝'; // Trouver le rôle du membre const member = this.allMembers.find(m => String(m.id) === memberId); const role = member?.roleName || 'Member'; // Déterminer le texte de la notification let notificationText = ''; if (message.type === 'file') { notificationText = `${memberEmoji} (${role}) in ${processEmoji}: New file - ${message.fileName}`; } else { notificationText = `${memberEmoji} (${role}) in ${processEmoji}: ${message.text}`; } // Créer la notification const notification = { memberId, text: notificationText, time: message.time }; // Ajouter la notification et mettre à jour l'interface this.notifications.push(notification); this.renderNotifications(); this.updateNotificationBadge(); } catch (error) { console.error('Error creating notification:', error); } } public isPaired(): boolean { try { return this.sdkClient.is_paired(); } catch (e) { throw new Error(`isPaired ~ Error: ${e}`); } } public async createMessagingProcess(otherMembers: Member[], relayAddress: string, feeRate: number): Promise { if (!this.isPaired()) { throw new Error('Device not paired'); } const me = await this.getMemberFromDevice(); console.log('My SP addresses:', me); if (!me) { throw new Error('No paired member in device'); } const allMembers: Member[] = otherMembers; allMembers.push({ sp_addresses: me }); const meAndOne = [{ sp_addresses: me }, otherMembers.pop()!]; const everyOneElse = otherMembers; const messagingTemplate = { process_id: crypto.randomUUID(), parent_id: null, description: 'messaging', messages: { state: 'initial', object: { type: 'message_list', content: [], content_type: { allowed: ['text', 'file'], default: 'text' }, metadata: { created_at: Date.now(), last_updated: Date.now() } } }, roles: { public: { members: allMembers, validation_rules: [ { quorum: 0.0, fields: ['description', 'roles', 'messages'], min_sig_member: 0.0, }, ], storages: [storageUrl] }, owner: { members: meAndOne, validation_rules: [ { quorum: 1.0, fields: ['description', 'roles', 'messages'], min_sig_member: 1.0, }, ], storages: [storageUrl] }, users: { members: everyOneElse, validation_rules: [ { quorum: 0.0, fields: ['description', 'roles', 'messages'], min_sig_member: 0.0, }, ], storages: [storageUrl] }, }, }; try { return this.sdkClient.create_new_process(JSON.stringify(messagingTemplate), null, relayAddress, feeRate); } catch (e) { throw new Error(`Creating process failed: ${e}`); } } async getMemberFromDevice(): Promise { try { const device = await this.getDeviceFromDatabase(); if (device) { const parsed: Device = JSON.parse(device); const pairedMember = parsed['paired_member']; return pairedMember.sp_addresses; } else { return null; } } catch (e) { throw new Error(`Failed to retrieve paired_member from device: ${e}`); } } async getDeviceFromDatabase(): Promise { const db = await Database.getInstance(); const walletStore = 'wallet'; try { const dbRes = await db.getObject(walletStore, '1'); if (dbRes) { const wallet = dbRes['device']; return wallet; } else { return null; } } catch (e) { throw new Error(`Failed to retrieve device from db: ${e}`); } } // Send a messsage private async sendMessage() { const messageInput = this.shadowRoot?.querySelector('#message-input') as HTMLInputElement; if (!messageInput || !this.selectedMemberId) return; const messageText = messageInput.value.trim(); if (messageText === '') return; try { const myAddresses = await this.getMemberFromDevice(); if (!myAddresses) { throw new Error('No paired member found'); } const now = new Date(); const formattedTime = now.toLocaleString('fr-FR', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' }); const newMessage = { id: Date.now(), sender: myAddresses[0], text: messageText, time: formattedTime, type: 'text', class: 'message user' }; 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, description: 'message', message:{ content: "Hello", content_type: 'file', created_at: Date.now(), sender: myAddresses, recipient: this.selectedMemberId } as Message, 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] } } }; console.log('Message template:', { timestamp: formattedTime, template: messageTemplate }); const result = await this.createMessagingProcess( [{ sp_addresses: [this.selectedMemberId] }], 'relay_address', 1 ); console.log('Final message process:', { template: messageTemplate, result: result, timestamp: new Date().toISOString() }); messageInput.value = ''; this.loadMemberChat(this.selectedMemberId); setTimeout(() => { const autoReply = this.generateAutoReply(this.selectedMemberId!); messageStore.addMessage(this.selectedMemberId!, autoReply); this.messagesMock = messageStore.getMessages(); this.loadMemberChat(this.selectedMemberId!); this.addNotification(this.selectedMemberId!, autoReply); }, 1000); } catch (error) { console.error('Error sending message:', error); } } private scrollToBottom(container: Element) { (container as HTMLElement).scrollTop = (container as HTMLElement).scrollHeight; } // Load the list of members private async loadMemberChat(memberId: string | number) { const myAddresses = await this.getMemberFromDevice(); if (!myAddresses) { console.error('No paired member found'); return; } this.selectedMemberId = String(memberId); const memberMessages = this.messagesMock.find(m => String(m.memberId) === String(memberId)); const chatHeader = this.shadowRoot?.querySelector('#chat-header'); const messagesContainer = this.shadowRoot?.querySelector('#messages'); if (!chatHeader || !messagesContainer) return; const memberAddress = String(memberId); const emojis = await addressToEmoji(memberAddress); chatHeader.textContent = `Chat with ${emojis}`; messagesContainer.innerHTML = ''; if (memberMessages) { for (const message of memberMessages.messages) { const messageElement = document.createElement('div'); messageElement.className = 'message-container'; // Ajouter le style pour aligner les messages if (message.sender === myAddresses[0]) { messageElement.style.justifyContent = 'flex-end'; } else { messageElement.style.justifyContent = 'flex-start'; } const messageContent = document.createElement('div'); messageContent.className = message.class || 'message'; if (message.type === 'file') { messageContent.innerHTML = `
${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(message.sender)}: ${message.text}
${message.time}
`; } messageElement.appendChild(messageContent); messagesContainer.appendChild(messageElement); } } this.scrollToBottom(messagesContainer); } private async toggleMembers(roleData: any, roleElement: HTMLElement) { let memberList = roleElement.querySelector('.member-list'); if (memberList) { (memberList as HTMLElement).style.display = (memberList as HTMLElement).style.display === 'none' ? 'block' : 'none'; return; } memberList = document.createElement('ul'); memberList.className = 'member-list'; if (roleData.members) { for (const member of roleData.members) { const memberItem = document.createElement('li'); memberItem.className = 'member-item'; const memberContainer = document.createElement('div'); memberContainer.className = 'member-container'; const emojiSpan = document.createElement('span'); emojiSpan.className = 'member-emoji'; if (member.sp_addresses?.[0]) { const emojis = await addressToEmoji(member.sp_addresses[0]); emojiSpan.textContent = emojis; } memberContainer.appendChild(emojiSpan); memberItem.appendChild(memberContainer); memberItem.onclick = async (event) => { event.stopPropagation(); try { // S'assurer que le SDK est initialisé if (!this.sdkClient) { await this.initSDKClient(); } const groupItem = roleElement.closest('[data-process-id]'); const processId = groupItem?.getAttribute('data-process-id'); if (!processId) { throw new Error('Process ID not found'); } console.log('Creating messaging process with:', { processId, member, sdkClientInitialized: !!this.sdkClient }); const result = await this.createMessagingProcess( [member], 'relay_address', 1 ); console.log('Messaging process created:', { processId, template: result, member: member }); this.loadMemberChat(member.sp_addresses[0]); } catch (error) { console.error('Error creating messaging process:', error); } }; memberList.appendChild(memberItem); } } roleElement.appendChild(memberList); } // Toggle the list of Roles private toggleRoles(group: Group, groupElement: HTMLElement) { console.log('=== toggleRoles START ==='); console.log('Group:', group.name); console.log('Group roles:', group.roles); // Afficher tous les rôles disponibles let roleList = groupElement.querySelector('.role-list'); console.log('Existing roleList:', roleList); if (roleList) { const roleItems = roleList.querySelectorAll('.role-item'); roleItems.forEach(roleItem => { console.log('Processing roleItem:', roleItem.innerHTML); // Voir le contenu HTML complet let container = roleItem.querySelector('.role-item-container'); if (!container) { container = document.createElement('div'); container.className = 'role-item-container'; // Créer un span pour le nom du rôle const nameSpan = document.createElement('span'); nameSpan.className = 'role-name'; nameSpan.textContent = roleItem.textContent?.trim() || ''; container.appendChild(nameSpan); roleItem.textContent = ''; roleItem.appendChild(container); } // Récupérer le nom du rôle const roleName = roleItem.textContent?.trim(); console.log('Role name from textContent:', roleName); // Alternative pour obtenir le nom du rôle const roleNameAlt = container.querySelector('.role-name')?.textContent; console.log('Role name from span:', roleNameAlt); if (!container.querySelector('.folder-icon')) { const folderButton = document.createElement('span'); folderButton.className = 'folder-icon'; folderButton.addEventListener('click', (event) => { event.stopPropagation(); console.log('Clicked role name:', roleName); console.log('Available roles:', group.roles.map(r => r.name)); const role = group.roles.find(r => r.name === roleName); if (role) { console.log('Found role:', role); } else { console.error('Role not found. Name:', roleName); console.error('Available roles:', group.roles); } }); container.appendChild(folderButton); } }); (roleList as HTMLElement).style.display = (roleList as HTMLElement).style.display === 'none' ? 'block' : 'none'; } } private async loadGroupList(processId: string): Promise { console.log('🔍 Loading group list with processId:', processId); const groupList = this.shadowRoot?.querySelector('#group-list'); if (!groupList) return; groupList.innerHTML = ''; const dbRequest = window.indexedDB.open('4nk'); const db = await new Promise((resolve, reject) => { dbRequest.onsuccess = () => resolve(dbRequest.result); dbRequest.onerror = () => reject(dbRequest.error); }); const transaction = db.transaction(['processes'], 'readonly'); const processStore = transaction.objectStore('processes'); const processRequest = processStore.get(processId); const process = await new Promise((resolve, reject) => { processRequest.onsuccess = () => { console.log('🔍 Process found:', processRequest.result); resolve(processRequest.result); }; processRequest.onerror = () => reject(processRequest.error); }); if (!process?.states?.[0]?.encrypted_pcd?.roles) { console.error('❌ Process structure invalid:', process); return; } const roles = process.states[0].encrypted_pcd.roles; console.log('🔑 Roles found:', roles); const li = document.createElement('li'); li.className = 'group-list-item'; li.setAttribute('data-process-id', processId); const container = document.createElement('div'); container.className = 'group-item-container'; const nameSpan = document.createElement('span'); nameSpan.textContent = `Process : `; nameSpan.className = 'process-name'; container.appendChild(nameSpan); await addressToEmoji(processId).then(emojis => { const emojiSpan = document.createElement('span'); emojiSpan.className = 'process-emoji'; emojiSpan.textContent = emojis; container.appendChild(emojiSpan); }); li.appendChild(container); const roleList = document.createElement('ul'); roleList.className = 'role-list'; // Traiter chaque rôle Object.entries(roles).forEach(([roleName, roleData]: [string, any]) => { const roleItem = document.createElement('li'); roleItem.className = 'role-item'; const roleContainer = document.createElement('div'); roleContainer.className = 'role-item-container'; const roleNameSpan = document.createElement('span'); roleNameSpan.className = 'role-name'; roleNameSpan.textContent = roleName; // Filtrer les membres dupliqués ici, avant de les passer à toggleMembers const uniqueMembers = new Map(); roleData.members?.forEach((member: any) => { const spAddress = member.sp_addresses?.[0]; if (spAddress && !uniqueMembers.has(spAddress)) { uniqueMembers.set(spAddress, member); } }); // Créer un nouveau roleData avec les membres uniques const filteredRoleData = { ...roleData, members: Array.from(uniqueMembers.values()) }; roleContainer.addEventListener('click', (event) => { event.stopPropagation(); this.toggleMembers(filteredRoleData, roleItem); }); roleContainer.appendChild(roleNameSpan); roleItem.appendChild(roleContainer); roleList.appendChild(roleItem); }); li.appendChild(roleList); groupList.appendChild(li); } // Function to manage the list of users private toggleUserList() { const userList = getCorrectDOM('userList'); if (!userList) return; if (!(userList as HTMLElement).classList.contains('show')) { (userList as HTMLElement).innerHTML = membersMock.map(member => `
${member.avatar}
${member.name} ${member.email}
`).join(''); } (userList as HTMLElement).classList.toggle('show'); } // Generate an automatic response private generateAutoReply(senderName: string): Message { const now = new Date(); const formattedTime = now.toLocaleString('fr-FR', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' }); return { id: Date.now(), sender: senderName, text: "OK...", time: formattedTime, type: 'text' as const }; } // Send a file private async sendFile(file: File) { const MAX_FILE_SIZE = 1 * 1024 * 1024; if (file.size > MAX_FILE_SIZE) { alert('Le fichier est trop volumineux. Taille maximum : 1MB'); return; } try { const myAddresses = await this.getMemberFromDevice(); if (!myAddresses) { throw new Error('No paired member found'); } let fileData: string; if (file.type.startsWith('image/')) { fileData = await this.compressImage(file); } else { fileData = await this.readFileAsBase64(file); } const newMessage = { id: Date.now(), sender: myAddresses[0], text: `Fichier envoyé: ${file.name}`, fileName: file.name, time: new Date().toLocaleString('fr-FR'), type: 'file', class: 'message user' }; 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(); } 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 = { parent_id: parentProcessId, description: 'file_message', message:{ content: fileData, content_type: 'file', created_at: Date.now(), sender: myAddresses, recipient: this.selectedMemberId } as Message, roles: { dm: { members: [ { sp_addresses: myAddresses }, { sp_addresses: [this.selectedMemberId] } ], validation_rules: [ { quorum: 0.01, fields: ['message'], min_sig_member: 0.01, }, ], 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 = ''; this.loadMemberChat(this.selectedMemberId!); setTimeout(() => { const autoReply = this.generateAutoReply(this.selectedMemberId!); messageStore.addMessage(this.selectedMemberId!, autoReply); this.messagesMock = messageStore.getMessages(); this.loadMemberChat(this.selectedMemberId!); 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() { if (this.processId) { console.log('🔍 Loading chat with process ID:', this.processId); this.loadGroupList(this.processId); } else { console.error('❌ No process ID found in element attributes'); } // Si un membre est sélectionné par défaut, charger ses messages if (this.selectedMemberId) { this.loadMemberChat(this.selectedMemberId); } } private addNewMessagingProcess(processId: string, processName: string) { console.log('🎯 Adding new messaging process:', { processId, processName }); const groupList = this.shadowRoot?.querySelector('#group-list'); if (!groupList) { console.error('Group list not found in shadow DOM'); return; } // Vérifier si le processus existe déjà const existingProcess = groupList.querySelector(`[data-process-id="${processId}"]`); if (existingProcess) { console.log('Process already exists:', processId); return; } // Créer le nouveau groupe const li = document.createElement('li'); li.className = 'group-list-item'; li.setAttribute('data-process-id', processId); // Créer le conteneur flex const container = document.createElement('div'); container.className = 'group-item-container'; // Créer un span pour le nom du processus const nameSpan = document.createElement('span'); nameSpan.textContent = processName; nameSpan.className = 'process-name'; // Créer un span pour les emojis const emojiSpan = document.createElement('span'); emojiSpan.className = 'process-emoji'; // Ajouter les emojis de l'adresse addressToEmoji(processId).then(emojis => { emojiSpan.textContent = emojis; }); container.appendChild(nameSpan); container.appendChild(emojiSpan); li.appendChild(container); // Créer la liste des rôles const roleList = document.createElement('ul'); roleList.className = 'role-list'; roleList.style.display = 'none'; // Ajouter un rôle par défaut pour le messaging const roleItem = document.createElement('li'); roleItem.className = 'role-item'; roleItem.textContent = 'Messaging'; roleList.appendChild(roleItem); li.appendChild(roleList); groupList.appendChild(li); console.log('🎯 New messaging process added successfully'); } private async initSDKClient() { try { // Récupérer l'instance du SDK depuis window ou l'initialiser this.sdkClient = (window as any).sdk || await this.createSDKClient(); if (!this.sdkClient) { throw new Error('Failed to initialize SDK client'); } } catch (error) { console.error('Error initializing SDK client:', error); } } private async createSDKClient() { // Implémentez ici la logique de création du SDK client // Ceci est un exemple, ajustez selon votre implémentation réelle return new Promise((resolve) => { // Logique d'initialisation du SDK resolve({ is_paired: () => true, // Valeur par défaut pour le test create_new_process: async (template: string, parentId: string | null, relayAddress: string, feeRate: number) => { // Implémentation de create_new_process return { success: true }; } }); }); } } customElements.define('chat-element', ChatElement); export { ChatElement };