ObsiViewer/docs/URL_STATE/URL_STATE_EXAMPLES.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

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.