import { ArmSiteDescriptor } from './../shared/resourceDescriptors' ; import { EmbeddedFunctionsNode } from './../tree-view/embedded-functions-node' ; import { ScenarioService } from './../shared/services/scenario/scenario.service' ; import { LogService } from './../shared/services/log.service' ; import { Router , ActivatedRoute } from '@angular/router' ; import { BroadcastEvent } from 'app/shared/models/broadcast-event' ; import { StoredSubscriptions } from './../shared/models/localStorage/local-storage' ; import { Dom } from './../shared/Utilities/dom' ; import { SubUtil } from './../shared/Utilities/sub-util' ; import { SearchBoxComponent } from './../search-box/search-box.component' ; import { Component , ViewChild , AfterViewInit , Injector , OnDestroy } from '@angular/core' ; import { Http } from '@angular/http' ; import { Observable } from 'rxjs/Observable' ; import { Subject } from 'rxjs/Subject' ; import { ReplaySubject } from 'rxjs/ReplaySubject' ; import { TranslateService } from '@ngx-translate/core' ; import { ConfigService } from './../shared/services/config.service' ; import { PortalResources } from './../shared/models/portal-resources' ; import { AuthzService } from './../shared/services/authz.service' ; import { LanguageService } from './../shared/services/language.service' ; import { LocalStorageKeys , ARM , LogCategories , ScenarioIds } from './../shared/models/constants' ; import { PortalService } from './../shared/services/portal.service' ; import { LocalStorageService } from './../shared/services/local-storage.service' ; import { TreeNode } from '../tree-view/tree-node' ; import { AppsNode } from '../tree-view/apps-node' ; import { ArmService } from '../shared/services/arm.service' ; import { CacheService } from '../shared/services/cache.service' ; import { UserService } from '../shared/services/user.service' ; import { TryFunctionsService } from '../shared/services/try-functions.service' ; import { GlobalStateService } from '../shared/services/global-state.service' ; import { BroadcastService } from '../shared/services/broadcast.service' ; import { AiService } from '../shared/services/ai.service' ; import { DropDownElement } from '../shared/models/drop-down-element' ; import { TreeViewInfo } from '../tree-view/models/tree-view-info' ; import { DashboardType } from '../tree-view/models/dashboard-type' ; import { Subscription } from '../shared/models/subscription' ; import { Url } from 'app/shared/Utilities/url' ; import { StartupInfo } from 'app/shared/models/portal' ; @ Component ( { selector : 'side-nav' , templateUrl : './side-nav.component.html' , styleUrls : [ './side-nav.component.scss' ] , } ) export class SideNavComponent implements AfterViewInit , OnDestroy { @ ViewChild ( 'treeViewContainer' ) treeViewContainer ; @ ViewChild ( SearchBoxComponent ) searchBox : SearchBoxComponent ; public rootNode : TreeNode ; public subscriptionOptions : DropDownElement < Subscription > [ ] = [ ] ; public selectedSubscriptions : Subscription [ ] = [ ] ; public subscriptionsDisplayText = '' ; public allSubscriptions = this . translateService . instant ( PortalResources . sideNav_AllSubscriptions ) ; public numberSubscriptions = this . translateService . instant ( PortalResources . sideNav_SubscriptionCount ) ; public resourceId : string ; public initialResourceId : string ; public searchTerm = '' ; public hasValue = false ; public headerOnTopOfSideNav = false ; public noPaddingOnSideNav = false ; public selectedNode : TreeNode ; public selectedDashboardType : DashboardType ; private _subscriptionsStream = new ReplaySubject < Subscription [ ] > ( 1 ) ; private _searchTermStream = new ReplaySubject < string > ( 1 ) ; private _ngUnsubscribe = new Subject ( ) ; private _initialized = false ; constructor ( public injector , public configService , public armService , public cacheService , public tryFunctionsSevice , public http , public globalStateService , public broadcastService , public translateService , public userService , public aiService , public localStorageService , public portalService , public languageService , public authZService , public logService , public router , public route , private _scenarioService ) { this . headerOnTopOfSideNav = this . _scenarioService . checkScenario ( ScenarioIds . headerOnTopOfSideNav ) . status === 'enabled' ; this . noPaddingOnSideNav = this . _scenarioService . checkScenario ( ScenarioIds . noPaddingOnSideNav ) . status === 'enabled' ; userService . getStartupInfo ( ) . subscribe ( info => { const sitenameIncoming = ! ! info . resourceId ? new ArmSiteDescriptor ( info . resourceId ) . site . toLocaleLowerCase ( ) : null ; const initialSiteName = ! ! this . initialResourceId ? new ArmSiteDescriptor ( this . initialResourceId ) . site . toLocaleLowerCase ( ) : null ; if ( sitenameIncoming !== initialSiteName ) { this . portalService . sendTimerEvent ( { timerId : 'TreeViewLoad' , timerAction : 'start' , } ) ; } if ( ! this . _initialized && ! this . globalStateService . showTryView ) { this . _initializeTree ( info ) ; } if ( this . _scenarioService . checkScenario ( ScenarioIds . addTopLevelAppsNode ) . status !== 'disabled' ) { this . _updateSubscriptions ( info ) ; this . initialResourceId = info . resourceId ; if ( this . initialResourceId ) { const descriptor = new ArmSiteDescriptor ( this . initialResourceId ) ; if ( descriptor . site ) { this . _searchTermStream . next ( `"${ descriptor . site }"` ) ; this . hasValue = true ; } else { this . _searchTermStream . next ( '' ) ; } } else if ( ! this . searchTerm ) { this . _searchTermStream . next ( '' ) ; } } } ) ; } ngAfterViewInit ( ) { if ( this . searchBox ) { this . searchBox . focus ( ) ; } } private _initializeTree ( info ) { this . _initialized = true ; this . rootNode = new TreeNode ( this , null , null ) ; if ( this . _scenarioService . checkScenario ( ScenarioIds . addTopLevelAppsNode ) . status !== 'disabled' ) { const appsNode = new AppsNode ( this , this . rootNode , this . _subscriptionsStream , this . _searchTermStream , info . resourceId ) ; this . rootNode . children = [ appsNode ] ; this . rootNode . isExpanded = true ; appsNode . parent = this . rootNode ; setTimeout ( ( ) => { appsNode . select ( ) ; } , 10 ) ; this . _searchTermStream . subscribe ( term => { this . searchTerm = term ; } ) ; } else { const resourceIdMatch = /\/resources([a-z0-9\-\/]+)/gi . exec ( this . router . url ) ; if ( resourceIdMatch && resourceIdMatch . length > 1 ) { const smallerMatch = resourceIdMatch [ 1 ] . split ( '/' ) . filter ( part => ! ! part ) . slice ( 0 , 4 ) . join ( '/' ) ; const smallerId = `/providers/Microsoft.BlueRidge/${ smallerMatch }` ; const functionsNode = new EmbeddedFunctionsNode ( this , this . rootNode , smallerId ) ; this . rootNode . children = [ functionsNode ] ; this . rootNode . isExpanded = true ; functionsNode . toggle ( null ) ; functionsNode . select ( ) ; } else { } } } private _getViewContainer ( ) { const treeViewContainer = this . treeViewContainer && < HTMLDivElement > this . treeViewContainer . nativeElement ; if ( ! treeViewContainer ) { return null ; } return < HTMLDivElement > treeViewContainer . querySelector ( '.top-level-children' ) ; } ngOnDestroy ( ) { this . _ngUnsubscribe . next ( ) ; } public scrollIntoView ( ) { setTimeout ( ( ) => { const containerElement = this . _getViewContainer ( ) ; if ( ! containerElement ) { return ; } const node = < HTMLDivElement > containerElement . querySelector ( ':focus' ) ; if ( ! node ) { return ; } Dom . scrollIntoView ( node , containerElement ) ; } , 0 ) ; } updateView ( newSelectedNode , newDashboardType , resourceId , force ? , keepChildBladesOpen ? ) { if ( this . selectedNode ) { if ( ! force && this . selectedNode === newSelectedNode && this . selectedDashboardType === newDashboardType ) { return Observable . of ( false ) ; } else { if ( this . selectedNode . shouldBlockNavChange ( ) ) { return Observable . of ( false ) ; } this . selectedNode . handleDeselection ( newSelectedNode ) ; this . selectedNode . showMenu = false ; } } this . _logDashboardTypeChange ( this . selectedDashboardType , newDashboardType , this . selectedNode && this . selectedNode . resourceId , newSelectedNode && newSelectedNode . resourceId ) ; this . selectedNode = newSelectedNode ; this . selectedDashboardType = newDashboardType ; this . resourceId = newSelectedNode . resourceId ; this . selectedNode . showMenu = true ; const viewInfo = < TreeViewInfo < any > > { resourceId : resourceId , dashboardType : newDashboardType , node : newSelectedNode , data : { } , } ; this . globalStateService . setDisabledMessage ( null ) ; const navId = resourceId . slice ( 1 , resourceId . length ) . toLowerCase ( ) . replace ( '/providers/microsoft.web' , '' ) ; this . logService . debug ( LogCategories . SideNav , `Navigating to ${ navId }` ) ; this . router . navigate ( [ navId ] , { relativeTo : this . route , queryParams : Url . getQueryStringObj ( ) } ) ; this . broadcastService . broadcastEvent ( BroadcastEvent . TreeNavigation , viewInfo ) ; this . _updateTitle ( newSelectedNode ) ; if ( ! keepChildBladesOpen ) { this . portalService . closeBlades ( ) ; } return newSelectedNode . handleSelection ( ) ; } navidateToNewSub ( ) { const navId = 'subs/new/subscription' ; this . router . navigate ( [ navId ] , { relativeTo : this . route , queryParams : Url . getQueryStringObj ( ) } ) ; } refreshSubs ( ) { this . cacheService . getArm ( '/subscriptions' , true ) . subscribe ( r => { this . userService . getStartupInfo ( ) . first ( ) . subscribe ( info => { const subs = r . json ( ) . value ; if ( ! SubUtil . subsChanged ( info . subscriptions , subs ) ) { return ; } info . subscriptions = subs ; this . userService . updateStartupInfo ( info ) ; } ) ; } ) ; } private _logDashboardTypeChange ( oldDashboard , newDashboard , oldResourceId ? , newResourceId ? ) { const oldDashboardType = DashboardType [ oldDashboard ] ; const newDashboardType = DashboardType [ newDashboard ] ; this . aiService . trackEvent ( '/sidenav/change-dashboard' , { newResourceId , oldResourceId , source : oldDashboardType , dest : newDashboardType , } ) ; } private _updateTitle ( node ) { const pathNames = node . getTreePathNames ( ) ; let title = '' ; let subtitle = '' ; for ( let i = 0 ; i < pathNames . length ; i ++ ) { if ( i % 2 === 1 ) { title += pathNames [ i ] + ' - ' ; } } if ( title . length > 3 ) { title = title . substring ( 0 , title . length - 3 ) ; } if ( ! title ) { title = this . translateService . instant ( PortalResources . functionApps ) ; subtitle = '' ; } else { subtitle = this . translateService . instant ( PortalResources . functionApps ) ; } this . portalService . updateBladeInfo ( title , subtitle ) ; } clearView ( resourceId ) { if ( this . resourceId . startsWith ( resourceId ) ) { this . router . navigate ( [ 'blank' ] , { relativeTo : this . route , queryParams : Url . getQueryStringObj ( ) } ) ; } } search ( event ) { if ( typeof event === 'string' ) { this . _searchTermStream . next ( event ) ; this . hasValue = ! ! event ; } else { this . hasValue = ! ! event . target . value ; const startPos = event . target . selectionStart ; const endPos = event . target . selectionEnd ; this . _searchTermStream . next ( event . target . value ) ; if ( event . target . value . length !== startPos ) { setTimeout ( ( ) => { event . target . selectionStart = startPos ; event . target . selectionEnd = endPos ; } ) ; } } } searchExact ( term ) { this . hasValue = ! ! term ; this . _searchTermStream . next ( `"${ term }"` ) ; } clearSearch ( ) { this . hasValue = false ; this . _searchTermStream . next ( '' ) ; } onSubscriptionsSelect ( subscriptions ) { let subIds ; if ( subscriptions . length === this . subscriptionOptions . length ) { subIds = [ ] ; } else { subIds = subscriptions . map < string > ( s => s . subscriptionId ) ; } const storedSelectedSubIds = { id : LocalStorageKeys . savedSubsKey , subscriptions : subIds , } ; this . localStorageService . setItem ( storedSelectedSubIds . id , storedSelectedSubIds ) ; this . selectedSubscriptions = subscriptions ; this . _subscriptionsStream . next ( subscriptions ) ; if ( subscriptions . length === this . subscriptionOptions . length ) { this . _updateSubDisplayText ( this . allSubscriptions ) ; } else if ( subscriptions . length > 1 ) { this . _updateSubDisplayText ( this . translateService . instant ( PortalResources . sideNav_SubscriptionCount ) . format ( subscriptions . length ) ) ; } else { this . _updateSubDisplayText ( `${ subscriptions [ 0 ] . displayName }` ) ; } } private _updateSubDisplayText ( displayText ) { setTimeout ( ( ) => { this . subscriptionsDisplayText = '' ; this . subscriptionsDisplayText = displayText ; } ) ; } private _updateSubscriptions ( info ) { const savedSubs = < StoredSubscriptions > this . localStorageService . getItem ( LocalStorageKeys . savedSubsKey ) ; const savedSelectedSubscriptionIds = savedSubs ? savedSubs . subscriptions : [ ] ; let descriptor ; if ( info . resourceId ) { descriptor = new ArmSiteDescriptor ( info . resourceId ) ; } let count = 0 ; this . subscriptionOptions = info . subscriptions . map ( e => { let subSelected ; if ( descriptor ) { subSelected = descriptor . subscription === e . subscriptionId ; } else { subSelected = savedSelectedSubscriptionIds . length === 0 || savedSelectedSubscriptionIds . findIndex ( s => s === e . subscriptionId ) > - 1 ; } if ( subSelected ) { count ++ ; } return { displayLabel : e . displayName , value : e , isSelected : subSelected && count <= ARM . MaxSubscriptionBatchSize , } ; } ) . sort ( ( a , b ) => a . displayLabel . localeCompare ( b . displayLabel ) ) ; } }