Feature: loading preview, better text popup preview (#8011)

This commit is contained in:
shamoon
2024-11-12 16:20:52 -08:00
committed by GitHub
parent 74d0c9fda5
commit a283a65813
15 changed files with 192 additions and 95 deletions

View File

@@ -5,7 +5,11 @@
</div>
} @else {
@if (renderAsObject) {
<object [data]="previewURL | safeUrl" width="100%" class="bg-light" [class.p-2]="!isPdf" [class.pdf]="isPdf"></object>
@if (previewText) {
<div class="bg-light p-3 overflow-auto whitespace-preserve" width="100%">{{previewText}}</div>
} @else {
<object [data]="previewURL | safeUrl" width="100%" class="bg-light" [class.p-2]="!isPdf"></object>
}
} @else {
@if (requiresPassword) {
<div class="w-100 h-100 position-relative">

View File

@@ -7,33 +7,3 @@
::ng-deep .popover.popover-preview {
max-width: 32rem;
}
// https://github.com/paperless-ngx/paperless-ngx/issues/7920
// TODO: remove me
@mixin ff_txt {
.preview-popup-container {
width: 30rem !important;
height: 22rem !important;
background-color: #e7e7e7;
}
object:not(.pdf) {
mix-blend-mode: difference;
background: white !important;
&.p-2 {
padding: 0 !important;
}
}
}
@-moz-document url-prefix() {
html[data-bs-theme='dark'] {
@include ff_txt;
}
html[data-bs-theme='auto'] {
@media screen and (prefers-color-scheme: dark) {
@include ff_txt;
}
}
}

View File

@@ -9,13 +9,20 @@ import { provideHttpClientTesting } from '@angular/common/http/testing'
import { DocumentService } from 'src/app/services/rest/document.service'
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
import { PdfViewerModule } from 'ng2-pdf-viewer'
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
import {
HttpClient,
provideHttpClient,
withInterceptorsFromDi,
} from '@angular/common/http'
import { of, throwError } from 'rxjs'
const doc = {
id: 10,
title: 'Document 10',
content: 'Cupcake ipsum dolor sit amet ice cream.',
original_file_name: 'sample.pdf',
archived_file_name: 'sample.pdf',
mime_type: 'application/pdf',
}
describe('PreviewPopupComponent', () => {
@@ -23,6 +30,7 @@ describe('PreviewPopupComponent', () => {
let fixture: ComponentFixture<PreviewPopupComponent>
let settingsService: SettingsService
let documentService: DocumentService
let http: HttpClient
beforeEach(() => {
TestBed.configureTestingModule({
@@ -35,23 +43,22 @@ describe('PreviewPopupComponent', () => {
})
settingsService = TestBed.inject(SettingsService)
documentService = TestBed.inject(DocumentService)
http = TestBed.inject(HttpClient)
jest
.spyOn(documentService, 'getPreviewUrl')
.mockImplementation((id) => doc.original_file_name)
fixture = TestBed.createComponent(PreviewPopupComponent)
component = fixture.componentInstance
component.document = doc
component.document = { ...doc }
fixture.detectChanges()
})
it('should guess if file is pdf by file name', () => {
expect(component.isPdf).toBeTruthy()
component.document.archived_file_name = 'sample.pdf'
it('should correctly report if document is pdf', () => {
expect(component.isPdf).toBeTruthy()
component.document.mime_type = 'application/msword'
expect(component.isPdf).toBeTruthy() // still has archive file
component.document.archived_file_name = undefined
component.document.original_file_name = 'sample.txt'
expect(component.isPdf).toBeFalsy()
component.document.original_file_name = 'sample.pdf'
})
it('should return settings for native PDF viewer', () => {
@@ -84,6 +91,8 @@ describe('PreviewPopupComponent', () => {
it('should fall back to object for non-pdf', () => {
component.document.original_file_name = 'sample.png'
component.document.mime_type = 'image/png'
component.document.archived_file_name = undefined
fixture.detectChanges()
expect(fixture.debugElement.query(By.css('object'))).not.toBeNull()
})
@@ -95,4 +104,22 @@ describe('PreviewPopupComponent', () => {
'Error loading preview'
)
})
it('should get text content from http if appropriate', () => {
component.document = {
...doc,
original_file_name: 'sample.txt',
mime_type: 'text/plain',
}
const httpSpy = jest.spyOn(http, 'get')
httpSpy.mockReturnValueOnce(
throwError(() => new Error('Error getting preview'))
)
component.init()
expect(httpSpy).toHaveBeenCalled()
expect(component.error).toBeTruthy()
httpSpy.mockReturnValueOnce(of('Preview text'))
component.init()
expect(component.previewText).toEqual('Preview text')
})
})

View File

@@ -1,4 +1,6 @@
import { Component, Input } from '@angular/core'
import { HttpClient } from '@angular/common/http'
import { Component, Input, OnDestroy } from '@angular/core'
import { first, Subject, takeUntil } from 'rxjs'
import { Document } from 'src/app/data/document'
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
import { DocumentService } from 'src/app/services/rest/document.service'
@@ -9,14 +11,26 @@ import { SettingsService } from 'src/app/services/settings.service'
templateUrl: './preview-popup.component.html',
styleUrls: ['./preview-popup.component.scss'],
})
export class PreviewPopupComponent {
export class PreviewPopupComponent implements OnDestroy {
private _document: Document
@Input()
document: Document
set document(document: Document) {
this._document = document
this.init()
}
get document(): Document {
return this._document
}
unsubscribeNotifier: Subject<any> = new Subject()
error = false
requiresPassword: boolean = false
previewText: string
get renderAsObject(): boolean {
return (this.isPdf && this.useNativePdfViewer) || !this.isPdf
}
@@ -30,18 +44,38 @@ export class PreviewPopupComponent {
}
get isPdf(): boolean {
// We dont have time to retrieve metadata, make a best guess by file name
return (
this.document?.original_file_name?.endsWith('.pdf') ||
this.document?.archived_file_name?.endsWith('.pdf')
this.document?.archived_file_name?.length > 0 ||
this.document?.mime_type?.includes('pdf')
)
}
constructor(
private settingsService: SettingsService,
private documentService: DocumentService
private documentService: DocumentService,
private http: HttpClient
) {}
ngOnDestroy(): void {
this.unsubscribeNotifier.next(this)
}
init() {
if (this.document.mime_type?.includes('text')) {
this.http
.get(this.previewURL, { responseType: 'text' })
.pipe(first(), takeUntil(this.unsubscribeNotifier))
.subscribe({
next: (res) => {
this.previewText = res.toString()
},
error: (err) => {
this.error = err
},
})
}
}
onError(event: any) {
if (event.name == 'PasswordException') {
this.requiresPassword = true