From 56112c0f8153ba2fdf76dc4ac2bfeeaa257ff6bd Mon Sep 17 00:00:00 2001 From: Noel De Martin Date: Tue, 1 Jun 2021 12:34:51 +0200 Subject: [PATCH] MOBILE-3766 more: Allow defining items in config --- src/core/constants.ts | 2 + .../features/mainmenu/services/mainmenu.ts | 69 ++++++++++++++++++- src/core/services/lang.ts | 7 +- src/core/services/tests/utils/text.test.ts | 15 ++++ src/core/services/utils/text.ts | 22 ++++++ 5 files changed, 111 insertions(+), 4 deletions(-) diff --git a/src/core/constants.ts b/src/core/constants.ts index 68f1f721e..d9bc996ed 100644 --- a/src/core/constants.ts +++ b/src/core/constants.ts @@ -15,6 +15,7 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { CoreColorScheme, CoreZoomLevel } from '@features/settings/services/settings-helper'; +import { CoreMainMenuLocalizedCustomItem } from '@features/mainmenu/services/mainmenu'; import { CoreSitesDemoSiteData } from '@services/sites'; import envJson from '@/assets/env.json'; import { OpenFileAction } from '@services/utils/utils'; @@ -175,6 +176,7 @@ export interface EnvironmentConfig { displayqronsitescreen?: boolean; forceOpenLinksIn: 'app' | 'browser'; iOSDefaultOpenFileAction?: OpenFileAction; + customMainMenuItems?: CoreMainMenuLocalizedCustomItem[]; }; export interface EnvironmentBuild { diff --git a/src/core/features/mainmenu/services/mainmenu.ts b/src/core/features/mainmenu/services/mainmenu.ts index aa602d7d4..6dc54ec70 100644 --- a/src/core/features/mainmenu/services/mainmenu.ts +++ b/src/core/features/mainmenu/services/mainmenu.ts @@ -15,11 +15,13 @@ import { Injectable } from '@angular/core'; import { CoreApp } from '@services/app'; -import { CoreLang } from '@services/lang'; +import { CoreLang, CoreLangLanguage } from '@services/lang'; import { CoreSites } from '@services/sites'; import { CoreConstants } from '@/core/constants'; import { CoreMainMenuDelegate, CoreMainMenuHandlerToDisplay } from './mainmenu-delegate'; -import { makeSingleton } from '@singletons'; +import { Device, makeSingleton } from '@singletons'; +import { CoreArray } from '@singletons/array'; +import { CoreTextUtils } from '@services/utils/text'; /** * Service that provides some features regarding Main Menu. @@ -48,13 +50,28 @@ export class CoreMainMenuProvider { return handlers.filter(handler => !handler.onlyInMore).slice(0, this.getNumItems()); } + /** + * Get a list of custom menu items. + * + * @param siteId Site to get custom items from. + * @return List of custom menu items. + */ + async getCustomMenuItems(siteId?: string): Promise { + const customItems = await Promise.all([ + this.getCustomMenuItemsFromSite(siteId), + this.getCustomItemsFromConfig(), + ]); + + return CoreArray.flatten(customItems); + } + /** * Get a list of custom menu items for a certain site. * * @param siteId Site ID. If not defined, current site. * @return List of custom menu items. */ - async getCustomMenuItems(siteId?: string): Promise { + protected async getCustomMenuItemsFromSite(siteId?: string): Promise { const site = await CoreSites.getSite(siteId); const itemsString = site.getStoredConfig('tool_mobile_custommenuitems'); @@ -148,6 +165,45 @@ export class CoreMainMenuProvider { return result.filter((entry) => typeof entry != 'undefined'); } + /** + * Get a list of custom menu items from config. + * + * @return List of custom menu items. + */ + protected async getCustomItemsFromConfig(): Promise { + const items = CoreConstants.CONFIG.customMainMenuItems; + + if (!items) { + return []; + } + + const currentLang = await CoreLang.getCurrentLanguage(); + + const fallbackLang = CoreConstants.CONFIG.default_lang || 'en'; + const replacements = { + devicetype: '', + osversion: Device.version, + }; + + if (CoreApp.isAndroid()) { + replacements.devicetype = 'Android'; + } else if (CoreApp.isIOS()) { + replacements.devicetype = 'iPhone or iPad'; + } else { + replacements.devicetype = 'Other'; + } + + return items + .filter(item => typeof item.label === 'string' || currentLang in item.label || fallbackLang in item.label) + .map(item => ({ + ...item, + url: CoreTextUtils.replaceArguments(item.url, replacements, 'uri'), + label: typeof item.label === 'string' + ? item.label + : item.label[currentLang] ?? item.label[fallbackLang], + })); + } + /** * Get the number of items to be shown on the main menu bar. * @@ -264,6 +320,13 @@ export interface CoreMainMenuCustomItem { icon: string; } +/** + * Custom main menu item with localized text. + */ +export type CoreMainMenuLocalizedCustomItem = Omit & { + label: string | Record; +}; + /** * Map of custom menu items. */ diff --git a/src/core/services/lang.ts b/src/core/services/lang.ts index 8e64effeb..7d9f54ebe 100644 --- a/src/core/services/lang.ts +++ b/src/core/services/lang.ts @@ -480,7 +480,7 @@ export class CoreLangProvider { * @param lang Language code. * @return Promise resolved with the file contents. */ - async readLangFile(lang: string): Promise> { + async readLangFile(lang: CoreLangLanguage): Promise> { const observable = Http.get(`assets/lang/${lang}.json`, { responseType: 'json', }); @@ -519,6 +519,11 @@ export class CoreLangProvider { export const CoreLang = makeSingleton(CoreLangProvider); +/** + * Language code. E.g. 'au', 'es', etc. + */ +export type CoreLangLanguage = string; + /** * Language object has two leves, first per language and second per string key. */ diff --git a/src/core/services/tests/utils/text.test.ts b/src/core/services/tests/utils/text.test.ts index 1c968dbce..19ba8fd6e 100644 --- a/src/core/services/tests/utils/text.test.ts +++ b/src/core/services/tests/utils/text.test.ts @@ -93,4 +93,19 @@ describe('CoreTextUtilsProvider', () => { expect(textUtils.matchesGlob('/foo/bar/baz', '/foo/ba?/ba?')).toBe(true); }); + it('replaces arguments', () => { + // Arrange + const url = 'http://campus.edu?device={{device}}&version={{version}}'; + const replacements = { + device: 'iPhone or iPad', + version: '1.2.3', + }; + + // Act + const replaced = textUtils.replaceArguments(url, replacements, 'uri'); + + // Assert + expect(replaced).toEqual('http://campus.edu?device=iPhone%20or%20iPad&version=1.2.3'); + }); + }); diff --git a/src/core/services/utils/text.ts b/src/core/services/utils/text.ts index cc8428e69..2045bc547 100644 --- a/src/core/services/utils/text.ts +++ b/src/core/services/utils/text.ts @@ -731,6 +731,28 @@ export class CoreTextUtilsProvider { return text.replace(/[#:/?\\]+/g, '_'); } + /** + * Replace {{ARGUMENT}} arguments in the text. + * + * @param text Text to treat. + * @param replacements Argument values. + * @param encoding Encoding to use in values. + * @returns Treated text. + */ + replaceArguments(text: string, replacements: Record = {}, encoding?: 'uri'): string { + let match; + + while ((match = text.match(/\{\{([^}]+)\}\}/))) { + const argument = match[1].trim(); + const value = replacements[argument] ?? ''; + const encodedValue = encoding ? encodeURIComponent(value) : value; + + text = text.replace(`{{${argument}}}`, encodedValue); + } + + return text; + } + /** * Replace all the new lines on a certain text. *