diff --git a/src/server_manager/bower.json b/src/server_manager/bower.json deleted file mode 100644 index 23dd797d..00000000 --- a/src/server_manager/bower.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "name": "outline-launcher", - "description": "Launches Outline Shadowsocks servers on the cloud", - "main": "", - "authors": [ - "Outline" - ], - "license": "Apache", - "private": true, - "ignore": [ - "**/.*", - "node_modules", - "bower_components", - "test", - "tests" - ], - "dependencies": { - "app-layout": "PolymerElements/app-layout#^2.0.4", - "app-localize-behavior": "PolymerElements/app-localize-behavior#^2.0.1", - "font-roboto": "PolymerElements/font-roboto-local#^1.1.0", - "iron-autogrow-textarea": "PolymerElements/iron-autogrow-textarea#^2.1.1", - "iron-icons": "PolymerElements/iron-icons#^2.0.1", - "iron-collapse": "PolymerElements/iron-collapse#^2.2.1", - "iron-fit-behavior": "PolymerElements/iron-fit-behavior#^2.1.0", - "iron-pages": "PolymerElements/iron-pages#^2.0.1", - "paper-button": "PolymerElements/paper-button#^2.0.0", - "paper-checkbox": "PolymerElements/paper-checkbox#^2.0.4", - "paper-dialog": "PolymerElements/paper-dialog#^2.0.1", - "paper-dialog-scrollable": "PolymerElements/paper-dialog-scrollable#^2.1.0", - "paper-dropdown-menu": "PolymerElements/paper-dropdown-menu#^2.0.2", - "paper-icon-button": "PolymerElements/paper-icon-button#^2.0.1", - "paper-input": "PolymerElements/paper-input#^2.1.0", - "paper-item": "PolymerElements/paper-item#^2.0.0", - "paper-listbox": "PolymerElements/paper-listbox#^2.0.0", - "paper-progress": "PolymerElements/paper-progress#^2.0.1", - "paper-tabs": "PolymerElements/paper-tabs#^2.1.1", - "paper-toast": "PolymerElements/paper-toast#^2.0.0", - "paper-tooltip": "PolymerElements/paper-tooltip#^2.0.0", - "web-animations-js": "web-animations/web-animations-js", - "webcomponentsjs": "webcomponents/webcomponentsjs#^v1.1.0" - } -} diff --git a/scripts/generate_rtl_css.js b/src/server_manager/css-in-js-rtl-loader.js similarity index 50% rename from scripts/generate_rtl_css.js rename to src/server_manager/css-in-js-rtl-loader.js index 777d8893..df5c6b16 100644 --- a/scripts/generate_rtl_css.js +++ b/src/server_manager/css-in-js-rtl-loader.js @@ -1,4 +1,4 @@ -// Copyright 2019 The Outline Authors +// Copyright 2020 The Outline Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,24 +12,26 @@ // See the License for the specific language governing permissions and // limitations under the License. -const gulp = require('gulp'); -const posthtml = require('gulp-posthtml'); -const posthtmlcss = require('posthtml-postcss'); +const postcss = require('postcss'); const rtl = require('postcss-rtl'); -const replace = require('gulp-replace'); -// Generates inline CSS RTL mirroring rules for Polymer components. -module.exports = function(src, dest) { - console.log('Generating RTL CSS'); - const plugins = [rtl()]; - const options = {from: undefined, sync: true}; - const filterType = /\/css$/; - return gulp.src([src]) - .pipe(posthtml([posthtmlcss(plugins, options, filterType)])) +const CSS_PROCESSOR = postcss([rtl()]); + +function generateRtlCss(css) { + return CSS_PROCESSOR.process(css).css // Replace the generated selectors with Shadow DOM selectors for Polymer compatibility. - .pipe(replace('[dir=rtl]', ':host(:dir(rtl))')) - .pipe(replace('[dir=ltr]', ':host(:dir(ltr))')) + .replace(/\[dir=rtl\]/g, ':host(:dir(rtl))') + .replace(/\[dir=ltr\]/g, ':host(:dir(ltr))') // rtlcss generates [dir] selectors for rules unaffected by directionality; ignore them. - .pipe(replace('[dir]', '')) - .pipe(gulp.dest(dest)); -} \ No newline at end of file + .replace(/\[dir\]/g, ''); +} +// This is a Webpack loader that searches for - - - - -
- - - - - -
-
- - -
- -
-
- [[localize('servers-digitalocean')]] - - -
-

