diff --git a/src/router.ts b/src/router.ts index 33f5f93..5452e9b 100755 --- a/src/router.ts +++ b/src/router.ts @@ -159,6 +159,62 @@ export async function init(): Promise { if (services.isPaired()) { await navigate('account'); } else { + // Check for device addition URL parameter + const urlParams = new URLSearchParams(window.location.search); + const addToId = urlParams.get('add_to_id'); + + if (addToId) { + try { + // This is a new device trying to join an existing pairing process + const pairingProcess = await services.getProcess(addToId); + console.debug(pairingProcess); + if (!pairingProcess) throw new Error('Non existent process'); // We were given a non existent process + const lastCommitedState = services.getLastCommitedState(pairingProcess!); + if (!lastCommitedState) throw new Error('Uncommited process'); // We were given an uncommited process + console.debug(lastCommitedState); + const currentPairedAddresses = lastCommitedState?.public_data['pairedAddresses']; + if (!currentPairedAddresses) throw new Error('Not a pairing process'); // We were given probably not a pairing process + const decodedAddresses = services.decodeValue(currentPairedAddresses!); + console.debug(decodedAddresses); + const modalService = await ModalService.getInstance(); + const result = await modalService.showConfirmationModal({ + title: 'Ajout d\'appareil', + content: ` + + `, + confirmText: 'Ajouter l\'appareil', + cancelText: 'Annuler' + }, true); + + if (!result) { + console.log('User refused to add device'); + await navigate('home'); + return; + } + + const myAddress = services.getDeviceAddress(); + const updatedPairedAddresses = [...decodedAddresses, myAddress]; + const updateProcessReturn = await services.updateProcess(pairingProcess!, {}, { pairedAddresses: updatedPairedAddresses }, null); + await services.handleApiReturn(updateProcessReturn); + // Now make the prd update and approve the change + const newStateId = updateProcessReturn.updated_process!.diffs[0].state_id; + console.debug(newStateId); + const prdUpdateReturn = await services.createPrdUpdate(addToId, newStateId); + await services.handleApiReturn(prdUpdateReturn); + const approveChangeReturn = await services.approveChange(addToId, newStateId); //Actually useless since this device is not a member of the process + await services.handleApiReturn(approveChangeReturn); + await navigate('account'); + return; + } catch (e) { + console.error(e); + await navigate('home'); + } + } await navigate('home'); } } catch (error) { @@ -781,6 +837,125 @@ export async function registerAllListeners() { } } + const handleGetMemberAddresses = async (event: MessageEvent) => { + if (event.data.type !== MessageType.GET_MEMBER_ADDRESSES) return; + + try { + const { accessToken, processId } = event.data; + + if (!accessToken || !(await tokenService.validateToken(accessToken, event.origin))) { + throw new Error('Invalid or expired session token'); + } + + const res = services.getAddressesForMemberId(processId); + + window.parent.postMessage( + { + type: MessageType.MEMBER_ADDRESSES_RETRIEVED, + memberAddresses: res, + messageId: event.data.messageId + }, + event.origin + ); + } catch (e) { + const errorMsg = `Failed to get member addresses: ${e}`; + errorResponse(errorMsg, event.origin, event.data.messageId); + } + } + + const handleAddDevice = async (event: MessageEvent) => { + if (event.data.type !== MessageType.ADD_DEVICE) return; + + try { + const { accessToken } = event.data; + + if (!accessToken || !(await tokenService.validateToken(accessToken, event.origin))) { + throw new Error('Invalid or expired session token'); + } + + // Get the current device address for the pairing URL + const myPairingProcessId = services.getPairingProcessId(); + const currentUrl = window.location.origin; + const pairingUrl = `${currentUrl}?add_to_id=${myPairingProcessId}`; + + // TODO get the current number of devices + + const modalService = await ModalService.getInstance(); + const result = await modalService.showConfirmationModal({ + title: 'Ajout d\'un nouvel appareil', + content: ` + + `, + confirmText: 'Terminer', + cancelText: 'Annuler' + }, true); + + if (!result) { + throw new Error('User cancelled device addition'); + } + + // Wait for the pairing process to be updated with the new device + const maxWaitTime = 300000; // 5 minutes + const pollInterval = 2000; // 2 seconds + const startTime = Date.now(); + let deviceAdded = false; + + while (Date.now() - startTime < maxWaitTime && !deviceAdded) { + console.debug('Polling for device addition'); + try { + const pairingProcess = await services.getProcess(myPairingProcessId); + if (pairingProcess) { + const uncommitedStates = services.getUncommitedStates(pairingProcess); + for (const state of uncommitedStates) { + const pairedAddresses = state.public_data['pairedAddresses']; + if (pairedAddresses) { + const decodedAddresses = services.decodeValue(pairedAddresses); + if (decodedAddresses.length > 1) { + console.log('New device detected in pairing process'); + deviceAdded = true; + break; + } + } + } + } else { + throw new Error('Process not found'); + } + } catch (e) { + console.warn('Error while polling for device addition:', e); + } + + await new Promise(resolve => setTimeout(resolve, pollInterval)); + } + + window.parent.postMessage( + { + type: MessageType.DEVICE_ADDED, + pairingUrl: pairingUrl, + deviceAdded: deviceAdded, + messageId: event.data.messageId + }, + event.origin + ); + + } catch (e) { + const errorMsg = `Failed to add device: ${e}`; + errorResponse(errorMsg, event.origin, event.data.messageId); + } + } + window.removeEventListener('message', handleMessage); window.addEventListener('message', handleMessage); @@ -832,6 +1007,12 @@ export async function registerAllListeners() { case MessageType.VALIDATE_MERKLE_PROOF: await handleValidateMerkleProof(event); break; + case MessageType.GET_MEMBER_ADDRESSES: + await handleGetMemberAddresses(event); + break; + case MessageType.ADD_DEVICE: + await handleAddDevice(event); + break; default: console.warn(`Unhandled message type: ${event.data.type}`); }