import modalHtml from '../components/login-modal/login-modal.html?raw'; import modalScript from '../components/login-modal/login-modal.js?raw'; import validationModalStyle from '../components/validation-modal/validation-modal.css?raw'; import Services from './service'; import { init, navigate } from '../router'; import { addressToEmoji } from '../utils/sp-address.utils'; import { RoleDefinition } from 'pkg/sdk_client'; import { initValidationModal } from '~/components/validation-modal/validation-modal'; import { interpolate } from '~/utils/html.utils'; export default class ModalService { private static instance: ModalService; private stateId: string | null = null; private processId: string | null = null; private constructor() {} private paired_addresses: string[] = []; private modal: HTMLElement | null = null; // Method to access the singleton instance of Services public static async getInstance(): Promise { if (!ModalService.instance) { ModalService.instance = new ModalService(); } return ModalService.instance; } public openLoginModal(myAddress: string, receiverAddress: string) { const container = document.querySelector('.page-container'); let html = modalHtml; html = html.replace('{{device1}}', myAddress); html = html.replace('{{device2}}', receiverAddress); if (container) container.innerHTML += html; const modal = document.getElementById('login-modal'); if (modal) modal.style.display = 'flex'; const newScript = document.createElement('script'); newScript.setAttribute('type', 'module'); newScript.textContent = modalScript; document.head.appendChild(newScript).parentNode?.removeChild(newScript); } async injectModal(members: any[]) { const container = document.querySelector('#containerId'); if (container) { let html = await fetch('/src/components/modal/confirmation-modal.html').then((res) => res.text()); html = html.replace('{{device1}}', await addressToEmoji(members[0]['sp_addresses'][0])); html = html.replace('{{device2}}', await addressToEmoji(members[0]['sp_addresses'][1])); container.innerHTML += html; // Dynamically load the header JS const script = document.createElement('script'); script.src = '/src/components/modal/confirmation-modal.ts'; script.type = 'module'; document.head.appendChild(script); } } async injectCreationModal(members: any[]) { const container = document.querySelector('#containerId'); if (container) { let html = await fetch('/src/components/modal/creation-modal.html').then((res) => res.text()); html = html.replace('{{device1}}', await addressToEmoji(members[0]['sp_addresses'][0])); container.innerHTML += html; // Dynamically load the header JS const script = document.createElement('script'); script.src = '/src/components/modal/confirmation-modal.ts'; script.type = 'module'; document.head.appendChild(script); } } // Device 1 wait Device 2 async injectWaitingModal() { const container = document.querySelector('#containerId'); if (container) { let html = await fetch('/src/components/modal/waiting-modal.html').then((res) => res.text()); container.innerHTML += html; } } async injectValidationModal(processDiff: any) { const container = document.querySelector('#containerId'); if (container) { let html = await fetch('/src/components/validation-modal/validation-modal.html').then((res) => res.text()); html = interpolate(html, {processId: processDiff.processId}) container.innerHTML += html; // Dynamically load the header JS const script = document.createElement('script'); script.id = 'validation-modal-script'; script.src = '/src/components/validation-modal/validation-modal.ts'; script.type = 'module'; document.head.appendChild(script); const css = document.createElement('style'); css.id = 'validation-modal-css'; css.innerText = validationModalStyle; document.head.appendChild(css); initValidationModal(processDiff) } } async closeValidationModal() { const script = document.querySelector('#validation-modal-script'); const css = document.querySelector('#validation-modal-css'); const component = document.querySelector('#validation-modal'); script?.remove(); css?.remove(); component?.remove(); } public async openPairingConfirmationModal(roleDefinition: Record, processId: string, stateId: string) { let members; if (roleDefinition['pairing']) { const owner = roleDefinition['pairing']; members = owner.members; } else { throw new Error('No "pairing" role'); } if (members.length != 1) { throw new Error('Must have exactly 1 member'); } console.log("MEMBERS:", members); // We take all the addresses except our own const service = await Services.getInstance(); const localAddress = await service.getDeviceAddress(); for (const member of members) { for (const address of member['sp_addresses']) { if (address !== localAddress) { this.paired_addresses.push(address); } } } this.processId = processId; this.stateId = stateId; if (members.sp_addresses.length === 1) { await this.injectCreationModal(members); this.modal = document.getElementById('creation-modal'); console.log("LENGTH:", members.sp_addresses.length); } else { await this.injectModal(members); this.modal = document.getElementById('modal'); console.log("LENGTH:", members.sp_addresses.length); } if (this.modal) this.modal.style.display = 'flex'; // Close modal when clicking outside of it window.onclick = (event) => { if (event.target === this.modal) { this.closeConfirmationModal(); } }; } confirmLogin() { console.log('=============> Confirm Login'); } async closeLoginModal() { if (this.modal) this.modal.style.display = 'none'; } async confirmPairing() { const service = await Services.getInstance(); if (this.modal) this.modal.style.display = 'none'; if (service.device1) { console.log("Device 1 detected"); // We send the prd update if (this.stateId && this.processId) { try { // Device B shouldn't do this again const createPrdUpdateReturn = service.createPrdUpdate(this.processId, this.stateId); await service.handleApiReturn(createPrdUpdateReturn); } catch (e) { throw e; } } else { throw new Error('No currentPcdCommitment'); } // We send confirmation that we validate the change try { const approveChangeReturn = service.approveChange(this.processId!, this.stateId!); await service.handleApiReturn(approveChangeReturn); await this.injectWaitingModal(); const waitingModal = document.getElementById('waiting-modal'); if (waitingModal) waitingModal.style.display = 'flex'; if (!service.device2Ready) { while (!service.device2Ready) { await new Promise(resolve => setTimeout(resolve, 1000)); } console.log("Device 2 is ready - Device 1 can now proceed"); } service.pairDevice(this.paired_addresses, this.processId); this.paired_addresses = []; this.processId = null; this.stateId = null; const newDevice = service.dumpDeviceFromMemory(); console.log(newDevice); await service.saveDeviceInDatabase(newDevice); navigate('chat'); service.resetState(); } catch (e) { throw e; } // try { // service.pairDevice(this.paired_addresses); // } catch (e) { // throw e; // } } else { console.log("Device 2 detected"); // if (this.stateId && this.processId) { // try { // // Device B shouldn't do this again // const createPrdUpdateReturn = service.createPrdUpdate(this.processId, this.stateId); // await service.handleApiReturn(createPrdUpdateReturn); // } catch (e) { // throw e; // } // } else { // throw new Error('No currentPcdCommitment'); // } // We send confirmation that we validate the change try { const approveChangeReturn = service.approveChange(this.processId!, this.stateId!); await service.handleApiReturn(approveChangeReturn); } catch (e) { throw e; } service.pairDevice(this.paired_addresses, this.processId!); this.paired_addresses = []; this.processId = null; this.stateId = null; const newDevice = service.dumpDeviceFromMemory(); console.log(newDevice); await service.saveDeviceInDatabase(newDevice); navigate('chat'); } } async closeConfirmationModal() { const service = await Services.getInstance(); await service.unpairDevice(); if (this.modal) this.modal.style.display = 'none'; } }