|
1 | 1 | <script lang="ts"> |
2 | | - import { EmptySearch, Paginator } from '$lib/components'; |
| 2 | + import { EmptySearch, PaginationInline } from '$lib/components'; |
3 | 3 | import { Button, InputSearch, InputSelect } from '$lib/elements/forms'; |
4 | 4 | import { timeFromNow } from '$lib/helpers/date'; |
5 | 5 | import { sdk } from '$lib/stores/sdk'; |
|
17 | 17 | import { IconLockClosed, IconPlus } from '@appwrite.io/pink-icons-svelte'; |
18 | 18 | import ConnectGit from './connectGit.svelte'; |
19 | 19 | import SvgIcon from '../svgIcon.svelte'; |
20 | | - import { VCSDetectionType, type Models } from '@appwrite.io/console'; |
| 20 | + import { Query, VCSDetectionType, type Models } from '@appwrite.io/console'; |
21 | 21 | import { getFrameworkIcon } from '$lib/stores/sites'; |
22 | 22 | import { connectGitHub } from '$lib/stores/git'; |
23 | 23 | import { page } from '$app/state'; |
|
48 | 48 | let selectedInstallation = $state(null); |
49 | 49 | let isLoadingRepositories = $state(null); |
50 | 50 | let installationsMap = $state(null); |
| 51 | + let offset = $state(0); |
| 52 | + const limit = 5; |
51 | 53 |
|
52 | 54 | onMount(() => { |
53 | 55 | loadInstallations(); |
|
65 | 67 | 300 |
66 | 68 | ); |
67 | 69 |
|
| 70 | + const loadRepositoryPage = async () => { |
| 71 | + isLoadingRepositories = true; |
| 72 | + try { |
| 73 | + await loadRepositories(selectedInstallation, search); |
| 74 | + } finally { |
| 75 | + isLoadingRepositories = false; |
| 76 | + } |
| 77 | + }; |
| 78 | +
|
68 | 79 | $effect(() => { |
69 | 80 | if (selectedInstallation && search !== undefined) { |
| 81 | + offset = 0; // reset offset to 0 when search changes |
70 | 82 | debouncedLoadRepositories(selectedInstallation, search); |
71 | 83 | } |
72 | 84 | }); |
|
103 | 115 | } |
104 | 116 |
|
105 | 117 | async function loadRepositories(installationId: string, search: string) { |
106 | | - if (product === 'functions') { |
107 | | - $repositories.repositories = ( |
108 | | - (await sdk |
109 | | - .forProject(page.params.region, page.params.project) |
110 | | - .vcs.listRepositories({ |
111 | | - installationId, |
112 | | - type: VCSDetectionType.Runtime, |
113 | | - search: search || undefined |
114 | | - })) as unknown as Models.ProviderRepositoryRuntimeList |
115 | | - ).runtimeProviderRepositories; //TODO: remove forced cast after backend fixes |
116 | | - } else { |
117 | | - $repositories.repositories = ( |
118 | | - (await sdk |
119 | | - .forProject(page.params.region, page.params.project) |
120 | | - .vcs.listRepositories({ |
121 | | - installationId, |
122 | | - type: VCSDetectionType.Framework, |
123 | | - search: search || undefined |
124 | | - })) as unknown as Models.ProviderRepositoryFrameworkList |
125 | | - ).frameworkProviderRepositories; |
126 | | - } |
| 118 | + const result = await sdk |
| 119 | + .forProject(page.params.region, page.params.project) |
| 120 | + .vcs.listRepositories({ |
| 121 | + installationId, |
| 122 | + type: |
| 123 | + product === 'functions' ? VCSDetectionType.Runtime : VCSDetectionType.Framework, |
| 124 | + search: search || undefined, |
| 125 | + queries: [Query.limit(limit), Query.offset(offset)] |
| 126 | + }); |
| 127 | +
|
| 128 | + $repositories.repositories = |
| 129 | + product === 'functions' |
| 130 | + ? (result as unknown as Models.ProviderRepositoryRuntimeList) |
| 131 | + .runtimeProviderRepositories |
| 132 | + : (result as unknown as Models.ProviderRepositoryFrameworkList) |
| 133 | + .frameworkProviderRepositories; //TODO: remove forced cast after backend fixes |
| 134 | + $repositories.total = result.total; |
127 | 135 | $repositories.search = search; |
128 | 136 | $repositories.installationId = installationId; |
129 | 137 | return $repositories.repositories; |
|
188 | 196 | {#if selectedInstallation} |
189 | 197 | <!-- manual installation change --> |
190 | 198 | {#if isLoadingRepositories} |
191 | | - <SkeletonRepoList /> |
192 | | - {:else if $repositories?.repositories?.length} |
193 | | - <Paginator |
194 | | - items={$repositories.repositories} |
195 | | - hideFooter={$repositories.repositories?.length <= 6} |
196 | | - limit={6}> |
197 | | - {#snippet children( |
198 | | - paginatedItems: Models.ProviderRepositoryRuntime[] & |
199 | | - Models.ProviderRepositoryFramework[] |
200 | | - )} |
201 | | - <Table.Root columns={1} let:root> |
202 | | - {#each paginatedItems as repo} |
203 | | - <Table.Row.Base {root}> |
204 | | - <Table.Cell {root}> |
205 | | - <Layout.Stack direction="row" alignItems="center" gap="s"> |
206 | | - {#if action === 'select'} |
207 | | - <input |
208 | | - class="is-small u-margin-inline-end-8" |
209 | | - type="radio" |
210 | | - name="repositories" |
211 | | - bind:group={selectedRepository} |
212 | | - onchange={() => repository.set(repo)} |
213 | | - value={repo.id} /> |
| 199 | + <SkeletonRepoList count={limit} /> |
| 200 | + {:else if $repositories.total > 0} |
| 201 | + <Table.Root columns={1} let:root> |
| 202 | + {#each $repositories.repositories as repo} |
| 203 | + <Table.Row.Base {root}> |
| 204 | + <Table.Cell {root}> |
| 205 | + <Layout.Stack direction="row" alignItems="center" gap="s"> |
| 206 | + {#if action === 'select'} |
| 207 | + <input |
| 208 | + class="is-small u-margin-inline-end-8" |
| 209 | + type="radio" |
| 210 | + name="repositories" |
| 211 | + bind:group={selectedRepository} |
| 212 | + onchange={() => repository.set(repo)} |
| 213 | + value={repo.id} /> |
| 214 | + {/if} |
| 215 | + {#if product === 'sites'} |
| 216 | + {#if 'framework' in repo && repo?.framework && repo.framework !== 'other'} |
| 217 | + <Avatar size="xs" alt={repo.name}> |
| 218 | + <SvgIcon |
| 219 | + name={getFrameworkIcon(repo.framework)} |
| 220 | + iconSize="small" /> |
| 221 | + </Avatar> |
| 222 | + {:else} |
| 223 | + <Avatar size="xs" alt={repo.name} empty /> |
| 224 | + {/if} |
| 225 | + {:else} |
| 226 | + {@const iconName = |
| 227 | + 'runtime' in repo && repo?.runtime |
| 228 | + ? repo.runtime.split('-')[0] |
| 229 | + : undefined} |
| 230 | + <Avatar size="xs" alt={repo.name} empty={!iconName}> |
| 231 | + {#if iconName} |
| 232 | + <SvgIcon name={iconName} iconSize="small" /> |
214 | 233 | {/if} |
215 | | - {#if product === 'sites'} |
216 | | - {#if repo?.framework && repo.framework !== 'other'} |
217 | | - <Avatar size="xs" alt={repo.name}> |
218 | | - <SvgIcon |
219 | | - name={getFrameworkIcon(repo.framework)} |
220 | | - iconSize="small" /> |
221 | | - </Avatar> |
222 | | - {:else} |
223 | | - <Avatar size="xs" alt={repo.name} empty /> |
224 | | - {/if} |
225 | | - {:else} |
226 | | - {@const iconName = repo?.runtime |
227 | | - ? repo.runtime.split('-')[0] |
228 | | - : undefined} |
229 | | - <Avatar size="xs" alt={repo.name} empty={!iconName}> |
230 | | - {#if iconName} |
231 | | - <SvgIcon name={iconName} iconSize="small" /> |
232 | | - {/if} |
233 | | - </Avatar> |
234 | | - {/if} |
235 | | - <Layout.Stack |
236 | | - direction="row" |
237 | | - alignItems="center" |
238 | | - gap="s" |
239 | | - style="flex: 1; min-width: 0;"> |
240 | | - <Typography.Text |
| 234 | + </Avatar> |
| 235 | + {/if} |
| 236 | + <Layout.Stack |
| 237 | + direction="row" |
| 238 | + alignItems="center" |
| 239 | + gap="s" |
| 240 | + style="flex: 1; min-width: 0;"> |
| 241 | + <Typography.Text |
| 242 | + truncate |
| 243 | + color="--fgcolor-neutral-secondary" |
| 244 | + style="flex: 1; min-width: 0;"> |
| 245 | + {repo.name} |
| 246 | + </Typography.Text> |
| 247 | + {#if repo.private} |
| 248 | + <Icon |
| 249 | + size="s" |
| 250 | + icon={IconLockClosed} |
| 251 | + color="--fgcolor-neutral-tertiary" /> |
| 252 | + {/if} |
| 253 | + {#if !$isSmallViewport} |
| 254 | + <time datetime={repo.pushedAt}> |
| 255 | + <Typography.Caption |
| 256 | + variant="400" |
241 | 257 | truncate |
242 | | - color="--fgcolor-neutral-secondary" |
243 | | - style="flex: 1; min-width: 0;"> |
244 | | - {repo.name} |
245 | | - </Typography.Text> |
246 | | - {#if repo.private} |
247 | | - <Icon |
248 | | - size="s" |
249 | | - icon={IconLockClosed} |
250 | | - color="--fgcolor-neutral-tertiary" /> |
251 | | - {/if} |
252 | | - {#if !$isSmallViewport} |
253 | | - <time datetime={repo.pushedAt}> |
254 | | - <Typography.Caption |
255 | | - variant="400" |
256 | | - truncate |
257 | | - color="--fgcolor-neutral-tertiary"> |
258 | | - {timeFromNow(repo.pushedAt)} |
259 | | - </Typography.Caption> |
260 | | - </time> |
261 | | - {/if} |
262 | | - </Layout.Stack> |
263 | | - {#if action === 'button'} |
264 | | - <PinkButton.Button |
265 | | - size="xs" |
266 | | - variant="secondary" |
267 | | - style="flex-shrink: 0;" |
268 | | - on:click={() => connect(repo)}> |
269 | | - Connect |
270 | | - </PinkButton.Button> |
271 | | - {/if} |
272 | | - </Layout.Stack> |
273 | | - </Table.Cell> |
274 | | - </Table.Row.Base> |
275 | | - {/each} |
276 | | - </Table.Root> |
277 | | - {/snippet} |
278 | | - </Paginator> |
| 258 | + color="--fgcolor-neutral-tertiary"> |
| 259 | + {timeFromNow(repo.pushedAt)} |
| 260 | + </Typography.Caption> |
| 261 | + </time> |
| 262 | + {/if} |
| 263 | + </Layout.Stack> |
| 264 | + {#if action === 'button'} |
| 265 | + <PinkButton.Button |
| 266 | + size="xs" |
| 267 | + variant="secondary" |
| 268 | + style="flex-shrink: 0;" |
| 269 | + on:click={() => connect(repo)}> |
| 270 | + Connect |
| 271 | + </PinkButton.Button> |
| 272 | + {/if} |
| 273 | + </Layout.Stack> |
| 274 | + </Table.Cell> |
| 275 | + </Table.Row.Base> |
| 276 | + {/each} |
| 277 | + </Table.Root> |
279 | 278 | {:else if search} |
280 | 279 | <EmptySearch hidePages hidePagination bind:search target="repositories"> |
281 | 280 | <svelte:fragment slot="actions"> |
|
293 | 292 | </Layout.Stack> |
294 | 293 | </Card> |
295 | 294 | {/if} |
| 295 | + |
| 296 | + {#if isLoadingRepositories || $repositories.total > 0} |
| 297 | + <Layout.Stack |
| 298 | + direction="row" |
| 299 | + justifyContent="space-between" |
| 300 | + alignItems="center" |
| 301 | + wrap="wrap"> |
| 302 | + <Typography.Text variant="m-400" color="--fgcolor-neutral-secondary"> |
| 303 | + Total results: {$repositories.total} |
| 304 | + </Typography.Text> |
| 305 | + <PaginationInline |
| 306 | + {limit} |
| 307 | + bind:offset |
| 308 | + total={$repositories.total} |
| 309 | + hidePages={true} |
| 310 | + on:change={loadRepositoryPage} /> |
| 311 | + </Layout.Stack> |
| 312 | + {/if} |
296 | 313 | {/if} |
297 | 314 | </Layout.Stack> |
298 | 315 | {:else} |
|
0 commit comments