From 289ebeb5ce8e5b69ed76f6b66066880b70dce87b Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com> Date: Thu, 18 Jun 2020 17:08:06 -0400 Subject: [PATCH] Extract NotificationList state (#2185) * Extract NotificationList state --- .../core/js/src/forum/ForumApplication.js | 8 ++ .../src/forum/components/HeaderSecondary.js | 2 +- .../src/forum/components/NotificationList.js | 97 ++----------------- .../forum/components/NotificationsDropdown.js | 6 +- .../src/forum/components/NotificationsPage.js | 9 +- .../src/forum/states/NotificationListState.js | 94 ++++++++++++++++++ 6 files changed, 117 insertions(+), 99 deletions(-) create mode 100644 framework/core/js/src/forum/states/NotificationListState.js diff --git a/framework/core/js/src/forum/ForumApplication.js b/framework/core/js/src/forum/ForumApplication.js index 8bd447f2d..8173dd73d 100644 --- a/framework/core/js/src/forum/ForumApplication.js +++ b/framework/core/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/framework/core/js/src/forum/components/HeaderSecondary.js b/framework/core/js/src/forum/components/HeaderSecondary.js index c3eee2f2b..cc6db8bb2 100644 --- a/framework/core/js/src/forum/components/HeaderSecondary.js +++ b/framework/core/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/framework/core/js/src/forum/components/NotificationList.js b/framework/core/js/src/forum/components/NotificationList.js index 81a961ee4..5d7f26a48 100644 --- a/framework/core/js/src/forum/components/NotificationList.js +++ b/framework/core/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/framework/core/js/src/forum/components/NotificationsDropdown.js b/framework/core/js/src/forum/components/NotificationsDropdown.js index de25ce397..f44a3ca3c 100644 --- a/framework/core/js/src/forum/components/NotificationsDropdown.js +++ b/framework/core/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/framework/core/js/src/forum/components/NotificationsPage.js b/framework/core/js/src/forum/components/NotificationsPage.js index c32e24865..4d8818e18 100644 --- a/framework/core/js/src/forum/components/NotificationsPage.js +++ b/framework/core/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/framework/core/js/src/forum/states/NotificationListState.js b/framework/core/js/src/forum/states/NotificationListState.js new file mode 100644 index 000000000..93ad0bff5 --- /dev/null +++ b/framework/core/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', + }); + } +}