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