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:
Aaron Greenspan
2021-02-16 13:45:09 -07:00
committed by Aiden McClelland
parent 46f32cb90b
commit 8d01ebe8b2
238 changed files with 25509 additions and 14852 deletions

View File

@@ -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)
}
}