From 6e306461d78407069324391d657df65bdb697b98 Mon Sep 17 00:00:00 2001 From: Amir Omidi Date: Wed, 12 Aug 2020 14:06:49 -0700 Subject: [PATCH] Introduces event queue processor to create a consistent UI (#11780) --- .../src/models/migrationWizardPage.ts | 34 +++++++++++++++++- .../sql-migration/src/models/stateMachine.ts | 10 ++++++ .../src/wizard/sourceConfigurationPage.ts | 36 +++++++++++-------- 3 files changed, 65 insertions(+), 15 deletions(-) diff --git a/extensions/sql-migration/src/models/migrationWizardPage.ts b/extensions/sql-migration/src/models/migrationWizardPage.ts index 721419c406..ba1b4d3681 100644 --- a/extensions/sql-migration/src/models/migrationWizardPage.ts +++ b/extensions/sql-migration/src/models/migrationWizardPage.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as azdata from 'azdata'; -import { MigrationStateModel } from './stateMachine'; +import { MigrationStateModel, StateChangeEvent } from './stateMachine'; export abstract class MigrationWizardPage { constructor(protected readonly wizardPage: azdata.window.WizardPage, protected readonly migrationStateModel: MigrationStateModel) { } @@ -16,5 +16,37 @@ export abstract class MigrationWizardPage { public abstract async onPageEnter(): Promise; public abstract async onPageLeave(): Promise; + + private readonly stateChanges: (() => Promise)[] = []; + protected async onStateChangeEvent(e: StateChangeEvent) { + + this.stateChanges.push((): Promise => { + return this.handleStateChange(e); + }); + + this.enableQueueProcessor(); + } + + private queueActive = false; + private async enableQueueProcessor(): Promise { + if (this.queueActive) { + return; + } + this.queueActive = true; + while (true) { + const stateChangeFunction = this.stateChanges.shift(); + if (!stateChangeFunction) { + break; + } + try { + await stateChangeFunction(); + } catch (ex) { + console.error(ex); + } + } + this.queueActive = false; + } + + protected abstract async handleStateChange(e: StateChangeEvent): Promise; } diff --git a/extensions/sql-migration/src/models/stateMachine.ts b/extensions/sql-migration/src/models/stateMachine.ts index 7314fdf024..28cfcc9a1b 100644 --- a/extensions/sql-migration/src/models/stateMachine.ts +++ b/extensions/sql-migration/src/models/stateMachine.ts @@ -27,6 +27,7 @@ export enum State { export interface Model { readonly sourceConnection: azdata.connection.Connection; readonly currentState: State; + gatheringInformationError: string | undefined; } export interface StateChangeEvent { @@ -37,6 +38,7 @@ export interface StateChangeEvent { export class MigrationStateModel implements Model, vscode.Disposable { private _stateChangeEventEmitter = new vscode.EventEmitter(); private _currentState: State; + private _gatheringInformationError: string | undefined; constructor(private readonly _sourceConnection: azdata.connection.Connection) { this._currentState = State.INIT; @@ -58,6 +60,14 @@ export class MigrationStateModel implements Model, vscode.Disposable { this._stateChangeEventEmitter.fire({ oldState, newState: this.currentState }); } + public get gatheringInformationError(): string | undefined { + return this._gatheringInformationError; + } + + public set gatheringInformationError(error: string | undefined) { + this._gatheringInformationError = error; + } + public get stateChangeEvent(): vscode.Event { return this._stateChangeEventEmitter.event; } diff --git a/extensions/sql-migration/src/wizard/sourceConfigurationPage.ts b/extensions/sql-migration/src/wizard/sourceConfigurationPage.ts index 9fb3510168..b9e1eceac4 100644 --- a/extensions/sql-migration/src/wizard/sourceConfigurationPage.ts +++ b/extensions/sql-migration/src/wizard/sourceConfigurationPage.ts @@ -6,7 +6,8 @@ import * as azdata from 'azdata'; import { MigrationWizardPage } from '../models/migrationWizardPage'; import { SOURCE_CONFIGURATION_PAGE_TITLE, COLLECTING_SOURCE_CONFIGURATIONS, COLLECTING_SOURCE_CONFIGURATIONS_INFO, COLLECTING_SOURCE_CONFIGURATIONS_ERROR } from '../models/strings'; -import { MigrationStateModel } from '../models/stateMachine'; +import { MigrationStateModel, StateChangeEvent, State } from '../models/stateMachine'; +import { Disposable } from 'vscode'; export class SourceConfigurationPage extends MigrationWizardPage { constructor(migrationStateModel: MigrationStateModel) { @@ -29,19 +30,11 @@ export class SourceConfigurationPage extends MigrationWizardPage { } private async registerContent(view: azdata.ModelView) { - await this.createGatheringInformationPage(view); - setTimeout(async () => { - console.log('doing it'); - try { - await this.createErrorInInformationGatheringPage(view); - } catch (ex) { - console.log(ex); - } - }, 5000); + await this.initialState(view); } private gatheringInfoComponent!: azdata.FormComponent; - private async createGatheringInformationPage(view: azdata.ModelView) { + private async initialState(view: azdata.ModelView) { this.gatheringInfoComponent = this.createGatheringInfoComponent(view); const form = view.modelBuilder.formContainer().withFormItems( [ @@ -59,9 +52,13 @@ export class SourceConfigurationPage extends MigrationWizardPage { // } - private async createErrorInInformationGatheringPage(view: azdata.ModelView) { + private async enterErrorState() { const component = this.gatheringInfoComponent.component as azdata.TextComponent; - component.value = COLLECTING_SOURCE_CONFIGURATIONS_ERROR('Some error'); + component.value = COLLECTING_SOURCE_CONFIGURATIONS_ERROR(this.migrationStateModel.gatheringInformationError); + } + + private async enterTargetSelectionState() { + } //#region component builders @@ -79,11 +76,22 @@ export class SourceConfigurationPage extends MigrationWizardPage { }; } //#endregion - public async onPageEnter(): Promise { + private eventListener: Disposable | undefined; + public async onPageEnter(): Promise { + this.eventListener = this.migrationStateModel.stateChangeEvent(async (e) => this.onStateChangeEvent(e)); } public async onPageLeave(): Promise { + this.eventListener?.dispose(); + } + protected async handleStateChange(e: StateChangeEvent): Promise { + switch (e.newState) { + case State.COLLECTION_SOURCE_INFO_ERROR: + return this.enterErrorState(); + case State.TARGET_SELECTION: + return this.enterTargetSelectionState(); + } } }