From 7ea1d48500436ccdbb9268e35ab62fc5b37ac56d Mon Sep 17 00:00:00 2001
From: Ben <43026681+bwp91@users.noreply.github.com>
Date: Sun, 22 Sep 2024 14:16:59 +0100
Subject: [PATCH] offer to set up a child bridge immediately after configuring
 a new plugin

---
 CHANGELOG.md                                  |  3 +-
 .../custom-plugins.component.ts               | 37 ++++++++++---
 .../manual-config/manual-config.component.ts  | 23 ++++++--
 .../plugin-config/plugin-config.component.ts  | 53 ++++++-------------
 .../uninstall-plugin.component.html           |  7 ++-
 5 files changed, 72 insertions(+), 51 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5af804bfe..641157cb8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,7 +19,8 @@ All notable changes to `homebridge-config-ui-x` will be documented in this file.
 - move lang, theme, temp units + port to main settings
   - relabelled the UI settings screen as 'UI Advanced Settings'
 - offer to unpair child bridges when uninstalling plugins
-- updates to `pl` language file (#2181) (@mkz212)
+- offer to set up a child bridge immediately after configuring a new plugin
+- updates to `pl` language file (#2181, #2189) (@mkz212)
 - updates to `th` language file (#2182) (@tomzt)
 
 ### Other Changes
diff --git a/ui/src/app/core/manage-plugins/custom-plugins/custom-plugins.component.ts b/ui/src/app/core/manage-plugins/custom-plugins/custom-plugins.component.ts
index 03aea622d..b9e9a9757 100644
--- a/ui/src/app/core/manage-plugins/custom-plugins/custom-plugins.component.ts
+++ b/ui/src/app/core/manage-plugins/custom-plugins/custom-plugins.component.ts
@@ -1,4 +1,6 @@
 import { ApiService } from '@/app/core/api.service'
+import { ManagePluginsService } from '@/app/core/manage-plugins/manage-plugins.service'
+import { SettingsService } from '@/app/core/settings.service'
 import { IoNamespace, WsService } from '@/app/core/ws.service'
 import { environment } from '@/environments/environment'
 import { Component, ElementRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'
@@ -37,6 +39,7 @@ export class CustomPluginsComponent implements OnInit, OnDestroy {
   public formUpdatedSubject = new Subject()
   public formActionSubject = new Subject()
   public childBridges: any[] = []
+  public isFirstSave = false
 
   private io: IoNamespace
   private basePath: string
@@ -47,10 +50,12 @@ export class CustomPluginsComponent implements OnInit, OnDestroy {
 
   constructor(
     public activeModal: NgbActiveModal,
-    private $translate: TranslateService,
-    private $toastr: ToastrService,
     private $api: ApiService,
+    public $plugin: ManagePluginsService,
     private $router: Router,
+    private $settings: SettingsService,
+    private $toastr: ToastrService,
+    private $translate: TranslateService,
     private $ws: WsService,
   ) {}
 
@@ -59,6 +64,10 @@ export class CustomPluginsComponent implements OnInit, OnDestroy {
     this.pluginAlias = this.schema.pluginAlias
     this.pluginType = this.schema.pluginType
 
+    if (this.pluginConfig.length === 0) {
+      this.isFirstSave = true
+    }
+
     // start accessory subscription
     if (this.io.connected) {
       this.io.socket.emit('start', this.plugin.name)
@@ -416,17 +425,31 @@ export class CustomPluginsComponent implements OnInit, OnDestroy {
 
   async savePluginConfig(exit = false) {
     this.saveInProgress = true
-    return firstValueFrom(this.$api.post(`/config-editor/plugin/${encodeURIComponent(this.plugin.name)}`, this.pluginConfig)).then(() => {
+
+    try {
+      const newConfig = await firstValueFrom(this.$api.post(`/config-editor/plugin/${encodeURIComponent(this.plugin.name)}`, this.pluginConfig))
+
       this.saveInProgress = false
 
       if (exit) {
-        this.getChildBridges()
-        this.justSavedAndExited = true
+        if (this.isFirstSave) {
+          if (this.$settings.env.recommendChildBridges && this.$settings.env.serviceMode && newConfig[0]?.platform) {
+            // Close the modal and open the child bridge setup modal
+            this.activeModal.close()
+            await this.$plugin.bridgeSettings(this.plugin)
+          }
+        } else {
+          this.getChildBridges()
+          this.justSavedAndExited = true
+        }
       }
-    }).catch(() => {
+
+      return newConfig
+    } catch (error) {
       this.saveInProgress = false
       this.$toastr.error(this.$translate.instant('config.toast_failed_to_save_config'), this.$translate.instant('toast.title_error'))
-    })
+      console.error(error)
+    }
   }
 
   getChildBridges(): any[] {
diff --git a/ui/src/app/core/manage-plugins/manual-config/manual-config.component.ts b/ui/src/app/core/manage-plugins/manual-config/manual-config.component.ts
index c6c8180fd..f4549814a 100644
--- a/ui/src/app/core/manage-plugins/manual-config/manual-config.component.ts
+++ b/ui/src/app/core/manage-plugins/manual-config/manual-config.component.ts
@@ -1,5 +1,6 @@
 import { ApiService } from '@/app/core/api.service'
 import { RestartComponent } from '@/app/core/components/restart/restart.component'
+import { ManagePluginsService } from '@/app/core/manage-plugins/manage-plugins.service'
 import { MobileDetectService } from '@/app/core/mobile-detect.service'
 import { SettingsService } from '@/app/core/settings.service'
 import { Component, Input, OnInit } from '@angular/core'
@@ -28,6 +29,7 @@ export class ManualConfigComponent implements OnInit {
   public currentBlock: string
   public currentBlockIndex: number | null = null
   public saveInProgress = false
+  public isFirstSave = false
 
   public monacoEditor: any
   public editorOptions: any
@@ -36,6 +38,7 @@ export class ManualConfigComponent implements OnInit {
     public activeModal: NgbActiveModal,
     private $api: ApiService,
     private $modal: NgbModal,
+    public $plugin: ManagePluginsService,
     private $settings: SettingsService,
     private $toastr: ToastrService,
     private $translate: TranslateService,
@@ -98,6 +101,7 @@ export class ManualConfigComponent implements OnInit {
         if (this.pluginConfig.length) {
           this.editBlock(0)
         } else {
+          this.isFirstSave = true
           this.addBlock()
         }
       },
@@ -215,12 +219,21 @@ export class ManualConfigComponent implements OnInit {
     }
 
     try {
-      await firstValueFrom(this.$api.post(`/config-editor/plugin/${encodeURIComponent(this.plugin.name)}`, this.pluginConfig))
+      const newConfig = await firstValueFrom(this.$api.post(`/config-editor/plugin/${encodeURIComponent(this.plugin.name)}`, this.pluginConfig))
       this.activeModal.close()
-      this.$modal.open(RestartComponent, {
-        size: 'lg',
-        backdrop: 'static',
-      })
+
+      // If it is the first time configuring a plugin, then offer to set up a child bridge straight away
+      if (this.isFirstSave) {
+        if (this.$settings.env.recommendChildBridges && this.$settings.env.serviceMode && newConfig[0]?.platform) {
+          // Close the modal and open the child bridge setup modal
+          await this.$plugin.bridgeSettings(this.plugin)
+        }
+      } else {
+        this.$modal.open(RestartComponent, {
+          size: 'lg',
+          backdrop: 'static',
+        })
+      }
     } catch {
       this.$toastr.error(this.$translate.instant('config.toast_failed_to_save_config'), this.$translate.instant('toast.title_error'))
       this.saveInProgress = false
diff --git a/ui/src/app/core/manage-plugins/plugin-config/plugin-config.component.ts b/ui/src/app/core/manage-plugins/plugin-config/plugin-config.component.ts
index be6640753..b454e3e91 100644
--- a/ui/src/app/core/manage-plugins/plugin-config/plugin-config.component.ts
+++ b/ui/src/app/core/manage-plugins/plugin-config/plugin-config.component.ts
@@ -1,10 +1,9 @@
 import { ApiService } from '@/app/core/api.service'
-import { DonateComponent } from '@/app/core/manage-plugins/donate/donate.component'
-import { PluginLogsComponent } from '@/app/core/manage-plugins/plugin-logs/plugin-logs.component'
+import { ManagePluginsService } from '@/app/core/manage-plugins/manage-plugins.service'
 import { SettingsService } from '@/app/core/settings.service'
 import { Component, Input, OnInit } from '@angular/core'
 import { Router } from '@angular/router'
-import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap'
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
 import { TranslateService } from '@ngx-translate/core'
 import { ToastrService } from 'ngx-toastr'
 import { firstValueFrom } from 'rxjs'
@@ -31,15 +30,16 @@ export class PluginConfigComponent implements OnInit {
   public show = ''
   public saveInProgress: boolean
   public justSavedAndExited = false
+  public isFirstSave = false
 
   public childBridges: any[] = []
 
   constructor(
     public activeModal: NgbActiveModal,
     private $api: ApiService,
-    private $modal: NgbModal,
-    private $settings: SettingsService,
+    public $plugin: ManagePluginsService,
     private $router: Router,
+    private $settings: SettingsService,
     private $toastr: ToastrService,
     private translate: TranslateService,
   ) {}
@@ -63,6 +63,7 @@ export class PluginConfigComponent implements OnInit {
         }
 
         if (!this.pluginConfig.length) {
+          this.isFirstSave = true
           this.addBlock()
         } else {
           this.show = this.pluginConfig[0].__uuid__
@@ -83,14 +84,23 @@ export class PluginConfigComponent implements OnInit {
     const configBlocks = this.pluginConfig.map(x => x.config)
 
     try {
-      await firstValueFrom(this.$api.post(`/config-editor/plugin/${encodeURIComponent(this.plugin.name)}`, configBlocks))
+      const newConfig = await firstValueFrom(this.$api.post(`/config-editor/plugin/${encodeURIComponent(this.plugin.name)}`, configBlocks))
 
       // reload app settings if the config was changed for Homebridge UI
       if (this.plugin.name === 'homebridge-config-ui-x') {
         this.$settings.getAppSettings().catch()
       }
 
-      this.getChildBridges()
+      // If it is the first time configuring a plugin, then offer to set up a child bridge straight away
+      if (this.isFirstSave) {
+        if (this.$settings.env.recommendChildBridges && this.$settings.env.serviceMode && newConfig[0]?.platform) {
+          // Close the modal and open the child bridge setup modal
+          this.activeModal.close()
+          await this.$plugin.bridgeSettings(this.plugin)
+        }
+      } else {
+        this.getChildBridges()
+      }
       this.justSavedAndExited = true
     } catch (err) {
       this.$toastr.error(
@@ -187,33 +197,4 @@ export class PluginConfigComponent implements OnInit {
       }
     }
   }
-
-  openFundingModalForUi() {
-    try {
-      this.$api.get('/plugins').subscribe((plugins) => {
-        const ref = this.$modal.open(DonateComponent, {
-          size: 'lg',
-          backdrop: 'static',
-        })
-        ref.componentInstance.plugin = plugins.find((x: any) => x.name === 'homebridge-config-ui-x')
-      })
-    } catch (e) {
-      // ignore
-    }
-  }
-
-  openPluginLogModalForUi() {
-    try {
-      this.$api.get('/plugins').subscribe((plugins) => {
-        const ref = this.$modal.open(PluginLogsComponent, {
-          size: 'xl',
-          backdrop: 'static',
-        })
-
-        ref.componentInstance.plugin = plugins.find((x: any) => x.name === 'homebridge-config-ui-x')
-      })
-    } catch (e) {
-      // ignore
-    }
-  }
 }
diff --git a/ui/src/app/core/manage-plugins/uninstall-plugin/uninstall-plugin.component.html b/ui/src/app/core/manage-plugins/uninstall-plugin/uninstall-plugin.component.html
index d6c7c4fc7..9de24a619 100644
--- a/ui/src/app/core/manage-plugins/uninstall-plugin/uninstall-plugin.component.html
+++ b/ui/src/app/core/manage-plugins/uninstall-plugin/uninstall-plugin.component.html
@@ -20,13 +20,16 @@ <h5 class="modal-title">{{ 'plugins.manage.label_uninstall' | translate }}: {{ p
           <span class="hb-uix-slider hb-uix-round"></span>
         </label>
       </p>
-      <p *ngIf="hasChildBridges">
-        <label class="hb-uix-switch d-inline" for="remove-plugin-config">
+      <p *ngIf="hasChildBridges" class="mb-4">
+        <label class="hb-uix-switch d-inline" for="remove-plugin-child-bridges">
           <input id="remove-plugin-child-bridges" type="checkbox" [(ngModel)]="removeChildBridges">
           <span>{{ 'plugins.manage.message_uninstall_unpair_child_bridges' | translate }}</span>
           <span class="hb-uix-slider hb-uix-round"></span>
         </label>
       </p>
+      <ngb-alert type="warning" [dismissible]="false" *ngIf="hasChildBridges && removeChildBridges">
+        {{ 'reset.bridges_single.list_2' | translate }}
+      </ngb-alert>
       <ngb-alert *ngIf="!pluginAlias || !pluginType" type="info" [dismissible]="false">
         {{ 'plugins.manage.message_uninstall_remove_config_required' | translate }}
       </ngb-alert>