Add lightboxes and fix memory leak problems

Signed-off-by: trivernis <trivernis@protonmail.com>
pull/4/head
trivernis 3 years ago
parent f3446c8696
commit 1b1451d910

@ -35,6 +35,7 @@
"src/assets"
],
"styles": [
"./node_modules/ngx-lightbox/lightbox.css",
"src/styles.scss"
],
"scripts": []

@ -23,6 +23,7 @@
"@angular/platform-browser-dynamic": "~12.2.0",
"@angular/router": "~12.2.0",
"@tauri-apps/api": "^1.0.0-beta.8",
"ngx-lightbox": "^2.5.1",
"primeicons": "^4.1.0",
"primeng": "^12.2.1",
"rxjs": "~6.6.0",
@ -39,6 +40,7 @@
"@angular/cli": "~12.2.9",
"@angular/compiler-cli": "~12.2.0",
"@tauri-apps/cli": "^1.0.0-beta.10",
"@types/file-saver": "^2.0.3",
"@types/jasmine": "~3.8.0",
"@types/node": "^12.11.1",
"@typescript-eslint/eslint-plugin": "4.28.2",

@ -23,6 +23,7 @@ import { FileGridEntryComponent } from './components/file-grid/file-grid-entry/f
import {MatProgressBarModule} from "@angular/material/progress-bar";
import {MatPaginatorModule} from "@angular/material/paginator";
import {ScrollingModule} from "@angular/cdk/scrolling";
import {LightboxModule} from "ngx-lightbox";
@NgModule({
declarations: [
@ -51,6 +52,7 @@ import {ScrollingModule} from "@angular/cdk/scrolling";
MatProgressBarModule,
MatPaginatorModule,
ScrollingModule,
LightboxModule
],
providers: [],
bootstrap: [AppComponent]

@ -1,4 +1,4 @@
<mat-card #card>
<mat-card #card (click)="clickEvent.emit(file)" (dblclick)="dblClickEvent.emit(file)">
<mat-card-title *ngIf="!!file?.name">{{file?.name}}</mat-card-title>
<mat-card-content *ngIf="contentUrl !== undefined">
<img *ngIf="contentUrl !== undefined" class="entry-image" loading="lazy" decoding="async" [src]="contentUrl" alt="File Image">

@ -1,26 +1,29 @@
import {
AfterContentChecked,
Component,
Input,
OnInit,
ViewChild,
ElementRef, AfterViewInit
ElementRef, Output, EventEmitter, OnDestroy
} from '@angular/core';
import {File} from "../../../models/File";
import {FileService} from "../../../services/file/file.service";
import {ErrorBrokerService} from "../../../services/error-broker/error-broker.service";
import {SafeResourceUrl} from "@angular/platform-browser";
import {MatCard} from "@angular/material/card";
import {Thumbnail} from "../../../models/Thumbnail";
@Component({
selector: 'app-file-grid-entry',
templateUrl: './file-grid-entry.component.html',
styleUrls: ['./file-grid-entry.component.scss']
})
export class FileGridEntryComponent implements OnInit {
export class FileGridEntryComponent implements OnInit, OnDestroy {
@ViewChild("card") card!: ElementRef;
@Input() file!: File;
@Output() clickEvent = new EventEmitter<File>();
@Output() dblClickEvent = new EventEmitter<File>();
selectedThumbnail: Thumbnail | undefined;
contentUrl: SafeResourceUrl | undefined;
constructor(private fileService: FileService, private errorBroker: ErrorBrokerService) { }
@ -29,11 +32,24 @@ export class FileGridEntryComponent implements OnInit {
await this.loadImage();
}
public ngOnDestroy(): void {
if (this.contentUrl) {
const url = this.contentUrl;
this.contentUrl = undefined;
URL?.revokeObjectURL(url as string);
}
}
async loadImage() {
try {
const thumbnails = await this.fileService.getThumbnails(this.file.hash);
let thumbnail = thumbnails.find(t => (t.height > 250 || t.width > 250) && (t.height < 500 && t.width < 500));
this.contentUrl = await this.fileService.readThumbnail(thumbnail!!);
this.selectedThumbnail = thumbnail;
if (!thumbnail) {
console.log("Thumbnail is empty?!", thumbnails);
} else {
this.contentUrl = await this.fileService.readThumbnail(thumbnail!!);
}
} catch (err) {
this.errorBroker.showError(err);
}

@ -1,7 +1,8 @@
<cdk-virtual-scroll-viewport class="file-scroll" maxBufferPx="500" minBufferPx="500" itemSize="250">
<div *cdkVirtualFor="let fileRow of fileRows">
<div class="file-row">
<app-file-grid-entry *ngFor="let file of fileRow" [file]="file"></app-file-grid-entry>
<app-file-grid-entry (clickEvent)="fileClickEvent.emit($event)" (dblClickEvent)="fileDblClickEvent.emit($event)"
*ngFor="let file of fileRow" [file]="file"></app-file-grid-entry>
</div>
</div>
</cdk-virtual-scroll-viewport>

@ -1,4 +1,4 @@
import {Component, Input, OnInit} from '@angular/core';
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import {File} from "../../models/File";
import {FileService} from "../../services/file/file.service";
@ -10,6 +10,8 @@ import {FileService} from "../../services/file/file.service";
export class FileGridComponent {
@Input() fileRows: File[][] = [];
@Output() fileDblClickEvent = new EventEmitter<File>();
@Output() fileClickEvent = new EventEmitter<File>();
constructor() { }
}

@ -7,7 +7,7 @@
<p>Drawer</p>
</mat-drawer>
<mat-drawer-content>
<app-file-grid [fileRows]="fileRows"></app-file-grid>
<app-file-grid (fileDblClickEvent)="openFile($event)" [fileRows]="fileRows"></app-file-grid>
</mat-drawer-content>
</mat-drawer-container>

@ -2,6 +2,8 @@ import { Component, OnInit } from '@angular/core';
import {FileService} from "../../services/file/file.service";
import {File} from "../../models/File";
import {PageEvent} from "@angular/material/paginator";
import {Lightbox, LIGHTBOX_EVENT, LightboxEvent} from "ngx-lightbox";
import {SafeResourceUrl} from "@angular/platform-browser";
@Component({
selector: 'app-home',
@ -14,7 +16,7 @@ export class HomeComponent implements OnInit {
page: number = 0;
pageSize: number = 25;
constructor(private fileService: FileService) { }
constructor(private fileService: FileService, private lightbox: Lightbox, private lightboxEvent: LightboxEvent) { }
async ngOnInit() {
this.fileService.displayedFiles.subscribe((files) => this.setFileRows(files));
@ -29,4 +31,28 @@ export class HomeComponent implements OnInit {
}
console.log(this.fileRows);
}
async openFile(file: File) {
let url = await this.fileService.readFile(file.hash, file.mime_type ?? "image/png");
let albums = [
{
src: url as string,
caption: file.name ?? file.comment,
thumb: url as string,
}
];
this.lightbox.open(albums, 0, {
disableScrolling: true,
showImageNumberLabel: false,
showDownloadButton: true,
centerVertically: true,
});
const lighboxSubscription = this.lightboxEvent.lightboxEvent$.subscribe((event: any) => {
if (event?.id == LIGHTBOX_EVENT.CLOSE) {
lighboxSubscription.unsubscribe();
URL?.revokeObjectURL(url as string);
}
})
}
}

@ -2,7 +2,6 @@ import {Component, Input, OnInit} from '@angular/core';
import {Repository} from "../../../models/Repository";
import {RepositoryService} from "../../../services/repository/repository.service";
import {Router} from "@angular/router";
import {MatSnackBar} from "@angular/material/snack-bar";
import {ErrorBrokerService} from "../../../services/error-broker/error-broker.service";
@Component({

@ -2,7 +2,6 @@ import {Inject, Injectable, Sanitizer} from '@angular/core';
import {BehaviorSubject} from "rxjs";
import {File} from "../../models/File";
import {invoke} from "@tauri-apps/api/tauri";
import {DOCUMENT} from "@angular/common";
import {DomSanitizer, SafeResourceUrl} from "@angular/platform-browser";
import {Thumbnail} from "../../models/Thumbnail";
@ -26,19 +25,22 @@ export class FileService {
const data = await invoke<number[]>("read_file_by_hash", {hash});
const blob = new Blob([new Uint8Array(data)], {type: mime_type});
const url = URL?.createObjectURL(blob);
return this.sanitizer.bypassSecurityTrustResourceUrl(url);
return this.createSafeObjectUrl(blob);
}
public async readThumbnail(thumbnail: Thumbnail): Promise<SafeResourceUrl> {
let data = await invoke<number[]>("read_thumbnail", {hash: thumbnail.hash});
const blob = new Blob([new Uint8Array(data)], {type: thumbnail.mime});
const url = URL?.createObjectURL(blob);
return this.sanitizer.bypassSecurityTrustResourceUrl(url);
return this.createSafeObjectUrl(blob);
}
public async getThumbnails(hash: string): Promise<Thumbnail[]> {
return await invoke<Thumbnail[]>("get_thumbnails", {hash});
}
createSafeObjectUrl(blob: Blob): SafeResourceUrl {
const url = URL?.createObjectURL(blob);
return this.sanitizer.bypassSecurityTrustResourceUrl(url);
}
}

@ -1572,6 +1572,11 @@
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.50.tgz#1e0caa9364d3fccd2931c3ed96fdbeaa5d4cca83"
integrity sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==
"@types/file-saver@^2.0.3":
version "2.0.3"
resolved "https://registry.yarnpkg.com/@types/file-saver/-/file-saver-2.0.3.tgz#b734c4f5a04d20615eaed3dc106e2ab321082009"
integrity sha512-MBIou8pd/41jkff7s97B47bc9+p0BszqqDJsO51yDm49uUxeKzrfuNl5fSLC6BpLEWKA8zlwyqALVmXrFwoBHQ==
"@types/glob@^7.1.1":
version "7.1.4"
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.4.tgz#ea59e21d2ee5c517914cb4bc8e4153b99e566672"
@ -4469,6 +4474,11 @@ file-entry-cache@^6.0.1:
dependencies:
flat-cache "^3.0.4"
file-saver@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/file-saver/-/file-saver-2.0.5.tgz#d61cfe2ce059f414d899e9dd6d4107ee25670c38"
integrity sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==
file-type@5.2.0, file-type@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/file-type/-/file-type-5.2.0.tgz#2ddbea7c73ffe36368dfae49dc338c058c2b8ad6"
@ -6882,6 +6892,21 @@ neo-async@^2.6.2:
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
ngx-filesaver@8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/ngx-filesaver/-/ngx-filesaver-8.1.0.tgz#f82a567f70c4ee81fc67b25bf594f3959c371b19"
integrity sha512-xAwThsB/ap9qs7BRgW+mJPWBK60uaqkfPnw3vzbykTdfIvGf62U/6osDCq4TCPl/POWLFui6b3Bu+gfDMqoZhw==
dependencies:
tslib "^1.9.0"
ngx-lightbox@^2.5.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/ngx-lightbox/-/ngx-lightbox-2.5.1.tgz#1d16b7445296b541c428c71c71b9f763ddc1cef8"
integrity sha512-/EOKxiT8s8pS7CIwAJDrUWt4DsNGdj7fj5ygzZZ4Jdl0iKcfwhGtnpGfrmGZlCeoFDYg5+u1PalFbvYJs0naSQ==
dependencies:
file-saver "^2.0.5"
ngx-filesaver "8.1.0"
nice-napi@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/nice-napi/-/nice-napi-1.0.2.tgz#dc0ab5a1eac20ce548802fc5686eaa6bc654927b"

Loading…
Cancel
Save