forked from mystiq/hydrogen-web
convert ViewModel to typescript
This commit is contained in:
parent
460780d602
commit
4d82dd22b6
2 changed files with 52 additions and 38 deletions
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
Copyright 2020 Bruno Windels <bruno@windels.cloud>
|
||||
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<O extends Options = Options> 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<T extends Object>(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<N extends keyof O>(name: N): O[N] {
|
||||
return this._options[name];
|
||||
}
|
||||
|
||||
track(disposable) {
|
||||
track<D extends Disposable>(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;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
Copyright 2020 Bruno Windels <bruno@windels.cloud>
|
||||
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<D extends Disposable>(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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue