mirror of
https://github.com/OutlineFoundation/outline-server.git
synced 2026-08-04 14:37:34 +00:00
Convert outline-server-view to a class
This commit is contained in:
parent
e0f2e7381f
commit
36f887cc9a
1 changed files with 171 additions and 156 deletions
|
|
@ -13,8 +13,6 @@
|
|||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
import '@polymer/polymer/polymer-legacy.js';
|
||||
|
||||
import '@polymer/paper-dialog/paper-dialog.js';
|
||||
import '@polymer/iron-icons/iron-icons.js';
|
||||
import '@polymer/iron-pages/iron-pages.js';
|
||||
|
|
@ -33,8 +31,9 @@ import './outline-help-bubble.js';
|
|||
import './outline-metrics-option-dialog.js';
|
||||
import './outline-server-settings.js';
|
||||
import './outline-share-dialog.js';
|
||||
import {Polymer} from '@polymer/polymer/lib/legacy/polymer-fn.js';
|
||||
import {html} from '@polymer/polymer/lib/utils/html-tag.js';
|
||||
import {html, PolymerElement} from '@polymer/polymer';
|
||||
import {DirMixin} from '@polymer/polymer/lib/mixins/dir-mixin.js';
|
||||
|
||||
|
||||
import * as byte_size from 'byte-size';
|
||||
|
||||
|
|
@ -47,8 +46,9 @@ byte_size.defaultOptions({
|
|||
|
||||
const MY_CONNECTION_USER_ID = '0';
|
||||
|
||||
Polymer({
|
||||
_template: html`
|
||||
export class ServerView extends DirMixin(PolymerElement) {
|
||||
static get template() {
|
||||
return html`
|
||||
<style include="cloud-install-styles"></style>
|
||||
<style>
|
||||
.container {
|
||||
|
|
@ -542,76 +542,83 @@ Polymer({
|
|||
<p>[[localize('data-limits-dialog-text')]]</p>
|
||||
<paper-button on-tap="closeDataLimitsHelpBubble">[[localize('ok')]]</paper-button>
|
||||
</outline-help-bubble>
|
||||
`,
|
||||
`;
|
||||
}
|
||||
|
||||
is: 'outline-server-view',
|
||||
static get is() {
|
||||
return 'outline-server-view';
|
||||
}
|
||||
|
||||
properties: {
|
||||
serverId: String,
|
||||
serverName: String,
|
||||
serverHostname: String,
|
||||
serverVersion: String,
|
||||
isHostnameEditable: {type: Boolean, value: false},
|
||||
serverManagementApiUrl: String,
|
||||
serverPortForNewAccessKeys: Number,
|
||||
isAccessKeyPortEditable: {type: Boolean, value: false},
|
||||
serverCreationDate: String,
|
||||
serverLocation: String,
|
||||
accessKeyDataLimit: {type: Object, value: null},
|
||||
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.
|
||||
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: []},
|
||||
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',
|
||||
},
|
||||
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,
|
||||
},
|
||||
},
|
||||
static get properties() {
|
||||
return {
|
||||
serverId: String,
|
||||
serverName: String,
|
||||
serverHostname: String,
|
||||
serverVersion: String,
|
||||
isHostnameEditable: {type: Boolean, value: false},
|
||||
serverManagementApiUrl: String,
|
||||
serverPortForNewAccessKeys: Number,
|
||||
isAccessKeyPortEditable: {type: Boolean, value: false},
|
||||
serverCreationDate: String,
|
||||
serverLocation: String,
|
||||
accessKeyDataLimit: {type: Object, value: null},
|
||||
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.
|
||||
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: []},
|
||||
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',
|
||||
},
|
||||
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,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
observers: [
|
||||
'_accessKeysAddedOrRemoved(accessKeyRows.splices)',
|
||||
'_myConnectionChanged(myConnection)',
|
||||
'_selectedTabChanged(selectedTab)',
|
||||
],
|
||||
static get observers() {
|
||||
return [
|
||||
'_accessKeysAddedOrRemoved(accessKeyRows.splices)',
|
||||
'_myConnectionChanged(myConnection)',
|
||||
'_selectedTabChanged(selectedTab)',
|
||||
];
|
||||
}
|
||||
|
||||
// Parameter `accessKey` has the format {
|
||||
// id: string,
|
||||
|
|
@ -621,7 +628,7 @@ Polymer({
|
|||
// transferredBytes: number;
|
||||
// relativeTraffic: number;
|
||||
// }
|
||||
addAccessKey: function(accessKey) {
|
||||
addAccessKey(accessKey) {
|
||||
// TODO(fortuna): Restore loading animation.
|
||||
// TODO(fortuna): Restore highlighting.
|
||||
this.push('accessKeyRows', accessKey);
|
||||
|
|
@ -629,23 +636,23 @@ Polymer({
|
|||
this.$.accessKeysContainer.querySelector('dom-repeat').render();
|
||||
const input = this.$$(`#access-key-${accessKey.id}`);
|
||||
input.select();
|
||||
},
|
||||
}
|
||||
|
||||
removeAccessKey: function(accessKeyId) {
|
||||
removeAccessKey(accessKeyId) {
|
||||
for (let ui in this.accessKeyRows) {
|
||||
if (this.accessKeyRows[ui].id === accessKeyId) {
|
||||
this.splice('accessKeyRows', ui, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_handleAddAccessKeyPressed: function() {
|
||||
this.fire('AddAccessKeyRequested');
|
||||
_handleAddAccessKeyPressed() {
|
||||
this.dispatchEvent(new CustomEvent('AddAccessKeyRequested', {bubbles: true, composed: true}));
|
||||
this.$.addAccessKeyHelpBubble.hide();
|
||||
},
|
||||
}
|
||||
|
||||
_handleNameInputKeyDown: function(event) {
|
||||
_handleNameInputKeyDown(event) {
|
||||
const input = event.target;
|
||||
if (event.key === 'Escape') {
|
||||
const accessKey = event.model.item;
|
||||
|
|
@ -654,9 +661,9 @@ Polymer({
|
|||
} else if (event.key === 'Enter') {
|
||||
input.blur();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_handleNameInputBlur: function(event) {
|
||||
_handleNameInputBlur(event) {
|
||||
const input = event.target;
|
||||
const accessKey = event.model.item;
|
||||
const displayName = input.value;
|
||||
|
|
@ -664,51 +671,57 @@ Polymer({
|
|||
return;
|
||||
}
|
||||
input.disabled = true;
|
||||
this.fire('RenameAccessKeyRequested', {
|
||||
accessKeyId: accessKey.id,
|
||||
newName: displayName,
|
||||
entry: {
|
||||
commitName: () => {
|
||||
accessKey.name = displayName;
|
||||
input.disabled = false;
|
||||
},
|
||||
revertName: () => {
|
||||
input.value = accessKey.name;
|
||||
input.disabled = false;
|
||||
this.dispatchEvent(new CustomEvent('RenameAccessKeyRequested', {
|
||||
detail: {
|
||||
accessKeyId: accessKey.id,
|
||||
newName: displayName,
|
||||
entry: {
|
||||
commitName: () => {
|
||||
accessKey.name = displayName;
|
||||
input.disabled = false;
|
||||
},
|
||||
revertName: () => {
|
||||
input.value = accessKey.name;
|
||||
input.disabled = false;
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
bubbles: true, composed: true}));
|
||||
}
|
||||
|
||||
_handleRenameAccessKeyPressed: function(event) {
|
||||
_handleRenameAccessKeyPressed(event) {
|
||||
const input = this.$.accessKeysContainer.querySelectorAll(
|
||||
'.access-key-row .access-key-container > input')[event.model.index];
|
||||
// This needs to be deferred because the closing menu messes up with the focus.
|
||||
window.setTimeout(() => {
|
||||
input.focus();
|
||||
}, 0);
|
||||
},
|
||||
}
|
||||
|
||||
_handleConnectPressed: function() {
|
||||
_handleConnectPressed() {
|
||||
this.$.getConnectedHelpBubble.hide();
|
||||
this.fire('OpenGetConnectedDialogRequested', {accessKey: this.myConnection.accessUrl});
|
||||
},
|
||||
this.dispatchEvent(new CustomEvent('OpenGetConnectedDialogRequested', {
|
||||
detail: {accessKey: this.myConnection.accessUrl},
|
||||
bubbles: true, composed: true}));
|
||||
}
|
||||
|
||||
_handleShareCodePressed: function(event) {
|
||||
_handleShareCodePressed(event) {
|
||||
const accessKey = event.model.item;
|
||||
this.fire('OpenShareDialogRequested', {accessKey: accessKey.accessUrl});
|
||||
},
|
||||
this.dispatchEvent(new CustomEvent('OpenShareDialogRequested', {
|
||||
detail: {accessKey: accessKey.accessUrl},
|
||||
bubbles: true, composed: true}));
|
||||
}
|
||||
|
||||
_handleRemoveAccessKeyPressed: function(e) {
|
||||
_handleRemoveAccessKeyPressed(e) {
|
||||
const accessKey = e.model.item;
|
||||
this.fire('RemoveAccessKeyRequested', {accessKeyId: accessKey.id});
|
||||
},
|
||||
|
||||
_encodeURIComponent: encodeURIComponent,
|
||||
this.dispatchEvent(new CustomEvent('RemoveAccessKeyRequested', {
|
||||
detail: {accessKeyId: accessKey.id},
|
||||
bubbles: true, composed: true}));
|
||||
}
|
||||
|
||||
setServerTransferredData(totalBytes) {
|
||||
this.totalInboundBytes = totalBytes;
|
||||
},
|
||||
}
|
||||
|
||||
updateAccessKeyRow(accessKeyId, fields) {
|
||||
let newAccessKeyRow;
|
||||
|
|
@ -723,9 +736,9 @@ Polymer({
|
|||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_formatBytesTransferred: function(numBytes, emptyValue = '') {
|
||||
_formatBytesTransferred(numBytes, emptyValue = '') {
|
||||
if (!numBytes) {
|
||||
// numBytes may not be set for manual servers, or may be 0 for
|
||||
// unused access keys.
|
||||
|
|
@ -741,9 +754,9 @@ Polymer({
|
|||
}
|
||||
|
||||
return byte_size(numBytes, {precision: numDecimals}).toString();
|
||||
},
|
||||
}
|
||||
|
||||
_getFormattedTransferredUnit: function(numBytes, emptyValue = '') {
|
||||
_getFormattedTransferredUnit(numBytes, emptyValue = '') {
|
||||
const formattedTransfer = this._formatBytesTransferred(numBytes);
|
||||
if (!formattedTransfer) {
|
||||
return emptyValue;
|
||||
|
|
@ -753,18 +766,18 @@ Polymer({
|
|||
return emptyValue;
|
||||
}
|
||||
return units.pop();
|
||||
},
|
||||
}
|
||||
|
||||
_getFormattedTransferredValue: function(numBytes, emptyValue = '') {
|
||||
_getFormattedTransferredValue(numBytes, emptyValue = '') {
|
||||
const formattedTransfer = this._formatBytesTransferred(numBytes);
|
||||
if (!formattedTransfer) {
|
||||
return emptyValue;
|
||||
}
|
||||
const value = parseFloat(formattedTransfer);
|
||||
return !!value ? value : emptyValue;
|
||||
},
|
||||
}
|
||||
|
||||
_computeManagedServerUtilzationPercentage: function(numBytes, monthlyLimitBytes) {
|
||||
_computeManagedServerUtilzationPercentage(numBytes, monthlyLimitBytes) {
|
||||
let utilizationPercentage = 0;
|
||||
if (monthlyLimitBytes && numBytes) {
|
||||
utilizationPercentage = Math.round((numBytes / monthlyLimitBytes) * 100);
|
||||
|
|
@ -773,9 +786,9 @@ Polymer({
|
|||
return `%${utilizationPercentage}`;
|
||||
}
|
||||
return `${utilizationPercentage}%`;
|
||||
},
|
||||
}
|
||||
|
||||
_accessKeysAddedOrRemoved: function(changeRecord) {
|
||||
_accessKeysAddedOrRemoved(changeRecord) {
|
||||
// Check for myConnection and regular access keys.
|
||||
let hasNonAdminAccessKeys = false;
|
||||
for (let ui in this.accessKeyRows) {
|
||||
|
|
@ -786,13 +799,13 @@ Polymer({
|
|||
}
|
||||
}
|
||||
this.hasNonAdminAccessKeys = hasNonAdminAccessKeys;
|
||||
},
|
||||
}
|
||||
|
||||
_myConnectionChanged: function(myConnection) {
|
||||
_myConnectionChanged(myConnection) {
|
||||
if (!myConnection) {
|
||||
this.$.getConnectedHelpBubble.hide();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_selectedTabChanged(selectedTab) {
|
||||
if (this.selectedTab === 'settings') {
|
||||
|
|
@ -801,50 +814,50 @@ Polymer({
|
|||
this.closeDataLimitsHelpBubble();
|
||||
this.$.serverSettings.update(this.serverName, this.metricsEnabled);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// Help bubbles should be shown after this outline-server-view
|
||||
// is on the screen (e.g. selected in iron-pages). If help bubbles
|
||||
// are initialized before this point, setPosition will not work and
|
||||
// they will appear in the top left of the view.
|
||||
showGetConnectedHelpBubble: function() {
|
||||
showGetConnectedHelpBubble() {
|
||||
return this._showHelpBubble('getConnectedHelpBubble', 'managerRow');
|
||||
},
|
||||
}
|
||||
|
||||
showAddAccessKeyHelpBubble: function() {
|
||||
showAddAccessKeyHelpBubble() {
|
||||
return this._showHelpBubble('addAccessKeyHelpBubble', 'addAccessKeyRow', 'down', 'left');
|
||||
},
|
||||
}
|
||||
|
||||
showDataLimitsHelpBubble: function() {
|
||||
showDataLimitsHelpBubble() {
|
||||
return this._showHelpBubble('dataLimitsHelpBubble', 'settingsTab', 'up', 'right');
|
||||
},
|
||||
}
|
||||
|
||||
closeAddAccessKeyHelpBubble: function() {
|
||||
closeAddAccessKeyHelpBubble() {
|
||||
this.$.addAccessKeyHelpBubble.hide();
|
||||
},
|
||||
}
|
||||
|
||||
closeGetConnectedHelpBubble: function() {
|
||||
closeGetConnectedHelpBubble() {
|
||||
this.$.getConnectedHelpBubble.hide();
|
||||
},
|
||||
}
|
||||
|
||||
closeDataLimitsHelpBubble: function() {
|
||||
closeDataLimitsHelpBubble() {
|
||||
this.$.dataLimitsHelpBubble.hide();
|
||||
},
|
||||
}
|
||||
|
||||
_showHelpBubble: function(
|
||||
_showHelpBubble(
|
||||
helpBubbleId, positionTargetId, arrowDirection = 'down', arrowAlignment = 'right') {
|
||||
return new Promise(resolve => {
|
||||
const helpBubble = this.$[helpBubbleId];
|
||||
helpBubble.show(this.$[positionTargetId], arrowDirection, arrowAlignment);
|
||||
helpBubble.addEventListener('outline-help-bubble-dismissed', resolve);
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
isRegularConnection: function(item) {
|
||||
isRegularConnection(item) {
|
||||
return item.id !== MY_CONNECTION_USER_ID;
|
||||
},
|
||||
}
|
||||
|
||||
_setSortByOrToggleDirection: function(e) {
|
||||
_setSortByOrToggleDirection(e) {
|
||||
const sortBy = e.target.dataset.sortBy;
|
||||
if (this.accessKeySortBy !== sortBy) {
|
||||
this.accessKeySortBy = sortBy;
|
||||
|
|
@ -852,9 +865,9 @@ Polymer({
|
|||
return;
|
||||
}
|
||||
this.accessKeySortDirection *= -1; // Toggle sort direction.
|
||||
},
|
||||
}
|
||||
|
||||
_sortAccessKeys: function(accessKeySortBy, accessKeySortDirection) {
|
||||
_sortAccessKeys(accessKeySortBy, accessKeySortDirection) {
|
||||
if (accessKeySortBy === 'usage') {
|
||||
return function(a, b) {
|
||||
return (a.transferredBytes - b.transferredBytes) * this.accessKeySortDirection;
|
||||
|
|
@ -872,26 +885,26 @@ Polymer({
|
|||
}
|
||||
return 0;
|
||||
}.bind(this);
|
||||
},
|
||||
}
|
||||
|
||||
_computeShouldShowSortIcon: function(
|
||||
_computeShouldShowSortIcon(
|
||||
accessKeySortBy, accessKeySortDirection, elementSortBy, elementSortDirection) {
|
||||
return accessKeySortBy === elementSortBy && accessKeySortDirection === elementSortDirection;
|
||||
},
|
||||
}
|
||||
|
||||
destroyServer: function() {
|
||||
this.fire('DeleteServerRequested');
|
||||
},
|
||||
destroyServer() {
|
||||
this.dispatchEvent(new CustomEvent('DeleteServerRequested', {bubbles: true, composed: true}));
|
||||
}
|
||||
|
||||
removeServer: function() {
|
||||
this.fire('ForgetServerRequested');
|
||||
},
|
||||
removeServer() {
|
||||
this.dispatchEvent(new CustomEvent('ForgetServerRequested', {bubbles: true, composed: true}));
|
||||
}
|
||||
|
||||
_computePaperProgressClass: function(isAccessKeyDataLimitEnabled) {
|
||||
_computePaperProgressClass(isAccessKeyDataLimitEnabled) {
|
||||
return isAccessKeyDataLimitEnabled ? 'data-limits' : '';
|
||||
},
|
||||
}
|
||||
|
||||
_getDataLimitsUsageString: function(accessKey) {
|
||||
_getDataLimitsUsageString(accessKey) {
|
||||
if (!this.accessKeyDataLimit) {
|
||||
return '';
|
||||
}
|
||||
|
|
@ -899,4 +912,6 @@ Polymer({
|
|||
const total = this.accessKeyDataLimit.value + ' ' + this.accessKeyDataLimit.unit;
|
||||
return this.localize('data-limits-usage', 'used', used, 'total', total);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
customElements.define(ServerView.is, ServerView);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue