- 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
		
			
				
	
	
		
			585 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			585 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| # UrlStateService - Exemples d'URL et Cas d'Usage
 | |
| 
 | |
| ## 📌 Table des matières
 | |
| 
 | |
| 1. [Exemples simples](#exemples-simples)
 | |
| 2. [Exemples combinés](#exemples-combinés)
 | |
| 3. [Cas d'usage réels](#cas-dusage-réels)
 | |
| 4. [Partage de liens](#partage-de-liens)
 | |
| 5. [Gestion des erreurs](#gestion-des-erreurs)
 | |
| 6. [Bonnes pratiques](#bonnes-pratiques)
 | |
| 
 | |
| ---
 | |
| 
 | |
| ## Exemples Simples
 | |
| 
 | |
| ### 1. Ouvrir une note
 | |
| 
 | |
| **URL:**
 | |
| ```
 | |
| https://app.example.com/viewer?note=Docs/Architecture.md
 | |
| ```
 | |
| 
 | |
| **Résultat:**
 | |
| - La note `Docs/Architecture.md` s'ouvre dans la vue note
 | |
| - Le contenu est chargé et affiché
 | |
| - La note est mise en surbrillance dans la liste
 | |
| 
 | |
| **Code:**
 | |
| ```typescript
 | |
| await this.urlState.openNote('Docs/Architecture.md');
 | |
| ```
 | |
| 
 | |
| ### 2. Filtrer par tag
 | |
| 
 | |
| **URL:**
 | |
| ```
 | |
| https://app.example.com/viewer?tag=Ideas
 | |
| ```
 | |
| 
 | |
| **Résultat:**
 | |
| - Affiche toutes les notes avec le tag `Ideas`
 | |
| - Le tag est mis en surbrillance dans la liste des tags
 | |
| - La liste des notes est filtrée
 | |
| 
 | |
| **Code:**
 | |
| ```typescript
 | |
| await this.urlState.filterByTag('Ideas');
 | |
| ```
 | |
| 
 | |
| ### 3. Filtrer par dossier
 | |
| 
 | |
| **URL:**
 | |
| ```
 | |
| https://app.example.com/viewer?folder=Notes/Meetings
 | |
| ```
 | |
| 
 | |
| **Résultat:**
 | |
| - Affiche toutes les notes du dossier `Notes/Meetings`
 | |
| - Le dossier est mis en surbrillance dans l'arborescence
 | |
| - La liste des notes est filtrée
 | |
| 
 | |
| **Code:**
 | |
| ```typescript
 | |
| await this.urlState.filterByFolder('Notes/Meetings');
 | |
| ```
 | |
| 
 | |
| ### 4. Afficher un quick link
 | |
| 
 | |
| **URL:**
 | |
| ```
 | |
| https://app.example.com/viewer?quick=Favoris
 | |
| ```
 | |
| 
 | |
| **Résultat:**
 | |
| - Affiche les notes marquées comme favoris
 | |
| - Le quick link est mis en surbrillance
 | |
| - La liste des notes est filtrée
 | |
| 
 | |
| **Code:**
 | |
| ```typescript
 | |
| await this.urlState.filterByQuickLink('Favoris');
 | |
| ```
 | |
| 
 | |
| ### 5. Rechercher
 | |
| 
 | |
| **URL:**
 | |
| ```
 | |
| https://app.example.com/viewer?search=performance
 | |
| ```
 | |
| 
 | |
| **Résultat:**
 | |
| - Affiche les résultats de recherche pour "performance"
 | |
| - Les notes contenant le terme sont listées
 | |
| - Le terme est mis en surbrillance
 | |
| 
 | |
| **Code:**
 | |
| ```typescript
 | |
| await this.urlState.updateSearch('performance');
 | |
| ```
 | |
| 
 | |
| ---
 | |
| 
 | |
| ## Exemples Combinés
 | |
| 
 | |
| ### 1. Note avec recherche
 | |
| 
 | |
| **URL:**
 | |
| ```
 | |
| https://app.example.com/viewer?note=Docs/Architecture.md&search=performance
 | |
| ```
 | |
| 
 | |
| **Résultat:**
 | |
| - Ouvre la note `Docs/Architecture.md`
 | |
| - Met en surbrillance les occurrences de "performance"
 | |
| - Permet de naviguer entre les occurrences
 | |
| 
 | |
| **Code:**
 | |
| ```typescript
 | |
| await this.urlState.openNote('Docs/Architecture.md');
 | |
| await this.urlState.updateSearch('performance');
 | |
| ```
 | |
| 
 | |
| ### 2. Dossier avec tag
 | |
| 
 | |
| **URL:**
 | |
| ```
 | |
| https://app.example.com/viewer?folder=Notes/Meetings&tag=Important
 | |
| ```
 | |
| 
 | |
| **Résultat:**
 | |
| - Affiche les notes du dossier `Notes/Meetings`
 | |
| - Filtre par le tag `Important`
 | |
| - Affiche l'intersection des deux filtres
 | |
| 
 | |
| **Code:**
 | |
| ```typescript
 | |
| await this.urlState.filterByFolder('Notes/Meetings');
 | |
| await this.urlState.filterByTag('Important');
 | |
| ```
 | |
| 
 | |
| ### 3. Quick link avec recherche
 | |
| 
 | |
| **URL:**
 | |
| ```
 | |
| https://app.example.com/viewer?quick=Archive&search=2024
 | |
| ```
 | |
| 
 | |
| **Résultat:**
 | |
| - Affiche les notes archivées
 | |
| - Filtre par le terme "2024"
 | |
| - Affiche les notes archivées contenant "2024"
 | |
| 
 | |
| **Code:**
 | |
| ```typescript
 | |
| await this.urlState.filterByQuickLink('Archive');
 | |
| await this.urlState.updateSearch('2024');
 | |
| ```
 | |
| 
 | |
| ### 4. Dossier avec recherche
 | |
| 
 | |
| **URL:**
 | |
| ```
 | |
| https://app.example.com/viewer?folder=Journal&search=2025
 | |
| ```
 | |
| 
 | |
| **Résultat:**
 | |
| - Affiche les notes du dossier `Journal`
 | |
| - Filtre par le terme "2025"
 | |
| - Affiche les notes du journal contenant "2025"
 | |
| 
 | |
| **Code:**
 | |
| ```typescript
 | |
| await this.urlState.filterByFolder('Journal');
 | |
| await this.urlState.updateSearch('2025');
 | |
| ```
 | |
| 
 | |
| ---
 | |
| 
 | |
| ## Cas d'Usage Réels
 | |
| 
 | |
| ### 1. Documentation d'Architecture
 | |
| 
 | |
| **Scénario:** Vous travaillez sur l'architecture et voulez partager une note spécifique avec votre équipe.
 | |
| 
 | |
| **URL:**
 | |
| ```
 | |
| https://app.example.com/viewer?note=Docs/Architecture.md
 | |
| ```
 | |
| 
 | |
| **Avantages:**
 | |
| - Lien direct vers la note
 | |
| - Pas besoin de naviguer manuellement
 | |
| - Contexte clair pour l'équipe
 | |
| 
 | |
| ### 2. Réunion d'équipe
 | |
| 
 | |
| **Scénario:** Vous voulez afficher les notes de réunion d'un mois spécifique.
 | |
| 
 | |
| **URL:**
 | |
| ```
 | |
| https://app.example.com/viewer?folder=Notes/Meetings/2025-01&tag=Important
 | |
| ```
 | |
| 
 | |
| **Avantages:**
 | |
| - Filtre par dossier et tag
 | |
| - Affiche uniquement les réunions importantes
 | |
| - Facile à partager avec l'équipe
 | |
| 
 | |
| ### 3. Recherche de bug
 | |
| 
 | |
| **Scénario:** Vous cherchez des notes contenant "bug" dans le dossier "Issues".
 | |
| 
 | |
| **URL:**
 | |
| ```
 | |
| https://app.example.com/viewer?folder=Issues&search=bug
 | |
| ```
 | |
| 
 | |
| **Avantages:**
 | |
| - Recherche ciblée dans un dossier
 | |
| - Résultats pertinents
 | |
| - Facile à reproduire
 | |
| 
 | |
| ### 4. Favoris du projet
 | |
| 
 | |
| **Scénario:** Vous voulez afficher les notes favorites du projet "ProjectX".
 | |
| 
 | |
| **URL:**
 | |
| ```
 | |
| https://app.example.com/viewer?quick=Favoris&search=ProjectX
 | |
| ```
 | |
| 
 | |
| **Avantages:**
 | |
| - Affiche les favoris
 | |
| - Filtre par projet
 | |
| - Vue d'ensemble rapide
 | |
| 
 | |
| ### 5. Tâches urgentes
 | |
| 
 | |
| **Scénario:** Vous voulez voir les tâches urgentes du jour.
 | |
| 
 | |
| **URL:**
 | |
| ```
 | |
| https://app.example.com/viewer?quick=Tâches&tag=Urgent
 | |
| ```
 | |
| 
 | |
| **Avantages:**
 | |
| - Affiche les tâches
 | |
| - Filtre par urgence
 | |
| - Priorisation facile
 | |
| 
 | |
| ### 6. Brouillons à publier
 | |
| 
 | |
| **Scénario:** Vous voulez voir les brouillons prêts à être publiés.
 | |
| 
 | |
| **URL:**
 | |
| ```
 | |
| https://app.example.com/viewer?quick=Brouillons&tag=Prêt
 | |
| ```
 | |
| 
 | |
| **Avantages:**
 | |
| - Affiche les brouillons
 | |
| - Filtre par statut
 | |
| - Workflow clair
 | |
| 
 | |
| ### 7. Archive par année
 | |
| 
 | |
| **Scénario:** Vous voulez consulter l'archive de 2024.
 | |
| 
 | |
| **URL:**
 | |
| ```
 | |
| https://app.example.com/viewer?folder=Archive/2024
 | |
| ```
 | |
| 
 | |
| **Avantages:**
 | |
| - Affiche l'archive d'une année
 | |
| - Navigation facile
 | |
| - Historique accessible
 | |
| 
 | |
| ---
 | |
| 
 | |
| ## Partage de Liens
 | |
| 
 | |
| ### 1. Partage simple
 | |
| 
 | |
| **Scénario:** Vous voulez partager une note avec un collègue.
 | |
| 
 | |
| ```typescript
 | |
| // Générer le lien
 | |
| const shareUrl = this.urlState.generateShareUrl();
 | |
| 
 | |
| // Copier dans le presse-papiers
 | |
| await this.urlState.copyCurrentUrlToClipboard();
 | |
| 
 | |
| // Afficher un toast
 | |
| this.toast.success('Lien copié!');
 | |
| ```
 | |
| 
 | |
| **Résultat:**
 | |
| ```
 | |
| https://app.example.com/viewer?note=Docs/Architecture.md
 | |
| ```
 | |
| 
 | |
| ### 2. Partage avec contexte
 | |
| 
 | |
| **Scénario:** Vous voulez partager une note avec un contexte de recherche.
 | |
| 
 | |
| ```typescript
 | |
| // Générer le lien avec contexte
 | |
| const shareUrl = this.urlState.generateShareUrl({
 | |
|   note: 'Docs/Architecture.md',
 | |
|   search: 'performance'
 | |
| });
 | |
| 
 | |
| // Partager
 | |
| await navigator.clipboard.writeText(shareUrl);
 | |
| ```
 | |
| 
 | |
| **Résultat:**
 | |
| ```
 | |
| https://app.example.com/viewer?note=Docs/Architecture.md&search=performance
 | |
| ```
 | |
| 
 | |
| ### 3. Partage de filtre
 | |
| 
 | |
| **Scénario:** Vous voulez partager un filtre avec votre équipe.
 | |
| 
 | |
| ```typescript
 | |
| // Appliquer le filtre
 | |
| await this.urlState.filterByTag('Ideas');
 | |
| 
 | |
| // Générer et copier le lien
 | |
| const shareUrl = this.urlState.generateShareUrl();
 | |
| await navigator.clipboard.writeText(shareUrl);
 | |
| ```
 | |
| 
 | |
| **Résultat:**
 | |
| ```
 | |
| https://app.example.com/viewer?tag=Ideas
 | |
| ```
 | |
| 
 | |
| ### 4. Bouton de partage
 | |
| 
 | |
| **Scénario:** Vous voulez ajouter un bouton de partage dans l'interface.
 | |
| 
 | |
| ```typescript
 | |
| @Component({
 | |
|   selector: 'app-share-button',
 | |
|   template: `
 | |
|     <button (click)="shareCurrentState()" class="share-btn">
 | |
|       📤 Partager
 | |
|     </button>
 | |
|   `
 | |
| })
 | |
| export class ShareButtonComponent {
 | |
|   urlState = inject(UrlStateService);
 | |
|   toast = inject(ToastService);
 | |
|   
 | |
|   async shareCurrentState(): Promise<void> {
 | |
|     try {
 | |
|       await this.urlState.copyCurrentUrlToClipboard();
 | |
|       this.toast.success('Lien copié!');
 | |
|     } catch (error) {
 | |
|       this.toast.error('Erreur lors de la copie');
 | |
|     }
 | |
|   }
 | |
| }
 | |
| ```
 | |
| 
 | |
| ---
 | |
| 
 | |
| ## Gestion des Erreurs
 | |
| 
 | |
| ### 1. Note introuvable
 | |
| 
 | |
| **URL:**
 | |
| ```
 | |
| https://app.example.com/viewer?note=NonExistent.md
 | |
| ```
 | |
| 
 | |
| **Résultat:**
 | |
| - Message d'avertissement dans la console
 | |
| - L'état n'est pas mis à jour
 | |
| - L'interface reste dans l'état précédent
 | |
| 
 | |
| **Code:**
 | |
| ```typescript
 | |
| try {
 | |
|   await this.urlState.openNote('NonExistent.md');
 | |
| } catch (error) {
 | |
|   console.error('Note not found');
 | |
|   this.toast.error('Note introuvable');
 | |
| }
 | |
| ```
 | |
| 
 | |
| ### 2. Tag inexistant
 | |
| 
 | |
| **URL:**
 | |
| ```
 | |
| https://app.example.com/viewer?tag=NonExistent
 | |
| ```
 | |
| 
 | |
| **Résultat:**
 | |
| - Message d'avertissement dans la console
 | |
| - L'état n'est pas mis à jour
 | |
| - L'interface reste dans l'état précédent
 | |
| 
 | |
| **Code:**
 | |
| ```typescript
 | |
| try {
 | |
|   await this.urlState.filterByTag('NonExistent');
 | |
| } catch (error) {
 | |
|   console.error('Tag not found');
 | |
|   this.toast.warning('Tag inexistant');
 | |
| }
 | |
| ```
 | |
| 
 | |
| ### 3. Dossier inexistant
 | |
| 
 | |
| **URL:**
 | |
| ```
 | |
| https://app.example.com/viewer?folder=NonExistent
 | |
| ```
 | |
| 
 | |
| **Résultat:**
 | |
| - Message d'avertissement dans la console
 | |
| - L'état n'est pas mis à jour
 | |
| - L'interface reste dans l'état précédent
 | |
| 
 | |
| **Code:**
 | |
| ```typescript
 | |
| try {
 | |
|   await this.urlState.filterByFolder('NonExistent');
 | |
| } catch (error) {
 | |
|   console.error('Folder not found');
 | |
|   this.toast.warning('Dossier inexistant');
 | |
| }
 | |
| ```
 | |
| 
 | |
| ### 4. Quick link invalide
 | |
| 
 | |
| **URL:**
 | |
| ```
 | |
| https://app.example.com/viewer?quick=Invalid
 | |
| ```
 | |
| 
 | |
| **Résultat:**
 | |
| - Message d'avertissement dans la console
 | |
| - L'état n'est pas mis à jour
 | |
| - L'interface reste dans l'état précédent
 | |
| 
 | |
| **Code:**
 | |
| ```typescript
 | |
| try {
 | |
|   await this.urlState.filterByQuickLink('Invalid');
 | |
| } catch (error) {
 | |
|   console.error('Invalid quick link');
 | |
|   this.toast.warning('Quick link invalide');
 | |
| }
 | |
| ```
 | |
| 
 | |
| ---
 | |
| 
 | |
| ## Bonnes Pratiques
 | |
| 
 | |
| ### 1. Toujours valider les données
 | |
| 
 | |
| ```typescript
 | |
| // ❌ Mauvais - Pas de validation
 | |
| await this.urlState.openNote(userInput);
 | |
| 
 | |
| // ✅ Bon - Validation
 | |
| const note = this.vault.allNotes().find(n => n.filePath === userInput);
 | |
| if (note) {
 | |
|   await this.urlState.openNote(userInput);
 | |
| } else {
 | |
|   this.toast.error('Note introuvable');
 | |
| }
 | |
| ```
 | |
| 
 | |
| ### 2. Gérer les erreurs
 | |
| 
 | |
| ```typescript
 | |
| // ❌ Mauvais - Pas de gestion d'erreur
 | |
| await this.urlState.filterByTag(userInput);
 | |
| 
 | |
| // ✅ Bon - Gestion d'erreur
 | |
| try {
 | |
|   await this.urlState.filterByTag(userInput);
 | |
| } catch (error) {
 | |
|   console.error('Error:', error);
 | |
|   this.toast.error('Erreur lors du filtrage');
 | |
| }
 | |
| ```
 | |
| 
 | |
| ### 3. Utiliser les signaux
 | |
| 
 | |
| ```typescript
 | |
| // ❌ Mauvais - Pas réactif
 | |
| const tag = this.urlState.currentState().tag;
 | |
| 
 | |
| // ✅ Bon - Réactif
 | |
| const tag = this.urlState.activeTag;
 | |
| ```
 | |
| 
 | |
| ### 4. Écouter les changements
 | |
| 
 | |
| ```typescript
 | |
| // ❌ Mauvais - Pas d'écoute
 | |
| this.urlState.openNote('Note.md');
 | |
| 
 | |
| // ✅ Bon - Écouter les changements
 | |
| effect(() => {
 | |
|   const note = this.urlState.currentNote();
 | |
|   if (note) {
 | |
|     console.log('Note changed:', note.title);
 | |
|   }
 | |
| });
 | |
| ```
 | |
| 
 | |
| ### 5. Nettoyer les ressources
 | |
| 
 | |
| ```typescript
 | |
| // ❌ Mauvais - Pas de nettoyage
 | |
| this.urlState.stateChange$.subscribe(event => {
 | |
|   console.log('State changed:', event);
 | |
| });
 | |
| 
 | |
| // ✅ Bon - Nettoyage
 | |
| private destroy$ = new Subject<void>();
 | |
| 
 | |
| constructor() {
 | |
|   this.urlState.stateChange$
 | |
|     .pipe(takeUntil(this.destroy$))
 | |
|     .subscribe(event => {
 | |
|       console.log('State changed:', event);
 | |
|     });
 | |
| }
 | |
| 
 | |
| ngOnDestroy() {
 | |
|   this.destroy$.next();
 | |
|   this.destroy$.complete();
 | |
| }
 | |
| ```
 | |
| 
 | |
| ### 6. Utiliser les types
 | |
| 
 | |
| ```typescript
 | |
| // ❌ Mauvais - Pas de types
 | |
| const state = this.urlState.currentState();
 | |
| 
 | |
| // ✅ Bon - Avec types
 | |
| const state: UrlState = this.urlState.currentState();
 | |
| ```
 | |
| 
 | |
| ### 7. Documenter les URLs
 | |
| 
 | |
| ```typescript
 | |
| /**
 | |
|  * Ouvre une note spécifique
 | |
|  * 
 | |
|  * URL: /viewer?note=Docs/Architecture.md
 | |
|  * 
 | |
|  * @param notePath - Chemin de la note (ex: 'Docs/Architecture.md')
 | |
|  */
 | |
| async openNote(notePath: string): Promise<void> {
 | |
|   // ...
 | |
| }
 | |
| ```
 | |
| 
 | |
| ---
 | |
| 
 | |
| ## Résumé
 | |
| 
 | |
| Le `UrlStateService` offre une solution flexible pour gérer l'état de l'interface via l'URL:
 | |
| 
 | |
| - ✅ Exemples simples pour les cas courants
 | |
| - ✅ Exemples combinés pour les cas complexes
 | |
| - ✅ Cas d'usage réels pour l'inspiration
 | |
| - ✅ Partage de liens facile
 | |
| - ✅ Gestion des erreurs robuste
 | |
| - ✅ Bonnes pratiques pour la qualité du code
 | |
| 
 | |
| Utilisez ces exemples comme base pour intégrer le service dans votre application.
 | |
| 
 |