From 0d31e76daa9f8ff78a174566c530d5c24354e4d3 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Fri, 18 Dec 2020 15:43:52 +0100 Subject: [PATCH] MOBILE-3633 notifications: Implement modules and main menu handler --- src/addons/addons.module.ts | 48 +------ src/addons/block/block.module.ts | 69 ++++++++++ .../notifications-lazy.module.ts | 44 ++++++ .../notifications/notifications.module.ts | 43 ++++++ .../services/handlers/mainmenu.ts | 125 ++++++++++++++++++ 5 files changed, 285 insertions(+), 44 deletions(-) create mode 100644 src/addons/block/block.module.ts create mode 100644 src/addons/notifications/notifications-lazy.module.ts create mode 100644 src/addons/notifications/notifications.module.ts create mode 100644 src/addons/notifications/services/handlers/mainmenu.ts diff --git a/src/addons/addons.module.ts b/src/addons/addons.module.ts index a49cffd18..4a719a45e 100644 --- a/src/addons/addons.module.ts +++ b/src/addons/addons.module.ts @@ -14,63 +14,23 @@ import { NgModule } from '@angular/core'; -import { AddonBlockActivityResultsModule } from './block/activityresults/activityresults.module'; -import { AddonBlockBadgesModule } from './block/badges/badges.module'; -import { AddonBlockBlogMenuModule } from './block/blogmenu/blogmenu.module'; -import { AddonBlockBlogRecentModule } from './block/blogrecent/blogrecent.module'; -import { AddonBlockBlogTagsModule } from './block/blogtags/blogtags.module'; -import { AddonBlockCalendarMonthModule } from './block/calendarmonth/calendarmonth.module'; -import { AddonBlockCalendarUpcomingModule } from './block/calendarupcoming/calendarupcoming.module'; -import { AddonBlockCommentsModule } from './block/comments/comments.module'; -import { AddonBlockCompletionStatusModule } from './block/completionstatus/completionstatus.module'; -import { AddonBlockGlossaryRandomModule } from './block/glossaryrandom/glossaryrandom.module'; -import { AddonBlockHtmlModule } from './block/html/html.module'; -import { AddonBlockLearningPlansModule } from './block/learningplans/learningplans.module'; -import { AddonBlockMyOverviewModule } from './block/myoverview/myoverview.module'; -import { AddonBlockNewsItemsModule } from './block/newsitems/newsitems.module'; -import { AddonBlockOnlineUsersModule } from './block/onlineusers/onlineusers.module'; -import { AddonBlockPrivateFilesModule } from './block/privatefiles/privatefiles.module'; -import { AddonBlockRecentlyAccessedCoursesModule } from './block/recentlyaccessedcourses/recentlyaccessedcourses.module'; -import { AddonBlockRssClientModule } from './block/rssclient/rssclient.module'; -import { AddonBlockSelfCompletionModule } from './block/selfcompletion/selfcompletion.module'; -import { AddonBlockSiteMainMenuModule } from './block/sitemainmenu/sitemainmenu.module'; -import { AddonBlockStarredCoursesModule } from './block/starredcourses/starredcourses.module'; -import { AddonBlockTagsModule } from './block/tags/tags.module'; +import { AddonBlockModule } from './block/block.module'; import { AddonPrivateFilesModule } from './privatefiles/privatefiles.module'; import { AddonFilterModule } from './filter/filter.module'; import { AddonUserProfileFieldModule } from './userprofilefield/userprofilefield.module'; import { AddonBadgesModule } from './badges/badges.module'; import { AddonCalendarModule } from './calendar/calendar.module'; +import { AddonNotificationsModule } from './notifications/notifications.module'; @NgModule({ imports: [ + AddonBlockModule, AddonBadgesModule, AddonCalendarModule, AddonPrivateFilesModule, AddonFilterModule, - AddonBlockActivityResultsModule, - AddonBlockBadgesModule, - AddonBlockBlogMenuModule, - AddonBlockBlogRecentModule, - AddonBlockBlogTagsModule, - AddonBlockCalendarMonthModule, - AddonBlockCalendarUpcomingModule, - AddonBlockCommentsModule, - AddonBlockCompletionStatusModule, - AddonBlockGlossaryRandomModule, - AddonBlockHtmlModule, - AddonBlockMyOverviewModule, - AddonBlockLearningPlansModule, - AddonBlockNewsItemsModule, - AddonBlockOnlineUsersModule, - AddonBlockPrivateFilesModule, - AddonBlockRecentlyAccessedCoursesModule, - AddonBlockRssClientModule, - AddonBlockSelfCompletionModule, - AddonBlockSiteMainMenuModule, - AddonBlockStarredCoursesModule, - AddonBlockTagsModule, AddonUserProfileFieldModule, + AddonNotificationsModule, ], }) export class AddonsModule {} diff --git a/src/addons/block/block.module.ts b/src/addons/block/block.module.ts new file mode 100644 index 000000000..420c0e632 --- /dev/null +++ b/src/addons/block/block.module.ts @@ -0,0 +1,69 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { NgModule } from '@angular/core'; + +import { AddonBlockActivityResultsModule } from './activityresults/activityresults.module'; +import { AddonBlockBadgesModule } from './badges/badges.module'; +import { AddonBlockBlogMenuModule } from './blogmenu/blogmenu.module'; +import { AddonBlockBlogRecentModule } from './blogrecent/blogrecent.module'; +import { AddonBlockBlogTagsModule } from './blogtags/blogtags.module'; +import { AddonBlockCalendarMonthModule } from './calendarmonth/calendarmonth.module'; +import { AddonBlockCalendarUpcomingModule } from './calendarupcoming/calendarupcoming.module'; +import { AddonBlockCommentsModule } from './comments/comments.module'; +import { AddonBlockCompletionStatusModule } from './completionstatus/completionstatus.module'; +import { AddonBlockGlossaryRandomModule } from './glossaryrandom/glossaryrandom.module'; +import { AddonBlockHtmlModule } from './html/html.module'; +import { AddonBlockLearningPlansModule } from './learningplans/learningplans.module'; +import { AddonBlockMyOverviewModule } from './myoverview/myoverview.module'; +import { AddonBlockNewsItemsModule } from './newsitems/newsitems.module'; +import { AddonBlockOnlineUsersModule } from './onlineusers/onlineusers.module'; +import { AddonBlockPrivateFilesModule } from './privatefiles/privatefiles.module'; +import { AddonBlockRecentlyAccessedCoursesModule } from './recentlyaccessedcourses/recentlyaccessedcourses.module'; +import { AddonBlockRssClientModule } from './rssclient/rssclient.module'; +import { AddonBlockSelfCompletionModule } from './selfcompletion/selfcompletion.module'; +import { AddonBlockSiteMainMenuModule } from './sitemainmenu/sitemainmenu.module'; +import { AddonBlockStarredCoursesModule } from './starredcourses/starredcourses.module'; +import { AddonBlockTagsModule } from './tags/tags.module'; + +@NgModule({ + declarations: [], + imports: [ + AddonBlockActivityResultsModule, + AddonBlockBadgesModule, + AddonBlockBlogMenuModule, + AddonBlockBlogRecentModule, + AddonBlockBlogTagsModule, + AddonBlockCalendarMonthModule, + AddonBlockCalendarUpcomingModule, + AddonBlockCommentsModule, + AddonBlockCompletionStatusModule, + AddonBlockGlossaryRandomModule, + AddonBlockHtmlModule, + AddonBlockMyOverviewModule, + AddonBlockLearningPlansModule, + AddonBlockNewsItemsModule, + AddonBlockOnlineUsersModule, + AddonBlockPrivateFilesModule, + AddonBlockRecentlyAccessedCoursesModule, + AddonBlockRssClientModule, + AddonBlockSelfCompletionModule, + AddonBlockSiteMainMenuModule, + AddonBlockStarredCoursesModule, + AddonBlockTagsModule, + ], + providers: [], + exports: [], +}) +export class AddonBlockModule { } diff --git a/src/addons/notifications/notifications-lazy.module.ts b/src/addons/notifications/notifications-lazy.module.ts new file mode 100644 index 000000000..890e8df7c --- /dev/null +++ b/src/addons/notifications/notifications-lazy.module.ts @@ -0,0 +1,44 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Injector, NgModule } from '@angular/core'; +import { RouterModule, ROUTES, Routes } from '@angular/router'; + +import { buildTabMainRoutes } from '@features/mainmenu/mainmenu-tab-routing.module'; + +function buildRoutes(injector: Injector): Routes { + return [ + { + path: 'list', + loadChildren: () => import('./pages/list/list.module').then(m => m.AddonNotificationsListPageModule), + }, + ...buildTabMainRoutes(injector, { + redirectTo: 'list', + pathMatch: 'full', + }), + ]; +} + +@NgModule({ + exports: [RouterModule], + providers: [ + { + provide: ROUTES, + multi: true, + deps: [Injector], + useFactory: buildRoutes, + }, + ], +}) +export class AddonNotificationsLazyModule {} diff --git a/src/addons/notifications/notifications.module.ts b/src/addons/notifications/notifications.module.ts new file mode 100644 index 000000000..09cec2cca --- /dev/null +++ b/src/addons/notifications/notifications.module.ts @@ -0,0 +1,43 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { APP_INITIALIZER, NgModule } from '@angular/core'; +import { Routes } from '@angular/router'; + +import { CoreMainMenuDelegate } from '@features/mainmenu/services/mainmenu-delegate'; +import { CoreMainMenuRoutingModule } from '@features/mainmenu/mainmenu-routing.module'; +import { AddonNotificationsMainMenuHandler, AddonNotificationsMainMenuHandlerService } from './services/handlers/mainmenu'; + +const routes: Routes = [ + { + path: AddonNotificationsMainMenuHandlerService.PAGE_NAME, + loadChildren: () => import('@/addons/notifications/notifications-lazy.module').then(m => m.AddonNotificationsLazyModule), + }, +]; + +@NgModule({ + imports: [CoreMainMenuRoutingModule.forChild({ children: routes })], + exports: [CoreMainMenuRoutingModule], + providers: [ + { + provide: APP_INITIALIZER, + multi: true, + deps: [], + useFactory: () => () => { + CoreMainMenuDelegate.instance.registerHandler(AddonNotificationsMainMenuHandler.instance); + }, + }, + ], +}) +export class AddonNotificationsModule {} diff --git a/src/addons/notifications/services/handlers/mainmenu.ts b/src/addons/notifications/services/handlers/mainmenu.ts new file mode 100644 index 000000000..ee646c8c2 --- /dev/null +++ b/src/addons/notifications/services/handlers/mainmenu.ts @@ -0,0 +1,125 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Injectable } from '@angular/core'; + +import { CoreSites } from '@services/sites'; +import { CoreUtils } from '@services/utils/utils'; +import { makeSingleton } from '@singletons'; +import { CoreEvents, CoreEventSiteData } from '@singletons/events'; +import { CoreMainMenuHandler, CoreMainMenuHandlerData } from '@features/mainmenu/services/mainmenu-delegate'; +import { CorePushNotifications } from '@features/pushnotifications/services/pushnotifications'; +import { CorePushNotificationsDelegate } from '@features/pushnotifications/services/push-delegate'; +import { AddonNotifications, AddonNotificationsProvider } from '../notifications'; + +/** + * Handler to inject an option into main menu. + */ +@Injectable({ providedIn: 'root' }) +export class AddonNotificationsMainMenuHandlerService implements CoreMainMenuHandler { + + static readonly PAGE_NAME = 'notifications'; + + name = 'AddonNotifications'; + priority = 700; + + protected handlerData: CoreMainMenuHandlerData = { + icon: 'fas-bell', + title: 'addon.notifications.notifications', + page: AddonNotificationsMainMenuHandlerService.PAGE_NAME, + class: 'addon-notifications-handler', + showBadge: true, + badge: '', + loading: true, + }; + + /** + * Initialize the handler. + */ + initialize(): void { + CoreEvents.on(AddonNotificationsProvider.READ_CHANGED_EVENT, (data: CoreEventSiteData) => { + this.updateBadge(data.siteId); + }); + + CoreEvents.on(AddonNotificationsProvider.READ_CRON_EVENT, (data: CoreEventSiteData) => { + this.updateBadge(data.siteId); + }); + + // Reset info on logout. + CoreEvents.on(CoreEvents.LOGOUT, () => { + this.handlerData.badge = ''; + this.handlerData.loading = true; + }); + + // If a push notification is received, refresh the count. + CorePushNotificationsDelegate.instance.on('receive').subscribe((notification) => { + // New notification received. If it's from current site, refresh the data. + if (CoreUtils.instance.isTrueOrOne(notification.notif) && CoreSites.instance.isCurrentSite(notification.site)) { + this.updateBadge(notification.site); + } + }); + + // Register Badge counter. + CorePushNotificationsDelegate.instance.registerCounterHandler('AddonNotifications'); + } + + /** + * Check if the handler is enabled on a site level. + * + * @return Whether or not the handler is enabled on a site level. + */ + async isEnabled(): Promise { + return true; + } + + /** + * Returns the data needed to render the handler. + * + * @return Data needed to render the handler. + */ + getDisplayData(): CoreMainMenuHandlerData { + if (this.handlerData.loading) { + this.updateBadge(); + } + + return this.handlerData; + } + + /** + * Triggers an update for the badge number and loading status. Mandatory if showBadge is enabled. + * + * @param siteId Site ID or current Site if undefined. + * @return Promise resolved when done. + */ + protected async updateBadge(siteId?: string): Promise { + siteId = siteId || CoreSites.instance.getCurrentSiteId(); + if (!siteId) { + return; + } + + try { + const unreadCount = await AddonNotifications.instance.getUnreadNotificationsCount(undefined, siteId); + + this.handlerData.badge = unreadCount > 0 ? String(unreadCount) : ''; + CorePushNotifications.instance.updateAddonCounter('AddonNotifications', unreadCount, siteId); + } catch { + this.handlerData.badge = ''; + } finally { + this.handlerData.loading = false; + } + } + +} + +export class AddonNotificationsMainMenuHandler extends makeSingleton(AddonNotificationsMainMenuHandlerService) {}