ui: cleanup wizard

This commit is contained in:
Aaron Greenspan
2021-02-05 14:48:46 -07:00
committed by Aiden McClelland
parent 7cb063b81e
commit d5b1b0b5c8
10 changed files with 292 additions and 165 deletions

View File

@@ -1,15 +1,15 @@
import { Component, Input, NgZone, OnInit, QueryList, ViewChild, ViewChildren } from '@angular/core'
import { IonContent, IonSlides, ModalController } from '@ionic/angular'
import { BehaviorSubject, combineLatest, Subscription } from 'rxjs'
import { map } from 'rxjs/operators'
import { BehaviorSubject, from, Observable, of } from 'rxjs'
import { Cleanup } from 'src/app/util/cleanup'
import { capitalizeFirstLetter, pauseFor } from 'src/app/util/misc.util'
import { CompleteComponent } from './complete/complete.component'
import { DependenciesComponent } from './dependencies/dependencies.component'
import { DependentsComponent } from './dependents/dependents.component'
import { NotesComponent } from './notes/notes.component'
import { Colorable, Loadable } from './loadable'
import { Loadable } from './loadable'
import { WizardAction } from './wizard-types'
import { concatMap, switchMap } from 'rxjs/operators'
@Component({
selector: 'install-wizard',
@@ -18,36 +18,35 @@ import { WizardAction } from './wizard-types'
})
export class InstallWizardComponent extends Cleanup implements OnInit {
@Input() params: {
// defines the slideshow in the html
// defines each slide along with bottom bar
slideDefinitions: SlideDefinition[]
toolbar: TopbarParams
}
// containers
// content container so we can scroll to top between slide transitions
@ViewChild(IonContent) contentContainer: IonContent
// slide container gives us hook into transitioning slides
@ViewChild(IonSlides) slideContainer: IonSlides
//don't use this, use slideComponents instead.
//a slide component gives us hook into a slide. Allows us to call load when slide comes into view
@ViewChildren('components')
public slideComponentsQL: QueryList<Loadable & Colorable>
slideComponentsQL: QueryList<Loadable>
get slideComponents (): Loadable[] { return this.slideComponentsQL.toArray() }
//don't use this, use currentSlide instead.
slideIndex = 0
get slideComponents (): (Loadable & Colorable)[] {
return this.slideComponentsQL.toArray()
}
get currentSlide (): (Loadable & Colorable) {
private slideIndex = 0
get currentSlide (): Loadable {
return this.slideComponents[this.slideIndex]
}
get currentSlideDef (): SlideDefinition {
return this.params.slideDefinitions[this.slideIndex]
get currentSlideLoading$ (): Observable<boolean> {
return this.$initializing$.pipe(switchMap(
i => i ? of(true) : this.currentSlide.$loading$,
))
}
get currentBottomBar (): SlideDefinition['bottomBar'] {
return this.params.slideDefinitions[this.slideIndex].bottomBar
}
$anythingLoading$: BehaviorSubject<boolean> = new BehaviorSubject(true)
$currentColor$: BehaviorSubject<string> = new BehaviorSubject('medium')
$initializing$ = new BehaviorSubject(true)
$error$ = new BehaviorSubject(undefined)
constructor (private readonly modalController: ModalController, private readonly zone: NgZone) { super() }
@@ -60,26 +59,28 @@ export class InstallWizardComponent extends Cleanup implements OnInit {
}
ionViewDidEnter () {
this.cleanup(
combineLatest(this.slideComponents.map(component => component.$loading$)).pipe(
map(loadings => !loadings.every(p => !p)),
).subscribe(this.$anythingLoading$),
combineLatest(this.slideComponents.map(component => component.$color$)).pipe(
map(colors => colors[this.slideIndex]),
).subscribe(this.$currentColor$),
)
this.$initializing$.next(false)
}
finished = (info: { error?: Error, cancelled?: true, final?: true }) => {
// process bottom bar buttons
private transition = (info: { error?: Error, cancelled?: true, final?: true }) => {
if (info.cancelled) this.currentSlide.$cancel$.next()
if (info.final || info.cancelled) return this.modalController.dismiss(info)
if (info.error) return this.$error$.next(capitalizeFirstLetter(info.error.message))
this.slide()
this.moveToNextSlide()
}
private async slide () {
if (this.slideComponents[this.slideIndex + 1] === undefined) { return this.finished({ final: true }) }
// bottom bar button callbacks
transitions = {
cancel: () => this.transition({ cancelled: true }),
next: () => this.transition({ }),
final: () => this.transition({ final: true }),
error: e => this.transition({ error: e }),
}
private async moveToNextSlide () {
if (this.slideComponents[this.slideIndex + 1] === undefined) { return this.transition({ final: true }) }
this.zone.run(async () => {
this.slideComponents[this.slideIndex + 1].load()
await pauseFor(50)
@@ -92,33 +93,23 @@ export class InstallWizardComponent extends Cleanup implements OnInit {
}
}
export interface SlideCommon {
selector: string // component http selector
cancelButton: {
// indicates the existence of a cancel button, and whether to have text or an icon 'x' by default.
afterLoading?: { text?: string },
whileLoading?: { text?: string }
export interface SlideDefinition {
slide:
{ selector: 'dependencies', params: DependenciesComponent['params'] } |
{ selector: 'dependents', params: DependentsComponent['params'] } |
{ selector: 'complete', params: CompleteComponent['params'] } |
{ selector: 'notes', params: NotesComponent['params'] }
bottomBar: {
cancel: {
// indicates the existence of a cancel button, and whether to have text or an icon 'x' by default.
afterLoading?: { text?: string },
whileLoading?: { text?: string }
}
next?: string
finish?: string
}
nextButton?: string, // existence and content of next button
finishButton?: string // existence and content of finish button
}
export type SlideDefinition = SlideCommon & (
{
selector: 'dependencies',
params: DependenciesComponent['params']
} | {
selector: 'dependents',
params: DependentsComponent['params']
} | {
selector: 'complete',
params: CompleteComponent['params']
} | {
selector: 'notes',
params: NotesComponent['params']
}
)
export type TopbarParams = { action: WizardAction, title: string, version: string }
export async function wizardModal (