From ce1dffbbd5c079dcafbc6d36b0a5d8bfa9d11a07 Mon Sep 17 00:00:00 2001
From: shamoon <4887959+shamoon@users.noreply.github.com>
Date: Sat, 5 Sep 2026 21:05:07 -0700
Subject: [PATCH] So we can use pagination
---
.../share-link-bundle-list.component.html | 14 ++++++
.../share-link-bundle-list.component.spec.ts | 46 ++++++++++--------
.../share-link-bundle-list.component.ts | 48 +++++++++++++------
3 files changed, 74 insertions(+), 34 deletions(-)
diff --git a/src-ui/src/app/components/manage/share-links/share-link-bundle-list/share-link-bundle-list.component.html b/src-ui/src/app/components/manage/share-links/share-link-bundle-list/share-link-bundle-list.component.html
index 4f6392c80..3b37baa87 100644
--- a/src-ui/src/app/components/manage/share-links/share-link-bundle-list/share-link-bundle-list.component.html
+++ b/src-ui/src/app/components/manage/share-links/share-link-bundle-list/share-link-bundle-list.component.html
@@ -149,6 +149,20 @@
+ @if (total() > pageSize) {
+
+
+
+ }
}
}
diff --git a/src-ui/src/app/components/manage/share-links/share-link-bundle-list/share-link-bundle-list.component.spec.ts b/src-ui/src/app/components/manage/share-links/share-link-bundle-list/share-link-bundle-list.component.spec.ts
index 6cd659f7d..90e5866d2 100644
--- a/src-ui/src/app/components/manage/share-links/share-link-bundle-list/share-link-bundle-list.component.spec.ts
+++ b/src-ui/src/app/components/manage/share-links/share-link-bundle-list/share-link-bundle-list.component.spec.ts
@@ -13,7 +13,7 @@ import { environment } from 'src/environments/environment'
import { ShareLinkBundleListComponent } from './share-link-bundle-list.component'
class MockShareLinkBundleService {
- listAllBundles = jest.fn()
+ list = jest.fn()
delete = jest.fn()
rebuildBundle = jest.fn()
}
@@ -36,7 +36,7 @@ describe('ShareLinkBundleListComponent', () => {
toastService = new MockToastService()
originalApiBaseUrl = environment.apiBaseUrl
- service.listAllBundles.mockReturnValue(of([]))
+ service.list.mockReturnValue(of({ count: 0, results: [] }))
service.delete.mockReturnValue(of(true))
service.rebuildBundle.mockReturnValue(of(sampleBundle()))
@@ -80,28 +80,28 @@ describe('ShareLinkBundleListComponent', () => {
it('loads bundles on init and polls periodically', () => {
jest.useFakeTimers()
const bundles = [sampleBundle({ status: ShareLinkBundleStatus.Ready })]
- service.listAllBundles.mockReset()
- service.listAllBundles
- .mockReturnValueOnce(of(bundles))
- .mockReturnValue(of(bundles))
+ service.list.mockReset()
+ service.list
+ .mockReturnValueOnce(of({ count: bundles.length, results: bundles }))
+ .mockReturnValue(of({ count: bundles.length, results: bundles }))
fixture.detectChanges()
- expect(service.listAllBundles).toHaveBeenCalledTimes(1)
+ expect(service.list).toHaveBeenCalledWith(1, 25, 'created', true)
expect(component.bundles()).toEqual(bundles)
expect(component.loading()).toBe(false)
expect(component.error()).toBeNull()
jest.advanceTimersByTime(5000)
- expect(service.listAllBundles).toHaveBeenCalledTimes(2)
+ expect(service.list).toHaveBeenCalledTimes(2)
})
it('handles errors when loading bundles', () => {
jest.useFakeTimers()
- service.listAllBundles.mockReset()
- service.listAllBundles
+ service.list.mockReset()
+ service.list
.mockReturnValueOnce(throwError(() => new Error('load fail')))
- .mockReturnValue(of([]))
+ .mockReturnValue(of({ count: 0, results: [] }))
fixture.detectChanges()
@@ -110,7 +110,15 @@ describe('ShareLinkBundleListComponent', () => {
expect(component.loading()).toBe(false)
jest.advanceTimersByTime(5000)
- expect(service.listAllBundles).toHaveBeenCalledTimes(2)
+ expect(service.list).toHaveBeenCalledTimes(2)
+ })
+
+ it('loads another page', () => {
+ fixture.detectChanges()
+
+ component.setPage(2)
+
+ expect(service.list).toHaveBeenLastCalledWith(2, 25, 'created', true)
})
it('copies bundle links when ready', () => {
@@ -150,7 +158,7 @@ describe('ShareLinkBundleListComponent', () => {
})
it('deletes bundles and refreshes list', () => {
- service.listAllBundles.mockReturnValue(of([]))
+ service.list.mockReturnValue(of({ count: 0, results: [] }))
service.delete.mockReturnValue(of(true))
fixture.detectChanges()
@@ -161,12 +169,12 @@ describe('ShareLinkBundleListComponent', () => {
expect(toastService.showInfo).toHaveBeenCalledWith(
expect.stringContaining('deleted.')
)
- expect(service.listAllBundles).toHaveBeenCalledTimes(2)
+ expect(service.list).toHaveBeenCalledTimes(2)
expect(component.loading()).toBe(false)
})
it('handles delete errors gracefully', () => {
- service.listAllBundles.mockReturnValue(of([]))
+ service.list.mockReturnValue(of({ count: 0, results: [] }))
service.delete.mockReturnValue(throwError(() => new Error('delete fail')))
fixture.detectChanges()
@@ -178,7 +186,7 @@ describe('ShareLinkBundleListComponent', () => {
})
it('retries bundle build and replaces existing entry', () => {
- service.listAllBundles.mockReturnValue(of([]))
+ service.list.mockReturnValue(of({ count: 0, results: [] }))
const updated = sampleBundle({ status: ShareLinkBundleStatus.Ready })
service.rebuildBundle.mockReturnValue(of(updated))
@@ -193,7 +201,7 @@ describe('ShareLinkBundleListComponent', () => {
})
it('adds new bundle when retry returns unknown entry', () => {
- service.listAllBundles.mockReturnValue(of([]))
+ service.list.mockReturnValue(of({ count: 0, results: [] }))
service.rebuildBundle.mockReturnValue(
of(sampleBundle({ id: 99, slug: 'new-slug' }))
)
@@ -207,7 +215,7 @@ describe('ShareLinkBundleListComponent', () => {
})
it('handles retry errors', () => {
- service.listAllBundles.mockReturnValue(of([]))
+ service.list.mockReturnValue(of({ count: 0, results: [] }))
service.rebuildBundle.mockReturnValue(throwError(() => new Error('fail')))
fixture.detectChanges()
@@ -218,7 +226,7 @@ describe('ShareLinkBundleListComponent', () => {
})
it('maps status and file version helpers', () => {
- service.listAllBundles.mockReturnValue(of([]))
+ service.list.mockReturnValue(of({ count: 0, results: [] }))
fixture.detectChanges()
expect(component.statusLabel(ShareLinkBundleStatus.Processing)).toContain(
diff --git a/src-ui/src/app/components/manage/share-links/share-link-bundle-list/share-link-bundle-list.component.ts b/src-ui/src/app/components/manage/share-links/share-link-bundle-list/share-link-bundle-list.component.ts
index a6d3c34f3..b01206717 100644
--- a/src-ui/src/app/components/manage/share-links/share-link-bundle-list/share-link-bundle-list.component.ts
+++ b/src-ui/src/app/components/manage/share-links/share-link-bundle-list/share-link-bundle-list.component.ts
@@ -1,7 +1,10 @@
import { Clipboard } from '@angular/cdk/clipboard'
import { CommonModule } from '@angular/common'
import { Component, OnDestroy, OnInit, inject, signal } from '@angular/core'
-import { NgbPopoverModule } from '@ng-bootstrap/ng-bootstrap'
+import {
+ NgbPaginationModule,
+ NgbPopoverModule,
+} from '@ng-bootstrap/ng-bootstrap'
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
import { Subject, catchError, of, switchMap, takeUntil, timer } from 'rxjs'
import { FileVersion } from 'src/app/data/share-link'
@@ -25,6 +28,7 @@ import { LoadingComponentWithPermissions } from 'src/app/components/loading-comp
imports: [
ConfirmButtonComponent,
CommonModule,
+ NgbPaginationModule,
NgbPopoverModule,
NgxBootstrapIconsModule,
FileSizePipe,
@@ -41,6 +45,9 @@ export class ShareLinkBundleListComponent
readonly bundles = signal([])
readonly error = signal(null)
readonly copiedSlug = signal(null)
+ readonly total = signal(0)
+ readonly page = signal(1)
+ readonly pageSize = 25
readonly statuses = ShareLinkBundleStatus
readonly fileVersions = FileVersion
@@ -55,25 +62,28 @@ export class ShareLinkBundleListComponent
this.loading.set(true)
}
this.error.set(null)
- return this.shareLinkBundleService.listAllBundles().pipe(
- catchError((error) => {
- if (!silent) {
- this.loading.set(false)
- }
- this.error.set($localize`Failed to load share link bundles.`)
- this.toastService.showError(
- $localize`Error retrieving share link bundles.`,
- error
- )
- return of(null)
- })
- )
+ return this.shareLinkBundleService
+ .list(this.page(), this.pageSize, 'created', true)
+ .pipe(
+ catchError((error) => {
+ if (!silent) {
+ this.loading.set(false)
+ }
+ this.error.set($localize`Failed to load share link bundles.`)
+ this.toastService.showError(
+ $localize`Error retrieving share link bundles.`,
+ error
+ )
+ return of(null)
+ })
+ )
}),
takeUntil(this.unsubscribeNotifier)
)
.subscribe((results) => {
if (results) {
- this.bundles.set(results)
+ this.bundles.set(results.results)
+ this.total.set(results.count)
this.copiedSlug.set(null)
}
this.loading.set(false)
@@ -96,6 +106,11 @@ export class ShareLinkBundleListComponent
}`
}
+ setPage(page: number): void {
+ this.page.set(page)
+ this.triggerRefresh(false)
+ }
+
copy(bundle: ShareLinkBundleSummary): void {
if (bundle.status !== ShareLinkBundleStatus.Ready) {
return
@@ -114,6 +129,9 @@ export class ShareLinkBundleListComponent
this.loading.set(true)
this.shareLinkBundleService.delete(bundle).subscribe({
next: () => {
+ if (this.bundles().length === 1 && this.page() > 1) {
+ this.page.update((page) => page - 1)
+ }
this.toastService.showInfo($localize`Share link bundle deleted.`)
this.triggerRefresh(false)
},