From ff60de7de56f1ad6521213c117802942e040e5fc Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Thu, 4 May 2023 16:59:58 +0200 Subject: [PATCH 1/6] MOBILE-4270 tag: Fix tag route in mobile --- src/core/features/tag/tag-lazy.module.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/features/tag/tag-lazy.module.ts b/src/core/features/tag/tag-lazy.module.ts index 83079cbcb..a6f3b6c7a 100644 --- a/src/core/features/tag/tag-lazy.module.ts +++ b/src/core/features/tag/tag-lazy.module.ts @@ -45,7 +45,7 @@ function buildRoutes(injector: Injector): Routes { }, { ...indexAreaRoute, - path: `${indexAreaRoute.path}/index`, + path: `index/${indexAreaRoute.path}`, }, ]; From f0ba0cc479c543d4499fa8de5a295e8bde163fa5 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Fri, 5 May 2023 12:06:47 +0200 Subject: [PATCH 2/6] MOBILE-4270 participants: Remove auto-focus in search box --- src/core/features/user/pages/participants/participants.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/features/user/pages/participants/participants.html b/src/core/features/user/pages/participants/participants.html index 21ef547ed..fa9e23016 100644 --- a/src/core/features/user/pages/participants/participants.html +++ b/src/core/features/user/pages/participants/participants.html @@ -4,7 +4,7 @@ - From 15faf463674f6a3232cf6fedddcd65e9c0daade4 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Fri, 5 May 2023 14:51:32 +0200 Subject: [PATCH 3/6] MOBILE-4270 comments: Update cached data after add/delete --- .../features/comments/pages/viewer/viewer.ts | 70 +++++++++++++------ 1 file changed, 50 insertions(+), 20 deletions(-) diff --git a/src/core/features/comments/pages/viewer/viewer.ts b/src/core/features/comments/pages/viewer/viewer.ts index f1e35112d..43a98924e 100644 --- a/src/core/features/comments/pages/viewer/viewer.ts +++ b/src/core/features/comments/pages/viewer/viewer.ts @@ -187,11 +187,7 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { this.comments = comments.concat(this.comments); - this.comments.forEach((comment, index) => { - comment.showDate = this.showDate(comment, this.comments[index - 1]); - comment.showUserData = this.showUserData(comment, this.comments[index - 1]); - comment.showTail = this.showTail(comment, this.comments[index + 1]); - }); + this.comments.forEach((comment, index) => this.calculateCommentData(comment, this.comments[index - 1])); this.canDeleteComments = this.addDeleteCommentsAvailable && (this.hasOffline || this.comments.some((comment) => !!comment.delete)); @@ -216,6 +212,18 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { } + /** + * Calculate some comment data. + * + * @param comment Comment. + * @param prevComment Previous comment. + */ + protected calculateCommentData(comment: CoreCommentsDataToDisplay, prevComment?: CoreCommentsDataToDisplay): void { + comment.showDate = this.showDate(comment, prevComment); + comment.showUserData = this.showUserData(comment, prevComment); + comment.showTail = this.showTail(comment, prevComment); + } + /** * Function to load more commemts. * @@ -245,17 +253,15 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { this.refreshIcon = CoreConstants.ICON_LOADING; this.syncIcon = CoreConstants.ICON_LOADING; - try { - await this.invalidateComments(); - } finally { - this.page = 0; - this.comments = []; + await CoreUtils.ignoreErrors(this.invalidateComments()); - try { - await this.fetchComments(true, showErrors); - } finally { - refresher?.complete(); - } + this.page = 0; + this.comments = []; + + try { + await this.fetchComments(true, showErrors); + } finally { + refresher?.complete(); } } @@ -325,13 +331,11 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { if (commentsResponse) { this.invalidateComments(); - const addedComments = await this.loadCommentProfile(commentsResponse); - addedComments.showDate = this.showDate(addedComments, this.comments[this.comments.length - 1]); - addedComments.showUserData = this.showUserData(addedComments, this.comments[this.comments.length - 1]); - addedComments.showTail = this.showTail(addedComments, this.comments[this.comments.length + 1]); + const addedComment = await this.loadCommentProfile(commentsResponse); + this.calculateCommentData(addedComment, this.comments[this.comments.length - 1]); // Add the comment to the top. - this.comments = this.comments.concat([addedComments]); + this.comments = this.comments.concat([addedComment]); this.canDeleteComments = this.addDeleteCommentsAvailable; CoreEvents.trigger(CoreCommentsProvider.COMMENTS_COUNT_CHANGED_EVENT, { @@ -343,6 +347,8 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { countChange: 1, }, CoreSites.getCurrentSiteId()); + this.refreshInBackground(); + } else if (commentsResponse === false) { // Comments added in offline mode. await this.loadOfflineData(); @@ -410,6 +416,8 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { area: this.area, countChange: -1, }, CoreSites.getCurrentSiteId()); + + this.refreshInBackground(); } } else { this.loadOfflineData(); @@ -602,6 +610,28 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { this.showDelete = !this.showDelete; } + /** + * Refresh cached data in background. + */ + protected async refreshInBackground(): Promise { + await CoreUtils.ignoreErrors(this.invalidateComments()); + + const promises: Promise[] = []; + + for (let i = 0; i <= this.page; i++) { + promises.push(CoreComments.getComments( + this.contextLevel, + this.instanceId, + this.componentName, + this.itemId, + this.area, + i, + )); + } + + await Promise.all(promises); + } + /** * @inheritdoc */ From f2997313b4d74cf0f73de7a794de84af0b2c5431 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Mon, 8 May 2023 07:50:05 +0200 Subject: [PATCH 4/6] MOBILE-4270 icon: Fix some broken icons names --- src/core/directives/format-text.ts | 2 +- src/core/features/question/services/question-helper.ts | 2 +- src/theme/theme.base.scss | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/directives/format-text.ts b/src/core/directives/format-text.ts index e2c48feab..af578e717 100644 --- a/src/core/directives/format-text.ts +++ b/src/core/directives/format-text.ts @@ -280,7 +280,7 @@ export class CoreFormatTextDirective implements OnChanges, OnDestroy, AsyncDirec button.setAttribute('aria-label', label); // Add an ion-icon item to apply the right styles, but the ion-icon component won't be executed. button.innerHTML = ''; button.addEventListener('click', (e: Event) => { diff --git a/src/core/features/question/services/question-helper.ts b/src/core/features/question/services/question-helper.ts index 846f4d640..5dc04b069 100644 --- a/src/core/features/question/services/question-helper.ts +++ b/src/core/features/question/services/question-helper.ts @@ -806,7 +806,7 @@ export class CoreQuestionHelperProvider { newIcon.className = 'core-correct-icon ion-color ion-color-success questioncorrectnessicon'; } else { newIcon.setAttribute('name', 'fas-xmark'); - newIcon.setAttribute('src', 'assets/fonts/font-awesome/solid/times.svg'); + newIcon.setAttribute('src', 'assets/fonts/font-awesome/solid/xmark.svg'); newIcon.className = 'core-correct-icon ion-color ion-color-danger questioncorrectnessicon'; } diff --git a/src/theme/theme.base.scss b/src/theme/theme.base.scss index 8d599f0ad..1a8e8cd89 100644 --- a/src/theme/theme.base.scss +++ b/src/theme/theme.base.scss @@ -451,8 +451,8 @@ div.core-iframe-network-error { width: 50%; height: 50%; background-color: var(--danger); - -webkit-mask: url("/assets/fonts/font-awesome/solid/exclamation-triangle.svg") no-repeat 50% 50%; - mask: url("/assets/fonts/font-awesome/solid/exclamation-triangle.svg") no-repeat 50% 50%; + -webkit-mask: url("/assets/fonts/font-awesome/solid/triangle-exclamation.svg") no-repeat 50% 50%; + mask: url("/assets/fonts/font-awesome/solid/triangle-exclamation.svg") no-repeat 50% 50%; } } From 90aededf123ccfdfcf1822b65620bcd0612dd8e9 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Mon, 8 May 2023 10:34:58 +0200 Subject: [PATCH 5/6] MOBILE-4270 notifications: Always display icon in notification --- scripts/langindex.json | 1 + .../classes/legacy-notifications-source.ts | 10 +-- .../classes/notifications-source.ts | 13 ++-- src/addons/notifications/notifications.scss | 10 +++ src/addons/notifications/pages/list/list.html | 17 ++--- src/addons/notifications/pages/list/list.ts | 5 +- .../pages/notification/notification.html | 34 ++++----- .../pages/notification/notification.ts | 52 ++++---------- .../services/handlers/push-click.ts | 8 +-- .../services/notifications-helper.ts | 29 ++------ .../notifications/services/notifications.ts | 71 +++++++++++++++---- .../services/pushnotifications.ts | 4 +- src/core/features/user/services/user.ts | 5 ++ src/core/lang.json | 1 + 14 files changed, 139 insertions(+), 121 deletions(-) diff --git a/scripts/langindex.json b/scripts/langindex.json index 051f03dde..53430cdc7 100644 --- a/scripts/langindex.json +++ b/scripts/langindex.json @@ -2202,6 +2202,7 @@ "core.nopermissionerror": "local_moodlemobileapp", "core.nopermissions": "error", "core.nopermissiontoaccesspage": "error", + "core.noreplyname": "moodle", "core.noresults": "moodle", "core.noselection": "form", "core.notapplicable": "local_moodlemobileapp", diff --git a/src/addons/notifications/classes/legacy-notifications-source.ts b/src/addons/notifications/classes/legacy-notifications-source.ts index 564689350..22319ca5f 100644 --- a/src/addons/notifications/classes/legacy-notifications-source.ts +++ b/src/addons/notifications/classes/legacy-notifications-source.ts @@ -13,8 +13,10 @@ // limitations under the License. import { AddonNotificationsNotificationsSource } from '@addons/notifications/classes/notifications-source'; -import { AddonNotificationsGetReadType } from '@addons/notifications/services/notifications'; -import { AddonNotificationsNotificationToRender } from '@addons/notifications/services/notifications-helper'; +import { + AddonNotificationsGetReadType, + AddonNotificationsNotificationMessageFormatted, +} from '@addons/notifications/services/notifications'; /** * Provides a list of notifications using legacy web services. @@ -25,10 +27,10 @@ export class AddonLegacyNotificationsNotificationsSource extends AddonNotificati * @inheritdoc */ protected async loadPageItems(page: number): Promise<{ - items: AddonNotificationsNotificationToRender[]; + items: AddonNotificationsNotificationMessageFormatted[]; hasMoreItems: boolean; }> { - let items: AddonNotificationsNotificationToRender[] = []; + let items: AddonNotificationsNotificationMessageFormatted[] = []; let hasMoreItems = false; let pageUnreadCount = 0; const pageLength = this.getPageLength(); diff --git a/src/addons/notifications/classes/notifications-source.ts b/src/addons/notifications/classes/notifications-source.ts index 4ff8edb85..033201956 100644 --- a/src/addons/notifications/classes/notifications-source.ts +++ b/src/addons/notifications/classes/notifications-source.ts @@ -15,22 +15,23 @@ import { AddonNotifications, AddonNotificationsGetReadType, + AddonNotificationsNotificationMessageFormatted, AddonNotificationsProvider, } from '@addons/notifications/services/notifications'; -import { AddonNotificationsNotificationToRender } from '@addons/notifications/services/notifications-helper'; import { CoreRoutedItemsManagerSource } from '@classes/items-management/routed-items-manager-source'; /** * Provides a list of notifications. */ -export class AddonNotificationsNotificationsSource extends CoreRoutedItemsManagerSource { +export class AddonNotificationsNotificationsSource + extends CoreRoutedItemsManagerSource { protected totals: Record = {}; /** * @inheritdoc */ - getItemPath(notification: AddonNotificationsNotificationToRender): string { + getItemPath(notification: AddonNotificationsNotificationMessageFormatted): string { return notification.id.toString(); } @@ -47,7 +48,7 @@ export class AddonNotificationsNotificationsSource extends CoreRoutedItemsManage * @inheritdoc */ protected async loadPageItems(page: number): Promise<{ - items: AddonNotificationsNotificationToRender[]; + items: AddonNotificationsNotificationMessageFormatted[]; hasMoreItems: boolean; }> { 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. */ protected async loadNotifications(type: AddonNotificationsGetReadType, offset: number, limit?: number): Promise<{ - notifications: AddonNotificationsNotificationToRender[]; + notifications: AddonNotificationsNotificationMessageFormatted[]; hasMoreNotifications: boolean; }> { limit = limit ?? this.getPageLength(); @@ -94,7 +95,7 @@ export class AddonNotificationsNotificationsSource extends CoreRoutedItemsManage /** * @inheritdoc */ - protected setItems(notifications: AddonNotificationsNotificationToRender[], hasMoreItems: boolean): void { + protected setItems(notifications: AddonNotificationsNotificationMessageFormatted[], hasMoreItems: boolean): void { const sortedNotifications = notifications.slice(0); sortedNotifications.sort((a, b) => a.timecreated < b.timecreated ? 1 : -1); diff --git a/src/addons/notifications/notifications.scss b/src/addons/notifications/notifications.scss index 0583e27fa..2f144dd2a 100644 --- a/src/addons/notifications/notifications.scss +++ b/src/addons/notifications/notifications.scss @@ -34,6 +34,9 @@ width: var(--icon-size); height: var(--icon-size); } + ion-icon { + font-size: var(--icon-size); + } padding: 0.7rem; background: var(--background-color); border-radius: var(--small-radius); @@ -43,4 +46,11 @@ --module-icon-size: var(--icon-size); @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; + } } diff --git a/src/addons/notifications/pages/list/list.html b/src/addons/notifications/pages/list/list.html index c0ccfd14d..b7f7a5897 100644 --- a/src/addons/notifications/pages/list/list.html +++ b/src/addons/notifications/pages/list/list.html @@ -28,21 +28,18 @@ -
+
- - - -
- + + +
+ +
- -
diff --git a/src/addons/notifications/pages/list/list.ts b/src/addons/notifications/pages/list/list.ts index e26e0f344..dc935a079 100644 --- a/src/addons/notifications/pages/list/list.ts +++ b/src/addons/notifications/pages/list/list.ts @@ -20,7 +20,7 @@ import { CoreDomUtils } from '@services/utils/dom'; import { CoreUtils } from '@services/utils/utils'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { - AddonNotifications, AddonNotificationsProvider, + AddonNotifications, AddonNotificationsNotificationMessageFormatted, AddonNotificationsProvider, } from '../../services/notifications'; import { CoreNavigator } from '@services/navigator'; 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 { AddonNotificationsNotificationsSource } from '@addons/notifications/classes/notifications-source'; 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'; /** @@ -45,7 +44,7 @@ import { AddonLegacyNotificationsNotificationsSource } from '@addons/notificatio export class AddonNotificationsListPage implements AfterViewInit, OnDestroy { @ViewChild(CoreSplitViewComponent) splitView!: CoreSplitViewComponent; - notifications!: CoreListItemsManager; + notifications!: CoreListItemsManager; fetchMoreNotificationsFailed = false; canMarkAllNotificationsAsRead = false; loadingMarkAllNotificationsAsRead = false; diff --git a/src/addons/notifications/pages/notification/notification.html b/src/addons/notifications/pages/notification/notification.html index 6c7f95dd6..808cfe861 100644 --- a/src/addons/notifications/pages/notification/notification.html +++ b/src/addons/notifications/pages/notification/notification.html @@ -10,40 +10,40 @@ -
+
- -
- + +
+
- -
- -
- + + +
+ +
- -

- +

-

{{ timecreated | coreTimeAgo }} · {{ - userFromFullName }} +

+ {{ notification.timecreated | coreTimeAgo }} + · {{ notification.userfromfullname }}

- + diff --git a/src/addons/notifications/pages/notification/notification.ts b/src/addons/notifications/pages/notification/notification.ts index eea8ff181..7a5b4edb8 100644 --- a/src/addons/notifications/pages/notification/notification.ts +++ b/src/addons/notifications/pages/notification/notification.ts @@ -14,10 +14,10 @@ import { AddonLegacyNotificationsNotificationsSource } from '@addons/notifications/classes/legacy-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 { AddonNotificationsHelper, - AddonNotificationsNotificationToRender, } from '@addons/notifications/services/notifications-helper'; import { Component, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRouteSnapshot } from '@angular/router'; @@ -39,21 +39,15 @@ import { CoreDomUtils } from '@services/utils/dom'; export class AddonNotificationsNotificationPage implements OnInit, OnDestroy { notifications?: AddonNotificationSwipeItemsManager; - subject = ''; // Notification subject. - content = ''; // Notification content. - userIdFrom = -1; // User ID who sent the notification. + notification?: AddonNotificationsNotificationMessageFormatted; 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; - timecreated = 0; // Actions data. actions: CoreContentLinksAction[] = []; - contextUrl?: string; - courseId?: number; - actionsData?: Record; // Extra data to handle the URL. + protected contextUrl?: string; + protected courseId?: number; + protected actionsData?: Record; // Extra data to handle the URL. /** * @inheritdoc @@ -70,31 +64,11 @@ export class AddonNotificationsNotificationPage implements OnInit, OnDestroy { return; } - 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; - 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); - } + this.notification = 'subject' in notification ? + notification : + await AddonNotifications.convertPushToMessage(notification); - await this.loadActions(notification); + await this.loadActions(this.notification); AddonNotificationsHelper.markNotificationAsRead(notification); this.loaded = true; @@ -153,7 +127,7 @@ export class AddonNotificationsNotificationPage implements OnInit, OnDestroy { * @param notification Notification. * @returns Promise resolved when done. */ - async loadActions(notification: AddonNotificationsNotification): Promise { + async loadActions(notification: AddonNotificationsNotificationMessageFormatted): Promise { if (!notification.contexturl && (!notification.customdata || !notification.customdata.appurl)) { // No URL, nothing to do. return; @@ -161,7 +135,7 @@ export class AddonNotificationsNotificationPage implements OnInit, OnDestroy { let actions: CoreContentLinksAction[] = []; this.actionsData = notification.customdata; - this.contextUrl = notification.contexturl; + this.contextUrl = notification.contexturl || undefined; this.courseId = 'courseid' in notification ? notification.courseid : undefined; // Treat appurl first if any. @@ -231,4 +205,4 @@ class AddonNotificationSwipeItemsManager extends CoreSwipeNavigationItemsManager } -type AddonNotificationsNotification = AddonNotificationsNotificationToRender | AddonNotificationsNotificationData; +type AddonNotificationsNotification = AddonNotificationsNotificationMessageFormatted | AddonNotificationsPushNotification; diff --git a/src/addons/notifications/services/handlers/push-click.ts b/src/addons/notifications/services/handlers/push-click.ts index 197ac86b8..8354de06a 100644 --- a/src/addons/notifications/services/handlers/push-click.ts +++ b/src/addons/notifications/services/handlers/push-click.ts @@ -41,7 +41,7 @@ export class AddonNotificationsPushClickHandlerService implements CorePushNotifi * @param notification The notification to check. * @returns Whether the notification click is handled by this handler */ - async handles(notification: AddonNotificationsNotificationData): Promise { + async handles(notification: AddonNotificationsPushNotification): Promise { if (!notification.moodlecomponent) { // The notification doesn't come from Moodle. Handle it. return true; @@ -63,7 +63,7 @@ export class AddonNotificationsPushClickHandlerService implements CorePushNotifi * @param notification Notification to mark. * @returns Promise resolved when done. */ - protected async markAsRead(notification: AddonNotificationsNotificationData): Promise { + protected async markAsRead(notification: AddonNotificationsPushNotification): Promise { await CoreUtils.ignoreErrors(AddonNotificationsHelper.markNotificationAsRead(notification)); } @@ -73,7 +73,7 @@ export class AddonNotificationsPushClickHandlerService implements CorePushNotifi * @param notification The notification to check. * @returns Promise resolved when done. */ - async handleClick(notification: AddonNotificationsNotificationData): Promise { + async handleClick(notification: AddonNotificationsPushNotification): Promise { if (notification.customdata?.extendedtext) { // Display the text in a modal. @@ -137,7 +137,7 @@ export class AddonNotificationsPushClickHandlerService implements CorePushNotifi export const AddonNotificationsPushClickHandler = makeSingleton(AddonNotificationsPushClickHandlerService); -export type AddonNotificationsNotificationData = CorePushNotificationsNotificationBasicData & { +export type AddonNotificationsPushNotification = CorePushNotificationsNotificationBasicData & { contexturl?: string; // URL related to the notification. savedmessageid?: number; // Notification ID (optional). id?: number; // Notification ID (optional). diff --git a/src/addons/notifications/services/notifications-helper.ts b/src/addons/notifications/services/notifications-helper.ts index 6dec099d1..e03c2b10c 100644 --- a/src/addons/notifications/services/notifications-helper.ts +++ b/src/addons/notifications/services/notifications-helper.ts @@ -28,7 +28,7 @@ import { AddonNotificationsProvider, } from './notifications'; import { CoreEvents } from '@singletons/events'; -import { AddonNotificationsNotificationData } from './handlers/push-click'; +import { AddonNotificationsPushNotification } from './handlers/push-click'; import { CoreTimeUtils } from '@services/utils/time'; /** @@ -42,23 +42,12 @@ export class AddonNotificationsHelperProvider { * * @param notification The notification object. * @returns The notification formatted to render. + * @deprecated since 4.2. This function isn't needed anymore. */ formatNotificationText( notification: AddonNotificationsNotificationMessageFormatted, - ): AddonNotificationsNotificationToRender { - const formattedNotification: AddonNotificationsNotificationToRender = 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; + ): AddonNotificationsNotificationMessageFormatted { + return notification; } /** @@ -128,7 +117,7 @@ export class AddonNotificationsHelperProvider { * @returns Promise resolved when done. */ async markNotificationAsRead( - notification: AddonNotificationsNotificationMessageFormatted | AddonNotificationsNotificationData, + notification: AddonNotificationsNotificationMessageFormatted | AddonNotificationsPushNotification, siteId?: string, ): Promise { if ('read' in notification && (notification.read || notification.timeread > 0)) { @@ -197,11 +186,3 @@ type AddonNotificationsPreferencesNotificationProcessorFormatted = AddonNotifica 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 & { - iconurl?: string; - modname?: string; -}; diff --git a/src/addons/notifications/services/notifications.ts b/src/addons/notifications/services/notifications.ts index 3e7e3f715..83ddd8b4d 100644 --- a/src/addons/notifications/services/notifications.ts +++ b/src/addons/notifications/services/notifications.ts @@ -18,11 +18,12 @@ import { CoreSites, CoreSitesCommonWSOptions } from '@services/sites'; import { CoreWSExternalWarning } from '@services/ws'; import { CoreTextUtils } from '@services/utils/text'; 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 { CoreLogger } from '@singletons/logger'; -import { makeSingleton } from '@singletons'; +import { Translate, makeSingleton } from '@singletons'; import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate'; +import { AddonNotificationsPushNotification } from './handlers/push-click'; declare module '@singletons/events' { @@ -56,6 +57,48 @@ export class 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 { + 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. * @@ -76,7 +119,7 @@ export class AddonNotificationsProvider { notification.read = notification.timeread > 0; if (typeof notification.customdata == 'string') { - notification.customdata = CoreTextUtils.parseJSON>(notification.customdata, {}); + notification.customdata = CoreTextUtils.parseJSON>(notification.customdata, {}); } // Try to set courseid the notification belongs to. @@ -91,13 +134,16 @@ export class AddonNotificationsProvider { if (!notification.iconurl) { // 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.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) { // Try to get the profile picture of the user. try { @@ -502,8 +548,8 @@ export type AddonNotificationsNotificationMessage = { fullmessagehtml: string; // The message in html. smallmessage: string; // The shorten message. notification: number; // Is a notification?. - contexturl: string; // Context URL. - contexturlname: string; // Context URL link name. + contexturl: string | null; // Context URL. + contexturlname: string | null; // Context URL link name. timecreated: number; // Time created. timeread: number; // Time read. usertofullname: string; // User to full name. @@ -532,15 +578,16 @@ export type AddonNotificationsGetUserNotificationPreferencesResult = { * Calculated data for messages returned by core_message_get_messages. */ 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. - 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. - read?: boolean; // Calculated in the app. Whether the notifications is read. + 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. + read: boolean; // Calculated in the app. Whether the notifications is read. courseid?: number; // Calculated in the app. Course the notification belongs to. 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. - customdata?: Record; // Parsed custom data. + customdata?: Record; // Parsed custom data. + imgUrl?: string; // Calculated in the app. URL of the image to use if the notification has no real user from. }; /** diff --git a/src/core/features/pushnotifications/services/pushnotifications.ts b/src/core/features/pushnotifications/services/pushnotifications.ts index 3f2ee4734..eb8fca182 100644 --- a/src/core/features/pushnotifications/services/pushnotifications.ts +++ b/src/core/features/pushnotifications/services/pushnotifications.ts @@ -460,7 +460,7 @@ export class CorePushNotificationsProvider { title: notification.title, message: notification.message, customdata: typeof rawData.customdata == 'string' ? - CoreTextUtils.parseJSON>(rawData.customdata, {}) : rawData.customdata, + CoreTextUtils.parseJSON>(rawData.customdata, {}) : rawData.customdata, }); let site: CoreSite | undefined; @@ -964,7 +964,7 @@ export type CorePushNotificationsNotificationBasicRawData = { export type CorePushNotificationsNotificationBasicData = Omit & { title?: string; // Notification title. message?: string; // Notification message. - customdata?: Record; // Parsed custom data. + customdata?: Record; // Parsed custom data. }; /** diff --git a/src/core/features/user/services/user.ts b/src/core/features/user/services/user.ts index 3e9ca4b0f..7484e800b 100644 --- a/src/core/features/user/services/user.ts +++ b/src/core/features/user/services/user.ts @@ -59,6 +59,11 @@ export const USER_PROFILE_PICTURE_UPDATED = 'CoreUserProfilePictureUpdated'; */ 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. */ diff --git a/src/core/lang.json b/src/core/lang.json index 913cf4c2f..6897372a9 100644 --- a/src/core/lang.json +++ b/src/core/lang.json @@ -216,6 +216,7 @@ "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}}).", "nopermissiontoaccesspage": "You don't have permission to access this page.", + "noreplyname": "Do not reply to this email", "noresults": "No results", "noselection": "No selection", "notapplicable": "n/a", From fee132d6e881b83615f6827e5373ea9df970fd06 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Mon, 8 May 2023 13:10:09 +0200 Subject: [PATCH 6/6] MOBILE-4270 mod_data: Fix wrong string --- scripts/langindex.json | 2 +- .../mod/data/components/action/addon-mod-data-action.html | 2 +- src/addons/mod/data/lang.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/langindex.json b/scripts/langindex.json index 53430cdc7..1170109fc 100644 --- a/scripts/langindex.json +++ b/scripts/langindex.json @@ -524,7 +524,7 @@ "addon.mod_choice.savemychoice": "choice", "addon.mod_choice.userchoosethisoption": "choice", "addon.mod_choice.yourselection": "choice", - "addon.mod_data.actions": "data", + "addon.mod_data.actionsmenu": "data", "addon.mod_data.addentries": "data", "addon.mod_data.advancedsearch": "data", "addon.mod_data.alttext": "data", diff --git a/src/addons/mod/data/components/action/addon-mod-data-action.html b/src/addons/mod/data/components/action/addon-mod-data-action.html index cf233c3fa..aa4160d05 100644 --- a/src/addons/mod/data/components/action/addon-mod-data-action.html +++ b/src/addons/mod/data/components/action/addon-mod-data-action.html @@ -1,5 +1,5 @@ + [attr.aria-label]="'addon.mod_data.actionsmenu' | translate"> diff --git a/src/addons/mod/data/lang.json b/src/addons/mod/data/lang.json index e77e2fcd1..d15f18e32 100644 --- a/src/addons/mod/data/lang.json +++ b/src/addons/mod/data/lang.json @@ -1,5 +1,5 @@ { - "actions": "Actions menu", + "actionsmenu": "Actions menu", "addentries": "Add entries", "advancedsearch": "Advanced search", "alttext": "Alternative text",