mirror of
https://github.com/flarum/framework.git
synced 2024-11-27 02:53:37 +08:00
Extract NotificationList state (#2185)
* Extract NotificationList state
This commit is contained in:
parent
5bca4fda9d
commit
65f2d5fb75
|
@ -14,6 +14,7 @@ import routes from './routes';
|
||||||
import alertEmailConfirmation from './utils/alertEmailConfirmation';
|
import alertEmailConfirmation from './utils/alertEmailConfirmation';
|
||||||
import Application from '../common/Application';
|
import Application from '../common/Application';
|
||||||
import Navigation from '../common/components/Navigation';
|
import Navigation from '../common/components/Navigation';
|
||||||
|
import NotificationListState from './states/NotificationListState';
|
||||||
|
|
||||||
export default class ForumApplication extends Application {
|
export default class ForumApplication extends Application {
|
||||||
/**
|
/**
|
||||||
|
@ -63,6 +64,13 @@ export default class ForumApplication extends Application {
|
||||||
*/
|
*/
|
||||||
history = new History();
|
history = new History();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object which controls the state of the user's notifications.
|
||||||
|
*
|
||||||
|
* @type {NotificationListState}
|
||||||
|
*/
|
||||||
|
notifications = new NotificationListState(this);
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
|
|
@ -67,7 +67,7 @@ export default class HeaderSecondary extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (app.session.user) {
|
if (app.session.user) {
|
||||||
items.add('notifications', NotificationsDropdown.component(), 10);
|
items.add('notifications', NotificationsDropdown.component({ state: app.notifications }), 10);
|
||||||
items.add('session', SessionDropdown.component(), 0);
|
items.add('session', SessionDropdown.component(), 0);
|
||||||
} else {
|
} else {
|
||||||
if (app.forum.attribute('allowSignUp')) {
|
if (app.forum.attribute('allowSignUp')) {
|
||||||
|
|
|
@ -10,23 +10,11 @@ import Discussion from '../../common/models/Discussion';
|
||||||
*/
|
*/
|
||||||
export default class NotificationList extends Component {
|
export default class NotificationList extends Component {
|
||||||
init() {
|
init() {
|
||||||
/**
|
this.state = this.props.state;
|
||||||
* Whether or not the notifications are loading.
|
|
||||||
*
|
|
||||||
* @type {Boolean}
|
|
||||||
*/
|
|
||||||
this.loading = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether or not there are more results that can be loaded.
|
|
||||||
*
|
|
||||||
* @type {Boolean}
|
|
||||||
*/
|
|
||||||
this.moreResults = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
view() {
|
view() {
|
||||||
const pages = app.cache.notifications || [];
|
const pages = this.state.getNotificationPages();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="NotificationList">
|
<div className="NotificationList">
|
||||||
|
@ -36,7 +24,7 @@ export default class NotificationList extends Component {
|
||||||
className: 'Button Button--icon Button--link',
|
className: 'Button Button--icon Button--link',
|
||||||
icon: 'fas fa-check',
|
icon: 'fas fa-check',
|
||||||
title: app.translator.trans('core.forum.notifications.mark_all_as_read_tooltip'),
|
title: app.translator.trans('core.forum.notifications.mark_all_as_read_tooltip'),
|
||||||
onclick: this.markAllAsRead.bind(this),
|
onclick: this.state.markAllAsRead.bind(this.state),
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -97,7 +85,7 @@ export default class NotificationList extends Component {
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
: ''}
|
: ''}
|
||||||
{this.loading ? (
|
{this.state.isLoading() ? (
|
||||||
<LoadingIndicator className="LoadingIndicator--block" />
|
<LoadingIndicator className="LoadingIndicator--block" />
|
||||||
) : pages.length ? (
|
) : pages.length ? (
|
||||||
''
|
''
|
||||||
|
@ -121,8 +109,8 @@ export default class NotificationList extends Component {
|
||||||
const contentTop = $scrollParent === $notifications ? 0 : $notifications.offset().top;
|
const contentTop = $scrollParent === $notifications ? 0 : $notifications.offset().top;
|
||||||
const contentHeight = $notifications[0].scrollHeight;
|
const contentHeight = $notifications[0].scrollHeight;
|
||||||
|
|
||||||
if (this.moreResults && !this.loading && scrollTop + viewportHeight >= contentTop + contentHeight) {
|
if (this.state.hasMoreResults() && !this.state.isLoading() && scrollTop + viewportHeight >= contentTop + contentHeight) {
|
||||||
this.loadMore();
|
this.state.loadMore();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -132,77 +120,4 @@ export default class NotificationList extends Component {
|
||||||
$scrollParent.off('scroll', scrollHandler);
|
$scrollParent.off('scroll', scrollHandler);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Load notifications into the application's cache if they haven't already
|
|
||||||
* been loaded.
|
|
||||||
*/
|
|
||||||
load() {
|
|
||||||
if (app.session.user.newNotificationCount()) {
|
|
||||||
delete app.cache.notifications;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (app.cache.notifications) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
app.session.user.pushAttributes({ newNotificationCount: 0 });
|
|
||||||
|
|
||||||
this.loadMore();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load the next page of notification results.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
loadMore() {
|
|
||||||
this.loading = true;
|
|
||||||
m.redraw();
|
|
||||||
|
|
||||||
const params = app.cache.notifications ? { page: { offset: app.cache.notifications.length * 10 } } : null;
|
|
||||||
|
|
||||||
return app.store
|
|
||||||
.find('notifications', params)
|
|
||||||
.then(this.parseResults.bind(this))
|
|
||||||
.catch(() => {})
|
|
||||||
.then(() => {
|
|
||||||
this.loading = false;
|
|
||||||
m.redraw();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse results and append them to the notification list.
|
|
||||||
*
|
|
||||||
* @param {Notification[]} results
|
|
||||||
* @return {Notification[]}
|
|
||||||
*/
|
|
||||||
parseResults(results) {
|
|
||||||
app.cache.notifications = app.cache.notifications || [];
|
|
||||||
|
|
||||||
if (results.length) app.cache.notifications.push(results);
|
|
||||||
|
|
||||||
this.moreResults = !!results.payload.links.next;
|
|
||||||
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mark all of the notifications as read.
|
|
||||||
*/
|
|
||||||
markAllAsRead() {
|
|
||||||
if (!app.cache.notifications) return;
|
|
||||||
|
|
||||||
app.session.user.pushAttributes({ unreadNotificationCount: 0 });
|
|
||||||
|
|
||||||
app.cache.notifications.forEach((notifications) => {
|
|
||||||
notifications.forEach((notification) => notification.pushAttributes({ isRead: true }));
|
|
||||||
});
|
|
||||||
|
|
||||||
app.request({
|
|
||||||
url: app.forum.attribute('apiUrl') + '/notifications/read',
|
|
||||||
method: 'POST',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,6 @@ export default class NotificationsDropdown extends Dropdown {
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
super.init();
|
super.init();
|
||||||
|
|
||||||
this.list = new NotificationList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getButton() {
|
getButton() {
|
||||||
|
@ -44,7 +42,7 @@ export default class NotificationsDropdown extends Dropdown {
|
||||||
getMenu() {
|
getMenu() {
|
||||||
return (
|
return (
|
||||||
<div className={'Dropdown-menu ' + this.props.menuClassName} onclick={this.menuClick.bind(this)}>
|
<div className={'Dropdown-menu ' + this.props.menuClassName} onclick={this.menuClick.bind(this)}>
|
||||||
{this.showing ? this.list.render() : ''}
|
{this.showing ? NotificationList.component({ state: this.props.state }) : ''}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -53,7 +51,7 @@ export default class NotificationsDropdown extends Dropdown {
|
||||||
if (app.drawer.isOpen()) {
|
if (app.drawer.isOpen()) {
|
||||||
this.goToRoute();
|
this.goToRoute();
|
||||||
} else {
|
} else {
|
||||||
this.list.load();
|
this.props.state.load();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,13 +11,16 @@ export default class NotificationsPage extends Page {
|
||||||
|
|
||||||
app.history.push('notifications');
|
app.history.push('notifications');
|
||||||
|
|
||||||
this.list = new NotificationList();
|
app.notifications.load();
|
||||||
this.list.load();
|
|
||||||
|
|
||||||
this.bodyClass = 'App--notifications';
|
this.bodyClass = 'App--notifications';
|
||||||
}
|
}
|
||||||
|
|
||||||
view() {
|
view() {
|
||||||
return <div className="NotificationsPage">{this.list.render()}</div>;
|
return (
|
||||||
|
<div className="NotificationsPage">
|
||||||
|
<NotificationList state={app.notifications}></NotificationList>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
94
js/src/forum/states/NotificationListState.js
Normal file
94
js/src/forum/states/NotificationListState.js
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
export default class NotificationListState {
|
||||||
|
constructor(app) {
|
||||||
|
this.app = app;
|
||||||
|
|
||||||
|
this.notificationPages = [];
|
||||||
|
|
||||||
|
this.loading = false;
|
||||||
|
|
||||||
|
this.moreResults = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
getNotificationPages() {
|
||||||
|
return this.notificationPages;
|
||||||
|
}
|
||||||
|
|
||||||
|
isLoading() {
|
||||||
|
return this.loading;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasMoreResults() {
|
||||||
|
return this.moreResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load notifications into the application's cache if they haven't already
|
||||||
|
* been loaded.
|
||||||
|
*/
|
||||||
|
load() {
|
||||||
|
if (this.app.session.user.newNotificationCount()) {
|
||||||
|
this.notificationPages = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.notificationPages.length > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.app.session.user.pushAttributes({ newNotificationCount: 0 });
|
||||||
|
|
||||||
|
this.loadMore();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load the next page of notification results.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
loadMore() {
|
||||||
|
this.loading = true;
|
||||||
|
m.redraw();
|
||||||
|
|
||||||
|
const params = this.notificationPages.length > 0 ? { page: { offset: this.notificationPages.length * 10 } } : null;
|
||||||
|
|
||||||
|
return this.app.store
|
||||||
|
.find('notifications', params)
|
||||||
|
.then(this.parseResults.bind(this))
|
||||||
|
.catch(() => {})
|
||||||
|
.then(() => {
|
||||||
|
this.loading = false;
|
||||||
|
m.redraw();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse results and append them to the notification list.
|
||||||
|
*
|
||||||
|
* @param {Notification[]} results
|
||||||
|
* @return {Notification[]}
|
||||||
|
*/
|
||||||
|
parseResults(results) {
|
||||||
|
if (results.length) this.notificationPages.push(results);
|
||||||
|
|
||||||
|
this.moreResults = !!results.payload.links.next;
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark all of the notifications as read.
|
||||||
|
*/
|
||||||
|
markAllAsRead() {
|
||||||
|
if (this.notificationPages.length === 0) return;
|
||||||
|
|
||||||
|
this.app.session.user.pushAttributes({ unreadNotificationCount: 0 });
|
||||||
|
|
||||||
|
this.notificationPages.forEach((notifications) => {
|
||||||
|
notifications.forEach((notification) => notification.pushAttributes({ isRead: true }));
|
||||||
|
});
|
||||||
|
|
||||||
|
this.app.request({
|
||||||
|
url: this.app.forum.attribute('apiUrl') + '/notifications/read',
|
||||||
|
method: 'POST',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user