mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-18 07:07:35 +01:00
Refactoring & Feature: edit/delete controls of tasks (#11357)
* Refactoring & Feature: edit/delete group and challenge tasks - Remove showOption from tasks props - Pass all needed data to task for understand in mapGetter function which controls we should show - Improve current solution with edit and delete logic * Fix: this in template * Fix & Test: extend tests, fix can Edit/Delete functions
This commit is contained in:
committed by
Matteo Pagliazzi
parent
c6281b6f17
commit
70b6501135
@@ -6,7 +6,7 @@ describe('canDelete getter', () => {
|
|||||||
|
|
||||||
|
|
||||||
const task = {userId: 1, challenge: {id: 2}};
|
const task = {userId: 1, challenge: {id: 2}};
|
||||||
expect(store.getters['tasks:canDelete'](task)).to.equal(false);
|
expect(store.getters['tasks:canDelete'](task, 'challenge', true, null, null)).to.equal(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can delete broken challenge task', () => {
|
it('can delete broken challenge task', () => {
|
||||||
@@ -14,6 +14,6 @@ describe('canDelete getter', () => {
|
|||||||
|
|
||||||
|
|
||||||
const task = {userId: 1, challenge: {id: 2, broken: true}};
|
const task = {userId: 1, challenge: {id: 2, broken: true}};
|
||||||
expect(store.getters['tasks:canDelete'](task)).to.equal(true);
|
expect(store.getters['tasks:canDelete'](task, 'challenge', true, null, null)).to.equal(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
19
test/client/unit/specs/store/getters/tasks/canEdit.js
Normal file
19
test/client/unit/specs/store/getters/tasks/canEdit.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import generateStore from 'client/store';
|
||||||
|
|
||||||
|
describe('canEdit getter', () => {
|
||||||
|
it('cannot Edit active challenge task', () => {
|
||||||
|
const store = generateStore();
|
||||||
|
|
||||||
|
|
||||||
|
const task = {userId: 1, challenge: {id: 2}};
|
||||||
|
expect(store.getters['tasks:canEdit'](task, 'challenge', true, null, null)).to.equal(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can Edit broken challenge task', () => {
|
||||||
|
const store = generateStore();
|
||||||
|
|
||||||
|
|
||||||
|
const task = {userId: 1, challenge: {id: 2, broken: true}};
|
||||||
|
expect(store.getters['tasks:canEdit'](task, 'challenge', true, null, null)).to.equal(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
:type="column",
|
:type="column",
|
||||||
:key="column",
|
:key="column",
|
||||||
:taskListOverride='tasksByType[column]',
|
:taskListOverride='tasksByType[column]',
|
||||||
:showOptions="showOptions",
|
:challenge="challenge"
|
||||||
@editTask="editTask",
|
@editTask="editTask",
|
||||||
@taskDestroyed="taskDestroyed",
|
@taskDestroyed="taskDestroyed",
|
||||||
v-if='tasksByType[column].length > 0')
|
v-if='tasksByType[column].length > 0')
|
||||||
@@ -252,9 +252,6 @@ export default {
|
|||||||
canJoin () {
|
canJoin () {
|
||||||
return !this.isMember;
|
return !this.isMember;
|
||||||
},
|
},
|
||||||
showOptions () {
|
|
||||||
return this.isLeader;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
if (!this.searchId) this.searchId = this.challengeId;
|
if (!this.searchId) this.searchId = this.challengeId;
|
||||||
|
|||||||
@@ -41,7 +41,6 @@
|
|||||||
:type="column",
|
:type="column",
|
||||||
:key="column",
|
:key="column",
|
||||||
:taskListOverride='tasksByType[column]',
|
:taskListOverride='tasksByType[column]',
|
||||||
:showOptions="showOptions"
|
|
||||||
v-on:editTask="editTask",
|
v-on:editTask="editTask",
|
||||||
v-on:loadGroupCompletedTodos="loadGroupCompletedTodos",
|
v-on:loadGroupCompletedTodos="loadGroupCompletedTodos",
|
||||||
v-on:taskDestroyed="taskDestroyed",
|
v-on:taskDestroyed="taskDestroyed",
|
||||||
@@ -177,9 +176,6 @@ export default {
|
|||||||
if (!this.group) return false;
|
if (!this.group) return false;
|
||||||
return this.group.leader && this.group.leader._id === this.user._id || this.group.managers && Boolean(this.group.managers[this.user._id]);
|
return this.group.leader && this.group.leader._id === this.user._id || this.group.managers && Boolean(this.group.managers[this.user._id]);
|
||||||
},
|
},
|
||||||
showOptions () {
|
|
||||||
return this.canCreateTasks;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async load () {
|
async load () {
|
||||||
|
|||||||
@@ -45,10 +45,10 @@
|
|||||||
v-for="task in taskList",
|
v-for="task in taskList",
|
||||||
:key="task.id", :task="task",
|
:key="task.id", :task="task",
|
||||||
:isUser="isUser",
|
:isUser="isUser",
|
||||||
:showOptions="showOptions"
|
|
||||||
@editTask="editTask",
|
@editTask="editTask",
|
||||||
@moveTo="moveTo",
|
@moveTo="moveTo",
|
||||||
:group='group',
|
:group='group',
|
||||||
|
:challenge="challenge"
|
||||||
v-on:taskDestroyed='taskDestroyed'
|
v-on:taskDestroyed='taskDestroyed'
|
||||||
)
|
)
|
||||||
template(v-if="hasRewardsList")
|
template(v-if="hasRewardsList")
|
||||||
@@ -312,10 +312,7 @@ export default {
|
|||||||
selectedTags: {},
|
selectedTags: {},
|
||||||
taskListOverride: {},
|
taskListOverride: {},
|
||||||
group: {},
|
group: {},
|
||||||
showOptions: {
|
challenge: {},
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
}, // @TODO: maybe we should store the group on state?
|
}, // @TODO: maybe we should store the group on state?
|
||||||
data () {
|
data () {
|
||||||
const icons = Object.freeze({
|
const icons = Object.freeze({
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
div(slot="dropdown-toggle", draggable=false)
|
div(slot="dropdown-toggle", draggable=false)
|
||||||
.svg-icon.dropdown-icon(v-html="icons.menu")
|
.svg-icon.dropdown-icon(v-html="icons.menu")
|
||||||
div(slot="dropdown-content", draggable=false)
|
div(slot="dropdown-content", draggable=false)
|
||||||
.dropdown-item.edit-task-item(ref="editTaskItem")
|
.dropdown-item.edit-task-item(ref="editTaskItem" v-if="showEdit")
|
||||||
span.dropdown-icon-item
|
span.dropdown-icon-item
|
||||||
span.svg-icon.inline.edit-icon(v-html="icons.edit")
|
span.svg-icon.inline.edit-icon(v-html="icons.edit")
|
||||||
span.text {{ $t('edit') }}
|
span.text {{ $t('edit') }}
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
span.dropdown-icon-item
|
span.dropdown-icon-item
|
||||||
span.svg-icon.inline.push-to-bottom(v-html="icons.bottom")
|
span.svg-icon.inline.push-to-bottom(v-html="icons.bottom")
|
||||||
span.text {{ $t('taskToBottom') }}
|
span.text {{ $t('taskToBottom') }}
|
||||||
.dropdown-item(@click="destroy", v-if="canDelete(task)")
|
.dropdown-item(@click="destroy", v-if="showDelete")
|
||||||
span.dropdown-icon-item.delete-task-item
|
span.dropdown-icon-item.delete-task-item
|
||||||
span.svg-icon.inline.delete(v-html="icons.delete")
|
span.svg-icon.inline.delete(v-html="icons.delete")
|
||||||
span.text {{ $t('delete') }}
|
span.text {{ $t('delete') }}
|
||||||
@@ -521,6 +521,7 @@ import axios from 'axios';
|
|||||||
import scoreTask from 'common/script/ops/scoreTask';
|
import scoreTask from 'common/script/ops/scoreTask';
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import * as Analytics from 'client/libs/analytics';
|
import * as Analytics from 'client/libs/analytics';
|
||||||
|
import isEmpty from 'lodash/isEmpty';
|
||||||
|
|
||||||
import positiveIcon from 'assets/svg/positive.svg';
|
import positiveIcon from 'assets/svg/positive.svg';
|
||||||
import negativeIcon from 'assets/svg/negative.svg';
|
import negativeIcon from 'assets/svg/negative.svg';
|
||||||
@@ -555,7 +556,7 @@ export default {
|
|||||||
directives: {
|
directives: {
|
||||||
markdown: markdownDirective,
|
markdown: markdownDirective,
|
||||||
},
|
},
|
||||||
props: ['task', 'isUser', 'group', 'dueDate', 'showOptions'], // @TODO: maybe we should store the group on state?
|
props: ['task', 'isUser', 'group', 'challenge', 'dueDate'], // @TODO: maybe we should store the group on state?
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
random: uuid.v4(), // used to avoid conflicts between checkboxes ids
|
random: uuid.v4(), // used to avoid conflicts between checkboxes ids
|
||||||
@@ -589,6 +590,7 @@ export default {
|
|||||||
getTagsFor: 'tasks:getTagsFor',
|
getTagsFor: 'tasks:getTagsFor',
|
||||||
getTaskClasses: 'tasks:getTaskClasses',
|
getTaskClasses: 'tasks:getTaskClasses',
|
||||||
canDelete: 'tasks:canDelete',
|
canDelete: 'tasks:canDelete',
|
||||||
|
canEdit: 'tasks:canEdit',
|
||||||
}),
|
}),
|
||||||
hasChecklist () {
|
hasChecklist () {
|
||||||
return this.task.checklist && this.task.checklist.length > 0;
|
return this.task.checklist && this.task.checklist.length > 0;
|
||||||
@@ -665,6 +667,27 @@ export default {
|
|||||||
|
|
||||||
return this.task.challenge.shortName ? this.task.challenge.shortName.toString() : '';
|
return this.task.challenge.shortName ? this.task.challenge.shortName.toString() : '';
|
||||||
},
|
},
|
||||||
|
isChallangeTask () {
|
||||||
|
return !isEmpty(this.task.challenge);
|
||||||
|
},
|
||||||
|
isGroupTask () {
|
||||||
|
return !isEmpty(this.task.group) && (this.task.group.taskId || this.task.group.id);
|
||||||
|
},
|
||||||
|
taskCategory () {
|
||||||
|
let taskCategory = 'default';
|
||||||
|
if (this.isGroupTask) taskCategory = 'group';
|
||||||
|
else if (this.isChallangeTask) taskCategory = 'challenge';
|
||||||
|
return taskCategory;
|
||||||
|
},
|
||||||
|
showDelete () {
|
||||||
|
return this.canDelete(this.task, this.taskCategory, this.isUser, this.group, this.challenge);
|
||||||
|
},
|
||||||
|
showEdit () {
|
||||||
|
return this.canEdit(this.task, this.taskCategory, this.isUser, this.group, this.challenge);
|
||||||
|
},
|
||||||
|
showOptions () {
|
||||||
|
return this.showEdit || this.showDelete || this.isUser;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions({
|
...mapActions({
|
||||||
@@ -678,7 +701,7 @@ export default {
|
|||||||
this.scoreChecklistItem({taskId: this.task._id, itemId: item.id});
|
this.scoreChecklistItem({taskId: this.task._id, itemId: item.id});
|
||||||
},
|
},
|
||||||
edit (e, task) {
|
edit (e, task) {
|
||||||
if (this.isRunningYesterdailies) return;
|
if (this.isRunningYesterdailies || !this.showEdit) return;
|
||||||
|
|
||||||
// Prevent clicking on a link from opening the edit modal
|
// Prevent clicking on a link from opening the edit modal
|
||||||
const target = e.target || e.srcElement;
|
const target = e.target || e.srcElement;
|
||||||
|
|||||||
@@ -37,11 +37,64 @@ function getTaskColor (task) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function canDelete () {
|
export function canDelete (store) {
|
||||||
return (task) => {
|
return (task, taskCategory, isTaskFromUserDashboard, group, challenge) => {
|
||||||
let isUserChallenge = Boolean(task.userId);
|
let isUserCanDeleteTask = isTaskFromUserDashboard;
|
||||||
let activeChallenge = isUserChallenge && task.challenge && task.challenge.id && !task.challenge.broken;
|
const user = store.state.user.data;
|
||||||
return !activeChallenge;
|
|
||||||
|
switch (taskCategory) {
|
||||||
|
case 'challenge':
|
||||||
|
if (challenge && !isTaskFromUserDashboard) {
|
||||||
|
let isUserChallenge = user.id === challenge.leader.id;
|
||||||
|
isUserCanDeleteTask = isUserChallenge || task.challenge.broken;
|
||||||
|
} else {
|
||||||
|
isUserCanDeleteTask = task.challenge.broken;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'group':
|
||||||
|
if (group && !isTaskFromUserDashboard) {
|
||||||
|
isUserCanDeleteTask =
|
||||||
|
group.leader &&
|
||||||
|
group.leader._id === user._id || group.managers &&
|
||||||
|
Boolean(group.managers[user._id]);
|
||||||
|
} else {
|
||||||
|
isUserCanDeleteTask = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return Boolean(isUserCanDeleteTask);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canEdit (store) {
|
||||||
|
return (task, taskCategory, isTaskFromUserDashboard, group, challenge) => {
|
||||||
|
let isUserCanEditTask = isTaskFromUserDashboard;
|
||||||
|
const user = store.state.user.data;
|
||||||
|
switch (taskCategory) {
|
||||||
|
case 'challenge':
|
||||||
|
if (challenge && !isTaskFromUserDashboard) {
|
||||||
|
let isUserChallenge = user.id === challenge.leader.id;
|
||||||
|
isUserCanEditTask = isUserChallenge || task.challenge.broken;
|
||||||
|
} else {
|
||||||
|
isUserCanEditTask = task.challenge.broken;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'group':
|
||||||
|
if (group && !isTaskFromUserDashboard) {
|
||||||
|
isUserCanEditTask =
|
||||||
|
group.leader &&
|
||||||
|
group.leader._id === user._id || group.managers &&
|
||||||
|
Boolean(group.managers[user._id]);
|
||||||
|
} else {
|
||||||
|
isUserCanEditTask = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return Boolean(isUserCanEditTask);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user