forked from EVOgeek/Vmeda.Online
		
	Merge pull request #3262 from alfonso-salces/MOBILE-4021
[4.1] Mobile 4021 notifications: refactor to add split-view and swipe
This commit is contained in:
		
						commit
						1062c9bd98
					
				
							
								
								
									
										47
									
								
								src/addons/notifications/classes/notifications-source.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/addons/notifications/classes/notifications-source.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,47 @@ | ||||
| // (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 { CoreRoutedItemsManagerSource } from '@classes/items-management/routed-items-manager-source'; | ||||
| import { AddonNotifications } from '../services/notifications'; | ||||
| import { AddonNotificationsHelper, AddonNotificationsNotificationToRender } from '../services/notifications-helper'; | ||||
| 
 | ||||
| /** | ||||
|  * Provides a list of notifications | ||||
|  */ | ||||
| export class AddonsNotificationsNotificationsSource extends CoreRoutedItemsManagerSource<AddonNotificationsNotificationToRender> { | ||||
| 
 | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     protected async loadPageItems(page: number): Promise<{ | ||||
|         items: AddonNotificationsNotificationToRender[]; | ||||
|         hasMoreItems: boolean; | ||||
|     }> { | ||||
|         // TODO this should be refactored to avoid using the existing items.
 | ||||
|         const { notifications, canLoadMore } = await AddonNotifications.getNotifications(page === 0 ? [] : this.getItems() ?? []); | ||||
| 
 | ||||
|         return { | ||||
|             items: notifications.map(notification => AddonNotificationsHelper.formatNotificationText(notification)), | ||||
|             hasMoreItems: canLoadMore, | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     getItemPath(notification: AddonNotificationsNotificationToRender): string { | ||||
|         return notification.id.toString(); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -12,10 +12,12 @@ | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { conditionalRoutes } from '@/app/app-routing.module'; | ||||
| import { Injector, NgModule } from '@angular/core'; | ||||
| import { RouterModule, ROUTES, Routes } from '@angular/router'; | ||||
| 
 | ||||
| import { buildTabMainRoutes } from '@features/mainmenu/mainmenu-tab-routing.module'; | ||||
| import { CoreScreen } from '@services/screen'; | ||||
| import { AddonNotificationsMainMenuHandlerService } from './services/handlers/mainmenu'; | ||||
| 
 | ||||
| function buildRoutes(injector: Injector): Routes { | ||||
| @ -27,6 +29,13 @@ function buildRoutes(injector: Injector): Routes { | ||||
|             }, | ||||
|             loadChildren: () => import('./pages/list/list.module').then(m => m.AddonNotificationsListPageModule), | ||||
|         }, | ||||
|         ...conditionalRoutes([ | ||||
|             { | ||||
|                 path: 'list/:id', | ||||
|                 loadChildren: () => import('./pages/notification/notification.module') | ||||
|                     .then(m => m.AddonNotificationsNotificationPageModule), | ||||
|             }, | ||||
|         ], () => CoreScreen.isMobile), | ||||
|         { | ||||
|             path: 'notification', | ||||
|             loadChildren: () => import('./pages/notification/notification.module') | ||||
|  | ||||
| @ -11,66 +11,72 @@ | ||||
|         </ion-buttons> | ||||
|     </ion-toolbar> | ||||
| </ion-header> | ||||
| <ion-content class="limited-width"> | ||||
|     <ion-refresher slot="fixed" [disabled]="!notificationsLoaded" (ionRefresh)="refreshNotifications($event.target)"> | ||||
|         <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content> | ||||
|     </ion-refresher> | ||||
|     <core-loading [hideUntil]="notificationsLoaded"> | ||||
| <ion-content> | ||||
|     <core-split-view> | ||||
|         <ion-refresher slot="fixed" [disabled]="!notifications.loaded" (ionRefresh)="refreshNotifications($event.target)"> | ||||
|             <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content> | ||||
|         </ion-refresher> | ||||
|         <core-loading [hideUntil]="notifications.loaded"> | ||||
| 
 | ||||
|         <ion-item *ngFor="let notification of notifications" class="ion-text-wrap" [attr.aria-label]=" | ||||
|                     notification.timeread | ||||
|                     ? notification.subject | ||||
|                     : 'addon.notifications.unreadnotification' | translate: {$a: notification.subject}" | ||||
|             (click)="openNotification(notification)" button [detail]="false" lines="full"> | ||||
|             <core-user-avatar *ngIf="notification.useridfrom > 0" [user]="notification" slot="start" | ||||
|                 [profileUrl]="notification.profileimageurlfrom" [fullname]="notification.userfromfullname" | ||||
|                 [userId]="notification.useridfrom"> | ||||
|                 <div class="core-avatar-extra-img" *ngIf="notification.iconurl && !notification.modname"> | ||||
|                     <img [src]="notification.iconurl" alt="" role="presentation"> | ||||
|                 </div> | ||||
|                 <core-mod-icon *ngIf="notification.modname" [modicon]="notification.iconurl" [modname]="notification.modname" | ||||
|                     [showAlt]="false"> | ||||
|                 </core-mod-icon> | ||||
|             </core-user-avatar> | ||||
|             <ion-item *ngFor="let notification of notifications.items" class="ion-text-wrap" | ||||
|                 [attr.aria-current]="notifications.getItemAriaCurrent(notification)" [attr.aria-label]=" | ||||
|                 notification.timeread | ||||
|                 ? notification.subject | ||||
|                 : 'addon.notifications.unreadnotification' | translate: {$a: notification.subject}" | ||||
|                 (click)="notifications.select(notification)" button [detail]="false" lines="full"> | ||||
| 
 | ||||
|             <ng-container *ngIf="notification.useridfrom <= 0 && notification.iconurl"> | ||||
|                 <div class="core-notification-icon" *ngIf="!notification.modname" slot="start"> | ||||
|                     <img [src]="notification.iconurl" alt="" role="presentation"> | ||||
|                 </div> | ||||
|                 <core-mod-icon *ngIf="notification.modname" [modicon]="notification.iconurl" [modname]="notification.modname" | ||||
|                     [showAlt]="false" class="core-notification-icon" slot="start"> | ||||
|                 </core-mod-icon> | ||||
|             </ng-container> | ||||
|                 <core-user-avatar *ngIf="notification.useridfrom > 0" [user]="notification" slot="start" | ||||
|                     [profileUrl]="notification.profileimageurlfrom" [fullname]="notification.userfromfullname" | ||||
|                     [userId]="notification.useridfrom"> | ||||
|                     <div class="core-avatar-extra-img" *ngIf="notification.iconurl && !notification.modname"> | ||||
|                         <img [src]="notification.iconurl" alt="" role="presentation"> | ||||
|                     </div> | ||||
|                     <core-mod-icon *ngIf="notification.modname" [modicon]="notification.iconurl" [modname]="notification.modname" | ||||
|                         [showAlt]="false"> | ||||
|                     </core-mod-icon> | ||||
|                 </core-user-avatar> | ||||
| 
 | ||||
|             <ion-label> | ||||
|                 <p class="item-heading"> | ||||
|                     <core-format-text [text]="notification.subject" contextLevel="system" [contextInstanceId]="0" [wsNotFiltered]="true"> | ||||
|                     </core-format-text> | ||||
|                 </p> | ||||
|                 <p>{{ notification.timecreated | coreTimeAgo }}<ng-container *ngIf="notification.useridfrom > 0"> · {{ | ||||
|                         notification.userfromfullname }}</ng-container> | ||||
|                 </p> | ||||
|             </ion-label> | ||||
|             <ion-note slot="end" *ngIf="!notification.timeread"> | ||||
|                 <ion-icon name="fas-circle" color="primary" aria-hidden="true"></ion-icon> | ||||
|             </ion-note> | ||||
|         </ion-item> | ||||
|                 <ng-container *ngIf="notification.useridfrom <= 0 && notification.iconurl"> | ||||
|                     <div class="core-notification-icon" *ngIf="!notification.modname" slot="start"> | ||||
|                         <img [src]="notification.iconurl" alt="" role="presentation"> | ||||
|                     </div> | ||||
|                     <core-mod-icon *ngIf="notification.modname" [modicon]="notification.iconurl" [modname]="notification.modname" | ||||
|                         [showAlt]="false" class="core-notification-icon" slot="start"> | ||||
|                     </core-mod-icon> | ||||
|                 </ng-container> | ||||
| 
 | ||||
|         <core-empty-box *ngIf="!notifications || notifications.length <= 0" icon="far-bell" | ||||
|             [message]="'addon.notifications.therearentnotificationsyet' | translate"> | ||||
|         </core-empty-box> | ||||
|         <core-infinite-loading [enabled]="canLoadMore" (action)="loadMoreNotifications($event)" [error]="loadMoreError"> | ||||
|         </core-infinite-loading> | ||||
|     </core-loading> | ||||
|                 <ion-label> | ||||
|                     <p class="item-heading"> | ||||
|                         <core-format-text [text]="notification.subject" contextLevel="system" [contextInstanceId]="0" | ||||
|                             [wsNotFiltered]="true"> | ||||
|                         </core-format-text> | ||||
|                     </p> | ||||
|                     <p>{{ notification.timecreated | coreTimeAgo }}<ng-container *ngIf="notification.useridfrom > 0"> · {{ | ||||
|                             notification.userfromfullname }}</ng-container> | ||||
|                     </p> | ||||
|                 </ion-label> | ||||
|                 <ion-note slot="end" *ngIf="!notification.timeread"> | ||||
|                     <ion-icon name="fas-circle" color="primary" aria-hidden="true"></ion-icon> | ||||
|                 </ion-note> | ||||
|             </ion-item> | ||||
| 
 | ||||
|             <core-empty-box *ngIf="!notifications || notifications.empty" icon="far-bell" | ||||
|                 [message]="'addon.notifications.therearentnotificationsyet' | translate"> | ||||
|             </core-empty-box> | ||||
|             <core-infinite-loading [enabled]="notifications.loaded && !notifications.completed" (action)="fetchMoreNotifications($event)" | ||||
|                 [error]="fetchMoreNotificationsFailed"> | ||||
|             </core-infinite-loading> | ||||
|         </core-loading> | ||||
| 
 | ||||
| 
 | ||||
|     <div class="mark-all-as-read" slot="fixed" collapsible-footer appearOnBottom> | ||||
|         <ion-chip *ngIf="notificationsLoaded && canMarkAllNotificationsAsRead" [disabled]="loadingMarkAllNotificationsAsRead" | ||||
|             color="primary" (click)="markAllNotificationsAsRead()"> | ||||
|             <ion-icon name="fas-eye" aria-hidden="true" *ngIf="!loadingMarkAllNotificationsAsRead"></ion-icon> | ||||
|             <ion-spinner [attr.aria-label]="'core.loading' | translate" *ngIf="loadingMarkAllNotificationsAsRead"> | ||||
|             </ion-spinner> | ||||
|             {{ 'addon.notifications.markallread' | translate }} | ||||
|         </ion-chip> | ||||
|     </div> | ||||
|         <div class="mark-all-as-read" slot="fixed" collapsible-footer appearOnBottom> | ||||
|             <ion-chip *ngIf="notifications.loaded && canMarkAllNotificationsAsRead" [disabled]="loadingMarkAllNotificationsAsRead" | ||||
|                 color="primary" (click)="markAllNotificationsAsRead()"> | ||||
|                 <ion-icon name="fas-eye" aria-hidden="true" *ngIf="!loadingMarkAllNotificationsAsRead"></ion-icon> | ||||
|                 <ion-spinner [attr.aria-label]="'core.loading' | translate" *ngIf="loadingMarkAllNotificationsAsRead"> | ||||
|                 </ion-spinner> | ||||
|                 {{ 'addon.notifications.markallread' | translate }} | ||||
|             </ion-chip> | ||||
|         </div> | ||||
|     </core-split-view> | ||||
| </ion-content> | ||||
|  | ||||
| @ -18,11 +18,20 @@ import { RouterModule, Routes } from '@angular/router'; | ||||
| import { CoreSharedModule } from '@/core/shared.module'; | ||||
| import { AddonNotificationsListPage } from './list'; | ||||
| import { CoreMainMenuComponentsModule } from '@features/mainmenu/components/components.module'; | ||||
| import { conditionalRoutes } from '@/app/app-routing.module'; | ||||
| import { CoreScreen } from '@services/screen'; | ||||
| 
 | ||||
| const routes: Routes = [ | ||||
|     { | ||||
|         path: '', | ||||
|         component: AddonNotificationsListPage, | ||||
|         children: conditionalRoutes([ | ||||
|             { | ||||
|                 path: ':id', | ||||
|                 loadChildren: () => import('../../pages/notification/notification.module') | ||||
|                     .then(m => m.AddonNotificationsNotificationPageModule), | ||||
|             }, | ||||
|         ], () => CoreScreen.isTablet), | ||||
|     }, | ||||
| ]; | ||||
| 
 | ||||
|  | ||||
| @ -12,26 +12,26 @@ | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { Component, OnDestroy, OnInit } from '@angular/core'; | ||||
| import { AfterViewInit, Component, OnDestroy, ViewChild } from '@angular/core'; | ||||
| import { IonRefresher } from '@ionic/angular'; | ||||
| import { Subscription } from 'rxjs'; | ||||
| 
 | ||||
| import { CoreSites } from '@services/sites'; | ||||
| import { CoreDomUtils } from '@services/utils/dom'; | ||||
| import { CoreUtils } from '@services/utils/utils'; | ||||
| import { CoreEvents, CoreEventObserver } from '@singletons/events'; | ||||
| import { CoreEventObserver, CoreEvents } from '@singletons/events'; | ||||
| import { | ||||
|     AddonNotifications, | ||||
|     AddonNotificationsProvider, | ||||
|     AddonNotifications, AddonNotificationsProvider, | ||||
| } from '../../services/notifications'; | ||||
| import { CorePushNotificationsDelegate } from '@features/pushnotifications/services/push-delegate'; | ||||
| import { | ||||
|     AddonNotificationsHelper, | ||||
|     AddonNotificationsNotificationToRender, | ||||
| } from '@addons/notifications/services/notifications-helper'; | ||||
| import { CoreMainMenuDeepLinkManager } from '@features/mainmenu/classes/deep-link-manager'; | ||||
| import { CoreNavigator } from '@services/navigator'; | ||||
| import { CoreSplitViewComponent } from '@components/split-view/split-view'; | ||||
| import { CoreRoutedItemsManagerSourcesTracker } from '@classes/items-management/routed-items-manager-sources-tracker'; | ||||
| import { CorePushNotificationsDelegate } from '@features/pushnotifications/services/push-delegate'; | ||||
| import { CoreSites } from '@services/sites'; | ||||
| import { CoreMainMenuDeepLinkManager } from '@features/mainmenu/classes/deep-link-manager'; | ||||
| import { CoreTimeUtils } from '@services/utils/time'; | ||||
| import { AddonsNotificationsNotificationsSource } from '@addons/notifications/classes/notifications-source'; | ||||
| import { CoreListItemsManager } from '@classes/items-management/list-items-manager'; | ||||
| import { AddonNotificationsNotificationToRender } from '@addons/notifications/services/notifications-helper'; | ||||
| 
 | ||||
| /** | ||||
|  * Page that displays the list of notifications. | ||||
| @ -41,12 +41,11 @@ import { CoreTimeUtils } from '@services/utils/time'; | ||||
|     templateUrl: 'list.html', | ||||
|     styleUrls: ['list.scss', '../../notifications.scss'], | ||||
| }) | ||||
| export class AddonNotificationsListPage implements OnInit, OnDestroy { | ||||
| export class AddonNotificationsListPage implements AfterViewInit, OnDestroy { | ||||
| 
 | ||||
|     notifications: AddonNotificationsNotificationToRender[] = []; | ||||
|     notificationsLoaded = false; | ||||
|     canLoadMore = false; | ||||
|     loadMoreError = false; | ||||
|     @ViewChild(CoreSplitViewComponent) splitView!: CoreSplitViewComponent; | ||||
|     notifications!: CoreListItemsManager<AddonNotificationsNotificationToRender, AddonsNotificationsNotificationsSource>; | ||||
|     fetchMoreNotificationsFailed = false; | ||||
|     canMarkAllNotificationsAsRead = false; | ||||
|     loadingMarkAllNotificationsAsRead = false; | ||||
| 
 | ||||
| @ -56,18 +55,33 @@ export class AddonNotificationsListPage implements OnInit, OnDestroy { | ||||
|     protected pushObserver?: Subscription; | ||||
|     protected pendingRefresh = false; | ||||
| 
 | ||||
|     constructor() { | ||||
|         try { | ||||
|             const source = CoreRoutedItemsManagerSourcesTracker.getOrCreateSource( | ||||
|                 AddonsNotificationsNotificationsSource, | ||||
|                 [], | ||||
|             ); | ||||
| 
 | ||||
|             this.notifications = new CoreListItemsManager(source, AddonNotificationsListPage); | ||||
|         } catch(error) { | ||||
|             CoreDomUtils.showErrorModal(error); | ||||
|             CoreNavigator.back(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     ngOnInit(): void { | ||||
|         this.fetchNotifications(); | ||||
|     async ngAfterViewInit(): Promise<void> { | ||||
|         await this.fetchInitialNotifications(); | ||||
| 
 | ||||
|         this.notifications.start(this.splitView); | ||||
| 
 | ||||
|         this.cronObserver = CoreEvents.on(AddonNotificationsProvider.READ_CRON_EVENT, () => { | ||||
|             if (!this.isCurrentView) { | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             this.notificationsLoaded = false; | ||||
|             this.refreshNotifications(); | ||||
|         }, CoreSites.getCurrentSiteId()); | ||||
| 
 | ||||
| @ -83,7 +97,6 @@ export class AddonNotificationsListPage implements OnInit, OnDestroy { | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             this.notificationsLoaded = false; | ||||
|             this.refreshNotifications(); | ||||
|         }); | ||||
| 
 | ||||
| @ -92,7 +105,7 @@ export class AddonNotificationsListPage implements OnInit, OnDestroy { | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             const notification = this.notifications.find((notification) => notification.id === data.id); | ||||
|             const notification = this.notifications.items.find((notification) => notification.id === data.id); | ||||
|             if (!notification) { | ||||
|                 return; | ||||
|             } | ||||
| @ -109,34 +122,47 @@ export class AddonNotificationsListPage implements OnInit, OnDestroy { | ||||
|     /** | ||||
|      * Convenience function to get notifications. Gets unread notifications first. | ||||
|      * | ||||
|      * @param refresh Whether we're refreshing data. | ||||
|      * @return Resolved when done. | ||||
|      * @param reload Whether to reload the list or load the next page. | ||||
|      */ | ||||
|     protected async fetchNotifications(refresh?: boolean): Promise<void> { | ||||
|         this.loadMoreError = false; | ||||
|     protected async fetchNotifications(reload: boolean): Promise<void> { | ||||
|         reload | ||||
|             ? await this.notifications.reload() | ||||
|             : await this.notifications.load(); | ||||
| 
 | ||||
|         this.fetchMoreNotificationsFailed = false; | ||||
|         this.loadMarkAllAsReadButton(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Obtain the initial batch of notifications. | ||||
|      */ | ||||
|     private async fetchInitialNotifications(): Promise<void> { | ||||
|         try { | ||||
|             const result = await AddonNotifications.getNotifications(refresh ? [] : this.notifications); | ||||
| 
 | ||||
|             const notifications = result.notifications | ||||
|                 .map((notification) => AddonNotificationsHelper.formatNotificationText(notification)); | ||||
| 
 | ||||
|             if (refresh) { | ||||
|                 this.notifications = notifications; | ||||
|             } else { | ||||
|                 this.notifications = this.notifications.concat(notifications); | ||||
|             } | ||||
|             this.canLoadMore = result.canLoadMore; | ||||
| 
 | ||||
|             await this.loadMarkAllAsReadButton(); | ||||
|             await this.fetchNotifications(true); | ||||
|         } catch (error) { | ||||
|             CoreDomUtils.showErrorModalDefault(error, 'addon.notifications.errorgetnotifications', true); | ||||
|             this.loadMoreError = true; // Set to prevent infinite calls with infinite-loading.
 | ||||
|         } finally { | ||||
|             this.notificationsLoaded = true; | ||||
|             CoreDomUtils.showErrorModalDefault(error, 'Error loading notifications'); | ||||
| 
 | ||||
|             this.notifications.reset(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Load a new batch of Notifications. | ||||
|      * | ||||
|      * @param complete Completion callback. | ||||
|      */ | ||||
|     async fetchMoreNotifications(complete: () => void): Promise<void> { | ||||
|         try { | ||||
|             await this.fetchNotifications(false); | ||||
|         } catch (error) { | ||||
|             CoreDomUtils.showErrorModalDefault(error, 'Error loading more notifications'); | ||||
| 
 | ||||
|             this.fetchMoreNotificationsFailed = true; | ||||
|         } | ||||
| 
 | ||||
|         complete(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Mark all notifications as read. | ||||
|      * | ||||
| @ -151,9 +177,6 @@ export class AddonNotificationsListPage implements OnInit, OnDestroy { | ||||
|             time: CoreTimeUtils.timestamp(), | ||||
|         }, CoreSites.getCurrentSiteId()); | ||||
| 
 | ||||
|         // All marked as read, refresh the list.
 | ||||
|         this.notificationsLoaded = false; | ||||
| 
 | ||||
|         await this.refreshNotifications(); | ||||
|     } | ||||
| 
 | ||||
| @ -179,38 +202,12 @@ export class AddonNotificationsListPage implements OnInit, OnDestroy { | ||||
|      * Refresh notifications. | ||||
|      * | ||||
|      * @param refresher Refresher. | ||||
|      * @return Promise<any> Promise resolved when done. | ||||
|      */ | ||||
|     async refreshNotifications(refresher?: IonRefresher): Promise<void> { | ||||
|         await CoreUtils.ignoreErrors(AddonNotifications.invalidateNotificationsList()); | ||||
|         await CoreUtils.ignoreErrors(this.fetchNotifications(true)); | ||||
| 
 | ||||
|         try { | ||||
|             await this.fetchNotifications(true); | ||||
|         } finally { | ||||
|             refresher?.complete(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Load more results. | ||||
|      * | ||||
|      * @param infiniteComplete Infinite scroll complete function. Only used from core-infinite-loading. | ||||
|      */ | ||||
|     async loadMoreNotifications(infiniteComplete?: () => void): Promise<void> { | ||||
|         try { | ||||
|             await this.fetchNotifications(); | ||||
|         } finally { | ||||
|             infiniteComplete?.(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Open Notification page. | ||||
|      * | ||||
|      * @param notification Notification to open. | ||||
|      */ | ||||
|     openNotification(notification: AddonNotificationsNotificationToRender): void { | ||||
|         CoreNavigator.navigate('../notification', { params: { notification } }); | ||||
|         refresher?.complete(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -224,7 +221,6 @@ export class AddonNotificationsListPage implements OnInit, OnDestroy { | ||||
|         } | ||||
| 
 | ||||
|         this.pendingRefresh = false; | ||||
|         this.notificationsLoaded = false; | ||||
| 
 | ||||
|         this.refreshNotifications(); | ||||
|     } | ||||
| @ -243,6 +239,7 @@ export class AddonNotificationsListPage implements OnInit, OnDestroy { | ||||
|         this.cronObserver?.off(); | ||||
|         this.readObserver?.off(); | ||||
|         this.pushObserver?.unsubscribe(); | ||||
|         this.notifications?.destroy(); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -8,7 +8,7 @@ | ||||
|         </ion-title> | ||||
|     </ion-toolbar> | ||||
| </ion-header> | ||||
| <ion-content> | ||||
| <ion-content [core-swipe-navigation]="notifications"> | ||||
|     <core-loading [hideUntil]="loaded"> | ||||
|         <div class="list-item-limited-width"> | ||||
| 
 | ||||
|  | ||||
| @ -12,18 +12,20 @@ | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { AddonsNotificationsNotificationsSource } from '@addons/notifications/classes/notifications-source'; | ||||
| import { AddonNotificationsNotificationData } from '@addons/notifications/services/handlers/push-click'; | ||||
| import { AddonNotifications } from '@addons/notifications/services/notifications'; | ||||
| import { | ||||
|     AddonNotificationsHelper, | ||||
|     AddonNotificationsNotificationToRender, | ||||
| } from '@addons/notifications/services/notifications-helper'; | ||||
| import { Component, OnInit } from '@angular/core'; | ||||
| import { Component, OnDestroy, OnInit } from '@angular/core'; | ||||
| import { ActivatedRouteSnapshot } from '@angular/router'; | ||||
| import { CoreRoutedItemsManagerSourcesTracker } from '@classes/items-management/routed-items-manager-sources-tracker'; | ||||
| import { CoreSwipeNavigationItemsManager } from '@classes/items-management/swipe-navigation-items-manager'; | ||||
| import { CoreContentLinksAction, CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate'; | ||||
| import { CoreNavigator } from '@services/navigator'; | ||||
| import { CoreSites } from '@services/sites'; | ||||
| import { CoreDomUtils } from '@services/utils/dom'; | ||||
| import { CoreUtils } from '@services/utils/utils'; | ||||
| 
 | ||||
| /** | ||||
|  * Page to render a notification. | ||||
| @ -33,8 +35,9 @@ import { CoreUtils } from '@services/utils/utils'; | ||||
|     templateUrl: 'notification.html', | ||||
|     styleUrls: ['../../notifications.scss', 'notification.scss'], | ||||
| }) | ||||
| export class AddonNotificationsNotificationPage implements OnInit { | ||||
| export class AddonNotificationsNotificationPage implements OnInit, OnDestroy { | ||||
| 
 | ||||
|     notifications?: AddonNotificationSwipeItemsManager; | ||||
|     subject = ''; // Notification subject.
 | ||||
|     content = ''; // Notification content.
 | ||||
|     userIdFrom = -1; // User ID who sent the notification.
 | ||||
| @ -58,28 +61,14 @@ export class AddonNotificationsNotificationPage implements OnInit { | ||||
|         let notification: AddonNotificationsNotification; | ||||
| 
 | ||||
|         try { | ||||
|             notification = CoreNavigator.getRequiredRouteParam('notification'); | ||||
|             notification = this.getNotification(); | ||||
|         } catch (error) { | ||||
|             CoreDomUtils.showErrorModal(error); | ||||
| 
 | ||||
|             CoreNavigator.back(); | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         if (!('subject' in notification)) { | ||||
|             // Try to find the notification using the WebService, it contains a better message.
 | ||||
|             const notifId = Number(notification.savedmessageid); | ||||
|             const result = await CoreUtils.ignoreErrors( | ||||
|                 AddonNotifications.getNotifications([], { siteId: notification.site }), | ||||
|             ); | ||||
| 
 | ||||
|             const foundNotification = result?.notifications.find(notif => notif.id === notifId); | ||||
|             if (foundNotification) { | ||||
|                 notification = AddonNotificationsHelper.formatNotificationText(foundNotification); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if ('subject' in notification) { | ||||
|             this.subject = notification.subject; | ||||
|             this.content = notification.mobiletext || notification.fullmessagehtml; | ||||
| @ -95,7 +84,6 @@ export class AddonNotificationsNotificationPage implements OnInit { | ||||
|                 } | ||||
|             } | ||||
|             this.timecreated = notification.timecreated; | ||||
| 
 | ||||
|         } else { | ||||
|             this.subject = notification.title || ''; | ||||
|             this.content = notification.message || ''; | ||||
| @ -110,6 +98,51 @@ export class AddonNotificationsNotificationPage implements OnInit { | ||||
|         this.loaded = true; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get notification. | ||||
|      * | ||||
|      * @returns notification. | ||||
|      */ | ||||
|     getNotification(): AddonNotificationsNotification { | ||||
|         const id = CoreNavigator.getRouteNumberParam('id'); | ||||
|         const notification = id ? this.getNotificationById(id) : undefined; | ||||
| 
 | ||||
|         return notification ?? CoreNavigator.getRequiredRouteParam('notification'); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Obtain notification by passed id. | ||||
|      * | ||||
|      * @param notificationId Notification id. | ||||
|      * @return Found notification. | ||||
|      */ | ||||
|     getNotificationById(notificationId: number): AddonNotificationsNotification | undefined { | ||||
|         const source = CoreRoutedItemsManagerSourcesTracker.getOrCreateSource( | ||||
|             AddonsNotificationsNotificationsSource, | ||||
|             [], | ||||
|         ); | ||||
|         const notification = source.getItems()?.find(({ id }) => id === notificationId); | ||||
| 
 | ||||
|         if (!notification) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         this.loadNotifications(source); | ||||
| 
 | ||||
|         return notification; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Load notifications from source. | ||||
|      * | ||||
|      * @param source Notifications source | ||||
|      */ | ||||
|     async loadNotifications(source: AddonsNotificationsNotificationsSource): Promise<void> { | ||||
|         this.notifications = new AddonNotificationSwipeItemsManager(source); | ||||
| 
 | ||||
|         await this.notifications.start(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Load notification actions | ||||
|      * | ||||
| @ -171,6 +204,27 @@ export class AddonNotificationsNotificationPage implements OnInit { | ||||
|         site.openInBrowserWithAutoLogin(url); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     ngOnDestroy(): void { | ||||
|         this.notifications?.destroy(); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Helper to manage swiping within a collection of notifications. | ||||
|  */ | ||||
| class AddonNotificationSwipeItemsManager extends CoreSwipeNavigationItemsManager { | ||||
| 
 | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     protected getSelectedItemPathFromRoute(route: ActivatedRouteSnapshot): string | null { | ||||
|         return route.params.id; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| type AddonNotificationsNotification = AddonNotificationsNotificationToRender | AddonNotificationsNotificationData; | ||||
|  | ||||
| @ -12,6 +12,7 @@ | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { CorePushNotifications, CorePushNotificationsProvider } from '@features/pushnotifications/services/pushnotifications'; | ||||
| import { CoreApp, CoreAppProvider } from '@services/app'; | ||||
| import { CoreConfig, CoreConfigProvider } from '@services/config'; | ||||
| import { CoreDB, CoreDbProvider } from '@services/db'; | ||||
| @ -25,6 +26,7 @@ type DevelopmentWindow = Window & { | ||||
|     configProvider?: CoreConfigProvider; | ||||
|     dbProvider?: CoreDbProvider; | ||||
|     urlSchemes?: CoreCustomURLSchemesProvider; | ||||
|     pushNotifications?: CorePushNotificationsProvider; | ||||
| }; | ||||
| 
 | ||||
| function initializeDevelopmentWindow(window: DevelopmentWindow) { | ||||
| @ -33,6 +35,7 @@ function initializeDevelopmentWindow(window: DevelopmentWindow) { | ||||
|     window.configProvider = CoreConfig.instance; | ||||
|     window.dbProvider = CoreDB.instance; | ||||
|     window.urlSchemes = CoreCustomURLSchemes.instance; | ||||
|     window.pushNotifications = CorePushNotifications.instance; | ||||
| } | ||||
| 
 | ||||
| export default function(): void { | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user