Drew cleanup (#380)

* accordion works

* cleanup

* styling

* more styling

* App show change (#387)

* show page change

* no marketplace

* app show changes

* update marketplace list

* icon

* top left icon

* toolbar

* right size

* out of toolbar

* no service details

* fix skeleton text and server metrics page

* stuck

* add session management

* complete sessions feature

* app show page

* remove unnecessary icons

* add cli to list of possible sessions

* Modal global (#383)

* modal checkpoint

* global modal

* black looks good now

* black looks good now

* not smaller

Co-authored-by: Drew Ansbacher <drew.ansbacher@spiredigital.com>

Co-authored-by: Drew Ansbacher <drew.ansbacher@spiredigital.com>
Co-authored-by: Drew Ansbacher <drew.ansbacher@gmail.com>

Co-authored-by: Drew Ansbacher <drew.ansbacher@spiredigital.com>
Co-authored-by: Matt Hill <matthewonthemoon@gmail.com>
Co-authored-by: Matt Hill <MattDHill@users.noreply.github.com>
This commit is contained in:
Drew Ansbacher
2021-07-29 16:59:03 -04:00
committed by Aiden McClelland
parent 4c294566d7
commit a43ff976a2
71 changed files with 808 additions and 694 deletions

View File

@@ -38,6 +38,9 @@
<ion-item detail="true" button [routerLink]="['ssh-keys']">
<ion-label>SSH Keys</ion-label>
</ion-item>
<ion-item detail="true" button [routerLink]="['sessions']">
<ion-label>Active Sessions</ion-label>
</ion-item>
</ion-item-group>
</ion-content>

View File

@@ -6,6 +6,10 @@ const routes: Routes = [
path: '',
loadChildren: () => import('./security-options/security-options.module').then(m => m.SecurityOptionsPageModule),
},
{
path: 'sessions',
loadChildren: () => import('./sessions/sessions.module').then(m => m.SessionsPageModule),
},
{
path: 'ssh-keys',
loadChildren: () => import('./ssh-keys/ssh-keys.module').then(m => m.SSHKeysPageModule),

View File

@@ -0,0 +1,28 @@
import { NgModule } from '@angular/core'
import { CommonModule } from '@angular/common'
import { IonicModule } from '@ionic/angular'
import { RouterModule, Routes } from '@angular/router'
import { SessionsPage } from './sessions.page'
import { PwaBackComponentModule } from 'src/app/components/pwa-back-button/pwa-back.component.module'
import { SharingModule } from 'src/app/modules/sharing.module'
import { TextSpinnerComponentModule } from 'src/app/components/text-spinner/text-spinner.component.module'
const routes: Routes = [
{
path: '',
component: SessionsPage,
},
]
@NgModule({
imports: [
CommonModule,
IonicModule,
RouterModule.forChild(routes),
PwaBackComponentModule,
SharingModule,
TextSpinnerComponentModule,
],
declarations: [SessionsPage],
})
export class SessionsPageModule { }

View File

@@ -0,0 +1,41 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<pwa-back-button></pwa-back-button>
</ion-buttons>
<ion-title>Active Sessions</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding-top">
<text-spinner *ngIf="loading" text="Loading Sessions"></text-spinner>
<ion-item-group *ngIf="!loading">
<ion-item-divider>Current Session</ion-item-divider>
<ion-item *ngIf="sessionInfo.sessions[sessionInfo.current] as current">
<ion-icon slot="start" [name]="getPlatformIcon(current.metadata.platforms)"></ion-icon>
<ion-label class="ion-text-wrap">
<h1>{{ getPlatformName(current.metadata.platforms) }}</h1>
<h2>Last Active: {{ current['last-active'] | date : 'medium' }}</h2>
<p>{{ current['user-agent'] }}</p>
</ion-label>
</ion-item>
<ion-item-divider>Other Sessions</ion-item-divider>
<div *ngFor="let session of sessionInfo.sessions | keyvalue : asIsOrder">
<ion-item *ngIf="session.key !== sessionInfo.current">
<ion-icon slot="start" [name]="getPlatformIcon(session.value.metadata.platforms)"></ion-icon>
<ion-label class="ion-text-wrap">
<h1>{{ getPlatformName(session.value.metadata.platforms) }}</h1>
<h2>Last Active: {{ session.value['last-active'] | date : 'medium' }}</h2>
<p>{{ session.value['user-agent'] }}</p>
</ion-label>
<ion-button slot="end" fill="clear" (click)="presentAlertKill(session.key)">
<ion-icon slot="icon-only" name="close-outline"></ion-icon>
</ion-button>
</ion-item>
</div>
</ion-item-group>
</ion-content>

View File

@@ -0,0 +1,99 @@
import { Component } from '@angular/core'
import { AlertController, getPlatforms, LoadingController } from '@ionic/angular'
import { ErrorToastService } from 'src/app/services/error-toast.service'
import { ApiService } from 'src/app/services/api/embassy/embassy-api.service'
import { PlatformType, RR, SessionMetadata } from 'src/app/services/api/api.types'
@Component({
selector: 'sessions',
templateUrl: 'sessions.page.html',
styleUrls: ['sessions.page.scss'],
})
export class SessionsPage {
loading = true
sessionInfo: RR.GetSessionsRes
constructor (
private readonly loadingCtrl: LoadingController,
private readonly errToast: ErrorToastService,
private readonly alertCtrl: AlertController,
private readonly embassyApi: ApiService,
) { }
async ngOnInit () {
getPlatforms()
this.sessionInfo = await this.embassyApi.getSessions({ })
this.loading = false
}
async presentAlertKill (hash: string) {
const alert = await this.alertCtrl.create({
backdropDismiss: false,
header: 'Caution',
message: `Are you sure you want to kill this session?`,
buttons: [
{
text: 'Cancel',
role: 'cancel',
},
{
text: 'Kill',
handler: () => {
this.kill(hash)
},
},
],
})
await alert.present()
}
async kill (hash: string): Promise<void> {
const loader = await this.loadingCtrl.create({
spinner: 'lines',
message: 'Killing session...',
cssClass: 'loader',
})
await loader.present()
try {
await this.embassyApi.killSessions({ hashes: [hash] })
delete this.sessionInfo.sessions[hash]
} catch (e) {
this.errToast.present(e)
} finally {
loader.dismiss()
}
}
getPlatformIcon (platforms: PlatformType[]): string {
if (platforms.includes('cli')) {
return 'terminal-outline'
} else if (platforms.includes('desktop')) {
return 'desktop-outline'
} else {
return 'phone-portrait-outline'
}
}
getPlatformName (platforms: PlatformType[]): string {
if (platforms.includes('cli')) {
return 'CLI'
} else if (platforms.includes('desktop')) {
return 'Desktop/Laptop'
} else if (platforms.includes('android')) {
return 'Android Device'
} else if (platforms.includes('iphone')) {
return 'iPhone'
} else if (platforms.includes('ipad')) {
return 'iPad'
} else if (platforms.includes('ios')) {
return 'iOS Device'
} else {
return 'Unknown Device'
}
}
asIsOrder (a: any, b: any) {
return 0
}
}

View File

@@ -22,7 +22,7 @@
{{ ssh.value.alg }} {{ ssh.key }} {{ ssh.value.hostname }}
</ion-label>
<ion-button slot="end" fill="clear" (click)="presentAlertDelete(ssh.key)">
<ion-icon slot="icon-only" name="close-outline" color="medium"></ion-icon>
<ion-icon slot="icon-only" name="close-outline"></ion-icon>
</ion-button>
</ion-item>
</ion-item-group>

View File

@@ -57,7 +57,6 @@ export class SSHKeysPage {
},
{
text: 'Delete',
cssClass: 'alert-danger',
handler: () => {
this.delete(hash)
},

View File

@@ -12,7 +12,7 @@
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding" color="light">
<ion-content class="ion-padding">
<text-spinner *ngIf="loading; else loaded" text="Loading Logs"></text-spinner>
<ng-template #loaded>

View File

@@ -7,16 +7,14 @@
</ion-toolbar>
</ion-header>
<ion-content>
<ion-content class="ion-padding">
<skeleton-list *ngIf="loading; else loaded" groups="3"></skeleton-list>
<ng-template #loaded>
<ion-item-group *ngFor="let metricGroup of metrics | keyvalue : asIsOrder">
<ion-item-divider class="divider">{{ metricGroup.key }}</ion-item-divider>
<ion-item-divider>{{ metricGroup.key }}</ion-item-divider>
<ion-item *ngFor="let metric of metricGroup.value | keyvalue : asIsOrder">
<ion-label>
<ion-text color="medium">{{ metric.key }}</ion-text>
</ion-label>
<ion-label>{{ metric.key }}</ion-label>
<ion-note *ngIf="metric.value" slot="end" class="metric-note">
<ion-text style="color: white;">{{ metric.value.value }} {{ metric.value.unit }}</ion-text>
</ion-note>

View File

@@ -1,3 +1,3 @@
.metric-note {
font-size: 16px;
}
}

View File

@@ -7,12 +7,11 @@
</ion-toolbar>
</ion-header>
<ion-content>
<ion-content class="ion-padding">
<ion-item-group>
<div *ngFor="let cat of settings | keyvalue : asIsOrder">
<ion-item-divider>{{ cat.key }}</ion-item-divider>
<ion-item style="cursor: pointer;" button *ngFor="let button of cat.value" (click)="button.action()">
<ion-item-divider><ion-text color="dark">{{ cat.key }}</ion-text></ion-item-divider>
<ion-item [detail]="button.detail" button *ngFor="let button of cat.value" (click)="button.action()">
<ion-icon slot="start" [name]="button.icon"></ion-icon>
<ion-label>{{ button.title }}</ion-label>
</ion-item>

View File

@@ -37,7 +37,6 @@ export class ServerShowPage {
},
{
text: 'Restart',
cssClass: 'alert-danger',
handler: () => {
this.restart()
},
@@ -59,7 +58,6 @@ export class ServerShowPage {
},
{
text: 'Shutdown',
cssClass: 'alert-danger',
handler: () => {
this.shutdown()
},
@@ -110,16 +108,19 @@ export class ServerShowPage {
title: 'Privacy and Security',
icon: 'shield-checkmark-outline',
action: () => this.navCtrl.navigateForward(['security'], { relativeTo: this.route }),
detail: true,
},
{
title: 'LAN',
icon: 'home-outline',
action: () => this.navCtrl.navigateForward(['lan'], { relativeTo: this.route }),
detail: true,
},
{
title: 'WiFi',
icon: 'wifi',
action: () => this.navCtrl.navigateForward(['wifi'], { relativeTo: this.route }),
detail: true,
},
],
'Insights': [
@@ -127,16 +128,19 @@ export class ServerShowPage {
title: 'About',
icon: 'information-circle-outline',
action: () => this.navCtrl.navigateForward(['specs'], { relativeTo: this.route }),
detail: true,
},
{
title: 'Monitor',
icon: 'pulse',
action: () => this.navCtrl.navigateForward(['metrics'], { relativeTo: this.route }),
detail: true,
},
{
title: 'Logs',
icon: 'newspaper-outline',
action: () => this.navCtrl.navigateForward(['logs'], { relativeTo: this.route }),
detail: true,
},
],
'Backups': [
@@ -144,6 +148,7 @@ export class ServerShowPage {
title: 'Create Backup',
icon: 'save-outline',
action: () => this.navCtrl.navigateForward(['backup'], { relativeTo: this.route }),
detail: true,
},
],
'Power': [
@@ -151,11 +156,13 @@ export class ServerShowPage {
title: 'Restart',
icon: 'reload-outline',
action: () => this.presentAlertRestart(),
detail: false,
},
{
title: 'Shutdown',
icon: 'power',
action: () => this.presentAlertShutdown(),
detail: false,
},
],
}
@@ -171,5 +178,6 @@ interface ServerSettings {
title: string
icon: string
action: Function
detail: boolean
}[]
}

View File

@@ -14,7 +14,7 @@
<ion-item-group *ngIf="patch.data['server-info'] as server">
<ion-item>
<ion-label>
<h2>Version</h2>
<h2>EmbassyOS Version</h2>
<p>{{ server.version | displayEmver }}</p>
</ion-label>
</ion-item>

View File

@@ -29,7 +29,6 @@ export class WifiListPage {
const buttons: ActionSheetButton[] = [
{
text: 'Forget',
cssClass: 'alert-danger',
handler: () => {
this.delete(ssid)
},