forked from mystiq/hydrogen-web
Merge pull request #543 from vector-im/bwindels/typescript-observable
Typescript conversion of base observables
This commit is contained in:
commit
de22a0790f
30 changed files with 259 additions and 170 deletions
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {BaseObservableValue, ObservableValue} from "../../observable/ObservableValue.js";
|
||||
import {BaseObservableValue, ObservableValue} from "../../observable/ObservableValue";
|
||||
|
||||
export class Navigation {
|
||||
constructor(allowsChild) {
|
||||
|
|
|
@ -186,7 +186,7 @@ export class RoomGridViewModel extends ViewModel {
|
|||
}
|
||||
|
||||
import {createNavigation} from "../navigation/index.js";
|
||||
import {ObservableValue} from "../../observable/ObservableValue.js";
|
||||
import {ObservableValue} from "../../observable/ObservableValue";
|
||||
|
||||
export function tests() {
|
||||
class RoomVMMock {
|
||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {ObservableValue} from "../../observable/ObservableValue.js";
|
||||
import {ObservableValue} from "../../observable/ObservableValue";
|
||||
|
||||
/**
|
||||
Depending on the status of a room (invited, joined, archived, or none),
|
||||
|
@ -77,4 +77,4 @@ export class RoomViewModelObservable extends ObservableValue {
|
|||
this.unsubscribeAll();
|
||||
this.get()?.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -189,7 +189,7 @@ import {HomeServer as MockHomeServer} from "../../../../mocks/HomeServer.js";
|
|||
// other imports
|
||||
import {BaseMessageTile} from "./tiles/BaseMessageTile.js";
|
||||
import {MappedList} from "../../../../observable/list/MappedList.js";
|
||||
import {ObservableValue} from "../../../../observable/ObservableValue.js";
|
||||
import {ObservableValue} from "../../../../observable/ObservableValue";
|
||||
import {PowerLevels} from "../../../../matrix/room/PowerLevels.js";
|
||||
|
||||
export function tests() {
|
||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {BaseObservableList} from "../../../../observable/list/BaseObservableList.js";
|
||||
import {BaseObservableList} from "../../../../observable/list/BaseObservableList";
|
||||
import {sortedIndex} from "../../../../utils/sortedIndex.js";
|
||||
|
||||
// maps 1..n entries to 0..1 tile. Entries are what is stored in the timeline, either an event or fragmentboundary
|
||||
|
|
|
@ -40,7 +40,7 @@ import {
|
|||
writeKey as ssssWriteKey,
|
||||
} from "./ssss/index.js";
|
||||
import {SecretStorage} from "./ssss/SecretStorage.js";
|
||||
import {ObservableValue, RetainedObservableValue} from "../observable/ObservableValue.js";
|
||||
import {ObservableValue, RetainedObservableValue} from "../observable/ObservableValue";
|
||||
|
||||
const PICKLE_KEY = "DEFAULT_KEY";
|
||||
const PUSHER_KEY = "pusher";
|
||||
|
|
|
@ -18,7 +18,7 @@ limitations under the License.
|
|||
import {createEnum} from "../utils/enum.js";
|
||||
import {lookupHomeserver} from "./well-known.js";
|
||||
import {AbortableOperation} from "../utils/AbortableOperation";
|
||||
import {ObservableValue} from "../observable/ObservableValue.js";
|
||||
import {ObservableValue} from "../observable/ObservableValue";
|
||||
import {HomeServerApi} from "./net/HomeServerApi.js";
|
||||
import {Reconnector, ConnectionStatus} from "./net/Reconnector.js";
|
||||
import {ExponentialRetryDelay} from "./net/ExponentialRetryDelay.js";
|
||||
|
|
|
@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {ObservableValue} from "../observable/ObservableValue.js";
|
||||
import {ObservableValue} from "../observable/ObservableValue";
|
||||
import {createEnum} from "../utils/enum.js";
|
||||
|
||||
const INCREMENTAL_TIMEOUT = 30000;
|
||||
|
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import {createEnum} from "../../utils/enum.js";
|
||||
import {ObservableValue} from "../../observable/ObservableValue.js";
|
||||
import {ObservableValue} from "../../observable/ObservableValue";
|
||||
|
||||
export const ConnectionStatus = createEnum(
|
||||
"Waiting",
|
||||
|
|
|
@ -29,7 +29,7 @@ import {ObservedEventMap} from "./ObservedEventMap.js";
|
|||
import {DecryptionSource} from "../e2ee/common.js";
|
||||
import {ensureLogItem} from "../../logging/utils.js";
|
||||
import {PowerLevels} from "./PowerLevels.js";
|
||||
import {RetainedObservableValue} from "../../observable/ObservableValue.js";
|
||||
import {RetainedObservableValue} from "../../observable/ObservableValue";
|
||||
|
||||
const EVENT_ENCRYPTED_TYPE = "m.room.encrypted";
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {BaseObservableValue} from "../../observable/ObservableValue.js";
|
||||
import {BaseObservableValue} from "../../observable/ObservableValue";
|
||||
|
||||
export class ObservedEventMap {
|
||||
constructor(notifyEmpty) {
|
||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {ObservableValue} from "../observable/ObservableValue.js";
|
||||
import {ObservableValue} from "../observable/ObservableValue";
|
||||
|
||||
class Timeout {
|
||||
constructor(elapsed, ms) {
|
||||
|
|
|
@ -14,20 +14,22 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
export class BaseObservable {
|
||||
constructor() {
|
||||
this._handlers = new Set();
|
||||
}
|
||||
// we return undefined so you can reassign any member
|
||||
// that uses `member?: T` syntax in one statement.
|
||||
export type SubscriptionHandle = () => undefined;
|
||||
|
||||
onSubscribeFirst() {
|
||||
export abstract class BaseObservable<T> {
|
||||
protected _handlers: Set<T> = new Set<T>();
|
||||
|
||||
onSubscribeFirst(): void {
|
||||
|
||||
}
|
||||
|
||||
onUnsubscribeLast() {
|
||||
onUnsubscribeLast(): void {
|
||||
|
||||
}
|
||||
|
||||
subscribe(handler) {
|
||||
subscribe(handler: T): SubscriptionHandle {
|
||||
this._handlers.add(handler);
|
||||
if (this._handlers.size === 1) {
|
||||
this.onSubscribeFirst();
|
||||
|
@ -37,25 +39,24 @@ export class BaseObservable {
|
|||
};
|
||||
}
|
||||
|
||||
unsubscribe(handler) {
|
||||
unsubscribe(handler?: T): undefined {
|
||||
if (handler) {
|
||||
this._handlers.delete(handler);
|
||||
if (this._handlers.size === 0) {
|
||||
this.onUnsubscribeLast();
|
||||
}
|
||||
handler = null;
|
||||
}
|
||||
return null;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
unsubscribeAll() {
|
||||
unsubscribeAll(): void {
|
||||
if (this._handlers.size !== 0) {
|
||||
this._handlers.clear();
|
||||
this.onUnsubscribeLast();
|
||||
}
|
||||
}
|
||||
|
||||
get hasSubscriptions() {
|
||||
get hasSubscriptions(): boolean {
|
||||
return this._handlers.size !== 0;
|
||||
}
|
||||
|
||||
|
@ -63,13 +64,11 @@ export class BaseObservable {
|
|||
}
|
||||
|
||||
export function tests() {
|
||||
class Collection extends BaseObservable {
|
||||
constructor() {
|
||||
super();
|
||||
this.firstSubscribeCalls = 0;
|
||||
this.firstUnsubscribeCalls = 0;
|
||||
}
|
||||
onSubscribeFirst() { this.firstSubscribeCalls += 1; }
|
||||
class Collection extends BaseObservable<{}> {
|
||||
firstSubscribeCalls: number = 0;
|
||||
firstUnsubscribeCalls: number = 0;
|
||||
|
||||
onSubscribeFirst() { this.firstSubscribeCalls += 1; }
|
||||
onUnsubscribeLast() { this.firstUnsubscribeCalls += 1; }
|
||||
}
|
||||
|
|
@ -15,21 +15,19 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import {AbortError} from "../utils/error.js";
|
||||
import {BaseObservable} from "./BaseObservable.js";
|
||||
import {BaseObservable} from "./BaseObservable";
|
||||
|
||||
// like an EventEmitter, but doesn't have an event type
|
||||
export class BaseObservableValue extends BaseObservable {
|
||||
emit(argument) {
|
||||
export abstract class BaseObservableValue<T> extends BaseObservable<(value: T) => void> {
|
||||
emit(argument: T) {
|
||||
for (const h of this._handlers) {
|
||||
h(argument);
|
||||
}
|
||||
}
|
||||
|
||||
get() {
|
||||
throw new Error("unimplemented");
|
||||
}
|
||||
abstract get(): T;
|
||||
|
||||
waitFor(predicate) {
|
||||
waitFor(predicate: (value: T) => boolean): IWaitHandle<T> {
|
||||
if (predicate(this.get())) {
|
||||
return new ResolvedWaitForHandle(Promise.resolve(this.get()));
|
||||
} else {
|
||||
|
@ -38,8 +36,17 @@ export class BaseObservableValue extends BaseObservable {
|
|||
}
|
||||
}
|
||||
|
||||
class WaitForHandle {
|
||||
constructor(observable, predicate) {
|
||||
interface IWaitHandle<T> {
|
||||
promise: Promise<T>;
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
class WaitForHandle<T> implements IWaitHandle<T> {
|
||||
private _promise: Promise<T>
|
||||
private _reject: ((reason?: any) => void) | null;
|
||||
private _subscription: (() => void) | null;
|
||||
|
||||
constructor(observable: BaseObservableValue<T>, predicate: (value: T) => boolean) {
|
||||
this._promise = new Promise((resolve, reject) => {
|
||||
this._reject = reject;
|
||||
this._subscription = observable.subscribe(v => {
|
||||
|
@ -52,7 +59,7 @@ class WaitForHandle {
|
|||
});
|
||||
}
|
||||
|
||||
get promise() {
|
||||
get promise(): Promise<T> {
|
||||
return this._promise;
|
||||
}
|
||||
|
||||
|
@ -68,25 +75,24 @@ class WaitForHandle {
|
|||
}
|
||||
}
|
||||
|
||||
class ResolvedWaitForHandle {
|
||||
constructor(promise) {
|
||||
this.promise = promise;
|
||||
}
|
||||
|
||||
class ResolvedWaitForHandle<T> implements IWaitHandle<T> {
|
||||
constructor(public promise: Promise<T>) {}
|
||||
dispose() {}
|
||||
}
|
||||
|
||||
export class ObservableValue extends BaseObservableValue {
|
||||
constructor(initialValue) {
|
||||
export class ObservableValue<T> extends BaseObservableValue<T> {
|
||||
private _value: T;
|
||||
|
||||
constructor(initialValue: T) {
|
||||
super();
|
||||
this._value = initialValue;
|
||||
}
|
||||
|
||||
get() {
|
||||
get(): T {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
set(value) {
|
||||
set(value: T): void {
|
||||
if (value !== this._value) {
|
||||
this._value = value;
|
||||
this.emit(this._value);
|
||||
|
@ -94,8 +100,10 @@ export class ObservableValue extends BaseObservableValue {
|
|||
}
|
||||
}
|
||||
|
||||
export class RetainedObservableValue extends ObservableValue {
|
||||
constructor(initialValue, freeCallback) {
|
||||
export class RetainedObservableValue<T> extends ObservableValue<T> {
|
||||
private _freeCallback: () => void;
|
||||
|
||||
constructor(initialValue: T, freeCallback: () => void) {
|
||||
super(initialValue);
|
||||
this._freeCallback = freeCallback;
|
||||
}
|
||||
|
@ -109,7 +117,7 @@ export class RetainedObservableValue extends ObservableValue {
|
|||
export function tests() {
|
||||
return {
|
||||
"set emits an update": assert => {
|
||||
const a = new ObservableValue();
|
||||
const a = new ObservableValue<number>(0);
|
||||
let fired = false;
|
||||
const subscription = a.subscribe(v => {
|
||||
fired = true;
|
||||
|
@ -140,7 +148,7 @@ export function tests() {
|
|||
assert.strictEqual(a.get(), 6);
|
||||
},
|
||||
"waitFor promise rejects when disposed": async assert => {
|
||||
const a = new ObservableValue();
|
||||
const a = new ObservableValue<number>(0);
|
||||
const handle = a.waitFor(() => false);
|
||||
Promise.resolve().then(() => {
|
||||
handle.dispose();
|
|
@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {BaseMappedList, runAdd, runUpdate, runRemove, runMove, runReset} from "./BaseMappedList.js";
|
||||
import {BaseMappedList, runAdd, runUpdate, runRemove, runMove, runReset} from "./BaseMappedList";
|
||||
|
||||
export class AsyncMappedList extends BaseMappedList {
|
||||
constructor(sourceList, mapper, updater, removeCallback) {
|
||||
|
|
|
@ -1,77 +0,0 @@
|
|||
/*
|
||||
Copyright 2020 Bruno Windels <bruno@windels.cloud>
|
||||
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
|
||||
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 {BaseObservableList} from "./BaseObservableList.js";
|
||||
import {findAndUpdateInArray} from "./common.js";
|
||||
|
||||
export class BaseMappedList extends BaseObservableList {
|
||||
constructor(sourceList, mapper, updater, removeCallback) {
|
||||
super();
|
||||
this._sourceList = sourceList;
|
||||
this._mapper = mapper;
|
||||
this._updater = updater;
|
||||
this._removeCallback = removeCallback;
|
||||
this._mappedValues = null;
|
||||
this._sourceUnsubscribe = null;
|
||||
}
|
||||
|
||||
findAndUpdate(predicate, updater) {
|
||||
return findAndUpdateInArray(predicate, this._mappedValues, this, updater);
|
||||
}
|
||||
|
||||
get length() {
|
||||
return this._mappedValues.length;
|
||||
}
|
||||
|
||||
[Symbol.iterator]() {
|
||||
return this._mappedValues.values();
|
||||
}
|
||||
}
|
||||
|
||||
export function runAdd(list, index, mappedValue) {
|
||||
list._mappedValues.splice(index, 0, mappedValue);
|
||||
list.emitAdd(index, mappedValue);
|
||||
}
|
||||
|
||||
export function runUpdate(list, index, value, params) {
|
||||
const mappedValue = list._mappedValues[index];
|
||||
if (list._updater) {
|
||||
list._updater(mappedValue, params, value);
|
||||
}
|
||||
list.emitUpdate(index, mappedValue, params);
|
||||
}
|
||||
|
||||
export function runRemove(list, index) {
|
||||
const mappedValue = list._mappedValues[index];
|
||||
list._mappedValues.splice(index, 1);
|
||||
if (list._removeCallback) {
|
||||
list._removeCallback(mappedValue);
|
||||
}
|
||||
list.emitRemove(index, mappedValue);
|
||||
}
|
||||
|
||||
export function runMove(list, fromIdx, toIdx) {
|
||||
const mappedValue = list._mappedValues[fromIdx];
|
||||
list._mappedValues.splice(fromIdx, 1);
|
||||
list._mappedValues.splice(toIdx, 0, mappedValue);
|
||||
list.emitMove(fromIdx, toIdx, mappedValue);
|
||||
}
|
||||
|
||||
export function runReset(list) {
|
||||
list._mappedValues = [];
|
||||
list.emitReset();
|
||||
}
|
85
src/observable/list/BaseMappedList.ts
Normal file
85
src/observable/list/BaseMappedList.ts
Normal file
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
Copyright 2020 Bruno Windels <bruno@windels.cloud>
|
||||
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
|
||||
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 {BaseObservableList} from "./BaseObservableList";
|
||||
import {findAndUpdateInArray} from "./common";
|
||||
|
||||
export type Mapper<F,T> = (value: F) => T
|
||||
export type Updater<F,T> = (mappedValue: T, params: any, value: F) => void;
|
||||
|
||||
export class BaseMappedList<F,T> extends BaseObservableList<T> {
|
||||
protected _sourceList: BaseObservableList<F>;
|
||||
protected _sourceUnsubscribe: (() => void) | null = null;
|
||||
_mapper: Mapper<F,T>;
|
||||
_updater: Updater<F,T>;
|
||||
_removeCallback?: (value: T) => void;
|
||||
_mappedValues: T[] | null = null;
|
||||
|
||||
constructor(sourceList: BaseObservableList<F>, mapper: Mapper<F,T>, updater: Updater<F,T>, removeCallback?: (value: T) => void) {
|
||||
super();
|
||||
this._sourceList = sourceList;
|
||||
this._mapper = mapper;
|
||||
this._updater = updater;
|
||||
this._removeCallback = removeCallback;
|
||||
}
|
||||
|
||||
findAndUpdate(predicate: (value: T) => boolean, updater: (value: T) => any | false) {
|
||||
return findAndUpdateInArray(predicate, this._mappedValues!, this, updater);
|
||||
}
|
||||
|
||||
get length() {
|
||||
return this._mappedValues!.length;
|
||||
}
|
||||
|
||||
[Symbol.iterator]() {
|
||||
return this._mappedValues!.values();
|
||||
}
|
||||
}
|
||||
|
||||
export function runAdd<F,T>(list: BaseMappedList<F,T>, index: number, mappedValue: T): void {
|
||||
list._mappedValues!.splice(index, 0, mappedValue);
|
||||
list.emitAdd(index, mappedValue);
|
||||
}
|
||||
|
||||
export function runUpdate<F,T>(list: BaseMappedList<F,T>, index: number, value: F, params: any): void {
|
||||
const mappedValue = list._mappedValues![index];
|
||||
if (list._updater) {
|
||||
list._updater(mappedValue, params, value);
|
||||
}
|
||||
list.emitUpdate(index, mappedValue, params);
|
||||
}
|
||||
|
||||
export function runRemove<F,T>(list: BaseMappedList<F,T>, index: number): void {
|
||||
const mappedValue = list._mappedValues![index];
|
||||
list._mappedValues!.splice(index, 1);
|
||||
if (list._removeCallback) {
|
||||
list._removeCallback(mappedValue);
|
||||
}
|
||||
list.emitRemove(index, mappedValue);
|
||||
}
|
||||
|
||||
export function runMove<F,T>(list: BaseMappedList<F,T>, fromIdx: number, toIdx: number): void {
|
||||
const mappedValue = list._mappedValues![fromIdx];
|
||||
list._mappedValues!.splice(fromIdx, 1);
|
||||
list._mappedValues!.splice(toIdx, 0, mappedValue);
|
||||
list.emitMove(fromIdx, toIdx, mappedValue);
|
||||
}
|
||||
|
||||
export function runReset<F,T>(list: BaseMappedList<F,T>): void {
|
||||
list._mappedValues = [];
|
||||
list.emitReset();
|
||||
}
|
|
@ -14,9 +14,17 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {BaseObservable} from "../BaseObservable.js";
|
||||
import {BaseObservable} from "../BaseObservable";
|
||||
|
||||
export class BaseObservableList extends BaseObservable {
|
||||
export interface IListObserver<T> {
|
||||
onReset(list: BaseObservableList<T>): void;
|
||||
onAdd(index: number, value:T, list: BaseObservableList<T>): void;
|
||||
onUpdate(index: number, value: T, params: any, list: BaseObservableList<T>): void;
|
||||
onRemove(index: number, value: T, list: BaseObservableList<T>): void
|
||||
onMove(from: number, to: number, value: T, list: BaseObservableList<T>): void
|
||||
}
|
||||
|
||||
export abstract class BaseObservableList<T> extends BaseObservable<IListObserver<T>> {
|
||||
emitReset() {
|
||||
for(let h of this._handlers) {
|
||||
h.onReset(this);
|
||||
|
@ -24,19 +32,19 @@ export class BaseObservableList extends BaseObservable {
|
|||
}
|
||||
// we need batch events, mostly on index based collection though?
|
||||
// maybe we should get started without?
|
||||
emitAdd(index, value) {
|
||||
emitAdd(index: number, value: T): void {
|
||||
for(let h of this._handlers) {
|
||||
h.onAdd(index, value, this);
|
||||
}
|
||||
}
|
||||
|
||||
emitUpdate(index, value, params) {
|
||||
emitUpdate(index: number, value: T, params: any): void {
|
||||
for(let h of this._handlers) {
|
||||
h.onUpdate(index, value, params, this);
|
||||
}
|
||||
}
|
||||
|
||||
emitRemove(index, value) {
|
||||
emitRemove(index: number, value: T): void {
|
||||
for(let h of this._handlers) {
|
||||
h.onRemove(index, value, this);
|
||||
}
|
||||
|
@ -44,17 +52,12 @@ export class BaseObservableList extends BaseObservable {
|
|||
|
||||
// toIdx assumes the item has already
|
||||
// been removed from its fromIdx
|
||||
emitMove(fromIdx, toIdx, value) {
|
||||
emitMove(fromIdx: number, toIdx: number, value: T): void {
|
||||
for(let h of this._handlers) {
|
||||
h.onMove(fromIdx, toIdx, value, this);
|
||||
}
|
||||
}
|
||||
|
||||
[Symbol.iterator]() {
|
||||
throw new Error("unimplemented");
|
||||
}
|
||||
|
||||
get length() {
|
||||
throw new Error("unimplemented");
|
||||
}
|
||||
abstract [Symbol.iterator](): IterableIterator<T>;
|
||||
abstract get length(): number;
|
||||
}
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {BaseObservableList} from "./BaseObservableList.js";
|
||||
import {BaseObservableList} from "./BaseObservableList";
|
||||
|
||||
export class ConcatList extends BaseObservableList {
|
||||
constructor(...sourceLists) {
|
||||
|
|
|
@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {BaseMappedList, runAdd, runUpdate, runRemove, runMove, runReset} from "./BaseMappedList.js";
|
||||
import {BaseMappedList, runAdd, runUpdate, runRemove, runMove, runReset} from "./BaseMappedList";
|
||||
|
||||
export class MappedList extends BaseMappedList {
|
||||
onSubscribeFirst() {
|
||||
|
@ -57,7 +57,7 @@ export class MappedList extends BaseMappedList {
|
|||
}
|
||||
|
||||
import {ObservableArray} from "./ObservableArray.js";
|
||||
import {BaseObservableList} from "./BaseObservableList.js";
|
||||
import {BaseObservableList} from "./BaseObservableList";
|
||||
|
||||
export async function tests() {
|
||||
class MockList extends BaseObservableList {
|
||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {BaseObservableList} from "./BaseObservableList.js";
|
||||
import {BaseObservableList} from "./BaseObservableList";
|
||||
|
||||
export class ObservableArray extends BaseObservableList {
|
||||
constructor(initialValues = []) {
|
||||
|
|
|
@ -14,9 +14,9 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {BaseObservableList} from "./BaseObservableList.js";
|
||||
import {BaseObservableList} from "./BaseObservableList";
|
||||
import {sortedIndex} from "../../utils/sortedIndex.js";
|
||||
import {findAndUpdateInArray} from "./common.js";
|
||||
import {findAndUpdateInArray} from "./common";
|
||||
|
||||
export class SortedArray extends BaseObservableList {
|
||||
constructor(comparator) {
|
||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {BaseObservableList} from "./BaseObservableList.js";
|
||||
import {BaseObservableList} from "./BaseObservableList";
|
||||
import {sortedIndex} from "../../utils/sortedIndex.js";
|
||||
|
||||
/*
|
||||
|
|
|
@ -14,9 +14,10 @@ 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 {BaseObservableList} from "./BaseObservableList";
|
||||
|
||||
/* inline update of item in collection backed by array, without replacing the preexising item */
|
||||
export function findAndUpdateInArray(predicate, array, observable, updater) {
|
||||
export function findAndUpdateInArray<T>(predicate: (value: T) => boolean, array: T[], observable: BaseObservableList<T>, updater: (value: T) => any | false) {
|
||||
const index = array.findIndex(predicate);
|
||||
if (index !== -1) {
|
||||
const value = array[index];
|
||||
|
@ -29,4 +30,4 @@ export function findAndUpdateInArray(predicate, array, observable, updater) {
|
|||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {BaseObservable} from "../BaseObservable.js";
|
||||
import {BaseObservable} from "../BaseObservable";
|
||||
|
||||
export class BaseObservableMap extends BaseObservable {
|
||||
emitReset() {
|
||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {BaseObservableValue} from "../../../observable/ObservableValue.js";
|
||||
import {BaseObservableValue} from "../../../observable/ObservableValue";
|
||||
|
||||
export class History extends BaseObservableValue {
|
||||
handleEvent(event) {
|
||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {BaseObservableValue} from "../../../observable/ObservableValue.js";
|
||||
import {BaseObservableValue} from "../../../observable/ObservableValue";
|
||||
|
||||
export class OnlineStatus extends BaseObservableValue {
|
||||
constructor() {
|
||||
|
|
|
@ -16,7 +16,8 @@ limitations under the License.
|
|||
|
||||
import {el} from "./html";
|
||||
import {mountView, insertAt} from "./utils";
|
||||
import {BaseObservableList as ObservableList} from "../../../../observable/list/BaseObservableList.js";
|
||||
import {SubscriptionHandle} from "../../../../observable/BaseObservable";
|
||||
import {BaseObservableList as ObservableList} from "../../../../observable/list/BaseObservableList";
|
||||
import {IView, IMountArgs} from "./types";
|
||||
|
||||
interface IOptions<T, V> {
|
||||
|
@ -27,8 +28,6 @@ interface IOptions<T, V> {
|
|||
parentProvidesUpdates?: boolean
|
||||
}
|
||||
|
||||
type SubscriptionHandle = () => undefined;
|
||||
|
||||
export class ListView<T, V extends IView> implements IView {
|
||||
|
||||
private _onItemClick?: (childView: V, evt: UIEvent) => void;
|
||||
|
@ -137,26 +136,34 @@ export class ListView<T, V extends IView> implements IView {
|
|||
this._root!.appendChild(fragment);
|
||||
}
|
||||
|
||||
protected onAdd(idx: number, value: T) {
|
||||
onReset() {
|
||||
for (const child of this._childInstances!) {
|
||||
child.root()!.remove();
|
||||
child.unmount();
|
||||
}
|
||||
this._childInstances!.length = 0;
|
||||
}
|
||||
|
||||
onAdd(idx: number, value: T) {
|
||||
const child = this._childCreator(value);
|
||||
this._childInstances!.splice(idx, 0, child);
|
||||
insertAt(this._root!, idx, mountView(child, this._mountArgs));
|
||||
}
|
||||
|
||||
protected onRemove(idx: number, value: T) {
|
||||
onRemove(idx: number, value: T) {
|
||||
const [child] = this._childInstances!.splice(idx, 1);
|
||||
child.root()!.remove();
|
||||
child.unmount();
|
||||
}
|
||||
|
||||
protected onMove(fromIdx: number, toIdx: number, value: T) {
|
||||
onMove(fromIdx: number, toIdx: number, value: T) {
|
||||
const [child] = this._childInstances!.splice(fromIdx, 1);
|
||||
this._childInstances!.splice(toIdx, 0, child);
|
||||
child.root()!.remove();
|
||||
insertAt(this._root!, toIdx, child.root()! as Element);
|
||||
}
|
||||
|
||||
protected onUpdate(i: number, value: T, params: any) {
|
||||
onUpdate(i: number, value: T, params: any) {
|
||||
if (this._childInstances) {
|
||||
const instance = this._childInstances![i];
|
||||
instance && instance.update(value, params);
|
||||
|
|
|
@ -26,7 +26,7 @@ import {MissingAttachmentView} from "./timeline/MissingAttachmentView.js";
|
|||
import {AnnouncementView} from "./timeline/AnnouncementView.js";
|
||||
import {RedactedView} from "./timeline/RedactedView.js";
|
||||
import {SimpleTile} from "../../../../../domain/session/room/timeline/tiles/SimpleTile.js";
|
||||
import {BaseObservableList as ObservableList} from "../../../../../observable/list/BaseObservableList.js";
|
||||
import {BaseObservableList as ObservableList} from "../../../../../observable/list/BaseObservableList";
|
||||
|
||||
//import {TimelineViewModel} from "../../../../../domain/session/room/timeline/TimelineViewModel.js";
|
||||
export interface TimelineViewModel extends IObservableValue {
|
||||
|
@ -211,7 +211,12 @@ class TilesListView extends ListView<SimpleTile, TileView> {
|
|||
this.onChanged = onChanged;
|
||||
}
|
||||
|
||||
protected onUpdate(index: number, value: SimpleTile, param: any) {
|
||||
override onReset() {
|
||||
super.onReset();
|
||||
this.onChanged();
|
||||
}
|
||||
|
||||
override onUpdate(index: number, value: SimpleTile, param: any) {
|
||||
if (param === "shape") {
|
||||
const ExpectedClass = viewClassForEntry(value);
|
||||
const child = this.getChildInstanceByIndex(index);
|
||||
|
@ -227,17 +232,17 @@ class TilesListView extends ListView<SimpleTile, TileView> {
|
|||
this.onChanged();
|
||||
}
|
||||
|
||||
protected onAdd(idx: number, value: SimpleTile) {
|
||||
override onAdd(idx: number, value: SimpleTile) {
|
||||
super.onAdd(idx, value);
|
||||
this.onChanged();
|
||||
}
|
||||
|
||||
protected onRemove(idx: number, value: SimpleTile) {
|
||||
override onRemove(idx: number, value: SimpleTile) {
|
||||
super.onRemove(idx, value);
|
||||
this.onChanged();
|
||||
}
|
||||
|
||||
protected onMove(fromIdx: number, toIdx: number, value: SimpleTile) {
|
||||
override onMove(fromIdx: number, toIdx: number, value: SimpleTile) {
|
||||
super.onMove(fromIdx, toIdx, value);
|
||||
this.onChanged();
|
||||
}
|
||||
|
|
58
src/sdk.ts
Normal file
58
src/sdk.ts
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
Copyright 2020 Bruno Windels <bruno@windels.cloud>
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
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 {RecordRequester, ReplayRequester} from "./matrix/net/request/replay.js";
|
||||
import {SessionContainer} from "./matrix/SessionContainer.js";
|
||||
import {RootViewModel} from "./domain/RootViewModel.js";
|
||||
import {createNavigation, createRouter} from "./domain/navigation/index.js";
|
||||
// Don't use a default export here, as we use multiple entries during legacy build,
|
||||
// which does not support default exports,
|
||||
// see https://github.com/rollup/plugins/tree/master/packages/multi-entry
|
||||
export async function main(platform) {
|
||||
try {
|
||||
// to replay:
|
||||
// const fetchLog = await (await fetch("/fetchlogs/constrainterror.json")).json();
|
||||
// const replay = new ReplayRequester(fetchLog, {delay: false});
|
||||
// const request = replay.request;
|
||||
|
||||
// to record:
|
||||
// const recorder = new RecordRequester(createFetchRequest(clock.createTimeout));
|
||||
// const request = recorder.request;
|
||||
// window.getBrawlFetchLog = () => recorder.log();
|
||||
const navigation = createNavigation();
|
||||
platform.setNavigation(navigation);
|
||||
const urlRouter = createRouter({navigation, history: platform.history});
|
||||
urlRouter.attach();
|
||||
const olmPromise = platform.loadOlm();
|
||||
const workerPromise = platform.loadOlmWorker();
|
||||
|
||||
const vm = new RootViewModel({
|
||||
createSessionContainer: () => {
|
||||
return new SessionContainer({platform, olmPromise, workerPromise});
|
||||
},
|
||||
platform,
|
||||
// the only public interface of the router is to create urls,
|
||||
// so we call it that in the view models
|
||||
urlCreator: urlRouter,
|
||||
navigation,
|
||||
});
|
||||
await vm.load();
|
||||
platform.createAndMountRootView(vm);
|
||||
} catch(err) {
|
||||
console.error(`${err.message}:\n${err.stack}`);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue