use error view (model) in call view (model)

This commit is contained in:
Bruno Windels 2023-01-12 14:37:28 +01:00
parent 64d6db556a
commit 4070d422cd
3 changed files with 65 additions and 16 deletions

View File

@ -29,6 +29,10 @@ export class ErrorViewModel extends ViewModel<Options> {
return this.getOption("error")?.message;
}
get error(): Error {
return this.getOption("error");
}
close() {
this.getOption("onClose")();
}

View File

@ -20,6 +20,7 @@ import {getStreamVideoTrack, getStreamAudioTrack} from "../../../matrix/calls/co
import {avatarInitials, getIdentifierColorNumber, getAvatarHttpUrl} from "../../avatar";
import {EventObservableValue} from "../../../observable/value/EventObservableValue";
import {ObservableValueMap} from "../../../observable/map/ObservableValueMap";
import { ErrorViewModel } from "../../ErrorViewModel";
import type {Room} from "../../../matrix/room/Room";
import type {GroupCall} from "../../../matrix/calls/group/GroupCall";
import type {Member} from "../../../matrix/calls/group/Member";
@ -28,14 +29,17 @@ import type {BaseObservableList} from "../../../observable/list/BaseObservableLi
import type {BaseObservableValue} from "../../../observable/value/BaseObservableValue";
import type {Stream} from "../../../platform/types/MediaDevices";
import type {MediaRepository} from "../../../matrix/net/MediaRepository";
import type { Session } from "../../../matrix/Session";
type Options = BaseOptions & {
call: GroupCall,
room: Room,
session: Session
};
export class CallViewModel extends ViewModel<Options> {
public readonly memberViewModels: BaseObservableList<IStreamViewModel>;
private _errorViewModel?: ErrorViewModel;
constructor(options: Options) {
super(options);
@ -88,8 +92,8 @@ export class CallViewModel extends ViewModel<Options> {
return this.call.id;
}
get error(): string | undefined {
return this.call.error?.message;
get errorViewModel(): ErrorViewModel | undefined {
return this._errorViewModel;
}
private get call(): GroupCall {
@ -97,12 +101,19 @@ export class CallViewModel extends ViewModel<Options> {
}
private onUpdate() {
if (this.call.error) {
this._reportError(this.call.error);
}
}
async hangup() {
try {
if (this.call.hasJoined) {
await this.call.leave();
}
} catch (err) {
this._reportError(err);
}
}
async toggleCamera() {
@ -125,7 +136,6 @@ export class CallViewModel extends ViewModel<Options> {
// unmute but no track?
if (muteSettings.microphone && !getStreamAudioTrack(localMedia.userMedia)) {
const stream = await this.platform.mediaDevices.getMediaTracks(true, !muteSettings.camera);
console.log("got tracks", Array.from(stream.getTracks()).map((t: MediaStreamTrack) => { return {kind: t.kind, id: t.id};}))
await this.call.setMedia(localMedia.withUserMedia(stream));
} else {
await this.call.setMuted(muteSettings.toggleMicrophone());
@ -133,6 +143,21 @@ export class CallViewModel extends ViewModel<Options> {
this.emitChange();
}
}
private _reportError(error: Error) {
if (this._errorViewModel?.error === error) {
return;
}
this.disposeTracked(this._errorViewModel);
this._errorViewModel = new ErrorViewModel(this.childOptions({
error,
onClose: () => {
this._errorViewModel = this.disposeTracked(this._errorViewModel);
this.emitChange("errorViewModel");
}
}));
this.emitChange("errorViewModel");
}
}
class OwnMemberViewModel extends ViewModel<Options> implements IStreamViewModel {
@ -151,7 +176,7 @@ class OwnMemberViewModel extends ViewModel<Options> implements IStreamViewModel
}));
}
get error(): string | undefined {
get errorViewModel(): ErrorViewModel | undefined {
return undefined;
}
@ -207,22 +232,25 @@ class OwnMemberViewModel extends ViewModel<Options> implements IStreamViewModel
type MemberOptions = BaseOptions & {
member: Member,
mediaRepository: MediaRepository
mediaRepository: MediaRepository,
session: Session
};
export class CallMemberViewModel extends ViewModel<MemberOptions> implements IStreamViewModel {
private _errorViewModel?: ErrorViewModel;
get stream(): Stream | undefined {
return this.member.remoteMedia?.userMedia;
}
get error(): string | undefined {
return this.member.error?.message;
}
private get member(): Member {
return this.getOption("member");
}
get errorViewModel(): ErrorViewModel | undefined {
return this._errorViewModel;
}
get isCameraMuted(): boolean {
return this.member.remoteMuteSettings?.camera ?? true;
}
@ -250,7 +278,23 @@ export class CallMemberViewModel extends ViewModel<MemberOptions> implements ISt
}
onUpdate() {
this.mapMemberSyncErrorIfNeeded();
}
private mapMemberSyncErrorIfNeeded() {
if (this.member.error && (!this._errorViewModel || this._errorViewModel.error !== this.member.error)) {
this.disposeTracked(this._errorViewModel);
this._errorViewModel = this.track(new ErrorViewModel(this.childOptions({
error: this.member.error,
onClose: () => {
this._errorViewModel = this.disposeTracked(this._errorViewModel);
this.emitChange("errorViewModel");
},
})));
this.emitChange("errorViewModel");
}
}
compare(other: OwnMemberViewModel | CallMemberViewModel): number {
if (other instanceof OwnMemberViewModel) {
return -other.compare(this);
@ -268,5 +312,5 @@ export interface IStreamViewModel extends AvatarSource, ViewModel {
get stream(): Stream | undefined;
get isCameraMuted(): boolean;
get isMicrophoneMuted(): boolean;
get error(): string | undefined;
get errorViewModel(): ErrorViewModel | undefined;
}

View File

@ -20,6 +20,7 @@ import {ListView} from "../../general/ListView";
import {classNames} from "../../general/html";
import {Stream} from "../../../../types/MediaDevices";
import type {CallViewModel, CallMemberViewModel, IStreamViewModel} from "../../../../../domain/session/room/CallViewModel";
import { ErrorView } from "../../general/ErrorView";
export class CallView extends TemplateView<CallViewModel> {
private resizeObserver?: ResizeObserver;
@ -44,8 +45,8 @@ export class CallView extends TemplateView<CallViewModel> {
}, onClick: disableTargetCallback(() => vm.toggleCamera())}),
t.button({className: "CallView_hangup", onClick: disableTargetCallback(() => vm.hangup())}),
]),
t.if(vm => !!vm.error, t => {
return t.div({className: "CallView_error"}, vm => vm.error);
t.if(vm => !!vm.errorViewModel, t => {
return t.div({className: "CallView_error"}, t.view(new ErrorView(vm.errorViewModel!)));
})
]);
}
@ -116,8 +117,8 @@ class StreamView extends TemplateView<IStreamViewModel> {
cameraMuted: vm => vm.isCameraMuted,
}
}),
t.if(vm => !!vm.error, t => {
return t.div({className: "StreamView_error"}, vm => vm.error);
t.if(vm => !!vm.errorViewModel, t => {
return t.div({className: "StreamView_error"}, t.view(new ErrorView(vm.errorViewModel!)));
})
]);
}