Add device basic flow

This commit is contained in:
Sosthene 2025-07-07 15:27:55 +02:00
parent 5a98fac745
commit a467c45401

View File

@ -159,6 +159,62 @@ export async function init(): Promise<void> {
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: `
<div class="modal-confirmation">
<h3>Ajouter cet appareil à votre compte</h3>
<p>Vous êtes sur le point d'ajouter cet appareil à votre compte existant.</p>
<p>Cette action permettra à cet appareil d'accéder à vos données et processus.</p>
<p>Voulez-vous continuer ?</p>
</div>
`,
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: `
<div class="modal-confirmation">
<h3>Ajouter un nouvel appareil</h3>
<p>Pour ajouter un nouvel appareil à votre compte, partagez l'URL suivante avec le nouvel appareil :</p>
<div style="margin: 15px 0; padding: 10px; background-color: #f5f5f5; border-radius: 4px; word-break: break-all; font-family: monospace; font-size: 12px;">
${pairingUrl}
</div>
<p><small>Gardez cette URL secrète, elle permettra à l'appareil de se connecter à votre compte.</small></p>
<p>Ou scannez ce QR code avec le nouvel appareil :</p>
<div style="text-align: center; margin: 15px 0;">
<img src="https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=${encodeURIComponent(pairingUrl)}"
alt="QR Code pour l'ajout d'appareil"
style="border: 1px solid #ddd; border-radius: 4px;">
</div>
</div>
`,
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}`);
}