mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-16 22:27:26 +01:00
feat(tasks): make task copy/mirror pref per-group
This commit is contained in:
@@ -39,7 +39,7 @@
|
|||||||
id="taskMirrorToggle"
|
id="taskMirrorToggle"
|
||||||
class="mr-3 mb-1 ml-auto"
|
class="mr-3 mb-1 ml-auto"
|
||||||
:label="'Copy tasks'"
|
:label="'Copy tasks'"
|
||||||
:checked="user.preferences.tasks.mirrorGroupTasks"
|
:checked="user.preferences.tasks.mirrorGroupTasks.indexOf(group._id) !== -1"
|
||||||
:hover-text="'Add assigned and open tasks to your personal task board'"
|
:hover-text="'Add assigned and open tasks to your personal task board'"
|
||||||
@change="changeMirrorPreference"
|
@change="changeMirrorPreference"
|
||||||
/>
|
/>
|
||||||
@@ -431,12 +431,16 @@ export default {
|
|||||||
eventCategory: 'behavior',
|
eventCategory: 'behavior',
|
||||||
hitType: 'event',
|
hitType: 'event',
|
||||||
mirror: newVal,
|
mirror: newVal,
|
||||||
|
group: this.group._id,
|
||||||
}, { trackOnClient: true });
|
}, { trackOnClient: true });
|
||||||
Analytics.updateUser({
|
const groupsToMirror = this.user.preferences.tasks.mirrorGroupTasks || [];
|
||||||
mirrorTasks: newVal,
|
if (newVal) { // we're turning copy ON for this group
|
||||||
});
|
groupsToMirror.push(this.group._id);
|
||||||
|
} else { // we're turning copy OFF for this group
|
||||||
|
groupsToMirror.splice(groupsToMirror.indexOf(this.group._id), 1);
|
||||||
|
}
|
||||||
this.$store.dispatch('user:set', {
|
this.$store.dispatch('user:set', {
|
||||||
'preferences.tasks.mirrorGroupTasks': newVal,
|
'preferences.tasks.mirrorGroupTasks': groupsToMirror,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -385,7 +385,7 @@ api.getUserTasks = {
|
|||||||
url: '/tasks/user',
|
url: '/tasks/user',
|
||||||
middlewares: [authWithHeaders({
|
middlewares: [authWithHeaders({
|
||||||
// Some fields (including _id, preferences) are always loaded (see middlewares/auth)
|
// Some fields (including _id, preferences) are always loaded (see middlewares/auth)
|
||||||
userFieldsToInclude: ['guilds', 'party', 'tasksOrder'],
|
userFieldsToInclude: ['tasksOrder'],
|
||||||
})],
|
})],
|
||||||
async handler (req, res) {
|
async handler (req, res) {
|
||||||
const types = Tasks.tasksTypes.map(type => `${type}s`);
|
const types = Tasks.tasksTypes.map(type => `${type}s`);
|
||||||
|
|||||||
@@ -165,10 +165,11 @@ async function getTasks (req, res, options = {}) {
|
|||||||
} else if (group) {
|
} else if (group) {
|
||||||
query = { 'group.id': group._id };
|
query = { 'group.id': group._id };
|
||||||
} else {
|
} else {
|
||||||
if (user.preferences.tasks.mirrorGroupTasks) {
|
const groupsToMirror = user.preferences.tasks.mirrorGroupTasks;
|
||||||
|
if (groupsToMirror && groupsToMirror.length > 0) {
|
||||||
upgradedGroups = await Group.find(
|
upgradedGroups = await Group.find(
|
||||||
{
|
{
|
||||||
_id: { $in: user.guilds.concat(user.party._id) },
|
_id: { $in: groupsToMirror },
|
||||||
'purchased.plan.customerId': { $exists: true },
|
'purchased.plan.customerId': { $exists: true },
|
||||||
$or: [
|
$or: [
|
||||||
{ 'purchased.plan.dateTerminated': { $exists: false } },
|
{ 'purchased.plan.dateTerminated': { $exists: false } },
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import common from '../../../common';
|
import common from '../../../common';
|
||||||
import * as Tasks from '../../models/task';
|
import * as Tasks from '../../models/task';
|
||||||
|
import { model as Groups } from '../../models/group';
|
||||||
import {
|
import {
|
||||||
BadRequest,
|
BadRequest,
|
||||||
NotAuthorized,
|
NotAuthorized,
|
||||||
@@ -132,6 +133,34 @@ export async function update (req, res, { isV3 = false }) {
|
|||||||
await checkNewInputForProfanity(user, res, newBlurb);
|
await checkNewInputForProfanity(user, res, newBlurb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (req.body['preferences.tasks.mirrorGroupTasks'] !== undefined) {
|
||||||
|
const groupsToMirror = req.body['preferences.tasks.mirrorGroupTasks'];
|
||||||
|
if (!Array.isArray(groupsToMirror)) {
|
||||||
|
throw new BadRequest('Groups to copy tasks from must be an array.');
|
||||||
|
}
|
||||||
|
const memberGroups = user.guilds;
|
||||||
|
if (user.party._id) memberGroups.push(user.party._id);
|
||||||
|
for (const targetGroup of groupsToMirror) {
|
||||||
|
if (memberGroups.indexOf(targetGroup) === -1) {
|
||||||
|
throw new BadRequest(`User not a member of group ${targetGroup}.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const matchingGroupsCount = await Groups.countDocuments({
|
||||||
|
_id: { $in: groupsToMirror },
|
||||||
|
'purchased.plan.customerId': { $exists: true },
|
||||||
|
$or: [
|
||||||
|
{ 'purchased.plan.dateTerminated': { $exists: false } },
|
||||||
|
{ 'purchased.plan.dateTerminated': null },
|
||||||
|
{ 'purchased.plan.dateTerminated': { $gt: new Date() } },
|
||||||
|
],
|
||||||
|
}).exec();
|
||||||
|
|
||||||
|
if (matchingGroupsCount !== groupsToMirror.length) {
|
||||||
|
throw new BadRequest('Groups to copy tasks from must have subscriptions.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_.each(req.body, (val, key) => {
|
_.each(req.body, (val, key) => {
|
||||||
const purchasable = requiresPurchase[key];
|
const purchasable = requiresPurchase[key];
|
||||||
|
|
||||||
@@ -140,7 +169,7 @@ export async function update (req, res, { isV3 = false }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (key === 'tags') {
|
if (key === 'tags') {
|
||||||
if (!Array.isArray(val)) throw new BadRequest('mustBeArray');
|
if (!Array.isArray(val)) throw new BadRequest('Tag list must be an array.');
|
||||||
|
|
||||||
const removedTagsIds = [];
|
const removedTagsIds = [];
|
||||||
|
|
||||||
|
|||||||
@@ -1386,10 +1386,13 @@ schema.methods.leave = async function leaveGroup (user, keep = 'keep-all', keepC
|
|||||||
const promises = user.isModified() ? [user.save()] : [];
|
const promises = user.isModified() ? [user.save()] : [];
|
||||||
|
|
||||||
// remove the group from the user's groups
|
// remove the group from the user's groups
|
||||||
|
const userUpdate = { $pull: { 'preferences.tasks.mirrorGroupTasks': group._id } };
|
||||||
if (group.type === 'guild') {
|
if (group.type === 'guild') {
|
||||||
promises.push(User.update({ _id: user._id }, { $pull: { guilds: group._id } }).exec());
|
userUpdate.$pull.guilds = group._id;
|
||||||
|
promises.push(User.update({ _id: user._id }, userUpdate).exec());
|
||||||
} else {
|
} else {
|
||||||
promises.push(User.update({ _id: user._id }, { $set: { party: {} } }).exec());
|
userUpdate.$set = { party: {} };
|
||||||
|
promises.push(User.update({ _id: user._id }, userUpdate).exec());
|
||||||
|
|
||||||
update.$unset = { [`quest.members.${user._id}`]: 1 };
|
update.$unset = { [`quest.members.${user._id}`]: 1 };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -582,7 +582,9 @@ export default new Schema({
|
|||||||
tasks: {
|
tasks: {
|
||||||
groupByChallenge: { $type: Boolean, default: false }, // @TODO remove? not used
|
groupByChallenge: { $type: Boolean, default: false }, // @TODO remove? not used
|
||||||
confirmScoreNotes: { $type: Boolean, default: false }, // @TODO remove? not used
|
confirmScoreNotes: { $type: Boolean, default: false }, // @TODO remove? not used
|
||||||
mirrorGroupTasks: { $type: Boolean, default: false },
|
mirrorGroupTasks: [
|
||||||
|
{ $type: String, validate: [v => validator.isUUID(v), 'Invalid group UUID.'], ref: 'Group' },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
improvementCategories: {
|
improvementCategories: {
|
||||||
$type: Array,
|
$type: Array,
|
||||||
|
|||||||
Reference in New Issue
Block a user