UI: Improve mobile action

This commit is contained in:
Artem Dzhereleiko 2025-08-11 12:57:28 +03:00 committed by ArtemDzhereleiko
parent 609a68c991
commit c24401603a
6 changed files with 292 additions and 120 deletions

View File

@ -35,117 +35,73 @@
</mat-icon> </mat-icon>
</mat-form-field> </mat-form-field>
</div> </div>
<ng-container [formGroup]="mobileActionTypeFormGroup" [ngSwitch]="mobileActionFormGroup.get('type').value"> <ng-container [formGroup]="mobileActionTypeFormGroup">
<ng-template [ngSwitchCase]="mobileActionType.deviceProvision"> @if (mobileActionFormGroup.get('type').value === mobileActionType.takePhoto ||
<tb-js-func mobileActionFormGroup.get('type').value === mobileActionType.takePictureFromGallery ||
formControlName="handleProvisionSuccessFunction" mobileActionFormGroup.get('type').value === mobileActionType.takeScreenshot) {
functionName="handleProvisionSuccess" <div class="tb-form-row">
withModules <mat-slide-toggle class="mat-slide" formControlName="saveToGallery">
[functionArgs]="['deviceName', '$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', 'entityLabel']" {{ 'widget-action.mobile.save-to-gallery' | translate }}
[globalVariables]="functionScopeVariables" </mat-slide-toggle>
[editorCompleter]="customActionEditorCompleter" </div>
hideBrackets }
></tb-js-func> @if (mobileActionFormGroup.get('type').value === mobileActionType.deviceProvision) {
</ng-template> <div class="tb-form-row">
<ng-template [ngSwitchCase]="mobileActionFormGroup.get('type').value === mobileActionType.mapDirection || <div class="fixed-title-width">{{ 'widget-action.mobile.provision-type' | translate }}*</div>
mobileActionFormGroup.get('type').value === mobileActionType.mapLocation ? <mat-form-field class="flex-1" appearance="outline" subscriptSizing="dynamic">
mobileActionFormGroup.get('type').value : ''"> <mat-select formControlName="provisionType">
<tb-js-func <mat-option *ngFor="let type of provisionTypes" [value]="type">
formControlName="getLocationFunction" {{ provisionTypeTranslationMap.get(type) | translate }}
functionName="getLocation" </mat-option>
withModules </mat-select>
[functionArgs]="['$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', 'entityLabel']" </mat-form-field>
[globalVariables]="functionScopeVariables" </div>
[editorCompleter]="customActionEditorCompleter" }
hideBrackets
helpId="widget/action/mobile_get_location_fn" @for (config of actionConfig; track config.formControlName) {
></tb-js-func> <div class="tb-form-panel stroked">
</ng-template> <mat-expansion-panel class="tb-settings">
<ng-template [ngSwitchCase]="mobileActionType.makePhoneCall"> <mat-expansion-panel-header>
<tb-js-func <mat-panel-description class="flex items-stretch justify-start">
formControlName="getPhoneNumberFunction" {{ config.title | translate }}
functionName="getPhoneNumber" </mat-panel-description>
withModules </mat-expansion-panel-header>
[functionArgs]="['$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', 'entityLabel']" <tb-js-func
[globalVariables]="functionScopeVariables" [formControlName]="config.formControlName"
[editorCompleter]="customActionEditorCompleter" [functionName]="config.functionName"
hideBrackets withModules
helpId="widget/action/mobile_get_phone_number_fn" [functionArgs]="config.functionArgs"
></tb-js-func> [globalVariables]="functionScopeVariables"
</ng-template> [editorCompleter]="customActionEditorCompleter"
<ng-template [ngSwitchCase]="mobileActionFormGroup.get('type').value === mobileActionType.takePhoto || hideBrackets
mobileActionFormGroup.get('type').value === mobileActionType.takePictureFromGallery || [helpId]="config.helpId"
mobileActionFormGroup.get('type').value === mobileActionType.takeScreenshot ? ></tb-js-func>
mobileActionFormGroup.get('type').value : ''"> </mat-expansion-panel>
<tb-js-func </div>
formControlName="processImageFunction" }
functionName="processImage"
withModules
[functionArgs]="['imageUrl', '$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', 'entityLabel']"
[globalVariables]="functionScopeVariables"
[editorCompleter]="customActionEditorCompleter"
hideBrackets
helpId="widget/action/mobile_process_image_fn"
></tb-js-func>
</ng-template>
<ng-template [ngSwitchCase]="mobileActionType.scanQrCode">
<tb-js-func
formControlName="processQrCodeFunction"
functionName="processQrCode"
withModules
[functionArgs]="['code', 'format', '$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', 'entityLabel']"
[globalVariables]="functionScopeVariables"
[editorCompleter]="customActionEditorCompleter"
hideBrackets
helpId="widget/action/mobile_process_qr_code_fn"
></tb-js-func>
</ng-template>
<ng-template [ngSwitchCase]="mobileActionType.getLocation">
<tb-js-func
formControlName="processLocationFunction"
functionName="processLocation"
withModules
[functionArgs]="['latitude', 'longitude', '$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', 'entityLabel']"
[globalVariables]="functionScopeVariables"
[editorCompleter]="customActionEditorCompleter"
hideBrackets
helpId="widget/action/mobile_process_location_fn"
></tb-js-func>
</ng-template>
<ng-template [ngSwitchCase]="mobileActionFormGroup.get('type').value === mobileActionType.mapDirection ||
mobileActionFormGroup.get('type').value === mobileActionType.mapLocation ||
mobileActionFormGroup.get('type').value === mobileActionType.makePhoneCall ?
mobileActionFormGroup.get('type').value : ''">
<tb-js-func
formControlName="processLaunchResultFunction"
functionName="processLaunchResult"
withModules
[functionArgs]="['launched', '$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', 'entityLabel']"
[globalVariables]="functionScopeVariables"
[editorCompleter]="customActionEditorCompleter"
hideBrackets
helpId="widget/action/mobile_process_launch_result_fn"
></tb-js-func>
</ng-template>
</ng-container> </ng-container>
<tb-js-func *ngIf="mobileActionFormGroup.get('type').value"
formControlName="handleEmptyResultFunction" @if(mobileActionFormGroup.get('type').value) {
functionName="handleEmptyResult" @for (config of commonActionConfig; track config.formControlName) {
withModules <div class="tb-form-panel stroked">
[functionArgs]="['$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', 'entityLabel']" <mat-expansion-panel class="tb-settings">
[globalVariables]="functionScopeVariables" <mat-expansion-panel-header>
[editorCompleter]="customActionEditorCompleter" <mat-panel-description class="flex items-stretch justify-start">
hideBrackets {{ config.title | translate }}
helpId="widget/action/mobile_handle_empty_result_fn" </mat-panel-description>
></tb-js-func> </mat-expansion-panel-header>
<tb-js-func *ngIf="mobileActionFormGroup.get('type').value" <tb-js-func
formControlName="handleErrorFunction" [formControlName]="config.formControlName"
functionName="handleError" [functionName]="config.functionName"
withModules withModules
[functionArgs]="['error', '$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', 'entityLabel']" [functionArgs]="config.functionArgs"
[globalVariables]="functionScopeVariables" [globalVariables]="functionScopeVariables"
[editorCompleter]="customActionEditorCompleter" [editorCompleter]="customActionEditorCompleter"
hideBrackets hideBrackets
helpId="widget/action/mobile_handle_error_fn" [helpId]="config.helpId"
></tb-js-func> ></tb-js-func>
</mat-expansion-panel>
</div>
}
}
</div> </div>

