MOBILE-3766 more: Allow defining items in config
parent
ed2b17cf9b
commit
56112c0f81
|
@ -15,6 +15,7 @@
|
||||||
/* eslint-disable @typescript-eslint/naming-convention */
|
/* eslint-disable @typescript-eslint/naming-convention */
|
||||||
|
|
||||||
import { CoreColorScheme, CoreZoomLevel } from '@features/settings/services/settings-helper';
|
import { CoreColorScheme, CoreZoomLevel } from '@features/settings/services/settings-helper';
|
||||||
|
import { CoreMainMenuLocalizedCustomItem } from '@features/mainmenu/services/mainmenu';
|
||||||
import { CoreSitesDemoSiteData } from '@services/sites';
|
import { CoreSitesDemoSiteData } from '@services/sites';
|
||||||
import envJson from '@/assets/env.json';
|
import envJson from '@/assets/env.json';
|
||||||
import { OpenFileAction } from '@services/utils/utils';
|
import { OpenFileAction } from '@services/utils/utils';
|
||||||
|
@ -175,6 +176,7 @@ export interface EnvironmentConfig {
|
||||||
displayqronsitescreen?: boolean;
|
displayqronsitescreen?: boolean;
|
||||||
forceOpenLinksIn: 'app' | 'browser';
|
forceOpenLinksIn: 'app' | 'browser';
|
||||||
iOSDefaultOpenFileAction?: OpenFileAction;
|
iOSDefaultOpenFileAction?: OpenFileAction;
|
||||||
|
customMainMenuItems?: CoreMainMenuLocalizedCustomItem[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface EnvironmentBuild {
|
export interface EnvironmentBuild {
|
||||||
|
|
|
@ -15,11 +15,13 @@
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
import { CoreApp } from '@services/app';
|
import { CoreApp } from '@services/app';
|
||||||
import { CoreLang } from '@services/lang';
|
import { CoreLang, CoreLangLanguage } from '@services/lang';
|
||||||
import { CoreSites } from '@services/sites';
|
import { CoreSites } from '@services/sites';
|
||||||
import { CoreConstants } from '@/core/constants';
|
import { CoreConstants } from '@/core/constants';
|
||||||
import { CoreMainMenuDelegate, CoreMainMenuHandlerToDisplay } from './mainmenu-delegate';
|
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.
|
* 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());
|
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<CoreMainMenuCustomItem[]> {
|
||||||
|
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.
|
* Get a list of custom menu items for a certain site.
|
||||||
*
|
*
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return List of custom menu items.
|
* @return List of custom menu items.
|
||||||
*/
|
*/
|
||||||
async getCustomMenuItems(siteId?: string): Promise<CoreMainMenuCustomItem[]> {
|
protected async getCustomMenuItemsFromSite(siteId?: string): Promise<CoreMainMenuCustomItem[]> {
|
||||||
const site = await CoreSites.getSite(siteId);
|
const site = await CoreSites.getSite(siteId);
|
||||||
|
|
||||||
const itemsString = site.getStoredConfig('tool_mobile_custommenuitems');
|
const itemsString = site.getStoredConfig('tool_mobile_custommenuitems');
|
||||||
|
@ -148,6 +165,45 @@ export class CoreMainMenuProvider {
|
||||||
return result.filter((entry) => typeof entry != 'undefined');
|
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<CoreMainMenuCustomItem[]> {
|
||||||
|
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.
|
* Get the number of items to be shown on the main menu bar.
|
||||||
*
|
*
|
||||||
|
@ -264,6 +320,13 @@ export interface CoreMainMenuCustomItem {
|
||||||
icon: string;
|
icon: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom main menu item with localized text.
|
||||||
|
*/
|
||||||
|
export type CoreMainMenuLocalizedCustomItem = Omit<CoreMainMenuCustomItem, 'label'> & {
|
||||||
|
label: string | Record<CoreLangLanguage, string>;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map of custom menu items.
|
* Map of custom menu items.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -480,7 +480,7 @@ export class CoreLangProvider {
|
||||||
* @param lang Language code.
|
* @param lang Language code.
|
||||||
* @return Promise resolved with the file contents.
|
* @return Promise resolved with the file contents.
|
||||||
*/
|
*/
|
||||||
async readLangFile(lang: string): Promise<Record<string, string>> {
|
async readLangFile(lang: CoreLangLanguage): Promise<Record<string, string>> {
|
||||||
const observable = Http.get(`assets/lang/${lang}.json`, {
|
const observable = Http.get(`assets/lang/${lang}.json`, {
|
||||||
responseType: 'json',
|
responseType: 'json',
|
||||||
});
|
});
|
||||||
|
@ -519,6 +519,11 @@ export class CoreLangProvider {
|
||||||
|
|
||||||
export const CoreLang = makeSingleton(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.
|
* Language object has two leves, first per language and second per string key.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -93,4 +93,19 @@ describe('CoreTextUtilsProvider', () => {
|
||||||
expect(textUtils.matchesGlob('/foo/bar/baz', '/foo/ba?/ba?')).toBe(true);
|
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');
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -731,6 +731,28 @@ export class CoreTextUtilsProvider {
|
||||||
return text.replace(/[#:/?\\]+/g, '_');
|
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<string, string> = {}, 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.
|
* Replace all the new lines on a certain text.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue