mirror of
https://github.com/wekan/wekan.git
synced 2026-02-20 15:04:07 +01:00
Add notification, allow watch boards / lists / cards
This commit is contained in:
parent
9ef8ebaf09
commit
9bbdacc79a
24 changed files with 579 additions and 16 deletions
|
|
@ -48,4 +48,77 @@ if (Meteor.isServer) {
|
|||
createdAt: -1,
|
||||
});
|
||||
});
|
||||
|
||||
Activities.after.insert((userId, doc) => {
|
||||
const activity = Activities.findOne(doc._id);
|
||||
let participants = [];
|
||||
let watchers = [];
|
||||
let title = 'Wekan Notification';
|
||||
let board = null;
|
||||
const description = `act-${activity.activityType}`;
|
||||
const params = {
|
||||
activityId: activity._id,
|
||||
};
|
||||
if (activity.userId) {
|
||||
// No need send notification to user of activity
|
||||
// participants = _.union(participants, [activity.userId]);
|
||||
params.user = activity.user().getName();
|
||||
}
|
||||
if (activity.boardId) {
|
||||
board = activity.board();
|
||||
params.board = board.title;
|
||||
title = 'act-withBoardTitle';
|
||||
params.url = board.absoluteUrl();
|
||||
}
|
||||
if (activity.memberId) {
|
||||
participants = _.union(participants, [activity.memberId]);
|
||||
params.member = activity.member().getName();
|
||||
}
|
||||
if (activity.listId) {
|
||||
const list = activity.list();
|
||||
watchers = _.union(watchers, list.watchers || []);
|
||||
params.list = list.title;
|
||||
}
|
||||
if (activity.oldListId) {
|
||||
const oldList = activity.oldList();
|
||||
watchers = _.union(watchers, oldList.watchers || []);
|
||||
params.oldList = oldList.title;
|
||||
}
|
||||
if (activity.cardId) {
|
||||
const card = activity.card();
|
||||
participants = _.union(participants, [card.userId], card.members || []);
|
||||
watchers = _.union(watchers, card.watchers || []);
|
||||
params.card = card.title;
|
||||
title = 'act-withCardTitle';
|
||||
params.url = card.absoluteUrl();
|
||||
}
|
||||
if (activity.commentId) {
|
||||
const comment = activity.comment();
|
||||
params.comment = comment.text;
|
||||
}
|
||||
if (activity.attachmentId) {
|
||||
const attachment = activity.attachment();
|
||||
params.attachment = attachment._id;
|
||||
}
|
||||
if (board) {
|
||||
const boardWatching = _.pluck(_.where(board.watchers, {level: 'watching'}), 'userId');
|
||||
const boardTracking = _.pluck(_.where(board.watchers, {level: 'tracking'}), 'userId');
|
||||
const boardMuted = _.pluck(_.where(board.watchers, {level: 'muted'}), 'userId');
|
||||
switch(board.getWatchDefault()) {
|
||||
case 'muted':
|
||||
participants = _.intersection(participants, boardTracking);
|
||||
watchers = _.intersection(watchers, boardTracking);
|
||||
break;
|
||||
case 'tracking':
|
||||
participants = _.difference(participants, boardMuted);
|
||||
watchers = _.difference(watchers, boardMuted);
|
||||
break;
|
||||
}
|
||||
watchers = _.union(watchers, boardWatching || []);
|
||||
}
|
||||
|
||||
Notifications.getUsers(participants, watchers).forEach((user) => {
|
||||
Notifications.notify(user, title, description, params);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ Boards.helpers({
|
|||
},
|
||||
|
||||
absoluteUrl() {
|
||||
return FlowRouter.path('board', { id: this._id, slug: this.slug });
|
||||
return FlowRouter.url('board', { id: this._id, slug: this.slug });
|
||||
},
|
||||
|
||||
colorClass() {
|
||||
|
|
|
|||
|
|
@ -116,16 +116,12 @@ Cards.helpers({
|
|||
|
||||
absoluteUrl() {
|
||||
const board = this.board();
|
||||
return FlowRouter.path('card', {
|
||||
return FlowRouter.url('card', {
|
||||
boardId: board._id,
|
||||
slug: board.slug,
|
||||
cardId: this._id,
|
||||
});
|
||||
},
|
||||
|
||||
rootUrl() {
|
||||
return Meteor.absoluteUrl(this.absoluteUrl().replace('/', ''));
|
||||
},
|
||||
});
|
||||
|
||||
Cards.mutations({
|
||||
|
|
|
|||
|
|
@ -47,6 +47,21 @@ Users.helpers({
|
|||
return _.contains(invitedBoards, boardId);
|
||||
},
|
||||
|
||||
hasTag(tag) {
|
||||
const {tags = []} = this.profile;
|
||||
return _.contains(tags, tag);
|
||||
},
|
||||
|
||||
hasNotification(activityId) {
|
||||
const {notifications = []} = this.profile;
|
||||
return _.contains(notifications, activityId);
|
||||
},
|
||||
|
||||
getEmailCache() {
|
||||
const {emailCache = []} = this.profile;
|
||||
return emailCache;
|
||||
},
|
||||
|
||||
getInitials() {
|
||||
const profile = this.profile || {};
|
||||
if (profile.initials)
|
||||
|
|
@ -99,6 +114,61 @@ Users.mutations({
|
|||
};
|
||||
},
|
||||
|
||||
addTag(tag) {
|
||||
return {
|
||||
$addToSet: {
|
||||
'profile.tags': tag,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
removeTag(tag) {
|
||||
return {
|
||||
$pull: {
|
||||
'profile.tags': tag,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
toggleTag(tag) {
|
||||
if (this.hasTag(tag))
|
||||
this.removeTag(tag);
|
||||
else
|
||||
this.addTag(tag);
|
||||
},
|
||||
|
||||
addNotification(activityId) {
|
||||
return {
|
||||
$addToSet: {
|
||||
'profile.notifications': activityId,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
removeNotification(activityId) {
|
||||
return {
|
||||
$pull: {
|
||||
'profile.notifications': activityId,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
addEmailCache(text) {
|
||||
return {
|
||||
$addToSet: {
|
||||
'profile.emailCache': text,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
clearEmailCache() {
|
||||
return {
|
||||
$set: {
|
||||
'profile.emailCache': [],
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
setAvatarUrl(avatarUrl) {
|
||||
return { $set: { 'profile.avatarUrl': avatarUrl }};
|
||||
},
|
||||
|
|
@ -167,21 +237,18 @@ if (Meteor.isServer) {
|
|||
user.addInvite(boardId);
|
||||
|
||||
try {
|
||||
const { _id, slug } = board;
|
||||
const boardUrl = FlowRouter.url('board', { id: _id, slug });
|
||||
|
||||
const vars = {
|
||||
const params = {
|
||||
user: user.username,
|
||||
inviter: inviter.username,
|
||||
board: board.title,
|
||||
url: boardUrl,
|
||||
url: board.absoluteUrl(),
|
||||
};
|
||||
const lang = user.getLanguage();
|
||||
Email.send({
|
||||
to: user.emails[0].address,
|
||||
from: Accounts.emailTemplates.from,
|
||||
subject: TAPi18n.__('email-invite-subject', vars, lang),
|
||||
text: TAPi18n.__('email-invite-text', vars, lang),
|
||||
subject: TAPi18n.__('email-invite-subject', params, lang),
|
||||
text: TAPi18n.__('email-invite-text', params, lang),
|
||||
});
|
||||
} catch (e) {
|
||||
throw new Meteor.Error('email-fail', e.message);
|
||||
|
|
|
|||
89
models/watchable.js
Normal file
89
models/watchable.js
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
// simple version, only toggle watch / unwatch
|
||||
const simpleWatchable = (collection) => {
|
||||
collection.attachSchema({
|
||||
watchers: {
|
||||
type: [String],
|
||||
optional: true,
|
||||
},
|
||||
});
|
||||
|
||||
collection.helpers({
|
||||
getWatchLevels() {
|
||||
return [true, false];
|
||||
},
|
||||
|
||||
watcherIndex(userId) {
|
||||
return this.watchers.indexOf(userId);
|
||||
},
|
||||
|
||||
findWatcher(userId) {
|
||||
return _.contains(this.watchers, userId);
|
||||
},
|
||||
});
|
||||
|
||||
collection.mutations({
|
||||
setWatcher(userId, level) {
|
||||
// if level undefined or null or false, then remove
|
||||
if (!level) return { $pull: { watchers: userId }};
|
||||
return { $addToSet: { watchers: userId }};
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// more complex version of same interface, with 3 watching levels
|
||||
const complexWatchOptions = ['watching', 'tracking', 'muted'];
|
||||
const complexWatchDefault = 'muted';
|
||||
|
||||
const complexWatchable = (collection) => {
|
||||
collection.attachSchema({
|
||||
'watchers.$.userId': {
|
||||
type: String,
|
||||
},
|
||||
'watchers.$.level': {
|
||||
type: String,
|
||||
allowedValues: complexWatchOptions,
|
||||
},
|
||||
});
|
||||
|
||||
collection.helpers({
|
||||
getWatchOptions() {
|
||||
return complexWatchOptions;
|
||||
},
|
||||
|
||||
getWatchDefault() {
|
||||
return complexWatchDefault;
|
||||
},
|
||||
|
||||
watcherIndex(userId) {
|
||||
return _.pluck(this.watchers, 'userId').indexOf(userId);
|
||||
},
|
||||
|
||||
findWatcher(userId) {
|
||||
return _.findWhere(this.watchers, { userId });
|
||||
},
|
||||
|
||||
getWatchLevel(userId) {
|
||||
const watcher = this.findWatcher(userId);
|
||||
return watcher ? watcher.level : complexWatchDefault;
|
||||
},
|
||||
});
|
||||
|
||||
collection.mutations({
|
||||
setWatcher(userId, level) {
|
||||
// if level undefined or null or false, then remove
|
||||
if (level === complexWatchDefault) level = null;
|
||||
if (!level) return { $pull: { watchers: { userId }}};
|
||||
const index = this.watcherIndex(userId);
|
||||
if (index<0) return { $push: { watchers: { userId, level }}};
|
||||
return {
|
||||
$set: {
|
||||
[`watchers.${index}.level`]: level,
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
complexWatchable(Boards);
|
||||
simpleWatchable(Lists);
|
||||
simpleWatchable(Cards);
|
||||
Loading…
Add table
Add a link
Reference in a new issue