View File

@ -24,6 +24,9 @@ import {
} from '@angular/forms'; } from '@angular/forms';
import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { import {
ActionConfig,
ProvisionType,
provisionTypeTranslationMap,
WidgetActionType, WidgetActionType,
WidgetMobileActionDescriptor, WidgetMobileActionDescriptor,
WidgetMobileActionType, WidgetMobileActionType,
@ -35,6 +38,7 @@ import {
getDefaultGetPhoneNumberFunction, getDefaultGetPhoneNumberFunction,
getDefaultHandleEmptyResultFunction, getDefaultHandleEmptyResultFunction,
getDefaultHandleErrorFunction, getDefaultHandleErrorFunction,
getDefaultHandleNonMobileFallBackFunction,
getDefaultProcessImageFunction, getDefaultProcessImageFunction,
getDefaultProcessLaunchResultFunction, getDefaultProcessLaunchResultFunction,
getDefaultProcessLocationFunction, getDefaultProcessLocationFunction,
@ -68,6 +72,12 @@ export class MobileActionEditorComponent implements ControlValueAccessor, OnInit
functionScopeVariables: string[]; functionScopeVariables: string[];
actionConfig: ActionConfig[];
commonActionConfig: ActionConfig[];
provisionTypes: string[] = Object.keys(ProvisionType);
provisionTypeTranslationMap = provisionTypeTranslationMap;
private requiredValue: boolean; private requiredValue: boolean;
get required(): boolean { get required(): boolean {
return this.requiredValue; return this.requiredValue;
@ -99,8 +109,10 @@ export class MobileActionEditorComponent implements ControlValueAccessor, OnInit
this.mobileActionFormGroup = this.fb.group({ this.mobileActionFormGroup = this.fb.group({
type: [null, Validators.required], type: [null, Validators.required],
handleEmptyResultFunction: [null], handleEmptyResultFunction: [null],
handleErrorFunction: [null] handleErrorFunction: [null],
handleNonMobileFallbackFunction: [null]
}); });
this.getCommonActionConfigs();
this.mobileActionFormGroup.get('type').valueChanges.pipe( this.mobileActionFormGroup.get('type').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef) takeUntilDestroyed(this.destroyRef)
).subscribe((type: WidgetMobileActionType) => { ).subscribe((type: WidgetMobileActionType) => {
@ -109,6 +121,7 @@ export class MobileActionEditorComponent implements ControlValueAccessor, OnInit
action = {...action, ...this.mobileActionTypeFormGroup.value}; action = {...action, ...this.mobileActionTypeFormGroup.value};
} }
this.updateMobileActionType(type, action); this.updateMobileActionType(type, action);
this.getActionConfigs();
}); });
this.mobileActionFormGroup.valueChanges.pipe( this.mobileActionFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef) takeUntilDestroyed(this.destroyRef)
@ -133,10 +146,14 @@ export class MobileActionEditorComponent implements ControlValueAccessor, OnInit
} }
writeValue(value: WidgetMobileActionDescriptor | null): void { writeValue(value: WidgetMobileActionDescriptor | null): void {
this.mobileActionFormGroup.patchValue({type: value?.type, this.mobileActionFormGroup.patchValue({
handleEmptyResultFunction: value?.handleEmptyResultFunction, type: value?.type,
handleErrorFunction: value?.handleErrorFunction}, {emitEvent: false}); handleEmptyResultFunction: value?.handleEmptyResultFunction,
handleErrorFunction: value?.handleErrorFunction,
handleNonMobileFallbackFunction: value?.handleNonMobileFallbackFunction
}, {emitEvent: false});
this.updateMobileActionType(value?.type, value); this.updateMobileActionType(value?.type, value);
this.getActionConfigs();
} }
private updateModel() { private updateModel() {
@ -164,6 +181,12 @@ export class MobileActionEditorComponent implements ControlValueAccessor, OnInit
handleErrorFunction = getDefaultHandleErrorFunction(type); handleErrorFunction = getDefaultHandleErrorFunction(type);
this.mobileActionFormGroup.patchValue({handleErrorFunction}, {emitEvent: false}); this.mobileActionFormGroup.patchValue({handleErrorFunction}, {emitEvent: false});
} }
let handleNonMobileFallbackFunction = action?.handleNonMobileFallbackFunction;
const defaultHandleNonMobileFallbackFunction = getDefaultHandleNonMobileFallBackFunction();
if (defaultHandleNonMobileFallbackFunction !== handleNonMobileFallbackFunction) {
handleNonMobileFallbackFunction = getDefaultHandleNonMobileFallBackFunction();
this.mobileActionFormGroup.patchValue({handleNonMobileFallbackFunction}, {emitEvent: false});
}
} }
this.mobileActionTypeFormGroup = this.fb.group({}); this.mobileActionTypeFormGroup = this.fb.group({});
if (type) { if (type) {
@ -183,6 +206,10 @@ export class MobileActionEditorComponent implements ControlValueAccessor, OnInit
'processImageFunction', 'processImageFunction',
this.fb.control(processImageFunction, []) this.fb.control(processImageFunction, [])
); );
this.mobileActionTypeFormGroup.addControl(
'saveToGallery',
this.fb.control(action?.saveToGallery || false, [])
);
break; break;
case WidgetMobileActionType.mapDirection: case WidgetMobileActionType.mapDirection:
case WidgetMobileActionType.mapLocation: case WidgetMobileActionType.mapLocation:
@ -267,6 +294,10 @@ export class MobileActionEditorComponent implements ControlValueAccessor, OnInit
'handleProvisionSuccessFunction', 'handleProvisionSuccessFunction',
this.fb.control(handleProvisionSuccessFunction, [Validators.required]) this.fb.control(handleProvisionSuccessFunction, [Validators.required])
); );
this.mobileActionTypeFormGroup.addControl(
'provisionType',
this.fb.control(action?.provisionType || ProvisionType.auto, [])
);
} }
} }
this.mobileActionTypeFormGroup.valueChanges.pipe( this.mobileActionTypeFormGroup.valueChanges.pipe(
@ -276,5 +307,108 @@ export class MobileActionEditorComponent implements ControlValueAccessor, OnInit
}); });
} }
getActionConfigs() {
const type = this.mobileActionFormGroup.get('type').value;
this.actionConfig = [];
switch (type) {
case this.mobileActionType.deviceProvision:
this.actionConfig.push({
title: 'widget-action.mobile.handle-provision-success-function',
formControlName: 'handleProvisionSuccessFunction',
functionName: 'handleProvisionSuccess',
functionArgs: ['deviceName', '$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', 'entityLabel']
});
break;
case this.mobileActionType.mapDirection:
case this.mobileActionType.mapLocation:
this.actionConfig.push({
title: 'widget-action.mobile.get-location-function',
formControlName: 'getLocationFunction',
functionName: 'getLocation',
functionArgs: ['$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', 'entityLabel'],
helpId: 'widget/action/mobile_get_location_fn'
});
this.actionConfig.push({
title: 'widget-action.mobile.process-launch-result-function',
formControlName: 'processLaunchResultFunction',
functionName: 'processLaunchResult',
functionArgs: ['launched', '$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', 'entityLabel'],
helpId: 'widget/action/mobile_process_launch_result_fn'
});
break;
case this.mobileActionType.makePhoneCall:
this.actionConfig.push({
title: 'widget-action.mobile.get-phone-number-function',
formControlName: 'getPhoneNumberFunction',
functionName: 'getPhoneNumber',
functionArgs: ['$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', 'entityLabel'],
helpId: 'widget/action/mobile_get_phone_number_fn'
});
this.actionConfig.push({
title: 'widget-action.mobile.process-launch-result-function',
formControlName: 'processLaunchResultFunction',
functionName: 'processLaunchResult',
functionArgs: ['launched', '$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', 'entityLabel'],
helpId: 'widget/action/mobile_process_launch_result_fn'
});
break;
case this.mobileActionType.takePhoto:
case this.mobileActionType.takePictureFromGallery:
case this.mobileActionType.takeScreenshot:
this.actionConfig.push({
title: 'widget-action.mobile.process-image-function',
formControlName: 'processImageFunction',
functionName: 'processImage',
functionArgs: ['imageUrl', '$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', 'entityLabel'],
helpId: 'widget/action/mobile_process_image_fn'
});
break;
case this.mobileActionType.scanQrCode:
this.actionConfig.push({
title: 'widget-action.mobile.process-qr-code-function',
formControlName: 'processQrCodeFunction',
functionName: 'processQrCode',
functionArgs: ['code', 'format', '$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', 'entityLabel'],
helpId: 'widget/action/mobile_process_qr_code_fn'
});
break;
case this.mobileActionType.getLocation:
this.actionConfig.push({
title: 'widget-action.mobile.process-location-function',
formControlName: 'processLocationFunction',
functionName: 'processLocation',
functionArgs: ['latitude', 'longitude', '$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', 'entityLabel'],
helpId: 'widget/action/mobile_process_location_fn'
});
break;
}
}
getCommonActionConfigs() {
this.commonActionConfig = [
{
title: 'widget-action.mobile.handle-empty-result-function',
formControlName: 'handleEmptyResultFunction',
functionName: 'handleEmptyResult',
functionArgs: ['$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', 'entityLabel'],
helpId: 'widget/action/mobile_handle_empty_result_fn'
},
{
title: 'widget-action.mobile.handle-error-function',
formControlName: 'handleErrorFunction',
functionName: 'handleError',
functionArgs: ['error', '$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', 'entityLabel'],
helpId: 'widget/action/mobile_handle_error_fn'
},
{
title: 'widget-action.mobile.handle-non-mobile-fallback-function',
formControlName: 'handleNonMobileFallbackFunction',
functionName: 'handleNonMobileFallback',
functionArgs: ['$event', 'widgetContext'],
helpId: 'widget/action/mobile_handle_non_mobile_fallback_fn'
}
];
}
protected readonly WidgetActionType = WidgetActionType; protected readonly WidgetActionType = WidgetActionType;
} }

