WIP on UI

This commit is contained in:
Bruno Windels 2022-03-17 13:07:55 +01:00
parent e760b8e556
commit 4be82cd472
4 changed files with 81 additions and 13 deletions

View file

@ -1,6 +1,5 @@
/* /*
Copyright 2020 Bruno Windels <bruno@windels.cloud> Copyright 2022 The Matrix.org Foundation C.I.C.
Copyright 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -15,20 +14,40 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {ViewModel} from "../../ViewModel"; import {ViewModel, Options as BaseOptions} from "../../ViewModel";
import type {GroupCall} from "../../../matrix/calls/group/GroupCall"; import type {GroupCall} from "../../../matrix/calls/group/GroupCall";
import type {Member} from "../../../matrix/calls/group/Member";
import type {BaseObservableList} from "../../../observable/list/BaseObservableList";
import type {Track} from "../../../platform/types/MediaDevices";
export class CallViewModel extends ViewModel { type Options = BaseOptions & {call: GroupCall};
private call: GroupCall; export class CallViewModel extends ViewModel<Options> {
public readonly memberViewModels: BaseObservableList<CallMemberViewModel>;
constructor(options) { constructor(options: Options) {
super(options); super(options);
const {call} = options; this.memberViewModels = this.getOption("call").members
this.call = call; .mapValues(member => new CallMemberViewModel(this.childOptions({member})))
.sortValues((a, b) => {
});
} }
get name(): string { get name(): string {
return this.call.name; return this.getOption("call").name;
}
get localTracks(): Track[] {
return this.getOption("call").localMedia?.tracks ?? [];
}
}
type MemberOptions = BaseOptions & {member: Member};
class CallMemberViewModel extends ViewModel<MemberOptions> {
get tracks(): Track[] {
return this.getOption("member").remoteTracks;
} }
} }

View file

@ -43,6 +43,10 @@ export class RoomViewModel extends ViewModel {
} }
this._clearUnreadTimout = null; this._clearUnreadTimout = null;
this._closeUrl = this.urlCreator.urlUntilSegment("session"); this._closeUrl = this.urlCreator.urlUntilSegment("session");
this._setupCallViewModel();
}
_setupCallViewModel() {
// pick call for this room with lowest key // pick call for this room with lowest key
this._callObservable = new PickMapObservableValue(session.callHandler.calls.filterValues(c => c.roomId === this.roomId)); this._callObservable = new PickMapObservableValue(session.callHandler.calls.filterValues(c => c.roomId === this.roomId));
this._callViewModel = undefined; this._callViewModel = undefined;
@ -53,8 +57,9 @@ export class RoomViewModel extends ViewModel {
} }
this.emitChange("callViewModel"); this.emitChange("callViewModel");
})); }));
if (this._callObservable.get()) { const call = this._callObservable.get();
this._callViewModel = new CallViewModel(this.childOptions({call: this._callObservable.get()})); if (call) {
this._callViewModel = new CallViewModel(this.childOptions({call}));
} }
} }

View file

@ -0,0 +1,43 @@
/*
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 {TemplateView, TemplateBuilder} from "../../general/TemplateView";
import {Track, TrackType} from "../../../../types/MediaDevices";
import type {TrackWrapper} from "../../../dom/MediaDevices";
import type {CallViewModel} from "../../../../../domain/session/room/CallViewModel";
function bindVideoTracks<T>(t: TemplateBuilder<T>, video: HTMLVideoElement, propSelector: (vm: T) => Track[]) {
t.mapSideEffect(propSelector, tracks => {
if (tracks.length) {
video.srcObject = (tracks[0] as TrackWrapper).stream;
}
});
}
export class CallView extends TemplateView<CallViewModel> {
render(t: TemplateBuilder<CallViewModel>, vm: CallViewModel): HTMLElement {
return t.div({class: "CallView"}, [
t.div({class: "CallView_me"}, bindVideoTracks(t, t.video(), vm => vm.localTracks)),
t.view(new ListView(vm.memberViewModels, vm => new MemberView(vm)))
]);
}
}
class MemberView extends TemplateView<CallMemberViewModel> {
render(t, vm) {
return bindVideoTracks(t, t.video(), vm => vm.tracks);
}
}

View file

@ -23,6 +23,7 @@ import {TimelineLoadingView} from "./TimelineLoadingView.js";
import {MessageComposer} from "./MessageComposer.js"; import {MessageComposer} from "./MessageComposer.js";
import {RoomArchivedView} from "./RoomArchivedView.js"; import {RoomArchivedView} from "./RoomArchivedView.js";
import {AvatarView} from "../../AvatarView.js"; import {AvatarView} from "../../AvatarView.js";
import {CallView} from "./CallView";
export class RoomView extends TemplateView { export class RoomView extends TemplateView {
constructor(options) { constructor(options) {
@ -52,8 +53,8 @@ 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) => { t.mapView(vm => vm.callViewModel, callViewModel => {
return t.p(["A call is in progress", callViewModel => callViewModel.name]) return new CallView(callViewModel);
}), }),
t.mapView(vm => vm.timelineViewModel, timelineViewModel => { t.mapView(vm => vm.timelineViewModel, timelineViewModel => {
return timelineViewModel ? return timelineViewModel ?