|
8 | 8 | import { Icon, Layout, Input } from '@appwrite.io/pink-svelte'; |
9 | 9 | import { IconPlus, IconTrash } from '@appwrite.io/pink-icons-svelte'; |
10 | 10 | import { debounce } from '$lib/helpers/debounce'; |
| 11 | + import { onMount } from 'svelte'; |
11 | 12 |
|
12 | 13 | type ProjectAccess = { projectId: string; roleName: string }; |
13 | 14 | type Option = { value: string; label: string }; |
|
25 | 26 | // discarded if a newer request has already been dispatched. |
26 | 27 | let rowGeneration = $state<number[]>([]); |
27 | 28 |
|
| 29 | + // Prefetch the first page the moment this component mounts (i.e. when the |
| 30 | + // user switches to "Specific projects") so results are ready or in-flight |
| 31 | + // before any row is opened. All rows share this single Promise for |
| 32 | + // unfiltered loads; typed searches always hit the API separately. |
| 33 | + let prefetchPromise: Promise<Option[]> | null = null; |
| 34 | +
|
| 35 | + onMount(() => { |
| 36 | + prefetchPromise = sdk.forConsole |
| 37 | + .organization($organization.$id) |
| 38 | + .listProjects({ |
| 39 | + queries: [ |
| 40 | + Query.limit(25), |
| 41 | + Query.orderDesc(''), |
| 42 | + Query.equal('teamId', $organization.$id) |
| 43 | + ] |
| 44 | + }) |
| 45 | + .then((r) => r.projects.map((p) => ({ value: p.$id, label: p.name }))) |
| 46 | + .catch(() => []); |
| 47 | + }); |
| 48 | +
|
28 | 49 | function takenIds(excludeIndex: number): Set<string> { |
29 | 50 | return new Set( |
30 | 51 | projectAccess |
|
39 | 60 | rowGeneration[index] = gen; |
40 | 61 | rowSearching[index] = true; |
41 | 62 |
|
42 | | - const queries: string[] = [ |
43 | | - Query.limit(25), |
44 | | - Query.orderDesc(''), |
45 | | - Query.equal('teamId', $organization.$id) |
46 | | - ]; |
47 | | - if (search) { |
48 | | - queries.push(Query.search('name', search)); |
| 63 | + let allOptions: Option[]; |
| 64 | + if (!search && prefetchPromise) { |
| 65 | + // Re-use the shared prefetch — avoids a redundant network request |
| 66 | + allOptions = await prefetchPromise; |
| 67 | + } else { |
| 68 | + const queries: string[] = [ |
| 69 | + Query.limit(25), |
| 70 | + Query.orderDesc(''), |
| 71 | + Query.equal('teamId', $organization.$id) |
| 72 | + ]; |
| 73 | + if (search) queries.push(Query.search('name', search)); |
| 74 | + const result = await sdk.forConsole |
| 75 | + .organization($organization.$id) |
| 76 | + .listProjects({ queries }) |
| 77 | + .catch(() => null); |
| 78 | + allOptions = (result?.projects ?? []).map((p) => ({ value: p.$id, label: p.name })); |
49 | 79 | } |
50 | 80 |
|
51 | | - const result = await sdk.forConsole |
52 | | - .organization($organization.$id) |
53 | | - .listProjects({ queries }) |
54 | | - .catch(() => null); |
55 | | -
|
56 | 81 | // Discard stale responses — a newer request has already been dispatched |
57 | 82 | if (rowGeneration[index] !== gen) return; |
58 | 83 |
|
59 | 84 | const taken = takenIds(index); |
60 | | - rowOptions[index] = (result?.projects ?? []) |
61 | | - .filter((p) => !taken.has(p.$id)) |
62 | | - .map((p) => ({ value: p.$id, label: p.name })); |
| 85 | + rowOptions[index] = allOptions.filter((p) => !taken.has(p.value)); |
63 | 86 | rowSearching[index] = false; |
64 | 87 | } |
65 | 88 |
|
|
0 commit comments