Isaiah Becker-Mayer 2022-08-20 16:39:39 -04:00
parent 92ed503700
commit 77f21f7a91
12 changed files with 92 additions and 110 deletions

View File

@ -20,7 +20,7 @@ import {RoomTileViewModel} from "./RoomTileViewModel.js";
import {InviteTileViewModel} from "./InviteTileViewModel.js"; import {InviteTileViewModel} from "./InviteTileViewModel.js";
import {RoomBeingCreatedTileViewModel} from "./RoomBeingCreatedTileViewModel.js"; import {RoomBeingCreatedTileViewModel} from "./RoomBeingCreatedTileViewModel.js";
import {RoomFilter} from "./RoomFilter.js"; import {RoomFilter} from "./RoomFilter.js";
import {ApplyMap} from "../../../observable/map/ApplyMap"; import {ApplyMap} from "../../../observable";
import {addPanelIfNeeded} from "../../navigation"; import {addPanelIfNeeded} from "../../navigation";
export class LeftPanelViewModel extends ViewModel { export class LeftPanelViewModel extends ViewModel {

View File

@ -78,8 +78,7 @@ export {
MappedList, MappedList,
AsyncMappedList, AsyncMappedList,
ConcatList, ConcatList,
ObservableMap } from "./observable";
} from "./observable/index";
export { export {
BaseObservableValue, BaseObservableValue,
ObservableValue, ObservableValue,

View File

@ -16,7 +16,7 @@ limitations under the License.
// re-export "root" (of chain) collection // 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 { ObservableArray } from "./list/ObservableArray";
export { SortedArray } from "./list/SortedArray"; export { SortedArray } from "./list/SortedArray";
export { MappedList } from "./list/MappedList"; export { MappedList } from "./list/MappedList";

View File

@ -14,11 +14,15 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {BaseObservableMap} from "./BaseObservableMap"; import {BaseObservableMap} from "./index";
import {SubscriptionHandle} from "../BaseObservable"; 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<K, V> extends BaseObservableMap<K, V> { export class ApplyMap<K, V> extends BaseObservableMap<K, V> {
private _source: BaseObservableMap<K, V>; private _source: BaseObservableMap<K, V>;
private _subscription?: SubscriptionHandle; private _subscription?: SubscriptionHandle;
@ -26,7 +30,7 @@ export class ApplyMap<K, V> extends BaseObservableMap<K, V> {
constructor(source: BaseObservableMap<K, V>, apply?: Apply<K, V>) { constructor(source: BaseObservableMap<K, V>, apply?: Apply<K, V>) {
super(new BaseObservableMapTransformers<K, V>()); super();
this._source = source; this._source = source;
this._apply = apply; this._apply = apply;
} }

View File

@ -15,11 +15,10 @@ limitations under the License.
*/ */
import {BaseObservable} from "../BaseObservable"; import {BaseObservable} from "../BaseObservable";
import {JoinedMap} from "../map/JoinedMap"; import {JoinedMap} from "./index";
import {MappedMap} from "../map/MappedMap"; import {MappedMap} from "./index";
import {FilteredMap} from "../map/FilteredMap"; import {FilteredMap} from "./index";
import {SortedMapList} from "../list/SortedMapList.js"; import {SortedMapList} from "../list/SortedMapList.js";
import type {BaseObservableMapTransformers, Mapper, Updater, Comparator, Filter} from "./BaseObservableMapTransformers";
export interface IMapObserver<K, V> { export interface IMapObserver<K, V> {
@ -29,12 +28,15 @@ export interface IMapObserver<K, V> {
onRemove(key: K, value: V): void 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<K, V> extends BaseObservable<IMapObserver<K, V>> { export abstract class BaseObservableMap<K, V> extends BaseObservable<IMapObserver<K, V>> {
private _defaults: BaseObservableMapTransformers<K, V>;
constructor(defaults: BaseObservableMapTransformers<K, V>) { constructor() {
super(); super();
this._defaults = defaults;
} }
emitReset(): void { emitReset(): void {
@ -63,23 +65,33 @@ export abstract class BaseObservableMap<K, V> extends BaseObservable<IMapObserve
} }
join(...otherMaps: Array<typeof this>): JoinedMap<K, V> { join(...otherMaps: Array<typeof this>): JoinedMap<K, V> {
return this._defaults.join(this, ...otherMaps); return new JoinedMap([this].concat(otherMaps));
} }
mapValues<MappedV>(mapper: Mapper<V, MappedV>, updater?: Updater<V, MappedV>): MappedMap<K, V, MappedV> { mapValues<MappedV>(mapper: Mapper<V, MappedV>, updater?: Updater<V, MappedV>): MappedMap<K, V, MappedV> {
return this._defaults.mapValues(this, mapper, updater); return new MappedMap(this, mapper, updater);
} }
sortValues(comparator: Comparator<V>): SortedMapList { sortValues(comparator: Comparator<V>): SortedMapList {
return this._defaults.sortValues(this, comparator); return new SortedMapList(this, comparator);
} }
filterValues(filter: Filter<K, V>): FilteredMap<K, V> { filterValues(filter: Filter<K, V>): FilteredMap<K, V> {
return this._defaults.filterValues(this, filter); return new FilteredMap(this, filter);
} }
abstract [Symbol.iterator](): Iterator<[K, V]>; abstract [Symbol.iterator](): Iterator<[K, V]>;
abstract get size(): number; abstract get size(): number;
abstract get(key: K): V | undefined; abstract get(key: K): V | undefined;
} }
export type Mapper<V, MappedV> = (
value: V,
emitSpontaneousUpdate: any,
) => MappedV;
export type Updater<V, MappedV> = (params: any, mappedValue?: MappedV, value?: V) => void;
export type Comparator<V> = (a: V, b: V) => number;
export type Filter<K, V> = (v: V, k: K) => boolean;

View File

@ -1,69 +0,0 @@
/*
Copyright 2022 Isaiah Becker-Mayer <ibeckermayer@gmail.com>
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<K, V> extends BaseObservable<IMapObserver<K, V>> {
// join(...otherMaps: Array<BaseObservableMap<K, V>>): JoinedMap<K, V> {
// 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<K, V> extends BaseObservableMap<K, V>`.
export class BaseObservableMapTransformers<K, V> {
join(_this: BaseObservableMap<K, V>, ...otherMaps: Array<BaseObservableMap<K, V>>): JoinedMap<K, V> {
return new JoinedMap([_this].concat(otherMaps));
}
mapValues<MappedV>(_this: BaseObservableMap<K, V>, mapper: Mapper<V, MappedV>, updater?: Updater<V, MappedV>): MappedMap<K, V, MappedV> {
return new MappedMap(_this, mapper, updater);
}
sortValues(_this: BaseObservableMap<K, V>, comparator: Comparator<V>): SortedMapList {
return new SortedMapList(_this, comparator);
}
filterValues(_this: BaseObservableMap<K, V>, filter: Filter<K, V>): FilteredMap<K, V> {
return new FilteredMap(_this, filter);
}
}
export type Mapper<V, MappedV> = (
value: V,
emitSpontaneousUpdate: any,
) => MappedV;
export type Updater<V, MappedV> = (params: any, mappedValue?: MappedV, value?: V) => void;
export type Comparator<V> = (a: V, b: V) => number;
export type Filter<K, V> = (v: V, k: K) => boolean;

View File

@ -14,11 +14,15 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {BaseObservableMap} from "./BaseObservableMap"; import {BaseObservableMap, Filter} from "./index";
import {SubscriptionHandle} from "../BaseObservable"; 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<K, V> extends BaseObservableMap<K, V> { export class FilteredMap<K, V> extends BaseObservableMap<K, V> {
private _source: BaseObservableMap<K, V>; private _source: BaseObservableMap<K, V>;
private _filter: Filter<K, V>; private _filter: Filter<K, V>;
@ -26,7 +30,7 @@ export class FilteredMap<K, V> extends BaseObservableMap<K, V> {
private _subscription?: SubscriptionHandle; private _subscription?: SubscriptionHandle;
constructor(source: BaseObservableMap<K, V>, filter: Filter<K, V>) { constructor(source: BaseObservableMap<K, V>, filter: Filter<K, V>) {
super(new BaseObservableMapTransformers<K, V>()); super();
this._source = source; this._source = source;
this._filter = filter; this._filter = filter;
} }

View File

@ -14,17 +14,21 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {BaseObservableMap} from "./BaseObservableMap"; import {BaseObservableMap} from ".";
import {BaseObservableMapTransformers} from "./BaseObservableMapTransformers";
import {SubscriptionHandle} from "../BaseObservable"; 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<K, V> extends BaseObservableMap<K, V> { export class JoinedMap<K, V> extends BaseObservableMap<K, V> {
protected _sources: BaseObservableMap<K, V>[]; protected _sources: BaseObservableMap<K, V>[];
private _subscriptions?: SourceSubscriptionHandler<K, V>[]; private _subscriptions?: SourceSubscriptionHandler<K, V>[];
constructor(sources: BaseObservableMap<K, V>[]) { constructor(sources: BaseObservableMap<K, V>[]) {
super(new BaseObservableMapTransformers<K, V>()); super();
this._sources = sources; this._sources = sources;
} }

View File

@ -14,20 +14,24 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {BaseObservableMap} from "./BaseObservableMap"; import {BaseObservableMap} from "./index";
import {BaseObservableMapTransformers} from "./BaseObservableMapTransformers";
import {SubscriptionHandle} from "../BaseObservable"; import {SubscriptionHandle} from "../BaseObservable";
import {ILogItem, LabelOrValues} from "../../logging/types"; import {ILogItem, LabelOrValues} from "../../logging/types";
import {LogLevel} from "../../logging/LogFilter"; 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<K, V> extends BaseObservableMap<K, V> { export class LogMap<K, V> extends BaseObservableMap<K, V> {
private _source: BaseObservableMap<K, V>; private _source: BaseObservableMap<K, V>;
private _subscription?: SubscriptionHandle; private _subscription?: SubscriptionHandle;
private _log: ILogItem; private _log: ILogItem;
constructor(source: BaseObservableMap<K, V>, log: ILogItem) { constructor(source: BaseObservableMap<K, V>, log: ILogItem) {
super(new BaseObservableMapTransformers<K, V>()); super();
this._source = source; this._source = source;
this._log = log; this._log = log;
} }

View File

@ -14,8 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {BaseObservableMap} from "./BaseObservableMap"; import {BaseObservableMap, Mapper, Updater} from "./index";
import {BaseObservableMapTransformers, Mapper, Updater} from "./BaseObservableMapTransformers";
import {SubscriptionHandle} from "../BaseObservable"; 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 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? 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<K, V, MappedV> extends BaseObservableMap<K, MappedV> { export class MappedMap<K, V, MappedV> extends BaseObservableMap<K, MappedV> {
private _source: BaseObservableMap<K, V>; private _source: BaseObservableMap<K, V>;
private _mapper: Mapper<V, MappedV>; private _mapper: Mapper<V, MappedV>;
@ -36,7 +40,7 @@ export class MappedMap<K, V, MappedV> extends BaseObservableMap<K, MappedV> {
mapper: Mapper<V, MappedV>, mapper: Mapper<V, MappedV>,
updater?: Updater<V, MappedV> updater?: Updater<V, MappedV>
) { ) {
super(new BaseObservableMapTransformers<K, MappedV>()); super();
this._source = source; this._source = source;
this._mapper = mapper; this._mapper = mapper;
this._updater = updater; this._updater = updater;

View File

@ -14,15 +14,19 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {BaseObservableMap} from "./BaseObservableMap"; import {BaseObservableMap} from "./index";
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 ObservableMap<K, V> extends BaseObservableMap<K, V> { export class ObservableMap<K, V> extends BaseObservableMap<K, V> {
private readonly _values: Map<K, V>; private readonly _values: Map<K, V>;
constructor(initialValues?: (readonly [K, V])[]) { constructor(initialValues?: (readonly [K, V])[]) {
super(new BaseObservableMapTransformers<K, V>()); super();
this._values = new Map(initialValues); this._values = new Map(initialValues);
} }

View File

@ -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';