diff --git a/test/api/v3/integration/tasks/groups/POST-tasks_group_id_assign_user_id.test.js b/test/api/v3/integration/tasks/groups/POST-tasks_group_id_assign_user_id.test.js index fee02aa02d..85eee161fd 100644 --- a/test/api/v3/integration/tasks/groups/POST-tasks_group_id_assign_user_id.test.js +++ b/test/api/v3/integration/tasks/groups/POST-tasks_group_id_assign_user_id.test.js @@ -74,7 +74,7 @@ describe('POST /tasks/:taskId', () => { }); it('returns error when non leader tries to create a task', async () => { - await expect(member.post(`/tasks/${task._id}/assign/${member._id}`)) + await expect(member2.post(`/tasks/${task._id}/assign/${member._id}`)) .to.eventually.be.rejected.and.eql({ code: 401, error: 'NotAuthorized', @@ -82,6 +82,17 @@ describe('POST /tasks/:taskId', () => { }); }); + it('allows user to assign themselves', async () => { + await member.post(`/tasks/${task._id}/assign/${member._id}`); + + let groupTask = await user.get(`/tasks/group/${guild._id}`); + let memberTasks = await member.get('/tasks/user'); + let syncedTask = find(memberTasks, findAssignedTask); + + expect(groupTask[0].group.assignedUsers).to.contain(member._id); + expect(syncedTask).to.exist; + }); + it('assigns a task to a user', async () => { await user.post(`/tasks/${task._id}/assign/${member._id}`); diff --git a/website/client-old/js/components/groupTaskMetaActions/groupTaskMetaActionsController.js b/website/client-old/js/components/groupTaskMetaActions/groupTaskMetaActionsController.js new file mode 100644 index 0000000000..03574b2443 --- /dev/null +++ b/website/client-old/js/components/groupTaskMetaActions/groupTaskMetaActionsController.js @@ -0,0 +1,15 @@ +habitrpg.controller('GroupTaskMetaActionsCtrl', ['$scope', 'Shared', 'Tasks', 'User', + function ($scope, Shared, Tasks, User) { + $scope.assignedMembers = []; + $scope.user = User.user; + + $scope.claim = function () { + if (!confirm("Are you sure you want to claim this task?")) return; + Tasks.assignTask($scope.task.id, $scope.user._id); + $scope.task.group.assignedUsers.push($scope.user._id); + }; + + $scope.userIsAssigned = function () { + return $scope.task.group.assignedUsers.indexOf($scope.user._id) !== -1; + }; + }]); diff --git a/website/client-old/js/components/groupTaskMetaActions/groupTaskMetaActionsDirective.js b/website/client-old/js/components/groupTaskMetaActions/groupTaskMetaActionsDirective.js new file mode 100644 index 0000000000..d1fbdc1f36 --- /dev/null +++ b/website/client-old/js/components/groupTaskMetaActions/groupTaskMetaActionsDirective.js @@ -0,0 +1,22 @@ +'use strict'; + +(function(){ + angular + .module('habitrpg') + .directive('groupTaskMetaActions', hrpgSortTags); + + hrpgSortTags.$inject = [ + ]; + + function hrpgSortTags() { + + return { + scope: { + task: '=', + group: '=', + }, + templateUrl: 'partials/groups.tasks.meta.actions.html', + controller: 'GroupTaskMetaActionsCtrl', + }; + } +}()); diff --git a/website/client-old/manifest.json b/website/client-old/manifest.json index e536820fa6..14c6bea92e 100644 --- a/website/client-old/manifest.json +++ b/website/client-old/manifest.json @@ -30,9 +30,12 @@ "bower_components/jquery-ui/ui/minified/jquery.ui.core.min.js", "bower_components/jquery-ui/ui/minified/jquery.ui.widget.min.js", + "bower_components/jquery-ui/ui/minified/jquery.ui.menu.min.js", "bower_components/jquery-ui/ui/minified/jquery.ui.mouse.min.js", "bower_components/jquery-ui/ui/minified/jquery.ui.sortable.min.js", + "bower_components/jquery-ui/ui/minified/jquery.ui.autocomplete.min.js", "bower_components/smart-app-banner/smart-app-banner.js", + "bower_components/taggle/src/taggle.js", "js/habitrpg-shared.js", diff --git a/website/common/locales/en/groups.json b/website/common/locales/en/groups.json index 5ee8ed8880..d750b4aec8 100644 --- a/website/common/locales/en/groups.json +++ b/website/common/locales/en/groups.json @@ -213,5 +213,7 @@ "desktopNotificationsText": "We need your permission to enable desktop notifications for new messages in party chat! Follow your browser's instructions to turn them on.

You'll receive these notifications only while you have Habitica open. If you decide you don't like them, they can be disabled in your browser's settings.

This box will close automatically when a decision is made.", "confirmAddTag": "Do you really want to add \"<%= tag %>\"?", "confirmRemoveTag": "Do you really want to remove \"<%= tag %>\"?", - "assignTask": "Assign Task" + "assignTask": "Assign Task", + "desktopNotificationsText": "We need your permission to enable desktop notifications for new messages in party chat! Follow your browser's instructions to turn them on.

You'll receive these notifications only while you have Habitica open. If you decide you don't like them, they can be disabled in your browser's settings.

This box will close automatically when a decision is made.", + "claim": "Claim" } diff --git a/website/server/controllers/api-v3/tasks/groups.js b/website/server/controllers/api-v3/tasks/groups.js index dc721cd545..92ac10a843 100644 --- a/website/server/controllers/api-v3/tasks/groups.js +++ b/website/server/controllers/api-v3/tasks/groups.js @@ -122,7 +122,7 @@ api.assignTask = { let group = await Group.getGroup({user, groupId: task.group.id, fields: requiredGroupFields}); if (!group) throw new NotFound(res.t('groupNotFound')); - if (group.leader !== user._id) throw new NotAuthorized(res.t('onlyGroupLeaderCanEditTasks')); + if (group.leader !== user._id && user._id !== assignedUserId) throw new NotAuthorized(res.t('onlyGroupLeaderCanEditTasks')); await group.syncTask(task, assignedUser); diff --git a/website/views/options/social/group.jade b/website/views/options/social/group.jade index 05c7a9f3d3..9555eaf38c 100644 --- a/website/views/options/social/group.jade +++ b/website/views/options/social/group.jade @@ -147,7 +147,7 @@ a.pull-right.gem-wallet(ng-if='group.type!="party"', popover-trigger='mouseenter h3.popover-title {{group.leader.profile.name}} .popover-content markdown(text='group._editing ? groupCopy.leaderMessage : group.leaderMessage') - + ul.options-menu(ng-init="groupPane = 'chat'", ng-show="group.purchased.active") li a(ng-click="groupPane = 'chat'") diff --git a/website/views/options/social/groups/group-tasks-meta-actions.jade b/website/views/options/social/groups/group-tasks-meta-actions.jade new file mode 100644 index 0000000000..bf85a9e456 --- /dev/null +++ b/website/views/options/social/groups/group-tasks-meta-actions.jade @@ -0,0 +1,4 @@ +script(type='text/ng-template', id='partials/groups.tasks.meta.actions.html') + a.badge(ng-click="claim()", ng-if="!userIsAssigned()")=env.t('claim') + + diff --git a/website/views/options/social/groups/group-tasks.jade b/website/views/options/social/groups/group-tasks.jade index e992454e15..d04a112537 100644 --- a/website/views/options/social/groups/group-tasks.jade +++ b/website/views/options/social/groups/group-tasks.jade @@ -1,4 +1,5 @@ include ./group-tasks-actions +include ./group-tasks-meta-actions include ./group-members-autocomplete script(type='text/ng-template', id='partials/groups.tasks.html') diff --git a/website/views/shared/tasks/edit/advanced_options.jade b/website/views/shared/tasks/edit/advanced_options.jade index bf4d6d867a..24c82aaff9 100644 --- a/website/views/shared/tasks/edit/advanced_options.jade +++ b/website/views/shared/tasks/edit/advanced_options.jade @@ -11,7 +11,7 @@ div(ng-if='::task.type!="reward"') fieldset.option-group.advanced-option(ng-show="task._edit._advanced") group-tasks-actions(ng-if="obj.type == 'guild'", task='task', group='obj') - + div(ng-show='task._edit._advanced') div(ng-if='::task.type == "daily"') .form-group diff --git a/website/views/shared/tasks/meta_controls.jade b/website/views/shared/tasks/meta_controls.jade index f3504cbe31..bd41ad4a0b 100644 --- a/website/views/shared/tasks/meta_controls.jade +++ b/website/views/shared/tasks/meta_controls.jade @@ -13,6 +13,8 @@ // Icons only available if you own the tasks (aka, hidden from challenge stats) span(ng-if='!obj._locked') + group-task-meta-actions(ng-if="obj.type == 'guild'", task='task', group='obj') + a(ng-click='pushTask(task,$index,"top")', tooltip=env.t('pushTaskToTop'), ng-class="{'push-down': ctrlPressed}") span(ng-hide="ctrlPressed").glyphicon.glyphicon-open span(ng-show="ctrlPressed").glyphicon.glyphicon-save