Skip to content

Commit

Permalink
Add auto connect option
Browse files Browse the repository at this point in the history
  • Loading branch information
manuelmayer-dev committed May 1, 2024
1 parent c60063d commit 5c515c6
Show file tree
Hide file tree
Showing 11 changed files with 170 additions and 37 deletions.
2 changes: 2 additions & 0 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {IonicStorageModule} from "@ionic/storage-angular";
import {WidgetContentComponentsModule} from "./widget-content-components/widget-content-components.module";
import { ServiceWorkerModule } from '@angular/service-worker';
import {SettingsModalComponent} from "./pages/shared/modals/settings-modal/settings-modal.component";
import {HttpClientModule} from "@angular/common/http";

@NgModule({
declarations: [
Expand All @@ -19,6 +20,7 @@ import {SettingsModalComponent} from "./pages/shared/modals/settings-modal/setti
],
imports: [
BrowserModule,
HttpClientModule,
IonicModule.forRoot(),
IonicStorageModule.forRoot(),
AppRoutingModule,
Expand Down
3 changes: 2 additions & 1 deletion src/app/datatypes/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ export interface Connection {
host: string,
port: number,
ssl: boolean,
index: number | undefined
index: number | undefined,
autoConnect: boolean | undefined
}
3 changes: 3 additions & 0 deletions src/app/datatypes/ping-response.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface PingResponse {
machineName: string;
}
33 changes: 9 additions & 24 deletions src/app/pages/home/connections/connections.page.html
Original file line number Diff line number Diff line change
@@ -1,39 +1,24 @@
<ion-content *ngIf="savedConnectionsInitialized; else connectionsLoading">
<ion-item-group *ngIf="discoveredConnections.length > 0">
<ion-item-divider>
<ion-label>Discovered</ion-label>
</ion-item-divider>

<ion-list lines="full" *ngFor="let connection of savedConnections">
<ion-item color="success" button="true" (click)="connect(connection)">
<ion-icon name="flash" slot="start"></ion-icon>
<ion-label>
<h3>{{connection.name}}</h3>
<span>{{connection.host}}:{{connection.port}}</span>
</ion-label>
</ion-item>
</ion-list>
</ion-item-group>

<ion-item-group *ngIf="savedConnectionsInitialized">
<ion-item-divider>
<ion-label>Saved connections</ion-label>
</ion-item-divider>

<ion-list lines="full" *ngIf="savedConnections.length > 0; else noConnections">
<ion-reorder-group [disabled]="!reorderEnabled" (ionItemReorder)="handleReorder($any($event))">
<ion-item-sliding *ngFor="let connection of savedConnections">
<ion-item button="true" (click)="connect(connection)">
<ion-icon name="star" slot="start"></ion-icon>
<ng-container *ngIf="availableConnections.includes(connection.id)">
<ion-icon slot="start" class="mdi mdi-wifi text-success d-flex align-items-center"></ion-icon>
</ng-container>
<ng-container *ngIf="!availableConnections.includes(connection.id)">
<ion-icon slot="start" class="mdi mdi-wifi-off text-danger d-flex align-items-center"></ion-icon>
</ng-container>
<ion-label>
<h3>{{connection.name}}</h3>
<h3>{{ connection.name }}</h3>
<span>
<ion-icon *ngIf="connection.ssl"
aria-hidden="true"
title="Secure connection"
class="text-success"
name="lock-closed-outline" />
{{connection.host}}:{{connection.port}}
name="lock-closed-outline"/>
{{ connection.host }}:{{ connection.port }}
</span>
</ion-label>
<ion-reorder slot="end"></ion-reorder>
Expand Down
43 changes: 38 additions & 5 deletions src/app/pages/home/connections/connections.page.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
import {Component, OnInit} from '@angular/core';
import {AfterContentInit, Component, ElementRef, HostListener, OnDestroy, OnInit} from '@angular/core';
import {Connection} from "../../../datatypes/connection";
import {ConnectionService} from "../../../services/connection/connection.service";
import {AlertController, ItemReorderEventDetail, ModalController} from "@ionic/angular";
import {AddConnectionComponent} from "./modals/add-connection/add-connection.component";
import {WebsocketService} from "../../../services/websocket/websocket.service";
import {WakelockService} from "../../../services/wakelock/wakelock.service";
import {SettingsService} from "../../../services/settings/settings.service";
import {PingService} from "../../../services/ping/ping.service";
import {Subscription} from "rxjs";

@Component({
selector: 'app-connections',
templateUrl: './connections.page.html',
styleUrls: ['./connections.page.scss'],
})
export class ConnectionsPage implements OnInit {
export class ConnectionsPage implements OnInit, OnDestroy {

private subscription: Subscription = new Subscription();

discoveredConnections: Connection[] = [];
savedConnections: Connection[] = [];
availableConnections: string[] = [];
savedConnectionsInitialized = false;
reorderEnabled: boolean = true;

Expand All @@ -26,10 +30,36 @@ export class ConnectionsPage implements OnInit {
private alertController: AlertController,
private websocketService: WebsocketService,
private wakeLockService: WakelockService,
private settingsService: SettingsService) { }
private settingsService: SettingsService,
private pingService: PingService) { }

async ngOnInit() {
await this.loadConnections();
await this.pingService.start();
this.subscription.add(this.pingService.connectionAvailable.subscribe(async connectionId=> {
if (!this.availableConnections.includes(connectionId)) {
this.availableConnections.push(connectionId);
let connection = this.savedConnections.find(x => x.id == connectionId);
if (connection?.autoConnect === true) {
await this.connect(connection);
}
}
}));
this.subscription.add(this.pingService.connectionUnavailable.subscribe(connectionId=> {
if (this.availableConnections.includes(connectionId)) {
this.availableConnections.splice(this.availableConnections.indexOf(connectionId), 1);
}
}));
this.subscription.add(this.websocketService.connected.subscribe(_ => {
this.pingService.stop();
}));
this.subscription.add(this.websocketService.closed.subscribe(_ => {
this.pingService.start();
}));
}

ngOnDestroy() {
this.subscription.unsubscribe();
}

private async loadConnections() {
Expand All @@ -47,7 +77,9 @@ export class ConnectionsPage implements OnInit {
name: existingConnection?.name,
host: existingConnection?.host,
port: existingConnection?.port ?? 8191,
useSsl: existingConnection?.ssl ?? false
useSsl: existingConnection?.ssl ?? false,
autoConnect: existingConnection?.autoConnect ?? false,
index: existingConnection?.index ?? 0,
}
});
await modal.present();
Expand All @@ -59,6 +91,7 @@ export class ConnectionsPage implements OnInit {

await this.connectionService.addUpdateConnection(data);
await this.loadConnections();
await this.pingService.restart();
}

async handleReorder(event: CustomEvent<ItemReorderEventDetail>) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,29 @@
</ion-header>

<ion-content class="ion-padding">

<ion-item>
<ion-input labelPlacement="floating" type="text" [placeholder]="host" [(ngModel)]="name">
<ion-input labelPlacement="floating" type="text" [placeholder]="host" [(ngModel)]="name" [clearInput]="true">
<div slot="label">Name</div>
</ion-input>
</ion-item>
<ion-item>
<ion-toggle [(ngModel)]="autoConnect">Automatically connect</ion-toggle>
</ion-item>

<ion-item class="mt-2">
<ion-input labelPlacement="floating" type="text" [(ngModel)]="host">
<ion-input labelPlacement="floating" type="text" [(ngModel)]="host" [clearInput]="true">
<div slot="label">IP Address / Hostname</div>
</ion-input>
</ion-item>

<ion-item class="mt-2">
<ion-item>
<ion-input min="1" labelPlacement="floating" type="number" [(ngModel)]="port">
<div slot="label">Port</div>
</ion-input>
</ion-item>

<ion-item class="mt-2">
<ion-item>
<ion-toggle [(ngModel)]="useSsl">Use SSL</ion-toggle>
</ion-item>
</ion-content>
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ export class AddConnectionComponent {
host: string = "";
port: number = 8191;
useSsl: boolean = false;
autoConnect: boolean = true;
index: number = 0;

constructor(private modalController: ModalController,
private alertController: AlertController) {
Expand All @@ -35,7 +37,8 @@ export class AddConnectionComponent {
name: this.name === undefined || this.name.length === 0 ? this.host : this.name,
port: this.port,
ssl: this.useSsl,
index: 0
index: this.index,
autoConnect: this.autoConnect
}
console.log(connection)
await this.modalController.dismiss(connection, 'confirm');
Expand Down
6 changes: 4 additions & 2 deletions src/app/services/connection/connection.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,16 @@ export class ConnectionService {
if (connection.id === undefined) {
connection.id = `connection${Math.floor(Date.now() / 1000)}`;
connection.index = connectionsObject.length;
connectionsObject.push(connection);
} else {
const existingIndex = connectionsObject.findIndex((x: Connection) => x.id === connection.id);
if (existingIndex > -1) {
connectionsObject.splice(existingIndex, 1);
connectionsObject[existingIndex] = connection;
} else {
connectionsObject.push(connection);
}
}

connectionsObject.push(connection);
await this.saveConnections(connectionsObject);
}

Expand Down
16 changes: 16 additions & 0 deletions src/app/services/ping/ping.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';

import { PingService } from './ping.service';

describe('PingService', () => {
let service: PingService;

beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(PingService);
});

it('should be created', () => {
expect(service).toBeTruthy();
});
});
79 changes: 79 additions & 0 deletions src/app/services/ping/ping.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import {EventEmitter, Injectable} from '@angular/core';
import {HttpClient} from "@angular/common/http";
import {catchError, interval, mergeMap, Observable, of, retry, Subscription, switchMap, timeout} from "rxjs";
import {PingResponse} from "../../datatypes/ping-response";
import {ConnectionService} from "../connection/connection.service";
import {Connection} from "../../datatypes/connection";

@Injectable({
providedIn: 'root'
})
export class PingService {
public connectionAvailable: EventEmitter<string> = new EventEmitter();

public connectionUnavailable: EventEmitter<string> = new EventEmitter();

private subscription: Subscription = new Subscription();

private availableConnections: string[] = [];

constructor(private http: HttpClient,
private connectionService: ConnectionService) { }

async start() {
console.log("Start ping");
this.subscription = new Subscription();
let connections = await this.connectionService.getConnections();
for (const connection of connections) {
this.subscription.add(this.periodicRequest(this.getPingUrl(connection), 1000).subscribe(response => {
if (response !== null) {
this.addAvailableConnection(connection);
} else {
this.removeAvailableConnection(connection);
}
}));
}
}

stop() {
console.log("Stop ping");
this.subscription.unsubscribe();
}

async restart() {
this.stop();
await this.start();
}

private getPingUrl(connection: Connection) {
return `${connection.ssl ? "https" : "http"}://${connection.host}:${connection.port}/ping`;
}

private removeAvailableConnection(connection: Connection) {
let existingConnectionIndex = this.availableConnections.findIndex(x => x == connection.id);
if (existingConnectionIndex !== -1) {
this.availableConnections.splice(existingConnectionIndex, 1);
this.connectionUnavailable.emit(connection.id);
}
}

private addAvailableConnection(connection: Connection) {
if (this.availableConnections.find(x => x == connection.id)) {
return;
}
this.availableConnections.push(connection.id);
this.connectionAvailable.emit(connection.id);
}

private periodicRequest(url: string, intervalTime: number): Observable<any> {
return interval(intervalTime).pipe(
switchMap(() => this.http.get(url).pipe(
timeout(500),
catchError(error => {
return of(null);
})
))
);
}

}
5 changes: 5 additions & 0 deletions src/app/services/websocket/websocket.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ export class WebsocketService {
private connectionOpened: Subject<any> = new Subject<any>();

public connectionLost: EventEmitter<any> = new EventEmitter<any>();
public connected: EventEmitter<any> = new EventEmitter<any>();
public closed: EventEmitter<any> = new EventEmitter<any>();

private subscription: Subscription = new Subscription();

Expand Down Expand Up @@ -67,6 +69,7 @@ export class WebsocketService {
},
error: async error => {
await this.loadingService.dismiss();
this.closed.emit();
if (error instanceof DOMException) {
switch (error.name) {
case "SecurityError":
Expand All @@ -88,6 +91,7 @@ export class WebsocketService {
console.info("Closed with code " + closeEvent.code);
await this.loadingService.dismiss();
this.subscription.unsubscribe();
this.closed.emit();

if (!this.closing) {
await this.handleError(closeEvent);
Expand All @@ -98,6 +102,7 @@ export class WebsocketService {
});

this.connectionOpened.subscribe(async () => {
this.connected.emit();
await this.settingsService.increaseConnectionCount();
await this.settingsService.setLastConnection(this.connectionId ?? "");
this.protocolHandlerService.setWebsocketSubject(this.socket!);
Expand Down

0 comments on commit 5c515c6

Please sign in to comment.