summaryrefslogtreecommitdiffstats
path: root/models
diff options
context:
space:
mode:
authorLiming Xie <liming.xie@gmail.com>2016-01-05 23:26:02 +0800
committerLiming Xie <liming.xie@gmail.com>2016-01-05 23:26:02 +0800
commit9bbdacc79a89667e0d6f1ed30c415e5350ad468b (patch)
treefc6d9918dcd77699295ecb5bdbaf59f9d7c2f479 /models
parent9ef8ebaf09e52d7133ebe08ab1354ef663ee948b (diff)
downloadwekan-9bbdacc79a89667e0d6f1ed30c415e5350ad468b.tar.gz
wekan-9bbdacc79a89667e0d6f1ed30c415e5350ad468b.tar.bz2
wekan-9bbdacc79a89667e0d6f1ed30c415e5350ad468b.zip
Add notification, allow watch boards / lists / cards
Diffstat (limited to 'models')
-rw-r--r--models/activities.js73
-rw-r--r--models/boards.js2
-rw-r--r--models/cards.js6
-rw-r--r--models/users.js81
-rw-r--r--models/watchable.js89
5 files changed, 238 insertions, 13 deletions
diff --git a/models/activities.js b/models/activities.js
index 5de07ee5..0aa4fa54 100644
--- a/models/activities.js
+++ b/models/activities.js
@@ -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);
+ });
+ });
}
diff --git a/models/boards.js b/models/boards.js
index e20ca8ce..64d6df62 100644
--- a/models/boards.js
+++ b/models/boards.js
@@ -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() {
diff --git a/models/cards.js b/models/cards.js
index 1895fc69..09c86191 100644
--- a/models/cards.js
+++ b/models/cards.js
@@ -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({
diff --git a/models/users.js b/models/users.js
index 5d9c218a..3bb7324f 100644
--- a/models/users.js
+++ b/models/users.js
@@ -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);
diff --git a/models/watchable.js b/models/watchable.js
new file mode 100644
index 00000000..6821f847
--- /dev/null
+++ b/models/watchable.js
@@ -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);