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 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");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with 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 {EventEmitter} from "../utils/EventEmitter";
|
||||||
import {Disposables} from "../utils/Disposables";
|
import {Disposables} from "../utils/Disposables";
|
||||||
|
|
||||||
export class ViewModel extends EventEmitter {
|
import type {Disposable} from "../utils/Disposables";
|
||||||
constructor(options = {}) {
|
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();
|
super();
|
||||||
this.disposables = null;
|
|
||||||
this._isDisposed = false;
|
|
||||||
this._options = options;
|
this._options = options;
|
||||||
}
|
}
|
||||||
|
|
||||||
childOptions(explicitOptions) {
|
childOptions<T extends Object>(explicitOptions: T): T & Options {
|
||||||
const {navigation, urlCreator, platform} = this._options;
|
return Object.assign({}, this._options, explicitOptions);
|
||||||
return Object.assign({navigation, urlCreator, platform}, explicitOptions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// makes it easier to pass through dependencies of a sub-view model
|
// 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];
|
return this._options[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
track(disposable) {
|
track<D extends Disposable>(disposable: D): D {
|
||||||
if (!this.disposables) {
|
if (!this.disposables) {
|
||||||
this.disposables = new Disposables();
|
this.disposables = new Disposables();
|
||||||
}
|
}
|
||||||
return this.disposables.track(disposable);
|
return this.disposables.track(disposable);
|
||||||
}
|
}
|
||||||
|
|
||||||
untrack(disposable) {
|
untrack(disposable: Disposable): undefined {
|
||||||
if (this.disposables) {
|
if (this.disposables) {
|
||||||
return this.disposables.untrack(disposable);
|
return this.disposables.untrack(disposable);
|
||||||
}
|
}
|
||||||
return null;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
dispose() {
|
dispose(): void {
|
||||||
if (this.disposables) {
|
if (this.disposables) {
|
||||||
this.disposables.dispose();
|
this.disposables.dispose();
|
||||||
}
|
}
|
||||||
this._isDisposed = true;
|
this._isDisposed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
get isDisposed() {
|
get isDisposed(): boolean {
|
||||||
return this._isDisposed;
|
return this._isDisposed;
|
||||||
}
|
}
|
||||||
|
|
||||||
disposeTracked(disposable) {
|
disposeTracked(disposable: Disposable | undefined): undefined {
|
||||||
if (this.disposables) {
|
if (this.disposables) {
|
||||||
return this.disposables.disposeTracked(disposable);
|
return this.disposables.disposeTracked(disposable);
|
||||||
}
|
}
|
||||||
return null;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: this will need to support binding
|
// 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?
|
// 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.
|
// 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
|
// just concat for now
|
||||||
let result = "";
|
let result = "";
|
||||||
for (let i = 0; i < parts.length; ++i) {
|
for (let i = 0; i < parts.length; ++i) {
|
||||||
|
@ -88,11 +105,11 @@ export class ViewModel extends EventEmitter {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateOptions(options) {
|
updateOptions(options: O): void {
|
||||||
this._options = Object.assign(this._options, options);
|
this._options = Object.assign(this._options, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
emitChange(changedProps) {
|
emitChange(changedProps: any): void {
|
||||||
if (this._options.emitChange) {
|
if (this._options.emitChange) {
|
||||||
this._options.emitChange(changedProps);
|
this._options.emitChange(changedProps);
|
||||||
} else {
|
} else {
|
||||||
|
@ -100,27 +117,23 @@ export class ViewModel extends EventEmitter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get platform() {
|
get platform(): Platform {
|
||||||
return this._options.platform;
|
return this._options.platform;
|
||||||
}
|
}
|
||||||
|
|
||||||
get clock() {
|
get clock(): Clock {
|
||||||
return this._options.platform.clock;
|
return this._options.platform.clock;
|
||||||
}
|
}
|
||||||
|
|
||||||
get logger() {
|
get logger(): ILogger {
|
||||||
return this.platform.logger;
|
return this.platform.logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
get urlCreator(): URLRouter {
|
||||||
* The url router, only meant to be used to create urls with from view models.
|
|
||||||
* @return {URLRouter}
|
|
||||||
*/
|
|
||||||
get urlCreator() {
|
|
||||||
return this._options.urlCreator;
|
return this._options.urlCreator;
|
||||||
}
|
}
|
||||||
|
|
||||||
get navigation() {
|
get navigation(): Navigation {
|
||||||
return this._options.navigation;
|
return this._options.navigation;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2020 Bruno Windels <bruno@windels.cloud>
|
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");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -18,7 +19,7 @@ export interface IDisposable {
|
||||||
dispose(): void;
|
dispose(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Disposable = IDisposable | (() => void);
|
export type Disposable = IDisposable | (() => void);
|
||||||
|
|
||||||
function disposeValue(value: Disposable): void {
|
function disposeValue(value: Disposable): void {
|
||||||
if (typeof value === "function") {
|
if (typeof value === "function") {
|
||||||
|
@ -33,9 +34,9 @@ function isDisposable(value: Disposable): boolean {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Disposables {
|
export class Disposables {
|
||||||
private _disposables: Disposable[] | null = [];
|
private _disposables?: Disposable[] = [];
|
||||||
|
|
||||||
track(disposable: Disposable): Disposable {
|
track<D extends Disposable>(disposable: D): D {
|
||||||
if (!isDisposable(disposable)) {
|
if (!isDisposable(disposable)) {
|
||||||
throw new Error("Not a disposable");
|
throw new Error("Not a disposable");
|
||||||
}
|
}
|
||||||
|
@ -48,16 +49,16 @@ export class Disposables {
|
||||||
return disposable;
|
return disposable;
|
||||||
}
|
}
|
||||||
|
|
||||||
untrack(disposable: Disposable): null {
|
untrack(disposable: Disposable): undefined {
|
||||||
if (this.isDisposed) {
|
if (this.isDisposed) {
|
||||||
console.warn("Disposables already disposed, cannot untrack");
|
console.warn("Disposables already disposed, cannot untrack");
|
||||||
return null;
|
return undefined;
|
||||||
}
|
}
|
||||||
const idx = this._disposables!.indexOf(disposable);
|
const idx = this._disposables!.indexOf(disposable);
|
||||||
if (idx >= 0) {
|
if (idx >= 0) {
|
||||||
this._disposables!.splice(idx, 1);
|
this._disposables!.splice(idx, 1);
|
||||||
}
|
}
|
||||||
return null;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
dispose(): void {
|
dispose(): void {
|
||||||
|
@ -65,17 +66,17 @@ export class Disposables {
|
||||||
for (const d of this._disposables) {
|
for (const d of this._disposables) {
|
||||||
disposeValue(d);
|
disposeValue(d);
|
||||||
}
|
}
|
||||||
this._disposables = null;
|
this._disposables = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get isDisposed(): boolean {
|
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) {
|
if (value === undefined || value === null || this.isDisposed) {
|
||||||
return null;
|
return undefined;
|
||||||
}
|
}
|
||||||
const idx = this._disposables!.indexOf(value);
|
const idx = this._disposables!.indexOf(value);
|
||||||
if (idx !== -1) {
|
if (idx !== -1) {
|
||||||
|
@ -84,6 +85,6 @@ export class Disposables {
|
||||||
} else {
|
} else {
|
||||||
console.warn("disposable not found, did it leak?", value);
|
console.warn("disposable not found, did it leak?", value);
|
||||||
}
|
}
|
||||||
return null;
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue