es6, itemManager

This commit is contained in:
Mo Bitar
2016-12-15 03:34:39 -06:00
parent 5eadcc2e73
commit 1d4905d17d
9 changed files with 238 additions and 208 deletions

View File

@@ -13,7 +13,7 @@ module.exports = function(grunt) {
js: { js: {
files: ['app/assets/javascripts/**/*.js'], files: ['app/assets/javascripts/**/*.js'],
tasks: ['concat'], tasks: ['concat', 'babel'],
options: { options: {
spawn: false, spawn: false,
}, },
@@ -114,6 +114,18 @@ module.exports = function(grunt) {
} }
}, },
babel: {
options: {
sourceMap: true,
presets: ['es2015']
},
dist: {
files: {
'vendor/assets/javascripts/app.js': 'vendor/assets/javascripts/app.js'
}
}
}
ngAnnotate: { ngAnnotate: {
options: { options: {
singleQuotes: true, singleQuotes: true,
@@ -145,5 +157,5 @@ module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-ng-annotate'); grunt.loadNpmTasks('grunt-ng-annotate');
grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.registerTask('default', ['haml', 'ngtemplates', 'sass', 'concat', 'ngAnnotate', 'uglify']); grunt.registerTask('default', ['haml', 'ngtemplates', 'sass', 'concat', 'babel', 'ngAnnotate', 'uglify']);
}; };

View File

@@ -9,7 +9,7 @@ angular.module('app.frontend')
$scope.allGroup = new Group({name: "All", all: true}); $scope.allGroup = new Group({name: "All", all: true});
$scope.groups = $scope.defaultUser.groups; $scope.groups = $scope.defaultUser.groups;
apiController.verifyEncryptionStatusOfAllNotes($scope.defaultUser, function(success){ apiController.verifyEncryptionStatusOfAllItems($scope.defaultUser, function(success){
}); });
} }
@@ -53,7 +53,7 @@ angular.module('app.frontend')
} }
$scope.groupsSave = function(group, callback) { $scope.groupsSave = function(group, callback) {
apiController.saveGroup($scope.defaultUser, group, callback); apiController.saveItem($scope.defaultUser, group, callback);
} }
/* /*
@@ -64,22 +64,15 @@ angular.module('app.frontend')
var originalNote = _.find($scope.defaultUser.notes, {id: noteCopy.id}); var originalNote = _.find($scope.defaultUser.notes, {id: noteCopy.id});
if(newGroup.all) { $scope.defaultUser.itemManager.removeReferencesBetweenItems(oldGroup, originalNote);
// going to new group, nil out group_id
originalNote.group_id = null;
} else {
originalNote.group_id = newGroup.id
originalNote.group = newGroup;
newGroup.notes.unshift(originalNote); if(!newGroup.all) {
newGroup.notes.sort(function(a,b){ $scope.defaultUser.itemManager.createReferencesBetweenItems(newGroup, originalNote);
//subtract to get a value that is either negative, positive, or zero. newGroup.updateReferencesLocalMapping();
return new Date(b.created_at) - new Date(a.created_at);
});
} }
apiController.saveNote($scope.defaultUser, originalNote, function(note){ apiController.saveBatchItems($scope.defaultUser, [originalNote, newGroup, oldGroup], function(){
_.merge(originalNote, note);
}); });
} }
@@ -91,7 +84,7 @@ angular.module('app.frontend')
var validNotes = Note.filterDummyNotes(group.notes); var validNotes = Note.filterDummyNotes(group.notes);
if(validNotes == 0) { if(validNotes == 0) {
// if no more notes, delete group // if no more notes, delete group
apiController.deleteGroup($scope.defaultUser, group, function(){ apiController.deleteItem($scope.defaultUser, group, function(){
// force scope groups to update on sub directives // force scope groups to update on sub directives
$scope.groups = []; $scope.groups = [];
$timeout(function(){ $timeout(function(){

View File

@@ -1,3 +1,13 @@
var Group = function (json_obj) { class Group extends Item {
_.merge(this, json_obj); constructor(json_obj) {
}; _.merge(this, json_obj);
}
updateReferencesLocalMapping() {
super.updateReferencesLocalMapping();
this.notes = this.referencesMatchingContentType("Note");
this.notes.sort(function(a,b){
return new Date(b.created_at) - new Date(a.created_at);
});
}
}

View File

@@ -0,0 +1,29 @@
class Item {
referencesMatchingContentType(contentType) {
return this.references.filter(function(reference){
return reference.content_type == content_type;
});
}
updateReferencesLocalMapping() {
// should be overriden to manage local properties
}
/* Returns true if note is shared individually or via group */
isPublic() {
return this.presentation;
}
isEncrypted() {
return this.encryptionEnabled() && typeof this.content === 'string' ? true : false;
}
encryptionEnabled() {
return this.loc_eek;
}
presentationURL() {
return this.presentation.url;
}
}

