From 4d82dd22b6482a9c01dd4922bebdb5b9918a160f Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 14 Feb 2022 17:50:17 +0100 Subject: [PATCH] convert ViewModel to typescript --- src/domain/{ViewModel.js => ViewModel.ts} | 67 ++++++++++++++--------- src/utils/Disposables.ts | 23 ++++---- 2 files changed, 52 insertions(+), 38 deletions(-) rename src/domain/{ViewModel.js => ViewModel.ts} (65%) diff --git a/src/domain/ViewModel.js b/src/domain/ViewModel.ts similarity index 65% rename from src/domain/ViewModel.js rename to src/domain/ViewModel.ts index 0c665194..99b23918 100644 --- a/src/domain/ViewModel.js +++ b/src/domain/ViewModel.ts @@ -1,5 +1,6 @@ /* Copyright 2020 Bruno Windels +Copyright 2022 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -21,54 +22,70 @@ limitations under the License. import {EventEmitter} from "../utils/EventEmitter"; import {Disposables} from "../utils/Disposables"; -export class ViewModel extends EventEmitter { - constructor(options = {}) { +import type {Disposable} from "../utils/Disposables"; +import type {Platform} from "../platform/web/Platform"; +import type {Clock} from "../platform/web/dom/Clock"; +import type {ILogger} from "../logging/types"; +import type {Navigation} from "./navigation/Navigation"; +import type {URLRouter} from "./navigation/URLRouter"; + +type Options = { + platform: Platform + logger: ILogger + urlCreator: URLRouter + navigation: Navigation + emitChange?: (params: any) => void +} + +export class ViewModel extends EventEmitter<{change: never}> { + private disposables?: Disposables; + private _isDisposed = false; + private _options: O; + + constructor(options: O) { super(); - this.disposables = null; - this._isDisposed = false; this._options = options; } - childOptions(explicitOptions) { - const {navigation, urlCreator, platform} = this._options; - return Object.assign({navigation, urlCreator, platform}, explicitOptions); + childOptions(explicitOptions: T): T & Options { + return Object.assign({}, this._options, explicitOptions); } // makes it easier to pass through dependencies of a sub-view model - getOption(name) { + getOption(name: N): O[N] { return this._options[name]; } - track(disposable) { + track(disposable: D): D { if (!this.disposables) { this.disposables = new Disposables(); } return this.disposables.track(disposable); } - untrack(disposable) { + untrack(disposable: Disposable): undefined { if (this.disposables) { return this.disposables.untrack(disposable); } - return null; + return undefined; } - dispose() { + dispose(): void { if (this.disposables) { this.disposables.dispose(); } this._isDisposed = true; } - get isDisposed() { + get isDisposed(): boolean { return this._isDisposed; } - disposeTracked(disposable) { + disposeTracked(disposable: Disposable | undefined): undefined { if (this.disposables) { return this.disposables.disposeTracked(disposable); } - return null; + return undefined; } // TODO: this will need to support binding @@ -76,7 +93,7 @@ export class ViewModel extends EventEmitter { // // translated string should probably always be bindings, unless we're fine with a refresh when changing the language? // we probably are, if we're using routing with a url, we could just refresh. - i18n(parts, ...expr) { + i18n(parts: string[], ...expr: any[]) { // just concat for now let result = ""; for (let i = 0; i < parts.length; ++i) { @@ -88,11 +105,11 @@ export class ViewModel extends EventEmitter { return result; } - updateOptions(options) { + updateOptions(options: O): void { this._options = Object.assign(this._options, options); } - emitChange(changedProps) { + emitChange(changedProps: any): void { if (this._options.emitChange) { this._options.emitChange(changedProps); } else { @@ -100,27 +117,23 @@ export class ViewModel extends EventEmitter { } } - get platform() { + get platform(): Platform { return this._options.platform; } - get clock() { + get clock(): Clock { return this._options.platform.clock; } - get logger() { + get logger(): ILogger { return this.platform.logger; } - /** - * The url router, only meant to be used to create urls with from view models. - * @return {URLRouter} - */ - get urlCreator() { + get urlCreator(): URLRouter { return this._options.urlCreator; } - get navigation() { + get navigation(): Navigation { return this._options.navigation; } } diff --git a/src/utils/Disposables.ts b/src/utils/Disposables.ts index 19a5983c..f7c7eb53 100644 --- a/src/utils/Disposables.ts +++ b/src/utils/Disposables.ts @@ -1,5 +1,6 @@ /* Copyright 2020 Bruno Windels +Copyright 2022 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -18,7 +19,7 @@ export interface IDisposable { dispose(): void; } -type Disposable = IDisposable | (() => void); +export type Disposable = IDisposable | (() => void); function disposeValue(value: Disposable): void { if (typeof value === "function") { @@ -33,9 +34,9 @@ function isDisposable(value: Disposable): boolean { } export class Disposables { - private _disposables: Disposable[] | null = []; + private _disposables?: Disposable[] = []; - track(disposable: Disposable): Disposable { + track(disposable: D): D { if (!isDisposable(disposable)) { throw new Error("Not a disposable"); } @@ -48,16 +49,16 @@ export class Disposables { return disposable; } - untrack(disposable: Disposable): null { + untrack(disposable: Disposable): undefined { if (this.isDisposed) { console.warn("Disposables already disposed, cannot untrack"); - return null; + return undefined; } const idx = this._disposables!.indexOf(disposable); if (idx >= 0) { this._disposables!.splice(idx, 1); } - return null; + return undefined; } dispose(): void { @@ -65,17 +66,17 @@ export class Disposables { for (const d of this._disposables) { disposeValue(d); } - this._disposables = null; + this._disposables = undefined; } } get isDisposed(): boolean { - return this._disposables === null; + return this._disposables === undefined; } - disposeTracked(value: Disposable): null { + disposeTracked(value: Disposable | undefined): undefined { if (value === undefined || value === null || this.isDisposed) { - return null; + return undefined; } const idx = this._disposables!.indexOf(value); if (idx !== -1) { @@ -84,6 +85,6 @@ export class Disposables { } else { console.warn("disposable not found, did it leak?", value); } - return null; + return undefined; } }