mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 20:14:49 +00:00
0.3.0 refactor
ui: adds overlay layer to patch-db-client ui: getting towards mocks ui: cleans up factory init ui: nice type hack ui: live api for patch ui: api service source + http starts up ui: api source + http ui: rework patchdb config, pass stashTimeout into patchDbModel wires in temp patching into api service ui: example of wiring patchdbmodel into page begin integration remove unnecessary method linting first data rendering rework app initialization http source working for ssh delete call temp patches working entire Embassy tab complete not in kansas anymore ripping, saving progress progress for API request response types and endoint defs Update data-model.ts shambles, but in a good way progress big progress progress installed list working big progress progress progress begin marketplace redesign Update api-types.ts Update api-types.ts marketplace improvements cosmetic dependencies and recommendations begin nym auth approach install wizard restore flow and donations
This commit is contained in:
committed by
Aiden McClelland
parent
46f32cb90b
commit
8d01ebe8b2
@@ -1,19 +1,18 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { ServerModel, ServerStatus } from './models/server-model'
|
||||
import { Storage } from '@ionic/storage'
|
||||
import { SyncDaemon } from './services/sync.service'
|
||||
import { AuthService, AuthState } from './services/auth.service'
|
||||
import { ApiService } from './services/api/api.service'
|
||||
import { Router } from '@angular/router'
|
||||
import { BehaviorSubject, Observable } from 'rxjs'
|
||||
import { AppModel } from './models/app-model'
|
||||
import { filter, take } from 'rxjs/operators'
|
||||
import { AlertController } from '@ionic/angular'
|
||||
import { Router, RoutesRecognized } from '@angular/router'
|
||||
import { distinctUntilChanged, filter, finalize, takeWhile } from 'rxjs/operators'
|
||||
import { AlertController, ToastController } from '@ionic/angular'
|
||||
import { LoaderService } from './services/loader.service'
|
||||
import { Emver } from './services/emver.service'
|
||||
import { SplitPaneTracker } from './services/split-pane.service'
|
||||
import { LoadingOptions } from '@ionic/core'
|
||||
import { pauseFor } from './util/misc.util'
|
||||
import { PatchDbModel } from './models/patch-db/patch-db-model'
|
||||
import { HttpService } from './services/http.service'
|
||||
import { ServerStatus } from './models/patch-db/data-model'
|
||||
import { ConnectionService } from './services/connection.service'
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
@@ -21,11 +20,8 @@ import { pauseFor } from './util/misc.util'
|
||||
styleUrls: ['app.component.scss'],
|
||||
})
|
||||
export class AppComponent {
|
||||
isUpdating = false
|
||||
fullPageMenu = true
|
||||
$showMenuContent$ = new BehaviorSubject(false)
|
||||
serverName$ : Observable<string>
|
||||
serverBadge$: Observable<number>
|
||||
ServerStatus = ServerStatus
|
||||
showMenu = false
|
||||
selectedIndex = 0
|
||||
untilLoaded = true
|
||||
appPages = [
|
||||
@@ -49,92 +45,118 @@ export class AppComponent {
|
||||
url: '/notifications',
|
||||
icon: 'notifications-outline',
|
||||
},
|
||||
// {
|
||||
// title: 'Backup drives',
|
||||
// url: '/drives',
|
||||
// icon: 'albums-outline',
|
||||
// },
|
||||
]
|
||||
|
||||
constructor (
|
||||
private readonly serverModel: ServerModel,
|
||||
private readonly syncDaemon: SyncDaemon,
|
||||
private readonly storage: Storage,
|
||||
private readonly appModel: AppModel,
|
||||
private readonly authService: AuthService,
|
||||
private readonly router: Router,
|
||||
private readonly api: ApiService,
|
||||
private readonly http: HttpService,
|
||||
private readonly alertCtrl: AlertController,
|
||||
private readonly loader: LoaderService,
|
||||
private readonly emver: Emver,
|
||||
private readonly connectionService: ConnectionService,
|
||||
private readonly toastCtrl: ToastController,
|
||||
readonly splitPane: SplitPaneTracker,
|
||||
readonly patch: PatchDbModel,
|
||||
) {
|
||||
// set dark theme
|
||||
document.body.classList.toggle('dark', true)
|
||||
this.serverName$ = this.serverModel.watch().name
|
||||
this.serverBadge$ = this.serverModel.watch().badge
|
||||
this.init()
|
||||
}
|
||||
|
||||
ionViewDidEnter () {
|
||||
// weird bug where a browser grabbed the value 'getdots' from the app.component.html preload input field.
|
||||
// this removes that field after prleloading occurs.
|
||||
pauseFor(500).then(() => this.untilLoaded = false)
|
||||
}
|
||||
|
||||
async init () {
|
||||
let fromFresh = true
|
||||
await this.storage.ready()
|
||||
await this.authService.restoreCache()
|
||||
await this.storage.create()
|
||||
await this.patch.init()
|
||||
await this.authService.init()
|
||||
await this.emver.init()
|
||||
|
||||
this.authService.listen({
|
||||
[AuthState.VERIFIED]: async () => {
|
||||
console.log('verified')
|
||||
this.api.authenticatedRequestsEnabled = true
|
||||
await this.serverModel.restoreCache()
|
||||
await this.appModel.restoreCache()
|
||||
this.syncDaemon.start()
|
||||
this.$showMenuContent$.next(true)
|
||||
if (fromFresh) {
|
||||
this.router.initialNavigation()
|
||||
fromFresh = false
|
||||
}
|
||||
},
|
||||
[AuthState.UNVERIFIED]: () => {
|
||||
console.log('unverified')
|
||||
this.api.authenticatedRequestsEnabled = false
|
||||
this.serverModel.clear()
|
||||
this.appModel.clear()
|
||||
this.syncDaemon.stop()
|
||||
this.router.initialNavigation()
|
||||
|
||||
// watch auth
|
||||
this.authService.watch$()
|
||||
.subscribe(auth => {
|
||||
// VERIFIED
|
||||
if (auth === AuthState.VERIFIED) {
|
||||
this.http.authReqEnabled = true
|
||||
this.showMenu = true
|
||||
this.patch.start()
|
||||
// watch network
|
||||
this.watchNetwork(auth)
|
||||
// watch router to highlight selected menu item
|
||||
this.watchRouter(auth)
|
||||
// watch status to display/hide maintenance page
|
||||
this.watchStatus(auth)
|
||||
// watch unread notification count to display toast
|
||||
this.watchNotifications(auth)
|
||||
// UNVERIFIED
|
||||
} else if (auth === AuthState.UNVERIFIED) {
|
||||
this.http.authReqEnabled = false
|
||||
this.showMenu = false
|
||||
this.patch.stop()
|
||||
this.storage.clear()
|
||||
this.router.navigate(['/authenticate'], { replaceUrl: true })
|
||||
this.$showMenuContent$.next(false)
|
||||
if (fromFresh) {
|
||||
this.router.initialNavigation()
|
||||
fromFresh = false
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
this.serverModel.watch().status.subscribe(s => {
|
||||
this.isUpdating = (s === ServerStatus.UPDATING)
|
||||
})
|
||||
|
||||
this.router.events.pipe(filter(e => !!(e as any).urlAfterRedirects)).subscribe((e: any) => {
|
||||
const appPageIndex = this.appPages.findIndex(
|
||||
appPage => (e.urlAfterRedirects || e.url || '').startsWith(appPage.url),
|
||||
)
|
||||
if (appPageIndex > -1) this.selectedIndex = appPageIndex
|
||||
|
||||
// TODO: while this works, it is dangerous and impractical.
|
||||
if (e.urlAfterRedirects !== '/embassy' && e.urlAfterRedirects !== '/authenticate' && this.isUpdating) {
|
||||
this.router.navigateByUrl('/embassy')
|
||||
this.router.navigate(['/auth'], { replaceUrl: true })
|
||||
}
|
||||
})
|
||||
this.api.watch401$().subscribe(() => {
|
||||
this.authService.setAuthStateUnverified()
|
||||
return this.api.postLogout()
|
||||
|
||||
this.http.watch401$().subscribe(() => {
|
||||
this.authService.setUnverified()
|
||||
})
|
||||
}
|
||||
|
||||
private watchNetwork (auth: AuthState): void {
|
||||
this.connectionService.monitor$()
|
||||
.pipe(
|
||||
distinctUntilChanged(),
|
||||
takeWhile(() => auth === AuthState.VERIFIED),
|
||||
)
|
||||
.subscribe(c => {
|
||||
console.log('CONNECTION CHANGED', c)
|
||||
})
|
||||
}
|
||||
|
||||
private watchRouter (auth: AuthState): void {
|
||||
this.router.events
|
||||
.pipe(
|
||||
filter((e: RoutesRecognized) => !!e.urlAfterRedirects),
|
||||
takeWhile(() => auth === AuthState.VERIFIED),
|
||||
)
|
||||
.subscribe(e => {
|
||||
const appPageIndex = this.appPages.findIndex(
|
||||
appPage => e.urlAfterRedirects.startsWith(appPage.url),
|
||||
)
|
||||
if (appPageIndex > -1) this.selectedIndex = appPageIndex
|
||||
})
|
||||
}
|
||||
|
||||
private watchStatus (auth: AuthState): void {
|
||||
this.patch.watch$('server-info', 'status')
|
||||
.pipe(
|
||||
takeWhile(() => auth === AuthState.VERIFIED),
|
||||
)
|
||||
.subscribe(status => {
|
||||
const maintenance = '/maintenance'
|
||||
const url = this.router.url
|
||||
if (status === ServerStatus.Running && url.startsWith(maintenance)) {
|
||||
this.router.navigate([''], { replaceUrl: true })
|
||||
}
|
||||
if ([ServerStatus.Updating, ServerStatus.BackingUp].includes(status) && !url.startsWith(maintenance)) {
|
||||
this.router.navigate([maintenance], { replaceUrl: true })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private watchNotifications (auth: AuthState): void {
|
||||
let previous: number
|
||||
this.patch.watch$('server-info', 'unread-notification-count')
|
||||
.pipe(
|
||||
takeWhile(() => auth === AuthState.VERIFIED),
|
||||
finalize(() => console.log('FINALIZING!!!')),
|
||||
)
|
||||
.subscribe(count => {
|
||||
if (previous !== undefined && count > previous) this.presentToastNotifications()
|
||||
previous = count
|
||||
})
|
||||
}
|
||||
|
||||
@@ -161,20 +183,45 @@ export class AppComponent {
|
||||
}
|
||||
|
||||
private async logout () {
|
||||
this.serverName$.pipe(take(1)).subscribe(name => {
|
||||
this.loader.of(LoadingSpinner(`Logging out ${name || ''}...`))
|
||||
.displayDuringP(this.api.postLogout())
|
||||
.then(() => this.authService.setAuthStateUnverified())
|
||||
.catch(e => this.setError(e))
|
||||
})
|
||||
this.loader.of(LoadingSpinner('Logging out...'))
|
||||
.displayDuringP(this.api.logout({ }))
|
||||
.then(() => this.authService.setUnverified())
|
||||
.catch(e => this.setError(e))
|
||||
}
|
||||
|
||||
async setError (e: Error) {
|
||||
private async presentToastNotifications () {
|
||||
const toast = await this.toastCtrl.create({
|
||||
header: 'Embassy',
|
||||
message: `New notifications`,
|
||||
position: 'bottom',
|
||||
duration: 4000,
|
||||
cssClass: 'notification-toast',
|
||||
buttons: [
|
||||
{
|
||||
side: 'start',
|
||||
icon: 'close',
|
||||
handler: () => {
|
||||
return true
|
||||
},
|
||||
},
|
||||
{
|
||||
side: 'end',
|
||||
text: 'View',
|
||||
handler: () => {
|
||||
this.router.navigate(['/notifications'])
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
await toast.present()
|
||||
}
|
||||
|
||||
private async setError (e: Error) {
|
||||
console.error(e)
|
||||
await this.presentError(e.message)
|
||||
}
|
||||
|
||||
async presentError (e: string) {
|
||||
private async presentError (e: string) {
|
||||
const alert = await this.alertCtrl.create({
|
||||
backdropDismiss: true,
|
||||
message: `Exception on logout: ${e}`,
|
||||
@@ -189,7 +236,7 @@ export class AppComponent {
|
||||
}
|
||||
|
||||
splitPaneVisible (e: any) {
|
||||
this.splitPane.$menuFixedOpenOnLeft$.next(e.detail.visible)
|
||||
this.splitPane.menuFixedOpenOnLeft$.next(e.detail.visible)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user