From 9819c9f801128d07374b0703b482bdb83a672297 Mon Sep 17 00:00:00 2001 From: Jonathan Baird Date: Fri, 27 Mar 2020 11:35:03 -0600 Subject: add a notification drawer like trello --- client/components/notifications/notification.jade | 10 ++++ client/components/notifications/notification.js | 28 +++++++++++ client/components/notifications/notification.styl | 57 ++++++++++++++++++++++ .../components/notifications/notificationIcon.jade | 53 ++++++++++++++++++++ client/components/notifications/notifications.jade | 5 ++ client/components/notifications/notifications.js | 32 ++++++++++++ client/components/notifications/notifications.styl | 17 +++++++ .../notifications/notificationsDrawer.jade | 16 ++++++ .../notifications/notificationsDrawer.js | 38 +++++++++++++++ .../notifications/notificationsDrawer.styl | 57 ++++++++++++++++++++++ 10 files changed, 313 insertions(+) create mode 100644 client/components/notifications/notification.jade create mode 100644 client/components/notifications/notification.js create mode 100644 client/components/notifications/notification.styl create mode 100644 client/components/notifications/notificationIcon.jade create mode 100644 client/components/notifications/notifications.jade create mode 100644 client/components/notifications/notifications.js create mode 100644 client/components/notifications/notifications.styl create mode 100644 client/components/notifications/notificationsDrawer.jade create mode 100644 client/components/notifications/notificationsDrawer.js create mode 100644 client/components/notifications/notificationsDrawer.styl (limited to 'client/components/notifications') diff --git a/client/components/notifications/notification.jade b/client/components/notifications/notification.jade new file mode 100644 index 00000000..c98bbdba --- /dev/null +++ b/client/components/notifications/notification.jade @@ -0,0 +1,10 @@ +template(name='notification') + li.notification(class="{{#if read}}read{{/if}}") + .read-status + .materialCheckBox(class="{{#if read}}is-checked{{/if}}") + +notificationIcon(activityData) + .details + +activity(activity=activityData mode='none') + if read + .remove + a.fa.fa-trash diff --git a/client/components/notifications/notification.js b/client/components/notifications/notification.js new file mode 100644 index 00000000..89277520 --- /dev/null +++ b/client/components/notifications/notification.js @@ -0,0 +1,28 @@ +Template.notification.events({ + 'click .read-status .materialCheckBox'() { + const update = {}; + update[`profile.notifications.${this.index}.read`] = this.read + ? null + : Date.now(); + Users.update(Meteor.userId(), { $set: update }); + }, + 'click .remove a'() { + Meteor.user().removeNotification(this.activityData._id); + }, +}); + +Template.notification.helpers({ + mode: 'board', + isOfActivityType(activityId, type) { + const activity = Activities.findOne(activityId); + return activity && activity.activityType === type; + }, + activityType(activityId) { + const activity = Activities.findOne(activityId); + return activity ? activity.activityType : ''; + }, + activityUser(activityId) { + const activity = Activities.findOne(activityId); + return activity && activity.userId; + }, +}); diff --git a/client/components/notifications/notification.styl b/client/components/notifications/notification.styl new file mode 100644 index 00000000..0cf0cfd5 --- /dev/null +++ b/client/components/notifications/notification.styl @@ -0,0 +1,57 @@ +#notifications-drawer + &.show-read .notification.read + display: flex + + .notification + display: flex + float: none + padding: 12px 8px 8px + color: black + border-bottom: 1px solid #dbdbdb + + &.read + display: none + + .read-status + width: 30px + + input + width: 24px + height: 24px + + .activity-type + margin: 16px 0 0 + width: 17px + height: 17px + font-size: 17px + display: block + color: #bbb + + .details + width: calc(100% - 30px) + + .activity + display: flex + + .activity-desc + width: 100%; + + .activity-comment + display: block + width: 100% + border-radius: 3px + background: #fff + text-decoration: none + box-shadow: 0 1px 2px rgba(0,0,0,0.2) + margin-top: 5px + padding: 5px + + .activity-meta + display: block + font-size: 0.8em + color: #999 + font-style: italic + + .remove + a:hover + color #eb4646 !important diff --git a/client/components/notifications/notificationIcon.jade b/client/components/notifications/notificationIcon.jade new file mode 100644 index 00000000..04377606 --- /dev/null +++ b/client/components/notifications/notificationIcon.jade @@ -0,0 +1,53 @@ +template(name='notificationIcon') + if($in activityType 'deleteAttachment' 'addAttachment') + i.fa.fa-paperclip.activity-type(title="attachment") + else if($in activityType 'createBoard' 'importBoard') + i.fa.fa-chalkboard.activity-type(title="board") + + else if($in activityType 'createCard' 'importCard' 'moveCard') + +cardNotificationIcon + else if($in activityType 'moveCardBoard' 'archivedCard' 'restoredCard') + +cardNotificationIcon + //- $in can only handle up to 3 cases so we have to break this case over 2 cases... use a simple template to keep it + //- DRY and consistant + + else if($in activityType 'addChecklist' 'removedChecklist' 'completeChecklist') + +checklistNotificationIcon + else if($in activityType 'uncompleteChecklist') + +checklistNotificationIcon + //- $in can only handle up to 3 cases so we have to break this case over 2 cases... use a simple template to keep it + //- DRY and consistant + + else if($in activityType 'checkedItem' 'uncheckedItem' 'addChecklistItem' 'removedChecklistItem') + i.fa.fa-check-square.activity-type(title="checklist item") + else if($in activityType 'addComment') + i.fa.fa-comment-o.activity-type(title="comment") + else if($in activityType 'createCustomField' 'setCustomField' 'unsetCustomField') + i.fa.fa-code.activity-type(title="custom field") + else if($in activityType 'addedLabel' 'removedLabel') + i.fa.fa-tag.activity-type(title="label") + + else if($in activityType 'createList' 'removeList' 'archivedList') + +listNotificationIcon + else if($in activityType 'importList') + +listNotificationIcon + //- $in can only handle up to 3 cases so we have to break this case over 2 cases... use a simple template to keep it + //- DRY and consistant + + //- elswhere in the app we use fa-trello to indicate lists... + //- i personally like fa-columns a bit better + else if($in activityType 'unjoinMember' 'addBoardMember' 'joinMember' 'removeBoardMember') + i.fa.fa-user.activity-type(title="member") + else if($in activityType 'createSwimlane' 'archivedSwimlane') + i.fa.fa-th-large.activity-type(title="swimlane") + else + i.fa.fa-bug.activity-type(title="can't find icon for #{activityType}") + +template(name='cardNotificationIcon') + i.fa.fa-clone.activity-type(title="card") + +template(name='checklistNotificationIcon') + i.fa.fa-list.activity-type(title="checklist") + +template(name='listNotificationIcon') + i.fa.fa-columns.activity-type(title="list") diff --git a/client/components/notifications/notifications.jade b/client/components/notifications/notifications.jade new file mode 100644 index 00000000..bf8acbbf --- /dev/null +++ b/client/components/notifications/notifications.jade @@ -0,0 +1,5 @@ +template(name='notifications') + #notifications.board-header-btns.right + a.notifications-drawer-toggle.fa.fa-bell(class="{{#if $gt unreadNotifications 0}}alert{{/if}}") + if $.Session.get 'showNotificationsDrawer' + +notificationsDrawer(unreadNotifications=unreadNotifications) diff --git a/client/components/notifications/notifications.js b/client/components/notifications/notifications.js new file mode 100644 index 00000000..c0aa6cb5 --- /dev/null +++ b/client/components/notifications/notifications.js @@ -0,0 +1,32 @@ +// this hides the notifications drawer if anyone clicks off of the panel +Template.body.events({ + click(event) { + if ( + !$(event.target).is('#notifications *') && + Session.get('showNotificationsDrawer') + ) { + toggleNotificationsDrawer(); + } + }, +}); + +Template.notifications.helpers({ + unreadNotifications() { + const notifications = Users.findOne(Meteor.userId()).notifications(); + const unreadNotifications = _.filter(notifications, v => !v.read); + return unreadNotifications.length; + }, +}); + +Template.notifications.events({ + 'click .notifications-drawer-toggle'() { + toggleNotificationsDrawer(); + }, +}); + +export function toggleNotificationsDrawer() { + Session.set( + 'showNotificationsDrawer', + !Session.get('showNotificationsDrawer'), + ); +} diff --git a/client/components/notifications/notifications.styl b/client/components/notifications/notifications.styl new file mode 100644 index 00000000..710cd3f9 --- /dev/null +++ b/client/components/notifications/notifications.styl @@ -0,0 +1,17 @@ +#notifications + position: relative + + .notifications-drawer-toggle + display: block + line-height: 28px + color: #f2f2f2 + margin: 0 10px + width: 28px + height: 28px + text-align: center + border: 0 + padding: 0 + + &.alert + background-color: #eb4646; + diff --git a/client/components/notifications/notificationsDrawer.jade b/client/components/notifications/notificationsDrawer.jade new file mode 100644 index 00000000..67fc6e65 --- /dev/null +++ b/client/components/notifications/notificationsDrawer.jade @@ -0,0 +1,16 @@ +template(name='notificationsDrawer') + section#notifications-drawer(class="{{#if $.Session.get 'showReadNotifications'}}show-read{{/if}}") + .header + if $.Session.get 'showReadNotifications' + a.toggle-read Filter by Unread + else + a.toggle-read View All + h5 Notifications + if($gt unreadNotifications 0) + |(#{unreadNotifications}) + a.fa.fa-times-thin.close + ul.notifications + each transformedProfile.notifications + +notification(activityData=activity index=dbIndex read=read) + if($gt unreadNotifications 0) + a.all-read Mark all as read diff --git a/client/components/notifications/notificationsDrawer.js b/client/components/notifications/notificationsDrawer.js new file mode 100644 index 00000000..98d4750d --- /dev/null +++ b/client/components/notifications/notificationsDrawer.js @@ -0,0 +1,38 @@ +import { toggleNotificationsDrawer } from './notifications.js'; + +Template.notificationsDrawer.onCreated(function() { + Meteor.subscribe('notificationActivities'); + Meteor.subscribe('notificationCards'); + Meteor.subscribe('notificationUsers'); + Meteor.subscribe('notificationsAttachments'); + Meteor.subscribe('notificationChecklistItems'); + Meteor.subscribe('notificationChecklists'); + Meteor.subscribe('notificationComments'); + Meteor.subscribe('notificationLists'); + Meteor.subscribe('notificationSwimlanes'); +}); + +Template.notificationsDrawer.helpers({ + transformedProfile() { + return Users.findOne(Meteor.userId()); + }, +}); + +Template.notificationsDrawer.events({ + 'click .all-read'() { + const notifications = Meteor.user().profile.notifications; + for (const index in notifications) { + if (notifications.hasOwnProperty(index) && !notifications[index].read) { + const update = {}; + update[`profile.notifications.${index}.read`] = Date.now(); + Users.update(Meteor.userId(), { $set: update }); + } + } + }, + 'click .close'() { + toggleNotificationsDrawer(); + }, + 'click .toggle-read'() { + Session.set('showReadNotifications', !Session.get('showReadNotifications')); + }, +}); diff --git a/client/components/notifications/notificationsDrawer.styl b/client/components/notifications/notificationsDrawer.styl new file mode 100644 index 00000000..a26b5e4a --- /dev/null +++ b/client/components/notifications/notificationsDrawer.styl @@ -0,0 +1,57 @@ +belize = #2980b9 + +section#notifications-drawer + position: fixed + top: 28px + right: 0 + width: 400px + background-color: #fafafa + box-shadow: 0 1px 2px rgba(0,0,0,0.15) + border-radius: 2px + max-height: calc(100vh - 28px - 36px) + color: black + padding-top 36px; + overflow: scroll + + a:hover + color: belize !important + + .header + position: fixed + top 28px + right 0 + width calc(400px - 32px) + padding: 8px 16px + background: #ededed + border-bottom: 1px solid #dbdbdb + z-index 2 + + .toggle-read + position absolute + left 16px + top calc(50% - 8px) + color belize + + h5 + text-align: center + margin: 0 + + .close + position: absolute + top: calc(50% - 12px) + right: 12px + font-size: 24px + height: 24px + line-height: 24px + opacity 1 + + .all-read + color belize + background-color: #fafafa + margin 8px 16px 12px + display inline-block + + ul.notifications + display: block + padding: 0px 16px + margin: 0 -- cgit v1.2.3-1-g7c22 From f4d0791c6c1051c90d268484dab03c0ea5a5a533 Mon Sep 17 00:00:00 2001 From: Jonathan Baird Date: Wed, 1 Apr 2020 14:50:31 -0600 Subject: add correct i18n for notifications menu --- client/components/notifications/notificationsDrawer.jade | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'client/components/notifications') diff --git a/client/components/notifications/notificationsDrawer.jade b/client/components/notifications/notificationsDrawer.jade index 67fc6e65..01117009 100644 --- a/client/components/notifications/notificationsDrawer.jade +++ b/client/components/notifications/notificationsDrawer.jade @@ -2,10 +2,10 @@ template(name='notificationsDrawer') section#notifications-drawer(class="{{#if $.Session.get 'showReadNotifications'}}show-read{{/if}}") .header if $.Session.get 'showReadNotifications' - a.toggle-read Filter by Unread + a.toggle-read {{_ 'filter-by-unread'}} else - a.toggle-read View All - h5 Notifications + a.toggle-read {{_ 'view-all'}} + h5 {{_ 'notifications'}} if($gt unreadNotifications 0) |(#{unreadNotifications}) a.fa.fa-times-thin.close @@ -13,4 +13,4 @@ template(name='notificationsDrawer') each transformedProfile.notifications +notification(activityData=activity index=dbIndex read=read) if($gt unreadNotifications 0) - a.all-read Mark all as read + a.all-read {{_ 'mark-all-as-read'}} -- cgit v1.2.3-1-g7c22 From a182dde11ff4f1ab8cd01a04e1e26affda5d8a3b Mon Sep 17 00:00:00 2001 From: Jonathan Baird Date: Wed, 8 Apr 2020 13:14:29 -0600 Subject: add a "remove all read" button to notification menu --- client/components/notifications/notificationsDrawer.jade | 4 ++++ client/components/notifications/notificationsDrawer.js | 15 +++++++++++++++ client/components/notifications/notificationsDrawer.styl | 13 ++++++++++++- 3 files changed, 31 insertions(+), 1 deletion(-) (limited to 'client/components/notifications') diff --git a/client/components/notifications/notificationsDrawer.jade b/client/components/notifications/notificationsDrawer.jade index 01117009..fee6aef6 100644 --- a/client/components/notifications/notificationsDrawer.jade +++ b/client/components/notifications/notificationsDrawer.jade @@ -14,3 +14,7 @@ template(name='notificationsDrawer') +notification(activityData=activity index=dbIndex read=read) if($gt unreadNotifications 0) a.all-read {{_ 'mark-all-as-read'}} + if ($and ($.Session.get 'showReadNotifications') ($gt readNotifications 0)) + a.remove-read + i.fa.fa-trash + | {{_ 'remove-all-read'}} diff --git a/client/components/notifications/notificationsDrawer.js b/client/components/notifications/notificationsDrawer.js index 98d4750d..76abeea7 100644 --- a/client/components/notifications/notificationsDrawer.js +++ b/client/components/notifications/notificationsDrawer.js @@ -16,6 +16,13 @@ Template.notificationsDrawer.helpers({ transformedProfile() { return Users.findOne(Meteor.userId()); }, + readNotifications() { + const readNotifications = _.filter( + Meteor.user().profile.notifications, + v => !!v.read, + ); + return readNotifications.length; + }, }); Template.notificationsDrawer.events({ @@ -35,4 +42,12 @@ Template.notificationsDrawer.events({ 'click .toggle-read'() { Session.set('showReadNotifications', !Session.get('showReadNotifications')); }, + 'click .remove-read'() { + const user = Meteor.user(); + for (const notification of user.profile.notifications) { + if (notification.read) { + user.removeNotification(notification.activity); + } + } + }, }); diff --git a/client/components/notifications/notificationsDrawer.styl b/client/components/notifications/notificationsDrawer.styl index a26b5e4a..b64f13f4 100644 --- a/client/components/notifications/notificationsDrawer.styl +++ b/client/components/notifications/notificationsDrawer.styl @@ -45,12 +45,23 @@ section#notifications-drawer line-height: 24px opacity 1 - .all-read + .all-read, + .remove-read color belize background-color: #fafafa margin 8px 16px 12px display inline-block + .remove-read + float right + + &:hover + color #eb4646 !important + + i.fa + color inherit + + ul.notifications display: block padding: 0px 16px -- cgit v1.2.3-1-g7c22 From 9cacaafb8f93b7043b9d88550ca20615bb1d921e Mon Sep 17 00:00:00 2001 From: boeserwolf Date: Sun, 12 Apr 2020 11:47:30 +0200 Subject: Fix styling issue in notifications drawer The header of the notification drawer had a margin to the right side in Google Chrome caused by overflow: scroll on the #notifications-drawer element. --- client/components/notifications/notificationsDrawer.styl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'client/components/notifications') diff --git a/client/components/notifications/notificationsDrawer.styl b/client/components/notifications/notificationsDrawer.styl index b64f13f4..f99e1299 100644 --- a/client/components/notifications/notificationsDrawer.styl +++ b/client/components/notifications/notificationsDrawer.styl @@ -10,8 +10,7 @@ section#notifications-drawer border-radius: 2px max-height: calc(100vh - 28px - 36px) color: black - padding-top 36px; - overflow: scroll + padding-top 36px a:hover color: belize !important @@ -66,3 +65,5 @@ section#notifications-drawer display: block padding: 0px 16px margin: 0 + height: calc(100vh - 102px) + overflow-y: scroll -- cgit v1.2.3-1-g7c22