Skip to content

Commit

Permalink
⚡️ show thumbnails asap
Browse files Browse the repository at this point in the history
  • Loading branch information
alexfriesen committed Oct 12, 2023
1 parent e4fcca2 commit da80d77
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 30 deletions.
6 changes: 1 addition & 5 deletions src/app/components/preview/preview.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,7 @@
<mat-icon>delete</mat-icon>
</button>

@if(previewRenders(); as previews) {
<img [src]="previews | preview : page" />
} @else {
<mat-progress-spinner mode="indeterminate" />
}
<app-thumb [pageIndex]="page" />
</div>
</span>
} @empty {}
Expand Down
11 changes: 0 additions & 11 deletions src/app/components/preview/preview.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@
}
}

mat-progress-spinner {
margin: 2rem;
}

.list {
display: flex;
flex-wrap: wrap;
Expand All @@ -40,13 +36,6 @@ mat-progress-spinner {
&:hover {
border-color: #3f51b5;
}

img {
width: 100%;
height: 100%;
object-fit: contain;
pointer-events: none;
}
}

button.remove {
Expand Down
5 changes: 3 additions & 2 deletions src/app/components/preview/preview.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { TranslocoModule } from '@ngneat/transloco';

import { DocumentService } from '@app/services/document.service';
import { PreviewService } from '@app/services/preview.service';
import { PreviewPipe } from '@app/pipes/preview.pipe';
import { ThumbnailComponent } from '../thumb/thumb.component';

@Component({
selector: 'app-preview',
Expand All @@ -29,7 +29,8 @@ import { PreviewPipe } from '@app/pipes/preview.pipe';
DragDropModule,
MixedCdkDragDropModule,
TranslocoModule,
PreviewPipe,

ThumbnailComponent,
],
})
export class PreviewComponent {
Expand Down
5 changes: 5 additions & 0 deletions src/app/components/thumb/thumb.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@if(imageSrc()) {
<img [src]="imageSrc()" />
} @else {
<mat-progress-spinner mode="indeterminate" />
}
14 changes: 14 additions & 0 deletions src/app/components/thumb/thumb.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
:host {
display: grid;
}

mat-progress-spinner {
margin: 2rem;
}

img {
width: 100%;
height: 100%;
object-fit: contain;
pointer-events: none;
}
39 changes: 39 additions & 0 deletions src/app/components/thumb/thumb.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import {
Component,
ChangeDetectionStrategy,
inject,
Input,
computed,
signal,
} from '@angular/core';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';

import { PreviewService } from '@app/services/preview.service';

@Component({
selector: 'app-thumb',
templateUrl: './thumb.component.html',
styleUrls: ['./thumb.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [MatProgressSpinnerModule],
})
export class ThumbnailComponent {
private readonly pagesPreviews = inject(PreviewService).pagesPreviews;

@Input()
set pageIndex(value: number) {
this.currentPageIndex.set(value);
}

readonly currentPageIndex = signal<number | undefined>(undefined);

readonly imageSrc = computed(() => {
const index = this.currentPageIndex();
const found = this.pagesPreviews()?.find(
(preview) => preview.pageIndex === index
);

return found?.base64 || undefined;
});
}
36 changes: 24 additions & 12 deletions src/app/services/preview.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { inject, Injectable, signal } from '@angular/core';
import { BehaviorSubject, debounceTime, switchMap, tap } from 'rxjs';
import { switchMap, tap } from 'rxjs';
import {
getDocument,
GlobalWorkerOptions,
Expand Down Expand Up @@ -27,39 +27,37 @@ export class PreviewService {

this.documentService.documentBuffer$
.pipe(
debounceTime(100),
tap(() => this.isProcessing.set(true)),
switchMap(async (buffer) => this.generatePagePreviews(buffer)),
tap((data) => this.pagesPreviews.set(data)),
switchMap((buffer) => this.generatePagePreviews(buffer)),
tap(() => this.isProcessing.set(false))
)
.subscribe();
}

private async generatePagePreviews(buffer: Uint8Array | null) {
if (!buffer) return null;
if (!buffer) return false;

const task = getDocument({ data: buffer });
const doc = await task.promise;

return Promise.all(
await Promise.all(
Array.from(Array(doc.numPages).keys()).map((pageNumber) =>
this.renderPagePreview(doc, pageNumber)
)
);

return true;
}

private async renderPagePreview(
doc: PDFDocumentProxy,
pageIndex: number
): Promise<PagePreview> {
private async renderPagePreview(doc: PDFDocumentProxy, pageIndex: number) {
const page = await doc.getPage(pageIndex + 1);

const viewport = page.getViewport({ scale: 0.5 });

const canvas = document.createElement('canvas');
const canvasContext = canvas.getContext('2d', {
willReadFrequently: true,
alpha: false,
})!;

canvas.height = viewport.height;
Expand All @@ -68,9 +66,23 @@ export class PreviewService {
const task = page.render({ canvasContext, viewport });
await task.promise;

return {
const base64 = canvas.toDataURL();
page.cleanup();

const data = {
pageIndex,
base64: canvas.toDataURL(),
base64,
};

this.addOrReplacePreview(data);
}

private async addOrReplacePreview(entry: PagePreview) {
this.pagesPreviews.update((data) => {
if (!data) return [entry];

const filteredData = data?.filter((p) => p.pageIndex !== entry.pageIndex);
return [...filteredData, entry];
});
}
}

0 comments on commit da80d77

Please sign in to comment.