mirror of
https://github.com/OutlineFoundation/outline-server.git
synced 2026-10-24 05:21:49 +00:00
Merge pull request #655 from Jigsaw-Code/fortuna-types
Add types to Javascript
This commit is contained in:
commit
49af369702
6 changed files with 182 additions and 121 deletions
|
|
@ -22,6 +22,8 @@ import {Surveys} from '../model/survey';
|
|||
import {App} from './app';
|
||||
import {TokenManager} from './digitalocean_oauth';
|
||||
import {DisplayServer, DisplayServerRepository, makeDisplayServer} from './display_server';
|
||||
import {AppRoot} from './ui_components/app-root.js';
|
||||
import {ServerView} from './ui_components/outline-server-view.js';
|
||||
|
||||
const TOKEN_WITH_NO_SERVERS = 'no-server-token';
|
||||
const TOKEN_WITH_ONE_SERVER = 'one-server-token';
|
||||
|
|
@ -257,11 +259,12 @@ enum AppRootScreen {
|
|||
DIALOG
|
||||
}
|
||||
|
||||
class FakePolymerAppRoot implements polymer.Base {
|
||||
class FakePolymerAppRoot extends AppRoot {
|
||||
events = new EventEmitter();
|
||||
backgroundScreen = AppRootScreen.NONE;
|
||||
currentScreen = AppRootScreen.NONE;
|
||||
serverView = {setServerTransferredData: () => {}, serverId: '', initHelpBubbles: () => {}};
|
||||
serverView = {setServerTransferredData: () => {}, serverId: '', initHelpBubbles: () => {}} as
|
||||
unknown as ServerView;
|
||||
serverList: DisplayServer[] = [];
|
||||
is: 'fake-polymer-app-root';
|
||||
|
||||
|
|
@ -290,9 +293,9 @@ class FakePolymerAppRoot implements polymer.Base {
|
|||
showModalDialog() {
|
||||
this.backgroundScreen = this.currentScreen;
|
||||
this.setScreen(AppRootScreen.DIALOG);
|
||||
const promise = new Promise(() => {});
|
||||
const promise = new Promise<number>(() => 0);
|
||||
// Supress Promise not handled warning.
|
||||
promise.then(() => {});
|
||||
promise.then(v => v);
|
||||
return promise;
|
||||
}
|
||||
|
||||
|
|
@ -304,7 +307,7 @@ class FakePolymerAppRoot implements polymer.Base {
|
|||
this.backgroundScreen = AppRootScreen.NONE;
|
||||
}
|
||||
|
||||
getServerView() {
|
||||
getServerView(serverId: string): ServerView {
|
||||
return this.serverView;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,9 @@ import * as digitalocean_server from './digitalocean_server';
|
|||
import {DisplayServer, DisplayServerRepository, makeDisplayServer} from './display_server';
|
||||
import {parseManualServerConfig} from './management_urls';
|
||||
|
||||
import {AppRoot} from './ui_components/app-root.js';
|
||||
import {DisplayAccessKey, DisplayDataAmount, ServerView} from './ui_components/outline-server-view.js';
|
||||
|
||||
// The Outline DigitalOcean team's referral code:
|
||||
// https://www.digitalocean.com/help/referral-program/
|
||||
const UNUSED_DIGITALOCEAN_REFERRAL_CODE = '5ddb4219b716';
|
||||
|
|
@ -38,20 +41,6 @@ const CHANGE_HOSTNAME_VERSION = '1.2.0';
|
|||
export const DATA_LIMITS_AVAILABILITY_DATE = new Date('2020-06-02');
|
||||
const MAX_ACCESS_KEY_DATA_LIMIT_BYTES = 50 * (10 ** 9); // 50GB
|
||||
|
||||
interface UiAccessKey {
|
||||
id: string;
|
||||
placeholderName: string;
|
||||
name: string;
|
||||
accessUrl: string;
|
||||
transferredBytes: number;
|
||||
relativeTraffic: number;
|
||||
}
|
||||
|
||||
interface DisplayDataAmount {
|
||||
unit: 'MB'|'GB';
|
||||
value: number;
|
||||
}
|
||||
|
||||
function dataLimitToDisplayDataAmount(limit: server.DataLimit): DisplayDataAmount|null {
|
||||
if (!limit) {
|
||||
return null;
|
||||
|
|
@ -99,7 +88,7 @@ async function computeDefaultAccessKeyDataLimit(
|
|||
}
|
||||
}
|
||||
|
||||
async function showHelpBubblesOnce(serverView: polymer.Base) {
|
||||
async function showHelpBubblesOnce(serverView: ServerView) {
|
||||
if (!window.localStorage.getItem('addAccessKeyHelpBubble-dismissed')) {
|
||||
await serverView.showAddAccessKeyHelpBubble();
|
||||
window.localStorage.setItem('addAccessKeyHelpBubble-dismissed', 'true');
|
||||
|
|
@ -137,7 +126,7 @@ export class App {
|
|||
private serverBeingCreated: server.ManagedServer;
|
||||
|
||||
constructor(
|
||||
private appRoot: polymer.Base, private readonly version: string,
|
||||
private appRoot: AppRoot, private readonly version: string,
|
||||
private createDigitalOceanSession: DigitalOceanSessionFactory,
|
||||
private createDigitalOceanServerRepository: DigitalOceanServerRepositoryFactory,
|
||||
private manualServerRepository: server.ManualServerRepository,
|
||||
|
|
@ -565,7 +554,7 @@ export class App {
|
|||
});
|
||||
} else {
|
||||
// Display the unreachable server state within the server view.
|
||||
const serverView = this.appRoot.getServerView(displayServer.id);
|
||||
const serverView = this.appRoot.getServerView(displayServer.id) as ServerView;
|
||||
serverView.isServerReachable = false;
|
||||
serverView.isServerManaged = isManagedServer(server);
|
||||
serverView.serverName = displayServer.name; // Don't get the name from the remote server.
|
||||
|
|
@ -892,7 +881,7 @@ export class App {
|
|||
this.showTransferStats(selectedServer, view);
|
||||
}
|
||||
|
||||
private showMetricsOptInWhenNeeded(selectedServer: server.Server, serverView: polymer.Base) {
|
||||
private showMetricsOptInWhenNeeded(selectedServer: server.Server, serverView: ServerView) {
|
||||
const showMetricsOptInOnce = () => {
|
||||
// Sanity check to make sure the running server is still displayed, i.e.
|
||||
// it hasn't been deleted.
|
||||
|
|
@ -922,7 +911,7 @@ export class App {
|
|||
}
|
||||
}
|
||||
|
||||
private async refreshTransferStats(selectedServer: server.Server, serverView: polymer.Base) {
|
||||
private async refreshTransferStats(selectedServer: server.Server, serverView: ServerView) {
|
||||
try {
|
||||
const stats = await selectedServer.getDataUsage();
|
||||
let totalBytes = 0;
|
||||
|
|
@ -963,7 +952,7 @@ export class App {
|
|||
}
|
||||
}
|
||||
|
||||
private showTransferStats(selectedServer: server.Server, serverView: polymer.Base) {
|
||||
private showTransferStats(selectedServer: server.Server, serverView: ServerView) {
|
||||
this.refreshTransferStats(selectedServer, serverView);
|
||||
// Get transfer stats once per minute for as long as server is selected.
|
||||
const statsRefreshRateMs = 60 * 1000;
|
||||
|
|
@ -986,7 +975,7 @@ export class App {
|
|||
|
||||
// Converts the access key from the remote service format to the
|
||||
// format used by outline-server-view.
|
||||
private convertToUiAccessKey(remoteAccessKey: server.AccessKey): UiAccessKey {
|
||||
private convertToUiAccessKey(remoteAccessKey: server.AccessKey): DisplayAccessKey {
|
||||
return {
|
||||
id: remoteAccessKey.id,
|
||||
placeholderName: `${this.appRoot.localize('key', 'keyId', remoteAccessKey.id)}`,
|
||||
|
|
@ -1224,7 +1213,8 @@ export class App {
|
|||
this.appRoot.showError(this.appRoot.localize('error-server-rename'));
|
||||
const oldName = this.selectedServer.getName();
|
||||
view.serverName = oldName;
|
||||
view.$.serverSettings.serverName = oldName;
|
||||
// tslint:disable-next-line:no-any
|
||||
(view.$.serverSettings as any).serverName = oldName;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import * as digitalocean_server from './digitalocean_server';
|
|||
import {DisplayServerRepository} from './display_server';
|
||||
import {ManualServerRepository} from './manual_server';
|
||||
import {DEFAULT_PROMPT_IMPRESSION_DELAY_MS, OutlineSurveys} from './survey';
|
||||
import {AppRoot} from './ui_components/app-root.js';
|
||||
|
||||
const SUPPORTED_LANGUAGES: {[key: string]: {id: string, dir: string}} = {
|
||||
'am': {id: 'am', dir: 'ltr'},
|
||||
|
|
@ -101,7 +102,7 @@ document.addEventListener('WebComponentsReady', () => {
|
|||
document.documentElement.setAttribute('dir', languageDirection);
|
||||
// NOTE: this cast is safe and allows us to leverage Polymer typings since we haven't migrated to
|
||||
// Polymer 3, which adds typescript support.
|
||||
const appRoot = document.getElementById('appRoot') as unknown as polymer.Base;
|
||||
const appRoot = document.getElementById('appRoot') as unknown as AppRoot;
|
||||
appRoot.setLanguage(language.string(), languageDirection);
|
||||
new App(
|
||||
appRoot, version, digitalocean_api.createDigitalOceanSession,
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@ 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';
|
||||
|
|
@ -44,9 +43,13 @@ 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';
|
||||
|
||||
import {DisplayServer} from '../display_server';
|
||||
|
||||
import {ServerView} from './outline-server-view.js';
|
||||
|
||||
const TOS_ACK_LOCAL_STORAGE_KEY = 'tos-ack';
|
||||
|
||||
class AppRoot extends mixinBehaviors
|
||||
export class AppRoot extends mixinBehaviors
|
||||
([AppLocalizeBehavior], PolymerElement) {
|
||||
static get template() {
|
||||
return html`
|
||||
|
|
@ -467,30 +470,15 @@ class AppRoot extends mixinBehaviors
|
|||
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,
|
||||
},
|
||||
language: {type: String, readonly: true},
|
||||
useKeyIfMissing: {type: Boolean},
|
||||
serverList: {type: Array},
|
||||
selectedServer: {type: Object},
|
||||
hasManualServers: {
|
||||
type: Boolean,
|
||||
computed: '_computeHasManualServers(serverList.*)',
|
||||
},
|
||||
adminEmail: {
|
||||
type: String,
|
||||
value: '',
|
||||
},
|
||||
adminEmail: {type: String},
|
||||
isSignedInToDigitalOcean: {
|
||||
type: Boolean,
|
||||
computed: '_computeIsSignedInToDigitalOcean(adminEmail)',
|
||||
|
|
@ -505,14 +493,8 @@ class AppRoot extends mixinBehaviors
|
|||
type: Boolean,
|
||||
computed: '_computeHasAcceptedTermsOfService(userAcceptedTos)',
|
||||
},
|
||||
currentPage: {
|
||||
type: String,
|
||||
value: 'intro',
|
||||
},
|
||||
shouldShowSideBar: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
currentPage: {type: String},
|
||||
shouldShowSideBar: {type: Boolean},
|
||||
sideBarMarginClass: {
|
||||
type: String,
|
||||
computed: '_computeSideBarMarginClass(shouldShowSideBar)',
|
||||
|
|
@ -522,6 +504,18 @@ class AppRoot extends mixinBehaviors
|
|||
|
||||
constructor() {
|
||||
super();
|
||||
/** @type {DisplayServer} */
|
||||
this.selectedServer = undefined;
|
||||
this.language = '';
|
||||
this.useKeyIfMissing = true;
|
||||
/** @type {DisplayServer[]} */
|
||||
this.serverList = [];
|
||||
this.adminEmail = '';
|
||||
this.outlineVersion = '';
|
||||
this.currentPage = 'intro';
|
||||
this.shouldShowSideBar = false;
|
||||
|
||||
|
||||
this.addEventListener('RegionSelected', this.handleRegionSelected);
|
||||
this.addEventListener(
|
||||
'SetUpGenericCloudProviderRequested', this.handleSetUpGenericCloudProviderRequested);
|
||||
|
|
@ -530,6 +524,11 @@ class AppRoot extends mixinBehaviors
|
|||
this.addEventListener('ManualServerEntryCancelled', this.handleManualCancelled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the language and direction for the application
|
||||
* @param {string} newLanguage
|
||||
* @param {string} langDir
|
||||
*/
|
||||
setLanguage(newLanguage, langDir) {
|
||||
const messagesUrl = `./messages/${newLanguage}.json`;
|
||||
this.loadResources(messagesUrl, newLanguage);
|
||||
|
|
@ -573,6 +572,10 @@ class AppRoot extends mixinBehaviors
|
|||
return this.$.manualEntry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} serverName
|
||||
* @param {boolean} showCancelButton
|
||||
*/
|
||||
showProgress(serverName, showCancelButton) {
|
||||
this.currentPage = 'serverProgressStep';
|
||||
this.$.serverProgressStep.serverName = serverName;
|
||||
|
|
@ -585,6 +588,11 @@ class AppRoot extends mixinBehaviors
|
|||
this.currentPage = 'serverView';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ServerView for the server given by its id
|
||||
* @param {string} displayServerId
|
||||
* @returns {ServerView}
|
||||
*/
|
||||
getServerView(displayServerId) {
|
||||
if (!displayServerId) {
|
||||
return null;
|
||||
|
|
@ -593,7 +601,7 @@ class AppRoot extends mixinBehaviors
|
|||
return this.$.serverView.querySelector(`#serverView-${selectedServerId}`);
|
||||
}
|
||||
|
||||
handleRegionSelected(e) {
|
||||
handleRegionSelected(/** @type {Event} */ e) {
|
||||
this.fire('SetUpServerRequested', {
|
||||
regionId: this.$.regionPicker.getSelectedRegionId(),
|
||||
});
|
||||
|
|
@ -611,7 +619,7 @@ class AppRoot extends mixinBehaviors
|
|||
this.handleManualServerSelected('gcp');
|
||||
}
|
||||
|
||||
handleManualServerSelected(cloudProvider) {
|
||||
handleManualServerSelected(/** @type {'generic'|'aws'|'gcp'} */ cloudProvider) {
|
||||
this.$.manualEntry.clear();
|
||||
this.$.manualEntry.cloudProvider = cloudProvider;
|
||||
this.currentPage = 'manualEntry';
|
||||
|
|
@ -621,14 +629,19 @@ class AppRoot extends mixinBehaviors
|
|||
this.currentPage = 'intro';
|
||||
}
|
||||
|
||||
showError(errorMsg) {
|
||||
showError(/** @type {string} */ errorMsg) {
|
||||
this.showToast(errorMsg, Infinity);
|
||||
}
|
||||
|
||||
showNotification(message, durationMs = 3000) {
|
||||
showNotification(/** @type {string} */ message, durationMs = 3000) {
|
||||
this.showToast(message, durationMs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a toast with a message
|
||||
* @param {string} message
|
||||
* @param {number} duration in seconds
|
||||
*/
|
||||
showToast(message, duration) {
|
||||
const toast = this.$.toast;
|
||||
toast.close();
|
||||
|
|
@ -647,8 +660,11 @@ class AppRoot extends mixinBehaviors
|
|||
this.$.toast.close();
|
||||
}
|
||||
|
||||
// cb is a function which accepts a single boolean which is true iff
|
||||
// the user chose to retry the failing operation.
|
||||
/**
|
||||
* @param {(retry: boolean) => void} cb 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');
|
||||
|
|
@ -668,6 +684,10 @@ class AppRoot extends mixinBehaviors
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} errorTitle
|
||||
* @param {string} errorText
|
||||
*/
|
||||
showManualServerError(errorTitle, errorText) {
|
||||
this.showModalDialog(errorTitle, errorText, [this.localize('cancel'), this.localize('retry')])
|
||||
.then(clickedButtonIndex => {
|
||||
|
|
@ -730,7 +750,7 @@ class AppRoot extends mixinBehaviors
|
|||
this.fire('SignOutRequested');
|
||||
}
|
||||
|
||||
openManualInstallFeedback(prepopulatedMessage) {
|
||||
openManualInstallFeedback(/** @type {string} */ prepopulatedMessage) {
|
||||
this.$.feedbackDialog.open(prepopulatedMessage, true);
|
||||
}
|
||||
|
||||
|
|
@ -738,7 +758,7 @@ class AppRoot extends mixinBehaviors
|
|||
this.$.shareDialog.open(accessKey, s3Url);
|
||||
}
|
||||
|
||||
openGetConnectedDialog(inviteUrl) {
|
||||
openGetConnectedDialog(/** @type {string} */ inviteUrl) {
|
||||
const dialog = this.$.getConnectedDialog;
|
||||
if (dialog.children.length > 1) {
|
||||
return; // The iframe is already loading.
|
||||
|
|
@ -766,7 +786,12 @@ class AppRoot extends mixinBehaviors
|
|||
this.$.metricsDialog.showMetricsOptInDialog();
|
||||
}
|
||||
|
||||
// Returns a Promise which fulfills with the index of the button clicked.
|
||||
/**
|
||||
* @param {string} title
|
||||
* @param {string} text
|
||||
* @param {string[]} buttons
|
||||
* @returns {Promise<number>} a Promise which fulfills with the index of the button clicked.
|
||||
*/
|
||||
showModalDialog(title, text, buttons) {
|
||||
return this.$.modalDialog.open(title, text, buttons);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,6 +55,23 @@ function makePublicEvent(eventName, detail) {
|
|||
return new CustomEvent(eventName, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* An access key to be displayed
|
||||
* @typedef {Object} DisplayAccessKey
|
||||
* @prop {string} id
|
||||
* @prop {string} placeholderName
|
||||
* @prop {string} name
|
||||
* @prop {string} accessUrl
|
||||
* @prop {number} transferredBytes
|
||||
* @prop {number} relativeTraffic
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} DisplayDataAmount
|
||||
* @prop {'MB'|'GB'} unit
|
||||
* @prop {number} value
|
||||
*/
|
||||
|
||||
export class ServerView extends DirMixin(PolymerElement) {
|
||||
static get template() {
|
||||
return html`
|
||||
|
|
@ -564,60 +581,35 @@ export class ServerView extends DirMixin(PolymerElement) {
|
|||
serverName: String,
|
||||
serverHostname: String,
|
||||
serverVersion: String,
|
||||
isHostnameEditable: {type: Boolean, value: false},
|
||||
isHostnameEditable: {type: Boolean},
|
||||
serverManagementApiUrl: String,
|
||||
serverPortForNewAccessKeys: Number,
|
||||
isAccessKeyPortEditable: {type: Boolean, value: false},
|
||||
isAccessKeyPortEditable: {type: Boolean},
|
||||
serverCreationDate: String,
|
||||
serverLocation: String,
|
||||
accessKeyDataLimit: {type: Object, value: null},
|
||||
accessKeyDataLimit: {type: Object},
|
||||
isAccessKeyDataLimitEnabled: {type: Boolean},
|
||||
supportsAccessKeyDataLimit:
|
||||
{type: Boolean, value: false}, // Whether the server supports data limits.
|
||||
dataLimitsAvailabilityDate:
|
||||
{type: String}, // Date by which the feature stops being an experiment.
|
||||
supportsAccessKeyDataLimit: {type: Boolean},
|
||||
dataLimitsAvailabilityDate: {type: String},
|
||||
isServerManaged: Boolean,
|
||||
isServerReachable: Boolean,
|
||||
// Callback for retrying to display an unreachable server.
|
||||
retryDisplayingServer: Function,
|
||||
// myConnection has the same fields as each item in accessKeyRows. It may
|
||||
// be unset in some old versions of Outline that allowed deleting this
|
||||
// row.
|
||||
myConnection: Object,
|
||||
totalInboundBytes: Number,
|
||||
accessKeyRows: {type: Array, value: []},
|
||||
accessKeyRows: {type: Array},
|
||||
hasNonAdminAccessKeys: Boolean,
|
||||
metricsEnabled: Boolean,
|
||||
// Initialize monthlyOutboundTransferBytes and monthlyCost to 0, so they can
|
||||
// be bound to hidden attributes. Initializing to undefined does not
|
||||
// cause hidden$=... expressions to be evaluated and so elements may be
|
||||
// shown incorrectly. See:
|
||||
// https://stackoverflow.com/questions/33700125/polymer-1-0-hidden-attribute-negate-operator
|
||||
// https://www.polymer-project.org/1.0/docs/devguide/data-binding.html
|
||||
monthlyOutboundTransferBytes: {type: Number, value: 0},
|
||||
monthlyCost: {type: Number, value: 0},
|
||||
selectedTab: {
|
||||
type: String,
|
||||
value: 'connections',
|
||||
},
|
||||
monthlyOutboundTransferBytes: {type: Number},
|
||||
monthlyCost: {type: Number},
|
||||
selectedTab: {type: String},
|
||||
managedServerUtilzationPercentage: {
|
||||
type: Number,
|
||||
computed:
|
||||
'_computeManagedServerUtilzationPercentage(totalInboundBytes, monthlyOutboundTransferBytes)',
|
||||
},
|
||||
accessKeySortBy: {
|
||||
type: String,
|
||||
value: 'name',
|
||||
},
|
||||
accessKeySortDirection: {
|
||||
// 1 == ascending, -1 == descending
|
||||
type: Number,
|
||||
value: 1,
|
||||
},
|
||||
localize: {
|
||||
type: Function,
|
||||
readonly: true,
|
||||
},
|
||||
accessKeySortBy: {type: String},
|
||||
accessKeySortDirection: {type: Number},
|
||||
localize: {type: Function, readonly: true},
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -629,23 +621,72 @@ export class ServerView extends DirMixin(PolymerElement) {
|
|||
];
|
||||
}
|
||||
|
||||
// Parameter `accessKey` has the format {
|
||||
// id: string,
|
||||
// placeholderName: string,
|
||||
// name: string,
|
||||
// accessUrl: string,
|
||||
// transferredBytes: number;
|
||||
// relativeTraffic: number;
|
||||
// }
|
||||
addAccessKey(accessKey) {
|
||||
// TODO(fortuna): Restore loading animation.
|
||||
// TODO(fortuna): Restore highlighting.
|
||||
this.push('accessKeyRows', accessKey);
|
||||
// Force render the access key list so that the input is present in the DOM
|
||||
this.$.accessKeysContainer.querySelector('dom-repeat').render();
|
||||
const input = this.shadowRoot.querySelector(`#access-key-${accessKey.id}`);
|
||||
input.select();
|
||||
}
|
||||
constructor() {
|
||||
super();
|
||||
this.serverId = '';
|
||||
this.serverName = '';
|
||||
this.serverHostname = '';
|
||||
this.serverVersion = '';
|
||||
this.isHostnameEditable = false;
|
||||
this.serverManagementApiUrl = '';
|
||||
/** @type {number} */
|
||||
this.serverPortForNewAccessKeys = null;
|
||||
this.isAccessKeyPortEditable = false;
|
||||
this.serverCreationDate = '';
|
||||
this.serverLocation = '';
|
||||
/** @type {DisplayDataAmount} */
|
||||
this.accessKeyDataLimit = null;
|
||||
this.isAccessKeyDataLimitEnabled = false;
|
||||
/** Whether the server supports data limits. */
|
||||
this.supportsAccessKeyDataLimit = false;
|
||||
/** Date by which the feature stops being an experiment. */
|
||||
this.dataLimitsAvailabilityDate = '';
|
||||
this.isServerManaged = false;
|
||||
this.isServerReachable = false;
|
||||
/**
|
||||
* Callback for retrying to display an unreachable server.
|
||||
* @type {() => void)}
|
||||
*/
|
||||
this.retryDisplayingServer = null;
|
||||
/**
|
||||
* myConnection has the same fields as each item in accessKeyRows. It may
|
||||
* be unset in some old versions of Outline that allowed deleting this row
|
||||
* @type {DisplayAccessKey}
|
||||
*/
|
||||
this.myConnection = null;
|
||||
this.totalInboundBytes = 0;
|
||||
/** @type {DisplayAccessKey[]} */
|
||||
this.accessKeyRows = [];
|
||||
this.hasNonAdminAccessKeys = false;
|
||||
this.metricsEnabled = true;
|
||||
// Initialize monthlyOutboundTransferBytes and monthlyCost to 0, so they can
|
||||
// be bound to hidden attributes. Initializing to undefined does not
|
||||
// cause hidden$=... expressions to be evaluated and so elements may be
|
||||
// shown incorrectly. See:
|
||||
// https://stackoverflow.com/questions/33700125/polymer-1-0-hidden-attribute-negate-operator
|
||||
// https://www.polymer-project.org/1.0/docs/devguide/data-binding.html
|
||||
this.monthlyOutboundTransferBytes = 0;
|
||||
this.monthlyCost = 0;
|
||||
this.selectedTab = 'connections';
|
||||
this.accessKeySortBy = 'name';
|
||||
/** The direction to sort: 1 == ascending, -1 == descending */
|
||||
this.accessKeySortDirection = 1;
|
||||
/** @type {(msgId: string, ...params: string[]) => string} */
|
||||
this.localize = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DisplayAccessKey} accessKey
|
||||
*/
|
||||
addAccessKey(accessKey) {
|
||||
// TODO(fortuna): Restore loading animation.
|
||||
// TODO(fortuna): Restore highlighting.
|
||||
this.push('accessKeyRows', accessKey);
|
||||
// Force render the access key list so that the input is present in the DOM
|
||||
this.$.accessKeysContainer.querySelector('dom-repeat').render();
|
||||
const input = this.shadowRoot.querySelector(`#access-key-${accessKey.id}`);
|
||||
input.select();
|
||||
}
|
||||
|
||||
removeAccessKey(accessKeyId) {
|
||||
for (let ui in this.accessKeyRows) {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
"outDir": "build/server_manager/web_app/js",
|
||||
"sourceMap": true,
|
||||
"experimentalDecorators":true,
|
||||
"allowJs": true,
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue