From 385dcd7c4d7f36f659f88965f321b09c24b88e5e Mon Sep 17 00:00:00 2001 From: Braun Patrik Date: Mon, 20 Mar 2017 21:37:23 +0100 Subject: [PATCH] improving thumbnail generation. adding thumbnails to map --- backend/middlewares/ThumbnailGeneratorMWs.ts | 17 ++- backend/model/mysql/GalleryManager.ts | 1 + backend/model/mysql/enitites/PhotoEntity.ts | 2 + common/config/Config.ts | 34 ++--- common/entities/PhotoDTO.ts | 1 + frontend/app/app.module.ts | 4 +- frontend/app/gallery/IconPhoto.ts | 42 ++++++ frontend/app/gallery/Photo.ts | 29 +--- .../directory/directory.gallery.component.ts | 2 +- .../photo/photo.grid.gallery.component.ts | 2 +- .../lightbox.map.gallery.component.ts | 30 +++- .../app/gallery/map/map.gallery.component.ts | 6 +- .../{grid => }/thumnailLoader.service.ts | 64 +++++++-- .../{grid => }/thumnailManager.service.ts | 132 ++++++++++++++---- 14 files changed, 270 insertions(+), 96 deletions(-) create mode 100644 frontend/app/gallery/IconPhoto.ts rename frontend/app/gallery/{grid => }/thumnailLoader.service.ts (69%) rename frontend/app/gallery/{grid => }/thumnailManager.service.ts (52%) diff --git a/backend/middlewares/ThumbnailGeneratorMWs.ts b/backend/middlewares/ThumbnailGeneratorMWs.ts index 3ee7db1..d7685d9 100644 --- a/backend/middlewares/ThumbnailGeneratorMWs.ts +++ b/backend/middlewares/ThumbnailGeneratorMWs.ts @@ -73,10 +73,10 @@ export class ThumbnailGeneratorMWs { private static addThInfoToPhotos(photos: Array) { let thumbnailFolder = ProjectPath.ThumbnailFolder; - for (let j = 0; j < Config.Client.thumbnailSizes.length; j++) { - let size = Config.Client.thumbnailSizes[j]; - for (let i = 0; i < photos.length; i++) { - let fullImagePath = path.join(ProjectPath.ImageFolder, photos[i].directory.path, photos[i].directory.name, photos[i].name); + for (let i = 0; i < photos.length; i++) { + let fullImagePath = path.join(ProjectPath.ImageFolder, photos[i].directory.path, photos[i].directory.name, photos[i].name); + for (let j = 0; j < Config.Client.thumbnailSizes.length; j++) { + let size = Config.Client.thumbnailSizes[j]; let thPath = path.join(thumbnailFolder, ThumbnailGeneratorMWs.generateThumbnailName(fullImagePath, size)); if (fs.existsSync(thPath) === true) { if (typeof photos[i].readyThumbnails == "undefined") { @@ -85,6 +85,11 @@ export class ThumbnailGeneratorMWs { photos[i].readyThumbnails.push(size); } } + let iconPath = path.join(thumbnailFolder, ThumbnailGeneratorMWs.generateThumbnailName(fullImagePath, Config.Client.iconSize)); + if (fs.existsSync(iconPath) === true) { + photos[i].readyIcon = true; + } + } } @@ -129,13 +134,13 @@ export class ThumbnailGeneratorMWs { //load parameters let imagePath = req.resultPipe; - let size: number = 30; + let size: number = Config.Client.iconSize; ThumbnailGeneratorMWs.generateImage(imagePath, size, true, req, res, next); } - private static generateImage(imagePath, size, makeSquare, req: Request, res: Response, next: NextFunction) { + private static generateImage(imagePath: string, size: number, makeSquare: boolean, req: Request, res: Response, next: NextFunction) { //generate thumbnail path let thPath = path.join(ProjectPath.ThumbnailFolder, ThumbnailGeneratorMWs.generateThumbnailName(imagePath, size)); diff --git a/backend/model/mysql/GalleryManager.ts b/backend/model/mysql/GalleryManager.ts index 2ae339f..41ca788 100644 --- a/backend/model/mysql/GalleryManager.ts +++ b/backend/model/mysql/GalleryManager.ts @@ -39,6 +39,7 @@ export class GalleryManager implements IGalleryManager { dir.photos[i].metadata.positionData = JSON.parse(dir.photos[i].metadata.positionData); dir.photos[i].metadata.size = JSON.parse(dir.photos[i].metadata.size); dir.photos[i].readyThumbnails = []; + dir.photos[i].readyIcon = false; } } diff --git a/backend/model/mysql/enitites/PhotoEntity.ts b/backend/model/mysql/enitites/PhotoEntity.ts index 8b6d170..4b6b467 100644 --- a/backend/model/mysql/enitites/PhotoEntity.ts +++ b/backend/model/mysql/enitites/PhotoEntity.ts @@ -26,6 +26,8 @@ export class PhotoEntity implements PhotoDTO { readyThumbnails: Array = []; + readyIcon: boolean = false; + } diff --git a/common/config/Config.ts b/common/config/Config.ts index 320c427..d17b609 100644 --- a/common/config/Config.ts +++ b/common/config/Config.ts @@ -14,34 +14,36 @@ interface DataBaseConfig { } interface ServerConfig { - port:number; - imagesFolder:string; - thumbnailFolder:string; + port: number; + imagesFolder: string; + thumbnailFolder: string; database: DataBaseConfig; } interface SearchConfig { - searchEnabled:boolean - instantSearchEnabled:boolean - autocompleteEnabled:boolean + searchEnabled: boolean + instantSearchEnabled: boolean + autocompleteEnabled: boolean } interface ClientConfig { - thumbnailSizes:Array; - Search:SearchConfig; - concurrentThumbnailGenerations:number; - enableCache:boolean; - enableOnScrollRendering:boolean; - enableOnScrollThumbnailPrioritising:boolean; - authenticationRequired:boolean; + iconSize: number; + thumbnailSizes: Array; + Search: SearchConfig; + concurrentThumbnailGenerations: number; + enableCache: boolean; + enableOnScrollRendering: boolean; + enableOnScrollThumbnailPrioritising: boolean; + authenticationRequired: boolean; googleApiKey: string; } export class ConfigClass { - public Server:ServerConfig = null; + public Server: ServerConfig = null; - public Client:ClientConfig = { + public Client: ClientConfig = { thumbnailSizes: [200, 400, 600], + iconSize: 30, Search: { searchEnabled: true, instantSearchEnabled: true, @@ -55,7 +57,7 @@ export class ConfigClass { googleApiKey: "" }; - public setDatabaseType(type:DatabaseType) { + public setDatabaseType(type: DatabaseType) { this.Server.database.type = type; if (type === DatabaseType.memory) { this.Client.Search.searchEnabled = false; diff --git a/common/entities/PhotoDTO.ts b/common/entities/PhotoDTO.ts index 68d7450..9fe07da 100644 --- a/common/entities/PhotoDTO.ts +++ b/common/entities/PhotoDTO.ts @@ -6,6 +6,7 @@ export interface PhotoDTO { directory: DirectoryDTO; metadata: PhotoMetadata; readyThumbnails: Array; + readyIcon: boolean; } export interface PhotoMetadata { diff --git a/frontend/app/app.module.ts b/frontend/app/app.module.ts index 9ac3112..26a6312 100644 --- a/frontend/app/app.module.ts +++ b/frontend/app/app.module.ts @@ -8,7 +8,7 @@ import {appRoutes} from "./app.routing"; import {UserService} from "./model/network/user.service"; import {GalleryService} from "./gallery/gallery.service"; import {NetworkService} from "./model/network/network.service"; -import {ThumbnailLoaderService} from "./gallery/grid/thumnailLoader.service"; +import {ThumbnailLoaderService} from "./gallery/thumnailLoader.service"; import {GalleryCacheService} from "./gallery/cache.gallery.service"; import {FullScreenService} from "./gallery/fullscreen.service"; import {AuthenticationService} from "./model/network/authentication.service"; @@ -29,7 +29,7 @@ import {StringifyRole} from "./pipes/StringifyRolePipe"; import {Config} from "./config/Config"; import {GalleryMapComponent} from "./gallery/map/map.gallery.component"; import {GalleryMapLightboxComponent} from "./gallery/map/lightbox/lightbox.map.gallery.component"; -import {ThumbnailManagerService} from "./gallery/grid/thumnailManager.service"; +import {ThumbnailManagerService} from "./gallery/thumnailManager.service"; @NgModule({ imports: [ diff --git a/frontend/app/gallery/IconPhoto.ts b/frontend/app/gallery/IconPhoto.ts new file mode 100644 index 0000000..798ee01 --- /dev/null +++ b/frontend/app/gallery/IconPhoto.ts @@ -0,0 +1,42 @@ +import {PhotoDTO} from "../../../common/entities/PhotoDTO"; +import {Utils} from "../../../common/Utils"; +export class IconPhoto { + + + protected replacementSizeCache: number|boolean = false; + + constructor(public photo: PhotoDTO) { + + } + + iconLoaded() { + this.photo.readyIcon = true; + } + + isIconAvailable() { + return this.photo.readyIcon; + } + + getIconPath() { + return Utils.concatUrls("/api/gallery/content/", this.photo.directory.path, this.photo.directory.name, this.photo.name, "icon"); + } + + getPhotoPath() { + return Utils.concatUrls("/api/gallery/content/", this.photo.directory.path, this.photo.directory.name, this.photo.name); + } + + + equals(other: PhotoDTO | IconPhoto): boolean { + //is gridphoto + if (other instanceof IconPhoto) { + return this.photo.directory.path === other.photo.directory.path && this.photo.directory.name === other.photo.directory.name && this.photo.name === other.photo.name + } + + //is photo + if (other.directory) { + return this.photo.directory.path === other.directory.path && this.photo.directory.name === other.directory.name && this.photo.name === other.name + } + + return false; + } +} \ No newline at end of file diff --git a/frontend/app/gallery/Photo.ts b/frontend/app/gallery/Photo.ts index 8523641..8bf6fbc 100644 --- a/frontend/app/gallery/Photo.ts +++ b/frontend/app/gallery/Photo.ts @@ -1,12 +1,12 @@ import {PhotoDTO} from "../../../common/entities/PhotoDTO"; import {Utils} from "../../../common/Utils"; import {Config} from "../config/Config"; -export class Photo { +import {IconPhoto} from "./IconPhoto"; +export class Photo extends IconPhoto { - protected replacementSizeCache: boolean|number = false; - - constructor(public photo: PhotoDTO, public renderWidth: number, public renderHeight: number) { + constructor(photo: PhotoDTO, public renderWidth: number, public renderHeight: number) { + super(photo); } @@ -22,7 +22,7 @@ export class Photo { return Utils.findClosest(renderSize, Config.Client.thumbnailSizes); } - getReplacementThumbnailSize() { + getReplacementThumbnailSize(): number { if (this.replacementSizeCache === false) { this.replacementSizeCache = null; @@ -37,7 +37,7 @@ export class Photo { } } } - return this.replacementSizeCache; + return this.replacementSizeCache; } isReplacementThumbnailAvailable() { @@ -59,22 +59,5 @@ export class Photo { return Utils.concatUrls("/api/gallery/content/", this.photo.directory.path, this.photo.directory.name, this.photo.name, "thumbnail", size.toString()); } - getPhotoPath() { - return Utils.concatUrls("/api/gallery/content/", this.photo.directory.path, this.photo.directory.name, this.photo.name); - } - - equals(other: any) { - //is gridphoto - if (other.photo) { - return this.photo.directory.path === other.photo.directory.path && this.photo.directory.name === other.photo.directory.name && this.photo.name === other.photo.name - } - - //is photo - if (other.directory) { - return this.photo.directory.path === other.directory.path && this.photo.directory.name === other.directory.name && this.photo.name === other.name - } - - return false; - } } \ No newline at end of file diff --git a/frontend/app/gallery/directory/directory.gallery.component.ts b/frontend/app/gallery/directory/directory.gallery.component.ts index 21ce293..5dfda96 100644 --- a/frontend/app/gallery/directory/directory.gallery.component.ts +++ b/frontend/app/gallery/directory/directory.gallery.component.ts @@ -3,7 +3,7 @@ import {DirectoryDTO} from "../../../../common/entities/DirectoryDTO"; import {RouterLink} from "@angular/router"; import {Utils} from "../../../../common/Utils"; import {Photo} from "../Photo"; -import {Thumbnail, ThumbnailManagerService} from "../grid/thumnailManager.service"; +import {Thumbnail, ThumbnailManagerService} from "../thumnailManager.service"; @Component({ selector: 'gallery-directory', diff --git a/frontend/app/gallery/grid/photo/photo.grid.gallery.component.ts b/frontend/app/gallery/grid/photo/photo.grid.gallery.component.ts index 489bc01..f3cee17 100644 --- a/frontend/app/gallery/grid/photo/photo.grid.gallery.component.ts +++ b/frontend/app/gallery/grid/photo/photo.grid.gallery.component.ts @@ -4,7 +4,7 @@ import {GridPhoto} from "../GridPhoto"; import {SearchTypes} from "../../../../../common/entities/AutoCompleteItem"; import {RouterLink} from "@angular/router"; import {Config} from "../../../config/Config"; -import {Thumbnail, ThumbnailManagerService} from "../thumnailManager.service"; +import {Thumbnail, ThumbnailManagerService} from "../../thumnailManager.service"; @Component({ selector: 'gallery-grid-photo', diff --git a/frontend/app/gallery/map/lightbox/lightbox.map.gallery.component.ts b/frontend/app/gallery/map/lightbox/lightbox.map.gallery.component.ts index 0754225..6488f88 100644 --- a/frontend/app/gallery/map/lightbox/lightbox.map.gallery.component.ts +++ b/frontend/app/gallery/map/lightbox/lightbox.map.gallery.component.ts @@ -2,8 +2,9 @@ import {Component, Input, OnChanges, ElementRef, ViewChild} from "@angular/core" import {PhotoDTO} from "../../../../../common/entities/PhotoDTO"; import {Dimension} from "../../../model/IRenderable"; import {FullScreenService} from "../../fullscreen.service"; -import {Utils} from "../../../../../common/Utils"; import {SebmGoogleMap} from "angular2-google-maps/core"; +import {ThumbnailManagerService, IconThumbnail} from "../../thumnailManager.service"; +import {IconPhoto} from "../../IconPhoto"; @Component({ selector: 'gallery-map-lightbox', @@ -18,7 +19,7 @@ export class GalleryMapLightboxComponent implements OnChanges { public mapDimension: Dimension = {top: 0, left: 0, width: 0, height: 0}; private visible = false; private opacity = 1.0; - mapPhotos: Array<{latitude: number, longitude: number, iconUrl}> = []; + mapPhotos: Array<{latitude: number, longitude: number, iconUrl?: string, thumbnail: IconThumbnail}> = []; mapCenter = {latitude: 0, longitude: 0}; @ViewChild("root") elementRef: ElementRef; @@ -26,7 +27,7 @@ export class GalleryMapLightboxComponent implements OnChanges { @ViewChild(SebmGoogleMap) map: SebmGoogleMap; - constructor(private fullScreenService: FullScreenService) { + constructor(private fullScreenService: FullScreenService, private thumbnailService: ThumbnailManagerService) { } @@ -81,21 +82,33 @@ export class GalleryMapLightboxComponent implements OnChanges { this.opacity = 0.0; setTimeout(() => { this.visible = false; - this.mapPhotos = []; + this.hideImages(); }, 500); } showImages() { + this.hideImages(); + this.mapPhotos = this.photos.filter(p => { return p.metadata && p.metadata.positionData && p.metadata.positionData.GPSData; }).map(p => { - return { + let th = this.thumbnailService.getIcon(new IconPhoto(p)); + let obj: {latitude: number, longitude: number, iconUrl?: string, thumbnail: IconThumbnail} = { latitude: p.metadata.positionData.GPSData.latitude, longitude: p.metadata.positionData.GPSData.longitude, - iconUrl: Utils.concatUrls("/api/gallery/content/", p.directory.path, p.directory.name, p.name, "icon") + thumbnail: th + }; + if (th.Available == true) { + obj.iconUrl = th.Src; + } else { + th.OnLoad = () => { + obj.iconUrl = th.Src; + }; + } + return obj; }); if (this.mapPhotos.length > 0) { @@ -103,6 +116,11 @@ export class GalleryMapLightboxComponent implements OnChanges { } } + hideImages() { + this.mapPhotos.forEach(mp => mp.thumbnail.destroy()); + this.mapPhotos = []; + } + private getBodyScrollTop(): number { return window.scrollY; diff --git a/frontend/app/gallery/map/map.gallery.component.ts b/frontend/app/gallery/map/map.gallery.component.ts index 883e052..4dde36f 100644 --- a/frontend/app/gallery/map/map.gallery.component.ts +++ b/frontend/app/gallery/map/map.gallery.component.ts @@ -1,6 +1,5 @@ import {Component, OnChanges, Input, ViewChild, ElementRef} from "@angular/core"; import {PhotoDTO} from "../../../../common/entities/PhotoDTO"; -import {Utils} from "../../../../common/Utils"; import {IRenderable, Dimension} from "../../model/IRenderable"; import {GalleryMapLightboxComponent} from "./lightbox/lightbox.map.gallery.component"; @Component({ @@ -13,7 +12,7 @@ export class GalleryMapComponent implements OnChanges, IRenderable { @Input() photos: Array; @ViewChild(GalleryMapLightboxComponent) mapLightbox: GalleryMapLightboxComponent; - mapPhotos: Array<{latitude: number, longitude: number, iconUrl}> = []; + mapPhotos: Array<{latitude: number, longitude: number}> = []; mapCenter = {latitude: 0, longitude: 0}; @ViewChild("map") map: ElementRef; @@ -24,8 +23,7 @@ export class GalleryMapComponent implements OnChanges, IRenderable { }).map(p => { return { latitude: p.metadata.positionData.GPSData.latitude, - longitude: p.metadata.positionData.GPSData.longitude, - iconUrl: Utils.concatUrls("/api/gallery/content/", p.directory.path, p.directory.name, p.name, "icon") + longitude: p.metadata.positionData.GPSData.longitude }; }); diff --git a/frontend/app/gallery/grid/thumnailLoader.service.ts b/frontend/app/gallery/thumnailLoader.service.ts similarity index 69% rename from frontend/app/gallery/grid/thumnailLoader.service.ts rename to frontend/app/gallery/thumnailLoader.service.ts index 9c17ee8..538ef5e 100644 --- a/frontend/app/gallery/grid/thumnailLoader.service.ts +++ b/frontend/app/gallery/thumnailLoader.service.ts @@ -1,7 +1,9 @@ import {Injectable} from "@angular/core"; -import {Config} from "../../config/Config"; -import {GalleryCacheService} from "../cache.gallery.service"; -import {Photo} from "../Photo"; +import {Config} from "../config/Config"; +import {GalleryCacheService} from "./cache.gallery.service"; +import {Photo} from "./Photo"; +import {IconPhoto} from "./IconPhoto"; +import {PhotoDTO} from "../../../common/entities/PhotoDTO"; export enum ThumbnailLoadingPriority{ high, medium, low @@ -36,12 +38,46 @@ export class ThumbnailLoaderService { } + loadIcon(photo: IconPhoto, priority: ThumbnailLoadingPriority, listener: ThumbnailLoadingListener): ThumbnailTaskEntity { + let tmp: ThumbnailTask = null; + //is image already qued? + for (let i = 0; i < this.que.length; i++) { + if (this.que[i].path == photo.getIconPath()) { + tmp = this.que[i]; + break; + } + } + + let thumbnailTaskEntity = {priority: priority, listener: listener}; + //add to previous + if (tmp != null) { + tmp.taskEntities.push(thumbnailTaskEntity); + if (tmp.inProgress == true) { + listener.onStartedLoading(); + } + + + } else {//create new task + this.que.push({ + photo: photo.photo, + inProgress: false, + taskEntities: [thumbnailTaskEntity], + onLoaded: () => { + photo.iconLoaded(); + }, + path: photo.getIconPath() + }); + } + setImmediate(this.run); + return thumbnailTaskEntity; + } + loadImage(photo: Photo, priority: ThumbnailLoadingPriority, listener: ThumbnailLoadingListener): ThumbnailTaskEntity { let tmp: ThumbnailTask = null; //is image already qued? for (let i = 0; i < this.que.length; i++) { - if (this.que[i].photo.getThumbnailPath() == photo.getThumbnailPath()) { + if (this.que[i].path == photo.getThumbnailPath()) { tmp = this.que[i]; break; } @@ -58,9 +94,13 @@ export class ThumbnailLoaderService { } else {//create new task this.que.push({ - photo: photo, + photo: photo.photo, inProgress: false, - taskEntities: [thumbnailTaskEntity] + taskEntities: [thumbnailTaskEntity], + onLoaded: () => { + photo.thumbnailLoaded(); + }, + path: photo.getThumbnailPath() }); } setImmediate(this.run); @@ -129,8 +169,8 @@ export class ThumbnailLoaderService { let curImg = new Image(); curImg.onload = () => { - task.photo.thumbnailLoaded(); - this.galleryChacheService.photoUpdated(task.photo.photo); + task.onLoaded(); + this.galleryChacheService.photoUpdated(task.photo); task.taskEntities.forEach((te: ThumbnailTaskEntity) => te.listener.onLoad()); this.taskReady(task); @@ -146,7 +186,7 @@ export class ThumbnailLoaderService { this.run(); }; - curImg.src = task.photo.getThumbnailPath(); + curImg.src = task.path; }; } @@ -164,8 +204,10 @@ export interface ThumbnailTaskEntity { } interface ThumbnailTask { - photo: Photo; + photo: PhotoDTO; inProgress: boolean; taskEntities: Array; - + path: string; + onLoaded: Function; } + diff --git a/frontend/app/gallery/grid/thumnailManager.service.ts b/frontend/app/gallery/thumnailManager.service.ts similarity index 52% rename from frontend/app/gallery/grid/thumnailManager.service.ts rename to frontend/app/gallery/thumnailManager.service.ts index d33a93a..6fb6414 100644 --- a/frontend/app/gallery/grid/thumnailManager.service.ts +++ b/frontend/app/gallery/thumnailManager.service.ts @@ -1,6 +1,7 @@ import {Injectable} from "@angular/core"; -import {Photo} from "../Photo"; import {ThumbnailLoaderService, ThumbnailLoadingListener, ThumbnailTaskEntity} from "./thumnailLoader.service"; +import {Photo} from "./Photo"; +import {IconPhoto} from "./IconPhoto"; export enum ThumbnailLoadingPriority{ high, medium, low @@ -16,21 +17,117 @@ export class ThumbnailManagerService { public getThumbnail(photo: Photo) { return new Thumbnail(photo, this.thumbnailLoader); } + + + public getIcon(photo: IconPhoto) { + return new IconThumbnail(photo, this.thumbnailLoader); + } } -export class Thumbnail { +export abstract class ThumbnailBase { - private available: boolean = false; - private src: string = null; - private loading: boolean = false; - private thumbnailTask: ThumbnailTaskEntity; + protected available: boolean = false; + protected src: string = null; + protected loading: boolean = false; + protected onLoad: Function = null; + protected thumbnailTask: ThumbnailTaskEntity; - constructor(private photo: Photo, private thumbnailService: ThumbnailLoaderService) { + constructor(protected thumbnailService: ThumbnailLoaderService) { + } + + abstract set Visible(visible: boolean); + + set OnLoad(onLoad: Function) { + this.onLoad = onLoad; + } + + + get Available() { + return this.available; + } + + get Src() { + return this.src; + } + + get Loading() { + return this.loading; + } + + destroy() { + if (this.thumbnailTask != null) { + this.thumbnailService.removeTask(this.thumbnailTask); + this.thumbnailTask = null; + } + } +} + + +export class IconThumbnail extends ThumbnailBase { + + constructor(private photo: IconPhoto, thumbnailService: ThumbnailLoaderService) { + super(thumbnailService); + this.src = ""; + if (this.photo.isIconAvailable()) { + this.src = this.photo.getIconPath(); + this.available = true; + if (this.onLoad) this.onLoad(); + } + + if (!this.photo.isIconAvailable()) { + setImmediate(() => { + + let listener: ThumbnailLoadingListener = { + onStartedLoading: () => { //onLoadStarted + this.loading = true; + }, + onLoad: () => {//onLoaded + this.src = this.photo.getIconPath(); + if (this.onLoad) this.onLoad(); + this.available = true; + this.loading = false; + this.thumbnailTask = null; + }, + onError: (error) => {//onError + this.thumbnailTask = null; + //TODO: handle error + //TODO: not an error if its from cache + console.error("something bad happened"); + console.error(error); + } + }; + this.thumbnailTask = this.thumbnailService.loadIcon(this.photo, ThumbnailLoadingPriority.high, listener); + + + }); + } + + } + + set Visible(visible: boolean) { + if (!this.thumbnailTask) return; + if (visible === true) { + this.thumbnailTask.priority = ThumbnailLoadingPriority.high; + } else { + this.thumbnailTask.priority = ThumbnailLoadingPriority.medium; + } + + } + + +} + +export class Thumbnail extends ThumbnailBase { + + + constructor(private photo: Photo, thumbnailService: ThumbnailLoaderService) { + super(thumbnailService); if (this.photo.isThumbnailAvailable()) { this.src = this.photo.getThumbnailPath(); this.available = true; + if (this.onLoad) this.onLoad(); } else if (this.photo.isReplacementThumbnailAvailable()) { this.src = this.photo.getReplacementThumbnailPath(); this.available = true; @@ -45,6 +142,7 @@ export class Thumbnail { }, onLoad: () => {//onLoaded this.src = this.photo.getThumbnailPath(); + if (this.onLoad) this.onLoad(); this.available = true; this.loading = false; this.thumbnailTask = null; @@ -70,6 +168,7 @@ export class Thumbnail { } set Visible(visible: boolean) { + if (!this.thumbnailTask) return; if (visible === true) { if (this.photo.isReplacementThumbnailAvailable()) { this.thumbnailTask.priority = ThumbnailLoadingPriority.medium; @@ -86,24 +185,5 @@ export class Thumbnail { } - - get Available() { - return this.available; - } - - get Src() { - return this.src; - } - - get Loading() { - return this.loading; - } - - destroy() { - if (this.thumbnailTask != null) { - this.thumbnailService.removeTask(this.thumbnailTask); - this.thumbnailTask = null; - } - } }