diff --git a/src/addons/addons.module.ts b/src/addons/addons.module.ts
index 4a719a45e..7cedffb7f 100644
--- a/src/addons/addons.module.ts
+++ b/src/addons/addons.module.ts
@@ -21,6 +21,7 @@ import { AddonUserProfileFieldModule } from './userprofilefield/userprofilefield
import { AddonBadgesModule } from './badges/badges.module';
import { AddonCalendarModule } from './calendar/calendar.module';
import { AddonNotificationsModule } from './notifications/notifications.module';
+import { AddonMessageOutputModule } from './messageoutput/messageoutput.module';
@NgModule({
imports: [
@@ -31,6 +32,7 @@ import { AddonNotificationsModule } from './notifications/notifications.module';
AddonFilterModule,
AddonUserProfileFieldModule,
AddonNotificationsModule,
+ AddonMessageOutputModule,
],
})
export class AddonsModule {}
diff --git a/src/addons/notifications/notifications-lazy.module.ts b/src/addons/notifications/notifications-lazy.module.ts
index 890e8df7c..556467262 100644
--- a/src/addons/notifications/notifications-lazy.module.ts
+++ b/src/addons/notifications/notifications-lazy.module.ts
@@ -16,6 +16,7 @@ import { Injector, NgModule } from '@angular/core';
import { RouterModule, ROUTES, Routes } from '@angular/router';
import { buildTabMainRoutes } from '@features/mainmenu/mainmenu-tab-routing.module';
+import { AddonNotificationsSettingsHandlerService } from './services/handlers/settings';
function buildRoutes(injector: Injector): Routes {
return [
@@ -23,6 +24,10 @@ function buildRoutes(injector: Injector): Routes {
path: 'list',
loadChildren: () => import('./pages/list/list.module').then(m => m.AddonNotificationsListPageModule),
},
+ {
+ path: AddonNotificationsSettingsHandlerService.PAGE_NAME,
+ loadChildren: () => import('./pages/settings/settings.module').then(m => m.AddonNotificationsSettingsPageModule),
+ },
...buildTabMainRoutes(injector, {
redirectTo: 'list',
pathMatch: 'full',
diff --git a/src/addons/notifications/notifications.module.ts b/src/addons/notifications/notifications.module.ts
index 167fba258..49d012e57 100644
--- a/src/addons/notifications/notifications.module.ts
+++ b/src/addons/notifications/notifications.module.ts
@@ -18,10 +18,13 @@ import { Routes } from '@angular/router';
import { CoreCronDelegate } from '@services/cron';
import { CoreMainMenuDelegate } from '@features/mainmenu/services/mainmenu-delegate';
import { CoreMainMenuRoutingModule } from '@features/mainmenu/mainmenu-routing.module';
+import { CoreMainMenuTabRoutingModule } from '@features/mainmenu/mainmenu-tab-routing.module';
import { CorePushNotificationsDelegate } from '@features/pushnotifications/services/push-delegate';
+import { CoreSettingsDelegate } from '@features/settings/services/settings-delegate';
import { AddonNotificationsMainMenuHandler, AddonNotificationsMainMenuHandlerService } from './services/handlers/mainmenu';
import { AddonNotificationsCronHandler } from './services/handlers/cron';
import { AddonNotificationsPushClickHandler } from './services/handlers/push-click';
+import { AddonNotificationsSettingsHandler } from './services/handlers/settings';
const routes: Routes = [
{
@@ -31,7 +34,10 @@ const routes: Routes = [
];
@NgModule({
- imports: [CoreMainMenuRoutingModule.forChild({ children: routes })],
+ imports: [
+ CoreMainMenuRoutingModule.forChild({ children: routes }),
+ CoreMainMenuTabRoutingModule.forChild(routes),
+ ],
exports: [CoreMainMenuRoutingModule],
providers: [
{
@@ -42,6 +48,7 @@ const routes: Routes = [
CoreMainMenuDelegate.instance.registerHandler(AddonNotificationsMainMenuHandler.instance);
CoreCronDelegate.instance.register(AddonNotificationsCronHandler.instance);
CorePushNotificationsDelegate.instance.registerClickHandler(AddonNotificationsPushClickHandler.instance);
+ CoreSettingsDelegate.instance.registerHandler(AddonNotificationsSettingsHandler.instance);
},
},
],
diff --git a/src/addons/notifications/pages/list/list.module.ts b/src/addons/notifications/pages/list/list.module.ts
index 0171deb9c..284696909 100644
--- a/src/addons/notifications/pages/list/list.module.ts
+++ b/src/addons/notifications/pages/list/list.module.ts
@@ -36,7 +36,6 @@ const routes: Routes = [
IonicModule,
TranslateModule.forChild(),
CoreSharedModule,
- // CoreComponentsModule,
AddonNotificationsComponentsModule,
],
declarations: [
diff --git a/src/addons/notifications/pages/settings/settings.html b/src/addons/notifications/pages/settings/settings.html
new file mode 100644
index 000000000..4209719f3
--- /dev/null
+++ b/src/addons/notifications/pages/settings/settings.html
@@ -0,0 +1,115 @@
+
+
+
+
+
+ {{ 'addon.notifications.notifications' | translate }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ 'addon.notifications.playsound' | translate }}
+
+
+
+
+
+
+ {{ 'addon.notifications.notifications' | translate }}
+
+
+
+ {{ 'addon.notifications.playsound' | translate }}
+
+
+
+
+
+
+ 0"
+ [ngModel]="currentProcessor!.name" (ngModelChange)="changeProcessor($event)" interface="action-sheet"
+ class="core-button-select">
+
+ {{ processor.displayname }}
+
+
+
+
+
+
+
+ {{ component.displayname }}
+
+ {{ 'core.settings.loggedin' | translate }}
+
+
+ {{ 'core.settings.loggedoff' | translate }}
+
+
+
+
+
+
+
+
+ {{ notification.displayname }}
+
+
+
+
+
+
+
+ {{'core.settings.locked' | translate }}
+
+
+ {{ 'core.settings.disabled' | translate }}
+
+
+
+
+
+
+ {{ notification.displayname }}
+
+
+
+ {{ 'core.settings.' + state | translate }}
+
+
+
+
+
+ {{'core.settings.locked' | translate }}
+
+ {{ 'core.settings.disabled' | translate }}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/addons/notifications/pages/settings/settings.module.ts b/src/addons/notifications/pages/settings/settings.module.ts
new file mode 100644
index 000000000..f01846203
--- /dev/null
+++ b/src/addons/notifications/pages/settings/settings.module.ts
@@ -0,0 +1,46 @@
+// (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 { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { RouterModule, Routes } from '@angular/router';
+import { IonicModule } from '@ionic/angular';
+import { TranslateModule } from '@ngx-translate/core';
+
+import { CoreSharedModule } from '@/core/shared.module';
+import { AddonNotificationsSettingsPage } from './settings';
+
+const routes: Routes = [
+ {
+ path: '',
+ component: AddonNotificationsSettingsPage,
+ },
+];
+
+@NgModule({
+ imports: [
+ RouterModule.forChild(routes),
+ CommonModule,
+ FormsModule,
+ IonicModule,
+ TranslateModule.forChild(),
+ CoreSharedModule,
+ ],
+ declarations: [
+ AddonNotificationsSettingsPage,
+ ],
+ exports: [RouterModule],
+})
+export class AddonNotificationsSettingsPageModule {}
diff --git a/src/addons/notifications/pages/settings/settings.scss b/src/addons/notifications/pages/settings/settings.scss
new file mode 100644
index 000000000..8fea99c17
--- /dev/null
+++ b/src/addons/notifications/pages/settings/settings.scss
@@ -0,0 +1,5 @@
+:host {
+ .addon-notifications-table-content ion-row {
+ min-height: 35px;
+ }
+}
diff --git a/src/addons/notifications/pages/settings/settings.ts b/src/addons/notifications/pages/settings/settings.ts
new file mode 100644
index 000000000..f3e8189c0
--- /dev/null
+++ b/src/addons/notifications/pages/settings/settings.ts
@@ -0,0 +1,308 @@
+// (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 { Component, OnInit, OnDestroy } from '@angular/core';
+import { IonRefresher, NavController } from '@ionic/angular';
+
+import { CoreConfig } from '@services/config';
+import { CoreLocalNotifications } from '@services/local-notifications';
+import { CoreSites } from '@services/sites';
+import { CoreDomUtils } from '@services/utils/dom';
+import { CoreUtils } from '@services/utils/utils';
+import { CoreUser } from '@features/user/services/user';
+import { AddonMessageOutputDelegate, AddonMessageOutputHandlerData } from '@addons/messageoutput/services/messageoutput-delegate';
+import { CoreConstants } from '@/core/constants';
+import { CoreError } from '@classes/errors/error';
+import { CoreEventNotificationSoundChangedData, CoreEvents } from '@singletons/events';
+import {
+ AddonNotifications,
+ AddonNotificationsPreferencesProcessor,
+ AddonNotificationsPreferencesNotificationProcessorState,
+} from '../../services/notifications';
+import {
+ AddonNotificationsHelper,
+ AddonNotificationsPreferencesComponentFormatted,
+ AddonNotificationsPreferencesFormatted,
+ AddonNotificationsPreferencesNotificationFormatted,
+ AddonNotificationsPreferencesProcessorFormatted,
+} from '@addons/notifications/services/notifications-helper';
+import { CoreNavHelper } from '@services/nav-helper';
+// import { CoreSplitViewComponent } from '@components/split-view/split-view';
+
+/**
+ * Page that displays notifications settings.
+ */
+@Component({
+ selector: 'page-addon-notifications-settings',
+ templateUrl: 'settings.html',
+ styleUrls: ['settings.scss'],
+})
+export class AddonNotificationsSettingsPage implements OnInit, OnDestroy {
+
+ preferences?: AddonNotificationsPreferencesFormatted;
+ components?: AddonNotificationsPreferencesComponentFormatted[];
+ currentProcessor?: AddonNotificationsPreferencesProcessor;
+ preferencesLoaded = false;
+ notificationSound = false;
+ notifPrefsEnabled: boolean;
+ canChangeSound: boolean;
+ processorHandlers: AddonMessageOutputHandlerData[] = [];
+
+ protected updateTimeout?: number;
+
+ constructor(
+ protected navCtrl: NavController,
+ // @Optional() protected svComponent: CoreSplitViewComponent,
+ ) {
+ this.notifPrefsEnabled = AddonNotifications.instance.isNotificationPreferencesEnabled();
+ this.canChangeSound = CoreLocalNotifications.instance.canDisableSound();
+ }
+
+ /**
+ * Component being initialized.
+ */
+ async ngOnInit(): Promise {
+ if (this.canChangeSound) {
+ this.notificationSound = await CoreConfig.instance.get(CoreConstants.SETTINGS_NOTIFICATION_SOUND, true);
+ }
+
+ if (this.notifPrefsEnabled) {
+ this.fetchPreferences();
+ } else {
+ this.preferencesLoaded = true;
+ }
+ }
+
+ /**
+ * Fetches preferences data.
+ *
+ * @return Resolved when done.
+ */
+ protected async fetchPreferences(): Promise {
+ try {
+ const preferences = await AddonNotifications.instance.getNotificationPreferences();
+
+ if (!this.currentProcessor) {
+ // Initialize current processor. Load "Mobile" (airnotifier) if available.
+ this.currentProcessor = AddonNotificationsHelper.instance.getProcessor(preferences.processors, 'airnotifier');
+ }
+
+ if (!this.currentProcessor) {
+ // Shouldn't happen.
+ throw new CoreError('No processor found');
+ }
+
+ preferences.enableall = !preferences.disableall;
+ this.preferences = AddonNotificationsHelper.instance.formatPreferences(preferences);
+ this.loadProcessor(this.currentProcessor);
+
+ } catch (error) {
+ CoreDomUtils.instance.showErrorModal(error);
+ } finally {
+ this.preferencesLoaded = true;
+ }
+ }
+
+ /**
+ * Load a processor.
+ *
+ * @param processor Processor object.
+ */
+ protected loadProcessor(processor: AddonNotificationsPreferencesProcessorFormatted): void {
+ if (!processor) {
+ return;
+ }
+
+ this.currentProcessor = processor;
+ this.processorHandlers = [];
+ this.components = AddonNotificationsHelper.instance.getProcessorComponents(
+ processor.name,
+ this.preferences?.components || [],
+ );
+
+ if (!processor.hassettings || !processor.supported) {
+ return;
+ }
+
+ const handlerData = AddonMessageOutputDelegate.instance.getDisplayData(processor);
+ if (handlerData) {
+ this.processorHandlers.push(handlerData);
+ }
+ }
+
+ /**
+ * Update preferences after a certain time. The purpose is to store the updated data, it won't be reflected in the view.
+ */
+ protected updatePreferencesAfterDelay(): void {
+ // Cancel pending updates.
+ clearTimeout(this.updateTimeout);
+
+ this.updateTimeout = window.setTimeout(() => {
+ this.updateTimeout = undefined;
+ this.updatePreferences();
+ }, 5000);
+ }
+
+ /**
+ * Update preferences. The purpose is to store the updated data, it won't be reflected in the view.
+ *
+ * @return Promise resolved when done.
+ */
+ protected async updatePreferences(): Promise {
+ await CoreUtils.instance.ignoreErrors(AddonNotifications.instance.invalidateNotificationPreferences());
+
+ await AddonNotifications.instance.getNotificationPreferences();
+ }
+
+ /**
+ * The selected processor was changed.
+ *
+ * @param name Name of the selected processor.
+ */
+ changeProcessor(name: string): void {
+ const processor = this.preferences!.processors.find((processor) => processor.name == name);
+
+ if (processor) {
+ this.loadProcessor(processor);
+ }
+ }
+
+ /**
+ * Refresh the list of preferences.
+ *
+ * @param refresher Refresher.
+ */
+ async refreshPreferences(refresher?: CustomEvent): Promise {
+ try {
+ await CoreUtils.instance.ignoreErrors(AddonNotifications.instance.invalidateNotificationPreferences());
+
+ await this.fetchPreferences();
+ } finally {
+ refresher?.detail.complete();
+ }
+ }
+
+ /**
+ * Open extra preferences.
+ *
+ * @param handlerData
+ */
+ openExtraPreferences(handlerData: AddonMessageOutputHandlerData): void {
+ // Decide which navCtrl to use. If this page is inside a split view, use the split view's master nav.
+ // @todo const navCtrl = this.svComponent ? this.svComponent.getMasterNav() : this.navCtrl;
+ CoreNavHelper.instance.goInCurrentMainMenuTab(handlerData.page, handlerData.pageParams);
+ }
+
+ /**
+ * Change the value of a certain preference.
+ *
+ * @param notification Notification object.
+ * @param state State name, ['loggedin', 'loggedoff'].
+ * @return Promise resolved when done.
+ */
+ async changePreference(notification: AddonNotificationsPreferencesNotificationFormatted, state: string): Promise {
+ const processor = notification.processorsByName?.[this.currentProcessor?.name || ''];
+ if (!processor) {
+ return;
+ }
+
+ const processorState: ProcessorStateFormatted = processor[state];
+ const preferenceName = notification.preferencekey + '_' + processorState.name;
+ let value: string | undefined;
+
+ notification.processors.forEach((processor) => {
+ if (processor[state].checked) {
+ if (!value) {
+ value = processor.name;
+ } else {
+ value += ',' + processor.name;
+ }
+ }
+ });
+
+ if (!value) {
+ value = 'none';
+ }
+
+ processorState.updating = true;
+
+ try {
+ await CoreUser.instance.updateUserPreference(preferenceName, value);
+
+ // Update the preferences since they were modified.
+ this.updatePreferencesAfterDelay();
+ } catch (error) {
+ // Show error and revert change.
+ CoreDomUtils.instance.showErrorModal(error);
+ processor[state].checked = !processor[state].checked;
+ } finally {
+ processorState.updating = false;
+ }
+ }
+
+ /**
+ * Enable all notifications changed.
+ *
+ * @param enable Whether to enable or disable.
+ * @return Promise resolved when done.
+ */
+ async enableAll(enable?: boolean): Promise {
+ const modal = await CoreDomUtils.instance.showModalLoading('core.sending', true);
+
+ try {
+ CoreUser.instance.updateUserPreferences([], !enable);
+
+ // Update the preferences since they were modified.
+ this.updatePreferencesAfterDelay();
+ } catch (error) {
+ // Show error and revert change.
+ CoreDomUtils.instance.showErrorModal(error);
+ this.preferences!.enableall = !this.preferences!.enableall;
+ } finally {
+ modal.dismiss();
+ }
+ }
+
+ /**
+ * Change the notification sound setting.
+ *
+ * @param enabled True to enable the notification sound, false to disable it.
+ */
+ async changeNotificationSound(enabled: boolean): Promise {
+ await CoreUtils.instance.ignoreErrors(CoreConfig.instance.set(CoreConstants.SETTINGS_NOTIFICATION_SOUND, enabled ? 1 : 0));
+
+ const siteId = CoreSites.instance.getCurrentSiteId();
+ CoreEvents.trigger(CoreEvents.NOTIFICATION_SOUND_CHANGED, { enabled }, siteId);
+ CoreLocalNotifications.instance.rescheduleAll();
+ }
+
+ /**
+ * Page destroyed.
+ */
+ ngOnDestroy(): void {
+ // If there is a pending action to update preferences, execute it right now.
+ if (this.updateTimeout) {
+ clearTimeout(this.updateTimeout);
+ this.updatePreferences();
+ }
+ }
+
+}
+
+/**
+ * State in notification processor in notification preferences component with some calculated data.
+ */
+type ProcessorStateFormatted = AddonNotificationsPreferencesNotificationProcessorState & {
+ updating?: boolean; // Calculated in the app. Whether the state is being updated.
+};
diff --git a/src/addons/notifications/services/handlers/cron.ts b/src/addons/notifications/services/handlers/cron.ts
index a199e423a..42831a38b 100644
--- a/src/addons/notifications/services/handlers/cron.ts
+++ b/src/addons/notifications/services/handlers/cron.ts
@@ -24,7 +24,7 @@ import { AddonNotifications, AddonNotificationsProvider } from '../notifications
/**
* Notifications cron handler.
*/
-@Injectable()
+@Injectable({ providedIn: 'root' })
export class AddonNotificationsCronHandlerService implements CoreCronHandler {
name = 'AddonNotificationsCronHandler';
diff --git a/src/addons/notifications/services/handlers/push-click.ts b/src/addons/notifications/services/handlers/push-click.ts
index 9b3b4d8c9..3d3f0c72b 100644
--- a/src/addons/notifications/services/handlers/push-click.ts
+++ b/src/addons/notifications/services/handlers/push-click.ts
@@ -27,7 +27,7 @@ import { AddonNotifications, AddonNotificationsProvider } from '../notifications
/**
* Handler for non-messaging push notifications clicks.
*/
-@Injectable()
+@Injectable({ providedIn: 'root' })
export class AddonNotificationsPushClickHandlerService implements CorePushNotificationsClickHandler {
name = 'AddonNotificationsPushClickHandler';
diff --git a/src/addons/notifications/services/handlers/settings.ts b/src/addons/notifications/services/handlers/settings.ts
new file mode 100644
index 000000000..ff127ae90
--- /dev/null
+++ b/src/addons/notifications/services/handlers/settings.ts
@@ -0,0 +1,60 @@
+// (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 { Injectable } from '@angular/core';
+
+import { CoreLocalNotifications } from '@services/local-notifications';
+import { makeSingleton } from '@singletons';
+import { CoreSettingsHandler, CoreSettingsHandlerData } from '@features/settings/services/settings-delegate';
+import { AddonNotifications } from '../notifications';
+import { AddonNotificationsMainMenuHandlerService } from './mainmenu';
+
+/**
+ * Notifications settings handler.
+ */
+@Injectable({ providedIn: 'root' })
+export class AddonNotificationsSettingsHandlerService implements CoreSettingsHandler {
+
+ static readonly PAGE_NAME = 'settings';
+
+ name = 'AddonNotifications';
+ priority = 500;
+
+ /**
+ * Check if the handler is enabled on a site level.
+ *
+ * @return Whether or not the handler is enabled on a site level.
+ */
+ async isEnabled(): Promise {
+ // Preferences or notification sound setting available.
+ return CoreLocalNotifications.instance.isAvailable() || AddonNotifications.instance.isNotificationPreferencesEnabled();
+ }
+
+ /**
+ * Returns the data needed to render the handler.
+ *
+ * @return Data needed to render the handler.
+ */
+ getDisplayData(): CoreSettingsHandlerData {
+ return {
+ icon: 'fas-bell',
+ title: 'addon.notifications.notifications',
+ page: AddonNotificationsMainMenuHandlerService.PAGE_NAME + '/' + AddonNotificationsSettingsHandlerService.PAGE_NAME,
+ class: 'addon-notifications-settings-handler',
+ };
+ }
+
+}
+
+export class AddonNotificationsSettingsHandler extends makeSingleton(AddonNotificationsSettingsHandlerService) {}
diff --git a/src/addons/notifications/services/notifications-helper.ts b/src/addons/notifications/services/notifications-helper.ts
index bcae25801..9491b28a8 100644
--- a/src/addons/notifications/services/notifications-helper.ts
+++ b/src/addons/notifications/services/notifications-helper.ts
@@ -15,11 +15,18 @@
import { Injectable } from '@angular/core';
import { CoreSites } from '@services/sites';
+import { CoreUtils } from '@services/utils/utils';
import { makeSingleton } from '@singletons';
+import { AddonMessageOutputDelegate } from '@addons/messageoutput/services/messageoutput-delegate';
import {
AddonNotifications,
AddonNotificationsAnyNotification,
AddonNotificationsGetNotificationsOptions,
+ AddonNotificationsPreferences,
+ AddonNotificationsPreferencesComponent,
+ AddonNotificationsPreferencesNotification,
+ AddonNotificationsPreferencesNotificationProcessor,
+ AddonNotificationsPreferencesProcessor,
AddonNotificationsProvider,
} from './notifications';
@@ -29,6 +36,28 @@ import {
@Injectable({ providedIn: 'root' })
export class AddonNotificationsHelperProvider {
+ /**
+ * Format preferences data.
+ *
+ * @param preferences Preferences to format.
+ * @return Formatted preferences.
+ */
+ formatPreferences(preferences: AddonNotificationsPreferences): AddonNotificationsPreferencesFormatted {
+ const formattedPreferences: AddonNotificationsPreferencesFormatted = preferences;
+
+ formattedPreferences.processors.forEach((processor) => {
+ processor.supported = AddonMessageOutputDelegate.instance.hasHandler(processor.name, true);
+ });
+
+ formattedPreferences.components.forEach((component) => {
+ component.notifications.forEach((notification) => {
+ notification.processorsByName = CoreUtils.instance.arrayToObject(notification.processors, 'name');
+ });
+ });
+
+ return formattedPreferences;
+ }
+
/**
* Get some notifications. It will try to use the new WS if available.
*
@@ -82,6 +111,101 @@ export class AddonNotificationsHelperProvider {
};
}
+ /**
+ * Get a certain processor from a list of processors.
+ *
+ * @param processors List of processors.
+ * @param name Name of the processor to get.
+ * @param fallback True to return first processor if not found, false to not return any. Defaults to true.
+ * @return Processor.
+ */
+ getProcessor(
+ processors: AddonNotificationsPreferencesProcessor[],
+ name: string,
+ fallback: boolean = true,
+ ): AddonNotificationsPreferencesProcessor | undefined {
+ if (!processors || !processors.length) {
+ return;
+ }
+
+ const processor = processors.find((processor) => processor.name == name);
+ if (processor) {
+ return processor;
+ }
+
+ // Processor not found, return first if requested.
+ if (fallback) {
+ return processors[0];
+ }
+ }
+
+ /**
+ * Return the components and notifications that have a certain processor.
+ *
+ * @param processorName Name of the processor to filter.
+ * @param components Array of components.
+ * @return Filtered components.
+ */
+ getProcessorComponents(
+ processorName: string,
+ components: AddonNotificationsPreferencesComponentFormatted[],
+ ): AddonNotificationsPreferencesComponentFormatted[] {
+ const result: AddonNotificationsPreferencesComponentFormatted[] = [];
+
+ components.forEach((component) => {
+ // Check if the component has any notification with this processor.
+ const notifications: AddonNotificationsPreferencesNotificationFormatted[] = [];
+
+ component.notifications.forEach((notification) => {
+ const processor = notification.processorsByName?.[processorName];
+
+ if (processor) {
+ // Add the notification.
+ notifications.push(notification);
+ }
+ });
+
+ if (notifications.length) {
+ // At least 1 notification added, add the component to the result.
+ result.push({
+ displayname: component.displayname,
+ notifications,
+ });
+ }
+ });
+
+ return result;
+ }
+
}
export class AddonNotificationsHelper extends makeSingleton(AddonNotificationsHelperProvider) {}
+
+/**
+ * Preferences with some calculated data.
+ */
+export type AddonNotificationsPreferencesFormatted = Omit & {
+ processors: AddonNotificationsPreferencesProcessorFormatted[]; // Config form values.
+ components: AddonNotificationsPreferencesComponentFormatted[]; // Available components.
+};
+
+/**
+ * Preferences component with some calculated data.
+ */
+export type AddonNotificationsPreferencesComponentFormatted = Omit & {
+ notifications: AddonNotificationsPreferencesNotificationFormatted[]; // List of notificaitons for the component.
+};
+
+/**
+ * Preferences notification with some calculated data.
+ */
+export type AddonNotificationsPreferencesNotificationFormatted = AddonNotificationsPreferencesNotification & {
+ processorsByName?: Record; // Calculated in the app.
+};
+
+/**
+ * Preferences processor with some calculated data.
+ */
+export type AddonNotificationsPreferencesProcessorFormatted = AddonNotificationsPreferencesProcessor & {
+ supported?: boolean; // Calculated in the app. Whether the processor is supported in the app.
+};
diff --git a/src/core/features/settings/pages/site/site.ts b/src/core/features/settings/pages/site/site.ts
index ca5bc7b65..79efc59bb 100644
--- a/src/core/features/settings/pages/site/site.ts
+++ b/src/core/features/settings/pages/site/site.ts
@@ -26,6 +26,7 @@ import { CoreSettingsHelper, CoreSiteSpaceUsage } from '../../services/settings-
import { CoreApp } from '@services/app';
import { CoreSiteInfo } from '@classes/site';
import { Translate } from '@singletons';
+import { CoreNavHelper } from '@services/nav-helper';
/**
* Page that displays the list of site settings pages.
@@ -182,7 +183,7 @@ export class CoreSitePreferencesPage implements OnInit, OnDestroy {
openHandler(page: string, params?: Params): void {
this.selectedPage = page;
// this.splitviewCtrl.push(page, params);
- this.router.navigate([page], { relativeTo: this.route, queryParams: params });
+ CoreNavHelper.instance.goInCurrentMainMenuTab(page, params);
}
/**
diff --git a/src/core/features/settings/services/settings-helper.ts b/src/core/features/settings/services/settings-helper.ts
index 3e442ed16..a42e5d872 100644
--- a/src/core/features/settings/services/settings-helper.ts
+++ b/src/core/features/settings/services/settings-helper.ts
@@ -184,66 +184,23 @@ export class CoreSettingsHelperProvider {
* @param name Name of the processor to get.
* @param fallback True to return first processor if not found, false to not return any. Defaults to true.
* @return Processor.
- * @todo typings
+ * @deprecated since 3.9.5. This function has been moved to AddonNotificationsHelperProvider.
*/
- getProcessor(processors: any[], name: string, fallback: boolean = true): any {
- if (!processors || !processors.length) {
- return;
- }
-
- const processor = processors.find((processor) => processor.name == name);
- if (processor) {
- return processor;
- }
-
- // Processor not found, return first if requested.
- if (fallback) {
- return processors[0];
- }
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ getProcessor(processors: unknown[], name: string, fallback: boolean = true): undefined {
+ return;
}
/**
* Return the components and notifications that have a certain processor.
*
- * @param processor Name of the processor to filter.
+ * @param processorName Name of the processor to filter.
* @param components Array of components.
* @return Filtered components.
- * @todo
+ * @deprecated since 3.9.5. This function has been moved to AddonNotificationsHelperProvider.
*/
- getProcessorComponents(processor: string, components: any[]): any[] {
- return processor? components : [];
- /*
- const result = [];
-
- components.forEach((component) => {
- // Create a copy of the component with an empty list of notifications.
- const componentCopy = CoreUtils.instance.clone(component);
- componentCopy.notifications = [];
-
- component.notifications.forEach((notification) => {
- let hasProcessor = false;
- for (let i = 0; i < notification.processors.length; i++) {
- const proc = notification.processors[i];
- if (proc.name == processor) {
- hasProcessor = true;
- notification.currentProcessor = proc;
- break;
- }
- }
-
- if (hasProcessor) {
- // Add the notification.
- componentCopy.notifications.push(notification);
- }
- });
-
- if (componentCopy.notifications.length) {
- // At least 1 notification added, add the component to the result.
- result.push(componentCopy);
- }
- });
-
- return result;*/
+ getProcessorComponents(processorName: string, components: unknown[]): unknown[] {
+ return components;
}
/**
diff --git a/src/core/singletons/events.ts b/src/core/singletons/events.ts
index 745e3d058..6c6029688 100644
--- a/src/core/singletons/events.ts
+++ b/src/core/singletons/events.ts
@@ -263,3 +263,11 @@ export type CoreEventFormActionData = CoreEventSiteData & {
form: HTMLElement; // Form element.
online?: boolean; // Whether the data was sent to server or not. Only when submitting.
};
+
+
+/**
+ * Data passed to NOTIFICATION_SOUND_CHANGED event.
+ */
+export type CoreEventNotificationSoundChangedData = CoreEventSiteData & {
+ enabled: boolean;
+};
diff --git a/src/theme/app.scss b/src/theme/app.scss
index 12f1c8a3e..8ef64fe3e 100644
--- a/src/theme/app.scss
+++ b/src/theme/app.scss
@@ -167,6 +167,11 @@ ion-toolbar {
--border-color: var(--ion-color-danger);
}
+// Extra text colors.
+.text-gray {
+ color: var(--gray-dark);
+}
+
// Card styles
// Message cards.
diff --git a/src/theme/variables.scss b/src/theme/variables.scss
index 6e85594df..58a18f945 100644
--- a/src/theme/variables.scss
+++ b/src/theme/variables.scss
@@ -91,6 +91,7 @@
--ion-text-color: #3a3a3a;
--ion-text-color-rgb: 58,58,58;
+ --ion-card-color: var(--ion-text-color);
ion-content {
--background: var(--gray-light);