This is an automated email from the ASF dual-hosted git repository.

riemer pushed a commit to branch 
2396-should-prevent-multiple-clicks-on-tutorial-button
in repository https://gitbox.apache.org/repos/asf/streampipes.git


The following commit(s) were added to 
refs/heads/2396-should-prevent-multiple-clicks-on-tutorial-button by this push:
     new f04f555f3 fix(#2396): Disable tutorial button, fix subscription cleanup
f04f555f3 is described below

commit f04f555f3c0ad888491230fe2b18ece1ef19787d
Author: Dominik Riemer <[email protected]>
AuthorDate: Mon Jan 8 20:25:38 2024 +0100

    fix(#2396): Disable tutorial button, fix subscription cleanup
---
 .../existing-adapters.component.html               |  4 +-
 .../existing-adapters.component.ts                 | 53 +++++++++----------
 ui/src/app/home/home.component.ts                  | 28 +++++-----
 ui/src/app/pipelines/pipelines.component.html      |  1 +
 ui/src/app/pipelines/pipelines.component.ts        | 60 +++++++++++++++-------
 ui/src/app/services/tour/shepherd.service.ts       | 26 ++++++++--
 6 files changed, 107 insertions(+), 65 deletions(-)

diff --git 
a/ui/src/app/connect/components/existing-adapters/existing-adapters.component.html
 
b/ui/src/app/connect/components/existing-adapters/existing-adapters.component.html
index b3beb2826..4e5cfefc8 100644
--- 
a/ui/src/app/connect/components/existing-adapters/existing-adapters.component.html
+++ 
b/ui/src/app/connect/components/existing-adapters/existing-adapters.component.html
@@ -66,11 +66,13 @@
             >
                 <button
                     mat-icon-button
+                    color="accent"
                     id="startAdapterTutorial3"
                     (click)="startAdapterTutorial()"
                     matTooltip="Tutorial: Generic Adapter"
+                    [disabled]="tutorialActive"
                 >
-                    <mat-icon color="accent">school</mat-icon>
+                    <mat-icon>school</mat-icon>
                 </button>
             </div>
             <button
diff --git 
a/ui/src/app/connect/components/existing-adapters/existing-adapters.component.ts
 
b/ui/src/app/connect/components/existing-adapters/existing-adapters.component.ts
index d46dbaf33..c10f7729f 100644
--- 
a/ui/src/app/connect/components/existing-adapters/existing-adapters.component.ts
+++ 
b/ui/src/app/connect/components/existing-adapters/existing-adapters.component.ts
@@ -16,18 +16,16 @@
  *
  */
 
-import { Component, OnInit, ViewChild } from '@angular/core';
+import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
 import {
     AdapterDescription,
     AdapterMonitoringService,
     AdapterService,
     PipelineElementService,
-    PipelineService,
     SpLogMessage,
     SpMetricsEntry,
 } from '@streampipes/platform-services';
 import { MatTableDataSource } from '@angular/material/table';
-import { ConnectService } from '../../services/connect.service';
 import {
     CurrentUserService,
     DialogRef,
@@ -47,7 +45,7 @@ import { Router } from '@angular/router';
 import { AdapterFilterSettingsModel } from 
'../../model/adapter-filter-settings.model';
 import { AdapterFilterPipe } from '../../filter/adapter-filter.pipe';
 import { SpConnectRoutes } from '../../connect.routes';
-import { zip } from 'rxjs';
+import { Subscription, zip } from 'rxjs';
 import { RestApi } from '../../../services/rest-api.service';
 import { ShepherdService } from '../../../services/tour/shepherd.service';
 
@@ -56,7 +54,7 @@ import { ShepherdService } from 
'../../../services/tour/shepherd.service';
     templateUrl: './existing-adapters.component.html',
     styleUrls: ['./existing-adapters.component.scss'],
 })
-export class ExistingAdaptersComponent implements OnInit {
+export class ExistingAdaptersComponent implements OnInit, OnDestroy {
     existingAdapters: AdapterDescription[] = [];
     filteredAdapters: AdapterDescription[] = [];
 
@@ -82,13 +80,16 @@ export class ExistingAdaptersComponent implements OnInit {
     isAdmin = false;
 
     adapterMetrics: Record<string, SpMetricsEntry> = {};
+    tutorialActive = false;
+
+    userSubscription: Subscription;
+    tutorialActiveSubscription: Subscription;
 
     constructor(
         private adapterService: AdapterService,
         private dialogService: DialogService,
         private currentUserService: CurrentUserService,
         private pipelineElementService: PipelineElementService,
-        private pipelineService: PipelineService,
         private router: Router,
         private restApi: RestApi,
         private adapterFilter: AdapterFilterPipe,
@@ -101,10 +102,16 @@ export class ExistingAdaptersComponent implements OnInit {
         this.breadcrumbService.updateBreadcrumb(
             this.breadcrumbService.getRootLink(SpConnectRoutes.BASE),
         );
-        this.currentUserService.user$.subscribe(user => {
-            this.isAdmin = user.roles.indexOf(UserRole.ROLE_ADMIN) > -1;
-            this.getAdaptersRunning();
-        });
+        this.userSubscription = this.currentUserService.user$.subscribe(
+            user => {
+                this.isAdmin = user.roles.indexOf(UserRole.ROLE_ADMIN) > -1;
+                this.getAdaptersRunning();
+            },
+        );
+        this.tutorialActiveSubscription =
+            this.shepherdService.tutorialActive$.subscribe(tutorialActive => {
+                this.tutorialActive = tutorialActive;
+            });
     }
 
     startAdapter(adapter: AdapterDescription) {
@@ -225,28 +232,11 @@ export class ExistingAdaptersComponent implements OnInit {
     }
 
     /**
-     * Start edit mode when adapter is not used within a pipeline, otherwise 
shows warning dialog
+     * Start edit mode
      * @param adapter
      */
     editAdapter(adapter: AdapterDescription) {
-        this.pipelineService
-            .getPipelinesContainingElementId(
-                adapter.correspondingDataStreamElementId,
-            )
-            .subscribe(effectedPipelines => {
-                // if (effectedPipelines.length > 0) {
-                //     this.dialogService.open(CanNotEditAdapterDialog, {
-                //         panelType: PanelType.STANDARD_PANEL,
-                //         title: 'No edit possible',
-                //         width: '50vw',
-                //         data: {
-                //             pipelines: effectedPipelines,
-                //         },
-                //     });
-                // } else {
-                this.router.navigate(['connect', 'edit', adapter.elementId]);
-                //}
-            });
+        this.router.navigate(['connect', 'edit', adapter.elementId]);
     }
 
     deleteAdapter(adapter: AdapterDescription): void {
@@ -331,4 +321,9 @@ export class ExistingAdaptersComponent implements OnInit {
             'metrics',
         ]);
     }
+
+    ngOnDestroy() {
+        this.userSubscription?.unsubscribe();
+        this.tutorialActiveSubscription?.unsubscribe();
+    }
 }
diff --git a/ui/src/app/home/home.component.ts 
b/ui/src/app/home/home.component.ts
index 158993a3a..6670c28a6 100644
--- a/ui/src/app/home/home.component.ts
+++ b/ui/src/app/home/home.component.ts
@@ -39,6 +39,7 @@ import {
     PipelineService,
 } from '@streampipes/platform-services';
 import { zip } from 'rxjs';
+import { UserInfo } from 
'../../../projects/streampipes/platform-services/src/lib/model/gen/streampipes-model';
 
 @Component({
     templateUrl: './home.component.html',
@@ -62,6 +63,7 @@ export class HomeComponent implements OnInit {
     missingElementsForTutorial: any = [];
 
     isTutorialOpen = false;
+    currentUser: UserInfo;
 
     constructor(
         private homeService: HomeService,
@@ -79,18 +81,19 @@ export class HomeComponent implements OnInit {
     }
 
     ngOnInit() {
-        this.currentUserService.user$.subscribe(userInfo => {
-            const isAdmin = userInfo.roles.indexOf(UserRole.ROLE_ADMIN) > -1;
-            this.showStatus =
-                isAdmin ||
-                userInfo.roles.indexOf(UserRole.ROLE_PIPELINE_ADMIN) > -1;
-            if (isAdmin) {
-                this.loadResources();
-            }
-        });
+        this.currentUser = this.currentUserService.getCurrentUser();
+        const isAdmin = this.hasRole(UserRole.ROLE_ADMIN);
+        this.showStatus = isAdmin || 
this.hasRole(UserRole.ROLE_PIPELINE_ADMIN);
+        if (isAdmin) {
+            this.loadResources();
+        }
         this.breadcrumbService.updateBreadcrumb([]);
     }
 
+    hasRole(role: UserRole): boolean {
+        return this.currentUser.roles.indexOf(role) > -1;
+    }
+
     openLink(link) {
         if (link.link.newWindow) {
             window.open(link.link.value);
@@ -100,8 +103,7 @@ export class HomeComponent implements OnInit {
     }
 
     checkForTutorial() {
-        const currentUser = this.currentUserService.getCurrentUser();
-        if (currentUser.showTutorial) {
+        if (this.currentUser.showTutorial) {
             if (this.requiredPipelineElementsForTourPresent()) {
                 this.isTutorialOpen = true;
                 const dialogRef = this.dialogService.open(
@@ -110,7 +112,7 @@ export class HomeComponent implements OnInit {
                         panelType: PanelType.STANDARD_PANEL,
                         title: 'Welcome to ' + this.appConstants.APP_NAME,
                         data: {
-                            userInfo: currentUser,
+                            userInfo: this.currentUser,
                         },
                     },
                 );
@@ -125,7 +127,7 @@ export class HomeComponent implements OnInit {
 
     startTutorial() {
         if (this.requiredPipelineElementsForTourPresent()) {
-            this.router.navigate(['connect']).then(next => {
+            this.router.navigate(['connect']).then(() => {
                 this.shepherdService.startAdapterTour();
             });
         } else {
diff --git a/ui/src/app/pipelines/pipelines.component.html 
b/ui/src/app/pipelines/pipelines.component.html
index 9f1851d6f..f8feda339 100644
--- a/ui/src/app/pipelines/pipelines.component.html
+++ b/ui/src/app/pipelines/pipelines.component.html
@@ -88,6 +88,7 @@
             color="accent"
             (click)="startPipelineTour()"
             [matTooltip]="'Tutorial'"
+            [disabled]="tutorialActive"
             style="margin-right: 5px"
         >
             <i class="material-icons"> school </i>
diff --git a/ui/src/app/pipelines/pipelines.component.ts 
b/ui/src/app/pipelines/pipelines.component.ts
index 99adc8f1b..e87d8f02f 100644
--- a/ui/src/app/pipelines/pipelines.component.ts
+++ b/ui/src/app/pipelines/pipelines.component.ts
@@ -17,7 +17,7 @@
  */
 
 import * as FileSaver from 'file-saver';
-import { Component, OnInit } from '@angular/core';
+import { Component, OnDestroy, OnInit } from '@angular/core';
 import {
     FunctionId,
     FunctionsService,
@@ -41,13 +41,14 @@ import { UserPrivilege } from 
'../_enums/user-privilege.enum';
 import { SpPipelineRoutes } from './pipelines.routes';
 import { UserRole } from '../_enums/user-role.enum';
 import { ShepherdService } from '../services/tour/shepherd.service';
+import { Subscription } from 'rxjs';
 
 @Component({
     selector: 'sp-pipelines',
     templateUrl: './pipelines.component.html',
     styleUrls: ['./pipelines.component.scss'],
 })
-export class PipelinesComponent implements OnInit {
+export class PipelinesComponent implements OnInit, OnDestroy {
     pipeline: Pipeline;
     pipelines: Pipeline[] = [];
     systemPipelines: Pipeline[] = [];
@@ -67,6 +68,12 @@ export class PipelinesComponent implements OnInit {
     functionsReady = false;
     isAdminRole = false;
 
+    tutorialActive = false;
+
+    activatedRouteSubscription: Subscription;
+    tutorialActiveSubscription: Subscription;
+    userSubscription: Subscription;
+
     constructor(
         private pipelineService: PipelineService,
         private dialogService: DialogService,
@@ -87,23 +94,32 @@ export class PipelinesComponent implements OnInit {
         this.breadcrumbService.updateBreadcrumb(
             this.breadcrumbService.getRootLink(SpPipelineRoutes.BASE),
         );
-        this.currentUserService.user$.subscribe(user => {
-            this.hasPipelineWritePrivileges = this.authService.hasRole(
-                UserPrivilege.PRIVILEGE_WRITE_PIPELINE,
-            );
-            this.isAdminRole = this.authService.hasRole(UserRole.ROLE_ADMIN);
-        });
-        this.activatedRoute.queryParams.subscribe(params => {
-            if (params['pipeline']) {
-                this.pipelineIdToStart = params['pipeline'];
-            }
-            if (params.startTutorial) {
-                this.startPipelineTour();
-            }
-            this.getPipelineCategories();
-            this.getPipelines();
-            this.getFunctions();
-        });
+        this.userSubscription = this.currentUserService.user$.subscribe(
+            user => {
+                this.hasPipelineWritePrivileges = this.authService.hasRole(
+                    UserPrivilege.PRIVILEGE_WRITE_PIPELINE,
+                );
+                this.isAdminRole = this.authService.hasRole(
+                    UserRole.ROLE_ADMIN,
+                );
+            },
+        );
+        this.activatedRouteSubscription =
+            this.activatedRoute.queryParams.subscribe(params => {
+                if (params['pipeline']) {
+                    this.pipelineIdToStart = params['pipeline'];
+                }
+                if (params.startTutorial) {
+                    this.startPipelineTour();
+                }
+                this.getPipelineCategories();
+                this.getPipelines();
+                this.getFunctions();
+            });
+        this.tutorialActiveSubscription =
+            this.shepherdService.tutorialActive$.subscribe(tutorialActive => {
+                this.tutorialActive = tutorialActive;
+            });
     }
 
     setSelectedTab(index) {
@@ -243,4 +259,10 @@ export class PipelinesComponent implements OnInit {
                 this.shepherdService.trigger('pipeline-new-button-clicked'),
             );
     }
+
+    ngOnDestroy() {
+        this.activatedRouteSubscription?.unsubscribe();
+        this.userSubscription?.unsubscribe();
+        this.tutorialActiveSubscription?.unsubscribe();
+    }
 }
diff --git a/ui/src/app/services/tour/shepherd.service.ts 
b/ui/src/app/services/tour/shepherd.service.ts
index 3eeeb263f..069c33f5d 100644
--- a/ui/src/app/services/tour/shepherd.service.ts
+++ b/ui/src/app/services/tour/shepherd.service.ts
@@ -21,6 +21,7 @@ import { Injectable } from '@angular/core';
 import { Router } from '@angular/router';
 import { TourProviderService } from './tour-provider.service';
 import Step from 'shepherd.js/src/types/step';
+import { BehaviorSubject } from 'rxjs';
 import StepOptions = Step.StepOptions;
 
 @Injectable({ providedIn: 'root' })
@@ -28,6 +29,9 @@ export class ShepherdService {
     currentTour: Shepherd.Tour;
     currentTourSettings: any;
     timeWaitMillis: number;
+    tutorialActive = false;
+
+    public tutorialActive$ = new BehaviorSubject(false);
 
     constructor(
         private router: Router,
@@ -38,12 +42,22 @@ export class ShepherdService {
 
     makeTour(currentTourSettings) {
         const tour = new Shepherd.Tour({
-            confirmCancel: true,
-            confirmCancelMessage: 'Do you really want to cancel the tour?',
+            confirmCancel: () => {
+                return new Promise(resolve => {
+                    const exit = window.confirm(
+                        'Do you really want to cancel the tour?',
+                    );
+                    if (exit) {
+                        resolve(true);
+                        this.changeTutorialStatus(false);
+                    } else {
+                        resolve(false);
+                    }
+                });
+            },
             defaultStepOptions: {
                 classes: 'shadow-md bg-purple-dark',
                 scrollTo: true,
-                // showCancelLink: true
             },
         });
 
@@ -101,6 +115,7 @@ export class ShepherdService {
         this.currentTourSettings = tourSettings;
         this.currentTour = this.makeTour(this.currentTourSettings);
         this.currentTour.start();
+        this.changeTutorialStatus(true);
     }
 
     makeCancelButton(tour) {
@@ -198,4 +213,9 @@ export class ShepherdService {
     getTimeWaitMillis() {
         return this.tourProviderService.getTime();
     }
+
+    changeTutorialStatus(tutorialActive: boolean): void {
+        this.tutorialActive = tutorialActive;
+        this.tutorialActive$.next(tutorialActive);
+    }
 }

Reply via email to