diff --git a/backend/config/Config.ts b/backend/config/Config.ts index cd0defb1..bef58e45 100644 --- a/backend/config/Config.ts +++ b/backend/config/Config.ts @@ -10,7 +10,7 @@ Config.Server = { port: 80, imagesFolder: "/demo/images", thumbnailFolder: "/demo/TEMP", - databaseType: DatabaseType.mongoDB + databaseType: DatabaseType.memory }; ConfigLoader.init(Config, path.join(__dirname, './../../config.json'), [["PORT", "Server-port"]]); diff --git a/backend/model/ObjectManagerRepository.ts b/backend/model/ObjectManagerRepository.ts index 72655389..afa449b0 100644 --- a/backend/model/ObjectManagerRepository.ts +++ b/backend/model/ObjectManagerRepository.ts @@ -11,19 +11,11 @@ export class ObjectManagerRepository { private _searchManager:ISearchManager; private static _instance:ObjectManagerRepository = null; - public static InitMongoManagers() { - let MongoGalleryManager = require("./mongoose/MongoGalleryManager"); - let MongoUserManager = require("./mongoose/MongoUserManager"); - let MongoSearchManager = require("./mongoose/MongoSearchManager"); - ObjectManagerRepository.getInstance().setGalleryManager(new MongoGalleryManager()); - ObjectManagerRepository.getInstance().setUserManager(new MongoUserManager()); - ObjectManagerRepository.getInstance().setSearchManager(new MongoSearchManager()); - } public static MemoryMongoManagers() { - let GalleryManager = require("./memory/GalleryManager"); - let UserManager = require("./memory/UserManager"); - let SearchManager = require("./memory/SearchManager"); + let GalleryManager = require("./memory/GalleryManager").GalleryManager; + let UserManager = require("./memory/UserManager").UserManager; + let SearchManager = require("./memory/SearchManager").SearchManager; ObjectManagerRepository.getInstance().setGalleryManager(new GalleryManager()); ObjectManagerRepository.getInstance().setUserManager(new UserManager()); ObjectManagerRepository.getInstance().setSearchManager(new SearchManager()); diff --git a/backend/model/mongoose/DatabaseManager.ts b/backend/model/mongoose/DatabaseManager.ts deleted file mode 100644 index 502ac9f0..00000000 --- a/backend/model/mongoose/DatabaseManager.ts +++ /dev/null @@ -1,55 +0,0 @@ -/// - -import * as mongoose from "mongoose"; -import {Schema} from "mongoose"; - -export class DatabaseManager { - private static _instance:DatabaseManager = null; - private connectionError = false; - private errorObject = null; - private connectionOpen = false; - - constructor() { - mongoose.connect('mongodb://localhost/EQZT6L'); - } - - public static getInstance(onError?:(err)=>void, onConnected?:() =>void) { - if (DatabaseManager._instance === null) { - DatabaseManager._instance = new DatabaseManager(); - } - return DatabaseManager._instance; - } - - public onConnectionError(onError:(err) => void){ - if (this.connectionError === true) { - return onError(DatabaseManager._instance.errorObject); - } - mongoose.connection.once('error', (err) => { - this.connectionError = true; - this.errorObject = err; - onError(err); - }); - } - - public onConnected(onConnected:() => void){ - if (this.connectionOpen === true) { - return onConnected(); - } - mongoose.connection.once('open', (err) => { - this.connectionOpen = true; - onConnected(); - }); - } - - public getModel(name:string, schema:any) { - return mongoose.model(name, new Schema(schema)); - } - - public disconnect() { - mongoose.disconnect(); - } - - public isConnectionError() { - return this.connectionError; - } -} \ No newline at end of file diff --git a/backend/model/mongoose/MongoGalleryManager.ts b/backend/model/mongoose/MongoGalleryManager.ts deleted file mode 100644 index 42137e9a..00000000 --- a/backend/model/mongoose/MongoGalleryManager.ts +++ /dev/null @@ -1,80 +0,0 @@ -import * as path from "path"; -import {Directory} from "../../../common/entities/Directory"; -import {IGalleryManager} from "../IGalleryManager"; -import {DiskManager} from "../DiskManger"; -import {Utils} from "../../../common/Utils"; -import {DirectoryModel} from "./entities/DirectoryModel"; -import {PhotoModel} from "./entities/PhotoModel"; -import {Photo} from "../../../common/entities/Photo"; - -export class MongoGalleryManager implements IGalleryManager { - - constructor() { - } - - public listDirectory(relativeDirectoryName, cb:(error:any, result:Directory) => void) { - let directoryName = path.basename(relativeDirectoryName); - let directoryParent = path.join(path.dirname(relativeDirectoryName), "/"); - - - DirectoryModel.findOne({ - name: directoryName, - path: directoryParent - }).populate('photos').populate('directories').exec((err, res:any) => { - if (err || !res) { - return this.indexDirectory(relativeDirectoryName, cb); - } - return cb(err, this.modelToEntity(res)); - }); - - } - - public indexDirectory(relativeDirectoryName, cb:(error:any, result:Directory) => void) { - DiskManager.scanDirectory(relativeDirectoryName, (err, scannedDirectory)=> { - let arr = []; - scannedDirectory.directories.forEach((value) => { - let dir = new DirectoryModel(value); - Utils.setKeys(dir, value); - dir.save(); - arr.push(dir); - }); - scannedDirectory.directories = arr; - arr = []; - scannedDirectory.photos.forEach((value) => { - let p = new PhotoModel(value); - Utils.setKeys(p, value); - p.save(); - arr.push(p); - }); - - scannedDirectory.photos = arr; - DirectoryModel.create(scannedDirectory, (err, savedDir)=> { - scannedDirectory.photos.forEach((value:any) => { - value['directory'] = savedDir; - value.save(); - }); - return cb(err, this.modelToEntity(savedDir)); - }); - - }); - } - - - private modelToEntity(directoryModel:any):Directory { - - let directoryEntity = new Directory(directoryModel._id, directoryModel.name, directoryModel.path, directoryModel.lastupdate, [], []); - - directoryModel.photos.forEach((photo) => { - let photoEntity = new Photo(photo._id, photo.name, directoryEntity, photo.metadata); - directoryEntity.photos.push(photoEntity); - }); - - directoryModel.directories.forEach((dir) => { - let dirEntity = new Directory(dir._id, dir.name, dir.path, dir.lastupdate, [], []); - directoryEntity.directories.push(dirEntity); - }); - - return directoryEntity; - - } -} \ No newline at end of file diff --git a/backend/model/mongoose/MongoSearchManager.ts b/backend/model/mongoose/MongoSearchManager.ts deleted file mode 100644 index 376afe6a..00000000 --- a/backend/model/mongoose/MongoSearchManager.ts +++ /dev/null @@ -1,182 +0,0 @@ -import {AutoCompleteItem, SearchTypes} from "../../../common/entities/AutoCompleteItem"; -import {ISearchManager} from "../ISearchManager"; -import {DirectoryModel} from "./entities/DirectoryModel"; -import {PhotoModel} from "./entities/PhotoModel"; -import {SearchResult} from "../../../common/entities/SearchResult"; - -export class MongoSearchManager implements ISearchManager { - - constructor() { - } - - autocomplete(text, cb:(error:any, result:Array) => void) { - - console.log("autocomplete: " + text); - let items:Array = []; - let promises = []; - - promises.push( - PhotoModel.find({name: {$regex: text, $options: "i"}}) - .limit(10).select('name').exec().then((res:Array)=> { - items = items.concat(this.encapsulateAutoComplete(res.map(r => r.name), SearchTypes.image)); - })); - - promises.push( - PhotoModel.find({"metadata.positionData.city": {$regex: text, $options: "i"}}) - .limit(10).select('metadata.positionData.city').exec().then((res:Array)=> { - items = items.concat(this.encapsulateAutoComplete(res.map(r => r.metadata.positionData.city), SearchTypes.position)); - })); - - promises.push( - PhotoModel.find({"metadata.positionData.state": {$regex: text, $options: "i"}}) - .limit(10).select('metadata.positionData.state').exec().then((res:Array)=> { - items = items.concat(this.encapsulateAutoComplete(res.map(r => r.metadata.positionData.state), SearchTypes.position)); - })); - - promises.push( - PhotoModel.find({"metadata.positionData.country": {$regex: text, $options: "i"}}) - .limit(10).select('metadata.positionData.country').exec().then((res:Array)=> { - items = items.concat(this.encapsulateAutoComplete(res.map(r => r.metadata.positionData.country), SearchTypes.position)); - })); - - //TODO: fix caseinsensitivity - promises.push( - PhotoModel.find({"metadata.keywords": {$regex: text, $options: "i"}}) - .limit(10).select('metadata.keywords').exec().then((res:Array)=> { - res.forEach((photo)=> { - items = items.concat(this.encapsulateAutoComplete(photo.metadata.keywords.filter(k => k.indexOf(text) != -1), SearchTypes.keyword)); - }); - })); - - promises.push( - DirectoryModel.find({ - name: {$regex: text, $options: "i"} - }).limit(10).select('name').exec().then((res:Array)=> { - items = items.concat(this.encapsulateAutoComplete(res.map(r => r.name), SearchTypes.directory)); - })); - - - Promise.all(promises).then(()=> { - return cb(null, this.autoCompleteItemsUnique(items)); - }).catch((err)=> { - console.error(err); - return cb(err, null); - }); - - - } - - search(text:string, searchType:SearchTypes, cb:(error:any, result:SearchResult) => void) { - console.log("search: " + text + ", type:" + searchType); - let result:SearchResult = new SearchResult(); - - let promises = []; - let photoFilterOR = []; - - result.searchText = text; - result.searchType = searchType; - - - if (!searchType || searchType === SearchTypes.image) { - photoFilterOR.push({name: {$regex: text, $options: "i"}}); - } - if (!searchType || searchType === SearchTypes.position) { - photoFilterOR.push({"metadata.positionData.city": {$regex: text, $options: "i"}}); - photoFilterOR.push({"metadata.positionData.state": {$regex: text, $options: "i"}}); - photoFilterOR.push({"metadata.positionData.country": {$regex: text, $options: "i"}}); - } - if (!searchType || searchType === SearchTypes.keyword) { - photoFilterOR.push({"metadata.keywords": {$regex: text, $options: "i"}}); - } - - let photoFilter = {}; - if (photoFilterOR.length == 1) { - photoFilter = photoFilterOR[0]; - } else { - photoFilter = {$or: photoFilterOR}; - } - - if (!searchType || photoFilterOR.length > 0) { - promises.push(PhotoModel.find(photoFilter).populate('directory', 'name path').exec().then((res:Array) => { - result.photos = res; - })); - } - - - if (!searchType || searchType === SearchTypes.directory) { - promises.push(DirectoryModel.find({ - name: { - $regex: text, - $options: "i" - } - }).exec().then((res:Array) => { - result.directories = res; - })); - } - - Promise.all(promises).then(()=> { - return cb(null, result); - }).catch((err)=> { - console.error(err); - return cb(err, null); - }); - } - - instantSearch(text, cb:(error:any, result:SearchResult) => void) { - console.log("instantSearch: " + text); - let result:SearchResult = new SearchResult(); - result.searchText = text; - PhotoModel.find({ - $or: [ - {name: {$regex: text, $options: "i"}}, - {"metadata.positionData.city": {$regex: text, $options: "i"}}, - {"metadata.positionData.state": {$regex: text, $options: "i"}}, - {"metadata.positionData.country": {$regex: text, $options: "i"}}, - {"metadata.keywords": {$regex: text, $options: "i"}} - ] - - }).limit(10).populate('directory', 'name path').exec((err, res:Array) => { - if (err || !res) { - return cb(err, null); - } - result.photos = res; - - DirectoryModel.find({ - name: { - $regex: text, - $options: "i" - } - }).limit(10).exec((err, res:Array) => { - if (err || !res) { - return cb(err, null); - } - result.directories = res; - return cb(null, result); - }); - - - }); - } - - private encapsulateAutoComplete(values:Array, type:SearchTypes) { - let res = []; - values.forEach((value)=> { - res.push(new AutoCompleteItem(value, type)); - }); - return res; - } - - private autoCompleteItemsUnique(array:Array) { - var a = array.concat(); - for (var i = 0; i < a.length; ++i) { - for (var j = i + 1; j < a.length; ++j) { - if (a[i].equals(a[j])) - a.splice(j--, 1); - } - } - - return a; - } - - -} \ No newline at end of file diff --git a/backend/model/mongoose/MongoUserManager.ts b/backend/model/mongoose/MongoUserManager.ts deleted file mode 100644 index 89d646f4..00000000 --- a/backend/model/mongoose/MongoUserManager.ts +++ /dev/null @@ -1,45 +0,0 @@ -import {User, UserRoles} from "../../../common/entities/User"; -import {IUserManager} from "../IUserManager"; -import {UserModel} from "./entities/UserModel"; - -export class MongoUserManager implements IUserManager { - - constructor() { - } - - public findOne(filter, cb:(error:any, result:User) => void) { - return UserModel.findOne(filter, function (err, result:any) { - return cb(err, result); - }); - } - - public find(filter, cb:(error:any, result:Array) => void) { - UserModel.find(filter, function (err, result:Array) { - return cb(err, result); - }); - } - - public createUser(user, cb:(error:any, result:User) => void) { - UserModel.create(user, cb); - } - - public deleteUser(id:number, cb:(error:any) => void) { - UserModel.remove({id: id}, cb); - } - - - public changeRole(id:number, newRole:UserRoles, cb:(error:any, result:string) => void) { - return UserModel.update({id: id}, {role: newRole}, function (err) { - if (!err) { - return cb(err, "ok") - } - return cb(err, null); - - }); - } - - public changePassword(request:any, cb:(error:any, result:string) => void) { - throw new Error("not implemented"); //TODO: implement - } - -} \ No newline at end of file diff --git a/backend/model/mongoose/entities/DirectoryModel.ts b/backend/model/mongoose/entities/DirectoryModel.ts deleted file mode 100644 index 76635d04..00000000 --- a/backend/model/mongoose/entities/DirectoryModel.ts +++ /dev/null @@ -1,16 +0,0 @@ -import {DatabaseManager} from "../DatabaseManager"; -import {Schema} from "mongoose"; - -export var DirectoryModel = DatabaseManager.getInstance().getModel('directory', { - name: String, - path: String, - lastUpdate: Date, - directories: [{ - type: Schema.Types.ObjectId, - ref: 'directory' - }], - photos: [{ - type: Schema.Types.ObjectId, - ref: 'photo' - }] -}); \ No newline at end of file diff --git a/backend/model/mongoose/entities/PhotoModel.ts b/backend/model/mongoose/entities/PhotoModel.ts deleted file mode 100644 index d6bd030a..00000000 --- a/backend/model/mongoose/entities/PhotoModel.ts +++ /dev/null @@ -1,39 +0,0 @@ -import {DatabaseManager} from "../DatabaseManager"; -import {Schema} from "mongoose"; - - -export var PhotoModel = DatabaseManager.getInstance().getModel('photo', { - name: String, - directory: { - type: Schema.Types.ObjectId, - ref: 'directory' - }, - metadata: { - keywords: [String], - cameraData: { - ISO: Number, - maker: String, - fStop: Number, - exposure: Number, - focalLength: Number, - lens: String - }, - positionData: { - GPSData: { - latitude: Number, - longitude: Number, - altitude: Number - }, - country: String, - state: String, - city: String - }, - size: { - width: Number, - height: Number - }, - creationDate: Date - } -}); - - \ No newline at end of file diff --git a/backend/model/mongoose/entities/UserModel.ts b/backend/model/mongoose/entities/UserModel.ts deleted file mode 100644 index 0ff38e9c..00000000 --- a/backend/model/mongoose/entities/UserModel.ts +++ /dev/null @@ -1,6 +0,0 @@ -import {DatabaseManager} from "../DatabaseManager"; -export var UserModel = DatabaseManager.getInstance().getModel('user', { - name: {type: String, index: {unique: true}}, - password: String, - role: Number -}); \ No newline at end of file diff --git a/backend/server.ts b/backend/server.ts index 6befc1aa..d90999cf 100644 --- a/backend/server.ts +++ b/backend/server.ts @@ -13,7 +13,6 @@ import {ErrorRouter} from "./routes/ErrorRouter"; import {SharingRouter} from "./routes/SharingRouter"; import {DatabaseType} from "./../common/config/Config"; import {ObjectManagerRepository} from "./model/ObjectManagerRepository"; -import {DatabaseManager} from "./model/mongoose/DatabaseManager"; import {Config} from "./config/Config"; @@ -61,13 +60,7 @@ export class Server { if (Config.Server.databaseType === DatabaseType.memory) { ObjectManagerRepository.MemoryMongoManagers(); } else { - ObjectManagerRepository.InitMongoManagers(); - DatabaseManager.getInstance().onConnectionError( - ()=> { - console.error("MongoDB connection error. Falling back to memory Object Managers"); - ObjectManagerRepository.MemoryMongoManagers(); - Config.setDatabaseType(DatabaseType.memory); - }); + throw new Error("not implemented alternative mangers"); } new PublicRouter(this.app); diff --git a/common/Utils.ts b/common/Utils.ts index 89cbf7f4..16161fed 100644 --- a/common/Utils.ts +++ b/common/Utils.ts @@ -15,6 +15,7 @@ export class Utils { url += part + "/"; } + url = url.replace("//", "/"); return url.substring(0, url.length - 1); } diff --git a/common/config/Config.ts b/common/config/Config.ts index ded6c71d..ffebbf01 100644 --- a/common/config/Config.ts +++ b/common/config/Config.ts @@ -1,5 +1,5 @@ export enum DatabaseType{ - memory, mongoDB + memory } interface ServerConfig { diff --git a/common/entities/Photo.ts b/common/entities/Photo.ts index de3e7962..682671ae 100644 --- a/common/entities/Photo.ts +++ b/common/entities/Photo.ts @@ -4,7 +4,8 @@ export class Photo { constructor(public id?:number, public name?:string, public directory?:Directory, - public metadata?:PhotoMetadata) { + public metadata?:PhotoMetadata, + public readyThumbnails:Array = []) { } } diff --git a/frontend/app/app.component.ts b/frontend/app/app.component.ts index 4c444588..9e87fcea 100644 --- a/frontend/app/app.component.ts +++ b/frontend/app/app.component.ts @@ -11,6 +11,7 @@ import {UserService} from "./model/network/user.service.ts"; import {GalleryService} from "./gallery/gallery.service"; import {AdminComponent} from "./admin/admin.component"; import {NetworkService} from "./model/network/network.service"; +import {ThumbnailLoaderService} from "./gallery/grid/thumnailLoader.service"; @Component({ @@ -23,7 +24,8 @@ import {NetworkService} from "./model/network/network.service"; NetworkService, UserService, GalleryService, - AuthenticationService] + AuthenticationService, + ThumbnailLoaderService] }) @RouteConfig([ { diff --git a/frontend/app/gallery/grid/GridPhoto.ts b/frontend/app/gallery/grid/GridPhoto.ts index 1a87b742..49449210 100644 --- a/frontend/app/gallery/grid/GridPhoto.ts +++ b/frontend/app/gallery/grid/GridPhoto.ts @@ -6,9 +6,24 @@ export class GridPhoto { } - getThumbnailPath() { + + thumbnailLoaded() { + if (!this.isThumbnailAvailable()) { + this.photo.readyThumbnails.push(this.getThumbnailSize()); + } + } + + getThumbnailSize() { let renderSize = Math.sqrt(this.renderWidth * this.renderHeight); - let size = Utils.findClosest(renderSize, Config.Client.thumbnailSizes); + return Utils.findClosest(renderSize, Config.Client.thumbnailSizes); + } + + isThumbnailAvailable() { + return this.photo.readyThumbnails.indexOf(this.getThumbnailSize()) != -1; + } + + getThumbnailPath() { + let size = this.getThumbnailSize(); return Utils.concatUrls("/api/gallery/content/", this.photo.directory.path, this.photo.directory.name, this.photo.name, "thumbnail", size.toString()); } diff --git a/frontend/app/gallery/grid/photo/photo.grid.gallery.component.css b/frontend/app/gallery/grid/photo/photo.grid.gallery.component.css index 51b09db7..f3621c9a 100644 --- a/frontend/app/gallery/grid/photo/photo.grid.gallery.component.css +++ b/frontend/app/gallery/grid/photo/photo.grid.gallery.component.css @@ -45,3 +45,84 @@ a { width: 100%; } +.sk-cube-grid { + width: 100%; + height: 100%; +} + +.sk-cube-grid .sk-cube { + width: 33%; + height: 33%; + background-color: rgba(0, 0, 0, 0.1); + float: left; + -webkit-animation: sk-cubeGridScaleDelay 4.6s infinite ease-in-out; + animation: sk-cubeGridScaleDelay 4.6s infinite ease-in-out; +} + +.sk-cube-grid .sk-cube1 { + -webkit-animation-delay: 2.4s; + animation-delay: 2.4s; +} + +.sk-cube-grid .sk-cube2 { + -webkit-animation-delay: 2.6s; + animation-delay: 2.6s; +} + +.sk-cube-grid .sk-cube3 { + -webkit-animation-delay: 2.8s; + animation-delay: 2.8s; +} + +.sk-cube-grid .sk-cube4 { + -webkit-animation-delay: 2.2s; + animation-delay: 2.2s; +} + +.sk-cube-grid .sk-cube5 { + -webkit-animation-delay: 2.4s; + animation-delay: 2.4s; +} + +.sk-cube-grid .sk-cube6 { + -webkit-animation-delay: 2.6s; + animation-delay: 2.6s; +} + +.sk-cube-grid .sk-cube7 { + -webkit-animation-delay: 2s; + animation-delay: 2s; +} + +.sk-cube-grid .sk-cube8 { + -webkit-animation-delay: 2.2s; + animation-delay: 2.2s; +} + +.sk-cube-grid .sk-cube9 { + -webkit-animation-delay: 2.4s; + animation-delay: 2.4s; +} + +@-webkit-keyframes sk-cubeGridScaleDelay { + 0%, 70%, 100% { + -webkit-transform: scale3D(1, 1, 1); + transform: scale3D(1, 1, 1); + } + 35% { + -webkit-transform: scale3D(0, 0, 1); + transform: scale3D(0, 0, 1); + } +} + +@keyframes sk-cubeGridScaleDelay { + 0%, 70%, 100% { + -webkit-transform: scale3D(1, 1, 1); + transform: scale3D(1, 1, 1); + } + 35% { + -webkit-transform: scale3D(0, 0, 1); + transform: scale3D(0, 0, 1); + } +} + diff --git a/frontend/app/gallery/grid/photo/photo.grid.gallery.component.html b/frontend/app/gallery/grid/photo/photo.grid.gallery.component.html index b65f5f0e..65bb1086 100644 --- a/frontend/app/gallery/grid/photo/photo.grid.gallery.component.html +++ b/frontend/app/gallery/grid/photo/photo.grid.gallery.component.html @@ -1,6 +1,20 @@
- -
+ +
+
+
+
+
+
+
+
+
+
+
+ + +
{{gridPhoto.photo.name}}
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 ea3c77d2..e4c1e092 100644 --- a/frontend/app/gallery/grid/photo/photo.grid.gallery.component.ts +++ b/frontend/app/gallery/grid/photo/photo.grid.gallery.component.ts @@ -1,11 +1,12 @@ /// -import {Component, Input, ElementRef, ViewChild} from "@angular/core"; +import {Component, Input, ElementRef, ViewChild, OnChanges} from "@angular/core"; import {IRenderable, Dimension} from "../../../model/IRenderable"; import {GridPhoto} from "../GridPhoto"; import {SearchTypes} from "../../../../../common/entities/AutoCompleteItem"; import {RouterLink} from "@angular/router-deprecated"; import {Config} from "../../../config/Config"; +import {ThumbnailLoaderService} from "../thumnailLoader.service"; @Component({ selector: 'gallery-grid-photo', @@ -13,23 +14,42 @@ import {Config} from "../../../config/Config"; styleUrls: ['app/gallery/grid/photo/photo.grid.gallery.component.css'], directives: [RouterLink], }) -export class GalleryPhotoComponent implements IRenderable { +export class GalleryPhotoComponent implements IRenderable, OnChanges { @Input() gridPhoto:GridPhoto; @ViewChild("image") imageRef:ElementRef; @ViewChild("info") infoDiv:ElementRef; + imageSrc = "#"; + showImage = false; infoStyle = { height: 0, background: "" }; + SearchTypes:any = []; searchEnabled:boolean = true; - constructor() { + constructor(private thumbnailService:ThumbnailLoaderService) { this.SearchTypes = SearchTypes; this.searchEnabled = Config.Client.Search.searchEnabled; } + ngOnChanges() { + if (this.gridPhoto.isThumbnailAvailable()) { + this.imageSrc = this.gridPhoto.getThumbnailPath(); + // this.showImage = true; + } else { + this.thumbnailService.loadImage(this.gridPhoto).then(()=> { + this.imageSrc = this.gridPhoto.getThumbnailPath(); + // this.showImage = true; + this.gridPhoto.thumbnailLoaded(); + }).catch((error)=> { + console.error("something bad happened"); + }); + } + } + + getPositionText():string { if (!this.gridPhoto) { return "" diff --git a/frontend/app/gallery/grid/thumnailLoader.service.ts b/frontend/app/gallery/grid/thumnailLoader.service.ts new file mode 100644 index 00000000..915fdcbd --- /dev/null +++ b/frontend/app/gallery/grid/thumnailLoader.service.ts @@ -0,0 +1,73 @@ +/// + +import {Injectable} from "@angular/core"; +import {GridPhoto} from "./GridPhoto"; + +@Injectable() +export class ThumbnailLoaderService { + + que:Array = []; + + constructor() { + } + + loadImage(gridPhoto:GridPhoto):Promise { + console.log("[LOAD IMG]" + gridPhoto.photo.name); + return new Promise((resolve:Function, reject:Function)=> { + let tmp:ThumbnailTask = null; + for (let i = 0; i < this.que.length; i++) { + if (this.que[i].src == gridPhoto.getThumbnailPath()) { + tmp = this.que[i]; + break; + } + } + if (tmp != null) { + tmp.resolve.push(resolve); + tmp.reject.push(reject); + } else { + this.que.push({src: gridPhoto.getThumbnailPath(), resolve: [resolve], reject: [reject]}); + } + this.run(); + }); + + } + + isRunning:boolean = false; + + run() { + if (this.que.length === 0 || this.isRunning === true) { + return; + } + this.isRunning = true; + let task = this.que.shift(); + console.log("loadingstarted: " + task.src); + + let curImg = new Image(); + curImg.src = task.src; + curImg.onload = () => { + console.log(task.src + "done"); + task.resolve.forEach((resolve:()=>{}) => { + resolve(); + + }); + this.isRunning = false; + this.run(); + }; + + curImg.onerror = (error) => { + console.error(task.src + "error"); + task.reject.forEach((reject:(error)=>{}) => { + reject(error); + }); + this.isRunning = false; + this.run(); + }; + } + +} + +interface ThumbnailTask { + src:string; + resolve:Array; + reject:Array; +} diff --git a/frontend/webpack.config.js b/frontend/webpack.config.js index 43f2a9be..13a02dab 100644 --- a/frontend/webpack.config.js +++ b/frontend/webpack.config.js @@ -11,7 +11,7 @@ module.exports = { library: ['peer'] }, // Turn on sourcemaps - //devtool: 'source-map', + devtool: 'source-map', resolve: { extensions: ['', '.webpack.js', '.web.js', '.ts', '.js'],