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