Compare commits
2 Commits
b473ddeefe
...
c09fa6f2f8
| Author | SHA1 | Date | |
|---|---|---|---|
| c09fa6f2f8 | |||
| 3a61ffe7a6 |
@ -1,35 +1,112 @@
|
|||||||
<div class="title-container">
|
<div id="iframe-loader" class="loader-container">
|
||||||
<h1>Create Account / New Session</h1>
|
<div class="loader-content">
|
||||||
</div>
|
<svg class="loader-spinner" viewBox="0 0 50 50">
|
||||||
|
<circle cx="25" cy="25" r="20" fill="none" stroke-width="5"></circle>
|
||||||
<div class="tab-container">
|
</svg>
|
||||||
<div class="tabs">
|
|
||||||
<div class="tab active" data-tab="tab1">Create an account</div>
|
|
||||||
<div class="tab" data-tab="tab2">Add a device for an existing memeber</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="page-container">
|
|
||||||
<div id="tab1" class="card tab-content active">
|
|
||||||
<div class="card-description">Create an account :</div>
|
|
||||||
<div class="pairing-request"></div>
|
|
||||||
<button id="createButton" class="create-btn"></button>
|
|
||||||
</div>
|
|
||||||
<div class="separator"></div>
|
|
||||||
<div id="tab2" class="card tab-content">
|
|
||||||
<div class="card-description">Add a device for an existing member :</div>
|
|
||||||
<div class="card-image camera-card">
|
|
||||||
<img id="scanner" src="assets/camera.jpg" alt="QR Code" width="150" height="150" />
|
|
||||||
<button id="scan-btn" onclick="scanDevice()">Scan</button> <div class="qr-code-scanner">
|
|
||||||
<div id="qr-reader" style="width: 200px; display: contents"></div>
|
|
||||||
<div id="qr-reader-results"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p>Or</p>
|
|
||||||
<div class="card-description">Chose a member :</div>
|
|
||||||
<select name="memberSelect" id="memberSelect" size="5" class="custom-select">
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<button id="okButton" style="display: none">OK</button>
|
<div id="loader-steps-container">
|
||||||
|
<p class="loader-step active">Initialisation...</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="main-content" style="display: none;">
|
||||||
|
|
||||||
|
<div class="title-container">
|
||||||
|
<h1>Create Account / New Session</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tab-container">
|
||||||
|
<div class="tabs">
|
||||||
|
<div class="tab active" data-tab="tab1">Create an account</div>
|
||||||
|
<div class="tab" data-tab="tab2">Add a device for an existing memeber</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="page-container">
|
||||||
|
<div id="tab1" class="card tab-content active">
|
||||||
|
<div class="card-description">Create an account :</div>
|
||||||
|
<div class="pairing-request"></div>
|
||||||
|
<button id="createButton" class="create-btn"></button>
|
||||||
|
</div>
|
||||||
|
<div class="separator"></div>
|
||||||
|
<div id="tab2" class="card tab-content">
|
||||||
|
<div class="card-description">Add a device for an existing member :</div>
|
||||||
|
<div class="card-image camera-card">
|
||||||
|
<img id="scanner" src="assets/camera.jpg" alt="QR Code" width="150" height="150" />
|
||||||
|
<button id="scan-btn" onclick="scanDevice()">Scan</button> <div class="qr-code-scanner">
|
||||||
|
<div id="qr-reader" style="width: 200px; display: contents"></div>
|
||||||
|
<div id="qr-reader-results"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p>Or</p>
|
||||||
|
<div class="card-description">Chose a member :</div>
|
||||||
|
<select name="memberSelect" id="memberSelect" size="5" class="custom-select">
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<button id="okButton" style="display: none">OK</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* --- Style du Loader --- */
|
||||||
|
.loader-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
/* Ajustez '400px' à la hauteur de votre modal */
|
||||||
|
height: 400px;
|
||||||
|
width: 100%;
|
||||||
|
/* Assurez-vous que le fond correspond à votre modal */
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
.loader-content {
|
||||||
|
text-align: center;
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
color: #333;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Style des Étapes --- */
|
||||||
|
#loader-steps-container {
|
||||||
|
margin-top: 20px;
|
||||||
|
text-align: left; /* Plus joli pour une liste */
|
||||||
|
width: 250px; /* Largeur fixe pour l'alignement */
|
||||||
|
height: 100px; /* Hauteur pour éviter les sauts de page */
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.loader-step {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #888; /* Anciennes étapes en gris */
|
||||||
|
margin: 4px 0;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.loader-step.active {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #333; /* Étape actuelle en noir */
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Animation du Spinner --- */
|
||||||
|
.loader-spinner {
|
||||||
|
animation: rotate 1.5s linear infinite;
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
.loader-spinner circle {
|
||||||
|
stroke: #007bff; /* Couleur du spinner */
|
||||||
|
stroke-linecap: round;
|
||||||
|
animation: dash 1.5s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
@keyframes rotate { 100% { transform: rotate(360deg); } }
|
||||||
|
@keyframes dash {
|
||||||
|
0% { stroke-dasharray: 1, 150; stroke-dashoffset: 0; }
|
||||||
|
50% { stroke-dasharray: 90, 150; stroke-dashoffset: -35; }
|
||||||
|
100% { stroke-dasharray: 90, 150; stroke-dashoffset: -124; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -3,11 +3,37 @@
|
|||||||
import Routing from '../../services/modal.service';
|
import Routing from '../../services/modal.service';
|
||||||
import Services from '../../services/service';
|
import Services from '../../services/service';
|
||||||
import { addSubscription } from '../../utils/subscription.utils';
|
import { addSubscription } from '../../utils/subscription.utils';
|
||||||
import { displayEmojis, generateQRCode, generateCreateBtn, addressToEmoji } from '../../utils/sp-address.utils';
|
import { displayEmojis, generateQRCode, generateCreateBtn, prepareAndSendPairingTx addressToEmoji } from '../../utils/sp-address.utils';
|
||||||
import QrScannerComponent from '../../components/qrcode-scanner/qrcode-scanner-component';
|
import QrScannerComponent from '../../components/qrcode-scanner/qrcode-scanner-component';
|
||||||
|
|
||||||
export { QrScannerComponent };
|
export { QrScannerComponent };
|
||||||
|
|
||||||
|
function addLoaderStep(container: ShadowRoot, text: string) {
|
||||||
|
// 1. Rendre l'ancienne étape "inactive"
|
||||||
|
const currentStep = container.querySelector('.loader-step.active') as HTMLParagraphElement;
|
||||||
|
if (currentStep) {
|
||||||
|
currentStep.classList.remove('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Trouver le conteneur
|
||||||
|
const stepsContainer = container.querySelector('#loader-steps-container') as HTMLDivElement;
|
||||||
|
if (stepsContainer) {
|
||||||
|
// 3. Créer et ajouter la nouvelle étape "active"
|
||||||
|
const newStep = document.createElement('p');
|
||||||
|
newStep.className = 'loader-step active';
|
||||||
|
newStep.textContent = text;
|
||||||
|
stepsContainer.appendChild(newStep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateLoaderText(container: ShadowRoot, text: string) {
|
||||||
|
const loaderText = container.querySelector('#loader-text') as HTMLParagraphElement;
|
||||||
|
if (loaderText) {
|
||||||
|
loaderText.textContent = text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
||||||
/**
|
/**
|
||||||
* Fonction d'initialisation principale.
|
* Fonction d'initialisation principale.
|
||||||
* Elle est appelée par home-component.ts et reçoit le ShadowRoot.
|
* Elle est appelée par home-component.ts et reçoit le ShadowRoot.
|
||||||
@ -19,6 +45,9 @@ export async function initHomePage(container: ShadowRoot): Promise<void> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const loaderDiv = container.querySelector('#iframe-loader') as HTMLDivElement;
|
||||||
|
const mainContentDiv = container.querySelector('#main-content') as HTMLDivElement;
|
||||||
|
|
||||||
const tabs = container.querySelectorAll('.tab');
|
const tabs = container.querySelectorAll('.tab');
|
||||||
|
|
||||||
tabs.forEach((tab) => {
|
tabs.forEach((tab) => {
|
||||||
@ -31,11 +60,60 @@ export async function initHomePage(container: ShadowRoot): Promise<void> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const service = await Services.getInstance();
|
const service = await Services.getInstance();
|
||||||
const spAddress = await service.getDeviceAddress();
|
|
||||||
generateCreateBtn();
|
|
||||||
displayEmojis(spAddress);
|
|
||||||
|
|
||||||
await populateMemberSelect(container);
|
// --- Début du flux de chargement ---
|
||||||
|
try {
|
||||||
|
// 'Initialisation...' est déjà dans le HTML
|
||||||
|
|
||||||
|
await delay(500); // Délai pour que "Initialisation..." soit visible
|
||||||
|
addLoaderStep(container, "Initialisation des services...");
|
||||||
|
const service = await Services.getInstance();
|
||||||
|
|
||||||
|
await delay(700);
|
||||||
|
addLoaderStep(container, "Vérification de l'appareil...");
|
||||||
|
const currentDevice = await service.getDeviceFromDatabase();
|
||||||
|
const pairingId = currentDevice?.pairing_process_commitment || null;
|
||||||
|
|
||||||
|
if (pairingId) {
|
||||||
|
await delay(300);
|
||||||
|
addLoaderStep(container, "Appairage existant trouvé.");
|
||||||
|
service.setProcessId(pairingId);
|
||||||
|
} else {
|
||||||
|
await delay(300);
|
||||||
|
addLoaderStep(container, "Création d'un appairage sécurisé...");
|
||||||
|
await prepareAndSendPairingTx();
|
||||||
|
addLoaderStep(container, "Appairage créé avec succès.");
|
||||||
|
}
|
||||||
|
|
||||||
|
await delay(500);
|
||||||
|
addLoaderStep(container, "Finalisation de la connexion...");
|
||||||
|
|
||||||
|
container.querySelectorAll('.tab').forEach((t) => t.classList.remove('active'));
|
||||||
|
container.querySelector('[data-tab="tab2"]')?.classList.add('active');
|
||||||
|
container.querySelectorAll('.tab-content').forEach((content) => content.classList.remove('active'));
|
||||||
|
container.querySelector('#tab2')?.classList.add('active');
|
||||||
|
|
||||||
|
const spAddress = await service.getDeviceAddress();
|
||||||
|
generateCreateBtn();
|
||||||
|
displayEmojis(spAddress);
|
||||||
|
await populateMemberSelect(container);
|
||||||
|
|
||||||
|
await delay(1000);
|
||||||
|
|
||||||
|
} catch (e: any) {
|
||||||
|
console.error('[home.ts] Échec de la logique auto-pairing:', e);
|
||||||
|
addLoaderStep(container, `Erreur: ${e.message}`);
|
||||||
|
// En cas d'erreur, on ne cache pas le loader
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Cacher le loader et afficher le contenu ---
|
||||||
|
// (Votre AuthModal le fermera si vite que ce ne sera pas visible,
|
||||||
|
// mais c'est une bonne pratique au cas où)
|
||||||
|
if (loaderDiv) loaderDiv.style.display = 'none';
|
||||||
|
if (mainContentDiv) mainContentDiv.style.display = 'block';
|
||||||
|
|
||||||
|
console.log('[home.ts] Init terminée. L\'iframe est prête pour le requestLink().');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -286,28 +286,32 @@ export async function registerAllListeners() {
|
|||||||
|
|
||||||
const handleRequestLink = async (event: MessageEvent) => {
|
const handleRequestLink = async (event: MessageEvent) => {
|
||||||
console.log(`[Router:API] 📨 Message ${MessageType.REQUEST_LINK} reçu de ${event.origin}`);
|
console.log(`[Router:API] 📨 Message ${MessageType.REQUEST_LINK} reçu de ${event.origin}`);
|
||||||
|
|
||||||
|
// L'appel à requestLink() depuis l'AuthModal est désormais la confirmation.
|
||||||
|
// Nous n'avons plus besoin du modal manuel.
|
||||||
|
console.log(`[Router:API] Liaison auto-confirmée pour ${event.origin}.`);
|
||||||
|
const result = true; // Simule le clic sur "Ajouter un service"
|
||||||
|
|
||||||
|
/*
|
||||||
|
// ANCIEN CODE SUPPRIMÉ :
|
||||||
const modalService = await ModalService.getInstance();
|
const modalService = await ModalService.getInstance();
|
||||||
const result = await modalService.showConfirmationModal(
|
const result = await modalService.showConfirmationModal(
|
||||||
{
|
{
|
||||||
title: 'Confirmation de liaison',
|
title: 'Confirmation de liaison',
|
||||||
content: `
|
content: `...`,
|
||||||
<div class="modal-confirmation">
|
|
||||||
<h3>Liaison avec ${event.origin}</h3>
|
|
||||||
<p>Vous êtes sur le point de lier l'identité numérique de la clé securisée propre à votre appareil avec ${event.origin}.</p>
|
|
||||||
<p>Cette action permettra à ${event.origin} d'intéragir avec votre appareil.</p>
|
|
||||||
<p>Voulez-vous continuer ?</p>
|
|
||||||
</div>
|
|
||||||
`,
|
|
||||||
confirmText: 'Ajouter un service',
|
confirmText: 'Ajouter un service',
|
||||||
cancelText: 'Annuler',
|
cancelText: 'Annuler',
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
throw new Error('Failed to pair device: User refused to link');
|
throw new Error('Failed to pair device: User refused to link');
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Le reste de votre fonction s'exécute immédiatement :
|
||||||
|
// Génération des tokens et envoi au parent.
|
||||||
const tokens = await tokenService.generateSessionToken(event.origin);
|
const tokens = await tokenService.generateSessionToken(event.origin);
|
||||||
window.parent.postMessage(
|
window.parent.postMessage(
|
||||||
{
|
{
|
||||||
@ -499,18 +503,48 @@ export async function registerAllListeners() {
|
|||||||
|
|
||||||
const handleGetPairingId = async (event: MessageEvent) => {
|
const handleGetPairingId = async (event: MessageEvent) => {
|
||||||
console.log(`[Router:API] 📨 Message ${MessageType.GET_PAIRING_ID} reçu`);
|
console.log(`[Router:API] 📨 Message ${MessageType.GET_PAIRING_ID} reçu`);
|
||||||
if (!services.isPaired()) throw new Error('Device not paired');
|
|
||||||
|
const maxRetries = 10;
|
||||||
|
const retryDelay = 300;
|
||||||
|
let pairingId: string | null = null;
|
||||||
|
|
||||||
|
for (let i = 0; i < maxRetries; i++) {
|
||||||
|
// On NE VÉRIFIE PAS services.isPaired() (qui est obsolète)
|
||||||
|
// On LIT DIRECTEMENT la BDD, comme le fait 'home.ts'
|
||||||
|
// (Je suppose que la méthode s'appelle 'getDeviceFromDatabase')
|
||||||
|
const device = await services.getDeviceFromDatabase();
|
||||||
|
|
||||||
|
// On vérifie si l'ID est maintenant présent dans la BDD
|
||||||
|
if (device && device.pairing_process_commitment) {
|
||||||
|
// SUCCÈS ! L'ID est dans la BDD
|
||||||
|
pairingId = device.pairing_process_commitment;
|
||||||
|
console.log(`[Router:API] GET_PAIRING_ID: ID trouvé en BDD (tentative ${i + 1}/${maxRetries})`);
|
||||||
|
break; // On sort de la boucle
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si non trouvé, on patiente (l'écriture est peut-être en cours)
|
||||||
|
console.warn(`[Router:API] GET_PAIRING_ID: Non trouvé en BDD, nouvelle tentative... (${i + 1}/${maxRetries})`);
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si la boucle se termine sans succès
|
||||||
|
if (!pairingId) {
|
||||||
|
console.error(`[Router:API] GET_PAIRING_ID: Échec final, non trouvé en BDD après ${maxRetries} tentatives.`);
|
||||||
|
throw new Error('Device not paired');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si on arrive ici, 'pairingId' contient l'ID
|
||||||
|
|
||||||
const { accessToken } = event.data;
|
const { accessToken } = event.data;
|
||||||
if (!accessToken || !(await tokenService.validateToken(accessToken, event.origin))) {
|
if (!accessToken || !(await tokenService.validateToken(accessToken, event.origin))) {
|
||||||
throw new Error('Invalid or expired session token');
|
throw new Error('Invalid or expired session token');
|
||||||
}
|
}
|
||||||
|
|
||||||
const userPairingId = services.getPairingProcessId();
|
// On renvoie l'ID qu'on vient de trouver
|
||||||
window.parent.postMessage(
|
window.parent.postMessage(
|
||||||
{
|
{
|
||||||
type: MessageType.GET_PAIRING_ID,
|
type: MessageType.GET_PAIRING_ID,
|
||||||
userPairingId,
|
userPairingId: pairingId, // Utiliser la variable 'pairingId'
|
||||||
messageId: event.data.messageId,
|
messageId: event.data.messageId,
|
||||||
},
|
},
|
||||||
event.origin,
|
event.origin,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user