From e760b8e5567425d016e88fe64ebf4b04aa66871a Mon Sep 17 00:00:00 2001 From: Bruno Windels <274386+bwindels@users.noreply.github.com> Date: Thu, 17 Mar 2022 13:04:14 +0100 Subject: [PATCH] basic view model setup --- src/domain/session/SessionViewModel.js | 2 +- src/domain/session/room/CallViewModel.ts | 34 +++++++++++++++++++ src/domain/session/room/RoomViewModel.js | 29 +++++++++++++++- src/matrix/calls/group/GroupCall.ts | 6 +++- ...bservable.ts => PickMapObservableValue.ts} | 12 +++---- src/platform/web/ui/session/room/RoomView.js | 4 +++ 6 files changed, 78 insertions(+), 9 deletions(-) create mode 100644 src/domain/session/room/CallViewModel.ts rename src/observable/value/{PickMapObservable.ts => PickMapObservableValue.ts} (87%) diff --git a/src/domain/session/SessionViewModel.js b/src/domain/session/SessionViewModel.js index a67df3a7..0c9dddaa 100644 --- a/src/domain/session/SessionViewModel.js +++ b/src/domain/session/SessionViewModel.js @@ -174,7 +174,7 @@ export class SessionViewModel extends ViewModel { _createRoomViewModelInstance(roomId) { const room = this._client.session.rooms.get(roomId); if (room) { - const roomVM = new RoomViewModel(this.childOptions({room})); + const roomVM = new RoomViewModel(this.childOptions({room, session: this._client.session})); roomVM.load(); return roomVM; } diff --git a/src/domain/session/room/CallViewModel.ts b/src/domain/session/room/CallViewModel.ts new file mode 100644 index 00000000..52f04d32 --- /dev/null +++ b/src/domain/session/room/CallViewModel.ts @@ -0,0 +1,34 @@ +/* +Copyright 2020 Bruno Windels +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 {ViewModel} from "../../ViewModel"; +import type {GroupCall} from "../../../matrix/calls/group/GroupCall"; + +export class CallViewModel extends ViewModel { + + private call: GroupCall; + + constructor(options) { + super(options); + const {call} = options; + this.call = call; + } + + get name(): string { + return this.call.name; + } +} diff --git a/src/domain/session/room/RoomViewModel.js b/src/domain/session/room/RoomViewModel.js index 71060728..4540fdb1 100644 --- a/src/domain/session/room/RoomViewModel.js +++ b/src/domain/session/room/RoomViewModel.js @@ -17,15 +17,18 @@ limitations under the License. import {TimelineViewModel} from "./timeline/TimelineViewModel.js"; import {ComposerViewModel} from "./ComposerViewModel.js" +import {CallViewModel} from "./CallViewModel" +import {PickMapObservableValue} from "../../../observable/value/PickMapObservableValue"; import {avatarInitials, getIdentifierColorNumber, getAvatarHttpUrl} from "../../avatar"; import {tilesCreator} from "./timeline/tilesCreator.js"; import {ViewModel} from "../../ViewModel"; import {imageToInfo} from "../common.js"; +import {LocalMedia} from "../../../matrix/calls/LocalMedia"; export class RoomViewModel extends ViewModel { constructor(options) { super(options); - const {room} = options; + const {room, session} = options; this._room = room; this._timelineVM = null; this._tilesCreator = null; @@ -40,6 +43,19 @@ export class RoomViewModel extends ViewModel { } this._clearUnreadTimout = null; this._closeUrl = this.urlCreator.urlUntilSegment("session"); + // pick call for this room with lowest key + this._callObservable = new PickMapObservableValue(session.callHandler.calls.filterValues(c => c.roomId === this.roomId)); + this._callViewModel = undefined; + this.track(this._callObservable.subscribe(call => { + this._callViewModel = this.disposeTracked(this._callViewModel); + if (call) { + this._callViewModel = new CallViewModel(this.childOptions({call})); + } + this.emitChange("callViewModel"); + })); + if (this._callObservable.get()) { + this._callViewModel = new CallViewModel(this.childOptions({call: this._callObservable.get()})); + } } async load() { @@ -308,6 +324,10 @@ export class RoomViewModel extends ViewModel { return this._composerVM; } + get callViewModel() { + return this._callViewModel; + } + openDetailsPanel() { let path = this.navigation.path.until("room"); path = path.with(this.navigation.segment("right-panel", true)); @@ -320,6 +340,13 @@ export class RoomViewModel extends ViewModel { this._composerVM.setReplyingTo(entry); } } + + async startCall() { + const mediaTracks = await this.platform.mediaDevices.getMediaTracks(true, true); + const localMedia = LocalMedia.fromTracks(mediaTracks); + // this will set the callViewModel above as a call will be added to callHandler.calls + await this.session.callHandler.createCall(this.roomId, localMedia, "A call " + Math.round(this.platform.random() * 100)); + } } function videoToInfo(video) { diff --git a/src/matrix/calls/group/GroupCall.ts b/src/matrix/calls/group/GroupCall.ts index 6c26c995..8ea4528a 100644 --- a/src/matrix/calls/group/GroupCall.ts +++ b/src/matrix/calls/group/GroupCall.ts @@ -56,7 +56,7 @@ export class GroupCall { constructor( id: string | undefined, private callContent: Record | undefined, - private readonly roomId: string, + public readonly roomId: string, private readonly options: Options ) { this.id = id ?? makeId("conf-"); @@ -77,6 +77,10 @@ export class GroupCall { return this.callContent?.["m.terminated"] === true; } + get name(): string { + return this.callContent?.["m.name"]; + } + async join(localMedia: LocalMedia) { if (this._state !== GroupCallState.Created) { return; diff --git a/src/observable/value/PickMapObservable.ts b/src/observable/value/PickMapObservableValue.ts similarity index 87% rename from src/observable/value/PickMapObservable.ts rename to src/observable/value/PickMapObservableValue.ts index 835cdf90..a20ec04e 100644 --- a/src/observable/value/PickMapObservable.ts +++ b/src/observable/value/PickMapObservableValue.ts @@ -22,7 +22,7 @@ function pickLowestKey(currentKey: K, newKey: K): boolean { return newKey < currentKey; } -export class PickMapObservable implements IMapObserver extends BaseObservableValue { +export class PickMapObservableValue extends BaseObservableValue implements IMapObserver{ private key?: K; private mapSubscription?: SubscriptionHandle; @@ -34,7 +34,7 @@ export class PickMapObservable implements IMapObserver extends BaseO super(); } - private trySetKey(newKey: K): boolean { + private updateKey(newKey: K): boolean { if (this.key === undefined || this.pickKey(this.key, newKey)) { this.key = newKey; return true; @@ -48,7 +48,7 @@ export class PickMapObservable implements IMapObserver extends BaseO } onAdd(key: K, value:V): void { - if (this.trySetKey(key)) { + if (this.updateKey(key)) { this.emit(this.get()); } } @@ -60,7 +60,7 @@ export class PickMapObservable implements IMapObserver extends BaseO this.key = undefined; let changed = false; for (const [key] of this.map) { - changed = this.trySetKey(key) || changed; + changed = this.updateKey(key) || changed; } if (changed) { this.emit(this.get()); @@ -71,12 +71,12 @@ export class PickMapObservable implements IMapObserver extends BaseO onSubscribeFirst(): void { this.mapSubscription = this.map.subscribe(this); for (const [key] of this.map) { - this.trySetKey(key); + this.updateKey(key); } } onUnsubscribeLast(): void { - this.mapSubscription(); + this.mapSubscription!(); this.key = undefined; } diff --git a/src/platform/web/ui/session/room/RoomView.js b/src/platform/web/ui/session/room/RoomView.js index c172766a..541cc4d8 100644 --- a/src/platform/web/ui/session/room/RoomView.js +++ b/src/platform/web/ui/session/room/RoomView.js @@ -52,6 +52,9 @@ export class RoomView extends TemplateView { ]), t.div({className: "RoomView_body"}, [ t.div({className: "RoomView_error"}, vm => vm.error), + t.map(vm => vm.callViewModel, (callViewModel, t) => { + return t.p(["A call is in progress", callViewModel => callViewModel.name]) + }), t.mapView(vm => vm.timelineViewModel, timelineViewModel => { return timelineViewModel ? new TimelineView(timelineViewModel) : @@ -69,6 +72,7 @@ export class RoomView extends TemplateView { const vm = this.value; const options = []; options.push(Menu.option(vm.i18n`Room details`, () => vm.openDetailsPanel())) + options.push(Menu.option(vm.i18n`Start call`, () => vm.startCall())) if (vm.canLeave) { options.push(Menu.option(vm.i18n`Leave room`, () => this._confirmToLeaveRoom()).setDestructive()); }