mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 02:11:53 +00:00
kill all sessions and remove ripple effect (#1567)
* button to kill all sessions, session sorting, remove ripple effect from buttons * pr cleanup Co-authored-by: Matt Hill <matthill@Matt-M1.local> Co-authored-by: Lucy Cifferello <12953208+elvece@users.noreply.github.com>
This commit is contained in:
@@ -11,7 +11,7 @@
|
||||
<ion-item-group>
|
||||
<ion-item-divider>Saved Marketplaces</ion-item-divider>
|
||||
<ion-item button detail="false" (click)="presentModalAdd()">
|
||||
<ion-icon slot="start" name="add" size="large" color="dark"></ion-icon>
|
||||
<ion-icon slot="start" name="add" color="dark"></ion-icon>
|
||||
<ion-label>
|
||||
<ion-text color="dark">
|
||||
<b>Add alt marketplace</b>
|
||||
@@ -37,9 +37,6 @@
|
||||
<h2>{{ mp.name }}</h2>
|
||||
<p>{{ mp.url }}</p>
|
||||
</ion-label>
|
||||
<ion-note *ngIf="mp.id === selectedId" slot="end">
|
||||
<ion-text color="success">Selected</ion-text>
|
||||
</ion-note>
|
||||
</ion-item>
|
||||
</ion-item-group>
|
||||
</ion-content>
|
||||
|
||||
@@ -328,7 +328,7 @@ export class ServerShowPage {
|
||||
},
|
||||
{
|
||||
title: 'Sideload Service',
|
||||
description: `Manually install any service package`,
|
||||
description: `Manually install a service`,
|
||||
icon: 'push-outline',
|
||||
action: () =>
|
||||
this.navCtrl.navigateForward(['sideload'], {
|
||||
|
||||
@@ -8,22 +8,33 @@
|
||||
</ion-header>
|
||||
|
||||
<ion-content class="ion-padding-top">
|
||||
|
||||
<!-- loading -->
|
||||
<ion-item-group *ngIf="loading">
|
||||
<div *ngFor="let entry of ['This Session', 'Other Sessions']">
|
||||
<div *ngFor="let entry of ['This Session', 'Other Sessions']">
|
||||
<ion-item-divider>{{ entry }}</ion-item-divider>
|
||||
<ion-item style="padding-bottom: 6px;">
|
||||
<ion-avatar slot="start" style="margin-right: 30px;">
|
||||
<ion-skeleton-text animated style="width: 40px; height: 40px; border-radius: 0;"></ion-skeleton-text>
|
||||
<ion-item style="padding-bottom: 6px">
|
||||
<ion-avatar slot="start" style="margin-right: 30px">
|
||||
<ion-skeleton-text
|
||||
animated
|
||||
style="width: 40px; height: 40px; border-radius: 0"
|
||||
></ion-skeleton-text>
|
||||
</ion-avatar>
|
||||
<ion-label>
|
||||
<ion-skeleton-text animated style="width: 150px; height: 20px; margin-bottom: 10px;"></ion-skeleton-text>
|
||||
<ion-skeleton-text animated style="width: 250px; height: 14px; margin-bottom: 12px;"></ion-skeleton-text>
|
||||
<ion-skeleton-text animated style="width: 350px;"></ion-skeleton-text>
|
||||
<ion-skeleton-text
|
||||
animated
|
||||
style="width: 150px; height: 20px; margin-bottom: 10px"
|
||||
></ion-skeleton-text>
|
||||
<ion-skeleton-text
|
||||
animated
|
||||
style="width: 250px; height: 14px; margin-bottom: 12px"
|
||||
></ion-skeleton-text>
|
||||
<ion-skeleton-text animated style="width: 350px"></ion-skeleton-text>
|
||||
</ion-label>
|
||||
<ion-button *ngIf="entry === 'second'" slot="end" fill="clear">
|
||||
<ion-skeleton-text animated style="width: 60px; border-radius: 0"></ion-skeleton-text>
|
||||
<ion-skeleton-text
|
||||
animated
|
||||
style="width: 60px; border-radius: 0"
|
||||
></ion-skeleton-text>
|
||||
</ion-button>
|
||||
</ion-item>
|
||||
</div>
|
||||
@@ -31,35 +42,61 @@
|
||||
|
||||
<!-- not loading -->
|
||||
<ion-item-group *ngIf="!loading">
|
||||
|
||||
<ion-item-divider>This Session</ion-item-divider>
|
||||
<ion-item *ngIf="sessionInfo.sessions[sessionInfo.current] as current">
|
||||
<ion-icon slot="start" size="large" [name]="getPlatformIcon(current.metadata.platforms)"></ion-icon>
|
||||
<ion-item-divider>Current Session</ion-item-divider>
|
||||
<ion-item>
|
||||
<ion-icon
|
||||
slot="start"
|
||||
size="large"
|
||||
[name]="getPlatformIcon(currentSession.metadata.platforms)"
|
||||
></ion-icon>
|
||||
<ion-label>
|
||||
<h1>{{ getPlatformName(current.metadata.platforms) }}</h1>
|
||||
<h2>Last Active: {{ current['last-active'] | date : 'medium' }}</h2>
|
||||
<p>{{ current['user-agent'] }}</p>
|
||||
<h1>{{ getPlatformName(currentSession.metadata.platforms) }}</h1>
|
||||
<h2>
|
||||
Last Active: {{ currentSession['last-active'] | date : 'medium' }}
|
||||
</h2>
|
||||
<p>{{ currentSession['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
|
||||
[id]="session.key"
|
||||
*ngIf="session.key !== sessionInfo.current"
|
||||
<ion-item-divider>
|
||||
Other Sessions
|
||||
<ion-button
|
||||
*ngIf="otherSessions.length"
|
||||
slot="end"
|
||||
fill="clear"
|
||||
color="danger"
|
||||
strong
|
||||
(click)="presentAlertKillAll()"
|
||||
>
|
||||
<ion-icon slot="start" size="large" [name]="getPlatformIcon(session.value.metadata.platforms)"></ion-icon>
|
||||
Kill All
|
||||
</ion-button>
|
||||
</ion-item-divider>
|
||||
<div *ngFor="let session of otherSessions">
|
||||
<ion-item>
|
||||
<ion-icon
|
||||
slot="start"
|
||||
size="large"
|
||||
[name]="getPlatformIcon(session.metadata.platforms)"
|
||||
></ion-icon>
|
||||
<ion-label>
|
||||
<h1>{{ getPlatformName(session.value.metadata.platforms) }}</h1>
|
||||
<h2>Last Active: {{ session.value['last-active'] | date : 'medium' }}</h2>
|
||||
<p>{{ session.value['user-agent'] }}</p>
|
||||
<h1>{{ getPlatformName(session.metadata.platforms) }}</h1>
|
||||
<h2>Last Active: {{ session['last-active'] | date : 'medium' }}</h2>
|
||||
<p>{{ session['user-agent'] }}</p>
|
||||
</ion-label>
|
||||
<ion-button slot="end" fill="clear" color="danger" (click)="presentAlertKill(session.key)">
|
||||
<ion-icon slot="start" name="close"></ion-icon>
|
||||
Kill
|
||||
<ion-button
|
||||
slot="end"
|
||||
fill="clear"
|
||||
color="danger"
|
||||
(click)="presentAlertKill(session.id)"
|
||||
>
|
||||
<ion-icon slot="icon-only" name="close"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-item>
|
||||
</div>
|
||||
<ion-item *ngIf="!otherSessions.length">
|
||||
<ion-label>
|
||||
<p>You are not logged in anywhere else</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-item-group>
|
||||
|
||||
</ion-content>
|
||||
</ion-content>
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { AlertController, LoadingController } from '@ionic/angular'
|
||||
import {
|
||||
AlertController,
|
||||
IonicSafeString,
|
||||
LoadingController,
|
||||
} from '@ionic/angular'
|
||||
import { ErrorToastService } from '@start9labs/shared'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import { PlatformType, RR } from 'src/app/services/api/api.types'
|
||||
import { PlatformType, Session } from 'src/app/services/api/api.types'
|
||||
|
||||
@Component({
|
||||
selector: 'sessions',
|
||||
@@ -11,7 +15,8 @@ import { PlatformType, RR } from 'src/app/services/api/api.types'
|
||||
})
|
||||
export class SessionsPage {
|
||||
loading = true
|
||||
sessionInfo: RR.GetSessionsRes
|
||||
currentSession: Session
|
||||
otherSessions: SessionWithId[] = []
|
||||
|
||||
constructor(
|
||||
private readonly loadingCtrl: LoadingController,
|
||||
@@ -22,7 +27,22 @@ export class SessionsPage {
|
||||
|
||||
async ngOnInit() {
|
||||
try {
|
||||
this.sessionInfo = await this.embassyApi.getSessions({})
|
||||
const sessionInfo = await this.embassyApi.getSessions({})
|
||||
this.currentSession = sessionInfo.sessions[sessionInfo.current]
|
||||
delete sessionInfo.sessions[sessionInfo.current]
|
||||
this.otherSessions = Object.entries(sessionInfo.sessions)
|
||||
.map(([id, session]) => {
|
||||
return {
|
||||
id,
|
||||
...session,
|
||||
}
|
||||
})
|
||||
.sort((a, b) => {
|
||||
return (
|
||||
new Date(b['last-active']).valueOf() -
|
||||
new Date(a['last-active']).valueOf()
|
||||
)
|
||||
})
|
||||
} catch (e: any) {
|
||||
this.errToast.present(e)
|
||||
} finally {
|
||||
@@ -30,19 +50,21 @@ export class SessionsPage {
|
||||
}
|
||||
}
|
||||
|
||||
async presentAlertKill(id: string) {
|
||||
async presentAlertKillAll() {
|
||||
const alert = await this.alertCtrl.create({
|
||||
header: 'Caution',
|
||||
message: `Are you sure you want to kill this session?`,
|
||||
header: 'Confirm',
|
||||
message: new IonicSafeString(
|
||||
`Kill all sessions?<br /><br />Note: you will <b>not</b> be logged out of your current session on this device.`,
|
||||
),
|
||||
buttons: [
|
||||
{
|
||||
text: 'Cancel',
|
||||
role: 'cancel',
|
||||
},
|
||||
{
|
||||
text: 'Kill',
|
||||
text: 'Kill All',
|
||||
handler: () => {
|
||||
this.kill(id)
|
||||
this.kill(this.otherSessions.map(s => s.id))
|
||||
},
|
||||
cssClass: 'enter-click',
|
||||
},
|
||||
@@ -51,15 +73,36 @@ export class SessionsPage {
|
||||
await alert.present()
|
||||
}
|
||||
|
||||
async kill(id: string): Promise<void> {
|
||||
async presentAlertKill(id: string) {
|
||||
const alert = await this.alertCtrl.create({
|
||||
header: 'Confirm',
|
||||
message: `Kill this session?`,
|
||||
buttons: [
|
||||
{
|
||||
text: 'Cancel',
|
||||
role: 'cancel',
|
||||
},
|
||||
{
|
||||
text: 'Kill',
|
||||
handler: () => {
|
||||
this.kill([id])
|
||||
},
|
||||
cssClass: 'enter-click',
|
||||
},
|
||||
],
|
||||
})
|
||||
await alert.present()
|
||||
}
|
||||
|
||||
async kill(ids: string[]): Promise<void> {
|
||||
const loader = await this.loadingCtrl.create({
|
||||
message: 'Killing session...',
|
||||
})
|
||||
await loader.present()
|
||||
|
||||
try {
|
||||
await this.embassyApi.killSessions({ ids: [id] })
|
||||
delete this.sessionInfo.sessions[id]
|
||||
await this.embassyApi.killSessions({ ids })
|
||||
this.otherSessions = this.otherSessions.filter(s => !ids.includes(s.id))
|
||||
} catch (e: any) {
|
||||
this.errToast.present(e)
|
||||
} finally {
|
||||
@@ -99,3 +142,7 @@ export class SessionsPage {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
interface SessionWithId extends Session {
|
||||
id: string
|
||||
}
|
||||
|
||||
@@ -9,13 +9,15 @@
|
||||
|
||||
<ion-content class="ion-padding-top">
|
||||
<ion-item-group>
|
||||
|
||||
<!-- always -->
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
<h2>
|
||||
Adding SSH keys to your Embassy is useful for command line access, as well as for debugging purposes.
|
||||
<a [href]="docsUrl" target="_blank" rel="noreferrer">View instructions</a>
|
||||
Adding SSH keys to your Embassy is useful for command line access, as
|
||||
well as for debugging purposes.
|
||||
<a [href]="docsUrl" target="_blank" rel="noreferrer"
|
||||
>View instructions</a
|
||||
>
|
||||
</h2>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
@@ -23,23 +25,37 @@
|
||||
<ion-item-divider>Saved Keys</ion-item-divider>
|
||||
|
||||
<ion-item button detail="false" (click)="presentModalAdd()">
|
||||
<ion-icon slot="start" name="add" size="large"></ion-icon>
|
||||
<ion-label>Add new key</ion-label>
|
||||
<ion-icon slot="start" name="add" color="dark"></ion-icon>
|
||||
<ion-label>
|
||||
<b>Add new key</b>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<!-- loading -->
|
||||
<ng-container *ngIf="loading">
|
||||
<ion-item *ngFor="let entry of ['', '']">
|
||||
<ion-avatar slot="start" style="margin-right: 30px;">
|
||||
<ion-skeleton-text animated style="width: 30px; height: 30px; border-radius: 0;"></ion-skeleton-text>
|
||||
<ion-avatar slot="start" style="margin-right: 30px">
|
||||
<ion-skeleton-text
|
||||
animated
|
||||
style="width: 30px; height: 30px; border-radius: 0"
|
||||
></ion-skeleton-text>
|
||||
</ion-avatar>
|
||||
<ion-label>
|
||||
<ion-skeleton-text animated style="width: 100px; height: 20px; margin-bottom: 12px;"></ion-skeleton-text>
|
||||
<ion-skeleton-text animated style="width: 150px; margin-bottom: 18px;"></ion-skeleton-text>
|
||||
<ion-skeleton-text animated style="width: 300px;"></ion-skeleton-text>
|
||||
<ion-skeleton-text
|
||||
animated
|
||||
style="width: 100px; height: 20px; margin-bottom: 12px"
|
||||
></ion-skeleton-text>
|
||||
<ion-skeleton-text
|
||||
animated
|
||||
style="width: 150px; margin-bottom: 18px"
|
||||
></ion-skeleton-text>
|
||||
<ion-skeleton-text animated style="width: 300px"></ion-skeleton-text>
|
||||
</ion-label>
|
||||
<ion-button slot="end" fill="clear">
|
||||
<ion-skeleton-text animated style="width: 60px; border-radius: 0"></ion-skeleton-text>
|
||||
<ion-skeleton-text
|
||||
animated
|
||||
style="width: 60px; border-radius: 0"
|
||||
></ion-skeleton-text>
|
||||
</ion-button>
|
||||
</ion-item>
|
||||
</ng-container>
|
||||
@@ -53,13 +69,16 @@
|
||||
<h2>{{ ssh['created-at'] | date: 'short' }}</h2>
|
||||
<p>{{ ssh.alg }} {{ ssh.fingerprint }}</p>
|
||||
</ion-label>
|
||||
<ion-button slot="end" fill="clear" color="danger" (click)="presentAlertDelete(i)">
|
||||
<ion-button
|
||||
slot="end"
|
||||
fill="clear"
|
||||
color="danger"
|
||||
(click)="presentAlertDelete(i)"
|
||||
>
|
||||
<ion-icon slot="start" name="close"></ion-icon>
|
||||
Remove
|
||||
</ion-button>
|
||||
</ion-item>
|
||||
</ng-container>
|
||||
|
||||
</ng-container>
|
||||
</ion-item-group>
|
||||
|
||||
</ion-content>
|
||||
</ion-content>
|
||||
|
||||
@@ -163,8 +163,10 @@
|
||||
</ion-item>
|
||||
</ng-container>
|
||||
<ion-item button detail="false" (click)="presentModalAdd()">
|
||||
<ion-icon slot="start" name="add" size="large"></ion-icon>
|
||||
<ion-label>Join other network</ion-label>
|
||||
<ion-icon slot="start" name="add" color="dark"></ion-icon>
|
||||
<ion-label>
|
||||
<b>Join another network</b>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ng-container>
|
||||
</ion-item-group>
|
||||
|
||||
@@ -247,6 +247,7 @@ ion-item-divider {
|
||||
|
||||
ion-item {
|
||||
border-radius: 6px;
|
||||
--ripple-color: transparent;
|
||||
}
|
||||
|
||||
ion-label {
|
||||
|
||||
Reference in New Issue
Block a user