View File

@@ -0,0 +1,55 @@
class ItemManager() {
set items(items) {
this.items = items;
resolveReferences();
}
referencesForItemId(itemId) {
return _.find(this.items, {uuid: itemId});
}
resolveReferences() {
this.items.forEach(function(item){
// build out references
_.map(item.references, function(reference){
return referencesForItemId(reference.uuid);
})
});
}
itemsForContentType(contentType) {
this.items.filter(function(item){
return item.content_type == contentType;
});
}
// returns dirty item references that need saving
deleteItem(item) {
_.remove(this.items, item);
item.references.forEach(function(reference){
removeReferencesFromItem(reference, item);
})
return item.references;
}
removeReferencesBetweenItems(itemOne, itemTwo) {
_.remove(itemOne.references, _.find(itemOne.references, {uuid: itemTwo.uuid}));
_.remove(itemTwo.references, _.find(itemTwo.references, {uuid: itemOne.uuid}));
return [itemOne, itemTwo];
}
removeReferencesBetweenItems(itemOne, itemTwo) {
itemOne.references.push(itemTwo);
itemTwo.references.push(itemOne);
return [itemOne, itemTwo];
}
createReferencesBetweenItems(itemOne, itemTwo) {
itemOne.references.push(itemTwo);
itemTwo.references.push(itemOne);
return [itemOne, itemTwo];
}
}

View File

@@ -1,57 +1,63 @@
var Note = function (json_obj) { class Note extends Item {
var content; constructor(json_obj) {
var content;
Object.defineProperty(this, "content", { Object.defineProperty(this, "content", {
get: function() { get: function() {
return content; return content;
}, },
set: function(value) { set: function(value) {
var finalValue = value; var finalValue = value;
if(typeof value === 'string') { if(typeof value === 'string') {
try { try {
decodedValue = JSON.parse(value); decodedValue = JSON.parse(value);
finalValue = decodedValue; finalValue = decodedValue;
} }
catch(e) { catch(e) {
finalValue = value; finalValue = value;
}
} }
content = finalValue;
},
enumerable: true,
});
this.setContentRaw = function(rawContent) {
content = rawContent;
}
_.merge(this, json_obj);
if(!this.content) {
this.content = {title: "", text: ""};
}
}
filterDummyNotes(notes) {
var filtered = notes.filter(function(note){return note.dummy == false || note.dummy == null});
return filtered;
}
updateReferencesLocalMapping() {
super.updateReferencesLocalMapping();
this.groups = this.referencesMatchingContentType("Group");
}
get hasOnePublicGroup() {
var hasPublicGroup = false;
this.groups.forEach(function(group){
if(group.isPublic()) {
hasPublicGroup = true;
return;
} }
})
content = finalValue; return hasPublicGroup;
},
enumerable: true,
});
this.setContentRaw = function(rawContent) {
content = rawContent;
} }
_.merge(this, json_obj); function isPublic() {
return super.isPublic() || this.hasOnePublicGroup;
if(!this.content) {
this.content = {title: "", text: ""};
} }
};
Note.filterDummyNotes = function(notes) {
var filtered = notes.filter(function(note){return note.dummy == false || note.dummy == null});
return filtered;
}
/* Returns true if note is shared individually or via group */
Note.prototype.isPublic = function() {
return this.presentation || (this.group && this.group.presentation);
};
Note.prototype.isEncrypted = function() {
return this.encryptionEnabled() && typeof this.content === 'string' ? true : false;
}
Note.prototype.encryptionEnabled = function() {
return this.loc_eek;
}
Note.prototype.presentationURL = function() {
return this.presentation.url;
} }

View File

@@ -1,23 +1,23 @@
var User = function (json_obj) { class User {
_.merge(this, json_obj); constructor(json_obj) {
_.merge(this, json_obj);
this.notes = _.map(this.notes, function(json_obj) { this.itemManager = new ItemManager();
return new Note(json_obj); this.itemManager.items = this.items;
}); this.items = null;
this.groups = _.map(this.groups, function(json_obj) { this.notes = _.map(this.itemManager.itemsForContentType("Note"), function(json_obj) {
return new Group(json_obj); return new Note(json_obj);
});
this.groups.forEach(function(group){
var notes = this.notes.filter(function(note){return note.group_id && note.group_id == group.id});
notes.forEach(function(note){
note.group = group;
}) })
group.notes = notes;
}.bind(this))
};
User.prototype.filteredNotes = function() { this.groups = _.map(this.itemManager.itemsForContentType("Group"), function(json_obj) {
return Note.filterDummyNotes(this.notes); var group = Group(json_obj);
group.updateReferencesLocalMapping();
return group;
})
}
filteredNotes() {
return Note.filterDummyNotes(this.notes);
}
} }

View File

@@ -144,7 +144,7 @@ angular.module('app.services')
Ensures that if encryption is disabled, all local notes are uncrypted, Ensures that if encryption is disabled, all local notes are uncrypted,
and that if it's enabled, that all local notes are encrypted and that if it's enabled, that all local notes are encrypted
*/ */
this.verifyEncryptionStatusOfAllNotes = function(user, callback) { this.verifyEncryptionStatusOfAllItems = function(user, callback) {
var allNotes = user.filteredNotes(); var allNotes = user.filteredNotes();
var notesNeedingUpdate = []; var notesNeedingUpdate = [];
allNotes.forEach(function(note){ allNotes.forEach(function(note){
@@ -168,83 +168,13 @@ angular.module('app.services')
/* /*
Groups Items
*/ */
this.restangularizeGroup = function(group, user) { this.saveBatchItems = function(user, items, callback) {
var request = Restangular.one("users", user.id).one("groups", group.id); var request = Restangular.one("users", user.uuid).one("items/batch_update");
_.merge(request, group); request.items = _.map(items, function(item){
return request; return this.createRequestParamsFromItem(item, user);
}
this.saveGroup = function(user, group, callback) {
if(user.id) {
if(!group.route) {
group = this.restangularizeGroup(group, user);
}
group.customOperation(group.id ? "put" : "post").then(function(response) {
callback(response.plain());
})
} else {
this.writeUserToLocalStorage(user);
callback(group);
}
}
this.deleteGroup = function(user, group, callback) {
if(!user.id) {
_.remove(user.groups, group);
this.writeUserToLocalStorage(user);
callback(true);
} else {
Restangular.one("users", user.id).one("groups", group.id).remove()
.then(function(response) {
_.remove(user.groups, group);
callback(true);
})
}
}
this.shareGroup = function(user, group, callback) {
Restangular.one("users", user.id).one("groups", group.id).one("presentations").post()
.then(function(response){
var presentation = response.plain();
_.merge(group, {presentation: presentation});
callback(presentation);
if(group.notes.length > 0) {
// decrypt notes
this.saveBatchNotes(user, group.notes, function(success){})
}
}.bind(this))
}
this.unshareGroup = function(user, group, callback) {
var request = Restangular.one("users", user.id).one("groups", group.id).one("presentations", group.presentation.id);
request.remove().then(function(response){
group.presentation = null;
callback(null);
if(group.notes.length > 0) {
// encrypt notes
var notes = group.notes;
this.saveBatchNotes(user, notes, function(success){})
}
}.bind(this))
}
/*
Notes
*/
this.saveBatchNotes = function(user, notes, callback) {
var request = Restangular.one("users", user.id).one("notes/batch_update");
request.notes = _.map(notes, function(note){
return this.createRequestParamsFromNote(note, user);
}.bind(this)); }.bind(this));
request.put().then(function(response){ request.put().then(function(response){
var success = response.plain().success; var success = response.plain().success;
@@ -252,95 +182,88 @@ angular.module('app.services')
}) })
} }
this.saveNote = function(user, note, callback) { this.saveItem = function(user, item, callback) {
if(!user.id) { if(!user.id) {
this.writeUserToLocalStorage(user); this.writeUserToLocalStorage(user);
callback(note); callback(item);
return; return;
} }
var params = this.createRequestParamsFromNote(note, user); var params = this.createRequestParamsForItem(item, user);
var request = Restangular.one("users", user.id).one("notes", note.id); var request = Restangular.one("users", user.uuid).one("item", item.uuid);
_.merge(request, params); _.merge(request, params);
request.customOperation(request.id ? "put" : "post") request.customOperation(request.uuid ? "put" : "post")
.then(function(response) { .then(function(response) {
var responseObject = response.plain(); var responseObject = response.plain();
responseObject.content = note.content; responseObject.content = item.content;
_.merge(note, responseObject); _.merge(item, responseObject);
callback(note); callback(item);
}) })
.catch(function(response){ .catch(function(response){
callback(null); callback(null);
}) })
} }
this.createRequestParamsFromNote = function(note, user) { this.createRequestParamsForItem = function(item, user) {
var params = {id: note.id}; var params = {uuid: item.uuid};
if(!note.pending_share && !note.isPublic()) { if(!item.isPublic()) {
// encrypted // encrypted
var noteCopy = _.cloneDeep(note); var itemCopy = _.cloneDeep(item);
this.encryptSingleNote(noteCopy, this.retrieveGk()); this.encryptSingleNote(itemCopy, this.retrieveGk());
params.content = noteCopy.content; params.content = itemCopy.content;
params.loc_eek = noteCopy.loc_eek; params.loc_eek = itemCopy.loc_eek;
} }
else { else {
// decrypted // decrypted
params.content = JSON.stringify(note.content); params.content = JSON.stringify(item.content);
params.loc_eek = null; params.loc_eek = null;
} }
return params; return params;
} }
this.deleteNote = function(user, note, callback) { this.deleteItem = function(user, item, callback) {
if(!user.id) { if(!user.id) {
this.writeUserToLocalStorage(user); this.writeUserToLocalStorage(user);
callback(true); callback(true);
} else { } else {
Restangular.one("users", user.id).one("notes", note.id).remove() Restangular.one("users", user.uuid).one("items", item.uuid).remove()
.then(function(response) { .then(function(response) {
callback(true); callback(true);
}) })
} }
} }
this.shareNote = function(user, note, callback) { this.shareItem = function(user, item, callback) {
if(!user.id) { if(!user.id) {
if(confirm("Note: You are not signed in. Any note you share cannot be edited or unshared.")) { alert("You must be signed in to share.");
var request = Restangular.one("notes").one("share");
_.merge(request, {name: note.content.title, content: note.content});
request.post().then(function(response){
var presentation = response.plain();
_.merge(note, {presentation: presentation});
note.locked = true;
this.writeUserToLocalStorage(user);
callback(note);
}.bind(this))
}
} else { } else {
var shareFn = function(note, callback) { Restangular.one("users", user.uuid).one("items", item.uuid).one("presentations").post()
Restangular.one("users", user.id).one("notes", note.id).one("presentations").post() .then(function(response){
.then(function(response){ var presentation = response.plain();
var presentation = response.plain(); _.merge(item, {presentation: presentation});
_.merge(note, {presentation: presentation}); callback(item);
callback(note);
})
}
note.pending_share = true; // decrypt references
this.saveNote(user, note, function(saved_note){ if(item.references.length > 0) {
shareFn(saved_note, callback); this.saveBatchItems(user, item.references, function(success){})
}
}) })
} }
} }
this.unshareNote = function(user, note, callback) { this.unshareItem = function(user, item, callback) {
var request = Restangular.one("users", user.id).one("notes", note.id).one("presentations", note.presentation.id); var request = Restangular.one("users", user.uuid).one("notes", item.uuid).one("presentations", item.presentation.uuid);
request.remove().then(function(response){ request.remove().then(function(response){
note.presentation = null; item.presentation = null;
callback(null); callback(null);
// encrypt references
if(item.references.length > 0) {
this.saveBatchItems(user, item.references, function(success){})
}
}) })
} }
@@ -351,7 +274,7 @@ angular.module('app.services')
this.updatePresentation = function(resource, presentation, callback) { this.updatePresentation = function(resource, presentation, callback) {
var request = Restangular.one("users", user.id) var request = Restangular.one("users", user.id)
.one(resource.constructor.name.toLowerCase() + "s", resource.id) .one("items", resource.id)
.one("presentations", resource.presentation.id); .one("presentations", resource.presentation.id);
_.merge(request, presentation); _.merge(request, presentation);
request.patch().then(function(response){ request.patch().then(function(response){

View File

@@ -2,8 +2,10 @@
"name": "neeto", "name": "neeto",
"version": "1.0.0", "version": "1.0.0",
"devDependencies": { "devDependencies": {
"babel-preset-es2015": "^6.18.0",
"grunt": "^1.0.1", "grunt": "^1.0.1",
"grunt-angular-templates": "^1.1.0", "grunt-angular-templates": "^1.1.0",
"grunt-babel": "^6.0.0",
"grunt-contrib-concat": "^1.0.1", "grunt-contrib-concat": "^1.0.1",
"grunt-contrib-cssmin": "^1.0.2", "grunt-contrib-cssmin": "^1.0.2",
"grunt-contrib-jshint": "~0.10.0", "grunt-contrib-jshint": "~0.10.0",