MOBILE-4021 notification: add split-view
parent
b67d27fc0e
commit
fa4ad175cc
|
@ -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
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { conditionalRoutes } from '@/app/app-routing.module';
|
||||||
import { Injector, NgModule } from '@angular/core';
|
import { Injector, NgModule } from '@angular/core';
|
||||||
import { RouterModule, ROUTES, Routes } from '@angular/router';
|
import { RouterModule, ROUTES, Routes } from '@angular/router';
|
||||||
|
|
||||||
import { buildTabMainRoutes } from '@features/mainmenu/mainmenu-tab-routing.module';
|
import { buildTabMainRoutes } from '@features/mainmenu/mainmenu-tab-routing.module';
|
||||||
|
import { CoreScreen } from '@services/screen';
|
||||||
import { AddonNotificationsMainMenuHandlerService } from './services/handlers/mainmenu';
|
import { AddonNotificationsMainMenuHandlerService } from './services/handlers/mainmenu';
|
||||||
|
|
||||||
function buildRoutes(injector: Injector): Routes {
|
function buildRoutes(injector: Injector): Routes {
|
||||||
|
@ -27,6 +29,13 @@ function buildRoutes(injector: Injector): Routes {
|
||||||
},
|
},
|
||||||
loadChildren: () => import('./pages/list/list.module').then(m => m.AddonNotificationsListPageModule),
|
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',
|
path: 'notification',
|
||||||
loadChildren: () => import('./pages/notification/notification.module')
|
loadChildren: () => import('./pages/notification/notification.module')
|
||||||
|
|
|
@ -11,66 +11,72 @@
|
||||||
</ion-buttons>
|
</ion-buttons>
|
||||||
</ion-toolbar>
|
</ion-toolbar>
|
||||||
</ion-header>
|
</ion-header>
|
||||||
<ion-content class="limited-width">
|
<ion-content>
|
||||||
<ion-refresher slot="fixed" [disabled]="!notificationsLoaded" (ionRefresh)="refreshNotifications($event.target)">
|
<core-split-view>
|
||||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
<ion-refresher slot="fixed" [disabled]="!notifications.loaded" (ionRefresh)="refreshNotifications($event.target)">
|
||||||
</ion-refresher>
|
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||||
<core-loading [hideUntil]="notificationsLoaded">
|
</ion-refresher>
|
||||||
|
<core-loading [hideUntil]="notifications.loaded">
|
||||||
|
|
||||||
<ion-item *ngFor="let notification of notifications" class="ion-text-wrap" [attr.aria-label]="
|
<ion-item *ngFor="let notification of notifications.items" class="ion-text-wrap"
|
||||||
notification.timeread
|
[attr.aria-current]="notifications.getItemAriaCurrent(notification)" [attr.aria-label]="
|
||||||
? notification.subject
|
notification.timeread
|
||||||
: 'addon.notifications.unreadnotification' | translate: {$a: notification.subject}"
|
? notification.subject
|
||||||
(click)="openNotification(notification)" button [detail]="false" lines="full">
|
: 'addon.notifications.unreadnotification' | translate: {$a: notification.subject}"
|
||||||
<core-user-avatar *ngIf="notification.useridfrom > 0" [user]="notification" slot="start"
|
(click)="notifications.select(notification)" button [detail]="false" lines="full">
|
||||||
[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>
|
|
||||||
|
|
||||||
<ng-container *ngIf="notification.useridfrom <= 0 && notification.iconurl">
|
<core-user-avatar *ngIf="notification.useridfrom > 0" [user]="notification" slot="start"
|
||||||
<div class="core-notification-icon" *ngIf="!notification.modname" slot="start">
|
[profileUrl]="notification.profileimageurlfrom" [fullname]="notification.userfromfullname"
|
||||||
<img [src]="notification.iconurl" alt="" role="presentation">
|
[userId]="notification.useridfrom">
|
||||||
</div>
|
<div class="core-avatar-extra-img" *ngIf="notification.iconurl && !notification.modname">
|
||||||
<core-mod-icon *ngIf="notification.modname" [modicon]="notification.iconurl" [modname]="notification.modname"
|
<img [src]="notification.iconurl" alt="" role="presentation">
|
||||||
[showAlt]="false" class="core-notification-icon" slot="start">
|
</div>
|
||||||
</core-mod-icon>
|
<core-mod-icon *ngIf="notification.modname" [modicon]="notification.iconurl" [modname]="notification.modname"
|
||||||
</ng-container>
|
[showAlt]="false">
|
||||||
|
</core-mod-icon>
|
||||||
|
</core-user-avatar>
|
||||||
|
|
||||||
<ion-label>
|
<ng-container *ngIf="notification.useridfrom <= 0 && notification.iconurl">
|
||||||
<p class="item-heading">
|
<div class="core-notification-icon" *ngIf="!notification.modname" slot="start">
|
||||||
<core-format-text [text]="notification.subject" contextLevel="system" [contextInstanceId]="0" [wsNotFiltered]="true">
|
<img [src]="notification.iconurl" alt="" role="presentation">
|
||||||
</core-format-text>
|
</div>
|
||||||
</p>
|
<core-mod-icon *ngIf="notification.modname" [modicon]="notification.iconurl" [modname]="notification.modname"
|
||||||
<p>{{ notification.timecreated | coreTimeAgo }}<ng-container *ngIf="notification.useridfrom > 0"> · {{
|
[showAlt]="false" class="core-notification-icon" slot="start">
|
||||||
notification.userfromfullname }}</ng-container>
|
</core-mod-icon>
|
||||||
</p>
|
</ng-container>
|
||||||
</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.length <= 0" icon="far-bell"
|
<ion-label>
|
||||||
[message]="'addon.notifications.therearentnotificationsyet' | translate">
|
<p class="item-heading">
|
||||||
</core-empty-box>
|
<core-format-text [text]="notification.subject" contextLevel="system" [contextInstanceId]="0"
|
||||||
<core-infinite-loading [enabled]="canLoadMore" (action)="loadMoreNotifications($event)" [error]="loadMoreError">
|
[wsNotFiltered]="true">
|
||||||
</core-infinite-loading>
|
</core-format-text>
|
||||||
</core-loading>
|
</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>
|
<div class="mark-all-as-read" slot="fixed" collapsible-footer appearOnBottom>
|
||||||
<ion-chip *ngIf="notificationsLoaded && canMarkAllNotificationsAsRead" [disabled]="loadingMarkAllNotificationsAsRead"
|
<ion-chip *ngIf="notifications.loaded && canMarkAllNotificationsAsRead" [disabled]="loadingMarkAllNotificationsAsRead"
|
||||||
color="primary" (click)="markAllNotificationsAsRead()">
|
color="primary" (click)="markAllNotificationsAsRead()">
|
||||||
<ion-icon name="fas-eye" aria-hidden="true" *ngIf="!loadingMarkAllNotificationsAsRead"></ion-icon>
|
<ion-icon name="fas-eye" aria-hidden="true" *ngIf="!loadingMarkAllNotificationsAsRead"></ion-icon>
|
||||||
<ion-spinner [attr.aria-label]="'core.loading' | translate" *ngIf="loadingMarkAllNotificationsAsRead">
|
<ion-spinner [attr.aria-label]="'core.loading' | translate" *ngIf="loadingMarkAllNotificationsAsRead">
|
||||||
</ion-spinner>
|
</ion-spinner>
|
||||||
{{ 'addon.notifications.markallread' | translate }}
|
{{ 'addon.notifications.markallread' | translate }}
|
||||||
</ion-chip>
|
</ion-chip>
|
||||||
</div>
|
</div>
|
||||||
|
</core-split-view>
|
||||||
</ion-content>
|
</ion-content>
|
||||||
|
|
|
@ -18,11 +18,20 @@ import { RouterModule, Routes } from '@angular/router';
|
||||||
import { CoreSharedModule } from '@/core/shared.module';
|
import { CoreSharedModule } from '@/core/shared.module';
|
||||||
import { AddonNotificationsListPage } from './list';
|
import { AddonNotificationsListPage } from './list';
|
||||||
import { CoreMainMenuComponentsModule } from '@features/mainmenu/components/components.module';
|
import { CoreMainMenuComponentsModule } from '@features/mainmenu/components/components.module';
|
||||||
|
import { conditionalRoutes } from '@/app/app-routing.module';
|
||||||
|
import { CoreScreen } from '@services/screen';
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
component: AddonNotificationsListPage,
|
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
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// 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 { IonRefresher } from '@ionic/angular';
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
|
|
||||||
import { CoreSites } from '@services/sites';
|
|
||||||
import { CoreDomUtils } from '@services/utils/dom';
|
import { CoreDomUtils } from '@services/utils/dom';
|
||||||
import { CoreUtils } from '@services/utils/utils';
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
import { CoreEvents, CoreEventObserver } from '@singletons/events';
|
import { CoreEventObserver, CoreEvents } from '@singletons/events';
|
||||||
import {
|
import {
|
||||||
AddonNotifications,
|
AddonNotifications, AddonNotificationsProvider,
|
||||||
AddonNotificationsProvider,
|
|
||||||
} from '../../services/notifications';
|
} 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 { 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 { 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.
|
* Page that displays the list of notifications.
|
||||||
|
@ -41,12 +41,11 @@ import { CoreTimeUtils } from '@services/utils/time';
|
||||||
templateUrl: 'list.html',
|
templateUrl: 'list.html',
|
||||||
styleUrls: ['list.scss', '../../notifications.scss'],
|
styleUrls: ['list.scss', '../../notifications.scss'],
|
||||||
})
|
})
|
||||||
export class AddonNotificationsListPage implements OnInit, OnDestroy {
|
export class AddonNotificationsListPage implements AfterViewInit, OnDestroy {
|
||||||
|
|
||||||
notifications: AddonNotificationsNotificationToRender[] = [];
|
@ViewChild(CoreSplitViewComponent) splitView!: CoreSplitViewComponent;
|
||||||
notificationsLoaded = false;
|
notifications!: CoreListItemsManager<AddonNotificationsNotificationToRender, AddonsNotificationsNotificationsSource>;
|
||||||
canLoadMore = false;
|
fetchMoreNotificationsFailed = false;
|
||||||
loadMoreError = false;
|
|
||||||
canMarkAllNotificationsAsRead = false;
|
canMarkAllNotificationsAsRead = false;
|
||||||
loadingMarkAllNotificationsAsRead = false;
|
loadingMarkAllNotificationsAsRead = false;
|
||||||
|
|
||||||
|
@ -56,18 +55,33 @@ export class AddonNotificationsListPage implements OnInit, OnDestroy {
|
||||||
protected pushObserver?: Subscription;
|
protected pushObserver?: Subscription;
|
||||||
protected pendingRefresh = false;
|
protected pendingRefresh = false;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
try {
|
||||||
|
const source = CoreRoutedItemsManagerSourcesTracker.getOrCreateSource(
|
||||||
|
AddonsNotificationsNotificationsSource,
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
this.notifications = new CoreListItemsManager(source, AddonNotificationsListPage);
|
||||||
|
} catch(error) {
|
||||||
|
CoreDomUtils.showErrorModal(error);
|
||||||
|
CoreNavigator.back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
ngOnInit(): void {
|
async ngAfterViewInit(): Promise<void> {
|
||||||
this.fetchNotifications();
|
await this.fetchInitialNotifications();
|
||||||
|
|
||||||
|
this.notifications.start(this.splitView);
|
||||||
|
|
||||||
this.cronObserver = CoreEvents.on(AddonNotificationsProvider.READ_CRON_EVENT, () => {
|
this.cronObserver = CoreEvents.on(AddonNotificationsProvider.READ_CRON_EVENT, () => {
|
||||||
if (!this.isCurrentView) {
|
if (!this.isCurrentView) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.notificationsLoaded = false;
|
|
||||||
this.refreshNotifications();
|
this.refreshNotifications();
|
||||||
}, CoreSites.getCurrentSiteId());
|
}, CoreSites.getCurrentSiteId());
|
||||||
|
|
||||||
|
@ -83,7 +97,6 @@ export class AddonNotificationsListPage implements OnInit, OnDestroy {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.notificationsLoaded = false;
|
|
||||||
this.refreshNotifications();
|
this.refreshNotifications();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -92,7 +105,7 @@ export class AddonNotificationsListPage implements OnInit, OnDestroy {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const notification = this.notifications.find((notification) => notification.id === data.id);
|
const notification = this.notifications.items.find((notification) => notification.id === data.id);
|
||||||
if (!notification) {
|
if (!notification) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -109,34 +122,47 @@ export class AddonNotificationsListPage implements OnInit, OnDestroy {
|
||||||
/**
|
/**
|
||||||
* Convenience function to get notifications. Gets unread notifications first.
|
* Convenience function to get notifications. Gets unread notifications first.
|
||||||
*
|
*
|
||||||
* @param refresh Whether we're refreshing data.
|
* @param reload Whether to reload the list or load the next page.
|
||||||
* @return Resolved when done.
|
|
||||||
*/
|
*/
|
||||||
protected async fetchNotifications(refresh?: boolean): Promise<void> {
|
protected async fetchNotifications(reload: boolean): Promise<void> {
|
||||||
this.loadMoreError = false;
|
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 {
|
try {
|
||||||
const result = await AddonNotifications.getNotifications(refresh ? [] : this.notifications);
|
await this.fetchNotifications(true);
|
||||||
|
|
||||||
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();
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
CoreDomUtils.showErrorModalDefault(error, 'addon.notifications.errorgetnotifications', true);
|
CoreDomUtils.showErrorModalDefault(error, 'Error loading notifications');
|
||||||
this.loadMoreError = true; // Set to prevent infinite calls with infinite-loading.
|
|
||||||
} finally {
|
this.notifications.reset();
|
||||||
this.notificationsLoaded = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
* Mark all notifications as read.
|
||||||
*
|
*
|
||||||
|
@ -151,9 +177,6 @@ export class AddonNotificationsListPage implements OnInit, OnDestroy {
|
||||||
time: CoreTimeUtils.timestamp(),
|
time: CoreTimeUtils.timestamp(),
|
||||||
}, CoreSites.getCurrentSiteId());
|
}, CoreSites.getCurrentSiteId());
|
||||||
|
|
||||||
// All marked as read, refresh the list.
|
|
||||||
this.notificationsLoaded = false;
|
|
||||||
|
|
||||||
await this.refreshNotifications();
|
await this.refreshNotifications();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,38 +202,12 @@ export class AddonNotificationsListPage implements OnInit, OnDestroy {
|
||||||
* Refresh notifications.
|
* Refresh notifications.
|
||||||
*
|
*
|
||||||
* @param refresher Refresher.
|
* @param refresher Refresher.
|
||||||
* @return Promise<any> Promise resolved when done.
|
|
||||||
*/
|
*/
|
||||||
async refreshNotifications(refresher?: IonRefresher): Promise<void> {
|
async refreshNotifications(refresher?: IonRefresher): Promise<void> {
|
||||||
await CoreUtils.ignoreErrors(AddonNotifications.invalidateNotificationsList());
|
await CoreUtils.ignoreErrors(AddonNotifications.invalidateNotificationsList());
|
||||||
|
await CoreUtils.ignoreErrors(this.fetchNotifications(true));
|
||||||
|
|
||||||
try {
|
refresher?.complete();
|
||||||
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 } });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -224,7 +221,6 @@ export class AddonNotificationsListPage implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.pendingRefresh = false;
|
this.pendingRefresh = false;
|
||||||
this.notificationsLoaded = false;
|
|
||||||
|
|
||||||
this.refreshNotifications();
|
this.refreshNotifications();
|
||||||
}
|
}
|
||||||
|
@ -243,6 +239,7 @@ export class AddonNotificationsListPage implements OnInit, OnDestroy {
|
||||||
this.cronObserver?.off();
|
this.cronObserver?.off();
|
||||||
this.readObserver?.off();
|
this.readObserver?.off();
|
||||||
this.pushObserver?.unsubscribe();
|
this.pushObserver?.unsubscribe();
|
||||||
|
this.notifications?.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,18 +12,19 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { AddonsNotificationsNotificationsSource } from '@addons/notifications/classes/notifications-source';
|
||||||
import { AddonNotificationsNotificationData } from '@addons/notifications/services/handlers/push-click';
|
import { AddonNotificationsNotificationData } from '@addons/notifications/services/handlers/push-click';
|
||||||
import { AddonNotifications } from '@addons/notifications/services/notifications';
|
|
||||||
import {
|
import {
|
||||||
AddonNotificationsHelper,
|
AddonNotificationsHelper,
|
||||||
AddonNotificationsNotificationToRender,
|
AddonNotificationsNotificationToRender,
|
||||||
} from '@addons/notifications/services/notifications-helper';
|
} from '@addons/notifications/services/notifications-helper';
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { CoreError } from '@classes/errors/error';
|
||||||
|
import { CoreRoutedItemsManagerSourcesTracker } from '@classes/items-management/routed-items-manager-sources-tracker';
|
||||||
import { CoreContentLinksAction, CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate';
|
import { CoreContentLinksAction, CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate';
|
||||||
import { CoreNavigator } from '@services/navigator';
|
import { CoreNavigator } from '@services/navigator';
|
||||||
import { CoreSites } from '@services/sites';
|
import { CoreSites } from '@services/sites';
|
||||||
import { CoreDomUtils } from '@services/utils/dom';
|
import { CoreDomUtils } from '@services/utils/dom';
|
||||||
import { CoreUtils } from '@services/utils/utils';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Page to render a notification.
|
* Page to render a notification.
|
||||||
|
@ -58,28 +59,14 @@ export class AddonNotificationsNotificationPage implements OnInit {
|
||||||
let notification: AddonNotificationsNotification;
|
let notification: AddonNotificationsNotification;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
notification = CoreNavigator.getRequiredRouteParam('notification');
|
notification = this.getCurrentNotification();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
CoreDomUtils.showErrorModal(error);
|
await CoreDomUtils.showErrorModal(error);
|
||||||
|
await CoreNavigator.back();
|
||||||
CoreNavigator.back();
|
|
||||||
|
|
||||||
return;
|
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) {
|
if ('subject' in notification) {
|
||||||
this.subject = notification.subject;
|
this.subject = notification.subject;
|
||||||
this.content = notification.mobiletext || notification.fullmessagehtml;
|
this.content = notification.mobiletext || notification.fullmessagehtml;
|
||||||
|
@ -89,8 +76,10 @@ export class AddonNotificationsNotificationPage implements OnInit {
|
||||||
this.iconUrl = notification.iconurl;
|
this.iconUrl = notification.iconurl;
|
||||||
if (notification.moodlecomponent?.startsWith('mod_') && notification.iconurl) {
|
if (notification.moodlecomponent?.startsWith('mod_') && notification.iconurl) {
|
||||||
const modname = notification.moodlecomponent.substring(4);
|
const modname = notification.moodlecomponent.substring(4);
|
||||||
if (notification.iconurl.match('/theme/image.php/[^/]+/' + modname + '/[-0-9]*/') ||
|
if (
|
||||||
notification.iconurl.match('/theme/image.php/[^/]+/' + notification.moodlecomponent + '/[-0-9]*/')) {
|
notification.iconurl.match('/theme/image.php/[^/]+/' + modname + '/[-0-9]*/') ||
|
||||||
|
notification.iconurl.match('/theme/image.php/[^/]+/' + notification.moodlecomponent + '/[-0-9]*/')
|
||||||
|
) {
|
||||||
this.modname = modname;
|
this.modname = modname;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,6 +99,39 @@ export class AddonNotificationsNotificationPage implements OnInit {
|
||||||
this.loaded = true;
|
this.loaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load notifications
|
||||||
|
*
|
||||||
|
* @param notificationId Notification id.
|
||||||
|
* @return Found notification
|
||||||
|
*/
|
||||||
|
loadNotifications(notificationId: number): AddonNotificationsNotification {
|
||||||
|
const source = CoreRoutedItemsManagerSourcesTracker.getOrCreateSource(
|
||||||
|
AddonsNotificationsNotificationsSource,
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
const notification = source.getItems()?.find(({ id }) => id === notificationId);
|
||||||
|
|
||||||
|
if (!notification) {
|
||||||
|
throw new CoreError(`Notification with id ${notificationId} not found`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return notification;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load current notification if it's found
|
||||||
|
*
|
||||||
|
* @return Found notification
|
||||||
|
*/
|
||||||
|
getCurrentNotification(): AddonNotificationsNotification {
|
||||||
|
const pushNotification: AddonNotificationsNotificationData | undefined = CoreNavigator.getRouteParam('notification');
|
||||||
|
|
||||||
|
return this.loadNotifications(
|
||||||
|
pushNotification ? Number(pushNotification?.savedmessageid) : CoreNavigator.getRequiredRouteNumberParam('id'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load notification actions
|
* Load notification actions
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue