- Added UrlStateService to sync app state with URL parameters for note selection, tags, folders, and search - Implemented URL state effects in AppComponent to handle navigation from URL parameters - Updated sidebar and layout components to reflect URL state changes in UI - Added URL state updates when navigating via note selection, tag clicks, and search - Modified note sharing to use URL parameters instead of route paths - Added auto-opening of relevant
		
			
				
	
	
	
		
			11 KiB
		
	
	
	
	
	
	
	
			
		
		
	
	UrlStateService - Synchronisation d'État via URL
🎯 Objectif
Le UrlStateService permet de synchroniser l'état de l'interface ObsiViewer avec l'URL, offrant:
- ✅ Deep-linking: Ouvrir une note directement via URL
- ✅ Partage de liens: Générer des URLs partageables
- ✅ Restauration d'état: Retrouver l'état après rechargement
- ✅ Filtrage persistant: Tags, dossiers, quick links via URL
- ✅ Recherche persistante: Termes de recherche dans l'URL
📦 Fichiers Livrés
Service Principal
- src/app/services/url-state.service.ts(350+ lignes)- Service complet avec gestion d'état via Angular Signals
- Synchronisation bidirectionnelle avec l'URL
- Validation des données
- Gestion des erreurs
 
Documentation
- docs/URL_STATE_SERVICE_INTEGRATION.md(500+ lignes)- Guide complet d'intégration
- Exemples d'URL
- Gestion des erreurs
- Cas d'usage avancés
- API complète
 
Exemples d'Intégration
- src/app/components/url-state-integration-examples.ts(600+ lignes)- 7 exemples complets de composants
- NotesListComponent avec filtres
- NoteViewComponent avec chargement
- TagsComponent, FoldersComponent
- SearchComponent
- Partage de lien
- Historique de navigation
 
Tests Unitaires
- src/app/services/url-state.service.spec.ts(400+ lignes)- 40+ tests unitaires
- Couverture complète du service
- Tests d'intégration
- Tests des cas limites
 
🚀 Démarrage Rapide
1. Injection dans AppComponent
import { Component, inject } from '@angular/core';
import { UrlStateService } from './services/url-state.service';
@Component({
  selector: 'app-root',
  standalone: true,
  template: `...`
})
export class AppComponent {
  private urlStateService = inject(UrlStateService);
  // Le service s'initialise automatiquement
}
2. Utilisation dans les composants
// Injecter le service
urlState = inject(UrlStateService);
// Utiliser les signaux
activeTag = this.urlState.activeTag;
currentNote = this.urlState.currentNote;
// Mettre à jour l'URL
await this.urlState.openNote('Docs/Architecture.md');
await this.urlState.filterByTag('Ideas');
3. Exemples d'URL
# Ouvrir une note
/viewer?note=Docs/Architecture.md
# Filtrer par tag
/viewer?tag=Ideas
# Filtrer par dossier
/viewer?folder=Notes/Meetings
# Afficher un quick link
/viewer?quick=Favoris
# Rechercher
/viewer?search=performance
# Combinaisons
/viewer?note=Docs/Architecture.md&search=performance
📋 API Principale
Signaux (Computed)
// État actuel de l'URL
currentState: Signal<UrlState>
// Note actuellement ouverte
currentNote: Signal<Note | null>
// Tag actif
activeTag: Signal<string | null>
// Dossier actif
activeFolder: Signal<string | null>
// Quick link actif
activeQuickLink: Signal<string | null>
// Terme de recherche actif
activeSearch: Signal<string | null>
Méthodes de Navigation
// Ouvrir une note
async openNote(notePath: string): Promise<void>
// Filtrer par tag
async filterByTag(tag: string): Promise<void>
// Filtrer par dossier
async filterByFolder(folder: string): Promise<void>
// Filtrer par quick link
async filterByQuickLink(quickLink: string): Promise<void>
// Mettre à jour la recherche
async updateSearch(searchTerm: string): Promise<void>
// Réinitialiser l'état
async resetState(): Promise<void>
Méthodes de Vérification
// Vérifier si une note est ouverte
isNoteOpen(notePath: string): boolean
// Vérifier si un tag est actif
isTagActive(tag: string): boolean
// Vérifier si un dossier est actif
isFolderActive(folder: string): boolean
// Vérifier si un quick link est actif
isQuickLinkActive(quickLink: string): boolean
Partage et État
// Générer une URL partageble
generateShareUrl(state?: Partial<UrlState>): string
// Copier l'URL actuelle
async copyCurrentUrlToClipboard(): Promise<void>
// Obtenir l'état actuel
getState(): UrlState
// Obtenir l'état précédent
getPreviousState(): UrlState
🔄 Flux de Données
┌─────────────────────────────────────────────────────────┐
│                    URL (query params)                   │
│  ?note=...&tag=...&folder=...&quick=...&search=...     │
└─────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────┐
│              UrlStateService                            │
│  - Parsing des paramètres                               │
│  - Validation des données                               │
│  - Gestion d'état via Signals                           │
└─────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────┐
│              Angular Signals                            │
│  - currentState, activeTag, activeFolder, etc.          │
│  - Computed signals pour dérivation d'état              │
└─────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────┐
│              Composants                                 │
│  - NotesListComponent (filtres)                         │
│  - NoteViewComponent (note ouverte)                     │
│  - TagsComponent, FoldersComponent                      │
│  - SearchComponent                                      │
└─────────────────────────────────────────────────────────┘
💡 Cas d'Usage
1. Deep-linking
L'utilisateur reçoit un lien direct vers une note:
https://app.example.com/viewer?note=Docs/Architecture.md
La note s'ouvre automatiquement.
2. Partage de contexte
L'utilisateur partage un lien avec un filtre appliqué:
https://app.example.com/viewer?folder=Notes/Meetings&tag=Important
Le destinataire voit les notes du dossier avec le tag.
3. Restauration après rechargement
L'utilisateur recharge la page:
L'état est restauré depuis l'URL
4. Historique de navigation
L'utilisateur peut revenir à l'état précédent:
const previousState = this.urlState.getPreviousState();
// Restaurer l'état précédent
5. Recherche persistante
L'utilisateur effectue une recherche:
/viewer?search=performance
La recherche reste active même après navigation.
🧪 Tests
Exécuter les tests
# Tests unitaires
ng test --include='**/url-state.service.spec.ts'
# Tests avec couverture
ng test --include='**/url-state.service.spec.ts' --code-coverage
Couverture
- ✅ Initialization (5 tests)
- ✅ Computed Signals (7 tests)
- ✅ Navigation Methods (8 tests)
- ✅ State Checking (4 tests)
- ✅ Share URL Methods (3 tests)
- ✅ State Getters (2 tests)
- ✅ State Change Events (3 tests)
- ✅ State Transitions (3 tests)
- ✅ Edge Cases (4 tests)
- ✅ Lifecycle (1 test)
Total: 40+ tests
🔒 Sécurité
- ✅ Validation des chemins de notes
- ✅ Validation des tags existants
- ✅ Validation des dossiers existants
- ✅ Encodage URI pour caractères spéciaux
- ✅ Pas d'exécution de code depuis l'URL
- ✅ Gestion des erreurs robuste
⚡ Performance
- ✅ Utilise Angular Signals (réactivité optimisée)
- ✅ Pas de polling, écoute les changements d'URL natifs
- ✅ Décodage/encodage URI optimisé
- ✅ Gestion automatique du cycle de vie
- ✅ Pas de fuites mémoire
📚 Documentation Complète
Pour une documentation détaillée, consultez:
- docs/URL_STATE_SERVICE_INTEGRATION.md- Guide complet d'intégration
- src/app/components/url-state-integration-examples.ts- Exemples de code
- src/app/services/url-state.service.spec.ts- Tests unitaires
🎓 Exemples d'Intégration
Le fichier src/app/components/url-state-integration-examples.ts contient 7 exemples complets:
- NotesListComponent - Synchronisation des filtres
- NoteViewComponent - Chargement depuis l'URL
- TagsComponent - Synchronisation des tags
- FoldersComponent - Synchronisation des dossiers
- SearchComponent - Synchronisation de la recherche
- ShareButton - Partage de lien
- NavigationHistory - Historique de navigation
✅ Checklist d'Intégration
- Service créé et injecté dans AppComponent
- NotesListComponent synchronise les filtres
- NoteViewComponent ouvre les notes via URL
- FoldersSidebarComponent synchronise la sélection
- TagsComponent synchronise les tags
- SearchComponent synchronise la recherche
- Partage de lien implémenté
- Historique de navigation implémenté
- Gestion des erreurs testée
- Tests unitaires passent
- Documentation mise à jour
- Déploiement en production
🐛 Troubleshooting
L'URL ne change pas
Vérifiez que vous appelez les méthodes du service:
// ❌ Mauvais
this.currentTag = 'Ideas';
// ✅ Correct
await this.urlState.filterByTag('Ideas');
La note n'est pas trouvée
Vérifiez le chemin exact:
// Afficher tous les chemins
console.log(this.vault.allNotes().map(n => n.filePath));
L'état n'est pas restauré
Assurez-vous que le service est injecté dans AppComponent:
export class AppComponent {
  private urlStateService = inject(UrlStateService);
}
📞 Support
Pour des questions ou des problèmes:
- Consultez la documentation complète
- Vérifiez les exemples d'intégration
- Exécutez les tests unitaires
- Vérifiez la console du navigateur
📝 Notes
- Le service utilise Angular Signals pour la réactivité
- Compatible avec Angular 20+
- Fonctionne avec le Router d'Angular
- Supporte les caractères spéciaux via encodage URI
- Gestion automatique du cycle de vie
🎉 Résumé
Le UrlStateService offre une solution complète pour synchroniser l'état de l'interface avec l'URL, permettant:
- ✅ Deep-linking vers des notes spécifiques
- ✅ Partage de liens avec contexte
- ✅ Restauration d'état après rechargement
- ✅ Filtrage persistant (tags, dossiers, quick links)
- ✅ Recherche persistante
- ✅ Historique de navigation
Avec une API simple, une documentation complète et des exemples d'intégration, le service est prêt pour une utilisation en production.