Add album view

This commit is contained in:
Timothy Farrell 2017-04-18 09:06:26 -05:00
parent 6576cf9a18
commit 30bfcd8e17
5 changed files with 119 additions and 52 deletions

View File

@ -1,62 +1,94 @@
import * as image from './data/image.js'; import * as image from './data/image.js';
import * as index from './data/indexType.js';
import { getDatabase } from './services/db.js'; import { getDatabase } from './services/db.js';
import * as imageTag from './context/manageImageTags.js'; import * as imageTag from './context/manageImageTags.js';
import './context/generateThumbnails.js'; import './context/generateThumbnails.js';
document.querySelector('#fInput').onchange = async evt => {
image.add(evt.currentTarget.files);
};
window.__DEV__ = true; window.__DEV__ = true;
window.db = getDatabase(); window.db = getDatabase();
image.imported.subscribe(refresh); image.imported.subscribe(refresh);
const header = document.querySelector('h1');
const container = document.querySelector('#app');
const displaySelector = document.querySelector('#display');
// Events
displaySelector.onchange = refresh;
document.querySelector('#fInput').onchange = async evt => {
image.add(evt.currentTarget.files);
};
// To test the output: // To test the output:
function refresh() { function refresh() {
setTimeout(() => history.go(0), 100); setTimeout(render, 100);
} }
db.allDocs({ include_docs: true, attachments: true }).then(async results => { function renderThumbnail(id, name, doc, tags) {
results.rows.forEach(r => { const c = document.createElement('div');
const doc = r.doc; const e = document.createElement('img');
});
function renderThumbnail(id, name, doc, tags) { c.appendChild(e);
const c = document.createElement('div'); c.id = id;
const e = document.createElement('img'); c.className = 'image';
e.title = `${id} ${name}`;
e.src = `data:${doc.content_type};base64,${doc.data}`;
e.dataset.id = id;
e.onclick = evt => image.remove(evt.currentTarget.dataset.id).then(refresh);
c.appendChild(e); Object.entries(tags)
c.id = id; .filter(([_, visible]) => visible)
c.className = 'image'; .forEach(([title, _]) => {
e.title = `${id} ${name}`; const t = document.createElement('span');
e.src = `data:${doc.content_type};base64,${doc.data}`; t.textContent = title;
e.dataset.id = id; t.className = 'tag';
e.onclick = evt => image.remove(evt.currentTarget.dataset.id).then(refresh); t.onclick = evt => imageTag.remove(title, id).then(refresh);
c.appendChild(t);
});
return c;
}
Object.entries(tags) function renderImage(imageRow, imageContainer, showTags = true) {
.filter(([_, visible]) => visible) for (let aName in imageRow.doc._attachments) {
.forEach(([title, _]) => { if (aName !== 'thumbnail') {
const t = document.createElement('span'); continue;
t.textContent = title;
t.className = 'tag';
t.onclick = evt => imageTag.remove(title, id).then(refresh);
c.appendChild(t);
});
return c;
}
results.rows.forEach(r => {
for (let aName in r.doc._attachments) {
if (aName !== 'thumbnail') {
continue;
}
document.body.appendChild(
renderThumbnail(r.doc._id, aName, r.doc._attachments[aName], r.doc.tags)
);
} }
}); imageContainer.appendChild(
renderThumbnail(
imageRow.doc._id,
aName,
imageRow.doc._attachments[aName],
showTags ? imageRow.doc.tags : []
)
);
}
}
async function renderAlbum(indexRow) {
const doc = indexRow.doc;
const l = document.createElement('h2');
l.innerText = indexRow.doc.props.title;
container.appendChild(l);
const albumContainer = document.createElement('div');
container.appendChild(albumContainer);
const results = await image.find(doc.members, { attachments: true });
results.rows.filter(i => i.doc).forEach(i => renderImage(i, albumContainer, false));
}
async function render() {
container.innerHTML = '';
if (displaySelector.value === 'i') {
header.innerText = 'Images';
const results = await image.find({ attachments: true });
results.rows.forEach(i => renderImage(i, container));
} else {
header.innerText = 'Albums';
const results = await index.find({ attachments: true });
results.rows.forEach(renderAlbum);
}
Array.from(document.querySelectorAll('.image')).forEach(i => { Array.from(document.querySelectorAll('.image')).forEach(i => {
const b = document.createElement('button'); const b = document.createElement('button');
@ -64,4 +96,6 @@ db.allDocs({ include_docs: true, attachments: true }).then(async results => {
b.textContent = '+'; b.textContent = '+';
i.appendChild(b); i.appendChild(b);
}); });
}); }
render();

View File

@ -3,7 +3,7 @@ import * as index from '../data/indexType.js';
export async function add(title, imageId, visible = true) { export async function add(title, imageId, visible = true) {
const trimmedTitle = title.trim(); const trimmedTitle = title.trim();
await index.add(trimmedTitle, [imageId]); await index.add(trimmedTitle, { title: trimmedTitle }, [imageId]);
return image.update(imageId, { return image.update(imageId, {
tags: { [trimmedTitle]: visible } tags: { [trimmedTitle]: visible }
}); });
@ -14,3 +14,7 @@ export async function remove(title, imageId) {
await image.update(imageId, { tags: { [title]: undefined } }); await image.update(imageId, { tags: { [title]: undefined } });
await index.removeMember(title, imageId); await index.removeMember(title, imageId);
} }
image.removed.subscribe(image => {
Object.keys(image.tags).forEach(t => index.removeMember(t, image._id));
});

View File

@ -8,13 +8,26 @@ import { Event, backgroundTask } from '../utils/event.js';
const db = getDatabase(); const db = getDatabase();
const PROCESS_PREFIX = 'importing'; const PROCESS_PREFIX = 'importing';
const PREFIX = 'image';
// Events // Events
export const imported = new Event('Image.imported'); export const imported = new Event('Image.imported');
export const removed = new Event('Image.removed');
// Methods // Methods
const getId = id => (id.startsWith(PREFIX) ? id : `${PREFIX}_${id}`);
export async function find(keys, options = {}) { export async function find(keys, options = {}) {
return await db.allDocs(Object.assign({ include_docs: true }, options, { keys })); let opts = { include_docs: true };
if (Array.isArray(keys)) {
Object.assign(opts, options);
opts.keys = keys.map(getId);
} else {
Object.assign(opts, keys);
opts.startkey = `${PREFIX}_0`;
opts.endkey = `${PREFIX}_\ufff0`;
}
return await db.allDocs(opts);
} }
export async function add(imageFileList) { export async function add(imageFileList) {
@ -45,6 +58,9 @@ export async function remove(ids, rev) {
try { try {
const doc = rev ? { _id: ids, _rev: rev } : await db.get(ids); const doc = rev ? { _id: ids, _rev: rev } : await db.get(ids);
await db.remove(doc); await db.remove(doc);
if (doc._id.startsWith(PREFIX)) {
removed.fire(doc);
}
return true; return true;
} catch (e) { } catch (e) {
if (e.status !== 404) { if (e.status !== 404) {
@ -76,8 +92,8 @@ export async function addAttachment(doc, key, blob) {
// Internal Functions // Internal Functions
const processImportables = backgroundTask(async function _processImportables() { const processImportables = backgroundTask(async function _processImportables() {
const result = await db.allDocs({ const result = await db.allDocs({
startkey: `${PROCESS_PREFIX}_0`, startkey: `${PROCESS_PREFIX}_`,
endkey: `${PROCESS_PREFIX}_z`, endkey: `${PROCESS_PREFIX}_\ufff0`,
include_docs: true, include_docs: true,
attachments: true, attachments: true,
binary: true, binary: true,
@ -97,7 +113,7 @@ const processImportables = backgroundTask(async function _processImportables() {
tags.DateTimeOriginal ? new Date(tags.DateTimeOriginal * 1000).toISOString() : doc.modifiedDate tags.DateTimeOriginal ? new Date(tags.DateTimeOriginal * 1000).toISOString() : doc.modifiedDate
); );
const { _id, _rev } = doc; const { _id, _rev } = doc;
const id = `image_${originalDate.getTime().toString(36)}_${digest.substr(0, 6)}`; const id = `${PREFIX}_${originalDate.getTime().toString(36)}_${digest.substr(0, 6)}`;
let continueProcessing = true; let continueProcessing = true;
try { try {

View File

@ -13,13 +13,23 @@ export const hashString = name =>
const getId = id => (id.startsWith(PREFIX) ? id : `${PREFIX}_${hashString(id)}`); const getId = id => (id.startsWith(PREFIX) ? id : `${PREFIX}_${hashString(id)}`);
export async function find(keys, options = {}) { export async function find(keys, options = {}) {
return await db.allDocs(Object.assign({ include_docs: true }, options, { keys: keys.map(getId) })); let opts = { include_docs: true };
if (Array.isArray(keys)) {
Object.assign(opts, options);
opts.keys = keys.map(getId);
} else {
Object.assign(opts, keys);
opts.startkey = `${PREFIX}_`;
opts.endkey = `${PREFIX}_\ufff0`;
}
return await db.allDocs(opts);
} }
export async function add(id, members = []) { export async function add(id, props = {}, members = []) {
const _id = getId(id); const _id = getId(id);
const [results, created] = await getOrCreate({ const [results, created] = await getOrCreate({
_id, _id,
props,
members: [] members: []
}); });

View File

@ -1,8 +1,11 @@
<body> <body>
<div> <input id='fInput' type="file" multiple accept="image/jpeg"/>
Images <select id='display' value='i'>
<input id='fInput' type="file" multiple accept="image/jpeg"/> <option value='i'>Images</option>
</div> <option value='a'>Albums</option>
</select>
<h1></h1>
<div id='app'></div>
</body> </body>
<script src="/assets/app.bundle.js"></script> <script src="/assets/app.bundle.js"></script>