forked from mystiq/hydrogen-web
Merge pull request #702 from vector-im/bwindels/observablemapts
convert (Base)ObservableMap to typescript
This commit is contained in:
commit
ca211f929b
11 changed files with 69 additions and 55 deletions
|
@ -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) {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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");
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -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 => {
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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?
|
||||||
|
|
|
@ -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);
|
Loading…
Reference in a new issue