View File

@ -172,6 +172,14 @@ const handleErrorFunctionTemplate: TbFunction =
' }, 100);\n' + ' }, 100);\n' +
'}\n'; '}\n';
const handleNonMobileFallbackFunctionTemplate: TbFunction =
'// Optional function body to handle non-mobile fallback \n' +
'showFallbackToast();\n' +
'\n' +
'function showFallbackToast(title, error) {\n' +
' widgetContext.showWarnToast(\'This action is only available in the mobile application.\');\n' +
'}\n';
const getLocationFunctionTemplate: TbFunction = const getLocationFunctionTemplate: TbFunction =
'// Function body that should return location as array of two numbers (latitude, longitude) for further processing by mobile action.\n' + '// Function body that should return location as array of two numbers (latitude, longitude) for further processing by mobile action.\n' +
'// Usually location can be obtained from entity attributes/telemetry. \n\n' + '// Usually location can be obtained from entity attributes/telemetry. \n\n' +
@ -326,3 +334,5 @@ export const getDefaultHandleErrorFunction = (type: WidgetMobileActionType): TbF
} }
return handleErrorFunctionTemplate.replace('--TITLE--', title); return handleErrorFunctionTemplate.replace('--TITLE--', title);
}; };
export const getDefaultHandleNonMobileFallBackFunction = () => handleNonMobileFallbackFunctionTemplate;

