Merge "todosapp: refinements for demo"
diff --git a/browser/defaults.js b/browser/defaults.js
index 6a876f3..9818dc6 100644
--- a/browser/defaults.js
+++ b/browser/defaults.js
@@ -50,6 +50,12 @@
function initData(disp, cb) {
cb = util.logFn('initData', cb);
+ if (util.DEMO) {
+ process.nextTick(function() {
+ return cb();
+ });
+ return;
+ }
var timestamp = Date.now();
async.each(data, function(list, cb) {
disp.addList({name: list.name}, function(err, listId) {
diff --git a/browser/dom_log.js b/browser/dom_log.js
index e7beb8b..51bdc60 100644
--- a/browser/dom_log.js
+++ b/browser/dom_log.js
@@ -36,7 +36,7 @@
logEl.classList.add('visible');
};
- Mousetrap.bind(['ctrl+l', 'meta+l'], function() {
+ Mousetrap.bind(['ctrl+shift+l', 'meta+shift+l'], function() {
logEl.classList.toggle('visible');
});
};
diff --git a/browser/index.js b/browser/index.js
index f916a9e..55a8852 100644
--- a/browser/index.js
+++ b/browser/index.js
@@ -84,7 +84,7 @@
throw err;
}
-// HACKETY HACK
+// HACKETY HACK for demo.
function emailToBlessing(email) {
return 'dev.v.io/u/' + email;
}
@@ -291,10 +291,10 @@
render: function() {
var that = this;
var children = [];
- // TODO(sadovsky): If there are no lists (and thus no todos), we end up
- // rendering "Loading..." here, which is wrong.
- if (!this.props.listId || !this.props.todos) {
- children.push(h('div.loading', {key: 'loading'}, 'Loading...'));
+ if (!this.props.listId) {
+ children.push(h('div.msg', {key: 'msg'}, 'No list selected.'));
+ } else if (!this.props.todos) {
+ children.push(h('div.msg', {key: 'msg'}, 'Loading...'));
} else {
var tagFilter = this.props.tagFilter, items = [];
_.each(this.props.todos, function(todo) {
@@ -372,7 +372,7 @@
}
}, list.name)));
}
- return h('div.list' + (list.selected ? '.selected' : ''), {
+ return h('div.list' + (that.props.selected ? '.selected' : ''), {
onClick: function() {
that.props.setListId(list._id);
},
@@ -396,11 +396,16 @@
},
render: function() {
var that = this, list = this.props.list, shared = Boolean(list.sg);
- var shareUrl;
+ var hShare;
if (shared) {
- var encodedSgName = util.strToHex(list.sg.name);
- var loc = window.location;
- shareUrl = loc.origin + '/share/' + encodedSgName + loc.search;
+ if (util.DEMO) {
+ hShare = h('span', list.sg.name);
+ } else {
+ var loc = window.location;
+ var encodedSgName = util.strToHex(list.sg.name);
+ var shareUrl = '/share/' + encodedSgName + loc.search;
+ hShare = h('a', {href: shareUrl}, shareUrl);
+ }
}
return h('div#status-pane', {
onClick: function(e) {
@@ -447,8 +452,8 @@
}).join(', '))
]),
!shared ? null : h('div.url', {key: 'url'}, [
- h('div.subtitle', {key: 'subtitle'}, 'URL to share with invitees'),
- h('div.value', {key: 'value'}, h('a', {href: shareUrl}, shareUrl))
+ h('div.subtitle', {key: 'subtitle'}, 'Thing to share with invitees'),
+ h('div.value', {key: 'value'}, hShare)
]),
h('div.close', {
key: 'close',
@@ -466,21 +471,23 @@
var that = this;
var children = [h('div.lists-title', {key: 'title'}, 'Todo Lists')];
if (!this.props.lists) {
- children.push(h('div.loading', {key: 'loading'}, 'Loading...'));
+ children.push(h('div.msg', {key: 'msg'}, 'Loading...'));
} else {
- var lists = [];
- _.each(this.props.lists, function(list) {
- list.selected = that.props.listId === list._id;
- lists.push(List({
+ children.push(h('div', {
+ key: 'lists'
+ }, _.map(this.props.lists, function(list) {
+ return List({
key: list._id,
list: list,
+ selected: that.props.listId === list._id,
useSyncbase: that.props.useSyncbase,
setListId: that.props.setListId,
openStatusDialog: that.props.openStatusDialog
- }));
- });
- children.push(h('div', {key: 'lists'}, lists));
- children.push(h('div.new-list', {key: 'new-list'}, h('input', _.assign({
+ });
+ })));
+ children.push(h('div.input-row', {
+ key: 'new-list'
+ }, h('input', _.assign({
type: 'text',
placeholder: 'New list'
}, okCancelEvents({
@@ -492,6 +499,22 @@
e.target.value = '';
}
})))));
+ if (util.DEMO && that.props.useSyncbase) {
+ children.push(h('div.input-row', {
+ key: 'join-list'
+ }, h('input', _.assign({
+ type: 'text',
+ placeholder: 'Join list'
+ }, okCancelEvents({
+ ok: function(value, e) {
+ that.props.joinSyncGroup(value, alertOnError);
+ e.target.value = '';
+ }
+ })))));
+ }
+ }
+ if (that.props.useSyncbase) {
+ children.push(h('div.user-id', {'data-text': userEmail}, userEmail));
}
return h('div#lists-pane', children);
}
@@ -590,6 +613,24 @@
}, cb);
});
},
+ // Joins the specified syncgroup and displays the associated list.
+ joinSyncGroup_: function(sgName, cb) {
+ var that = this;
+ console.assert(this.props.dispType === DISP_TYPE_SYNCBASE);
+ disp.joinSyncGroup(sgName, function(err) {
+ // Note, joinSyncGroup is a noop (no error) if the caller is already a
+ // member, which is the desired behavior here.
+ if (err) return cb(err);
+ var listId = disp.sgNameToListId(sgName);
+ // TODO(sadovsky): Wait for all items to get synced before attempting to
+ // read them?
+ that.updateTodos_(listId, function(err) {
+ if (err) return cb(err);
+ // Note, componentDidUpdate() will update the url.
+ that.setState({listId: listId}, cb);
+ });
+ });
+ },
componentWillMount: function() {
var that = this, props = this.props;
if (props.benchmark) {
@@ -602,22 +643,9 @@
initDispatcher(props.dispType, props.syncbaseName, function(err) {
alertOnError(err);
that.setState({dispInitialized: true}, function() {
- if (!props.joinSgName) return;
- // TODO(sadovsky): Show "please wait..." modal?
- console.assert(props.dispType === DISP_TYPE_SYNCBASE);
- disp.joinSyncGroup(props.joinSgName, function(err) {
- // Note, joinSyncGroup is a noop (no error) if the caller is already a
- // member, which is the desired behavior here.
- alertOnError(err);
- var listId = disp.sgNameToListId(props.joinSgName);
- // TODO(sadovsky): Wait for all items to get synced before attempting
- // to read them?
- that.updateTodos_(listId, function(err) {
- alertOnError(err);
- // Note, componentDidUpdate() will update the url.
- that.setState({listId: listId});
- });
- });
+ if (props.joinSgName) {
+ that.joinSyncGroup_(props.joinSgName, alertOnError);
+ }
});
});
},
@@ -719,6 +747,9 @@
// will be merged with ours.
that.setListId_(listId);
that.setState({showStatusDialog: true});
+ },
+ joinSyncGroup: function(sgName, cb) {
+ that.joinSyncGroup_(sgName, cb);
}
}),
h('div#tags-and-todos-pane', {key: 'tags-and-todos-pane'}, [
@@ -751,13 +782,14 @@
////////////////////////////////////////
// Initialization
-// Start our DOM log module. Developers can press Ctrl+L (or Meta+L) to toggle
+// DOM log module. Developers can press Ctrl+Shift+L (or Meta+Shift+L) to toggle
// visibility of the log.
domLog.init();
function render(props) {
+ var defaultDispType = util.DEMO ? DISP_TYPE_SYNCBASE : DISP_TYPE_COLLECTION;
props = _.assign({
- dispType: u.query.d || DISP_TYPE_COLLECTION,
+ dispType: u.query.d || defaultDispType,
syncbaseName: syncbaseName,
benchmark: Boolean(u.query.bm)
}, props);
diff --git a/browser/util.js b/browser/util.js
index 6fab1e4..7dc9060 100644
--- a/browser/util.js
+++ b/browser/util.js
@@ -6,6 +6,12 @@
var React = require('react');
var vtrace = require('vanadium').vtrace;
+// If true, run in "demo mode":
+// - Start from a blank slate (no predefined lists)
+// - Default to syncbase dispatcher
+// - Include "join list" input box, share list codes instead of urls
+exports.DEMO = true;
+
exports.h = function(selector, props, children) {
if (_.isPlainObject(props)) {
console.assert(!props.id && !props.className);
diff --git a/stylesheets/constants.less b/stylesheets/constants.less
index 8f36820..8c170fd 100644
--- a/stylesheets/constants.less
+++ b/stylesheets/constants.less
@@ -1,4 +1,6 @@
/* https://www.google.com/design/spec/style/color.html */
+@color-green-100: #c8e6c9;
@color-green-700: #388e3c;
+@color-red-100: #ffcdd2;
@color-red-500: #f44336;
@color-red-700: #d32f2f;
diff --git a/stylesheets/index.less b/stylesheets/index.less
index 7bf81b5..5d00d6a 100644
--- a/stylesheets/index.less
+++ b/stylesheets/index.less
@@ -86,6 +86,7 @@
}
#todos-pane, #lists-pane, #tags-pane {
+ position: relative;
overflow: auto;
}
@@ -101,11 +102,11 @@
align-items: center;
}
-.loading {
+#page-pane .msg {
padding: 4px 8px;
}
-.tag {
+#page-pane .tag {
display: inline-block;
margin-left: 8px;
padding: 4px 8px;
@@ -131,14 +132,14 @@
text-align: center;
}
- .list, .new-list {
+ .list, .input-row {
.vcenter;
padding: 0 12px;
width: 100%;
height: 40px;
}
- .list{
+ .list {
&.selected {
background-color: #9be;
font-weight: bold;
@@ -170,10 +171,27 @@
}
}
- .new-list input {
+ .input-row input {
margin-top: 8px;
width: 100%;
}
+
+ .user-id {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ width: 100%;
+ padding: 8px;
+ word-break: break-word;
+
+ /* HACKETY HACK for demo. */
+ &[data-text*=hpucha] {
+ background-color: @color-green-100;
+ }
+ &[data-text*=sadovsky] {
+ background-color: @color-red-100;
+ }
+ }
}
/* Status pane (currently, within #lists-pane) ********************************/