mirror of
https://github.com/vector-im/hydrogen-web.git
synced 2025-01-22 10:11:39 +01:00
start logging in view model and pass it on to model methods (calls+room)
This commit is contained in:
parent
0dbb7d4e50
commit
e33209b747
@ -102,9 +102,9 @@ export class CallViewModel extends ErrorReportViewModel<Options> {
|
||||
}
|
||||
|
||||
async hangup() {
|
||||
this.logAndCatch("Call.hangup", async log => {
|
||||
this.logAndCatch("CallViewModel.hangup", async log => {
|
||||
if (this.call.hasJoined) {
|
||||
await this.call.leave();
|
||||
await this.call.leave(log);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -24,7 +24,6 @@ import {ErrorReportViewModel} from "../../ErrorReportViewModel";
|
||||
import {ViewModel} from "../../ViewModel";
|
||||
import {imageToInfo} from "../common.js";
|
||||
import {LocalMedia} from "../../../matrix/calls/LocalMedia";
|
||||
import {ErrorViewModel} from "../../ErrorViewModel";
|
||||
// TODO: remove fallback so default isn't included in bundle for SDK users that have their custom tileClassForEntry
|
||||
// this is a breaking SDK change though to make this option mandatory
|
||||
import {tileClassForEntry as defaultTileClassForEntry} from "./timeline/tiles/index";
|
||||
@ -74,7 +73,7 @@ export class RoomViewModel extends ErrorReportViewModel {
|
||||
}
|
||||
|
||||
async load() {
|
||||
this.logAndCatch("Room.load", async log => {
|
||||
this.logAndCatch("RoomViewModel.load", async log => {
|
||||
this._room.on("change", this._onRoomChange);
|
||||
const timeline = await this._room.openTimeline(log);
|
||||
this._tileOptions = this.childOptions({
|
||||
@ -99,7 +98,7 @@ export class RoomViewModel extends ErrorReportViewModel {
|
||||
this._clearUnreadTimout = this.clock.createTimeout(2000);
|
||||
try {
|
||||
await this._clearUnreadTimout.elapsed();
|
||||
await this._room.clearUnread();
|
||||
await this._room.clearUnread(log);
|
||||
this._clearUnreadTimout = null;
|
||||
} catch (err) {
|
||||
if (err.name === "AbortError") {
|
||||
@ -111,7 +110,9 @@ export class RoomViewModel extends ErrorReportViewModel {
|
||||
}
|
||||
|
||||
focus() {
|
||||
this._clearUnreadAfterDelay();
|
||||
this.logAndCatch("RoomViewModel.focus", async log => {
|
||||
this._clearUnreadAfterDelay(log);
|
||||
});
|
||||
}
|
||||
|
||||
dispose() {
|
||||
@ -191,7 +192,7 @@ export class RoomViewModel extends ErrorReportViewModel {
|
||||
}
|
||||
|
||||
_sendMessage(message, replyingTo) {
|
||||
return this.logAndCatch("sendMessage", async log => {
|
||||
return this.logAndCatch("RoomViewModel.sendMessage", async log => {
|
||||
let success = false;
|
||||
if (!this._room.isArchived && message) {
|
||||
let msgtype = "m.text";
|
||||
@ -214,7 +215,7 @@ export class RoomViewModel extends ErrorReportViewModel {
|
||||
}
|
||||
|
||||
_pickAndSendFile() {
|
||||
return this.logAndCatch("sendFile", async log => {
|
||||
return this.logAndCatch("RoomViewModel.sendFile", async log => {
|
||||
const file = await this.platform.openFile();
|
||||
if (!file) {
|
||||
log.set("cancelled", true);
|
||||
@ -235,7 +236,7 @@ export class RoomViewModel extends ErrorReportViewModel {
|
||||
}
|
||||
|
||||
_pickAndSendVideo() {
|
||||
return this.logAndCatch("sendVideo", async log => {
|
||||
return this.logAndCatch("RoomViewModel.sendVideo", async log => {
|
||||
if (!this.platform.hasReadPixelPermission()) {
|
||||
throw new Error("Please allow canvas image data access, so we can scale your images down.");
|
||||
}
|
||||
@ -277,7 +278,7 @@ export class RoomViewModel extends ErrorReportViewModel {
|
||||
}
|
||||
|
||||
async _pickAndSendPicture() {
|
||||
this.logAndCatch("sendPicture", async log => {
|
||||
this.logAndCatch("RoomViewModel.sendPicture", async log => {
|
||||
if (!this.platform.hasReadPixelPermission()) {
|
||||
alert("Please allow canvas image data access, so we can scale your images down.");
|
||||
return;
|
||||
@ -341,7 +342,8 @@ export class RoomViewModel extends ErrorReportViewModel {
|
||||
}
|
||||
|
||||
startCall() {
|
||||
return this.logAndCatch("startCall", async log => {
|
||||
return this.logAndCatch("RoomViewModel.startCall", async log => {
|
||||
log.set("roomId", this._room.id);
|
||||
let localMedia;
|
||||
try {
|
||||
const stream = await this.platform.mediaDevices.getMediaTracks(false, true);
|
||||
@ -353,12 +355,18 @@ export class RoomViewModel extends ErrorReportViewModel {
|
||||
let call;
|
||||
try {
|
||||
// this will set the callViewModel above as a call will be added to callHandler.calls
|
||||
call = await session.callHandler.createCall(this._room.id, "m.video", "A call " + Math.round(this.platform.random() * 100));
|
||||
call = await session.callHandler.createCall(
|
||||
this._room.id,
|
||||
"m.video",
|
||||
"A call " + Math.round(this.platform.random() * 100),
|
||||
undefined,
|
||||
log
|
||||
);
|
||||
} catch (err) {
|
||||
throw new Error(`Could not create call: ${err.message}`);
|
||||
}
|
||||
try {
|
||||
await call.join(localMedia);
|
||||
await call.join(localMedia, log);
|
||||
} catch (err) {
|
||||
throw new Error(`Could not join call: ${err.message}`);
|
||||
}
|
||||
|
@ -16,7 +16,6 @@ limitations under the License.
|
||||
|
||||
import {SimpleTile} from "./SimpleTile.js";
|
||||
import {LocalMedia} from "../../../../../matrix/calls/LocalMedia";
|
||||
import {ErrorViewModel} from "../../../../ErrorViewModel"
|
||||
// TODO: timeline entries for state events with the same state key and type
|
||||
// should also update previous entries in the timeline, so we can update the name of the call, whether it is terminated, etc ...
|
||||
|
||||
@ -73,19 +72,19 @@ export class CallTile extends SimpleTile {
|
||||
}
|
||||
|
||||
async join() {
|
||||
await this.logAndCatch("Call.join", async log => {
|
||||
await this.logAndCatch("CallTile.join", async log => {
|
||||
if (this.canJoin) {
|
||||
const stream = await this.platform.mediaDevices.getMediaTracks(false, true);
|
||||
const localMedia = new LocalMedia().withUserMedia(stream);
|
||||
await this._call.join(localMedia);
|
||||
await this._call.join(localMedia, log);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async leave() {
|
||||
await this.logAndCatch("Call.leave", async log => {
|
||||
await this.logAndCatch("CallTile.leave", async log => {
|
||||
if (this.canLeave) {
|
||||
await this._call.leave();
|
||||
await this._call.leave(log);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -65,16 +65,26 @@ export class CallHandler implements RoomStateHandler {
|
||||
});
|
||||
}
|
||||
|
||||
async loadCalls(intent: CallIntent = CallIntent.Ring) {
|
||||
const txn = await this._getLoadTxn();
|
||||
const callEntries = await txn.calls.getByIntent(intent);
|
||||
this._loadCallEntries(callEntries, txn);
|
||||
loadCalls(intent?: CallIntent, log?: ILogItem) {
|
||||
return this.options.logger.wrapOrRun(log, "CallHandler.loadCalls", async log => {
|
||||
if (!intent) {
|
||||
intent = CallIntent.Ring;
|
||||
}
|
||||
log.set("intent", intent);
|
||||
const txn = await this._getLoadTxn();
|
||||
const callEntries = await txn.calls.getByIntent(intent);
|
||||
await this._loadCallEntries(callEntries, txn, log);
|
||||
});
|
||||
}
|
||||
|
||||
async loadCallsForRoom(intent: CallIntent, roomId: string) {
|
||||
const txn = await this._getLoadTxn();
|
||||
const callEntries = await txn.calls.getByIntentAndRoom(intent, roomId);
|
||||
this._loadCallEntries(callEntries, txn);
|
||||
loadCallsForRoom(intent: CallIntent, roomId: string, log?: ILogItem) {
|
||||
return this.options.logger.wrapOrRun(log, "CallHandler.loadCallsForRoom", async log => {
|
||||
log.set("intent", intent);
|
||||
log.set("roomId", roomId);
|
||||
const txn = await this._getLoadTxn();
|
||||
const callEntries = await txn.calls.getByIntentAndRoom(intent, roomId);
|
||||
await this._loadCallEntries(callEntries, txn, log);
|
||||
});
|
||||
}
|
||||
|
||||
private async _getLoadTxn(): Promise<Transaction> {
|
||||
@ -86,68 +96,71 @@ export class CallHandler implements RoomStateHandler {
|
||||
return txn;
|
||||
}
|
||||
|
||||
private async _loadCallEntries(callEntries: CallEntry[], txn: Transaction): Promise<void> {
|
||||
return this.options.logger.run({l: "loading calls", t: CALL_LOG_TYPE}, async log => {
|
||||
log.set("entries", callEntries.length);
|
||||
await Promise.all(callEntries.map(async callEntry => {
|
||||
if (this._calls.get(callEntry.callId)) {
|
||||
return;
|
||||
private async _loadCallEntries(callEntries: CallEntry[], txn: Transaction, log: ILogItem): Promise<void> {
|
||||
log.set("entries", callEntries.length);
|
||||
await Promise.all(callEntries.map(async callEntry => {
|
||||
if (this._calls.get(callEntry.callId)) {
|
||||
return;
|
||||
}
|
||||
const event = await txn.roomState.get(callEntry.roomId, EventType.GroupCall, callEntry.callId);
|
||||
if (event) {
|
||||
const call = new GroupCall(event.event.state_key, false, event.event.content, event.roomId, this.groupCallOptions);
|
||||
this._calls.set(call.id, call);
|
||||
}
|
||||
}));
|
||||
const roomIds = Array.from(new Set(callEntries.map(e => e.roomId)));
|
||||
await Promise.all(roomIds.map(async roomId => {
|
||||
// TODO: don't load all members until we need them
|
||||
const callsMemberEvents = await txn.roomState.getAllForType(roomId, EventType.GroupCallMember);
|
||||
await Promise.all(callsMemberEvents.map(async entry => {
|
||||
const userId = entry.event.sender;
|
||||
const roomMemberState = await txn.roomState.get(roomId, MEMBER_EVENT_TYPE, userId);
|
||||
let roomMember;
|
||||
if (roomMemberState) {
|
||||
roomMember = RoomMember.fromMemberEvent(roomMemberState.event);
|
||||
}
|
||||
const event = await txn.roomState.get(callEntry.roomId, EventType.GroupCall, callEntry.callId);
|
||||
if (event) {
|
||||
const call = new GroupCall(event.event.state_key, false, event.event.content, event.roomId, this.groupCallOptions);
|
||||
this._calls.set(call.id, call);
|
||||
if (!roomMember) {
|
||||
// we'll be missing the member here if we received a call and it's members
|
||||
// as pre-gap state and the members weren't active in the timeline we got.
|
||||
roomMember = RoomMember.fromUserId(roomId, userId, "join");
|
||||
}
|
||||
this.handleCallMemberEvent(entry.event, roomMember, roomId, log);
|
||||
}));
|
||||
const roomIds = Array.from(new Set(callEntries.map(e => e.roomId)));
|
||||
await Promise.all(roomIds.map(async roomId => {
|
||||
// TODO: don't load all members until we need them
|
||||
const callsMemberEvents = await txn.roomState.getAllForType(roomId, EventType.GroupCallMember);
|
||||
await Promise.all(callsMemberEvents.map(async entry => {
|
||||
const userId = entry.event.sender;
|
||||
const roomMemberState = await txn.roomState.get(roomId, MEMBER_EVENT_TYPE, userId);
|
||||
let roomMember;
|
||||
if (roomMemberState) {
|
||||
roomMember = RoomMember.fromMemberEvent(roomMemberState.event);
|
||||
}
|
||||
if (!roomMember) {
|
||||
// we'll be missing the member here if we received a call and it's members
|
||||
// as pre-gap state and the members weren't active in the timeline we got.
|
||||
roomMember = RoomMember.fromUserId(roomId, userId, "join");
|
||||
}
|
||||
this.handleCallMemberEvent(entry.event, roomMember, roomId, log);
|
||||
}));
|
||||
}));
|
||||
log.set("newSize", this._calls.size);
|
||||
});
|
||||
}));
|
||||
log.set("newSize", this._calls.size);
|
||||
}
|
||||
|
||||
async createCall(roomId: string, type: "m.video" | "m.voice", name: string, intent: CallIntent = CallIntent.Ring): Promise<GroupCall> {
|
||||
const call = new GroupCall(makeId("conf-"), true, {
|
||||
"m.name": name,
|
||||
"m.intent": intent
|
||||
}, roomId, this.groupCallOptions);
|
||||
this._calls.set(call.id, call);
|
||||
createCall(roomId: string, type: "m.video" | "m.voice", name: string, intent?: CallIntent, log?: ILogItem): Promise<GroupCall> {
|
||||
return this.options.logger.wrapOrRun(log, "CallHandler.createCall", async log => {
|
||||
if (!intent) {
|
||||
intent = CallIntent.Ring;
|
||||
}
|
||||
const call = new GroupCall(makeId("conf-"), true, {
|
||||
"m.name": name,
|
||||
"m.intent": intent
|
||||
}, roomId, this.groupCallOptions);
|
||||
this._calls.set(call.id, call);
|
||||
|
||||
try {
|
||||
await call.create(type);
|
||||
// store call info so it will ring again when reopening the app
|
||||
const txn = await this.options.storage.readWriteTxn([this.options.storage.storeNames.calls]);
|
||||
txn.calls.add({
|
||||
intent: call.intent,
|
||||
callId: call.id,
|
||||
timestamp: this.options.clock.now(),
|
||||
roomId: roomId
|
||||
});
|
||||
await txn.complete();
|
||||
} catch (err) {
|
||||
//if (err.name === "ConnectionError") {
|
||||
// if we're offline, give up and remove the call again
|
||||
this._calls.remove(call.id);
|
||||
//}
|
||||
throw err;
|
||||
}
|
||||
return call;
|
||||
try {
|
||||
await call.create(type, log);
|
||||
// store call info so it will ring again when reopening the app
|
||||
const txn = await this.options.storage.readWriteTxn([this.options.storage.storeNames.calls]);
|
||||
txn.calls.add({
|
||||
intent: call.intent,
|
||||
callId: call.id,
|
||||
timestamp: this.options.clock.now(),
|
||||
roomId: roomId
|
||||
});
|
||||
await txn.complete();
|
||||
} catch (err) {
|
||||
//if (err.name === "ConnectionError") {
|
||||
// if we're offline, give up and remove the call again
|
||||
this._calls.remove(call.id);
|
||||
//}
|
||||
throw err;
|
||||
}
|
||||
return call;
|
||||
});
|
||||
}
|
||||
|
||||
get calls(): BaseObservableMap<string, GroupCall> { return this._calls; }
|
||||
|
@ -162,45 +162,48 @@ export class GroupCall extends EventEmitter<{change: never}> {
|
||||
return this.errorBoundary.error;
|
||||
}
|
||||
|
||||
async join(localMedia: LocalMedia): Promise<void> {
|
||||
if (this._state !== GroupCallState.Created || this.joinedData) {
|
||||
return;
|
||||
}
|
||||
const logItem = this.options.logger.child({
|
||||
l: "answer call",
|
||||
t: CALL_LOG_TYPE,
|
||||
id: this.id,
|
||||
ownSessionId: this.options.sessionId
|
||||
});
|
||||
const turnServer = await this.options.turnServerSource.getSettings(logItem);
|
||||
const membersLogItem = logItem.child("member connections");
|
||||
const localMuteSettings = new MuteSettings();
|
||||
localMuteSettings.updateTrackInfo(localMedia.userMedia);
|
||||
const localPreviewMedia = localMedia.asPreview();
|
||||
const joinedData = new JoinedData(
|
||||
logItem,
|
||||
membersLogItem,
|
||||
localMedia,
|
||||
localPreviewMedia,
|
||||
localMuteSettings,
|
||||
turnServer
|
||||
);
|
||||
this.joinedData = joinedData;
|
||||
await joinedData.logItem.wrap("join", async log => {
|
||||
this._state = GroupCallState.Joining;
|
||||
this.emitChange();
|
||||
await log.wrap("update member state", async log => {
|
||||
const memberContent = await this._createMemberPayload(true);
|
||||
log.set("payload", memberContent);
|
||||
// send m.call.member state event
|
||||
const request = this.options.hsApi.sendState(this.roomId, EventType.GroupCallMember, this.options.ownUserId, memberContent, {log});
|
||||
await request.response();
|
||||
this.emitChange();
|
||||
});
|
||||
// send invite to all members that are < my userId
|
||||
for (const [,member] of this._members) {
|
||||
this.connectToMember(member, joinedData, log);
|
||||
join(localMedia: LocalMedia, log?: ILogItem): Promise<void> {
|
||||
return this.options.logger.wrapOrRun(log, "Call.join", async joinLog => {
|
||||
if (this._state !== GroupCallState.Created || this.joinedData) {
|
||||
return;
|
||||
}
|
||||
const logItem = this.options.logger.child({
|
||||
l: "Call.connection",
|
||||
t: CALL_LOG_TYPE,
|
||||
id: this.id,
|
||||
ownSessionId: this.options.sessionId
|
||||
});
|
||||
const turnServer = await this.options.turnServerSource.getSettings(logItem);
|
||||
const membersLogItem = logItem.child("member connections");
|
||||
const localMuteSettings = new MuteSettings();
|
||||
localMuteSettings.updateTrackInfo(localMedia.userMedia);
|
||||
const localPreviewMedia = localMedia.asPreview();
|
||||
const joinedData = new JoinedData(
|
||||
logItem,
|
||||
membersLogItem,
|
||||
localMedia,
|
||||
localPreviewMedia,
|
||||
localMuteSettings,
|
||||
turnServer
|
||||
);
|
||||
this.joinedData = joinedData;
|
||||
await joinedData.logItem.wrap("join", async log => {
|
||||
joinLog.refDetached(log);
|
||||
this._state = GroupCallState.Joining;
|
||||
this.emitChange();
|
||||
await log.wrap("update member state", async log => {
|
||||
const memberContent = await this._createMemberPayload(true);
|
||||
log.set("payload", memberContent);
|
||||
// send m.call.member state event
|
||||
const request = this.options.hsApi.sendState(this.roomId, EventType.GroupCallMember, this.options.ownUserId, memberContent, {log});
|
||||
await request.response();
|
||||
this.emitChange();
|
||||
});
|
||||
// send invite to all members that are < my userId
|
||||
for (const [,member] of this._members) {
|
||||
this.connectToMember(member, joinedData, log);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -263,12 +266,12 @@ export class GroupCall extends EventEmitter<{change: never}> {
|
||||
return this._state === GroupCallState.Joining || this._state === GroupCallState.Joined;
|
||||
}
|
||||
|
||||
async leave(): Promise<void> {
|
||||
const {joinedData} = this;
|
||||
if (!joinedData) {
|
||||
return;
|
||||
}
|
||||
await joinedData.logItem.wrap("leave", async log => {
|
||||
async leave(log?: ILogItem): Promise<void> {
|
||||
await this.options.logger.wrapOrRun(log, "Call.leave", async log => {
|
||||
const {joinedData} = this;
|
||||
if (!joinedData) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
joinedData.renewMembershipTimeout?.dispose();
|
||||
joinedData.renewMembershipTimeout = undefined;
|
||||
@ -310,8 +313,8 @@ export class GroupCall extends EventEmitter<{change: never}> {
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
create(type: "m.video" | "m.voice", log?: ILogItem): Promise<void> {
|
||||
return this.options.logger.wrapOrRun(log, {l: "create call", t: CALL_LOG_TYPE}, async log => {
|
||||
create(type: "m.video" | "m.voice", log: ILogItem): Promise<void> {
|
||||
return log.wrap({l: "create call", t: CALL_LOG_TYPE}, async log => {
|
||||
if (this._state !== GroupCallState.Fledgling) {
|
||||
return;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user