From a71aa53daee8ec438c54a23a86113f0be31b82cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Wed, 4 Nov 2020 17:37:02 +0100 Subject: [PATCH] MOBILE-3565 settings: Add general settings page --- config/config.json | 6 +- .../core/settings/pages/general/general.html | 65 +++ .../pages/general/general.page.module.ts | 48 ++ .../settings/pages/general/general.page.ts | 168 +++++++ .../core/settings/pages/general/general.scss | 5 + .../core/settings/services/settings.helper.ts | 431 ++++++++++++++++++ src/app/core/settings/settings-init.module.ts | 21 +- .../core/settings/settings-routing.module.ts | 4 + src/app/services/app.ts | 3 +- src/app/services/utils/dom.ts | 7 +- src/theme/app.scss | 18 + src/theme/variables.scss | 15 + src/types/global.d.ts | 3 +- 13 files changed, 781 insertions(+), 13 deletions(-) create mode 100644 src/app/core/settings/pages/general/general.html create mode 100644 src/app/core/settings/pages/general/general.page.module.ts create mode 100644 src/app/core/settings/pages/general/general.page.ts create mode 100644 src/app/core/settings/pages/general/general.scss create mode 100644 src/app/core/settings/services/settings.helper.ts diff --git a/config/config.json b/config/config.json index 1a9e41f39..4c11f9b4f 100644 --- a/config/config.json +++ b/config/config.json @@ -84,9 +84,9 @@ } }, "font_sizes": [ - 62.5, - 75.89, - 93.75 + 100, + 110, + 120 ], "customurlscheme": "moodlemobile", "siteurl": "", diff --git a/src/app/core/settings/pages/general/general.html b/src/app/core/settings/pages/general/general.html new file mode 100644 index 000000000..141186cd4 --- /dev/null +++ b/src/app/core/settings/pages/general/general.html @@ -0,0 +1,65 @@ + + + + + + + {{ 'core.settings.general' | translate }} + + + + + +

{{ 'core.settings.language' | translate }}

+
+ + {{ entry.name }} + +
+ + +

{{ 'core.settings.fontsize' | translate }}

+
+ + + {{ 'core.settings.fontsizecharacter' | translate }} + + + + +
+ + +

{{ 'core.settings.colorscheme' | translate }}

+

{{ 'core.settings.forcedsetting' | translate }}

+
+ + + {{ 'core.settings.colorscheme-' + scheme | translate }} + +
+ + +

{{ 'core.settings.enablerichtexteditor' | translate }}

+

{{ 'core.settings.enablerichtexteditordescription' | translate }}

+
+ +
+ + +

{{ 'core.settings.debugdisplay' | translate }}

+

{{ 'core.settings.debugdisplaydescription' | translate }}

+
+ +
+ + +

{{ 'core.settings.enablefirebaseanalytics' | translate }}

+

{{ 'core.settings.enablefirebaseanalyticsdescription' | translate }}

