2024-05-02 21:37:11 +03:00
|
|
|
///
|
2025-02-25 09:39:16 +02:00
|
|
|
/// Copyright © 2016-2025 The Thingsboard Authors
|
2024-05-02 21:37:11 +03:00
|
|
|
///
|
|
|
|
|
/// 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.
|
|
|
|
|
///
|
|
|
|
|
|
2024-05-15 17:26:36 +03:00
|
|
|
import { HasTenantId } from '@shared/models/entity.models';
|
2024-10-11 11:24:39 +03:00
|
|
|
import { BaseData } from '@shared/models/base-data';
|
|
|
|
|
import { MobileAppId } from '@shared/models/id/mobile-app-id';
|
|
|
|
|
import { OAuth2ClientInfo, PlatformType } from '@shared/models/oauth2.models';
|
|
|
|
|
import { MobileAppBundleId } from '@shared/models/id/mobile-app-bundle-id';
|
2024-10-22 15:35:32 +03:00
|
|
|
import { deepClone, isNotEmptyStr } from '@core/utils';
|
2024-05-02 21:37:11 +03:00
|
|
|
|
2025-01-27 19:01:43 +02:00
|
|
|
export const WEB_URL_REGEX = /^(https?:\/\/)?(localhost|([\p{L}\p{M}\w-]+\.)+[\p{L}\p{M}\w-]+)(:\d+)?(\/[\w\-._~:/?#[\]@!$&'()*+,;=%\p{L}\p{N}]*)?$/u;
|
|
|
|
|
|
2024-10-11 11:24:39 +03:00
|
|
|
export interface QrCodeSettings extends HasTenantId {
|
2024-05-02 21:37:11 +03:00
|
|
|
useDefaultApp: boolean;
|
2024-10-11 11:24:39 +03:00
|
|
|
mobileAppBundleId: MobileAppBundleId
|
2024-11-01 11:53:26 +02:00
|
|
|
androidEnabled: boolean;
|
|
|
|
|
iosEnabled: boolean;
|
2024-05-02 21:37:11 +03:00
|
|
|
qrCodeConfig: QRCodeConfig;
|
2024-11-01 11:53:26 +02:00
|
|
|
readonly googlePlayLink: string;
|
|
|
|
|
readonly appStoreLink: string;
|
2024-10-11 11:24:39 +03:00
|
|
|
id: {
|
|
|
|
|
id: string;
|
|
|
|
|
}
|
2024-05-02 21:37:11 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface QRCodeConfig {
|
|
|
|
|
showOnHomePage: boolean;
|
|
|
|
|
badgeEnabled: boolean;
|
|
|
|
|
badgePosition: BadgePosition;
|
|
|
|
|
qrCodeLabelEnabled: boolean;
|
|
|
|
|
qrCodeLabel: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export enum BadgePosition {
|
|
|
|
|
RIGHT = 'RIGHT',
|
|
|
|
|
LEFT = 'LEFT'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const badgePositionTranslationsMap = new Map<BadgePosition, string>([
|
|
|
|
|
[BadgePosition.RIGHT, 'admin.mobile-app.right'],
|
|
|
|
|
[BadgePosition.LEFT, 'admin.mobile-app.left']
|
|
|
|
|
]);
|
2024-10-11 11:24:39 +03:00
|
|
|
|
|
|
|
|
export enum MobileAppStatus {
|
|
|
|
|
DRAFT = 'DRAFT',
|
|
|
|
|
PUBLISHED = 'PUBLISHED',
|
|
|
|
|
DEPRECATED = 'DEPRECATED',
|
|
|
|
|
SUSPENDED = 'SUSPENDED'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const mobileAppStatusTranslations = new Map<MobileAppStatus, string>(
|
|
|
|
|
[
|
|
|
|
|
[MobileAppStatus.DRAFT, 'mobile.status-type.draft'],
|
|
|
|
|
[MobileAppStatus.PUBLISHED, 'mobile.status-type.published'],
|
|
|
|
|
[MobileAppStatus.DEPRECATED, 'mobile.status-type.deprecated'],
|
|
|
|
|
[MobileAppStatus.SUSPENDED, 'mobile.status-type.suspended'],
|
|
|
|
|
]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
export interface VersionInfo {
|
|
|
|
|
minVersion: string;
|
|
|
|
|
minVersionReleaseNotes?: string;
|
|
|
|
|
latestVersion: string;
|
|
|
|
|
latestVersionReleaseNotes?: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface StoreInfo {
|
|
|
|
|
sha256CertFingerprints?: string;
|
|
|
|
|
storeLink: string;
|
|
|
|
|
appId?: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface MobileApp extends BaseData<MobileAppId>, HasTenantId {
|
|
|
|
|
pkgName: string;
|
|
|
|
|
appSecret: string;
|
|
|
|
|
platformType: PlatformType;
|
|
|
|
|
status: MobileAppStatus;
|
|
|
|
|
versionInfo: VersionInfo;
|
|
|
|
|
storeInfo: StoreInfo;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum MobileMenuPath {
|
|
|
|
|
HOME = 'HOME',
|
|
|
|
|
ASSETS = 'ASSETS',
|
|
|
|
|
DEVICES = 'DEVICES',
|
|
|
|
|
DEVICE_LIST = 'DEVICE_LIST',
|
|
|
|
|
ALARMS = 'ALARMS',
|
|
|
|
|
DASHBOARDS = 'DASHBOARDS',
|
|
|
|
|
DASHBOARD = 'DASHBOARD',
|
|
|
|
|
AUDIT_LOGS = 'AUDIT_LOGS',
|
|
|
|
|
CUSTOMERS = 'CUSTOMERS',
|
2024-10-22 15:35:32 +03:00
|
|
|
NOTIFICATIONS = 'NOTIFICATIONS'
|
2024-10-11 11:24:39 +03:00
|
|
|
}
|
|
|
|
|
|
2024-10-22 15:35:32 +03:00
|
|
|
export enum MobilePageType {
|
|
|
|
|
DEFAULT = 'DEFAULT',
|
|
|
|
|
CUSTOM = 'CUSTOM',
|
|
|
|
|
DASHBOARD = 'DASHBOARD',
|
|
|
|
|
WEB_VIEW = 'WEB_VIEW',
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const mobilePageTypeTranslations = new Map<MobilePageType, string>(
|
|
|
|
|
[
|
|
|
|
|
[MobilePageType.CUSTOM, 'mobile.pages-types.custom'],
|
|
|
|
|
[MobilePageType.DASHBOARD, 'mobile.pages-types.dashboard'],
|
|
|
|
|
[MobilePageType.WEB_VIEW, 'mobile.pages-types.web-view'],
|
|
|
|
|
]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
export interface MobilePage {
|
|
|
|
|
label?: string;
|
|
|
|
|
icon?: string;
|
|
|
|
|
type: MobilePageType;
|
|
|
|
|
visible: boolean;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface DefaultMobilePage extends MobilePage {
|
|
|
|
|
id: MobileMenuPath;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface CustomMobilePage extends MobilePage {
|
|
|
|
|
dashboardId?: string;
|
|
|
|
|
url?: string;
|
|
|
|
|
path?: string;
|
2024-10-11 11:24:39 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface MobileLayoutConfig {
|
2024-10-22 15:35:32 +03:00
|
|
|
pages: MobilePage[];
|
2024-10-11 11:24:39 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface MobileAppBundle extends Omit<BaseData<MobileAppBundleId>, 'label'>, HasTenantId {
|
|
|
|
|
title?: string;
|
|
|
|
|
description?: string;
|
|
|
|
|
androidAppId?: MobileAppId;
|
|
|
|
|
iosAppId?: MobileAppId;
|
|
|
|
|
layoutConfig?: MobileLayoutConfig;
|
|
|
|
|
oauth2Enabled: boolean;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface MobileAppBundleInfo extends MobileAppBundle {
|
|
|
|
|
androidPkgName: string;
|
|
|
|
|
iosPkgName: string;
|
2024-10-22 15:35:32 +03:00
|
|
|
androidPkg?: {
|
|
|
|
|
name: string;
|
|
|
|
|
id: MobileAppId
|
|
|
|
|
};
|
|
|
|
|
iosPkg?: {
|
|
|
|
|
name: string;
|
|
|
|
|
id: MobileAppId
|
|
|
|
|
}
|
2024-10-11 11:24:39 +03:00
|
|
|
oauth2ClientInfos?: Array<OAuth2ClientInfo>;
|
2024-10-22 15:35:32 +03:00
|
|
|
qrCodeEnabled: boolean;
|
2024-10-11 11:24:39 +03:00
|
|
|
}
|
2024-10-22 15:35:32 +03:00
|
|
|
|
|
|
|
|
const defaultMobileMenu = [
|
|
|
|
|
MobileMenuPath.HOME,
|
|
|
|
|
MobileMenuPath.ALARMS,
|
|
|
|
|
MobileMenuPath.DEVICES,
|
|
|
|
|
MobileMenuPath.CUSTOMERS,
|
|
|
|
|
MobileMenuPath.ASSETS,
|
|
|
|
|
MobileMenuPath.AUDIT_LOGS,
|
|
|
|
|
MobileMenuPath.NOTIFICATIONS,
|
|
|
|
|
MobileMenuPath.DEVICE_LIST,
|
|
|
|
|
MobileMenuPath.DASHBOARDS
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
export const hideDefaultMenuItems = [
|
|
|
|
|
MobileMenuPath.DEVICE_LIST,
|
|
|
|
|
MobileMenuPath.DASHBOARDS
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
export const getDefaultMobileMenuItem = (): DefaultMobilePage[] => {
|
|
|
|
|
return deepClone(defaultMobileMenu).map(item => ({
|
|
|
|
|
visible: !hideDefaultMenuItems.includes(item),
|
|
|
|
|
type: MobilePageType.DEFAULT,
|
|
|
|
|
id: item
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const isDefaultMobileMenuItem = (item: MobilePage): item is DefaultMobilePage => {
|
|
|
|
|
const path = (item as DefaultMobilePage).id;
|
|
|
|
|
return isNotEmptyStr(path) && defaultMobilePageMap.has(path);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const mobilePageEqualToDefault = (item: MobilePage, defaultMobilePage: DefaultMobilePage): boolean => {
|
|
|
|
|
if (isDefaultMobileMenuItem(item) && (hideDefaultMenuItems.includes(item.id) ? !item.visible : item.visible)) {
|
|
|
|
|
return !(item.id !== defaultMobilePage.id || !!item.label || !!item.icon);
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const isDefaultMobilePagesConfig = (items: MobilePage[]): boolean => {
|
|
|
|
|
const defaultMenus = getDefaultMobileMenuItem();
|
|
|
|
|
if (!items?.length && !defaultMenus?.length) {
|
|
|
|
|
return true;
|
|
|
|
|
} else if (items.length !== defaultMenus.length) {
|
|
|
|
|
return false;
|
|
|
|
|
} else {
|
|
|
|
|
for (let i = 0; i < items.length; i++) {
|
|
|
|
|
const item = items[i];
|
|
|
|
|
const defaultMenuItem = defaultMenus[i];
|
|
|
|
|
if (!mobilePageEqualToDefault(item, defaultMenuItem)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const mobileMenuDividers = new Map<number, string>([
|
|
|
|
|
[2, 'mobile.mobile-599'],
|
|
|
|
|
[4, 'mobile.tablet-959'],
|
|
|
|
|
[6, 'mobile.max-element-number'],
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
export const defaultMobilePageMap = new Map<MobileMenuPath, Omit<DefaultMobilePage, 'type' | 'visible'>>([
|
|
|
|
|
[
|
|
|
|
|
MobileMenuPath.HOME,
|
|
|
|
|
{
|
|
|
|
|
id: MobileMenuPath.HOME,
|
|
|
|
|
icon: 'home',
|
|
|
|
|
label: 'Home'
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
MobileMenuPath.ALARMS,
|
|
|
|
|
{
|
|
|
|
|
id: MobileMenuPath.ALARMS,
|
|
|
|
|
icon: 'notifications',
|
|
|
|
|
label: 'Alarms'
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
MobileMenuPath.DEVICES,
|
|
|
|
|
{
|
|
|
|
|
id: MobileMenuPath.DEVICES,
|
|
|
|
|
icon: 'devices_other',
|
|
|
|
|
label: 'Devices'
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
MobileMenuPath.CUSTOMERS,
|
|
|
|
|
{
|
|
|
|
|
id: MobileMenuPath.CUSTOMERS,
|
|
|
|
|
icon: 'supervisor_account',
|
|
|
|
|
label: 'Customers'
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
MobileMenuPath.ASSETS,
|
|
|
|
|
{
|
|
|
|
|
id: MobileMenuPath.ASSETS,
|
|
|
|
|
icon: 'domain',
|
|
|
|
|
label: 'Assets'
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
MobileMenuPath.DEVICE_LIST,
|
|
|
|
|
{
|
|
|
|
|
id: MobileMenuPath.DEVICE_LIST,
|
|
|
|
|
icon: 'devices',
|
|
|
|
|
label: 'Device list'
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
MobileMenuPath.DASHBOARDS,
|
|
|
|
|
{
|
|
|
|
|
id: MobileMenuPath.DASHBOARDS,
|
|
|
|
|
icon: 'dashboard',
|
|
|
|
|
label: 'Dashboards'
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
MobileMenuPath.AUDIT_LOGS,
|
|
|
|
|
{
|
|
|
|
|
id: MobileMenuPath.AUDIT_LOGS,
|
|
|
|
|
icon: 'track_changes',
|
|
|
|
|
label: 'Audit logs'
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
MobileMenuPath.NOTIFICATIONS,
|
|
|
|
|
{
|
|
|
|
|
id: MobileMenuPath.NOTIFICATIONS,
|
|
|
|
|
icon: 'notifications_active',
|
|
|
|
|
label: 'Notification'
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
])
|