fix(person-mention): show loading state during debounce + fetch
Leonie #5507 concern 7: on slow networks the popup sat empty for up to 1.5s while the user wondered if anything was happening. Add a loading flag that flips on as soon as scheduleSearch is asked to query and back off in the fetch's finally branch. Reuses the existing comp_typeahead_loading message ("Suche…") so no new i18n keys. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -37,6 +37,7 @@ let query: string | null = $state(null);
|
|||||||
let results: Person[] = $state([]);
|
let results: Person[] = $state([]);
|
||||||
let highlightedIndex = $state(0);
|
let highlightedIndex = $state(0);
|
||||||
let mentionStart = $state(0);
|
let mentionStart = $state(0);
|
||||||
|
let loading = $state(false);
|
||||||
|
|
||||||
let textarea: HTMLTextAreaElement | null = null;
|
let textarea: HTMLTextAreaElement | null = null;
|
||||||
let debounceTimer: ReturnType<typeof setTimeout> | undefined;
|
let debounceTimer: ReturnType<typeof setTimeout> | undefined;
|
||||||
@@ -90,8 +91,10 @@ function scheduleSearch(q: string) {
|
|||||||
// Empty query: keep popup open with last results so the user can browse,
|
// Empty query: keep popup open with last results so the user can browse,
|
||||||
// but don't fire a backend call until they actually type something.
|
// but don't fire a backend call until they actually type something.
|
||||||
results = [];
|
results = [];
|
||||||
|
loading = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
loading = true;
|
||||||
debounceTimer = setTimeout(async () => {
|
debounceTimer = setTimeout(async () => {
|
||||||
try {
|
try {
|
||||||
// SECURITY: relies on the SvelteKit Vite proxy injecting the auth_token
|
// SECURITY: relies on the SvelteKit Vite proxy injecting the auth_token
|
||||||
@@ -108,6 +111,8 @@ function scheduleSearch(q: string) {
|
|||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
results = [];
|
results = [];
|
||||||
|
} finally {
|
||||||
|
loading = false;
|
||||||
}
|
}
|
||||||
}, 200);
|
}, 200);
|
||||||
}
|
}
|
||||||
@@ -140,6 +145,7 @@ function closePopup() {
|
|||||||
query = null;
|
query = null;
|
||||||
results = [];
|
results = [];
|
||||||
highlightedIndex = 0;
|
highlightedIndex = 0;
|
||||||
|
loading = false;
|
||||||
clearTimeout(debounceTimer);
|
clearTimeout(debounceTimer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,7 +214,9 @@ const popupOpen = $derived(query !== null);
|
|||||||
role="listbox"
|
role="listbox"
|
||||||
aria-label={m.person_mention_btn_label()}
|
aria-label={m.person_mention_btn_label()}
|
||||||
>
|
>
|
||||||
{#if results.length === 0}
|
{#if loading}
|
||||||
|
<p class="px-3 py-2 font-sans text-sm text-ink-3">{m.comp_typeahead_loading()}</p>
|
||||||
|
{:else if results.length === 0}
|
||||||
<p class="px-3 py-2 font-sans text-sm text-ink-3">{m.person_mention_popup_empty()}</p>
|
<p class="px-3 py-2 font-sans text-sm text-ink-3">{m.person_mention_popup_empty()}</p>
|
||||||
{:else}
|
{:else}
|
||||||
{#each results as person, i (person.id)}
|
{#each results as person, i (person.id)}
|
||||||
|
|||||||
Reference in New Issue
Block a user