From 725c3b36f3613721f92d616ae20037a05d9b6a17 Mon Sep 17 00:00:00 2001 From: Husman Date: Sat, 6 Aug 2016 15:32:03 -0700 Subject: [PATCH] feat: allowing modification of challenge tasks after challenge creation. fixes #7320 closes #7877 --- test/api/v3/unit/models/challenge.test.js | 53 +++++++++++++++++++ .../client/js/controllers/challengesCtrl.js | 22 ++++++-- website/client/js/controllers/tasksCtrl.js | 17 ++++++ website/server/models/challenge.js | 3 +- .../shared/tasks/edit/advanced_options.jade | 12 ++--- .../tasks/edit/dailies/repeat_options.jade | 6 +-- .../shared/tasks/edit/habits/plus_minus.jade | 2 +- .../views/shared/tasks/edit/text_notes.jade | 2 +- .../shared/tasks/edit/todos/due_date.jade | 2 +- .../views/shared/tasks/task_view/index.jade | 2 +- 10 files changed, 104 insertions(+), 17 deletions(-) diff --git a/test/api/v3/unit/models/challenge.test.js b/test/api/v3/unit/models/challenge.test.js index b2f0e7ff98..f4ea3370b4 100644 --- a/test/api/v3/unit/models/challenge.test.js +++ b/test/api/v3/unit/models/challenge.test.js @@ -158,4 +158,57 @@ describe('Challenge Model', () => { }); }); }); + + context('type specific updates', () => { + it('updates habit specific field to challenge and challenge members', async () => { + task = new Tasks.habit(Tasks.Task.sanitize(tasksToTest.habit)); // eslint-disable-line babel/new-cap + task.challenge.id = challenge._id; + await task.save(); + + await challenge.addTasks([task]); + + task.up = true; + task.down = false; + + await challenge.updateTask(task); + + let updatedLeader = await User.findOne({_id: leader._id}); + let updatedUserTask = await Tasks.Task.findById(updatedLeader.tasksOrder.habits[0]); + + expect(updatedUserTask.up).to.equal(true); + expect(updatedUserTask.down).to.equal(false); + }); + + it('updates todo specific field to challenge and challenge members', async () => { + task = new Tasks.todo(Tasks.Task.sanitize(tasksToTest.todo)); // eslint-disable-line babel/new-cap + task.challenge.id = challenge._id; + await task.save(); + + await challenge.addTasks([task]); + + task.date = new Date(); + await challenge.updateTask(task); + + let updatedLeader = await User.findOne({_id: leader._id}); + let updatedUserTask = await Tasks.Task.findById(updatedLeader.tasksOrder.todos[0]); + + expect(updatedUserTask.date).to.exist; + }); + + it('updates daily specific field to challenge and challenge members', async () => { + task = new Tasks.daily(Tasks.Task.sanitize(tasksToTest.daily)); // eslint-disable-line babel/new-cap + task.challenge.id = challenge._id; + await task.save(); + + await challenge.addTasks([task]); + + task.everyX = 2; + await challenge.updateTask(task); + + let updatedLeader = await User.findOne({_id: leader._id}); + let updatedUserTask = await Tasks.Task.findById(updatedLeader.tasksOrder.dailys[0]); + + expect(updatedUserTask.everyX).to.eql(2); + }); + }); }); diff --git a/website/client/js/controllers/challengesCtrl.js b/website/client/js/controllers/challengesCtrl.js index 359db5f985..6d1f4f9dab 100644 --- a/website/client/js/controllers/challengesCtrl.js +++ b/website/client/js/controllers/challengesCtrl.js @@ -1,5 +1,5 @@ -habitrpg.controller("ChallengesCtrl", ['$rootScope','$scope', 'Shared', 'User', 'Challenges', 'Notification', '$compile', 'Groups', '$state', '$stateParams', 'Members', 'Tasks', 'TAVERN_ID', - function($rootScope, $scope, Shared, User, Challenges, Notification, $compile, Groups, $state, $stateParams, Members, Tasks, TAVERN_ID) { +habitrpg.controller("ChallengesCtrl", ['$rootScope','$scope', 'Shared', 'User', 'Tasks', 'Challenges', 'Notification', '$compile', 'Groups', '$state', '$stateParams', 'Members', 'Tasks', 'TAVERN_ID', + function($rootScope, $scope, Shared, User, Tasks, Challenges, Notification, $compile, Groups, $state, $stateParams, Members, Tasks, TAVERN_ID) { // Use presence of cid to determine whether to show a list or a single // challenge @@ -36,6 +36,22 @@ habitrpg.controller("ChallengesCtrl", ['$rootScope','$scope', 'Shared', 'User', $scope.editTask = Tasks.editTask; + $scope.canEdit = function(task) { + return true; + } + + $scope.doubleClickTask = function (obj, task) { + if (obj._locked) { + return false; + } + + if (task._editing) { + $scope.saveTask(task); + } else { + $scope.editTask(task); + } + } + /** * Create */ @@ -274,8 +290,8 @@ habitrpg.controller("ChallengesCtrl", ['$rootScope','$scope', 'Shared', 'User', }; $scope.saveTask = function(task){ + Tasks.updateTask(task._id, task); task._editing = false; - // TODO persist } $scope.toggleBulk = function(list) { diff --git a/website/client/js/controllers/tasksCtrl.js b/website/client/js/controllers/tasksCtrl.js index 313b6c7d8f..b0d680fb9d 100644 --- a/website/client/js/controllers/tasksCtrl.js +++ b/website/client/js/controllers/tasksCtrl.js @@ -70,6 +70,23 @@ habitrpg.controller("TasksCtrl", ['$scope', '$rootScope', '$location', 'User','N $scope.editTask = Tasks.editTask; + $scope.canEdit = function(task) { + // can't edit challenge tasks + return !task.challenge.id; + } + + $scope.doubleClickTask = function (obj, task) { + if (obj._locked) { + return false; + } + + if (task._editing) { + $scope.saveTask(task); + } else { + $scope.editTask(task); + } + } + /** * Add the new task to the actions log */ diff --git a/website/server/models/challenge.js b/website/server/models/challenge.js index 902b23171a..aa3e68cd48 100644 --- a/website/server/models/challenge.js +++ b/website/server/models/challenge.js @@ -209,8 +209,9 @@ schema.methods.updateTask = async function challengeUpdateTask (task) { updateCmd.$set[key] = syncableAttrs[key]; } + let taskSchema = Tasks[task.type]; // Updating instead of loading and saving for performances, risks becoming a problem if we introduce more complexity in tasks - await Tasks.Task.update({ + await taskSchema.update({ userId: {$exists: true}, 'challenge.id': challenge.id, 'challenge.taskId': task._id, diff --git a/website/views/shared/tasks/edit/advanced_options.jade b/website/views/shared/tasks/edit/advanced_options.jade index c78c206bee..5a3b1a5ab7 100644 --- a/website/views/shared/tasks/edit/advanced_options.jade +++ b/website/views/shared/tasks/edit/advanced_options.jade @@ -18,13 +18,13 @@ div(ng-if='::task.type!="reward"') input.form-control(type='text', ng-model='task.startDate', datepicker-popup='{{::user.preferences.dateFormat}}', is-open='datepickerOpened', - ng-click='datepickerOpened = true', ng-disabled='task.challenge.id') + ng-click='datepickerOpened = true', ng-disabled='!canEdit(task)') hr .form-group legend.option-title=env.t('repeat') - select.form-control(ng-model='task.frequency', ng-disabled='task.challenge.id') + select.form-control(ng-model='task.frequency', ng-disabled='!canEdit(task)') option(value='weekly')=env.t('repeatWeek') option(value='daily')=env.t('repeatDays') @@ -39,19 +39,19 @@ div(ng-if='::task.type!="reward"') ul.priority-multiplier li button(type='button', ng-class='{active: task.priority==0.1}', - ng-click='task.challenge.id || (task.priority=0.1)') + ng-click='!canEdit(task) || (task.priority=0.1)') =env.t('trivial') li button(type='button', ng-class='{active: task.priority==1 || !task.priority}', - ng-click='task.challenge.id || (task.priority=1)') + ng-click='!canEdit(task) || (task.priority=1)') =env.t('easy') li button(type='button', ng-class='{active: task.priority==1.5}', - ng-click='task.challenge.id || (task.priority=1.5)') + ng-click='!canEdit(task) || (task.priority=1.5)') =env.t('medium') li button(type='button', ng-class='{active: task.priority==2}', - ng-click='task.challenge.id || (task.priority=2)') + ng-click='!canEdit(task) || (task.priority=2)') =env.t('hard') span(ng-if='task.type=="daily"') diff --git a/website/views/shared/tasks/edit/dailies/repeat_options.jade b/website/views/shared/tasks/edit/dailies/repeat_options.jade index 7f2127af71..8388c4ecaa 100644 --- a/website/views/shared/tasks/edit/dailies/repeat_options.jade +++ b/website/views/shared/tasks/edit/dailies/repeat_options.jade @@ -3,9 +3,9 @@ legend.option-title popover='{{env.t(task.frequency + "RepeatHelpContent")}}')=env.t('repeatEvery') // If frequency is daily -ng-form.form-group(name='everyX' ng-if='task.frequency=="daily"') +ng-form.form-group(name='everyX', ng-if='task.frequency=="daily"') .input-group - input.form-control(type='number', ng-model='task.everyX', ng-disabled='task.challenge.id', min='0', required) + input.form-control(type='number', ng-model='task.everyX', min='0', ng-disabled='!canEdit(task)', required) span.input-group-addon {{task.everyX == 1 ? env.t('day') : env.t('days')}} // If frequency is weekly @@ -15,7 +15,7 @@ ng-form.form-group(name='everyX' ng-if='task.frequency=="daily"') mixin dayOfWeek(day, num) li button(type='button', ng-class='{active: task.repeat.#{day}}', - ng-disabled='task.challenge.id', ng-click='task.repeat.#{day} = !task.repeat.#{day}', + ng-disabled='!canEdit(task)', ng-click='task.repeat.#{day} = !task.repeat.#{day}', tooltip='{{env.t((task.repeat.#{day}) ? "due" : "notDue")}}') | {{::moment.weekdaysMin(#{num})}} diff --git a/website/views/shared/tasks/edit/habits/plus_minus.jade b/website/views/shared/tasks/edit/habits/plus_minus.jade index 0a67ff9001..034fb432ce 100644 --- a/website/views/shared/tasks/edit/habits/plus_minus.jade +++ b/website/views/shared/tasks/edit/habits/plus_minus.jade @@ -1,4 +1,4 @@ -fieldset.option-group.plusminus(ng-if='task.type=="habit" && !task.challenge.id') +fieldset.option-group.plusminus(ng-if='task.type=="habit" && canEdit(task)') legend.option-title=env.t('direction/Actions') span.task-checker input.visuallyhidden.focusable(id='{{obj._id}}_{{task._id}}-option-plus', type='checkbox', ng-model='task.up') diff --git a/website/views/shared/tasks/edit/text_notes.jade b/website/views/shared/tasks/edit/text_notes.jade index 95dc1ef9af..ef947557c3 100644 --- a/website/views/shared/tasks/edit/text_notes.jade +++ b/website/views/shared/tasks/edit/text_notes.jade @@ -1,6 +1,6 @@ fieldset.option-group label.option-title=env.t('text') - input.form-control(type='text', ng-model='task.text', required, ng-disabled='task.challenge.id') + input.form-control(type='text', ng-model='task.text', ng-disabled='!canEdit(task)', required) fieldset.option-group label.option-title=env.t('extraNotes') diff --git a/website/views/shared/tasks/edit/todos/due_date.jade b/website/views/shared/tasks/edit/todos/due_date.jade index c9cbfe7d0e..221c168a54 100644 --- a/website/views/shared/tasks/edit/todos/due_date.jade +++ b/website/views/shared/tasks/edit/todos/due_date.jade @@ -1,4 +1,4 @@ -fieldset.option-group(ng-if='task.type=="todo" && !task.challenge.id') +fieldset.option-group(ng-if='task.type=="todo" && canEdit(task)') legend.option-title=env.t('dueDate') input.option-content.datepicker(type='text', ng-model='task.date', datepicker-popup='{{::user.preferences.dateFormat}}', is-open='datepickerOpened', diff --git a/website/views/shared/tasks/task_view/index.jade b/website/views/shared/tasks/task_view/index.jade index db8493a291..0c782358bf 100644 --- a/website/views/shared/tasks/task_view/index.jade +++ b/website/views/shared/tasks/task_view/index.jade @@ -37,7 +37,7 @@ label(for='box-{{::obj._id}}_{{::task._id}}') // main content -.task-text(ng-dblclick='task._editing ? saveTask(task) : editTask(task, user)') +.task-text(ng-dblclick='doubleClickTask(obj, task)') markdown(text='task.text',target='_blank') div(ng-if='task.checklist && !$state.includes("options.social.challenges") && !task.collapseChecklist && !task._editing')