From b33db1df3643dfdaf6b674cb6600fae3ff6c2341 Mon Sep 17 00:00:00 2001 From: Isaiah Becker-Mayer Date: Mon, 4 Jul 2022 19:14:34 -0400 Subject: [PATCH 01/24] refactors ObservableMap --- .eslintrc.js | 34 +-- src/domain/SessionPickerViewModel.js | 2 +- .../room/timeline/ReactionsViewModel.js | 2 +- src/matrix/Session.js | 8 +- src/matrix/room/members/MemberList.js | 2 +- src/matrix/room/timeline/Timeline.js | 14 +- src/observable/{index.js => index.ts} | 32 +- src/observable/list/SortedMapList.js | 4 +- src/observable/map/BaseObservableMap.ts | 15 + src/observable/map/FilteredMap.js | 2 +- src/observable/map/JoinedMap.js | 2 +- src/observable/map/ObservableMap.ts | 288 ++++++++++-------- 12 files changed, 234 insertions(+), 171 deletions(-) rename src/observable/{index.js => index.ts} (56%) diff --git a/.eslintrc.js b/.eslintrc.js index cb28f4c8..cf1fc3bf 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,25 +1,25 @@ module.exports = { - "env": { + root: true, + env: { "browser": true, "es6": true }, - "extends": "eslint:recommended", - "parserOptions": { + extends: [ + // "plugin:@typescript-eslint/recommended", + // "plugin:@typescript-eslint/recommended-requiring-type-checking", + ], + parser: '@typescript-eslint/parser', + parserOptions: { "ecmaVersion": 2020, - "sourceType": "module" + "sourceType": "module", + "project": "./tsconfig.json" }, - "rules": { - "no-console": "off", - "no-empty": "off", - "no-prototype-builtins": "off", - "no-unused-vars": "warn" - }, - "globals": { - "DEFINE_VERSION": "readonly", - "DEFINE_GLOBAL_HASH": "readonly", - // only available in sw.js - "DEFINE_UNHASHED_PRECACHED_ASSETS": "readonly", - "DEFINE_HASHED_PRECACHED_ASSETS": "readonly", - "DEFINE_HASHED_CACHED_ON_REQUEST_ASSETS": "readonly" + plugins: [ + '@typescript-eslint', + ], + rules: { + "@typescript-eslint/no-floating-promises": 2, + "@typescript-eslint/no-misused-promises": 2, + "semi": ["error", "always"] } }; diff --git a/src/domain/SessionPickerViewModel.js b/src/domain/SessionPickerViewModel.js index e486c64f..f4a16f1c 100644 --- a/src/domain/SessionPickerViewModel.js +++ b/src/domain/SessionPickerViewModel.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {SortedArray} from "../observable/index.js"; +import {SortedArray} from "../observable"; import {ViewModel} from "./ViewModel"; import {avatarInitials, getIdentifierColorNumber} from "./avatar"; diff --git a/src/domain/session/room/timeline/ReactionsViewModel.js b/src/domain/session/room/timeline/ReactionsViewModel.js index 1977b6f4..c5ea1224 100644 --- a/src/domain/session/room/timeline/ReactionsViewModel.js +++ b/src/domain/session/room/timeline/ReactionsViewModel.js @@ -13,7 +13,7 @@ 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 {ObservableMap} from "../../../../observable/map/ObservableMap"; +import {ObservableMap} from "../../../../observable"; export class ReactionsViewModel { constructor(parentTile) { diff --git a/src/matrix/Session.js b/src/matrix/Session.js index ae1dea61..048ddbc8 100644 --- a/src/matrix/Session.js +++ b/src/matrix/Session.js @@ -21,7 +21,7 @@ import {RoomStatus} from "./room/common"; import {RoomBeingCreated} from "./room/RoomBeingCreated"; import {Invite} from "./room/Invite.js"; import {Pusher} from "./push/Pusher"; -import { ObservableMap } from "../observable/index.js"; +import {ObservableMap} from "../observable"; import {User} from "./User.js"; import {DeviceMessageHandler} from "./DeviceMessageHandler.js"; import {Account as E2EEAccount} from "./e2ee/Account.js"; @@ -192,7 +192,7 @@ export class Session { /** * Enable secret storage by providing the secret storage credential. * This will also see if there is a megolm key backup and try to enable that if so. - * + * * @param {string} type either "passphrase" or "recoverykey" * @param {string} credential either the passphrase or the recovery key, depending on the type * @return {Promise} resolves or rejects after having tried to enable secret storage @@ -663,7 +663,7 @@ export class Session { if (this._e2eeAccount && deviceOneTimeKeysCount) { changes.e2eeAccountChanges = this._e2eeAccount.writeSync(deviceOneTimeKeysCount, txn, log); } - + const deviceLists = syncResponse.device_lists; if (this._deviceTracker && Array.isArray(deviceLists?.changed) && deviceLists.changed.length) { await log.wrap("deviceLists", log => this._deviceTracker.writeDeviceChanges(deviceLists.changed, txn, log)); @@ -908,7 +908,7 @@ export class Session { Creates an empty (summary isn't loaded) the archived room if it isn't loaded already, assuming sync will either remove it (when rejoining) or write a full summary adopting it from the joined room when leaving - + @internal */ createOrGetArchivedRoomForSync(roomId) { diff --git a/src/matrix/room/members/MemberList.js b/src/matrix/room/members/MemberList.js index f32a63d3..74b3fe7d 100644 --- a/src/matrix/room/members/MemberList.js +++ b/src/matrix/room/members/MemberList.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {ObservableMap} from "../../../observable/map/ObservableMap"; +import {ObservableMap} from "../../../observable"; import {RetainedValue} from "../../../utils/RetainedValue"; export class MemberList extends RetainedValue { diff --git a/src/matrix/room/timeline/Timeline.js b/src/matrix/room/timeline/Timeline.js index 3332a5b0..a721092e 100644 --- a/src/matrix/room/timeline/Timeline.js +++ b/src/matrix/room/timeline/Timeline.js @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {SortedArray, AsyncMappedList, ConcatList, ObservableArray} from "../../../observable/index.js"; +import {SortedArray, AsyncMappedList, ConcatList, ObservableArray} from "../../../observable"; import {Disposables} from "../../../utils/Disposables"; import {Direction} from "./Direction"; import {TimelineReader} from "./persistence/TimelineReader.js"; @@ -45,7 +45,7 @@ export class Timeline { }); this._readerRequest = null; this._allEntries = null; - /** Stores event entries that we had to fetch from hs/storage for reply previews (because they were not in timeline) */ + /** Stores event entries that we had to fetch from hs/storage for reply previews (because they were not in timeline) */ this._contextEntriesNotInTimeline = new Map(); /** Only used to decrypt non-persisted context entries fetched from the homeserver */ this._decryptEntries = null; @@ -189,7 +189,7 @@ export class Timeline { // before it has any subscriptions, we bail out if this isn't // the case yet. This can happen when sync adds or replaces entries // before load has finished and the view has subscribed to the timeline. - // + // // Once the subscription is setup, MappedList will set up the local // relations as needed with _applyAndEmitLocalRelationChange, // so we're not missing anything by bailing out. @@ -239,7 +239,7 @@ export class Timeline { if (err.name === "CompareError") { // see FragmentIdComparer, if the replacing entry is on a fragment // that is currently not loaded into the FragmentIdComparer, it will - // throw a CompareError, and it means that the event is not loaded + // throw a CompareError, and it means that the event is not loaded // in the timeline (like when receiving a relation for an event // that is not loaded in memory) so we can just drop this error as // replacing an event that is not already loaded is a no-op. @@ -311,7 +311,7 @@ export class Timeline { * - timeline * - storage * - homeserver - * @param {EventEntry[]} entries + * @param {EventEntry[]} entries */ async _loadContextEntriesWhereNeeded(entries) { for (const entry of entries) { @@ -392,7 +392,7 @@ export class Timeline { * [loadAtTop description] * @param {[type]} amount [description] * @return {boolean} true if the top of the timeline has been reached - * + * */ async loadAtTop(amount) { if (this._disposables.isDisposed) { @@ -547,7 +547,7 @@ export function tests() { content: {}, relatedEventId: event2.event_id }})); - // 4. subscribe (it's now safe to iterate timeline.entries) + // 4. subscribe (it's now safe to iterate timeline.entries) timeline.entries.subscribe(new ListObserver()); // 5. check the local relation got correctly aggregated const locallyRedacted = await poll(() => Array.from(timeline.entries)[0].isRedacting); diff --git a/src/observable/index.js b/src/observable/index.ts similarity index 56% rename from src/observable/index.js rename to src/observable/index.ts index 6057174b..1b05f798 100644 --- a/src/observable/index.js +++ b/src/observable/index.ts @@ -18,14 +18,40 @@ import {SortedMapList} from "./list/SortedMapList.js"; import {FilteredMap} from "./map/FilteredMap.js"; import {MappedMap} from "./map/MappedMap.js"; import {JoinedMap} from "./map/JoinedMap.js"; -import {BaseObservableMap} from "./map/BaseObservableMap"; +import {BaseObservableMap, BaseObservableMapConfig} from "./map/BaseObservableMap"; +import {ObservableMapInternal} from "./map/ObservableMap"; // re-export "root" (of chain) collections export { ObservableArray } from "./list/ObservableArray"; export { SortedArray } from "./list/SortedArray"; export { MappedList } from "./list/MappedList"; export { AsyncMappedList } from "./list/AsyncMappedList"; export { ConcatList } from "./list/ConcatList"; -export { ObservableMap } from "./map/ObservableMap"; + +// avoid circular dependency between these classes +// and BaseObservableMap (as they extend it) +function config(): BaseObservableMapConfig { + return { + join: (_this: BaseObservableMap, ...otherMaps: Array>): JoinedMap => { + return new JoinedMap([_this].concat(otherMaps)); + }, + mapValues: (_this: BaseObservableMap, mapper: any, updater?: (params: any) => void): MappedMap => { + return new MappedMap(_this, mapper, updater); + }, + sortValues: (_this: BaseObservableMap, comparator?: (a: any, b: any) => number): SortedMapList => { + return new SortedMapList(_this, comparator); + }, + filterValues: (_this: BaseObservableMap, filter: (v: V, k: K) => boolean): FilteredMap => { + return new FilteredMap(_this, filter); + } + }; +}; + + +export class ObservableMap extends ObservableMapInternal { + constructor(initialValues?: (readonly [K, V])[]) { + super(config(), initialValues); + } +} // avoid circular dependency between these classes // and BaseObservableMap (as they extend it) @@ -45,4 +71,4 @@ Object.assign(BaseObservableMap.prototype, { join(...otherMaps) { return new JoinedMap([this].concat(otherMaps)); } -}); +}); \ No newline at end of file diff --git a/src/observable/list/SortedMapList.js b/src/observable/list/SortedMapList.js index d74dbade..21a3aa55 100644 --- a/src/observable/list/SortedMapList.js +++ b/src/observable/list/SortedMapList.js @@ -53,7 +53,7 @@ export class SortedMapList extends BaseObservableList { this._sortedPairs = null; this._mapSubscription = null; } - + onAdd(key, value) { const pair = {key, value}; const idx = sortedIndex(this._sortedPairs, pair, this._comparator); @@ -133,7 +133,7 @@ export class SortedMapList extends BaseObservableList { } } -import {ObservableMap} from "../map/ObservableMap"; +import {ObservableMap} from "../"; export function tests() { return { diff --git a/src/observable/map/BaseObservableMap.ts b/src/observable/map/BaseObservableMap.ts index 694c017e..39189cdc 100644 --- a/src/observable/map/BaseObservableMap.ts +++ b/src/observable/map/BaseObservableMap.ts @@ -15,6 +15,10 @@ limitations under the License. */ import {BaseObservable} from "../BaseObservable"; +import {JoinedMap} from "../map/JoinedMap.js"; +import {MappedMap} from "../map/MappedMap.js"; +import {FilteredMap} from "../map/FilteredMap.js"; +import {SortedMapList} from "../list/SortedMapList.js"; export interface IMapObserver { onReset(): void; @@ -23,6 +27,13 @@ export interface IMapObserver { onRemove(key: K, value: V): void } +export type BaseObservableMapConfig = { + join(_this: BaseObservableMap, ...otherMaps: Array>): JoinedMap; + mapValues(_this: BaseObservableMap, mapper: any, updater?: (params: any) => void): MappedMap; + sortValues(_this: BaseObservableMap, comparator?: (a: any, b: any) => number): SortedMapList; + filterValues(_this: BaseObservableMap, filter: (v: V, k: K) => boolean): FilteredMap; +} + export abstract class BaseObservableMap extends BaseObservable> { emitReset() { for(let h of this._handlers) { @@ -49,6 +60,10 @@ export abstract class BaseObservableMap extends BaseObservable): JoinedMap; + abstract mapValues(mapper: any, updater?: (params: any) => void): MappedMap; + abstract sortValues(comparator?: (a: any, b: any) => number): SortedMapList; + abstract filterValues(filter: (v: V, k: K) => boolean): FilteredMap; abstract [Symbol.iterator](): Iterator<[K, V]>; abstract get size(): number; abstract get(key: K): V | undefined; diff --git a/src/observable/map/FilteredMap.js b/src/observable/map/FilteredMap.js index d7e11fbe..98dc4650 100644 --- a/src/observable/map/FilteredMap.js +++ b/src/observable/map/FilteredMap.js @@ -166,7 +166,7 @@ class FilterIterator { } } -import {ObservableMap} from "./ObservableMap"; +import {ObservableMap} from "../"; export function tests() { return { "filter preloaded list": assert => { diff --git a/src/observable/map/JoinedMap.js b/src/observable/map/JoinedMap.js index d97c5677..ea5ad784 100644 --- a/src/observable/map/JoinedMap.js +++ b/src/observable/map/JoinedMap.js @@ -191,7 +191,7 @@ class SourceSubscriptionHandler { } -import { ObservableMap } from "./ObservableMap"; +import {ObservableMap} from "../"; export function tests() { diff --git a/src/observable/map/ObservableMap.ts b/src/observable/map/ObservableMap.ts index d604ab0a..ced58df1 100644 --- a/src/observable/map/ObservableMap.ts +++ b/src/observable/map/ObservableMap.ts @@ -14,16 +14,38 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {BaseObservableMap} from "./BaseObservableMap"; +import {BaseObservableMap, BaseObservableMapConfig} from "./BaseObservableMap"; +import {JoinedMap} from "../map/JoinedMap.js"; +import {MappedMap} from "../map/MappedMap.js"; +import {FilteredMap} from "../map/FilteredMap.js"; +import {SortedMapList} from "../list/SortedMapList.js"; -export class ObservableMap extends BaseObservableMap { +export class ObservableMapInternal extends BaseObservableMap { + private _config: BaseObservableMapConfig private readonly _values: Map; - constructor(initialValues?: (readonly [K, V])[]) { + constructor(config: BaseObservableMapConfig, initialValues?: (readonly [K, V])[]) { super(); + this._config = config; this._values = new Map(initialValues); } + join(...otherMaps: Array): JoinedMap { + return this._config.join(this, ...otherMaps); + } + + mapValues(mapper: any, updater?: (params: any) => void): MappedMap{ + return this._config.mapValues(this, mapper, updater); + } + + sortValues(comparator?: (a: any, b: any) => number): SortedMapList { + return this._config.sortValues(this, comparator); + } + + filterValues(filter: (v: V, k: K) => boolean): FilteredMap { + return this._config.filterValues(this, filter); + } + update(key: K, params?: any): boolean { const value = this._values.get(key); if (value !== undefined) { @@ -61,7 +83,7 @@ export class ObservableMap extends BaseObservableMap { // We set the value here because update only supports inline updates this._values.set(key, value); return this.update(key, undefined); - } + } else { return this.add(key, value); } @@ -91,139 +113,139 @@ export class ObservableMap extends BaseObservableMap { keys(): Iterator { return this._values.keys(); } -} +}; -export function tests() { - return { - test_initial_values(assert) { - const map = new ObservableMap([ - ["a", 5], - ["b", 10] - ]); - assert.equal(map.size, 2); - assert.equal(map.get("a"), 5); - assert.equal(map.get("b"), 10); - }, +// export function tests() { +// return { +// test_initial_values(assert) { +// const map = new ObservableMap([ +// ["a", 5], +// ["b", 10] +// ]); +// assert.equal(map.size, 2); +// assert.equal(map.get("a"), 5); +// assert.equal(map.get("b"), 10); +// }, - test_add(assert) { - let fired = 0; - const map = new ObservableMap(); - map.subscribe({ - onAdd(key, value) { - fired += 1; - assert.equal(key, 1); - assert.deepEqual(value, {value: 5}); - }, - onUpdate() {}, - onRemove() {}, - onReset() {} - }); - map.add(1, {value: 5}); - assert.equal(map.size, 1); - assert.equal(fired, 1); - }, +// test_add(assert) { +// let fired = 0; +// const map = new ObservableMap(); +// map.subscribe({ +// onAdd(key, value) { +// fired += 1; +// assert.equal(key, 1); +// assert.deepEqual(value, {value: 5}); +// }, +// onUpdate() {}, +// onRemove() {}, +// onReset() {} +// }); +// map.add(1, {value: 5}); +// assert.equal(map.size, 1); +// assert.equal(fired, 1); +// }, - test_update(assert) { - let fired = 0; - const map = new ObservableMap(); - const value = {number: 5}; - map.add(1, value); - map.subscribe({ - onUpdate(key, value, params) { - fired += 1; - assert.equal(key, 1); - assert.deepEqual(value, {number: 6}); - assert.equal(params, "test"); - }, - onAdd() {}, - onRemove() {}, - onReset() {} - }); - value.number = 6; - map.update(1, "test"); - assert.equal(fired, 1); - }, +// test_update(assert) { +// let fired = 0; +// const map = new ObservableMap(); +// const value = {number: 5}; +// map.add(1, value); +// map.subscribe({ +// onUpdate(key, value, params) { +// fired += 1; +// assert.equal(key, 1); +// assert.deepEqual(value, {number: 6}); +// assert.equal(params, "test"); +// }, +// onAdd() {}, +// onRemove() {}, +// onReset() {} +// }); +// value.number = 6; +// map.update(1, "test"); +// assert.equal(fired, 1); +// }, - test_update_unknown(assert) { - let fired = 0; - const map = new ObservableMap(); - map.subscribe({ - onUpdate() { fired += 1; }, - onAdd() {}, - onRemove() {}, - onReset() {} - }); - const result = map.update(1); - assert.equal(fired, 0); - assert.equal(result, false); - }, +// test_update_unknown(assert) { +// let fired = 0; +// const map = new ObservableMap(); +// map.subscribe({ +// onUpdate() { fired += 1; }, +// onAdd() {}, +// onRemove() {}, +// onReset() {} +// }); +// const result = map.update(1); +// assert.equal(fired, 0); +// assert.equal(result, false); +// }, - test_set(assert) { - let add_fired = 0, update_fired = 0; - const map = new ObservableMap(); - map.subscribe({ - onAdd(key, value) { - add_fired += 1; - assert.equal(key, 1); - assert.deepEqual(value, {value: 5}); - }, - onUpdate(key, value/*, params*/) { - update_fired += 1; - assert.equal(key, 1); - assert.deepEqual(value, {value: 7}); - }, - onRemove() {}, - onReset() {} - }); - // Add - map.set(1, {value: 5}); - assert.equal(map.size, 1); - assert.equal(add_fired, 1); - // Update - map.set(1, {value: 7}); - assert.equal(map.size, 1); - assert.equal(update_fired, 1); - }, +// test_set(assert) { +// let add_fired = 0, update_fired = 0; +// const map = new ObservableMap(); +// map.subscribe({ +// onAdd(key, value) { +// add_fired += 1; +// assert.equal(key, 1); +// assert.deepEqual(value, {value: 5}); +// }, +// onUpdate(key, value/*, params*/) { +// update_fired += 1; +// assert.equal(key, 1); +// assert.deepEqual(value, {value: 7}); +// }, +// onRemove() {}, +// onReset() {} +// }); +// // Add +// map.set(1, {value: 5}); +// assert.equal(map.size, 1); +// assert.equal(add_fired, 1); +// // Update +// map.set(1, {value: 7}); +// assert.equal(map.size, 1); +// assert.equal(update_fired, 1); +// }, - test_remove(assert) { - let fired = 0; - const map = new ObservableMap(); - const value = {value: 5}; - map.add(1, value); - map.subscribe({ - onRemove(key, value) { - fired += 1; - assert.equal(key, 1); - assert.deepEqual(value, {value: 5}); - }, - onAdd() {}, - onUpdate() {}, - onReset() {} - }); - map.remove(1); - assert.equal(map.size, 0); - assert.equal(fired, 1); - }, +// test_remove(assert) { +// let fired = 0; +// const map = new ObservableMap(); +// const value = {value: 5}; +// map.add(1, value); +// map.subscribe({ +// onRemove(key, value) { +// fired += 1; +// assert.equal(key, 1); +// assert.deepEqual(value, {value: 5}); +// }, +// onAdd() {}, +// onUpdate() {}, +// onReset() {} +// }); +// map.remove(1); +// assert.equal(map.size, 0); +// assert.equal(fired, 1); +// }, - test_iterate(assert) { - const results: any[] = []; - const map = new ObservableMap(); - map.add(1, {number: 5}); - map.add(2, {number: 6}); - map.add(3, {number: 7}); - for (let e of map) { - results.push(e); - } - assert.equal(results.length, 3); - assert.equal(results.find(([key]) => key === 1)[1].number, 5); - assert.equal(results.find(([key]) => key === 2)[1].number, 6); - assert.equal(results.find(([key]) => key === 3)[1].number, 7); - }, - test_size(assert) { - const map = new ObservableMap(); - map.add(1, {number: 5}); - map.add(2, {number: 6}); - assert.equal(map.size, 2); - }, - } -} +// test_iterate(assert) { +// const results: any[] = []; +// const map = new ObservableMap(); +// map.add(1, {number: 5}); +// map.add(2, {number: 6}); +// map.add(3, {number: 7}); +// for (let e of map) { +// results.push(e); +// } +// assert.equal(results.length, 3); +// assert.equal(results.find(([key]) => key === 1)[1].number, 5); +// assert.equal(results.find(([key]) => key === 2)[1].number, 6); +// assert.equal(results.find(([key]) => key === 3)[1].number, 7); +// }, +// test_size(assert) { +// const map = new ObservableMap(); +// map.add(1, {number: 5}); +// map.add(2, {number: 6}); +// assert.equal(map.size, 2); +// }, +// } +// } From 7645eb875374c33593ba2f85a7891061ac78e503 Mon Sep 17 00:00:00 2001 From: Isaiah Becker-Mayer Date: Mon, 4 Jul 2022 19:47:43 -0400 Subject: [PATCH 02/24] moves config into its own file --- src/observable/index.ts | 31 ++---------------- src/observable/map/BaseObservableMap.ts | 7 ++++ src/observable/map/ObservableMap.ts | 14 ++++---- src/observable/map/config.ts | 43 +++++++++++++++++++++++++ 4 files changed, 61 insertions(+), 34 deletions(-) create mode 100644 src/observable/map/config.ts diff --git a/src/observable/index.ts b/src/observable/index.ts index 1b05f798..47ab84ca 100644 --- a/src/observable/index.ts +++ b/src/observable/index.ts @@ -18,40 +18,15 @@ import {SortedMapList} from "./list/SortedMapList.js"; import {FilteredMap} from "./map/FilteredMap.js"; import {MappedMap} from "./map/MappedMap.js"; import {JoinedMap} from "./map/JoinedMap.js"; -import {BaseObservableMap, BaseObservableMapConfig} from "./map/BaseObservableMap"; -import {ObservableMapInternal} from "./map/ObservableMap"; -// re-export "root" (of chain) collections +import {BaseObservableMap} from "./map/BaseObservableMap"; +// re-export "root" (of chain) collection +export { ObservableMap } from "./map/ObservableMap"; export { ObservableArray } from "./list/ObservableArray"; export { SortedArray } from "./list/SortedArray"; export { MappedList } from "./list/MappedList"; export { AsyncMappedList } from "./list/AsyncMappedList"; export { ConcatList } from "./list/ConcatList"; -// avoid circular dependency between these classes -// and BaseObservableMap (as they extend it) -function config(): BaseObservableMapConfig { - return { - join: (_this: BaseObservableMap, ...otherMaps: Array>): JoinedMap => { - return new JoinedMap([_this].concat(otherMaps)); - }, - mapValues: (_this: BaseObservableMap, mapper: any, updater?: (params: any) => void): MappedMap => { - return new MappedMap(_this, mapper, updater); - }, - sortValues: (_this: BaseObservableMap, comparator?: (a: any, b: any) => number): SortedMapList => { - return new SortedMapList(_this, comparator); - }, - filterValues: (_this: BaseObservableMap, filter: (v: V, k: K) => boolean): FilteredMap => { - return new FilteredMap(_this, filter); - } - }; -}; - - -export class ObservableMap extends ObservableMapInternal { - constructor(initialValues?: (readonly [K, V])[]) { - super(config(), initialValues); - } -} // avoid circular dependency between these classes // and BaseObservableMap (as they extend it) diff --git a/src/observable/map/BaseObservableMap.ts b/src/observable/map/BaseObservableMap.ts index 39189cdc..5eee5f95 100644 --- a/src/observable/map/BaseObservableMap.ts +++ b/src/observable/map/BaseObservableMap.ts @@ -60,10 +60,17 @@ export abstract class BaseObservableMap extends BaseObservable): JoinedMap; abstract mapValues(mapper: any, updater?: (params: any) => void): MappedMap; abstract sortValues(comparator?: (a: any, b: any) => number): SortedMapList; abstract filterValues(filter: (v: V, k: K) => boolean): FilteredMap; + abstract [Symbol.iterator](): Iterator<[K, V]>; abstract get size(): number; abstract get(key: K): V | undefined; diff --git a/src/observable/map/ObservableMap.ts b/src/observable/map/ObservableMap.ts index ced58df1..85e46703 100644 --- a/src/observable/map/ObservableMap.ts +++ b/src/observable/map/ObservableMap.ts @@ -15,18 +15,20 @@ limitations under the License. */ import {BaseObservableMap, BaseObservableMapConfig} from "./BaseObservableMap"; -import {JoinedMap} from "../map/JoinedMap.js"; -import {MappedMap} from "../map/MappedMap.js"; -import {FilteredMap} from "../map/FilteredMap.js"; +import {config} from "./config"; +import {JoinedMap} from "./JoinedMap.js"; +import {MappedMap} from "./MappedMap.js"; +import {FilteredMap} from "./FilteredMap.js"; import {SortedMapList} from "../list/SortedMapList.js"; -export class ObservableMapInternal extends BaseObservableMap { + +export class ObservableMap extends BaseObservableMap { private _config: BaseObservableMapConfig private readonly _values: Map; - constructor(config: BaseObservableMapConfig, initialValues?: (readonly [K, V])[]) { + constructor(initialValues?: (readonly [K, V])[]) { super(); - this._config = config; + this._config = config(); this._values = new Map(initialValues); } diff --git a/src/observable/map/config.ts b/src/observable/map/config.ts new file mode 100644 index 00000000..7a7ea0c4 --- /dev/null +++ b/src/observable/map/config.ts @@ -0,0 +1,43 @@ +/* +Copyright 2022 Isaiah Becker-Mayer + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +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 {BaseObservableMap, BaseObservableMapConfig} from "./BaseObservableMap"; +import {FilteredMap} from "./FilteredMap.js"; +import {MappedMap} from "./MappedMap.js"; +import {JoinedMap} from "./JoinedMap.js"; +import {SortedMapList} from "../list/SortedMapList.js"; + + +// This function is used as a default implementation of +// the respective abstract functions in BaseObservableMap. +// We implement it this way in order to avoid a circular +// dependency between the classes that are instantiated here +// (i.e. `new JoinedMap()`) and BaseObservableMap (as they extend it). +export function config(): BaseObservableMapConfig { + return { + join: (_this: BaseObservableMap, ...otherMaps: Array>): JoinedMap => { + return new JoinedMap([_this].concat(otherMaps)); + }, + mapValues: (_this: BaseObservableMap, mapper: any, updater?: (params: any) => void): MappedMap => { + return new MappedMap(_this, mapper, updater); + }, + sortValues: (_this: BaseObservableMap, comparator?: (a: any, b: any) => number): SortedMapList => { + return new SortedMapList(_this, comparator); + }, + filterValues: (_this: BaseObservableMap, filter: (v: V, k: K) => boolean): FilteredMap => { + return new FilteredMap(_this, filter); + } + }; +}; \ No newline at end of file From f1751a24b0f9568df6741bdc94128eb75cd65ba0 Mon Sep 17 00:00:00 2001 From: Isaiah Becker-Mayer Date: Mon, 4 Jul 2022 20:30:18 -0400 Subject: [PATCH 03/24] FilteredMap --- src/observable/BaseObservable.ts | 2 +- src/observable/map/BaseObservableMap.ts | 8 +- .../map/{FilteredMap.js => FilteredMap.ts} | 119 +++++++++++++----- src/observable/map/ObservableMap.ts | 2 +- src/observable/map/config.ts | 2 +- 5 files changed, 93 insertions(+), 40 deletions(-) rename src/observable/map/{FilteredMap.js => FilteredMap.ts} (60%) diff --git a/src/observable/BaseObservable.ts b/src/observable/BaseObservable.ts index 44d716ac..11ecd8a0 100644 --- a/src/observable/BaseObservable.ts +++ b/src/observable/BaseObservable.ts @@ -80,5 +80,5 @@ export function tests() { assert.equal(c.firstSubscribeCalls, 1); assert.equal(c.firstUnsubscribeCalls, 1); } - } + }; } diff --git a/src/observable/map/BaseObservableMap.ts b/src/observable/map/BaseObservableMap.ts index 5eee5f95..4e8c88d0 100644 --- a/src/observable/map/BaseObservableMap.ts +++ b/src/observable/map/BaseObservableMap.ts @@ -31,7 +31,7 @@ export type BaseObservableMapConfig = { join(_this: BaseObservableMap, ...otherMaps: Array>): JoinedMap; mapValues(_this: BaseObservableMap, mapper: any, updater?: (params: any) => void): MappedMap; sortValues(_this: BaseObservableMap, comparator?: (a: any, b: any) => number): SortedMapList; - filterValues(_this: BaseObservableMap, filter: (v: V, k: K) => boolean): FilteredMap; + filterValues(_this: BaseObservableMap, filter: (v: V, k: K) => boolean): FilteredMap; } export abstract class BaseObservableMap extends BaseObservable> { @@ -48,13 +48,13 @@ export abstract class BaseObservableMap extends BaseObservable extends BaseObservable): JoinedMap; abstract mapValues(mapper: any, updater?: (params: any) => void): MappedMap; abstract sortValues(comparator?: (a: any, b: any) => number): SortedMapList; - abstract filterValues(filter: (v: V, k: K) => boolean): FilteredMap; + abstract filterValues(filter: (v: V, k: K) => boolean): FilteredMap; abstract [Symbol.iterator](): Iterator<[K, V]>; abstract get size(): number; diff --git a/src/observable/map/FilteredMap.js b/src/observable/map/FilteredMap.ts similarity index 60% rename from src/observable/map/FilteredMap.js rename to src/observable/map/FilteredMap.ts index 98dc4650..a2250f21 100644 --- a/src/observable/map/FilteredMap.js +++ b/src/observable/map/FilteredMap.ts @@ -14,19 +14,44 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {BaseObservableMap} from "./BaseObservableMap"; +import {BaseObservableMap, BaseObservableMapConfig} from "./BaseObservableMap"; +import {SubscriptionHandle} from "../BaseObservable"; +import {config} from "./config"; +import {JoinedMap} from "./JoinedMap.js"; +import {MappedMap} from "./MappedMap.js"; +import {SortedMapList} from "../list/SortedMapList.js"; -export class FilteredMap extends BaseObservableMap { - constructor(source, filter) { +export class FilteredMap extends BaseObservableMap { + private _source: BaseObservableMap; + private _config: BaseObservableMapConfig + private _filter: (value: V, key: K) => boolean; + private _included?: Map; + private _subscription?: SubscriptionHandle; + + constructor(source: BaseObservableMap, filter: (value: V, key: K) => boolean) { super(); this._source = source; this._filter = filter; - /** @type {Map} */ - this._included = null; - this._subscription = null; + this._config = config(); } - setFilter(filter) { + join(...otherMaps: Array): JoinedMap { + return this._config.join(this, ...otherMaps); + } + + mapValues(mapper: any, updater?: (params: any) => void): MappedMap{ + return this._config.mapValues(this, mapper, updater); + } + + sortValues(comparator?: (a: any, b: any) => number): SortedMapList { + return this._config.sortValues(this, comparator); + } + + filterValues(filter: (v: V, k: K) => boolean): FilteredMap { + return this._config.filterValues(this, filter); + } + + setFilter(filter: (value: V, key: K) => boolean) { this._filter = filter; if (this._subscription) { this._reapplyFilter(); @@ -58,30 +83,38 @@ export class FilteredMap extends BaseObservableMap { } } } - this._included = null; + this._included = undefined; } } - onAdd(key, value) { + onAdd(key: K, value: V) { if (this._filter) { - const included = this._filter(value, key); - this._included.set(key, included); - if (!included) { - return; + if (this._included) { + const included = this._filter(value, key); + this._included.set(key, included); + if (!included) { + return; + } + } else { + throw new Error("Internal logic error: FilteredMap._included used before initialized"); } } this.emitAdd(key, value); } - onRemove(key, value) { - const wasIncluded = !this._filter || this._included.get(key); - this._included.delete(key); - if (wasIncluded) { - this.emitRemove(key, value); + onRemove(key: K, value: V) { + const wasIncluded = !this._filter || this._included?.get(key); + if (this._included) { + this._included.delete(key); + if (wasIncluded) { + this.emitRemove(key, value); + } + } else { + throw new Error("Internal logic error: FilteredMap._included used before initialized"); } } - onUpdate(key, value, params) { + onUpdate(key: K, value: V, params: any) { // if an update is emitted while calling source.subscribe() from onSubscribeFirst, ignore it if (!this._included) { return; @@ -96,7 +129,7 @@ export class FilteredMap extends BaseObservableMap { } } - _emitForUpdate(wasIncluded, isIncluded, key, value, params = null) { + _emitForUpdate(wasIncluded: boolean | undefined, isIncluded: boolean, key: K, value: V, params: any = null) { if (wasIncluded && !isIncluded) { this.emitRemove(key, value); } else if (!wasIncluded && isIncluded) { @@ -114,8 +147,10 @@ export class FilteredMap extends BaseObservableMap { onUnsubscribeLast() { super.onUnsubscribeLast(); - this._included = null; - this._subscription = this._subscription(); + this._included = undefined; + if (this._subscription) { + this._subscription = this._subscription(); + } } onReset() { @@ -124,12 +159,12 @@ export class FilteredMap extends BaseObservableMap { } [Symbol.iterator]() { - return new FilterIterator(this._source, this._included); + return new FilterIterator(this._source, this._included); } get size() { let count = 0; - this._included.forEach(included => { + this._included?.forEach(included => { if (included) { count += 1; } @@ -145,9 +180,11 @@ export class FilteredMap extends BaseObservableMap { } } -class FilterIterator { - constructor(map, _included) { - this._included = _included; +class FilterIterator { + private _included?: Map + private _sourceIterator: Iterator<[K, V], any, undefined> + constructor(map: BaseObservableMap, included?: Map) { + this._included = included; this._sourceIterator = map[Symbol.iterator](); } @@ -159,14 +196,14 @@ class FilterIterator { return sourceResult; } const key = sourceResult.value[0]; - if (this._included.get(key)) { + if (this._included?.get(key)) { return sourceResult; } } } } -import {ObservableMap} from "../"; +import {ObservableMap} from ".."; export function tests() { return { "filter preloaded list": assert => { @@ -174,9 +211,22 @@ export function tests() { source.add("one", 1); source.add("two", 2); source.add("three", 3); - const oddNumbers = new FilteredMap(source, x => x % 2 !== 0); + const oddNumbers = new FilteredMap(source, (x: number) => x % 2 !== 0); // can only iterate after subscribing - oddNumbers.subscribe({}); + oddNumbers.subscribe({ + onAdd() { + return; + }, + onRemove() { + return; + }, + onUpdate() { + return; + }, + onReset() { + return; + }, + }); assert.equal(oddNumbers.size, 2); const it = oddNumbers[Symbol.iterator](); assert.deepEqual(it.next().value, ["one", 1]); @@ -199,7 +249,7 @@ export function tests() { source.add("num1", 1); source.add("num2", 2); source.add("num3", 3); - const oddMap = new FilteredMap(source, x => x % 2 !== 0); + const oddMap = new FilteredMap(source, (x: number) => x % 2 !== 0); oddMap.subscribe({ onAdd() { count_add += 1; @@ -209,6 +259,9 @@ export function tests() { }, onUpdate() { count_update += 1; + }, + onReset() { + return; } }); source.set("num3", 4); @@ -218,5 +271,5 @@ export function tests() { assert.strictEqual(count_update, 1); assert.strictEqual(count_remove, 1); } - } + }; } diff --git a/src/observable/map/ObservableMap.ts b/src/observable/map/ObservableMap.ts index 85e46703..f4625a6f 100644 --- a/src/observable/map/ObservableMap.ts +++ b/src/observable/map/ObservableMap.ts @@ -44,7 +44,7 @@ export class ObservableMap extends BaseObservableMap { return this._config.sortValues(this, comparator); } - filterValues(filter: (v: V, k: K) => boolean): FilteredMap { + filterValues(filter: (v: V, k: K) => boolean): FilteredMap { return this._config.filterValues(this, filter); } diff --git a/src/observable/map/config.ts b/src/observable/map/config.ts index 7a7ea0c4..ce280d2f 100644 --- a/src/observable/map/config.ts +++ b/src/observable/map/config.ts @@ -36,7 +36,7 @@ export function config(): BaseObservableMapConfig { sortValues: (_this: BaseObservableMap, comparator?: (a: any, b: any) => number): SortedMapList => { return new SortedMapList(_this, comparator); }, - filterValues: (_this: BaseObservableMap, filter: (v: V, k: K) => boolean): FilteredMap => { + filterValues: (_this: BaseObservableMap, filter: (v: V, k: K) => boolean): FilteredMap => { return new FilteredMap(_this, filter); } }; From 3ba2bab59f9bca9ce89c1fc80a30dc3d1efa5380 Mon Sep 17 00:00:00 2001 From: Isaiah Becker-Mayer Date: Wed, 6 Jul 2022 20:23:37 -0400 Subject: [PATCH 04/24] JoinedMap to typescript --- .eslintrc.js | 34 +++--- src/observable/map/BaseObservableMap.ts | 4 +- src/observable/map/FilteredMap.ts | 2 +- .../map/{JoinedMap.js => JoinedMap.ts} | 102 ++++++++++++------ src/observable/map/ObservableMap.ts | 2 +- src/observable/map/config.ts | 4 +- 6 files changed, 90 insertions(+), 58 deletions(-) rename src/observable/map/{JoinedMap.js => JoinedMap.ts} (72%) diff --git a/.eslintrc.js b/.eslintrc.js index cf1fc3bf..cb28f4c8 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,25 +1,25 @@ module.exports = { - root: true, - env: { + "env": { "browser": true, "es6": true }, - extends: [ - // "plugin:@typescript-eslint/recommended", - // "plugin:@typescript-eslint/recommended-requiring-type-checking", - ], - parser: '@typescript-eslint/parser', - parserOptions: { + "extends": "eslint:recommended", + "parserOptions": { "ecmaVersion": 2020, - "sourceType": "module", - "project": "./tsconfig.json" + "sourceType": "module" }, - plugins: [ - '@typescript-eslint', - ], - rules: { - "@typescript-eslint/no-floating-promises": 2, - "@typescript-eslint/no-misused-promises": 2, - "semi": ["error", "always"] + "rules": { + "no-console": "off", + "no-empty": "off", + "no-prototype-builtins": "off", + "no-unused-vars": "warn" + }, + "globals": { + "DEFINE_VERSION": "readonly", + "DEFINE_GLOBAL_HASH": "readonly", + // only available in sw.js + "DEFINE_UNHASHED_PRECACHED_ASSETS": "readonly", + "DEFINE_HASHED_PRECACHED_ASSETS": "readonly", + "DEFINE_HASHED_CACHED_ON_REQUEST_ASSETS": "readonly" } }; diff --git a/src/observable/map/BaseObservableMap.ts b/src/observable/map/BaseObservableMap.ts index 4e8c88d0..c4f7daae 100644 --- a/src/observable/map/BaseObservableMap.ts +++ b/src/observable/map/BaseObservableMap.ts @@ -28,7 +28,7 @@ export interface IMapObserver { } export type BaseObservableMapConfig = { - join(_this: BaseObservableMap, ...otherMaps: Array>): JoinedMap; + join(_this: BaseObservableMap, ...otherMaps: Array>): JoinedMap; mapValues(_this: BaseObservableMap, mapper: any, updater?: (params: any) => void): MappedMap; sortValues(_this: BaseObservableMap, comparator?: (a: any, b: any) => number): SortedMapList; filterValues(_this: BaseObservableMap, filter: (v: V, k: K) => boolean): FilteredMap; @@ -66,7 +66,7 @@ export abstract class BaseObservableMap extends BaseObservable): JoinedMap; + abstract join(...otherMaps: Array): JoinedMap; abstract mapValues(mapper: any, updater?: (params: any) => void): MappedMap; abstract sortValues(comparator?: (a: any, b: any) => number): SortedMapList; abstract filterValues(filter: (v: V, k: K) => boolean): FilteredMap; diff --git a/src/observable/map/FilteredMap.ts b/src/observable/map/FilteredMap.ts index a2250f21..c4810d9b 100644 --- a/src/observable/map/FilteredMap.ts +++ b/src/observable/map/FilteredMap.ts @@ -35,7 +35,7 @@ export class FilteredMap extends BaseObservableMap { this._config = config(); } - join(...otherMaps: Array): JoinedMap { + join(...otherMaps: Array): JoinedMap { return this._config.join(this, ...otherMaps); } diff --git a/src/observable/map/JoinedMap.js b/src/observable/map/JoinedMap.ts similarity index 72% rename from src/observable/map/JoinedMap.js rename to src/observable/map/JoinedMap.ts index ea5ad784..11589ba5 100644 --- a/src/observable/map/JoinedMap.js +++ b/src/observable/map/JoinedMap.ts @@ -14,16 +14,41 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {BaseObservableMap} from "./BaseObservableMap"; +import {BaseObservableMap, BaseObservableMapConfig} from "./BaseObservableMap"; +import {config} from "./config"; +import {FilteredMap} from "./FilteredMap.js"; +import {MappedMap} from "./MappedMap.js"; +import {SortedMapList} from "../list/SortedMapList.js"; -export class JoinedMap extends BaseObservableMap { - constructor(sources) { + +export class JoinedMap extends BaseObservableMap { + protected _sources: BaseObservableMap[]; + private _config: BaseObservableMapConfig + private _subscriptions?: SourceSubscriptionHandler[]; + + constructor(sources: BaseObservableMap[]) { super(); this._sources = sources; - this._subscriptions = null; + this._config = config(); } - onAdd(source, key, value) { + join(...otherMaps: Array): JoinedMap { + return this._config.join(this, ...otherMaps); + } + + mapValues(mapper: any, updater?: (params: any) => void): MappedMap{ + return this._config.mapValues(this, mapper, updater); + } + + sortValues(comparator?: (a: any, b: any) => number): SortedMapList { + return this._config.sortValues(this, comparator); + } + + filterValues(filter: (v: V, k: K) => boolean): FilteredMap { + return this._config.filterValues(this, filter); + } + + onAdd(source: BaseObservableMap, key: K, value: V) { if (!this._isKeyAtSourceOccluded(source, key)) { const occludingValue = this._getValueFromOccludedSources(source, key); if (occludingValue !== undefined) { @@ -35,7 +60,7 @@ export class JoinedMap extends BaseObservableMap { } } - onRemove(source, key, value) { + onRemove(source: BaseObservableMap, key: K, value: V) { if (!this._isKeyAtSourceOccluded(source, key)) { this.emitRemove(key, value); const occludedValue = this._getValueFromOccludedSources(source, key); @@ -47,7 +72,7 @@ export class JoinedMap extends BaseObservableMap { } } - onUpdate(source, key, value, params) { + onUpdate(source: BaseObservableMap, key: K, value: V, params: any) { // if an update is emitted while calling source.subscribe() from onSubscribeFirst, ignore it if (!this._subscriptions) { return; @@ -66,7 +91,7 @@ export class JoinedMap extends BaseObservableMap { super.onSubscribeFirst(); } - _isKeyAtSourceOccluded(source, key) { + _isKeyAtSourceOccluded(source: BaseObservableMap, key: K) { // sources that come first in the sources array can // hide the keys in later sources, to prevent events // being emitted for the same key and different values, @@ -81,7 +106,7 @@ export class JoinedMap extends BaseObservableMap { } // get the value that the given source and key occlude, if any - _getValueFromOccludedSources(source, key) { + _getValueFromOccludedSources(source: BaseObservableMap, key: K) { // sources that come first in the sources array can // hide the keys in later sources, to prevent events // being emitted for the same key and different values, @@ -99,51 +124,55 @@ export class JoinedMap extends BaseObservableMap { onUnsubscribeLast() { super.onUnsubscribeLast(); - for (const s of this._subscriptions) { - s.dispose(); + if (this._subscriptions) { + for (const s of this._subscriptions) { + s.dispose(); + } } } [Symbol.iterator]() { - return new JoinedIterator(this._sources); + return new JoinedIterator(this._sources); } get size() { return this._sources.reduce((sum, s) => sum + s.size, 0); } - get(key) { + get(key: K): V | undefined{ for (const s of this._sources) { const value = s.get(key); if (value) { return value; } } - return null; + return undefined; } } -class JoinedIterator { - constructor(sources) { +class JoinedIterator implements Iterator<[K, V]> { + private _sources: {[Symbol.iterator](): Iterator<[K, V]>}[]; + private _sourceIndex = -1; + private _encounteredKeys = new Set(); + private _currentIterator?: Iterator<[K, V]> + + constructor(sources: {[Symbol.iterator](): Iterator<[K, V]>}[]) { this._sources = sources; - this._sourceIndex = -1; - this._currentIterator = null; - this._encounteredKeys = new Set(); } - next() { - let result; + next(): IteratorYieldResult<[K, V]> | IteratorReturnResult { + let result: IteratorYieldResult<[K, V]> | undefined = undefined; while (!result) { if (!this._currentIterator) { this._sourceIndex += 1; if (this._sources.length <= this._sourceIndex) { - return {done: true}; + return {done: true, value: null}; } this._currentIterator = this._sources[this._sourceIndex][Symbol.iterator](); } - const sourceResult = this._currentIterator.next(); - if (sourceResult.done) { - this._currentIterator = null; + const sourceResult = this._currentIterator?.next(); + if (!sourceResult || sourceResult.done) { + this._currentIterator = undefined; continue; } else { const key = sourceResult.value[0]; @@ -191,26 +220,29 @@ class SourceSubscriptionHandler { } -import {ObservableMap} from "../"; +import {ObservableMap} from ".."; export function tests() { - function observeMap(map) { - const events = []; + function observeMap(map: JoinedMap) { + const events: { type: string, key: any, value: any, params?: any }[] = []; map.subscribe({ - onAdd(key, value) { events.push({type: "add", key, value}); }, - onRemove(key, value) { events.push({type: "remove", key, value}); }, - onUpdate(key, value, params) { events.push({type: "update", key, value, params}); } + onAdd(key, value) { events.push({ type: "add", key, value }); }, + onRemove(key, value) { events.push({ type: "remove", key, value }); }, + onUpdate(key, value, params) { events.push({ type: "update", key, value, params }); }, + onReset: function (): void { + return; + } }); return events; } return { "joined iterator": assert => { - const firstKV = ["a", 1]; - const secondKV = ["b", 2]; - const thirdKV = ["c", 3]; - const it = new JoinedIterator([[firstKV, secondKV], [thirdKV]]); + const firstKV: [string, number] = ["a", 1]; + const secondKV: [string, number] = ["b", 2]; + const thirdKV: [string, number] = ["c", 3]; + const it = new JoinedIterator([[firstKV, secondKV], [thirdKV]]); assert.equal(it.next().value, firstKV); assert.equal(it.next().value, secondKV); assert.equal(it.next().value, thirdKV); diff --git a/src/observable/map/ObservableMap.ts b/src/observable/map/ObservableMap.ts index f4625a6f..42d47c8d 100644 --- a/src/observable/map/ObservableMap.ts +++ b/src/observable/map/ObservableMap.ts @@ -32,7 +32,7 @@ export class ObservableMap extends BaseObservableMap { this._values = new Map(initialValues); } - join(...otherMaps: Array): JoinedMap { + join(...otherMaps: Array): JoinedMap { return this._config.join(this, ...otherMaps); } diff --git a/src/observable/map/config.ts b/src/observable/map/config.ts index ce280d2f..a0a63014 100644 --- a/src/observable/map/config.ts +++ b/src/observable/map/config.ts @@ -27,7 +27,7 @@ import {SortedMapList} from "../list/SortedMapList.js"; // (i.e. `new JoinedMap()`) and BaseObservableMap (as they extend it). export function config(): BaseObservableMapConfig { return { - join: (_this: BaseObservableMap, ...otherMaps: Array>): JoinedMap => { + join: (_this: BaseObservableMap, ...otherMaps: Array>): JoinedMap => { return new JoinedMap([_this].concat(otherMaps)); }, mapValues: (_this: BaseObservableMap, mapper: any, updater?: (params: any) => void): MappedMap => { @@ -40,4 +40,4 @@ export function config(): BaseObservableMapConfig { return new FilteredMap(_this, filter); } }; -}; \ No newline at end of file +} \ No newline at end of file From bd58674626d33c80fcd82335b51b6466e5c7cd81 Mon Sep 17 00:00:00 2001 From: Isaiah Becker-Mayer Date: Thu, 7 Jul 2022 22:26:58 -0400 Subject: [PATCH 05/24] Updates .ts-eslintrc with new settings, see https://github.com/import-js/eslint-plugin-import/issues/653#issuecomment-840228881 and https://typescript-eslint.io/docs/linting/troubleshooting/#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors for the impetus --- .ts-eslintrc.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.ts-eslintrc.js b/.ts-eslintrc.js index cf1fc3bf..ac73bb34 100644 --- a/.ts-eslintrc.js +++ b/.ts-eslintrc.js @@ -20,6 +20,8 @@ module.exports = { rules: { "@typescript-eslint/no-floating-promises": 2, "@typescript-eslint/no-misused-promises": 2, - "semi": ["error", "always"] + 'no-unused-vars': 'off', + '@typescript-eslint/no-unused-vars': ['warn'], + 'no-undef': 'off', } }; From 63e9b49ebe7cbefcf30f04a305d9f627da9874d2 Mon Sep 17 00:00:00 2001 From: Isaiah Becker-Mayer Date: Thu, 7 Jul 2022 22:29:28 -0400 Subject: [PATCH 06/24] uncommenting tests --- src/observable/map/ObservableMap.ts | 256 ++++++++++++++-------------- 1 file changed, 128 insertions(+), 128 deletions(-) diff --git a/src/observable/map/ObservableMap.ts b/src/observable/map/ObservableMap.ts index 42d47c8d..7ee3d666 100644 --- a/src/observable/map/ObservableMap.ts +++ b/src/observable/map/ObservableMap.ts @@ -117,137 +117,137 @@ export class ObservableMap extends BaseObservableMap { } }; -// export function tests() { -// return { -// test_initial_values(assert) { -// const map = new ObservableMap([ -// ["a", 5], -// ["b", 10] -// ]); -// assert.equal(map.size, 2); -// assert.equal(map.get("a"), 5); -// assert.equal(map.get("b"), 10); -// }, +export function tests() { + return { + test_initial_values(assert) { + const map = new ObservableMap([ + ["a", 5], + ["b", 10] + ]); + assert.equal(map.size, 2); + assert.equal(map.get("a"), 5); + assert.equal(map.get("b"), 10); + }, -// test_add(assert) { -// let fired = 0; -// const map = new ObservableMap(); -// map.subscribe({ -// onAdd(key, value) { -// fired += 1; -// assert.equal(key, 1); -// assert.deepEqual(value, {value: 5}); -// }, -// onUpdate() {}, -// onRemove() {}, -// onReset() {} -// }); -// map.add(1, {value: 5}); -// assert.equal(map.size, 1); -// assert.equal(fired, 1); -// }, + test_add(assert) { + let fired = 0; + const map = new ObservableMap(); + map.subscribe({ + onAdd(key, value) { + fired += 1; + assert.equal(key, 1); + assert.deepEqual(value, {value: 5}); + }, + onUpdate() {}, + onRemove() {}, + onReset() {} + }); + map.add(1, {value: 5}); + assert.equal(map.size, 1); + assert.equal(fired, 1); + }, -// test_update(assert) { -// let fired = 0; -// const map = new ObservableMap(); -// const value = {number: 5}; -// map.add(1, value); -// map.subscribe({ -// onUpdate(key, value, params) { -// fired += 1; -// assert.equal(key, 1); -// assert.deepEqual(value, {number: 6}); -// assert.equal(params, "test"); -// }, -// onAdd() {}, -// onRemove() {}, -// onReset() {} -// }); -// value.number = 6; -// map.update(1, "test"); -// assert.equal(fired, 1); -// }, + test_update(assert) { + let fired = 0; + const map = new ObservableMap(); + const value = {number: 5}; + map.add(1, value); + map.subscribe({ + onUpdate(key, value, params) { + fired += 1; + assert.equal(key, 1); + assert.deepEqual(value, {number: 6}); + assert.equal(params, "test"); + }, + onAdd() {}, + onRemove() {}, + onReset() {} + }); + value.number = 6; + map.update(1, "test"); + assert.equal(fired, 1); + }, -// test_update_unknown(assert) { -// let fired = 0; -// const map = new ObservableMap(); -// map.subscribe({ -// onUpdate() { fired += 1; }, -// onAdd() {}, -// onRemove() {}, -// onReset() {} -// }); -// const result = map.update(1); -// assert.equal(fired, 0); -// assert.equal(result, false); -// }, + test_update_unknown(assert) { + let fired = 0; + const map = new ObservableMap(); + map.subscribe({ + onUpdate() { fired += 1; }, + onAdd() {}, + onRemove() {}, + onReset() {} + }); + const result = map.update(1); + assert.equal(fired, 0); + assert.equal(result, false); + }, -// test_set(assert) { -// let add_fired = 0, update_fired = 0; -// const map = new ObservableMap(); -// map.subscribe({ -// onAdd(key, value) { -// add_fired += 1; -// assert.equal(key, 1); -// assert.deepEqual(value, {value: 5}); -// }, -// onUpdate(key, value/*, params*/) { -// update_fired += 1; -// assert.equal(key, 1); -// assert.deepEqual(value, {value: 7}); -// }, -// onRemove() {}, -// onReset() {} -// }); -// // Add -// map.set(1, {value: 5}); -// assert.equal(map.size, 1); -// assert.equal(add_fired, 1); -// // Update -// map.set(1, {value: 7}); -// assert.equal(map.size, 1); -// assert.equal(update_fired, 1); -// }, + test_set(assert) { + let add_fired = 0, update_fired = 0; + const map = new ObservableMap(); + map.subscribe({ + onAdd(key, value) { + add_fired += 1; + assert.equal(key, 1); + assert.deepEqual(value, {value: 5}); + }, + onUpdate(key, value/*, params*/) { + update_fired += 1; + assert.equal(key, 1); + assert.deepEqual(value, {value: 7}); + }, + onRemove() {}, + onReset() {} + }); + // Add + map.set(1, {value: 5}); + assert.equal(map.size, 1); + assert.equal(add_fired, 1); + // Update + map.set(1, {value: 7}); + assert.equal(map.size, 1); + assert.equal(update_fired, 1); + }, -// test_remove(assert) { -// let fired = 0; -// const map = new ObservableMap(); -// const value = {value: 5}; -// map.add(1, value); -// map.subscribe({ -// onRemove(key, value) { -// fired += 1; -// assert.equal(key, 1); -// assert.deepEqual(value, {value: 5}); -// }, -// onAdd() {}, -// onUpdate() {}, -// onReset() {} -// }); -// map.remove(1); -// assert.equal(map.size, 0); -// assert.equal(fired, 1); -// }, + test_remove(assert) { + let fired = 0; + const map = new ObservableMap(); + const value = {value: 5}; + map.add(1, value); + map.subscribe({ + onRemove(key, value) { + fired += 1; + assert.equal(key, 1); + assert.deepEqual(value, {value: 5}); + }, + onAdd() {}, + onUpdate() {}, + onReset() {} + }); + map.remove(1); + assert.equal(map.size, 0); + assert.equal(fired, 1); + }, -// test_iterate(assert) { -// const results: any[] = []; -// const map = new ObservableMap(); -// map.add(1, {number: 5}); -// map.add(2, {number: 6}); -// map.add(3, {number: 7}); -// for (let e of map) { -// results.push(e); -// } -// assert.equal(results.length, 3); -// assert.equal(results.find(([key]) => key === 1)[1].number, 5); -// assert.equal(results.find(([key]) => key === 2)[1].number, 6); -// assert.equal(results.find(([key]) => key === 3)[1].number, 7); -// }, -// test_size(assert) { -// const map = new ObservableMap(); -// map.add(1, {number: 5}); -// map.add(2, {number: 6}); -// assert.equal(map.size, 2); -// }, -// } -// } + test_iterate(assert) { + const results: any[] = []; + const map = new ObservableMap(); + map.add(1, {number: 5}); + map.add(2, {number: 6}); + map.add(3, {number: 7}); + for (let e of map) { + results.push(e); + } + assert.equal(results.length, 3); + assert.equal(results.find(([key]) => key === 1)[1].number, 5); + assert.equal(results.find(([key]) => key === 2)[1].number, 6); + assert.equal(results.find(([key]) => key === 3)[1].number, 7); + }, + test_size(assert) { + const map = new ObservableMap(); + map.add(1, {number: 5}); + map.add(2, {number: 6}); + assert.equal(map.size, 2); + }, + } +} From edeec896ae6aa7d55a89d41400f35a62087a193a Mon Sep 17 00:00:00 2001 From: Isaiah Becker-Mayer Date: Fri, 8 Jul 2022 20:39:07 -0400 Subject: [PATCH 07/24] typescriptifies SourceSubscriptionHandler --- src/observable/map/JoinedMap.ts | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/observable/map/JoinedMap.ts b/src/observable/map/JoinedMap.ts index 11589ba5..6f285fd9 100644 --- a/src/observable/map/JoinedMap.ts +++ b/src/observable/map/JoinedMap.ts @@ -19,12 +19,13 @@ import {config} from "./config"; import {FilteredMap} from "./FilteredMap.js"; import {MappedMap} from "./MappedMap.js"; import {SortedMapList} from "../list/SortedMapList.js"; +import {SubscriptionHandle} from "../BaseObservable" export class JoinedMap extends BaseObservableMap { protected _sources: BaseObservableMap[]; private _config: BaseObservableMapConfig - private _subscriptions?: SourceSubscriptionHandler[]; + private _subscriptions?: SourceSubscriptionHandler[]; constructor(sources: BaseObservableMap[]) { super(); @@ -186,11 +187,15 @@ class JoinedIterator implements Iterator<[K, V]> { } } -class SourceSubscriptionHandler { - constructor(source, joinedMap) { +class SourceSubscriptionHandler { + private _source: BaseObservableMap; + private _joinedMap: JoinedMap; + private _subscription?: SubscriptionHandle; + + constructor(source: BaseObservableMap, joinedMap: JoinedMap) { this._source = source; this._joinedMap = joinedMap; - this._subscription = null; + this._subscription = undefined; } subscribe() { @@ -199,23 +204,23 @@ class SourceSubscriptionHandler { } dispose() { - this._subscription = this._subscription(); + if (this._subscription) this._subscription = this._subscription(); } - onAdd(key, value) { + onAdd(key: K, value: V) { this._joinedMap.onAdd(this._source, key, value); } - onRemove(key, value) { + onRemove(key: K, value: V) { this._joinedMap.onRemove(this._source, key, value); } - onUpdate(key, value, params) { + onUpdate(key: K, value: V, params: any) { this._joinedMap.onUpdate(this._source, key, value, params); } onReset() { - this._joinedMap.onReset(this._source); + this._joinedMap.onReset(); } } From d060d337b63493e890bad3fb3bb7b17b82334f4a Mon Sep 17 00:00:00 2001 From: Isaiah Becker-Mayer Date: Fri, 8 Jul 2022 20:58:30 -0400 Subject: [PATCH 08/24] typescriptifies LogMap --- src/observable/map/LogMap.js | 70 ----------------------- src/observable/map/LogMap.ts | 104 +++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 70 deletions(-) delete mode 100644 src/observable/map/LogMap.js create mode 100644 src/observable/map/LogMap.ts diff --git a/src/observable/map/LogMap.js b/src/observable/map/LogMap.js deleted file mode 100644 index 1beb4846..00000000 --- a/src/observable/map/LogMap.js +++ /dev/null @@ -1,70 +0,0 @@ -/* -Copyright 2020 Bruno Windels - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -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 {BaseObservableMap} from "./BaseObservableMap"; - -export class LogMap extends BaseObservableMap { - constructor(source, log) { - super(); - this._source = source; - this.log = log; - this._subscription = null; - } - - onAdd(key, value) { - this.log("add", key, value); - this.emitAdd(key, value); - } - - onRemove(key, value) { - this.log("remove", key, value); - this.emitRemove(key, value); - } - - onUpdate(key, value, params) { - this.log("update", key, value, params); - this.emitUpdate(key, value, params); - } - - onSubscribeFirst() { - this.log("subscribeFirst"); - this._subscription = this._source.subscribe(this); - super.onSubscribeFirst(); - } - - onUnsubscribeLast() { - super.onUnsubscribeLast(); - this._subscription = this._subscription(); - this.log("unsubscribeLast"); - } - - onReset() { - this.log("reset"); - this.emitReset(); - } - - [Symbol.iterator]() { - return this._source[Symbol.iterator](); - } - - get size() { - return this._source.size; - } - - get(key) { - return this._source.get(key); - } -} diff --git a/src/observable/map/LogMap.ts b/src/observable/map/LogMap.ts new file mode 100644 index 00000000..988aa71b --- /dev/null +++ b/src/observable/map/LogMap.ts @@ -0,0 +1,104 @@ +/* +Copyright 2020 Bruno Windels + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +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 {BaseObservableMap, BaseObservableMapConfig} from "./BaseObservableMap"; +import {config} from "./config"; +import {FilteredMap} from "./FilteredMap.js"; +import {MappedMap} from "./MappedMap.js"; +import {JoinedMap} from "./JoinedMap.js"; +import {SortedMapList} from "../list/SortedMapList.js"; +import {SubscriptionHandle} from "../BaseObservable" +import {ILogItem, LabelOrValues} from "../../logging/types"; +import {LogLevel} from "../../logging/LogFilter"; + +export class LogMap extends BaseObservableMap { + private _source: BaseObservableMap; + private _subscription?: SubscriptionHandle; + private _log: ILogItem; + private _config: BaseObservableMapConfig + + + constructor(source: BaseObservableMap, log: ILogItem) { + super(); + this._source = source; + this._log = log; + this._config = config(); + } + + join(...otherMaps: Array): JoinedMap { + return this._config.join(this, ...otherMaps); + } + + mapValues(mapper: any, updater?: (params: any) => void): MappedMap { + return this._config.mapValues(this, mapper, updater); + } + + sortValues(comparator?: (a: any, b: any) => number): SortedMapList { + return this._config.sortValues(this, comparator); + } + + filterValues(filter: (v: V, k: K) => boolean): FilteredMap { + return this._config.filterValues(this, filter); + } + + private log(labelOrValues: LabelOrValues, logLevel?: LogLevel): ILogItem { + return this._log.log(labelOrValues, logLevel); + } + + onAdd(key: K, value: V) { + this.log("add " + JSON.stringify({key, value})); + this.emitAdd(key, value); + } + + onRemove(key: K, value: V) { + this.log("remove " + JSON.stringify({key, value})); + this.emitRemove(key, value); + } + + onUpdate(key: K, value: V, params: any) { + this.log("update" + JSON.stringify({key, value, params})); + this.emitUpdate(key, value, params); + } + + onSubscribeFirst() { + this.log("subscribeFirst"); + this._subscription = this._source.subscribe(this); + super.onSubscribeFirst(); + } + + onUnsubscribeLast() { + super.onUnsubscribeLast(); + if (this._subscription) this._subscription = this._subscription(); + this.log("unsubscribeLast"); + } + + onReset() { + this.log("reset"); + this.emitReset(); + } + + [Symbol.iterator]() { + return this._source[Symbol.iterator](); + } + + get size() { + return this._source.size; + } + + get(key: K) { + return this._source.get(key); + } +} From 95c65280ef303b1ebd44315f53b5ec8e71e581fd Mon Sep 17 00:00:00 2001 From: Isaiah Becker-Mayer Date: Fri, 8 Jul 2022 22:07:36 -0400 Subject: [PATCH 09/24] typescriptifying MappedMap --- .../session/rightpanel/MemberListViewModel.js | 2 +- src/observable/map/BaseObservableMap.ts | 4 +- src/observable/map/FilteredMap.ts | 2 +- src/observable/map/JoinedMap.ts | 4 +- src/observable/map/LogMap.ts | 2 +- .../map/{MappedMap.js => MappedMap.ts} | 65 +++++++++++++++---- src/observable/map/ObservableMap.ts | 2 +- src/observable/map/config.ts | 2 +- 8 files changed, 62 insertions(+), 21 deletions(-) rename src/observable/map/{MappedMap.js => MappedMap.ts} (57%) diff --git a/src/domain/session/rightpanel/MemberListViewModel.js b/src/domain/session/rightpanel/MemberListViewModel.js index b75a3d1c..1633936a 100644 --- a/src/domain/session/rightpanel/MemberListViewModel.js +++ b/src/domain/session/rightpanel/MemberListViewModel.js @@ -47,7 +47,7 @@ export class MemberListViewModel extends ViewModel { this.nameDisambiguator.disambiguate(vm); return vm; } - const updater = (vm, params, newMember) => { + const updater = (params, vm, newMember) => { vm.updateFrom(newMember); this.nameDisambiguator.disambiguate(vm); }; diff --git a/src/observable/map/BaseObservableMap.ts b/src/observable/map/BaseObservableMap.ts index c4f7daae..b2f63c03 100644 --- a/src/observable/map/BaseObservableMap.ts +++ b/src/observable/map/BaseObservableMap.ts @@ -29,7 +29,7 @@ export interface IMapObserver { export type BaseObservableMapConfig = { join(_this: BaseObservableMap, ...otherMaps: Array>): JoinedMap; - mapValues(_this: BaseObservableMap, mapper: any, updater?: (params: any) => void): MappedMap; + mapValues(_this: BaseObservableMap, mapper: any, updater?: (params: any) => void): MappedMap; sortValues(_this: BaseObservableMap, comparator?: (a: any, b: any) => number): SortedMapList; filterValues(_this: BaseObservableMap, filter: (v: V, k: K) => boolean): FilteredMap; } @@ -67,7 +67,7 @@ export abstract class BaseObservableMap extends BaseObservable): JoinedMap; - abstract mapValues(mapper: any, updater?: (params: any) => void): MappedMap; + abstract mapValues(mapper: any, updater?: (params: any) => void): MappedMap; abstract sortValues(comparator?: (a: any, b: any) => number): SortedMapList; abstract filterValues(filter: (v: V, k: K) => boolean): FilteredMap; diff --git a/src/observable/map/FilteredMap.ts b/src/observable/map/FilteredMap.ts index c4810d9b..a9603d6d 100644 --- a/src/observable/map/FilteredMap.ts +++ b/src/observable/map/FilteredMap.ts @@ -39,7 +39,7 @@ export class FilteredMap extends BaseObservableMap { return this._config.join(this, ...otherMaps); } - mapValues(mapper: any, updater?: (params: any) => void): MappedMap{ + mapValues(mapper: any, updater?: (params: any) => void): MappedMap{ return this._config.mapValues(this, mapper, updater); } diff --git a/src/observable/map/JoinedMap.ts b/src/observable/map/JoinedMap.ts index 6f285fd9..fbf2f39d 100644 --- a/src/observable/map/JoinedMap.ts +++ b/src/observable/map/JoinedMap.ts @@ -24,7 +24,7 @@ import {SubscriptionHandle} from "../BaseObservable" export class JoinedMap extends BaseObservableMap { protected _sources: BaseObservableMap[]; - private _config: BaseObservableMapConfig + private _config: BaseObservableMapConfig; private _subscriptions?: SourceSubscriptionHandler[]; constructor(sources: BaseObservableMap[]) { @@ -37,7 +37,7 @@ export class JoinedMap extends BaseObservableMap { return this._config.join(this, ...otherMaps); } - mapValues(mapper: any, updater?: (params: any) => void): MappedMap{ + mapValues(mapper: any, updater?: (params: any) => void): MappedMap { return this._config.mapValues(this, mapper, updater); } diff --git a/src/observable/map/LogMap.ts b/src/observable/map/LogMap.ts index 988aa71b..a287014a 100644 --- a/src/observable/map/LogMap.ts +++ b/src/observable/map/LogMap.ts @@ -42,7 +42,7 @@ export class LogMap extends BaseObservableMap { return this._config.join(this, ...otherMaps); } - mapValues(mapper: any, updater?: (params: any) => void): MappedMap { + mapValues(mapper: any, updater?: (params: any) => void): MappedMap { return this._config.mapValues(this, mapper, updater); } diff --git a/src/observable/map/MappedMap.js b/src/observable/map/MappedMap.ts similarity index 57% rename from src/observable/map/MappedMap.js rename to src/observable/map/MappedMap.ts index a6b65c41..ff522621 100644 --- a/src/observable/map/MappedMap.js +++ b/src/observable/map/MappedMap.ts @@ -14,49 +14,83 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {BaseObservableMap} from "./BaseObservableMap"; +import {BaseObservableMap, BaseObservableMapConfig} from "./BaseObservableMap"; +import {config} from "./config"; +import {JoinedMap} from "./JoinedMap.js"; +import {FilteredMap} from "./FilteredMap.js"; +import {SortedMapList} from "../list/SortedMapList.js"; +import {SubscriptionHandle} from "../BaseObservable"; + /* so a mapped value can emit updates on it's own with this._emitSpontaneousUpdate that is passed in the mapping function how should the mapped value be notified of an update though? and can it then decide to not propagate the update? */ -export class MappedMap extends BaseObservableMap { - constructor(source, mapper, updater) { +export class MappedMap extends BaseObservableMap { + private _source: BaseObservableMap; + private _mapper: Mapper; + private _updater?: Updater; + private _mappedValues: Map; + private _subscription?: SubscriptionHandle; + private _config: BaseObservableMapConfig + + constructor( + source: BaseObservableMap, + mapper: Mapper, + updater?: Updater + ) { super(); this._source = source; this._mapper = mapper; this._updater = updater; - this._mappedValues = new Map(); + this._mappedValues = new Map(); + this._config = config(); } - _emitSpontaneousUpdate(key, params) { + join(...otherMaps: Array): JoinedMap { + return this._config.join(this, ...otherMaps); + } + + mapValues(mapper: any, updater?: (params: any) => void): MappedMap{ + return this._config.mapValues(this, mapper, updater); + } + + sortValues(comparator?: (a: any, b: any) => number): SortedMapList { + return this._config.sortValues(this, comparator); + } + + filterValues(filter: (v: V, k: K) => boolean): FilteredMap { + return this._config.filterValues(this, filter); + } + + _emitSpontaneousUpdate(key: K, params: any) { const value = this._mappedValues.get(key); if (value) { this.emitUpdate(key, value, params); } } - onAdd(key, value) { + onAdd(key: K, value: V) { const emitSpontaneousUpdate = this._emitSpontaneousUpdate.bind(this, key); const mappedValue = this._mapper(value, emitSpontaneousUpdate); this._mappedValues.set(key, mappedValue); this.emitAdd(key, mappedValue); } - onRemove(key/*, _value*/) { + onRemove(key: K/*, _value*/) { const mappedValue = this._mappedValues.get(key); if (this._mappedValues.delete(key)) { - this.emitRemove(key, mappedValue); + if (mappedValue) this.emitRemove(key, mappedValue); } } - onUpdate(key, value, params) { + onUpdate(key: K, value: V, params: any) { // if an update is emitted while calling source.subscribe() from onSubscribeFirst, ignore it if (!this._mappedValues) { return; } const mappedValue = this._mappedValues.get(key); if (mappedValue !== undefined) { - this._updater?.(mappedValue, params, value); + this._updater?.(params, mappedValue, value); // TODO: map params somehow if needed? this.emitUpdate(key, mappedValue, params); } @@ -74,7 +108,7 @@ export class MappedMap extends BaseObservableMap { onUnsubscribeLast() { super.onUnsubscribeLast(); - this._subscription = this._subscription(); + if (this._subscription) this._subscription = this._subscription(); this._mappedValues.clear(); } @@ -91,7 +125,14 @@ export class MappedMap extends BaseObservableMap { return this._mappedValues.size; } - get(key) { + get(key: K): V | undefined { return this._mappedValues.get(key); } } + +type Mapper = ( + value: V, + emitSpontaneousUpdate: any, +) => V; + +type Updater = (params: any, mappedValue?: V, value?: V) => void; \ No newline at end of file diff --git a/src/observable/map/ObservableMap.ts b/src/observable/map/ObservableMap.ts index 7ee3d666..06776927 100644 --- a/src/observable/map/ObservableMap.ts +++ b/src/observable/map/ObservableMap.ts @@ -36,7 +36,7 @@ export class ObservableMap extends BaseObservableMap { return this._config.join(this, ...otherMaps); } - mapValues(mapper: any, updater?: (params: any) => void): MappedMap{ + mapValues(mapper: any, updater?: (params: any) => void): MappedMap { return this._config.mapValues(this, mapper, updater); } diff --git a/src/observable/map/config.ts b/src/observable/map/config.ts index a0a63014..c91cfe1a 100644 --- a/src/observable/map/config.ts +++ b/src/observable/map/config.ts @@ -30,7 +30,7 @@ export function config(): BaseObservableMapConfig { join: (_this: BaseObservableMap, ...otherMaps: Array>): JoinedMap => { return new JoinedMap([_this].concat(otherMaps)); }, - mapValues: (_this: BaseObservableMap, mapper: any, updater?: (params: any) => void): MappedMap => { + mapValues: (_this: BaseObservableMap, mapper: any, updater: (params: any) => void): MappedMap => { return new MappedMap(_this, mapper, updater); }, sortValues: (_this: BaseObservableMap, comparator?: (a: any, b: any) => number): SortedMapList => { From ab6a8ad3aac1ba9493119ab00c824f48da745c0e Mon Sep 17 00:00:00 2001 From: Isaiah Becker-Mayer Date: Sat, 9 Jul 2022 10:16:46 -0400 Subject: [PATCH 10/24] typescriptifying ApplyMap --- .../session/leftpanel/LeftPanelViewModel.js | 4 +- src/observable/index.ts | 29 +--------- .../map/{ApplyMap.js => ApplyMap.ts} | 54 ++++++++++++++----- src/observable/map/FilteredMap.ts | 2 +- 4 files changed, 47 insertions(+), 42 deletions(-) rename src/observable/map/{ApplyMap.js => ApplyMap.ts} (52%) diff --git a/src/domain/session/leftpanel/LeftPanelViewModel.js b/src/domain/session/leftpanel/LeftPanelViewModel.js index 8c8d71a2..8e814151 100644 --- a/src/domain/session/leftpanel/LeftPanelViewModel.js +++ b/src/domain/session/leftpanel/LeftPanelViewModel.js @@ -20,8 +20,8 @@ import {RoomTileViewModel} from "./RoomTileViewModel.js"; import {InviteTileViewModel} from "./InviteTileViewModel.js"; import {RoomBeingCreatedTileViewModel} from "./RoomBeingCreatedTileViewModel.js"; import {RoomFilter} from "./RoomFilter.js"; -import {ApplyMap} from "../../../observable/map/ApplyMap.js"; -import {addPanelIfNeeded} from "../../navigation/index"; +import {ApplyMap} from "../../../observable/map/ApplyMap"; +import {addPanelIfNeeded} from "../../navigation"; export class LeftPanelViewModel extends ViewModel { constructor(options) { diff --git a/src/observable/index.ts b/src/observable/index.ts index 47ab84ca..dfd272fd 100644 --- a/src/observable/index.ts +++ b/src/observable/index.ts @@ -14,36 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {SortedMapList} from "./list/SortedMapList.js"; -import {FilteredMap} from "./map/FilteredMap.js"; -import {MappedMap} from "./map/MappedMap.js"; -import {JoinedMap} from "./map/JoinedMap.js"; -import {BaseObservableMap} from "./map/BaseObservableMap"; + // re-export "root" (of chain) collection export { ObservableMap } from "./map/ObservableMap"; export { ObservableArray } from "./list/ObservableArray"; export { SortedArray } from "./list/SortedArray"; export { MappedList } from "./list/MappedList"; export { AsyncMappedList } from "./list/AsyncMappedList"; -export { ConcatList } from "./list/ConcatList"; - - -// avoid circular dependency between these classes -// and BaseObservableMap (as they extend it) -Object.assign(BaseObservableMap.prototype, { - sortValues(comparator) { - return new SortedMapList(this, comparator); - }, - - mapValues(mapper, updater) { - return new MappedMap(this, mapper, updater); - }, - - filterValues(filter) { - return new FilteredMap(this, filter); - }, - - join(...otherMaps) { - return new JoinedMap([this].concat(otherMaps)); - } -}); \ No newline at end of file +export { ConcatList } from "./list/ConcatList"; \ No newline at end of file diff --git a/src/observable/map/ApplyMap.js b/src/observable/map/ApplyMap.ts similarity index 52% rename from src/observable/map/ApplyMap.js rename to src/observable/map/ApplyMap.ts index 6be7278a..da485af9 100644 --- a/src/observable/map/ApplyMap.js +++ b/src/observable/map/ApplyMap.ts @@ -14,45 +14,73 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {BaseObservableMap} from "./BaseObservableMap"; +import {BaseObservableMap, BaseObservableMapConfig} from "./BaseObservableMap"; +import {SubscriptionHandle} from "../BaseObservable"; +import {config} from "./config"; +import {JoinedMap} from "./JoinedMap.js"; +import {MappedMap} from "./MappedMap.js"; +import {FilteredMap} from "./FilteredMap.js"; +import {SortedMapList} from "../list/SortedMapList.js"; -export class ApplyMap extends BaseObservableMap { - constructor(source, apply) { + +export class ApplyMap extends BaseObservableMap { + private _source: BaseObservableMap; + private _subscription?: SubscriptionHandle; + private _apply?: Apply; + private _config: BaseObservableMapConfig; + + constructor(source: BaseObservableMap, apply?: Apply) { super(); this._source = source; this._apply = apply; - this._subscription = null; + this._config = config(); + } + + join(...otherMaps: Array): JoinedMap { + return this._config.join(this, ...otherMaps); + } + + mapValues(mapper: any, updater?: (params: any) => void): MappedMap { + return this._config.mapValues(this, mapper, updater); + } + + sortValues(comparator?: (a: any, b: any) => number): SortedMapList { + return this._config.sortValues(this, comparator); + } + + filterValues(filter: (v: V, k: K) => boolean): FilteredMap { + return this._config.filterValues(this, filter); } hasApply() { return !!this._apply; } - setApply(apply) { + setApply(apply?: Apply) { this._apply = apply; - if (apply) { + if (this._apply) { this.applyOnce(this._apply); } } - applyOnce(apply) { + applyOnce(apply: Apply) { for (const [key, value] of this._source) { apply(key, value); } } - onAdd(key, value) { + onAdd(key: K, value: V) { if (this._apply) { this._apply(key, value); } this.emitAdd(key, value); } - onRemove(key, value) { + onRemove(key: K, value: V) { this.emitRemove(key, value); } - onUpdate(key, value, params) { + onUpdate(key: K, value: V, params: any) { if (this._apply) { this._apply(key, value, params); } @@ -69,7 +97,7 @@ export class ApplyMap extends BaseObservableMap { onUnsubscribeLast() { super.onUnsubscribeLast(); - this._subscription = this._subscription(); + if (this._subscription) this._subscription = this._subscription(); } onReset() { @@ -87,7 +115,9 @@ export class ApplyMap extends BaseObservableMap { return this._source.size; } - get(key) { + get(key: K) { return this._source.get(key); } } + +type Apply = (key: K, value: V, params?: any) => void; \ No newline at end of file diff --git a/src/observable/map/FilteredMap.ts b/src/observable/map/FilteredMap.ts index a9603d6d..8b544072 100644 --- a/src/observable/map/FilteredMap.ts +++ b/src/observable/map/FilteredMap.ts @@ -23,7 +23,7 @@ import {SortedMapList} from "../list/SortedMapList.js"; export class FilteredMap extends BaseObservableMap { private _source: BaseObservableMap; - private _config: BaseObservableMapConfig + private _config: BaseObservableMapConfig; private _filter: (value: V, key: K) => boolean; private _included?: Map; private _subscription?: SubscriptionHandle; From 081cc05fa65868ddeb5601ae33b64321db078ad5 Mon Sep 17 00:00:00 2001 From: Isaiah Becker-Mayer Date: Sat, 9 Jul 2022 11:53:09 -0400 Subject: [PATCH 11/24] Updates comparator --- src/observable/list/BaseObservableList.ts | 2 +- src/observable/list/SortedArray.ts | 6 +++--- src/observable/list/SortedMapList.js | 4 ++-- src/observable/map/ApplyMap.ts | 2 +- src/observable/map/BaseObservableMap.ts | 4 ++-- src/observable/map/FilteredMap.ts | 2 +- src/observable/map/JoinedMap.ts | 2 +- src/observable/map/LogMap.ts | 4 ++-- src/observable/map/MappedMap.ts | 2 +- src/observable/map/ObservableMap.ts | 4 ++-- src/observable/map/config.ts | 2 +- 11 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/observable/list/BaseObservableList.ts b/src/observable/list/BaseObservableList.ts index d103eb64..e2806c72 100644 --- a/src/observable/list/BaseObservableList.ts +++ b/src/observable/list/BaseObservableList.ts @@ -31,7 +31,7 @@ export function defaultObserverWith(overrides: { [key in keyof IListObserver< onUpdate(){}, onRemove(){}, onMove(){}, - } + }; return Object.assign(defaults, overrides); } diff --git a/src/observable/list/SortedArray.ts b/src/observable/list/SortedArray.ts index c85cca27..7df285a5 100644 --- a/src/observable/list/SortedArray.ts +++ b/src/observable/list/SortedArray.ts @@ -87,7 +87,7 @@ export class SortedArray extends BaseObservableList { const idx = sortedIndex(this._items, item, this._comparator); if (idx >= this._items.length || this._comparator(this._items[idx], item) !== 0) { this._items.splice(idx, 0, item); - this.emitAdd(idx, item) + this.emitAdd(idx, item); } else { this._items[idx] = item; this.emitUpdate(idx, item, updateParams); @@ -156,7 +156,7 @@ export function tests() { assert.equal(sa.get(0), "a"); assert.equal(sa.get(1), "b"); assert.equal(sa.get(2), "c"); - }, + }, "_getNext": assert => { const sa = new SortedArray((a, b) => a.localeCompare(b)); sa.setManyUnsorted(["b", "a", "f"]); @@ -183,5 +183,5 @@ export function tests() { // check done persists assert.equal(it.next().done, true); } - } + }; } diff --git a/src/observable/list/SortedMapList.js b/src/observable/list/SortedMapList.js index 21a3aa55..6f4be123 100644 --- a/src/observable/list/SortedMapList.js +++ b/src/observable/list/SortedMapList.js @@ -129,7 +129,7 @@ export class SortedMapList extends BaseObservableList { } return v; } - } + }; } } @@ -267,5 +267,5 @@ export function tests() { assert.equal(updateFired, 1); assert.deepEqual(Array.from(list).map(v => v.number), [1, 3, 11]); }, - } + }; } diff --git a/src/observable/map/ApplyMap.ts b/src/observable/map/ApplyMap.ts index da485af9..23b6f6ea 100644 --- a/src/observable/map/ApplyMap.ts +++ b/src/observable/map/ApplyMap.ts @@ -44,7 +44,7 @@ export class ApplyMap extends BaseObservableMap { return this._config.mapValues(this, mapper, updater); } - sortValues(comparator?: (a: any, b: any) => number): SortedMapList { + sortValues(comparator: (a: V, b: V) => number): SortedMapList { return this._config.sortValues(this, comparator); } diff --git a/src/observable/map/BaseObservableMap.ts b/src/observable/map/BaseObservableMap.ts index b2f63c03..37597932 100644 --- a/src/observable/map/BaseObservableMap.ts +++ b/src/observable/map/BaseObservableMap.ts @@ -30,7 +30,7 @@ export interface IMapObserver { export type BaseObservableMapConfig = { join(_this: BaseObservableMap, ...otherMaps: Array>): JoinedMap; mapValues(_this: BaseObservableMap, mapper: any, updater?: (params: any) => void): MappedMap; - sortValues(_this: BaseObservableMap, comparator?: (a: any, b: any) => number): SortedMapList; + sortValues(_this: BaseObservableMap, comparator: (a: V, b: V) => number): SortedMapList; filterValues(_this: BaseObservableMap, filter: (v: V, k: K) => boolean): FilteredMap; } @@ -68,7 +68,7 @@ export abstract class BaseObservableMap extends BaseObservable): JoinedMap; abstract mapValues(mapper: any, updater?: (params: any) => void): MappedMap; - abstract sortValues(comparator?: (a: any, b: any) => number): SortedMapList; + abstract sortValues(comparator: (a: V, b: V) => number): SortedMapList; abstract filterValues(filter: (v: V, k: K) => boolean): FilteredMap; abstract [Symbol.iterator](): Iterator<[K, V]>; diff --git a/src/observable/map/FilteredMap.ts b/src/observable/map/FilteredMap.ts index 8b544072..41017bfd 100644 --- a/src/observable/map/FilteredMap.ts +++ b/src/observable/map/FilteredMap.ts @@ -43,7 +43,7 @@ export class FilteredMap extends BaseObservableMap { return this._config.mapValues(this, mapper, updater); } - sortValues(comparator?: (a: any, b: any) => number): SortedMapList { + sortValues(comparator: (a: V, b: V) => number): SortedMapList { return this._config.sortValues(this, comparator); } diff --git a/src/observable/map/JoinedMap.ts b/src/observable/map/JoinedMap.ts index fbf2f39d..8973f1bf 100644 --- a/src/observable/map/JoinedMap.ts +++ b/src/observable/map/JoinedMap.ts @@ -41,7 +41,7 @@ export class JoinedMap extends BaseObservableMap { return this._config.mapValues(this, mapper, updater); } - sortValues(comparator?: (a: any, b: any) => number): SortedMapList { + sortValues(comparator: (a: V, b: V) => number): SortedMapList { return this._config.sortValues(this, comparator); } diff --git a/src/observable/map/LogMap.ts b/src/observable/map/LogMap.ts index a287014a..1417b14c 100644 --- a/src/observable/map/LogMap.ts +++ b/src/observable/map/LogMap.ts @@ -20,7 +20,7 @@ import {FilteredMap} from "./FilteredMap.js"; import {MappedMap} from "./MappedMap.js"; import {JoinedMap} from "./JoinedMap.js"; import {SortedMapList} from "../list/SortedMapList.js"; -import {SubscriptionHandle} from "../BaseObservable" +import {SubscriptionHandle} from "../BaseObservable"; import {ILogItem, LabelOrValues} from "../../logging/types"; import {LogLevel} from "../../logging/LogFilter"; @@ -46,7 +46,7 @@ export class LogMap extends BaseObservableMap { return this._config.mapValues(this, mapper, updater); } - sortValues(comparator?: (a: any, b: any) => number): SortedMapList { + sortValues(comparator: (a: V, b: V) => number): SortedMapList { return this._config.sortValues(this, comparator); } diff --git a/src/observable/map/MappedMap.ts b/src/observable/map/MappedMap.ts index ff522621..a3dbb000 100644 --- a/src/observable/map/MappedMap.ts +++ b/src/observable/map/MappedMap.ts @@ -54,7 +54,7 @@ export class MappedMap extends BaseObservableMap { return this._config.mapValues(this, mapper, updater); } - sortValues(comparator?: (a: any, b: any) => number): SortedMapList { + sortValues(comparator: (a: V, b: V) => number): SortedMapList { return this._config.sortValues(this, comparator); } diff --git a/src/observable/map/ObservableMap.ts b/src/observable/map/ObservableMap.ts index 06776927..169d2b58 100644 --- a/src/observable/map/ObservableMap.ts +++ b/src/observable/map/ObservableMap.ts @@ -40,7 +40,7 @@ export class ObservableMap extends BaseObservableMap { return this._config.mapValues(this, mapper, updater); } - sortValues(comparator?: (a: any, b: any) => number): SortedMapList { + sortValues(comparator: (a: V, b: V) => number): SortedMapList { return this._config.sortValues(this, comparator); } @@ -249,5 +249,5 @@ export function tests() { map.add(2, {number: 6}); assert.equal(map.size, 2); }, - } + }; } diff --git a/src/observable/map/config.ts b/src/observable/map/config.ts index c91cfe1a..8557031d 100644 --- a/src/observable/map/config.ts +++ b/src/observable/map/config.ts @@ -33,7 +33,7 @@ export function config(): BaseObservableMapConfig { mapValues: (_this: BaseObservableMap, mapper: any, updater: (params: any) => void): MappedMap => { return new MappedMap(_this, mapper, updater); }, - sortValues: (_this: BaseObservableMap, comparator?: (a: any, b: any) => number): SortedMapList => { + sortValues: (_this: BaseObservableMap, comparator: (a: V, b: V) => number): SortedMapList => { return new SortedMapList(_this, comparator); }, filterValues: (_this: BaseObservableMap, filter: (v: V, k: K) => boolean): FilteredMap => { From 674e7bd1c6f88a9dbaf19ee07eab646bfb25b0c1 Mon Sep 17 00:00:00 2001 From: Isaiah Becker-Mayer Date: Sat, 9 Jul 2022 11:57:58 -0400 Subject: [PATCH 12/24] fixing linting errors --- src/observable/ObservableValue.ts | 6 +++--- src/observable/list/AsyncMappedList.ts | 30 +++++++++++++------------- src/observable/list/ConcatList.ts | 4 ++-- src/observable/map/JoinedMap.ts | 2 +- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/observable/ObservableValue.ts b/src/observable/ObservableValue.ts index ad0a226d..dab8fb52 100644 --- a/src/observable/ObservableValue.ts +++ b/src/observable/ObservableValue.ts @@ -201,7 +201,7 @@ export function tests() { "waitFor promise resolves on matching update": async assert => { const a = new ObservableValue(5); const handle = a.waitFor(v => v === 6); - Promise.resolve().then(() => { + await Promise.resolve().then(() => { a.set(6); }); await handle.promise; @@ -210,7 +210,7 @@ export function tests() { "waitFor promise rejects when disposed": async assert => { const a = new ObservableValue(0); const handle = a.waitFor(() => false); - Promise.resolve().then(() => { + await Promise.resolve().then(() => { handle.dispose(); }); await assert.rejects(handle.promise, AbortError); @@ -244,5 +244,5 @@ export function tests() { count.set(5); assert.deepEqual(updates, [0, 5]); } - } + }; } diff --git a/src/observable/list/AsyncMappedList.ts b/src/observable/list/AsyncMappedList.ts index 0a919cdc..53edde21 100644 --- a/src/observable/list/AsyncMappedList.ts +++ b/src/observable/list/AsyncMappedList.ts @@ -16,13 +16,13 @@ limitations under the License. */ import {IListObserver} from "./BaseObservableList"; -import {BaseMappedList, Mapper, Updater, runAdd, runUpdate, runRemove, runMove, runReset} from "./BaseMappedList"; +import {BaseMappedList, runAdd, runUpdate, runRemove, runMove, runReset} from "./BaseMappedList"; export class AsyncMappedList extends BaseMappedList> implements IListObserver { private _eventQueue: AsyncEvent[] | null = null; private _flushing: boolean = false; - onSubscribeFirst(): void { + async onSubscribeFirst(): Promise { this._sourceUnsubscribe = this._sourceList.subscribe(this); this._eventQueue = []; this._mappedValues = []; @@ -31,7 +31,7 @@ export class AsyncMappedList extends BaseMappedList> impleme this._eventQueue.push(new AddEvent(idx, item)); idx += 1; } - this._flush(); + await this._flush(); } async _flush(): Promise { @@ -49,38 +49,38 @@ export class AsyncMappedList extends BaseMappedList> impleme } } - onReset(): void { + async onReset(): Promise { if (this._eventQueue) { this._eventQueue.push(new ResetEvent()); - this._flush(); + await this._flush(); } } - onAdd(index: number, value: F): void { + async onAdd(index: number, value: F): Promise { if (this._eventQueue) { this._eventQueue.push(new AddEvent(index, value)); - this._flush(); + await this._flush(); } } - onUpdate(index: number, value: F, params: any): void { + async onUpdate(index: number, value: F, params: any): Promise { if (this._eventQueue) { this._eventQueue.push(new UpdateEvent(index, value, params)); - this._flush(); + await this._flush(); } } - onRemove(index: number): void { + async onRemove(index: number): Promise { if (this._eventQueue) { this._eventQueue.push(new RemoveEvent(index)); - this._flush(); + await this._flush(); } } - onMove(fromIdx: number, toIdx: number): void { + async onMove(fromIdx: number, toIdx: number): Promise { if (this._eventQueue) { this._eventQueue.push(new MoveEvent(fromIdx, toIdx)); - this._flush(); + await this._flush(); } } @@ -150,7 +150,7 @@ export function tests() { mapper.subscribe(observer); source.append(2); // will sleep this amount, so second append would take less time source.append(1); - source.update(0, 7, "lucky seven") + source.update(0, 7, "lucky seven"); source.remove(0); { const {type, index, value} = await observer.next(); @@ -182,5 +182,5 @@ export function tests() { assert.equal(value.n, 49); } } - } + }; } diff --git a/src/observable/list/ConcatList.ts b/src/observable/list/ConcatList.ts index 5822468a..8ef7326c 100644 --- a/src/observable/list/ConcatList.ts +++ b/src/observable/list/ConcatList.ts @@ -47,7 +47,7 @@ export class ConcatList extends BaseObservableList implements IListObserve onReset(): void { // TODO: not ideal if other source lists are large // but working impl for now - // reset, and + // reset, and this.emitReset(); let idx = 0; for(const item of this) { @@ -102,7 +102,7 @@ export class ConcatList extends BaseObservableList implements IListObserve } return result; } - } + }; } } diff --git a/src/observable/map/JoinedMap.ts b/src/observable/map/JoinedMap.ts index 8973f1bf..fb67e934 100644 --- a/src/observable/map/JoinedMap.ts +++ b/src/observable/map/JoinedMap.ts @@ -19,7 +19,7 @@ import {config} from "./config"; import {FilteredMap} from "./FilteredMap.js"; import {MappedMap} from "./MappedMap.js"; import {SortedMapList} from "../list/SortedMapList.js"; -import {SubscriptionHandle} from "../BaseObservable" +import {SubscriptionHandle} from "../BaseObservable"; export class JoinedMap extends BaseObservableMap { From be570cafb0912440cb7ff9956653eab693e2f4e9 Mon Sep 17 00:00:00 2001 From: Isaiah Becker-Mayer Date: Sat, 9 Jul 2022 12:15:32 -0400 Subject: [PATCH 13/24] Adds types for common functions --- .../session/rightpanel/MemberListViewModel.js | 2 +- src/observable/map/ApplyMap.ts | 8 ++++---- src/observable/map/BaseObservableMap.ts | 13 +++++++------ src/observable/map/FilteredMap.ts | 14 +++++++------- src/observable/map/JoinedMap.ts | 8 ++++---- src/observable/map/LogMap.ts | 8 ++++---- src/observable/map/MappedMap.ts | 17 +++++------------ src/observable/map/ObservableMap.ts | 8 ++++---- src/observable/map/config.ts | 19 +++++++++++++++---- 9 files changed, 51 insertions(+), 46 deletions(-) diff --git a/src/domain/session/rightpanel/MemberListViewModel.js b/src/domain/session/rightpanel/MemberListViewModel.js index 1633936a..1c878c85 100644 --- a/src/domain/session/rightpanel/MemberListViewModel.js +++ b/src/domain/session/rightpanel/MemberListViewModel.js @@ -46,7 +46,7 @@ export class MemberListViewModel extends ViewModel { const vm = new MemberTileViewModel(this.childOptions({member, emitChange, mediaRepository})); this.nameDisambiguator.disambiguate(vm); return vm; - } + }; const updater = (params, vm, newMember) => { vm.updateFrom(newMember); this.nameDisambiguator.disambiguate(vm); diff --git a/src/observable/map/ApplyMap.ts b/src/observable/map/ApplyMap.ts index 23b6f6ea..4f79a9df 100644 --- a/src/observable/map/ApplyMap.ts +++ b/src/observable/map/ApplyMap.ts @@ -16,7 +16,7 @@ limitations under the License. import {BaseObservableMap, BaseObservableMapConfig} from "./BaseObservableMap"; import {SubscriptionHandle} from "../BaseObservable"; -import {config} from "./config"; +import {config, Mapper, Updater, Comparator, Filter} from "./config"; import {JoinedMap} from "./JoinedMap.js"; import {MappedMap} from "./MappedMap.js"; import {FilteredMap} from "./FilteredMap.js"; @@ -40,15 +40,15 @@ export class ApplyMap extends BaseObservableMap { return this._config.join(this, ...otherMaps); } - mapValues(mapper: any, updater?: (params: any) => void): MappedMap { + mapValues(mapper: Mapper, updater?: Updater): MappedMap { return this._config.mapValues(this, mapper, updater); } - sortValues(comparator: (a: V, b: V) => number): SortedMapList { + sortValues(comparator: Comparator): SortedMapList { return this._config.sortValues(this, comparator); } - filterValues(filter: (v: V, k: K) => boolean): FilteredMap { + filterValues(filter: Filter): FilteredMap { return this._config.filterValues(this, filter); } diff --git a/src/observable/map/BaseObservableMap.ts b/src/observable/map/BaseObservableMap.ts index 37597932..d8a7d43b 100644 --- a/src/observable/map/BaseObservableMap.ts +++ b/src/observable/map/BaseObservableMap.ts @@ -19,6 +19,7 @@ import {JoinedMap} from "../map/JoinedMap.js"; import {MappedMap} from "../map/MappedMap.js"; import {FilteredMap} from "../map/FilteredMap.js"; import {SortedMapList} from "../list/SortedMapList.js"; +import {Mapper, Updater, Comparator, Filter} from "./config"; export interface IMapObserver { onReset(): void; @@ -29,9 +30,9 @@ export interface IMapObserver { export type BaseObservableMapConfig = { join(_this: BaseObservableMap, ...otherMaps: Array>): JoinedMap; - mapValues(_this: BaseObservableMap, mapper: any, updater?: (params: any) => void): MappedMap; - sortValues(_this: BaseObservableMap, comparator: (a: V, b: V) => number): SortedMapList; - filterValues(_this: BaseObservableMap, filter: (v: V, k: K) => boolean): FilteredMap; + mapValues(_this: BaseObservableMap, mapper: any, updater?: Updater): MappedMap; + sortValues(_this: BaseObservableMap, comparator: Comparator): SortedMapList; + filterValues(_this: BaseObservableMap, filter: Filter): FilteredMap; } export abstract class BaseObservableMap extends BaseObservable> { @@ -67,9 +68,9 @@ export abstract class BaseObservableMap extends BaseObservable): JoinedMap; - abstract mapValues(mapper: any, updater?: (params: any) => void): MappedMap; - abstract sortValues(comparator: (a: V, b: V) => number): SortedMapList; - abstract filterValues(filter: (v: V, k: K) => boolean): FilteredMap; + abstract mapValues(mapper: Mapper, updater?: Updater): MappedMap; + abstract sortValues(comparator: Comparator): SortedMapList; + abstract filterValues(filter: Filter): FilteredMap; abstract [Symbol.iterator](): Iterator<[K, V]>; abstract get size(): number; diff --git a/src/observable/map/FilteredMap.ts b/src/observable/map/FilteredMap.ts index 41017bfd..4e3f8ee6 100644 --- a/src/observable/map/FilteredMap.ts +++ b/src/observable/map/FilteredMap.ts @@ -16,7 +16,7 @@ limitations under the License. import {BaseObservableMap, BaseObservableMapConfig} from "./BaseObservableMap"; import {SubscriptionHandle} from "../BaseObservable"; -import {config} from "./config"; +import {config, Mapper, Updater, Comparator, Filter} from "./config"; import {JoinedMap} from "./JoinedMap.js"; import {MappedMap} from "./MappedMap.js"; import {SortedMapList} from "../list/SortedMapList.js"; @@ -24,11 +24,11 @@ import {SortedMapList} from "../list/SortedMapList.js"; export class FilteredMap extends BaseObservableMap { private _source: BaseObservableMap; private _config: BaseObservableMapConfig; - private _filter: (value: V, key: K) => boolean; + private _filter: Filter; private _included?: Map; private _subscription?: SubscriptionHandle; - constructor(source: BaseObservableMap, filter: (value: V, key: K) => boolean) { + constructor(source: BaseObservableMap, filter: Filter) { super(); this._source = source; this._filter = filter; @@ -39,19 +39,19 @@ export class FilteredMap extends BaseObservableMap { return this._config.join(this, ...otherMaps); } - mapValues(mapper: any, updater?: (params: any) => void): MappedMap{ + mapValues(mapper: Mapper, updater?: Updater): MappedMap{ return this._config.mapValues(this, mapper, updater); } - sortValues(comparator: (a: V, b: V) => number): SortedMapList { + sortValues(comparator: Comparator): SortedMapList { return this._config.sortValues(this, comparator); } - filterValues(filter: (v: V, k: K) => boolean): FilteredMap { + filterValues(filter: Filter): FilteredMap { return this._config.filterValues(this, filter); } - setFilter(filter: (value: V, key: K) => boolean) { + setFilter(filter: Filter) { this._filter = filter; if (this._subscription) { this._reapplyFilter(); diff --git a/src/observable/map/JoinedMap.ts b/src/observable/map/JoinedMap.ts index fb67e934..67a85368 100644 --- a/src/observable/map/JoinedMap.ts +++ b/src/observable/map/JoinedMap.ts @@ -15,7 +15,7 @@ limitations under the License. */ import {BaseObservableMap, BaseObservableMapConfig} from "./BaseObservableMap"; -import {config} from "./config"; +import {config, Mapper, Updater, Comparator, Filter} from "./config"; import {FilteredMap} from "./FilteredMap.js"; import {MappedMap} from "./MappedMap.js"; import {SortedMapList} from "../list/SortedMapList.js"; @@ -37,15 +37,15 @@ export class JoinedMap extends BaseObservableMap { return this._config.join(this, ...otherMaps); } - mapValues(mapper: any, updater?: (params: any) => void): MappedMap { + mapValues(mapper: Mapper, updater?: Updater): MappedMap { return this._config.mapValues(this, mapper, updater); } - sortValues(comparator: (a: V, b: V) => number): SortedMapList { + sortValues(comparator: Comparator): SortedMapList { return this._config.sortValues(this, comparator); } - filterValues(filter: (v: V, k: K) => boolean): FilteredMap { + filterValues(filter: Filter): FilteredMap { return this._config.filterValues(this, filter); } diff --git a/src/observable/map/LogMap.ts b/src/observable/map/LogMap.ts index 1417b14c..44b0a59c 100644 --- a/src/observable/map/LogMap.ts +++ b/src/observable/map/LogMap.ts @@ -15,7 +15,7 @@ limitations under the License. */ import {BaseObservableMap, BaseObservableMapConfig} from "./BaseObservableMap"; -import {config} from "./config"; +import {config, Mapper, Updater, Comparator, Filter} from "./config"; import {FilteredMap} from "./FilteredMap.js"; import {MappedMap} from "./MappedMap.js"; import {JoinedMap} from "./JoinedMap.js"; @@ -42,15 +42,15 @@ export class LogMap extends BaseObservableMap { return this._config.join(this, ...otherMaps); } - mapValues(mapper: any, updater?: (params: any) => void): MappedMap { + mapValues(mapper: Mapper, updater?: Updater): MappedMap { return this._config.mapValues(this, mapper, updater); } - sortValues(comparator: (a: V, b: V) => number): SortedMapList { + sortValues(comparator: Comparator): SortedMapList { return this._config.sortValues(this, comparator); } - filterValues(filter: (v: V, k: K) => boolean): FilteredMap { + filterValues(filter: Filter): FilteredMap { return this._config.filterValues(this, filter); } diff --git a/src/observable/map/MappedMap.ts b/src/observable/map/MappedMap.ts index a3dbb000..950273ec 100644 --- a/src/observable/map/MappedMap.ts +++ b/src/observable/map/MappedMap.ts @@ -15,7 +15,7 @@ limitations under the License. */ import {BaseObservableMap, BaseObservableMapConfig} from "./BaseObservableMap"; -import {config} from "./config"; +import {config, Mapper, Updater, Comparator, Filter} from "./config"; import {JoinedMap} from "./JoinedMap.js"; import {FilteredMap} from "./FilteredMap.js"; import {SortedMapList} from "../list/SortedMapList.js"; @@ -50,15 +50,15 @@ export class MappedMap extends BaseObservableMap { return this._config.join(this, ...otherMaps); } - mapValues(mapper: any, updater?: (params: any) => void): MappedMap{ + mapValues(mapper: Mapper, updater?: Updater): MappedMap{ return this._config.mapValues(this, mapper, updater); } - sortValues(comparator: (a: V, b: V) => number): SortedMapList { + sortValues(comparator: Comparator): SortedMapList { return this._config.sortValues(this, comparator); } - filterValues(filter: (v: V, k: K) => boolean): FilteredMap { + filterValues(filter: Filter): FilteredMap { return this._config.filterValues(this, filter); } @@ -128,11 +128,4 @@ export class MappedMap extends BaseObservableMap { get(key: K): V | undefined { return this._mappedValues.get(key); } -} - -type Mapper = ( - value: V, - emitSpontaneousUpdate: any, -) => V; - -type Updater = (params: any, mappedValue?: V, value?: V) => void; \ No newline at end of file +} \ No newline at end of file diff --git a/src/observable/map/ObservableMap.ts b/src/observable/map/ObservableMap.ts index 169d2b58..1be7b2c1 100644 --- a/src/observable/map/ObservableMap.ts +++ b/src/observable/map/ObservableMap.ts @@ -15,7 +15,7 @@ limitations under the License. */ import {BaseObservableMap, BaseObservableMapConfig} from "./BaseObservableMap"; -import {config} from "./config"; +import {config, Mapper, Updater, Comparator, Filter} from "./config"; import {JoinedMap} from "./JoinedMap.js"; import {MappedMap} from "./MappedMap.js"; import {FilteredMap} from "./FilteredMap.js"; @@ -36,15 +36,15 @@ export class ObservableMap extends BaseObservableMap { return this._config.join(this, ...otherMaps); } - mapValues(mapper: any, updater?: (params: any) => void): MappedMap { + mapValues(mapper: Mapper, updater?: Updater): MappedMap { return this._config.mapValues(this, mapper, updater); } - sortValues(comparator: (a: V, b: V) => number): SortedMapList { + sortValues(comparator: Comparator): SortedMapList { return this._config.sortValues(this, comparator); } - filterValues(filter: (v: V, k: K) => boolean): FilteredMap { + filterValues(filter: Filter): FilteredMap { return this._config.filterValues(this, filter); } diff --git a/src/observable/map/config.ts b/src/observable/map/config.ts index 8557031d..54e291e2 100644 --- a/src/observable/map/config.ts +++ b/src/observable/map/config.ts @@ -30,14 +30,25 @@ export function config(): BaseObservableMapConfig { join: (_this: BaseObservableMap, ...otherMaps: Array>): JoinedMap => { return new JoinedMap([_this].concat(otherMaps)); }, - mapValues: (_this: BaseObservableMap, mapper: any, updater: (params: any) => void): MappedMap => { + mapValues: (_this: BaseObservableMap, mapper: Mapper, updater?: Updater): MappedMap => { return new MappedMap(_this, mapper, updater); }, - sortValues: (_this: BaseObservableMap, comparator: (a: V, b: V) => number): SortedMapList => { + sortValues: (_this: BaseObservableMap, comparator: Comparator): SortedMapList => { return new SortedMapList(_this, comparator); }, - filterValues: (_this: BaseObservableMap, filter: (v: V, k: K) => boolean): FilteredMap => { + filterValues: (_this: BaseObservableMap, filter: Filter): FilteredMap => { return new FilteredMap(_this, filter); } }; -} \ No newline at end of file +} + +export type Mapper = ( + value: V, + emitSpontaneousUpdate: any, +) => V; + +export type Updater = (params: any, mappedValue?: V, value?: V) => void; + +export type Comparator = (a: V, b: V) => number; + +export type Filter = (v: V, k: K) => boolean; \ No newline at end of file From deab8bdaf074aafe3a88b77ad529a9552cb4063c Mon Sep 17 00:00:00 2001 From: Isaiah Becker-Mayer Date: Sat, 9 Jul 2022 12:17:59 -0400 Subject: [PATCH 14/24] moves boilerplate to bottom of classes --- src/observable/map/ApplyMap.ts | 33 +++++++++++++++-------------- src/observable/map/FilteredMap.ts | 32 ++++++++++++++-------------- src/observable/map/JoinedMap.ts | 33 +++++++++++++++-------------- src/observable/map/LogMap.ts | 33 +++++++++++++++-------------- src/observable/map/MappedMap.ts | 32 ++++++++++++++-------------- src/observable/map/ObservableMap.ts | 32 ++++++++++++++-------------- 6 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/observable/map/ApplyMap.ts b/src/observable/map/ApplyMap.ts index 4f79a9df..4dbbc3d3 100644 --- a/src/observable/map/ApplyMap.ts +++ b/src/observable/map/ApplyMap.ts @@ -36,22 +36,6 @@ export class ApplyMap extends BaseObservableMap { this._config = config(); } - join(...otherMaps: Array): JoinedMap { - return this._config.join(this, ...otherMaps); - } - - mapValues(mapper: Mapper, updater?: Updater): MappedMap { - return this._config.mapValues(this, mapper, updater); - } - - sortValues(comparator: Comparator): SortedMapList { - return this._config.sortValues(this, comparator); - } - - filterValues(filter: Filter): FilteredMap { - return this._config.filterValues(this, filter); - } - hasApply() { return !!this._apply; } @@ -118,6 +102,23 @@ export class ApplyMap extends BaseObservableMap { get(key: K) { return this._source.get(key); } + + join(...otherMaps: Array): JoinedMap { + return this._config.join(this, ...otherMaps); + } + + mapValues(mapper: Mapper, updater?: Updater): MappedMap { + return this._config.mapValues(this, mapper, updater); + } + + sortValues(comparator: Comparator): SortedMapList { + return this._config.sortValues(this, comparator); + } + + filterValues(filter: Filter): FilteredMap { + return this._config.filterValues(this, filter); + } + } type Apply = (key: K, value: V, params?: any) => void; \ No newline at end of file diff --git a/src/observable/map/FilteredMap.ts b/src/observable/map/FilteredMap.ts index 4e3f8ee6..782a67fe 100644 --- a/src/observable/map/FilteredMap.ts +++ b/src/observable/map/FilteredMap.ts @@ -35,22 +35,6 @@ export class FilteredMap extends BaseObservableMap { this._config = config(); } - join(...otherMaps: Array): JoinedMap { - return this._config.join(this, ...otherMaps); - } - - mapValues(mapper: Mapper, updater?: Updater): MappedMap{ - return this._config.mapValues(this, mapper, updater); - } - - sortValues(comparator: Comparator): SortedMapList { - return this._config.sortValues(this, comparator); - } - - filterValues(filter: Filter): FilteredMap { - return this._config.filterValues(this, filter); - } - setFilter(filter: Filter) { this._filter = filter; if (this._subscription) { @@ -178,6 +162,22 @@ export class FilteredMap extends BaseObservableMap { return value; } } + + join(...otherMaps: Array): JoinedMap { + return this._config.join(this, ...otherMaps); + } + + mapValues(mapper: Mapper, updater?: Updater): MappedMap{ + return this._config.mapValues(this, mapper, updater); + } + + sortValues(comparator: Comparator): SortedMapList { + return this._config.sortValues(this, comparator); + } + + filterValues(filter: Filter): FilteredMap { + return this._config.filterValues(this, filter); + } } class FilterIterator { diff --git a/src/observable/map/JoinedMap.ts b/src/observable/map/JoinedMap.ts index 67a85368..2be95bdf 100644 --- a/src/observable/map/JoinedMap.ts +++ b/src/observable/map/JoinedMap.ts @@ -33,22 +33,6 @@ export class JoinedMap extends BaseObservableMap { this._config = config(); } - join(...otherMaps: Array): JoinedMap { - return this._config.join(this, ...otherMaps); - } - - mapValues(mapper: Mapper, updater?: Updater): MappedMap { - return this._config.mapValues(this, mapper, updater); - } - - sortValues(comparator: Comparator): SortedMapList { - return this._config.sortValues(this, comparator); - } - - filterValues(filter: Filter): FilteredMap { - return this._config.filterValues(this, filter); - } - onAdd(source: BaseObservableMap, key: K, value: V) { if (!this._isKeyAtSourceOccluded(source, key)) { const occludingValue = this._getValueFromOccludedSources(source, key); @@ -149,6 +133,23 @@ export class JoinedMap extends BaseObservableMap { } return undefined; } + + join(...otherMaps: Array): JoinedMap { + return this._config.join(this, ...otherMaps); + } + + mapValues(mapper: Mapper, updater?: Updater): MappedMap { + return this._config.mapValues(this, mapper, updater); + } + + sortValues(comparator: Comparator): SortedMapList { + return this._config.sortValues(this, comparator); + } + + filterValues(filter: Filter): FilteredMap { + return this._config.filterValues(this, filter); + } + } class JoinedIterator implements Iterator<[K, V]> { diff --git a/src/observable/map/LogMap.ts b/src/observable/map/LogMap.ts index 44b0a59c..6f3bf676 100644 --- a/src/observable/map/LogMap.ts +++ b/src/observable/map/LogMap.ts @@ -38,22 +38,6 @@ export class LogMap extends BaseObservableMap { this._config = config(); } - join(...otherMaps: Array): JoinedMap { - return this._config.join(this, ...otherMaps); - } - - mapValues(mapper: Mapper, updater?: Updater): MappedMap { - return this._config.mapValues(this, mapper, updater); - } - - sortValues(comparator: Comparator): SortedMapList { - return this._config.sortValues(this, comparator); - } - - filterValues(filter: Filter): FilteredMap { - return this._config.filterValues(this, filter); - } - private log(labelOrValues: LabelOrValues, logLevel?: LogLevel): ILogItem { return this._log.log(labelOrValues, logLevel); } @@ -101,4 +85,21 @@ export class LogMap extends BaseObservableMap { get(key: K) { return this._source.get(key); } + + join(...otherMaps: Array): JoinedMap { + return this._config.join(this, ...otherMaps); + } + + mapValues(mapper: Mapper, updater?: Updater): MappedMap { + return this._config.mapValues(this, mapper, updater); + } + + sortValues(comparator: Comparator): SortedMapList { + return this._config.sortValues(this, comparator); + } + + filterValues(filter: Filter): FilteredMap { + return this._config.filterValues(this, filter); + } + } diff --git a/src/observable/map/MappedMap.ts b/src/observable/map/MappedMap.ts index 950273ec..396802bb 100644 --- a/src/observable/map/MappedMap.ts +++ b/src/observable/map/MappedMap.ts @@ -46,22 +46,6 @@ export class MappedMap extends BaseObservableMap { this._config = config(); } - join(...otherMaps: Array): JoinedMap { - return this._config.join(this, ...otherMaps); - } - - mapValues(mapper: Mapper, updater?: Updater): MappedMap{ - return this._config.mapValues(this, mapper, updater); - } - - sortValues(comparator: Comparator): SortedMapList { - return this._config.sortValues(this, comparator); - } - - filterValues(filter: Filter): FilteredMap { - return this._config.filterValues(this, filter); - } - _emitSpontaneousUpdate(key: K, params: any) { const value = this._mappedValues.get(key); if (value) { @@ -128,4 +112,20 @@ export class MappedMap extends BaseObservableMap { get(key: K): V | undefined { return this._mappedValues.get(key); } + + join(...otherMaps: Array): JoinedMap { + return this._config.join(this, ...otherMaps); + } + + mapValues(mapper: Mapper, updater?: Updater): MappedMap{ + return this._config.mapValues(this, mapper, updater); + } + + sortValues(comparator: Comparator): SortedMapList { + return this._config.sortValues(this, comparator); + } + + filterValues(filter: Filter): FilteredMap { + return this._config.filterValues(this, filter); + } } \ No newline at end of file diff --git a/src/observable/map/ObservableMap.ts b/src/observable/map/ObservableMap.ts index 1be7b2c1..6ce6ea91 100644 --- a/src/observable/map/ObservableMap.ts +++ b/src/observable/map/ObservableMap.ts @@ -32,22 +32,6 @@ export class ObservableMap extends BaseObservableMap { this._values = new Map(initialValues); } - join(...otherMaps: Array): JoinedMap { - return this._config.join(this, ...otherMaps); - } - - mapValues(mapper: Mapper, updater?: Updater): MappedMap { - return this._config.mapValues(this, mapper, updater); - } - - sortValues(comparator: Comparator): SortedMapList { - return this._config.sortValues(this, comparator); - } - - filterValues(filter: Filter): FilteredMap { - return this._config.filterValues(this, filter); - } - update(key: K, params?: any): boolean { const value = this._values.get(key); if (value !== undefined) { @@ -115,6 +99,22 @@ export class ObservableMap extends BaseObservableMap { keys(): Iterator { return this._values.keys(); } + + join(...otherMaps: Array): JoinedMap { + return this._config.join(this, ...otherMaps); + } + + mapValues(mapper: Mapper, updater?: Updater): MappedMap { + return this._config.mapValues(this, mapper, updater); + } + + sortValues(comparator: Comparator): SortedMapList { + return this._config.sortValues(this, comparator); + } + + filterValues(filter: Filter): FilteredMap { + return this._config.filterValues(this, filter); + } }; export function tests() { From 0203ece3bd593608b693e14806ed6a53f97d6ba2 Mon Sep 17 00:00:00 2001 From: Isaiah Becker-Mayer Date: Sat, 9 Jul 2022 12:43:24 -0400 Subject: [PATCH 15/24] updates ts-eslint and fixes errors in src/observable --- .ts-eslintrc.js | 8 ++-- src/observable/BaseObservable.ts | 9 +++-- src/observable/ObservableValue.ts | 29 +++++++------- src/observable/list/AsyncMappedList.ts | 6 ++- src/observable/list/BaseMappedList.ts | 5 ++- src/observable/list/BaseObservableList.ts | 12 +++--- src/observable/list/ConcatList.ts | 13 ++++-- src/observable/list/MappedList.ts | 19 +++++---- src/observable/list/ObservableArray.ts | 1 + src/observable/list/SortedArray.ts | 9 +++-- src/observable/list/common.ts | 7 +++- src/observable/map/ApplyMap.ts | 23 ++++++----- src/observable/map/BaseObservableMap.ts | 8 ++-- src/observable/map/FilteredMap.ts | 36 +++++++++-------- src/observable/map/JoinedMap.ts | 49 ++++++++++++----------- src/observable/map/LogMap.ts | 17 ++++---- src/observable/map/MappedMap.ts | 17 ++++---- src/observable/map/ObservableMap.ts | 17 ++++---- 18 files changed, 159 insertions(+), 126 deletions(-) diff --git a/.ts-eslintrc.js b/.ts-eslintrc.js index ac73bb34..ae7233ed 100644 --- a/.ts-eslintrc.js +++ b/.ts-eslintrc.js @@ -20,8 +20,10 @@ module.exports = { rules: { "@typescript-eslint/no-floating-promises": 2, "@typescript-eslint/no-misused-promises": 2, - 'no-unused-vars': 'off', - '@typescript-eslint/no-unused-vars': ['warn'], - 'no-undef': 'off', + "no-unused-vars": "off", + "@typescript-eslint/no-unused-vars": ["warn"], + "no-undef": "off", + "semi": ["error", "always"], + "@typescript-eslint/explicit-function-return-type": ["error"] } }; diff --git a/src/observable/BaseObservable.ts b/src/observable/BaseObservable.ts index 11ecd8a0..edbdd8bc 100644 --- a/src/observable/BaseObservable.ts +++ b/src/observable/BaseObservable.ts @@ -34,7 +34,7 @@ export abstract class BaseObservable { if (this._handlers.size === 1) { this.onSubscribeFirst(); } - return () => { + return (): undefined => { return this.unsubscribe(handler); }; } @@ -63,17 +63,18 @@ export abstract class BaseObservable { // Add iterator over handlers here } +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type export function tests() { class Collection extends BaseObservable<{}> { firstSubscribeCalls: number = 0; firstUnsubscribeCalls: number = 0; - onSubscribeFirst() { this.firstSubscribeCalls += 1; } - onUnsubscribeLast() { this.firstUnsubscribeCalls += 1; } + onSubscribeFirst(): void { this.firstSubscribeCalls += 1; } + onUnsubscribeLast(): void { this.firstUnsubscribeCalls += 1; } } return { - test_unsubscribe(assert) { + test_unsubscribe(assert): void { const c = new Collection(); const unsubscribe = c.subscribe({}); unsubscribe(); diff --git a/src/observable/ObservableValue.ts b/src/observable/ObservableValue.ts index dab8fb52..96791f91 100644 --- a/src/observable/ObservableValue.ts +++ b/src/observable/ObservableValue.ts @@ -20,7 +20,7 @@ import type {SubscriptionHandle} from "./BaseObservable"; // like an EventEmitter, but doesn't have an event type export abstract class BaseObservableValue extends BaseObservable<(value: T) => void> { - emit(argument: T) { + emit(argument: T): void { for (const h of this._handlers) { h(argument); } @@ -68,7 +68,7 @@ class WaitForHandle implements IWaitHandle { return this._promise; } - dispose() { + dispose(): void { if (this._subscription) { this._subscription(); this._subscription = null; @@ -82,7 +82,7 @@ class WaitForHandle implements IWaitHandle { class ResolvedWaitForHandle implements IWaitHandle { constructor(public promise: Promise) {} - dispose() {} + dispose(): void {} } export class ObservableValue extends BaseObservableValue { @@ -113,7 +113,7 @@ export class RetainedObservableValue extends ObservableValue { this._freeCallback = freeCallback; } - onUnsubscribeLast() { + onUnsubscribeLast(): void { super.onUnsubscribeLast(); this._freeCallback(); } @@ -130,7 +130,7 @@ export class FlatMapObservableValue extends BaseObservableValue extends BaseObservableValue { this.updateTargetSubscription(); @@ -147,7 +147,7 @@ export class FlatMapObservableValue extends BaseObservableValue extends BaseObservableValue { + "set emits an update": (assert): void => { const a = new ObservableValue(0); let fired = false; const subscription = a.subscribe(v => { @@ -187,7 +188,7 @@ export function tests() { assert(fired); subscription(); }, - "set doesn't emit if value hasn't changed": assert => { + "set doesn't emit if value hasn't changed": (assert): void => { const a = new ObservableValue(5); let fired = false; const subscription = a.subscribe(() => { @@ -198,7 +199,7 @@ export function tests() { assert(!fired); subscription(); }, - "waitFor promise resolves on matching update": async assert => { + "waitFor promise resolves on matching update": async (assert): Promise => { const a = new ObservableValue(5); const handle = a.waitFor(v => v === 6); await Promise.resolve().then(() => { @@ -207,7 +208,7 @@ export function tests() { await handle.promise; assert.strictEqual(a.get(), 6); }, - "waitFor promise rejects when disposed": async assert => { + "waitFor promise rejects when disposed": async (assert): Promise => { const a = new ObservableValue(0); const handle = a.waitFor(() => false); await Promise.resolve().then(() => { @@ -215,7 +216,7 @@ export function tests() { }); await assert.rejects(handle.promise, AbortError); }, - "flatMap.get": assert => { + "flatMap.get": (assert): void => { const a = new ObservableValue}>(undefined); const countProxy = a.flatMap(a => a!.count); assert.strictEqual(countProxy.get(), undefined); @@ -223,7 +224,7 @@ export function tests() { a.set({count}); assert.strictEqual(countProxy.get(), 0); }, - "flatMap update from source": assert => { + "flatMap update from source": (assert): void => { const a = new ObservableValue}>(undefined); const updates: (number | undefined)[] = []; a.flatMap(a => a!.count).subscribe(count => { @@ -233,7 +234,7 @@ export function tests() { a.set({count}); assert.deepEqual(updates, [0]); }, - "flatMap update from target": assert => { + "flatMap update from target": (assert): void => { const a = new ObservableValue}>(undefined); const updates: (number | undefined)[] = []; a.flatMap(a => a!.count).subscribe(count => { diff --git a/src/observable/list/AsyncMappedList.ts b/src/observable/list/AsyncMappedList.ts index 53edde21..f1785c13 100644 --- a/src/observable/list/AsyncMappedList.ts +++ b/src/observable/list/AsyncMappedList.ts @@ -135,10 +135,12 @@ class ResetEvent { import {ObservableArray} from "./ObservableArray"; import {ListObserver} from "../../mocks/ListObserver.js"; + +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type export function tests() { return { - "events are emitted in order": async assert => { - const double = n => n * n; + "events are emitted in order": async (assert): Promise => { + const double = (n: number): number => n * n; const source = new ObservableArray(); const mapper = new AsyncMappedList(source, async n => { await new Promise(r => setTimeout(r, n)); diff --git a/src/observable/list/BaseMappedList.ts b/src/observable/list/BaseMappedList.ts index 4e3d05e0..0435a760 100644 --- a/src/observable/list/BaseMappedList.ts +++ b/src/observable/list/BaseMappedList.ts @@ -37,14 +37,15 @@ export class BaseMappedList extends BaseObservableList { this._removeCallback = removeCallback; } - findAndUpdate(predicate: (value: T) => boolean, updater: (value: T) => any | false) { + findAndUpdate(predicate: (value: T) => boolean, updater: (value: T) => any | false): boolean { return findAndUpdateInArray(predicate, this._mappedValues!, this, updater); } - get length() { + get length(): number { return this._mappedValues!.length; } + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type [Symbol.iterator]() { return this._mappedValues!.values(); } diff --git a/src/observable/list/BaseObservableList.ts b/src/observable/list/BaseObservableList.ts index e2806c72..1fd82c25 100644 --- a/src/observable/list/BaseObservableList.ts +++ b/src/observable/list/BaseObservableList.ts @@ -26,17 +26,17 @@ export interface IListObserver { export function defaultObserverWith(overrides: { [key in keyof IListObserver]?: IListObserver[key] }): IListObserver { const defaults = { - onReset(){}, - onAdd(){}, - onUpdate(){}, - onRemove(){}, - onMove(){}, + onReset(): void {}, + onAdd(): void {}, + onUpdate(): void {}, + onRemove(): void {}, + onMove(): void {}, }; return Object.assign(defaults, overrides); } export abstract class BaseObservableList extends BaseObservable> implements Iterable { - emitReset() { + emitReset(): void { for(let h of this._handlers) { h.onReset(this); } diff --git a/src/observable/list/ConcatList.ts b/src/observable/list/ConcatList.ts index 8ef7326c..aaad5a6a 100644 --- a/src/observable/list/ConcatList.ts +++ b/src/observable/list/ConcatList.ts @@ -86,10 +86,12 @@ export class ConcatList extends BaseObservableList implements IListObserve return len; } + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type [Symbol.iterator]() { let sourceListIdx = 0; let it = this._sourceLists[0][Symbol.iterator](); return { + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type next: () => { let result = it.next(); while (result.done) { @@ -108,16 +110,19 @@ export class ConcatList extends BaseObservableList implements IListObserve import {ObservableArray} from "./ObservableArray"; import {defaultObserverWith} from "./BaseObservableList"; + + +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type export async function tests() { return { - test_length(assert) { + test_length(assert): void { const all = new ConcatList( new ObservableArray([1, 2, 3]), new ObservableArray([11, 12, 13]) ); assert.equal(all.length, 6); }, - test_iterator(assert) { + test_iterator(assert): void { const all = new ConcatList( new ObservableArray([1, 2, 3]), new ObservableArray([11, 12, 13]) @@ -131,7 +136,7 @@ export async function tests() { assert.equal(it.next().value, 13); assert(it.next().done); }, - test_add(assert) { + test_add(assert): void { const list1 = new ObservableArray([1, 2, 3]); const list2 = new ObservableArray([11, 12, 13]); const all = new ConcatList(list1, list2); @@ -146,7 +151,7 @@ export async function tests() { list2.insert(1, 11.5); assert(fired); }, - test_update(assert) { + test_update(assert): void { const list1 = new ObservableArray([1, 2, 3]); const list2 = new ObservableArray([11, 12, 13]); const all = new ConcatList(list1, list2); diff --git a/src/observable/list/MappedList.ts b/src/observable/list/MappedList.ts index ebb418d3..2ddae698 100644 --- a/src/observable/list/MappedList.ts +++ b/src/observable/list/MappedList.ts @@ -19,7 +19,7 @@ import {IListObserver} from "./BaseObservableList"; import {BaseMappedList, runAdd, runUpdate, runRemove, runMove, runReset} from "./BaseMappedList"; export class MappedList extends BaseMappedList implements IListObserver { - onSubscribeFirst() { + onSubscribeFirst(): void { this._sourceUnsubscribe = this._sourceList.subscribe(this); this._mappedValues = []; for (const item of this._sourceList) { @@ -61,18 +61,21 @@ import {ObservableArray} from "./ObservableArray"; import {BaseObservableList} from "./BaseObservableList"; import {defaultObserverWith} from "./BaseObservableList"; +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type export async function tests() { class MockList extends BaseObservableList { - get length() { + get length(): 0 { return 0; } + + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type [Symbol.iterator]() { return [].values(); } } return { - test_add(assert) { + test_add(assert): void { const source = new MockList(); const mapped = new MappedList(source, n => {return {n: n*n};}); let fired = false; @@ -87,7 +90,7 @@ export async function tests() { assert(fired); unsubscribe(); }, - test_update(assert) { + test_update(assert): void { const source = new MockList(); const mapped = new MappedList( source, @@ -109,7 +112,7 @@ export async function tests() { assert(fired); unsubscribe(); }, - "test findAndUpdate not found": assert => { + "test findAndUpdate not found": (assert): void => { const source = new ObservableArray([1, 3, 4]); const mapped = new MappedList( source, @@ -123,7 +126,7 @@ export async function tests() { () => assert.fail() ), false); }, - "test findAndUpdate found but updater bails out of update": assert => { + "test findAndUpdate found but updater bails out of update": (assert): void => { const source = new ObservableArray([1, 3, 4]); const mapped = new MappedList( source, @@ -143,7 +146,7 @@ export async function tests() { ), true); assert.equal(fired, true); }, - "test findAndUpdate emits update": assert => { + "test findAndUpdate emits update": (assert): void => { const source = new ObservableArray([1, 3, 4]); const mapped = new MappedList( source, @@ -161,6 +164,6 @@ export async function tests() { assert.equal(mapped.findAndUpdate(n => n === 9, () => "param"), true); assert.equal(fired, true); }, - + }; } diff --git a/src/observable/list/ObservableArray.ts b/src/observable/list/ObservableArray.ts index 0771d0f6..662f715e 100644 --- a/src/observable/list/ObservableArray.ts +++ b/src/observable/list/ObservableArray.ts @@ -75,6 +75,7 @@ export class ObservableArray extends BaseObservableList { return this._items.length; } + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type [Symbol.iterator]() { return this._items.values(); } diff --git a/src/observable/list/SortedArray.ts b/src/observable/list/SortedArray.ts index 7df285a5..c956f7b8 100644 --- a/src/observable/list/SortedArray.ts +++ b/src/observable/list/SortedArray.ts @@ -112,6 +112,7 @@ export class SortedArray extends BaseObservableList { return this._items.length; } + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type [Symbol.iterator]() { return new Iterator(this); } @@ -127,6 +128,7 @@ class Iterator { this._current = null; } + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type next() { if (this._sortedArray) { if (this._current) { @@ -147,9 +149,10 @@ class Iterator { } } +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type export function tests() { return { - "setManyUnsorted": assert => { + "setManyUnsorted": (assert): void => { const sa = new SortedArray((a, b) => a.localeCompare(b)); sa.setManyUnsorted(["b", "a", "c"]); assert.equal(sa.length, 3); @@ -157,7 +160,7 @@ export function tests() { assert.equal(sa.get(1), "b"); assert.equal(sa.get(2), "c"); }, - "_getNext": assert => { + "_getNext": (assert): void => { const sa = new SortedArray((a, b) => a.localeCompare(b)); sa.setManyUnsorted(["b", "a", "f"]); assert.equal(sa._getNext("a"), "b"); @@ -166,7 +169,7 @@ export function tests() { assert.equal(sa._getNext("c"), "f"); assert.equal(sa._getNext("f"), undefined); }, - "iterator with removals": assert => { + "iterator with removals": (assert): void => { const queue = new SortedArray<{idx: number}>((a, b) => a.idx - b.idx); queue.setManyUnsorted([{idx: 5}, {idx: 3}, {idx: 1}, {idx: 4}, {idx: 2}]); const it = queue[Symbol.iterator](); diff --git a/src/observable/list/common.ts b/src/observable/list/common.ts index c67a841b..20f3a8bf 100644 --- a/src/observable/list/common.ts +++ b/src/observable/list/common.ts @@ -17,7 +17,12 @@ 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: (value: T) => boolean, array: T[], observable: BaseObservableList, updater: (value: T) => any | false) { +export function findAndUpdateInArray( + predicate: (value: T) => boolean, + array: T[], + observable: BaseObservableList, + updater: (value: T) => any | false +): boolean { const index = array.findIndex(predicate); if (index !== -1) { const value = array[index]; diff --git a/src/observable/map/ApplyMap.ts b/src/observable/map/ApplyMap.ts index 4dbbc3d3..4d45b519 100644 --- a/src/observable/map/ApplyMap.ts +++ b/src/observable/map/ApplyMap.ts @@ -36,42 +36,42 @@ export class ApplyMap extends BaseObservableMap { this._config = config(); } - hasApply() { + hasApply(): boolean { return !!this._apply; } - setApply(apply?: Apply) { + setApply(apply?: Apply): void { this._apply = apply; if (this._apply) { this.applyOnce(this._apply); } } - applyOnce(apply: Apply) { + applyOnce(apply: Apply): void { for (const [key, value] of this._source) { apply(key, value); } } - onAdd(key: K, value: V) { + onAdd(key: K, value: V): void { if (this._apply) { this._apply(key, value); } this.emitAdd(key, value); } - onRemove(key: K, value: V) { + onRemove(key: K, value: V): void { this.emitRemove(key, value); } - onUpdate(key: K, value: V, params: any) { + onUpdate(key: K, value: V, params: any): void { if (this._apply) { this._apply(key, value, params); } this.emitUpdate(key, value, params); } - onSubscribeFirst() { + onSubscribeFirst(): void { this._subscription = this._source.subscribe(this); if (this._apply) { this.applyOnce(this._apply); @@ -79,27 +79,28 @@ export class ApplyMap extends BaseObservableMap { super.onSubscribeFirst(); } - onUnsubscribeLast() { + onUnsubscribeLast(): void { super.onUnsubscribeLast(); if (this._subscription) this._subscription = this._subscription(); } - onReset() { + onReset(): void { if (this._apply) { this.applyOnce(this._apply); } this.emitReset(); } + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type [Symbol.iterator]() { return this._source[Symbol.iterator](); } - get size() { + get size(): number { return this._source.size; } - get(key: K) { + get(key: K): V | undefined { return this._source.get(key); } diff --git a/src/observable/map/BaseObservableMap.ts b/src/observable/map/BaseObservableMap.ts index d8a7d43b..206770ee 100644 --- a/src/observable/map/BaseObservableMap.ts +++ b/src/observable/map/BaseObservableMap.ts @@ -36,26 +36,26 @@ export type BaseObservableMapConfig = { } export abstract class BaseObservableMap extends BaseObservable> { - emitReset() { + emitReset(): void { for(let h of this._handlers) { h.onReset(); } } // we need batch events, mostly on index based collection though? // maybe we should get started without? - emitAdd(key: K, value: V) { + emitAdd(key: K, value: V): void { for(let h of this._handlers) { h.onAdd(key, value); } } - emitUpdate(key: K, value: V, params: any) { + emitUpdate(key: K, value: V, params: any): void { for(let h of this._handlers) { h.onUpdate(key, value, params); } } - emitRemove(key: K, value: V) { + emitRemove(key: K, value: V): void { for(let h of this._handlers) { h.onRemove(key, value); } diff --git a/src/observable/map/FilteredMap.ts b/src/observable/map/FilteredMap.ts index 782a67fe..613b1439 100644 --- a/src/observable/map/FilteredMap.ts +++ b/src/observable/map/FilteredMap.ts @@ -35,7 +35,7 @@ export class FilteredMap extends BaseObservableMap { this._config = config(); } - setFilter(filter: Filter) { + setFilter(filter: Filter): void { this._filter = filter; if (this._subscription) { this._reapplyFilter(); @@ -45,7 +45,7 @@ export class FilteredMap extends BaseObservableMap { /** * reapply the filter */ - _reapplyFilter(silent = false) { + _reapplyFilter(silent = false): void { if (this._filter) { const oldIncluded = this._included; this._included = this._included || new Map(); @@ -71,7 +71,7 @@ export class FilteredMap extends BaseObservableMap { } } - onAdd(key: K, value: V) { + onAdd(key: K, value: V): void { if (this._filter) { if (this._included) { const included = this._filter(value, key); @@ -86,7 +86,7 @@ export class FilteredMap extends BaseObservableMap { this.emitAdd(key, value); } - onRemove(key: K, value: V) { + onRemove(key: K, value: V): void { const wasIncluded = !this._filter || this._included?.get(key); if (this._included) { this._included.delete(key); @@ -98,7 +98,7 @@ export class FilteredMap extends BaseObservableMap { } } - onUpdate(key: K, value: V, params: any) { + onUpdate(key: K, value: V, params: any): void { // if an update is emitted while calling source.subscribe() from onSubscribeFirst, ignore it if (!this._included) { return; @@ -113,7 +113,7 @@ export class FilteredMap extends BaseObservableMap { } } - _emitForUpdate(wasIncluded: boolean | undefined, isIncluded: boolean, key: K, value: V, params: any = null) { + _emitForUpdate(wasIncluded: boolean | undefined, isIncluded: boolean, key: K, value: V, params: any = null): void { if (wasIncluded && !isIncluded) { this.emitRemove(key, value); } else if (!wasIncluded && isIncluded) { @@ -123,13 +123,13 @@ export class FilteredMap extends BaseObservableMap { } } - onSubscribeFirst() { + onSubscribeFirst(): void { this._subscription = this._source.subscribe(this); this._reapplyFilter(true); super.onSubscribeFirst(); } - onUnsubscribeLast() { + onUnsubscribeLast(): void { super.onUnsubscribeLast(); this._included = undefined; if (this._subscription) { @@ -137,16 +137,17 @@ export class FilteredMap extends BaseObservableMap { } } - onReset() { + onReset(): void { this._reapplyFilter(); this.emitReset(); } + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type [Symbol.iterator]() { return new FilterIterator(this._source, this._included); } - get size() { + get size(): number { let count = 0; this._included?.forEach(included => { if (included) { @@ -156,7 +157,7 @@ export class FilteredMap extends BaseObservableMap { return count; } - get(key) { + get(key): V | undefined{ const value = this._source.get(key); if (value && this._filter(value, key)) { return value; @@ -188,6 +189,7 @@ class FilterIterator { this._sourceIterator = map[Symbol.iterator](); } + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type next() { // eslint-disable-next-line no-constant-condition while (true) { @@ -204,9 +206,11 @@ class FilterIterator { } import {ObservableMap} from ".."; + +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type export function tests() { return { - "filter preloaded list": assert => { + "filter preloaded list": (assert): void => { const source = new ObservableMap(); source.add("one", 1); source.add("two", 2); @@ -233,17 +237,17 @@ export function tests() { assert.deepEqual(it.next().value, ["three", 3]); assert.equal(it.next().done, true); }, - // "filter added values": assert => { + // "filter added values": (assert): void => { // }, - // "filter removed values": assert => { + // "filter removed values": (assert): void => { // }, - // "filter changed values": assert => { + // "filter changed values": (assert): void => { // }, - "emits must trigger once": assert => { + "emits must trigger once": (assert): void => { const source = new ObservableMap(); let count_add = 0, count_update = 0, count_remove = 0; source.add("num1", 1); diff --git a/src/observable/map/JoinedMap.ts b/src/observable/map/JoinedMap.ts index 2be95bdf..1b145d85 100644 --- a/src/observable/map/JoinedMap.ts +++ b/src/observable/map/JoinedMap.ts @@ -33,7 +33,7 @@ export class JoinedMap extends BaseObservableMap { this._config = config(); } - onAdd(source: BaseObservableMap, key: K, value: V) { + onAdd(source: BaseObservableMap, key: K, value: V): void { if (!this._isKeyAtSourceOccluded(source, key)) { const occludingValue = this._getValueFromOccludedSources(source, key); if (occludingValue !== undefined) { @@ -45,7 +45,7 @@ export class JoinedMap extends BaseObservableMap { } } - onRemove(source: BaseObservableMap, key: K, value: V) { + onRemove(source: BaseObservableMap, key: K, value: V): void { if (!this._isKeyAtSourceOccluded(source, key)) { this.emitRemove(key, value); const occludedValue = this._getValueFromOccludedSources(source, key); @@ -57,7 +57,7 @@ export class JoinedMap extends BaseObservableMap { } } - onUpdate(source: BaseObservableMap, key: K, value: V, params: any) { + onUpdate(source: BaseObservableMap, key: K, value: V, params: any): void { // if an update is emitted while calling source.subscribe() from onSubscribeFirst, ignore it if (!this._subscriptions) { return; @@ -67,16 +67,16 @@ export class JoinedMap extends BaseObservableMap { } } - onReset() { + onReset(): void { this.emitReset(); } - onSubscribeFirst() { + onSubscribeFirst(): void { this._subscriptions = this._sources.map(source => new SourceSubscriptionHandler(source, this).subscribe()); super.onSubscribeFirst(); } - _isKeyAtSourceOccluded(source: BaseObservableMap, key: K) { + _isKeyAtSourceOccluded(source: BaseObservableMap, key: K): boolean { // sources that come first in the sources array can // hide the keys in later sources, to prevent events // being emitted for the same key and different values, @@ -91,7 +91,7 @@ export class JoinedMap extends BaseObservableMap { } // get the value that the given source and key occlude, if any - _getValueFromOccludedSources(source: BaseObservableMap, key: K) { + _getValueFromOccludedSources(source: BaseObservableMap, key: K): V | undefined{ // sources that come first in the sources array can // hide the keys in later sources, to prevent events // being emitted for the same key and different values, @@ -107,7 +107,7 @@ export class JoinedMap extends BaseObservableMap { return undefined; } - onUnsubscribeLast() { + onUnsubscribeLast(): void { super.onUnsubscribeLast(); if (this._subscriptions) { for (const s of this._subscriptions) { @@ -116,15 +116,16 @@ export class JoinedMap extends BaseObservableMap { } } + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type [Symbol.iterator]() { return new JoinedIterator(this._sources); } - get size() { + get size(): number { return this._sources.reduce((sum, s) => sum + s.size, 0); } - get(key: K): V | undefined{ + get(key: K): V | undefined { for (const s of this._sources) { const value = s.get(key); if (value) { @@ -199,28 +200,28 @@ class SourceSubscriptionHandler { this._subscription = undefined; } - subscribe() { + subscribe(): this { this._subscription = this._source.subscribe(this); return this; } - dispose() { + dispose(): void { if (this._subscription) this._subscription = this._subscription(); } - onAdd(key: K, value: V) { + onAdd(key: K, value: V): void { this._joinedMap.onAdd(this._source, key, value); } - onRemove(key: K, value: V) { + onRemove(key: K, value: V): void { this._joinedMap.onRemove(this._source, key, value); } - onUpdate(key: K, value: V, params: any) { + onUpdate(key: K, value: V, params: any): void { this._joinedMap.onUpdate(this._source, key, value, params); } - onReset() { + onReset(): void { this._joinedMap.onReset(); } } @@ -228,9 +229,9 @@ class SourceSubscriptionHandler { import {ObservableMap} from ".."; +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type export function tests() { - - function observeMap(map: JoinedMap) { + function observeMap(map: JoinedMap): { type: string; key: any; value: any; params?: any; }[] { const events: { type: string, key: any, value: any, params?: any }[] = []; map.subscribe({ onAdd(key, value) { events.push({ type: "add", key, value }); }, @@ -244,7 +245,7 @@ export function tests() { } return { - "joined iterator": assert => { + "joined iterator": (assert): void => { const firstKV: [string, number] = ["a", 1]; const secondKV: [string, number] = ["b", 2]; const thirdKV: [string, number] = ["c", 3]; @@ -254,7 +255,7 @@ export function tests() { assert.equal(it.next().value, thirdKV); assert.equal(it.next().done, true); }, - "prevent key collision during iteration": assert => { + "prevent key collision during iteration": (assert): void => { const first = new ObservableMap(); const second = new ObservableMap(); const join = new JoinedMap([first, second]); @@ -266,7 +267,7 @@ export function tests() { assert.deepEqual(it.next().value, ["b", 3]); assert.equal(it.next().done, true); }, - "adding occluded key doesn't emit add": assert => { + "adding occluded key doesn't emit add": (assert): void => { const first = new ObservableMap(); const second = new ObservableMap(); const join = new JoinedMap([first, second]); @@ -278,7 +279,7 @@ export function tests() { assert.equal(events[0].key, "a"); assert.equal(events[0].value, 1); }, - "updating occluded key doesn't emit update": assert => { + "updating occluded key doesn't emit update": (assert): void => { const first = new ObservableMap(); const second = new ObservableMap(); const join = new JoinedMap([first, second]); @@ -288,7 +289,7 @@ export function tests() { second.update("a", 3); assert.equal(events.length, 0); }, - "removal of occluding key emits add after remove": assert => { + "removal of occluding key emits add after remove": (assert): void => { const first = new ObservableMap(); const second = new ObservableMap(); const join = new JoinedMap([first, second]); @@ -304,7 +305,7 @@ export function tests() { assert.equal(events[1].key, "a"); assert.equal(events[1].value, 2); }, - "adding occluding key emits remove first": assert => { + "adding occluding key emits remove first": (assert): void => { const first = new ObservableMap(); const second = new ObservableMap(); const join = new JoinedMap([first, second]); diff --git a/src/observable/map/LogMap.ts b/src/observable/map/LogMap.ts index 6f3bf676..2a084401 100644 --- a/src/observable/map/LogMap.ts +++ b/src/observable/map/LogMap.ts @@ -42,47 +42,48 @@ export class LogMap extends BaseObservableMap { return this._log.log(labelOrValues, logLevel); } - onAdd(key: K, value: V) { + onAdd(key: K, value: V): void { this.log("add " + JSON.stringify({key, value})); this.emitAdd(key, value); } - onRemove(key: K, value: V) { + onRemove(key: K, value: V): void { this.log("remove " + JSON.stringify({key, value})); this.emitRemove(key, value); } - onUpdate(key: K, value: V, params: any) { + onUpdate(key: K, value: V, params: any): void { this.log("update" + JSON.stringify({key, value, params})); this.emitUpdate(key, value, params); } - onSubscribeFirst() { + onSubscribeFirst(): void { this.log("subscribeFirst"); this._subscription = this._source.subscribe(this); super.onSubscribeFirst(); } - onUnsubscribeLast() { + onUnsubscribeLast(): void { super.onUnsubscribeLast(); if (this._subscription) this._subscription = this._subscription(); this.log("unsubscribeLast"); } - onReset() { + onReset(): void { this.log("reset"); this.emitReset(); } + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type [Symbol.iterator]() { return this._source[Symbol.iterator](); } - get size() { + get size(): number { return this._source.size; } - get(key: K) { + get(key: K): V | undefined{ return this._source.get(key); } diff --git a/src/observable/map/MappedMap.ts b/src/observable/map/MappedMap.ts index 396802bb..e37e313b 100644 --- a/src/observable/map/MappedMap.ts +++ b/src/observable/map/MappedMap.ts @@ -46,28 +46,28 @@ export class MappedMap extends BaseObservableMap { this._config = config(); } - _emitSpontaneousUpdate(key: K, params: any) { + _emitSpontaneousUpdate(key: K, params: any): void { const value = this._mappedValues.get(key); if (value) { this.emitUpdate(key, value, params); } } - onAdd(key: K, value: V) { + onAdd(key: K, value: V): void { const emitSpontaneousUpdate = this._emitSpontaneousUpdate.bind(this, key); const mappedValue = this._mapper(value, emitSpontaneousUpdate); this._mappedValues.set(key, mappedValue); this.emitAdd(key, mappedValue); } - onRemove(key: K/*, _value*/) { + onRemove(key: K/*, _value*/): void { const mappedValue = this._mappedValues.get(key); if (this._mappedValues.delete(key)) { if (mappedValue) this.emitRemove(key, mappedValue); } } - onUpdate(key: K, value: V, params: any) { + onUpdate(key: K, value: V, params: any): void { // if an update is emitted while calling source.subscribe() from onSubscribeFirst, ignore it if (!this._mappedValues) { return; @@ -80,7 +80,7 @@ export class MappedMap extends BaseObservableMap { } } - onSubscribeFirst() { + onSubscribeFirst(): void { this._subscription = this._source.subscribe(this); for (let [key, value] of this._source) { const emitSpontaneousUpdate = this._emitSpontaneousUpdate.bind(this, key); @@ -90,22 +90,23 @@ export class MappedMap extends BaseObservableMap { super.onSubscribeFirst(); } - onUnsubscribeLast() { + onUnsubscribeLast(): void { super.onUnsubscribeLast(); if (this._subscription) this._subscription = this._subscription(); this._mappedValues.clear(); } - onReset() { + onReset(): void { this._mappedValues.clear(); this.emitReset(); } + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type [Symbol.iterator]() { return this._mappedValues.entries(); } - get size() { + get size(): number { return this._mappedValues.size; } diff --git a/src/observable/map/ObservableMap.ts b/src/observable/map/ObservableMap.ts index 6ce6ea91..c49d31dc 100644 --- a/src/observable/map/ObservableMap.ts +++ b/src/observable/map/ObservableMap.ts @@ -117,9 +117,10 @@ export class ObservableMap extends BaseObservableMap { } }; +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type export function tests() { return { - test_initial_values(assert) { + test_initial_values(assert): void { const map = new ObservableMap([ ["a", 5], ["b", 10] @@ -129,7 +130,7 @@ export function tests() { assert.equal(map.get("b"), 10); }, - test_add(assert) { + test_add(assert): void { let fired = 0; const map = new ObservableMap(); map.subscribe({ @@ -147,7 +148,7 @@ export function tests() { assert.equal(fired, 1); }, - test_update(assert) { + test_update(assert): void { let fired = 0; const map = new ObservableMap(); const value = {number: 5}; @@ -168,7 +169,7 @@ export function tests() { assert.equal(fired, 1); }, - test_update_unknown(assert) { + test_update_unknown(assert): void { let fired = 0; const map = new ObservableMap(); map.subscribe({ @@ -182,7 +183,7 @@ export function tests() { assert.equal(result, false); }, - test_set(assert) { + test_set(assert): void { let add_fired = 0, update_fired = 0; const map = new ObservableMap(); map.subscribe({ @@ -209,7 +210,7 @@ export function tests() { assert.equal(update_fired, 1); }, - test_remove(assert) { + test_remove(assert): void { let fired = 0; const map = new ObservableMap(); const value = {value: 5}; @@ -229,7 +230,7 @@ export function tests() { assert.equal(fired, 1); }, - test_iterate(assert) { + test_iterate(assert): void { const results: any[] = []; const map = new ObservableMap(); map.add(1, {number: 5}); @@ -243,7 +244,7 @@ export function tests() { assert.equal(results.find(([key]) => key === 2)[1].number, 6); assert.equal(results.find(([key]) => key === 3)[1].number, 7); }, - test_size(assert) { + test_size(assert): void { const map = new ObservableMap(); map.add(1, {number: 5}); map.add(2, {number: 6}); From bed66ada883317447700a3ad296a5ab988b41456 Mon Sep 17 00:00:00 2001 From: Isaiah Becker-Mayer Date: Sat, 9 Jul 2022 13:01:53 -0400 Subject: [PATCH 16/24] removes .js to files that are now typescripted --- src/observable/map/ApplyMap.ts | 6 +++--- src/observable/map/BaseObservableMap.ts | 6 +++--- src/observable/map/FilteredMap.ts | 4 ++-- src/observable/map/JoinedMap.ts | 4 ++-- src/observable/map/LogMap.ts | 6 +++--- src/observable/map/MappedMap.ts | 4 ++-- src/observable/map/ObservableMap.ts | 6 +++--- src/observable/map/config.ts | 6 +++--- 8 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/observable/map/ApplyMap.ts b/src/observable/map/ApplyMap.ts index 4d45b519..0fef91c9 100644 --- a/src/observable/map/ApplyMap.ts +++ b/src/observable/map/ApplyMap.ts @@ -17,9 +17,9 @@ limitations under the License. import {BaseObservableMap, BaseObservableMapConfig} from "./BaseObservableMap"; import {SubscriptionHandle} from "../BaseObservable"; import {config, Mapper, Updater, Comparator, Filter} from "./config"; -import {JoinedMap} from "./JoinedMap.js"; -import {MappedMap} from "./MappedMap.js"; -import {FilteredMap} from "./FilteredMap.js"; +import {JoinedMap} from "./JoinedMap"; +import {MappedMap} from "./MappedMap"; +import {FilteredMap} from "./FilteredMap"; import {SortedMapList} from "../list/SortedMapList.js"; diff --git a/src/observable/map/BaseObservableMap.ts b/src/observable/map/BaseObservableMap.ts index 206770ee..98781955 100644 --- a/src/observable/map/BaseObservableMap.ts +++ b/src/observable/map/BaseObservableMap.ts @@ -15,9 +15,9 @@ limitations under the License. */ import {BaseObservable} from "../BaseObservable"; -import {JoinedMap} from "../map/JoinedMap.js"; -import {MappedMap} from "../map/MappedMap.js"; -import {FilteredMap} from "../map/FilteredMap.js"; +import {JoinedMap} from "../map/JoinedMap"; +import {MappedMap} from "../map/MappedMap"; +import {FilteredMap} from "../map/FilteredMap"; import {SortedMapList} from "../list/SortedMapList.js"; import {Mapper, Updater, Comparator, Filter} from "./config"; diff --git a/src/observable/map/FilteredMap.ts b/src/observable/map/FilteredMap.ts index 613b1439..00006a6d 100644 --- a/src/observable/map/FilteredMap.ts +++ b/src/observable/map/FilteredMap.ts @@ -17,8 +17,8 @@ limitations under the License. import {BaseObservableMap, BaseObservableMapConfig} from "./BaseObservableMap"; import {SubscriptionHandle} from "../BaseObservable"; import {config, Mapper, Updater, Comparator, Filter} from "./config"; -import {JoinedMap} from "./JoinedMap.js"; -import {MappedMap} from "./MappedMap.js"; +import {JoinedMap} from "./JoinedMap"; +import {MappedMap} from "./MappedMap"; import {SortedMapList} from "../list/SortedMapList.js"; export class FilteredMap extends BaseObservableMap { diff --git a/src/observable/map/JoinedMap.ts b/src/observable/map/JoinedMap.ts index 1b145d85..bf9f8b97 100644 --- a/src/observable/map/JoinedMap.ts +++ b/src/observable/map/JoinedMap.ts @@ -16,8 +16,8 @@ limitations under the License. import {BaseObservableMap, BaseObservableMapConfig} from "./BaseObservableMap"; import {config, Mapper, Updater, Comparator, Filter} from "./config"; -import {FilteredMap} from "./FilteredMap.js"; -import {MappedMap} from "./MappedMap.js"; +import {FilteredMap} from "./FilteredMap"; +import {MappedMap} from "./MappedMap"; import {SortedMapList} from "../list/SortedMapList.js"; import {SubscriptionHandle} from "../BaseObservable"; diff --git a/src/observable/map/LogMap.ts b/src/observable/map/LogMap.ts index 2a084401..70fe623a 100644 --- a/src/observable/map/LogMap.ts +++ b/src/observable/map/LogMap.ts @@ -16,9 +16,9 @@ limitations under the License. import {BaseObservableMap, BaseObservableMapConfig} from "./BaseObservableMap"; import {config, Mapper, Updater, Comparator, Filter} from "./config"; -import {FilteredMap} from "./FilteredMap.js"; -import {MappedMap} from "./MappedMap.js"; -import {JoinedMap} from "./JoinedMap.js"; +import {FilteredMap} from "./FilteredMap"; +import {MappedMap} from "./MappedMap"; +import {JoinedMap} from "./JoinedMap"; import {SortedMapList} from "../list/SortedMapList.js"; import {SubscriptionHandle} from "../BaseObservable"; import {ILogItem, LabelOrValues} from "../../logging/types"; diff --git a/src/observable/map/MappedMap.ts b/src/observable/map/MappedMap.ts index e37e313b..95f49c2b 100644 --- a/src/observable/map/MappedMap.ts +++ b/src/observable/map/MappedMap.ts @@ -16,8 +16,8 @@ limitations under the License. import {BaseObservableMap, BaseObservableMapConfig} from "./BaseObservableMap"; import {config, Mapper, Updater, Comparator, Filter} from "./config"; -import {JoinedMap} from "./JoinedMap.js"; -import {FilteredMap} from "./FilteredMap.js"; +import {JoinedMap} from "./JoinedMap"; +import {FilteredMap} from "./FilteredMap"; import {SortedMapList} from "../list/SortedMapList.js"; import {SubscriptionHandle} from "../BaseObservable"; diff --git a/src/observable/map/ObservableMap.ts b/src/observable/map/ObservableMap.ts index c49d31dc..950fb818 100644 --- a/src/observable/map/ObservableMap.ts +++ b/src/observable/map/ObservableMap.ts @@ -16,9 +16,9 @@ limitations under the License. import {BaseObservableMap, BaseObservableMapConfig} from "./BaseObservableMap"; import {config, Mapper, Updater, Comparator, Filter} from "./config"; -import {JoinedMap} from "./JoinedMap.js"; -import {MappedMap} from "./MappedMap.js"; -import {FilteredMap} from "./FilteredMap.js"; +import {JoinedMap} from "./JoinedMap"; +import {MappedMap} from "./MappedMap"; +import {FilteredMap} from "./FilteredMap"; import {SortedMapList} from "../list/SortedMapList.js"; diff --git a/src/observable/map/config.ts b/src/observable/map/config.ts index 54e291e2..fab0bcf8 100644 --- a/src/observable/map/config.ts +++ b/src/observable/map/config.ts @@ -14,9 +14,9 @@ See the License for the specific language governing permissions and limitations under the License. */ import {BaseObservableMap, BaseObservableMapConfig} from "./BaseObservableMap"; -import {FilteredMap} from "./FilteredMap.js"; -import {MappedMap} from "./MappedMap.js"; -import {JoinedMap} from "./JoinedMap.js"; +import {FilteredMap} from "./FilteredMap"; +import {MappedMap} from "./MappedMap"; +import {JoinedMap} from "./JoinedMap"; import {SortedMapList} from "../list/SortedMapList.js"; From 73b83fdab8f055600ee043f1e57415b0326b895b Mon Sep 17 00:00:00 2001 From: Isaiah Becker-Mayer Date: Sun, 10 Jul 2022 00:21:30 -0400 Subject: [PATCH 17/24] Changes config from a function that returns an objectinto a more aptly named BaseObservableMapDefaults class --- src/observable/map/ApplyMap.ts | 16 +++--- src/observable/map/BaseObservableMap.ts | 18 ++---- .../map/BaseObservableMapDefaults.ts | 55 +++++++++++++++++++ src/observable/map/FilteredMap.ts | 15 +++-- src/observable/map/JoinedMap.ts | 15 +++-- src/observable/map/LogMap.ts | 16 +++--- src/observable/map/MappedMap.ts | 16 +++--- src/observable/map/ObservableMap.ts | 15 +++-- src/observable/map/config.ts | 54 ------------------ 9 files changed, 105 insertions(+), 115 deletions(-) create mode 100644 src/observable/map/BaseObservableMapDefaults.ts delete mode 100644 src/observable/map/config.ts diff --git a/src/observable/map/ApplyMap.ts b/src/observable/map/ApplyMap.ts index 0fef91c9..f8d09914 100644 --- a/src/observable/map/ApplyMap.ts +++ b/src/observable/map/ApplyMap.ts @@ -14,9 +14,9 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {BaseObservableMap, BaseObservableMapConfig} from "./BaseObservableMap"; +import {BaseObservableMap} from "./BaseObservableMap"; import {SubscriptionHandle} from "../BaseObservable"; -import {config, Mapper, Updater, Comparator, Filter} from "./config"; +import {BaseObservableMapDefaults, Mapper, Updater, Comparator, Filter} from "./BaseObservableMapDefaults"; import {JoinedMap} from "./JoinedMap"; import {MappedMap} from "./MappedMap"; import {FilteredMap} from "./FilteredMap"; @@ -24,16 +24,16 @@ import {SortedMapList} from "../list/SortedMapList.js"; export class ApplyMap extends BaseObservableMap { + private _defaults = new BaseObservableMapDefaults(); private _source: BaseObservableMap; private _subscription?: SubscriptionHandle; private _apply?: Apply; - private _config: BaseObservableMapConfig; + constructor(source: BaseObservableMap, apply?: Apply) { super(); this._source = source; this._apply = apply; - this._config = config(); } hasApply(): boolean { @@ -105,19 +105,19 @@ export class ApplyMap extends BaseObservableMap { } join(...otherMaps: Array): JoinedMap { - return this._config.join(this, ...otherMaps); + return this._defaults.join(this, ...otherMaps); } mapValues(mapper: Mapper, updater?: Updater): MappedMap { - return this._config.mapValues(this, mapper, updater); + return this._defaults.mapValues(this, mapper, updater); } sortValues(comparator: Comparator): SortedMapList { - return this._config.sortValues(this, comparator); + return this._defaults.sortValues(this, comparator); } filterValues(filter: Filter): FilteredMap { - return this._config.filterValues(this, filter); + return this._defaults.filterValues(this, filter); } } diff --git a/src/observable/map/BaseObservableMap.ts b/src/observable/map/BaseObservableMap.ts index 98781955..02dedce5 100644 --- a/src/observable/map/BaseObservableMap.ts +++ b/src/observable/map/BaseObservableMap.ts @@ -19,7 +19,7 @@ import {JoinedMap} from "../map/JoinedMap"; import {MappedMap} from "../map/MappedMap"; import {FilteredMap} from "../map/FilteredMap"; import {SortedMapList} from "../list/SortedMapList.js"; -import {Mapper, Updater, Comparator, Filter} from "./config"; +import {Mapper, Updater, Comparator, Filter} from "./BaseObservableMapDefaults"; export interface IMapObserver { onReset(): void; @@ -28,13 +28,6 @@ export interface IMapObserver { onRemove(key: K, value: V): void } -export type BaseObservableMapConfig = { - join(_this: BaseObservableMap, ...otherMaps: Array>): JoinedMap; - mapValues(_this: BaseObservableMap, mapper: any, updater?: Updater): MappedMap; - sortValues(_this: BaseObservableMap, comparator: Comparator): SortedMapList; - filterValues(_this: BaseObservableMap, filter: Filter): FilteredMap; -} - export abstract class BaseObservableMap extends BaseObservable> { emitReset(): void { for(let h of this._handlers) { @@ -62,10 +55,11 @@ export abstract class BaseObservableMap extends BaseObservable): JoinedMap; abstract mapValues(mapper: Mapper, updater?: Updater): MappedMap; diff --git a/src/observable/map/BaseObservableMapDefaults.ts b/src/observable/map/BaseObservableMapDefaults.ts new file mode 100644 index 00000000..46d9148e --- /dev/null +++ b/src/observable/map/BaseObservableMapDefaults.ts @@ -0,0 +1,55 @@ +/* +Copyright 2022 Isaiah Becker-Mayer + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +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 {BaseObservableMap} from "./BaseObservableMap"; +import {FilteredMap} from "./FilteredMap"; +import {MappedMap} from "./MappedMap"; +import {JoinedMap} from "./JoinedMap"; +import {SortedMapList} from "../list/SortedMapList.js"; + + +// This class is used as a default implementation of +// the respective abstract functions in BaseObservableMap. +// It is kept as its own class in its own file in order to avoid a circular +// dependency between the classes that extend BaseObservableMap which are +// instantiated here (i.e. `new JoinedMap()`). +export class BaseObservableMapDefaults { + join(_this: BaseObservableMap, ...otherMaps: Array>): JoinedMap { + return new JoinedMap([_this].concat(otherMaps)); + } + + mapValues(_this: BaseObservableMap, mapper: Mapper, updater?: Updater): MappedMap { + return new MappedMap(_this, mapper, updater); + } + + sortValues(_this: BaseObservableMap, comparator: Comparator): SortedMapList { + return new SortedMapList(_this, comparator); + } + + filterValues(_this: BaseObservableMap, filter: Filter): FilteredMap { + return new FilteredMap(_this, filter); + } +} + +export type Mapper = ( + value: V, + emitSpontaneousUpdate: any, +) => V; + +export type Updater = (params: any, mappedValue?: V, value?: V) => void; + +export type Comparator = (a: V, b: V) => number; + +export type Filter = (v: V, k: K) => boolean; \ No newline at end of file diff --git a/src/observable/map/FilteredMap.ts b/src/observable/map/FilteredMap.ts index 00006a6d..b0ed2e82 100644 --- a/src/observable/map/FilteredMap.ts +++ b/src/observable/map/FilteredMap.ts @@ -14,16 +14,16 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {BaseObservableMap, BaseObservableMapConfig} from "./BaseObservableMap"; +import {BaseObservableMap} from "./BaseObservableMap"; import {SubscriptionHandle} from "../BaseObservable"; -import {config, Mapper, Updater, Comparator, Filter} from "./config"; +import {BaseObservableMapDefaults, Mapper, Updater, Comparator, Filter} from "./BaseObservableMapDefaults"; import {JoinedMap} from "./JoinedMap"; import {MappedMap} from "./MappedMap"; import {SortedMapList} from "../list/SortedMapList.js"; export class FilteredMap extends BaseObservableMap { + private _defaults = new BaseObservableMapDefaults(); private _source: BaseObservableMap; - private _config: BaseObservableMapConfig; private _filter: Filter; private _included?: Map; private _subscription?: SubscriptionHandle; @@ -32,7 +32,6 @@ export class FilteredMap extends BaseObservableMap { super(); this._source = source; this._filter = filter; - this._config = config(); } setFilter(filter: Filter): void { @@ -165,19 +164,19 @@ export class FilteredMap extends BaseObservableMap { } join(...otherMaps: Array): JoinedMap { - return this._config.join(this, ...otherMaps); + return this._defaults.join(this, ...otherMaps); } mapValues(mapper: Mapper, updater?: Updater): MappedMap{ - return this._config.mapValues(this, mapper, updater); + return this._defaults.mapValues(this, mapper, updater); } sortValues(comparator: Comparator): SortedMapList { - return this._config.sortValues(this, comparator); + return this._defaults.sortValues(this, comparator); } filterValues(filter: Filter): FilteredMap { - return this._config.filterValues(this, filter); + return this._defaults.filterValues(this, filter); } } diff --git a/src/observable/map/JoinedMap.ts b/src/observable/map/JoinedMap.ts index bf9f8b97..d4164f79 100644 --- a/src/observable/map/JoinedMap.ts +++ b/src/observable/map/JoinedMap.ts @@ -14,8 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {BaseObservableMap, BaseObservableMapConfig} from "./BaseObservableMap"; -import {config, Mapper, Updater, Comparator, Filter} from "./config"; +import {BaseObservableMap} from "./BaseObservableMap"; +import {BaseObservableMapDefaults, Mapper, Updater, Comparator, Filter} from "./BaseObservableMapDefaults"; import {FilteredMap} from "./FilteredMap"; import {MappedMap} from "./MappedMap"; import {SortedMapList} from "../list/SortedMapList.js"; @@ -23,14 +23,13 @@ import {SubscriptionHandle} from "../BaseObservable"; export class JoinedMap extends BaseObservableMap { + private _defaults = new BaseObservableMapDefaults(); protected _sources: BaseObservableMap[]; - private _config: BaseObservableMapConfig; private _subscriptions?: SourceSubscriptionHandler[]; constructor(sources: BaseObservableMap[]) { super(); this._sources = sources; - this._config = config(); } onAdd(source: BaseObservableMap, key: K, value: V): void { @@ -136,19 +135,19 @@ export class JoinedMap extends BaseObservableMap { } join(...otherMaps: Array): JoinedMap { - return this._config.join(this, ...otherMaps); + return this._defaults.join(this, ...otherMaps); } mapValues(mapper: Mapper, updater?: Updater): MappedMap { - return this._config.mapValues(this, mapper, updater); + return this._defaults.mapValues(this, mapper, updater); } sortValues(comparator: Comparator): SortedMapList { - return this._config.sortValues(this, comparator); + return this._defaults.sortValues(this, comparator); } filterValues(filter: Filter): FilteredMap { - return this._config.filterValues(this, filter); + return this._defaults.filterValues(this, filter); } } diff --git a/src/observable/map/LogMap.ts b/src/observable/map/LogMap.ts index 70fe623a..7b857678 100644 --- a/src/observable/map/LogMap.ts +++ b/src/observable/map/LogMap.ts @@ -14,8 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {BaseObservableMap, BaseObservableMapConfig} from "./BaseObservableMap"; -import {config, Mapper, Updater, Comparator, Filter} from "./config"; +import {BaseObservableMap} from "./BaseObservableMap"; +import {BaseObservableMapDefaults, Mapper, Updater, Comparator, Filter} from "./BaseObservableMapDefaults"; import {FilteredMap} from "./FilteredMap"; import {MappedMap} from "./MappedMap"; import {JoinedMap} from "./JoinedMap"; @@ -25,17 +25,15 @@ import {ILogItem, LabelOrValues} from "../../logging/types"; import {LogLevel} from "../../logging/LogFilter"; export class LogMap extends BaseObservableMap { + private _defaults = new BaseObservableMapDefaults(); private _source: BaseObservableMap; private _subscription?: SubscriptionHandle; private _log: ILogItem; - private _config: BaseObservableMapConfig - constructor(source: BaseObservableMap, log: ILogItem) { super(); this._source = source; this._log = log; - this._config = config(); } private log(labelOrValues: LabelOrValues, logLevel?: LogLevel): ILogItem { @@ -88,19 +86,19 @@ export class LogMap extends BaseObservableMap { } join(...otherMaps: Array): JoinedMap { - return this._config.join(this, ...otherMaps); + return this._defaults.join(this, ...otherMaps); } mapValues(mapper: Mapper, updater?: Updater): MappedMap { - return this._config.mapValues(this, mapper, updater); + return this._defaults.mapValues(this, mapper, updater); } sortValues(comparator: Comparator): SortedMapList { - return this._config.sortValues(this, comparator); + return this._defaults.sortValues(this, comparator); } filterValues(filter: Filter): FilteredMap { - return this._config.filterValues(this, filter); + return this._defaults.filterValues(this, filter); } } diff --git a/src/observable/map/MappedMap.ts b/src/observable/map/MappedMap.ts index 95f49c2b..3d9e3a3c 100644 --- a/src/observable/map/MappedMap.ts +++ b/src/observable/map/MappedMap.ts @@ -14,8 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {BaseObservableMap, BaseObservableMapConfig} from "./BaseObservableMap"; -import {config, Mapper, Updater, Comparator, Filter} from "./config"; +import {BaseObservableMap} from "./BaseObservableMap"; +import {BaseObservableMapDefaults, Mapper, Updater, Comparator, Filter} from "./BaseObservableMapDefaults"; import {JoinedMap} from "./JoinedMap"; import {FilteredMap} from "./FilteredMap"; import {SortedMapList} from "../list/SortedMapList.js"; @@ -26,12 +26,13 @@ so a mapped value can emit updates on it's own with this._emitSpontaneousUpdate how should the mapped value be notified of an update though? and can it then decide to not propagate the update? */ export class MappedMap extends BaseObservableMap { + private _defaults = new BaseObservableMapDefaults(); private _source: BaseObservableMap; private _mapper: Mapper; private _updater?: Updater; private _mappedValues: Map; private _subscription?: SubscriptionHandle; - private _config: BaseObservableMapConfig + constructor( source: BaseObservableMap, @@ -43,7 +44,6 @@ export class MappedMap extends BaseObservableMap { this._mapper = mapper; this._updater = updater; this._mappedValues = new Map(); - this._config = config(); } _emitSpontaneousUpdate(key: K, params: any): void { @@ -115,18 +115,18 @@ export class MappedMap extends BaseObservableMap { } join(...otherMaps: Array): JoinedMap { - return this._config.join(this, ...otherMaps); + return this._defaults.join(this, ...otherMaps); } mapValues(mapper: Mapper, updater?: Updater): MappedMap{ - return this._config.mapValues(this, mapper, updater); + return this._defaults.mapValues(this, mapper, updater); } sortValues(comparator: Comparator): SortedMapList { - return this._config.sortValues(this, comparator); + return this._defaults.sortValues(this, comparator); } filterValues(filter: Filter): FilteredMap { - return this._config.filterValues(this, filter); + return this._defaults.filterValues(this, filter); } } \ No newline at end of file diff --git a/src/observable/map/ObservableMap.ts b/src/observable/map/ObservableMap.ts index 950fb818..4b98c089 100644 --- a/src/observable/map/ObservableMap.ts +++ b/src/observable/map/ObservableMap.ts @@ -14,8 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {BaseObservableMap, BaseObservableMapConfig} from "./BaseObservableMap"; -import {config, Mapper, Updater, Comparator, Filter} from "./config"; +import {BaseObservableMap} from "./BaseObservableMap"; +import {BaseObservableMapDefaults, Mapper, Updater, Comparator, Filter} from "./BaseObservableMapDefaults"; import {JoinedMap} from "./JoinedMap"; import {MappedMap} from "./MappedMap"; import {FilteredMap} from "./FilteredMap"; @@ -23,12 +23,11 @@ import {SortedMapList} from "../list/SortedMapList.js"; export class ObservableMap extends BaseObservableMap { - private _config: BaseObservableMapConfig + private _defaults = new BaseObservableMapDefaults(); private readonly _values: Map; constructor(initialValues?: (readonly [K, V])[]) { super(); - this._config = config(); this._values = new Map(initialValues); } @@ -101,19 +100,19 @@ export class ObservableMap extends BaseObservableMap { } join(...otherMaps: Array): JoinedMap { - return this._config.join(this, ...otherMaps); + return this._defaults.join(this, ...otherMaps); } mapValues(mapper: Mapper, updater?: Updater): MappedMap { - return this._config.mapValues(this, mapper, updater); + return this._defaults.mapValues(this, mapper, updater); } sortValues(comparator: Comparator): SortedMapList { - return this._config.sortValues(this, comparator); + return this._defaults.sortValues(this, comparator); } filterValues(filter: Filter): FilteredMap { - return this._config.filterValues(this, filter); + return this._defaults.filterValues(this, filter); } }; diff --git a/src/observable/map/config.ts b/src/observable/map/config.ts deleted file mode 100644 index fab0bcf8..00000000 --- a/src/observable/map/config.ts +++ /dev/null @@ -1,54 +0,0 @@ -/* -Copyright 2022 Isaiah Becker-Mayer - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -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 {BaseObservableMap, BaseObservableMapConfig} from "./BaseObservableMap"; -import {FilteredMap} from "./FilteredMap"; -import {MappedMap} from "./MappedMap"; -import {JoinedMap} from "./JoinedMap"; -import {SortedMapList} from "../list/SortedMapList.js"; - - -// This function is used as a default implementation of -// the respective abstract functions in BaseObservableMap. -// We implement it this way in order to avoid a circular -// dependency between the classes that are instantiated here -// (i.e. `new JoinedMap()`) and BaseObservableMap (as they extend it). -export function config(): BaseObservableMapConfig { - return { - join: (_this: BaseObservableMap, ...otherMaps: Array>): JoinedMap => { - return new JoinedMap([_this].concat(otherMaps)); - }, - mapValues: (_this: BaseObservableMap, mapper: Mapper, updater?: Updater): MappedMap => { - return new MappedMap(_this, mapper, updater); - }, - sortValues: (_this: BaseObservableMap, comparator: Comparator): SortedMapList => { - return new SortedMapList(_this, comparator); - }, - filterValues: (_this: BaseObservableMap, filter: Filter): FilteredMap => { - return new FilteredMap(_this, filter); - } - }; -} - -export type Mapper = ( - value: V, - emitSpontaneousUpdate: any, -) => V; - -export type Updater = (params: any, mappedValue?: V, value?: V) => void; - -export type Comparator = (a: V, b: V) => number; - -export type Filter = (v: V, k: K) => boolean; \ No newline at end of file From 70b68c5b162a9cd302154f99a5e4fe7174cc5a4e Mon Sep 17 00:00:00 2001 From: Isaiah Becker-Mayer Date: Tue, 26 Jul 2022 22:14:14 -0700 Subject: [PATCH 18/24] found a more clever way to do this which eliminates boilerplate --- src/observable/map/ApplyMap.ts | 26 +------------ src/observable/map/BaseObservableMap.ts | 37 +++++++++++++------ .../map/BaseObservableMapDefaults.ts | 5 +-- src/observable/map/FilteredMap.ts | 25 ++----------- src/observable/map/JoinedMap.ts | 25 +------------ src/observable/map/LogMap.ts | 27 ++------------ src/observable/map/MappedMap.ts | 25 ++----------- src/observable/map/ObservableMap.ts | 25 +------------ 8 files changed, 42 insertions(+), 153 deletions(-) diff --git a/src/observable/map/ApplyMap.ts b/src/observable/map/ApplyMap.ts index f8d09914..1786873a 100644 --- a/src/observable/map/ApplyMap.ts +++ b/src/observable/map/ApplyMap.ts @@ -16,22 +16,17 @@ limitations under the License. import {BaseObservableMap} from "./BaseObservableMap"; import {SubscriptionHandle} from "../BaseObservable"; -import {BaseObservableMapDefaults, Mapper, Updater, Comparator, Filter} from "./BaseObservableMapDefaults"; -import {JoinedMap} from "./JoinedMap"; -import {MappedMap} from "./MappedMap"; -import {FilteredMap} from "./FilteredMap"; -import {SortedMapList} from "../list/SortedMapList.js"; +import {BaseObservableMapDefaults} from "./BaseObservableMapDefaults"; export class ApplyMap extends BaseObservableMap { - private _defaults = new BaseObservableMapDefaults(); private _source: BaseObservableMap; private _subscription?: SubscriptionHandle; private _apply?: Apply; constructor(source: BaseObservableMap, apply?: Apply) { - super(); + super(new BaseObservableMapDefaults()); this._source = source; this._apply = apply; } @@ -103,23 +98,6 @@ export class ApplyMap extends BaseObservableMap { get(key: K): V | undefined { return this._source.get(key); } - - join(...otherMaps: Array): JoinedMap { - return this._defaults.join(this, ...otherMaps); - } - - mapValues(mapper: Mapper, updater?: Updater): MappedMap { - return this._defaults.mapValues(this, mapper, updater); - } - - sortValues(comparator: Comparator): SortedMapList { - return this._defaults.sortValues(this, comparator); - } - - filterValues(filter: Filter): FilteredMap { - return this._defaults.filterValues(this, filter); - } - } type Apply = (key: K, value: V, params?: any) => void; \ No newline at end of file diff --git a/src/observable/map/BaseObservableMap.ts b/src/observable/map/BaseObservableMap.ts index 02dedce5..86300439 100644 --- a/src/observable/map/BaseObservableMap.ts +++ b/src/observable/map/BaseObservableMap.ts @@ -19,7 +19,8 @@ import {JoinedMap} from "../map/JoinedMap"; import {MappedMap} from "../map/MappedMap"; import {FilteredMap} from "../map/FilteredMap"; import {SortedMapList} from "../list/SortedMapList.js"; -import {Mapper, Updater, Comparator, Filter} from "./BaseObservableMapDefaults"; +import type {BaseObservableMapDefaults, Mapper, Updater, Comparator, Filter} from "./BaseObservableMapDefaults"; + export interface IMapObserver { onReset(): void; @@ -29,6 +30,13 @@ export interface IMapObserver { } export abstract class BaseObservableMap extends BaseObservable> { + private _defaults: BaseObservableMapDefaults; + + constructor(defaults: BaseObservableMapDefaults) { + super(); + this._defaults = defaults; + } + emitReset(): void { for(let h of this._handlers) { h.onReset(); @@ -54,17 +62,22 @@ export abstract class BaseObservableMap extends BaseObservable): JoinedMap; - abstract mapValues(mapper: Mapper, updater?: Updater): MappedMap; - abstract sortValues(comparator: Comparator): SortedMapList; - abstract filterValues(filter: Filter): FilteredMap; + join(...otherMaps: Array): JoinedMap { + return this._defaults.join(this, ...otherMaps); + } + + mapValues(mapper: Mapper, updater?: Updater): MappedMap { + return this._defaults.mapValues(this, mapper, updater); + } + + sortValues(comparator: Comparator): SortedMapList { + return this._defaults.sortValues(this, comparator); + } + + filterValues(filter: Filter): FilteredMap { + return this._defaults.filterValues(this, filter); + } + abstract [Symbol.iterator](): Iterator<[K, V]>; abstract get size(): number; diff --git a/src/observable/map/BaseObservableMapDefaults.ts b/src/observable/map/BaseObservableMapDefaults.ts index 46d9148e..444b8640 100644 --- a/src/observable/map/BaseObservableMapDefaults.ts +++ b/src/observable/map/BaseObservableMapDefaults.ts @@ -13,15 +13,14 @@ 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 {BaseObservableMap} from "./BaseObservableMap"; +import type {BaseObservableMap} from "./BaseObservableMap"; import {FilteredMap} from "./FilteredMap"; import {MappedMap} from "./MappedMap"; import {JoinedMap} from "./JoinedMap"; import {SortedMapList} from "../list/SortedMapList.js"; -// This class is used as a default implementation of -// the respective abstract functions in BaseObservableMap. +// This class provides implementations of functions that are part of BaseObservableMap. // It is kept as its own class in its own file in order to avoid a circular // dependency between the classes that extend BaseObservableMap which are // instantiated here (i.e. `new JoinedMap()`). diff --git a/src/observable/map/FilteredMap.ts b/src/observable/map/FilteredMap.ts index b0ed2e82..267d0d35 100644 --- a/src/observable/map/FilteredMap.ts +++ b/src/observable/map/FilteredMap.ts @@ -16,20 +16,17 @@ limitations under the License. import {BaseObservableMap} from "./BaseObservableMap"; import {SubscriptionHandle} from "../BaseObservable"; -import {BaseObservableMapDefaults, Mapper, Updater, Comparator, Filter} from "./BaseObservableMapDefaults"; -import {JoinedMap} from "./JoinedMap"; -import {MappedMap} from "./MappedMap"; -import {SortedMapList} from "../list/SortedMapList.js"; +import {BaseObservableMapDefaults, Filter} from "./BaseObservableMapDefaults"; + export class FilteredMap extends BaseObservableMap { - private _defaults = new BaseObservableMapDefaults(); private _source: BaseObservableMap; private _filter: Filter; private _included?: Map; private _subscription?: SubscriptionHandle; constructor(source: BaseObservableMap, filter: Filter) { - super(); + super(new BaseObservableMapDefaults()); this._source = source; this._filter = filter; } @@ -162,22 +159,6 @@ export class FilteredMap extends BaseObservableMap { return value; } } - - join(...otherMaps: Array): JoinedMap { - return this._defaults.join(this, ...otherMaps); - } - - mapValues(mapper: Mapper, updater?: Updater): MappedMap{ - return this._defaults.mapValues(this, mapper, updater); - } - - sortValues(comparator: Comparator): SortedMapList { - return this._defaults.sortValues(this, comparator); - } - - filterValues(filter: Filter): FilteredMap { - return this._defaults.filterValues(this, filter); - } } class FilterIterator { diff --git a/src/observable/map/JoinedMap.ts b/src/observable/map/JoinedMap.ts index d4164f79..b343d22f 100644 --- a/src/observable/map/JoinedMap.ts +++ b/src/observable/map/JoinedMap.ts @@ -15,20 +15,16 @@ limitations under the License. */ import {BaseObservableMap} from "./BaseObservableMap"; -import {BaseObservableMapDefaults, Mapper, Updater, Comparator, Filter} from "./BaseObservableMapDefaults"; -import {FilteredMap} from "./FilteredMap"; -import {MappedMap} from "./MappedMap"; -import {SortedMapList} from "../list/SortedMapList.js"; +import {BaseObservableMapDefaults} from "./BaseObservableMapDefaults"; import {SubscriptionHandle} from "../BaseObservable"; export class JoinedMap extends BaseObservableMap { - private _defaults = new BaseObservableMapDefaults(); protected _sources: BaseObservableMap[]; private _subscriptions?: SourceSubscriptionHandler[]; constructor(sources: BaseObservableMap[]) { - super(); + super(new BaseObservableMapDefaults()); this._sources = sources; } @@ -133,23 +129,6 @@ export class JoinedMap extends BaseObservableMap { } return undefined; } - - join(...otherMaps: Array): JoinedMap { - return this._defaults.join(this, ...otherMaps); - } - - mapValues(mapper: Mapper, updater?: Updater): MappedMap { - return this._defaults.mapValues(this, mapper, updater); - } - - sortValues(comparator: Comparator): SortedMapList { - return this._defaults.sortValues(this, comparator); - } - - filterValues(filter: Filter): FilteredMap { - return this._defaults.filterValues(this, filter); - } - } class JoinedIterator implements Iterator<[K, V]> { diff --git a/src/observable/map/LogMap.ts b/src/observable/map/LogMap.ts index 7b857678..6db70f01 100644 --- a/src/observable/map/LogMap.ts +++ b/src/observable/map/LogMap.ts @@ -15,23 +15,19 @@ limitations under the License. */ import {BaseObservableMap} from "./BaseObservableMap"; -import {BaseObservableMapDefaults, Mapper, Updater, Comparator, Filter} from "./BaseObservableMapDefaults"; -import {FilteredMap} from "./FilteredMap"; -import {MappedMap} from "./MappedMap"; -import {JoinedMap} from "./JoinedMap"; -import {SortedMapList} from "../list/SortedMapList.js"; +import {BaseObservableMapDefaults} from "./BaseObservableMapDefaults"; import {SubscriptionHandle} from "../BaseObservable"; import {ILogItem, LabelOrValues} from "../../logging/types"; import {LogLevel} from "../../logging/LogFilter"; + export class LogMap extends BaseObservableMap { - private _defaults = new BaseObservableMapDefaults(); private _source: BaseObservableMap; private _subscription?: SubscriptionHandle; private _log: ILogItem; constructor(source: BaseObservableMap, log: ILogItem) { - super(); + super(new BaseObservableMapDefaults()); this._source = source; this._log = log; } @@ -84,21 +80,4 @@ export class LogMap extends BaseObservableMap { get(key: K): V | undefined{ return this._source.get(key); } - - join(...otherMaps: Array): JoinedMap { - return this._defaults.join(this, ...otherMaps); - } - - mapValues(mapper: Mapper, updater?: Updater): MappedMap { - return this._defaults.mapValues(this, mapper, updater); - } - - sortValues(comparator: Comparator): SortedMapList { - return this._defaults.sortValues(this, comparator); - } - - filterValues(filter: Filter): FilteredMap { - return this._defaults.filterValues(this, filter); - } - } diff --git a/src/observable/map/MappedMap.ts b/src/observable/map/MappedMap.ts index 3d9e3a3c..72115d84 100644 --- a/src/observable/map/MappedMap.ts +++ b/src/observable/map/MappedMap.ts @@ -15,18 +15,15 @@ limitations under the License. */ import {BaseObservableMap} from "./BaseObservableMap"; -import {BaseObservableMapDefaults, Mapper, Updater, Comparator, Filter} from "./BaseObservableMapDefaults"; -import {JoinedMap} from "./JoinedMap"; -import {FilteredMap} from "./FilteredMap"; -import {SortedMapList} from "../list/SortedMapList.js"; +import {BaseObservableMapDefaults, Mapper, Updater} from "./BaseObservableMapDefaults"; import {SubscriptionHandle} from "../BaseObservable"; + /* so a mapped value can emit updates on it's own with this._emitSpontaneousUpdate that is passed in the mapping function how should the mapped value be notified of an update though? and can it then decide to not propagate the update? */ export class MappedMap extends BaseObservableMap { - private _defaults = new BaseObservableMapDefaults(); private _source: BaseObservableMap; private _mapper: Mapper; private _updater?: Updater; @@ -39,7 +36,7 @@ export class MappedMap extends BaseObservableMap { mapper: Mapper, updater?: Updater ) { - super(); + super(new BaseObservableMapDefaults()); this._source = source; this._mapper = mapper; this._updater = updater; @@ -113,20 +110,4 @@ export class MappedMap extends BaseObservableMap { get(key: K): V | undefined { return this._mappedValues.get(key); } - - join(...otherMaps: Array): JoinedMap { - return this._defaults.join(this, ...otherMaps); - } - - mapValues(mapper: Mapper, updater?: Updater): MappedMap{ - return this._defaults.mapValues(this, mapper, updater); - } - - sortValues(comparator: Comparator): SortedMapList { - return this._defaults.sortValues(this, comparator); - } - - filterValues(filter: Filter): FilteredMap { - return this._defaults.filterValues(this, filter); - } } \ No newline at end of file diff --git a/src/observable/map/ObservableMap.ts b/src/observable/map/ObservableMap.ts index 4b98c089..9527ffdd 100644 --- a/src/observable/map/ObservableMap.ts +++ b/src/observable/map/ObservableMap.ts @@ -15,19 +15,14 @@ limitations under the License. */ import {BaseObservableMap} from "./BaseObservableMap"; -import {BaseObservableMapDefaults, Mapper, Updater, Comparator, Filter} from "./BaseObservableMapDefaults"; -import {JoinedMap} from "./JoinedMap"; -import {MappedMap} from "./MappedMap"; -import {FilteredMap} from "./FilteredMap"; -import {SortedMapList} from "../list/SortedMapList.js"; +import {BaseObservableMapDefaults} from "./BaseObservableMapDefaults"; export class ObservableMap extends BaseObservableMap { - private _defaults = new BaseObservableMapDefaults(); private readonly _values: Map; constructor(initialValues?: (readonly [K, V])[]) { - super(); + super(new BaseObservableMapDefaults()); this._values = new Map(initialValues); } @@ -98,22 +93,6 @@ export class ObservableMap extends BaseObservableMap { keys(): Iterator { return this._values.keys(); } - - join(...otherMaps: Array): JoinedMap { - return this._defaults.join(this, ...otherMaps); - } - - mapValues(mapper: Mapper, updater?: Updater): MappedMap { - return this._defaults.mapValues(this, mapper, updater); - } - - sortValues(comparator: Comparator): SortedMapList { - return this._defaults.sortValues(this, comparator); - } - - filterValues(filter: Filter): FilteredMap { - return this._defaults.filterValues(this, filter); - } }; // eslint-disable-next-line @typescript-eslint/explicit-function-return-type From dd01e70b4a3a90ab4b7d9d1b8adb08d2635e69c0 Mon Sep 17 00:00:00 2001 From: Isaiah Becker-Mayer Date: Sun, 31 Jul 2022 19:53:34 -0700 Subject: [PATCH 19/24] src/observable/map/BaseObservableMapDefaults.ts -> src/observable/map/BaseObservableMapTransformers.ts --- src/observable/map/ApplyMap.ts | 4 +-- src/observable/map/BaseObservableMap.ts | 6 ++--- ...ts.ts => BaseObservableMapTransformers.ts} | 25 +++++++++++++++---- src/observable/map/FilteredMap.ts | 4 +-- src/observable/map/JoinedMap.ts | 4 +-- src/observable/map/LogMap.ts | 4 +-- src/observable/map/MappedMap.ts | 4 +-- src/observable/map/ObservableMap.ts | 4 +-- 8 files changed, 35 insertions(+), 20 deletions(-) rename src/observable/map/{BaseObservableMapDefaults.ts => BaseObservableMapTransformers.ts} (63%) diff --git a/src/observable/map/ApplyMap.ts b/src/observable/map/ApplyMap.ts index 1786873a..ee40c797 100644 --- a/src/observable/map/ApplyMap.ts +++ b/src/observable/map/ApplyMap.ts @@ -16,7 +16,7 @@ limitations under the License. import {BaseObservableMap} from "./BaseObservableMap"; import {SubscriptionHandle} from "../BaseObservable"; -import {BaseObservableMapDefaults} from "./BaseObservableMapDefaults"; +import {BaseObservableMapTransformers} from "./BaseObservableMapTransformers"; export class ApplyMap extends BaseObservableMap { @@ -26,7 +26,7 @@ export class ApplyMap extends BaseObservableMap { constructor(source: BaseObservableMap, apply?: Apply) { - super(new BaseObservableMapDefaults()); + super(new BaseObservableMapTransformers()); this._source = source; this._apply = apply; } diff --git a/src/observable/map/BaseObservableMap.ts b/src/observable/map/BaseObservableMap.ts index 86300439..aa76f641 100644 --- a/src/observable/map/BaseObservableMap.ts +++ b/src/observable/map/BaseObservableMap.ts @@ -19,7 +19,7 @@ import {JoinedMap} from "../map/JoinedMap"; import {MappedMap} from "../map/MappedMap"; import {FilteredMap} from "../map/FilteredMap"; import {SortedMapList} from "../list/SortedMapList.js"; -import type {BaseObservableMapDefaults, Mapper, Updater, Comparator, Filter} from "./BaseObservableMapDefaults"; +import type {BaseObservableMapTransformers, Mapper, Updater, Comparator, Filter} from "./BaseObservableMapTransformers"; export interface IMapObserver { @@ -30,9 +30,9 @@ export interface IMapObserver { } export abstract class BaseObservableMap extends BaseObservable> { - private _defaults: BaseObservableMapDefaults; + private _defaults: BaseObservableMapTransformers; - constructor(defaults: BaseObservableMapDefaults) { + constructor(defaults: BaseObservableMapTransformers) { super(); this._defaults = defaults; } diff --git a/src/observable/map/BaseObservableMapDefaults.ts b/src/observable/map/BaseObservableMapTransformers.ts similarity index 63% rename from src/observable/map/BaseObservableMapDefaults.ts rename to src/observable/map/BaseObservableMapTransformers.ts index 444b8640..3b39587d 100644 --- a/src/observable/map/BaseObservableMapDefaults.ts +++ b/src/observable/map/BaseObservableMapTransformers.ts @@ -20,11 +20,26 @@ import {JoinedMap} from "./JoinedMap"; import {SortedMapList} from "../list/SortedMapList.js"; -// This class provides implementations of functions that are part of BaseObservableMap. -// It is kept as its own class in its own file in order to avoid a circular -// dependency between the classes that extend BaseObservableMap which are -// instantiated here (i.e. `new JoinedMap()`). -export class BaseObservableMapDefaults { +// This class provides implementations of functions that transform one BaseObservableMap +// to another type of Map. It's methods are effectively default implementations of the +// methods by the same name on BaseObservableMap. +// +// It is kept as its own class in its own file in order to avoid circular dependencies +// which would occur if these method implementations were defined on BaseObservableMap +// itself. For example, if we attmpted to do the following on BaseObservableMap: +// +// class BaseObservableMap extends BaseObservable> { +// join(...otherMaps: Array>): JoinedMap { +// return new JoinedMap(this.concat(otherMaps)); +// } +// } +// +// we would end up with a circular dependency between BaseObservableMap and JoinedMap, +// since BaseObservableMap would need to import JoinedMap for the +// `return new JoinedMap(this.concat(otherMaps))`, and +// JoinedMap would need to import BaseObservableMap to do +// `JoinedMap extends BaseObservableMap`. +export class BaseObservableMapTransformers { join(_this: BaseObservableMap, ...otherMaps: Array>): JoinedMap { return new JoinedMap([_this].concat(otherMaps)); } diff --git a/src/observable/map/FilteredMap.ts b/src/observable/map/FilteredMap.ts index 267d0d35..2f3ba9fd 100644 --- a/src/observable/map/FilteredMap.ts +++ b/src/observable/map/FilteredMap.ts @@ -16,7 +16,7 @@ limitations under the License. import {BaseObservableMap} from "./BaseObservableMap"; import {SubscriptionHandle} from "../BaseObservable"; -import {BaseObservableMapDefaults, Filter} from "./BaseObservableMapDefaults"; +import {BaseObservableMapTransformers, Filter} from "./BaseObservableMapTransformers"; export class FilteredMap extends BaseObservableMap { @@ -26,7 +26,7 @@ export class FilteredMap extends BaseObservableMap { private _subscription?: SubscriptionHandle; constructor(source: BaseObservableMap, filter: Filter) { - super(new BaseObservableMapDefaults()); + super(new BaseObservableMapTransformers()); this._source = source; this._filter = filter; } diff --git a/src/observable/map/JoinedMap.ts b/src/observable/map/JoinedMap.ts index b343d22f..f6cb074a 100644 --- a/src/observable/map/JoinedMap.ts +++ b/src/observable/map/JoinedMap.ts @@ -15,7 +15,7 @@ limitations under the License. */ import {BaseObservableMap} from "./BaseObservableMap"; -import {BaseObservableMapDefaults} from "./BaseObservableMapDefaults"; +import {BaseObservableMapTransformers} from "./BaseObservableMapTransformers"; import {SubscriptionHandle} from "../BaseObservable"; @@ -24,7 +24,7 @@ export class JoinedMap extends BaseObservableMap { private _subscriptions?: SourceSubscriptionHandler[]; constructor(sources: BaseObservableMap[]) { - super(new BaseObservableMapDefaults()); + super(new BaseObservableMapTransformers()); this._sources = sources; } diff --git a/src/observable/map/LogMap.ts b/src/observable/map/LogMap.ts index 6db70f01..15f204d2 100644 --- a/src/observable/map/LogMap.ts +++ b/src/observable/map/LogMap.ts @@ -15,7 +15,7 @@ limitations under the License. */ import {BaseObservableMap} from "./BaseObservableMap"; -import {BaseObservableMapDefaults} from "./BaseObservableMapDefaults"; +import {BaseObservableMapTransformers} from "./BaseObservableMapTransformers"; import {SubscriptionHandle} from "../BaseObservable"; import {ILogItem, LabelOrValues} from "../../logging/types"; import {LogLevel} from "../../logging/LogFilter"; @@ -27,7 +27,7 @@ export class LogMap extends BaseObservableMap { private _log: ILogItem; constructor(source: BaseObservableMap, log: ILogItem) { - super(new BaseObservableMapDefaults()); + super(new BaseObservableMapTransformers()); this._source = source; this._log = log; } diff --git a/src/observable/map/MappedMap.ts b/src/observable/map/MappedMap.ts index 72115d84..bd091856 100644 --- a/src/observable/map/MappedMap.ts +++ b/src/observable/map/MappedMap.ts @@ -15,7 +15,7 @@ limitations under the License. */ import {BaseObservableMap} from "./BaseObservableMap"; -import {BaseObservableMapDefaults, Mapper, Updater} from "./BaseObservableMapDefaults"; +import {BaseObservableMapTransformers, Mapper, Updater} from "./BaseObservableMapTransformers"; import {SubscriptionHandle} from "../BaseObservable"; @@ -36,7 +36,7 @@ export class MappedMap extends BaseObservableMap { mapper: Mapper, updater?: Updater ) { - super(new BaseObservableMapDefaults()); + super(new BaseObservableMapTransformers()); this._source = source; this._mapper = mapper; this._updater = updater; diff --git a/src/observable/map/ObservableMap.ts b/src/observable/map/ObservableMap.ts index 9527ffdd..09a73d71 100644 --- a/src/observable/map/ObservableMap.ts +++ b/src/observable/map/ObservableMap.ts @@ -15,14 +15,14 @@ limitations under the License. */ import {BaseObservableMap} from "./BaseObservableMap"; -import {BaseObservableMapDefaults} from "./BaseObservableMapDefaults"; +import {BaseObservableMapTransformers} from "./BaseObservableMapTransformers"; export class ObservableMap extends BaseObservableMap { private readonly _values: Map; constructor(initialValues?: (readonly [K, V])[]) { - super(new BaseObservableMapDefaults()); + super(new BaseObservableMapTransformers()); this._values = new Map(initialValues); } From 92ed503700e9c8350849e62067f295eead66865d Mon Sep 17 00:00:00 2001 From: Isaiah Becker-Mayer Date: Fri, 19 Aug 2022 19:02:06 -0400 Subject: [PATCH 20/24] Fixes MappedMap type system --- src/observable/map/BaseObservableMap.ts | 2 +- .../map/BaseObservableMapTransformers.ts | 8 ++++---- src/observable/map/MappedMap.ts | 18 +++++++++--------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/observable/map/BaseObservableMap.ts b/src/observable/map/BaseObservableMap.ts index aa76f641..807a11e7 100644 --- a/src/observable/map/BaseObservableMap.ts +++ b/src/observable/map/BaseObservableMap.ts @@ -66,7 +66,7 @@ export abstract class BaseObservableMap extends BaseObservable, updater?: Updater): MappedMap { + mapValues(mapper: Mapper, updater?: Updater): MappedMap { return this._defaults.mapValues(this, mapper, updater); } diff --git a/src/observable/map/BaseObservableMapTransformers.ts b/src/observable/map/BaseObservableMapTransformers.ts index 3b39587d..b81ffe33 100644 --- a/src/observable/map/BaseObservableMapTransformers.ts +++ b/src/observable/map/BaseObservableMapTransformers.ts @@ -44,7 +44,7 @@ export class BaseObservableMapTransformers { return new JoinedMap([_this].concat(otherMaps)); } - mapValues(_this: BaseObservableMap, mapper: Mapper, updater?: Updater): MappedMap { + mapValues(_this: BaseObservableMap, mapper: Mapper, updater?: Updater): MappedMap { return new MappedMap(_this, mapper, updater); } @@ -57,12 +57,12 @@ export class BaseObservableMapTransformers { } } -export type Mapper = ( +export type Mapper = ( value: V, emitSpontaneousUpdate: any, -) => V; +) => MappedV; -export type Updater = (params: any, mappedValue?: V, value?: V) => void; +export type Updater = (params: any, mappedValue?: MappedV, value?: V) => void; export type Comparator = (a: V, b: V) => number; diff --git a/src/observable/map/MappedMap.ts b/src/observable/map/MappedMap.ts index bd091856..e932e1b8 100644 --- a/src/observable/map/MappedMap.ts +++ b/src/observable/map/MappedMap.ts @@ -23,24 +23,24 @@ import {SubscriptionHandle} from "../BaseObservable"; so a mapped value can emit updates on it's own with this._emitSpontaneousUpdate that is passed in the mapping function how should the mapped value be notified of an update though? and can it then decide to not propagate the update? */ -export class MappedMap extends BaseObservableMap { +export class MappedMap extends BaseObservableMap { private _source: BaseObservableMap; - private _mapper: Mapper; - private _updater?: Updater; - private _mappedValues: Map; + private _mapper: Mapper; + private _updater?: Updater; + private _mappedValues: Map; private _subscription?: SubscriptionHandle; constructor( source: BaseObservableMap, - mapper: Mapper, - updater?: Updater + mapper: Mapper, + updater?: Updater ) { - super(new BaseObservableMapTransformers()); + super(new BaseObservableMapTransformers()); this._source = source; this._mapper = mapper; this._updater = updater; - this._mappedValues = new Map(); + this._mappedValues = new Map(); } _emitSpontaneousUpdate(key: K, params: any): void { @@ -107,7 +107,7 @@ export class MappedMap extends BaseObservableMap { return this._mappedValues.size; } - get(key: K): V | undefined { + get(key: K): MappedV | undefined { return this._mappedValues.get(key); } } \ No newline at end of file From 77f21f7a911a6d4e7e0878516ef922c39c28d252 Mon Sep 17 00:00:00 2001 From: Isaiah Becker-Mayer Date: Sat, 20 Aug 2022 16:39:39 -0400 Subject: [PATCH 21/24] fixes import order according to https://medium.com/visual-development/how-to-fix-nasty-circular-dependency-issues-once-and-for-all-in-javascript-typescript-a04c987cf0de --- .../session/leftpanel/LeftPanelViewModel.js | 2 +- src/lib.ts | 3 +- src/observable/index.ts | 2 +- src/observable/map/ApplyMap.ts | 10 ++- src/observable/map/BaseObservableMap.ts | 50 +++++++++----- .../map/BaseObservableMapTransformers.ts | 69 ------------------- src/observable/map/FilteredMap.ts | 10 ++- src/observable/map/JoinedMap.ts | 10 ++- src/observable/map/LogMap.ts | 10 ++- src/observable/map/MappedMap.ts | 10 ++- src/observable/map/ObservableMap.ts | 10 ++- src/observable/map/index.ts | 16 +++++ 12 files changed, 92 insertions(+), 110 deletions(-) delete mode 100644 src/observable/map/BaseObservableMapTransformers.ts create mode 100644 src/observable/map/index.ts diff --git a/src/domain/session/leftpanel/LeftPanelViewModel.js b/src/domain/session/leftpanel/LeftPanelViewModel.js index 8e814151..2c657201 100644 --- a/src/domain/session/leftpanel/LeftPanelViewModel.js +++ b/src/domain/session/leftpanel/LeftPanelViewModel.js @@ -20,7 +20,7 @@ import {RoomTileViewModel} from "./RoomTileViewModel.js"; import {InviteTileViewModel} from "./InviteTileViewModel.js"; import {RoomBeingCreatedTileViewModel} from "./RoomBeingCreatedTileViewModel.js"; import {RoomFilter} from "./RoomFilter.js"; -import {ApplyMap} from "../../../observable/map/ApplyMap"; +import {ApplyMap} from "../../../observable"; import {addPanelIfNeeded} from "../../navigation"; export class LeftPanelViewModel extends ViewModel { diff --git a/src/lib.ts b/src/lib.ts index 4d1f906f..df96bfcd 100644 --- a/src/lib.ts +++ b/src/lib.ts @@ -78,8 +78,7 @@ export { MappedList, AsyncMappedList, ConcatList, - ObservableMap -} from "./observable/index"; +} from "./observable"; export { BaseObservableValue, ObservableValue, diff --git a/src/observable/index.ts b/src/observable/index.ts index dfd272fd..25af50b8 100644 --- a/src/observable/index.ts +++ b/src/observable/index.ts @@ -16,7 +16,7 @@ limitations under the License. // re-export "root" (of chain) collection -export { ObservableMap } from "./map/ObservableMap"; +export { ObservableMap, ApplyMap, FilteredMap, JoinedMap, LogMap, MappedMap } from "./map"; export { ObservableArray } from "./list/ObservableArray"; export { SortedArray } from "./list/SortedArray"; export { MappedList } from "./list/MappedList"; diff --git a/src/observable/map/ApplyMap.ts b/src/observable/map/ApplyMap.ts index ee40c797..44acd1fe 100644 --- a/src/observable/map/ApplyMap.ts +++ b/src/observable/map/ApplyMap.ts @@ -14,11 +14,15 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {BaseObservableMap} from "./BaseObservableMap"; +import {BaseObservableMap} from "./index"; import {SubscriptionHandle} from "../BaseObservable"; -import {BaseObservableMapTransformers} from "./BaseObservableMapTransformers"; +/* +This class MUST never be imported directly from here. +Instead, it MUST be imported from index.ts. See the +top level comment in index.ts for details. +*/ export class ApplyMap extends BaseObservableMap { private _source: BaseObservableMap; private _subscription?: SubscriptionHandle; @@ -26,7 +30,7 @@ export class ApplyMap extends BaseObservableMap { constructor(source: BaseObservableMap, apply?: Apply) { - super(new BaseObservableMapTransformers()); + super(); this._source = source; this._apply = apply; } diff --git a/src/observable/map/BaseObservableMap.ts b/src/observable/map/BaseObservableMap.ts index 807a11e7..9b501285 100644 --- a/src/observable/map/BaseObservableMap.ts +++ b/src/observable/map/BaseObservableMap.ts @@ -15,11 +15,10 @@ limitations under the License. */ import {BaseObservable} from "../BaseObservable"; -import {JoinedMap} from "../map/JoinedMap"; -import {MappedMap} from "../map/MappedMap"; -import {FilteredMap} from "../map/FilteredMap"; +import {JoinedMap} from "./index"; +import {MappedMap} from "./index"; +import {FilteredMap} from "./index"; import {SortedMapList} from "../list/SortedMapList.js"; -import type {BaseObservableMapTransformers, Mapper, Updater, Comparator, Filter} from "./BaseObservableMapTransformers"; export interface IMapObserver { @@ -29,12 +28,15 @@ export interface IMapObserver { onRemove(key: K, value: V): void } +/* +This class MUST never be imported directly from here. +Instead, it MUST be imported from index.ts. See the +top level comment in index.ts for details. +*/ export abstract class BaseObservableMap extends BaseObservable> { - private _defaults: BaseObservableMapTransformers; - constructor(defaults: BaseObservableMapTransformers) { + constructor() { super(); - this._defaults = defaults; } emitReset(): void { @@ -63,23 +65,33 @@ export abstract class BaseObservableMap extends BaseObservable): JoinedMap { - return this._defaults.join(this, ...otherMaps); - } + return new JoinedMap([this].concat(otherMaps)); + } - mapValues(mapper: Mapper, updater?: Updater): MappedMap { - return this._defaults.mapValues(this, mapper, updater); - } + mapValues(mapper: Mapper, updater?: Updater): MappedMap { + return new MappedMap(this, mapper, updater); + } - sortValues(comparator: Comparator): SortedMapList { - return this._defaults.sortValues(this, comparator); - } - - filterValues(filter: Filter): FilteredMap { - return this._defaults.filterValues(this, filter); - } + sortValues(comparator: Comparator): SortedMapList { + return new SortedMapList(this, comparator); + } + filterValues(filter: Filter): FilteredMap { + return new FilteredMap(this, filter); + } abstract [Symbol.iterator](): Iterator<[K, V]>; abstract get size(): number; abstract get(key: K): V | undefined; } + +export type Mapper = ( + value: V, + emitSpontaneousUpdate: any, +) => MappedV; + +export type Updater = (params: any, mappedValue?: MappedV, value?: V) => void; + +export type Comparator = (a: V, b: V) => number; + +export type Filter = (v: V, k: K) => boolean; \ No newline at end of file diff --git a/src/observable/map/BaseObservableMapTransformers.ts b/src/observable/map/BaseObservableMapTransformers.ts deleted file mode 100644 index b81ffe33..00000000 --- a/src/observable/map/BaseObservableMapTransformers.ts +++ /dev/null @@ -1,69 +0,0 @@ -/* -Copyright 2022 Isaiah Becker-Mayer - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -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 type {BaseObservableMap} from "./BaseObservableMap"; -import {FilteredMap} from "./FilteredMap"; -import {MappedMap} from "./MappedMap"; -import {JoinedMap} from "./JoinedMap"; -import {SortedMapList} from "../list/SortedMapList.js"; - - -// This class provides implementations of functions that transform one BaseObservableMap -// to another type of Map. It's methods are effectively default implementations of the -// methods by the same name on BaseObservableMap. -// -// It is kept as its own class in its own file in order to avoid circular dependencies -// which would occur if these method implementations were defined on BaseObservableMap -// itself. For example, if we attmpted to do the following on BaseObservableMap: -// -// class BaseObservableMap extends BaseObservable> { -// join(...otherMaps: Array>): JoinedMap { -// return new JoinedMap(this.concat(otherMaps)); -// } -// } -// -// we would end up with a circular dependency between BaseObservableMap and JoinedMap, -// since BaseObservableMap would need to import JoinedMap for the -// `return new JoinedMap(this.concat(otherMaps))`, and -// JoinedMap would need to import BaseObservableMap to do -// `JoinedMap extends BaseObservableMap`. -export class BaseObservableMapTransformers { - join(_this: BaseObservableMap, ...otherMaps: Array>): JoinedMap { - return new JoinedMap([_this].concat(otherMaps)); - } - - mapValues(_this: BaseObservableMap, mapper: Mapper, updater?: Updater): MappedMap { - return new MappedMap(_this, mapper, updater); - } - - sortValues(_this: BaseObservableMap, comparator: Comparator): SortedMapList { - return new SortedMapList(_this, comparator); - } - - filterValues(_this: BaseObservableMap, filter: Filter): FilteredMap { - return new FilteredMap(_this, filter); - } -} - -export type Mapper = ( - value: V, - emitSpontaneousUpdate: any, -) => MappedV; - -export type Updater = (params: any, mappedValue?: MappedV, value?: V) => void; - -export type Comparator = (a: V, b: V) => number; - -export type Filter = (v: V, k: K) => boolean; \ No newline at end of file diff --git a/src/observable/map/FilteredMap.ts b/src/observable/map/FilteredMap.ts index 2f3ba9fd..1fd7d89a 100644 --- a/src/observable/map/FilteredMap.ts +++ b/src/observable/map/FilteredMap.ts @@ -14,11 +14,15 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {BaseObservableMap} from "./BaseObservableMap"; +import {BaseObservableMap, Filter} from "./index"; import {SubscriptionHandle} from "../BaseObservable"; -import {BaseObservableMapTransformers, Filter} from "./BaseObservableMapTransformers"; +/* +This class MUST never be imported directly from here. +Instead, it MUST be imported from index.ts. See the +top level comment in index.ts for details. +*/ export class FilteredMap extends BaseObservableMap { private _source: BaseObservableMap; private _filter: Filter; @@ -26,7 +30,7 @@ export class FilteredMap extends BaseObservableMap { private _subscription?: SubscriptionHandle; constructor(source: BaseObservableMap, filter: Filter) { - super(new BaseObservableMapTransformers()); + super(); this._source = source; this._filter = filter; } diff --git a/src/observable/map/JoinedMap.ts b/src/observable/map/JoinedMap.ts index f6cb074a..c125f4da 100644 --- a/src/observable/map/JoinedMap.ts +++ b/src/observable/map/JoinedMap.ts @@ -14,17 +14,21 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {BaseObservableMap} from "./BaseObservableMap"; -import {BaseObservableMapTransformers} from "./BaseObservableMapTransformers"; +import {BaseObservableMap} from "."; import {SubscriptionHandle} from "../BaseObservable"; +/* +This class MUST never be imported directly from here. +Instead, it MUST be imported from index.ts. See the +top level comment in index.ts for details. +*/ export class JoinedMap extends BaseObservableMap { protected _sources: BaseObservableMap[]; private _subscriptions?: SourceSubscriptionHandler[]; constructor(sources: BaseObservableMap[]) { - super(new BaseObservableMapTransformers()); + super(); this._sources = sources; } diff --git a/src/observable/map/LogMap.ts b/src/observable/map/LogMap.ts index 15f204d2..60ac7721 100644 --- a/src/observable/map/LogMap.ts +++ b/src/observable/map/LogMap.ts @@ -14,20 +14,24 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {BaseObservableMap} from "./BaseObservableMap"; -import {BaseObservableMapTransformers} from "./BaseObservableMapTransformers"; +import {BaseObservableMap} from "./index"; import {SubscriptionHandle} from "../BaseObservable"; import {ILogItem, LabelOrValues} from "../../logging/types"; import {LogLevel} from "../../logging/LogFilter"; +/* +This class MUST never be imported directly from here. +Instead, it MUST be imported from index.ts. See the +top level comment in index.ts for details. +*/ export class LogMap extends BaseObservableMap { private _source: BaseObservableMap; private _subscription?: SubscriptionHandle; private _log: ILogItem; constructor(source: BaseObservableMap, log: ILogItem) { - super(new BaseObservableMapTransformers()); + super(); this._source = source; this._log = log; } diff --git a/src/observable/map/MappedMap.ts b/src/observable/map/MappedMap.ts index e932e1b8..0d2c47c0 100644 --- a/src/observable/map/MappedMap.ts +++ b/src/observable/map/MappedMap.ts @@ -14,8 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {BaseObservableMap} from "./BaseObservableMap"; -import {BaseObservableMapTransformers, Mapper, Updater} from "./BaseObservableMapTransformers"; +import {BaseObservableMap, Mapper, Updater} from "./index"; import {SubscriptionHandle} from "../BaseObservable"; @@ -23,6 +22,11 @@ import {SubscriptionHandle} from "../BaseObservable"; so a mapped value can emit updates on it's own with this._emitSpontaneousUpdate that is passed in the mapping function how should the mapped value be notified of an update though? and can it then decide to not propagate the update? */ +/* +This class MUST never be imported directly from here. +Instead, it MUST be imported from index.ts. See the +top level comment in index.ts for details. +*/ export class MappedMap extends BaseObservableMap { private _source: BaseObservableMap; private _mapper: Mapper; @@ -36,7 +40,7 @@ export class MappedMap extends BaseObservableMap { mapper: Mapper, updater?: Updater ) { - super(new BaseObservableMapTransformers()); + super(); this._source = source; this._mapper = mapper; this._updater = updater; diff --git a/src/observable/map/ObservableMap.ts b/src/observable/map/ObservableMap.ts index 09a73d71..c5ffe397 100644 --- a/src/observable/map/ObservableMap.ts +++ b/src/observable/map/ObservableMap.ts @@ -14,15 +14,19 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {BaseObservableMap} from "./BaseObservableMap"; -import {BaseObservableMapTransformers} from "./BaseObservableMapTransformers"; +import {BaseObservableMap} from "./index"; +/* +This class MUST never be imported directly from here. +Instead, it MUST be imported from index.ts. See the +top level comment in index.ts for details. +*/ export class ObservableMap extends BaseObservableMap { private readonly _values: Map; constructor(initialValues?: (readonly [K, V])[]) { - super(new BaseObservableMapTransformers()); + super(); this._values = new Map(initialValues); } diff --git a/src/observable/map/index.ts b/src/observable/map/index.ts new file mode 100644 index 00000000..1f4afc3e --- /dev/null +++ b/src/observable/map/index.ts @@ -0,0 +1,16 @@ +// In order to avoid a circular dependency problem at runtime between BaseObservableMap +// and the classes that extend it, it's important that: +// +// 1) It always remain the first module exported below. +// 2) Anything that imports any of the classes in this module +// ONLY import them from this index.ts file. +// +// See https://medium.com/visual-development/how-to-fix-nasty-circular-dependency-issues-once-and-for-all-in-javascript-typescript-a04c987cf0de +// for more on why this discipline is necessary. +export {BaseObservableMap, Mapper, Updater, Comparator, Filter} from './BaseObservableMap'; +export {ApplyMap} from './ApplyMap'; +export {FilteredMap} from './FilteredMap'; +export {JoinedMap} from './JoinedMap'; +export {LogMap} from './LogMap'; +export {MappedMap} from './MappedMap'; +export {ObservableMap} from './ObservableMap'; \ No newline at end of file From ebd8c0751a11e63e877d74a68dd836cac4fcfa5c Mon Sep 17 00:00:00 2001 From: Isaiah Becker-Mayer Date: Sat, 20 Aug 2022 17:04:13 -0400 Subject: [PATCH 22/24] fixes AsyncMappedList --- src/observable/list/AsyncMappedList.ts | 24 ++++++++++++------------ src/observable/map/ApplyMap.ts | 1 - 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/observable/list/AsyncMappedList.ts b/src/observable/list/AsyncMappedList.ts index f1785c13..2c5ef63f 100644 --- a/src/observable/list/AsyncMappedList.ts +++ b/src/observable/list/AsyncMappedList.ts @@ -22,7 +22,7 @@ export class AsyncMappedList extends BaseMappedList> impleme private _eventQueue: AsyncEvent[] | null = null; private _flushing: boolean = false; - async onSubscribeFirst(): Promise { + onSubscribeFirst(): void { this._sourceUnsubscribe = this._sourceList.subscribe(this); this._eventQueue = []; this._mappedValues = []; @@ -31,7 +31,7 @@ export class AsyncMappedList extends BaseMappedList> impleme this._eventQueue.push(new AddEvent(idx, item)); idx += 1; } - await this._flush(); + void this._flush(); } async _flush(): Promise { @@ -49,38 +49,38 @@ export class AsyncMappedList extends BaseMappedList> impleme } } - async onReset(): Promise { + onReset(): void { if (this._eventQueue) { this._eventQueue.push(new ResetEvent()); - await this._flush(); + void this._flush(); } } - async onAdd(index: number, value: F): Promise { + onAdd(index: number, value: F): void { if (this._eventQueue) { this._eventQueue.push(new AddEvent(index, value)); - await this._flush(); + void this._flush(); } } - async onUpdate(index: number, value: F, params: any): Promise { + onUpdate(index: number, value: F, params: any): void { if (this._eventQueue) { this._eventQueue.push(new UpdateEvent(index, value, params)); - await this._flush(); + void this._flush(); } } - async onRemove(index: number): Promise { + onRemove(index: number): void { if (this._eventQueue) { this._eventQueue.push(new RemoveEvent(index)); - await this._flush(); + void this._flush(); } } - async onMove(fromIdx: number, toIdx: number): Promise { + onMove(fromIdx: number, toIdx: number): void { if (this._eventQueue) { this._eventQueue.push(new MoveEvent(fromIdx, toIdx)); - await this._flush(); + void this._flush(); } } diff --git a/src/observable/map/ApplyMap.ts b/src/observable/map/ApplyMap.ts index 44acd1fe..a13cf757 100644 --- a/src/observable/map/ApplyMap.ts +++ b/src/observable/map/ApplyMap.ts @@ -28,7 +28,6 @@ export class ApplyMap extends BaseObservableMap { private _subscription?: SubscriptionHandle; private _apply?: Apply; - constructor(source: BaseObservableMap, apply?: Apply) { super(); this._source = source; From 1e6d5ca42fc361bed81648292c0957773e05ab3d Mon Sep 17 00:00:00 2001 From: Isaiah Becker-Mayer Date: Sun, 21 Aug 2022 07:42:06 -0400 Subject: [PATCH 23/24] exports types instead of objects --- src/observable/map/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/observable/map/index.ts b/src/observable/map/index.ts index 1f4afc3e..a78446c4 100644 --- a/src/observable/map/index.ts +++ b/src/observable/map/index.ts @@ -7,7 +7,8 @@ // // See https://medium.com/visual-development/how-to-fix-nasty-circular-dependency-issues-once-and-for-all-in-javascript-typescript-a04c987cf0de // for more on why this discipline is necessary. -export {BaseObservableMap, Mapper, Updater, Comparator, Filter} from './BaseObservableMap'; +export {BaseObservableMap} from './BaseObservableMap'; +export type {Mapper, Updater, Comparator, Filter} from './BaseObservableMap'; export {ApplyMap} from './ApplyMap'; export {FilteredMap} from './FilteredMap'; export {JoinedMap} from './JoinedMap'; From d025c1111e98ac481dfdf685ed54b2ca9a30e09c Mon Sep 17 00:00:00 2001 From: Isaiah Becker-Mayer Date: Thu, 25 Aug 2022 22:03:46 -0400 Subject: [PATCH 24/24] fixes [Symbol.iterator] typing --- src/observable/list/BaseMappedList.ts | 3 +-- src/observable/list/ConcatList.ts | 6 ++--- src/observable/list/ObservableArray.ts | 3 +-- src/observable/list/SortedArray.ts | 31 +++++++++----------------- src/observable/map/ApplyMap.ts | 7 +++--- src/observable/map/FilteredMap.ts | 8 +++---- src/observable/map/LogMap.ts | 3 +-- src/observable/map/MappedMap.ts | 11 +++++---- src/observable/map/ObservableMap.ts | 2 +- 9 files changed, 31 insertions(+), 43 deletions(-) diff --git a/src/observable/list/BaseMappedList.ts b/src/observable/list/BaseMappedList.ts index 0435a760..8646153e 100644 --- a/src/observable/list/BaseMappedList.ts +++ b/src/observable/list/BaseMappedList.ts @@ -45,8 +45,7 @@ export class BaseMappedList extends BaseObservableList { return this._mappedValues!.length; } - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - [Symbol.iterator]() { + [Symbol.iterator](): IterableIterator { return this._mappedValues!.values(); } } diff --git a/src/observable/list/ConcatList.ts b/src/observable/list/ConcatList.ts index aaad5a6a..80accb81 100644 --- a/src/observable/list/ConcatList.ts +++ b/src/observable/list/ConcatList.ts @@ -86,13 +86,11 @@ export class ConcatList extends BaseObservableList implements IListObserve return len; } - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - [Symbol.iterator]() { + [Symbol.iterator](): Iterator { let sourceListIdx = 0; let it = this._sourceLists[0][Symbol.iterator](); return { - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - next: () => { + next: (): IteratorResult => { let result = it.next(); while (result.done) { sourceListIdx += 1; diff --git a/src/observable/list/ObservableArray.ts b/src/observable/list/ObservableArray.ts index 662f715e..1b962e81 100644 --- a/src/observable/list/ObservableArray.ts +++ b/src/observable/list/ObservableArray.ts @@ -75,8 +75,7 @@ export class ObservableArray extends BaseObservableList { return this._items.length; } - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - [Symbol.iterator]() { + [Symbol.iterator](): IterableIterator { return this._items.values(); } } diff --git a/src/observable/list/SortedArray.ts b/src/observable/list/SortedArray.ts index c956f7b8..e4723db1 100644 --- a/src/observable/list/SortedArray.ts +++ b/src/observable/list/SortedArray.ts @@ -112,40 +112,31 @@ export class SortedArray extends BaseObservableList { return this._items.length; } - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - [Symbol.iterator]() { + [Symbol.iterator](): Iterator { return new Iterator(this); } } // iterator that works even if the current value is removed while iterating class Iterator { - private _sortedArray: SortedArray | null - private _current: T | null | undefined + private _sortedArray: SortedArray; + private _current: T | null | undefined; + private _consumed: boolean = false; constructor(sortedArray: SortedArray) { this._sortedArray = sortedArray; this._current = null; } - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - next() { - if (this._sortedArray) { - if (this._current) { - this._current = this._sortedArray._getNext(this._current); - } else { - this._current = this._sortedArray.get(0); - } - if (this._current) { - return {value: this._current}; - } else { - // cause done below - this._sortedArray = null; - } + next(): IteratorResult { + if (this._consumed) { + return {value: undefined, done: true}; } - if (!this._sortedArray) { - return {done: true}; + this._current = this._current? this._sortedArray._getNext(this._current): this._sortedArray.get(0); + if (!this._current) { + this._consumed = true; } + return { value: this._current, done: this._consumed } as IteratorResult; } } diff --git a/src/observable/map/ApplyMap.ts b/src/observable/map/ApplyMap.ts index a13cf757..0c4962c8 100644 --- a/src/observable/map/ApplyMap.ts +++ b/src/observable/map/ApplyMap.ts @@ -79,7 +79,9 @@ export class ApplyMap extends BaseObservableMap { onUnsubscribeLast(): void { super.onUnsubscribeLast(); - if (this._subscription) this._subscription = this._subscription(); + if (this._subscription) { + this._subscription = this._subscription(); + } } onReset(): void { @@ -89,8 +91,7 @@ export class ApplyMap extends BaseObservableMap { this.emitReset(); } - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - [Symbol.iterator]() { + [Symbol.iterator](): Iterator<[K, V]> { return this._source[Symbol.iterator](); } diff --git a/src/observable/map/FilteredMap.ts b/src/observable/map/FilteredMap.ts index 1fd7d89a..c97bc48a 100644 --- a/src/observable/map/FilteredMap.ts +++ b/src/observable/map/FilteredMap.ts @@ -142,8 +142,7 @@ export class FilteredMap extends BaseObservableMap { this.emitReset(); } - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - [Symbol.iterator]() { + [Symbol.iterator](): FilterIterator { return new FilterIterator(this._source, this._included); } @@ -157,7 +156,7 @@ export class FilteredMap extends BaseObservableMap { return count; } - get(key): V | undefined{ + get(key: K): V | undefined { const value = this._source.get(key); if (value && this._filter(value, key)) { return value; @@ -173,8 +172,7 @@ class FilterIterator { this._sourceIterator = map[Symbol.iterator](); } - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - next() { + next(): IteratorResult<[K, V]> { // eslint-disable-next-line no-constant-condition while (true) { const sourceResult = this._sourceIterator.next(); diff --git a/src/observable/map/LogMap.ts b/src/observable/map/LogMap.ts index 60ac7721..ce9d343e 100644 --- a/src/observable/map/LogMap.ts +++ b/src/observable/map/LogMap.ts @@ -72,8 +72,7 @@ export class LogMap extends BaseObservableMap { this.emitReset(); } - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - [Symbol.iterator]() { + [Symbol.iterator](): Iterator<[K, V]> { return this._source[Symbol.iterator](); } diff --git a/src/observable/map/MappedMap.ts b/src/observable/map/MappedMap.ts index 0d2c47c0..6ada079f 100644 --- a/src/observable/map/MappedMap.ts +++ b/src/observable/map/MappedMap.ts @@ -64,7 +64,9 @@ export class MappedMap extends BaseObservableMap { onRemove(key: K/*, _value*/): void { const mappedValue = this._mappedValues.get(key); if (this._mappedValues.delete(key)) { - if (mappedValue) this.emitRemove(key, mappedValue); + if (mappedValue) { + this.emitRemove(key, mappedValue); + } } } @@ -93,7 +95,9 @@ export class MappedMap extends BaseObservableMap { onUnsubscribeLast(): void { super.onUnsubscribeLast(); - if (this._subscription) this._subscription = this._subscription(); + if (this._subscription) { + this._subscription = this._subscription(); + } this._mappedValues.clear(); } @@ -102,8 +106,7 @@ export class MappedMap extends BaseObservableMap { this.emitReset(); } - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - [Symbol.iterator]() { + [Symbol.iterator](): IterableIterator<[K, MappedV]> { return this._mappedValues.entries(); } diff --git a/src/observable/map/ObservableMap.ts b/src/observable/map/ObservableMap.ts index c5ffe397..f0d4c77a 100644 --- a/src/observable/map/ObservableMap.ts +++ b/src/observable/map/ObservableMap.ts @@ -97,7 +97,7 @@ export class ObservableMap extends BaseObservableMap { keys(): Iterator { return this._values.keys(); } -}; +} // eslint-disable-next-line @typescript-eslint/explicit-function-return-type export function tests() {