mirror of
https://github.com/flarum/core.git
synced 2025-08-08 01:16:52 +02:00
Implement notifications
This commit is contained in:
12
ember/app/components/application/notification-item.js
Normal file
12
ember/app/components/application/notification-item.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
import FadeIn from 'flarum/mixins/fade-in';
|
||||
|
||||
export default Ember.Component.extend(FadeIn, {
|
||||
layoutName: 'components/application/notification-item',
|
||||
tagName: 'li',
|
||||
|
||||
componentName: Ember.computed('notification.contentType', function() {
|
||||
return 'application/notification-'+this.get('notification.contentType');
|
||||
})
|
||||
});
|
3
ember/app/components/application/notification-renamed.js
Normal file
3
ember/app/components/application/notification-renamed.js
Normal file
@@ -0,0 +1,3 @@
|
||||
import Notification from './notification';
|
||||
|
||||
export default Notification.extend();
|
11
ember/app/components/application/notification.js
Normal file
11
ember/app/components/application/notification.js
Normal file
@@ -0,0 +1,11 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNames: ['notification'],
|
||||
classNameBindings: ['notification.isRead::unread'],
|
||||
|
||||
click: function() {
|
||||
console.log('click')
|
||||
this.get('notification').set('isRead', true).save();
|
||||
}
|
||||
});
|
39
ember/app/components/application/user-notifications.js
Normal file
39
ember/app/components/application/user-notifications.js
Normal file
@@ -0,0 +1,39 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
import DropdownButton from 'flarum/components/ui/dropdown-button';
|
||||
|
||||
var precompileTemplate = Ember.Handlebars.compile;
|
||||
|
||||
export default DropdownButton.extend({
|
||||
layoutName: 'components/application/user-notifications',
|
||||
classNames: ['notifications'],
|
||||
classNameBindings: ['unread'],
|
||||
|
||||
buttonClass: 'btn btn-default btn-rounded btn-naked btn-icon',
|
||||
menuClass: 'pull-right',
|
||||
|
||||
unread: Ember.computed.bool('user.unreadNotificationsCount'),
|
||||
|
||||
actions: {
|
||||
buttonClick: function() {
|
||||
if (!this.get('notifications')) {
|
||||
var component = this;
|
||||
this.set('notificationsLoading', true);
|
||||
this.get('parentController.store').find('notification').then(function(notifications) {
|
||||
component.set('user.unreadNotificationsCount', 0);
|
||||
component.set('notifications', notifications);
|
||||
component.set('notificationsLoading', false);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
markAllAsRead: function() {
|
||||
this.get('notifications').forEach(function(notification) {
|
||||
if (!notification.get('isRead')) {
|
||||
notification.set('isRead', true);
|
||||
notification.save();
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
})
|
@@ -2,8 +2,9 @@ import Ember from 'ember';
|
||||
import DS from 'ember-data';
|
||||
|
||||
import HasItemLists from 'flarum/mixins/has-item-lists';
|
||||
import Subject from './subject';
|
||||
|
||||
export default DS.Model.extend(HasItemLists, {
|
||||
export default Subject.extend(HasItemLists, {
|
||||
/**
|
||||
Define a "badges" item list. Example usage:
|
||||
```
|
||||
|
21
ember/app/models/notification.js
Normal file
21
ember/app/models/notification.js
Normal file
@@ -0,0 +1,21 @@
|
||||
import DS from 'ember-data';
|
||||
|
||||
export default DS.Model.extend({
|
||||
contentType: DS.attr('string'),
|
||||
subjectId: DS.attr('number'),
|
||||
content: DS.attr(),
|
||||
time: DS.attr('date'),
|
||||
isRead: DS.attr('boolean'),
|
||||
unreadCount: DS.attr('number'),
|
||||
additionalUnreadCount: Ember.computed('unreadCount', function() {
|
||||
return Math.max(0, this.get('unreadCount') - 1);
|
||||
}),
|
||||
|
||||
decodedContent: Ember.computed('content', function() {
|
||||
return JSON.parse(this.get('content'));
|
||||
}),
|
||||
|
||||
user: DS.belongsTo('user'),
|
||||
sender: DS.belongsTo('user'),
|
||||
subject: DS.belongsTo('subject', {polymorphic: true})
|
||||
});
|
@@ -1,7 +1,8 @@
|
||||
import Ember from 'ember';
|
||||
import DS from 'ember-data';
|
||||
import Subject from './subject';
|
||||
|
||||
export default DS.Model.extend({
|
||||
export default Subject.extend({
|
||||
discussion: DS.belongsTo('discussion', {inverse: 'loadedPosts'}),
|
||||
number: DS.attr('number'),
|
||||
|
||||
|
5
ember/app/models/subject.js
Normal file
5
ember/app/models/subject.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import DS from 'ember-data';
|
||||
|
||||
export default DS.Model.extend({
|
||||
notification: DS.belongsTo('notification')
|
||||
});
|
@@ -18,6 +18,7 @@ export default DS.Model.extend(HasItemLists, {
|
||||
joinTime: DS.attr('date'),
|
||||
lastSeenTime: DS.attr('date'),
|
||||
readTime: DS.attr('date'),
|
||||
unreadNotificationsCount: DS.attr('number'),
|
||||
|
||||
discussionsCount: DS.attr('number'),
|
||||
commentsCount: DS.attr('number'),
|
||||
|
@@ -30,6 +30,7 @@
|
||||
@import "@{flarum-base}modals.less";
|
||||
@import "@{flarum-base}layout.less";
|
||||
@import "@{flarum-base}composer.less";
|
||||
@import "@{flarum-base}notifications.less";
|
||||
|
||||
@import "@{flarum-base}index.less";
|
||||
@import "@{flarum-base}discussion.less";
|
||||
|
@@ -3,6 +3,7 @@
|
||||
padding: 8px 0;
|
||||
margin-top: 7px;
|
||||
background: @fl-body-bg;
|
||||
color: @fl-body-color;
|
||||
.box-shadow(0 2px 6px @fl-shadow-color);
|
||||
|
||||
& > li > a {
|
||||
|
@@ -154,7 +154,7 @@ body {
|
||||
color: @fl-drawer-color;
|
||||
}
|
||||
}
|
||||
&, & a, & .btn-link {
|
||||
&:not(.dropdown-menu), & a:not(.dropdown-menu a), & .btn-link:not(.dropdown-menu .btn-link) {
|
||||
color: @fl-drawer-control-color;
|
||||
}
|
||||
& .form-control {
|
||||
@@ -207,6 +207,10 @@ body {
|
||||
visibility: visible;
|
||||
transition-delay: 0s;
|
||||
}
|
||||
|
||||
& .dropdown-menu {
|
||||
width: @drawer-width !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,6 +251,11 @@ body {
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
& .dropdown-menu {
|
||||
& .btn-group, & .btn {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,7 +288,7 @@ body {
|
||||
.header-controls {
|
||||
&, & > li {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
.header-primary {
|
||||
|
114
ember/app/styles/flarum/notifications.less
Normal file
114
ember/app/styles/flarum/notifications.less
Normal file
@@ -0,0 +1,114 @@
|
||||
.notifications {
|
||||
& .dropdown-menu {
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
& .loading-indicator {
|
||||
height: 100px;
|
||||
}
|
||||
& .dropdown-toggle .label {
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
@media @tablet, @desktop, @desktop-hd {
|
||||
.notifications {
|
||||
& .dropdown-menu {
|
||||
width: 400px;
|
||||
}
|
||||
& .dropdown-toggle .label {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.notifications-icon {
|
||||
display: inline-block;
|
||||
border-radius: 12px;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
text-align: center;
|
||||
padding: 2px 0;
|
||||
font-weight: bold;
|
||||
margin: -2px -3px;
|
||||
}
|
||||
&.unread .notifications-icon {
|
||||
background: #e7562e;
|
||||
color: #fff;
|
||||
}
|
||||
.notifications-header {
|
||||
padding: 12px 15px;
|
||||
border-bottom: 1px solid @fl-body-secondary-color;
|
||||
|
||||
& h4 {
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
margin: 0;
|
||||
}
|
||||
& .btn {
|
||||
float: right;
|
||||
margin-top: -5px;
|
||||
margin-right: -5px;
|
||||
}
|
||||
}
|
||||
.notifications-list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
max-height: 600px;
|
||||
overflow: auto;
|
||||
}
|
||||
.no-notifications {
|
||||
color: @fl-body-muted-color;
|
||||
text-align: center;
|
||||
padding: 50px 0;
|
||||
font-size: 16px;
|
||||
}
|
||||
.notification {
|
||||
& > a {
|
||||
display: block;
|
||||
padding: 15px 15px 15px 75px;
|
||||
color: @fl-body-muted-color;
|
||||
overflow: hidden;
|
||||
|
||||
.unread& {
|
||||
background: @fl-body-secondary-color;
|
||||
}
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
background: @fl-body-secondary-color;
|
||||
}
|
||||
}
|
||||
& .avatar {
|
||||
float: left;
|
||||
margin-left: -60px;
|
||||
}
|
||||
}
|
||||
.notification-title {
|
||||
color: @fl-body-heading-color;
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
margin: 0 0 6px;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
.notification-info {
|
||||
font-size: 12px;
|
||||
|
||||
& .fa {
|
||||
font-size: 14px;
|
||||
}
|
||||
& .username {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
@media @phone {
|
||||
.notification {
|
||||
& > a {
|
||||
padding-left: 60px;
|
||||
}
|
||||
& .avatar {
|
||||
margin-left: -45px;
|
||||
.avatar-size(32px);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1 @@
|
||||
{{component componentName notification=notification}}
|
@@ -0,0 +1,14 @@
|
||||
{{#link-to "discussion" notification.subject (query-params start=notification.content.number)}}
|
||||
{{user-avatar notification.sender}}
|
||||
|
||||
<h3 class="notification-title">{{notification.content.oldTitle}}</h3>
|
||||
|
||||
<div class="notification-info">
|
||||
{{fa-icon "pencil"}}
|
||||
Renamed by {{user-name notification.sender}}
|
||||
{{#if notification.additionalUnreadCount}}
|
||||
and {{notification.additionalUnreadCount}} others
|
||||
{{/if}}
|
||||
{{human-time notification.time}}
|
||||
</div>
|
||||
{{/link-to}}
|
@@ -0,0 +1,26 @@
|
||||
<a href="#" {{bind-attr class=":dropdown-toggle buttonClass"}} data-toggle="dropdown" {{action "buttonClick"}}>
|
||||
<span class="notifications-icon">
|
||||
{{#if unread}}
|
||||
{{user.unreadNotificationsCount}}
|
||||
{{else}}
|
||||
{{fa-icon "bell" class="icon-glyph"}}
|
||||
{{/if}}
|
||||
</span>
|
||||
<span class="label">Notifications</span>
|
||||
</a>
|
||||
<div class="{{dropdownMenuClass}}">
|
||||
<div class="notifications-header">
|
||||
{{ui/action-button class="btn btn-icon btn-link btn-sm" icon="check" title="Mark All as Read" action="markAllAsRead"}}
|
||||
<h4>Notifications</h4>
|
||||
</div>
|
||||
<ul class="notifications-list">
|
||||
{{#each notifications as |notification|}}
|
||||
{{application/notification-item notification=notification}}
|
||||
{{else unless notificationsLoading}}
|
||||
<li class="no-notifications">No Notifications</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{#if notificationsLoading}}
|
||||
{{ui/loading-indicator}}
|
||||
{{/if}}
|
||||
</div>
|
@@ -2,6 +2,7 @@ import Ember from 'ember';
|
||||
|
||||
import HasItemLists from 'flarum/mixins/has-item-lists';
|
||||
import SearchInput from 'flarum/components/ui/search-input';
|
||||
import UserNotifications from 'flarum/components/application/user-notifications';
|
||||
import UserDropdown from 'flarum/components/application/user-dropdown';
|
||||
import ForumStatistic from 'flarum/components/application/forum-statistic';
|
||||
import PoweredBy from 'flarum/components/application/powered-by';
|
||||
@@ -88,6 +89,11 @@ export default Ember.View.extend(HasItemLists, {
|
||||
}), 'search');
|
||||
|
||||
if (this.get('controller.session.isAuthenticated')) {
|
||||
items.pushObjectWithTag(UserNotifications.extend({
|
||||
user: this.get('controller.session.user'),
|
||||
parentController: controller
|
||||
}), 'notifications');
|
||||
|
||||
items.pushObjectWithTag(UserDropdown.extend({
|
||||
user: this.get('controller.session.user'),
|
||||
parentController: controller
|
||||
|
Reference in New Issue
Block a user