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

11 KiB

UrlStateService - Exemples d'URL et Cas d'Usage

📌 Table des matières

  1. Exemples simples
  2. Exemples combinés
  3. Cas d'usage réels
  4. Partage de liens
  5. Gestion des erreurs
  6. 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:

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:

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:

await this.urlState.filterByFolder('Notes/Meetings');

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:

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:

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:

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:

await this.urlState.filterByFolder('Notes/Meetings');
await this.urlState.filterByTag('Important');

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:

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:

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.

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

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

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

@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:

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:

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:

try {
  await this.urlState.filterByFolder('NonExistent');
} catch (error) {
  console.error('Folder not found');
  this.toast.warning('Dossier inexistant');
}

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:

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

// ❌ 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

// ❌ 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

// ❌ Mauvais - Pas réactif
const tag = this.urlState.currentState().tag;

// ✅ Bon - Réactif
const tag = this.urlState.activeTag;

4. Écouter les changements

// ❌ 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

// ❌ 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

// ❌ Mauvais - Pas de types
const state = this.urlState.currentState();

// ✅ Bon - Avec types
const state: UrlState = this.urlState.currentState();

7. Documenter les URLs

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