View File

@ -38,6 +38,7 @@ import {
} from '@angular/core'; } from '@angular/core';
import { DashboardWidget } from '@home/models/dashboard-component.models'; import { DashboardWidget } from '@home/models/dashboard-component.models';
import { import {
MobileImageResult,
Widget, Widget,
WidgetAction, WidgetAction,
WidgetActionDescriptor, WidgetActionDescriptor,
@ -126,6 +127,7 @@ import { IModulesMap } from '@modules/common/modules-map.models';
import { DashboardUtilsService } from '@core/services/dashboard-utils.service'; import { DashboardUtilsService } from '@core/services/dashboard-utils.service';
import { CompiledTbFunction, compileTbFunction, isNotEmptyTbFunction } from '@shared/models/js-function.models'; import { CompiledTbFunction, compileTbFunction, isNotEmptyTbFunction } from '@shared/models/js-function.models';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { addDiagnosticChain } from '@angular/compiler-cli/src/ngtsc/diagnostics';
@Component({ @Component({
selector: 'tb-widget', selector: 'tb-widget',
@ -1222,12 +1224,16 @@ export class WidgetComponent extends PageComponent implements OnInit, OnChanges,
switch (type) { switch (type) {
case WidgetMobileActionType.takePictureFromGallery: case WidgetMobileActionType.takePictureFromGallery:
case WidgetMobileActionType.takePhoto: case WidgetMobileActionType.takePhoto:
case WidgetMobileActionType.takeScreenshot:
argsObservable = of([mobileAction.saveToGallery]);
break;
case WidgetMobileActionType.scanQrCode: case WidgetMobileActionType.scanQrCode:
case WidgetMobileActionType.getLocation: case WidgetMobileActionType.getLocation:
case WidgetMobileActionType.takeScreenshot:
case WidgetMobileActionType.deviceProvision:
argsObservable = of([]); argsObservable = of([]);
break; break;
case WidgetMobileActionType.deviceProvision:
argsObservable = of([mobileAction.provisionType]);
break;
case WidgetMobileActionType.mapDirection: case WidgetMobileActionType.mapDirection:
case WidgetMobileActionType.mapLocation: case WidgetMobileActionType.mapLocation:
argsObservable = compileTbFunction(this.http, mobileAction.getLocationFunction, '$event', 'widgetContext', 'entityId', argsObservable = compileTbFunction(this.http, mobileAction.getLocationFunction, '$event', 'widgetContext', 'entityId',
@ -1297,6 +1303,10 @@ export class WidgetComponent extends PageComponent implements OnInit, OnChanges,
case WidgetMobileActionType.takePhoto: case WidgetMobileActionType.takePhoto:
case WidgetMobileActionType.takeScreenshot: case WidgetMobileActionType.takeScreenshot:
const imageUrl = actionResult.imageUrl; const imageUrl = actionResult.imageUrl;
if (!additionalParams) {
additionalParams = {};
}
additionalParams.imageInfo = actionResult.imageInfo;
if (isNotEmptyTbFunction(mobileAction.processImageFunction)) { if (isNotEmptyTbFunction(mobileAction.processImageFunction)) {
compileTbFunction(this.http, mobileAction.processImageFunction, 'imageUrl', '$event', 'widgetContext', 'entityId', compileTbFunction(this.http, mobileAction.processImageFunction, 'imageUrl', '$event', 'widgetContext', 'entityId',
'entityName', 'additionalParams', 'entityLabel').subscribe( 'entityName', 'additionalParams', 'entityLabel').subscribe(
@ -1421,6 +1431,23 @@ export class WidgetComponent extends PageComponent implements OnInit, OnChanges,
); );
} }
} }
} else if (!this.mobileService.isMobileApp()) {
if (isNotEmptyTbFunction(mobileAction.handleNonMobileFallbackFunction)) {
compileTbFunction(this.http, mobileAction.handleNonMobileFallbackFunction, '$event', 'widgetContext',).subscribe(
{
next: (compiled) => {
try {
compiled.execute($event, this.widgetContext);
} catch (e) {
console.error(e);
}
},
error: (err) => {
console.error(err);
}
}
);
}
} }
} }
); );

View File

@ -33,7 +33,7 @@ import { PageComponent } from '@shared/components/page.component';
import { AfterViewInit, DestroyRef, Directive, EventEmitter, inject, Inject, OnInit, Type } from '@angular/core'; import { AfterViewInit, DestroyRef, Directive, EventEmitter, inject, Inject, OnInit, Type } from '@angular/core';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { AbstractControl, UntypedFormGroup } from '@angular/forms'; import { AbstractControl, UntypedFormGroup, ValidatorFn } from '@angular/forms';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { Dashboard } from '@shared/models/dashboard.models'; import { Dashboard } from '@shared/models/dashboard.models';
import { IAliasController } from '@core/api/widget-api.models'; import { IAliasController } from '@core/api/widget-api.models';
@ -51,6 +51,7 @@ import { TbFunction } from '@shared/models/js-function.models';
import { FormProperty, jsonFormSchemaToFormProperties } from '@shared/models/dynamic-form.models'; import { FormProperty, jsonFormSchemaToFormProperties } from '@shared/models/dynamic-form.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { TbUnit } from '@shared/models/unit.models'; import { TbUnit } from '@shared/models/unit.models';
import { ImageResourceInfo } from '@shared/models/resource.models';
export enum widgetType { export enum widgetType {
timeseries = 'timeseries', timeseries = 'timeseries',
@ -622,6 +623,30 @@ export enum WidgetMobileActionType {
deviceProvision = 'deviceProvision', deviceProvision = 'deviceProvision',
} }
export interface ActionConfig {
title: string,
formControlName: string,
functionName: string,
functionArgs: string[],
helpId?: string
}
export enum ProvisionType {
auto = 'auto',
wiFi = 'wiFi',
ble = 'ble',
softAp = 'softAp'
}
export const provisionTypeTranslationMap = new Map<ProvisionType, string>(
[
[ ProvisionType.auto, 'widget-action.mobile.auto' ],
[ ProvisionType.wiFi, 'widget-action.mobile.wi-fi' ],
[ ProvisionType.ble, 'widget-action.mobile.ble' ],
[ ProvisionType.softAp, 'widget-action.mobile.soft-ap' ],
]
);
export enum MapItemType { export enum MapItemType {
marker = 'marker', marker = 'marker',
polygon = 'polygon', polygon = 'polygon',
@ -675,6 +700,7 @@ export interface MobileLaunchResult {
export interface MobileImageResult { export interface MobileImageResult {
imageUrl: string; imageUrl: string;
imageInfo?: ImageResourceInfo;
} }
export interface MobileQrCodeResult { export interface MobileQrCodeResult {
@ -706,10 +732,12 @@ export interface WidgetMobileActionResult<T extends MobileActionResult> {
export interface ProvisionSuccessDescriptor { export interface ProvisionSuccessDescriptor {
handleProvisionSuccessFunction: TbFunction; handleProvisionSuccessFunction: TbFunction;
provisionType?: string;
} }
export interface ProcessImageDescriptor { export interface ProcessImageDescriptor {
processImageFunction: TbFunction; processImageFunction: TbFunction;
saveToGallery?: boolean;
} }
export interface ProcessLaunchResultDescriptor { export interface ProcessLaunchResultDescriptor {
@ -743,6 +771,7 @@ export interface WidgetMobileActionDescriptor extends WidgetMobileActionDescript
type: WidgetMobileActionType; type: WidgetMobileActionType;
handleErrorFunction?: TbFunction; handleErrorFunction?: TbFunction;
handleEmptyResultFunction?: TbFunction; handleEmptyResultFunction?: TbFunction;
handleNonMobileFallbackFunction?: TbFunction;
} }
export interface CustomActionDescriptor { export interface CustomActionDescriptor {

View File

@ -6868,7 +6868,23 @@
"scan-qr-code": "Scan QR Code", "scan-qr-code": "Scan QR Code",
"make-phone-call": "Make phone call", "make-phone-call": "Make phone call",
"get-location": "Get phone location", "get-location": "Get phone location",
"take-screenshot": "Take screenshot" "take-screenshot": "Take screenshot",
"handle-provision-success-function": "Handle provision success function",
"get-location-function": "Get location function",
"process-launch-result-function": "Process launch result function",
"get-phone-number-function": "Get phone number function",
"process-image-function": "Process image function",
"process-qr-code-function": "Process QR code function",
"process-location-function": "Process location function",
"handle-empty-result-function": "Handle empty result function",
"handle-error-function": "Handle error function",
"handle-non-mobile-fallback-function": "Handle Non-Mobile fallback function",
"save-to-gallery": "Save to gallery",
"provision-type": "Provision type",
"auto": "Auto",
"wi-fi": "Wi-Fi",
"ble": "BLE",
"soft-ap": "Soft AP"
}, },
"custom-action-function": "Custom action function", "custom-action-function": "Custom action function",
"custom-pretty-function": "Custom action (with HTML template) function", "custom-pretty-function": "Custom action (with HTML template) function",