|
23 | 23 | import { page } from '$app/state';
|
24 | 24 | import Card from '../card.svelte';
|
25 | 25 | import SkeletonRepoList from './skeletonRepoList.svelte';
|
26 |
| - import { onMount, untrack } from 'svelte'; |
| 26 | + import { onMount, untrack, onDestroy } from 'svelte'; |
27 | 27 |
|
28 | 28 | let {
|
29 | 29 | action = $bindable('select'),
|
|
48 | 48 | let isLoadingRepositories = $state(null);
|
49 | 49 | let installationsMap = $state(null);
|
50 | 50 |
|
| 51 | + // Debounce and race condition handling |
| 52 | + let searchTimeout: ReturnType<typeof setTimeout> | null = null; |
| 53 | + let currentRequestId = 0; |
| 54 | + let lastResolvedRequestId = 0; |
| 55 | +
|
51 | 56 | onMount(() => {
|
52 | 57 | loadInstallations();
|
53 | 58 | });
|
54 | 59 |
|
| 60 | + $effect(() => { |
| 61 | + if (selectedInstallation && search !== undefined) { |
| 62 | + if (searchTimeout) { |
| 63 | + clearTimeout(searchTimeout); |
| 64 | + } |
| 65 | + searchTimeout = setTimeout(() => { |
| 66 | + if (selectedInstallation) { |
| 67 | + isLoadingRepositories = true; |
| 68 | + const requestId = ++currentRequestId; |
| 69 | + loadRepositories(selectedInstallation, search) |
| 70 | + .then(() => { |
| 71 | + if (requestId > lastResolvedRequestId) { |
| 72 | + lastResolvedRequestId = requestId; |
| 73 | + isLoadingRepositories = false; |
| 74 | + } |
| 75 | + }) |
| 76 | + .catch(() => { |
| 77 | + if (requestId > lastResolvedRequestId) { |
| 78 | + lastResolvedRequestId = requestId; |
| 79 | + isLoadingRepositories = false; |
| 80 | + } |
| 81 | + }); |
| 82 | + } |
| 83 | + }, 300); |
| 84 | + } |
| 85 | + }); |
| 86 | +
|
| 87 | + onDestroy(() => { |
| 88 | + if (searchTimeout) clearTimeout(searchTimeout); |
| 89 | + }); |
| 90 | +
|
55 | 91 | async function loadInstallations() {
|
56 | 92 | if (installationList) {
|
57 | 93 | if (installationList.installations.length) {
|
|
71 | 107 | .vcs.listInstallations();
|
72 | 108 | if (installations.length) {
|
73 | 109 | if (!selectedInstallation) {
|
74 |
| - untrack(() => (selectedInstallation = installationList.installations[0].$id)); |
| 110 | + untrack(() => (selectedInstallation = installations[0].$id)); |
75 | 111 | }
|
76 | 112 | installation.set(installations.find((entry) => entry.$id === selectedInstallation));
|
77 | 113 | }
|
|
80 | 116 | }
|
81 | 117 |
|
82 | 118 | async function loadRepositories(installationId: string, search: string) {
|
83 |
| - if ( |
84 |
| - !$repositories || |
85 |
| - $repositories.installationId !== installationId || |
86 |
| - $repositories.search !== search |
87 |
| - ) { |
88 |
| - await fetchRepos(installationId, search); |
89 |
| - } |
90 |
| -
|
91 |
| - if ($repositories.repositories.length && action === 'select') { |
92 |
| - selectedRepository = $repositories.repositories[0].id; |
93 |
| - $repository = $repositories.repositories[0]; |
94 |
| - } |
95 |
| - return $repositories.repositories; |
96 |
| - } |
97 |
| -
|
98 |
| - async function fetchRepos(installationId: string, search: string) { |
99 | 119 | if (product === 'functions') {
|
100 | 120 | $repositories.repositories = (
|
101 | 121 | (await sdk
|
|
117 | 137 | )) as unknown as Models.ProviderRepositoryFrameworkList
|
118 | 138 | ).frameworkProviderRepositories;
|
119 | 139 | }
|
120 |
| -
|
121 | 140 | $repositories.search = search;
|
122 | 141 | $repositories.installationId = installationId;
|
| 142 | + return $repositories.repositories; |
123 | 143 | }
|
124 | 144 |
|
125 | 145 | selectedRepository;
|
|
168 | 188 | installationsMap.find((entry) => entry.$id === selectedInstallation)
|
169 | 189 | );
|
170 | 190 |
|
171 |
| - isLoadingRepositories = true; |
172 |
| - loadRepositories(selectedInstallation, search).then(() => { |
173 |
| - isLoadingRepositories = false; |
174 |
| - }); |
| 191 | + if (searchTimeout) { |
| 192 | + clearTimeout(searchTimeout); |
| 193 | + } |
175 | 194 | }}
|
176 | 195 | bind:value={selectedInstallation} />
|
177 | 196 | <InputSearch
|
|
0 commit comments