MOBILE-3829 notifications: Display notification clicked in new page
parent
1ffe48c915
commit
7ee3324455
|
@ -27,6 +27,11 @@ 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),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'notification',
|
||||||
|
loadChildren: () => import('./pages/notification/notification.module')
|
||||||
|
.then(m => m.AddonNotificationsNotificationPageModule),
|
||||||
|
},
|
||||||
...buildTabMainRoutes(injector, {
|
...buildTabMainRoutes(injector, {
|
||||||
redirectTo: 'list',
|
redirectTo: 'list',
|
||||||
pathMatch: 'full',
|
pathMatch: 'full',
|
||||||
|
|
|
@ -18,15 +18,17 @@ import { Subscription } from 'rxjs';
|
||||||
|
|
||||||
import { CoreSites } from '@services/sites';
|
import { CoreSites } from '@services/sites';
|
||||||
import { CoreDomUtils } from '@services/utils/dom';
|
import { CoreDomUtils } from '@services/utils/dom';
|
||||||
import { CoreTextUtils } from '@services/utils/text';
|
|
||||||
import { CoreUtils } from '@services/utils/utils';
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
import { CoreEvents, CoreEventObserver } from '@singletons/events';
|
import { CoreEvents, CoreEventObserver } from '@singletons/events';
|
||||||
import {
|
import {
|
||||||
AddonNotifications,
|
AddonNotifications,
|
||||||
AddonNotificationsNotificationMessageFormatted,
|
|
||||||
AddonNotificationsProvider,
|
AddonNotificationsProvider,
|
||||||
} from '../../services/notifications';
|
} from '../../services/notifications';
|
||||||
import { CorePushNotificationsDelegate } from '@features/pushnotifications/services/push-delegate';
|
import { CorePushNotificationsDelegate } from '@features/pushnotifications/services/push-delegate';
|
||||||
|
import {
|
||||||
|
AddonNotificationsHelper,
|
||||||
|
AddonNotificationsNotificationToRender,
|
||||||
|
} from '@addons/notifications/services/notifications-helper';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Page that displays the list of notifications.
|
* Page that displays the list of notifications.
|
||||||
|
@ -38,7 +40,7 @@ import { CorePushNotificationsDelegate } from '@features/pushnotifications/servi
|
||||||
})
|
})
|
||||||
export class AddonNotificationsListPage implements OnInit, OnDestroy {
|
export class AddonNotificationsListPage implements OnInit, OnDestroy {
|
||||||
|
|
||||||
notifications: FormattedNotification[] = [];
|
notifications: AddonNotificationsNotificationToRender[] = [];
|
||||||
notificationsLoaded = false;
|
notificationsLoaded = false;
|
||||||
canLoadMore = false;
|
canLoadMore = false;
|
||||||
loadMoreError = false;
|
loadMoreError = false;
|
||||||
|
@ -94,7 +96,8 @@ export class AddonNotificationsListPage implements OnInit, OnDestroy {
|
||||||
try {
|
try {
|
||||||
const result = await AddonNotifications.getNotifications(refresh ? [] : this.notifications);
|
const result = await AddonNotifications.getNotifications(refresh ? [] : this.notifications);
|
||||||
|
|
||||||
const notifications = result.notifications.map((notification) => this.formatText(notification));
|
const notifications = result.notifications
|
||||||
|
.map((notification) => AddonNotificationsHelper.formatNotificationText(notification));
|
||||||
|
|
||||||
if (refresh) {
|
if (refresh) {
|
||||||
this.notifications = notifications;
|
this.notifications = notifications;
|
||||||
|
@ -135,7 +138,7 @@ export class AddonNotificationsListPage implements OnInit, OnDestroy {
|
||||||
*
|
*
|
||||||
* @param notifications Array of notification objects.
|
* @param notifications Array of notification objects.
|
||||||
*/
|
*/
|
||||||
protected async markNotificationsAsRead(notifications: FormattedNotification[]): Promise<void> {
|
protected async markNotificationsAsRead(notifications: AddonNotificationsNotificationToRender[]): Promise<void> {
|
||||||
if (notifications.length > 0) {
|
if (notifications.length > 0) {
|
||||||
const promises = notifications.map(async (notification) => {
|
const promises = notifications.map(async (notification) => {
|
||||||
if (notification.read) {
|
if (notification.read) {
|
||||||
|
@ -194,33 +197,6 @@ export class AddonNotificationsListPage implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Formats the text of a notification.
|
|
||||||
*
|
|
||||||
* @param notification The notification object.
|
|
||||||
*/
|
|
||||||
protected formatText(notification: AddonNotificationsNotificationMessageFormatted): FormattedNotification {
|
|
||||||
const formattedNotification: FormattedNotification = notification;
|
|
||||||
formattedNotification.displayfullhtml = this.shouldDisplayFullHtml(notification);
|
|
||||||
formattedNotification.iconurl = formattedNotification.iconurl || undefined; // Make sure the property exists.
|
|
||||||
|
|
||||||
formattedNotification.mobiletext = formattedNotification.displayfullhtml ?
|
|
||||||
notification.fullmessagehtml :
|
|
||||||
CoreTextUtils.replaceNewLines((formattedNotification.mobiletext || '').replace(/-{4,}/ig, ''), '<br>');
|
|
||||||
|
|
||||||
return formattedNotification;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check whether we should display full HTML of the notification.
|
|
||||||
*
|
|
||||||
* @param notification Notification.
|
|
||||||
* @return Whether to display full HTML.
|
|
||||||
*/
|
|
||||||
protected shouldDisplayFullHtml(notification: FormattedNotification): boolean {
|
|
||||||
return notification.component == 'mod_forum' && notification.eventtype == 'digests';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User entered the page.
|
* User entered the page.
|
||||||
*/
|
*/
|
||||||
|
@ -253,8 +229,3 @@ export class AddonNotificationsListPage implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type FormattedNotification = AddonNotificationsNotificationMessageFormatted & {
|
|
||||||
displayfullhtml?: boolean; // Whether to display the full HTML of the notification.
|
|
||||||
iconurl?: string;
|
|
||||||
};
|
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
<ion-header>
|
||||||
|
<ion-toolbar>
|
||||||
|
<ion-buttons slot="start">
|
||||||
|
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
|
||||||
|
</ion-buttons>
|
||||||
|
<h2>{{ 'addon.notifications.notifications' | translate }}</h2>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-header>
|
||||||
|
<ion-content>
|
||||||
|
<core-loading [hideUntil]="loaded">
|
||||||
|
<ion-item class="ion-text-wrap" lines="none" [attr.aria-label]="subject">
|
||||||
|
<core-user-avatar *ngIf="userIdFrom > 0" slot="start" [userId]="userIdFrom" [profileUrl]="profileImageUrlFrom"
|
||||||
|
[fullname]="userFromFullName" [extraIcon]="iconUrl"></core-user-avatar>
|
||||||
|
|
||||||
|
<img *ngIf="userIdFrom <= 0 && iconUrl" [src]="iconUrl" alt=""
|
||||||
|
role="presentation" class="core-notification-icon" slot="start">
|
||||||
|
|
||||||
|
<ion-label>
|
||||||
|
<p class="item-heading">{{ subject }}</p>
|
||||||
|
<p *ngIf="userIdFrom > 0">{{ userFromFullName }}</p>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
<ion-item class="ion-text-wrap">
|
||||||
|
<ion-label>
|
||||||
|
<core-format-text [text]="content | coreCreateLinks" contextLevel="system" [contextInstanceId]="0">
|
||||||
|
</core-format-text>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
</core-loading>
|
||||||
|
</ion-content>
|
|
@ -0,0 +1,40 @@
|
||||||
|
// (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 { RouterModule, Routes } from '@angular/router';
|
||||||
|
|
||||||
|
import { CoreSharedModule } from '@/core/shared.module';
|
||||||
|
import { AddonNotificationsComponentsModule } from '../../components/components.module';
|
||||||
|
import { AddonNotificationsNotificationPage } from './notification';
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: AddonNotificationsNotificationPage,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
RouterModule.forChild(routes),
|
||||||
|
CoreSharedModule,
|
||||||
|
AddonNotificationsComponentsModule,
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
AddonNotificationsNotificationPage,
|
||||||
|
],
|
||||||
|
exports: [RouterModule],
|
||||||
|
})
|
||||||
|
export class AddonNotificationsNotificationPageModule {}
|
|
@ -0,0 +1,90 @@
|
||||||
|
// (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 { 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 { CoreNavigator } from '@services/navigator';
|
||||||
|
import { CoreDomUtils } from '@services/utils/dom';
|
||||||
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Page to render a notification.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'page-addon-notifications-notification',
|
||||||
|
templateUrl: 'notification.html',
|
||||||
|
})
|
||||||
|
export class AddonNotificationsNotificationPage implements OnInit {
|
||||||
|
|
||||||
|
subject = ''; // Notification subject.
|
||||||
|
content = ''; // Notification content.
|
||||||
|
userIdFrom = -1; // User ID who sent the notification.
|
||||||
|
profileImageUrlFrom?: string; // Avatar of the user who sent the notification.
|
||||||
|
userFromFullName?: string; // Name of the user who sent the notification.
|
||||||
|
iconUrl?: string; // Icon URL.
|
||||||
|
loaded = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async ngOnInit(): Promise<void> {
|
||||||
|
let notification: AddonNotificationsNotificationToRender | AddonNotificationsNotificationData;
|
||||||
|
|
||||||
|
try {
|
||||||
|
notification = CoreNavigator.getRequiredRouteParam('notification');
|
||||||
|
} 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;
|
||||||
|
this.userIdFrom = notification.useridfrom;
|
||||||
|
this.profileImageUrlFrom = notification.profileimageurlfrom;
|
||||||
|
this.userFromFullName = notification.userfromfullname;
|
||||||
|
this.iconUrl = notification.iconurl;
|
||||||
|
} else {
|
||||||
|
this.subject = notification.title || '';
|
||||||
|
this.content = notification.message || '';
|
||||||
|
this.userIdFrom = notification.userfromid ? Number(notification.userfromid) : -1;
|
||||||
|
this.profileImageUrlFrom = notification.senderImage;
|
||||||
|
this.userFromFullName = notification.userfromfullname;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -127,10 +127,16 @@ export class AddonNotificationsPushClickHandlerService implements CorePushNotifi
|
||||||
await CoreUtils.ignoreErrors(AddonNotifications.invalidateNotificationsList(notification.site));
|
await CoreUtils.ignoreErrors(AddonNotifications.invalidateNotificationsList(notification.site));
|
||||||
|
|
||||||
await CoreNavigator.navigateToSitePath(
|
await CoreNavigator.navigateToSitePath(
|
||||||
AddonNotificationsMainMenuHandlerService.PAGE_NAME,
|
`${AddonNotificationsMainMenuHandlerService.PAGE_NAME}/list`,
|
||||||
{
|
{
|
||||||
siteId: notification.site,
|
siteId: notification.site,
|
||||||
preferCurrentTab: false,
|
preferCurrentTab: false,
|
||||||
|
nextNavigation: {
|
||||||
|
path: '../notification',
|
||||||
|
options: {
|
||||||
|
params: { notification },
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -139,7 +145,7 @@ export class AddonNotificationsPushClickHandlerService implements CorePushNotifi
|
||||||
|
|
||||||
export const AddonNotificationsPushClickHandler = makeSingleton(AddonNotificationsPushClickHandlerService);
|
export const AddonNotificationsPushClickHandler = makeSingleton(AddonNotificationsPushClickHandlerService);
|
||||||
|
|
||||||
type AddonNotificationsNotificationData = CorePushNotificationsNotificationBasicData & {
|
export type AddonNotificationsNotificationData = CorePushNotificationsNotificationBasicData & {
|
||||||
contexturl?: string; // URL related to the notification.
|
contexturl?: string; // URL related to the notification.
|
||||||
savedmessageid?: number; // Notification ID (optional).
|
savedmessageid?: number; // Notification ID (optional).
|
||||||
id?: number; // Notification ID (optional).
|
id?: number; // Notification ID (optional).
|
||||||
|
|
|
@ -18,12 +18,14 @@ import { CoreUtils } from '@services/utils/utils';
|
||||||
import { makeSingleton } from '@singletons';
|
import { makeSingleton } from '@singletons';
|
||||||
import { AddonMessageOutputDelegate } from '@addons/messageoutput/services/messageoutput-delegate';
|
import { AddonMessageOutputDelegate } from '@addons/messageoutput/services/messageoutput-delegate';
|
||||||
import {
|
import {
|
||||||
|
AddonNotificationsNotificationMessageFormatted,
|
||||||
AddonNotificationsPreferences,
|
AddonNotificationsPreferences,
|
||||||
AddonNotificationsPreferencesComponent,
|
AddonNotificationsPreferencesComponent,
|
||||||
AddonNotificationsPreferencesNotification,
|
AddonNotificationsPreferencesNotification,
|
||||||
AddonNotificationsPreferencesNotificationProcessor,
|
AddonNotificationsPreferencesNotificationProcessor,
|
||||||
AddonNotificationsPreferencesProcessor,
|
AddonNotificationsPreferencesProcessor,
|
||||||
} from './notifications';
|
} from './notifications';
|
||||||
|
import { CoreTextUtils } from '@services/utils/text';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service that provides some helper functions for notifications.
|
* Service that provides some helper functions for notifications.
|
||||||
|
@ -31,6 +33,25 @@ import {
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class AddonNotificationsHelperProvider {
|
export class AddonNotificationsHelperProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats the text of a notification.
|
||||||
|
*
|
||||||
|
* @param notification The notification object.
|
||||||
|
*/
|
||||||
|
formatNotificationText(
|
||||||
|
notification: AddonNotificationsNotificationMessageFormatted,
|
||||||
|
): AddonNotificationsNotificationToRender {
|
||||||
|
const formattedNotification: AddonNotificationsNotificationToRender = notification;
|
||||||
|
formattedNotification.displayfullhtml = this.shouldDisplayFullHtml(notification);
|
||||||
|
formattedNotification.iconurl = formattedNotification.iconurl || undefined; // Make sure the property exists.
|
||||||
|
|
||||||
|
formattedNotification.mobiletext = formattedNotification.displayfullhtml ?
|
||||||
|
notification.fullmessagehtml :
|
||||||
|
CoreTextUtils.replaceNewLines((formattedNotification.mobiletext || '').replace(/-{4,}/ig, ''), '<br>');
|
||||||
|
|
||||||
|
return formattedNotification;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format preferences data.
|
* Format preferences data.
|
||||||
*
|
*
|
||||||
|
@ -119,6 +140,16 @@ export class AddonNotificationsHelperProvider {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether we should display full HTML of the notification.
|
||||||
|
*
|
||||||
|
* @param notification Notification.
|
||||||
|
* @return Whether to display full HTML.
|
||||||
|
*/
|
||||||
|
protected shouldDisplayFullHtml(notification: AddonNotificationsNotificationToRender): boolean {
|
||||||
|
return notification.component == 'mod_forum' && notification.eventtype == 'digests';
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AddonNotificationsHelper = makeSingleton(AddonNotificationsHelperProvider);
|
export const AddonNotificationsHelper = makeSingleton(AddonNotificationsHelperProvider);
|
||||||
|
@ -151,3 +182,11 @@ export type AddonNotificationsPreferencesNotificationFormatted = AddonNotificati
|
||||||
export type AddonNotificationsPreferencesProcessorFormatted = AddonNotificationsPreferencesProcessor & {
|
export type AddonNotificationsPreferencesProcessorFormatted = AddonNotificationsPreferencesProcessor & {
|
||||||
supported?: boolean; // Calculated in the app. Whether the processor is supported in the app.
|
supported?: boolean; // Calculated in the app. Whether the processor is supported in the app.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notification with some calculated data to render it.
|
||||||
|
*/
|
||||||
|
export type AddonNotificationsNotificationToRender = AddonNotificationsNotificationMessageFormatted & {
|
||||||
|
displayfullhtml?: boolean; // Whether to display the full HTML of the notification.
|
||||||
|
iconurl?: string;
|
||||||
|
};
|
||||||
|
|
|
@ -209,7 +209,7 @@ export class CoreLoginReconnectPage implements OnInit, OnDestroy {
|
||||||
|
|
||||||
// Go to the site initial page.
|
// Go to the site initial page.
|
||||||
this.page
|
this.page
|
||||||
? await CoreNavigator.navigateToSitePath(this.page, { params: this.pageOptions })
|
? await CoreNavigator.navigateToSitePath(this.page, this.pageOptions)
|
||||||
: await CoreNavigator.navigateToSiteHome();
|
: await CoreNavigator.navigateToSiteHome();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
CoreLoginHelper.treatUserTokenError(this.siteUrl, error, this.username, password);
|
CoreLoginHelper.treatUserTokenError(this.siteUrl, error, this.username, password);
|
||||||
|
|
Loading…
Reference in New Issue