[[localize('digitalocean-disconnect-account')]]

- -
[[localize('digitalocean-disconnect')]]
-
-
-
-
- -
-
- -
-
- [[localize('servers-manual')]] -
-
- -
-
-
- - -
- - [[localize('servers-add')]] - -
- - - - [[localize('nav-data-collection')]] - [[localize('nav-feedback')]] - [[localize('nav-help')]] - [[localize('nav-about')]] - - -
- - -
- - - - - - -
- -
-
-
-
-
- - - - - - - - - - - - - - - - - - -
- [[localize('close')]] -
-
- - - - - [[localize('error-licenses')]] - - -
- [[localize('close')]] -
-
-
- - - diff --git a/src/server_manager/ui_components/cloud-install-styles.html b/src/server_manager/ui_components/cloud-install-styles.html deleted file mode 100644 index 02cd582b..00000000 --- a/src/server_manager/ui_components/cloud-install-styles.html +++ /dev/null @@ -1,243 +0,0 @@ - - - - - - - diff --git a/src/server_manager/ui_components/outline-feedback-dialog.html b/src/server_manager/ui_components/outline-feedback-dialog.html deleted file mode 100644 index 22ffaf66..00000000 --- a/src/server_manager/ui_components/outline-feedback-dialog.html +++ /dev/null @@ -1,249 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/src/server_manager/ui_components/outline-iconset.html b/src/server_manager/ui_components/outline-iconset.html deleted file mode 100644 index 4b184861..00000000 --- a/src/server_manager/ui_components/outline-iconset.html +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/server_manager/ui_components/outline-metrics-option-dialog.html b/src/server_manager/ui_components/outline-metrics-option-dialog.html deleted file mode 100644 index 742a033e..00000000 --- a/src/server_manager/ui_components/outline-metrics-option-dialog.html +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - - - - - - diff --git a/src/server_manager/ui_components/outline-modal-dialog.html b/src/server_manager/ui_components/outline-modal-dialog.html deleted file mode 100644 index a1da11c1..00000000 --- a/src/server_manager/ui_components/outline-modal-dialog.html +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - - - diff --git a/src/server_manager/ui_components/outline-region-picker-step.html b/src/server_manager/ui_components/outline-region-picker-step.html deleted file mode 100644 index db3c92e3..00000000 --- a/src/server_manager/ui_components/outline-region-picker-step.html +++ /dev/null @@ -1,204 +0,0 @@ - - - - - - - - - - - - - - diff --git a/src/server_manager/ui_components/outline-server-progress-step.html b/src/server_manager/ui_components/outline-server-progress-step.html deleted file mode 100644 index 31cc6217..00000000 --- a/src/server_manager/ui_components/outline-server-progress-step.html +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/src/server_manager/ui_components/outline-server-settings-styles.html b/src/server_manager/ui_components/outline-server-settings-styles.html deleted file mode 100644 index a5f0df12..00000000 --- a/src/server_manager/ui_components/outline-server-settings-styles.html +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - - diff --git a/src/server_manager/ui_components/outline-server-settings.html b/src/server_manager/ui_components/outline-server-settings.html deleted file mode 100644 index e31717ff..00000000 --- a/src/server_manager/ui_components/outline-server-settings.html +++ /dev/null @@ -1,454 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/src/server_manager/ui_components/outline-server-view.html b/src/server_manager/ui_components/outline-server-view.html deleted file mode 100644 index 895995d3..00000000 --- a/src/server_manager/ui_components/outline-server-view.html +++ /dev/null @@ -1,961 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/server_manager/ui_components/outline-validated-input.html b/src/server_manager/ui_components/outline-validated-input.html deleted file mode 100644 index 1ac3cb40..00000000 --- a/src/server_manager/ui_components/outline-validated-input.html +++ /dev/null @@ -1,208 +0,0 @@ - - - - - - - - - - - diff --git a/src/server_manager/web_app/build_action.sh b/src/server_manager/web_app/build_action.sh index 272369ec..d8a97a4d 100755 --- a/src/server_manager/web_app/build_action.sh +++ b/src/server_manager/web_app/build_action.sh @@ -22,7 +22,6 @@ readonly OUT_DIR=$BUILD_DIR/server_manager/web_app rm -rf $OUT_DIR # Create do_install_script.ts, which has a variable with the content of do_install_server.sh. -mkdir -p $OUT_DIR/ts/server_manager/web_app mkdir -p $OUT_DIR/sh/server_manager/web_app pushd $ROOT_DIR/src/server_manager/install_scripts > /dev/null @@ -36,20 +35,9 @@ pushd $ROOT_DIR > /dev/null node src/server_manager/install_scripts/build_install_script_ts.node.js \ build/server_manager/web_app/sh/server_manager/web_app/scripts.tgz > $ROOT_DIR/src/server_manager/install_scripts/do_install_script.ts -# Notice that we forward the build environment if defined. -webpack --config=src/server_manager/webpack.config.js ${BUILD_ENV:+--mode=${BUILD_ENV}} -popd > /dev/null - readonly STATIC_DIR=$OUT_DIR/static mkdir -p $STATIC_DIR -# Browserify node_modules/ (just a couple of key NPMs) and app. -# TODO(fortuna): Use polymer-webpack-loader instead. -pushd $OUT_DIR > /dev/null -mkdir -p browserified/server_manager/web_app -$NODE_MODULES_BIN_DIR/browserify --require byte-size --require clipboard-polyfill -o static/node_modules.js +# Notice that we forward the build environment if defined. +webpack --config=src/server_manager/webpack.config.js ${BUILD_ENV:+--mode=${BUILD_ENV}} popd > /dev/null - -# Generate CSS rules to mirror the UI in RTL languages. -# TODO(fortuna): Move this to a Webpack loader. -node -e "require('./scripts/generate_rtl_css.js')('$STATIC_DIR/ui_components/*.html', '$STATIC_DIR/ui_components')" diff --git a/src/server_manager/web_app/main.ts b/src/server_manager/web_app/main.ts index 16620d53..18bc66ab 100644 --- a/src/server_manager/web_app/main.ts +++ b/src/server_manager/web_app/main.ts @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +import './ui_components/app-root.js'; + import * as digitalocean_api from '../cloud/digitalocean_api'; import * as i18n from '../infrastructure/i18n'; import {getSentryApiUrl} from '../infrastructure/sentry'; diff --git a/src/server_manager/web_app/ui_components/app-root.js b/src/server_manager/web_app/ui_components/app-root.js new file mode 100644 index 00000000..8a07f824 --- /dev/null +++ b/src/server_manager/web_app/ui_components/app-root.js @@ -0,0 +1,859 @@ +/* + Copyright 2018 The Outline 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. +*/ +import '@polymer/polymer/polymer-legacy.js'; +import '@polymer/app-layout/app-drawer/app-drawer.js'; +import '@polymer/app-layout/app-drawer-layout/app-drawer-layout.js'; +import '@polymer/app-layout/app-toolbar/app-toolbar.js'; +import '@polymer/iron-icon/iron-icon.js'; +import '@polymer/iron-icons/iron-icons.js'; +import '@polymer/iron-pages/iron-pages.js'; +import '@polymer/paper-icon-button/paper-icon-button.js'; +import '@polymer/paper-toast/paper-toast.js'; +import '@polymer/paper-dialog/paper-dialog.js'; +import '@polymer/paper-dialog-scrollable/paper-dialog-scrollable.js'; +import '@polymer/paper-listbox/paper-listbox.js'; +import '@polymer/paper-menu-button/paper-menu-button.js'; +import './cloud-install-styles.js'; +import './outline-about-dialog.js'; +import './outline-do-oauth-step.js'; +import './outline-feedback-dialog.js'; +import './outline-survey-dialog.js'; +import './outline-intro-step.js'; +import './outline-manual-server-entry.js'; +import './outline-modal-dialog.js'; +import './outline-region-picker-step.js'; +import './outline-server-progress-step.js'; +import './outline-server-view.js'; +import './outline-tos-view.js'; + +import {AppLocalizeBehavior} from '@polymer/app-localize-behavior/app-localize-behavior.js'; +import {mixinBehaviors} from '@polymer/polymer/lib/legacy/class.js'; +import {html} from '@polymer/polymer/lib/utils/html-tag.js'; +import {PolymerElement} from '@polymer/polymer/polymer-element.js'; + +const TOS_ACK_LOCAL_STORAGE_KEY = 'tos-ack'; + +class AppRoot extends mixinBehaviors +([AppLocalizeBehavior], PolymerElement) { + static get template() { + return html` + + + + + +
+ + + + + +
+
+ + +
+ +
+
+ [[localize('servers-digitalocean')]] + + +
+

