basic view model setup

This commit is contained in:
Bruno Windels 2022-03-17 13:04:14 +01:00
parent e482e3aeef
commit e760b8e556
6 changed files with 78 additions and 9 deletions

View file

@ -174,7 +174,7 @@ export class SessionViewModel extends ViewModel {
_createRoomViewModelInstance(roomId) { _createRoomViewModelInstance(roomId) {
const room = this._client.session.rooms.get(roomId); const room = this._client.session.rooms.get(roomId);
if (room) { if (room) {
const roomVM = new RoomViewModel(this.childOptions({room})); const roomVM = new RoomViewModel(this.childOptions({room, session: this._client.session}));
roomVM.load(); roomVM.load();
return roomVM; return roomVM;
} }

View file

@ -0,0 +1,34 @@
/*
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 {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;
}
}

View file

@ -17,15 +17,18 @@ limitations under the License.
import {TimelineViewModel} from "./timeline/TimelineViewModel.js"; import {TimelineViewModel} from "./timeline/TimelineViewModel.js";
import {ComposerViewModel} from "./ComposerViewModel.js" import {ComposerViewModel} from "./ComposerViewModel.js"
import {CallViewModel} from "./CallViewModel"
import {PickMapObservableValue} from "../../../observable/value/PickMapObservableValue";
import {avatarInitials, getIdentifierColorNumber, getAvatarHttpUrl} from "../../avatar"; import {avatarInitials, getIdentifierColorNumber, getAvatarHttpUrl} from "../../avatar";
import {tilesCreator} from "./timeline/tilesCreator.js"; import {tilesCreator} from "./timeline/tilesCreator.js";
import {ViewModel} from "../../ViewModel"; import {ViewModel} from "../../ViewModel";
import {imageToInfo} from "../common.js"; import {imageToInfo} from "../common.js";
import {LocalMedia} from "../../../matrix/calls/LocalMedia";
export class RoomViewModel extends ViewModel { export class RoomViewModel extends ViewModel {
constructor(options) { constructor(options) {
super(options); super(options);
const {room} = options; const {room, session} = options;
this._room = room; this._room = room;
this._timelineVM = null; this._timelineVM = null;
this._tilesCreator = null; this._tilesCreator = null;
@ -40,6 +43,19 @@ export class RoomViewModel extends ViewModel {
} }
this._clearUnreadTimout = null; this._clearUnreadTimout = null;
this._closeUrl = this.urlCreator.urlUntilSegment("session"); 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() { async load() {
@ -308,6 +324,10 @@ export class RoomViewModel extends ViewModel {
return this._composerVM; return this._composerVM;
} }
get callViewModel() {
return this._callViewModel;
}
openDetailsPanel() { openDetailsPanel() {
let path = this.navigation.path.until("room"); let path = this.navigation.path.until("room");
path = path.with(this.navigation.segment("right-panel", true)); path = path.with(this.navigation.segment("right-panel", true));
@ -320,6 +340,13 @@ export class RoomViewModel extends ViewModel {
this._composerVM.setReplyingTo(entry); 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) { function videoToInfo(video) {

View file

@ -56,7 +56,7 @@ export class GroupCall {
constructor( constructor(
id: string | undefined, id: string | undefined,
private callContent: Record<string, any> | undefined, private callContent: Record<string, any> | undefined,
private readonly roomId: string, public readonly roomId: string,
private readonly options: Options private readonly options: Options
) { ) {
this.id = id ?? makeId("conf-"); this.id = id ?? makeId("conf-");
@ -77,6 +77,10 @@ export class GroupCall {
return this.callContent?.["m.terminated"] === true; return this.callContent?.["m.terminated"] === true;
} }
get name(): string {
return this.callContent?.["m.name"];
}
async join(localMedia: LocalMedia) { async join(localMedia: LocalMedia) {
if (this._state !== GroupCallState.Created) { if (this._state !== GroupCallState.Created) {
return; return;

View file

@ -22,7 +22,7 @@ function pickLowestKey<K>(currentKey: K, newKey: K): boolean {
return newKey < currentKey; return newKey < currentKey;
} }
export class PickMapObservable<K, V> implements IMapObserver<K, V> extends BaseObservableValue<V | undefined> { export class PickMapObservableValue<K, V> extends BaseObservableValue<V | undefined> implements IMapObserver<K, V>{
private key?: K; private key?: K;
private mapSubscription?: SubscriptionHandle; private mapSubscription?: SubscriptionHandle;
@ -34,7 +34,7 @@ export class PickMapObservable<K, V> implements IMapObserver<K, V> extends BaseO
super(); super();
} }
private trySetKey(newKey: K): boolean { private updateKey(newKey: K): boolean {
if (this.key === undefined || this.pickKey(this.key, newKey)) { if (this.key === undefined || this.pickKey(this.key, newKey)) {
this.key = newKey; this.key = newKey;
return true; return true;
@ -48,7 +48,7 @@ export class PickMapObservable<K, V> implements IMapObserver<K, V> extends BaseO
} }
onAdd(key: K, value:V): void { onAdd(key: K, value:V): void {
if (this.trySetKey(key)) { if (this.updateKey(key)) {
this.emit(this.get()); this.emit(this.get());
} }
} }
@ -60,7 +60,7 @@ export class PickMapObservable<K, V> implements IMapObserver<K, V> extends BaseO
this.key = undefined; this.key = undefined;
let changed = false; let changed = false;
for (const [key] of this.map) { for (const [key] of this.map) {
changed = this.trySetKey(key) || changed; changed = this.updateKey(key) || changed;
} }
if (changed) { if (changed) {
this.emit(this.get()); this.emit(this.get());
@ -71,12 +71,12 @@ export class PickMapObservable<K, V> implements IMapObserver<K, V> extends BaseO
onSubscribeFirst(): void { onSubscribeFirst(): void {
this.mapSubscription = this.map.subscribe(this); this.mapSubscription = this.map.subscribe(this);
for (const [key] of this.map) { for (const [key] of this.map) {
this.trySetKey(key); this.updateKey(key);
} }
} }
onUnsubscribeLast(): void { onUnsubscribeLast(): void {
this.mapSubscription(); this.mapSubscription!();
this.key = undefined; this.key = undefined;
} }

View file

@ -52,6 +52,9 @@ export class RoomView extends TemplateView {
]), ]),
t.div({className: "RoomView_body"}, [ t.div({className: "RoomView_body"}, [
t.div({className: "RoomView_error"}, vm => vm.error), 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 => { t.mapView(vm => vm.timelineViewModel, timelineViewModel => {
return timelineViewModel ? return timelineViewModel ?
new TimelineView(timelineViewModel) : new TimelineView(timelineViewModel) :
@ -69,6 +72,7 @@ export class RoomView extends TemplateView {
const vm = this.value; const vm = this.value;
const options = []; const options = [];
options.push(Menu.option(vm.i18n`Room details`, () => vm.openDetailsPanel())) options.push(Menu.option(vm.i18n`Room details`, () => vm.openDetailsPanel()))
options.push(Menu.option(vm.i18n`Start call`, () => vm.startCall()))
if (vm.canLeave) { if (vm.canLeave) {
options.push(Menu.option(vm.i18n`Leave room`, () => this._confirmToLeaveRoom()).setDestructive()); options.push(Menu.option(vm.i18n`Leave room`, () => this._confirmToLeaveRoom()).setDestructive());
} }