Associate images with albums via image.$links.

All types now have a $links property.
This commit is contained in:
Timothy Farrell 2017-11-21 23:58:22 -06:00
parent ae51d825ef
commit 549d5be3f6
7 changed files with 87 additions and 81 deletions

View File

@ -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
// };
// }
}

View File

@ -91,29 +91,25 @@ const processImportables = backgroundTask(async function _processImportables(ima
tags.DateTimeOriginal ? new Date(tags.DateTimeOriginal * 1000).toISOString() : image.originalDate
).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;
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');
module.generateThumbnailForImage(image);

View File

@ -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(

View File

@ -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);
}))
])
]);

View File

@ -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) {

View File

@ -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);

View File

@ -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');