forked from mystiq/hydrogen-web
basic view model setup
This commit is contained in:
parent
e482e3aeef
commit
e760b8e556
6 changed files with 78 additions and 9 deletions
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
34
src/domain/session/room/CallViewModel.ts
Normal file
34
src/domain/session/room/CallViewModel.ts
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue