blob: 22401160bdc06539aca21ca67d18cf3e982745dd [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
var constellation = require('./components/constellation');
var css = require('./components/base/index.css');
var debug = require('debug')('reader:main');
var document = require('global/document');
var domready = require('domready');
var each = require('./util').each;
var eos = require('end-of-stream');
var files = require('./components/files');
var footer = require('./components/footer');
var h = require('mercury').h;
var header = require('./components/header');
var hg = require('mercury');
var insert = require('insert-css');
var pdf = require('./components/pdf');
var mover = require('./components/mover');
var removed = require('./util').removed;
var router = require('./router');
var routes = require('./routes');
var vanadium = require('./vanadium');
var window = require('global/window');
var qs = require('qs');
var url = require('url');
// Expose globals for debugging.
window.debug = require('debug');
window.require = require;
global.require = require;
domready(function ondomready() {
debug('domready');
// Top level state.
var state = hg.state({
store: hg.value(null),
files: files.state(),
pdf: pdf.state({}),
mover: mover.state({}),
constellation: constellation.state(),
error: hg.value(null)
});
// TODO(jasoncampbell): add an error component for aggregating, logging, and
// displaying errors in the UI.
state.error(function onstateerror(err) {
if (!err) {
return;
}
console.error('TODO(jasoncampbell): add an error component');
console.error(err.stack);
for (var key in err) {
if (err.hasOwnProperty(key)) {
console.error('* %s => %o', key, err[key]);
}
}
});
router(state, routes).on('notfound', notfound);
// Quick way to change the id of the running application using a query param.
// TODO(jasoncampbell): Create a configuration screen/component.
var query = url.parse(window.location.href).query;
var id = qs.parse(query).id || process.env.ID;
debug('##### %s #####', id);
// The vanadium client is coupled to the application state here so that async
// code paths in the ./vanadium modules can be isolated to the application
// initialization. This allows components to be separately tested/interacted
// with as mappings between data and UI without being tangled into the
// local vanadium discovery process.
var client = vanadium({ id: id });
client.on('error', function onvanadiumerror(err) {
state.error.set(err);
});
client.on('syncbase', function onsyncbase(store) {
state.store.set(store);
store.sync(function onsync(err) {
if (err) {
state.error.set(err);
return;
}
debug('store.sync succeeded!');
});
// Setup watch.
var ws = store.createWatchStream('files');
eos(ws, function(err) {
if (err) {
state.error.set(err);
}
});
ws.on('data', function onwatchchange(change) {
debug('watch stream change: %o', change);
// NOTE: this triggers a recursion between clients :(
if (change.type === 'put') {
state.files.collection.put(change.key, change.value);
}
if (change.type === 'delete') {
state.files.collection.delete(change.key);
}
});
// Scan all keys and populate state.
var stream = store.createReadStream('files');
stream.on('data', function onreadstreamdata(data) {
state.files.collection.put(data.key, data.value);
});
eos(stream, function(err) {
if (err) {
state.error.set(err);
}
// Add the watcher here, after the collection has been populated to
// prevent firing the listener and re-puting all the files again.
state.files.collection(fileschange);
});
});
function fileschange(collection) {
debug('state.files.collection => change: %o', collection);
var store = state.store();
// TODO(jasoncampbell): make sure to try and sync this at some point or
// block all UI until the runtime is ready.
// SEE: http://git.io/vn5YV
if (!store) {
return;
}
removed(collection, function(key) {
store.del('files', key, function(err) {
if (err) {
state.error.set(err);
}
});
});
each(collection, function(key, value) {
store.put('files', value, function callback(err, file) {
if (err) {
state.error.set(err);
}
state.files.collection[file.id].ref.set(file.ref);
});
});
}
hg.app(document.body, state, render);
});
function render(state) {
insert(css);
return h('.reader-app', [
hg.partial(header.render, state),
hg.partial(content, state),
hg.partial(mover.render, state.mover, state.mover.channels),
hg.partial(footer.render, state, state.files.channels.add)
]);
}
function content(state) {
var partial;
if (state.hash) {
partial = hg.partial(pdf.render, state.pdf, state.pdf.channels);
} else {
partial = hg.partial(files.render, state.files, state.files.channels);
}
return h('main', [ partial ]);
}
function notfound(href) {
console.error('TODO: not found error - %s', href);
}