Associate images with albums via image.$links.
All types now have a $links property.
This commit is contained in:
parent
ae51d825ef
commit
549d5be3f6
@ -1,5 +1,6 @@
|
||||
import { PouchDB, TypeSpec } from '../services/db.js';
|
||||
import { log } from '../services/console.js';
|
||||
import { ImageType } from '../data/image.js';
|
||||
import { extractID } from '../utils/conversion.js';
|
||||
|
||||
class AlbumSpec extends TypeSpec {
|
||||
static getUniqueID(doc) {
|
||||
@ -9,23 +10,48 @@ class AlbumSpec extends TypeSpec {
|
||||
.toLowerCase();
|
||||
}
|
||||
|
||||
async addMember(member, position) {
|
||||
const currentPosition = this.members.indexOf(member);
|
||||
const newPosition = position ? position : this.members.length;
|
||||
if (currentPosition !== -1) {
|
||||
this.members.splice(currentPosition, 1);
|
||||
}
|
||||
this.members.splice(newPosition, 0, member);
|
||||
await this.save();
|
||||
async findImages(live = false) {
|
||||
return await ImageType.find(
|
||||
Object.assign({ [`$links.${this._id}`]: { $exists: true } }, ImageType.selector),
|
||||
live
|
||||
);
|
||||
}
|
||||
|
||||
async removeMember(member) {
|
||||
const currentPosition = this.members.indexOf(member);
|
||||
|
||||
if (currentPosition !== -1) {
|
||||
this.members.splice(currentPosition, 1);
|
||||
async addImage(image) {
|
||||
if (!image.$links[this._id]) {
|
||||
await image.update({
|
||||
$links: {
|
||||
[this._id]: {
|
||||
sequence: this.count
|
||||
}
|
||||
}
|
||||
});
|
||||
this.count += 1;
|
||||
await this.save();
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
async removeImage(image) {
|
||||
if (image.$links[this._id]) {
|
||||
delete image.$links[this._id];
|
||||
this.count -= 1;
|
||||
await image.save();
|
||||
await this.save();
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
async addImageBlob(blob) {
|
||||
return await this.addImage(await ImageType.upload(blob));
|
||||
}
|
||||
|
||||
async delete(cascade = true) {
|
||||
if (cascade) {
|
||||
const images = await this.findImages();
|
||||
images.map(async i => await this.removeImage(i));
|
||||
}
|
||||
return await this.update({ _deleted: true });
|
||||
}
|
||||
|
||||
//
|
||||
@ -34,10 +60,8 @@ class AlbumSpec extends TypeSpec {
|
||||
//
|
||||
// const schema = {
|
||||
// title: t.REQUIRED_STRING,
|
||||
// members: {
|
||||
// type: "array",
|
||||
// items: t.STRING
|
||||
// }
|
||||
// createdDate: t.REQUIRED_DATE,
|
||||
// count: t.REQUIRED_INTEGER
|
||||
// };
|
||||
// }
|
||||
}
|
||||
|
||||
@ -91,8 +91,8 @@ const processImportables = backgroundTask(async function _processImportables(ima
|
||||
tags.DateTimeOriginal ? new Date(tags.DateTimeOriginal * 1000).toISOString() : image.originalDate
|
||||
).toISOString();
|
||||
|
||||
await image.update(
|
||||
{
|
||||
delete image.importing;
|
||||
await image.update({
|
||||
originalDate,
|
||||
width,
|
||||
height,
|
||||
@ -109,11 +109,7 @@ const processImportables = backgroundTask(async function _processImportables(ima
|
||||
altitude: tags.GPSAltitude,
|
||||
heading: tags.GPSImgDirection
|
||||
}
|
||||
},
|
||||
false
|
||||
);
|
||||
delete image.importing;
|
||||
await image.save();
|
||||
});
|
||||
|
||||
const module = await import('../context/generateThumbnails');
|
||||
module.generateThumbnailForImage(image);
|
||||
|
||||
@ -6,7 +6,7 @@ import { pouchDocArrayHash, pouchDocHash } from '../utils/conversion.js';
|
||||
import { ThumbnailView } from './thumbnail.js';
|
||||
import { prop, computed, bundle } from 'frptools';
|
||||
|
||||
export function AlbumView(vm, params) {
|
||||
export function AlbumView(vm, doc) {
|
||||
const model = prop({}, pouchDocHash);
|
||||
const images = prop([], pouchDocArrayHash);
|
||||
|
||||
@ -16,53 +16,49 @@ export function AlbumView(vm, params) {
|
||||
|
||||
let laCleanup = null;
|
||||
|
||||
id.subscribe(async () => {
|
||||
const la = await ImageType.find(
|
||||
{
|
||||
_id: { $in: members() }
|
||||
},
|
||||
true
|
||||
);
|
||||
|
||||
function refresh() {
|
||||
images(la());
|
||||
vm.redraw();
|
||||
model.subscribe(async album => {
|
||||
if (!album.findImages) {
|
||||
return;
|
||||
}
|
||||
const imagesLiveArray = await album.findImages(true);
|
||||
|
||||
if (laCleanup) {
|
||||
laCleanup();
|
||||
}
|
||||
|
||||
laCleanup = la.subscribe(refresh);
|
||||
la.ready.subscribe(refresh);
|
||||
function refresh() {
|
||||
images(imagesLiveArray());
|
||||
vm.redraw();
|
||||
}
|
||||
|
||||
laCleanup = imagesLiveArray.subscribe(refresh);
|
||||
imagesLiveArray.ready.subscribe(refresh);
|
||||
});
|
||||
|
||||
function removeImageFromAlbum(image) {
|
||||
model().removeMember(image._id);
|
||||
model().removeImage(image);
|
||||
}
|
||||
|
||||
function removeAlbum() {
|
||||
model().delete();
|
||||
function removeAlbum(album) {
|
||||
album.delete();
|
||||
}
|
||||
|
||||
function uploadImages(album, evt) {
|
||||
Promise.all(Array.from(evt.currentTarget.files).map(ImageType.upload)).then(images => {
|
||||
images.forEach(i => album.addMember(i._id));
|
||||
});
|
||||
Array.from(evt.currentTarget.files).forEach(f => album.addImageBlob(f));
|
||||
}
|
||||
|
||||
model(params.doc);
|
||||
model(doc);
|
||||
|
||||
return function(vm, params, key, opts) {
|
||||
model(params.doc);
|
||||
return function(vm, album, key, opts) {
|
||||
model(album);
|
||||
|
||||
return el('.album', [
|
||||
el('h2', [title(), el('button', { onclick: removeAlbum }, 'X')]),
|
||||
el('h2', [title(), el('button', { onclick: [removeAlbum, album] }, 'X')]),
|
||||
el('input#fInput', {
|
||||
type: 'file',
|
||||
multiple: true,
|
||||
accept: 'image/jpeg',
|
||||
onchange: [uploadImages, model()]
|
||||
onchange: [uploadImages, album]
|
||||
}),
|
||||
...images().map(i => {
|
||||
return defineView(
|
||||
|
||||
@ -89,23 +89,13 @@ export function GalleryView(vm, model) {
|
||||
{
|
||||
doc: i,
|
||||
showTags: true,
|
||||
// addTag: imageTag.add,
|
||||
remove: deleteImage
|
||||
// removeTag: imageTag.remove
|
||||
},
|
||||
i._id + i._rev
|
||||
);
|
||||
})
|
||||
: data().map(a => {
|
||||
return vw(
|
||||
AlbumView,
|
||||
{
|
||||
doc: a
|
||||
// addTag: imageTag.add,
|
||||
// remove: imageTag.remove
|
||||
},
|
||||
a._id + a._rev
|
||||
);
|
||||
return vw(AlbumView, a, a._id + a._rev);
|
||||
}))
|
||||
])
|
||||
]);
|
||||
|
||||
@ -19,7 +19,7 @@ export const PouchDB = core
|
||||
export class TypeSpec {
|
||||
constructor(props) {
|
||||
this._populateId(props);
|
||||
Object.assign(this, props, { type: this._prefix });
|
||||
Object.assign(this, { $links: {} }, props, { type: this._prefix });
|
||||
}
|
||||
|
||||
static getSequence(doc) {
|
||||
|
||||
@ -1,7 +1,4 @@
|
||||
import { pick } from './conversion.js';
|
||||
|
||||
const extractID = pick('_id');
|
||||
const extractREV = pick('_rev');
|
||||
import { extractID, extractREV } from './conversion.js';
|
||||
|
||||
export function pouchDocComparator(a, b) {
|
||||
return extractID(a) === extractID(b) && extractREV(a) === extractREV(b);
|
||||
|
||||
@ -46,3 +46,6 @@ export function deepAssign(to, ...rest) {
|
||||
}
|
||||
|
||||
export const pick = id => doc => doc[id];
|
||||
|
||||
export const extractID = pick('_id');
|
||||
export const extractREV = pick('_rev');
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user