added loading indicator for large vaults

This commit is contained in:
Gamosoft 2026-06-30 12:26:32 +02:00
parent 0cd5332a99
commit c21d44a169
13 changed files with 71 additions and 5 deletions

View File

@ -262,6 +262,13 @@ function noteApp() {
alreadyDonated: false,
autosaveDelayMs: CONFIG.AUTOSAVE_DELAY, // hydrated from /api/config in loadConfig()
notes: [],
// True while /api/notes is in flight. Drives the "Loading your vault…"
// placeholder + the delayed overlay (notesLoadingShowOverlay).
notesLoading: true,
notesLoadingShowOverlay: false,
_notesLoadingOverlayTimer: null,
currentNote: '',
currentNoteName: '',
noteContent: '',
@ -1524,6 +1531,16 @@ function noteApp() {
// Load all notes
async loadNotes() {
this.notesLoading = true;
// Show the overlay only if we're still loading after 800ms AND
// the tree is currently empty (first load). Refreshes after a
// save typically resolve in <100ms and never trip this.
clearTimeout(this._notesLoadingOverlayTimer);
this._notesLoadingOverlayTimer = setTimeout(() => {
if (this.notesLoading && this.notes.length === 0 && this.allFolders.length === 0) {
this.notesLoadingShowOverlay = true;
}
}, 800);
try {
const response = await fetch('/api/notes');
const data = await response.json();
@ -1534,6 +1551,10 @@ function noteApp() {
await this.loadTags(); // Load tags after notes are loaded
} catch (error) {
ErrorHandler.handle('load notes', error);
} finally {
clearTimeout(this._notesLoadingOverlayTimer);
this.notesLoading = false;
this.notesLoadingShowOverlay = false;
}
},

View File

@ -1397,7 +1397,30 @@
</style>
</head>
<body x-data="noteApp()" x-init="init()" style="background-color: var(--bg-primary);" :class="{'zen-mode-active': zenMode}">
<!-- Vault loading overlay. Centered floating card; the outer div is
pointer-events:none so settings/theme/search/icon-rail stay clickable.
Shown only when /api/notes is still in flight 800ms after boot AND the
tree is currently empty — so small vaults / warm caches never see it. -->
<div x-show="notesLoadingShowOverlay"
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100"
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0"
class="fixed inset-0 flex items-center justify-center"
style="z-index: 60; pointer-events: none;">
<div class="px-6 py-4 rounded-lg shadow-2xl flex items-center gap-3"
style="background-color: var(--bg-secondary); border: 1px solid var(--border-primary); color: var(--text-primary); pointer-events: auto;">
<svg class="animate-spin h-5 w-5 flex-shrink-0" style="color: var(--accent-primary);" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
<span class="text-sm" x-text="t('sidebar.loading_notes')"></span>
</div>
</div>
<!-- Zen Mode Exit Button -->
<button
x-show="zenMode"
@ -1800,8 +1823,19 @@
<!-- Root notes and images (no folder) -->
<div x-html="renderRootItems()"></div>
<!-- Empty state -->
<template x-if="notes.length === 0 && allFolders.length === 0">
<!-- Loading state (initial /api/notes still in flight) -->
<template x-if="notesLoading && notes.length === 0 && allFolders.length === 0">
<div class="px-3 py-4 text-sm text-center flex items-center justify-center gap-2" style="color: var(--text-tertiary);">
<svg class="animate-spin h-4 w-4" style="color: var(--accent-primary);" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
<span x-text="t('sidebar.loading_notes')"></span>
</div>
</template>
<!-- Empty state (load finished, vault genuinely empty) -->
<template x-if="!notesLoading && notes.length === 0 && allFolders.length === 0">
<div class="px-3 py-4 text-sm text-center" style="color: var(--text-tertiary);">
<span x-text="t('sidebar.no_notes_yet')"></span><br>
<span x-text="t('sidebar.create_first')"></span>
@ -2432,8 +2466,8 @@
</button>
</div>
<!-- Welcome Hero - Show when app is empty -->
<template x-if="isAppEmpty">
<!-- Welcome Hero - Show when app is empty (but not while still loading) -->
<template x-if="!notesLoading && isAppEmpty">
<div class="flex flex-col items-center justify-center h-full py-16 text-center">
<svg class="mx-auto h-24 w-24 mb-4" style="color: var(--text-tertiary); opacity: 0.5;" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>

View File

@ -49,6 +49,7 @@
"no_notes": "Du hast noch keine Notizen. Erstelle deine erste!",
"no_notes_yet": "Noch keine Notizen oder Ordner.",
"create_first": "Erstelle deine erste Notiz oder Ordner!",
"loading_notes": "Notizen werden geladen…",
"drop_to_root": "Hier ablegen für Stammverzeichnis",
"no_favorites": "Noch keine Favoriten",
"no_results": "Keine Ergebnisse gefunden",

View File

@ -49,6 +49,7 @@
"no_notes": "No notes yet. Create your first note!",
"no_notes_yet": "No notes or folders yet.",
"create_first": "Create your first note or folder!",
"loading_notes": "Loading your notes…",
"no_favorites": "No favourites yet",
"no_results": "No results found",
"expand_all": "Expand all folders",

View File

@ -49,6 +49,7 @@
"no_notes": "No notes yet. Create your first note!",
"no_notes_yet": "No notes or folders yet.",
"create_first": "Create your first note or folder!",
"loading_notes": "Loading your notes…",
"drop_to_root": "Drop here for root",
"no_favorites": "No favorites yet",
"no_results": "No results found",

View File

@ -49,6 +49,7 @@
"no_notes": "Aún no hay notas. ¡Crea tu primera nota!",
"no_notes_yet": "Aún no hay notas ni carpetas.",
"create_first": "¡Crea tu primera nota o carpeta!",
"loading_notes": "Cargando tus notas…",
"drop_to_root": "Soltar aquí para raíz",
"no_favorites": "Aún no hay favoritos",
"no_results": "No se encontraron resultados",

View File

@ -49,6 +49,7 @@
"no_notes": "Pas encore de notes. Créez votre première note !",
"no_notes_yet": "Pas encore de notes ni de dossiers.",
"create_first": "Créez votre première note ou dossier !",
"loading_notes": "Chargement de vos notes…",
"drop_to_root": "Déposer ici pour la racine",
"no_favorites": "Pas encore de favoris",
"no_results": "Aucun résultat trouvé",

View File

@ -49,6 +49,7 @@
"no_notes": "Még nincsenek jegyzetek. Hozd létre az első jegyzeted!",
"no_notes_yet": "Még nincsenek jegyzetek vagy mappák.",
"create_first": "Hozd létre az első jegyzeted vagy mappád!",
"loading_notes": "Jegyzeteid betöltése…",
"drop_to_root": "Húzd ide a gyökérhez",
"no_favorites": "Még nincsenek kedvencek",
"no_results": "Nincs találat",

View File

@ -49,6 +49,7 @@
"no_notes": "Nessuna nota. Crea la tua prima nota!",
"no_notes_yet": "Nessuna nota o cartella.",
"create_first": "Crea la tua prima nota o cartella!",
"loading_notes": "Caricamento delle tue note…",
"no_favorites": "Nessun preferito",
"no_results": "Nessun risultato trovato",
"expand_all": "Espandi tutte le cartelle",

View File

@ -49,6 +49,7 @@
"no_notes": "ノートがありません。最初のノートを作成しましょう!",
"no_notes_yet": "ノートやフォルダがありません。",
"create_first": "最初のノートまたはフォルダを作成しましょう!",
"loading_notes": "ノートを読み込み中…",
"no_favorites": "お気に入りがありません",
"no_results": "結果が見つかりません",
"expand_all": "すべて展開",

View File

@ -49,6 +49,7 @@
"no_notes": "Заметок пока нет. Создайте первую!",
"no_notes_yet": "Нет заметок или папок.",
"create_first": "Создайте первую заметку или папку!",
"loading_notes": "Загрузка ваших заметок…",
"no_favorites": "Нет избранного",
"no_results": "Ничего не найдено",
"expand_all": "Развернуть все папки",

View File

@ -49,6 +49,7 @@
"no_notes": "Ni še zapiskov. Ustvarite svoj prvi zapis!",
"no_notes_yet": "Ni še zapiskov ali map.",
"create_first": "Ustvarite svoj prvi zapis ali mapo!",
"loading_notes": "Nalaganje vaših zapiskov…",
"no_favorites": "Ni priljubljenih",
"no_results": "Ni najdenih rezultatov",
"expand_all": "Razširi vse mape",

View File

@ -49,6 +49,7 @@
"no_notes": "还没有笔记。创建您的第一篇笔记!",
"no_notes_yet": "还没有笔记或文件夹。",
"create_first": "创建您的第一篇笔记或文件夹!",
"loading_notes": "正在加载您的笔记…",
"no_favorites": "还没有收藏夹",
"no_results": "未找到结果",
"expand_all": "展开所有文件夹",