import '@polymer/polymer/polymer-legacy' ; import '@polymer/app-layout/app-drawer/app-drawer' ; import '@polymer/app-layout/app-drawer-layout/app-drawer-layout' ; import '@polymer/app-layout/app-toolbar/app-toolbar' ; import '@polymer/iron-icon/iron-icon' ; import '@polymer/iron-icons/iron-icons' ; import '@polymer/iron-pages/iron-pages' ; import '@polymer/paper-icon-button/paper-icon-button' ; import '@polymer/paper-toast/paper-toast' ; import '@polymer/paper-dialog/paper-dialog' ; import '@polymer/paper-dialog-scrollable/paper-dialog-scrollable' ; import '@polymer/paper-listbox/paper-listbox' ; import '@polymer/paper-menu-button/paper-menu-button' ; import './cloud-install-styles' ; import './outline-about-dialog' ; import './outline-do-oauth-step' ; import './outline-gcp-oauth-step' ; import './outline-gcp-create-server-app' ; import './outline-feedback-dialog' ; import './outline-survey-dialog' ; import './outline-intro-step' ; import './outline-per-key-data-limit-dialog' ; import './outline-language-picker' ; import './outline-manual-server-entry' ; import './outline-modal-dialog' ; import './outline-region-picker-step' ; import './outline-server-list' ; import './outline-tos-view' ; import { AppLocalizeBehavior } from '@polymer/app-localize-behavior/app-localize-behavior' ; import { mixinBehaviors } from '@polymer/polymer/lib/legacy/class' ; import { html } from '@polymer/polymer/lib/utils/html-tag' ; import { PolymerElement } from '@polymer/polymer/polymer-element' ; import { DisplayCloudId } from './cloud-assets' ; import type { LegacyElementMixin } from '@polymer/polymer/lib/legacy/legacy-element-mixin' ; import type { AppDrawerElement } from '@polymer/app-layout/app-drawer/app-drawer' ; import type { AppDrawerLayoutElement } from '@polymer/app-layout/app-drawer-layout/app-drawer-layout' ; import type { PaperDialogElement } from '@polymer/paper-dialog/paper-dialog' ; import type { PaperToastElement } from '@polymer/paper-toast/paper-toast' ; import type { PolymerElementProperties } from '@polymer/polymer/interfaces' ; import type { OutlineRegionPicker } from './outline-region-picker-step' ; import type { OutlineDoOauthStep } from './outline-do-oauth-step' ; import type { GcpConnectAccountApp } from './outline-gcp-oauth-step' ; import type { GcpCreateServerApp } from './outline-gcp-create-server-app' ; import type { OutlineServerList , ServerViewListEntry } from './outline-server-list' ; import type { OutlineManualServerEntry } from './outline-manual-server-entry' ; import type { OutlinePerKeyDataLimitDialog } from './outline-per-key-data-limit-dialog' ; import type { OutlineFeedbackDialog } from './outline-feedback-dialog' ; import type { OutlineAboutDialog } from './outline-about-dialog' ; import type { OutlineShareDialog } from './outline-share-dialog' ; import type { OutlineMetricsOptionDialog } from './outline-metrics-option-dialog' ; import type { OutlineModalDialog } from './outline-modal-dialog' ; import type { ServerView } from './outline-server-view' ; import type { LanguageDef } from './outline-language-picker' ; const TOS_ACK_LOCAL_STORAGE_KEY = 'tos-ack' ; /** A cloud account to be displayed */ type AccountListEntry = { id : string ; name : string ; } ; /** An access key to be displayed */ export type ServerListEntry = { id : string ; accountId : string ; name : string ; isSynced : boolean ; } ; const polymerElementWithLocalize = mixinBehaviors ( AppLocalizeBehavior , PolymerElement ) as new ( ) => PolymerElement & LegacyElementMixin & AppLocalizeBehavior ; export class AppRoot extends polymerElementWithLocalize { static get template ( ) { return html `
    <style include="cloud-install-styles"></style>
    <style>
      :host {
        --side-bar-width: 48px;
      }
      .app-container {
        margin: 0 auto;
      }
      /* Large display desktops */
      @media (min-width: 1281px) {
        .app-container {
          max-width: 1200px;
        }
      }
      /* Laptop, desktops */
      @media (min-width: 1025px) and (max-width: 1280px) {
        .app-container {
          max-width: 920px;
        }
      }
      #toast {
        align-items: center;
        display: flex;
        justify-content: space-between;
        padding: 24px;
        max-width: 450px;
      }
      #toast paper-icon-button {
        /* prevents the icon from resizing when there is a long message in the toast */
        flex-shrink: 0;
        padding: 0;
        height: 20px;
        width: 20px;
      }
      /* rtl:begin:ignore */
      #appDrawer {
        --app-drawer-content-container: {
          color: var(--medium-gray);
          background-color: var(--background-contrast-color);
          display: flex;
          flex-direction: column;
          justify-content: space-between;
          align-items: right;
        }
      }
      /* rtl:end:ignore */
      #appDrawer > * {
        width: 100%;
      }
      .servers {
        overflow-y: scroll;
        flex: 1;
      }
      .servers::-webkit-scrollbar {
        /* Do not display the scroll bar in the drawer or side bar. It is not styled on some platforms. */
        display: none;
      }
      .servers-section {
        padding: 12px 0;
        border-bottom: 1px solid var(--border-color);
      }
      .servers-section:last-child {
        border-bottom: none;
      }
      .servers-header {
        display: flex;
        justify-content: space-between;
        align-items: center;
        padding-left: 24px;
        line-height: 39px;
      }
      .servers-header > span {
        flex: 1;
      }
      .do-overflow-menu {
        padding: 24px;
        color: var(--dark-gray);
        text-align: left;
        display: flex;
        flex-direction: column;
      }
      .do-overflow-menu h4 {
        margin-top: 0;
        white-space: nowrap;
      }
      .do-overflow-menu .account-info {
        display: flex;
        align-items: center;
        color: var(--faded-gray);
      }
      .do-overflow-menu .account-info img {
        margin-right: 12px;
        width: 24px;
      }
      .do-overflow-menu .sign-out-button {
        margin-top: 24px;
        align-self: flex-end;
        font-weight: bold;
        cursor: pointer;
        text-transform: uppercase;
      }
      .servers-container {
        padding-right: 12px; /* In case the server name is wraps. */
      }
      .server {
        display: flex;
        align-items: center;
        width: 100%; /* For the side bar icons. */
        margin: 18px 0;
        padding: 6px 0;
        cursor: pointer;
      }
      .server.selected {
        color: white;
        border-left: 2px solid var(--primary-green);
      }
      @keyframes rotate {
        0% {
          transform: rotate(0deg);
        }
        100% {
          transform: rotate(360deg);
        }
      }
      .server.syncing {
        cursor: wait;
      }
      .syncing .server-icon {
        animation: rotate 1.75s ease-out infinite;
        opacity: 0.5;
      }
      .server-icon {
        width: 22px;
        height: 22px;
        /* Prevent the image from shrinking when the server title spans multiple lines */
        min-width: 22px !important;
        margin: 0 24px;
      }
      .selected > .server-icon {
        /* Account for the selected border width to preserve center alignment. */
        margin-left: 22px;
      }
      .add-server-section {
        padding: 24px 0;
        text-transform: uppercase;
        color: var(--primary-green);
        font-size: 12px;
        letter-spacing: 0.6px;
        border-top: 1px solid var(--border-color);
        border-bottom: 1px solid var(--border-color);
        cursor: pointer;
      }
      .add-server-section paper-icon-item {
        margin-left: 24px;
      }
      .add-server-section paper-icon-item iron-icon {
        margin-right: 24px;
      }
      #appDrawer > paper-listbox {
        color: var(--medium-gray);
        background-color: var(--background-contrast-color);
      }
      #appDrawer > paper-listbox > * {
        display: block;
        cursor: pointer;
        padding-left: 24px;
        font-size: 14px;
        line-height: 40px;
        outline: none;
      }
      #appDrawer a {
        color: inherit;
      }
      #appDrawer a:focus {
        outline: none;
      }
      #links-footer {
        margin-top: 36px;
      }
      .legal-links {
        margin: 0 -6px;
      }
      .legal-links > * {
        margin: 0 6px;
      }
      #language-row {
        display: flex;
        align-items: center;
      }
      #language-icon {
        padding-top: 10px;
      }
      #language-dropdown {
        padding-left: 22px;
        --paper-input-container: {
          width: 156px;
        };
      }
      app-toolbar [main-title] img {
        height: 16px;
        margin-top: 8px;
      }
      .side-bar-margin {
        margin-left: var(--side-bar-width);
      }
      /* rtl:begin:ignore */
      #sideBar {
        --app-drawer-width: var(--side-bar-width);
        --app-drawer-content-container: {
          background-color: var(--background-contrast-color);
        }
      }
      /* rtl:end:ignore */
      .side-bar-container {
        height: 100%;
        text-align: center;
        color: var(--light-gray);
        display: flex;
        flex-direction: column;
      }
      .side-bar-container .servers {
        flex: initial; /* Prevent the server list pushing down the add server button. */
      }
      .side-bar-section {
        display: flex;
        flex-direction: column;
        align-items: center;
        padding: 12px 0;
        border-bottom: 1px solid var(--border-color);
      }
      .side-bar-section.menu {
        min-height: 32px;
      }
      .side-bar-section.servers-section {
        padding: 24px 0;
      }
      .side-bar-section .server {
        justify-content: center;
        margin: 12px auto;
        padding: 2px 0;
      }
      .side-bar-section .provider-icon {
        margin-bottom: 12px;
        padding: 12px 0;
        opacity: 0.54;
        filter: grayscale(100%);
      }
      .side-bar-section.add-server-section {
        flex: 1 0 24px;
        border-bottom: none;
      }
      .side-bar-section > .server-icon {
        margin: 0;
      }
      #getConnectedDialog {
        height: 562px;
        background: white;
      }
      #getConnectedDialog iframe {
        padding: 0;
        margin: 0;
        width: 100%;
        border: none;
        border-bottom: 1px solid #ccc;
        height: 500px;
      }
      #getConnectedDialog .buttons {
        margin-top: -5px; /* undo spacing added after iframe */
      }
    </style>

    <outline-tos-view id="tosView" has-accepted-terms-of-service="{{userAcceptedTos}}" hidden\$="{{hasAcceptedTos}}" localize="[[localize]]"></outline-tos-view>

    <div hidden\$="{{!hasAcceptedTos}}">
      <!-- This responsive width sets the minimum layout area to 648px.  -->
      <app-drawer-layout id="drawerLayout" responsive-width="886px" on-narrow-changed="_computeShouldShowSideBar" class\$="[[sideBarMarginClass]]">
        <app-drawer id="appDrawer" slot="drawer" on-opened-changed="_computeShouldShowSideBar">
          <app-toolbar class="toolbar" hidden\$="[[shouldShowSideBar]]">
            <paper-icon-button icon="menu" on-click="_toggleAppDrawer"></paper-icon-button>
            <div main-title=""><img src="images/outline-manager-logo.svg"></div>
          </app-toolbar>

          <!-- Servers section -->
          <div class="servers">
            ${ this . expandedServersTemplate ( ) }
          </div>

          <!-- Add server -->
          <div class="add-server-section" on-tap="showIntro">
            <paper-icon-item>
              <iron-icon icon="add" slot="item-icon"></iron-icon>[[localize('servers-add')]]
            </paper-icon-item>
          </div>

          <!-- Links section -->
          <paper-listbox>
            <span on-tap="maybeCloseDrawer"><a href="https://s3.amazonaws.com/outline-vpn/index.html#/en/support/dataCollection">[[localize('nav-data-collection')]]</a></span>
            <span on-tap="submitFeedbackTapped">[[localize('nav-feedback')]]</span>
            <span on-tap="maybeCloseDrawer"><a href="https://s3.amazonaws.com/outline-vpn/index.html#/en/support/">[[localize('nav-help')]]</a></span>
            <span on-tap="aboutTapped">[[localize('nav-about')]]</span>
            <div id="links-footer">
              <paper-icon-item id="language-row">
                <iron-icon id="language-icon" icon="language" slot="item-icon"></iron-icon>
                <outline-language-picker id="language-dropdown" selected-language="{{language}}" languages="{{supportedLanguages}}"></outline-language-picker>
              </paper-icon-item>
              <div class="legal-links" on-tap="maybeCloseDrawer">
                <a href="https://www.google.com/policies/privacy/">[[localize('nav-privacy')]]</a>
                <a href="https://s3.amazonaws.com/outline-vpn/static_downloads/Outline-Terms-of-Service.html">[[localize('nav-terms')]]</a>
                <span on-tap="showLicensesTapped">[[localize('nav-licenses')]]</span>
              </div>
            </div>
          </paper-listbox>
        </app-drawer>

        <app-header-layout>
          <div class="app-container">
            <iron-pages attr-for-selected="id" selected="{{ currentPage }}">
              <outline-intro-step id="intro" digital-ocean-account-name="{{digitalOceanAccount.name}}" gcp-account-name="{{gcpAccount.name}}" localize="[[localize]]"></outline-intro-step>
              <outline-do-oauth-step id="digitalOceanOauth" localize="[[localize]]"></outline-do-oauth-step>
              <outline-gcp-oauth-step id="gcpOauth" localize="[[localize]]"></outline-gcp-oauth-step>
              <outline-gcp-create-server-app id="gcpCreateServer" localize="[[localize]]" language="[[language]]"></outline-gcp-create-server-app>
              <outline-manual-server-entry id="manualEntry" localize="[[localize]]"></outline-manual-server-entry>
              <!-- TODO: Move to a new outline-do-oauth-step. -->
              <outline-region-picker-step id="regionPicker" localize="[[localize]]" language="[[language]]"></outline-region-picker-step>
              <outline-server-list id="serverView" server-list="[[_serverViewList(serverList)]]" selected-server-id="[[selectedServerId]]" language="[[language]]" localize="[[localize]]"></outline-server-list>
              </div>
            </iron-pages>
          </div>
        </app-header-layout>
      </app-drawer-layout>

      <!-- Side bar -->
      <app-drawer id="sideBar" opened\$="[[shouldShowSideBar]]" persistent="">
        <div class="side-bar-container">
          <div class="side-bar-section menu">
            <paper-icon-button icon="menu" on-click="_toggleAppDrawer"></paper-icon-button>
          </div>
          <div class="servers">
            ${ this . minimizedServersTemplate ( ) }
          </div>
          <div class="side-bar-section add-server-section" on-tap="showIntro">
            <paper-icon-item>
              <iron-icon icon="add" slot="item-icon"></iron-icon>
            </paper-icon-item>
          </div>
        </div>
      </app-drawer>

      <paper-toast id="toast"><paper-icon-button icon="icons:close" on-tap="closeError"></paper-icon-button></paper-toast>

      <!-- Modal dialogs must be outside the app container; otherwise the backdrop covers them.  -->
      <outline-survey-dialog id="surveyDialog" localize="[[localize]]"></outline-survey-dialog>
      <outline-feedback-dialog id="feedbackDialog" localize="[[localize]]"></outline-feedback-dialog>
      <outline-about-dialog id="aboutDialog" outline-version="[[outlineVersion]]" localize="[[localize]]"></outline-about-dialog>
      <outline-modal-dialog id="modalDialog"></outline-modal-dialog>
      <outline-share-dialog id="shareDialog" localize="[[localize]]"></outline-share-dialog>
      <outline-metrics-option-dialog id="metricsDialog" localize="[[localize]]"></outline-metrics-option-dialog>
      <outline-per-key-data-limit-dialog id="perKeyDataLimitDialog" language="[[language]]" localize="[[localize]]"></outline-per-key-data-limit-dialog>

      <paper-dialog id="getConnectedDialog" modal="">
        <!-- iframe gets inserted here once we are given the invite URL. -->
        <div class="buttons">
          <paper-button on-tap="closeGetConnectedDialog" autofocus="">[[localize('close')]]</paper-button>
        </div>
      </paper-dialog>

      <paper-dialog id="licenses" modal="" restorefocusonclose="">
        <paper-dialog-scrollable>
          <code id="licensesText">
            [[localize('error-licenses')]]
          </code>
        </paper-dialog-scrollable>
        <div class="buttons">
          <paper-button dialog-dismiss="" autofocus="">[[localize('close')]]</paper-button>
        </div>
      </paper-dialog>
    </div>
` ; } static expandedServersTemplate ( ) { return html `
      <!-- DigitalOcean servers -->
      <div class="servers-section" hidden\$="[[!digitalOceanAccount]]">
        <div class="servers-header">
          <span>[[localize('servers-digitalocean')]]</span>
          <paper-menu-button horizontal-align="left" class="" close-on-activate="" no-animations="" dynamic-align="" no-overlap="">
            <paper-icon-button icon="more-vert" slot="dropdown-trigger"></paper-icon-button>
            <div class="do-overflow-menu" slot="dropdown-content">
              <h4>[[localize('digitalocean-disconnect-account')]]</h4>
              <div class="account-info"><img src="images/digital_ocean_logo.svg">[[digitalOceanAccount.name]]</div>
              <div class="sign-out-button" on-tap="_digitalOceanSignOutTapped">[[localize('disconnect')]]</div>
            </div>
          </paper-menu-button>
        </div>
        <div class="servers-container">
          <template is="dom-repeat" items="[[serverList]]" as="server" filter="[[_accountServerFilter(digitalOceanAccount)]]" sort="_sortServersByName">
            <div class\$="server [[_computeServerClasses(selectedServerId, server)]]" data-server\$="[[server]]" on-tap="_showServer">
              <img class="server-icon" src\$="images/[[_computeServerImage(selectedServerId, server)]]">
              <span>[[server.name]]</span>
            </div>
          </template>
        </div>
      </div>
      <!-- GCP servers -->
      <div class="servers-section" hidden\$="[[!gcpAccount]]">
        <div class="servers-header">
          <span>[[localize('servers-gcp')]]</span>
          <paper-menu-button horizontal-align="left" class="" close-on-activate="" no-animations="" dynamic-align="" no-overlap="">
            <paper-icon-button icon="more-vert" slot="dropdown-trigger"></paper-icon-button>
            <div class="do-overflow-menu" slot="dropdown-content">
              <h4>[[localize('gcp-disconnect-account')]]</h4>
              <div class="account-info"><img src="images/gcp-logo.svg">[[gcpAccount.name]]</div>
              <div class="sign-out-button" on-tap="_gcpSignOutTapped">[[localize('disconnect')]]</div>
            </div>
          </paper-menu-button>
        </div>
        <div class="servers-container">
          <template is="dom-repeat" items="[[serverList]]" as="server" filter="[[_accountServerFilter(gcpAccount)]]" sort="_sortServersByName">
            <div class\$="server [[_computeServerClasses(selectedServerId, server)]]" data-server\$="[[server]]" on-tap="_showServer">
              <img class="server-icon" src\$="images/[[_computeServerImage(selectedServerId, server)]]">
              <span>[[server.name]]</span>
            </div>
          </template>
        </div>
      </div>
      <!-- Manual servers -->
      <div class="servers-section" hidden\$="[[!_hasManualServers(serverList)]]">
        <div class="servers-header">
          <span>[[localize('servers-manual')]]</span>
        </div>
        <div class="servers-container">
          <template is="dom-repeat" items="[[serverList]]" as="server" filter="_isServerManual" sort="_sortServersByName">
            <div class\$="server [[_computeServerClasses(selectedServerId, server)]]" data-server\$="[[server]]" on-tap="_showServer">
              <img class="server-icon" src\$="images/[[_computeServerImage(selectedServerId, server)]]">
              <span>[[server.name]]</span>
            </div>
          </template>
        </div>
      </div>
    ` ; } static minimizedServersTemplate ( ) { return html `
      <!-- DigitalOcean servers -->
      <div class="side-bar-section servers-section" hidden\$="[[!digitalOceanAccount]]">
        <img class="provider-icon" src="images/do_white_logo.svg">
        <template is="dom-repeat" items="[[serverList]]" as="server" filter="[[_accountServerFilter(digitalOceanAccount)]]" sort="_sortServersByName">
          <div class\$="server [[_computeServerClasses(selectedServerId, server)]]" data-server\$="[[server]]" on-tap="_showServer">
            <img class="server-icon" src\$="images/[[_computeServerImage(selectedServerId, server)]]">
          </div>
        </template>
      </div>
      <!-- GCP servers -->
      <div class="side-bar-section servers-section" hidden\$="[[!gcpAccount]]">
        <img class="provider-icon" src="images/gcp-logo.svg">
        <template is="dom-repeat" items="[[serverList]]" as="server" filter="[[_accountServerFilter(gcpAccount)]]" sort="_sortServersByName">
          <div class\$="server [[_computeServerClasses(selectedServerId, server)]]" data-server\$="[[server]]" on-tap="_showServer">
            <img class="server-icon" src\$="images/[[_computeServerImage(selectedServerId, server)]]">
          </div>
        </template>
      </div>
      <!-- Manual servers -->
      <div class="side-bar-section servers-section" hidden\$="[[!_hasManualServers(serverList)]]">
        <img class="provider-icon" src="images/cloud.svg">
        <template is="dom-repeat" items="[[serverList]]" as="server" filter="_isServerManual" sort="_sortServersByName">
          <div class\$="server [[_computeServerClasses(selectedServerId, server)]]" data-server\$="[[server]]" on-tap="_showServer">
            <img class="server-icon" src\$="images/[[_computeServerImage(selectedServerId, server)]]">
          </div>
        </template>
      </div>
    ` ; } static get is ( ) { return 'app-root' ; } static get properties ( ) : PolymerElementProperties { return { language : { type : String } , supportedLanguages : { type : Array } , useKeyIfMissing : { type : Boolean } , serverList : { type : Array } , selectedServerId : { type : String } , digitalOceanAccount : Object , gcpAccount : Object , outlineVersion : String , userAcceptedTos : { type : Boolean , observer : '_userAcceptedTosChanged' , } , hasAcceptedTos : { type : Boolean , computed : '_computeHasAcceptedTermsOfService(userAcceptedTos)' , } , currentPage : { type : String , observer : '_currentPageChanged' , } , shouldShowSideBar : { type : Boolean } , sideBarMarginClass : { type : String , computed : '_computeSideBarMarginClass(shouldShowSideBar)' , } , } ; } selectedServerId = '' ; language = '' ; supportedLanguages : LanguageDef [ ] = [ ] ; useKeyIfMissing = true ; serverList : ServerListEntry [ ] = [ ] ; digitalOceanAccount : AccountListEntry = null ; gcpAccount : AccountListEntry = null ; outlineVersion = '' ; currentPage = 'intro' ; shouldShowSideBar = false ; constructor ( ) { super ( ) ; this . addEventListener ( 'RegionSelected' , this . handleRegionSelected ) ; this . addEventListener ( 'SetUpGenericCloudProviderRequested' , this . handleSetUpGenericCloudProviderRequested ) ; this . addEventListener ( 'SetUpAwsRequested' , this . handleSetUpAwsRequested ) ; this . addEventListener ( 'SetUpGcpRequested' , this . handleSetUpGcpRequested ) ; this . addEventListener ( 'ManualServerEntryCancelled' , this . handleManualCancelled ) ; } param language loadLanguageResources ( language ) { const localizeResourcesResponder = new Promise < void > ( ( resolve , reject ) => { let successHandler , failureHandler ; successHandler = ( ) => { this . removeEventListener ( 'app-localize-resources-error' , failureHandler ) ; resolve ( ) ; } ; failureHandler = ( ) => { this . removeEventListener ( 'app-localize-resources-loaded' , successHandler ) ; reject ( new Error ( `Failed to load resources for language ${ language }` ) ) ; } ; this . addEventListener ( 'app-localize-resources-loaded' , successHandler , { once : true } ) ; this . addEventListener ( 'app-localize-resources-error' , failureHandler , { once : true } ) ; } ) ; const messagesUrl = `./messages/${ language }.json` ; this . loadResources ( messagesUrl , language , false ) ; return localizeResourcesResponder ; } param language async setLanguage ( language , direction ) { await this . loadLanguageResources ( language ) ; const alignDir = direction === 'ltr' ? 'left' : 'right' ; ( this . $ . appDrawer as AppDrawerElement ) . align = alignDir ; ( this . $ . sideBar as AppDrawerElement ) . align = alignDir ; this . language = language ; } showIntro ( ) { this . maybeCloseDrawer ( ) ; this . selectedServerId = '' ; this . currentPage = 'intro' ; } getDigitalOceanOauthFlow ( onCancel ) { const oauthFlow = this . $ . digitalOceanOauth as OutlineDoOauthStep ; oauthFlow . onCancel = onCancel ; return oauthFlow ; } showDigitalOceanOauthFlow ( ) { this . currentPage = 'digitalOceanOauth' ; } getAndShowDigitalOceanOauthFlow ( onCancel ) { this . currentPage = 'digitalOceanOauth' ; const oauthFlow = this . getDigitalOceanOauthFlow ( onCancel ) ; oauthFlow . showConnectAccount ( ) ; return oauthFlow ; } getAndShowGcpOauthFlow ( onCancel ) { this . currentPage = 'gcpOauth' ; const oauthFlow = this . $ . gcpOauth as GcpConnectAccountApp ; oauthFlow . onCancel = onCancel ; return oauthFlow ; } getAndShowGcpCreateServerApp ( ) { this . currentPage = 'gcpCreateServer' ; return this . $ . gcpCreateServer as GcpCreateServerApp ; } getAndShowRegionPicker ( ) { this . currentPage = 'regionPicker' ; const regionPicker = this . $ . regionPicker as OutlineRegionPicker ; regionPicker . reset ( ) ; return regionPicker ; } getManualServerEntry ( ) { return this . $ . manualEntry as OutlineManualServerEntry ; } showServerView ( ) { this . currentPage = 'serverView' ; } _currentPageChanged ( ) { if ( this . currentPage !== 'gcpCreateServer' ) { ( this . $ . gcpCreateServer as GcpCreateServerApp ) . stopRefreshingBillingAccounts ( ) ; } } /** Gets the ServerView for the server given by its id */ getServerView ( displayServerId ) { const serverList = this . shadowRoot . querySelector < OutlineServerList > ( '#serverView' ) ; return serverList . getServerView ( displayServerId ) ; } handleRegionSelected ( e ) { this . fire ( 'SetUpDigitalOceanServerRequested' , { region : e . detail . selectedLocation } ) ; } handleSetUpGenericCloudProviderRequested ( ) { this . handleManualServerSelected ( 'generic' ) ; } handleSetUpAwsRequested ( ) { this . handleManualServerSelected ( 'aws' ) ; } handleSetUpGcpRequested ( ) { this . handleManualServerSelected ( 'gcp' ) ; } handleManualServerSelected ( cloudProvider ) { const manualEntry = this . $ . manualEntry as OutlineManualServerEntry ; manualEntry . clear ( ) ; manualEntry . cloudProvider = cloudProvider ; this . currentPage = 'manualEntry' ; } handleManualCancelled ( ) { this . currentPage = 'intro' ; } showError ( errorMsg ) { this . showToast ( errorMsg , Infinity ) ; } showNotification ( message , durationMs = 3000 ) { this . showToast ( message , durationMs ) ; } param duration showToast ( message , duration ) { const toast = this . $ . toast as PaperToastElement ; toast . close ( ) ; setTimeout ( ( ) => { toast . show ( { text : message , duration , noOverlap : true , } ) ; } , 0 ) ; } closeError ( ) { ( this . $ . toast as PaperToastElement ) . close ( ) ; } param cb showConnectivityDialog ( cb ) { const dialogTitle = this . localize ( 'error-connectivity-title' ) ; const dialogText = this . localize ( 'error-connectivity' ) ; this . showModalDialog ( dialogTitle , dialogText , [ this . localize ( 'digitalocean-disconnect' ) , this . localize ( 'retry' ) ] ) . then ( clickedButtonIndex => { cb ( clickedButtonIndex === 1 ) ; } ) ; } getConfirmation ( title , text , confirmButtonText , continueFunc ) { this . showModalDialog ( title , text , [ this . localize ( 'cancel' ) , confirmButtonText ] ) . then ( clickedButtonIndex => { if ( clickedButtonIndex === 1 ) { continueFunc ( ) ; } } ) ; } showManualServerError ( errorTitle , errorText ) { this . showModalDialog ( errorTitle , errorText , [ this . localize ( 'cancel' ) , this . localize ( 'retry' ) ] ) . then ( clickedButtonIndex => { const manualEntry = this . $ . manualEntry as OutlineManualServerEntry ; if ( clickedButtonIndex === 1 ) { manualEntry . retryTapped ( ) ; } else { manualEntry . cancelTapped ( ) ; } } ) ; } _hasManualServers ( serverList ) { return serverList . filter ( server => ! server . accountId ) . length > 0 ; } _userAcceptedTosChanged ( userAcceptedTos ) { if ( userAcceptedTos ) { window . localStorage [ TOS_ACK_LOCAL_STORAGE_KEY ] = Date . now ( ) ; } } _computeHasAcceptedTermsOfService ( userAcceptedTos ) { return userAcceptedTos || ! ! window . localStorage [ TOS_ACK_LOCAL_STORAGE_KEY ] ; } _toggleAppDrawer ( ) { const drawerLayout = this . $ . drawerLayout as AppDrawerLayoutElement ; const drawerNarrow = drawerLayout . narrow ; const forceNarrow = drawerLayout . forceNarrow ; if ( drawerNarrow ) { if ( forceNarrow ) { drawerLayout . forceNarrow = false ; } ( this . $ . appDrawer as AppDrawerElement ) . toggle ( ) ; } else { drawerLayout . forceNarrow = ! forceNarrow ; } } maybeCloseDrawer ( ) { const drawerLayout = this . $ . drawerLayout as AppDrawerLayoutElement ; if ( drawerLayout . narrow || drawerLayout . forceNarrow ) { ( this . $ . appDrawer as AppDrawerElement ) . close ( ) ; } } submitFeedbackTapped ( ) { ( this . $ . feedbackDialog as OutlineFeedbackDialog ) . open ( ) ; this . maybeCloseDrawer ( ) ; } aboutTapped ( ) { ( this . $ . aboutDialog as OutlineAboutDialog ) . open ( ) ; this . maybeCloseDrawer ( ) ; } _digitalOceanSignOutTapped ( ) { this . fire ( 'DigitalOceanSignOutRequested' ) ; } _gcpSignOutTapped ( ) { this . fire ( 'GcpSignOutRequested' ) ; } openManualInstallFeedback ( prepopulatedMessage ) { ( this . $ . feedbackDialog as OutlineFeedbackDialog ) . open ( prepopulatedMessage , true ) ; } openShareDialog ( accessKey , s3Url ) { ( this . $ . shareDialog as OutlineShareDialog ) . open ( accessKey , s3Url ) ; } openPerKeyDataLimitDialog ( keyName , activeDataLimitBytes , onDataLimitSet , onDataLimitRemoved ) { ( this . $ . perKeyDataLimitDialog as OutlinePerKeyDataLimitDialog ) . open ( keyName , activeDataLimitBytes , onDataLimitSet , onDataLimitRemoved ) ; } openGetConnectedDialog ( inviteUrl ) { const dialog = this . $ . getConnectedDialog as PaperDialogElement ; if ( dialog . children . length > 1 ) { return ; } const iframe = document . createElement ( 'iframe' ) ; iframe . onload = ( ) => dialog . open ( ) ; iframe . src = inviteUrl ; dialog . insertBefore ( iframe , dialog . children [ 0 ] ) ; } closeGetConnectedDialog ( ) { const dialog = this . $ . getConnectedDialog as PaperDialogElement ; dialog . close ( ) ; const oldIframe = dialog . children [ 0 ] ; dialog . removeChild ( oldIframe ) ; } showMetricsDialogForNewServer ( ) { ( this . $ . metricsDialog as OutlineMetricsOptionDialog ) . showMetricsOptInDialog ( ) ; } return showModalDialog ( title , text , buttons ) { return ( this . $ . modalDialog as OutlineModalDialog ) . open ( title , text , buttons ) ; } closeModalDialog ( ) { return ( this . $ . modalDialog as OutlineModalDialog ) . close ( ) ; } showLicensesTapped ( ) { const xhr = new XMLHttpRequest ( ) ; xhr . onload = ( ) => { ( this . $ . licensesText as HTMLElement ) . innerText = xhr . responseText ; ( this . $ . licenses as PaperDialogElement ) . open ( ) ; } ; xhr . onerror = ( ) => { console . error ( 'could not load license.txt' ) ; } ; xhr . open ( 'GET' , '/ui_components/licenses/licenses.txt' , true ) ; xhr . send ( ) ; } _computeShouldShowSideBar ( ) { const drawerNarrow = ( this . $ . drawerLayout as AppDrawerLayoutElement ) . narrow ; const drawerOpened = ( this . $ . appDrawer as AppDrawerElement ) . opened ; if ( drawerOpened && drawerNarrow ) { this . shouldShowSideBar = false ; } else { this . shouldShowSideBar = drawerNarrow ; } } _computeSideBarMarginClass ( shouldShowSideBar ) { return shouldShowSideBar ? 'side-bar-margin' : '' ; } _accountServerFilter ( account ) { return ( server ) => account && server . accountId === account . id ; } _isServerManual ( server ) { return ! server . accountId ; } _sortServersByName ( a , b ) { const aName = a . name . toUpperCase ( ) ; const bName = b . name . toUpperCase ( ) ; if ( aName < bName ) { return - 1 ; } else if ( aName > bName ) { return 1 ; } return 0 ; } _computeServerClasses ( selectedServerId , server ) { const serverClasses = [ ] ; if ( this . _isServerSelected ( selectedServerId , server ) ) { serverClasses . push ( 'selected' ) ; } if ( ! server . isSynced ) { serverClasses . push ( 'syncing' ) ; } return serverClasses . join ( ' ' ) ; } _computeServerImage ( selectedServerId , server ) { if ( this . _isServerSelected ( selectedServerId , server ) ) { return 'server-icon-selected.png' ; } return 'server-icon.png' ; } _getCloudId ( accountId ) { if ( this . gcpAccount && accountId === this . gcpAccount . id ) { return DisplayCloudId . GCP ; } else if ( this . digitalOceanAccount && accountId === this . digitalOceanAccount . id ) { return DisplayCloudId . DO ; } return null ; } _serverViewList ( serverList ) { return serverList . map ( entry => ( { id : entry . id , name : entry . name , cloudId : this . _getCloudId ( entry . accountId ) , } ) ) ; } _isServerSelected ( selectedServerId , server ) { return ! ! selectedServerId && selectedServerId === server . id ; } _showServer ( event ) { const server = event . model . server ; this . fire ( 'ShowServerRequested' , { displayServerId : server . id } ) ; this . maybeCloseDrawer ( ) ; } } customElements . define ( AppRoot . is , AppRoot ) ;