- 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.
|
|
|