Use streams for a little better memory reuse.
This commit is contained in:
parent
55828f1351
commit
86ccbfbb13
@ -1,13 +1,16 @@
|
||||
import { createView } from './utils/domvm.js';
|
||||
import { createView, config } from './utils/domvm.js';
|
||||
|
||||
import * as styles from './app.css';
|
||||
import { GalleryView } from './interface/gallery.js';
|
||||
import { router } from './services/router.js';
|
||||
import { streamConfig } from './utils/event.js';
|
||||
|
||||
import { EventEmitter } from 'events';
|
||||
|
||||
EventEmitter.defaultMaxListeners = 1000; // https://github.com/pouchdb/pouchdb/issues/6123
|
||||
|
||||
config({ stream: streamConfig });
|
||||
|
||||
// Attach our root view to the DOM
|
||||
createView(GalleryView, {}).mount(document.body);
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@ import { ImageType } from '../data/image.js';
|
||||
import { FileType } from '../data/file.js';
|
||||
import { pouchDocArrayHash, pouchDocHash } from '../utils/conversion.js';
|
||||
import { ThumbnailView } from './thumbnail.js';
|
||||
import { prop, computed, bundle } from 'frptools';
|
||||
import { prop, computed } from 'frptools';
|
||||
|
||||
export function AlbumView(vm, doc) {
|
||||
const model = prop({}, pouchDocHash);
|
||||
@ -15,25 +15,28 @@ export function AlbumView(vm, doc) {
|
||||
const title = computed(d => d.title, [model]); // always update
|
||||
|
||||
let laCleanup = null;
|
||||
const refresh = _ => vm.redraw();
|
||||
const subscriptions = [
|
||||
images.subscribe(refresh),
|
||||
model.subscribe(async album => {
|
||||
if (!album.findImages) {
|
||||
return;
|
||||
}
|
||||
const imagesLiveArray = await album.findImages(true);
|
||||
|
||||
model.subscribe(async album => {
|
||||
if (!album.findImages) {
|
||||
return;
|
||||
}
|
||||
const imagesLiveArray = await album.findImages(true);
|
||||
if (laCleanup) {
|
||||
laCleanup();
|
||||
}
|
||||
|
||||
if (laCleanup) {
|
||||
laCleanup();
|
||||
}
|
||||
function refresh() {
|
||||
images(imagesLiveArray());
|
||||
vm.redraw();
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
images(imagesLiveArray());
|
||||
vm.redraw();
|
||||
}
|
||||
|
||||
laCleanup = imagesLiveArray.subscribe(refresh);
|
||||
imagesLiveArray.ready.subscribe(refresh);
|
||||
});
|
||||
laCleanup = imagesLiveArray.subscribe(refresh);
|
||||
imagesLiveArray.ready.subscribe(refresh);
|
||||
})
|
||||
];
|
||||
|
||||
function removeImageFromAlbum(image) {
|
||||
model().removeImage(image);
|
||||
@ -47,13 +50,19 @@ export function AlbumView(vm, doc) {
|
||||
Array.from(evt.currentTarget.files).forEach(f => album.addImageBlob(f));
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
if (laCleanup) {
|
||||
laCleanup();
|
||||
}
|
||||
subscriptions.forEach(s => s());
|
||||
}
|
||||
|
||||
model(doc);
|
||||
|
||||
return function(vm, album, key, opts) {
|
||||
model(album);
|
||||
|
||||
return el('.album', [
|
||||
el('h2', [title(), el('button', { onclick: [removeAlbum, album] }, 'X')]),
|
||||
el('h2', title),
|
||||
el('button', { onclick: [removeAlbum, album] }, 'X'),
|
||||
el('input#fInput', {
|
||||
type: 'file',
|
||||
multiple: true,
|
||||
|
||||
@ -3,7 +3,7 @@ import { prop, computed, bundle } from 'frptools';
|
||||
|
||||
import { ImageType } from '../data/image.js';
|
||||
import { FileType } from '../data/file.js';
|
||||
import { pouchDocArrayHash, pouchDocHash } from '../utils/conversion.js';
|
||||
import { pouchDocHash } from '../utils/conversion.js';
|
||||
|
||||
export function AttachmentImageView(vm, image) {
|
||||
const model = prop(image, pouchDocHash);
|
||||
@ -42,17 +42,12 @@ export function AttachmentImageView(vm, image) {
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
redrawOff();
|
||||
URL.revokeObjectURL(blobURL());
|
||||
}
|
||||
|
||||
const redrawOff = imageURL.subscribe(() => vm.redraw());
|
||||
|
||||
return function render(vm, doc) {
|
||||
model(doc);
|
||||
|
||||
return el('img', {
|
||||
src: imageURL(),
|
||||
src: imageURL,
|
||||
onerror: loadImageFromBlob,
|
||||
_key: id(),
|
||||
_hooks: {
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { prop, computed } from 'frptools';
|
||||
import { injectStyle, el } from '../services/style';
|
||||
import { injectStyle } from '../services/style';
|
||||
import { defineElement as el } from '../utils/domvm.js';
|
||||
|
||||
const CSS_DROPZONE = {
|
||||
width: '200px',
|
||||
@ -14,10 +15,13 @@ const CSS_DROPZONE_ACTIVE = {
|
||||
};
|
||||
|
||||
export function Dropzone(vm, model) {
|
||||
const { ondrop, ondragenter, ondragleave } = model;
|
||||
const { ondrop, ondragenter, ondragleave, className, activeClassName, children } = model;
|
||||
|
||||
const baseClassName = className || injectStyle(CSS_DROPZONE);
|
||||
const hoverClassName = `${baseClassName} ${activeClassName || injectStyle(CSS_DROPZONE_ACTIVE)}`;
|
||||
|
||||
const enterCounter = prop(0);
|
||||
enterCounter.subscribe(() => vm.redraw());
|
||||
const class_ = computed(c => (c === 0 ? baseClassName : hoverClassName), [enterCounter]);
|
||||
|
||||
function onDragOver(evt) {
|
||||
// allows the browser to accept drops.
|
||||
@ -50,16 +54,6 @@ export function Dropzone(vm, model) {
|
||||
}
|
||||
|
||||
return function render(vm, model) {
|
||||
const { className, activeClassName, class: _class, children } = model;
|
||||
|
||||
const class_ = Object.assign(
|
||||
{
|
||||
[className || injectStyle(CSS_DROPZONE)]: true,
|
||||
[activeClassName || injectStyle(CSS_DROPZONE_ACTIVE)]: enterCounter() > 0
|
||||
},
|
||||
_class || {}
|
||||
);
|
||||
|
||||
return el(
|
||||
'div',
|
||||
{
|
||||
@ -69,7 +63,7 @@ export function Dropzone(vm, model) {
|
||||
ondragleave: onDragLeave,
|
||||
ondrop: onDrop
|
||||
},
|
||||
...children
|
||||
children()
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,14 +1,15 @@
|
||||
import { defineView as vw } from '../utils/domvm.js';
|
||||
import { prop } from 'frptools';
|
||||
|
||||
import { defineView as vw, defineElement as el } from '../utils/domvm.js';
|
||||
import { ImageType } from '../data/image.js';
|
||||
import { AlbumType } from '../data/album.js';
|
||||
import { ThumbnailView } from './thumbnail.js';
|
||||
import { AlbumView } from './album.js';
|
||||
import { Dropzone } from './dropzone.js';
|
||||
import { router, routeChanged } from '../services/router.js';
|
||||
import { injectStyle, styled, el } from '../services/style.js';
|
||||
import { injectStyle, styled } from '../services/style.js';
|
||||
|
||||
export function GalleryView(vm, model) {
|
||||
const { db } = model;
|
||||
const NAV_OPTIONS = {
|
||||
images: {
|
||||
data: ImageType.find(
|
||||
@ -27,7 +28,7 @@ export function GalleryView(vm, model) {
|
||||
|
||||
let data = null;
|
||||
let laCleanup = null;
|
||||
let title = '';
|
||||
const title = prop('');
|
||||
|
||||
function uploadImages(files) {
|
||||
Array.from(files).forEach(ImageType.upload);
|
||||
@ -53,15 +54,13 @@ export function GalleryView(vm, model) {
|
||||
laCleanup();
|
||||
}
|
||||
const o = NAV_OPTIONS[route.name];
|
||||
title = o.title;
|
||||
vm.redraw();
|
||||
title(o.title);
|
||||
|
||||
return o.data.then(la => {
|
||||
data = la;
|
||||
laCleanup = data.subscribe(() => {
|
||||
vm.redraw();
|
||||
});
|
||||
data.ready.subscribe(() => vm.redraw);
|
||||
});
|
||||
});
|
||||
|
||||
@ -70,13 +69,12 @@ export function GalleryView(vm, model) {
|
||||
el('a', { href: router.href('images') }, 'Images'),
|
||||
el('a', { href: router.href('albums') }, 'Albums'),
|
||||
el('h1', title),
|
||||
...(title === 'Images'
|
||||
...(title() === 'Images'
|
||||
? data().map(i => {
|
||||
return vw(
|
||||
ThumbnailView,
|
||||
{
|
||||
doc: i,
|
||||
showTags: true,
|
||||
remove: deleteImage
|
||||
},
|
||||
i._hash()
|
||||
@ -93,9 +91,7 @@ export function GalleryView(vm, model) {
|
||||
return el('h1', 'Loading...');
|
||||
}
|
||||
|
||||
return el(
|
||||
'.gallery',
|
||||
{ class: slate },
|
||||
return el('.gallery', { class: slate }, [
|
||||
header([
|
||||
el('div', { css: { fontSize: '20pt' } }, 'Gallery'),
|
||||
headerRight(
|
||||
@ -114,11 +110,11 @@ export function GalleryView(vm, model) {
|
||||
type: 'file',
|
||||
multiple: true, // FIXME - these don't carry through to the input tag
|
||||
accept: 'image/jpeg',
|
||||
children: renderDropzone()
|
||||
children: renderDropzone
|
||||
},
|
||||
'dz'
|
||||
)
|
||||
);
|
||||
]);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -2,6 +2,7 @@ import Styletron from 'styletron';
|
||||
import { injectStyle as _injectStyle } from 'styletron-utils';
|
||||
import { defineElement } from '../utils/domvm.js';
|
||||
import { isObject } from '../utils/comparators.js';
|
||||
import { streamConfig } from '../utils/event.js';
|
||||
|
||||
const styletronSingleton = new Styletron();
|
||||
|
||||
@ -12,7 +13,11 @@ export function injectStyle(...styles) {
|
||||
export function el(sig, ...attrsOrChildren) {
|
||||
let attrs = {};
|
||||
let children = attrsOrChildren;
|
||||
if (attrsOrChildren.length && isObject(attrsOrChildren[0])) {
|
||||
if (
|
||||
attrsOrChildren.length &&
|
||||
isObject(attrsOrChildren[0]) &&
|
||||
!streamConfig.is(attrsOrChildren[0])
|
||||
) {
|
||||
attrs = attrsOrChildren[0];
|
||||
children = attrsOrChildren.slice(1);
|
||||
if (isObject(attrs.css)) {
|
||||
@ -33,6 +38,9 @@ export function el(sig, ...attrsOrChildren) {
|
||||
.join(' ');
|
||||
}
|
||||
}
|
||||
if (children.length === 1 && streamConfig.is(attrsOrChildren[0])) {
|
||||
children = children[0];
|
||||
}
|
||||
return defineElement(sig, attrs, children);
|
||||
}
|
||||
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
// export * from 'domvm/dist/dev/domvm.dev.js';
|
||||
export * from 'domvm/dist/micro/domvm.micro.js';
|
||||
export * from 'domvm/dist/mini/domvm.mini.js';
|
||||
|
||||
@ -98,3 +98,10 @@ export function backgroundTask(fn, initialDelay = 500) {
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
export const streamConfig = {
|
||||
is: s => s && typeof s.subscribe === 'function',
|
||||
val: s => s(),
|
||||
sub: (s, fn) => s.subscribe(fn),
|
||||
unsub: s => s()
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user