MOBILE-4270 notifications: Always display icon in notification
parent
f2997313b4
commit
90aededf12
|
@ -2202,6 +2202,7 @@
|
||||||
"core.nopermissionerror": "local_moodlemobileapp",
|
"core.nopermissionerror": "local_moodlemobileapp",
|
||||||
"core.nopermissions": "error",
|
"core.nopermissions": "error",
|
||||||
"core.nopermissiontoaccesspage": "error",
|
"core.nopermissiontoaccesspage": "error",
|
||||||
|
"core.noreplyname": "moodle",
|
||||||
"core.noresults": "moodle",
|
"core.noresults": "moodle",
|
||||||
"core.noselection": "form",
|
"core.noselection": "form",
|
||||||
"core.notapplicable": "local_moodlemobileapp",
|
"core.notapplicable": "local_moodlemobileapp",
|
||||||
|
|
|
@ -13,8 +13,10 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { AddonNotificationsNotificationsSource } from '@addons/notifications/classes/notifications-source';
|
import { AddonNotificationsNotificationsSource } from '@addons/notifications/classes/notifications-source';
|
||||||
import { AddonNotificationsGetReadType } from '@addons/notifications/services/notifications';
|
import {
|
||||||
import { AddonNotificationsNotificationToRender } from '@addons/notifications/services/notifications-helper';
|
AddonNotificationsGetReadType,
|
||||||
|
AddonNotificationsNotificationMessageFormatted,
|
||||||
|
} from '@addons/notifications/services/notifications';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides a list of notifications using legacy web services.
|
* Provides a list of notifications using legacy web services.
|
||||||
|
@ -25,10 +27,10 @@ export class AddonLegacyNotificationsNotificationsSource extends AddonNotificati
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
protected async loadPageItems(page: number): Promise<{
|
protected async loadPageItems(page: number): Promise<{
|
||||||
items: AddonNotificationsNotificationToRender[];
|
items: AddonNotificationsNotificationMessageFormatted[];
|
||||||
hasMoreItems: boolean;
|
hasMoreItems: boolean;
|
||||||
}> {
|
}> {
|
||||||
let items: AddonNotificationsNotificationToRender[] = [];
|
let items: AddonNotificationsNotificationMessageFormatted[] = [];
|
||||||
let hasMoreItems = false;
|
let hasMoreItems = false;
|
||||||
let pageUnreadCount = 0;
|
let pageUnreadCount = 0;
|
||||||
const pageLength = this.getPageLength();
|
const pageLength = this.getPageLength();
|
||||||
|
|
|
@ -15,22 +15,23 @@
|
||||||
import {
|
import {
|
||||||
AddonNotifications,
|
AddonNotifications,
|
||||||
AddonNotificationsGetReadType,
|
AddonNotificationsGetReadType,
|
||||||
|
AddonNotificationsNotificationMessageFormatted,
|
||||||
AddonNotificationsProvider,
|
AddonNotificationsProvider,
|
||||||
} from '@addons/notifications/services/notifications';
|
} from '@addons/notifications/services/notifications';
|
||||||
import { AddonNotificationsNotificationToRender } from '@addons/notifications/services/notifications-helper';
|
|
||||||
import { CoreRoutedItemsManagerSource } from '@classes/items-management/routed-items-manager-source';
|
import { CoreRoutedItemsManagerSource } from '@classes/items-management/routed-items-manager-source';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides a list of notifications.
|
* Provides a list of notifications.
|
||||||
*/
|
*/
|
||||||
export class AddonNotificationsNotificationsSource extends CoreRoutedItemsManagerSource<AddonNotificationsNotificationToRender> {
|
export class AddonNotificationsNotificationsSource
|
||||||
|
extends CoreRoutedItemsManagerSource<AddonNotificationsNotificationMessageFormatted> {
|
||||||
|
|
||||||
protected totals: Record<string, number> = {};
|
protected totals: Record<string, number> = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
getItemPath(notification: AddonNotificationsNotificationToRender): string {
|
getItemPath(notification: AddonNotificationsNotificationMessageFormatted): string {
|
||||||
return notification.id.toString();
|
return notification.id.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,7 +48,7 @@ export class AddonNotificationsNotificationsSource extends CoreRoutedItemsManage
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
protected async loadPageItems(page: number): Promise<{
|
protected async loadPageItems(page: number): Promise<{
|
||||||
items: AddonNotificationsNotificationToRender[];
|
items: AddonNotificationsNotificationMessageFormatted[];
|
||||||
hasMoreItems: boolean;
|
hasMoreItems: boolean;
|
||||||
}> {
|
}> {
|
||||||
const results = await this.loadNotifications(AddonNotificationsGetReadType.BOTH, page * this.getPageLength());
|
const results = await this.loadNotifications(AddonNotificationsGetReadType.BOTH, page * this.getPageLength());
|
||||||
|
@ -67,7 +68,7 @@ export class AddonNotificationsNotificationsSource extends CoreRoutedItemsManage
|
||||||
* @returns Notifications and whether there are any more.
|
* @returns Notifications and whether there are any more.
|
||||||
*/
|
*/
|
||||||
protected async loadNotifications(type: AddonNotificationsGetReadType, offset: number, limit?: number): Promise<{
|
protected async loadNotifications(type: AddonNotificationsGetReadType, offset: number, limit?: number): Promise<{
|
||||||
notifications: AddonNotificationsNotificationToRender[];
|
notifications: AddonNotificationsNotificationMessageFormatted[];
|
||||||
hasMoreNotifications: boolean;
|
hasMoreNotifications: boolean;
|
||||||
}> {
|
}> {
|
||||||
limit = limit ?? this.getPageLength();
|
limit = limit ?? this.getPageLength();
|
||||||
|
@ -94,7 +95,7 @@ export class AddonNotificationsNotificationsSource extends CoreRoutedItemsManage
|
||||||
/**
|
/**
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
protected setItems(notifications: AddonNotificationsNotificationToRender[], hasMoreItems: boolean): void {
|
protected setItems(notifications: AddonNotificationsNotificationMessageFormatted[], hasMoreItems: boolean): void {
|
||||||
const sortedNotifications = notifications.slice(0);
|
const sortedNotifications = notifications.slice(0);
|
||||||
|
|
||||||
sortedNotifications.sort((a, b) => a.timecreated < b.timecreated ? 1 : -1);
|
sortedNotifications.sort((a, b) => a.timecreated < b.timecreated ? 1 : -1);
|
||||||
|
|
|
@ -34,6 +34,9 @@
|
||||||
width: var(--icon-size);
|
width: var(--icon-size);
|
||||||
height: var(--icon-size);
|
height: var(--icon-size);
|
||||||
}
|
}
|
||||||
|
ion-icon {
|
||||||
|
font-size: var(--icon-size);
|
||||||
|
}
|
||||||
padding: 0.7rem;
|
padding: 0.7rem;
|
||||||
background: var(--background-color);
|
background: var(--background-color);
|
||||||
border-radius: var(--small-radius);
|
border-radius: var(--small-radius);
|
||||||
|
@ -43,4 +46,11 @@
|
||||||
--module-icon-size: var(--icon-size);
|
--module-icon-size: var(--icon-size);
|
||||||
@include margin(6px, 8px, 6px, 0px);
|
@include margin(6px, 8px, 6px, 0px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.core-notification-img {
|
||||||
|
@include margin(6px, 8px, 6px, 0px);
|
||||||
|
width: var(--core-avatar-size);
|
||||||
|
height: var(--core-avatar-size);
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,21 +28,18 @@
|
||||||
<core-user-avatar *ngIf="notification.useridfrom > 0" [user]="notification" slot="start"
|
<core-user-avatar *ngIf="notification.useridfrom > 0" [user]="notification" slot="start"
|
||||||
[profileUrl]="notification.profileimageurlfrom" [fullname]="notification.userfromfullname"
|
[profileUrl]="notification.profileimageurlfrom" [fullname]="notification.userfromfullname"
|
||||||
[userId]="notification.useridfrom">
|
[userId]="notification.useridfrom">
|
||||||
<div class="core-avatar-extra-img" *ngIf="notification.iconurl && !notification.modname">
|
<div class="core-avatar-extra-img" *ngIf="notification.iconurl">
|
||||||
<img [src]="notification.iconurl" alt="" role="presentation">
|
<img [src]="notification.iconurl" alt="" role="presentation">
|
||||||
</div>
|
</div>
|
||||||
<core-mod-icon *ngIf="notification.modname" [modicon]="notification.iconurl" [modname]="notification.modname"
|
|
||||||
[showAlt]="false">
|
|
||||||
</core-mod-icon>
|
|
||||||
</core-user-avatar>
|
</core-user-avatar>
|
||||||
|
|
||||||
<ng-container *ngIf="notification.useridfrom <= 0 && notification.iconurl">
|
<ng-container *ngIf="notification.useridfrom <= 0">
|
||||||
<div class="core-notification-icon" *ngIf="!notification.modname" slot="start">
|
<img *ngIf="notification.imgUrl" class="core-notification-img" [src]="notification.imgUrl" core-external-content alt=""
|
||||||
<img [src]="notification.iconurl" alt="" role="presentation">
|
role="presentation" slot="start">
|
||||||
|
<div class="core-notification-icon" *ngIf="!notification.imgUrl" slot="start">
|
||||||
|
<img *ngIf="notification.iconurl" [src]="notification.iconurl" core-external-content alt="" role="presentation">
|
||||||
|
<ion-icon *ngIf="!notification.iconurl" name="fas-bell" aria-hidden="true"></ion-icon>
|
||||||
</div>
|
</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>
|
</ng-container>
|
||||||
|
|
||||||
<ion-label>
|
<ion-label>
|
||||||
|
|
|
@ -20,7 +20,7 @@ import { CoreDomUtils } from '@services/utils/dom';
|
||||||
import { CoreUtils } from '@services/utils/utils';
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
import { CoreEventObserver, CoreEvents } from '@singletons/events';
|
import { CoreEventObserver, CoreEvents } from '@singletons/events';
|
||||||
import {
|
import {
|
||||||
AddonNotifications, AddonNotificationsProvider,
|
AddonNotifications, AddonNotificationsNotificationMessageFormatted, AddonNotificationsProvider,
|
||||||
} from '../../services/notifications';
|
} from '../../services/notifications';
|
||||||
import { CoreNavigator } from '@services/navigator';
|
import { CoreNavigator } from '@services/navigator';
|
||||||
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
||||||
|
@ -31,7 +31,6 @@ import { CoreMainMenuDeepLinkManager } from '@features/mainmenu/classes/deep-lin
|
||||||
import { CoreTimeUtils } from '@services/utils/time';
|
import { CoreTimeUtils } from '@services/utils/time';
|
||||||
import { AddonNotificationsNotificationsSource } from '@addons/notifications/classes/notifications-source';
|
import { AddonNotificationsNotificationsSource } from '@addons/notifications/classes/notifications-source';
|
||||||
import { CoreListItemsManager } from '@classes/items-management/list-items-manager';
|
import { CoreListItemsManager } from '@classes/items-management/list-items-manager';
|
||||||
import { AddonNotificationsNotificationToRender } from '@addons/notifications/services/notifications-helper';
|
|
||||||
import { AddonLegacyNotificationsNotificationsSource } from '@addons/notifications/classes/legacy-notifications-source';
|
import { AddonLegacyNotificationsNotificationsSource } from '@addons/notifications/classes/legacy-notifications-source';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -45,7 +44,7 @@ import { AddonLegacyNotificationsNotificationsSource } from '@addons/notificatio
|
||||||
export class AddonNotificationsListPage implements AfterViewInit, OnDestroy {
|
export class AddonNotificationsListPage implements AfterViewInit, OnDestroy {
|
||||||
|
|
||||||
@ViewChild(CoreSplitViewComponent) splitView!: CoreSplitViewComponent;
|
@ViewChild(CoreSplitViewComponent) splitView!: CoreSplitViewComponent;
|
||||||
notifications!: CoreListItemsManager<AddonNotificationsNotificationToRender, AddonNotificationsNotificationsSource>;
|
notifications!: CoreListItemsManager<AddonNotificationsNotificationMessageFormatted, AddonNotificationsNotificationsSource>;
|
||||||
fetchMoreNotificationsFailed = false;
|
fetchMoreNotificationsFailed = false;
|
||||||
canMarkAllNotificationsAsRead = false;
|
canMarkAllNotificationsAsRead = false;
|
||||||
loadingMarkAllNotificationsAsRead = false;
|
loadingMarkAllNotificationsAsRead = false;
|
||||||
|
|
|
@ -10,40 +10,40 @@
|
||||||
</ion-header>
|
</ion-header>
|
||||||
<ion-content [core-swipe-navigation]="notifications">
|
<ion-content [core-swipe-navigation]="notifications">
|
||||||
<core-loading [hideUntil]="loaded">
|
<core-loading [hideUntil]="loaded">
|
||||||
<div class="list-item-limited-width">
|
<div class="list-item-limited-width" *ngIf="notification">
|
||||||
|
|
||||||
<ion-item class="ion-text-wrap core-notification-title" lines="full">
|
<ion-item class="ion-text-wrap core-notification-title" lines="full">
|
||||||
<core-user-avatar *ngIf="userIdFrom > 0" slot="start" [userId]="userIdFrom" [profileUrl]="profileImageUrlFrom"
|
<core-user-avatar *ngIf="notification.useridfrom > 0" slot="start" [userId]="notification.useridfrom"
|
||||||
[fullname]="userFromFullName">
|
[profileUrl]="notification.profileimageurlfrom" [fullname]="notification.userfromfullname">
|
||||||
<div class="core-avatar-extra-img" *ngIf="iconUrl && !modname">
|
<div class="core-avatar-extra-img" *ngIf="notification.iconurl">
|
||||||
<img [src]="iconUrl" alt="" role="presentation">
|
<img [src]="notification.iconurl" alt="" role="presentation">
|
||||||
</div>
|
</div>
|
||||||
<core-mod-icon *ngIf="modname" [modicon]="iconUrl" [modname]="modname" [showAlt]="false">
|
|
||||||
</core-mod-icon>
|
|
||||||
</core-user-avatar>
|
</core-user-avatar>
|
||||||
|
|
||||||
<ng-container *ngIf="userIdFrom <= 0 && iconUrl">
|
<ng-container *ngIf="notification.useridfrom <= 0">
|
||||||
<div class="core-notification-icon" *ngIf="!modname" slot="start">
|
<img *ngIf="notification.imgUrl" class="core-notification-img" [src]="notification.imgUrl" core-external-content alt=""
|
||||||
<img [src]="iconUrl" alt="" role="presentation">
|
role="presentation" slot="start">
|
||||||
|
<div class="core-notification-icon" *ngIf="!notification.imgUrl" slot="start">
|
||||||
|
<img *ngIf="notification.iconurl" [src]="notification.iconurl" core-external-content alt="" role="presentation">
|
||||||
|
<ion-icon *ngIf="!notification.iconurl" name="fas-bell" aria-hidden="true"></ion-icon>
|
||||||
</div>
|
</div>
|
||||||
<core-mod-icon *ngIf="modname" [modicon]="iconUrl" [modname]="modname" [showAlt]="false" class="core-notification-icon"
|
|
||||||
slot="start">
|
|
||||||
</core-mod-icon>
|
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ion-label>
|
<ion-label>
|
||||||
<p class="item-heading">
|
<p class="item-heading">
|
||||||
<core-format-text [text]="subject" contextLevel="system" [contextInstanceId]="0" [wsNotFiltered]="true">
|
<core-format-text [text]="notification.subject" contextLevel="system" [contextInstanceId]="0"
|
||||||
|
[wsNotFiltered]="true">
|
||||||
</core-format-text>
|
</core-format-text>
|
||||||
</p>
|
</p>
|
||||||
<p *ngIf="timecreated > 0">{{ timecreated | coreTimeAgo }}<ng-container *ngIf="userIdFrom > 0"> · {{
|
<p *ngIf="notification.timecreated > 0">
|
||||||
userFromFullName }}</ng-container>
|
{{ notification.timecreated | coreTimeAgo }}
|
||||||
|
<ng-container *ngIf="notification.useridfrom > 0"> · {{ notification.userfromfullname }}</ng-container>
|
||||||
</p>
|
</p>
|
||||||
</ion-label>
|
</ion-label>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item class="ion-text-wrap core-notification-body">
|
<ion-item class="ion-text-wrap core-notification-body">
|
||||||
<ion-label>
|
<ion-label>
|
||||||
<core-format-text [text]="content | coreCreateLinks" contextLevel="system" [contextInstanceId]="0">
|
<core-format-text [text]="notification.mobiletext | coreCreateLinks" contextLevel="system" [contextInstanceId]="0">
|
||||||
</core-format-text>
|
</core-format-text>
|
||||||
</ion-label>
|
</ion-label>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
|
|
|
@ -14,10 +14,10 @@
|
||||||
|
|
||||||
import { AddonLegacyNotificationsNotificationsSource } from '@addons/notifications/classes/legacy-notifications-source';
|
import { AddonLegacyNotificationsNotificationsSource } from '@addons/notifications/classes/legacy-notifications-source';
|
||||||
import { AddonNotificationsNotificationsSource } from '@addons/notifications/classes/notifications-source';
|
import { AddonNotificationsNotificationsSource } from '@addons/notifications/classes/notifications-source';
|
||||||
import { AddonNotificationsNotificationData } from '@addons/notifications/services/handlers/push-click';
|
import { AddonNotificationsPushNotification } from '@addons/notifications/services/handlers/push-click';
|
||||||
|
import { AddonNotifications, AddonNotificationsNotificationMessageFormatted } from '@addons/notifications/services/notifications';
|
||||||
import {
|
import {
|
||||||
AddonNotificationsHelper,
|
AddonNotificationsHelper,
|
||||||
AddonNotificationsNotificationToRender,
|
|
||||||
} from '@addons/notifications/services/notifications-helper';
|
} from '@addons/notifications/services/notifications-helper';
|
||||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||||
import { ActivatedRouteSnapshot } from '@angular/router';
|
import { ActivatedRouteSnapshot } from '@angular/router';
|
||||||
|
@ -39,21 +39,15 @@ import { CoreDomUtils } from '@services/utils/dom';
|
||||||
export class AddonNotificationsNotificationPage implements OnInit, OnDestroy {
|
export class AddonNotificationsNotificationPage implements OnInit, OnDestroy {
|
||||||
|
|
||||||
notifications?: AddonNotificationSwipeItemsManager;
|
notifications?: AddonNotificationSwipeItemsManager;
|
||||||
subject = ''; // Notification subject.
|
notification?: AddonNotificationsNotificationMessageFormatted;
|
||||||
content = ''; // Notification content.
|
|
||||||
userIdFrom = -1; // User ID who sent the notification.
|
|
||||||
profileImageUrlFrom?: string; // Avatar of the user 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.
|
|
||||||
modname?: string; // Module name.
|
|
||||||
loaded = false;
|
loaded = false;
|
||||||
timecreated = 0;
|
|
||||||
|
|
||||||
// Actions data.
|
// Actions data.
|
||||||
actions: CoreContentLinksAction[] = [];
|
actions: CoreContentLinksAction[] = [];
|
||||||
contextUrl?: string;
|
protected contextUrl?: string;
|
||||||
courseId?: number;
|
protected courseId?: number;
|
||||||
actionsData?: Record<string, unknown>; // Extra data to handle the URL.
|
protected actionsData?: Record<string, string|number>; // Extra data to handle the URL.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
|
@ -70,31 +64,11 @@ export class AddonNotificationsNotificationPage implements OnInit, OnDestroy {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('subject' in notification) {
|
this.notification = 'subject' in notification ?
|
||||||
this.subject = notification.subject;
|
notification :
|
||||||
this.content = notification.mobiletext || notification.fullmessagehtml;
|
await AddonNotifications.convertPushToMessage(notification);
|
||||||
this.userIdFrom = notification.useridfrom;
|
|
||||||
this.profileImageUrlFrom = notification.profileimageurlfrom;
|
|
||||||
this.userFromFullName = notification.userfromfullname;
|
|
||||||
this.iconUrl = notification.iconurl;
|
|
||||||
if (notification.moodlecomponent?.startsWith('mod_') && notification.iconurl) {
|
|
||||||
const modname = notification.moodlecomponent.substring(4);
|
|
||||||
if (notification.iconurl.match('/theme/image.php/[^/]+/' + modname + '/[-0-9]*/') ||
|
|
||||||
notification.iconurl.match('/theme/image.php/[^/]+/' + notification.moodlecomponent + '/[-0-9]*/')) {
|
|
||||||
this.modname = modname;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.timecreated = notification.timecreated;
|
|
||||||
} 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.timecreated = Number(notification.date ?? 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.loadActions(notification);
|
await this.loadActions(this.notification);
|
||||||
AddonNotificationsHelper.markNotificationAsRead(notification);
|
AddonNotificationsHelper.markNotificationAsRead(notification);
|
||||||
|
|
||||||
this.loaded = true;
|
this.loaded = true;
|
||||||
|
@ -153,7 +127,7 @@ export class AddonNotificationsNotificationPage implements OnInit, OnDestroy {
|
||||||
* @param notification Notification.
|
* @param notification Notification.
|
||||||
* @returns Promise resolved when done.
|
* @returns Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
async loadActions(notification: AddonNotificationsNotification): Promise<void> {
|
async loadActions(notification: AddonNotificationsNotificationMessageFormatted): Promise<void> {
|
||||||
if (!notification.contexturl && (!notification.customdata || !notification.customdata.appurl)) {
|
if (!notification.contexturl && (!notification.customdata || !notification.customdata.appurl)) {
|
||||||
// No URL, nothing to do.
|
// No URL, nothing to do.
|
||||||
return;
|
return;
|
||||||
|
@ -161,7 +135,7 @@ export class AddonNotificationsNotificationPage implements OnInit, OnDestroy {
|
||||||
|
|
||||||
let actions: CoreContentLinksAction[] = [];
|
let actions: CoreContentLinksAction[] = [];
|
||||||
this.actionsData = notification.customdata;
|
this.actionsData = notification.customdata;
|
||||||
this.contextUrl = notification.contexturl;
|
this.contextUrl = notification.contexturl || undefined;
|
||||||
this.courseId = 'courseid' in notification ? notification.courseid : undefined;
|
this.courseId = 'courseid' in notification ? notification.courseid : undefined;
|
||||||
|
|
||||||
// Treat appurl first if any.
|
// Treat appurl first if any.
|
||||||
|
@ -231,4 +205,4 @@ class AddonNotificationSwipeItemsManager extends CoreSwipeNavigationItemsManager
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type AddonNotificationsNotification = AddonNotificationsNotificationToRender | AddonNotificationsNotificationData;
|
type AddonNotificationsNotification = AddonNotificationsNotificationMessageFormatted | AddonNotificationsPushNotification;
|
||||||
|
|
|
@ -41,7 +41,7 @@ export class AddonNotificationsPushClickHandlerService implements CorePushNotifi
|
||||||
* @param notification The notification to check.
|
* @param notification The notification to check.
|
||||||
* @returns Whether the notification click is handled by this handler
|
* @returns Whether the notification click is handled by this handler
|
||||||
*/
|
*/
|
||||||
async handles(notification: AddonNotificationsNotificationData): Promise<boolean> {
|
async handles(notification: AddonNotificationsPushNotification): Promise<boolean> {
|
||||||
if (!notification.moodlecomponent) {
|
if (!notification.moodlecomponent) {
|
||||||
// The notification doesn't come from Moodle. Handle it.
|
// The notification doesn't come from Moodle. Handle it.
|
||||||
return true;
|
return true;
|
||||||
|
@ -63,7 +63,7 @@ export class AddonNotificationsPushClickHandlerService implements CorePushNotifi
|
||||||
* @param notification Notification to mark.
|
* @param notification Notification to mark.
|
||||||
* @returns Promise resolved when done.
|
* @returns Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
protected async markAsRead(notification: AddonNotificationsNotificationData): Promise<void> {
|
protected async markAsRead(notification: AddonNotificationsPushNotification): Promise<void> {
|
||||||
await CoreUtils.ignoreErrors(AddonNotificationsHelper.markNotificationAsRead(notification));
|
await CoreUtils.ignoreErrors(AddonNotificationsHelper.markNotificationAsRead(notification));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,7 +73,7 @@ export class AddonNotificationsPushClickHandlerService implements CorePushNotifi
|
||||||
* @param notification The notification to check.
|
* @param notification The notification to check.
|
||||||
* @returns Promise resolved when done.
|
* @returns Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
async handleClick(notification: AddonNotificationsNotificationData): Promise<void> {
|
async handleClick(notification: AddonNotificationsPushNotification): Promise<void> {
|
||||||
|
|
||||||
if (notification.customdata?.extendedtext) {
|
if (notification.customdata?.extendedtext) {
|
||||||
// Display the text in a modal.
|
// Display the text in a modal.
|
||||||
|
@ -137,7 +137,7 @@ export class AddonNotificationsPushClickHandlerService implements CorePushNotifi
|
||||||
|
|
||||||
export const AddonNotificationsPushClickHandler = makeSingleton(AddonNotificationsPushClickHandlerService);
|
export const AddonNotificationsPushClickHandler = makeSingleton(AddonNotificationsPushClickHandlerService);
|
||||||
|
|
||||||
export type AddonNotificationsNotificationData = CorePushNotificationsNotificationBasicData & {
|
export type AddonNotificationsPushNotification = 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).
|
||||||
|
|
|
@ -28,7 +28,7 @@ import {
|
||||||
AddonNotificationsProvider,
|
AddonNotificationsProvider,
|
||||||
} from './notifications';
|
} from './notifications';
|
||||||
import { CoreEvents } from '@singletons/events';
|
import { CoreEvents } from '@singletons/events';
|
||||||
import { AddonNotificationsNotificationData } from './handlers/push-click';
|
import { AddonNotificationsPushNotification } from './handlers/push-click';
|
||||||
import { CoreTimeUtils } from '@services/utils/time';
|
import { CoreTimeUtils } from '@services/utils/time';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -42,23 +42,12 @@ export class AddonNotificationsHelperProvider {
|
||||||
*
|
*
|
||||||
* @param notification The notification object.
|
* @param notification The notification object.
|
||||||
* @returns The notification formatted to render.
|
* @returns The notification formatted to render.
|
||||||
|
* @deprecated since 4.2. This function isn't needed anymore.
|
||||||
*/
|
*/
|
||||||
formatNotificationText(
|
formatNotificationText(
|
||||||
notification: AddonNotificationsNotificationMessageFormatted,
|
notification: AddonNotificationsNotificationMessageFormatted,
|
||||||
): AddonNotificationsNotificationToRender {
|
): AddonNotificationsNotificationMessageFormatted {
|
||||||
const formattedNotification: AddonNotificationsNotificationToRender = notification;
|
return notification;
|
||||||
|
|
||||||
if (notification.moodlecomponent?.startsWith('mod_') && notification.iconurl) {
|
|
||||||
const modname = notification.moodlecomponent.substring(4);
|
|
||||||
if (notification.iconurl.match('/theme/image.php/[^/]+/' + modname + '/[-0-9]*/') ||
|
|
||||||
notification.iconurl.match('/theme/image.php/[^/]+/' + notification.moodlecomponent + '/[-0-9]*/')) {
|
|
||||||
formattedNotification.modname = modname;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
formattedNotification.iconurl = formattedNotification.iconurl || undefined; // Make sure the property exists.
|
|
||||||
}
|
|
||||||
|
|
||||||
return formattedNotification;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -128,7 +117,7 @@ export class AddonNotificationsHelperProvider {
|
||||||
* @returns Promise resolved when done.
|
* @returns Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
async markNotificationAsRead(
|
async markNotificationAsRead(
|
||||||
notification: AddonNotificationsNotificationMessageFormatted | AddonNotificationsNotificationData,
|
notification: AddonNotificationsNotificationMessageFormatted | AddonNotificationsPushNotification,
|
||||||
siteId?: string,
|
siteId?: string,
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
if ('read' in notification && (notification.read || notification.timeread > 0)) {
|
if ('read' in notification && (notification.read || notification.timeread > 0)) {
|
||||||
|
@ -197,11 +186,3 @@ type AddonNotificationsPreferencesNotificationProcessorFormatted = AddonNotifica
|
||||||
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 & {
|
|
||||||
iconurl?: string;
|
|
||||||
modname?: string;
|
|
||||||
};
|
|
||||||
|
|
|
@ -18,11 +18,12 @@ import { CoreSites, CoreSitesCommonWSOptions } from '@services/sites';
|
||||||
import { CoreWSExternalWarning } from '@services/ws';
|
import { CoreWSExternalWarning } from '@services/ws';
|
||||||
import { CoreTextUtils } from '@services/utils/text';
|
import { CoreTextUtils } from '@services/utils/text';
|
||||||
import { CoreTimeUtils } from '@services/utils/time';
|
import { CoreTimeUtils } from '@services/utils/time';
|
||||||
import { CoreUser } from '@features/user/services/user';
|
import { CoreUser, USER_NOREPLY_USER } from '@features/user/services/user';
|
||||||
import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
|
import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
|
||||||
import { CoreLogger } from '@singletons/logger';
|
import { CoreLogger } from '@singletons/logger';
|
||||||
import { makeSingleton } from '@singletons';
|
import { Translate, makeSingleton } from '@singletons';
|
||||||
import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate';
|
import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate';
|
||||||
|
import { AddonNotificationsPushNotification } from './handlers/push-click';
|
||||||
|
|
||||||
declare module '@singletons/events' {
|
declare module '@singletons/events' {
|
||||||
|
|
||||||
|
@ -56,6 +57,48 @@ export class AddonNotificationsProvider {
|
||||||
this.logger = CoreLogger.getInstance('AddonNotificationsProvider');
|
this.logger = CoreLogger.getInstance('AddonNotificationsProvider');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a push notification data to use the same format as the get_messages WS.
|
||||||
|
*
|
||||||
|
* @param notification Push notification to convert.
|
||||||
|
* @returns Converted notification.
|
||||||
|
*/
|
||||||
|
async convertPushToMessage(
|
||||||
|
notification: AddonNotificationsPushNotification,
|
||||||
|
): Promise<AddonNotificationsNotificationMessageFormatted> {
|
||||||
|
const message = notification.message ?? '';
|
||||||
|
const siteInfo = CoreSites.getCurrentSite()?.getInfo();
|
||||||
|
|
||||||
|
if (notification.senderImage && notification.customdata && !notification.customdata.notificationiconurl) {
|
||||||
|
notification.customdata.notificationiconurl = notification.senderImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
const notificationMessage: AddonNotificationsNotificationMessage = {
|
||||||
|
id: notification.id ?? 0,
|
||||||
|
useridfrom: notification.userfromid ? Number(notification.userfromid) : USER_NOREPLY_USER,
|
||||||
|
userfromfullname: notification.userfromfullname ?? Translate.instant('core.noreplyname'),
|
||||||
|
useridto: notification.usertoid ? Number(notification.usertoid) : (siteInfo?.userid ?? 0),
|
||||||
|
usertofullname: siteInfo?.fullname ?? '',
|
||||||
|
subject: notification.title ?? '',
|
||||||
|
text: message,
|
||||||
|
fullmessage: message,
|
||||||
|
fullmessageformat: 1,
|
||||||
|
fullmessagehtml: message,
|
||||||
|
smallmessage: message,
|
||||||
|
notification: Number(notification.notif ?? 1),
|
||||||
|
contexturl: notification.contexturl || null,
|
||||||
|
contexturlname: null,
|
||||||
|
timecreated: Number(notification.date ?? 0),
|
||||||
|
timeread: 0,
|
||||||
|
component: notification.moodlecomponent,
|
||||||
|
customdata: notification.customdata ? JSON.stringify(notification.customdata) : undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatted = await this.formatNotificationsData([notificationMessage]);
|
||||||
|
|
||||||
|
return formatted[0];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function to format notification data.
|
* Function to format notification data.
|
||||||
*
|
*
|
||||||
|
@ -76,7 +119,7 @@ export class AddonNotificationsProvider {
|
||||||
notification.read = notification.timeread > 0;
|
notification.read = notification.timeread > 0;
|
||||||
|
|
||||||
if (typeof notification.customdata == 'string') {
|
if (typeof notification.customdata == 'string') {
|
||||||
notification.customdata = CoreTextUtils.parseJSON<Record<string, unknown>>(notification.customdata, {});
|
notification.customdata = CoreTextUtils.parseJSON<Record<string, string|number>>(notification.customdata, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to set courseid the notification belongs to.
|
// Try to set courseid the notification belongs to.
|
||||||
|
@ -91,13 +134,16 @@ export class AddonNotificationsProvider {
|
||||||
|
|
||||||
if (!notification.iconurl) {
|
if (!notification.iconurl) {
|
||||||
// The iconurl is only returned in 4.0 or above. Calculate it if not present.
|
// The iconurl is only returned in 4.0 or above. Calculate it if not present.
|
||||||
if (notification.component && notification.component.startsWith('mod_')) {
|
if (notification.moodlecomponent && notification.moodlecomponent.startsWith('mod_')) {
|
||||||
notification.iconurl = await CoreCourseModuleDelegate.getModuleIconSrc(
|
notification.iconurl = await CoreCourseModuleDelegate.getModuleIconSrc(
|
||||||
notification.component.replace('mod_', ''),
|
notification.moodlecomponent.replace('mod_', ''),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const imgUrl = notification.customdata?.notificationpictureurl || notification.customdata?.notificationiconurl;
|
||||||
|
notification.imgUrl = imgUrl ? String(imgUrl) : undefined;
|
||||||
|
|
||||||
if (notification.useridfrom > 0) {
|
if (notification.useridfrom > 0) {
|
||||||
// Try to get the profile picture of the user.
|
// Try to get the profile picture of the user.
|
||||||
try {
|
try {
|
||||||
|
@ -502,8 +548,8 @@ export type AddonNotificationsNotificationMessage = {
|
||||||
fullmessagehtml: string; // The message in html.
|
fullmessagehtml: string; // The message in html.
|
||||||
smallmessage: string; // The shorten message.
|
smallmessage: string; // The shorten message.
|
||||||
notification: number; // Is a notification?.
|
notification: number; // Is a notification?.
|
||||||
contexturl: string; // Context URL.
|
contexturl: string | null; // Context URL.
|
||||||
contexturlname: string; // Context URL link name.
|
contexturlname: string | null; // Context URL link name.
|
||||||
timecreated: number; // Time created.
|
timecreated: number; // Time created.
|
||||||
timeread: number; // Time read.
|
timeread: number; // Time read.
|
||||||
usertofullname: string; // User to full name.
|
usertofullname: string; // User to full name.
|
||||||
|
@ -532,15 +578,16 @@ export type AddonNotificationsGetUserNotificationPreferencesResult = {
|
||||||
* Calculated data for messages returned by core_message_get_messages.
|
* Calculated data for messages returned by core_message_get_messages.
|
||||||
*/
|
*/
|
||||||
export type AddonNotificationsNotificationCalculatedData = {
|
export type AddonNotificationsNotificationCalculatedData = {
|
||||||
mobiletext?: string; // Calculated in the app. Text to display for the notification.
|
mobiletext: string; // Calculated in the app. Text to display for the notification.
|
||||||
moodlecomponent?: string; // Calculated in the app. Moodle's component.
|
moodlecomponent?: string; // Calculated in the app. Moodle's component.
|
||||||
notif?: number; // Calculated in the app. Whether it's a notification.
|
notif: number; // Calculated in the app. Whether it's a notification.
|
||||||
notification?: number; // Calculated in the app in some cases. Whether it's a notification.
|
notification: number; // Calculated in the app in some cases. Whether it's a notification.
|
||||||
read?: boolean; // Calculated in the app. Whether the notifications is read.
|
read: boolean; // Calculated in the app. Whether the notifications is read.
|
||||||
courseid?: number; // Calculated in the app. Course the notification belongs to.
|
courseid?: number; // Calculated in the app. Course the notification belongs to.
|
||||||
profileimageurlfrom?: string; // Calculated in the app. Avatar of user that sent the notification.
|
profileimageurlfrom?: string; // Calculated in the app. Avatar of user that sent the notification.
|
||||||
userfromfullname?: string; // Calculated in the app in some cases. User from full name.
|
userfromfullname?: string; // Calculated in the app in some cases. User from full name.
|
||||||
customdata?: Record<string, unknown>; // Parsed custom data.
|
customdata?: Record<string, string|number>; // Parsed custom data.
|
||||||
|
imgUrl?: string; // Calculated in the app. URL of the image to use if the notification has no real user from.
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -460,7 +460,7 @@ export class CorePushNotificationsProvider {
|
||||||
title: notification.title,
|
title: notification.title,
|
||||||
message: notification.message,
|
message: notification.message,
|
||||||
customdata: typeof rawData.customdata == 'string' ?
|
customdata: typeof rawData.customdata == 'string' ?
|
||||||
CoreTextUtils.parseJSON<Record<string, unknown>>(rawData.customdata, {}) : rawData.customdata,
|
CoreTextUtils.parseJSON<Record<string, string|number>>(rawData.customdata, {}) : rawData.customdata,
|
||||||
});
|
});
|
||||||
|
|
||||||
let site: CoreSite | undefined;
|
let site: CoreSite | undefined;
|
||||||
|
@ -964,7 +964,7 @@ export type CorePushNotificationsNotificationBasicRawData = {
|
||||||
export type CorePushNotificationsNotificationBasicData = Omit<CorePushNotificationsNotificationBasicRawData, 'customdata'> & {
|
export type CorePushNotificationsNotificationBasicData = Omit<CorePushNotificationsNotificationBasicRawData, 'customdata'> & {
|
||||||
title?: string; // Notification title.
|
title?: string; // Notification title.
|
||||||
message?: string; // Notification message.
|
message?: string; // Notification message.
|
||||||
customdata?: Record<string, unknown>; // Parsed custom data.
|
customdata?: Record<string, string|number>; // Parsed custom data.
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -59,6 +59,11 @@ export const USER_PROFILE_PICTURE_UPDATED = 'CoreUserProfilePictureUpdated';
|
||||||
*/
|
*/
|
||||||
export const USER_PROFILE_SERVER_TIMEZONE = '99';
|
export const USER_PROFILE_SERVER_TIMEZONE = '99';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fake ID for a "no reply" user.
|
||||||
|
*/
|
||||||
|
export const USER_NOREPLY_USER = -10;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service to provide user functionalities.
|
* Service to provide user functionalities.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -216,6 +216,7 @@
|
||||||
"nopermissionerror": "Sorry, but you do not currently have permissions to do that",
|
"nopermissionerror": "Sorry, but you do not currently have permissions to do that",
|
||||||
"nopermissions": "Sorry, but you do not currently have permissions to do that ({{$a}}).",
|
"nopermissions": "Sorry, but you do not currently have permissions to do that ({{$a}}).",
|
||||||
"nopermissiontoaccesspage": "You don't have permission to access this page.",
|
"nopermissiontoaccesspage": "You don't have permission to access this page.",
|
||||||
|
"noreplyname": "Do not reply to this email",
|
||||||
"noresults": "No results",
|
"noresults": "No results",
|
||||||
"noselection": "No selection",
|
"noselection": "No selection",
|
||||||
"notapplicable": "n/a",
|
"notapplicable": "n/a",
|
||||||
|
|
Loading…
Reference in New Issue