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 }} + + + + + + + + + {{ 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);