-
+
error
{{ 'apps.skysocks-client-settings.no-history' | translate:{number: maxHistoryElements} }}
-
0" class="top-history-margin">
+
+
+
+
+
+
+
+
+
+
+
+ {{ 'apps.skysocks-client-settings.key' | translate }}
+ {{ entry.key }}
+
+
+ {{ 'apps.skysocks-client-settings.note' | translate }}
+
+ {{ entry.note }}
+
+
+
+ {{ 'apps.skysocks-client-settings.note-entered-manually' | translate }}
+
+
+ {{ 'apps.skysocks-client-settings.note-obtained' | translate }}
+
+ ({{ entry.location }})
+
+
+
+
+
+
+
+
+
+
+ add
+
+
+
+
diff --git a/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skysocks-client-settings/skysocks-client-settings.component.scss b/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skysocks-client-settings/skysocks-client-settings.component.scss
index 7249a87014..eeaf745912 100644
--- a/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skysocks-client-settings/skysocks-client-settings.component.scss
+++ b/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skysocks-client-settings/skysocks-client-settings.component.scss
@@ -4,7 +4,7 @@ form {
margin-top: 15px;
}
-.no-history-text {
+.info-text {
margin-top: 20px;
margin-bottom: 2px;
text-align: center;
@@ -16,12 +16,78 @@ form {
}
}
-.top-history-margin {
- width: 100%;
- height: 15px;
+.loading-indicator {
+ height: 100px;
}
-.history-button-content {
- text-align: left;
- padding: 5px 0px;
+.list-button {
+ border-bottom: solid 1px $separator;
+
+ .filter-button-content {
+ padding: 15px 0px;
+ white-space: normal;
+ line-height: 1.3;
+ color: $black;
+ text-align: left;
+ display: flex;
+ font-size: $font-size-smaller;
+ word-break: break-word;
+
+ .icon-area {
+ font-size: 20px;
+ margin-right: 15px;
+ color: $lighter-gray;
+ opacity: 0.4;
+ align-self: center;
+ }
+
+ .item {
+ margin: 4px 0px;
+
+ span:first-of-type {
+ color: $lighter-gray;
+ }
+ }
+
+ .blue-part {
+ color: $blue;
+ }
+ }
+
+ .button-content {
+ text-align: left;
+ padding: 15px 0px;
+ white-space: normal;
+
+ .full-size-area {
+ flex-grow: 1;
+ }
+
+ .item {
+ line-height: 1.3;
+ margin: 4px 0px;
+ font-size: $font-size-smaller;
+ color: $black;
+ word-break: break-all;
+
+ span:first-of-type {
+ color: $lighter-gray;
+ }
+ }
+
+ .options-container {
+ flex-shrink: 0;
+ margin-left: 5px;
+ text-align: right;
+ line-height: 1;
+
+ .small-button {
+ width: 24px;
+ height: 24px;
+ line-height: 14px;
+ font-size: 14px;
+ margin-left: 5px;
+ }
+ }
+ }
}
diff --git a/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skysocks-client-settings/skysocks-client-settings.component.ts b/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skysocks-client-settings/skysocks-client-settings.component.ts
index ae0986b422..34c2085d4b 100644
--- a/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skysocks-client-settings/skysocks-client-settings.component.ts
+++ b/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skysocks-client-settings/skysocks-client-settings.component.ts
@@ -11,7 +11,38 @@ import { processServiceError } from 'src/app/utils/errors';
import { OperationError } from 'src/app/utils/operation-error';
import { AppsService } from 'src/app/services/apps.service';
import GeneralUtils from 'src/app/utils/generalUtils';
-import { Application } from 'src/app/app.datatypes';
+import { Application, ProxyDiscoveryEntry } from 'src/app/app.datatypes';
+import { ProxyDiscoveryService } from 'src/app/services/proxy-discovery.service';
+import { EditSkysocksClientNoteComponent } from './edit-skysocks-client-note/edit-skysocks-client-note.component';
+import { SelectableOption, SelectOptionComponent } from 'src/app/components/layout/select-option/select-option.component';
+import {
+ SkysocksClientFilterComponent,
+ SkysocksClientFilters,
+ StateFilterStates
+} from './skysocks-client-filter/skysocks-client-filter.component';
+
+/**
+ * Data of the entries from the history.
+ */
+export interface HistoryEntry {
+ /**
+ * Remote public key.
+ */
+ key: string;
+ /**
+ * If true, the user entered the data manually using the form. If false, the data was obtained
+ * from the discovery service.
+ */
+ enteredManually: boolean;
+ /**
+ * Location of the visor. Only if it was obtained from the discovery service.
+ */
+ location?: string;
+ /**
+ * Custom note added by the user.
+ */
+ note?: string;
+}
/**
* Modal window used for configuring the Skysocks-client app.
@@ -23,7 +54,7 @@ import { Application } from 'src/app/app.datatypes';
})
export class SkysocksClientSettingsComponent implements OnInit, OnDestroy {
// Key for saving the history in persistent storage.
- private readonly historyStorageKey = 'SkysocksClientHistory';
+ private readonly historyStorageKey = 'SkysocksClientHistory_';
// Max elements the history can contain.
readonly maxHistoryElements = 10;
@@ -31,13 +62,29 @@ export class SkysocksClientSettingsComponent implements OnInit, OnDestroy {
@ViewChild('firstInput', { static: false }) firstInput: ElementRef;
form: FormGroup;
// Entries to show on the history.
- history: string[];
+ history: HistoryEntry[];
- // If the operation in being currently made.
+ // Proxies obtained from the discovery service.
+ proxiesFromDiscovery: ProxyDiscoveryEntry[];
+ // Filtered proxies.
+ proxiesFromDiscoveryToShow: ProxyDiscoveryEntry[];
+ // If the system is still getting the proxies from the discovery service.
+ loadingFromDiscovery = true;
+
+ // Current filters for the poxies from the discovery service.
+ currentFilters = new SkysocksClientFilters();
+ // Texts to be shown on the filter button. Each element represents a filter and has 3
+ // elements. The fist one is a translatable var which describes the filter, the second one has
+ // the value selected by the user if it is a variable for the translate pipe and the third one
+ // has the value selected by the user if the translate pipe is not needed,
+ currentFiltersTexts: string[][] = [];
+
+ stateFilterStates = StateFilterStates;
+
+ // If the operation in currently being made.
private working = false;
- // Last public key set to be sent to the backend.
- private lastPublicKey: string;
private operationSubscription: Subscription;
+ private discoverySubscription: Subscription;
/**
* Opens the modal window. Please use this function instead of opening the window "by hand".
@@ -46,7 +93,7 @@ export class SkysocksClientSettingsComponent implements OnInit, OnDestroy {
const config = new MatDialogConfig();
config.data = app;
config.autoFocus = false;
- config.width = AppConfig.mediumModalWidth;
+ config.width = AppConfig.largeModalWidth;
return dialog.open(SkysocksClientSettingsComponent, config);
}
@@ -58,9 +105,17 @@ export class SkysocksClientSettingsComponent implements OnInit, OnDestroy {
private formBuilder: FormBuilder,
private snackbarService: SnackbarService,
private dialog: MatDialog,
+ private proxyDiscoveryService: ProxyDiscoveryService,
) { }
ngOnInit() {
+ // Get the proxies from the discovery service.
+ this.discoverySubscription = this.proxyDiscoveryService.getProxies().subscribe(response => {
+ this.proxiesFromDiscovery = response;
+ this.filterProxies();
+ this.loadingFromDiscovery = false;
+ });
+
// Get the history.
const retrievedHistory = localStorage.getItem(this.historyStorageKey);
if (retrievedHistory) {
@@ -92,31 +147,164 @@ export class SkysocksClientSettingsComponent implements OnInit, OnDestroy {
}
ngOnDestroy() {
+ this.discoverySubscription.unsubscribe();
if (this.operationSubscription) {
this.operationSubscription.unsubscribe();
}
}
+ // Opens the modal window for selecting the filters.
+ changeFilters() {
+ SkysocksClientFilterComponent.openDialog(this.dialog, this.currentFilters).afterClosed().subscribe(response => {
+ if (response) {
+ this.currentFilters = response;
+ this.filterProxies();
+ }
+ });
+ }
+
+ // Filters the proxies obtained from the discovery service using the filters selected by
+ // the user.
+ private filterProxies() {
+ if (this.currentFilters.state.state === StateFilterStates.NoFilter && !this.currentFilters.location && !this.currentFilters.key) {
+ this.proxiesFromDiscoveryToShow = this.proxiesFromDiscovery;
+ } else {
+ this.proxiesFromDiscoveryToShow = this.proxiesFromDiscovery.filter(proxy => {
+ if (this.currentFilters.state.state === StateFilterStates.Available && !proxy.available) {
+ return false;
+ }
+ if (this.currentFilters.state.state === StateFilterStates.Offline && proxy.available) {
+ return false;
+ }
+ if (this.currentFilters.location && !proxy.location.toLowerCase().includes(this.currentFilters.location.toLowerCase())) {
+ return false;
+ }
+ if (this.currentFilters.key && !proxy.publicKeyPort.toLowerCase().includes(this.currentFilters.key.toLowerCase())) {
+ return false;
+ }
+
+ return true;
+ });
+ }
+
+ this.updateCurrentFilters();
+ }
+
+ // Updates the texts of the filter button.
+ private updateCurrentFilters() {
+ this.currentFiltersTexts = [];
+
+ if (this.currentFilters.state.state !== StateFilterStates.NoFilter) {
+ this.currentFiltersTexts.push(['apps.skysocks-client-settings.filter-dialog.state', this.currentFilters.state.text, '']);
+ }
+ if (this.currentFilters.location) {
+ this.currentFiltersTexts.push(['apps.skysocks-client-settings.filter-dialog.location', '', this.currentFilters.location]);
+ }
+ if (this.currentFilters.key) {
+ this.currentFiltersTexts.push(['apps.skysocks-client-settings.filter-dialog.pub-key', '', this.currentFilters.key]);
+ }
+ }
+
+ // Opens the modal window used on small screens with the options of an history entry.
+ openHistoryOptions(historyEntry: HistoryEntry) {
+ const options: SelectableOption[] = [
+ {
+ icon: 'chevron_right',
+ label: 'apps.skysocks-client-settings.use',
+ },
+ {
+ icon: 'edit',
+ label: 'apps.skysocks-client-settings.change-note',
+ },
+ {
+ icon: 'close',
+ label: 'apps.skysocks-client-settings.remove-entry',
+ }
+ ];
+
+ SelectOptionComponent.openDialog(this.dialog, options).afterClosed().subscribe((selectedOption: number) => {
+ if (selectedOption === 1) {
+ this.saveChanges(historyEntry.key, historyEntry.enteredManually, historyEntry.location, historyEntry.note);
+ } else if (selectedOption === 2) {
+ this.changeNote(historyEntry);
+ } else if (selectedOption === 3) {
+ this.removeFromHistory(historyEntry.key);
+ }
+ });
+ }
+
+ // Removes an element from the history.
+ removeFromHistory(key: String) {
+ // Ask for confirmation.
+ const confirmationMsg = 'apps.skysocks-client-settings.remove-from-history-confirmation';
+ const confirmationDialog = GeneralUtils.createConfirmationDialog(this.dialog, confirmationMsg);
+
+ confirmationDialog.componentInstance.operationAccepted.subscribe(() => {
+ this.history = this.history.filter(value => value.key !== key);
+ const dataToSave = JSON.stringify(this.history);
+ localStorage.setItem(this.historyStorageKey, dataToSave);
+
+ confirmationDialog.close();
+ });
+ }
+
+ // Opens the modal window for changing the personal note of an history entry.
+ changeNote(entry: HistoryEntry) {
+ EditSkysocksClientNoteComponent.openDialog(this.dialog, entry.note).afterClosed().subscribe((response: string) => {
+ if (response) {
+ // Remove the "-" char the modal window adds at the start of the note.
+ response = response.substr(1, response.length - 1);
+
+ // Change the note.
+ this.history.forEach(value => {
+ if (value.key === entry.key) {
+ value.note = response;
+ }
+ });
+
+ // Save the changes..
+ const dataToSave = JSON.stringify(this.history);
+ localStorage.setItem(this.historyStorageKey, dataToSave);
+
+ if (!response) {
+ this.snackbarService.showWarning('apps.skysocks-client-settings.default-note-warning');
+ } else {
+ this.snackbarService.showDone('apps.skysocks-client-settings.changes-made');
+ }
+ }
+ });
+ }
+
/**
- * Saves the settings.
+ * Saves the settings. If no argument is provided, the function will take the public key
+ * from the form and fill the rest of the data. The arguments are mainly for proxies selected
+ * from the discovery list and entries from the history.
+ * @param publicKey New public key to be used.
+ * @param enteredManually If the user manually entered the data using the form.
+ * @param location Location of the proxy server.
+ * @param note Personal note for the history.
*/
- saveChanges(publicKey: string = null) {
+ saveChanges(publicKey: string = null, enteredManually: boolean = null, location: string = null, note: string = null) {
+ // If no public key was provided, the data will be retrieved from the form, so the form
+ // must be valid. Also, the operation can not continue if the component is already working.
if ((!this.form.valid && !publicKey) || this.working) {
return;
}
- this.lastPublicKey = publicKey ? publicKey : this.form.get('pk').value;
+ enteredManually = publicKey ? enteredManually : true;
+ publicKey = publicKey ? publicKey : this.form.get('pk').value;
// Ask for confirmation.
const confirmationMsg = 'apps.skysocks-client-settings.change-key-confirmation';
const confirmationDialog = GeneralUtils.createConfirmationDialog(this.dialog, confirmationMsg);
confirmationDialog.componentInstance.operationAccepted.subscribe(() => {
confirmationDialog.close();
- this.continueSavingChanges();
+ this.continueSavingChanges(publicKey, enteredManually, location, note);
});
}
- private continueSavingChanges() {
+ // Makes the call to the hypervisor API for changing the configuration.
+ private continueSavingChanges(publicKey: string, enteredManually: boolean, location: string, note: string) {
this.button.showLoading();
this.working = true;
@@ -124,19 +312,31 @@ export class SkysocksClientSettingsComponent implements OnInit, OnDestroy {
// The node pk is obtained from the currently openned node page.
NodeComponent.getCurrentNodeKey(),
this.data.name,
- { pk: this.lastPublicKey },
- ).subscribe({
- next: this.onSuccess.bind(this),
- error: this.onError.bind(this)
- });
+ { pk: publicKey },
+ ).subscribe(
+ () => this.onSuccess(publicKey, enteredManually, location, note),
+ err => this.onError(err),
+ );
}
- private onSuccess() {
+ private onSuccess(publicKey: string, enteredManually: boolean, location: string, note: string) {
// Remove any repeated entry from the history.
- this.history = this.history.filter(value => value !== this.lastPublicKey);
+ this.history = this.history.filter(value => value.key !== publicKey);
+
+ // Add the available data to the history entry.
+ const newEntry: HistoryEntry = {
+ key: publicKey,
+ enteredManually: enteredManually,
+ };
+ if (location) {
+ newEntry.location = location;
+ }
+ if (note) {
+ newEntry.note = note;
+ }
- // Save the new public key on the history.
- this.history = [this.lastPublicKey].concat(this.history);
+ // Save the data on the history.
+ this.history = [newEntry].concat(this.history);
if (this.history.length > this.maxHistoryElements) {
const itemsToRemove = this.history.length - this.maxHistoryElements;
this.history.splice(this.history.length - itemsToRemove, itemsToRemove);
diff --git a/static/skywire-manager-src/src/app/services/proxy-discovery.service.ts b/static/skywire-manager-src/src/app/services/proxy-discovery.service.ts
new file mode 100644
index 0000000000..3ad7075393
--- /dev/null
+++ b/static/skywire-manager-src/src/app/services/proxy-discovery.service.ts
@@ -0,0 +1,62 @@
+import { Injectable } from '@angular/core';
+import { Observable } from 'rxjs';
+import { HttpClient } from '@angular/common/http';
+import { retryWhen, delay, map } from 'rxjs/operators';
+
+import { ProxyDiscoveryEntry } from '../app.datatypes';
+
+/**
+ * Allows to get the proxies registered in the proxy discovery service.
+ */
+@Injectable({
+ providedIn: 'root'
+})
+export class ProxyDiscoveryService {
+ /**
+ * URL of the proxy discovery service.
+ */
+ private readonly discoveryServiceUrl = 'http://localhost:8081';
+
+ constructor(
+ private http: HttpClient,
+ ) {}
+
+ /**
+ * Get the proxies registered in the proxy discovery service.
+ */
+ getProxies(): Observable
{
+ return this.http.get(this.discoveryServiceUrl + '/api/v1/getAll').pipe(
+ // In case of error, retry.
+ retryWhen(errors => errors.pipe(delay(4000))),
+ map((response: ProxyDiscoveryEntry[]) => {
+ // Process the data.
+ response.forEach(proxy => {
+ // Remove the invalid dates.
+ if (proxy.updatedAt) {
+ proxy.updatedAt = proxy.updatedAt.startsWith('0001-01-01') ? null : proxy.updatedAt;
+ }
+
+ // Process the status.
+ if (proxy.status) {
+ proxy.available = proxy.status.toLowerCase() === 'available';
+ }
+
+ // Process the location.
+ let location = '';
+ if (proxy.city) {
+ location += proxy.city;
+ }
+ if (proxy.city && proxy.country) {
+ location += ', ';
+ }
+ if (proxy.country) {
+ location += proxy.country;
+ }
+ proxy.location = location;
+ });
+
+ return response;
+ })
+ );
+ }
+}
diff --git a/static/skywire-manager-src/src/assets/i18n/en.json b/static/skywire-manager-src/src/assets/i18n/en.json
index 7929c82b97..6d4d4933f0 100644
--- a/static/skywire-manager-src/src/assets/i18n/en.json
+++ b/static/skywire-manager-src/src/assets/i18n/en.json
@@ -302,15 +302,48 @@
},
"skysocks-client-settings": {
"title": "Skysocks-Client Settings",
- "remote-visor-tab": "Remote Visor",
+ "discovery-tab": "Search visor",
+ "remote-visor-tab": "Enter manually",
"history-tab": "History",
+ "use": "Use this data",
+ "change-note": "Change note",
+ "remove-entry": "Remove entry",
+ "note": "Note:",
+ "note-entered-manually": "Entered manually",
+ "note-obtained": "Obtained from the discovery service",
+ "key": "Key:",
+ "location": "Location:",
+ "state": "State:",
+ "state-available": "Available",
+ "state-offline": "Offline",
+ "update": "Last updated:",
"public-key": "Remote visor public key",
+ "no-elements": "Currently there are no elements to show. Please try again later.",
+ "no-elements-for-filters": "There are no elements that meet the filter criteria.",
+ "no-filter": "No filter has been selected",
+ "click-to-change": "Click to change",
"remote-key-length-error": "The public key must be 66 characters long.",
"remote-key-chars-error": "The public key must only contain hexadecimal characters.",
"save": "Save",
+ "remove-from-history-confirmation": "Are you sure you want to remove the entry from the history?",
"change-key-confirmation": "Are you sure you want to change the remote visor public key?",
"changes-made": "The changes have been made.",
- "no-history": "This tab will show the last {{ number }} public keys used."
+ "no-history": "This tab will show the last {{ number }} public keys used.",
+ "default-note-warning": "The default note has been used.",
+
+ "change-note-dialog": {
+ "title": "Change Note",
+ "note": "Note"
+ },
+
+ "filter-dialog": {
+ "title": "Filters",
+ "state": "The state must be",
+ "state-no-filter": "Do not filter",
+ "location": "The location must contain",
+ "pub-key": "The public key must contain",
+ "apply": "Apply"
+ }
},
"stop-app": "Stop",
"start-app": "Start",
diff --git a/static/skywire-manager-src/src/assets/i18n/es.json b/static/skywire-manager-src/src/assets/i18n/es.json
index 462944a3d6..62c37b86f9 100644
--- a/static/skywire-manager-src/src/assets/i18n/es.json
+++ b/static/skywire-manager-src/src/assets/i18n/es.json
@@ -272,15 +272,48 @@
},
"skysocks-client-settings": {
"title": "Configuración de Skysocks-Client",
- "remote-visor-tab": "Visor Remoto",
+ "discovery-tab": "Buscar visor",
+ "remote-visor-tab": "Introducir manualmente",
"history-tab": "Historial",
+ "use": "Usar estos datos",
+ "change-note": "Cambiar nota",
+ "remove-entry": "Remover entrada",
+ "note": "Nota:",
+ "note-entered-manually": "Introducido manualmente",
+ "note-obtained": "Obtenido del servicio de descubrimiento",
+ "key": "Llave:",
+ "location": "Ubicación:",
+ "state": "Estado:",
+ "state-available": "Disponible",
+ "state-offline": "Offline",
+ "update": "Última vez actualizado:",
"public-key": "Llave pública del visor remoto",
+ "no-elements": "Actualmente no hay elementos para mostrar. Por favor, inténtelo de nuevo más tarde.",
+ "no-elements-for-filters": "No hay elementos que cumplan los criterios de filtro.",
+ "no-filter": "No se ha seleccionado ningún filtro",
+ "click-to-change": "Haga clic para cambiar",
"remote-key-length-error": "La llave pública debe tener 66 caracteres.",
"remote-key-chars-error": "La llave pública sólo debe contener caracteres hexadecimales.",
"save": "Guardar",
+ "remove-from-history-confirmation": "¿Seguro de que desea eliminar la entrada del historial?",
"change-key-confirmation": "¿Seguro que desea cambiar la llave pública del visor remoto?",
"changes-made": "Los cambios han sido realizados.",
- "no-history": "Esta pestaña mostrará las últimas {{ number }} llaves públicas usadas."
+ "no-history": "Esta pestaña mostrará las últimas {{ number }} llaves públicas usadas.",
+ "default-note-warning": "La nota por defecto ha sido utilizada.",
+
+ "change-note-dialog": {
+ "title": "Cambiar Nota",
+ "note": "Nota"
+ },
+
+ "filter-dialog": {
+ "title": "Filtros",
+ "state": "El estado debe ser",
+ "state-no-filter": "No filtrar",
+ "location": "La ubicación debe contener",
+ "pub-key": "La llave pública debe contener",
+ "apply": "Aplicar"
+ }
},
"stop-app": "Detener",
"start-app": "Iniciar",
diff --git a/static/skywire-manager-src/src/assets/i18n/es_base.json b/static/skywire-manager-src/src/assets/i18n/es_base.json
index a7a8bfcbdc..4018bfeb35 100644
--- a/static/skywire-manager-src/src/assets/i18n/es_base.json
+++ b/static/skywire-manager-src/src/assets/i18n/es_base.json
@@ -272,15 +272,48 @@
},
"skysocks-client-settings": {
"title": "Skysocks-Client Settings",
- "remote-visor-tab": "Remote Visor",
+ "discovery-tab": "Search visor",
+ "remote-visor-tab": "Enter manually",
"history-tab": "History",
+ "use": "Use this data",
+ "change-note": "Change note",
+ "remove-entry": "Remove entry",
+ "note": "Note:",
+ "note-entered-manually": "Entered manually",
+ "note-obtained": "Obtained from the discovery service",
+ "key": "Key:",
+ "location": "Location:",
+ "state": "State:",
+ "state-available": "Available",
+ "state-offline": "Offline",
+ "update": "Last updated:",
"public-key": "Remote visor public key",
+ "no-elements": "Currently there are no elements to show. Please try again later.",
+ "no-elements-for-filters": "There are no elements that meet the filter criteria.",
+ "no-filter": "No filter has been selected",
+ "click-to-change": "Click to change",
"remote-key-length-error": "The public key must be 66 characters long.",
"remote-key-chars-error": "The public key must only contain hexadecimal characters.",
"save": "Save",
+ "remove-from-history-confirmation": "Are you sure you want to remove the entry from the history?",
"change-key-confirmation": "Are you sure you want to change the remote visor public key?",
"changes-made": "The changes have been made.",
- "no-history": "This tab will show the last {{ number }} public keys used."
+ "no-history": "This tab will show the last {{ number }} public keys used.",
+ "default-note-warning": "The default note has been used.",
+
+ "change-note-dialog": {
+ "title": "Change Note",
+ "note": "Note"
+ },
+
+ "filter-dialog": {
+ "title": "Filters",
+ "state": "The state must be",
+ "state-no-filter": "Do not filter",
+ "location": "The location must contain",
+ "pub-key": "The public key must contain",
+ "apply": "Apply"
+ }
},
"stop-app": "Stop",
"start-app": "Start",
From 6a1546cf3f2e2ca93f68e5fa17720bf815adf533 Mon Sep 17 00:00:00 2001
From: Senyoret1 <34079003+Senyoret1@users.noreply.github.com>
Date: Fri, 17 Apr 2020 19:22:30 -0400
Subject: [PATCH 2/6] Add pagination to the skysocks-client config in the
manager
---
.../skysocks-client-settings.component.html | 14 ++++-
.../skysocks-client-settings.component.scss | 18 ++++++
.../skysocks-client-settings.component.ts | 60 ++++++++++++++++++-
.../src/assets/i18n/en.json | 1 +
.../src/assets/i18n/es.json | 1 +
.../src/assets/i18n/es_base.json | 1 +
6 files changed, 92 insertions(+), 3 deletions(-)
diff --git a/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skysocks-client-settings/skysocks-client-settings.component.html b/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skysocks-client-settings/skysocks-client-settings.component.html
index 8d526a1415..0172361250 100644
--- a/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skysocks-client-settings/skysocks-client-settings.component.html
+++ b/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skysocks-client-settings/skysocks-client-settings.component.html
@@ -70,7 +70,7 @@
-
+
error
{{ 'apps.skysocks-client-settings.no-elements-for-filters' | translate }}
@@ -105,6 +105,18 @@
+
+
+
1">
+ {{ 'apps.skysocks-client-settings.pagination-info' | translate:{currentElementsRange: currentRange, totalElements: filteredProxiesFromDiscovery.length} }}
+
+
+
+
diff --git a/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skysocks-client-settings/skysocks-client-settings.component.scss b/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skysocks-client-settings/skysocks-client-settings.component.scss
index eeaf745912..c920187a37 100644
--- a/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skysocks-client-settings/skysocks-client-settings.component.scss
+++ b/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skysocks-client-settings/skysocks-client-settings.component.scss
@@ -1,4 +1,5 @@
@import "variables";
+@import "bootstrap_overrides";
form {
margin-top: 15px;
@@ -91,3 +92,20 @@ form {
}
}
}
+
+.paginator {
+ float: right;
+ margin-top: 15px;
+
+ > span {
+ @media (max-width: (map-get($grid-breakpoints, md) - 1)) {
+ & {
+ font-size: $font-size-mini;
+ }
+ }
+ }
+
+ button {
+ margin-left: 5px;
+ }
+}
diff --git a/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skysocks-client-settings/skysocks-client-settings.component.ts b/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skysocks-client-settings/skysocks-client-settings.component.ts
index 34c2085d4b..edf07188d3 100644
--- a/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skysocks-client-settings/skysocks-client-settings.component.ts
+++ b/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skysocks-client-settings/skysocks-client-settings.component.ts
@@ -57,6 +57,8 @@ export class SkysocksClientSettingsComponent implements OnInit, OnDestroy {
private readonly historyStorageKey = 'SkysocksClientHistory_';
// Max elements the history can contain.
readonly maxHistoryElements = 10;
+ // How many elements to show per page on the proxy discovery tab.
+ readonly maxElementsPerPage = 10;
@ViewChild('button', { static: false }) button: ButtonComponent;
@ViewChild('firstInput', { static: false }) firstInput: ElementRef;
@@ -67,9 +69,17 @@ export class SkysocksClientSettingsComponent implements OnInit, OnDestroy {
// Proxies obtained from the discovery service.
proxiesFromDiscovery: ProxyDiscoveryEntry[];
// Filtered proxies.
+ filteredProxiesFromDiscovery: ProxyDiscoveryEntry[];
+ // Proxies to show in the currently selected page.
proxiesFromDiscoveryToShow: ProxyDiscoveryEntry[];
// If the system is still getting the proxies from the discovery service.
loadingFromDiscovery = true;
+ // How many pages with proxies there are.
+ numberOfPages = 1;
+ // Current page.
+ currentPage = 1;
+ // Which elements are being shown in the currently selected page.
+ currentRange = '1 - 1';
// Current filters for the poxies from the discovery service.
currentFilters = new SkysocksClientFilters();
@@ -167,9 +177,9 @@ export class SkysocksClientSettingsComponent implements OnInit, OnDestroy {
// the user.
private filterProxies() {
if (this.currentFilters.state.state === StateFilterStates.NoFilter && !this.currentFilters.location && !this.currentFilters.key) {
- this.proxiesFromDiscoveryToShow = this.proxiesFromDiscovery;
+ this.filteredProxiesFromDiscovery = this.proxiesFromDiscovery;
} else {
- this.proxiesFromDiscoveryToShow = this.proxiesFromDiscovery.filter(proxy => {
+ this.filteredProxiesFromDiscovery = this.proxiesFromDiscovery.filter(proxy => {
if (this.currentFilters.state.state === StateFilterStates.Available && !proxy.available) {
return false;
}
@@ -188,6 +198,7 @@ export class SkysocksClientSettingsComponent implements OnInit, OnDestroy {
}
this.updateCurrentFilters();
+ this.updatePagination();
}
// Updates the texts of the filter button.
@@ -205,6 +216,51 @@ export class SkysocksClientSettingsComponent implements OnInit, OnDestroy {
}
}
+ // Updates the vars related to the pagination of the proxy discovery tab and shows
+ // the first page.
+ private updatePagination() {
+ this.currentPage = 1;
+ this.numberOfPages = Math.ceil(this.filteredProxiesFromDiscovery.length / this.maxElementsPerPage);
+ this.showCurrentPage();
+ }
+
+ // Goes to the next page in the proxy discovery tab.
+ goToNextPage() {
+ if (this.currentPage >= this.numberOfPages) {
+ return;
+ }
+
+ this.currentPage += 1;
+ this.showCurrentPage();
+ }
+
+ // Goes to the previous page in the proxy discovery tab.
+ goToPreviousPage() {
+ if (this.currentPage <= 1) {
+ return;
+ }
+
+ this.currentPage -= 1;
+ this.showCurrentPage();
+ }
+
+ // Updates the UI to show the elements of the page indicated in the currentPage var.
+ private showCurrentPage() {
+ // Update the elements to show.
+ this.proxiesFromDiscoveryToShow = this.filteredProxiesFromDiscovery.slice(
+ (this.currentPage - 1) * this.maxElementsPerPage,
+ this.currentPage * this.maxElementsPerPage
+ );
+
+ // Update the text with the range currently shown.
+ this.currentRange = (((this.currentPage - 1) * this.maxElementsPerPage) + 1) + ' - ';
+ if (this.currentPage < this.numberOfPages) {
+ this.currentRange += (this.currentPage * this.maxElementsPerPage) + '';
+ } else {
+ this.currentRange += this.filteredProxiesFromDiscovery.length + '';
+ }
+ }
+
// Opens the modal window used on small screens with the options of an history entry.
openHistoryOptions(historyEntry: HistoryEntry) {
const options: SelectableOption[] = [
diff --git a/static/skywire-manager-src/src/assets/i18n/en.json b/static/skywire-manager-src/src/assets/i18n/en.json
index 6d4d4933f0..babbfe4c1e 100644
--- a/static/skywire-manager-src/src/assets/i18n/en.json
+++ b/static/skywire-manager-src/src/assets/i18n/en.json
@@ -330,6 +330,7 @@
"changes-made": "The changes have been made.",
"no-history": "This tab will show the last {{ number }} public keys used.",
"default-note-warning": "The default note has been used.",
+ "pagination-info": "{{ currentElementsRange }} of {{ totalElements }}",
"change-note-dialog": {
"title": "Change Note",
diff --git a/static/skywire-manager-src/src/assets/i18n/es.json b/static/skywire-manager-src/src/assets/i18n/es.json
index 62c37b86f9..0debda68f9 100644
--- a/static/skywire-manager-src/src/assets/i18n/es.json
+++ b/static/skywire-manager-src/src/assets/i18n/es.json
@@ -300,6 +300,7 @@
"changes-made": "Los cambios han sido realizados.",
"no-history": "Esta pestaña mostrará las últimas {{ number }} llaves públicas usadas.",
"default-note-warning": "La nota por defecto ha sido utilizada.",
+ "pagination-info": "{{ currentElementsRange }} de {{ totalElements }}",
"change-note-dialog": {
"title": "Cambiar Nota",
diff --git a/static/skywire-manager-src/src/assets/i18n/es_base.json b/static/skywire-manager-src/src/assets/i18n/es_base.json
index 4018bfeb35..68c4a83b60 100644
--- a/static/skywire-manager-src/src/assets/i18n/es_base.json
+++ b/static/skywire-manager-src/src/assets/i18n/es_base.json
@@ -300,6 +300,7 @@
"changes-made": "The changes have been made.",
"no-history": "This tab will show the last {{ number }} public keys used.",
"default-note-warning": "The default note has been used.",
+ "pagination-info": "{{ currentElementsRange }} of {{ totalElements }}",
"change-note-dialog": {
"title": "Change Note",
From c81d8c4846bda198fbd1723aae8260fb023d308c Mon Sep 17 00:00:00 2001
From: Senyoret1 <34079003+Senyoret1@users.noreply.github.com>
Date: Wed, 29 Apr 2020 15:26:22 -0400
Subject: [PATCH 3/6] Improvements for the proxy discovery UI on the manager
---
static/skywire-manager-src/proxy.config.json | 8 +++
.../src/app/app.datatypes.ts | 9 ++-
.../skysocks-client-filter.component.html | 9 ---
.../skysocks-client-filter.component.ts | 54 ----------------
.../skysocks-client-settings.component.html | 24 ++++---
.../skysocks-client-settings.component.scss | 6 +-
.../skysocks-client-settings.component.ts | 63 ++++++++++++++-----
.../app/services/proxy-discovery.service.ts | 60 +++++++++++-------
.../src/assets/i18n/en.json | 5 +-
.../src/assets/i18n/es.json | 5 +-
.../src/assets/i18n/es_base.json | 5 +-
11 files changed, 115 insertions(+), 133 deletions(-)
diff --git a/static/skywire-manager-src/proxy.config.json b/static/skywire-manager-src/proxy.config.json
index 4d59f228d0..3b26a04b5a 100644
--- a/static/skywire-manager-src/proxy.config.json
+++ b/static/skywire-manager-src/proxy.config.json
@@ -11,5 +11,13 @@
"pathRewrite": {
"^/http-api" : "/api"
}
+ },
+ "/discovery-api": {
+ "target": "http://127.0.0.1:8001",
+ "secure": false,
+ "changeOrigin": true,
+ "pathRewrite": {
+ "^/discovery-api" : "/api"
+ }
}
}
diff --git a/static/skywire-manager-src/src/app/app.datatypes.ts b/static/skywire-manager-src/src/app/app.datatypes.ts
index a3734ad498..e246d57c57 100644
--- a/static/skywire-manager-src/src/app/app.datatypes.ts
+++ b/static/skywire-manager-src/src/app/app.datatypes.ts
@@ -55,13 +55,12 @@ export interface HealthInfo {
}
export class ProxyDiscoveryEntry {
- publicKeyPort: string;
+ address: string;
+ pk: string;
+ port: string;
country?: string;
- city?: string;
+ region?: string;
location?: string;
- status?: string;
- available?: boolean;
- updatedAt?: string;
}
// old
diff --git a/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skysocks-client-settings/skysocks-client-filter/skysocks-client-filter.component.html b/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skysocks-client-settings/skysocks-client-filter/skysocks-client-filter.component.html
index 29f6885608..07f33236e1 100644
--- a/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skysocks-client-settings/skysocks-client-filter/skysocks-client-filter.component.html
+++ b/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skysocks-client-settings/skysocks-client-filter/skysocks-client-filter.component.html
@@ -1,14 +1,5 @@
-
-
- {{ 'apps.skysocks-client-settings.port' | translate }}
- {{ proxy.port }}
-
{{ 'apps.skysocks-client-settings.location' | translate }}
diff --git a/static/skywire-manager-src/src/app/services/proxy-discovery.service.ts b/static/skywire-manager-src/src/app/services/proxy-discovery.service.ts
index 63b7bdb2da..73e9261e8a 100644
--- a/static/skywire-manager-src/src/app/services/proxy-discovery.service.ts
+++ b/static/skywire-manager-src/src/app/services/proxy-discovery.service.ts
@@ -17,7 +17,9 @@ export class ProxyDiscoveryService {
* URL of the proxy discovery service. While in dev mode the url is managed by the
* dev server proxy.
*/
- private readonly discoveryServiceUrl = environment.production ? 'http://proxy.discovery.skywire.cc/api' : '/discovery-api';
+ private readonly discoveryServiceUrl = environment.production ?
+ (window.location.protocol + '//proxy.discovery.skywire.cc/api') :
+ '/discovery-api';
constructor(
private http: HttpClient,
From dd8b61d5cea1e40bafce10432c01333179ca9e07 Mon Sep 17 00:00:00 2001
From: Senyoret1 <34079003+Senyoret1@users.noreply.github.com>
Date: Wed, 12 Aug 2020 12:05:17 -0400
Subject: [PATCH 5/6] Fixes for configuring the skysocks-client app using the
manager
---
.../skysocks-client-settings.component.scss | 2 +-
.../skysocks-client-settings.component.ts | 2 +-
.../src/app/services/proxy-discovery.service.ts | 4 ++--
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skysocks-client-settings/skysocks-client-settings.component.scss b/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skysocks-client-settings/skysocks-client-settings.component.scss
index 161f4fa11d..21008b5443 100644
--- a/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skysocks-client-settings/skysocks-client-settings.component.scss
+++ b/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skysocks-client-settings/skysocks-client-settings.component.scss
@@ -51,7 +51,7 @@ form {
}
.blue-part {
- color: $blue;
+ color: $blue-medium;
}
}
diff --git a/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skysocks-client-settings/skysocks-client-settings.component.ts b/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skysocks-client-settings/skysocks-client-settings.component.ts
index fc5c68cc94..a61b69b4f0 100644
--- a/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skysocks-client-settings/skysocks-client-settings.component.ts
+++ b/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skysocks-client-settings/skysocks-client-settings.component.ts
@@ -311,7 +311,7 @@ export class SkysocksClientSettingsComponent implements OnInit, OnDestroy {
}
];
- SelectOptionComponent.openDialog(this.dialog, options).afterClosed().subscribe((selectedOption: number) => {
+ SelectOptionComponent.openDialog(this.dialog, options, 'common.options').afterClosed().subscribe((selectedOption: number) => {
if (selectedOption === 1) {
this.saveChanges(historyEntry.key, historyEntry.enteredManually, historyEntry.location, historyEntry.note);
} else if (selectedOption === 2) {
diff --git a/static/skywire-manager-src/src/app/services/proxy-discovery.service.ts b/static/skywire-manager-src/src/app/services/proxy-discovery.service.ts
index 73e9261e8a..0edd689ddc 100644
--- a/static/skywire-manager-src/src/app/services/proxy-discovery.service.ts
+++ b/static/skywire-manager-src/src/app/services/proxy-discovery.service.ts
@@ -18,7 +18,7 @@ export class ProxyDiscoveryService {
* dev server proxy.
*/
private readonly discoveryServiceUrl = environment.production ?
- (window.location.protocol + '//proxy.discovery.skywire.cc/api') :
+ (window.location.protocol + '//service.discovery.skycoin.com/api/services?type=proxy') :
'/discovery-api';
constructor(
@@ -31,7 +31,7 @@ export class ProxyDiscoveryService {
getProxies(): Observable {
const response: ProxyDiscoveryEntry[] = [];
- return this.http.get(this.discoveryServiceUrl + '/proxies').pipe(
+ return this.http.get(this.discoveryServiceUrl).pipe(
// In case of error, retry.
retryWhen(errors => errors.pipe(delay(4000))),
map((result: any[]) => {
From 60cc41b403445c1464540c4e869455ea10753660 Mon Sep 17 00:00:00 2001
From: Senyoret1 <34079003+Senyoret1@users.noreply.github.com>
Date: Thu, 13 Aug 2020 10:43:19 -0400
Subject: [PATCH 6/6] Additional fixes for configuring the skysocks-client app
using the manager
---
.../skysocks-client-settings.component.scss | 2 +-
.../src/app/services/proxy-discovery.service.ts | 4 +---
2 files changed, 2 insertions(+), 4 deletions(-)
diff --git a/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skysocks-client-settings/skysocks-client-settings.component.scss b/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skysocks-client-settings/skysocks-client-settings.component.scss
index 21008b5443..fa4bdc06cf 100644
--- a/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skysocks-client-settings/skysocks-client-settings.component.scss
+++ b/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skysocks-client-settings/skysocks-client-settings.component.scss
@@ -22,7 +22,7 @@ form {
}
.list-button {
- border-bottom: solid 1px $separator;
+ border-bottom: solid 1px $grey-separator;
.filter-button-content {
padding: 15px 0px;
diff --git a/static/skywire-manager-src/src/app/services/proxy-discovery.service.ts b/static/skywire-manager-src/src/app/services/proxy-discovery.service.ts
index 0edd689ddc..5deac4ed56 100644
--- a/static/skywire-manager-src/src/app/services/proxy-discovery.service.ts
+++ b/static/skywire-manager-src/src/app/services/proxy-discovery.service.ts
@@ -17,9 +17,7 @@ export class ProxyDiscoveryService {
* URL of the proxy discovery service. While in dev mode the url is managed by the
* dev server proxy.
*/
- private readonly discoveryServiceUrl = environment.production ?
- (window.location.protocol + '//service.discovery.skycoin.com/api/services?type=proxy') :
- '/discovery-api';
+ private readonly discoveryServiceUrl = 'https://service.discovery.skycoin.com/api/services?type=proxy';
constructor(
private http: HttpClient,