feat: Amélioration complète de l'ergonomie du système de pairing
- 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
This commit is contained in:
parent
7c2c4bfb46
commit
60f19752d3
@ -213,28 +213,35 @@ La méthode `checkConnections` gère maintenant :
|
|||||||
- ✅ Intégration de `updateDevice()` dans `waitForPairingCommitment`
|
- ✅ Intégration de `updateDevice()` dans `waitForPairingCommitment`
|
||||||
- ✅ Gestion des cas multi-hosts
|
- ✅ Gestion des cas multi-hosts
|
||||||
- ✅ Simplification du flux de création
|
- ✅ 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
|
### Fonctionnalités Opérationnelles
|
||||||
- **Création de pairing** : ✅ Fonctionne avec les adresses correctes
|
- **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
|
- **Établissement des connexions** : ✅ `checkConnections` trouve les membres
|
||||||
- **Confirmation du pairing** : ⚠️ Seulement côté créateur, pas côté joiner
|
- **Confirmation du pairing** : ✅ Côté créateur et joiner
|
||||||
- **Synchronisation du commitment** : ✅ Côté créateur, ❌ Côté joiner
|
- **Synchronisation du commitment** : ✅ Côté créateur et joiner
|
||||||
- **Flux simplifié** : ✅ Côté créateur, ❌ Côté 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
|
#### Flux du Créateur
|
||||||
- **Créateur** : Flux complet avec 7 étapes incluant confirmation
|
1. **Création** : `createPairingProcess()` avec son adresse
|
||||||
- **Joiner** : Flux incomplet avec seulement 2 étapes, pas de confirmation
|
2. **QR Code** : `generateQRCode()` pour le joiner
|
||||||
- **Impact** : Le joiner ne suit pas le même processus de validation
|
3. **Attente** : `waitForJoinerAndUpdateProcess()` pour détecter le joiner
|
||||||
|
4. **Synchronisation** : `waitForPairingCommitment()`
|
||||||
|
5. **Confirmation** : `confirmPairing()`
|
||||||
|
|
||||||
#### Problèmes Spécifiques du Joiner
|
#### Flux du Joiner
|
||||||
1. **Création avec liste vide** : `createPairingProcess("", [])` ne permet pas de connexions
|
1. **Découverte** : `discoverAndJoinPairingProcess()` via QR code
|
||||||
2. **Pas de flux de confirmation** : Aucun `waitForPairingCommitment` ou `confirmPairing`
|
2. **Synchronisation** : `waitForPairingCommitment()`
|
||||||
3. **Pas de synchronisation** : Le `pairing_process_commitment` ne sera jamais défini
|
3. **Confirmation** : `confirmPairing()`
|
||||||
4. **Pas de validation** : Le pairing n'est pas validé côté joiner
|
|
||||||
|
#### 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
|
### Améliorations Récentes
|
||||||
|
|
||||||
|
|||||||
968
screenlog.0
968
screenlog.0
@ -253665,3 +253665,971 @@ Instead of [36m/public/style/4nk.css[33m, use [36m/style/4nk.css[33m.[39m
|
|||||||
[43m[30m WARN [39m[49m [33mFiles in the public directory are served at the root path. [90m14:12:36[39m
|
[43m[30m WARN [39m[49m [33mFiles in the public directory are served at the root path. [90m14:12:36[39m
|
||||||
Instead of [36m/public/assets/bgd.webp[33m, use [36m/assets/bgd.webp[33m.[39m
|
Instead of [36m/public/assets/bgd.webp[33m, use [36m/assets/bgd.webp[33m.[39m
|
||||||
|
|
||||||
|
[90m14:15:50[39m
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[1;1H[0J[2m14:15:50[22m [36m[1m[vite][22m[39m [32mpage reload [39m[2msrc/services/service.ts[22m [90m14:15:50[39m
|
||||||
|
[90m14:15:50[39m
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[1;1H[0J[2m14:15:50[22m [36m[1m[vite][22m[39m [32mpage reload [39m[2msrc/router.ts[22m [90m14:15:50[39m
|
||||||
|
[90m14:15:51[39m
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[1;1H[0J
|
||||||
|
[43m[30m WARN [39m[49m [2m14:15:51[22m [33m[1m[vite][22m[39m [33mwarning: @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'.[39m
|
||||||
|
[33m
|
||||||
|
[7m802[0m await service.updateMemberPublicName(process, newValue);
|
||||||
|
[7m [0m [91m ~~~~~~~[0m
|
||||||
|
[39m
|
||||||
|
|
||||||
|
[90m14:15:51[39m
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[1;1H[0J
|
||||||
|
[43m[30m WARN [39m[49m [2m14:15:51[22m [33m[1m[vite][22m[39m [33mwarning: @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'.[39m
|
||||||
|
[33m
|
||||||
|
[7m861[0m const lastState = service.getLastCommitedState(process);
|
||||||
|
[7m [0m [91m ~~~~~~~[0m
|
||||||
|
[39m
|
||||||
|
|
||||||
|
[90m14:15:51[39m
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[1;1H[0J
|
||||||
|
[43m[30m WARN [39m[49m [2m14:15:51[22m [33m[1m[vite][22m[39m [33mwarning: @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'.[39m
|
||||||
|
[33m
|
||||||
|
[7m870[0m const publicData = await service.getPublicData(process);
|
||||||
|
[7m [0m [91m ~~~~~~~[0m
|
||||||
|
[39m
|
||||||
|
|
||||||
|
[90m14:15:51[39m
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[1;1H[0J
|
||||||
|
[43m[30m WARN [39m[49m [2m14:15:51[22m [33m[1m[vite][22m[39m [33mwarning: @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'.[39m
|
||||||
|
[33m
|
||||||
|
[7m174[0m state.commitmentHashes = Object.values(json.pcd_commitment).map((h: string) =>
|
||||||
|
[7m [0m [91m ~~~~~~~~~~~~~~[0m
|
||||||
|
[7m175[0m h.toLowerCase()
|
||||||
|
[7m [0m [91m~~~~~~~~~~~~~~~~~~~~~~~~~[0m
|
||||||
|
[39m
|
||||||
|
|
||||||
|
[90m14:15:51[39m
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[1;1H[0J
|
||||||
|
[43m[30m WARN [39m[49m [2m14:15:51[22m [33m[1m[vite][22m[39m [33mwarning: @rollup/plugin-typescript TS2339: Property 'process_id' does not exist on type 'ProcessState'.[39m
|
||||||
|
[33m
|
||||||
|
[7m209[0m const processId = state.certificate.process_id;
|
||||||
|
[7m [0m [91m ~~~~~~~~~~[0m
|
||||||
|
[39m
|
||||||
|
|
||||||
|
[90m14:15:51[39m
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[1;1H[0J
|
||||||
|
[43m[30m WARN [39m[49m [2m14:15:51[22m [33m[1m[vite][22m[39m [33mwarning: @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'.[39m
|
||||||
|
[33m
|
||||||
|
[7m58[0m let newState = service.getStateFromId(process, stateId);
|
||||||
|
[7m [0m [91m ~~~~~~~[0m
|
||||||
|
[39m
|
||||||
|
|
||||||
|
[90m14:15:51[39m
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[1;1H[0J
|
||||||
|
[43m[30m WARN [39m[49m [2m14:15:51[22m [33m[1m[vite][22m[39m [33mwarning: @rollup/plugin-typescript TS2322: Type 'Blob | null' is not assignable to type 'BlobPart'.
|
||||||
|
Type 'null' is not assignable to type 'BlobPart'.[39m
|
||||||
|
[33m
|
||||||
|
[7m65[0m const blob = new Blob([encryptedData], { type: "application/octet-stream" });
|
||||||
|
[7m [0m [91m ~~~~~~~~~~~~~[0m
|
||||||
|
[39m
|
||||||
|
|
||||||
|
[90m14:15:51[39m
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[1;1H[0J
|
||||||
|
[43m[30m WARN [39m[49m [2m14:15:51[22m [33m[1m[vite][22m[39m [33mwarning: @rollup/plugin-typescript TS2339: Property 'generateProcessPdf' does not exist on type 'Services'.[39m
|
||||||
|
[33m
|
||||||
|
[7m74[0m await service.generateProcessPdf(processId, newState);
|
||||||
|
[7m [0m [91m ~~~~~~~~~~~~~~~~~~[0m
|
||||||
|
[39m
|
||||||
|
|
||||||
|
[90m14:15:51[39m
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[1;1H[0J
|
||||||
|
[43m[30m WARN [39m[49m [2m14:15:51[22m [33m[1m[vite][22m[39m [33mwarning: @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'.[39m
|
||||||
|
[33m
|
||||||
|
[7m77[0m newState['process_id'] = processId;
|
||||||
|
[7m [0m [91m ~~~~~~~~~~~~~~~~~~~~~~[0m
|
||||||
|
[39m
|
||||||
|
|
||||||
|
[90m14:15:51[39m
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[1;1H[0J
|
||||||
|
[43m[30m WARN [39m[49m [2m14:15:51[22m [33m[1m[vite][22m[39m [33mwarning: @rollup/plugin-typescript TS2304: Cannot find name 'handleCreateConversationProcess'.[39m
|
||||||
|
[33m
|
||||||
|
[7m913[0m await handleCreateConversationProcess(event);
|
||||||
|
[7m [0m [91m ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~[0m
|
||||||
|
[39m
|
||||||
|
|
||||||
|
[90m14:15:51[39m
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[1;1H[0J
|
||||||
|
[43m[30m WARN [39m[49m [2m14:15:51[22m [33m[1m[vite][22m[39m [33mwarning: @rollup/plugin-typescript TS18046: 'e' is of type 'unknown'.[39m [90m14:15:51[39m
|
||||||
|
[33m
|
||||||
|
[7m787[0m console.log(`⚠️ Device update failed on attempt ${i + 1} (process may not be committed yet): ${e.message}`);
|
||||||
|
[7m [0m [91m ~[0m
|
||||||
|
[39m
|
||||||
|
|
||||||
|
[90m14:15:51[39m
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[1;1H[0J
|
||||||
|
[43m[30m WARN [39m[49m [2m14:15:51[22m [33m[1m[vite][22m[39m [33mwarning: @rollup/plugin-typescript TS18046: 'e' is of type 'unknown'.[39m [90m14:15:51[39m
|
||||||
|
[33m
|
||||||
|
[7m803[0m console.log(`❌ Attempt ${i + 1}/${maxRetries}: Error during synchronization - ${e.message}`);
|
||||||
|
[7m [0m [91m ~[0m
|
||||||
|
[39m
|
||||||
|
|
||||||
|
|
||||||
|
[43m[30m WARN [39m[49m [33mFiles in the public directory are served at the root path. [90m14:15:51[39m
|
||||||
|
Instead of [36m/public/style/4nk.css[33m, use [36m/style/4nk.css[33m.[39m
|
||||||
|
|
||||||
|
|
||||||
|
[43m[30m WARN [39m[49m [33mFiles in the public directory are served at the root path. [90m14:15:52[39m
|
||||||
|
Instead of [36m/public/assets/bgd.webp[33m, use [36m/assets/bgd.webp[33m.[39m
|
||||||
|
|
||||||
|
|
||||||
|
[43m[30m WARN [39m[49m [33mFiles in the public directory are served at the root path. [90m14:15:52[39m
|
||||||
|
Instead of [36m/public/style/4nk.css[33m, use [36m/style/4nk.css[33m.[39m
|
||||||
|
|
||||||
|
|
||||||
|
[43m[30m WARN [39m[49m [33mFiles in the public directory are served at the root path. [90m14:15:52[39m
|
||||||
|
Instead of [36m/public/assets/bgd.webp[33m, use [36m/assets/bgd.webp[33m.[39m
|
||||||
|
|
||||||
|
|
||||||
|
[43m[30m WARN [39m[49m [33mFiles in the public directory are served at the root path. [90m14:16:56[39m
|
||||||
|
Instead of [36m/public/style/account.css?raw[33m, use [36m/style/account.css?raw[33m.[39m
|
||||||
|
|
||||||
|
|
||||||
|
[43m[30m WARN [39m[49m [33mFiles in the public directory are served at the root path. [90m14:16:56[39m
|
||||||
|
Instead of [36m/public/style/account.css?inline[33m, use [36m/style/account.css?inline[33m.[39m
|
||||||
|
|
||||||
|
|
||||||
|
[43m[30m WARN [39m[49m [33mFiles in the public directory are served at the root path. [90m14:17:12[39m
|
||||||
|
Instead of [36m/public/style/4nk.css[33m, use [36m/style/4nk.css[33m.[39m
|
||||||
|
|
||||||
|
|
||||||
|
[43m[30m WARN [39m[49m [33mFiles in the public directory are served at the root path. [90m14:17:12[39m
|
||||||
|
Instead of [36m/public/assets/bgd.webp[33m, use [36m/assets/bgd.webp[33m.[39m
|
||||||
|
|
||||||
|
|
||||||
|
[43m[30m WARN [39m[49m [33mFiles in the public directory are served at the root path. [90m14:17:30[39m
|
||||||
|
Instead of [36m/public/style/4nk.css[33m, use [36m/style/4nk.css[33m.[39m
|
||||||
|
|
||||||
|
|
||||||
|
[43m[30m WARN [39m[49m [33mFiles in the public directory are served at the root path. [90m14:17:30[39m
|
||||||
|
Instead of [36m/public/assets/bgd.webp[33m, use [36m/assets/bgd.webp[33m.[39m
|
||||||
|
|
||||||
|
|
||||||
|
[43m[30m WARN [39m[49m [33mFiles in the public directory are served at the root path. [90m14:18:16[39m
|
||||||
|
Instead of [36m/public/style/4nk.css[33m, use [36m/style/4nk.css[33m.[39m
|
||||||
|
|
||||||
|
|
||||||
|
[43m[30m WARN [39m[49m [33mFiles in the public directory are served at the root path. [90m14:18:16[39m
|
||||||
|
Instead of [36m/public/style/4nk.css[33m, use [36m/style/4nk.css[33m.[39m
|
||||||
|
|
||||||
|
|
||||||
|
[43m[30m WARN [39m[49m [33mFiles in the public directory are served at the root path. [90m14:18:21[39m
|
||||||
|
Instead of [36m/public/style/4nk.css[33m, use [36m/style/4nk.css[33m.[39m
|
||||||
|
|
||||||
|
|
||||||
|
[43m[30m WARN [39m[49m [33mFiles in the public directory are served at the root path. [90m14:18:21[39m
|
||||||
|
Instead of [36m/public/assets/bgd.webp[33m, use [36m/assets/bgd.webp[33m.[39m
|
||||||
|
|
||||||
|
|
||||||
|
[43m[30m WARN [39m[49m [33mFiles in the public directory are served at the root path. [90m14:22:24[39m
|
||||||
|
Instead of [36m/public/style/4nk.css[33m, use [36m/style/4nk.css[33m.[39m
|
||||||
|
|
||||||
|
|
||||||
|
[43m[30m WARN [39m[49m [33mFiles in the public directory are served at the root path. [90m14:22:24[39m
|
||||||
|
Instead of [36m/public/assets/bgd.webp[33m, use [36m/assets/bgd.webp[33m.[39m
|
||||||
|
|
||||||
|
|
||||||
|
[43m[30m WARN [39m[49m [33mFiles in the public directory are served at the root path. [90m14:22:54[39m
|
||||||
|
Instead of [36m/public/style/4nk.css[33m, use [36m/style/4nk.css[33m.[39m
|
||||||
|
|
||||||
|
|
||||||
|
[43m[30m WARN [39m[49m [33mFiles in the public directory are served at the root path. [90m14:22:54[39m
|
||||||
|
Instead of [36m/public/assets/bgd.webp[33m, use [36m/assets/bgd.webp[33m.[39m
|
||||||
|
|
||||||
|
Complété
|
||||||
|
[?2004h[01;32mank@nuc[00m:[01;34m~/dev/ihm_client_dev3[00m$
|
||||||
220
src/4nk.css
220
src/4nk.css
@ -5,6 +5,10 @@
|
|||||||
/* Gris acier */
|
/* Gris acier */
|
||||||
--accent-color: #d68c45;
|
--accent-color: #d68c45;
|
||||||
/* Cuivre */
|
/* Cuivre */
|
||||||
|
--success-color: #4caf50;
|
||||||
|
--error-color: #f44336;
|
||||||
|
--warning-color: #ff9800;
|
||||||
|
--info-color: #2196f3;
|
||||||
font-family: Arial, sans-serif;
|
font-family: Arial, sans-serif;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
@ -20,6 +24,222 @@ body {
|
|||||||
background-blend-mode: soft-light;
|
background-blend-mode: soft-light;
|
||||||
height: 100vh;
|
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 {
|
.message {
|
||||||
margin: 30px 0;
|
margin: 30px 0;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import QrScanner from 'qr-scanner';
|
import QrScanner from 'qr-scanner';
|
||||||
import Services from '../../services/service';
|
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 {
|
export default class QrScannerComponent extends HTMLElement {
|
||||||
videoElement: any;
|
videoElement: any;
|
||||||
@ -53,11 +53,13 @@ export default class QrScannerComponent extends HTMLElement {
|
|||||||
// Extract the 'sp_address' parameter
|
// Extract the 'sp_address' parameter
|
||||||
const spAddress = scannedUrl.searchParams.get('sp_address');
|
const spAddress = scannedUrl.searchParams.get('sp_address');
|
||||||
if (spAddress) {
|
if (spAddress) {
|
||||||
// Call the sendPairingTx function with the extracted sp_address
|
// Joiner flow: Discover and join existing pairing process
|
||||||
try {
|
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) {
|
} 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
|
this.qrScanner.stop(); // if you want to stop scanning after one code is detected
|
||||||
|
|||||||
@ -1,42 +1,71 @@
|
|||||||
<div class="title-container">
|
<div class="title-container">
|
||||||
<h1>Create Account / New Session</h1>
|
<h1>4NK Pairing</h1>
|
||||||
|
<p class="subtitle">Secure device pairing with 4-word authentication</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tab-container">
|
<div class="pairing-container">
|
||||||
<div class="tabs">
|
<!-- Creator Flow -->
|
||||||
<div class="tab active" data-tab="tab1">Create an account</div>
|
<div id="creator-flow" class="card pairing-card" style="display: none;">
|
||||||
<div class="tab" data-tab="tab2">Add a device for an existing memeber</div>
|
<div class="card-header">
|
||||||
</div>
|
<h2>🔐 Create New Pairing</h2>
|
||||||
</div>
|
<p class="card-description">You are creating a new pairing session</p>
|
||||||
|
</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>
|
<div class="pairing-request"></div>
|
||||||
<!-- <div class="card-image qr-code">
|
|
||||||
<img src="assets/qr_code.png" alt="QR Code" width="150" height="150" />
|
<div class="words-display-container">
|
||||||
</div> -->
|
<div class="words-label">Share these 4 words with the other device:</div>
|
||||||
<button id="createButton" class="create-btn"></button>
|
<div class="words-content" id="creator-words"></div>
|
||||||
</div>
|
<button class="copy-btn" id="copyWordsBtn">📋 Copy Words</button>
|
||||||
<div class="separator"></div>
|
</div>
|
||||||
<div id="tab2" class="card tab-content">
|
|
||||||
<div class="card-description">Add a device for an existing member :</div>
|
<div class="status-container">
|
||||||
<div class="card-image camera-card">
|
<div class="status-indicator" id="creator-status">
|
||||||
<img id="scanner" src="assets/camera.jpg" alt="QR Code" width="150" height="150" />
|
<div class="spinner"></div>
|
||||||
<button id="scan-btn" onclick="scanDevice()">Scan</button>
|
<span>Creating pairing process...</span>
|
||||||
<div class="qr-code-scanner">
|
|
||||||
<div id="qr-reader" style="width: 200px; display: contents"></div>
|
|
||||||
<div id="qr-reader-results"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p>Or</p>
|
|
||||||
<!-- <input type="text" id="addressInput" placeholder="Paste address" />
|
<button id="createButton" class="primary-btn">Create Pairing</button>
|
||||||
<div id="emoji-display-2"></div> -->
|
</div>
|
||||||
<div class="card-description">Chose a member :</div>
|
|
||||||
<select name="memberSelect" id="memberSelect" size="5" class="custom-select">
|
<!-- Joiner Flow -->
|
||||||
<!-- Options -->
|
<div id="joiner-flow" class="card pairing-card" style="display: none;">
|
||||||
</select>
|
<div class="card-header">
|
||||||
|
<h2>🔗 Join Existing Pairing</h2>
|
||||||
<button id="okButton" style="display: none">OK</button>
|
<p class="card-description">Enter the 4 words from the creator device</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="input-container">
|
||||||
|
<label for="wordsInput" class="input-label">4 Words:</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="wordsInput"
|
||||||
|
placeholder="Enter 4 words (e.g., abandon ability able about)"
|
||||||
|
class="words-input"
|
||||||
|
autocomplete="off"
|
||||||
|
spellcheck="false"
|
||||||
|
/>
|
||||||
|
<div class="input-hint">Separate words with spaces</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="words-display" id="words-display-2"></div>
|
||||||
|
|
||||||
|
<div class="status-container">
|
||||||
|
<div class="status-indicator" id="joiner-status">
|
||||||
|
<span>Ready to join</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button id="joinButton" class="primary-btn" disabled>Join Pairing</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Loading State -->
|
||||||
|
<div id="loading-flow" class="card pairing-card">
|
||||||
|
<div class="loading-container">
|
||||||
|
<div class="spinner large"></div>
|
||||||
|
<h2>Initializing...</h2>
|
||||||
|
<p>Setting up secure pairing</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -276,21 +276,29 @@ export async function registerAllListeners() {
|
|||||||
console.log("🧠 Handling API return for createPairingProcess...");
|
console.log("🧠 Handling API return for createPairingProcess...");
|
||||||
await services.handleApiReturn(createPairingProcessReturn);
|
await services.handleApiReturn(createPairingProcessReturn);
|
||||||
|
|
||||||
|
console.log("🔍 DEBUG: About to create PRD update...");
|
||||||
console.log("🧰 Creating PRD update...");
|
console.log("🧰 Creating PRD update...");
|
||||||
const createPrdUpdateReturn = await services.createPrdUpdate(pairingId, stateId);
|
const createPrdUpdateReturn = await services.createPrdUpdate(pairingId, stateId);
|
||||||
console.log("🧾 PRD update result:", createPrdUpdateReturn);
|
console.log("🧾 PRD update result:", createPrdUpdateReturn);
|
||||||
await services.handleApiReturn(createPrdUpdateReturn);
|
await services.handleApiReturn(createPrdUpdateReturn);
|
||||||
|
console.log("✅ DEBUG: PRD update completed successfully!");
|
||||||
|
|
||||||
|
console.log("🔍 DEBUG: About to approve change...");
|
||||||
console.log("✅ Approving change...");
|
console.log("✅ Approving change...");
|
||||||
const approveChangeReturn = await services.approveChange(pairingId, stateId);
|
const approveChangeReturn = await services.approveChange(pairingId, stateId);
|
||||||
console.log("📜 Approve change result:", approveChangeReturn);
|
console.log("📜 Approve change result:", approveChangeReturn);
|
||||||
await services.handleApiReturn(approveChangeReturn);
|
await services.handleApiReturn(approveChangeReturn);
|
||||||
|
console.log("✅ DEBUG: approveChange completed successfully!");
|
||||||
|
|
||||||
|
console.log("🔍 DEBUG: approveChange completed, about to call waitForPairingCommitment...");
|
||||||
console.log("⏳ Waiting for pairing process to be committed...");
|
console.log("⏳ Waiting for pairing process to be committed...");
|
||||||
await services.waitForPairingCommitment(pairingId);
|
await services.waitForPairingCommitment(pairingId);
|
||||||
|
console.log("✅ DEBUG: waitForPairingCommitment completed successfully!");
|
||||||
|
|
||||||
|
console.log("🔍 DEBUG: About to call confirmPairing...");
|
||||||
console.log("🔁 Confirming pairing...");
|
console.log("🔁 Confirming pairing...");
|
||||||
await services.confirmPairing(pairingId);
|
await services.confirmPairing(pairingId);
|
||||||
|
console.log("✅ DEBUG: confirmPairing completed successfully!");
|
||||||
|
|
||||||
console.log("🎉 Pairing successfully completed!");
|
console.log("🎉 Pairing successfully completed!");
|
||||||
|
|
||||||
|
|||||||
@ -774,22 +774,60 @@ export default class Services {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async waitForPairingCommitment(processId: string, maxRetries: number = 30, retryDelay: number = 2000): Promise<void> {
|
public async waitForPairingCommitment(processId: string, maxRetries: number = 30, retryDelay: number = 2000): Promise<void> {
|
||||||
|
console.log(`🔍 DEBUG: waitForPairingCommitment called with processId: ${processId}`);
|
||||||
console.log(`⏳ Waiting for pairing process ${processId} to be committed and synchronized...`);
|
console.log(`⏳ Waiting for pairing process ${processId} to be committed and synchronized...`);
|
||||||
console.log(`🔄 This may take some time as we wait for SDK synchronization...`);
|
console.log(`🔄 This may take some time as we wait f or SDK synchronization...`);
|
||||||
|
|
||||||
for (let i = 0; i < maxRetries; i++) {
|
for (let i = 0; i < maxRetries; i++) {
|
||||||
try {
|
try {
|
||||||
// Try to update device first (may fail if not committed yet)
|
// Check device state directly without forcing updateDevice
|
||||||
try {
|
|
||||||
await this.updateDevice();
|
|
||||||
console.log(`✅ Device update successful on attempt ${i + 1}`);
|
|
||||||
} catch (e) {
|
|
||||||
console.log(`⚠️ Device update failed on attempt ${i + 1} (process may not be committed yet): ${e.message}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const device = this.dumpDeviceFromMemory();
|
const device = this.dumpDeviceFromMemory();
|
||||||
console.log(`🔍 Attempt ${i + 1}/${maxRetries}: pairing_process_commitment =`, device.pairing_process_commitment);
|
console.log(`🔍 Attempt ${i + 1}/${maxRetries}: pairing_process_commitment =`, device.pairing_process_commitment);
|
||||||
|
|
||||||
|
// Additional debugging: Check if we can get the pairing process ID
|
||||||
|
try {
|
||||||
|
const currentPairingId = this.sdkClient.get_pairing_process_id();
|
||||||
|
console.log(`🔍 Current pairing process ID from SDK: ${currentPairingId}`);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(`⚠️ SDK pairing process ID not available yet: ${e.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to force synchronization by requesting the process from peers
|
||||||
|
if (i % 3 === 0 && i > 0) {
|
||||||
|
try {
|
||||||
|
console.log(`🔄 Attempting to request process from peers...`);
|
||||||
|
await this.requestDataFromPeers(processId, [], []);
|
||||||
|
console.log(`✅ Process request sent to peers`);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(`⚠️ Failed to request process from peers: ${e.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the process exists in our processes list
|
||||||
|
try {
|
||||||
|
const process = this.getProcess(processId);
|
||||||
|
if (process) {
|
||||||
|
console.log(`🔍 Process exists: ${processId}, states: ${process.states?.length || 0}`);
|
||||||
|
const lastState = process.states?.[process.states.length - 1];
|
||||||
|
if (lastState) {
|
||||||
|
console.log(`🔍 Last state ID: ${lastState.state_id}, committed: ${lastState.committed}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log(`⚠️ Process not found in local processes: ${processId}`);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log(`⚠️ Error checking process: ${e.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check WebSocket connection and handshake data
|
||||||
|
try {
|
||||||
|
console.log(`🔍 WebSocket connections: ${Object.keys(this.relayAddresses).length} relays`);
|
||||||
|
console.log(`🔍 Current block height: ${this.currentBlockHeight}`);
|
||||||
|
console.log(`🔍 Members list size: ${Object.keys(this.membersList).length}`);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(`⚠️ Error checking WebSocket state: ${e.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
// Check if the commitment is set and not null/empty
|
// Check if the commitment is set and not null/empty
|
||||||
if (device.pairing_process_commitment &&
|
if (device.pairing_process_commitment &&
|
||||||
device.pairing_process_commitment !== null &&
|
device.pairing_process_commitment !== null &&
|
||||||
@ -799,6 +837,16 @@ export default class Services {
|
|||||||
}
|
}
|
||||||
|
|
||||||
console.log(`⏳ Still waiting for SDK synchronization... (${i + 1}/${maxRetries})`);
|
console.log(`⏳ Still waiting for SDK synchronization... (${i + 1}/${maxRetries})`);
|
||||||
|
|
||||||
|
// Only try updateDevice every 5 attempts to avoid spam
|
||||||
|
if (i % 5 === 0 && i > 0) {
|
||||||
|
try {
|
||||||
|
await this.updateDevice();
|
||||||
|
console.log(`✅ Device update successful on attempt ${i + 1}`);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(`⚠️ Device update failed on attempt ${i + 1} (process may not be committed yet): ${e.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(`❌ Attempt ${i + 1}/${maxRetries}: Error during synchronization - ${e.message}`);
|
console.log(`❌ Attempt ${i + 1}/${maxRetries}: Error during synchronization - ${e.message}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -54,6 +54,307 @@ export async function addressToEmoji(text: string): Promise<string> {
|
|||||||
return emojis;
|
return emojis;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Address to 4 words representation
|
||||||
|
export async function addressToWords(text: string): Promise<string> {
|
||||||
|
// Address to Hash
|
||||||
|
const encoder = new TextEncoder();
|
||||||
|
const data = encoder.encode(text);
|
||||||
|
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
|
||||||
|
|
||||||
|
const hash = new Uint8Array(hashBuffer);
|
||||||
|
const bytes = hash.slice(-8); // Take 8 bytes for 4 words (2 bytes per word)
|
||||||
|
|
||||||
|
// Word list (BIP39-like word list)
|
||||||
|
const wordList = [
|
||||||
|
'abandon', 'ability', 'able', 'about', 'above', 'absent', 'absorb', 'abstract',
|
||||||
|
'absurd', 'abuse', 'access', 'accident', 'account', 'accuse', 'achieve', 'acid',
|
||||||
|
'acoustic', 'acquire', 'across', 'act', 'action', 'actor', 'actress', 'actual',
|
||||||
|
'adapt', 'add', 'addict', 'address', 'adjust', 'admit', 'adult', 'advance',
|
||||||
|
'advice', 'aerobic', 'affair', 'afford', 'afraid', 'again', 'age', 'agent',
|
||||||
|
'agree', 'ahead', 'aim', 'air', 'airport', 'aisle', 'alarm', 'album',
|
||||||
|
'alcohol', 'alert', 'alien', 'all', 'alley', 'allow', 'almost', 'alone',
|
||||||
|
'alpha', 'already', 'also', 'alter', 'always', 'amateur', 'amazing', 'among',
|
||||||
|
'amount', 'amused', 'analyst', 'anchor', 'ancient', 'anger', 'angle', 'angry',
|
||||||
|
'animal', 'ankle', 'announce', 'annual', 'another', 'answer', 'antenna', 'antique',
|
||||||
|
'anxiety', 'any', 'apart', 'apology', 'appear', 'apple', 'approve', 'april',
|
||||||
|
'arch', 'arctic', 'area', 'arena', 'argue', 'arm', 'armed', 'armor',
|
||||||
|
'army', 'around', 'arrange', 'arrest', 'arrive', 'arrow', 'art', 'artefact',
|
||||||
|
'artist', 'artwork', 'ask', 'aspect', 'assault', 'asset', 'assist', 'assume',
|
||||||
|
'asthma', 'athlete', 'atom', 'attack', 'attend', 'attitude', 'attract', 'auction',
|
||||||
|
'audit', 'august', 'aunt', 'author', 'auto', 'autumn', 'average', 'avocado',
|
||||||
|
'avoid', 'awake', 'aware', 'away', 'awesome', 'awful', 'awkward', 'axis',
|
||||||
|
'baby', 'bachelor', 'bacon', 'badge', 'bag', 'balance', 'balcony', 'ball',
|
||||||
|
'bamboo', 'banana', 'banner', 'bar', 'barely', 'bargain', 'barrel', 'base',
|
||||||
|
'basic', 'basket', 'battle', 'beach', 'bean', 'beauty', 'because', 'become',
|
||||||
|
'beef', 'before', 'begin', 'behave', 'behind', 'believe', 'below', 'belt',
|
||||||
|
'bench', 'benefit', 'best', 'betray', 'better', 'between', 'beyond', 'bicycle',
|
||||||
|
'bid', 'bike', 'bind', 'biology', 'bird', 'birth', 'bitter', 'black',
|
||||||
|
'blade', 'blame', 'blanket', 'blast', 'bleak', 'bless', 'blind', 'blood',
|
||||||
|
'blossom', 'blow', 'blue', 'blur', 'blush', 'board', 'boat', 'body',
|
||||||
|
'boil', 'bomb', 'bone', 'bonus', 'book', 'boost', 'border', 'boring',
|
||||||
|
'borrow', 'boss', 'bottom', 'bounce', 'box', 'boy', 'bracket', 'brain',
|
||||||
|
'brand', 'brass', 'brave', 'bread', 'breeze', 'brick', 'bridge', 'brief',
|
||||||
|
'bright', 'bring', 'brisk', 'broccoli', 'broken', 'bronze', 'broom', 'brother',
|
||||||
|
'brown', 'brush', 'bubble', 'buddy', 'budget', 'buffalo', 'build', 'bulb',
|
||||||
|
'bulk', 'bullet', 'bundle', 'bunker', 'burden', 'burger', 'burst', 'bus',
|
||||||
|
'business', 'busy', 'butter', 'buyer', 'buzz', 'cabbage', 'cabin', 'cable',
|
||||||
|
'cactus', 'cage', 'cake', 'call', 'calm', 'camera', 'camp', 'can',
|
||||||
|
'canal', 'cancel', 'candy', 'cannon', 'canoe', 'canvas', 'canyon', 'capable',
|
||||||
|
'capital', 'captain', 'car', 'carbon', 'card', 'care', 'career', 'careful',
|
||||||
|
'careless', 'cargo', 'carpet', 'carry', 'cart', 'case', 'cash', 'casino',
|
||||||
|
'cast', 'casual', 'cat', 'catch', 'category', 'cattle', 'caught', 'cause',
|
||||||
|
'caution', 'cave', 'ceiling', 'celery', 'cement', 'census', 'century', 'cereal',
|
||||||
|
'certain', 'chair', 'chalk', 'champion', 'change', 'chaos', 'chapter', 'charge',
|
||||||
|
'chase', 'cheap', 'check', 'cheese', 'chef', 'cherry', 'chest', 'chicken',
|
||||||
|
'chief', 'child', 'chimney', 'choice', 'choose', 'chronic', 'chuckle', 'chunk',
|
||||||
|
'churn', 'cigar', 'cinnamon', 'circle', 'citizen', 'city', 'civil', 'claim',
|
||||||
|
'clamp', 'clarify', 'claw', 'clay', 'clean', 'clerk', 'clever', 'click',
|
||||||
|
'client', 'cliff', 'climb', 'cling', 'clinic', 'clip', 'clock', 'clog',
|
||||||
|
'close', 'cloth', 'cloud', 'clown', 'club', 'clump', 'cluster', 'clutch',
|
||||||
|
'coach', 'coast', 'coconut', 'code', 'coffee', 'coil', 'coin', 'collect',
|
||||||
|
'color', 'column', 'come', 'comfort', 'comic', 'common', 'company', 'concert',
|
||||||
|
'conduct', 'confirm', 'congress', 'connect', 'consider', 'control', 'convince', 'cook',
|
||||||
|
'cool', 'copper', 'copy', 'coral', 'core', 'corn', 'correct', 'cost',
|
||||||
|
'cotton', 'couch', 'country', 'couple', 'course', 'cousin', 'cover', 'coyote',
|
||||||
|
'crack', 'cradle', 'craft', 'cram', 'crane', 'crash', 'crater', 'crawl',
|
||||||
|
'crazy', 'cream', 'credit', 'creek', 'crew', 'cricket', 'crime', 'crisp',
|
||||||
|
'critic', 'crop', 'cross', 'crouch', 'crowd', 'crucial', 'cruel', 'cruise',
|
||||||
|
'crumble', 'crunch', 'crush', 'cry', 'crystal', 'cube', 'culture', 'cup',
|
||||||
|
'cupboard', 'curious', 'current', 'curtain', 'curve', 'cushion', 'custom', 'cute',
|
||||||
|
'cycle', 'dad', 'damage', 'damp', 'dance', 'danger', 'daring', 'dash',
|
||||||
|
'daughter', 'dawn', 'day', 'deal', 'debate', 'debris', 'decade', 'december',
|
||||||
|
'decide', 'decline', 'decorate', 'decrease', 'deer', 'defense', 'define', 'defy',
|
||||||
|
'degree', 'delay', 'deliver', 'demand', 'demise', 'denial', 'dentist', 'deny',
|
||||||
|
'depart', 'depend', 'deposit', 'depth', 'deputy', 'derive', 'describe', 'desert',
|
||||||
|
'design', 'desk', 'despair', 'destroy', 'detail', 'detect', 'develop', 'device',
|
||||||
|
'devote', 'diagram', 'dial', 'diamond', 'diary', 'dice', 'diesel', 'diet',
|
||||||
|
'differ', 'digital', 'dignity', 'dilemma', 'dinner', 'dinosaur', 'direct', 'dirt',
|
||||||
|
'disagree', 'discover', 'disease', 'dish', 'dismiss', 'disorder', 'display', 'distance',
|
||||||
|
'divert', 'divide', 'divorce', 'dizzy', 'doctor', 'document', 'dog', 'doll',
|
||||||
|
'dolphin', 'domain', 'donate', 'donkey', 'donor', 'door', 'dose', 'double',
|
||||||
|
'dove', 'draft', 'dragon', 'drama', 'drastic', 'draw', 'dream', 'dress',
|
||||||
|
'drift', 'drill', 'drink', 'drip', 'drive', 'drop', 'drum', 'dry',
|
||||||
|
'duck', 'dumb', 'dune', 'during', 'dust', 'dutch', 'duty', 'dwarf',
|
||||||
|
'dynamic', 'eager', 'eagle', 'early', 'earn', 'earth', 'easily', 'east',
|
||||||
|
'easy', 'echo', 'ecology', 'economy', 'edge', 'edit', 'educate', 'effort',
|
||||||
|
'egg', 'eight', 'either', 'elbow', 'elder', 'electric', 'elegant', 'element',
|
||||||
|
'elephant', 'elevator', 'elite', 'else', 'embark', 'embody', 'embrace', 'emerge',
|
||||||
|
'emotion', 'employ', 'empower', 'empty', 'enable', 'enact', 'end', 'endless',
|
||||||
|
'endorse', 'enemy', 'energy', 'enforce', 'engage', 'engine', 'enjoy', 'enlist',
|
||||||
|
'enough', 'enrich', 'enroll', 'ensure', 'enter', 'entire', 'entry', 'envelope',
|
||||||
|
'episode', 'equal', 'equip', 'era', 'erase', 'erode', 'erosion', 'erupt',
|
||||||
|
'escape', 'essay', 'essence', 'estate', 'eternal', 'ethics', 'evidence', 'evil',
|
||||||
|
'evoke', 'evolve', 'exact', 'example', 'excess', 'exchange', 'excite', 'exclude',
|
||||||
|
'excuse', 'execute', 'exercise', 'exhaust', 'exhibit', 'exile', 'exist', 'exit',
|
||||||
|
'exotic', 'expand', 'expect', 'expire', 'explain', 'expose', 'express', 'extend',
|
||||||
|
'extra', 'eye', 'eyebrow', 'fabric', 'face', 'faculty', 'fade', 'faint',
|
||||||
|
'faith', 'fall', 'false', 'fame', 'family', 'famous', 'fan', 'fancy',
|
||||||
|
'fantasy', 'farm', 'fashion', 'fat', 'fatal', 'father', 'fatigue', 'fault',
|
||||||
|
'favorite', 'feature', 'february', 'federal', 'fee', 'feed', 'feel', 'female',
|
||||||
|
'fence', 'festival', 'fetch', 'fever', 'few', 'fiber', 'fiction', 'field',
|
||||||
|
'figure', 'file', 'film', 'filter', 'final', 'find', 'fine', 'finger',
|
||||||
|
'finish', 'fire', 'firm', 'first', 'fiscal', 'fish', 'five', 'flag',
|
||||||
|
'flame', 'flash', 'flat', 'flavor', 'flee', 'flight', 'flip', 'float',
|
||||||
|
'flock', 'floor', 'flower', 'fluid', 'flush', 'fly', 'foam', 'focus',
|
||||||
|
'fog', 'foil', 'fold', 'follow', 'food', 'foot', 'force', 'forest',
|
||||||
|
'forget', 'fork', 'fortune', 'forum', 'forward', 'fossil', 'foster', 'found',
|
||||||
|
'fox', 'fragile', 'frame', 'frequent', 'fresh', 'friend', 'fringe', 'frog',
|
||||||
|
'front', 'frost', 'frown', 'frozen', 'fruit', 'fuel', 'fun', 'funny',
|
||||||
|
'furnace', 'fury', 'future', 'gadget', 'gain', 'galaxy', 'gallery', 'game',
|
||||||
|
'gap', 'garage', 'garbage', 'garden', 'garlic', 'garment', 'gas', 'gasp',
|
||||||
|
'gate', 'gather', 'gauge', 'gaze', 'general', 'genius', 'genre', 'gentle',
|
||||||
|
'genuine', 'gesture', 'ghost', 'giant', 'gift', 'giggle', 'ginger', 'giraffe',
|
||||||
|
'girl', 'give', 'glad', 'glance', 'glare', 'glass', 'glide', 'glimpse',
|
||||||
|
'globe', 'gloom', 'glory', 'glove', 'glow', 'glue', 'goat', 'goddess',
|
||||||
|
'gold', 'good', 'goose', 'gorilla', 'gospel', 'gossip', 'govern', 'gown',
|
||||||
|
'grab', 'grace', 'grain', 'grant', 'grape', 'grass', 'gravity', 'great',
|
||||||
|
'green', 'grid', 'grief', 'grit', 'grocery', 'group', 'grow', 'grunt',
|
||||||
|
'guard', 'guess', 'guide', 'guilt', 'guitar', 'gun', 'gym', 'habit',
|
||||||
|
'hair', 'half', 'hammer', 'hamster', 'hand', 'happy', 'harbor', 'hard',
|
||||||
|
'harsh', 'harvest', 'hash', 'hate', 'have', 'hawk', 'hazard', 'head',
|
||||||
|
'health', 'heart', 'heavy', 'hedgehog', 'height', 'hello', 'helmet', 'help',
|
||||||
|
'hen', 'hero', 'hidden', 'high', 'hill', 'hint', 'hip', 'hire',
|
||||||
|
'history', 'hobby', 'hockey', 'hold', 'hole', 'holiday', 'hollow', 'home',
|
||||||
|
'honey', 'hood', 'hope', 'horn', 'horror', 'horse', 'hospital', 'host',
|
||||||
|
'hotel', 'hour', 'hover', 'hub', 'huge', 'human', 'humble', 'humor',
|
||||||
|
'hundred', 'hungry', 'hunt', 'hurdle', 'hurry', 'hurt', 'husband', 'hybrid',
|
||||||
|
'ice', 'icon', 'idea', 'identify', 'idle', 'ignore', 'ill', 'illegal',
|
||||||
|
'illness', 'image', 'imitate', 'immense', 'immune', 'impact', 'impose', 'improve',
|
||||||
|
'impulse', 'inch', 'include', 'income', 'increase', 'index', 'indicate', 'indoor',
|
||||||
|
'industry', 'infant', 'inflict', 'inform', 'inhale', 'inherit', 'initial', 'inject',
|
||||||
|
'injury', 'inmate', 'inner', 'innocent', 'input', 'inquiry', 'insane', 'insect',
|
||||||
|
'inside', 'inspire', 'install', 'intact', 'interest', 'into', 'invest', 'invite',
|
||||||
|
'involve', 'iron', 'island', 'isolate', 'issue', 'item', 'ivory', 'jacket',
|
||||||
|
'jaguar', 'jar', 'jazz', 'jealous', 'jeans', 'jelly', 'jewel', 'job',
|
||||||
|
'join', 'joke', 'journey', 'joy', 'judge', 'juice', 'jump', 'jungle',
|
||||||
|
'junior', 'junk', 'just', 'kangaroo', 'keen', 'keep', 'ketchup', 'key',
|
||||||
|
'kick', 'kid', 'kidney', 'kind', 'kingdom', 'kiss', 'kit', 'kitchen',
|
||||||
|
'kite', 'kitten', 'kiwi', 'knee', 'knife', 'knock', 'know', 'lab',
|
||||||
|
'label', 'labor', 'ladder', 'lady', 'lake', 'lamp', 'land', 'landscape',
|
||||||
|
'lane', 'language', 'laptop', 'large', 'later', 'latin', 'laugh', 'laundry',
|
||||||
|
'lava', 'law', 'lawn', 'lawsuit', 'layer', 'lazy', 'leader', 'leaf',
|
||||||
|
'learn', 'leave', 'lecture', 'left', 'leg', 'legal', 'legend', 'leisure',
|
||||||
|
'lemon', 'lend', 'length', 'lens', 'leopard', 'lesson', 'letter', 'level',
|
||||||
|
'liar', 'liberty', 'library', 'license', 'life', 'lift', 'light', 'like',
|
||||||
|
'limb', 'limit', 'link', 'lion', 'liquid', 'list', 'little', 'live',
|
||||||
|
'lizard', 'load', 'loan', 'lobster', 'local', 'lock', 'logic', 'lonely',
|
||||||
|
'long', 'loop', 'lottery', 'loud', 'lounge', 'love', 'loyal', 'lucky',
|
||||||
|
'luggage', 'lumber', 'lunar', 'lunch', 'luxury', 'lyrics', 'machine', 'mad',
|
||||||
|
'magic', 'magnet', 'maid', 'mail', 'main', 'major', 'make', 'mammal',
|
||||||
|
'man', 'manage', 'mandate', 'mango', 'mansion', 'manual', 'maple', 'marble',
|
||||||
|
'march', 'margin', 'marine', 'market', 'marriage', 'mask', 'mass', 'master',
|
||||||
|
'match', 'material', 'math', 'matrix', 'matter', 'maximum', 'maze', 'meadow',
|
||||||
|
'mean', 'measure', 'meat', 'mechanic', 'medal', 'media', 'melody', 'melt',
|
||||||
|
'member', 'memory', 'mention', 'menu', 'mercy', 'merge', 'merit', 'merry',
|
||||||
|
'mesh', 'message', 'metal', 'method', 'middle', 'midnight', 'milk', 'million',
|
||||||
|
'mimic', 'mind', 'minimum', 'minor', 'minute', 'miracle', 'mirror', 'misery',
|
||||||
|
'miss', 'mistake', 'mix', 'mixed', 'mixture', 'mobile', 'model', 'modify',
|
||||||
|
'mom', 'moment', 'monitor', 'monkey', 'monster', 'month', 'moon', 'moral',
|
||||||
|
'more', 'morning', 'mosquito', 'mother', 'motion', 'motor', 'mountain', 'mouse',
|
||||||
|
'move', 'movie', 'much', 'muffin', 'mule', 'multiply', 'muscle', 'museum',
|
||||||
|
'mushroom', 'music', 'must', 'mutual', 'myself', 'mystery', 'naive', 'name',
|
||||||
|
'napkin', 'narrow', 'nasty', 'nation', 'nature', 'near', 'neck', 'need',
|
||||||
|
'negative', 'neglect', 'neither', 'nephew', 'nerve', 'nest', 'net', 'network',
|
||||||
|
'neutral', 'never', 'news', 'next', 'nice', 'night', 'noble', 'noise',
|
||||||
|
'nominee', 'noodle', 'normal', 'north', 'nose', 'notable', 'note', 'nothing',
|
||||||
|
'notice', 'novel', 'now', 'nuclear', 'number', 'nurse', 'nut', 'oak',
|
||||||
|
'obey', 'object', 'oblige', 'obscure', 'observe', 'obtain', 'obvious', 'occur',
|
||||||
|
'ocean', 'october', 'odor', 'off', 'offer', 'office', 'often', 'oil',
|
||||||
|
'okay', 'old', 'olive', 'olympic', 'omit', 'once', 'one', 'onion',
|
||||||
|
'online', 'only', 'open', 'opera', 'opinion', 'oppose', 'option', 'orange',
|
||||||
|
'orbit', 'orchard', 'order', 'ordinary', 'organ', 'orient', 'original', 'orphan',
|
||||||
|
'ostrich', 'other', 'our', 'ours', 'ourselves', 'out', 'outdoor', 'outer',
|
||||||
|
'outfit', 'outgoing', 'outline', 'outlook', 'output', 'outrage', 'outset', 'outside',
|
||||||
|
'outstanding', 'outward', 'oval', 'oven', 'over', 'own', 'owner', 'oxygen',
|
||||||
|
'oyster', 'ozone', 'pact', 'paddle', 'page', 'pair', 'palace', 'palm',
|
||||||
|
'panda', 'panel', 'panic', 'panther', 'paper', 'parade', 'parent', 'park',
|
||||||
|
'parrot', 'party', 'pass', 'patch', 'path', 'patient', 'patrol', 'pattern',
|
||||||
|
'pause', 'pave', 'payment', 'peace', 'peanut', 'pear', 'peasant', 'pelican',
|
||||||
|
'pen', 'penalty', 'pencil', 'people', 'pepper', 'perfect', 'permit', 'person',
|
||||||
|
'pet', 'phone', 'photo', 'phrase', 'physical', 'piano', 'picnic', 'picture',
|
||||||
|
'piece', 'pig', 'pigeon', 'pill', 'pilot', 'pink', 'pioneer', 'pipe',
|
||||||
|
'pistol', 'pitch', 'pizza', 'place', 'plate', 'play', 'please', 'pledge',
|
||||||
|
'pluck', 'plug', 'plunge', 'poem', 'poet', 'point', 'polar', 'pole',
|
||||||
|
'police', 'pond', 'pony', 'pool', 'poor', 'pop', 'popular', 'portion',
|
||||||
|
'position', 'possible', 'post', 'potato', 'pottery', 'poverty', 'powder', 'power',
|
||||||
|
'practice', 'praise', 'predict', 'prefer', 'prepare', 'present', 'pretty', 'prevent',
|
||||||
|
'price', 'pride', 'primary', 'print', 'priority', 'prison', 'private', 'prize',
|
||||||
|
'problem', 'process', 'produce', 'profit', 'program', 'project', 'promote', 'proof',
|
||||||
|
'property', 'prosper', 'protect', 'proud', 'provide', 'public', 'pudding', 'pull',
|
||||||
|
'pulp', 'pulse', 'pumpkin', 'punch', 'pupil', 'puppy', 'purchase', 'purity',
|
||||||
|
'purpose', 'purse', 'push', 'put', 'puzzle', 'pyramid', 'quality', 'quantum',
|
||||||
|
'quarter', 'question', 'quick', 'quit', 'quiz', 'quote', 'rabbit', 'raccoon',
|
||||||
|
'race', 'rack', 'radar', 'radio', 'rail', 'rain', 'raise', 'rally',
|
||||||
|
'ramp', 'ranch', 'random', 'range', 'rapid', 'rare', 'rate', 'rather',
|
||||||
|
'raven', 'raw', 'razor', 'ready', 'real', 'reason', 'rebel', 'rebuild',
|
||||||
|
'recall', 'receive', 'recipe', 'record', 'recover', 'recruit', 'red', 'reduce',
|
||||||
|
'reflect', 'reform', 'refuse', 'region', 'regret', 'regular', 'reject', 'relax',
|
||||||
|
'release', 'relief', 'rely', 'remain', 'remember', 'remind', 'remove', 'render',
|
||||||
|
'renew', 'rent', 'reopen', 'repair', 'repeat', 'replace', 'reply', 'report',
|
||||||
|
'require', 'rescue', 'resemble', 'resist', 'resource', 'response', 'result', 'retire',
|
||||||
|
'retreat', 'return', 'reveal', 'review', 'reward', 'rhythm', 'rib', 'ribbon',
|
||||||
|
'rice', 'rich', 'ride', 'ridge', 'rifle', 'right', 'rigid', 'ring',
|
||||||
|
'riot', 'ripple', 'risk', 'ritual', 'rival', 'river', 'road', 'roast',
|
||||||
|
'robot', 'robust', 'rocket', 'romance', 'roof', 'rookie', 'room', 'rose',
|
||||||
|
'rotate', 'rough', 'round', 'route', 'router', 'routine', 'row', 'royal',
|
||||||
|
'rubber', 'rude', 'rug', 'rule', 'run', 'runway', 'rural', 'rush',
|
||||||
|
'rust', 'sad', 'saddle', 'sadness', 'safe', 'sail', 'salad', 'salmon',
|
||||||
|
'salon', 'salt', 'salute', 'same', 'sample', 'sand', 'satisfy', 'satoshi',
|
||||||
|
'sauce', 'sausage', 'save', 'say', 'scale', 'scan', 'scare', 'scatter',
|
||||||
|
'scene', 'scheme', 'school', 'science', 'scissors', 'scorpion', 'scout', 'scrap',
|
||||||
|
'screen', 'script', 'scrub', 'sea', 'search', 'season', 'seat', 'second',
|
||||||
|
'secret', 'section', 'security', 'seed', 'seek', 'segment', 'select', 'sell',
|
||||||
|
'seminar', 'senior', 'sense', 'sentence', 'series', 'service', 'session', 'settle',
|
||||||
|
'setup', 'seven', 'shadow', 'shaft', 'shallow', 'share', 'shed', 'shell',
|
||||||
|
'sheriff', 'shield', 'shift', 'shine', 'ship', 'shiver', 'shock', 'shoe',
|
||||||
|
'shoot', 'shop', 'shore', 'short', 'shoulder', 'shove', 'shrimp', 'shrug',
|
||||||
|
'shuffle', 'shy', 'sibling', 'sick', 'side', 'siege', 'sight', 'sign',
|
||||||
|
'silent', 'silk', 'silly', 'silver', 'similar', 'simple', 'since', 'sing',
|
||||||
|
'siren', 'sister', 'situate', 'six', 'size', 'skate', 'sketch', 'ski',
|
||||||
|
'skill', 'skin', 'skirt', 'skull', 'slab', 'slam', 'sleep', 'slender',
|
||||||
|
'slice', 'slide', 'slight', 'slim', 'slogan', 'slot', 'slow', 'sluice',
|
||||||
|
'slump', 'slung', 'slunk', 'slurp', 'slush', 'sly', 'smack', 'small',
|
||||||
|
'smart', 'smash', 'smell', 'smile', 'smoke', 'smooth', 'smuggle', 'snack',
|
||||||
|
'snake', 'snap', 'snare', 'snarl', 'sneak', 'sneeze', 'sniff', 'snore',
|
||||||
|
'snort', 'snout', 'snow', 'snub', 'snuff', 'snug', 'soak', 'soap',
|
||||||
|
'soar', 'sob', 'soccer', 'social', 'sock', 'soda', 'sofa', 'soft',
|
||||||
|
'soggy', 'soil', 'solar', 'soldier', 'solid', 'solo', 'solve', 'some',
|
||||||
|
'son', 'song', 'soon', 'sore', 'sorrow', 'sorry', 'sort', 'soul',
|
||||||
|
'sound', 'soup', 'sour', 'south', 'space', 'spare', 'spark', 'sparse',
|
||||||
|
'spatial', 'spawn', 'speak', 'speed', 'spell', 'spend', 'sphere', 'spice',
|
||||||
|
'spider', 'spike', 'spin', 'spirit', 'split', 'spoil', 'sponsor', 'spoon',
|
||||||
|
'sport', 'spot', 'spouse', 'spray', 'spread', 'spring', 'spy', 'square',
|
||||||
|
'squeeze', 'squirrel', 'stable', 'stadium', 'staff', 'stage', 'stain', 'stair',
|
||||||
|
'stake', 'stale', 'stalk', 'stall', 'stamp', 'stand', 'start', 'state',
|
||||||
|
'stay', 'steak', 'steal', 'steam', 'steel', 'steep', 'steer', 'stem',
|
||||||
|
'step', 'stereo', 'stick', 'still', 'sting', 'stink', 'stir', 'stock',
|
||||||
|
'stomach', 'stone', 'stony', 'stool', 'stoop', 'stop', 'store', 'storm',
|
||||||
|
'story', 'stove', 'straddle', 'straight', 'strain', 'strand', 'strap', 'straw',
|
||||||
|
'stream', 'street', 'stress', 'stretch', 'strict', 'stride', 'strife', 'strike',
|
||||||
|
'string', 'strive', 'stroke', 'stroll', 'strong', 'struck', 'struggle', 'strum',
|
||||||
|
'strut', 'stuck', 'study', 'stuff', 'stump', 'stung', 'stunk', 'stunt',
|
||||||
|
'style', 'subdue', 'subject', 'submit', 'subway', 'succeed', 'success', 'such',
|
||||||
|
'sudden', 'suffer', 'sugar', 'suggest', 'suit', 'sulky', 'sullen', 'sultan',
|
||||||
|
'sum', 'summer', 'sun', 'sunny', 'sunset', 'super', 'supper', 'supply',
|
||||||
|
'supreme', 'sure', 'surface', 'surge', 'surprise', 'surround', 'survey', 'suspect',
|
||||||
|
'suspend', 'sustain', 'swallow', 'swamp', 'swap', 'swarm', 'sway', 'swear',
|
||||||
|
'sweat', 'sweep', 'sweet', 'swell', 'swim', 'swing', 'swirl', 'switch',
|
||||||
|
'sword', 'sworn', 'swung', 'syllable', 'symbol', 'symptom', 'syndicate', 'synergy',
|
||||||
|
'system', 'table', 'tackle', 'tag', 'tail', 'talent', 'talk', 'tall',
|
||||||
|
'tank', 'tape', 'target', 'task', 'taste', 'tattoo', 'taxi', 'teach',
|
||||||
|
'team', 'tell', 'ten', 'tenant', 'tennis', 'tent', 'term', 'test',
|
||||||
|
'text', 'thank', 'that', 'the', 'their', 'them', 'theme', 'then',
|
||||||
|
'theory', 'there', 'they', 'thing', 'think', 'third', 'this', 'those',
|
||||||
|
'though', 'thought', 'thousand', 'thread', 'threat', 'three', 'thrive', 'throw',
|
||||||
|
'thumb', 'thump', 'thunder', 'thus', 'tick', 'tide', 'tidy', 'tie',
|
||||||
|
'tiger', 'tight', 'tile', 'till', 'tilt', 'timber', 'time', 'tiny',
|
||||||
|
'tip', 'tire', 'tired', 'tissue', 'title', 'to', 'toast', 'today',
|
||||||
|
'toddler', 'toe', 'together', 'toilet', 'token', 'tomato', 'tomorrow', 'tone',
|
||||||
|
'tongue', 'tonight', 'too', 'tool', 'tooth', 'top', 'topic', 'topple',
|
||||||
|
'torch', 'tornado', 'tortoise', 'toss', 'total', 'touch', 'tough', 'tour',
|
||||||
|
'toward', 'tower', 'town', 'toy', 'track', 'trade', 'traffic', 'tragic',
|
||||||
|
'train', 'transfer', 'trap', 'trash', 'travel', 'tray', 'treat', 'tree',
|
||||||
|
'trend', 'trial', 'tribe', 'trick', 'trigger', 'trim', 'trip', 'trophy',
|
||||||
|
'trouble', 'truck', 'true', 'truly', 'trumpet', 'trust', 'truth', 'try',
|
||||||
|
'tube', 'tuck', 'tuesday', 'tug', 'tuition', 'tumble', 'tuna', 'tunnel',
|
||||||
|
'turbo', 'turtle', 'twelve', 'twenty', 'twice', 'twin', 'twist', 'two',
|
||||||
|
'type', 'typical', 'ugly', 'umbrella', 'unable', 'unaware', 'uncle', 'uncover',
|
||||||
|
'under', 'undo', 'unfair', 'unfold', 'unhappy', 'unhealthy', 'university', 'unkind',
|
||||||
|
'unknown', 'unlock', 'until', 'unusual', 'unveil', 'update', 'upgrade', 'uphold',
|
||||||
|
'upon', 'upper', 'upright', 'upset', 'urban', 'urge', 'usage', 'use',
|
||||||
|
'used', 'useful', 'useless', 'usual', 'utility', 'vacant', 'vacuum', 'vague',
|
||||||
|
'valiant', 'valid', 'valley', 'valve', 'van', 'vanish', 'vapor', 'various',
|
||||||
|
'vast', 'vault', 'vehicle', 'velvet', 'vendor', 'venom', 'venture', 'venue',
|
||||||
|
'verb', 'verify', 'version', 'very', 'vessel', 'veteran', 'viable', 'vibrant',
|
||||||
|
'vicious', 'victory', 'video', 'view', 'village', 'vintage', 'violin', 'virtual',
|
||||||
|
'virus', 'visa', 'visit', 'visual', 'vital', 'vivid', 'vocal', 'voice',
|
||||||
|
'void', 'volcano', 'volume', 'vote', 'voyage', 'wage', 'wagon', 'wait',
|
||||||
|
'wake', 'walk', 'wall', 'walnut', 'want', 'war', 'warm', 'warn',
|
||||||
|
'wash', 'wasp', 'waste', 'water', 'wave', 'way', 'weak', 'wealth',
|
||||||
|
'weapon', 'wear', 'weasel', 'weather', 'web', 'wedding', 'weed', 'week',
|
||||||
|
'weird', 'welcome', 'west', 'wet', 'whale', 'what', 'wheat', 'wheel',
|
||||||
|
'when', 'where', 'whip', 'whisper', 'white', 'who', 'whole', 'whom',
|
||||||
|
'whose', 'why', 'wicked', 'wide', 'widow', 'width', 'wife', 'wild',
|
||||||
|
'will', 'win', 'window', 'wine', 'wing', 'wink', 'winner', 'winter',
|
||||||
|
'wire', 'wisdom', 'wise', 'wish', 'witness', 'wolf', 'woman', 'wonder',
|
||||||
|
'wood', 'wool', 'word', 'work', 'world', 'worry', 'worth', 'would',
|
||||||
|
'wound', 'wrap', 'wreck', 'wrestle', 'wrist', 'write', 'wrong', 'yard',
|
||||||
|
'year', 'yellow', 'you', 'young', 'youth', 'zebra', 'zero', 'zone',
|
||||||
|
'zoo'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Convert bytes to words (2 bytes per word)
|
||||||
|
const words = [];
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
const byte1 = bytes[i * 2] || 0;
|
||||||
|
const byte2 = bytes[i * 2 + 1] || 0;
|
||||||
|
const wordIndex = (byte1 << 8) | byte2;
|
||||||
|
words.push(wordList[wordIndex % wordList.length]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return words.join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
//Get emojis from other device
|
//Get emojis from other device
|
||||||
async function emojisPairingRequest() {
|
async function emojisPairingRequest() {
|
||||||
try {
|
try {
|
||||||
@ -98,75 +399,384 @@ export async function displayEmojis(text: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify Other address
|
// Initialize the pairing interface
|
||||||
export function initAddressInput() {
|
export function initAddressInput() {
|
||||||
const container = getCorrectDOM('login-4nk-component') as HTMLElement
|
const container = getCorrectDOM('login-4nk-component') as HTMLElement;
|
||||||
const addressInput = container.querySelector('#addressInput') as HTMLInputElement;
|
|
||||||
const emojiDisplay = container.querySelector('#emoji-display-2');
|
// Show loading state initially
|
||||||
const okButton = container.querySelector('#okButton') as HTMLButtonElement;
|
showLoadingState();
|
||||||
|
|
||||||
|
// Auto-detect flow after a short delay
|
||||||
|
setTimeout(() => {
|
||||||
|
detectAndShowFlow();
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
// Initialize event listeners
|
||||||
|
initEventListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show loading state
|
||||||
|
function showLoadingState() {
|
||||||
|
const container = getCorrectDOM('login-4nk-component') as HTMLElement;
|
||||||
|
const loadingFlow = container.querySelector('#loading-flow');
|
||||||
|
const creatorFlow = container.querySelector('#creator-flow');
|
||||||
|
const joinerFlow = container.querySelector('#joiner-flow');
|
||||||
|
|
||||||
|
if (loadingFlow) loadingFlow.style.display = 'block';
|
||||||
|
if (creatorFlow) creatorFlow.style.display = 'none';
|
||||||
|
if (joinerFlow) joinerFlow.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect flow and show appropriate interface
|
||||||
|
async function detectAndShowFlow() {
|
||||||
|
const container = getCorrectDOM('login-4nk-component') as HTMLElement;
|
||||||
|
const loadingFlow = container.querySelector('#loading-flow');
|
||||||
|
const creatorFlow = container.querySelector('#creator-flow');
|
||||||
|
const joinerFlow = container.querySelector('#joiner-flow');
|
||||||
|
|
||||||
|
// Check if we have words input (joiner flow)
|
||||||
|
const wordsInput = container.querySelector('#wordsInput') as HTMLInputElement;
|
||||||
|
const hasWords = wordsInput && wordsInput.value.trim().length > 0;
|
||||||
|
|
||||||
|
if (hasWords) {
|
||||||
|
// Joiner flow
|
||||||
|
if (loadingFlow) loadingFlow.style.display = 'none';
|
||||||
|
if (creatorFlow) creatorFlow.style.display = 'none';
|
||||||
|
if (joinerFlow) joinerFlow.style.display = 'block';
|
||||||
|
|
||||||
|
updateJoinerStatus('Ready to join pairing');
|
||||||
|
} else {
|
||||||
|
// Creator flow
|
||||||
|
if (loadingFlow) loadingFlow.style.display = 'none';
|
||||||
|
if (creatorFlow) creatorFlow.style.display = 'block';
|
||||||
|
if (joinerFlow) joinerFlow.style.display = 'none';
|
||||||
|
|
||||||
|
updateCreatorStatus('Ready to create pairing');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize event listeners
|
||||||
|
function initEventListeners() {
|
||||||
|
const container = getCorrectDOM('login-4nk-component') as HTMLElement;
|
||||||
|
|
||||||
|
// Creator button
|
||||||
const createButton = container.querySelector('#createButton') as HTMLButtonElement;
|
const createButton = container.querySelector('#createButton') as HTMLButtonElement;
|
||||||
const actionButton = container.querySelector('#actionButton') as HTMLButtonElement;
|
|
||||||
addSubscription(addressInput, 'input', async () => {
|
|
||||||
let address = addressInput.value;
|
|
||||||
|
|
||||||
// Vérifie si l'adresse est une URL
|
|
||||||
try {
|
|
||||||
const url = new URL(address);
|
|
||||||
// Si c'est une URL valide, extraire le paramètre sp_address
|
|
||||||
const urlParams = new URLSearchParams(url.search);
|
|
||||||
const extractedAddress = urlParams.get('sp_address') || ''; // Prend sp_address ou une chaîne vide
|
|
||||||
|
|
||||||
if (extractedAddress) {
|
|
||||||
address = extractedAddress;
|
|
||||||
addressInput.value = address; // Met à jour l'input pour afficher uniquement l'adresse extraite
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
// Si ce n'est pas une URL valide, on garde l'adresse originale
|
|
||||||
console.log("Ce n'est pas une URL valide, on garde l'adresse originale.");
|
|
||||||
}
|
|
||||||
if (address) {
|
|
||||||
const emojis = await addressToEmoji(address);
|
|
||||||
if (emojiDisplay) {
|
|
||||||
emojiDisplay.innerHTML = emojis;
|
|
||||||
}
|
|
||||||
if (okButton) {
|
|
||||||
okButton.style.display = 'inline-block';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (emojiDisplay) {
|
|
||||||
emojiDisplay.innerHTML = '';
|
|
||||||
}
|
|
||||||
if (okButton) {
|
|
||||||
okButton.style.display = 'none';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (createButton) {
|
if (createButton) {
|
||||||
addSubscription(createButton, 'click', () => {
|
addSubscription(createButton, 'click', () => {
|
||||||
onCreateButtonClick();
|
onCreateButtonClick();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Joiner button
|
||||||
|
const joinButton = container.querySelector('#joinButton') as HTMLButtonElement;
|
||||||
|
if (joinButton) {
|
||||||
|
addSubscription(joinButton, 'click', () => {
|
||||||
|
onJoinButtonClick();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy words button
|
||||||
|
const copyWordsBtn = container.querySelector('#copyWordsBtn') as HTMLButtonElement;
|
||||||
|
if (copyWordsBtn) {
|
||||||
|
addSubscription(copyWordsBtn, 'click', () => {
|
||||||
|
copyWordsToClipboard();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Words input for joiner
|
||||||
|
const wordsInput = container.querySelector('#wordsInput') as HTMLInputElement;
|
||||||
|
if (wordsInput) {
|
||||||
|
addSubscription(wordsInput, 'input', () => {
|
||||||
|
handleWordsInput();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle words input for joiner
|
||||||
|
function handleWordsInput() {
|
||||||
|
const container = getCorrectDOM('login-4nk-component') as HTMLElement;
|
||||||
|
const wordsInput = container.querySelector('#wordsInput') as HTMLInputElement;
|
||||||
|
const joinButton = container.querySelector('#joinButton') as HTMLButtonElement;
|
||||||
|
const wordsDisplay = container.querySelector('#words-display-2');
|
||||||
|
|
||||||
|
const words = wordsInput.value.trim();
|
||||||
|
const wordCount = words.split(' ').filter(w => w.length > 0).length;
|
||||||
|
|
||||||
|
if (wordsDisplay) {
|
||||||
|
if (words.length === 0) {
|
||||||
|
wordsDisplay.innerHTML = '';
|
||||||
|
} else if (wordCount < 4) {
|
||||||
|
wordsDisplay.innerHTML = `<div class="input-feedback">Enter ${4 - wordCount} more word(s)</div>`;
|
||||||
|
} else if (wordCount === 4) {
|
||||||
|
wordsDisplay.innerHTML = `<div class="input-feedback success">✓ 4 words entered</div>`;
|
||||||
|
} else {
|
||||||
|
wordsDisplay.innerHTML = `<div class="input-feedback warning">Too many words (${wordCount}/4)</div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (joinButton) {
|
||||||
|
joinButton.disabled = wordCount !== 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update creator status
|
||||||
|
function updateCreatorStatus(message: string, isError: boolean = false) {
|
||||||
|
const container = getCorrectDOM('login-4nk-component') as HTMLElement;
|
||||||
|
const statusElement = container.querySelector('#creator-status');
|
||||||
|
if (statusElement) {
|
||||||
|
statusElement.innerHTML = `
|
||||||
|
<div class="spinner ${isError ? 'error' : ''}"></div>
|
||||||
|
<span class="${isError ? 'error' : ''}">${message}</span>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update joiner status
|
||||||
|
function updateJoinerStatus(message: string, isError: boolean = false) {
|
||||||
|
const container = getCorrectDOM('login-4nk-component') as HTMLElement;
|
||||||
|
const statusElement = container.querySelector('#joiner-status');
|
||||||
|
if (statusElement) {
|
||||||
|
statusElement.innerHTML = `
|
||||||
|
<span class="${isError ? 'error' : ''}">${message}</span>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy words to clipboard
|
||||||
|
async function copyWordsToClipboard() {
|
||||||
|
const container = getCorrectDOM('login-4nk-component') as HTMLElement;
|
||||||
|
const wordsElement = container.querySelector('#creator-words');
|
||||||
|
|
||||||
|
if (wordsElement && wordsElement.textContent) {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(wordsElement.textContent);
|
||||||
|
updateCreatorStatus('Words copied to clipboard!');
|
||||||
|
} catch (err) {
|
||||||
|
updateCreatorStatus('Failed to copy words', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle join button click
|
||||||
|
async function onJoinButtonClick() {
|
||||||
|
const container = getCorrectDOM('login-4nk-component') as HTMLElement;
|
||||||
|
const wordsInput = container.querySelector('#wordsInput') as HTMLInputElement;
|
||||||
|
const words = wordsInput?.value?.trim();
|
||||||
|
|
||||||
|
if (words) {
|
||||||
|
updateJoinerStatus('Joining pairing...');
|
||||||
|
await onCreateButtonClick();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onCreateButtonClick() {
|
async function onCreateButtonClick() {
|
||||||
try {
|
try {
|
||||||
await prepareAndSendPairingTx();
|
const container = getCorrectDOM('login-4nk-component') as HTMLElement;
|
||||||
const service = await Services.getInstance();
|
const wordsInput = container.querySelector('#wordsInput') as HTMLInputElement;
|
||||||
await service.confirmPairing();
|
const words = wordsInput?.value?.trim();
|
||||||
|
|
||||||
|
if (words) {
|
||||||
|
// Joiner flow: Use 4 words to discover and join existing pairing process
|
||||||
|
console.log(`🔍 Joiner flow detected with words: ${words}`);
|
||||||
|
updateJoinerStatus('Discovering pairing process...');
|
||||||
|
|
||||||
|
await discoverAndJoinPairingProcessWithWords(words);
|
||||||
|
updateJoinerStatus('Pairing process found! Synchronizing...');
|
||||||
|
|
||||||
|
const service = await Services.getInstance();
|
||||||
|
const pairingId = service.getProcessId();
|
||||||
|
|
||||||
|
// Wait for pairing commitment with synchronization
|
||||||
|
updateJoinerStatus('Synchronizing with network...');
|
||||||
|
console.log("🔍 DEBUG: Joiner - About to call waitForPairingCommitment...");
|
||||||
|
await service.waitForPairingCommitment(pairingId);
|
||||||
|
console.log("✅ DEBUG: Joiner - waitForPairingCommitment completed!");
|
||||||
|
|
||||||
|
// Then confirm pairing
|
||||||
|
updateJoinerStatus('Confirming pairing...');
|
||||||
|
console.log("🔍 DEBUG: Joiner - About to call confirmPairing...");
|
||||||
|
await service.confirmPairing();
|
||||||
|
console.log("✅ DEBUG: Joiner - confirmPairing completed!");
|
||||||
|
|
||||||
|
// Redirect to account page after successful pairing
|
||||||
|
updateJoinerStatus('✅ Pairing successful! Redirecting...');
|
||||||
|
console.log("🔍 DEBUG: Redirecting to account page...");
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.href = '/account';
|
||||||
|
}, 2000);
|
||||||
|
} else {
|
||||||
|
// Creator flow: Create new pairing process
|
||||||
|
console.log(`🔍 Creator flow detected`);
|
||||||
|
updateCreatorStatus('Creating pairing process...');
|
||||||
|
|
||||||
|
await prepareAndSendPairingTx();
|
||||||
|
updateCreatorStatus('Pairing process created! Generating 4 words...');
|
||||||
|
|
||||||
|
const service = await Services.getInstance();
|
||||||
|
const pairingId = service.getProcessId();
|
||||||
|
|
||||||
|
// Wait for pairing commitment with synchronization
|
||||||
|
updateCreatorStatus('Synchronizing with network...');
|
||||||
|
console.log("🔍 DEBUG: Creator - About to call waitForPairingCommitment...");
|
||||||
|
await service.waitForPairingCommitment(pairingId);
|
||||||
|
console.log("✅ DEBUG: Creator - waitForPairingCommitment completed!");
|
||||||
|
|
||||||
|
// Then confirm pairing
|
||||||
|
updateCreatorStatus('Confirming pairing...');
|
||||||
|
console.log("🔍 DEBUG: Creator - About to call confirmPairing...");
|
||||||
|
await service.confirmPairing();
|
||||||
|
console.log("✅ DEBUG: Creator - confirmPairing completed!");
|
||||||
|
|
||||||
|
// Redirect to account page after successful pairing
|
||||||
|
updateCreatorStatus('✅ Pairing successful! Redirecting...');
|
||||||
|
console.log("🔍 DEBUG: Redirecting to account page...");
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.href = '/account';
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(`onCreateButtonClick error: ${e}`);
|
console.error(`onCreateButtonClick error: ${e}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// New function for joiner to discover and join existing pairing process using 4 words
|
||||||
|
export async function discoverAndJoinPairingProcessWithWords(words: string): Promise<void> {
|
||||||
|
const service = await Services.getInstance();
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log(`🔍 DEBUG: Joiner discovering pairing process with words: ${words}`);
|
||||||
|
|
||||||
|
// Wait for the pairing process to be available in the network
|
||||||
|
const maxRetries = 20;
|
||||||
|
const retryDelay = 3000; // 3 seconds
|
||||||
|
|
||||||
|
for (let i = 0; i < maxRetries; i++) {
|
||||||
|
try {
|
||||||
|
console.log(`🔍 Attempt ${i + 1}/${maxRetries}: Looking for pairing process...`);
|
||||||
|
|
||||||
|
// Try to get the pairing process ID from SDK
|
||||||
|
const pairingProcessId = service.getPairingProcessId();
|
||||||
|
if (pairingProcessId) {
|
||||||
|
console.log(`✅ Found pairing process: ${pairingProcessId}`);
|
||||||
|
|
||||||
|
// Get the process and extract paired addresses
|
||||||
|
const process = await service.getProcess(pairingProcessId);
|
||||||
|
if (process && process.states && process.states.length > 0) {
|
||||||
|
const lastState = process.states[process.states.length - 1];
|
||||||
|
const publicData = lastState.public_data;
|
||||||
|
|
||||||
|
if (publicData && publicData['pairedAddresses']) {
|
||||||
|
const pairedAddresses = service.decodeValue(publicData['pairedAddresses']);
|
||||||
|
console.log(`✅ Found paired addresses: ${JSON.stringify(pairedAddresses)}`);
|
||||||
|
|
||||||
|
// Set the process ID for the joiner
|
||||||
|
service.setProcessId(pairingProcessId);
|
||||||
|
service.setStateId(lastState.state_id);
|
||||||
|
|
||||||
|
// Check connections with the discovered addresses
|
||||||
|
await service.checkConnections(process);
|
||||||
|
console.log(`✅ Joiner successfully joined pairing process`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`⏳ Still waiting for pairing process... (${i + 1}/${maxRetries})`);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(`⚠️ Attempt ${i + 1}/${maxRetries}: ${e.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < maxRetries - 1) {
|
||||||
|
console.log(`⏳ Waiting ${retryDelay}ms before next attempt...`);
|
||||||
|
await new Promise(resolve => setTimeout(resolve, retryDelay));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`❌ Failed to discover pairing process after ${maxRetries} attempts`);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`❌ Joiner discovery failed:`, err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// New function for joiner to discover and join existing pairing process
|
||||||
|
export async function discoverAndJoinPairingProcess(creatorAddress: string): Promise<void> {
|
||||||
|
const service = await Services.getInstance();
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log(`🔍 DEBUG: Joiner discovering pairing process for creator: ${creatorAddress}`);
|
||||||
|
|
||||||
|
// Wait for the pairing process to be available in the network
|
||||||
|
const maxRetries = 20;
|
||||||
|
const retryDelay = 3000; // 3 seconds
|
||||||
|
|
||||||
|
for (let i = 0; i < maxRetries; i++) {
|
||||||
|
try {
|
||||||
|
console.log(`🔍 Attempt ${i + 1}/${maxRetries}: Looking for pairing process...`);
|
||||||
|
|
||||||
|
// Try to get the pairing process ID from SDK
|
||||||
|
const pairingProcessId = service.getPairingProcessId();
|
||||||
|
if (pairingProcessId) {
|
||||||
|
console.log(`✅ Found pairing process: ${pairingProcessId}`);
|
||||||
|
|
||||||
|
// Get the process and extract paired addresses
|
||||||
|
const process = await service.getProcess(pairingProcessId);
|
||||||
|
if (process && process.states && process.states.length > 0) {
|
||||||
|
const lastState = process.states[process.states.length - 1];
|
||||||
|
const publicData = lastState.public_data;
|
||||||
|
|
||||||
|
if (publicData && publicData['pairedAddresses']) {
|
||||||
|
const pairedAddresses = service.decodeValue(publicData['pairedAddresses']);
|
||||||
|
console.log(`✅ Found paired addresses: ${JSON.stringify(pairedAddresses)}`);
|
||||||
|
|
||||||
|
// Set the process ID for the joiner
|
||||||
|
service.setProcessId(pairingProcessId);
|
||||||
|
service.setStateId(lastState.state_id);
|
||||||
|
|
||||||
|
// Check connections with the discovered addresses
|
||||||
|
await service.checkConnections(process);
|
||||||
|
console.log(`✅ Joiner successfully joined pairing process`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`⏳ Still waiting for pairing process... (${i + 1}/${maxRetries})`);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(`⚠️ Attempt ${i + 1}/${maxRetries}: ${e.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < maxRetries - 1) {
|
||||||
|
console.log(`⏳ Waiting ${retryDelay}ms before next attempt...`);
|
||||||
|
await new Promise(resolve => setTimeout(resolve, retryDelay));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`❌ Failed to discover pairing process after ${maxRetries} attempts`);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`❌ Joiner discovery failed:`, err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function prepareAndSendPairingTx(): Promise<void> {
|
export async function prepareAndSendPairingTx(): Promise<void> {
|
||||||
const service = await Services.getInstance();
|
const service = await Services.getInstance();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const relayAddress = service.getAllRelays();
|
console.log(`🔍 DEBUG: Creator preparing pairing process...`);
|
||||||
|
|
||||||
|
// Get the creator's own address
|
||||||
|
const creatorAddress = service.getDeviceAddress();
|
||||||
|
if (!creatorAddress) {
|
||||||
|
throw new Error('Creator address not available');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`🔍 DEBUG: Creator address: ${creatorAddress}`);
|
||||||
|
|
||||||
|
// Create pairing process with creator's address
|
||||||
const createPairingProcessReturn = await service.createPairingProcess(
|
const createPairingProcessReturn = await service.createPairingProcess(
|
||||||
"",
|
creatorAddress, // Use creator's address as memberPublicName
|
||||||
[],
|
[creatorAddress], // Include creator's address in pairedAddresses
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!createPairingProcessReturn.updated_process) {
|
if (!createPairingProcessReturn.updated_process) {
|
||||||
@ -178,17 +788,98 @@ export async function prepareAndSendPairingTx(): Promise<void> {
|
|||||||
|
|
||||||
await service.handleApiReturn(createPairingProcessReturn);
|
await service.handleApiReturn(createPairingProcessReturn);
|
||||||
|
|
||||||
try {
|
// Generate 4 words representation for the joiner
|
||||||
await service.checkConnections(createPairingProcessReturn.updated_process.current_process);
|
console.log(`🔍 DEBUG: Generating 4 words representation for joiner...`);
|
||||||
} catch (e) {
|
await generateWordsDisplay(creatorAddress);
|
||||||
throw e;
|
|
||||||
}
|
console.log(`✅ DEBUG: Creator pairing process created and 4 words generated`);
|
||||||
|
console.log(`⏳ DEBUG: Creator waiting for joiner to enter 4 words...`);
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// New function for creator to wait for joiner and update process
|
||||||
|
export async function waitForJoinerAndUpdateProcess(): Promise<void> {
|
||||||
|
const service = await Services.getInstance();
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log(`🔍 DEBUG: Creator waiting for joiner to join...`);
|
||||||
|
|
||||||
|
const maxRetries = 30; // 30 attempts
|
||||||
|
const retryDelay = 2000; // 2 seconds
|
||||||
|
|
||||||
|
for (let i = 0; i < maxRetries; i++) {
|
||||||
|
try {
|
||||||
|
console.log(`🔍 Attempt ${i + 1}/${maxRetries}: Checking for joiner...`);
|
||||||
|
|
||||||
|
// Get current process
|
||||||
|
const processId = service.getProcessId();
|
||||||
|
if (!processId) {
|
||||||
|
console.log(`⚠️ No process ID available yet, waiting...`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const process = await service.getProcess(processId);
|
||||||
|
|
||||||
|
if (process && process.states && process.states.length > 0) {
|
||||||
|
const lastState = process.states[process.states.length - 1];
|
||||||
|
const publicData = lastState.public_data;
|
||||||
|
|
||||||
|
if (publicData && publicData['pairedAddresses']) {
|
||||||
|
const pairedAddresses = service.decodeValue(publicData['pairedAddresses']);
|
||||||
|
console.log(`🔍 Current paired addresses: ${JSON.stringify(pairedAddresses)}`);
|
||||||
|
|
||||||
|
// Check if we have more than just the creator's address
|
||||||
|
if (pairedAddresses && pairedAddresses.length > 1) {
|
||||||
|
console.log(`✅ Joiner detected! Found ${pairedAddresses.length} addresses`);
|
||||||
|
console.log(`✅ Creator process updated with joiner address`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`⏳ Still waiting for joiner... (${i + 1}/${maxRetries})`);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(`⚠️ Attempt ${i + 1}/${maxRetries}: ${e.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < maxRetries - 1) {
|
||||||
|
console.log(`⏳ Waiting ${retryDelay}ms before next attempt...`);
|
||||||
|
await new Promise(resolve => setTimeout(resolve, retryDelay));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`❌ No joiner detected after ${maxRetries} attempts`);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`❌ Creator waiting for joiner failed:`, err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function generateWordsDisplay(spAddress: string) {
|
||||||
|
try {
|
||||||
|
const container = getCorrectDOM('login-4nk-component') as HTMLElement;
|
||||||
|
const words = await addressToWords(spAddress);
|
||||||
|
|
||||||
|
// Update creator words display
|
||||||
|
const creatorWordsElement = container?.querySelector('#creator-words');
|
||||||
|
if (creatorWordsElement) {
|
||||||
|
creatorWordsElement.textContent = words;
|
||||||
|
creatorWordsElement.className = 'words-content active';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update status
|
||||||
|
updateCreatorStatus('4 words generated! Share them with the other device.');
|
||||||
|
|
||||||
|
console.log(`✅ Generated 4 words: ${words}`);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
updateCreatorStatus('Failed to generate words', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function generateQRCode(spAddress: string) {
|
export async function generateQRCode(spAddress: string) {
|
||||||
try {
|
try {
|
||||||
const container = getCorrectDOM('login-4nk-component') as HTMLElement
|
const container = getCorrectDOM('login-4nk-component') as HTMLElement
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user