From e08db9bae0d3f08c12ebb0ec4b0340d5502de0bb Mon Sep 17 00:00:00 2001 From: Timothy Farrell Date: Thu, 28 Dec 2017 22:14:46 -0600 Subject: [PATCH] Initial FocusView --- .../src/interface/components/appbar.js | 59 ++++---- packages/gallery/src/interface/focus.js | 132 ++++++++++++++++++ packages/gallery/src/interface/gallery.js | 7 +- packages/gallery/src/services/router.js | 4 +- packages/gallery/src/utils/conversion.js | 2 +- packages/gallery/src/utils/domvm.js | 4 +- 6 files changed, 173 insertions(+), 35 deletions(-) create mode 100644 packages/gallery/src/interface/focus.js diff --git a/packages/gallery/src/interface/components/appbar.js b/packages/gallery/src/interface/components/appbar.js index 24a5fbe..f2229b8 100644 --- a/packages/gallery/src/interface/components/appbar.js +++ b/packages/gallery/src/interface/components/appbar.js @@ -2,6 +2,7 @@ import { prop, computed, container } from 'frptools'; import { Icon } from './icon.js'; import { defineElement as el, subscribeToRender } from '../../utils/domvm.js'; +import { pick } from '../../utils/conversion.js'; import { injectStyle, styled } from '../../services/style.js'; import { CLICKABLE } from '../styles.js'; @@ -13,8 +14,9 @@ export function AppBarView(vm, params, key, opts) { const companionScrollTop = prop(0); const currentState = computed(stack => stack[0] || {}, [stateStack]); - const title = computed(state => state.title || '', [currentState]); - const renderButtons = computed(state => state.buttons, [currentState]); + const title = computed(pick('title', ''), [currentState]); + const renderButtons = computed(pick('buttons'), [currentState]); + const stateStyle = computed(pick('style', {}), [currentState]); const backButton = computed( (state, stack) => stack.length > 1 ? (state.backButton !== undefined ? state.backButton : 'arrow_left') : null, @@ -27,6 +29,13 @@ export function AppBarView(vm, params, key, opts) { [companionScrollTop] ); + const containerStyle = computed( + (boxShadow, style) => ({ + css: Object.assign({ boxShadow }, style) + }), + [boxShadowStyle, stateStyle] + ); + if (opts.appbar) { throw new Error('Cannot have more than one AppBar.'); } @@ -48,42 +57,38 @@ export function AppBarView(vm, params, key, opts) { subscribe: stateChange.subscribe }; - subscribeToRender(vm, [boxShadowStyle, renderButtons, backButton, title]); + subscribeToRender(vm, [containerStyle, renderButtons, backButton, title]); return (vm, params) => { const _buttons = renderButtons() || (() => {}); - return header( - { - css: { boxShadow: boxShadowStyle() } - }, - [ - backButton() !== null - ? backButtonContainer( - { - onclick: popState - }, - [ - Icon({ - name: backButton(), - size: 0.75 - }) - ] - ) - : null, - titleContainer(title()), - headerRight(_buttons()) - ] - ); + return appBarContainer(containerStyle(), [ + backButton() !== null + ? backButtonContainer( + { + onclick: popState + }, + [ + Icon({ + name: backButton(), + size: 0.75 + }) + ] + ) + : null, + titleContainer(title()), + headerRight(_buttons()) + ]); }; } -const header = styled({ +const appBarContainer = styled({ justifyContent: 'space-between', padding: '1em', zIndex: 1000, display: 'flex', - alignItems: 'center' + alignItems: 'center', + width: '100%' }); const backButtonContainer = styled( diff --git a/packages/gallery/src/interface/focus.js b/packages/gallery/src/interface/focus.js new file mode 100644 index 0000000..131f1f4 --- /dev/null +++ b/packages/gallery/src/interface/focus.js @@ -0,0 +1,132 @@ +import { prop, computed, container } from 'frptools'; + +import { + subscribeToRender, + defineView, + nodeParentWithType, + defineElement as el +} from '../utils/domvm.js'; + +import { ImageType } from '../data/image.js'; +import { pouchDocHash, pick } from '../utils/conversion.js'; +import { AttachmentImageView } from './components/attachmentImage.js'; +import { Overlay } from './components/overlay.js'; +import { Icon } from './components/icon.js'; +import { styled, injectStyle } from '../services/style.js'; +import { error } from '../services/console.js'; +import { CLICKABLE } from './styles.js'; + +export function FocusView(vm, params, key, { appbar }) { + const id = params.vars.id; + const { body } = document; + const windowSize = prop({}, o => (o ? `${o.width}x${o.height}` : '')); + + const extractWindowSize = () => + windowSize({ width: window.innerWidth, height: window.innerHeight }); + + const doc = container({}, pouchDocHash); + + const imageStyle = computed( + ({ width: iw, height: ih }, { width: vw, height: vh }) => { + const imageRatio = iw / ih; + const windowRatio = vw / vh; + + if (windowRatio > imageRatio) { + return { + height: vw / windowRatio, + width: vw / windowRatio * imageRatio + }; + } else { + return { + height: vh * windowRatio / imageRatio, + width: vh * windowRatio + }; + } + }, + [doc, windowSize] + ); + + async function goBack() { + history.go(-1); + } + + function navBack() { + // appbar.popState(); + goBack(); + } + + async function clickTrash() { + await ImageType.delete(id); + navBack(); + } + + function renderAppBarButtons() { + return [ + trashButtonContainer( + { + onclick: clickTrash + }, + [ + Icon({ + name: 'trash', + size: 0.75 + }) + ] + ) + ]; + } + + // Prime our window size + extractWindowSize(); + window.addEventListener('resize', extractWindowSize); + + // Set the appbar title. + appbar.pushState({ title: '', buttons: renderAppBarButtons, style: { position: 'fixed' } }); + + // Look for our image and set it. + ImageType.find(id) + .then(d => { + doc.src = d.sizes.full || d.sizes.preview || d.sizes.thumbnail; + doc.width = d.width; + doc.height = d.height; + doc._id = d._id; + }) + .catch(error); + + // Subscribe to our changables. + subscribeToRender( + vm, + [doc, imageStyle], + [appbar.subscribe(goBack), () => window.removeEventListener('resize', extractWindowSize)] + ); + + return function() { + if (!doc._id) { + return Overlay('Loading...'); + } + return focusContainer([ + AttachmentImageView({ + src: doc._id ? doc.src : null, + style: imageStyle() + }) + ]); + }; +} + +const trashButtonContainer = styled( + { + marginRight: '1em' + }, + CLICKABLE +); + +const focusContainer = styled({ + display: 'flex', + flex: 1, + justifyContent: 'center', + alignItems: 'center', + overflow: 'hidden' +}); + +const WIDE = injectStyle({ width: '100%' }); +const TALL = injectStyle({ height: '100%' }); diff --git a/packages/gallery/src/interface/gallery.js b/packages/gallery/src/interface/gallery.js index 514699a..d2a9bbb 100644 --- a/packages/gallery/src/interface/gallery.js +++ b/packages/gallery/src/interface/gallery.js @@ -12,6 +12,7 @@ import { ImageType } from '../data/image.js'; import { AlbumType } from '../data/album.js'; import { ThumbnailTemplate } from './components/thumbnail.js'; import { AllImagesView, uploadImages } from './allImages.js'; +import { FocusView } from './focus.js'; import { Dropzone } from './components/dropzone.js'; import { Overlay } from './components/overlay.js'; import { AppBarView } from './components/appbar.js'; @@ -28,6 +29,7 @@ export function GalleryView(vm) { routeChanged.subscribe(function onRouteChange(name, params) { routeName(name); routeParams(params); + vm.redraw(); }); function handleContentScroll(evt) { @@ -41,8 +43,8 @@ export function GalleryView(vm) { { onscroll: handleContentScroll }, renderSwitch( { - photos: [AllImagesView, {}, 'allImages', context] - // focus: renderFocus + photos: [AllImagesView, {}, 'allImages', context], + focus: [FocusView, routeParams(), 'focus', context] }, routeName() ) @@ -88,7 +90,6 @@ const fill = injectStyle(FILL_STYLE); const content = styled( { - overflow: 'scroll', ['-webkit-transform']: 'translate3d(0,0,0);' // http://blog.getpostman.com/2015/01/23/ui-repaint-issue-on-chrome/ }, FILL_STYLE diff --git a/packages/gallery/src/services/router.js b/packages/gallery/src/services/router.js index 4de3994..753e683 100644 --- a/packages/gallery/src/services/router.js +++ b/packages/gallery/src/services/router.js @@ -15,9 +15,9 @@ export const router = Router([ name: 'focus', path: '/focus/:id', vars: { - id: /^[_A-Za-z0-9]+$/ + id: /[_A-Za-z0-9]+/ }, - enter: (r, route) => fire('image', route) + enter: (r, route) => fire('focus', route) }, { id: '404', diff --git a/packages/gallery/src/utils/conversion.js b/packages/gallery/src/utils/conversion.js index 98fd776..10a34e1 100644 --- a/packages/gallery/src/utils/conversion.js +++ b/packages/gallery/src/utils/conversion.js @@ -45,7 +45,7 @@ export function deepAssign(to, ...rest) { return to; } -export const pick = id => doc => doc && doc[id]; +export const pick = (id, def) => doc => (doc && doc.hasOwnProperty(id) ? doc[id] : def); export const extractID = pick('_id'); export const extractREV = pick('_rev'); diff --git a/packages/gallery/src/utils/domvm.js b/packages/gallery/src/utils/domvm.js index 0687e38..6b71546 100644 --- a/packages/gallery/src/utils/domvm.js +++ b/packages/gallery/src/utils/domvm.js @@ -6,7 +6,7 @@ import { deepAssign } from './conversion.js'; import { error } from '../services/console.js'; export function subscribeToRender(vm, subscribables, subscriptions) { - const redraw = (...args) => vm.redraw(); + const redraw = () => vm.redraw(); const subList = subscribables.map(s => s.subscribe(redraw)); vm.config({ @@ -48,5 +48,5 @@ export function nodeParentWithType(node, type) { export function renderSwitch(renderMap, switchValue) { const params = renderMap[switchValue]; - return params ? defineView.apply(null, params) : 'NOT FOUND'; + return params ? defineView.apply(null, params) : `VIEW ${switchValue} NOT FOUND`; }