Merge pull request #702 from vector-im/bwindels/observablemapts

convert (Base)ObservableMap to typescript
This commit is contained in:
Bruno Windels 2022-03-09 11:53:59 +01:00 committed by GitHub
commit ca211f929b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 69 additions and 55 deletions

View file

@ -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 See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {ObservableMap} from "../../../../observable/map/ObservableMap.js"; import {ObservableMap} from "../../../../observable/map/ObservableMap";
export class ReactionsViewModel { export class ReactionsViewModel {
constructor(parentTile) { constructor(parentTile) {

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {ObservableMap} from "../../../observable/map/ObservableMap.js"; import {ObservableMap} from "../../../observable/map/ObservableMap";
import {RetainedValue} from "../../../utils/RetainedValue"; import {RetainedValue} from "../../../utils/RetainedValue";
export class MemberList extends RetainedValue { export class MemberList extends RetainedValue {

View file

@ -18,14 +18,14 @@ import {SortedMapList} from "./list/SortedMapList.js";
import {FilteredMap} from "./map/FilteredMap.js"; import {FilteredMap} from "./map/FilteredMap.js";
import {MappedMap} from "./map/MappedMap.js"; import {MappedMap} from "./map/MappedMap.js";
import {JoinedMap} from "./map/JoinedMap.js"; import {JoinedMap} from "./map/JoinedMap.js";
import {BaseObservableMap} from "./map/BaseObservableMap.js"; import {BaseObservableMap} from "./map/BaseObservableMap";
// re-export "root" (of chain) collections // re-export "root" (of chain) collections
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";
export { AsyncMappedList } from "./list/AsyncMappedList"; export { AsyncMappedList } from "./list/AsyncMappedList";
export { ConcatList } from "./list/ConcatList"; export { ConcatList } from "./list/ConcatList";
export { ObservableMap } from "./map/ObservableMap.js"; export { ObservableMap } from "./map/ObservableMap";
// avoid circular dependency between these classes // avoid circular dependency between these classes
// and BaseObservableMap (as they extend it) // and BaseObservableMap (as they extend it)

View file

@ -133,7 +133,7 @@ export class SortedMapList extends BaseObservableList {
} }
} }
import {ObservableMap} from "../map/ObservableMap.js"; import {ObservableMap} from "../map/ObservableMap";
export function tests() { export function tests() {
return { return {

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {BaseObservableMap} from "./BaseObservableMap.js"; import {BaseObservableMap} from "./BaseObservableMap";
export class ApplyMap extends BaseObservableMap { export class ApplyMap extends BaseObservableMap {
constructor(source, apply) { constructor(source, apply) {

View file

@ -16,7 +16,14 @@ limitations under the License.
import {BaseObservable} from "../BaseObservable"; import {BaseObservable} from "../BaseObservable";
export class BaseObservableMap extends BaseObservable { export interface IMapObserver<K, V> {
onReset(): void;
onAdd(key: K, value:V): void;
onUpdate(key: K, value: V, params: any): void;
onRemove(key: K, value: V): void
}
export abstract class BaseObservableMap<K, V> extends BaseObservable<IMapObserver<K, V>> {
emitReset() { emitReset() {
for(let h of this._handlers) { for(let h of this._handlers) {
h.onReset(); h.onReset();
@ -24,15 +31,15 @@ export class BaseObservableMap extends BaseObservable {
} }
// we need batch events, mostly on index based collection though? // we need batch events, mostly on index based collection though?
// maybe we should get started without? // maybe we should get started without?
emitAdd(key, value) { emitAdd(key: K, value: V) {
for(let h of this._handlers) { for(let h of this._handlers) {
h.onAdd(key, value); h.onAdd(key, value);
} }
} }
emitUpdate(key, value, ...params) { emitUpdate(key, value, params) {
for(let h of this._handlers) { for(let h of this._handlers) {
h.onUpdate(key, value, ...params); h.onUpdate(key, value, params);
} }
} }
@ -42,16 +49,7 @@ export class BaseObservableMap extends BaseObservable {
} }
} }
[Symbol.iterator]() { abstract [Symbol.iterator](): Iterator<[K, V]>;
throw new Error("unimplemented"); abstract get size(): number;
} abstract get(key: K): V | undefined;
get size() {
throw new Error("unimplemented");
}
// eslint-disable-next-line no-unused-vars
get(key) {
throw new Error("unimplemented");
}
} }

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {BaseObservableMap} from "./BaseObservableMap.js"; import {BaseObservableMap} from "./BaseObservableMap";
export class FilteredMap extends BaseObservableMap { export class FilteredMap extends BaseObservableMap {
constructor(source, filter) { constructor(source, filter) {
@ -166,7 +166,7 @@ class FilterIterator {
} }
} }
import {ObservableMap} from "./ObservableMap.js"; import {ObservableMap} from "./ObservableMap";
export function tests() { export function tests() {
return { return {
"filter preloaded list": assert => { "filter preloaded list": assert => {

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {BaseObservableMap} from "./BaseObservableMap.js"; import {BaseObservableMap} from "./BaseObservableMap";
export class JoinedMap extends BaseObservableMap { export class JoinedMap extends BaseObservableMap {
constructor(sources) { constructor(sources) {
@ -191,7 +191,7 @@ class SourceSubscriptionHandler {
} }
import { ObservableMap } from "./ObservableMap.js"; import { ObservableMap } from "./ObservableMap";
export function tests() { export function tests() {

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {BaseObservableMap} from "./BaseObservableMap.js"; import {BaseObservableMap} from "./BaseObservableMap";
export class LogMap extends BaseObservableMap { export class LogMap extends BaseObservableMap {
constructor(source, log) { constructor(source, log) {

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {BaseObservableMap} from "./BaseObservableMap.js"; import {BaseObservableMap} from "./BaseObservableMap";
/* /*
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?

View file

@ -14,15 +14,17 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {BaseObservableMap} from "./BaseObservableMap.js"; import {BaseObservableMap} from "./BaseObservableMap";
export class ObservableMap extends BaseObservableMap { export class ObservableMap<K, V> extends BaseObservableMap<K, V> {
constructor(initialValues) { private readonly _values: Map<K, V>;
constructor(initialValues?: (readonly [K, V])[]) {
super(); super();
this._values = new Map(initialValues); this._values = new Map(initialValues);
} }
update(key, params) { update(key: K, params?: any): boolean {
const value = this._values.get(key); const value = this._values.get(key);
if (value !== undefined) { if (value !== undefined) {
// could be the same value, so it's already updated // could be the same value, so it's already updated
@ -34,7 +36,7 @@ export class ObservableMap extends BaseObservableMap {
return false; // or return existing value? return false; // or return existing value?
} }
add(key, value) { add(key: K, value: V): boolean {
if (!this._values.has(key)) { if (!this._values.has(key)) {
this._values.set(key, value); this._values.set(key, value);
this.emitAdd(key, value); this.emitAdd(key, value);
@ -43,7 +45,7 @@ export class ObservableMap extends BaseObservableMap {
return false; // or return existing value? return false; // or return existing value?
} }
remove(key) { remove(key: K): boolean {
const value = this._values.get(key); const value = this._values.get(key);
if (value !== undefined) { if (value !== undefined) {
this._values.delete(key); this._values.delete(key);
@ -54,39 +56,39 @@ export class ObservableMap extends BaseObservableMap {
} }
} }
set(key, value) { set(key: K, value: V): boolean {
if (this._values.has(key)) { if (this._values.has(key)) {
// We set the value here because update only supports inline updates // We set the value here because update only supports inline updates
this._values.set(key, value); this._values.set(key, value);
return this.update(key); return this.update(key, undefined);
} }
else { else {
return this.add(key, value); return this.add(key, value);
} }
} }
reset() { reset(): void {
this._values.clear(); this._values.clear();
this.emitReset(); this.emitReset();
} }
get(key) { get(key: K): V | undefined {
return this._values.get(key); return this._values.get(key);
} }
get size() { get size(): number {
return this._values.size; return this._values.size;
} }
[Symbol.iterator]() { [Symbol.iterator](): Iterator<[K, V]> {
return this._values.entries(); return this._values.entries();
} }
values() { values(): Iterator<V> {
return this._values.values(); return this._values.values();
} }
keys() { keys(): Iterator<K> {
return this._values.keys(); return this._values.keys();
} }
} }
@ -105,13 +107,16 @@ export function tests() {
test_add(assert) { test_add(assert) {
let fired = 0; let fired = 0;
const map = new ObservableMap(); const map = new ObservableMap<number, {value: number}>();
map.subscribe({ map.subscribe({
onAdd(key, value) { onAdd(key, value) {
fired += 1; fired += 1;
assert.equal(key, 1); assert.equal(key, 1);
assert.deepEqual(value, {value: 5}); assert.deepEqual(value, {value: 5});
} },
onUpdate() {},
onRemove() {},
onReset() {}
}); });
map.add(1, {value: 5}); map.add(1, {value: 5});
assert.equal(map.size, 1); assert.equal(map.size, 1);
@ -120,7 +125,7 @@ export function tests() {
test_update(assert) { test_update(assert) {
let fired = 0; let fired = 0;
const map = new ObservableMap(); const map = new ObservableMap<number, {number: number}>();
const value = {number: 5}; const value = {number: 5};
map.add(1, value); map.add(1, value);
map.subscribe({ map.subscribe({
@ -129,7 +134,10 @@ export function tests() {
assert.equal(key, 1); assert.equal(key, 1);
assert.deepEqual(value, {number: 6}); assert.deepEqual(value, {number: 6});
assert.equal(params, "test"); assert.equal(params, "test");
} },
onAdd() {},
onRemove() {},
onReset() {}
}); });
value.number = 6; value.number = 6;
map.update(1, "test"); map.update(1, "test");
@ -138,9 +146,12 @@ export function tests() {
test_update_unknown(assert) { test_update_unknown(assert) {
let fired = 0; let fired = 0;
const map = new ObservableMap(); const map = new ObservableMap<number, {number: number}>();
map.subscribe({ map.subscribe({
onUpdate() { fired += 1; } onUpdate() { fired += 1; },
onAdd() {},
onRemove() {},
onReset() {}
}); });
const result = map.update(1); const result = map.update(1);
assert.equal(fired, 0); assert.equal(fired, 0);
@ -149,7 +160,7 @@ export function tests() {
test_set(assert) { test_set(assert) {
let add_fired = 0, update_fired = 0; let add_fired = 0, update_fired = 0;
const map = new ObservableMap(); const map = new ObservableMap<number, {value: number}>();
map.subscribe({ map.subscribe({
onAdd(key, value) { onAdd(key, value) {
add_fired += 1; add_fired += 1;
@ -160,7 +171,9 @@ export function tests() {
update_fired += 1; update_fired += 1;
assert.equal(key, 1); assert.equal(key, 1);
assert.deepEqual(value, {value: 7}); assert.deepEqual(value, {value: 7});
} },
onRemove() {},
onReset() {}
}); });
// Add // Add
map.set(1, {value: 5}); map.set(1, {value: 5});
@ -174,7 +187,7 @@ export function tests() {
test_remove(assert) { test_remove(assert) {
let fired = 0; let fired = 0;
const map = new ObservableMap(); const map = new ObservableMap<number, {value: number}>();
const value = {value: 5}; const value = {value: 5};
map.add(1, value); map.add(1, value);
map.subscribe({ map.subscribe({
@ -182,7 +195,10 @@ export function tests() {
fired += 1; fired += 1;
assert.equal(key, 1); assert.equal(key, 1);
assert.deepEqual(value, {value: 5}); assert.deepEqual(value, {value: 5});
} },
onAdd() {},
onUpdate() {},
onReset() {}
}); });
map.remove(1); map.remove(1);
assert.equal(map.size, 0); assert.equal(map.size, 0);
@ -190,8 +206,8 @@ export function tests() {
}, },
test_iterate(assert) { test_iterate(assert) {
const results = []; const results: any[] = [];
const map = new ObservableMap(); const map = new ObservableMap<number, {number: number}>();
map.add(1, {number: 5}); map.add(1, {number: 5});
map.add(2, {number: 6}); map.add(2, {number: 6});
map.add(3, {number: 7}); map.add(3, {number: 7});
@ -204,7 +220,7 @@ export function tests() {
assert.equal(results.find(([key]) => key === 3)[1].number, 7); assert.equal(results.find(([key]) => key === 3)[1].number, 7);
}, },
test_size(assert) { test_size(assert) {
const map = new ObservableMap(); const map = new ObservableMap<number, {number: number}>();
map.add(1, {number: 5}); map.add(1, {number: 5});
map.add(2, {number: 6}); map.add(2, {number: 6});
assert.equal(map.size, 2); assert.equal(map.size, 2);