From 0c2e8179e22789d65f0dbf2347c9345720b8c84c Mon Sep 17 00:00:00 2001 From: Is Isilon Date: Tue, 2 Feb 2016 13:25:24 +0800 Subject: [PATCH] isfolded column, uuid instead of int for id --- bower.json | 4 +- db/models/task.js | 9 +- public/javascripts/collections/list.js | 25 ++- public/javascripts/data/demo.json | 227 ++++++++++++------------- public/javascripts/models/task.js | 20 ++- public/javascripts/util/constants.js | 1 + public/javascripts/views/list.js | 42 ----- public/javascripts/views/page.js | 23 +-- public/javascripts/views/task.js | 32 ++-- public/stylesheets/app.css | 4 + public/templates/task.html | 14 +- server.js | 29 +++- 12 files changed, 215 insertions(+), 215 deletions(-) diff --git a/bower.json b/bower.json index eb8d4fa..d8b466f 100644 --- a/bower.json +++ b/bower.json @@ -22,7 +22,9 @@ "localforage-backbone": "~0.6.2", "normalize-css": "normalize.css#~3.0.3", "backbone.marionette": "~2.4.4", - "underscore": "~1.8.3" + "underscore": "~1.8.3", + "foundation-sites": "~6.1.2", + "foundation-icon-fonts": "*" }, "resolutions": { "jquery": "^1.8.0 || ^2.0.0" diff --git a/db/models/task.js b/db/models/task.js index 251a2ea..183e133 100644 --- a/db/models/task.js +++ b/db/models/task.js @@ -3,13 +3,14 @@ var Sequelize = require('sequelize'); module.exports = { instance: function(orm) { task = orm.define('Tasks', { - id : {type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true}, + id : {type: Sequelize.UUID, primaryKey: true, defaultValue: Sequelize.UUIDV4,}, content : {type: Sequelize.TEXT, allowNull: false}, - parentId : {type: Sequelize.INTEGER, defaultValue: 0}, + parentId : {type: Sequelize.UUID, defaultValue: '00000000-0000-0000-0000-000000000000'}, isCompleted: {type: Sequelize.BOOLEAN, defaultValue: false}, priority : {type: Sequelize.INTEGER, defaultValue: 0}, - createdAt : Sequelize.DATE, - updatedAt : Sequelize.DATE, + isFolded : {type: Sequelize.BOOLEAN, defaultValue: false}, + createdAt : {type: Sequelize.DATE, defaultValue: Sequelize.NOW}, + updatedAt : {type: Sequelize.DATE, defaultValue: Sequelize.NOW}, }); orm.sync(); diff --git a/public/javascripts/collections/list.js b/public/javascripts/collections/list.js index c874f9b..bb4c415 100644 --- a/public/javascripts/collections/list.js +++ b/public/javascripts/collections/list.js @@ -18,6 +18,22 @@ localforageBackbone model: Task, offlineSync: Backbone.localforage.sync("tasks"), + + /** Override backbone parse **/ + // parse: function(models){ + // if (this._isModel(models)) + // return models; + // var parsedModels =[]; + // for (var i = 0; i < models.length; i++) { + // if (this._isModel(models[i])) + // parsedModels.push(models[i]); + // else + // parsedModels.push(new this.model(models[i])); + // } + // return parsedModels; + // + // }, + initialize: function(){ // update order on add, remove. // Ref: http://stackoverflow.com/a/11665085/221742 @@ -53,8 +69,9 @@ localforageBackbone /** Updated priority of each member of list **/ updateModelPriority: function() { this.each(function(model, index) { - if (model) - model.set('priority', index); + if (model && model.get('priority')!==index) + model.save({'priority': index}); + }, this); }, @@ -71,8 +88,8 @@ localforageBackbone }, /** sort by priority then created date **/ - comporator: function(child){ - return [child.get('priority'), child.get('createdAt')]; + comparator: function(child){ + return [child.get('priority')]; }, url: '/tasks' diff --git a/public/javascripts/data/demo.json b/public/javascripts/data/demo.json index 5c0fc67..81d031d 100644 --- a/public/javascripts/data/demo.json +++ b/public/javascripts/data/demo.json @@ -1,115 +1,112 @@ - define(function (require) { - return [{ - "id": 80, - "content": "Welcome to HackFlowy!", - "parentId": 0, - "isCompleted": false, - "priority": 0, - "createdAt": "2016-01-29T05:44:30.858Z", - "updatedAt": "2016-01-29T05:44:30.858Z" - }, { - "id": 81, - "content": "An open-source WorkFlowy clone", - "parentId": 0, - "isCompleted": false, - "priority": 0, - "createdAt": "2016-01-29T05:44:30.858Z", - "updatedAt": "2016-01-29T05:44:30.858Z" - }, { - "id": 82, - "content": "Built using Backbone + Socket.IO", - "parentId": 0, - "isCompleted": false, - "priority": 0, - "createdAt": "2016-01-29T05:44:30.858Z", - "updatedAt": "2016-01-29T05:44:30.858Z" - }, { - "id": 83, - "content": "Pulled this together in a few hours to learn Backbone", - "parentId": 0, - "isCompleted": false, - "priority": 0, - "createdAt": "2016-01-29T05:44:30.858Z", - "updatedAt": "2016-01-29T05:44:30.858Z" - }, { - "id": 84, - "content": "Feel free to try it out and hack on it", - "parentId": 0, - "isCompleted": false, - "priority": 0, - "createdAt": "2016-01-29T05:44:30.858Z", - "updatedAt": "2016-01-29T05:44:30.858Z" - }, { - "id": 85, - "content": "Good Luck!", - "parentId": 0, - "isCompleted": false, - "priority": 0, - "createdAt": "2016-01-29T05:44:30.858Z", - "updatedAt": "2016-01-29T05:44:30.858Z" - }, { - "id": 86, - "content": "P.S", - "parentId": 0, - "isCompleted": false, - "priority": 0, - "createdAt": "2016-01-29T05:44:40.978Z", - "updatedAt": "2016-01-29T05:44:40.978Z" - }, { - "id": 88, - "content": "It makes sense if you don't think about it; I haven't", - "parentId": 0, - "isCompleted": false, - "priority": 0, - "createdAt": "2016-01-29T05:44:58.737Z", - "updatedAt": "2016-01-29T05:45:50.939Z" - }, { - "id": 89, - "content": "Make love not war", - "parentId": 88, - "isCompleted": false, - "priority": 0, - "createdAt": "2016-01-29T05:45:03.048Z", - "updatedAt": "2016-01-29T05:45:57.481Z" - }, { - "id": 91, - "content": "Love can be brought not sold", - "parentId": 88, - "isCompleted": false, - "priority": 0, - "createdAt": "2016-01-29T05:45:32.331Z", - "updatedAt": "2016-01-29T05:46:10.478Z" - }, { - "id": 93, - "content": "How do I love thee? Let me count the ways - Shakespeare", - "parentId": 88, - "isCompleted": false, - "priority": 0, - "createdAt": "2016-01-29T05:46:25.119Z", - "updatedAt": "2016-01-29T05:48:00.604Z" - }, { - "id": 95, - "content": "Therefore: love can be listed and lists can be loved", - "parentId": 88, - "isCompleted": false, - "priority": 0, - "createdAt": "2016-01-29T05:46:38.998Z", - "updatedAt": "2016-01-29T05:48:22.937Z" - }, { - "id": 96, - "content": "Conclusion: lists and love should be free", - "parentId": 88, - "isCompleted": false, - "priority": 0, - "createdAt": "2016-01-29T05:47:26.684Z", - "updatedAt": "2016-01-29T05:48:29.796Z" - }, { - "id": 97, - "content": "But how can our lists be real if our love isnt? - Jaden Smith", - "parentId": 88, - "isCompleted": false, - "priority": 0, - "createdAt": "2016-01-29T05:47:46.930Z", - "updatedAt": "2016-01-29T05:47:46.930Z" - }]; - }); +[{ + "id": "fba0d0d9-d540-03c2-ecd5-cb5c391484ef", + "content": "Welcome to HackFlowy!", + "parentId": "00000000-0000-0000-0000-000000000000", + "isCompleted": false, + "priority": 1, + "createdAt": "2016-01-29T05:44:30.858Z", + "updatedAt": "2016-01-29T05:44:30.858Z" +}, { + "id": "886689fd-8ca1-75ed-8086-fe05904ff039", + "content": "An open-source WorkFlowy clone", + "parentId": "00000000-0000-0000-0000-000000000000", + "isCompleted": false, + "priority": 2, + "createdAt": "2016-01-29T05:44:30.858Z", + "updatedAt": "2016-01-29T05:44:30.858Z" +}, { + "id": "bbc8e79d-5e51-83b8-2639-812471972e04", + "content": "Built using Backbone + Socket.IO", + "parentId": "00000000-0000-0000-0000-000000000000", + "isCompleted": false, + "priority": 3, + "createdAt": "2016-01-29T05:44:30.858Z", + "updatedAt": "2016-01-29T05:44:30.858Z" +}, { + "id": "602f03a8-bfdd-5193-f0bb-b4d0f5fa165c", + "content": "Pulled this together in a few hours to learn Backbone", + "parentId": "00000000-0000-0000-0000-000000000000", + "isCompleted": false, + "priority": 4, + "createdAt": "2016-01-29T05:44:30.858Z", + "updatedAt": "2016-01-29T05:44:30.858Z" +}, { + "id": "1e663464-4c22-bd0a-85a7-3a287ac2f02e", + "content": "Feel free to try it out and hack on it", + "parentId": "00000000-0000-0000-0000-000000000000", + "isCompleted": false, + "priority": 5, + "createdAt": "2016-01-29T05:44:30.858Z", + "updatedAt": "2016-01-29T05:44:30.858Z" +}, { + "id": "d25704a8-fe09-ab2a-7c54-58c5e8113e88", + "content": "Good Luck!", + "parentId": "00000000-0000-0000-0000-000000000000", + "isCompleted": false, + "priority": 6, + "createdAt": "2016-01-29T05:44:30.858Z", + "updatedAt": "2016-01-29T05:44:30.858Z" +}, { + "id": "b61c6f7d-07d0-3fa3-4c30-6e2159280f18", + "content": "P.S", + "parentId": "00000000-0000-0000-0000-000000000000", + "isCompleted": false, + "priority": 7, + "isFolded": true, + "createdAt": "2016-01-29T05:44:40.978Z", + "updatedAt": "2016-01-29T05:44:40.978Z" +}, { + "id": "70a84964-658d-fbf9-4552-e8f243138f6e", + "content": "It makes sense if you don't think about it; I haven't", + "parentId": "00000000-0000-0000-0000-000000000000", + "isCompleted": false, + "priority": 8, + "createdAt": "2016-01-29T05:44:58.737Z", + "updatedAt": "2016-01-29T05:45:50.939Z" +}, { + "id": 89, + "content": "Make love not war", + "parentId": "b61c6f7d-07d0-3fa3-4c30-6e2159280f18", + "isCompleted": false, + "priority": 1, + "createdAt": "2016-01-29T05:45:03.048Z", + "updatedAt": "2016-01-29T05:45:57.481Z" +}, { + "id": 91, + "content": "Love can be brought not sold", + "parentId": "b61c6f7d-07d0-3fa3-4c30-6e2159280f18", + "isCompleted": false, + "priority": 2, + "createdAt": "2016-01-29T05:45:32.331Z", + "updatedAt": "2016-01-29T05:46:10.478Z" +}, { + "id": 93, + "content": "How do I love thee? Let me count the ways - Shakespeare", + "parentId": "b61c6f7d-07d0-3fa3-4c30-6e2159280f18", + "isCompleted": false, + "priority": 3, + "createdAt": "2016-01-29T05:46:25.119Z", + "updatedAt": "2016-01-29T05:48:00.604Z" +}, { + "id": 95, + "content": "Therefore: love can be listed and lists can be loved", + "parentId": "b61c6f7d-07d0-3fa3-4c30-6e2159280f18", + "isCompleted": false, + "priority": 4, + "createdAt": "2016-01-29T05:46:38.998Z", + "updatedAt": "2016-01-29T05:48:22.937Z" +}, { + "content": "Conclusion: lists and love should be free", + "parentId": "b61c6f7d-07d0-3fa3-4c30-6e2159280f18", + "isCompleted": false, + "priority": 5, + "createdAt": "2016-01-29T05:47:26.684Z", + "updatedAt": "2016-01-29T05:48:29.796Z" +}, { + "content": "But how can our lists be real if our love isnt? - Jaden Smith", + "parentId": "b61c6f7d-07d0-3fa3-4c30-6e2159280f18", + "isCompleted": false, + "priority": 6, + "createdAt": "2016-01-29T05:47:46.930Z", + "updatedAt": "2016-01-29T05:47:46.930Z" +}] diff --git a/public/javascripts/models/task.js b/public/javascripts/models/task.js index 9e6bfa5..8c0c009 100644 --- a/public/javascripts/models/task.js +++ b/public/javascripts/models/task.js @@ -1,13 +1,13 @@ define( ['backbone', 'localforage', - 'localforagebackbone' -], - -function ( + 'localforagebackbone', + 'util/constants', +],function ( Backbone, localforage, - localforageBackbone + localforageBackbone, + constants ) { var TaskModel = Backbone.Model.extend({ @@ -24,11 +24,12 @@ function ( }, defaults: { - parentId: 0, + parentId: constants.ROOT_PARENT_ID, content: '', - isCompleted: 0, + isCompleted: false, + isFolded: false, priority: 0, - id: '', + id: undefined, }, toggelCompletedStatus: function (isCompleted) { @@ -48,7 +49,8 @@ function ( }, focusOnView: function(){ - return this.view.$('input:first').focus(); + if (this.view && this.view.$el) + return this.view.$('input:first').focus(); } }); diff --git a/public/javascripts/util/constants.js b/public/javascripts/util/constants.js index 0814ddc..db7b32a 100644 --- a/public/javascripts/util/constants.js +++ b/public/javascripts/util/constants.js @@ -4,6 +4,7 @@ define( function() { var constants = { + ROOT_PARENT_ID: '00000000-0000-0000-0000-000000000000', ENTER_KEY :13, UP_ARROW: 38, diff --git a/public/javascripts/views/list.js b/public/javascripts/views/list.js index 82f4125..9e451f2 100644 --- a/public/javascripts/views/list.js +++ b/public/javascripts/views/list.js @@ -31,48 +31,6 @@ define( initialize: function () { }, - - /** Get root id for the displayed list from the url hash or 0 **/ - // getRootId: function(){ - // var hash = window.location.hash.slice(1); - // return hash ? hash: 0; - // - // }, - - // /** Called when window location hash changes **/ - // changeRootId: function(){ - // this.collection.remove(this.collection.models); - // this.collection.add(pageView.collection.filter(this.filterDirectChildren,this)); - // this.updateBreadCrumbs(); - // }, - - // updateBreadCrumbs: function(){ - // var rootId = this.getRootId(); - // if (rootId){ - // var current = Tasks.get(rootId); - // var breadCrumbs = _.template('<%= content %>')(current.attributes); - // var depth=0; - // while (current.get('parentId') && depth<100){ - // depth++; - // current = Tasks.get(current.get('parentId')); - // breadCrumbs=_.template('<%= content %> > ')(current.attributes)+breadCrumbs; - // } - // if (depth>=100) console.error('Max depth exceeded while making breadCrumbs'); - // breadCrumbs='Home > '+breadCrumbs; - // $('#task-breadcrumbs').append(breadCrumbs); - // - // } - // }, - - // Only show direct children - // filterDirectChildren: function (child, index, collection) { - // var rootId = this.getRootId(); - // if (rootId) - // return child.get('id') == rootId; - // else - // return child.get('parentId') == rootId; - // }, - /** This is the root view in the tree **/ getParentView: function () { return this; diff --git a/public/javascripts/views/page.js b/public/javascripts/views/page.js index 2815289..2e31c8d 100644 --- a/public/javascripts/views/page.js +++ b/public/javascripts/views/page.js @@ -3,9 +3,10 @@ define( 'backbone', 'marionette', 'views/list', -'data/demo', +'text!data/demo.json', 'models/task', -'collections/list' +'collections/list', +'util/constants', ], function( @@ -15,7 +16,8 @@ Marionette, ListView, demoData, Task, -List +List, +constants ) { var PageView = Marionette.View.extend({ @@ -87,7 +89,7 @@ List return hash; else if (hash) window.location.hash=''; - return 0; + return constants.ROOT_PARENT_ID; }, /** Called when window location hash changes **/ @@ -101,23 +103,24 @@ List // Only show direct children filterDirectChildren: function (child, index, collection) { var rootId = this.getRootId(); - if (rootId) - return child.get('id') == rootId; + if (rootId === constants.ROOT_PARENT_ID) + return child.get('parentId') === rootId; else - return child.get('parentId') == rootId; + return child.get('id') == rootId; }, updateBreadCrumbs: function(){ var rootId = this.getRootId(); this.$('#task-breadcrumbs').empty(); - if (rootId){ + if (rootId!==constants.ROOT_PARENT_ID){ var current = this.collection.get(rootId); var breadCrumbs = '';//_.template('<%= content %>')(current.attributes); var depth=0; - while (current.get('parentId') && depth<100){ + while (current && current.get('parentId') && depth<100){ depth++; current = this.collection.get(current.get('parentId')); - breadCrumbs=_.template('<%= content %> > ')(current.attributes)+breadCrumbs; + if (current) + breadCrumbs=_.template('<%= content %> > ')(current.attributes)+breadCrumbs; } if (depth>=100) console.error('Max depth exceeded while making breadCrumbs'); breadCrumbs='Home > '+breadCrumbs; diff --git a/public/javascripts/views/task.js b/public/javascripts/views/task.js index 1e5a4ef..62645f3 100644 --- a/public/javascripts/views/task.js +++ b/public/javascripts/views/task.js @@ -5,7 +5,7 @@ define( 'collections/list', 'util/constants', 'text!../../templates/task.html', - 'marionette', + 'marionette' ], function ( @@ -53,6 +53,7 @@ define( 'click .note:first': 'addNote', 'click .fold-button:first': 'foldChildren', 'click .fold:first': 'foldChildren', + // TODO make this destroy children too 'click .destroy:first': 'destroy', // custom events 'focus': 'this.model.focusOnView', @@ -70,6 +71,7 @@ define( // backlink this.model.view = this; + // get only direct children from all tasks in pageView.collection var children = pageView.collection.filter( function (child, index, collection) { return child.get('parentId') === this.model.id; @@ -98,8 +100,6 @@ define( 'content': data.content, 'isCompleted': data.isCompleted }); - } else { - console.error("task.model.id != data.id", task.model.id, data.id); } }); } @@ -159,8 +159,8 @@ define( if (this.ui.input.val().length < 2 && this.$('.children>ul').length===0) { e.preventDefault(); prev = this.focusOnPrev(); - this.destroy(); this.model.destroy(); + this.destroy(); // set the cursor to the end of the previous input prev.find('input:first').focus(); var input = prev.find('input:first')[0]; @@ -230,9 +230,8 @@ define( /** Finish editing an item **/ close: function () { var value = this.$('.edit:first').val().trim(); - this.model.save({ - content: value - }); + if (this.model.get('content')!=value) + this.model.save({content: value}); this.$el.removeClass('editing'); }, @@ -266,32 +265,23 @@ define( /** Add a new blank note below this **/ addNote: function () { - var currentId = this.ui.input.data('id') || 0; + // var currentId = this.ui.input.data('id') || 0; parentId = this.ui.input.data('parent-id'); - - var index= this.getParentView().collection.indexOf(this.model); - var task = pageView.collection.add({parentId: this.model.get('parentId')}); task.save(); if (index>=0) this.getParentView().collection.add(task, {at:index+1}); else this.getParentView().collection.add(task); - console.log(index); - this.ui.input.blur(); - task.view.ui.input.focus(); + this.$('input:first').blur(); + task.focusOnView(); }, /** Fold children of the clicked element */ foldChildren: function () { - if (this.ui.children.length > 0) { - this.ui.children.toggle(); - this.$el.find('li:first').toggleClass('folded'); - } else { - this.ui.children.show(); - this.$el.find('li:first').removeClass('folded'); - } + var folded = this.$el.find('li:first').hasClass('folded'); + this.model.save({isFolded: !folded}); }, }); diff --git a/public/stylesheets/app.css b/public/stylesheets/app.css index 396dd9b..59370f6 100644 --- a/public/stylesheets/app.css +++ b/public/stylesheets/app.css @@ -5812,6 +5812,10 @@ body #hackflowy #main .children ul.task-view { content: "+"; } +.folded.children { + display: none; +} + .task-completed{ text-decoration: line-through; } diff --git a/public/templates/task.html b/public/templates/task.html index 30fb4f1..a8144d1 100644 --- a/public/templates/task.html +++ b/public/templates/task.html @@ -1,4 +1,8 @@ -
  • +<% if (obj.isFolded){ %> +
  • +<% } else { %> +
  • +<% } %>
  • -
    + +<% if (isFolded){ %> +
    +<% } else { %> +
    +<% } %> +
    diff --git a/server.js b/server.js index 9a4f909..19ae36d 100644 --- a/server.js +++ b/server.js @@ -42,9 +42,21 @@ app.get('/tasks', function (req, res) { }); app.post('/tasks', function (req, res) { + console.log({view: "post('/tasks/:id)", id:req.params.id, body:req.body}); Tasks.create({ content: req.body.content, - parentId: parseInt(req.body.parentId) || 0, + parentId: req.body.parentId || '00000000-0000-0000-0000-000000000000', + isCompleted: false + }).then(function (task) { + res.send(task); + }); +}); + +app.put('/tasks', function (req, res) { + console.log({view: "put('/tasks/:id)", id:req.params.id, body:req.body}); + Tasks.create({ + content: req.body.content, + parentId: req.body.parentId || '00000000-0000-0000-0000-000000000000', isCompleted: false }).then(function (task) { res.send(task); @@ -52,25 +64,29 @@ app.post('/tasks', function (req, res) { }); app.get('/tasks/:id', function (req, res) { + console.log({view: "get('/tasks/:id)", id:req.params.id, body:req.body}); Tasks.findById(req.params.id).then(function (task) { res.send(task); }); }); app.put('/tasks/:id', function (req, res) { - console.log({isCompleted: req.body.isCompleted}); + console.log({view: "put('/tasks/:id)", id:req.params.id, body:req.body}); Tasks.findById(req.params.id).then(function (task) { task.content = req.body.content; - console.log({view: "put('/tasks/:id)", body:req.body}); - task.parentId = parseInt(req.body.parentId) || 0, - task.isCompleted = req.body.isCompleted == 1; + task.priority = req.body.priority; + task.parentId = req.body.parentId || '00000000-0000-0000-0000-000000000000'; + task.isFolded = req.body.isFolded == true; + task.isCompleted = req.body.isCompleted == true; task.save().then(function (task) { res.send(task); - }) + }); }); }); app.delete('/tasks/:id', function (req, res) { + console.log({view: "delete('/tasks/:id)", id:req.params.id, body:req.body}); + console.log({isCompleted: req.body.isCompleted}); Tasks.findById(req.params.id).then(function (task) { task.destroy().then(function () { res.send(''); @@ -80,7 +96,6 @@ app.delete('/tasks/:id', function (req, res) { io.sockets.on('connection', function (socket) { socket.on('task', function (data) { - console.log(data); socket.broadcast.emit('task', data); }); });