+
+ +
+
diff --git a/src/app/core/settings/pages/general/general.page.module.ts b/src/app/core/settings/pages/general/general.page.module.ts new file mode 100644 index 000000000..923e5cb1d --- /dev/null +++ b/src/app/core/settings/pages/general/general.page.module.ts @@ -0,0 +1,48 @@ +// (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 { RouterModule, Routes } from '@angular/router'; +import { IonicModule } from '@ionic/angular'; +import { TranslateModule } from '@ngx-translate/core'; +import { FormsModule } from '@angular/forms'; + +import { CoreComponentsModule } from '@components/components.module'; +import { CoreDirectivesModule } from '@directives/directives.module'; + +import { CoreSettingsGeneralPage } from './general.page'; + +const routes: Routes = [ + { + path: '', + component: CoreSettingsGeneralPage, + }, +]; + +@NgModule({ + declarations: [ + CoreSettingsGeneralPage, + ], + imports: [ + RouterModule.forChild(routes), + CommonModule, + IonicModule, + TranslateModule.forChild(), + CoreComponentsModule, + CoreDirectivesModule, + FormsModule, + ], +}) +export class CoreSettingsGeneralPageModule {} diff --git a/src/app/core/settings/pages/general/general.page.ts b/src/app/core/settings/pages/general/general.page.ts new file mode 100644 index 000000000..797af5c78 --- /dev/null +++ b/src/app/core/settings/pages/general/general.page.ts @@ -0,0 +1,168 @@ +// (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 } from '@angular/core'; +import { CoreConstants } from '@core/constants'; +import { CoreConfig } from '@services/config'; +import { CoreEvents } from '@singletons/events'; +import { CoreLang } from '@services/lang'; +import { CoreDomUtils } from '@services/utils/dom'; +// import { CorePushNotifications } from '@core/pushnotifications/services/pushnotifications'; +import { CoreSettingsHelper, CoreColorScheme } from '../../services/settings.helper'; + +/** + * Page that displays the general settings. + */ +@Component({ + selector: 'page-core-settings-general', + templateUrl: 'general.html', + styleUrls: ['general.scss'], +}) +export class CoreSettingsGeneralPage { + + languages: { code: string; name: string }[] = []; + selectedLanguage = ''; + fontSizes: { size: number; style: number; selected: boolean }[] = []; + selectedFontSize = 0; + richTextEditor = true; + debugDisplay = false; + analyticsSupported = false; + analyticsEnabled = false; + colorSchemes: CoreColorScheme[] = []; + selectedScheme: CoreColorScheme = CoreColorScheme.LIGHT; + colorSchemeDisabled = false; + + constructor() { + this.asyncInit(); + } + + /** + * Async part of the constructor. + */ + protected async asyncInit(): Promise { + + // Get the supported languages. + const languages = CoreConstants.CONFIG.languages; + for (const code in languages) { + this.languages.push({ + code: code, + name: languages[code], + }); + } + // Sort them by name. + this.languages.sort((a, b) => a.name.localeCompare(b.name)); + this.selectedLanguage = await CoreLang.instance.getCurrentLanguage(); + + // Configure color schemes. + if (!CoreConstants.CONFIG.forceColorScheme) { + this.colorSchemeDisabled = CoreSettingsHelper.instance.isColorSchemeDisabledInSite(); + + if (this.colorSchemeDisabled) { + this.colorSchemes.push(CoreColorScheme.LIGHT); + this.selectedScheme = this.colorSchemes[0]; + } else { + this.colorSchemes.push(CoreColorScheme.LIGHT); + this.colorSchemes.push(CoreColorScheme.DARK); + + if (window.matchMedia('(prefers-color-scheme: dark)').matches || + window.matchMedia('(prefers-color-scheme: light)').matches) { + this.colorSchemes.push(CoreColorScheme.AUTO); + } + + this.selectedScheme = await CoreConfig.instance.get(CoreConstants.SETTINGS_COLOR_SCHEME, CoreColorScheme.LIGHT); + } + } + + this.selectedFontSize = await CoreConfig.instance.get( + CoreConstants.SETTINGS_FONT_SIZE, + CoreConstants.CONFIG.font_sizes[0], + ); + + this.fontSizes = CoreConstants.CONFIG.font_sizes.map((size) => + ({ + size, + // Absolute pixel size based on 1.4rem body text when this size is selected. + style: Math.round(size * 16 / 100), + selected: size === this.selectedFontSize, + })); + + + this.richTextEditor = await CoreConfig.instance.get(CoreConstants.SETTINGS_RICH_TEXT_EDITOR, true); + + this.debugDisplay = await CoreConfig.instance.get(CoreConstants.SETTINGS_DEBUG_DISPLAY, false); + + this.analyticsSupported = CoreConstants.CONFIG.enableanalytics; + if (this.analyticsSupported) { + this.analyticsEnabled = await CoreConfig.instance.get(CoreConstants.SETTINGS_ANALYTICS_ENABLED, true); + } + } + + /** + * Called when a new language is selected. + */ + languageChanged(): void { + CoreLang.instance.changeCurrentLanguage(this.selectedLanguage).finally(() => { + CoreEvents.trigger(CoreEvents.LANGUAGE_CHANGED, this.selectedLanguage); + }); + } + + /** + * Called when a new font size is selected. + */ + fontSizeChanged(): void { + this.fontSizes = this.fontSizes.map((fontSize) => { + fontSize.selected = fontSize.size === this.selectedFontSize; + + return fontSize; + }); + + CoreSettingsHelper.instance.setFontSize(this.selectedFontSize); + CoreConfig.instance.set(CoreConstants.SETTINGS_FONT_SIZE, this.selectedFontSize); + } + + /** + * Called when a new color scheme is selected. + */ + colorSchemeChanged(): void { + CoreSettingsHelper.instance.setColorScheme(this.selectedScheme); + CoreConfig.instance.set(CoreConstants.SETTINGS_COLOR_SCHEME, this.selectedScheme); + } + + /** + * Called when the rich text editor is enabled or disabled. + */ + richTextEditorChanged(): void { + CoreConfig.instance.set(CoreConstants.SETTINGS_RICH_TEXT_EDITOR, this.richTextEditor ? 1 : 0); + } + + /** + * Called when the debug display setting is enabled or disabled. + */ + debugDisplayChanged(): void { + CoreConfig.instance.set(CoreConstants.SETTINGS_DEBUG_DISPLAY, this.debugDisplay ? 1 : 0); + CoreDomUtils.instance.setDebugDisplay(this.debugDisplay); + } + + /** + * Called when the analytics setting is enabled or disabled. + * + * @todo + */ + async analyticsEnabledChanged(): Promise { + // await this.pushNotificationsProvider.enableAnalytics(this.analyticsEnabled); + + CoreConfig.instance.set(CoreConstants.SETTINGS_ANALYTICS_ENABLED, this.analyticsEnabled ? 1 : 0); + } + +} diff --git a/src/app/core/settings/pages/general/general.scss b/src/app/core/settings/pages/general/general.scss new file mode 100644 index 000000000..7c37341d2 --- /dev/null +++ b/src/app/core/settings/pages/general/general.scss @@ -0,0 +1,5 @@ +:host { + .core-settings-general-font-size ion-segment { + max-width: 250px; + } +} diff --git a/src/app/core/settings/services/settings.helper.ts b/src/app/core/settings/services/settings.helper.ts new file mode 100644 index 000000000..840300527 --- /dev/null +++ b/src/app/core/settings/services/settings.helper.ts @@ -0,0 +1,431 @@ +// (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 { CoreApp } from '@services/app'; +import { CoreCron } from '@services/cron'; +import { CoreEvents } from '@singletons/events'; +import { CoreFilepool } from '@services/filepool'; +import { CoreSite } from '@classes/site'; +import { CoreSites } from '@services/sites'; +import { CoreUtils } from '@services/utils/utils'; +import { CoreConstants } from '@core/constants'; +import { CoreConfig } from '@services/config'; +// import { CoreFilterProvider } from '@core/filter/providers/filter'; +import { CoreDomUtils } from '@services/utils/dom'; +// import { CoreCourseProvider } from '@core/course/providers/course'; +import { makeSingleton, Translate } from '@singletons/core.singletons'; + +/** + * Object with space usage and cache entries that can be erased. + */ +export interface CoreSiteSpaceUsage { + cacheEntries?: number; // Number of cached entries that can be cleared. + spaceUsage?: number; // Space used in this site (total files + estimate of cache). +} + +/** + * Constants to define color schemes. + */ +export const enum CoreColorScheme { + AUTO = 'auto', + LIGHT = 'light', + DARK = 'dark', +} + +/** + * Settings helper service. + */ +@Injectable({ + providedIn: 'root', +}) +export class CoreSettingsHelperProvider { + + protected syncPromises: { [s: string]: Promise } = {}; + protected prefersDark?: MediaQueryList; + + constructor() { + // protected filterProvider: CoreFilterProvider, + // protected courseProvider: CoreCourseProvider, + + if (!CoreConstants.CONFIG.forceColorScheme) { + // Update color scheme when a user enters or leaves a site, or when the site info is updated. + const applySiteScheme = (): void => { + if (this.isColorSchemeDisabledInSite()) { + // Dark mode is disabled, force light mode. + this.setColorScheme(CoreColorScheme.LIGHT); + } else { + this.prefersDark = window.matchMedia('(prefers-color-scheme: dark)'); + // Reset color scheme settings. + this.initColorScheme(); + } + }; + + CoreEvents.on(CoreEvents.LOGIN, applySiteScheme.bind(this)); + + CoreEvents.on(CoreEvents.SITE_UPDATED, applySiteScheme.bind(this)); + + CoreEvents.on(CoreEvents.LOGOUT, () => { + // Reset color scheme settings. + this.initColorScheme(); + }); + } + } + + /** + * Deletes files of a site and the tables that can be cleared. + * + * @param siteName Site Name. + * @param siteId: Site ID. + * @return Resolved with detailed new info when done. + * @todo filterProvider and courseProviderpart. + */ + async deleteSiteStorage(siteName: string, siteId: string): Promise { + const siteInfo: CoreSiteSpaceUsage = { + cacheEntries: 0, + spaceUsage: 0, + }; + + // siteName = await this.filterProvider.formatText(siteName, { clean: true, singleLine: true, filter: false }, [], siteId); + + const title = Translate.instance.instant('core.settings.deletesitefilestitle'); + const message = Translate.instance.instant('core.settings.deletesitefiles', { sitename: siteName }); + + await CoreDomUtils.instance.showConfirm(message, title); + + const site = await CoreSites.instance.getSite(siteId); + + // Clear cache tables. + const cleanSchemas = CoreSites.instance.getSiteTableSchemasToClear(site); + const promises: Promise[] = cleanSchemas.map((name) => site.getDb().deleteRecords(name)); + const filepoolService = CoreFilepool.instance; + + + promises.push(site.deleteFolder().then(() => { + filepoolService.clearAllPackagesStatus(siteId); + filepoolService.clearFilepool(siteId); + // this.courseProvider.clearAllCoursesStatus(siteId); + + siteInfo.spaceUsage = 0; + + return; + }).catch(async (error) => { + if (error && error.code === FileError.NOT_FOUND_ERR) { + // Not found, set size 0. + filepoolService.clearAllPackagesStatus(siteId); + siteInfo.spaceUsage = 0; + } else { + // Error, recalculate the site usage. + CoreDomUtils.instance.showErrorModal('core.settings.errordeletesitefiles', true); + + siteInfo.spaceUsage = await site.getSpaceUsage(); + } + }).then(async () => { + CoreEvents.trigger(CoreEvents.SITE_STORAGE_DELETED, {}, siteId); + + siteInfo.cacheEntries = await this.calcSiteClearRows(site); + + return; + })); + + await Promise.all(promises); + + return siteInfo; + } + + /** + * Calculates each site's usage, and the total usage. + * + * @param siteId ID of the site. Current site if undefined. + * @return Resolved with detailed info when done. + */ + async getSiteSpaceUsage(siteId?: string): Promise { + const site = await CoreSites.instance.getSite(siteId); + + // Get space usage. + const siteInfo: CoreSiteSpaceUsage = { + cacheEntries: 0, + spaceUsage: 0, + }; + + siteInfo.cacheEntries = await this.calcSiteClearRows(site); + siteInfo.spaceUsage = await site.getTotalUsage(); + + return siteInfo; + } + + /** + * Calculate the number of rows to be deleted on a site. + * + * @param site Site object. + * @return If there are rows to delete or not. + */ + protected async calcSiteClearRows(site: CoreSite): Promise { + const clearTables = CoreSites.instance.getSiteTableSchemasToClear(site); + + let totalEntries = 0; + + await Promise.all(clearTables.map(async (name) => + totalEntries = await site.getDb().countRecords(name) + totalEntries)); + + return totalEntries; + } + + /** + * 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. + * @todo + */ + getProcessor(processors: any[], name: string, fallback: boolean = true): any { + if (!processors || !processors.length) { + return; + } + for (let i = 0; i < processors.length; i++) { + if (processors[i].name == name) { + return processors[i]; + } + } + + // Processor not found, return first if requested. + if (fallback) { + return processors[0]; + } + } + + /** + * Return the components and notifications that have a certain processor. + * + * @param processor Name of the processor to filter. + * @param components Array of components. + * @return Filtered components. + * @todo + */ + 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;*/ + } + + /** + * Get the synchronization promise of a site. + * + * @param siteId ID of the site. + * @return Sync promise or null if site is not being syncrhonized. + */ + async getSiteSyncPromise(siteId: string): Promise { + if (this.syncPromises[siteId]) { + return this.syncPromises[siteId]; + } + } + + /** + * Synchronize a site. + * + * @param syncOnlyOnWifi True to sync only on wifi, false otherwise. + * @param siteId ID of the site to synchronize. + * @return Promise resolved when synchronized, rejected if failure. + */ + async synchronizeSite(syncOnlyOnWifi: boolean, siteId: string): Promise { + if (this.syncPromises[siteId]) { + // There's already a sync ongoing for this site, return the promise. + return this.syncPromises[siteId]; + } + + const site = await CoreSites.instance.getSite(siteId); + const hasSyncHandlers = CoreCron.instance.hasManualSyncHandlers(); + + if (site.isLoggedOut()) { + // Cannot sync logged out sites. + throw Translate.instance.instant('core.settings.cannotsyncloggedout'); + } else if (hasSyncHandlers && !CoreApp.instance.isOnline()) { + // We need connection to execute sync. + throw Translate.instance.instant('core.settings.cannotsyncoffline'); + } else if (hasSyncHandlers && syncOnlyOnWifi && CoreApp.instance.isNetworkAccessLimited()) { + throw Translate.instance.instant('core.settings.cannotsyncwithoutwifi'); + } + + const syncPromise = Promise.all([ + // Invalidate all the site files so they are re-downloaded. + CoreUtils.instance.ignoreErrors(CoreFilepool.instance.invalidateAllFiles(siteId)), + // Invalidate and synchronize site data. + site.invalidateWsCache(), + this.checkSiteLocalMobile(site), + CoreSites.instance.updateSiteInfo(site.getId()), + CoreCron.instance.forceSyncExecution(site.getId()), + // eslint-disable-next-line arrow-body-style + ]).then(() => { + return; + }); + + this.syncPromises[siteId] = syncPromise; + + try { + await syncPromise; + } finally { + delete this.syncPromises[siteId]; + } + } + + /** + * Check if local_mobile was added to the site. + * + * @param site Site to check. + * @return Promise resolved if no action needed. + */ + protected async checkSiteLocalMobile(site: CoreSite): Promise { + try { + // Check if local_mobile was installed in Moodle. + await site.checkIfLocalMobileInstalledAndNotUsed(); + } catch { + // Not added, nothing to do. + return; + } + + // Local mobile was added. Throw invalid session to force reconnect and create a new token. + CoreEvents.trigger(CoreEvents.SESSION_EXPIRED, {}, site.getId()); + + throw Translate.instance.instant('core.lostconnection'); + } + + /** + * Init Settings related to DOM. + */ + async initDomSettings(): Promise { + // Set the font size based on user preference. + const fontSize = await CoreConfig.instance.get(CoreConstants.SETTINGS_FONT_SIZE, CoreConstants.CONFIG.font_sizes[0]); + this.setFontSize(fontSize); + + this.initColorScheme(); + } + + /** + * Init the color scheme. + */ + async initColorScheme(): Promise { + if (CoreConstants.CONFIG.forceColorScheme) { + this.setColorScheme(CoreConstants.CONFIG.forceColorScheme); + } else { + const scheme = await CoreConfig.instance.get(CoreConstants.SETTINGS_COLOR_SCHEME, CoreColorScheme.LIGHT); + this.setColorScheme(scheme); + } + } + + /** + * Check if color scheme is disabled in a site. + * + * @param siteId Site ID. If not defined, current site. + * @return Promise resolved with whether color scheme is disabled. + */ + async isColorSchemeDisabled(siteId?: string): Promise { + const site = await CoreSites.instance.getSite(siteId); + + return this.isColorSchemeDisabledInSite(site); + } + + /** + * Check if color scheme is disabled in a site. + * + * @param site Site instance. If not defined, current site. + * @return Whether color scheme is disabled. + */ + isColorSchemeDisabledInSite(site?: CoreSite): boolean { + site = site || CoreSites.instance.getCurrentSite(); + + return site ? site.isFeatureDisabled('NoDelegate_DarkMode') : false; + } + + /** + * Set document default font size. + * + * @param fontSize Font size in percentage. + */ + setFontSize(fontSize: number): void { + // @todo Since zoom is deprecated and fontSize is not working, we should do some research here. + document.documentElement.style.zoom = fontSize + '%'; + } + + /** + * Set body color scheme. + * + * @param colorScheme Name of the color scheme. + */ + setColorScheme(colorScheme: CoreColorScheme): void { + if (colorScheme == CoreColorScheme.AUTO && this.prefersDark) { + // Listen for changes to the prefers-color-scheme media query. + this.prefersDark.addEventListener('change', this.toggleDarkModeListener); + + this.toggleDarkMode(this.prefersDark.matches); + } else { + // Stop listening to changes. + this.prefersDark?.removeEventListener('change', this.toggleDarkModeListener); + + this.toggleDarkMode(colorScheme == CoreColorScheme.DARK); + } + } + + /** + * Listener function to toggle dark mode. + * + * @param e Event object. + */ + protected toggleDarkModeListener = (e: MediaQueryListEvent): void => { + document.body.classList.toggle('dark', e.matches); + }; + + /** + * Toggles dark mode based on enabled boolean. + * + * @param enable True to enable dark mode, false to disable. + */ + protected toggleDarkMode(enable: boolean = false): void { + document.body.classList.toggle('dark', enable); + } + +} + +export class CoreSettingsHelper extends makeSingleton(CoreSettingsHelperProvider) {} diff --git a/src/app/core/settings/settings-init.module.ts b/src/app/core/settings/settings-init.module.ts index 6810084e2..2f0534f1d 100644 --- a/src/app/core/settings/settings-init.module.ts +++ b/src/app/core/settings/settings-init.module.ts @@ -16,6 +16,7 @@ import { NgModule } from '@angular/core'; import { Routes } from '@angular/router'; import { CoreMainMenuRoutingModule } from '@core/mainmenu/mainmenu-routing.module'; +import { CoreSettingsHelperProvider } from './services/settings.helper'; const routes: Routes = [ { @@ -25,7 +26,21 @@ const routes: Routes = [ ]; @NgModule({ - imports: [CoreMainMenuRoutingModule.forChild(routes)], - exports: [CoreMainMenuRoutingModule], + imports: [ + CoreMainMenuRoutingModule.forChild(routes), + ], + exports: [ + CoreMainMenuRoutingModule, + ], + providers: [ + CoreSettingsHelperProvider, + ], }) -export class CoreSettingsInitModule {} +export class CoreSettingsInitModule { + + constructor(settingsHelper: CoreSettingsHelperProvider) { + // @todo + // settingsHelper.initDomSettings(); + } + +} diff --git a/src/app/core/settings/settings-routing.module.ts b/src/app/core/settings/settings-routing.module.ts index 20554d6dd..aa46c4a83 100644 --- a/src/app/core/settings/settings-routing.module.ts +++ b/src/app/core/settings/settings-routing.module.ts @@ -20,6 +20,10 @@ const routes: Routes = [ path: 'about', loadChildren: () => import('./pages/about/about.page.module').then( m => m.CoreSettingsAboutPageModule), }, + { + path: 'general', + loadChildren: () => import('./pages/general/general.page.module').then( m => m.CoreSettingsGeneralPageModule), + }, { path: '', loadChildren: () => import('./pages/app/app.page.module').then( m => m.CoreSettingsAppPageModule), diff --git a/src/app/services/app.ts b/src/app/services/app.ts index 489cb067f..a8efc544e 100644 --- a/src/app/services/app.ts +++ b/src/app/services/app.ts @@ -101,6 +101,7 @@ export class CoreAppProvider { }); }); + // @todo // this.platform.registerBackButtonAction(() => { // this.backButtonAction(); // }, 100); @@ -157,7 +158,7 @@ export class CoreAppProvider { async createTablesFromSchema(schema: CoreAppSchema): Promise { this.logger.debug(`Apply schema to app DB: ${schema.name}`); - let oldVersion; + let oldVersion: number; try { // Wait for the schema versions table to be created. diff --git a/src/app/services/utils/dom.ts b/src/app/services/utils/dom.ts index 8ad03ba23..d709d2925 100644 --- a/src/app/services/utils/dom.ts +++ b/src/app/services/utils/dom.ts @@ -834,17 +834,14 @@ export class CoreDomUtilsProvider { * @return Promise resolved with boolean: true if enabled, false otherwise. */ isRichTextEditorEnabled(): Promise { - if (this.isRichTextEditorSupported()) { - return CoreConfig.instance.get(CoreConstants.SETTINGS_RICH_TEXT_EDITOR, true).then((enabled) => !!enabled); - } - - return Promise.resolve(false); + return CoreConfig.instance.get(CoreConstants.SETTINGS_RICH_TEXT_EDITOR, true).then((enabled) => !!enabled); } /** * Check if rich text editor is supported in the platform. * * @return Whether it's supported. + * @deprecated since 3.9.5 */ isRichTextEditorSupported(): boolean { return true; diff --git a/src/theme/app.scss b/src/theme/app.scss index 486b5d975..3995d3bef 100644 --- a/src/theme/app.scss +++ b/src/theme/app.scss @@ -127,3 +127,21 @@ ion-avatar ion-img, ion-avatar img { background-color: --var(--gray-light); } +// Action sheet. +.md ion-action-sheet { + .action-sheet-group-cancel { + -webkit-filter: drop-shadow(0px 3px 3px rgba(var(--action-sheet-shadow-color))); + filter: drop-shadow(0px 3px 3px rgba(var(--action-sheet-shadow-color))); + } + + .action-sheet-title { + border-bottom: 1px solid var(--title-border-color); + } +} + +.ios ion-action-sheet { + .action-sheet-title { + font-size: 16px; + } +} + diff --git a/src/theme/variables.scss b/src/theme/variables.scss index 909025d64..44eb78fde 100644 --- a/src/theme/variables.scss +++ b/src/theme/variables.scss @@ -105,6 +105,21 @@ --background: var(--custom-toolbar-background, var(--ion-color-primary)); } + ion-action-sheet { + --action-sheet-shadow-color: var(--custom--action-sheet-shadow-color, 0, 0, 0, 1); + --button-color-selected: var(--custom--action-sheet-selected-color, var(--core-color)); + --title-border-color: var(--custom-title-border-color, var(--gray)); + + @media (min-height: 500px) { + --max-height: 50%; + --height: 100%; + } + + .action-sheet-cancel { + --button-color: var(--ion-color-danger); + } + } + core-tabs { --background: var(--custom-tabs-background, var(--white)); ion-slide { diff --git a/src/types/global.d.ts b/src/types/global.d.ts index 19e8234c4..7d0373840 100644 --- a/src/types/global.d.ts +++ b/src/types/global.d.ts @@ -14,6 +14,7 @@ /* eslint-disable @typescript-eslint/naming-convention */ +import { CoreColorScheme } from '@core/settings/services/settings.helper'; import { CoreSitesDemoSiteData } from '@services/sites'; declare global { @@ -59,7 +60,7 @@ declare global { statusbarlighttextremotetheme: boolean; enableanalytics: boolean; enableonboarding: boolean; - forceColorScheme: string; + forceColorScheme: CoreColorScheme; forceLoginLogo: boolean; ioswebviewscheme: string; appstores: Record;