feat: add tag editor API endpoint and improve tag editing UI
This commit is contained in:
@@ -15,9 +15,9 @@ import { CommonModule } from '@angular/common';
|
||||
import { Note } from '../../../types';
|
||||
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
||||
import { NotePreviewService, PreviewData } from '../../../services/note-preview.service';
|
||||
import { NoteHeaderComponent } from '../../../app/features/note/components/note-header/note-header.component';
|
||||
import { ClipboardService } from '../../../app/shared/services/clipboard.service';
|
||||
import { ToastService } from '../../../app/shared/toast/toast.service';
|
||||
import { TagsEditorComponent } from '../../../app/shared/tags-editor/tags-editor.component';
|
||||
import { VaultService } from '../../../services/vault.service';
|
||||
import { Subscription } from 'rxjs';
|
||||
import mermaid from 'mermaid';
|
||||
@@ -65,29 +65,21 @@ interface MetadataEntry {
|
||||
@Component({
|
||||
selector: 'app-note-viewer',
|
||||
standalone: true,
|
||||
imports: [CommonModule, TagsEditorComponent],
|
||||
imports: [CommonModule, NoteHeaderComponent],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<div class="relative p-1 prose prose-lg dark:prose-invert max-w-none prose-p:leading-[1] prose-li:leading-[1] prose-blockquote:leading-[1]">
|
||||
<div class="sr-only" role="status" aria-live="polite">{{ copyStatus() }}</div>
|
||||
<!-- Compact Top Bar -->
|
||||
<div class="flex items-center justify-between gap-2 pl-1 pr-2 py-1 mb-2 text-text-muted text-xs">
|
||||
<div class="flex items-center gap-1">
|
||||
|
||||
<!-- Directory clickable -->
|
||||
<button type="button" class="note-toolbar-icon note-toolbar-path hidden lg:inline-flex" (click)="directoryClicked.emit(getDirectoryFromPath(note().filePath))" title="Open directory">🗂️ {{ note().filePath }}</button>
|
||||
<button type="button" class="note-toolbar-icon hidden lg:inline-flex" (click)="copyPath()" aria-label="Copier le chemin" title="Copier le chemin">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16V6a2 2 0 0 1 2-2h10"/></svg>
|
||||
</button>
|
||||
|
||||
<!-- Tags editor (read-only -> edit on click) -->
|
||||
<app-tags-editor class="hidden md:flex ml-3"
|
||||
[noteId]="note().id"
|
||||
[tags]="note().tags"
|
||||
(tagsChange)="onTagsChange($event)"
|
||||
(commit)="commitTagsNow()"
|
||||
></app-tags-editor>
|
||||
</div>
|
||||
<app-note-header class="flex-1 min-w-0"
|
||||
[fullPath]="note().filePath"
|
||||
[noteId]="note().id"
|
||||
[tags]="note().tags ?? []"
|
||||
(copyRequested)="copyPath()"
|
||||
(openDirectory)="directoryClicked.emit(getDirectoryFromPath(note().filePath))"
|
||||
(tagsChange)="onTagsChange($event)"
|
||||
></app-note-header>
|
||||
|
||||
<div class="flex items-center gap-1">
|
||||
<button
|
||||
@@ -458,34 +450,14 @@ export class NoteViewerComponent implements OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
private tagsSaveTimer: number | null = null;
|
||||
private pendingTags: string[] | null = null;
|
||||
|
||||
// La sauvegarde des tags est maintenant gérée automatiquement par TagsEditorComponent
|
||||
// Cette méthode met simplement à jour l'état local après sauvegarde
|
||||
onTagsChange(next: string[]): void {
|
||||
this.pendingTags = next.slice();
|
||||
if (this.tagsSaveTimer !== null) {
|
||||
window.clearTimeout(this.tagsSaveTimer);
|
||||
this.tagsSaveTimer = null;
|
||||
}
|
||||
this.tagsSaveTimer = window.setTimeout(() => this.commitTagsNow(), 1000);
|
||||
}
|
||||
|
||||
async commitTagsNow(): Promise<void> {
|
||||
if (!this.pendingTags) return;
|
||||
const note = this.note();
|
||||
if (!note) return;
|
||||
const tags = this.pendingTags.slice().map(t => `${t}`.trim()).filter(Boolean);
|
||||
this.pendingTags = null;
|
||||
try {
|
||||
const ok = await this.vault.updateNoteTags(note.id, tags);
|
||||
if (ok) {
|
||||
this.toast.success('Tags enregistrés');
|
||||
} else {
|
||||
this.toast.error('Échec d’enregistrement des tags');
|
||||
}
|
||||
} catch {
|
||||
this.toast.error('Échec d’enregistrement des tags');
|
||||
}
|
||||
|
||||
// Mettre à jour l'état local (la note sera rafraîchie par le vault service)
|
||||
// Pas besoin de sauvegarder ici, c'est déjà fait par TagsEditorComponent
|
||||
}
|
||||
|
||||
async copyPath(): Promise<void> {
|
||||
@@ -493,15 +465,15 @@ export class NoteViewerComponent implements OnDestroy {
|
||||
try {
|
||||
const ok = await this.clipboard.write(path);
|
||||
if (ok) {
|
||||
this.toast.success(path ? `Path copié — « ${path} »` : 'Path copié');
|
||||
this.copyStatus.set('Path copié');
|
||||
this.toast.success('📋 Chemin copié');
|
||||
this.copyStatus.set('Chemin copié');
|
||||
} else {
|
||||
this.toast.error('Impossible de copier le path. Vérifiez les permissions du navigateur.');
|
||||
this.copyStatus.set('Échec de la copie du path');
|
||||
this.toast.error('Impossible de copier le chemin. Vérifiez les permissions du navigateur.');
|
||||
this.copyStatus.set('Échec de la copie du chemin');
|
||||
}
|
||||
} catch {
|
||||
this.toast.error('Impossible de copier le path. Vérifiez les permissions du navigateur.');
|
||||
this.copyStatus.set('Échec de la copie du path');
|
||||
this.toast.error('Impossible de copier le chemin. Vérifiez les permissions du navigateur.');
|
||||
this.copyStatus.set('Échec de la copie du chemin');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user