From 4070cd0e1b03f2e122a8cf70ade879b79c359b61 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Wed, 22 Jan 2025 12:13:35 -0800 Subject: [PATCH] Just save this [ci skip] --- .../preview-popup.component.html | 16 +- .../preview-popup.component.scss | 10 + .../preview-popup/preview-popup.component.ts | 10 + .../document-list.component.html | 565 ++++++++++-------- .../document-list.component.scss | 6 + .../document-list/document-list.component.ts | 65 +- .../services/document-list-view.service.ts | 20 + src-ui/src/main.ts | 2 + 8 files changed, 422 insertions(+), 272 deletions(-) diff --git a/src-ui/src/app/components/common/preview-popup/preview-popup.component.html b/src-ui/src/app/components/common/preview-popup/preview-popup.component.html index 59c179832..8261db465 100644 --- a/src-ui/src/app/components/common/preview-popup/preview-popup.component.html +++ b/src-ui/src/app/components/common/preview-popup/preview-popup.component.html @@ -1,10 +1,14 @@ - - - +@if (!previewOnly) { + + + +} @else { + +} -
+
@if (error) {

Error loading preview

diff --git a/src-ui/src/app/components/common/preview-popup/preview-popup.component.scss b/src-ui/src/app/components/common/preview-popup/preview-popup.component.scss index af8dc565a..ab8f8067c 100644 --- a/src-ui/src/app/components/common/preview-popup/preview-popup.component.scss +++ b/src-ui/src/app/components/common/preview-popup/preview-popup.component.scss @@ -4,6 +4,16 @@ overflow-y: scroll; } +.preview-popup-container.full-size { + width: 100% !important; + height: 100% !important; + + > * { + width: 100% !important; + height: 100% !important; + } +} + ::ng-deep .popover.popover-preview { max-width: 32rem; } diff --git a/src-ui/src/app/components/common/preview-popup/preview-popup.component.ts b/src-ui/src/app/components/common/preview-popup/preview-popup.component.ts index 039264fd0..e2c0edb6c 100644 --- a/src-ui/src/app/components/common/preview-popup/preview-popup.component.ts +++ b/src-ui/src/app/components/common/preview-popup/preview-popup.component.ts @@ -1,3 +1,4 @@ +import { NgTemplateOutlet } from '@angular/common' import { HttpClient } from '@angular/common/http' import { Component, Input, OnDestroy, ViewChild } from '@angular/core' import { NgbPopover, NgbPopoverModule } from '@ng-bootstrap/ng-bootstrap' @@ -17,6 +18,7 @@ import { SettingsService } from 'src/app/services/settings.service' styleUrls: ['./preview-popup.component.scss'], imports: [ NgbPopoverModule, + NgTemplateOutlet, DocumentTitlePipe, PdfViewerModule, SafeUrlPipe, @@ -47,6 +49,9 @@ export class PreviewPopupComponent implements OnDestroy { @Input() linkTitle: string = $localize`Open preview` + @Input() + previewOnly: boolean = false + unsubscribeNotifier: Subject = new Subject() error = false @@ -91,6 +96,8 @@ export class PreviewPopupComponent implements OnDestroy { } init() { + this.error = false + this.requiresPassword = false if (this.document.mime_type?.includes('text')) { this.http .get(this.previewURL, { responseType: 'text' }) @@ -119,6 +126,7 @@ export class PreviewPopupComponent implements OnDestroy { } mouseEnterPreview() { + if (this.previewOnly) return this.mouseOnPreview = true if (!this.popover.isOpen()) { // we're going to open but hide to pre-load content during hover delay @@ -136,10 +144,12 @@ export class PreviewPopupComponent implements OnDestroy { } mouseLeavePreview() { + if (this.previewOnly) return this.mouseOnPreview = false } public close(immediate: boolean = false) { + if (this.previewOnly) return setTimeout( () => { if (!this.mouseOnPreview) this.popover.close() diff --git a/src-ui/src/app/components/document-list/document-list.component.html b/src-ui/src/app/components/document-list/document-list.component.html index 65d291464..abe00a733 100644 --- a/src-ui/src/app/components/document-list/document-list.component.html +++ b/src-ui/src/app/components/document-list/document-list.component.html @@ -27,6 +27,7 @@
+
+
+ + +
+
- - -
-
- @if (list.isReloading) { -
- Loading... - } - @if (list.selected.size > 0) { - {list.collectionSize, plural, =1 {Selected {{list.selected.size}} of one document} other {Selected {{list.selected.size}} of {{list.collectionSize || 0}} documents}} - } - @if (!list.isReloading) { - @if (list.selected.size === 0) { - {list.collectionSize, plural, =1 {One document} other {{{list.collectionSize || 0}} documents}} - } @if (isFiltered) { -  (filtered) +
+
+ +
+
+ @if (list.isReloading) { +
+ Loading... } - } - @if (!list.isReloading && isFiltered) { - + @if (list.selected.size > 0) { + {list.collectionSize, plural, =1 {Selected {{list.selected.size}} of one document} other {Selected {{list.selected.size}} of {{list.collectionSize || 0}} documents}} + } + @if (!list.isReloading) { + @if (list.selected.size === 0) { + {list.collectionSize, plural, =1 {One document} other {{{list.collectionSize || 0}} documents}} + } @if (isFiltered) { +  (filtered) + } + } + @if (!list.isReloading && isFiltered) { + + } +
+ @if (list.collectionSize) { + }
- @if (list.collectionSize) { - - } +
+ +
+
- -
- -
- - @if (list.error ) { - - } @else { - @if (list.displayMode === DisplayMode.LARGE_CARDS) { -
- @for (d of list.documents; track d.id) { - - - } -
- } - @if (list.displayMode === DisplayMode.TABLE) { -
- - - - - @if (activeDisplayFields.includes(DisplayField.ASN)) { - - } - @if (activeDisplayFields.includes(DisplayField.CORRESPONDENT) && permissionService.currentUserCan(PermissionAction.View, PermissionType.Correspondent)) { - - } - @if (activeDisplayFields.includes(DisplayField.TITLE)) { - - } - @if (activeDisplayFields.includes(DisplayField.TAGS) && !activeDisplayFields.includes(DisplayField.TITLE)) { - - } - @if (activeDisplayFields.includes(DisplayField.OWNER) && permissionService.currentUserCan(PermissionAction.View, PermissionType.User)) { - - } - @if (activeDisplayFields.includes(DisplayField.NOTES) && notesEnabled) { - - } - @if (activeDisplayFields.includes(DisplayField.DOCUMENT_TYPE) && permissionService.currentUserCan(PermissionAction.View, PermissionType.DocumentType)) { - - } - @if (activeDisplayFields.includes(DisplayField.STORAGE_PATH) && permissionService.currentUserCan(PermissionAction.View, PermissionType.StoragePath)) { - - } - @if (activeDisplayFields.includes(DisplayField.CREATED)) { - - } - @if (activeDisplayFields.includes(DisplayField.ADDED)) { - - } - @if (activeDisplayFields.includes(DisplayField.PAGE_COUNT)) { + @if (list.error ) { + + } @else { + @if (list.displayMode === DisplayMode.LARGE_CARDS) { +
+ @for (d of list.documents; track d.id) { + + + } +
+ } + @if (list.displayMode === DisplayMode.TABLE) { +
+
ASNCorrespondentTitleTagsOwnerNotesDocument typeStorage pathCreatedAdded
+ + + + @if (activeDisplayFields.includes(DisplayField.ASN)) { - } - @if (activeDisplayFields.includes(DisplayField.SHARED)) { - - } - @for (field_id of activeDisplayCustomFields; track field_id) { - - } - - - - @for (d of list.documents; track d.id) { - - - @if (activeDisplayFields.includes(DisplayField.ASN)) { - + i18n>ASN } @if (activeDisplayFields.includes(DisplayField.CORRESPONDENT) && permissionService.currentUserCan(PermissionAction.View, PermissionType.Correspondent)) { - + } - @if (activeDisplayFields.includes(DisplayField.TITLE) || activeDisplayFields.includes(DisplayField.TAGS)) { - + @if (activeDisplayFields.includes(DisplayField.TITLE)) { + + } + @if (activeDisplayFields.includes(DisplayField.TAGS) && !activeDisplayFields.includes(DisplayField.TITLE)) { + } @if (activeDisplayFields.includes(DisplayField.OWNER) && permissionService.currentUserCan(PermissionAction.View, PermissionType.User)) { - + } @if (activeDisplayFields.includes(DisplayField.NOTES) && notesEnabled) { - + } @if (activeDisplayFields.includes(DisplayField.DOCUMENT_TYPE) && permissionService.currentUserCan(PermissionAction.View, PermissionType.DocumentType)) { - + } @if (activeDisplayFields.includes(DisplayField.STORAGE_PATH) && permissionService.currentUserCan(PermissionAction.View, PermissionType.StoragePath)) { - + } @if (activeDisplayFields.includes(DisplayField.CREATED)) { - + } @if (activeDisplayFields.includes(DisplayField.ADDED)) { - + } @if (activeDisplayFields.includes(DisplayField.PAGE_COUNT)) { - + } @if (activeDisplayFields.includes(DisplayField.SHARED)) { - + } - @for (field of activeDisplayCustomFields; track field) { - + @for (field_id of activeDisplayCustomFields; track field_id) { + } - } - -
Pages - Shared - - {{getDisplayCustomFieldTitle(field_id)}} -
-
- - -
-
- {{d.archive_serial_number}} - - @if (d.correspondent) { - {{(d.correspondent$ | async)?.name}} - } - Correspondent - @if (activeDisplayFields.includes(DisplayField.TITLE)) { - - } - @if (activeDisplayFields.includes(DisplayField.TAGS)) { - @for (t of d.tags$ | async; track t) { - - } - } - TitleTags - {{d.owner | username}} - Owner - @if (d.notes.length) { - - - - {{d.notes.length}} - - } - Notes - @if (d.document_type) { - {{(d.document_type$ | async)?.name}} - } - Document type - @if (d.storage_path) { - {{(d.storage_path$ | async)?.name}} - } - Storage path - {{d.created_date | customDate}} - Created - {{d.added | customDate}} - Added - {{ d.page_count }} - Pages - @if (d.is_shared_by_requester) { Yes } @else { No } - + Shared + - - + {{getDisplayCustomFieldTitle(field_id)}} +
-
+ + + @for (d of list.documents; track d.id) { + + +
+ + +
+ + @if (activeDisplayFields.includes(DisplayField.ASN)) { + + {{d.archive_serial_number}} + + } + @if (activeDisplayFields.includes(DisplayField.CORRESPONDENT) && permissionService.currentUserCan(PermissionAction.View, PermissionType.Correspondent)) { + + @if (d.correspondent) { + {{(d.correspondent$ | async)?.name}} + } + + } + @if (activeDisplayFields.includes(DisplayField.TITLE) || activeDisplayFields.includes(DisplayField.TAGS)) { + + @if (activeDisplayFields.includes(DisplayField.TITLE)) { + + } + @if (activeDisplayFields.includes(DisplayField.TAGS)) { + @for (t of d.tags$ | async; track t) { + + } + } + + } + @if (activeDisplayFields.includes(DisplayField.OWNER) && permissionService.currentUserCan(PermissionAction.View, PermissionType.User)) { + + {{d.owner | username}} + + } + @if (activeDisplayFields.includes(DisplayField.NOTES) && notesEnabled) { + + @if (d.notes.length) { + + + + {{d.notes.length}} + + } + + } + @if (activeDisplayFields.includes(DisplayField.DOCUMENT_TYPE) && permissionService.currentUserCan(PermissionAction.View, PermissionType.DocumentType)) { + + @if (d.document_type) { + {{(d.document_type$ | async)?.name}} + } + + } + @if (activeDisplayFields.includes(DisplayField.STORAGE_PATH) && permissionService.currentUserCan(PermissionAction.View, PermissionType.StoragePath)) { + + @if (d.storage_path) { + {{(d.storage_path$ | async)?.name}} + } + + } + @if (activeDisplayFields.includes(DisplayField.CREATED)) { + + {{d.created_date | customDate}} + + } + @if (activeDisplayFields.includes(DisplayField.ADDED)) { + + {{d.added | customDate}} + + } + @if (activeDisplayFields.includes(DisplayField.PAGE_COUNT)) { + + {{ d.page_count }} + + } + @if (activeDisplayFields.includes(DisplayField.SHARED)) { + + @if (d.is_shared_by_requester) { Yes } @else { No } + + } + @for (field of activeDisplayCustomFields; track field) { + + + + } + + } + + +
+ } + @if (list.displayMode === DisplayMode.SMALL_CARDS) { +
+ @for (d of list.documents; track d.id) { + + + } +
+ } + @if (list.documents?.length > 15) { +
+ +
+ } } - @if (list.displayMode === DisplayMode.SMALL_CARDS) { -
- @for (d of list.documents; track d.id) { - - - } +
+ @if (list.showPreviewPane) { +
+
+
+
+ + +
+
+
+ {{list.firstSelectedDocument?.title}} +
+
+
- } - @if (list.documents?.length > 15) { -
- +
+
+ @if (list.selected.size > 0) { + + } @else { +
+

+ No document selected +

+
+ } +
- } +
} +
diff --git a/src-ui/src/app/components/document-list/document-list.component.scss b/src-ui/src/app/components/document-list/document-list.component.scss index 0e10b83da..29512efaf 100644 --- a/src-ui/src/app/components/document-list/document-list.component.scss +++ b/src-ui/src/app/components/document-list/document-list.component.scss @@ -80,3 +80,9 @@ a { pngx-page-header .dropdown-menu { --bs-dropdown-min-width: 12em; } + +.preview-pane { + height: 60rem; + top: 70px; + position: sticky; +} diff --git a/src-ui/src/app/components/document-list/document-list.component.ts b/src-ui/src/app/components/document-list/document-list.component.ts index a19ac341d..b2617ecbf 100644 --- a/src-ui/src/app/components/document-list/document-list.component.ts +++ b/src-ui/src/app/components/document-list/document-list.component.ts @@ -326,24 +326,36 @@ export class DocumentListComponent this.hotKeyService .addShortcut({ keys: 'control.arrowleft', - description: $localize`Previous page`, + description: $localize`Previous page / document`, }) .pipe(takeUntil(this.unsubscribeNotifier)) .subscribe(() => { - if (this.list.currentPage > 1) { - this.list.currentPage-- + if (this.list.showPreviewPane) { + if (this.hasPrevious) { + this.previousDoc() + } + } else { + if (this.list.currentPage > 1) { + this.list.currentPage-- + } } }) this.hotKeyService .addShortcut({ keys: 'control.arrowright', - description: $localize`Next page`, + description: $localize`Next page / document`, }) .pipe(takeUntil(this.unsubscribeNotifier)) .subscribe(() => { - if (this.list.currentPage < this.list.getLastPage()) { - this.list.currentPage++ + if (this.list.showPreviewPane) { + if (this.hasNext) { + this.nextDoc() + } + } else { + if (this.list.currentPage < this.list.getLastPage()) { + this.list.currentPage++ + } } }) } @@ -473,4 +485,45 @@ export class DocumentListComponent resetFilters() { this.filterEditor.resetSelected() } + + public get hasPrevious(): boolean { + return ( + (this.list.selected.size > 0 && + this.list.documents.indexOf(this.list.firstSelectedDocument) > 0) || + (this.list.selected.size === 0 && this.list.documents.length > 0) + ) + } + + public get hasNext(): boolean { + return ( + (this.list.selected.size > 0 && + this.list.documents.indexOf(this.list.firstSelectedDocument) < + this.list.documents.length - 1) || + (this.list.selected.size === 0 && this.list.documents.length > 0) + ) + } + + public nextDoc(): void { + const index = + this.list.selected.size === 0 + ? 0 + : Math.min( + this.list.documents.indexOf(this.list.firstSelectedDocument) + 1, + this.list.documents.length - 1 + ) + this.list.selected.clear() + this.list.selected.add(this.list.documents[index].id) + } + + public previousDoc(): void { + const index = + this.list.selected.size === 0 + ? 0 + : Math.max( + this.list.documents.indexOf(this.list.firstSelectedDocument) - 1, + 0 + ) + this.list.selected.clear() + this.list.selected.add(this.list.documents[index].id) + } } diff --git a/src-ui/src/app/services/document-list-view.service.ts b/src-ui/src/app/services/document-list-view.service.ts index 09d83a2fb..bea73578d 100644 --- a/src-ui/src/app/services/document-list-view.service.ts +++ b/src-ui/src/app/services/document-list-view.service.ts @@ -79,6 +79,11 @@ export interface ListViewState { * The fields to display in the document list. */ displayFields?: DisplayField[] + + /** + * Whether the preview pane is shown. + */ + showPreviewPane?: boolean } /** @@ -165,6 +170,7 @@ export class DocumentListViewService { sortReverse: true, filterRules: [], selected: new Set(), + showPreviewPane: false, } } @@ -451,6 +457,15 @@ export class DocumentListViewService { this.saveDocumentListView() } + get showPreviewPane(): boolean { + return this.activeListViewState.showPreviewPane + } + + set showPreviewPane(show: boolean) { + this.activeListViewState.showPreviewPane = show + this.saveDocumentListView() + } + private saveDocumentListView() { if (this._activeSavedViewId == null) { let savedState: ListViewState = { @@ -461,6 +476,7 @@ export class DocumentListViewService { sortReverse: this.activeListViewState.sortReverse, displayMode: this.activeListViewState.displayMode, displayFields: this.activeListViewState.displayFields, + showPreviewPane: this.activeListViewState.showPreviewPane, } localStorage.setItem( DOCUMENT_LIST_SERVICE.CURRENT_VIEW_CONFIG, @@ -626,4 +642,8 @@ export class DocumentListViewService { documentIndexInCurrentView(documentID: number): number { return this.documents.map((d) => d.id).indexOf(documentID) } + + get firstSelectedDocument(): Document { + return this.documents.find((d) => this.selected.has(d.id)) + } } diff --git a/src-ui/src/main.ts b/src-ui/src/main.ts index 998ebf260..774bdcb10 100644 --- a/src-ui/src/main.ts +++ b/src-ui/src/main.ts @@ -125,6 +125,7 @@ import { trash, uiRadios, upcScan, + windowSplit, windowStack, x, xCircle, @@ -323,6 +324,7 @@ const icons = { trash, uiRadios, upcScan, + windowSplit, windowStack, x, xCircle,