From 07487cd1485550dfa84a4bafaa645479e282e843 Mon Sep 17 00:00:00 2001 From: rusikv Date: Mon, 3 Jun 2024 15:10:52 +0300 Subject: [PATCH 01/14] UI: hide qr code label instead of title on smaller sceens --- .../widget/lib/mobile-app-qrcode-widget.component.scss | 4 ++-- ui-ngx/src/assets/dashboard/sys_admin_home_page.json | 2 +- ui-ngx/src/assets/dashboard/tenant_admin_home_page.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.scss index b13fbe0f8f..516532ab63 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.scss @@ -60,7 +60,7 @@ .tb-qrcode { display: flex; - flex: 0.3; + flex: 0.33; min-width: 100px; padding: 3px; border-radius: 6px; @@ -70,7 +70,7 @@ .tb-badges { display: flex; flex-direction: column; - flex: 0.4; + flex: 0.44; gap: 8px; } } diff --git a/ui-ngx/src/assets/dashboard/sys_admin_home_page.json b/ui-ngx/src/assets/dashboard/sys_admin_home_page.json index bb38c53b6f..20e3b0b48a 100644 --- a/ui-ngx/src/assets/dashboard/sys_admin_home_page.json +++ b/ui-ngx/src/assets/dashboard/sys_admin_home_page.json @@ -2665,7 +2665,7 @@ "dropShadow": false, "enableFullscreen": false, "widgetStyle": {}, - "widgetCss": " .tb-widget-container > .tb-widget > .tb-widget-content .tb-widget-title > .title-row > .title {\n padding: 0;\n}\n\n .tb-widget-container > .tb-widget > .tb-widget-content .tb-widget-title > .title-row > .title {\n padding-bottom: 8px;\n font-weight: 600;\n font-size: 20px;\n line-height: 24px;\n letter-spacing: 0.1px;\n color: rgba(0, 0, 0, 0.76);\n}\n\n.tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel {\n padding: 0;\n}\n\n@media screen and (min-width: 960px) and (max-height: 900px) {\n .tb-widget-container > .tb-widget > .tb-widget-content .tb-widget-title {\n display: none;\n }\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-widget-container > .tb-widget > .tb-widget-content .tb-widget-title > .title-row > .title {\n padding-bottom: 0;\n font-weight: 500;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.25px;\n }\n}\n\n@media screen and (min-width: 960px) and (max-width: 1279px) {\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode-label {\n display: none;\n }\n}\n\n@media screen and (min-width: 960px) and (max-width: 1190px) {\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel {\n gap: 0;\n }\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode {\n min-width: 150px;\n }\n}\n\n@media screen and (max-width: 960px) {\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode {\n max-width: 110px;\n }\n \n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-badges {\n max-width: 140px;\n }\n}\n\n@media screen and (min-width: 1819px) {\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode {\n max-width: 125px;\n }\n \n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-badges {\n max-width: 160px;\n }\n}", + "widgetCss": " .tb-widget-container > .tb-widget > .tb-widget-content .tb-widget-title > .title-row > .title {\n padding: 0;\n}\n\n .tb-widget-container > .tb-widget > .tb-widget-content .tb-widget-title > .title-row > .title {\n padding-bottom: 8px;\n font-weight: 600;\n font-size: 20px;\n line-height: 24px;\n letter-spacing: 0.1px;\n color: rgba(0, 0, 0, 0.76);\n}\n\n.tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel {\n padding: 0;\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-widget-container > .tb-widget > .tb-widget-content .tb-widget-title > .title-row > .title {\n padding-bottom: 0;\n font-weight: 500;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.25px;\n }\n}\n\n@media screen and (min-width: 960px) and (max-width: 1190px) {\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel {\n gap: 0;\n }\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode {\n min-width: 150px;\n }\n}\n\n@media screen and (max-width: 960px) {\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode {\n max-width: 110px;\n }\n \n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-badges {\n max-width: 140px;\n }\n}\n\n@media screen and (min-width: 1819px) {\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode {\n max-width: 125px;\n }\n \n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-badges {\n max-width: 160px;\n }\n}\n\n@media screen and (min-width: 960px) and (max-height: 960px) {\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode-label {\n display: none;\n }\n}", "showTitleIcon": false, "titleTooltip": "", "titleStyle": null, diff --git a/ui-ngx/src/assets/dashboard/tenant_admin_home_page.json b/ui-ngx/src/assets/dashboard/tenant_admin_home_page.json index bdc90f529f..e3277ac88a 100644 --- a/ui-ngx/src/assets/dashboard/tenant_admin_home_page.json +++ b/ui-ngx/src/assets/dashboard/tenant_admin_home_page.json @@ -1208,7 +1208,7 @@ "dropShadow": false, "enableFullscreen": false, "widgetStyle": {}, - "widgetCss": " .tb-widget-container > .tb-widget > .tb-widget-content .tb-widget-title > .title-row > .title {\n padding: 0;\n}\n\n .tb-widget-container > .tb-widget > .tb-widget-content .tb-widget-title > .title-row > .title {\n padding-bottom: 8px;\n font-weight: 600;\n font-size: 20px;\n line-height: 24px;\n letter-spacing: 0.1px;\n color: rgba(0, 0, 0, 0.76);\n}\n\n.tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel {\n padding: 0;\n}\n\n@media screen and (min-width: 960px) and (max-height: 900px) {\n .tb-widget-container > .tb-widget > .tb-widget-content .tb-widget-title {\n display: none;\n }\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-widget-container > .tb-widget > .tb-widget-content .tb-widget-title > .title-row > .title {\n padding-bottom: 0;\n font-weight: 500;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.25px;\n }\n}\n\n@media screen and (min-width: 960px) and (max-width: 1279px) {\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode-label {\n display: none;\n }\n}\n\n@media screen and (min-width: 960px) and (max-width: 1182px) {\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel {\n gap: 0;\n }\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode {\n min-width: 150px;\n }\n}\n\n@media screen and (max-width: 960px) {\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode {\n max-width: 120px;\n }\n \n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-badges {\n max-width: 160px;\n }\n}\n\n@media screen and (min-width: 1819px) {\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode {\n max-width: 125px;\n }\n \n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-badges {\n max-width: 160px;\n }\n}", + "widgetCss": " .tb-widget-container > .tb-widget > .tb-widget-content .tb-widget-title > .title-row > .title {\n padding: 0;\n}\n\n .tb-widget-container > .tb-widget > .tb-widget-content .tb-widget-title > .title-row > .title {\n padding-bottom: 8px;\n font-weight: 600;\n font-size: 20px;\n line-height: 24px;\n letter-spacing: 0.1px;\n color: rgba(0, 0, 0, 0.76);\n}\n\n.tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel {\n padding: 0;\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-widget-container > .tb-widget > .tb-widget-content .tb-widget-title > .title-row > .title {\n padding-bottom: 0;\n font-weight: 500;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.25px;\n }\n}\n\n@media screen and (min-width: 960px) and (max-width: 1182px) {\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel {\n gap: 0;\n }\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode {\n min-width: 150px;\n }\n}\n\n@media screen and (max-width: 960px) {\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode {\n max-width: 120px;\n }\n \n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-badges {\n max-width: 160px;\n }\n}\n\n@media screen and (min-width: 1819px) {\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode {\n max-width: 125px;\n }\n \n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-badges {\n max-width: 160px;\n }\n}\n\n@media screen and (min-width: 960px) and (max-height: 960px) {\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode-label {\n display: none;\n }\n}", "showTitleIcon": false, "titleTooltip": "", "titleStyle": null, From e60d0839b449c029544f127efb3787cd8a38bde8 Mon Sep 17 00:00:00 2001 From: rusikv Date: Mon, 3 Jun 2024 16:17:54 +0300 Subject: [PATCH 02/14] UI: removed maxLength from label input of mobile app qr code widget settings --- .../basic/cards/mobile-app-qr-code-basic-config.component.html | 2 +- .../cards/mobile-app-qr-code-widget-settings.component.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/mobile-app-qr-code-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/mobile-app-qr-code-basic-config.component.html index 2c29e15287..5d7f1599b9 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/mobile-app-qr-code-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/mobile-app-qr-code-basic-config.component.html @@ -45,7 +45,7 @@ {{ 'admin.mobile-app.label' | translate }} - + - + Date: Tue, 4 Jun 2024 19:28:21 +0300 Subject: [PATCH 03/14] UI: mobile app qr code widget: added default settings for title and icon, added card padding, fixed title not visible under overlay, enlarged preview width, code cleanup --- .../system/widget_types/mobile_app_qr_code.json | 4 ++-- ...mobile-app-qr-code-basic-config.component.html | 15 ++++++--------- .../mobile-app-qr-code-basic-config.component.ts | 2 ++ .../lib/cards/mobile-app-qr-code-widget.models.ts | 3 ++- .../lib/mobile-app-qrcode-widget.component.html | 1 + .../lib/mobile-app-qrcode-widget.component.scss | 12 ++++++++---- .../lib/mobile-app-qrcode-widget.component.ts | 2 ++ ...ile-app-qr-code-widget-settings.component.html | 15 ++++++--------- ...obile-app-qr-code-widget-settings.component.ts | 3 ++- 9 files changed, 31 insertions(+), 26 deletions(-) diff --git a/application/src/main/data/json/system/widget_types/mobile_app_qr_code.json b/application/src/main/data/json/system/widget_types/mobile_app_qr_code.json index f3929d88f1..3aef1f30fd 100644 --- a/application/src/main/data/json/system/widget_types/mobile_app_qr_code.json +++ b/application/src/main/data/json/system/widget_types/mobile_app_qr_code.json @@ -11,13 +11,13 @@ "resources": [], "templateHtml": "\n", "templateCss": "", - "controllerScript": "self.onInit = function() {\n}\n\nself.typeParameters = function() {\n return {\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true\n };\n};", + "controllerScript": "self.onInit = function() {\n}\n\nself.typeParameters = function() {\n return {\n previewWidth: '500px',\n previewHeight: '250px',\n embedTitlePanel: true\n };\n};", "settingsSchema": "", "dataKeySettingsSchema": "", "settingsDirective": "tb-mobile-app-qr-code-widget-settings", "hasBasicMode": true, "basicModeDirective": "tb-mobile-app-qr-code-basic-config", - "defaultConfig": "{\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"useSystemSettings\":true,\"qrCodeConfig\":{\"badgeEnabled\":true,\"badgePosition\":\"RIGHT\",\"qrCodeLabelEnabled\":true,\"qrCodeLabel\":\"Scan to connect or download mobile app\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Mobile app QR code\",\"dropShadow\":true,\"showTitleIcon\":false,\"titleTooltip\":\"\",\"enableFullscreen\":false,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"configMode\":\"basic\"}" + "defaultConfig": "{\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"useSystemSettings\":true,\"qrCodeConfig\":{\"badgeEnabled\":true,\"badgePosition\":\"RIGHT\",\"qrCodeLabelEnabled\":true,\"qrCodeLabel\":\"Scan to connect or download mobile app\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"padding\":\"12px\"},\"title\":\"Download mobile app\",\"dropShadow\":true,\"showTitleIcon\":false,\"titleTooltip\":\"\",\"enableFullscreen\":false,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"configMode\":\"basic\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1.5\"},\"titleColor\":\"#000000\",\"iconSize\":\"26px\",\"titleIcon\":\"qr_code\",\"iconColor\":\"#000000\",\"actions\":{}}" }, "tags": [ "ios", diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/mobile-app-qr-code-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/mobile-app-qr-code-basic-config.component.html index 5d7f1599b9..f4b5290777 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/mobile-app-qr-code-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/mobile-app-qr-code-basic-config.component.html @@ -46,15 +46,6 @@ - - warning -
@@ -110,6 +101,12 @@
+
+
{{ 'widget-config.card-padding' | translate }}
+ + + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/mobile-app-qr-code-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/mobile-app-qr-code-basic-config.component.ts index 25f091df7b..f415e1d17c 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/mobile-app-qr-code-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/mobile-app-qr-code-basic-config.component.ts @@ -82,6 +82,7 @@ export class MobileAppQrCodeBasicConfigComponent extends BasicWidgetConfigCompon cardButtons: [this.getCardButtons(configData.config), []], borderRadius: [configData.config.borderRadius, []], + padding: [settings.padding, []], actions: [configData.config.actions || {}, []] }); @@ -110,6 +111,7 @@ export class MobileAppQrCodeBasicConfigComponent extends BasicWidgetConfigCompon this.widgetConfig.config.settings.background = config.background; this.setCardButtons(config.cardButtons, this.widgetConfig.config); this.widgetConfig.config.borderRadius = config.borderRadius; + this.widgetConfig.config.settings.padding = config.padding; this.widgetConfig.config.actions = config.actions; return this.widgetConfig; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/mobile-app-qr-code-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/cards/mobile-app-qr-code-widget.models.ts index df18b8ac49..573fa20bab 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/cards/mobile-app-qr-code-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/mobile-app-qr-code-widget.models.ts @@ -50,5 +50,6 @@ export const mobileAppQrCodeWidgetDefaultSettings: MobileAppQrCodeWidgetSettings color: 'rgba(255,255,255,0.72)', blur: 3 } - } + }, + padding: '12px' } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.html index 6dc33eb07f..0acb8d3b8d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.html @@ -16,6 +16,7 @@ -->
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.scss index 516532ab63..e7ff7864bd 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.scss @@ -47,10 +47,6 @@ right: 12px; } - div.tb-widget-title { - padding: 0; - } - .tb-qrcode-label { align-self: center; text-align: center; @@ -75,3 +71,11 @@ } } +:host ::ng-deep { + .tb-mobile-app-qrcode-panel { + > div.tb-widget-title { + padding: 0; + z-index: 1; + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.ts index 6aeef19b19..4b761bf59d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.ts @@ -52,6 +52,7 @@ export class MobileAppQrcodeWidgetComponent extends PageComponent implements OnI backgroundStyle$: Observable; overlayStyle: ComponentStyle = {}; + padding: string; qrCodeSVG = ''; @@ -101,6 +102,7 @@ export class MobileAppQrcodeWidgetComponent extends PageComponent implements OnI this.widgetResize$.observe(this.elementRef.nativeElement); this.backgroundStyle$ = backgroundStyle(this.ctx.settings.background, this.imagePipe, this.sanitizer); this.overlayStyle = overlayStyle(this.ctx.settings.background.overlay); + this.padding = this.ctx.settings.background.overlay.enabled ? undefined : this.ctx.settings.padding; this.cd.markForCheck(); })); } else { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/mobile-app-qr-code-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/mobile-app-qr-code-widget-settings.component.html index 14542b2140..6da0bb97c8 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/mobile-app-qr-code-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/mobile-app-qr-code-widget-settings.component.html @@ -49,15 +49,6 @@ - - warning -
@@ -69,5 +60,11 @@ +
+
{{ 'widget-config.card-padding' | translate }}
+ + + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/mobile-app-qr-code-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/mobile-app-qr-code-widget-settings.component.ts index d4bae4624e..8215c2e14a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/mobile-app-qr-code-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/mobile-app-qr-code-widget-settings.component.ts @@ -55,7 +55,8 @@ export class MobileAppQrCodeWidgetSettingsComponent extends WidgetSettingsCompon qrCodeLabelEnabled: [settings.qrCodeConfig.qrCodeLabelEnabled], qrCodeLabel: [settings.qrCodeConfig.qrCodeLabel] }), - background: [settings.background] + background: [settings.background], + padding: [settings.padding, []] }); } From 95c9e7b7bf29cb72ba3888bd095c83dd95ffc2ed Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Tue, 4 Jun 2024 19:52:43 +0300 Subject: [PATCH 04/14] added api to retrieve app store links --- .../MobileApplicationController.java | 33 ++++++++++++++++--- .../common/data/mobile/AndroidConfig.java | 2 ++ .../server/common/data/mobile/IosConfig.java | 2 ++ 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/MobileApplicationController.java b/application/src/main/java/org/thingsboard/server/controller/MobileApplicationController.java index 77e34fc18c..0e12ddb273 100644 --- a/application/src/main/java/org/thingsboard/server/controller/MobileApplicationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/MobileApplicationController.java @@ -16,6 +16,7 @@ package org.thingsboard.server.controller; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import io.swagger.v3.oas.annotations.Parameter; import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; @@ -47,6 +48,7 @@ import org.thingsboard.server.service.security.system.SystemSecurityService; import java.net.URI; import java.net.URISyntaxException; +import java.util.Optional; import static org.thingsboard.server.controller.ControllerConstants.AVAILABLE_FOR_ANY_AUTHORIZED_USER; import static org.thingsboard.server.controller.ControllerConstants.SYSTEM_AUTHORITY_PARAGRAPH; @@ -81,8 +83,8 @@ public class MobileApplicationController extends BaseController { " }\n" + "}"; - public static final String ANDROID_APPLICATION_STORE_LINK = "https://play.google.com/store/apps/details?id=org.thingsboard.demo.app"; - public static final String APPLE_APPLICATION_STORE_LINK = "https://apps.apple.com/us/app/thingsboard-live/id1594355695"; + public static final String DEFAULT_GOOGLE_APP_STORE_LINK = "https://play.google.com/store/apps/details?id=org.thingsboard.demo.app"; + public static final String DEFAULT_APPLE_APP_STORE_LINK = "https://apps.apple.com/us/app/thingsboard-live/id1594355695"; public static final String SECRET = "secret"; public static final String SECRET_PARAM_DESCRIPTION = "A string value representing short-lived secret key"; public static final String DEFAULT_APP_DOMAIN = "demo.thingsboard.io"; @@ -171,13 +173,14 @@ public class MobileApplicationController extends BaseController { @GetMapping(value = "/api/noauth/qr") public ResponseEntity getApplicationRedirect(@RequestHeader(value = "User-Agent") String userAgent) { + MobileAppSettings mobileAppSettings = mobileAppSettingsService.getMobileAppSettings(TenantId.SYS_TENANT_ID); if (userAgent.contains("Android")) { return ResponseEntity.status(HttpStatus.FOUND) - .header("Location", ANDROID_APPLICATION_STORE_LINK) + .header("Location", getGoogleStoreLink(mobileAppSettings)) .build(); } else if (userAgent.contains("iPhone") || userAgent.contains("iPad")) { return ResponseEntity.status(HttpStatus.FOUND) - .header("Location", APPLE_APPLICATION_STORE_LINK) + .header("Location", getAppleStoreLink(mobileAppSettings)) .build(); } else { return ResponseEntity.status(HttpStatus.NOT_FOUND) @@ -185,4 +188,26 @@ public class MobileApplicationController extends BaseController { } } + @ApiOperation(value = "Get Mobile application store link (getMobileAppStoreLinks)", + notes = "The response payload contains links to google play and apple store." + AVAILABLE_FOR_ANY_AUTHORIZED_USER) + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @GetMapping(value = "/api/mobile/app/storeLinks") + public JsonNode getMobileAppStoreLinks() { + MobileAppSettings mobileAppSettings = mobileAppSettingsService.getMobileAppSettings(TenantId.SYS_TENANT_ID); + ObjectNode infoObject = JacksonUtil.newObjectNode(); + infoObject.put("googlePlayLink", getGoogleStoreLink(mobileAppSettings)); + infoObject.put("appStoreLink", getAppleStoreLink(mobileAppSettings)); + return infoObject; + } + + private String getGoogleStoreLink(MobileAppSettings mobileAppSettings) { + return mobileAppSettings.isUseDefaultApp() ? DEFAULT_GOOGLE_APP_STORE_LINK : + Optional.ofNullable(mobileAppSettings.getAndroidConfig().getStoreLink()).orElse(DEFAULT_GOOGLE_APP_STORE_LINK); + } + + private String getAppleStoreLink(MobileAppSettings mobileAppSettings) { + return mobileAppSettings.isUseDefaultApp() ? DEFAULT_APPLE_APP_STORE_LINK : + Optional.ofNullable(mobileAppSettings.getIosConfig().getStoreLink()).orElse(DEFAULT_APPLE_APP_STORE_LINK); + } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/AndroidConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/AndroidConfig.java index e1fb5056fc..d670382462 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/AndroidConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/AndroidConfig.java @@ -34,5 +34,7 @@ public class AndroidConfig { private String appPackage; @NoXss private String sha256CertFingerprints; + @NoXss + private String storeLink; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/IosConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/IosConfig.java index f03b446b3b..7d40dfe805 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/IosConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/IosConfig.java @@ -32,5 +32,7 @@ public class IosConfig { private boolean enabled; @NoXss private String appId; + @NoXss + private String storeLink; } From 99e712127e61e48332a43916c53ec59be88a7fae Mon Sep 17 00:00:00 2001 From: rusikv Date: Wed, 5 Jun 2024 11:25:49 +0300 Subject: [PATCH 05/14] UI: mob app qr code - added links to stores for badges --- ui-ngx/src/app/core/http/mobile-app.service.ts | 6 +++++- .../mobile-app-qrcode-widget.component.html | 8 ++++++-- .../mobile-app-qrcode-widget.component.scss | 3 +++ .../lib/mobile-app-qrcode-widget.component.ts | 14 ++++++++++++++ .../admin/mobile-app-settings.component.html | 18 ++++++++++++++++++ .../admin/mobile-app-settings.component.ts | 6 ++++-- .../src/app/shared/models/mobile-app.models.ts | 9 ++++++++- .../assets/locale/locale.constant-en_US.json | 1 + 8 files changed, 59 insertions(+), 6 deletions(-) diff --git a/ui-ngx/src/app/core/http/mobile-app.service.ts b/ui-ngx/src/app/core/http/mobile-app.service.ts index 7b108e487c..ded753f39c 100644 --- a/ui-ngx/src/app/core/http/mobile-app.service.ts +++ b/ui-ngx/src/app/core/http/mobile-app.service.ts @@ -18,7 +18,7 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { defaultHttpOptionsFromConfig, RequestConfig } from '@core/http/http-utils'; import { Observable } from 'rxjs'; -import { MobileAppSettings } from '@shared/models/mobile-app.models'; +import { MobileAppSettings, MobileAppStoreLinks } from '@shared/models/mobile-app.models'; @Injectable({ providedIn: 'root' @@ -42,4 +42,8 @@ export class MobileAppService { return this.http.get(`/api/mobile/deepLink`, defaultHttpOptionsFromConfig(config)); } + public getMobileAppStoreLinks(config?: RequestConfig): Observable { + return this.http.get('/api/mobile/app/storeLinks', defaultHttpOptionsFromConfig(config)); + } + } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.html index 0acb8d3b8d..a682e26c1d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.html @@ -28,8 +28,12 @@
- - + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.scss index e7ff7864bd..fd51d8af4a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.scss @@ -68,6 +68,9 @@ flex-direction: column; flex: 0.44; gap: 8px; + img { + cursor: pointer; + } } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.ts index 4b761bf59d..4a49cfd6b8 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.ts @@ -45,6 +45,9 @@ export class MobileAppQrcodeWidgetComponent extends PageComponent implements OnI private deepLinkTTL: number; private deepLinkTTLTimeoutID: NodeJS.Timeout; + googlePlayLink: string; + appStoreLink: string; + previewMode = false; badgePosition = BadgePosition; @@ -108,6 +111,10 @@ export class MobileAppQrcodeWidgetComponent extends PageComponent implements OnI } else { this.previewMode = true; } + this.mobileAppService.getMobileAppStoreLinks().subscribe(storeLinks => { + this.googlePlayLink = storeLinks.googlePlayLink; + this.appStoreLink = storeLinks.appStoreLink; + }); this.initMobileAppQRCode(); } @@ -130,6 +137,13 @@ export class MobileAppQrcodeWidgetComponent extends PageComponent implements OnI } } + navigateByStoreLink($event, storeLink: string) { + if ($event) { + $event.stopPropagation(); + } + window.open(storeLink, '_blank'); + } + private initMobileAppQRCode() { if (this.deepLinkTTLTimeoutID) { clearTimeout(this.deepLinkTTLTimeoutID); diff --git a/ui-ngx/src/app/modules/home/pages/admin/mobile-app-settings.component.html b/ui-ngx/src/app/modules/home/pages/admin/mobile-app-settings.component.html index 208520bbf0..cb4e8170a9 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/mobile-app-settings.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/mobile-app-settings.component.html @@ -76,6 +76,15 @@
+
+
+
{{ 'admin.mobile-app.store-link' | translate }}
+ + + +
+
@@ -101,6 +110,15 @@
+
+
+
{{ 'admin.mobile-app.store-link' | translate }}
+ + + +
+
diff --git a/ui-ngx/src/app/modules/home/pages/admin/mobile-app-settings.component.ts b/ui-ngx/src/app/modules/home/pages/admin/mobile-app-settings.component.ts index edba183919..250db94267 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/mobile-app-settings.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/mobile-app-settings.component.ts @@ -128,11 +128,13 @@ export class MobileAppSettingsComponent extends PageComponent implements HasConf androidConfig: this.fb.group({ enabled: [true], appPackage: [{value: '', disabled: true}, [Validators.required]], - sha256CertFingerprints: [{value: '', disabled: true}, [Validators.required]] + sha256CertFingerprints: [{value: '', disabled: true}, [Validators.required]], + storeLink: [''] }), iosConfig: this.fb.group({ enabled: [true], - appId: [{value: '', disabled: true}, [Validators.required]] + appId: [{value: '', disabled: true}, [Validators.required]], + storeLink: [''] }), qrCodeConfig: this.fb.group({ showOnHomePage: [true], diff --git a/ui-ngx/src/app/shared/models/mobile-app.models.ts b/ui-ngx/src/app/shared/models/mobile-app.models.ts index 7b98da0744..5ce28802cf 100644 --- a/ui-ngx/src/app/shared/models/mobile-app.models.ts +++ b/ui-ngx/src/app/shared/models/mobile-app.models.ts @@ -26,12 +26,14 @@ export interface MobileAppSettings extends HasTenantId { export interface AndroidConfig { enabled: boolean; appPackage: string; - sha256CertFingerprints: string + sha256CertFingerprints: string; + storeLink: string; } export interface IosConfig { enabled: boolean; appId: string; + storeLink: string; } export interface QRCodeConfig { @@ -52,6 +54,11 @@ export enum BadgePosition { LEFT = 'LEFT' } +export interface MobileAppStoreLinks { + googlePlayLink: string; + appStoreLink: string; +} + export const badgePositionTranslationsMap = new Map([ [BadgePosition.RIGHT, 'admin.mobile-app.right'], [BadgePosition.LEFT, 'admin.mobile-app.left'] diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 170d4836ee..2d2de1a5cc 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -437,6 +437,7 @@ "ios": "iOS", "app-id": "App ID", "app-id-required": "App ID is required", + "store-link": "Store link", "appearance": "Appearance", "appearance-on-home-page": "Appearance on Home page", "enabled": "Enabled", From 1619e6038098e1f437a50eff844084fd63f0b89d Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Wed, 5 Jun 2024 12:03:52 +0300 Subject: [PATCH 06/14] minor refactoring --- .../MobileApplicationController.java | 22 +++++++++---------- .../common/data/mobile/AndroidConfig.java | 2 +- .../common/data/mobile/HasStoreLink.java | 21 ++++++++++++++++++ .../server/common/data/mobile/IosConfig.java | 2 +- 4 files changed, 33 insertions(+), 14 deletions(-) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/mobile/HasStoreLink.java diff --git a/application/src/main/java/org/thingsboard/server/controller/MobileApplicationController.java b/application/src/main/java/org/thingsboard/server/controller/MobileApplicationController.java index 0e12ddb273..452e2a3bf9 100644 --- a/application/src/main/java/org/thingsboard/server/controller/MobileApplicationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/MobileApplicationController.java @@ -34,6 +34,7 @@ import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.mobile.AndroidConfig; +import org.thingsboard.server.common.data.mobile.HasStoreLink; import org.thingsboard.server.common.data.mobile.IosConfig; import org.thingsboard.server.common.data.mobile.MobileAppSettings; import org.thingsboard.server.common.data.security.model.JwtPair; @@ -174,13 +175,14 @@ public class MobileApplicationController extends BaseController { @GetMapping(value = "/api/noauth/qr") public ResponseEntity getApplicationRedirect(@RequestHeader(value = "User-Agent") String userAgent) { MobileAppSettings mobileAppSettings = mobileAppSettingsService.getMobileAppSettings(TenantId.SYS_TENANT_ID); + boolean isDefaultApp = mobileAppSettings.isUseDefaultApp(); if (userAgent.contains("Android")) { return ResponseEntity.status(HttpStatus.FOUND) - .header("Location", getGoogleStoreLink(mobileAppSettings)) + .header("Location", getAppStoreLink(isDefaultApp, mobileAppSettings.getAndroidConfig(), DEFAULT_GOOGLE_APP_STORE_LINK)) .build(); } else if (userAgent.contains("iPhone") || userAgent.contains("iPad")) { return ResponseEntity.status(HttpStatus.FOUND) - .header("Location", getAppleStoreLink(mobileAppSettings)) + .header("Location", getAppStoreLink(isDefaultApp, mobileAppSettings.getIosConfig(), DEFAULT_APPLE_APP_STORE_LINK)) .build(); } else { return ResponseEntity.status(HttpStatus.NOT_FOUND) @@ -195,19 +197,15 @@ public class MobileApplicationController extends BaseController { public JsonNode getMobileAppStoreLinks() { MobileAppSettings mobileAppSettings = mobileAppSettingsService.getMobileAppSettings(TenantId.SYS_TENANT_ID); ObjectNode infoObject = JacksonUtil.newObjectNode(); - infoObject.put("googlePlayLink", getGoogleStoreLink(mobileAppSettings)); - infoObject.put("appStoreLink", getAppleStoreLink(mobileAppSettings)); + boolean isDefaultApp = mobileAppSettings.isUseDefaultApp(); + infoObject.put("googlePlayLink", getAppStoreLink(isDefaultApp, mobileAppSettings.getAndroidConfig(), DEFAULT_GOOGLE_APP_STORE_LINK)); + infoObject.put("appStoreLink", getAppStoreLink(isDefaultApp, mobileAppSettings.getIosConfig(), DEFAULT_APPLE_APP_STORE_LINK)); return infoObject; } - private String getGoogleStoreLink(MobileAppSettings mobileAppSettings) { - return mobileAppSettings.isUseDefaultApp() ? DEFAULT_GOOGLE_APP_STORE_LINK : - Optional.ofNullable(mobileAppSettings.getAndroidConfig().getStoreLink()).orElse(DEFAULT_GOOGLE_APP_STORE_LINK); - } - - private String getAppleStoreLink(MobileAppSettings mobileAppSettings) { - return mobileAppSettings.isUseDefaultApp() ? DEFAULT_APPLE_APP_STORE_LINK : - Optional.ofNullable(mobileAppSettings.getIosConfig().getStoreLink()).orElse(DEFAULT_APPLE_APP_STORE_LINK); + private String getAppStoreLink(boolean isDefault, HasStoreLink storeLink, String defaultAppStoreLink) { + return isDefault ? defaultAppStoreLink : + Optional.ofNullable(storeLink.getStoreLink()).orElse(defaultAppStoreLink); } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/AndroidConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/AndroidConfig.java index d670382462..1918a691c7 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/AndroidConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/AndroidConfig.java @@ -27,7 +27,7 @@ import org.thingsboard.server.common.data.validation.NoXss; @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode -public class AndroidConfig { +public class AndroidConfig implements HasStoreLink { private boolean enabled; @NoXss diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/HasStoreLink.java b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/HasStoreLink.java new file mode 100644 index 0000000000..568ecb6fdc --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/HasStoreLink.java @@ -0,0 +1,21 @@ +/** + * Copyright © 2016-2024 The Thingsboard Authors + * + * 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. + */ +package org.thingsboard.server.common.data.mobile; + +public interface HasStoreLink { + + String getStoreLink(); +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/IosConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/IosConfig.java index 7d40dfe805..3dcba1bc60 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/IosConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/IosConfig.java @@ -27,7 +27,7 @@ import org.thingsboard.server.common.data.validation.NoXss; @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode -public class IosConfig { +public class IosConfig implements HasStoreLink { private boolean enabled; @NoXss From df8820be08fb6b58bb70ec85b0acdb3a927b186a Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Wed, 5 Jun 2024 14:46:27 +0300 Subject: [PATCH 07/14] updated getAppStoreLink to return default if link is empty --- .../MobileApplicationController.java | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/MobileApplicationController.java b/application/src/main/java/org/thingsboard/server/controller/MobileApplicationController.java index 452e2a3bf9..a85193aa45 100644 --- a/application/src/main/java/org/thingsboard/server/controller/MobileApplicationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/MobileApplicationController.java @@ -31,6 +31,7 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RestController; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.mobile.AndroidConfig; @@ -175,14 +176,14 @@ public class MobileApplicationController extends BaseController { @GetMapping(value = "/api/noauth/qr") public ResponseEntity getApplicationRedirect(@RequestHeader(value = "User-Agent") String userAgent) { MobileAppSettings mobileAppSettings = mobileAppSettingsService.getMobileAppSettings(TenantId.SYS_TENANT_ID); - boolean isDefaultApp = mobileAppSettings.isUseDefaultApp(); + boolean useDefaultApp = mobileAppSettings.isUseDefaultApp(); if (userAgent.contains("Android")) { return ResponseEntity.status(HttpStatus.FOUND) - .header("Location", getAppStoreLink(isDefaultApp, mobileAppSettings.getAndroidConfig(), DEFAULT_GOOGLE_APP_STORE_LINK)) + .header("Location", getAppStoreLink(useDefaultApp, mobileAppSettings.getAndroidConfig(), DEFAULT_GOOGLE_APP_STORE_LINK)) .build(); } else if (userAgent.contains("iPhone") || userAgent.contains("iPad")) { return ResponseEntity.status(HttpStatus.FOUND) - .header("Location", getAppStoreLink(isDefaultApp, mobileAppSettings.getIosConfig(), DEFAULT_APPLE_APP_STORE_LINK)) + .header("Location", getAppStoreLink(useDefaultApp, mobileAppSettings.getIosConfig(), DEFAULT_APPLE_APP_STORE_LINK)) .build(); } else { return ResponseEntity.status(HttpStatus.NOT_FOUND) @@ -197,15 +198,18 @@ public class MobileApplicationController extends BaseController { public JsonNode getMobileAppStoreLinks() { MobileAppSettings mobileAppSettings = mobileAppSettingsService.getMobileAppSettings(TenantId.SYS_TENANT_ID); ObjectNode infoObject = JacksonUtil.newObjectNode(); - boolean isDefaultApp = mobileAppSettings.isUseDefaultApp(); - infoObject.put("googlePlayLink", getAppStoreLink(isDefaultApp, mobileAppSettings.getAndroidConfig(), DEFAULT_GOOGLE_APP_STORE_LINK)); - infoObject.put("appStoreLink", getAppStoreLink(isDefaultApp, mobileAppSettings.getIosConfig(), DEFAULT_APPLE_APP_STORE_LINK)); + boolean useDefaultApp = mobileAppSettings.isUseDefaultApp(); + infoObject.put("googlePlayLink", getAppStoreLink(useDefaultApp, mobileAppSettings.getAndroidConfig(), DEFAULT_GOOGLE_APP_STORE_LINK)); + infoObject.put("appStoreLink", getAppStoreLink(useDefaultApp, mobileAppSettings.getIosConfig(), DEFAULT_APPLE_APP_STORE_LINK)); return infoObject; } - private String getAppStoreLink(boolean isDefault, HasStoreLink storeLink, String defaultAppStoreLink) { - return isDefault ? defaultAppStoreLink : - Optional.ofNullable(storeLink.getStoreLink()).orElse(defaultAppStoreLink); + private String getAppStoreLink(boolean useDefaultApp, HasStoreLink storeLink, String defaultAppStoreLink) { + if (useDefaultApp || StringUtils.isEmpty(storeLink.getStoreLink())) { + return defaultAppStoreLink; + } else { + return storeLink.getStoreLink(); + } } } From 2c2b48f5e817e43019eec0ed022edc6f98915904 Mon Sep 17 00:00:00 2001 From: rusikv Date: Wed, 5 Jun 2024 15:07:46 +0300 Subject: [PATCH 08/14] UI: qr code settigns store links form labels renaming --- .../home/pages/admin/mobile-app-settings.component.html | 4 ++-- ui-ngx/src/assets/locale/locale.constant-en_US.json | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/admin/mobile-app-settings.component.html b/ui-ngx/src/app/modules/home/pages/admin/mobile-app-settings.component.html index cb4e8170a9..4b82f17a5f 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/mobile-app-settings.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/mobile-app-settings.component.html @@ -79,7 +79,7 @@
-
{{ 'admin.mobile-app.store-link' | translate }}
+
{{ 'admin.mobile-app.google-play-link' | translate }}
@@ -113,7 +113,7 @@
-
{{ 'admin.mobile-app.store-link' | translate }}
+
{{ 'admin.mobile-app.app-store-link' | translate }}
diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 2d2de1a5cc..90e455b925 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -437,7 +437,8 @@ "ios": "iOS", "app-id": "App ID", "app-id-required": "App ID is required", - "store-link": "Store link", + "google-play-link": "Google Play link", + "app-store-link": "App Store link", "appearance": "Appearance", "appearance-on-home-page": "Appearance on Home page", "enabled": "Enabled", From 44f4cd3ad152d77bd9e896c46c5941cf7bc1eec6 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Wed, 5 Jun 2024 16:36:59 +0300 Subject: [PATCH 09/14] deleted redundant api --- .../controller/MobileApplicationController.java | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/MobileApplicationController.java b/application/src/main/java/org/thingsboard/server/controller/MobileApplicationController.java index a85193aa45..837359dc62 100644 --- a/application/src/main/java/org/thingsboard/server/controller/MobileApplicationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/MobileApplicationController.java @@ -50,7 +50,6 @@ import org.thingsboard.server.service.security.system.SystemSecurityService; import java.net.URI; import java.net.URISyntaxException; -import java.util.Optional; import static org.thingsboard.server.controller.ControllerConstants.AVAILABLE_FOR_ANY_AUTHORIZED_USER; import static org.thingsboard.server.controller.ControllerConstants.SYSTEM_AUTHORITY_PARAGRAPH; @@ -191,19 +190,6 @@ public class MobileApplicationController extends BaseController { } } - @ApiOperation(value = "Get Mobile application store link (getMobileAppStoreLinks)", - notes = "The response payload contains links to google play and apple store." + AVAILABLE_FOR_ANY_AUTHORIZED_USER) - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @GetMapping(value = "/api/mobile/app/storeLinks") - public JsonNode getMobileAppStoreLinks() { - MobileAppSettings mobileAppSettings = mobileAppSettingsService.getMobileAppSettings(TenantId.SYS_TENANT_ID); - ObjectNode infoObject = JacksonUtil.newObjectNode(); - boolean useDefaultApp = mobileAppSettings.isUseDefaultApp(); - infoObject.put("googlePlayLink", getAppStoreLink(useDefaultApp, mobileAppSettings.getAndroidConfig(), DEFAULT_GOOGLE_APP_STORE_LINK)); - infoObject.put("appStoreLink", getAppStoreLink(useDefaultApp, mobileAppSettings.getIosConfig(), DEFAULT_APPLE_APP_STORE_LINK)); - return infoObject; - } - private String getAppStoreLink(boolean useDefaultApp, HasStoreLink storeLink, String defaultAppStoreLink) { if (useDefaultApp || StringUtils.isEmpty(storeLink.getStoreLink())) { return defaultAppStoreLink; From be5220a9e9dc02fd1555ac9a54475f37d2856b10 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Wed, 5 Jun 2024 19:02:42 +0300 Subject: [PATCH 10/14] fixed mobile app settings default value for store links --- .../MobileApplicationController.java | 18 ++-------------- .../common/data/mobile/AndroidConfig.java | 2 +- .../common/data/mobile/HasStoreLink.java | 21 ------------------- .../server/common/data/mobile/IosConfig.java | 2 +- .../mobile/BaseMobileAppSettingsService.java | 12 +++++++++++ 5 files changed, 16 insertions(+), 39 deletions(-) delete mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/mobile/HasStoreLink.java diff --git a/application/src/main/java/org/thingsboard/server/controller/MobileApplicationController.java b/application/src/main/java/org/thingsboard/server/controller/MobileApplicationController.java index 837359dc62..486eac4444 100644 --- a/application/src/main/java/org/thingsboard/server/controller/MobileApplicationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/MobileApplicationController.java @@ -16,7 +16,6 @@ package org.thingsboard.server.controller; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; import io.swagger.v3.oas.annotations.Parameter; import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; @@ -31,11 +30,9 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RestController; import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.mobile.AndroidConfig; -import org.thingsboard.server.common.data.mobile.HasStoreLink; import org.thingsboard.server.common.data.mobile.IosConfig; import org.thingsboard.server.common.data.mobile.MobileAppSettings; import org.thingsboard.server.common.data.security.model.JwtPair; @@ -84,8 +81,6 @@ public class MobileApplicationController extends BaseController { " }\n" + "}"; - public static final String DEFAULT_GOOGLE_APP_STORE_LINK = "https://play.google.com/store/apps/details?id=org.thingsboard.demo.app"; - public static final String DEFAULT_APPLE_APP_STORE_LINK = "https://apps.apple.com/us/app/thingsboard-live/id1594355695"; public static final String SECRET = "secret"; public static final String SECRET_PARAM_DESCRIPTION = "A string value representing short-lived secret key"; public static final String DEFAULT_APP_DOMAIN = "demo.thingsboard.io"; @@ -175,14 +170,13 @@ public class MobileApplicationController extends BaseController { @GetMapping(value = "/api/noauth/qr") public ResponseEntity getApplicationRedirect(@RequestHeader(value = "User-Agent") String userAgent) { MobileAppSettings mobileAppSettings = mobileAppSettingsService.getMobileAppSettings(TenantId.SYS_TENANT_ID); - boolean useDefaultApp = mobileAppSettings.isUseDefaultApp(); if (userAgent.contains("Android")) { return ResponseEntity.status(HttpStatus.FOUND) - .header("Location", getAppStoreLink(useDefaultApp, mobileAppSettings.getAndroidConfig(), DEFAULT_GOOGLE_APP_STORE_LINK)) + .header("Location", mobileAppSettings.getAndroidConfig().getStoreLink()) .build(); } else if (userAgent.contains("iPhone") || userAgent.contains("iPad")) { return ResponseEntity.status(HttpStatus.FOUND) - .header("Location", getAppStoreLink(useDefaultApp, mobileAppSettings.getIosConfig(), DEFAULT_APPLE_APP_STORE_LINK)) + .header("Location", mobileAppSettings.getIosConfig().getStoreLink()) .build(); } else { return ResponseEntity.status(HttpStatus.NOT_FOUND) @@ -190,12 +184,4 @@ public class MobileApplicationController extends BaseController { } } - private String getAppStoreLink(boolean useDefaultApp, HasStoreLink storeLink, String defaultAppStoreLink) { - if (useDefaultApp || StringUtils.isEmpty(storeLink.getStoreLink())) { - return defaultAppStoreLink; - } else { - return storeLink.getStoreLink(); - } - } - } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/AndroidConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/AndroidConfig.java index 1918a691c7..d670382462 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/AndroidConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/AndroidConfig.java @@ -27,7 +27,7 @@ import org.thingsboard.server.common.data.validation.NoXss; @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode -public class AndroidConfig implements HasStoreLink { +public class AndroidConfig { private boolean enabled; @NoXss diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/HasStoreLink.java b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/HasStoreLink.java deleted file mode 100644 index 568ecb6fdc..0000000000 --- a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/HasStoreLink.java +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright © 2016-2024 The Thingsboard Authors - * - * 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. - */ -package org.thingsboard.server.common.data.mobile; - -public interface HasStoreLink { - - String getStoreLink(); -} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/IosConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/IosConfig.java index 3dcba1bc60..7d40dfe805 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/IosConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/IosConfig.java @@ -27,7 +27,7 @@ import org.thingsboard.server.common.data.validation.NoXss; @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode -public class IosConfig implements HasStoreLink { +public class IosConfig { private boolean enabled; @NoXss diff --git a/dao/src/main/java/org/thingsboard/server/dao/mobile/BaseMobileAppSettingsService.java b/dao/src/main/java/org/thingsboard/server/dao/mobile/BaseMobileAppSettingsService.java index 57e95cc56f..4c67d97884 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/mobile/BaseMobileAppSettingsService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/mobile/BaseMobileAppSettingsService.java @@ -19,6 +19,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.event.TransactionalEventListener; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.mobile.AndroidConfig; import org.thingsboard.server.common.data.mobile.BadgePosition; @@ -39,6 +40,8 @@ public class BaseMobileAppSettingsService extends AbstractCachedEntityService mobileAppSettingsDataValidator; @@ -87,9 +90,11 @@ public class BaseMobileAppSettingsService extends AbstractCachedEntityService Date: Thu, 6 Jun 2024 11:53:02 +0300 Subject: [PATCH 11/14] extended mobile application settings with default store links --- .../MobileApplicationController.java | 12 ++++++---- .../src/main/resources/thingsboard.yml | 15 +++++++++--- .../common/data/mobile/MobileAppSettings.java | 17 +++++++++++++ .../mobile/BaseMobileAppSettingsService.java | 24 +++++++++---------- 4 files changed, 48 insertions(+), 20 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/MobileApplicationController.java b/application/src/main/java/org/thingsboard/server/controller/MobileApplicationController.java index 486eac4444..b736da8ab4 100644 --- a/application/src/main/java/org/thingsboard/server/controller/MobileApplicationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/MobileApplicationController.java @@ -58,6 +58,8 @@ public class MobileApplicationController extends BaseController { @Value("${cache.specs.mobileSecretKey.timeToLiveInMinutes:2}") private int mobileSecretKeyTtl; + @Value("${mobileApp.domain:demo.thingsboard.io}") + private String defaultAppDomain; public static final String ASSET_LINKS_PATTERN = "[{\n" + " \"relation\": [\"delegate_permission/common.handle_all_urls\"],\n" + @@ -83,7 +85,6 @@ public class MobileApplicationController extends BaseController { public static final String SECRET = "secret"; public static final String SECRET_PARAM_DESCRIPTION = "A string value representing short-lived secret key"; - public static final String DEFAULT_APP_DOMAIN = "demo.thingsboard.io"; public static final String DEEP_LINK_PATTERN = "https://%s/api/noauth/qr?secret=%s&ttl=%s"; private final SystemSecurityService systemSecurityService; @@ -149,7 +150,7 @@ public class MobileApplicationController extends BaseController { if (!mobileAppSettings.isUseDefaultApp()) { appDomain = platformDomain; } else { - appDomain = DEFAULT_APP_DOMAIN; + appDomain = defaultAppDomain; } String deepLink = String.format(DEEP_LINK_PATTERN, appDomain, secret, mobileSecretKeyTtl); if (!appDomain.equals(platformDomain)) { @@ -170,13 +171,16 @@ public class MobileApplicationController extends BaseController { @GetMapping(value = "/api/noauth/qr") public ResponseEntity getApplicationRedirect(@RequestHeader(value = "User-Agent") String userAgent) { MobileAppSettings mobileAppSettings = mobileAppSettingsService.getMobileAppSettings(TenantId.SYS_TENANT_ID); + boolean useDefaultApp = mobileAppSettings.isUseDefaultApp(); + String googlePlayLink = useDefaultApp ? mobileAppSettings.getDefaultGooglePlayLink() : mobileAppSettings.getAndroidConfig().getStoreLink(); + String appStoreLink = useDefaultApp ? mobileAppSettings.getDefaultGooglePlayLink() : mobileAppSettings.getIosConfig().getStoreLink(); if (userAgent.contains("Android")) { return ResponseEntity.status(HttpStatus.FOUND) - .header("Location", mobileAppSettings.getAndroidConfig().getStoreLink()) + .header("Location", googlePlayLink) .build(); } else if (userAgent.contains("iPhone") || userAgent.contains("iPad")) { return ResponseEntity.status(HttpStatus.FOUND) - .header("Location", mobileAppSettings.getIosConfig().getStoreLink()) + .header("Location", appStoreLink) .build(); } else { return ResponseEntity.status(HttpStatus.NOT_FOUND) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 454ca9427f..d61c376a83 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -1463,9 +1463,9 @@ queue: - key: max.poll.interval.ms # Example of specific consumer properties value per topic for VC value: "${TB_QUEUE_KAFKA_VC_MAX_POLL_INTERVAL_MS:600000}" - # tb_rule_engine.sq: - # - key: max.poll.records - # value: "${TB_QUEUE_KAFKA_SQ_MAX_POLL_RECORDS:1024}" + # tb_rule_engine.sq: + # - key: max.poll.records + # value: "${TB_QUEUE_KAFKA_SQ_MAX_POLL_RECORDS:1024}" tb_housekeeper: # Consumer properties for Housekeeper tasks topic - key: max.poll.records @@ -1794,3 +1794,12 @@ management: elasticsearch: # Enable the org.springframework.boot.actuate.elasticsearch.ElasticsearchRestClientHealthIndicator.doHealthCheck enabled: "false" + +# Mobile application settings for Thingsboard mobile application +mobileApp: + # Host for default Thingsboard mobile application for common edition + domain: "${TB_MOBILE_APP_DOMAIN:demo.thingsboard.io}" + # Link to Google Play store for default Thingsboard mobile application + googlePlayLink: "${TB_MOBILE_APP_GOOGLE_PLAY_LINK:https://play.google.com/store/apps/details?id=org.thingsboard.demo.app}" + # Link to App Store for default Thingsboard mobile application + appStoreLink: "${TB_MOBILE_APP_APP_STORE_LINK:https://apps.apple.com/us/app/thingsboard-live/id1594355695}" diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileAppSettings.java b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileAppSettings.java index 9a7b88f22c..31b2029bfc 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileAppSettings.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileAppSettings.java @@ -15,29 +15,46 @@ */ package org.thingsboard.server.common.data.mobile; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; import lombok.Data; +import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.id.MobileAppSettingsId; import org.thingsboard.server.common.data.id.TenantId; +@Schema @Data +@EqualsAndHashCode(callSuper = true) public class MobileAppSettings extends BaseData implements HasTenantId { private static final long serialVersionUID = 2628323657987010348L; + @Schema(description = "JSON object with Tenant Id.", accessMode = Schema.AccessMode.READ_ONLY) private TenantId tenantId; + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "Type of application: true means use default Thingsboard app", example = "true") private boolean useDefaultApp; @Valid + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "Android mobile app configuration.") private AndroidConfig androidConfig; @Valid + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "Ios mobile app configuration.") private IosConfig iosConfig; @Valid + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "QR code config configuration.") private QRCodeConfig qrCodeConfig; + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private String defaultGooglePlayLink; + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private String defaultAppStoreLink; + public MobileAppSettings() { } + public MobileAppSettings(MobileAppSettingsId id) { super(id); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/mobile/BaseMobileAppSettingsService.java b/dao/src/main/java/org/thingsboard/server/dao/mobile/BaseMobileAppSettingsService.java index 4c67d97884..bb445426f1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/mobile/BaseMobileAppSettingsService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/mobile/BaseMobileAppSettingsService.java @@ -17,9 +17,9 @@ package org.thingsboard.server.dao.mobile; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.event.TransactionalEventListener; -import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.mobile.AndroidConfig; import org.thingsboard.server.common.data.mobile.BadgePosition; @@ -40,8 +40,11 @@ public class BaseMobileAppSettingsService extends AbstractCachedEntityService mobileAppSettingsDataValidator; @@ -52,7 +55,7 @@ public class BaseMobileAppSettingsService extends AbstractCachedEntityService Date: Thu, 6 Jun 2024 12:17:57 +0300 Subject: [PATCH 12/14] fixed app store link retrieval --- .../server/controller/MobileApplicationController.java | 2 +- application/src/main/resources/thingsboard.yml | 6 +++--- .../server/dao/mobile/BaseMobileAppSettingsService.java | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/MobileApplicationController.java b/application/src/main/java/org/thingsboard/server/controller/MobileApplicationController.java index b736da8ab4..ca8b11ee80 100644 --- a/application/src/main/java/org/thingsboard/server/controller/MobileApplicationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/MobileApplicationController.java @@ -173,7 +173,7 @@ public class MobileApplicationController extends BaseController { MobileAppSettings mobileAppSettings = mobileAppSettingsService.getMobileAppSettings(TenantId.SYS_TENANT_ID); boolean useDefaultApp = mobileAppSettings.isUseDefaultApp(); String googlePlayLink = useDefaultApp ? mobileAppSettings.getDefaultGooglePlayLink() : mobileAppSettings.getAndroidConfig().getStoreLink(); - String appStoreLink = useDefaultApp ? mobileAppSettings.getDefaultGooglePlayLink() : mobileAppSettings.getIosConfig().getStoreLink(); + String appStoreLink = useDefaultApp ? mobileAppSettings.getDefaultAppStoreLink() : mobileAppSettings.getIosConfig().getStoreLink(); if (userAgent.contains("Android")) { return ResponseEntity.status(HttpStatus.FOUND) .header("Location", googlePlayLink) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index d61c376a83..4e5458bd18 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -1797,9 +1797,9 @@ management: # Mobile application settings for Thingsboard mobile application mobileApp: - # Host for default Thingsboard mobile application for common edition + # Server domain name for Thingsboard Live mobile application domain: "${TB_MOBILE_APP_DOMAIN:demo.thingsboard.io}" - # Link to Google Play store for default Thingsboard mobile application + # Link to Google Play store for Thingsboard Live mobile application googlePlayLink: "${TB_MOBILE_APP_GOOGLE_PLAY_LINK:https://play.google.com/store/apps/details?id=org.thingsboard.demo.app}" - # Link to App Store for default Thingsboard mobile application + # Link to App Store for Thingsboard Live mobile application appStoreLink: "${TB_MOBILE_APP_APP_STORE_LINK:https://apps.apple.com/us/app/thingsboard-live/id1594355695}" diff --git a/dao/src/main/java/org/thingsboard/server/dao/mobile/BaseMobileAppSettingsService.java b/dao/src/main/java/org/thingsboard/server/dao/mobile/BaseMobileAppSettingsService.java index bb445426f1..5ec717da09 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/mobile/BaseMobileAppSettingsService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/mobile/BaseMobileAppSettingsService.java @@ -43,7 +43,7 @@ public class BaseMobileAppSettingsService extends AbstractCachedEntityService Date: Thu, 6 Jun 2024 13:25:23 +0300 Subject: [PATCH 13/14] UI: qr code: extended mobile app settings with default links, refactored badges, removed redundant apis --- .../src/app/core/http/mobile-app.service.ts | 6 +----- .../mobile-app-qrcode-widget.component.html | 19 +++++++++++-------- .../mobile-app-qrcode-widget.component.scss | 19 ++++++++++++------- .../lib/mobile-app-qrcode-widget.component.ts | 18 +++++++----------- .../admin/mobile-app-settings.component.html | 18 ++++++++++++++++++ .../admin/mobile-app-settings.component.ts | 12 ++++++++++-- .../app/shared/models/mobile-app.models.ts | 7 ++----- .../assets/locale/locale.constant-en_US.json | 6 +++++- 8 files changed, 66 insertions(+), 39 deletions(-) diff --git a/ui-ngx/src/app/core/http/mobile-app.service.ts b/ui-ngx/src/app/core/http/mobile-app.service.ts index ded753f39c..7b108e487c 100644 --- a/ui-ngx/src/app/core/http/mobile-app.service.ts +++ b/ui-ngx/src/app/core/http/mobile-app.service.ts @@ -18,7 +18,7 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { defaultHttpOptionsFromConfig, RequestConfig } from '@core/http/http-utils'; import { Observable } from 'rxjs'; -import { MobileAppSettings, MobileAppStoreLinks } from '@shared/models/mobile-app.models'; +import { MobileAppSettings } from '@shared/models/mobile-app.models'; @Injectable({ providedIn: 'root' @@ -42,8 +42,4 @@ export class MobileAppService { return this.http.get(`/api/mobile/deepLink`, defaultHttpOptionsFromConfig(config)); } - public getMobileAppStoreLinks(config?: RequestConfig): Observable { - return this.http.get('/api/mobile/app/storeLinks', defaultHttpOptionsFromConfig(config)); - } - } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.html index a682e26c1d..b526cfd079 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.html @@ -26,14 +26,17 @@ [class.row]="mobileAppSettings?.qrCodeConfig.badgePosition === badgePosition.RIGHT" [class.row-reverse]="mobileAppSettings?.qrCodeConfig.badgePosition === badgePosition.LEFT">
-
- - +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.scss index fd51d8af4a..1447e5cf61 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.scss @@ -68,17 +68,22 @@ flex-direction: column; flex: 0.44; gap: 8px; - img { - cursor: pointer; + &.tb-no-interaction { + pointer-events: none; + } + a { + display: contents; + border-bottom: none; + &:hover { + border-bottom: none; + } } } } :host ::ng-deep { - .tb-mobile-app-qrcode-panel { - > div.tb-widget-title { - padding: 0; - z-index: 1; - } + .tb-mobile-app-qrcode-panel .tb-widget-title { + padding: 0; + z-index: 1; } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.ts index 4a49cfd6b8..b484b9eed1 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.ts @@ -90,6 +90,13 @@ export class MobileAppQrcodeWidgetComponent extends PageComponent implements OnI if (!this.mobileAppSettings) { this.mobileAppService.getMobileAppSettings().subscribe((settings => { this.mobileAppSettings = settings; + + const useDefaultApp = this.mobileAppSettings.useDefaultApp; + this.appStoreLink = useDefaultApp ? this.mobileAppSettings.defaultAppStoreLink : + this.mobileAppSettings.iosConfig.storeLink; + this.googlePlayLink = useDefaultApp ? this.mobileAppSettings.defaultGooglePlayLink : + this.mobileAppSettings.androidConfig.storeLink; + if (isDefinedAndNotNull(this.ctx.settings.useSystemSettings) && !this.ctx.settings.useSystemSettings) { this.mobileAppSettings = mergeDeep(this.mobileAppSettings, this.ctx.settings); } @@ -111,10 +118,6 @@ export class MobileAppQrcodeWidgetComponent extends PageComponent implements OnI } else { this.previewMode = true; } - this.mobileAppService.getMobileAppStoreLinks().subscribe(storeLinks => { - this.googlePlayLink = storeLinks.googlePlayLink; - this.appStoreLink = storeLinks.appStoreLink; - }); this.initMobileAppQRCode(); } @@ -137,13 +140,6 @@ export class MobileAppQrcodeWidgetComponent extends PageComponent implements OnI } } - navigateByStoreLink($event, storeLink: string) { - if ($event) { - $event.stopPropagation(); - } - window.open(storeLink, '_blank'); - } - private initMobileAppQRCode() { if (this.deepLinkTTLTimeoutID) { clearTimeout(this.deepLinkTTLTimeoutID); diff --git a/ui-ngx/src/app/modules/home/pages/admin/mobile-app-settings.component.html b/ui-ngx/src/app/modules/home/pages/admin/mobile-app-settings.component.html index 4b82f17a5f..51e7c6f43d 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/mobile-app-settings.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/mobile-app-settings.component.html @@ -82,6 +82,15 @@
{{ 'admin.mobile-app.google-play-link' | translate }}
+ + warning +
@@ -116,6 +125,15 @@
{{ 'admin.mobile-app.app-store-link' | translate }}
+ + warning +
diff --git a/ui-ngx/src/app/modules/home/pages/admin/mobile-app-settings.component.ts b/ui-ngx/src/app/modules/home/pages/admin/mobile-app-settings.component.ts index 250db94267..0f2527d833 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/mobile-app-settings.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/mobile-app-settings.component.ts @@ -57,14 +57,18 @@ export class MobileAppSettingsComponent extends PageComponent implements HasConf if (value) { this.mobileAppSettingsForm.get('androidConfig.appPackage').disable({emitEvent: false}); this.mobileAppSettingsForm.get('androidConfig.sha256CertFingerprints').disable({emitEvent: false}); + this.mobileAppSettingsForm.get('androidConfig.storeLink').disable({emitEvent: false}); this.mobileAppSettingsForm.get('iosConfig.appId').disable({emitEvent: false}); + this.mobileAppSettingsForm.get('iosConfig.storeLink').disable({emitEvent: false}); } else { if (this.mobileAppSettingsForm.get('androidConfig.enabled').value) { this.mobileAppSettingsForm.get('androidConfig.appPackage').enable({emitEvent: false}); this.mobileAppSettingsForm.get('androidConfig.sha256CertFingerprints').enable({emitEvent: false}); + this.mobileAppSettingsForm.get('androidConfig.storeLink').enable({emitEvent: false}); } if (this.mobileAppSettingsForm.get('iosConfig.enabled').value) { this.mobileAppSettingsForm.get('iosConfig.appId').enable({emitEvent: false}); + this.mobileAppSettingsForm.get('iosConfig.storeLink').enable({emitEvent: false}); } } }); @@ -129,12 +133,12 @@ export class MobileAppSettingsComponent extends PageComponent implements HasConf enabled: [true], appPackage: [{value: '', disabled: true}, [Validators.required]], sha256CertFingerprints: [{value: '', disabled: true}, [Validators.required]], - storeLink: [''] + storeLink: ['', [Validators.required]] }), iosConfig: this.fb.group({ enabled: [true], appId: [{value: '', disabled: true}, [Validators.required]], - storeLink: [''] + storeLink: ['', [Validators.required]] }), qrCodeConfig: this.fb.group({ showOnHomePage: [true], @@ -156,10 +160,12 @@ export class MobileAppSettingsComponent extends PageComponent implements HasConf if (!this.mobileAppSettingsForm.get('useDefaultApp').value) { this.mobileAppSettingsForm.get('androidConfig.appPackage').enable({emitEvent: false}); this.mobileAppSettingsForm.get('androidConfig.sha256CertFingerprints').enable({emitEvent: false}); + this.mobileAppSettingsForm.get('androidConfig.storeLink').enable({emitEvent: false}); } } else { this.mobileAppSettingsForm.get('androidConfig.appPackage').disable({emitEvent: false}); this.mobileAppSettingsForm.get('androidConfig.sha256CertFingerprints').disable({emitEvent: false}); + this.mobileAppSettingsForm.get('androidConfig.storeLink').disable({emitEvent: false}); } this.mobileAppSettingsForm.get('qrCodeConfig.badgeEnabled').updateValueAndValidity({onlySelf: true}); } @@ -168,9 +174,11 @@ export class MobileAppSettingsComponent extends PageComponent implements HasConf if (value) { if (!this.mobileAppSettingsForm.get('useDefaultApp').value) { this.mobileAppSettingsForm.get('iosConfig.appId').enable({emitEvent: false}); + this.mobileAppSettingsForm.get('iosConfig.storeLink').enable({emitEvent: false}); } } else { this.mobileAppSettingsForm.get('iosConfig.appId').disable({emitEvent: false}); + this.mobileAppSettingsForm.get('iosConfig.storeLink').disable({emitEvent: false}); } this.mobileAppSettingsForm.get('qrCodeConfig.badgeEnabled').updateValueAndValidity({onlySelf: true}); } diff --git a/ui-ngx/src/app/shared/models/mobile-app.models.ts b/ui-ngx/src/app/shared/models/mobile-app.models.ts index 5ce28802cf..b879a9eb45 100644 --- a/ui-ngx/src/app/shared/models/mobile-app.models.ts +++ b/ui-ngx/src/app/shared/models/mobile-app.models.ts @@ -21,6 +21,8 @@ export interface MobileAppSettings extends HasTenantId { androidConfig: AndroidConfig; iosConfig: IosConfig; qrCodeConfig: QRCodeConfig; + defaultGooglePlayLink: string; + defaultAppStoreLink: string; } export interface AndroidConfig { @@ -54,11 +56,6 @@ export enum BadgePosition { LEFT = 'LEFT' } -export interface MobileAppStoreLinks { - googlePlayLink: string; - appStoreLink: string; -} - export const badgePositionTranslationsMap = new Map([ [BadgePosition.RIGHT, 'admin.mobile-app.right'], [BadgePosition.LEFT, 'admin.mobile-app.left'] diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 90e455b925..e852ffbc3c 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -438,7 +438,9 @@ "app-id": "App ID", "app-id-required": "App ID is required", "google-play-link": "Google Play link", + "google-play-link-required": "Google Play link is required", "app-store-link": "App Store link", + "app-store-link-required": "App Store link is required", "appearance": "Appearance", "appearance-on-home-page": "Appearance on Home page", "enabled": "Enabled", @@ -5567,7 +5569,9 @@ }, "widgets": { "mobile-app-qr-code": { - "configuration-hint": "The configuration depends on the Mobile app QR code widget in platform main settings" + "configuration-hint": "The configuration depends on the Mobile app QR code widget in platform main settings", + "get-it-on-google-play": "Get it on Google Play", + "download-on-the-app-store": "Download on the App Store" }, "action-button": { "behavior": "Behavior", From 663883a4f7260a030b6ebee7b2635035d9ab593f Mon Sep 17 00:00:00 2001 From: rusikv Date: Thu, 6 Jun 2024 16:56:14 +0300 Subject: [PATCH 14/14] UI: fixed qr code not rendering on apple devices --- .../lib/mobile-app-qrcode-widget.component.html | 2 +- .../lib/mobile-app-qrcode-widget.component.scss | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.html index b526cfd079..1426d02a0d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.html @@ -25,7 +25,7 @@
-
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.scss index 1447e5cf61..5bd423d796 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.scss @@ -82,8 +82,14 @@ } :host ::ng-deep { - .tb-mobile-app-qrcode-panel .tb-widget-title { - padding: 0; - z-index: 1; + .tb-mobile-app-qrcode-panel { + .tb-widget-title { + padding: 0; + z-index: 1; + } + .tb-qrcode svg { + width: 100%; + height: 100%; + } } }