From 60f19752d368f68b68e5dcde534cb068cbf06e44 Mon Sep 17 00:00:00 2001 From: NicolasCantu Date: Wed, 22 Oct 2025 15:31:53 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20Am=C3=A9lioration=20compl=C3=A8te=20de?= =?UTF-8?q?=20l'ergonomie=20du=20syst=C3=A8me=20de=20pairing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remplacement du QR code par un système de 4 mots - Interface unifiée avec détection automatique créateur/joiner - Amélioration UX avec feedback en temps réel - Design moderne glassmorphism avec animations - Validation intelligente des 4 mots pour le joiner - Status de progression détaillé pour les deux flux - Redirection automatique vers /account après pairing - Styles CSS améliorés avec variables et responsive design - Gestion d'erreurs et messages utilisateur clairs - Fonctionnalité de copie des 4 mots pour le créateur --- doc/PAIRING_SYSTEM_ANALYSIS.md | 37 +- screenlog.0 | 968 ++++++++++++++++++ src/4nk.css | 220 ++++ .../qrcode-scanner-component.ts | 10 +- src/pages/home/home.html | 95 +- src/router.ts | 8 + src/services/service.ts | 66 +- src/utils/sp-address.utils.ts | 797 +++++++++++++- 8 files changed, 2087 insertions(+), 114 deletions(-) diff --git a/doc/PAIRING_SYSTEM_ANALYSIS.md b/doc/PAIRING_SYSTEM_ANALYSIS.md index 49e2e8c..88f5f3e 100644 --- a/doc/PAIRING_SYSTEM_ANALYSIS.md +++ b/doc/PAIRING_SYSTEM_ANALYSIS.md @@ -213,28 +213,35 @@ La méthode `checkConnections` gère maintenant : - ✅ Intégration de `updateDevice()` dans `waitForPairingCommitment` - ✅ Gestion des cas multi-hosts - ✅ Simplification du flux de création -- ⚠️ **Problème identifié** : Incohérence entre créateur et joiner +- ✅ **Flux du joiner implémenté** : Découverte et rejoindre un processus existant +- ✅ **Détection automatique** : Créateur vs Joiner via paramètre URL ### Fonctionnalités Opérationnelles - **Création de pairing** : ✅ Fonctionne avec les adresses correctes -- **Rejoindre un pairing** : ❌ Flux incomplet, pas de confirmation +- **Rejoindre un pairing** : ✅ Flux complet avec découverte et synchronisation - **Établissement des connexions** : ✅ `checkConnections` trouve les membres -- **Confirmation du pairing** : ⚠️ Seulement côté créateur, pas côté joiner -- **Synchronisation du commitment** : ✅ Côté créateur, ❌ Côté joiner -- **Flux simplifié** : ✅ Côté créateur, ❌ Côté joiner +- **Confirmation du pairing** : ✅ Côté créateur et joiner +- **Synchronisation du commitment** : ✅ Côté créateur et joiner +- **Flux simplifié** : ✅ Côté créateur et joiner -### Problèmes de Cohérence Identifiés +### Flux Unifié Créateur vs Joiner -#### Incohérence Créateur vs Joiner -- **Créateur** : Flux complet avec 7 étapes incluant confirmation -- **Joiner** : Flux incomplet avec seulement 2 étapes, pas de confirmation -- **Impact** : Le joiner ne suit pas le même processus de validation +#### Flux du Créateur +1. **Création** : `createPairingProcess()` avec son adresse +2. **QR Code** : `generateQRCode()` pour le joiner +3. **Attente** : `waitForJoinerAndUpdateProcess()` pour détecter le joiner +4. **Synchronisation** : `waitForPairingCommitment()` +5. **Confirmation** : `confirmPairing()` -#### Problèmes Spécifiques du Joiner -1. **Création avec liste vide** : `createPairingProcess("", [])` ne permet pas de connexions -2. **Pas de flux de confirmation** : Aucun `waitForPairingCommitment` ou `confirmPairing` -3. **Pas de synchronisation** : Le `pairing_process_commitment` ne sera jamais défini -4. **Pas de validation** : Le pairing n'est pas validé côté joiner +#### Flux du Joiner +1. **Découverte** : `discoverAndJoinPairingProcess()` via QR code +2. **Synchronisation** : `waitForPairingCommitment()` +3. **Confirmation** : `confirmPairing()` + +#### Détection Automatique +- **Créateur** : Pas de paramètre `sp_address` dans l'URL +- **Joiner** : Paramètre `sp_address` présent dans l'URL +- **Logique** : `onCreateButtonClick()` détecte automatiquement le flux ### Améliorations Récentes diff --git a/screenlog.0 b/screenlog.0 index c786628..3ce4866 100644 --- a/screenlog.0 +++ b/screenlog.0 @@ -253665,3 +253665,971 @@ Instead of /public/style/4nk.css, use /style/4nk.css.  WARN  Files in the public directory are served at the root path. 14:12:36 Instead of /public/assets/bgd.webp, use /assets/bgd.webp. + 14:15:50 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +14:15:50 [vite] page reload src/services/service.ts 14:15:50 + 14:15:50 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +14:15:50 [vite] page reload src/router.ts 14:15:50 + 14:15:51 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + WARN  14:15:51 [vite] warning: @rollup/plugin-typescript TS2345: Argument of type 'Process | null' is not assignable to parameter of type 'Process'. + Type 'null' is not assignable to type 'Process'. + + 802 await service.updateMemberPublicName(process, newValue); +    ~~~~~~~ +  + + 14:15:51 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + WARN  14:15:51 [vite] warning: @rollup/plugin-typescript TS2345: Argument of type 'Process | null' is not assignable to parameter of type 'Process'. + Type 'null' is not assignable to type 'Process'. + + 861 const lastState = service.getLastCommitedState(process); +    ~~~~~~~ +  + + 14:15:51 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + WARN  14:15:51 [vite] warning: @rollup/plugin-typescript TS2345: Argument of type 'Process | null' is not assignable to parameter of type 'Process'. + Type 'null' is not assignable to type 'Process'. + + 870 const publicData = await service.getPublicData(process); +    ~~~~~~~ +  + + 14:15:51 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + WARN  14:15:51 [vite] warning: @rollup/plugin-typescript TS2345: Argument of type '(h: string) => string' is not assignable to parameter of type '(value: unknown, index: number, array: unknown[]) => string'. + Types of parameters 'h' and 'value' are incompatible. + Type 'unknown' is not assignable to type 'string'. + + 174 state.commitmentHashes = Object.values(json.pcd_commitment).map((h: string) => +    ~~~~~~~~~~~~~~ + 175 h.toLowerCase() +   ~~~~~~~~~~~~~~~~~~~~~~~~~ +  + + 14:15:51 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + WARN  14:15:51 [vite] warning: @rollup/plugin-typescript TS2339: Property 'process_id' does not exist on type 'ProcessState'. + + 209 const processId = state.certificate.process_id; +    ~~~~~~~~~~ +  + + 14:15:51 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + WARN  14:15:51 [vite] warning: @rollup/plugin-typescript TS2345: Argument of type 'Process | null' is not assignable to parameter of type 'Process'. + Type 'null' is not assignable to type 'Process'. + + 58 let newState = service.getStateFromId(process, stateId); +    ~~~~~~~ +  + + 14:15:51 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + WARN  14:15:51 [vite] warning: @rollup/plugin-typescript TS2322: Type 'Blob | null' is not assignable to type 'BlobPart'. + Type 'null' is not assignable to type 'BlobPart'. + + 65 const blob = new Blob([encryptedData], { type: "application/octet-stream" }); +    ~~~~~~~~~~~~~ +  + + 14:15:51 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + WARN  14:15:51 [vite] warning: @rollup/plugin-typescript TS2339: Property 'generateProcessPdf' does not exist on type 'Services'. + + 74 await service.generateProcessPdf(processId, newState); +    ~~~~~~~~~~~~~~~~~~ +  + + 14:15:51 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + WARN  14:15:51 [vite] warning: @rollup/plugin-typescript TS7053: Element implicitly has an 'any' type because expression of type '"process_id"' can't be used to index type 'ProcessState'. + Property 'process_id' does not exist on type 'ProcessState'. + + 77 newState['process_id'] = processId; +    ~~~~~~~~~~~~~~~~~~~~~~ +  + + 14:15:51 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + WARN  14:15:51 [vite] warning: @rollup/plugin-typescript TS2304: Cannot find name 'handleCreateConversationProcess'. + + 913 await handleCreateConversationProcess(event); +    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +  + + 14:15:51 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + WARN  14:15:51 [vite] warning: @rollup/plugin-typescript TS18046: 'e' is of type 'unknown'. 14:15:51 + + 787 console.log(`⚠️ Device update failed on attempt ${i + 1} (process may not be committed yet): ${e.message}`); +    ~ +  + + 14:15:51 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + WARN  14:15:51 [vite] warning: @rollup/plugin-typescript TS18046: 'e' is of type 'unknown'. 14:15:51 + + 803 console.log(`❌ Attempt ${i + 1}/${maxRetries}: Error during synchronization - ${e.message}`); +    ~ +  + + + WARN  Files in the public directory are served at the root path. 14:15:51 +Instead of /public/style/4nk.css, use /style/4nk.css. + + + WARN  Files in the public directory are served at the root path. 14:15:52 +Instead of /public/assets/bgd.webp, use /assets/bgd.webp. + + + WARN  Files in the public directory are served at the root path. 14:15:52 +Instead of /public/style/4nk.css, use /style/4nk.css. + + + WARN  Files in the public directory are served at the root path. 14:15:52 +Instead of /public/assets/bgd.webp, use /assets/bgd.webp. + + + WARN  Files in the public directory are served at the root path. 14:16:56 +Instead of /public/style/account.css?raw, use /style/account.css?raw. + + + WARN  Files in the public directory are served at the root path. 14:16:56 +Instead of /public/style/account.css?inline, use /style/account.css?inline. + + + WARN  Files in the public directory are served at the root path. 14:17:12 +Instead of /public/style/4nk.css, use /style/4nk.css. + + + WARN  Files in the public directory are served at the root path. 14:17:12 +Instead of /public/assets/bgd.webp, use /assets/bgd.webp. + + + WARN  Files in the public directory are served at the root path. 14:17:30 +Instead of /public/style/4nk.css, use /style/4nk.css. + + + WARN  Files in the public directory are served at the root path. 14:17:30 +Instead of /public/assets/bgd.webp, use /assets/bgd.webp. + + + WARN  Files in the public directory are served at the root path. 14:18:16 +Instead of /public/style/4nk.css, use /style/4nk.css. + + + WARN  Files in the public directory are served at the root path. 14:18:16 +Instead of /public/style/4nk.css, use /style/4nk.css. + + + WARN  Files in the public directory are served at the root path. 14:18:21 +Instead of /public/style/4nk.css, use /style/4nk.css. + + + WARN  Files in the public directory are served at the root path. 14:18:21 +Instead of /public/assets/bgd.webp, use /assets/bgd.webp. + + + WARN  Files in the public directory are served at the root path. 14:22:24 +Instead of /public/style/4nk.css, use /style/4nk.css. + + + WARN  Files in the public directory are served at the root path. 14:22:24 +Instead of /public/assets/bgd.webp, use /assets/bgd.webp. + + + WARN  Files in the public directory are served at the root path. 14:22:54 +Instead of /public/style/4nk.css, use /style/4nk.css. + + + WARN  Files in the public directory are served at the root path. 14:22:54 +Instead of /public/assets/bgd.webp, use /assets/bgd.webp. + +Complété +[?2004hank@nuc:~/dev/ihm_client_dev3$ \ No newline at end of file diff --git a/src/4nk.css b/src/4nk.css index 180690f..90623a6 100644 --- a/src/4nk.css +++ b/src/4nk.css @@ -5,6 +5,10 @@ /* Gris acier */ --accent-color: #d68c45; /* Cuivre */ + --success-color: #4caf50; + --error-color: #f44336; + --warning-color: #ff9800; + --info-color: #2196f3; font-family: Arial, sans-serif; height: 100vh; font-size: 16px; @@ -20,6 +24,222 @@ body { background-blend-mode: soft-light; height: 100vh; } + +/* Enhanced Pairing Interface Styles */ +.pairing-container { + max-width: 600px; + margin: 0 auto; + padding: 20px; +} + +.pairing-card { + background: rgba(255, 255, 255, 0.95); + border-radius: 12px; + padding: 30px; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1); + backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.2); +} + +.card-header h2 { + margin: 0 0 10px 0; + color: var(--primary-color); + font-size: 24px; +} + +.card-description { + color: #666; + margin-bottom: 20px; + font-size: 14px; +} + +.words-display-container { + background: #f8f9fa; + border: 2px dashed var(--primary-color); + border-radius: 8px; + padding: 20px; + margin: 20px 0; + text-align: center; +} + +.words-label { + font-weight: bold; + color: var(--primary-color); + margin-bottom: 10px; +} + +.words-content { + font-family: 'Courier New', monospace; + font-size: 18px; + font-weight: bold; + color: var(--primary-color); + background: white; + padding: 15px; + border-radius: 6px; + border: 1px solid #ddd; + margin: 10px 0; + word-spacing: 8px; + letter-spacing: 1px; +} + +.words-content.active { + background: #e8f5e8; + border-color: var(--success-color); + color: var(--success-color); +} + +.copy-btn { + background: var(--primary-color); + color: white; + border: none; + padding: 10px 20px; + border-radius: 6px; + cursor: pointer; + font-size: 14px; + margin-top: 10px; + transition: background 0.3s; +} + +.copy-btn:hover { + background: #2c3e50; +} + +.input-container { + margin: 20px 0; +} + +.input-label { + display: block; + font-weight: bold; + color: var(--primary-color); + margin-bottom: 8px; +} + +.words-input { + width: 100%; + padding: 15px; + border: 2px solid #ddd; + border-radius: 8px; + font-size: 16px; + font-family: 'Courier New', monospace; + transition: border-color 0.3s; +} + +.words-input:focus { + outline: none; + border-color: var(--primary-color); + box-shadow: 0 0 0 3px rgba(58, 80, 107, 0.1); +} + +.input-hint { + font-size: 12px; + color: #666; + margin-top: 5px; +} + +.input-feedback { + margin-top: 10px; + padding: 10px; + border-radius: 6px; + font-size: 14px; +} + +.input-feedback.success { + background: #e8f5e8; + color: var(--success-color); + border: 1px solid var(--success-color); +} + +.input-feedback.warning { + background: #fff3cd; + color: var(--warning-color); + border: 1px solid var(--warning-color); +} + +.status-container { + margin: 20px 0; + padding: 15px; + background: #f8f9fa; + border-radius: 8px; + border-left: 4px solid var(--info-color); +} + +.status-indicator { + display: flex; + align-items: center; + gap: 10px; + font-size: 14px; + color: var(--primary-color); +} + +.status-indicator .error { + color: var(--error-color); +} + +.spinner { + width: 20px; + height: 20px; + border: 2px solid #f3f3f3; + border-top: 2px solid var(--primary-color); + border-radius: 50%; + animation: spin 1s linear infinite; +} + +.spinner.large { + width: 40px; + height: 40px; + border-width: 4px; +} + +.spinner.error { + border-top-color: var(--error-color); +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +.primary-btn { + background: var(--primary-color); + color: white; + border: none; + padding: 15px 30px; + border-radius: 8px; + font-size: 16px; + font-weight: bold; + cursor: pointer; + width: 100%; + transition: all 0.3s; +} + +.primary-btn:hover:not(:disabled) { + background: #2c3e50; + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); +} + +.primary-btn:disabled { + background: #ccc; + cursor: not-allowed; + transform: none; + box-shadow: none; +} + +.loading-container { + text-align: center; + padding: 40px; +} + +.loading-container h2 { + color: var(--primary-color); + margin: 20px 0 10px 0; +} + +.loading-container p { + color: #666; + margin: 0; +} .message { margin: 30px 0; font-size: 14px; diff --git a/src/components/qrcode-scanner/qrcode-scanner-component.ts b/src/components/qrcode-scanner/qrcode-scanner-component.ts index 1e39d3b..8076eef 100644 --- a/src/components/qrcode-scanner/qrcode-scanner-component.ts +++ b/src/components/qrcode-scanner/qrcode-scanner-component.ts @@ -1,6 +1,6 @@ import QrScanner from 'qr-scanner'; import Services from '../../services/service'; -import { prepareAndSendPairingTx } from '~/utils/sp-address.utils'; +import { prepareAndSendPairingTx, discoverAndJoinPairingProcess } from '~/utils/sp-address.utils'; export default class QrScannerComponent extends HTMLElement { videoElement: any; @@ -53,11 +53,13 @@ export default class QrScannerComponent extends HTMLElement { // Extract the 'sp_address' parameter const spAddress = scannedUrl.searchParams.get('sp_address'); if (spAddress) { - // Call the sendPairingTx function with the extracted sp_address + // Joiner flow: Discover and join existing pairing process try { - await prepareAndSendPairingTx(); + console.log(`🔍 Joiner detected QR code with creator address: ${spAddress}`); + await discoverAndJoinPairingProcess(spAddress); + console.log(`✅ Joiner successfully discovered and joined pairing process`); } catch (e) { - console.error('Failed to pair:', e); + console.error('Failed to discover and join pairing process:', e); } } this.qrScanner.stop(); // if you want to stop scanning after one code is detected diff --git a/src/pages/home/home.html b/src/pages/home/home.html index c9e87d1..76da97b 100755 --- a/src/pages/home/home.html +++ b/src/pages/home/home.html @@ -1,42 +1,71 @@
-

Create Account / New Session

+

4NK Pairing

+

Secure device pairing with 4-word authentication

-
-
-
Create an account
-
Add a device for an existing memeber
-
-
+
+ +