From e140a4ba64f54a4f897aa8ff8367c9047bffb091 Mon Sep 17 00:00:00 2001 From: Bruno Windels <274386+bwindels@users.noreply.github.com> Date: Mon, 23 Jan 2023 15:06:06 +0100 Subject: [PATCH 1/4] element call puts string in terminated, not a boolean --- src/matrix/calls/group/GroupCall.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/matrix/calls/group/GroupCall.ts b/src/matrix/calls/group/GroupCall.ts index 0b8c7db5..d323f6b2 100644 --- a/src/matrix/calls/group/GroupCall.ts +++ b/src/matrix/calls/group/GroupCall.ts @@ -140,7 +140,7 @@ export class GroupCall extends EventEmitter<{change: never}> { get members(): BaseObservableMap { return this._members; } get isTerminated(): boolean { - return this.callContent?.["m.terminated"] === true; + return !!this.callContent?.["m.terminated"]; } get isRinging(): boolean { From 043ad988669842d8ea71d822714af0263fcb9a62 Mon Sep 17 00:00:00 2001 From: Bruno Windels <274386+bwindels@users.noreply.github.com> Date: Mon, 23 Jan 2023 15:06:24 +0100 Subject: [PATCH 2/4] element call also terminates prompt calls, so do so too --- src/matrix/calls/group/GroupCall.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/matrix/calls/group/GroupCall.ts b/src/matrix/calls/group/GroupCall.ts index d323f6b2..6407584a 100644 --- a/src/matrix/calls/group/GroupCall.ts +++ b/src/matrix/calls/group/GroupCall.ts @@ -286,7 +286,7 @@ export class GroupCall extends EventEmitter<{change: never}> { const request = this.options.hsApi.sendState(this.roomId, EventType.GroupCallMember, this.options.ownUserId, memberContent, {log}); await request.response(); // our own user isn't included in members, so not in the count - if (this.intent === CallIntent.Ring && this._members.size === 0) { + if ((this.intent === CallIntent.Ring || this.intent === CallIntent.Prompt) && this._members.size === 0) { await this.terminate(log); } } else { From 7f422882dd0ed52e3df418318ed42f999fe252dc Mon Sep 17 00:00:00 2001 From: Bruno Windels <274386+bwindels@users.noreply.github.com> Date: Mon, 23 Jan 2023 15:08:38 +0100 Subject: [PATCH 3/4] make call error view clickable above video elements --- src/platform/web/ui/css/themes/element/call.css | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/platform/web/ui/css/themes/element/call.css b/src/platform/web/ui/css/themes/element/call.css index c0f1550e..3efe3c56 100644 --- a/src/platform/web/ui/css/themes/element/call.css +++ b/src/platform/web/ui/css/themes/element/call.css @@ -25,11 +25,13 @@ limitations under the License. } .CallView_error { - color: red; - font-weight: bold; align-self: start; justify-self: center; margin: 16px; + /** Chrome (v100) requires this to make the buttons clickable + * where they overlap with the video element, even though + * the buttons come later in the DOM. */ + z-index: 1; } .CallView_members { From e1fc2b46c491cd963de419a15041d2a29fb840e0 Mon Sep 17 00:00:00 2001 From: Bruno Windels <274386+bwindels@users.noreply.github.com> Date: Thu, 26 Jan 2023 11:40:40 +0100 Subject: [PATCH 4/4] add observeSize operator on ObservableMap --- src/observable/map/BaseObservableMap.ts | 27 ++++--- .../value/MapSizeObservableValue.ts | 71 +++++++++++++++++++ src/observable/value/index.ts | 1 + 3 files changed, 88 insertions(+), 11 deletions(-) create mode 100644 src/observable/value/MapSizeObservableValue.ts diff --git a/src/observable/map/BaseObservableMap.ts b/src/observable/map/BaseObservableMap.ts index f81a94f8..c82e531c 100644 --- a/src/observable/map/BaseObservableMap.ts +++ b/src/observable/map/BaseObservableMap.ts @@ -18,6 +18,7 @@ import {BaseObservable} from "../BaseObservable"; import {JoinedMap} from "./index"; import {MappedMap} from "./index"; import {FilteredMap} from "./index"; +import {BaseObservableValue, MapSizeObservableValue} from "../value/index"; import {SortedMapList} from "../list/SortedMapList.js"; @@ -66,19 +67,23 @@ export abstract class BaseObservableMap extends BaseObservable>(...otherMaps: Array): JoinedMap { return new JoinedMap([this as BaseObservableMap].concat(otherMaps)); - } + } - mapValues(mapper: Mapper, updater?: Updater): MappedMap { - return new MappedMap(this, mapper, updater); - } + mapValues(mapper: Mapper, updater?: Updater): MappedMap { + return new MappedMap(this, mapper, updater); + } - sortValues(comparator: Comparator): SortedMapList { - return new SortedMapList(this, comparator); - } + sortValues(comparator: Comparator): SortedMapList { + return new SortedMapList(this, comparator); + } - filterValues(filter: Filter): FilteredMap { - return new FilteredMap(this, filter); - } + filterValues(filter: Filter): FilteredMap { + return new FilteredMap(this, filter); + } + + observeSize(): BaseObservableValue { + return new MapSizeObservableValue(this); + } abstract [Symbol.iterator](): Iterator<[K, V]>; abstract get size(): number; @@ -94,4 +99,4 @@ export type Updater = (params: any, mappedValue?: MappedV, value?: V export type Comparator = (a: V, b: V) => number; -export type Filter = (v: V, k: K) => boolean; \ No newline at end of file +export type Filter = (v: V, k: K) => boolean; diff --git a/src/observable/value/MapSizeObservableValue.ts b/src/observable/value/MapSizeObservableValue.ts new file mode 100644 index 00000000..13bac604 --- /dev/null +++ b/src/observable/value/MapSizeObservableValue.ts @@ -0,0 +1,71 @@ +/* +Copyright 2022 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 {BaseObservableValue} from "./index"; +import {BaseObservableMap} from "../map/index"; +import type {SubscriptionHandle} from "../BaseObservable"; + +export class MapSizeObservableValue extends BaseObservableValue { + private subscription?: SubscriptionHandle; + + constructor(private readonly map: BaseObservableMap) + { + super(); + } + + onSubscribeFirst(): void { + this.subscription = this.map.subscribe({ + onAdd: (key: K, value: V) => { + this.emit(this.get()); + }, + onRemove: (key: K, value: V) => { + this.emit(this.get()); + }, + onUpdate: (key: K, value: V) => {}, + onReset: () => { + this.emit(this.get()); + }, + }); + } + + onUnsubscribeLast(): void { + this.subscription = this.subscription?.(); + } + + get(): number { + return this.map.size; + } +} + +import {ObservableMap} from "../map/index"; + +export function tests() { + return { + "emits update on add and remove": assert => { + const map = new ObservableMap(); + const size = new MapSizeObservableValue(map); + const updates: number[] = []; + size.subscribe(size => { + updates.push(size); + }); + map.add("hello", 1); + map.add("world", 2); + map.remove("world"); + map.remove("hello"); + assert.deepEqual(updates, [1, 2, 1, 0]); + } + }; +} diff --git a/src/observable/value/index.ts b/src/observable/value/index.ts index ceb53628..1b0f1347 100644 --- a/src/observable/value/index.ts +++ b/src/observable/value/index.ts @@ -12,4 +12,5 @@ export {EventObservableValue} from './EventObservableValue'; export {FlatMapObservableValue} from './FlatMapObservableValue'; export {PickMapObservableValue} from './PickMapObservableValue'; export {RetainedObservableValue} from './RetainedObservableValue'; +export {MapSizeObservableValue} from './MapSizeObservableValue'; export {ObservableValue} from './ObservableValue';