Feat/combine uis (#2633)

* wip

* restructure backend for new ui structure

* new patchdb bootstrap, single websocket api, local storage migration, more

* update db websocket

* init apis

* update patch-db

* setup progress

* feat: implement state service, alert and routing

Signed-off-by: waterplea <alexander@inkin.ru>

* update setup wizard for new types

* feat: add init page

Signed-off-by: waterplea <alexander@inkin.ru>

* chore: refactor message, patch-db source stream and connection service

Signed-off-by: waterplea <alexander@inkin.ru>

* fix method not found on state

* fix backend bugs

* fix compat assets

* address comments

* remove unneeded styling

* cleaner progress

* bugfixes

* fix init logs

* fix progress reporting

* fix navigation by getting state after init

* remove patch dependency from live api

* fix caching

* re-add patchDB to live api

* fix metrics values

* send close frame

* add bootId and fix polling

---------

Signed-off-by: waterplea <alexander@inkin.ru>
Co-authored-by: Aiden McClelland <me@drbonez.dev>
Co-authored-by: waterplea <alexander@inkin.ru>
This commit is contained in:
Matt Hill
2024-06-19 13:51:44 -06:00
committed by GitHub
parent e92d4ff147
commit da3720c7a9
147 changed files with 3939 additions and 2637 deletions

View File

@@ -1,15 +1,23 @@
import { Component } from '@angular/core'
import { NavController } from '@ionic/angular'
import { StateService } from 'src/app/services/state.service'
import { Pipe, PipeTransform } from '@angular/core'
import { BehaviorSubject } from 'rxjs'
import {
EMPTY,
Observable,
catchError,
filter,
from,
interval,
map,
of,
startWith,
switchMap,
take,
tap,
} from 'rxjs'
import { ApiService } from 'src/app/services/api/api.service'
import { ErrorToastService, pauseFor } from '@start9labs/shared'
type Progress = {
totalBytes: number | null
transferred: number
}
import { ErrorToastService } from '@start9labs/shared'
import { T } from '@start9labs/start-sdk'
@Component({
selector: 'app-loading',
@@ -17,10 +25,46 @@ type Progress = {
styleUrls: ['loading.page.scss'],
})
export class LoadingPage {
readonly progress$ = new BehaviorSubject<Progress>({
totalBytes: null,
transferred: 0,
})
readonly progress$ = this.getRunningStatus$().pipe(
switchMap(res =>
this.api.openProgressWebsocket$(res.guid).pipe(
startWith(res.progress),
catchError((_, watch$) => {
return interval(2000).pipe(
switchMap(() =>
from(this.api.getStatus()).pipe(catchError(() => EMPTY)),
),
take(1),
switchMap(() => watch$),
)
}),
tap(progress => {
if (progress.overall === true) {
this.getStatus()
}
}),
),
),
map(({ phases, overall }) => {
return {
total: getDecimal(overall),
message: phases
.filter(
(
p,
): p is {
name: string
progress: {
done: number
total: number | null
}
} => p.progress !== true && p.progress !== null,
)
.map(p => `${p.name}${getPhaseBytes(p.progress)}`)
.join(','),
}
}),
)
constructor(
private readonly navCtrl: NavController,
@@ -28,55 +72,55 @@ export class LoadingPage {
private readonly errorToastService: ErrorToastService,
) {}
ngOnInit() {
this.poll()
}
private async getStatus(): Promise<{
status: 'running'
guid: string
progress: T.FullProgress
} | void> {
const res = await this.api.getStatus()
async poll() {
try {
const progress = await this.api.getStatus()
if (!progress) return
const { totalBytes, bytesTransferred } = progress
this.progress$.next({
totalBytes,
transferred: totalBytes ? bytesTransferred / totalBytes : 0,
})
if (progress.complete) {
this.navCtrl.navigateForward(`/success`)
this.progress$.complete()
return
}
await pauseFor(250)
setTimeout(() => this.poll(), 0) // prevent call stack from growing
} catch (e: any) {
this.errorToastService.present(e)
}
}
}
@Pipe({
name: 'toMessage',
})
export class ToMessagePipe implements PipeTransform {
constructor(private readonly stateService: StateService) {}
transform(progress: number | null): string {
if (['fresh', 'attach'].includes(this.stateService.setupType || '')) {
return 'Setting up your server'
}
if (!progress) {
return 'Calculating size'
} else if (progress < 1) {
return 'Copying data'
if (!res) {
this.navCtrl.navigateRoot('/home')
} else if (res.status === 'complete') {
this.navCtrl.navigateForward(`/success`)
} else {
return 'Finalizing'
return res
}
}
private getRunningStatus$(): Observable<{
status: 'running'
guid: string
progress: T.FullProgress
}> {
return from(this.getStatus()).pipe(
filter(Boolean),
catchError(e => {
this.errorToastService.present(e)
return of(e)
}),
take(1),
)
}
}
function getDecimal(progress: T.Progress): number {
if (progress === true) {
return 1
} else if (!progress || !progress.total) {
return 0
} else {
return progress.total && progress.done / progress.total
}
}
function getPhaseBytes(
progress:
| false
| {
done: number
total: number | null
},
): string {
return progress === false ? '' : `: (${progress.done}/${progress.total})`
}