[[localize('digitalocean-disconnect-account')]]

+ +
[[localize('digitalocean-disconnect')]]
+
+
+
+
+ +
+
+ +
+
+ [[localize('servers-manual')]] +
+
+ +
+
+
+ + +
+ + [[localize('servers-add')]] + +
+ + + + [[localize('nav-data-collection')]] + [[localize('nav-feedback')]] + [[localize('nav-help')]] + [[localize('nav-about')]] + + +
+ + +
+ + + + + + +
+ +
+
+
+
+
+ + + + + + + + + + + + + + + + + + +
+ [[localize('close')]] +
+
+ + + + + [[localize('error-licenses')]] + + +
+ [[localize('close')]] +
+
+
+`; + } + + static get is() { + return 'app-root'; + } + + static get properties() { + return { + // Properties language and useKeyIfMissing are used by Polymer.AppLocalizeBehavior. + language: { + type: String, + readonly: true, + }, + useKeyIfMissing: { + type: Boolean, + value: true, + }, + serverList: { + type: Array, + value: [], + }, + selectedServer: { + type: Object, // DisplayServer in display_server.ts + value: undefined, + }, + hasManualServers: { + type: Boolean, + computed: '_computeHasManualServers(serverList.*)', + }, + adminEmail: { + type: String, + value: '', + }, + isSignedInToDigitalOcean: { + type: Boolean, + computed: '_computeIsSignedInToDigitalOcean(adminEmail)', + }, + outlineVersion: String, + userAcceptedTos: { + type: Boolean, + // Get notified when the user clicks the OK button in the ToS view + observer: '_userAcceptedTosChanged', + }, + hasAcceptedTos: { + type: Boolean, + computed: '_computeHasAcceptedTermsOfService(userAcceptedTos)', + }, + currentPage: { + type: String, + value: 'intro', + }, + shouldShowSideBar: { + type: Boolean, + value: false, + }, + sideBarMarginClass: { + type: String, + computed: '_computeSideBarMarginClass(shouldShowSideBar)', + }, + }; + } + + 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); + } + + setLanguage(newLanguage, langDir) { + const messagesUrl = `/messages/${newLanguage}.json`; + this.loadResources(messagesUrl, newLanguage); + if (langDir === 'rtl') { + this.$.appDrawer.align = 'right'; + this.$.sideBar.align = 'right'; + } + this.language = newLanguage; + } + + showIntro() { + this.maybeCloseDrawer(); + this.selectedServer = undefined; + this.currentPage = 'intro'; + } + + getDigitalOceanOauthFlow(onCancel) { + const oauthFlow = this.$.digitalOceanOauth; + oauthFlow.onCancel = onCancel; + return oauthFlow; + } + + showDigitalOceanOauthFlow() { + this.currentPage = 'digitalOceanOauth'; + } + + getAndShowDigitalOceanOauthFlow(onCancel) { + this.currentPage = 'digitalOceanOauth'; + const oauthFlow = this.getDigitalOceanOauthFlow(onCancel); + oauthFlow.showConnectAccount(); + return oauthFlow; + } + + getAndShowRegionPicker() { + this.currentPage = 'regionPicker'; + this.$.regionPicker.init(); + return this.$.regionPicker; + } + + getManualServerEntry() { + return this.$.manualEntry; + } + + showProgress(serverName, showCancelButton) { + this.currentPage = 'serverProgressStep'; + this.$.serverProgressStep.serverName = serverName; + this.$.serverProgressStep.showCancelButton = showCancelButton; + this.$.serverProgressStep.start(); + } + + showServerView() { + this.$.serverProgressStep.stop(); + this.currentPage = 'serverView'; + } + + getServerView(displayServerId) { + if (!displayServerId) { + return null; + } + const selectedServerId = this._base64Encode(displayServerId); + return this.$.serverView.querySelector(`#serverView-${selectedServerId}`); + } + + handleRegionSelected(e) { + this.fire('SetUpServerRequested', { + regionId: this.$.regionPicker.getSelectedRegionId(), + }); + } + + handleSetUpGenericCloudProviderRequested() { + this.handleManualServerSelected('generic'); + } + + handleSetUpAwsRequested() { + this.handleManualServerSelected('aws'); + } + + handleSetUpGcpRequested() { + this.handleManualServerSelected('gcp'); + } + + handleManualServerSelected(cloudProvider) { + this.$.manualEntry.clear(); + this.$.manualEntry.cloudProvider = cloudProvider; + this.currentPage = 'manualEntry'; + } + + handleManualCancelled() { + this.currentPage = 'intro'; + } + + showError(errorMsg) { + this.showToast(errorMsg, Infinity); + } + + showNotification(message, durationMs = 3000) { + this.showToast(message, durationMs); + } + + showToast(message, duration) { + const toast = this.$.toast; + toast.close(); + // Defer in order to trigger the toast animation, otherwise the + // update happens in place. + setTimeout(() => { + toast.show({ + text: message, + duration: duration, + noOverlap: true, + }); + }, 0); + } + + closeError() { + this.$.toast.close(); + } + + // cb is a function which accepts a single boolean which is true iff + // the user chose to retry the failing operation. + showConnectivityDialog(cb) { + const dialogTitle = this.localize('error-connectivity-title'); + const dialogText = this.localize('error-connectivity'); + this.showModalDialog(dialogTitle, dialogText, [this.localize('cancel'), this.localize('retry')]) + .then(clickedButtonIndex => { + cb(clickedButtonIndex === 1); // pass true if user clicked retry + }); + } + + getConfirmation(title, text, confirmButtonText, continueFunc) { + this.showModalDialog(title, text, [this.localize('cancel'), confirmButtonText]) + .then(clickedButtonIndex => { + if (clickedButtonIndex === 1) { + // user clicked to confirm. + continueFunc(); + } + }); + } + + showManualServerError(errorTitle, errorText) { + this.showModalDialog(errorTitle, errorText, [this.localize('cancel'), this.localize('retry')]) + .then(clickedButtonIndex => { + if (clickedButtonIndex == 1) { + this.$.manualEntry.retryTapped(); + } else { + this.$.manualEntry.cancelTapped(); + } + }); + } + _computeIsSignedInToDigitalOcean(adminEmail) { + return Boolean(adminEmail); + } + + _computeHasManualServers(serverList) { + return this.serverList.filter(server => !server.isManaged).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 drawerNarrow = this.$.drawerLayout.narrow; + const forceNarrow = this.$.drawerLayout.forceNarrow; + if (drawerNarrow) { + if (forceNarrow) { + // The window width is below the responsive threshold. Do not force narrow mode. + this.$.drawerLayout.forceNarrow = false; + } + this.$.appDrawer.toggle(); + } else { + // Forcing narrow mode when the window width is above the responsive threshold effectively + // collapses the drawer. Conversely, reverting force narrow expands the drawer. + this.$.drawerLayout.forceNarrow = !forceNarrow; + } + } + + maybeCloseDrawer() { + if (this.$.drawerLayout.narrow || this.$.drawerLayout.forceNarrow) { + this.$.appDrawer.close(); + } + } + + submitFeedbackTapped() { + this.$.feedbackDialog.open(); + } + + aboutTapped() { + this.$.aboutDialog.open(); + } + + signOutTapped() { + this.fire('SignOutRequested'); + } + + openManualInstallFeedback(prepopulatedMessage) { + this.$.feedbackDialog.open(prepopulatedMessage, true); + } + + openShareDialog(accessKey, s3Url) { + this.$.shareDialog.open(accessKey, s3Url); + } + + openGetConnectedDialog(inviteUrl) { + const dialog = this.$.getConnectedDialog; + if (dialog.children.length > 1) { + return; // The iframe is already loading. + } + // Reset the iframe's state, by replacing it with a newly constructed + // iframe. Unfortunately the location.reload API does not work in our case due to + // this Chrome error: + // "Blocked a frame with origin "outline://web_app" from accessing a cross-origin frame." + const iframe = document.createElement('iframe'); + iframe.onload = function() { + dialog.open(); + }; + iframe.src = inviteUrl; + dialog.insertBefore(iframe, dialog.children[0]); + } + + closeGetConnectedDialog() { + const dialog = this.$.getConnectedDialog; + dialog.close(); + const oldIframe = dialog.children[0]; + dialog.removeChild(oldIframe); + } + + showMetricsDialogForNewServer() { + this.$.metricsDialog.showMetricsOptInDialog(); + } + + // Returns a Promise which fulfills with the index of the button clicked. + showModalDialog(title, text, buttons) { + return this.$.modalDialog.open(title, text, buttons); + } + + closeModalDialog(title, text, buttons) { + return this.$.modalDialog.close(); + } + + showLicensesTapped() { + var xhr = new XMLHttpRequest(); + xhr.onload = () => { + this.$.licensesText.innerText = xhr.responseText; + this.$.licenses.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.narrow; + const drawerOpened = this.$.appDrawer.opened; + if (drawerOpened && drawerNarrow) { + this.shouldShowSideBar = false; + } else { + this.shouldShowSideBar = drawerNarrow; + } + } + + _computeSideBarMarginClass(shouldShowSideBar) { + return shouldShowSideBar ? 'side-bar-margin' : ''; + } + + _isServerManaged(server) { + return server.isManaged; + } + + _isServerManual(server) { + return !server.isManaged; + } + + _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(selectedServer, server) { + let serverClasses = []; + if (this._isServerSelected(selectedServer, server)) { + serverClasses.push('selected'); + } + if (!server.isSynced) { + serverClasses.push('syncing'); + } + return serverClasses.join(' '); + } + + _computeServerImage(selectedServer, server) { + if (this._isServerSelected(selectedServer, server)) { + return 'server-icon-selected.png'; + } + return 'server-icon.png'; + } + + _isServerSelected(selectedServer, server) { + return !!selectedServer && selectedServer.id === server.id; + } + + _showServer(event) { + const server = event.model.server; + this.fire('ShowServerRequested', {displayServerId: server.id}); + this.maybeCloseDrawer(); + } + + // Wrapper to encode a string in base64. This is necessary to set the server view IDs to + // the display server IDs, which are URLs, so they can be used with selector methods. The IDs + // are never decoded. + _base64Encode(s) { + return btoa(s).replace(/=/g, ''); + } +} +customElements.define(AppRoot.is, AppRoot); diff --git a/src/server_manager/web_app/ui_components/cloud-install-styles.js b/src/server_manager/web_app/ui_components/cloud-install-styles.js new file mode 100644 index 00000000..32ef5a06 --- /dev/null +++ b/src/server_manager/web_app/ui_components/cloud-install-styles.js @@ -0,0 +1,244 @@ +/* + Copyright 2018 The Outline 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. +*/ + +import {html} from '@polymer/polymer/lib/utils/html-tag.js'; + +// Polymer style module to share styles between steps +// https://polymer-library.polymer-project.org/3.0/docs/devguide/style-shadow-dom#share-styles-between-elements +const styleElement = document.createElement('dom-module'); +styleElement.appendChild(html` +`); + +styleElement.register('cloud-install-styles'); diff --git a/src/server_manager/ui_components/licenses/README.md b/src/server_manager/web_app/ui_components/licenses/README.md similarity index 100% rename from src/server_manager/ui_components/licenses/README.md rename to src/server_manager/web_app/ui_components/licenses/README.md diff --git a/src/server_manager/ui_components/licenses/db-ip_license.txt b/src/server_manager/web_app/ui_components/licenses/db-ip_license.txt similarity index 100% rename from src/server_manager/ui_components/licenses/db-ip_license.txt rename to src/server_manager/web_app/ui_components/licenses/db-ip_license.txt diff --git a/src/server_manager/ui_components/licenses/licenses.txt b/src/server_manager/web_app/ui_components/licenses/licenses.txt similarity index 100% rename from src/server_manager/ui_components/licenses/licenses.txt rename to src/server_manager/web_app/ui_components/licenses/licenses.txt diff --git a/src/server_manager/ui_components/outline-about-dialog.html b/src/server_manager/web_app/ui_components/outline-about-dialog.js similarity index 58% rename from src/server_manager/ui_components/outline-about-dialog.html rename to src/server_manager/web_app/ui_components/outline-about-dialog.js index 411b7aba..762c256d 100644 --- a/src/server_manager/ui_components/outline-about-dialog.html +++ b/src/server_manager/web_app/ui_components/outline-about-dialog.js @@ -1,4 +1,4 @@ - - - +*/ +import '@polymer/polymer/polymer-legacy.js'; - - - - +`, - - + is: 'outline-about-dialog', + + properties: { + localize: {type: Function, readonly: true}, + outlineVersion: String, + }, + + open: function() { + this.$.dialog.open(); + } +}); diff --git a/src/server_manager/ui_components/outline-cloud-instructions-view.html b/src/server_manager/web_app/ui_components/outline-cloud-instructions-view.js similarity index 73% rename from src/server_manager/ui_components/outline-cloud-instructions-view.html rename to src/server_manager/web_app/ui_components/outline-cloud-instructions-view.js index 80fd95dd..83a4a21d 100644 --- a/src/server_manager/ui_components/outline-cloud-instructions-view.html +++ b/src/server_manager/web_app/ui_components/outline-cloud-instructions-view.js @@ -1,4 +1,4 @@ - - - - +*/ +import '@polymer/polymer/polymer-legacy.js'; - -