Merge pull request #2980 from dpalou/MOBILE-3829

MOBILE-3829 notifications: Display notification clicked in new page
main
Noel De Martin 2021-10-26 10:21:44 +02:00 committed by GitHub
commit 07fae0152a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 221 additions and 40 deletions

View File

@ -27,6 +27,11 @@ function buildRoutes(injector: Injector): Routes {
},
loadChildren: () => import('./pages/list/list.module').then(m => m.AddonNotificationsListPageModule),
},
{
path: 'notification',
loadChildren: () => import('./pages/notification/notification.module')
.then(m => m.AddonNotificationsNotificationPageModule),
},
...buildTabMainRoutes(injector, {
redirectTo: 'list',
pathMatch: 'full',

View File

@ -18,15 +18,17 @@ import { Subscription } from 'rxjs';
import { CoreSites } from '@services/sites';
import { CoreDomUtils } from '@services/utils/dom';
import { CoreTextUtils } from '@services/utils/text';
import { CoreUtils } from '@services/utils/utils';
import { CoreEvents, CoreEventObserver } from '@singletons/events';
import {
AddonNotifications,
AddonNotificationsNotificationMessageFormatted,
AddonNotificationsProvider,
} from '../../services/notifications';
import { CorePushNotificationsDelegate } from '@features/pushnotifications/services/push-delegate';
import {
AddonNotificationsHelper,
AddonNotificationsNotificationToRender,
} from '@addons/notifications/services/notifications-helper';
/**
* Page that displays the list of notifications.
@ -38,7 +40,7 @@ import { CorePushNotificationsDelegate } from '@features/pushnotifications/servi
})
export class AddonNotificationsListPage implements OnInit, OnDestroy {
notifications: FormattedNotification[] = [];
notifications: AddonNotificationsNotificationToRender[] = [];
notificationsLoaded = false;
canLoadMore = false;
loadMoreError = false;
@ -94,7 +96,8 @@ export class AddonNotificationsListPage implements OnInit, OnDestroy {
try {
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) {
this.notifications = notifications;
@ -135,7 +138,7 @@ export class AddonNotificationsListPage implements OnInit, OnDestroy {
*
* @param notifications Array of notification objects.
*/
protected async markNotificationsAsRead(notifications: FormattedNotification[]): Promise<void> {
protected async markNotificationsAsRead(notifications: AddonNotificationsNotificationToRender[]): Promise<void> {
if (notifications.length > 0) {
const promises = notifications.map(async (notification) => {
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.
*/
@ -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;
};

View File

@ -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>

View File

@ -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 {}

View File

@ -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;
}
}

View File

@ -127,10 +127,16 @@ export class AddonNotificationsPushClickHandlerService implements CorePushNotifi
await CoreUtils.ignoreErrors(AddonNotifications.invalidateNotificationsList(notification.site));
await CoreNavigator.navigateToSitePath(
AddonNotificationsMainMenuHandlerService.PAGE_NAME,
`${AddonNotificationsMainMenuHandlerService.PAGE_NAME}/list`,
{
siteId: notification.site,
preferCurrentTab: false,
nextNavigation: {
path: '../notification',
options: {
params: { notification },
},
},
},
);
}
@ -139,7 +145,7 @@ export class AddonNotificationsPushClickHandlerService implements CorePushNotifi
export const AddonNotificationsPushClickHandler = makeSingleton(AddonNotificationsPushClickHandlerService);
type AddonNotificationsNotificationData = CorePushNotificationsNotificationBasicData & {
export type AddonNotificationsNotificationData = CorePushNotificationsNotificationBasicData & {
contexturl?: string; // URL related to the notification.
savedmessageid?: number; // Notification ID (optional).
id?: number; // Notification ID (optional).

View File

@ -18,12 +18,14 @@ import { CoreUtils } from '@services/utils/utils';
import { makeSingleton } from '@singletons';
import { AddonMessageOutputDelegate } from '@addons/messageoutput/services/messageoutput-delegate';
import {
AddonNotificationsNotificationMessageFormatted,
AddonNotificationsPreferences,
AddonNotificationsPreferencesComponent,
AddonNotificationsPreferencesNotification,
AddonNotificationsPreferencesNotificationProcessor,
AddonNotificationsPreferencesProcessor,
} from './notifications';
import { CoreTextUtils } from '@services/utils/text';
/**
* Service that provides some helper functions for notifications.
@ -31,6 +33,25 @@ import {
@Injectable({ providedIn: 'root' })
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.
*
@ -119,6 +140,16 @@ export class AddonNotificationsHelperProvider {
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);
@ -151,3 +182,11 @@ export type AddonNotificationsPreferencesNotificationFormatted = AddonNotificati
export type AddonNotificationsPreferencesProcessorFormatted = AddonNotificationsPreferencesProcessor & {
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;
};

View File

@ -209,7 +209,7 @@ export class CoreLoginReconnectPage implements OnInit, OnDestroy {
// Go to the site initial page.
this.page
? await CoreNavigator.navigateToSitePath(this.page, { params: this.pageOptions })
? await CoreNavigator.navigateToSitePath(this.page, this.pageOptions)
: await CoreNavigator.navigateToSiteHome();
} catch (error) {
CoreLoginHelper.treatUserTokenError(this.siteUrl, error, this.username, password);