ObsiViewer/docs/URL_STATE/URL_STATE_SERVICE_README.md
Bruno Charest 96745e9997 feat: add URL state synchronization for navigation
- 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
2025-10-24 23:23:30 -04:00

381 lines
11 KiB
Markdown

# 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
```typescript
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
```typescript
// 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)
```typescript
// É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
```typescript
// 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
```typescript
// 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
```typescript
// 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:
```typescript
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
```bash
# 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:
1. **NotesListComponent** - Synchronisation des filtres
2. **NoteViewComponent** - Chargement depuis l'URL
3. **TagsComponent** - Synchronisation des tags
4. **FoldersComponent** - Synchronisation des dossiers
5. **SearchComponent** - Synchronisation de la recherche
6. **ShareButton** - Partage de lien
7. **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:
```typescript
// ❌ Mauvais
this.currentTag = 'Ideas';
// ✅ Correct
await this.urlState.filterByTag('Ideas');
```
### La note n'est pas trouvée
Vérifiez le chemin exact:
```typescript
// 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:
```typescript
export class AppComponent {
private urlStateService = inject(UrlStateService);
}
```
## 📞 Support
Pour des questions ou des problèmes:
1. Consultez la documentation complète
2. Vérifiez les exemples d'intégration
3. Exécutez les tests unitaires
4. 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.