From d73dea797a51e6c5a3b375adbf2249535d3b958e Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Wed, 29 Sep 2021 17:18:22 -0700 Subject: [PATCH 01/11] Rename BaseObservable to TypeScript --- src/observable/{BaseObservable.js => BaseObservable.ts} | 0 src/observable/ObservableValue.js | 2 +- src/observable/list/BaseObservableList.js | 2 +- src/observable/map/BaseObservableMap.js | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename src/observable/{BaseObservable.js => BaseObservable.ts} (100%) diff --git a/src/observable/BaseObservable.js b/src/observable/BaseObservable.ts similarity index 100% rename from src/observable/BaseObservable.js rename to src/observable/BaseObservable.ts diff --git a/src/observable/ObservableValue.js b/src/observable/ObservableValue.js index 8a433444..00e3f021 100644 --- a/src/observable/ObservableValue.js +++ b/src/observable/ObservableValue.js @@ -15,7 +15,7 @@ limitations under the License. */ import {AbortError} from "../utils/error.js"; -import {BaseObservable} from "./BaseObservable.js"; +import {BaseObservable} from "./BaseObservable"; // like an EventEmitter, but doesn't have an event type export class BaseObservableValue extends BaseObservable { diff --git a/src/observable/list/BaseObservableList.js b/src/observable/list/BaseObservableList.js index b3757a13..e2d8c3d5 100644 --- a/src/observable/list/BaseObservableList.js +++ b/src/observable/list/BaseObservableList.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {BaseObservable} from "../BaseObservable.js"; +import {BaseObservable} from "../BaseObservable"; export class BaseObservableList extends BaseObservable { emitReset() { diff --git a/src/observable/map/BaseObservableMap.js b/src/observable/map/BaseObservableMap.js index 79df21f6..d3193931 100644 --- a/src/observable/map/BaseObservableMap.js +++ b/src/observable/map/BaseObservableMap.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {BaseObservable} from "../BaseObservable.js"; +import {BaseObservable} from "../BaseObservable"; export class BaseObservableMap extends BaseObservable { emitReset() { From 319027e2e32e62ec51f0c429fdb8bc01964f012e Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Wed, 29 Sep 2021 17:27:31 -0700 Subject: [PATCH 02/11] Add type annotations to BaseObservable --- src/observable/BaseObservable.ts | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/src/observable/BaseObservable.ts b/src/observable/BaseObservable.ts index 0f9934c3..16bd3b81 100644 --- a/src/observable/BaseObservable.ts +++ b/src/observable/BaseObservable.ts @@ -14,20 +14,18 @@ See the License for the specific language governing permissions and limitations under the License. */ -export class BaseObservable { - constructor() { - this._handlers = new Set(); - } +export abstract class BaseObservable { + protected _handlers: Set = new Set(); - onSubscribeFirst() { + onSubscribeFirst(): void { } - onUnsubscribeLast() { + onUnsubscribeLast(): void { } - subscribe(handler) { + subscribe(handler: T): () => void { this._handlers.add(handler); if (this._handlers.size === 1) { this.onSubscribeFirst(); @@ -37,7 +35,7 @@ export class BaseObservable { }; } - unsubscribe(handler) { + unsubscribe(handler: T | null): null { if (handler) { this._handlers.delete(handler); if (this._handlers.size === 0) { @@ -48,14 +46,14 @@ export class BaseObservable { return null; } - unsubscribeAll() { + unsubscribeAll(): void { if (this._handlers.size !== 0) { this._handlers.clear(); this.onUnsubscribeLast(); } } - get hasSubscriptions() { + get hasSubscriptions(): boolean { return this._handlers.size !== 0; } @@ -63,13 +61,11 @@ export class BaseObservable { } export function tests() { - class Collection extends BaseObservable { - constructor() { - super(); - this.firstSubscribeCalls = 0; - this.firstUnsubscribeCalls = 0; - } - onSubscribeFirst() { this.firstSubscribeCalls += 1; } + class Collection extends BaseObservable<{}> { + firstSubscribeCalls: number= 0; + firstUnsubscribeCalls: number = 0; + + onSubscribeFirst() { this.firstSubscribeCalls += 1; } onUnsubscribeLast() { this.firstUnsubscribeCalls += 1; } } From a7360f409e58a302dad092216ec56df4e72d903f Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Wed, 29 Sep 2021 17:30:21 -0700 Subject: [PATCH 03/11] Rename ObservableValue to TypeScript --- src/domain/navigation/Navigation.js | 2 +- src/domain/session/RoomGridViewModel.js | 2 +- src/domain/session/RoomViewModelObservable.js | 4 ++-- src/domain/session/room/timeline/ReactionsViewModel.js | 2 +- src/matrix/Session.js | 2 +- src/matrix/SessionContainer.js | 2 +- src/matrix/Sync.js | 2 +- src/matrix/net/Reconnector.js | 2 +- src/matrix/room/BaseRoom.js | 2 +- src/matrix/room/ObservedEventMap.js | 2 +- src/mocks/Clock.js | 2 +- src/observable/{ObservableValue.js => ObservableValue.ts} | 0 src/platform/web/dom/History.js | 2 +- src/platform/web/dom/OnlineStatus.js | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) rename src/observable/{ObservableValue.js => ObservableValue.ts} (100%) diff --git a/src/domain/navigation/Navigation.js b/src/domain/navigation/Navigation.js index 3167475f..340ae0d5 100644 --- a/src/domain/navigation/Navigation.js +++ b/src/domain/navigation/Navigation.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {BaseObservableValue, ObservableValue} from "../../observable/ObservableValue.js"; +import {BaseObservableValue, ObservableValue} from "../../observable/ObservableValue"; export class Navigation { constructor(allowsChild) { diff --git a/src/domain/session/RoomGridViewModel.js b/src/domain/session/RoomGridViewModel.js index 08887f3e..d89d821a 100644 --- a/src/domain/session/RoomGridViewModel.js +++ b/src/domain/session/RoomGridViewModel.js @@ -186,7 +186,7 @@ export class RoomGridViewModel extends ViewModel { } import {createNavigation} from "../navigation/index.js"; -import {ObservableValue} from "../../observable/ObservableValue.js"; +import {ObservableValue} from "../../observable/ObservableValue"; export function tests() { class RoomVMMock { diff --git a/src/domain/session/RoomViewModelObservable.js b/src/domain/session/RoomViewModelObservable.js index b9549905..2a706045 100644 --- a/src/domain/session/RoomViewModelObservable.js +++ b/src/domain/session/RoomViewModelObservable.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {ObservableValue} from "../../observable/ObservableValue.js"; +import {ObservableValue} from "../../observable/ObservableValue"; /** Depending on the status of a room (invited, joined, archived, or none), @@ -77,4 +77,4 @@ export class RoomViewModelObservable extends ObservableValue { this.unsubscribeAll(); this.get()?.dispose(); } -} \ No newline at end of file +} diff --git a/src/domain/session/room/timeline/ReactionsViewModel.js b/src/domain/session/room/timeline/ReactionsViewModel.js index ca5d43f0..25d74b49 100644 --- a/src/domain/session/room/timeline/ReactionsViewModel.js +++ b/src/domain/session/room/timeline/ReactionsViewModel.js @@ -189,7 +189,7 @@ import {HomeServer as MockHomeServer} from "../../../../mocks/HomeServer.js"; // other imports import {BaseMessageTile} from "./tiles/BaseMessageTile.js"; import {MappedList} from "../../../../observable/list/MappedList.js"; -import {ObservableValue} from "../../../../observable/ObservableValue.js"; +import {ObservableValue} from "../../../../observable/ObservableValue"; import {PowerLevels} from "../../../../matrix/room/PowerLevels.js"; export function tests() { diff --git a/src/matrix/Session.js b/src/matrix/Session.js index 63aece42..794cd622 100644 --- a/src/matrix/Session.js +++ b/src/matrix/Session.js @@ -40,7 +40,7 @@ import { writeKey as ssssWriteKey, } from "./ssss/index.js"; import {SecretStorage} from "./ssss/SecretStorage.js"; -import {ObservableValue, RetainedObservableValue} from "../observable/ObservableValue.js"; +import {ObservableValue, RetainedObservableValue} from "../observable/ObservableValue"; const PICKLE_KEY = "DEFAULT_KEY"; const PUSHER_KEY = "pusher"; diff --git a/src/matrix/SessionContainer.js b/src/matrix/SessionContainer.js index b58589d0..b645a9ec 100644 --- a/src/matrix/SessionContainer.js +++ b/src/matrix/SessionContainer.js @@ -18,7 +18,7 @@ limitations under the License. import {createEnum} from "../utils/enum.js"; import {lookupHomeserver} from "./well-known.js"; import {AbortableOperation} from "../utils/AbortableOperation"; -import {ObservableValue} from "../observable/ObservableValue.js"; +import {ObservableValue} from "../observable/ObservableValue"; import {HomeServerApi} from "./net/HomeServerApi.js"; import {Reconnector, ConnectionStatus} from "./net/Reconnector.js"; import {ExponentialRetryDelay} from "./net/ExponentialRetryDelay.js"; diff --git a/src/matrix/Sync.js b/src/matrix/Sync.js index bcd29ab4..e010f90d 100644 --- a/src/matrix/Sync.js +++ b/src/matrix/Sync.js @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {ObservableValue} from "../observable/ObservableValue.js"; +import {ObservableValue} from "../observable/ObservableValue"; import {createEnum} from "../utils/enum.js"; const INCREMENTAL_TIMEOUT = 30000; diff --git a/src/matrix/net/Reconnector.js b/src/matrix/net/Reconnector.js index d3cf790f..6fd2ca94 100644 --- a/src/matrix/net/Reconnector.js +++ b/src/matrix/net/Reconnector.js @@ -15,7 +15,7 @@ limitations under the License. */ import {createEnum} from "../../utils/enum.js"; -import {ObservableValue} from "../../observable/ObservableValue.js"; +import {ObservableValue} from "../../observable/ObservableValue"; export const ConnectionStatus = createEnum( "Waiting", diff --git a/src/matrix/room/BaseRoom.js b/src/matrix/room/BaseRoom.js index aac962ac..43b2cf51 100644 --- a/src/matrix/room/BaseRoom.js +++ b/src/matrix/room/BaseRoom.js @@ -29,7 +29,7 @@ import {ObservedEventMap} from "./ObservedEventMap.js"; import {DecryptionSource} from "../e2ee/common.js"; import {ensureLogItem} from "../../logging/utils.js"; import {PowerLevels} from "./PowerLevels.js"; -import {RetainedObservableValue} from "../../observable/ObservableValue.js"; +import {RetainedObservableValue} from "../../observable/ObservableValue"; const EVENT_ENCRYPTED_TYPE = "m.room.encrypted"; diff --git a/src/matrix/room/ObservedEventMap.js b/src/matrix/room/ObservedEventMap.js index 59f0e26a..6b20f85e 100644 --- a/src/matrix/room/ObservedEventMap.js +++ b/src/matrix/room/ObservedEventMap.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {BaseObservableValue} from "../../observable/ObservableValue.js"; +import {BaseObservableValue} from "../../observable/ObservableValue"; export class ObservedEventMap { constructor(notifyEmpty) { diff --git a/src/mocks/Clock.js b/src/mocks/Clock.js index 97b7462a..440c4cb4 100644 --- a/src/mocks/Clock.js +++ b/src/mocks/Clock.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {ObservableValue} from "../observable/ObservableValue.js"; +import {ObservableValue} from "../observable/ObservableValue"; class Timeout { constructor(elapsed, ms) { diff --git a/src/observable/ObservableValue.js b/src/observable/ObservableValue.ts similarity index 100% rename from src/observable/ObservableValue.js rename to src/observable/ObservableValue.ts diff --git a/src/platform/web/dom/History.js b/src/platform/web/dom/History.js index 68e4ef78..d51974bb 100644 --- a/src/platform/web/dom/History.js +++ b/src/platform/web/dom/History.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {BaseObservableValue} from "../../../observable/ObservableValue.js"; +import {BaseObservableValue} from "../../../observable/ObservableValue"; export class History extends BaseObservableValue { handleEvent(event) { diff --git a/src/platform/web/dom/OnlineStatus.js b/src/platform/web/dom/OnlineStatus.js index 588c0815..48e4e912 100644 --- a/src/platform/web/dom/OnlineStatus.js +++ b/src/platform/web/dom/OnlineStatus.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {BaseObservableValue} from "../../../observable/ObservableValue.js"; +import {BaseObservableValue} from "../../../observable/ObservableValue"; export class OnlineStatus extends BaseObservableValue { constructor() { From ab6ce62551be555b3d07c635a66256072933063c Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Wed, 29 Sep 2021 17:43:17 -0700 Subject: [PATCH 04/11] Add type annotations to ObservableValue --- src/observable/ObservableValue.ts | 52 ++++++++++++++++++------------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/src/observable/ObservableValue.ts b/src/observable/ObservableValue.ts index 00e3f021..02d5fc69 100644 --- a/src/observable/ObservableValue.ts +++ b/src/observable/ObservableValue.ts @@ -18,18 +18,16 @@ import {AbortError} from "../utils/error.js"; import {BaseObservable} from "./BaseObservable"; // like an EventEmitter, but doesn't have an event type -export class BaseObservableValue extends BaseObservable { - emit(argument) { +export abstract class BaseObservableValue extends BaseObservable<(value: T) => void> { + emit(argument: T) { for (const h of this._handlers) { h(argument); } } - get() { - throw new Error("unimplemented"); - } + abstract get(): T; - waitFor(predicate) { + waitFor(predicate: (value: T) => boolean): IWaitHandle { if (predicate(this.get())) { return new ResolvedWaitForHandle(Promise.resolve(this.get())); } else { @@ -38,8 +36,17 @@ export class BaseObservableValue extends BaseObservable { } } -class WaitForHandle { - constructor(observable, predicate) { +interface IWaitHandle { + promise: Promise; + dispose(): void; +} + +class WaitForHandle implements IWaitHandle { + private _promise: Promise + private _reject: ((reason?: any) => void) | null; + private _subscription: (() => void) | null; + + constructor(observable: BaseObservableValue, predicate: (value: T) => boolean) { this._promise = new Promise((resolve, reject) => { this._reject = reject; this._subscription = observable.subscribe(v => { @@ -52,7 +59,7 @@ class WaitForHandle { }); } - get promise() { + get promise(): Promise { return this._promise; } @@ -68,25 +75,24 @@ class WaitForHandle { } } -class ResolvedWaitForHandle { - constructor(promise) { - this.promise = promise; - } - +class ResolvedWaitForHandle implements IWaitHandle { + constructor(public promise: Promise) {} dispose() {} } -export class ObservableValue extends BaseObservableValue { - constructor(initialValue) { +export class ObservableValue extends BaseObservableValue { + private _value: T; + + constructor(initialValue: T) { super(); this._value = initialValue; } - get() { + get(): T { return this._value; } - set(value) { + set(value: T): void { if (value !== this._value) { this._value = value; this.emit(this._value); @@ -94,8 +100,10 @@ export class ObservableValue extends BaseObservableValue { } } -export class RetainedObservableValue extends ObservableValue { - constructor(initialValue, freeCallback) { +export class RetainedObservableValue extends ObservableValue { + private _freeCallback: () => void; + + constructor(initialValue: T, freeCallback: () => void) { super(initialValue); this._freeCallback = freeCallback; } @@ -109,7 +117,7 @@ export class RetainedObservableValue extends ObservableValue { export function tests() { return { "set emits an update": assert => { - const a = new ObservableValue(); + const a = new ObservableValue(0); let fired = false; const subscription = a.subscribe(v => { fired = true; @@ -140,7 +148,7 @@ export function tests() { assert.strictEqual(a.get(), 6); }, "waitFor promise rejects when disposed": async assert => { - const a = new ObservableValue(); + const a = new ObservableValue(0); const handle = a.waitFor(() => false); Promise.resolve().then(() => { handle.dispose(); From 3952c3b9699c3e9b2edbc7b7ecbff85c5b9d506d Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Wed, 29 Sep 2021 17:46:51 -0700 Subject: [PATCH 05/11] Rename BaseObservableList to TypeScript --- src/domain/session/room/timeline/TilesCollection.js | 2 +- src/observable/list/BaseMappedList.js | 2 +- .../list/{BaseObservableList.js => BaseObservableList.ts} | 0 src/observable/list/ConcatList.js | 2 +- src/observable/list/MappedList.js | 2 +- src/observable/list/ObservableArray.js | 2 +- src/observable/list/SortedArray.js | 2 +- src/observable/list/SortedMapList.js | 2 +- src/platform/web/ui/general/ListView.ts | 2 +- src/platform/web/ui/session/room/TimelineView.ts | 2 +- 10 files changed, 9 insertions(+), 9 deletions(-) rename src/observable/list/{BaseObservableList.js => BaseObservableList.ts} (100%) diff --git a/src/domain/session/room/timeline/TilesCollection.js b/src/domain/session/room/timeline/TilesCollection.js index 497c0b0d..b3696fad 100644 --- a/src/domain/session/room/timeline/TilesCollection.js +++ b/src/domain/session/room/timeline/TilesCollection.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {BaseObservableList} from "../../../../observable/list/BaseObservableList.js"; +import {BaseObservableList} from "../../../../observable/list/BaseObservableList"; import {sortedIndex} from "../../../../utils/sortedIndex.js"; // maps 1..n entries to 0..1 tile. Entries are what is stored in the timeline, either an event or fragmentboundary diff --git a/src/observable/list/BaseMappedList.js b/src/observable/list/BaseMappedList.js index 1ccd4e12..19c98b88 100644 --- a/src/observable/list/BaseMappedList.js +++ b/src/observable/list/BaseMappedList.js @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {BaseObservableList} from "./BaseObservableList.js"; +import {BaseObservableList} from "./BaseObservableList"; import {findAndUpdateInArray} from "./common.js"; export class BaseMappedList extends BaseObservableList { diff --git a/src/observable/list/BaseObservableList.js b/src/observable/list/BaseObservableList.ts similarity index 100% rename from src/observable/list/BaseObservableList.js rename to src/observable/list/BaseObservableList.ts diff --git a/src/observable/list/ConcatList.js b/src/observable/list/ConcatList.js index d395e807..e87ccb60 100644 --- a/src/observable/list/ConcatList.js +++ b/src/observable/list/ConcatList.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {BaseObservableList} from "./BaseObservableList.js"; +import {BaseObservableList} from "./BaseObservableList"; export class ConcatList extends BaseObservableList { constructor(...sourceLists) { diff --git a/src/observable/list/MappedList.js b/src/observable/list/MappedList.js index 096a018f..dbd64921 100644 --- a/src/observable/list/MappedList.js +++ b/src/observable/list/MappedList.js @@ -57,7 +57,7 @@ export class MappedList extends BaseMappedList { } import {ObservableArray} from "./ObservableArray.js"; -import {BaseObservableList} from "./BaseObservableList.js"; +import {BaseObservableList} from "./BaseObservableList"; export async function tests() { class MockList extends BaseObservableList { diff --git a/src/observable/list/ObservableArray.js b/src/observable/list/ObservableArray.js index 0f9ee99d..5d9f5c12 100644 --- a/src/observable/list/ObservableArray.js +++ b/src/observable/list/ObservableArray.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {BaseObservableList} from "./BaseObservableList.js"; +import {BaseObservableList} from "./BaseObservableList"; export class ObservableArray extends BaseObservableList { constructor(initialValues = []) { diff --git a/src/observable/list/SortedArray.js b/src/observable/list/SortedArray.js index 323cf776..29e0c3f1 100644 --- a/src/observable/list/SortedArray.js +++ b/src/observable/list/SortedArray.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {BaseObservableList} from "./BaseObservableList.js"; +import {BaseObservableList} from "./BaseObservableList"; import {sortedIndex} from "../../utils/sortedIndex.js"; import {findAndUpdateInArray} from "./common.js"; diff --git a/src/observable/list/SortedMapList.js b/src/observable/list/SortedMapList.js index babf5c35..2421419e 100644 --- a/src/observable/list/SortedMapList.js +++ b/src/observable/list/SortedMapList.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {BaseObservableList} from "./BaseObservableList.js"; +import {BaseObservableList} from "./BaseObservableList"; import {sortedIndex} from "../../utils/sortedIndex.js"; /* diff --git a/src/platform/web/ui/general/ListView.ts b/src/platform/web/ui/general/ListView.ts index cdfdbf76..c3ac49a4 100644 --- a/src/platform/web/ui/general/ListView.ts +++ b/src/platform/web/ui/general/ListView.ts @@ -16,7 +16,7 @@ limitations under the License. import {el} from "./html"; import {mountView, insertAt} from "./utils"; -import {BaseObservableList as ObservableList} from "../../../../observable/list/BaseObservableList.js"; +import {BaseObservableList as ObservableList} from "../../../../observable/list/BaseObservableList"; import {IView, IMountArgs} from "./types"; interface IOptions { diff --git a/src/platform/web/ui/session/room/TimelineView.ts b/src/platform/web/ui/session/room/TimelineView.ts index a4e143aa..23cf0aac 100644 --- a/src/platform/web/ui/session/room/TimelineView.ts +++ b/src/platform/web/ui/session/room/TimelineView.ts @@ -26,7 +26,7 @@ import {MissingAttachmentView} from "./timeline/MissingAttachmentView.js"; import {AnnouncementView} from "./timeline/AnnouncementView.js"; import {RedactedView} from "./timeline/RedactedView.js"; import {SimpleTile} from "../../../../../domain/session/room/timeline/tiles/SimpleTile.js"; -import {BaseObservableList as ObservableList} from "../../../../../observable/list/BaseObservableList.js"; +import {BaseObservableList as ObservableList} from "../../../../../observable/list/BaseObservableList"; //import {TimelineViewModel} from "../../../../../domain/session/room/timeline/TimelineViewModel.js"; export interface TimelineViewModel extends IObservableValue { From 414280ada9f28fcbc739243abfb5a145a1e0d4a3 Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Wed, 29 Sep 2021 18:05:30 -0700 Subject: [PATCH 06/11] Add type annotations to BaseObservableList --- src/observable/list/BaseObservableList.ts | 27 +++++++++++++---------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/observable/list/BaseObservableList.ts b/src/observable/list/BaseObservableList.ts index e2d8c3d5..9ba6559a 100644 --- a/src/observable/list/BaseObservableList.ts +++ b/src/observable/list/BaseObservableList.ts @@ -16,7 +16,15 @@ limitations under the License. import {BaseObservable} from "../BaseObservable"; -export class BaseObservableList extends BaseObservable { +export interface IListObserver { + onReset(list: BaseObservableList): void; + onAdd(index: number, value:T, list: BaseObservableList): void; + onUpdate(index: number, value: T, params: any, list: BaseObservableList): void; + onRemove(index: number, value: T, list: BaseObservableList): void + onMove(from: number, to: number, value: T, list: BaseObservableList): void +} + +export abstract class BaseObservableList extends BaseObservable> { emitReset() { for(let h of this._handlers) { h.onReset(this); @@ -24,19 +32,19 @@ export class BaseObservableList extends BaseObservable { } // we need batch events, mostly on index based collection though? // maybe we should get started without? - emitAdd(index, value) { + emitAdd(index: number, value: T): void { for(let h of this._handlers) { h.onAdd(index, value, this); } } - emitUpdate(index, value, params) { + emitUpdate(index: number, value: T, params: any): void { for(let h of this._handlers) { h.onUpdate(index, value, params, this); } } - emitRemove(index, value) { + emitRemove(index: number, value: T): void { for(let h of this._handlers) { h.onRemove(index, value, this); } @@ -44,17 +52,12 @@ export class BaseObservableList extends BaseObservable { // toIdx assumes the item has already // been removed from its fromIdx - emitMove(fromIdx, toIdx, value) { + emitMove(fromIdx: number, toIdx: number, value: T): void { for(let h of this._handlers) { h.onMove(fromIdx, toIdx, value, this); } } - [Symbol.iterator]() { - throw new Error("unimplemented"); - } - - get length() { - throw new Error("unimplemented"); - } + abstract [Symbol.iterator](): IterableIterator; + abstract get length(): number; } From 64ba656043703f12e48e7fdf056b9021b3f6acf5 Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Wed, 29 Sep 2021 18:08:13 -0700 Subject: [PATCH 07/11] Update ListView and TimelineListView --- src/platform/web/ui/general/ListView.ts | 21 +++++++++++++------ .../web/ui/session/room/TimelineView.ts | 13 ++++++++---- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/platform/web/ui/general/ListView.ts b/src/platform/web/ui/general/ListView.ts index c3ac49a4..e6d15755 100644 --- a/src/platform/web/ui/general/ListView.ts +++ b/src/platform/web/ui/general/ListView.ts @@ -27,7 +27,7 @@ interface IOptions { parentProvidesUpdates?: boolean } -type SubscriptionHandle = () => undefined; +type SubscriptionHandle = () => void; export class ListView implements IView { @@ -115,7 +115,8 @@ export class ListView implements IView { } private _unloadList() { - this._subscription = this._subscription!(); + this._subscription!() + this._subscription = undefined; for (let child of this._childInstances!) { child.unmount(); } @@ -137,26 +138,34 @@ export class ListView implements IView { this._root!.appendChild(fragment); } - protected onAdd(idx: number, value: T) { + onReset() { + for (const child of this._childInstances!) { + child.root()!.remove(); + child.unmount(); + } + this._childInstances!.length = 0; + } + + onAdd(idx: number, value: T) { const child = this._childCreator(value); this._childInstances!.splice(idx, 0, child); insertAt(this._root!, idx, mountView(child, this._mountArgs)); } - protected onRemove(idx: number, value: T) { + onRemove(idx: number, value: T) { const [child] = this._childInstances!.splice(idx, 1); child.root()!.remove(); child.unmount(); } - protected onMove(fromIdx: number, toIdx: number, value: T) { + onMove(fromIdx: number, toIdx: number, value: T) { const [child] = this._childInstances!.splice(fromIdx, 1); this._childInstances!.splice(toIdx, 0, child); child.root()!.remove(); insertAt(this._root!, toIdx, child.root()! as Element); } - protected onUpdate(i: number, value: T, params: any) { + onUpdate(i: number, value: T, params: any) { if (this._childInstances) { const instance = this._childInstances![i]; instance && instance.update(value, params); diff --git a/src/platform/web/ui/session/room/TimelineView.ts b/src/platform/web/ui/session/room/TimelineView.ts index 23cf0aac..da3dc537 100644 --- a/src/platform/web/ui/session/room/TimelineView.ts +++ b/src/platform/web/ui/session/room/TimelineView.ts @@ -211,7 +211,12 @@ class TilesListView extends ListView { this.onChanged = onChanged; } - protected onUpdate(index: number, value: SimpleTile, param: any) { + onReset() { + super.onReset(); + this.onChanged(); + } + + onUpdate(index: number, value: SimpleTile, param: any) { if (param === "shape") { const ExpectedClass = viewClassForEntry(value); const child = this.getChildInstanceByIndex(index); @@ -227,17 +232,17 @@ class TilesListView extends ListView { this.onChanged(); } - protected onAdd(idx: number, value: SimpleTile) { + onAdd(idx: number, value: SimpleTile) { super.onAdd(idx, value); this.onChanged(); } - protected onRemove(idx: number, value: SimpleTile) { + onRemove(idx: number, value: SimpleTile) { super.onRemove(idx, value); this.onChanged(); } - protected onMove(fromIdx: number, toIdx: number, value: SimpleTile) { + onMove(fromIdx: number, toIdx: number, value: SimpleTile) { super.onMove(fromIdx, toIdx, value); this.onChanged(); } From e53f3d23d5c891ef39f3fa38df162b684089a3d5 Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Wed, 29 Sep 2021 18:10:09 -0700 Subject: [PATCH 08/11] Rename common to TypeScript --- src/observable/list/BaseMappedList.js | 2 +- src/observable/list/SortedArray.js | 2 +- src/observable/list/{common.js => common.ts} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename src/observable/list/{common.js => common.ts} (100%) diff --git a/src/observable/list/BaseMappedList.js b/src/observable/list/BaseMappedList.js index 19c98b88..314c083e 100644 --- a/src/observable/list/BaseMappedList.js +++ b/src/observable/list/BaseMappedList.js @@ -16,7 +16,7 @@ limitations under the License. */ import {BaseObservableList} from "./BaseObservableList"; -import {findAndUpdateInArray} from "./common.js"; +import {findAndUpdateInArray} from "./common"; export class BaseMappedList extends BaseObservableList { constructor(sourceList, mapper, updater, removeCallback) { diff --git a/src/observable/list/SortedArray.js b/src/observable/list/SortedArray.js index 29e0c3f1..874d1b04 100644 --- a/src/observable/list/SortedArray.js +++ b/src/observable/list/SortedArray.js @@ -16,7 +16,7 @@ limitations under the License. import {BaseObservableList} from "./BaseObservableList"; import {sortedIndex} from "../../utils/sortedIndex.js"; -import {findAndUpdateInArray} from "./common.js"; +import {findAndUpdateInArray} from "./common"; export class SortedArray extends BaseObservableList { constructor(comparator) { diff --git a/src/observable/list/common.js b/src/observable/list/common.ts similarity index 100% rename from src/observable/list/common.js rename to src/observable/list/common.ts From bf53449f66668e635b5c33629fb1542273ef9c71 Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Wed, 29 Sep 2021 18:13:49 -0700 Subject: [PATCH 09/11] Add type annotations to common --- src/observable/list/common.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/observable/list/common.ts b/src/observable/list/common.ts index 8dbf7873..c67a841b 100644 --- a/src/observable/list/common.ts +++ b/src/observable/list/common.ts @@ -14,9 +14,10 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ +import {BaseObservableList} from "./BaseObservableList"; /* inline update of item in collection backed by array, without replacing the preexising item */ -export function findAndUpdateInArray(predicate, array, observable, updater) { +export function findAndUpdateInArray(predicate: (value: T) => boolean, array: T[], observable: BaseObservableList, updater: (value: T) => any | false) { const index = array.findIndex(predicate); if (index !== -1) { const value = array[index]; @@ -29,4 +30,4 @@ export function findAndUpdateInArray(predicate, array, observable, updater) { return true; } return false; -} \ No newline at end of file +} From 99164eb0d8971489832e57050cb410bed007e040 Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Wed, 29 Sep 2021 18:17:38 -0700 Subject: [PATCH 10/11] Rename BaseMappedList to TypeScript --- src/observable/list/AsyncMappedList.js | 2 +- src/observable/list/{BaseMappedList.js => BaseMappedList.ts} | 0 src/observable/list/MappedList.js | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename src/observable/list/{BaseMappedList.js => BaseMappedList.ts} (100%) diff --git a/src/observable/list/AsyncMappedList.js b/src/observable/list/AsyncMappedList.js index 604d8e94..9af1f95a 100644 --- a/src/observable/list/AsyncMappedList.js +++ b/src/observable/list/AsyncMappedList.js @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {BaseMappedList, runAdd, runUpdate, runRemove, runMove, runReset} from "./BaseMappedList.js"; +import {BaseMappedList, runAdd, runUpdate, runRemove, runMove, runReset} from "./BaseMappedList"; export class AsyncMappedList extends BaseMappedList { constructor(sourceList, mapper, updater, removeCallback) { diff --git a/src/observable/list/BaseMappedList.js b/src/observable/list/BaseMappedList.ts similarity index 100% rename from src/observable/list/BaseMappedList.js rename to src/observable/list/BaseMappedList.ts diff --git a/src/observable/list/MappedList.js b/src/observable/list/MappedList.js index dbd64921..c799fcff 100644 --- a/src/observable/list/MappedList.js +++ b/src/observable/list/MappedList.js @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {BaseMappedList, runAdd, runUpdate, runRemove, runMove, runReset} from "./BaseMappedList.js"; +import {BaseMappedList, runAdd, runUpdate, runRemove, runMove, runReset} from "./BaseMappedList"; export class MappedList extends BaseMappedList { onSubscribeFirst() { From c80dfb10a2f103165dfcddce5da92205560845f0 Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Wed, 29 Sep 2021 18:41:30 -0700 Subject: [PATCH 11/11] Add type annotations to BaseMappedList --- src/observable/list/BaseMappedList.ts | 48 ++++++++++++++++----------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/src/observable/list/BaseMappedList.ts b/src/observable/list/BaseMappedList.ts index 314c083e..f339d113 100644 --- a/src/observable/list/BaseMappedList.ts +++ b/src/observable/list/BaseMappedList.ts @@ -18,60 +18,68 @@ limitations under the License. import {BaseObservableList} from "./BaseObservableList"; import {findAndUpdateInArray} from "./common"; -export class BaseMappedList extends BaseObservableList { - constructor(sourceList, mapper, updater, removeCallback) { +export type Mapper = (value: F) => T +export type Updater = (mappedValue: T, params: any, value: F) => void; + +export class BaseMappedList extends BaseObservableList { + protected _sourceList: BaseObservableList; + protected _sourceUnsubscribe: (() => void) | null = null; + _mapper: Mapper; + _updater: Updater; + _removeCallback: ((value: T) => void) | null; + _mappedValues: T[] | null = null; + + constructor(sourceList: BaseObservableList, mapper: Mapper, updater: Updater, removeCallback: ((value: T) => void) | null = null) { super(); this._sourceList = sourceList; this._mapper = mapper; this._updater = updater; this._removeCallback = removeCallback; - this._mappedValues = null; - this._sourceUnsubscribe = null; } - findAndUpdate(predicate, updater) { - return findAndUpdateInArray(predicate, this._mappedValues, this, updater); + findAndUpdate(predicate: (value: T) => boolean, updater: (value: T) => any | false) { + return findAndUpdateInArray(predicate, this._mappedValues!, this, updater); } get length() { - return this._mappedValues.length; + return this._mappedValues!.length; } [Symbol.iterator]() { - return this._mappedValues.values(); + return this._mappedValues!.values(); } } -export function runAdd(list, index, mappedValue) { - list._mappedValues.splice(index, 0, mappedValue); +export function runAdd(list: BaseMappedList, index: number, mappedValue: T): void { + list._mappedValues!.splice(index, 0, mappedValue); list.emitAdd(index, mappedValue); } -export function runUpdate(list, index, value, params) { - const mappedValue = list._mappedValues[index]; +export function runUpdate(list: BaseMappedList, index: number, value: F, params: any): void { + const mappedValue = list._mappedValues![index]; if (list._updater) { list._updater(mappedValue, params, value); } list.emitUpdate(index, mappedValue, params); } -export function runRemove(list, index) { - const mappedValue = list._mappedValues[index]; - list._mappedValues.splice(index, 1); +export function runRemove(list: BaseMappedList, index: number): void { + const mappedValue = list._mappedValues![index]; + list._mappedValues!.splice(index, 1); if (list._removeCallback) { list._removeCallback(mappedValue); } list.emitRemove(index, mappedValue); } -export function runMove(list, fromIdx, toIdx) { - const mappedValue = list._mappedValues[fromIdx]; - list._mappedValues.splice(fromIdx, 1); - list._mappedValues.splice(toIdx, 0, mappedValue); +export function runMove(list: BaseMappedList, fromIdx: number, toIdx: number): void { + const mappedValue = list._mappedValues![fromIdx]; + list._mappedValues!.splice(fromIdx, 1); + list._mappedValues!.splice(toIdx, 0, mappedValue); list.emitMove(fromIdx, toIdx, mappedValue); } -export function runReset(list) { +export function runReset(list: BaseMappedList): void { list._mappedValues = []; list.emitReset(); }