Group tasks ui picked (#7996)

* Added initial group tasks ui

* Changed group compnent directory

* Added group task checklist support

* Added checklist support to ui

* Fixed delete tags route

* Added checklist routes to support new group tasks

* Added assign user tag input

* Added new group members autocomplete directive

* Linked assign ui to api

* Added styles

* Limited tag use

* Fixed line endings

* Updated to new file structure

* Fixed failing task tests

* Updatd with new checklist logic and fixed columns

* Added purchased info to group and prevented non purchased group from seeing new group tasks

* Updated add task function

* Added userid check back to tag routes

* Marked tag tests as pending

* Added comments to pending tests

* Added back routes accidently deleted

* Added locale strings

* Other clarity fixes

* Moved common task function to task service

* Removed files from manifest

* Fixed naming collision and remove logic

* Removed group get tasks until live

* Fixed test to check update task. Removed extra removeTask call. Synced updated checklists. Added purchased to noset

* Fixed delete group task
This commit is contained in:
Keith Holliday
2016-10-03 13:12:20 -07:00
committed by Matteo Pagliazzi
parent 6a82206f81
commit 285041cdee
30 changed files with 1325 additions and 370 deletions
+13 -4
View File
@@ -159,11 +159,11 @@ window.habitrpg = angular.module('habitrpg',
url: '/:gid',
templateUrl: 'partials/options.social.guilds.detail.html',
title: env.t('titleGuilds'),
controller: ['$scope', 'Groups', 'Chat', '$stateParams', 'Members', 'Challenges',
function($scope, Groups, Chat, $stateParams, Members, Challenges){
controller: ['$scope', 'Groups', 'Chat', '$stateParams', 'Members', 'Challenges', 'Tasks',
function($scope, Groups, Chat, $stateParams, Members, Challenges, Tasks) {
Groups.Group.get($stateParams.gid)
.then(function (response) {
$scope.group = response.data.data;
$scope.obj = $scope.group = response.data.data;
Chat.markChatSeen($scope.group._id);
Members.getGroupMembers($scope.group._id)
.then(function (response) {
@@ -177,7 +177,16 @@ window.habitrpg = angular.module('habitrpg',
.then(function (response) {
$scope.group.challenges = response.data.data;
});
});
//@TODO: Add this back when group tasks go live
// return Tasks.getGroupTasks($scope.group._id);
})
// .then(function (response) {
// var tasks = response.data.data;
// tasks.forEach(function (element, index, array) {
// if (!$scope.group[element.type + 's']) $scope.group[element.type + 's'] = [];
// $scope.group[element.type + 's'].push(element);
// })
// });
}]
})
@@ -0,0 +1,73 @@
'use strict';
(function(){
angular
.module('habitrpg')
.directive('groupMembersAutocomplete', groupMembersAutocomplete);
groupMembersAutocomplete.$inject = [
'$parse',
'$rootScope',
];
function groupMembersAutocomplete($parse, $rootScope) {
return {
templateUrl: 'partials/groups.members.autocomplete.html',
compile: function (element, attrs) {
var modelAccessor = $parse(attrs.ngModel);
return function (scope, element, attrs, controller) {
var availableTags = _.pluck(scope.group.members, 'profile.name');
var memberProfileNameToIdMap = _.object(_.map(scope.group.members, function(item) {
return [item.profile.name, item.id]
}));
var memberIdToProfileNameMap = _.object(_.map(scope.group.members, function(item) {
return [item.id, item.profile.name]
}));
var currentTags = [];
_.each(scope.task.group.assignedUsers, function(userId) { currentTags.push(memberIdToProfileNameMap[userId]) })
var taggle = new Taggle('taggle', {
tags: currentTags,
allowedTags: currentTags,
allowDuplicates: false,
onBeforeTagAdd: function(event, tag) {
return confirm(window.env.t('confirmAddTag', {tag: tag}));
},
onTagAdd: function(event, tag) {
$rootScope.$broadcast('addedGroupMember', memberProfileNameToIdMap[tag]);
},
onBeforeTagRemove: function(event, tag) {
return confirm(window.env.t('confirmRemoveTag', {tag: tag}))
},
onTagRemove: function(event, tag) {
$rootScope.$broadcast('removedGroupMember', memberProfileNameToIdMap[tag]);
}
});
var container = taggle.getContainer();
var input = taggle.getInput();
$(input).autocomplete({
source: availableTags, // See jQuery UI documentaton for options
appendTo: container,
position: { at: "left bottom", of: container },
select: function(event, data) {
event.preventDefault();
//Add the tag if user clicks
if (event.which === 1) {
taggle.add(data.item.value);
var taggleTags = taggle.getTags();
scope.$apply(function (scope) {
// Change bound variable
modelAccessor.assign(scope, taggleTags.values);
});
}
}
});
};
},
};
}
}());
@@ -0,0 +1,13 @@
habitrpg.controller('GroupTaskActionsCtrl', ['$scope', 'Shared', 'Tasks', 'User',
function ($scope, Shared, Tasks, User) {
$scope.assignedMembers = [];
$scope.user = User.user;
$scope.$on('addedGroupMember', function(evt, userId) {
Tasks.assignTask($scope.task.id, userId);
});
$scope.$on('removedGroupMember', function(evt, userId) {
Tasks.unAssignTask($scope.task.id, userId);
});
}]);
@@ -0,0 +1,22 @@
'use strict';
(function(){
angular
.module('habitrpg')
.directive('groupTasksActions', hrpgSortTags);
hrpgSortTags.$inject = [
];
function hrpgSortTags() {
return {
scope: {
task: '=',
group: '=',
},
templateUrl: 'partials/groups.tasks.actions.html',
controller: 'GroupTaskActionsCtrl',
};
}
}());
@@ -0,0 +1,79 @@
habitrpg.controller('GroupTasksCtrl', ['$scope', 'Shared', 'Tasks', 'User', function ($scope, Shared, Tasks, User) {
$scope.editTask = Tasks.editTask;
$scope.toggleBulk = Tasks.toggleBulk;
$scope.cancelTaskEdit = Tasks.cancelTaskEdit;
function addTask (listDef, task) {
var task = Shared.taskDefaults({text: task, type: listDef.type});
//If the group has not been created, we bulk add tasks on save
var group = $scope.obj;
if (group._id) Tasks.createGroupTasks(group._id, task);
if (!group[task.type + 's']) group[task.type + 's'] = [];
group[task.type + 's'].unshift(task);
delete listDef.newTask;
};
$scope.addTask = function(listDef) {
Tasks.addTasks(listDef, addTask);
};
$scope.removeTask = function(task, group) {
if (!Tasks.removeTask(task)) return;
//We only pass to the api if the group exists, otherwise, the tasks only exist on the client
if (group._id) Tasks.deleteTask(task._id);
var index = group[task.type + 's'].indexOf(task);
group[task.type + 's'].splice(index, 1);
};
$scope.saveTask = function(task, stayOpen, isSaveAndClose) {
Tasks.saveTask (task, stayOpen, isSaveAndClose);
Tasks.updateTask(task._id, task);
};
$scope.shouldShow = function(task, list, prefs){
return true;
};
$scope.canEdit = function(task) {
return true;
};
/*
------------------------
Tags
------------------------
*/
$scope.updateTaskTags = function (tagId, task) {
var tagIndex = task.tags.indexOf(tagId);
if (tagIndex === -1) {
Tasks.addTagToTask(task._id, tagId);
task.tags.push(tagId);
} else {
Tasks.removeTagFromTask(task._id, tagId);
task.tags.splice(tagIndex, 1);
}
};
/*
------------------------
Checklists
------------------------
*/
$scope.addChecklist = Tasks.addChecklist;
$scope.addChecklistItem = Tasks.addChecklistItemToUI;
$scope.removeChecklistItem = Tasks.removeChecklistItemFromUI;
$scope.swapChecklistItems = Tasks.swapChecklistItems;
$scope.navigateChecklist = Tasks.navigateChecklist;
$scope.checklistCompletion = Tasks.checklistCompletion;
$scope.collapseChecklist = function (task) {
Tasks.collapseChecklist(task);
//@TODO: Currently the api save of the task is separate, so whenever we need to save the task we need to call the respective api
Tasks.updateTask(task._id, task);
};
}]);
@@ -0,0 +1,21 @@
'use strict';
(function(){
angular
.module('habitrpg')
.directive('groupTasks', hrpgSortTags);
hrpgSortTags.$inject = [
];
function hrpgSortTags() {
return {
scope: true,
templateUrl: 'partials/groups.tasks.html',
controller: 'GroupTasksCtrl',
link: function($scope, element, attrs, ngModel) {
},
};
}
}());
+18 -67
View File
@@ -101,24 +101,8 @@ habitrpg.controller("TasksCtrl", ['$scope', '$rootScope', '$location', 'User','N
};
$scope.saveTask = function(task, stayOpen, isSaveAndClose) {
if (task._edit) {
angular.copy(task._edit, task);
}
task._edit = undefined;
if (task.checklist) {
task.checklist = _.filter(task.checklist, function (i) {
return !!i.text
});
}
Tasks.saveTask (task, stayOpen, isSaveAndClose);
User.updateTask(task, {body: task});
if (!stayOpen) task._editing = false;
if (isSaveAndClose) {
$("#task-" + task._id).parent().children('.popover').removeClass('in');
}
if (task.type == 'habit') Guide.goto('intro', 3);
};
@@ -132,8 +116,7 @@ habitrpg.controller("TasksCtrl", ['$scope', '$rootScope', '$location', 'User','N
$scope.cancelTaskEdit = Tasks.cancelTaskEdit;
$scope.removeTask = function(task) {
if (!confirm(window.env.t('sureDelete', {taskType: window.env.t(task.type), taskText: task.text}))) return;
task._edit = undefined;
if (!Tasks.removeTask(task)) return;
User.deleteTask({params:{id: task._id, taskType: task.type}})
};
@@ -190,60 +173,28 @@ habitrpg.controller("TasksCtrl", ['$scope', '$rootScope', '$location', 'User','N
Checklists
------------------------
*/
function focusChecklist(task,index) {
window.setTimeout(function(){
$('#task-'+task._id+' .checklist-form input[type="text"]')[index].focus();
});
}
/*
------------------------
Checklists
------------------------
*/
$scope.addChecklist = Tasks.addChecklist;
$scope.addChecklist = function(task) {
task._edit.checklist = [{completed:false, text:""}];
focusChecklist(task._edit,0);
}
$scope.addChecklistItem = Tasks.addChecklistItemToUI;
$scope.addChecklistItem = function(task, $event, $index) {
if (task._edit.checklist[$index].text) {
if ($index === task._edit.checklist.length - 1) {
task._edit.checklist.push({ completed: false, text: '' });
}
focusChecklist(task._edit, $index + 1);
} else {
// TODO Provide UI feedback that this item is still blank
}
}
$scope.removeChecklistItem = Tasks.removeChecklistItemFromUI;
$scope.removeChecklistItem = function(task, $event, $index, force) {
// Remove item if clicked on trash icon
if (force) {
task._edit.checklist.splice($index, 1);
} else if (!task._edit.checklist[$index].text) {
// User deleted all the text and is now wishing to delete the item
// saveTask will prune the empty item
// Move focus if the list is still non-empty
if ($index > 0)
focusChecklist(task._edit, $index-1);
// Don't allow the backspace key to navigate back now that the field is gone
$event.preventDefault();
}
}
$scope.swapChecklistItems = Tasks.swapChecklistItems;
$scope.swapChecklistItems = function(task, oldIndex, newIndex) {
var toSwap = task._edit.checklist.splice(oldIndex, 1)[0];
task._edit.checklist.splice(newIndex, 0, toSwap);
}
$scope.navigateChecklist = Tasks.navigateChecklist;
$scope.navigateChecklist = function(task,$index,$event){
focusChecklist(task, $event.keyCode == '40' ? $index+1 : $index-1);
}
$scope.checklistCompletion = Tasks.checklistCompletion;
$scope.checklistCompletion = function(checklist){
return _.reduce(checklist,function(m,i){return m+(i.completed ? 1 : 0);},0)
}
$scope.collapseChecklist = function(task) {
task.collapseChecklist = !task.collapseChecklist;
$scope.saveTask(task,true);
}
$scope.collapseChecklist = function (task) {
Tasks.collapseChecklist(task);
//@TODO: Currently the api save of the task is separate, so whenever we need to save the task we need to call the respective api
Tasks.updateTask(task._id, task);
};
/*
------------------------
+132 -1
View File
@@ -29,6 +29,33 @@ angular.module('habitrpg')
list.focus = true;
};
function removeTask (task) {
if (!confirm(window.env.t('sureDelete', {taskType: window.env.t(task.type), taskText: task.text}))) {
return false;
};
task._edit = undefined;
return true;
}
function saveTask (task, stayOpen, isSaveAndClose) {
if (task._edit) {
angular.copy(task._edit, task);
}
task._edit = undefined;
if (task.checklist) {
task.checklist = _.filter(task.checklist, function (i) {
return !!i.text
});
}
if (!stayOpen) task._editing = false;
if (isSaveAndClose) {
$("#task-" + task._id).parent().children('.popover').removeClass('in');
}
}
function getUserTasks (getCompletedTodos) {
var url = '/api/v3/tasks/user';
@@ -64,6 +91,21 @@ angular.module('habitrpg')
});
};
function getGroupTasks (groupId) {
return $http({
method: 'GET',
url: '/api/v3/tasks/group/' + groupId,
});
};
function createGroupTasks (groupId, taskDetails) {
return $http({
method: 'POST',
url: '/api/v3/tasks/group/' + groupId,
data: taskDetails,
});
};
function getTask (taskId) {
return $http({
method: 'GET',
@@ -175,6 +217,20 @@ angular.module('habitrpg')
});
};
function assignTask (taskId, userId) {
return $http({
method: 'POST',
url: '/api/v3/tasks/' + taskId + '/assign/' + userId,
});
};
function unAssignTask (taskId, userId) {
return $http({
method: 'POST',
url: '/api/v3/tasks/' + taskId + '/unassign/' + userId,
});
};
function editTask(task, user) {
task._editing = true;
task._tags = !user.preferences.tagsCollapsed;
@@ -211,14 +267,79 @@ angular.module('habitrpg')
return cleansedTask;
}
/*
------------------------
Checklists
------------------------
*/
function focusChecklist(task, index) {
window.setTimeout(function(){
$('#task-'+task._id+' .checklist-form input[type="text"]')[index].focus();
});
}
function addChecklist(task) {
task._edit.checklist = [{completed:false, text:""}];
focusChecklist(task._edit,0);
}
function addChecklistItemToUI(task, $event, $index) {
if (task._edit.checklist[$index].text) {
if ($index === task._edit.checklist.length - 1) {
task._edit.checklist.push({ completed: false, text: '' });
}
focusChecklist(task._edit, $index + 1);
} else {
// TODO Provide UI feedback that this item is still blank
}
}
function removeChecklistItemFromUI(task, $event, $index, force) {
// Remove item if clicked on trash icon
if (force) {
task._edit.checklist.splice($index, 1);
} else if (!task._edit.checklist[$index].text) {
// User deleted all the text and is now wishing to delete the item
// saveTask will prune the empty item
// Move focus if the list is still non-empty
if ($index > 0)
focusChecklist(task._edit, $index-1);
// Don't allow the backspace key to navigate back now that the field is gone
$event.preventDefault();
}
}
function swapChecklistItems(task, oldIndex, newIndex) {
var toSwap = task._edit.checklist.splice(oldIndex, 1)[0];
task._edit.checklist.splice(newIndex, 0, toSwap);
}
function navigateChecklist(task,$index,$event) {
focusChecklist(task, $event.keyCode == '40' ? $index+1 : $index-1);
}
function checklistCompletion(checklist) {
return _.reduce(checklist,function(m,i){return m+(i.completed ? 1 : 0);},0)
}
function collapseChecklist(task) {
task.collapseChecklist = !task.collapseChecklist;
saveTask(task, true);
}
return {
addTasks: addTasks,
toggleBulk: toggleBulk,
getUserTasks: getUserTasks,
removeTask: removeTask,
saveTask: saveTask,
loadedCompletedTodos: false,
createUserTasks: createUserTasks,
getChallengeTasks: getChallengeTasks,
createChallengeTasks: createChallengeTasks,
getGroupTasks: getGroupTasks,
createGroupTasks: createGroupTasks,
getTask: getTask,
updateTask: updateTask,
deleteTask: deleteTask,
@@ -235,6 +356,16 @@ angular.module('habitrpg')
clearCompletedTodos: clearCompletedTodos,
editTask: editTask,
cancelTaskEdit: cancelTaskEdit,
cloneTask: cloneTask
cloneTask: cloneTask,
assignTask: assignTask,
unAssignTask: unAssignTask,
addChecklist: addChecklist,
addChecklistItemToUI: addChecklistItemToUI,
removeChecklistItemFromUI: removeChecklistItemFromUI,
swapChecklistItems: swapChecklistItems,
navigateChecklist: navigateChecklist,
checklistCompletion: checklistCompletion,
collapseChecklist: collapseChecklist,
};
}]);