diff --git a/js/src/forum/ForumApplication.js b/js/src/forum/ForumApplication.js
index 8bd447f2d..8173dd73d 100644
--- a/js/src/forum/ForumApplication.js
+++ b/js/src/forum/ForumApplication.js
@@ -14,6 +14,7 @@ import routes from './routes';
import alertEmailConfirmation from './utils/alertEmailConfirmation';
import Application from '../common/Application';
import Navigation from '../common/components/Navigation';
+import NotificationListState from './states/NotificationListState';
export default class ForumApplication extends Application {
/**
@@ -63,6 +64,13 @@ export default class ForumApplication extends Application {
*/
history = new History();
+ /**
+ * An object which controls the state of the user's notifications.
+ *
+ * @type {NotificationListState}
+ */
+ notifications = new NotificationListState(this);
+
constructor() {
super();
diff --git a/js/src/forum/components/HeaderSecondary.js b/js/src/forum/components/HeaderSecondary.js
index c3eee2f2b..cc6db8bb2 100644
--- a/js/src/forum/components/HeaderSecondary.js
+++ b/js/src/forum/components/HeaderSecondary.js
@@ -67,7 +67,7 @@ export default class HeaderSecondary extends Component {
}
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);
} else {
if (app.forum.attribute('allowSignUp')) {
diff --git a/js/src/forum/components/NotificationList.js b/js/src/forum/components/NotificationList.js
index 81a961ee4..5d7f26a48 100644
--- a/js/src/forum/components/NotificationList.js
+++ b/js/src/forum/components/NotificationList.js
@@ -10,23 +10,11 @@ import Discussion from '../../common/models/Discussion';
*/
export default class NotificationList extends Component {
init() {
- /**
- * 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;
+ this.state = this.props.state;
}
view() {
- const pages = app.cache.notifications || [];
+ const pages = this.state.getNotificationPages();
return (
@@ -36,7 +24,7 @@ export default class NotificationList extends Component {
className: 'Button Button--icon Button--link',
icon: 'fas fa-check',
title: app.translator.trans('core.forum.notifications.mark_all_as_read_tooltip'),
- onclick: this.markAllAsRead.bind(this),
+ onclick: this.state.markAllAsRead.bind(this.state),
})}
@@ -97,7 +85,7 @@ export default class NotificationList extends Component {
});
})
: ''}
- {this.loading ? (
+ {this.state.isLoading() ? (
) : pages.length ? (
''
@@ -121,8 +109,8 @@ export default class NotificationList extends Component {
const contentTop = $scrollParent === $notifications ? 0 : $notifications.offset().top;
const contentHeight = $notifications[0].scrollHeight;
- if (this.moreResults && !this.loading && scrollTop + viewportHeight >= contentTop + contentHeight) {
- this.loadMore();
+ if (this.state.hasMoreResults() && !this.state.isLoading() && scrollTop + viewportHeight >= contentTop + contentHeight) {
+ this.state.loadMore();
}
};
@@ -132,77 +120,4 @@ export default class NotificationList extends Component {
$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',
- });
- }
}
diff --git a/js/src/forum/components/NotificationsDropdown.js b/js/src/forum/components/NotificationsDropdown.js
index de25ce397..f44a3ca3c 100644
--- a/js/src/forum/components/NotificationsDropdown.js
+++ b/js/src/forum/components/NotificationsDropdown.js
@@ -15,8 +15,6 @@ export default class NotificationsDropdown extends Dropdown {
init() {
super.init();
-
- this.list = new NotificationList();
}
getButton() {
@@ -44,7 +42,7 @@ export default class NotificationsDropdown extends Dropdown {
getMenu() {
return (
- {this.showing ? this.list.render() : ''}
+ {this.showing ? NotificationList.component({ state: this.props.state }) : ''}
);
}
@@ -53,7 +51,7 @@ export default class NotificationsDropdown extends Dropdown {
if (app.drawer.isOpen()) {
this.goToRoute();
} else {
- this.list.load();
+ this.props.state.load();
}
}
diff --git a/js/src/forum/components/NotificationsPage.js b/js/src/forum/components/NotificationsPage.js
index c32e24865..4d8818e18 100644
--- a/js/src/forum/components/NotificationsPage.js
+++ b/js/src/forum/components/NotificationsPage.js
@@ -11,13 +11,16 @@ export default class NotificationsPage extends Page {
app.history.push('notifications');
- this.list = new NotificationList();
- this.list.load();
+ app.notifications.load();
this.bodyClass = 'App--notifications';
}
view() {
- return {this.list.render()}
;
+ return (
+
+
+
+ );
}
}
diff --git a/js/src/forum/states/NotificationListState.js b/js/src/forum/states/NotificationListState.js
new file mode 100644
index 000000000..93ad0bff5
--- /dev/null
+++ b/js/src/forum/states/NotificationListState.js
@@ -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',
+ });
+ }
+}