mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 20:14:49 +00:00
use hostname from patchDB as default server name (#1758)
* replace offline toast with global indicator * use hostname from patchDB as default server name * add alert to marketplace delete and reword logout alert
This commit is contained in:
@@ -18,5 +18,10 @@
|
|||||||
<ion-footer>
|
<ion-footer>
|
||||||
<footer appFooter></footer>
|
<footer appFooter></footer>
|
||||||
</ion-footer>
|
</ion-footer>
|
||||||
|
<ion-footer
|
||||||
|
*ngIf="(authService.isVerified$ | async) && !(sidebarOpen$ | async)"
|
||||||
|
>
|
||||||
|
<connection-bar></connection-bar>
|
||||||
|
</ion-footer>
|
||||||
<toast-container></toast-container>
|
<toast-container></toast-container>
|
||||||
</ion-app>
|
</ion-app>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { PatchMonitorService } from './services/patch-monitor.service'
|
|||||||
})
|
})
|
||||||
export class AppComponent implements OnDestroy {
|
export class AppComponent implements OnDestroy {
|
||||||
readonly subscription = merge(this.patchData, this.patchMonitor).subscribe()
|
readonly subscription = merge(this.patchData, this.patchMonitor).subscribe()
|
||||||
|
readonly sidebarOpen$ = this.splitPane.sidebarOpen$
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly patchData: PatchDataService,
|
private readonly patchData: PatchDataService,
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import { EnterModule } from './app/enter/enter.module'
|
|||||||
import { APP_PROVIDERS } from './app.providers'
|
import { APP_PROVIDERS } from './app.providers'
|
||||||
import { PatchDbModule } from './services/patch-db/patch-db.module'
|
import { PatchDbModule } from './services/patch-db/patch-db.module'
|
||||||
import { ToastContainerModule } from './components/toast-container/toast-container.module'
|
import { ToastContainerModule } from './components/toast-container/toast-container.module'
|
||||||
|
import { ConnectionBarComponentModule } from './components/connection-bar/connection-bar.component.module'
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [AppComponent],
|
declarations: [AppComponent],
|
||||||
@@ -47,6 +48,7 @@ import { ToastContainerModule } from './components/toast-container/toast-contain
|
|||||||
MarketplaceModule,
|
MarketplaceModule,
|
||||||
PatchDbModule,
|
PatchDbModule,
|
||||||
ToastContainerModule,
|
ToastContainerModule,
|
||||||
|
ConnectionBarComponentModule,
|
||||||
],
|
],
|
||||||
providers: APP_PROVIDERS,
|
providers: APP_PROVIDERS,
|
||||||
bootstrap: [AppComponent],
|
bootstrap: [AppComponent],
|
||||||
|
|||||||
@@ -49,6 +49,7 @@
|
|||||||
</ion-item>
|
</ion-item>
|
||||||
</ion-menu-toggle>
|
</ion-menu-toggle>
|
||||||
</ion-item-group>
|
</ion-item-group>
|
||||||
|
|
||||||
<img
|
<img
|
||||||
appSnek
|
appSnek
|
||||||
class="snek"
|
class="snek"
|
||||||
@@ -56,21 +57,6 @@
|
|||||||
src="assets/img/icons/snek.png"
|
src="assets/img/icons/snek.png"
|
||||||
[appSnekHighScore]="snekScore$ | async"
|
[appSnekHighScore]="snekScore$ | async"
|
||||||
/>
|
/>
|
||||||
<div class="bottom">
|
<ion-footer class="bottom">
|
||||||
<div class="divider" style="margin-bottom: 10px"></div>
|
<connection-bar></connection-bar>
|
||||||
<ion-menu-toggle auto-hide="false">
|
</ion-footer>
|
||||||
<ion-item
|
|
||||||
button
|
|
||||||
lines="none"
|
|
||||||
style="--background: transparent; margin-bottom: 86px; text-align: center"
|
|
||||||
fill="clear"
|
|
||||||
(click)="presentAlertLogout()"
|
|
||||||
>
|
|
||||||
<ion-label class="inline">
|
|
||||||
<h2>Log Out</h2>
|
|
||||||
|
|
||||||
<ion-icon name="log-out-outline"></ion-icon>
|
|
||||||
</ion-label>
|
|
||||||
</ion-item>
|
|
||||||
</ion-menu-toggle>
|
|
||||||
</div>
|
|
||||||
|
|||||||
@@ -27,8 +27,8 @@
|
|||||||
|
|
||||||
.snek {
|
.snek {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 90px;
|
bottom: 56px;
|
||||||
left: 20px;
|
right: 20px;
|
||||||
width: 20px;
|
width: 20px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
@@ -36,8 +36,4 @@
|
|||||||
.bottom {
|
.bottom {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
right: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 75px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core'
|
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core'
|
||||||
import { AlertController } from '@ionic/angular'
|
|
||||||
import { LocalStorageService } from '../../services/local-storage.service'
|
import { LocalStorageService } from '../../services/local-storage.service'
|
||||||
import { EOSService } from '../../services/eos.service'
|
import { EOSService } from '../../services/eos.service'
|
||||||
import { ApiService } from '../../services/api/embassy-api.service'
|
|
||||||
import { AuthService } from '../../services/auth.service'
|
|
||||||
import { PatchDbService } from '../../services/patch-db/patch-db.service'
|
import { PatchDbService } from '../../services/patch-db/patch-db.service'
|
||||||
import { Observable } from 'rxjs'
|
import { Observable } from 'rxjs'
|
||||||
import { map } from 'rxjs/operators'
|
import { map } from 'rxjs/operators'
|
||||||
@@ -61,40 +58,10 @@ export class MenuComponent {
|
|||||||
.pipe(map(pkgs => pkgs.length))
|
.pipe(map(pkgs => pkgs.length))
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly alertCtrl: AlertController,
|
|
||||||
private readonly embassyApi: ApiService,
|
|
||||||
private readonly authService: AuthService,
|
|
||||||
private readonly patch: PatchDbService,
|
private readonly patch: PatchDbService,
|
||||||
private readonly localStorageService: LocalStorageService,
|
private readonly localStorageService: LocalStorageService,
|
||||||
private readonly eosService: EOSService,
|
private readonly eosService: EOSService,
|
||||||
@Inject(AbstractMarketplaceService)
|
@Inject(AbstractMarketplaceService)
|
||||||
private readonly marketplaceService: MarketplaceService,
|
private readonly marketplaceService: MarketplaceService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async presentAlertLogout() {
|
|
||||||
const alert = await this.alertCtrl.create({
|
|
||||||
header: 'Caution',
|
|
||||||
message:
|
|
||||||
'Do you know your password? If you log out and forget your password, you may permanently lose access to your Embassy.',
|
|
||||||
buttons: [
|
|
||||||
{
|
|
||||||
text: 'Cancel',
|
|
||||||
role: 'cancel',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: 'Logout',
|
|
||||||
handler: () => this.logout(),
|
|
||||||
cssClass: 'enter-click',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
await alert.present()
|
|
||||||
}
|
|
||||||
|
|
||||||
// should wipe cache independent of actual BE logout
|
|
||||||
private logout() {
|
|
||||||
this.embassyApi.logout({}).catch(e => console.error('Failed to log out', e))
|
|
||||||
this.authService.setUnverified()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,16 @@ import { RouterModule } from '@angular/router'
|
|||||||
import { IonicModule } from '@ionic/angular'
|
import { IonicModule } from '@ionic/angular'
|
||||||
import { MenuComponent } from './menu.component'
|
import { MenuComponent } from './menu.component'
|
||||||
import { SnekModule } from '../snek/snek.module'
|
import { SnekModule } from '../snek/snek.module'
|
||||||
|
import { ConnectionBarComponentModule } from 'src/app/components/connection-bar/connection-bar.component.module'
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [CommonModule, IonicModule, RouterModule, SnekModule],
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
IonicModule,
|
||||||
|
RouterModule,
|
||||||
|
SnekModule,
|
||||||
|
ConnectionBarComponentModule,
|
||||||
|
],
|
||||||
declarations: [MenuComponent],
|
declarations: [MenuComponent],
|
||||||
exports: [MenuComponent],
|
exports: [MenuComponent],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ const ICONS = [
|
|||||||
'chevron-forward',
|
'chevron-forward',
|
||||||
'close',
|
'close',
|
||||||
'cloud-outline',
|
'cloud-outline',
|
||||||
|
'cloud-done',
|
||||||
'cloud-done-outline',
|
'cloud-done-outline',
|
||||||
'cloud-download-outline',
|
'cloud-download-outline',
|
||||||
'cloud-offline-outline',
|
'cloud-offline-outline',
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<ion-toolbar
|
||||||
|
*ngIf="connection$ | async as connection"
|
||||||
|
class="connection-toolbar"
|
||||||
|
[color]="connection.color"
|
||||||
|
>
|
||||||
|
<div class="inline" slot="start">
|
||||||
|
<ion-icon
|
||||||
|
slot="end"
|
||||||
|
[name]="connection.icon"
|
||||||
|
class="icon"
|
||||||
|
color="light"
|
||||||
|
></ion-icon>
|
||||||
|
<p style="margin: 8px 0; font-weight: 600">{{ connection.message }}</p>
|
||||||
|
<ion-spinner
|
||||||
|
*ngIf="connection.dots"
|
||||||
|
name="dots"
|
||||||
|
color="light"
|
||||||
|
class="ion-margin-start"
|
||||||
|
></ion-spinner>
|
||||||
|
</div>
|
||||||
|
</ion-toolbar>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { NgModule } from '@angular/core'
|
||||||
|
import { CommonModule } from '@angular/common'
|
||||||
|
import { IonicModule } from '@ionic/angular'
|
||||||
|
import { ConnectionBarComponent } from './connection-bar.component'
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [ConnectionBarComponent],
|
||||||
|
imports: [CommonModule, IonicModule],
|
||||||
|
exports: [ConnectionBarComponent],
|
||||||
|
})
|
||||||
|
export class ConnectionBarComponentModule {}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
.connection-toolbar {
|
||||||
|
padding: 0 24px;
|
||||||
|
--min-height: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
font-size: 23px;
|
||||||
|
padding-right: 12px;
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import { ChangeDetectionStrategy, Component } from '@angular/core'
|
||||||
|
import { combineLatest, map, Observable, startWith, tap } from 'rxjs'
|
||||||
|
import { ConnectionService } from 'src/app/services/connection.service'
|
||||||
|
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'connection-bar',
|
||||||
|
templateUrl: './connection-bar.component.html',
|
||||||
|
styleUrls: ['./connection-bar.component.scss'],
|
||||||
|
// changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
})
|
||||||
|
export class ConnectionBarComponent {
|
||||||
|
private readonly websocket$ = this.connectionService.websocketConnected$
|
||||||
|
|
||||||
|
readonly connection$: Observable<{
|
||||||
|
message: string
|
||||||
|
icon: string
|
||||||
|
color: string
|
||||||
|
dots: boolean
|
||||||
|
}> = combineLatest([
|
||||||
|
this.connectionService.networkConnected$,
|
||||||
|
this.websocket$,
|
||||||
|
]).pipe(
|
||||||
|
map(([network, websocket]) => {
|
||||||
|
if (!network)
|
||||||
|
return {
|
||||||
|
message: 'No Internet',
|
||||||
|
icon: 'cloud-offline-outline',
|
||||||
|
color: 'dark',
|
||||||
|
dots: false,
|
||||||
|
}
|
||||||
|
if (!websocket)
|
||||||
|
return {
|
||||||
|
message: 'Connecting',
|
||||||
|
icon: 'cloud-offline-outline',
|
||||||
|
color: 'warning',
|
||||||
|
dots: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
message: 'Connected',
|
||||||
|
icon: 'cloud-done',
|
||||||
|
color: 'success',
|
||||||
|
dots: false,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly connectionService: ConnectionService,
|
||||||
|
private readonly patch: PatchDbService,
|
||||||
|
) {}
|
||||||
|
}
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
<toast
|
|
||||||
*ngIf="content$ | async as content"
|
|
||||||
class="warning-toast"
|
|
||||||
header="Unable to Connect"
|
|
||||||
(dismiss)="onDismiss()"
|
|
||||||
>
|
|
||||||
{{ content.message }}
|
|
||||||
<button toastButton icon="close" side="start" (click)="onDismiss()"></button>
|
|
||||||
<a
|
|
||||||
*ngIf="content.link"
|
|
||||||
toastButton
|
|
||||||
side="end"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
[href]="content.link"
|
|
||||||
>
|
|
||||||
View solutions
|
|
||||||
</a>
|
|
||||||
</toast>
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core'
|
|
||||||
import { Observable, Subject, merge, tap, map } from 'rxjs'
|
|
||||||
|
|
||||||
import { OfflineMessage, OfflineToastService } from './offline-toast.service'
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'offline-toast',
|
|
||||||
templateUrl: './offline-toast.component.html',
|
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
||||||
})
|
|
||||||
export class OfflineToastComponent {
|
|
||||||
private readonly dismiss$ = new Subject<null>()
|
|
||||||
|
|
||||||
readonly content$ = merge(this.dismiss$, this.failure$)
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
@Inject(OfflineToastService)
|
|
||||||
private readonly failure$: Observable<OfflineMessage | null>,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
onDismiss() {
|
|
||||||
this.dismiss$.next(null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
import { Injectable } from '@angular/core'
|
|
||||||
import { combineLatest, Observable, of } from 'rxjs'
|
|
||||||
import { map, switchMap } from 'rxjs/operators'
|
|
||||||
import { AuthService } from 'src/app/services/auth.service'
|
|
||||||
import { ConnectionService } from 'src/app/services/connection.service'
|
|
||||||
|
|
||||||
export interface OfflineMessage {
|
|
||||||
readonly message: string
|
|
||||||
readonly link?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Watch for connection status
|
|
||||||
@Injectable({ providedIn: 'root' })
|
|
||||||
export class OfflineToastService extends Observable<OfflineMessage | null> {
|
|
||||||
private readonly stream$ = this.authService.isVerified$.pipe(
|
|
||||||
switchMap(verified => (verified ? this.failure$ : of(null))),
|
|
||||||
)
|
|
||||||
|
|
||||||
private readonly failure$ = combineLatest([
|
|
||||||
this.connectionService.networkConnected$,
|
|
||||||
this.connectionService.websocketConnected$,
|
|
||||||
]).pipe(
|
|
||||||
map(([network, websocket]) => {
|
|
||||||
if (!network) return { message: 'No Internet' }
|
|
||||||
if (!websocket)
|
|
||||||
return {
|
|
||||||
message: 'Connecting to Embassy...',
|
|
||||||
link: 'https://start9.com/latest/support/common-issues',
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private readonly authService: AuthService,
|
|
||||||
private readonly connectionService: ConnectionService,
|
|
||||||
) {
|
|
||||||
super(subscriber => this.stream$.subscribe(subscriber))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
<notifications-toast></notifications-toast>
|
<notifications-toast></notifications-toast>
|
||||||
<offline-toast></offline-toast>
|
|
||||||
<refresh-alert></refresh-alert>
|
<refresh-alert></refresh-alert>
|
||||||
<update-toast></update-toast>
|
<update-toast></update-toast>
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import { AlertModule, ToastModule } from '@start9labs/shared'
|
|||||||
|
|
||||||
import { ToastContainerComponent } from './toast-container.component'
|
import { ToastContainerComponent } from './toast-container.component'
|
||||||
import { NotificationsToastComponent } from './notifications-toast/notifications-toast.component'
|
import { NotificationsToastComponent } from './notifications-toast/notifications-toast.component'
|
||||||
import { OfflineToastComponent } from './offline-toast/offline-toast.component'
|
|
||||||
import { RefreshAlertComponent } from './refresh-alert/refresh-alert.component'
|
import { RefreshAlertComponent } from './refresh-alert/refresh-alert.component'
|
||||||
import { UpdateToastComponent } from './update-toast/update-toast.component'
|
import { UpdateToastComponent } from './update-toast/update-toast.component'
|
||||||
|
|
||||||
@@ -14,7 +13,6 @@ import { UpdateToastComponent } from './update-toast/update-toast.component'
|
|||||||
declarations: [
|
declarations: [
|
||||||
ToastContainerComponent,
|
ToastContainerComponent,
|
||||||
NotificationsToastComponent,
|
NotificationsToastComponent,
|
||||||
OfflineToastComponent,
|
|
||||||
RefreshAlertComponent,
|
RefreshAlertComponent,
|
||||||
UpdateToastComponent,
|
UpdateToastComponent,
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ChangeDetectionStrategy, Component } from '@angular/core'
|
import { Component } from '@angular/core'
|
||||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||||
import {
|
import {
|
||||||
ServerNotifications,
|
ServerNotifications,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Component, Inject } from '@angular/core'
|
import { Component, Inject } from '@angular/core'
|
||||||
import {
|
import {
|
||||||
ActionSheetController,
|
ActionSheetController,
|
||||||
|
AlertController,
|
||||||
LoadingController,
|
LoadingController,
|
||||||
ModalController,
|
ModalController,
|
||||||
} from '@ionic/angular'
|
} from '@ionic/angular'
|
||||||
@@ -51,6 +52,7 @@ export class MarketplacesPage {
|
|||||||
private readonly config: ConfigService,
|
private readonly config: ConfigService,
|
||||||
private readonly patch: PatchDbService,
|
private readonly patch: PatchDbService,
|
||||||
private readonly destroy$: DestroyService,
|
private readonly destroy$: DestroyService,
|
||||||
|
private readonly alertCtrl: AlertController,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
@@ -129,7 +131,7 @@ export class MarketplacesPage {
|
|||||||
text: 'Delete',
|
text: 'Delete',
|
||||||
role: 'destructive',
|
role: 'destructive',
|
||||||
handler: () => {
|
handler: () => {
|
||||||
this.delete(id)
|
this.presentAlertDelete(id)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -189,6 +191,28 @@ export class MarketplacesPage {
|
|||||||
.subscribe()
|
.subscribe()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async presentAlertDelete(id: string) {
|
||||||
|
const name = this.marketplaces.find(m => m.id === id)?.name
|
||||||
|
|
||||||
|
const alert = await this.alertCtrl.create({
|
||||||
|
header: 'Confirm',
|
||||||
|
message: `Are you sure you want to delete ${name}?`,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: 'Cancel',
|
||||||
|
role: 'cancel',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Delete',
|
||||||
|
handler: () => this.delete(id),
|
||||||
|
cssClass: 'enter-click',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
await alert.present()
|
||||||
|
}
|
||||||
|
|
||||||
private async delete(id: string): Promise<void> {
|
private async delete(id: string): Promise<void> {
|
||||||
const data = await getMarketplace(this.patch)
|
const data = await getMarketplace(this.patch)
|
||||||
const marketplace: UIMarketplaceData = JSON.parse(JSON.stringify(data))
|
const marketplace: UIMarketplaceData = JSON.parse(JSON.stringify(data))
|
||||||
|
|||||||
@@ -8,24 +8,21 @@
|
|||||||
</ion-header>
|
</ion-header>
|
||||||
|
|
||||||
<ion-content class="ion-padding-top">
|
<ion-content class="ion-padding-top">
|
||||||
<ng-container *ngIf="ui$ | async as ui">
|
<ion-item-group *ngIf="name$ | async as name">
|
||||||
<ion-item-group *ngIf="server$ | async as server">
|
<ion-item-divider>General</ion-item-divider>
|
||||||
<ion-item-divider>General</ion-item-divider>
|
<ion-item button (click)="presentModalName(name)">
|
||||||
<ion-item button (click)="presentModalName('My Embassy', ui.name)">
|
<ion-label>Device Name</ion-label>
|
||||||
<ion-label>Device Name</ion-label>
|
<ion-note slot="end">{{ name.current }}</ion-note>
|
||||||
<ion-note slot="end">{{ ui.name || 'My Embassy' }}</ion-note>
|
</ion-item>
|
||||||
</ion-item>
|
|
||||||
|
|
||||||
<ion-item-divider>Marketplace</ion-item-divider>
|
<ion-item-divider>Marketplace</ion-item-divider>
|
||||||
<ion-item
|
<ion-item
|
||||||
button
|
*ngIf="autoCheck$ | async as auto"
|
||||||
(click)="serverConfig.presentAlert('auto-check-updates', ui['auto-check-updates'] !== false)"
|
button
|
||||||
>
|
(click)="serverConfig.presentAlert('auto-check-updates', auto)"
|
||||||
<ion-label>Auto Check for Updates</ion-label>
|
>
|
||||||
<ion-note slot="end">
|
<ion-label>Auto Check for Updates</ion-label>
|
||||||
{{ ui['auto-check-updates'] !== false ? 'Enabled' : 'Disabled' }}
|
<ion-note slot="end"> {{ auto ? 'Enabled' : 'Disabled' }} </ion-note>
|
||||||
</ion-note>
|
</ion-item>
|
||||||
</ion-item>
|
</ion-item-group>
|
||||||
</ion-item-group>
|
|
||||||
</ng-container>
|
|
||||||
</ion-content>
|
</ion-content>
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ import {
|
|||||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||||
import { ServerConfigService } from 'src/app/services/server-config.service'
|
import { ServerConfigService } from 'src/app/services/server-config.service'
|
||||||
import { LocalStorageService } from '../../../services/local-storage.service'
|
import { LocalStorageService } from '../../../services/local-storage.service'
|
||||||
|
import {
|
||||||
|
ServerNameInfo,
|
||||||
|
ServerNameService,
|
||||||
|
} from 'src/app/services/server-name.service'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'preferences',
|
selector: 'preferences',
|
||||||
@@ -22,8 +26,9 @@ import { LocalStorageService } from '../../../services/local-storage.service'
|
|||||||
export class PreferencesPage {
|
export class PreferencesPage {
|
||||||
clicks = 0
|
clicks = 0
|
||||||
|
|
||||||
readonly ui$ = this.patch.watch$('ui')
|
readonly autoCheck$ = this.patch.watch$('ui', 'auto-check-updates')
|
||||||
readonly server$ = this.patch.watch$('server-info')
|
readonly server$ = this.patch.watch$('server-info')
|
||||||
|
readonly name$ = this.serverNameService.name$
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly loadingCtrl: LoadingController,
|
private readonly loadingCtrl: LoadingController,
|
||||||
@@ -32,24 +37,22 @@ export class PreferencesPage {
|
|||||||
private readonly toastCtrl: ToastController,
|
private readonly toastCtrl: ToastController,
|
||||||
private readonly localStorageService: LocalStorageService,
|
private readonly localStorageService: LocalStorageService,
|
||||||
private readonly patch: PatchDbService,
|
private readonly patch: PatchDbService,
|
||||||
|
private readonly serverNameService: ServerNameService,
|
||||||
readonly serverConfig: ServerConfigService,
|
readonly serverConfig: ServerConfigService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async presentModalName(
|
async presentModalName(name: ServerNameInfo): Promise<void> {
|
||||||
placeholder: string,
|
|
||||||
initialValue: string,
|
|
||||||
): Promise<void> {
|
|
||||||
const options: GenericInputOptions = {
|
const options: GenericInputOptions = {
|
||||||
title: 'Edit Device Name',
|
title: 'Edit Device Name',
|
||||||
message: 'This is for your reference only.',
|
message: 'This is for your reference only.',
|
||||||
label: 'Device Name',
|
label: 'Device Name',
|
||||||
useMask: false,
|
useMask: false,
|
||||||
placeholder,
|
placeholder: name.default,
|
||||||
nullable: true,
|
nullable: true,
|
||||||
initialValue,
|
initialValue: name.current,
|
||||||
buttonText: 'Save',
|
buttonText: 'Save',
|
||||||
submitFn: (value: string) =>
|
submitFn: (value: string) =>
|
||||||
this.setDbValue('name', value || placeholder),
|
this.setDbValue('name', value || name.default),
|
||||||
}
|
}
|
||||||
|
|
||||||
const modal = await this.modalCtrl.create({
|
const modal = await this.modalCtrl.create({
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<ion-header>
|
<ion-header>
|
||||||
<ion-toolbar>
|
<ion-toolbar>
|
||||||
<ion-title *ngIf="ui$ | async as ui; else loadingTitle">
|
<ion-title *ngIf="name$ | async as name; else loadingTitle">
|
||||||
{{ ui.name || "My Embassy" }}
|
{{ name.current }}
|
||||||
</ion-title>
|
</ion-title>
|
||||||
<ng-template #loadingTitle>
|
<ng-template #loadingTitle>
|
||||||
<ion-title>
|
<ion-title>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||||
import { ActivatedRoute } from '@angular/router'
|
import { ActivatedRoute } from '@angular/router'
|
||||||
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
|
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
|
||||||
|
import { ServerNameService } from 'src/app/services/server-name.service'
|
||||||
import { Observable, of } from 'rxjs'
|
import { Observable, of } from 'rxjs'
|
||||||
import { filter, take, tap } from 'rxjs/operators'
|
import { filter, take, tap } from 'rxjs/operators'
|
||||||
import { isEmptyObject, ErrorToastService } from '@start9labs/shared'
|
import { isEmptyObject, ErrorToastService } from '@start9labs/shared'
|
||||||
@@ -15,6 +16,7 @@ import { EOSService } from 'src/app/services/eos.service'
|
|||||||
import { LocalStorageService } from 'src/app/services/local-storage.service'
|
import { LocalStorageService } from 'src/app/services/local-storage.service'
|
||||||
import { OSUpdatePage } from 'src/app/modals/os-update/os-update.page'
|
import { OSUpdatePage } from 'src/app/modals/os-update/os-update.page'
|
||||||
import { getAllPackages } from '../../../util/get-package-data'
|
import { getAllPackages } from '../../../util/get-package-data'
|
||||||
|
import { AuthService } from 'src/app/services/auth.service'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'server-show',
|
selector: 'server-show',
|
||||||
@@ -26,7 +28,7 @@ export class ServerShowPage {
|
|||||||
clicks = 0
|
clicks = 0
|
||||||
|
|
||||||
readonly server$ = this.patch.watch$('server-info')
|
readonly server$ = this.patch.watch$('server-info')
|
||||||
readonly ui$ = this.patch.watch$('ui')
|
readonly name$ = this.serverNameService.name$
|
||||||
readonly showUpdate$ = this.eosService.showUpdate$
|
readonly showUpdate$ = this.eosService.showUpdate$
|
||||||
readonly showDiskRepair$ = this.localStorageService.showDiskRepair$
|
readonly showDiskRepair$ = this.localStorageService.showDiskRepair$
|
||||||
|
|
||||||
@@ -41,6 +43,8 @@ export class ServerShowPage {
|
|||||||
private readonly patch: PatchDbService,
|
private readonly patch: PatchDbService,
|
||||||
private readonly eosService: EOSService,
|
private readonly eosService: EOSService,
|
||||||
private readonly localStorageService: LocalStorageService,
|
private readonly localStorageService: LocalStorageService,
|
||||||
|
private readonly serverNameService: ServerNameService,
|
||||||
|
private readonly authService: AuthService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
@@ -74,6 +78,26 @@ export class ServerShowPage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async presentAlertLogout() {
|
||||||
|
const alert = await this.alertCtrl.create({
|
||||||
|
header: 'Confirm',
|
||||||
|
message: 'Are you sure you want to log out?',
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: 'Cancel',
|
||||||
|
role: 'cancel',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Logout',
|
||||||
|
handler: () => this.logout(),
|
||||||
|
cssClass: 'enter-click',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
await alert.present()
|
||||||
|
}
|
||||||
|
|
||||||
async presentAlertRestart() {
|
async presentAlertRestart() {
|
||||||
const alert = await this.alertCtrl.create({
|
const alert = await this.alertCtrl.create({
|
||||||
header: 'Restart',
|
header: 'Restart',
|
||||||
@@ -171,6 +195,12 @@ export class ServerShowPage {
|
|||||||
await alert.present()
|
await alert.present()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// should wipe cache independent of actual BE logout
|
||||||
|
private logout() {
|
||||||
|
this.embassyApi.logout({}).catch(e => console.error('Failed to log out', e))
|
||||||
|
this.authService.setUnverified()
|
||||||
|
}
|
||||||
|
|
||||||
private async restart() {
|
private async restart() {
|
||||||
const action = 'Restart'
|
const action = 'Restart'
|
||||||
|
|
||||||
@@ -456,6 +486,14 @@ export class ServerShowPage {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
Power: [
|
Power: [
|
||||||
|
{
|
||||||
|
title: 'Log Out',
|
||||||
|
description: '',
|
||||||
|
icon: 'log-out-outline',
|
||||||
|
action: () => this.presentAlertLogout(),
|
||||||
|
detail: false,
|
||||||
|
disabled$: of(false),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: 'Restart',
|
title: 'Restart',
|
||||||
description: '',
|
description: '',
|
||||||
|
|||||||
@@ -95,18 +95,14 @@ export class SideloadPage {
|
|||||||
manifest: this.toUpload.manifest!,
|
manifest: this.toUpload.manifest!,
|
||||||
icon: this.toUpload.icon!,
|
icon: this.toUpload.icon!,
|
||||||
})
|
})
|
||||||
this.api
|
const buffer = await blobToBuffer(this.toUpload.file!)
|
||||||
.uploadPackage(guid, await blobToBuffer(this.toUpload.file!))
|
this.api.uploadPackage(guid, buffer).catch(e => console.error(e))
|
||||||
.catch(e => {
|
|
||||||
this.errToast.present(e)
|
this.navCtrl.navigateRoot('/services')
|
||||||
})
|
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
this.errToast.present(e)
|
this.errToast.present(e)
|
||||||
} finally {
|
} finally {
|
||||||
loader.dismiss()
|
loader.dismiss()
|
||||||
await this.navCtrl.navigateForward(
|
|
||||||
`/services/${this.toUpload.manifest!.id}`,
|
|
||||||
)
|
|
||||||
this.clearToUpload()
|
this.clearToUpload()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ export const mockPatchData: DataModel = {
|
|||||||
updated: false,
|
updated: false,
|
||||||
'update-progress': null,
|
'update-progress': null,
|
||||||
},
|
},
|
||||||
|
hostname: 'random-words',
|
||||||
},
|
},
|
||||||
'recovered-packages': {
|
'recovered-packages': {
|
||||||
'btc-rpc-proxy': {
|
'btc-rpc-proxy': {
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ export interface ServerInfo {
|
|||||||
'status-info': ServerStatusInfo
|
'status-info': ServerStatusInfo
|
||||||
'eos-version-compat': string
|
'eos-version-compat': string
|
||||||
'password-hash': string
|
'password-hash': string
|
||||||
|
hostname: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ServerStatusInfo {
|
export interface ServerStatusInfo {
|
||||||
|
|||||||
30
frontend/projects/ui/src/app/services/server-name.service.ts
Normal file
30
frontend/projects/ui/src/app/services/server-name.service.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { Injectable } from '@angular/core'
|
||||||
|
import { PatchDbService } from './patch-db/patch-db.service'
|
||||||
|
import { combineLatest, filter, map, Observable } from 'rxjs'
|
||||||
|
|
||||||
|
export interface ServerNameInfo {
|
||||||
|
current: string
|
||||||
|
default: string
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class ServerNameService {
|
||||||
|
private readonly chosenName$ = this.patch.watch$('ui', 'name')
|
||||||
|
private readonly hostname$ = this.patch
|
||||||
|
.watch$('server-info', 'hostname')
|
||||||
|
.pipe(filter(Boolean))
|
||||||
|
|
||||||
|
readonly name$: Observable<ServerNameInfo> = combineLatest([
|
||||||
|
this.chosenName$,
|
||||||
|
this.hostname$,
|
||||||
|
]).pipe(
|
||||||
|
map(([chosen, hostname]) => {
|
||||||
|
return {
|
||||||
|
current: chosen || hostname,
|
||||||
|
default: hostname,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
constructor(private readonly patch: PatchDbService) {}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user