save project with a loading sign and a notification

This commit is contained in:
Radek Stepan 2014-10-03 20:11:40 -07:00
parent f108813d29
commit 51be190aea
7 changed files with 157 additions and 62 deletions

61
TODO.md
View File

@ -2,36 +2,57 @@
##Release: MVP ##Release: MVP
- [ ] create a 500/400/loading system messages ###GitHub
- [ ] deal with Firebase timing out, are we still logged-in?
- [ ] mediator `!app/notify/edit` will edit the current notification
- [ ] verify that project exists on project page when fetching it remotely (add behind the scenes)
- [ ] need to show status (receiving information etc.) per repo
- [ ] provide a documentation site
- [ ] visiting a chart page saves the project if it isn't saved already
- [ ] landing page for the project and put message on my repo
- [ ] Handle [404](https://www.firebase.com/docs/hosting/guide/url-redirects-rewrites.html#section-404) on routes; from catch all check if '/' or go 404 controller
- [ ] allow `pushState` when [Firebase hosted](https://www.firebase.com/docs/hosting/guide/url-redirects-rewrites.html#section-rewrites)
- [ ] progress needs to be calculated based on strategy even on homepage, then sort the milestones based on priority - [ ] progress needs to be calculated based on strategy even on homepage, then sort the milestones based on priority
- [ ] calculate left margin based on the total number of points text width
- [ ] Do not show login/logged-in state when we are still fetching that information from Firebase ###Notifications
- [ ] create a 500/400/loading system messages
- [ ] mediator `!app/notify/edit` will edit the current notification
- [ ] need to show status (receiving information etc.) per repo
###Error Handling
- [ ] verify that project exists on project page when fetching it remotely (add behind the scenes)
- [ ] deal with Firebase timing out, are we still logged-in?
- [ ] visiting a chart page saves the project if it isn't saved already
- [ ] Check that we have not run out of requests to make
- [ ] can we get more than 1 notification at a time?
- [x] Validate repo input and show a loading sign of sorts
###Bugs
- [ ] local storage is getting reset - [ ] local storage is getting reset
- [ ] a bit of a freeze when fetching `mbostock/d3` - [ ] a bit of a freeze when fetching `mbostock/d3`
- [ ] Validate repo input and show a loading sign of sorts
- [ ] Check that we have not run out of requests to make ###Docs
- [ ] Show loading sign on top of [browser window](https://github.com/buunguyen/topbar) which is unobtrusive enough we can show it immediately.
- [ ] landing page for the project and put message on my repo
- [ ] provide a documentation site
###Routing
- [ ] Handle [404](https://www.firebase.com/docs/hosting/guide/url-redirects-rewrites.html#section-404) on routes; from catch all check if '/' or go 404 controller
- [ ] allow `pushState` when [Firebase hosted](https://www.firebase.com/docs/hosting/guide/url-redirects-rewrites.html#section-rewrites)
###Style
- [ ] focus on form fields style
- [ ] switch off user select on buttons
###Misc
- [ ] show hero box or projects with a fade in and only when known
- [ ] calculate left margin based on the total number of points text width
- [ ] show a countdown clock towards the end of the milestone or show overdue - [ ] show a countdown clock towards the end of the milestone or show overdue
- [ ] highlight for a moment recently changed milestone
- [ ] smooth animation when transitioning between icons and notifications
- [ ] show logged-in state only after it is known
- [ ] trigger success topbar when we have completed a milestone on chart page; show plain when we are behind - [ ] trigger success topbar when we have completed a milestone on chart page; show plain when we are behind
- [ ] on chart page show a little progress bar in the title - [ ] on chart page show a little progress bar in the title
- [ ] use tap plugin for Ractive - [ ] use tap plugin for Ractive
- [x] logged-in state fade into view independent of page loading
- [x] upgrade to Ractive [0.6.0](https://github.com/ractivejs/ractive/blob/dev/CHANGELOG.md)
##Future Releases ##Future Releases
- [ ] smooth animation when transitioning between icons and notifications
- [ ] show animated lines when drawing the chart - [ ] show animated lines when drawing the chart
- [ ] highlight changes from past fetch - [ ] highlight changes from past fetch
- [ ] In add a project form autocomplete on my username, orgs I am member of and repos I have access to - [ ] In add a project form autocomplete on my username, orgs I am member of and repos I have access to

View File

@ -41004,7 +41004,7 @@ Router.prototype.mount = function(routes, path) {
} }
}); });
}, },
add: function(repo, add) { add: function(repo, done) {
var _this = this; var _this = this;
return request.allMilestones(repo, function(err, res) { return request.allMilestones(repo, function(err, res) {
var milestones; var milestones;
@ -41772,7 +41772,7 @@ Router.prototype.mount = function(routes, path) {
// new.mustache // new.mustache
root.require.register('burnchart/src/templates/pages/new.js', function(exports, require, module) { root.require.register('burnchart/src/templates/pages/new.js', function(exports, require, module) {
module.exports = ["<div id=\"content\" class=\"wrap\">"," <div id=\"add\">"," <div class=\"header\">"," <h2>Add a Project</h2>"," <p>Type in the name of the repository as you would normally. If you'd like to add a private GitHub project, <a href=\"#\">Sign In</a> first.</p>"," </div>",""," <div class=\"form\">"," <table>"," <tr>"," <td>"," <input type=\"text\" placeholder=\"user/repo\" autocomplete=\"off\" value=\"{{value}}\">"," </td>"," <td>"," <a on-click=\"submit\">Add</a>"," </td>"," </tr>"," </table>"," </div>"," </div>","</div>"].join("\n"); module.exports = ["<div id=\"content\" class=\"wrap\">"," <div id=\"add\">"," <div class=\"header\">"," <h2>Add a Project</h2>"," <p>Type in the name of the repository as you would normally. If you'd like to add a private GitHub project, <a href=\"#\">Sign In</a> first.</p>"," </div>",""," <div class=\"form\">"," <table>"," <tr>"," <td>"," <input type=\"text\" placeholder=\"user/repo\" autocomplete=\"off\" value=\"{{value}}\" on-keyup=\"submit:{{value}}\">"," </td>"," <td>"," <a on-click=\"submit:{{value}}\">Add</a>"," </td>"," </tr>"," </table>"," </div>"," </div>","</div>"].join("\n");
}); });
// project.mustache // project.mustache
@ -41849,6 +41849,21 @@ Router.prototype.mount = function(routes, path) {
}); });
// key.coffee
root.require.register('burnchart/src/utils/key.js', function(exports, require, module) {
module.exports = {
is: function(evt) {
var _ref;
return (_ref = evt.original.type) === 'keyup' || _ref === 'keydown';
},
isEnter: function(evt) {
return evt.original.which === 13;
}
};
});
// mixins.coffee // mixins.coffee
root.require.register('burnchart/src/utils/mixins.js', function(exports, require, module) { root.require.register('burnchart/src/utils/mixins.js', function(exports, require, module) {
@ -42158,12 +42173,16 @@ Router.prototype.mount = function(routes, path) {
// new.coffee // new.coffee
root.require.register('burnchart/src/views/pages/new.js', function(exports, require, module) { root.require.register('burnchart/src/views/pages/new.js', function(exports, require, module) {
var mediator, user; var key, mediator, system, user;
mediator = require('../../modules/mediator'); mediator = require('../../modules/mediator');
system = require('../../models/system');
user = require('../../models/user'); user = require('../../models/user');
key = require('../../utils/key');
module.exports = Ractive.extend({ module.exports = Ractive.extend({
'template': require('../../templates/pages/new'), 'template': require('../../templates/pages/new'),
'data': { 'data': {
@ -42171,6 +42190,25 @@ Router.prototype.mount = function(routes, path) {
user: user user: user
}, },
'adapt': [Ractive.adaptors.Ractive], 'adapt': [Ractive.adaptors.Ractive],
submit: function(evt, value) {
var done, name, owner, _ref;
if (key.is(evt) && !key.isEnter(evt)) {
return;
}
_ref = value.split('/'), owner = _ref[0], name = _ref[1];
done = system.async();
return mediator.fire('!projects/add', {
owner: owner,
name: name
}, function(err) {
done();
mediator.fire('!app/notify', {
'text': err || ("Project " + value + " saved."),
'type': err ? 'error' : 'success'
});
return window.location.hash = '#';
});
},
onrender: function() { onrender: function() {
var autocomplete; var autocomplete;
document.title = 'Add a new project'; document.title = 'Add a new project';
@ -42178,16 +42216,8 @@ Router.prototype.mount = function(routes, path) {
this.observe('value', _.debounce(autocomplete, 200), { this.observe('value', _.debounce(autocomplete, 200), {
'init': false 'init': false
}); });
return this.on('submit', function() { this.el.querySelector('input').focus();
var name, owner, _ref; return this.on('submit', this.submit);
_ref = this.get('value').split('/'), owner = _ref[0], name = _ref[1];
return mediator.fire('!projects/add', {
owner: owner,
name: name
}, function() {
return window.location.hash = '#';
});
});
} }
}); });

View File

@ -95,7 +95,7 @@
} }
}); });
}, },
add: function(repo, add) { add: function(repo, done) {
var _this = this; var _this = this;
return request.allMilestones(repo, function(err, res) { return request.allMilestones(repo, function(err, res) {
var milestones; var milestones;
@ -863,7 +863,7 @@
// new.mustache // new.mustache
root.require.register('burnchart/src/templates/pages/new.js', function(exports, require, module) { root.require.register('burnchart/src/templates/pages/new.js', function(exports, require, module) {
module.exports = ["<div id=\"content\" class=\"wrap\">"," <div id=\"add\">"," <div class=\"header\">"," <h2>Add a Project</h2>"," <p>Type in the name of the repository as you would normally. If you'd like to add a private GitHub project, <a href=\"#\">Sign In</a> first.</p>"," </div>",""," <div class=\"form\">"," <table>"," <tr>"," <td>"," <input type=\"text\" placeholder=\"user/repo\" autocomplete=\"off\" value=\"{{value}}\">"," </td>"," <td>"," <a on-click=\"submit\">Add</a>"," </td>"," </tr>"," </table>"," </div>"," </div>","</div>"].join("\n"); module.exports = ["<div id=\"content\" class=\"wrap\">"," <div id=\"add\">"," <div class=\"header\">"," <h2>Add a Project</h2>"," <p>Type in the name of the repository as you would normally. If you'd like to add a private GitHub project, <a href=\"#\">Sign In</a> first.</p>"," </div>",""," <div class=\"form\">"," <table>"," <tr>"," <td>"," <input type=\"text\" placeholder=\"user/repo\" autocomplete=\"off\" value=\"{{value}}\" on-keyup=\"submit:{{value}}\">"," </td>"," <td>"," <a on-click=\"submit:{{value}}\">Add</a>"," </td>"," </tr>"," </table>"," </div>"," </div>","</div>"].join("\n");
}); });
// project.mustache // project.mustache
@ -940,6 +940,21 @@
}); });
// key.coffee
root.require.register('burnchart/src/utils/key.js', function(exports, require, module) {
module.exports = {
is: function(evt) {
var _ref;
return (_ref = evt.original.type) === 'keyup' || _ref === 'keydown';
},
isEnter: function(evt) {
return evt.original.which === 13;
}
};
});
// mixins.coffee // mixins.coffee
root.require.register('burnchart/src/utils/mixins.js', function(exports, require, module) { root.require.register('burnchart/src/utils/mixins.js', function(exports, require, module) {
@ -1249,12 +1264,16 @@
// new.coffee // new.coffee
root.require.register('burnchart/src/views/pages/new.js', function(exports, require, module) { root.require.register('burnchart/src/views/pages/new.js', function(exports, require, module) {
var mediator, user; var key, mediator, system, user;
mediator = require('../../modules/mediator'); mediator = require('../../modules/mediator');
system = require('../../models/system');
user = require('../../models/user'); user = require('../../models/user');
key = require('../../utils/key');
module.exports = Ractive.extend({ module.exports = Ractive.extend({
'template': require('../../templates/pages/new'), 'template': require('../../templates/pages/new'),
'data': { 'data': {
@ -1262,6 +1281,25 @@
user: user user: user
}, },
'adapt': [Ractive.adaptors.Ractive], 'adapt': [Ractive.adaptors.Ractive],
submit: function(evt, value) {
var done, name, owner, _ref;
if (key.is(evt) && !key.isEnter(evt)) {
return;
}
_ref = value.split('/'), owner = _ref[0], name = _ref[1];
done = system.async();
return mediator.fire('!projects/add', {
owner: owner,
name: name
}, function(err) {
done();
mediator.fire('!app/notify', {
'text': err || ("Project " + value + " saved."),
'type': err ? 'error' : 'success'
});
return window.location.hash = '#';
});
},
onrender: function() { onrender: function() {
var autocomplete; var autocomplete;
document.title = 'Add a new project'; document.title = 'Add a new project';
@ -1269,16 +1307,8 @@
this.observe('value', _.debounce(autocomplete, 200), { this.observe('value', _.debounce(autocomplete, 200), {
'init': false 'init': false
}); });
return this.on('submit', function() { this.el.querySelector('input').focus();
var name, owner, _ref; return this.on('submit', this.submit);
_ref = this.get('value').split('/'), owner = _ref[0], name = _ref[1];
return mediator.fire('!projects/add', {
owner: owner,
name: name
}, function() {
return window.location.hash = '#';
});
});
} }
}); });

View File

@ -17,7 +17,7 @@ module.exports = new Model
, (err) -> , (err) ->
throw err if err throw err if err
add: (repo, add) -> add: (repo, done) ->
# TODO: warn when we are adding an existing repo (or # TODO: warn when we are adding an existing repo (or
# silently go to index again). # silently go to index again).

View File

@ -9,10 +9,10 @@
<table> <table>
<tr> <tr>
<td> <td>
<input type="text" placeholder="user/repo" autocomplete="off" value="{{value}}"> <input type="text" placeholder="user/repo" autocomplete="off" value="{{value}}" on-keyup="submit:{{value}}">
</td> </td>
<td> <td>
<a on-click="submit">Add</a> <a on-click="submit:{{value}}">Add</a>
</td> </td>
</tr> </tr>
</table> </table>

View File

@ -12,7 +12,7 @@ module.exports = Ractive.extend
'hidden': yes 'hidden': yes
'defaults': 'defaults':
'text': '' 'text': ''
'type': '' 'type': '' # bland grey style
'system': no 'system': no
'icon': 'megaphone' 'icon': 'megaphone'
'ttl': 5e3 'ttl': 5e3
@ -20,7 +20,6 @@ module.exports = Ractive.extend
# Show a notification. # Show a notification.
show: (opts) -> show: (opts) ->
@set 'hidden', no @set 'hidden', no
# Set the opts. # Set the opts.
@set opts = _.defaults opts, @data.defaults @set opts = _.defaults opts, @data.defaults
# Which position to slide to? # Which position to slide to?

View File

@ -1,5 +1,7 @@
mediator = require '../../modules/mediator' mediator = require '../../modules/mediator'
system = require '../../models/system'
user = require '../../models/user' user = require '../../models/user'
key = require '../../utils/key'
module.exports = Ractive.extend module.exports = Ractive.extend
@ -9,6 +11,26 @@ module.exports = Ractive.extend
'adapt': [ Ractive.adaptors.Ractive ] 'adapt': [ Ractive.adaptors.Ractive ]
# Listen to Enter keypress or Submit button click.
submit: (evt, value) ->
return if key.is(evt) and not key.isEnter(evt)
[ owner, name ] = value.split('/')
done = do system.async
# Save repo.
mediator.fire '!projects/add', { owner, name }, (err) ->
do done
mediator.fire '!app/notify',
'text': err or "Project #{value} saved."
'type': if err then 'error' else 'success'
# Redirect to the dashboard.
# TODO: trigger a named route
window.location.hash = '#'
onrender: -> onrender: ->
document.title = 'Add a new project' document.title = 'Add a new project'
@ -18,14 +40,7 @@ module.exports = Ractive.extend
@observe 'value', _.debounce(autocomplete, 200), { 'init': no } @observe 'value', _.debounce(autocomplete, 200), { 'init': no }
# TODO: focus on the input field # Focus on the input field.
do @el.querySelector('input').focus
# TODO: listen to Enter keypress. @on 'submit', @submit
@on 'submit', ->
[ owner, name ] = @get('value').split('/')
# TODO: save repo & persist.
mediator.fire '!projects/add', { owner, name }, ->
# Redirect to the dashboard.
# TODO: trigger a named route
window.location.hash = '#'