Compare commits

..

21 Commits

Author SHA1 Message Date
David Baker
f20fc78bd7 Use branch of js-sdk with Olm debugging
Pulls in changes from https://github.com/matrix-org/matrix-js-sdk/pull/3055

Not intended to stay long-term.
2023-01-12 11:28:15 +00:00
Robin
741233909d Merge pull request #829 from RiotTranslateBot/weblate-element-call-element-call
Translations update from Weblate
2023-01-09 13:35:43 -05:00
Robin
4e0f4a8dc7 Merge pull request #835 from robintown/unmuted-when-speaking
Work around mute state updates being slow
2023-01-09 13:35:05 -05:00
Šimon Brandner
0d151452ba Merge pull request #833 from vector-im/SimonBrandner/feat/hide-audio 2023-01-09 19:28:04 +01:00
Robin Townsend
4fd76f9599 Work around mute state updates being slow
Since the app already determines when someone is speaking, we can use that information to make it less obvious when to-device messages are being slow to deliver mute state updates.
2023-01-09 11:10:59 -05:00
Robin
d123793deb Merge pull request #832 from robintown/update-js-sdk
Update matrix-js-sdk
2023-01-09 10:52:19 -05:00
Robin Townsend
449c1c9d79 Try updating Olm to fix type errors 2023-01-09 10:49:01 -05:00
Robin Townsend
de5b58792e Update matrix-js-sdk 2023-01-09 10:32:05 -05:00
Robin Townsend
7769074410 Merge branch 'main' into update-js-sdk 2023-01-09 10:31:39 -05:00
Šimon Brandner
881054e265 Hide local volume controls for tiles with no audio
Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com>
2023-01-07 10:09:20 +01:00
Robin
767f9cdc4a Merge pull request #831 from robintown/no-video-mute
Leave audio elements unmuted regardless of mute state
2023-01-06 12:08:13 -05:00
Robin Townsend
946f564f84 Update matrix-js-sdk 2023-01-06 10:39:29 -05:00
Robin Townsend
468e389324 Leave audio elements unmuted regardless of mute state 2023-01-06 10:26:10 -05:00
Jozef Gaal
62e98f6c47 Translated using Weblate (Slovak)
Currently translated at 100.0% (141 of 141 strings)

Translation: Element Call/element-call
Translate-URL: https://translate.element.io/projects/element-call/element-call/sk/
2023-01-06 09:33:22 +00:00
Priit Jõerüüt
de31c099e3 Translated using Weblate (Estonian)
Currently translated at 100.0% (141 of 141 strings)

Translation: Element Call/element-call
Translate-URL: https://translate.element.io/projects/element-call/element-call/et/
2023-01-06 09:33:22 +00:00
Ihor Hordiichuk
49cae76387 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (141 of 141 strings)

Translation: Element Call/element-call
Translate-URL: https://translate.element.io/projects/element-call/element-call/uk/
2023-01-06 09:33:22 +00:00
Glandos
d45ea78ddb Translated using Weblate (French)
Currently translated at 100.0% (141 of 141 strings)

Translation: Element Call/element-call
Translate-URL: https://translate.element.io/projects/element-call/element-call/fr/
2023-01-06 09:33:22 +00:00
Linerly
dcbc3ed865 Translated using Weblate (Indonesian)
Currently translated at 100.0% (141 of 141 strings)

Translation: Element Call/element-call
Translate-URL: https://translate.element.io/projects/element-call/element-call/id/
2023-01-06 09:33:22 +00:00
Vri
ff19135d4e Translated using Weblate (German)
Currently translated at 100.0% (141 of 141 strings)

Translation: Element Call/element-call
Translate-URL: https://translate.element.io/projects/element-call/element-call/de/
2023-01-06 09:33:22 +00:00
Robin
de7343d16a Merge pull request #821 from robintown/save-lockfile
Save lockfile
2023-01-05 10:35:52 -05:00
Robin Townsend
c09fec5f88 Save lockfile 2023-01-04 08:25:26 -05:00
22 changed files with 250 additions and 319 deletions

View File

@@ -2,24 +2,9 @@ server {
listen 8080;
server_name localhost;
root /app;
location / {
# disable cache entriely by default (apart from Etag which is accurate enough)
add_header Cache-Control 'private no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
if_modified_since off;
expires off;
# also turn off last-modified since they are just the timestamps of the file in the docker image
# and may or may not bear any resemblance to when the resource changed
add_header Last-Modified "";
root /app;
try_files $uri /$uri /index.html;
}
# assets can be cached because they have hashed filenames
location /assets {
expires 1w;
add_header Cache-Control "public, no-transform";
}
}

View File

@@ -18,7 +18,7 @@
},
"dependencies": {
"@juggle/resize-observer": "^3.3.1",
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.8.tgz",
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.14.tgz",
"@react-aria/button": "^3.3.4",
"@react-aria/dialog": "^3.1.4",
"@react-aria/focus": "^3.5.0",
@@ -45,8 +45,7 @@
"i18next": "^21.10.0",
"i18next-browser-languagedetector": "^6.1.8",
"i18next-http-backend": "^1.4.4",
"lodash": "^4.17.21",
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#a34d06c7c24c3523a77f70c01d4e31c45e92aa6b",
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#79575ef3760bb6085b7b770f241b3d32756d5b96",
"matrix-widget-api": "^1.0.0",
"mermaid": "^8.13.8",
"normalize.css": "^8.0.1",

View File

@@ -137,5 +137,7 @@
"Whether to enable single-key keyboard shortcuts, e.g. 'm' to mute/unmute the mic.": "Ob Tastenkürzel mit nur einer Taste aktiviert sein sollen, z. B. „m“ um das Mikrofon stumm/aktiv zu schalten.",
"Single-key keyboard shortcuts": "Ein-Tasten-Tastenkürzel",
"{{name}} (Waiting for video...)": "{{name}} (Warte auf Video …)",
"This feature is only supported on Firefox.": "Diese Funktion wird nur in Firefox unterstützt."
"This feature is only supported on Firefox.": "Diese Funktion wird nur in Firefox unterstützt.",
"<0>Submitting debug logs will help us track down the problem.</0>": "<0>Übermittelte Problemberichte helfen uns, Fehler zu beheben.</0>",
"<0>Oops, something's gone wrong.</0>": "<0>Hoppla, etwas ist schiefgelaufen.</0>"
}

View File

@@ -137,5 +137,7 @@
"Whether to enable single-key keyboard shortcuts, e.g. 'm' to mute/unmute the mic.": "Kas kasutame üheklahvilisi kiirklahve, näiteks „m“ mikrofoni sisse/välja lülitamiseks.",
"Single-key keyboard shortcuts": "Üheklahvilised kiirklahvid",
"{{name}} (Waiting for video...)": "{{name}} (Ootame videovoo algust...)",
"This feature is only supported on Firefox.": "See funktsionaalsus on toetatud vaid Firefoxis."
"This feature is only supported on Firefox.": "See funktsionaalsus on toetatud vaid Firefoxis.",
"<0>Submitting debug logs will help us track down the problem.</0>": "<0>Kui saadad meile vealogid, siis on lihtsam vea põhjust otsida.</0>",
"<0>Oops, something's gone wrong.</0>": "<0>Ohoo, midagi on nüüd katki.</0>"
}

View File

@@ -137,5 +137,7 @@
"Whether to enable single-key keyboard shortcuts, e.g. 'm' to mute/unmute the mic.": "Bascule sur les raccourcis clavier à touche unique, par exemple « m » pour désactiver / activer le micro.",
"Single-key keyboard shortcuts": "Raccourcis clavier en une touche",
"{{name}} (Waiting for video...)": "{{name}} (En attente de vidéo…)",
"This feature is only supported on Firefox.": "Cette fonctionnalité est prise en charge dans Firefox uniquement."
"This feature is only supported on Firefox.": "Cette fonctionnalité est prise en charge dans Firefox uniquement.",
"<0>Submitting debug logs will help us track down the problem.</0>": "<0>Soumettre les journaux de débogage nous aidera à déterminer le problème.</0>",
"<0>Oops, something's gone wrong.</0>": "<0>Oups, quelque chose sest mal passé.</0>"
}

View File

@@ -137,5 +137,7 @@
"Whether to enable single-key keyboard shortcuts, e.g. 'm' to mute/unmute the mic.": "Apakah pintasan papan ketik seharusnya diaktifkan, mis. 'm' untuk membisukan/menyuarakan mikrofon.",
"Single-key keyboard shortcuts": "Pintasan papan ketik satu tombol",
"{{name}} (Waiting for video...)": "{{name}} (Menunggu video...)",
"This feature is only supported on Firefox.": "Fitur ini hanya didukung di Firefox."
"This feature is only supported on Firefox.": "Fitur ini hanya didukung di Firefox.",
"<0>Submitting debug logs will help us track down the problem.</0>": "<0>Mengirim catatan pengawakutuan akan membantu kami melacak masalahnya.</0>",
"<0>Oops, something's gone wrong.</0>": "<0>Aduh, ada yang salah.</0>"
}

View File

@@ -137,5 +137,7 @@
"{{displayName}}, your call is now ended": "{{displayName}}, váš hovor je teraz ukončený",
"{{count}} people connected|other": "{{count}} osôb pripojených",
"{{count}} people connected|one": "{{count}} osoba pripojená",
"This feature is only supported on Firefox.": "Táto funkcia je podporovaná len v prehliadači Firefox."
"This feature is only supported on Firefox.": "Táto funkcia je podporovaná len v prehliadači Firefox.",
"<0>Submitting debug logs will help us track down the problem.</0>": "<0>Odoslanie záznamov ladenia nám pomôže nájsť problém.</0>",
"<0>Oops, something's gone wrong.</0>": "<0>Hups, niečo sa pokazilo.</0>"
}

View File

@@ -137,5 +137,7 @@
"Whether to enable single-key keyboard shortcuts, e.g. 'm' to mute/unmute the mic.": "Чи вмикати/вимикати мікрофон однією клавішею, наприклад, «m» для ввімкнення/вимкнення мікрофона.",
"Single-key keyboard shortcuts": "Одноклавішні комбінації клавіш",
"{{name}} (Waiting for video...)": "{{name}} (Очікування на відео...)",
"This feature is only supported on Firefox.": "Ця функція підтримується лише в браузері Firefox."
"This feature is only supported on Firefox.": "Ця функція підтримується лише в браузері Firefox.",
"<0>Submitting debug logs will help us track down the problem.</0>": "<0>Надсилання журналів зневадження допоможе нам виявити проблему.</0>",
"<0>Oops, something's gone wrong.</0>": "<0>Йой, щось пішло не за планом.</0>"
}

View File

@@ -28,7 +28,6 @@ import {
SignupTracker,
MuteCameraTracker,
MuteMicrophoneTracker,
UndecryptableToDeviceEventTracker,
} from "./PosthogEvents";
import { Config } from "./config/Config";
import { getUrlParams } from "./UrlParams";
@@ -416,5 +415,4 @@ export class PosthogAnalytics {
public eventLogin = new LoginTracker();
public eventMuteMicrophone = new MuteMicrophoneTracker();
public eventMuteCamera = new MuteCameraTracker();
public eventUndecryptableToDevice = new UndecryptableToDeviceEventTracker();
}

View File

@@ -149,17 +149,3 @@ export class MuteCameraTracker {
});
}
}
interface UndecryptableToDeviceEvent {
eventName: "UndecryptableToDeviceEvent";
callId: string;
}
export class UndecryptableToDeviceEventTracker {
track(callId: string) {
PosthogAnalytics.instance.trackEvent<UndecryptableToDeviceEvent>({
eventName: "UndecryptableToDeviceEvent",
callId,
});
}
}

View File

@@ -94,17 +94,12 @@ export async function initClient(
const storeOpts = {} as ICreateClientOpts;
if (indexedDB && localStorage) {
if (indexedDB && localStorage && !import.meta.env.DEV) {
storeOpts.store = new IndexedDBStore({
indexedDB: window.indexedDB,
localStorage,
dbName: SYNC_STORE_NAME,
// We can't use the worker in dev mode because Vite simply doesn't bundle workers
// in dev mode: it expects them to use native modules. Ours don't, and even then only
// Chrome supports it. (It bundles them fine in production mode.)
workerFactory: import.meta.env.DEV
? undefined
: () => new IndexedDBWorker(),
workerFactory: () => new IndexedDBWorker(),
});
} else if (localStorage) {
storeOpts.store = new MemoryStore({ localStorage });

View File

@@ -14,7 +14,6 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import * as Sentry from "@sentry/react";
import { Resizable } from "re-resizable";
import React, {
useEffect,
@@ -35,7 +34,6 @@ import { CallEvent } from "matrix-js-sdk/src/webrtc/call";
import styles from "./GroupCallInspector.module.css";
import { SelectInput } from "../input/SelectInput";
import { PosthogAnalytics } from "../PosthogAnalytics";
interface InspectorContextState {
eventsByUserId?: { [userId: string]: SequenceDiagramMatrixEvent[] };
@@ -110,19 +108,6 @@ function formatTimestamp(timestamp: number | Date) {
return dateFormatter.format(timestamp);
}
function formatType(event: SequenceDiagramMatrixEvent): string {
if (event.content.msgtype === "m.bad.encrypted") return "Undecryptable";
return event.type;
}
function lineForEvent(event: SequenceDiagramMatrixEvent): string {
return `${getUserName(event.from)} ${
event.ignored ? "-x" : "->>"
} ${getUserName(event.to)}: ${formatTimestamp(event.timestamp)} ${formatType(
event
)} ${formatContent(event.type, event.content)}`;
}
export const InspectorContext =
createContext<
[
@@ -202,7 +187,21 @@ export function SequenceDiagramViewer({
participant ${getUserName(localUserId)}
participant Room
participant ${selectedUserId ? getUserName(selectedUserId) : "unknown"}
${events ? events.map(lineForEvent).join("\n ") : ""}
${
events
? events
.map(
({ to, from, timestamp, type, content, ignored }) =>
`${getUserName(from)} ${ignored ? "-x" : "->>"} ${getUserName(
to
)}: ${formatTimestamp(timestamp)} ${type} ${formatContent(
type,
content
)}`
)
.join("\n ")
: ""
}
`;
mermaid.mermaidAPI.render("mermaid", graphDefinition, (svgCode: string) => {
@@ -390,23 +389,12 @@ function useGroupCallState(
function onSendVoipEvent(event: Record<string, unknown>) {
dispatch({ type: CallEvent.SendVoipEvent, rawEvent: event });
}
function onUndecryptableToDevice(event: MatrixEvent) {
dispatch({ type: ClientEvent.ReceivedVoipEvent, event });
Sentry.captureMessage("Undecryptable to-device Event");
PosthogAnalytics.instance.eventUndecryptableToDevice.track(
groupCall.groupCallId
);
}
client.on(RoomStateEvent.Events, onUpdateRoomState);
//groupCall.on("calls_changed", onCallsChanged);
groupCall.on(CallEvent.SendVoipEvent, onSendVoipEvent);
//client.on("state", onCallsChanged);
//client.on("hangup", onCallHangup);
client.on(ClientEvent.ReceivedVoipEvent, onReceivedVoipEvent);
client.on(ClientEvent.UndecryptableToDeviceEvent, onUndecryptableToDevice);
onUpdateRoomState();
@@ -417,10 +405,6 @@ function useGroupCallState(
//client.removeListener("state", onCallsChanged);
//client.removeListener("hangup", onCallHangup);
client.removeListener(ClientEvent.ReceivedVoipEvent, onReceivedVoipEvent);
client.removeListener(
ClientEvent.UndecryptableToDeviceEvent,
onUndecryptableToDevice
);
};
}, [client, groupCall]);

View File

@@ -75,7 +75,6 @@ export function GroupCallView({
toggleLocalVideoMuted,
toggleMicrophoneMuted,
toggleScreensharing,
setMicrophoneMuted,
requestingScreenshare,
isScreensharing,
screenshareFeeds,
@@ -252,7 +251,6 @@ export function GroupCallView({
localVideoMuted={localVideoMuted}
toggleLocalVideoMuted={toggleLocalVideoMuted}
toggleMicrophoneMuted={toggleMicrophoneMuted}
setMicrophoneMuted={setMicrophoneMuted}
userMediaFeeds={userMediaFeeds}
activeSpeaker={activeSpeaker}
onLeave={onLeave}

View File

@@ -63,7 +63,6 @@ import { usePrefersReducedMotion } from "../usePrefersReducedMotion";
import { ParticipantInfo } from "./useGroupCall";
import { TileDescriptor } from "../video-grid/TileDescriptor";
import { AudioSink } from "../video-grid/AudioSink";
import { useCallViewKeyboardShortcuts } from "../useCallViewKeyboardShortcuts";
const canScreenshare = "getDisplayMedia" in (navigator.mediaDevices ?? {});
// There is currently a bug in Safari our our code with cloning and sending MediaStreams
@@ -82,7 +81,6 @@ interface Props {
toggleLocalVideoMuted: () => void;
toggleMicrophoneMuted: () => void;
toggleScreensharing: () => void;
setMicrophoneMuted: (muted: boolean) => void;
userMediaFeeds: CallFeed[];
activeSpeaker: CallFeed | null;
onLeave: () => void;
@@ -103,7 +101,6 @@ export function InCallView({
localVideoMuted,
toggleLocalVideoMuted,
toggleMicrophoneMuted,
setMicrophoneMuted,
userMediaFeeds,
activeSpeaker,
onLeave,
@@ -144,13 +141,6 @@ export function InCallView({
const { hideScreensharing } = useUrlParams();
useCallViewKeyboardShortcuts(
!feedbackModalState.isOpen,
toggleMicrophoneMuted,
toggleLocalVideoMuted,
setMicrophoneMuted
);
useEffect(() => {
widget?.api.transport.send(
layout === "freedom"

View File

@@ -32,6 +32,8 @@ import { usePageUnload } from "./usePageUnload";
import { PosthogAnalytics } from "../PosthogAnalytics";
import { TranslatedError, translatedError } from "../TranslatedError";
import { ElementWidgetActions, ScreenshareStartData, widget } from "../widget";
import { getSetting } from "../settings/useSetting";
import { useEventTarget } from "../useEvents";
export enum ConnectionState {
EstablishingCall = "establishing call", // call hasn't been established yet
@@ -58,7 +60,6 @@ export interface UseGroupCallReturnType {
toggleLocalVideoMuted: () => void;
toggleMicrophoneMuted: () => void;
toggleScreensharing: () => void;
setMicrophoneMuted: (muted: boolean) => void;
requestingScreenshare: boolean;
isScreensharing: boolean;
screenshareFeeds: CallFeed[];
@@ -471,6 +472,68 @@ export function useGroupCall(groupCall: GroupCall): UseGroupCallReturnType {
}
}, [t, updateState]);
const [spacebarHeld, setSpacebarHeld] = useState(false);
useEventTarget(
window,
"keydown",
useCallback(
(event: KeyboardEvent) => {
// Check if keyboard shortcuts are enabled
const keyboardShortcuts = getSetting("keyboard-shortcuts", true);
if (!keyboardShortcuts) {
return;
}
if (event.key === "m") {
toggleMicrophoneMuted();
} else if (event.key == "v") {
toggleLocalVideoMuted();
} else if (event.key === " ") {
setSpacebarHeld(true);
setMicrophoneMuted(false);
}
},
[
toggleLocalVideoMuted,
toggleMicrophoneMuted,
setMicrophoneMuted,
setSpacebarHeld,
]
)
);
useEventTarget(
window,
"keyup",
useCallback(
(event: KeyboardEvent) => {
// Check if keyboard shortcuts are enabled
const keyboardShortcuts = getSetting("keyboard-shortcuts", true);
if (!keyboardShortcuts) {
return;
}
if (event.key === " ") {
setSpacebarHeld(false);
setMicrophoneMuted(true);
}
},
[setMicrophoneMuted, setSpacebarHeld]
)
);
useEventTarget(
window,
"blur",
useCallback(() => {
if (spacebarHeld) {
setSpacebarHeld(false);
setMicrophoneMuted(true);
}
}, [setMicrophoneMuted, setSpacebarHeld, spacebarHeld])
);
return {
state,
localCallFeed,
@@ -485,7 +548,6 @@ export function useGroupCall(groupCall: GroupCall): UseGroupCallReturnType {
toggleLocalVideoMuted,
toggleMicrophoneMuted,
toggleScreensharing,
setMicrophoneMuted,
requestingScreenshare,
isScreensharing,
screenshareFeeds,

View File

@@ -1,4 +1,21 @@
/*
Copyright 2022 New Vector Ltd
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.
*/
/* eslint-disable @typescript-eslint/ban-ts-comment */
/*
Copyright 2017 OpenMarket Ltd
Copyright 2018 New Vector Ltd
Copyright 2019 The New Vector Ltd
@@ -37,23 +54,15 @@ limitations under the License.
// actually timestamps. We then purge the remaining logs. We also do this
// purge on startup to prevent logs from accumulating.
import EventEmitter from "events";
import { throttle } from "lodash";
import { logger } from "matrix-js-sdk/src/logger";
import { randomString } from "matrix-js-sdk/src/randomstring";
// the frequency with which we flush to indexeddb
const FLUSH_RATE_MS = 30 * 1000;
// the length of log data we keep in indexeddb (and include in the reports)
const MAX_LOG_SIZE = 1024 * 1024 * 5; // 5 MB
// Shortest amount of time between flushes. We are just appending to an
// IndexedDB table so we don't expect flushing to be that expensive, but
// we can batch the writes a little.
const MAX_FLUSH_INTERVAL_MS = 2 * 1000;
enum ConsoleLoggerEvent {
Log = "log",
}
type LogFunction = (
...args: (Error | DOMException | object | string)[]
) => void;
@@ -67,7 +76,7 @@ interface LogEntry {
index?: number;
}
export class ConsoleLogger extends EventEmitter {
export class ConsoleLogger {
private logs = "";
private originalFunctions: { [key in LogFunctionName]?: LogFunction } = {};
@@ -90,6 +99,13 @@ export class ConsoleLogger extends EventEmitter {
});
}
public bypassRageshake(
fnName: LogFunctionName,
...args: (Error | DOMException | object | string)[]
): void {
this.originalFunctions[fnName](...args);
}
public log(
level: string,
...args: (Error | DOMException | object | string)[]
@@ -121,27 +137,23 @@ export class ConsoleLogger extends EventEmitter {
// Using + really is the quickest way in JS
// http://jsperf.com/concat-vs-plus-vs-join
this.logs += line;
this.emit(ConsoleLoggerEvent.Log);
}
/**
* Returns the log lines to flush to disk and empties the internal log buffer
* @return {string} \n delimited log lines
* Retrieve log lines to flush to disk.
* @param {boolean} keepLogs True to not delete logs after flushing.
* @return {string} \n delimited log lines to flush.
*/
public popLogs(): string {
public flush(keepLogs?: boolean): string {
// The ConsoleLogger doesn't care how these end up on disk, it just
// flushes them to the caller.
if (keepLogs) {
return this.logs;
}
const logsToFlush = this.logs;
this.logs = "";
return logsToFlush;
}
/**
* Returns lines currently in the log buffer without removing them
* @return {string} \n delimited log lines
*/
public peekLogs(): string {
return this.logs;
}
}
// A class which stores log lines in an IndexedDB instance.
@@ -152,14 +164,8 @@ export class IndexedDBLogStore {
private flushAgainPromise: Promise<void> = null;
private id: string;
constructor(
private indexedDB: IDBFactory,
private loggerInstance: ConsoleLogger
) {
constructor(private indexedDB: IDBFactory, private logger: ConsoleLogger) {
this.id = "instance-" + randomString(16);
loggerInstance.on(ConsoleLoggerEvent.Log, this.onLoggerLog);
window.addEventListener("beforeunload", this.flush);
}
/**
@@ -168,31 +174,30 @@ export class IndexedDBLogStore {
public connect(): Promise<void> {
const req = this.indexedDB.open("logs");
return new Promise((resolve, reject) => {
req.onsuccess = () => {
this.db = req.result;
req.onsuccess = (event: Event) => {
// @ts-ignore
this.db = event.target.result;
// Periodically flush logs to local storage / indexeddb
setInterval(this.flush.bind(this), FLUSH_RATE_MS);
resolve();
};
req.onerror = () => {
const err = "Failed to open log database: " + req.error.name;
req.onerror = (event) => {
const err =
// @ts-ignore
"Failed to open log database: " + event.target.error.name;
logger.error(err);
reject(new Error(err));
};
// First time: Setup the object store
req.onupgradeneeded = () => {
const db = req.result;
// This is the log entries themselves. Each entry is a chunk of
// logs (ie multiple lines). 'id' is the instance ID (so logs with
// the same instance ID are all from the same session) and 'index'
// is a sequence number for the chunk. The log lines live in the
// 'lines' key, which is a chunk of many newline-separated log lines.
req.onupgradeneeded = (event) => {
// @ts-ignore
const db = event.target.result;
const logObjStore = db.createObjectStore("logs", {
keyPath: ["id", "index"],
});
// Keys in the database look like: [ "instance-148938490", 0 ]
// (The instance ID plus the ID of each log chunk).
// Later on we need to query everything based on an instance id.
// In order to do this, we need to set up indexes "id".
logObjStore.createIndex("id", "id", { unique: false });
@@ -201,9 +206,6 @@ export class IndexedDBLogStore {
this.generateLogEntry(new Date() + " ::: Log database was created.")
);
// This records the last time each instance ID generated a log message, such
// that the logs from each session can be collated in the order they last logged
// something.
const lastModifiedStore = db.createObjectStore("logslastmod", {
keyPath: "id",
});
@@ -212,26 +214,6 @@ export class IndexedDBLogStore {
});
}
private onLoggerLog = () => {
if (!this.db) return;
this.throttledFlush();
};
// Throttled function to flush logs. We use throttle rather
// than debounce as we want logs to be written regularly, otherwise
// if there's a constant stream of logging, we'd never write anything.
private throttledFlush = throttle(
() => {
this.flush();
},
MAX_FLUSH_INTERVAL_MS,
{
leading: false,
trailing: true,
}
);
/**
* Flush logs to disk.
*
@@ -251,7 +233,7 @@ export class IndexedDBLogStore {
*
* @return {Promise} Resolved when the logs have been flushed.
*/
public flush = (): Promise<void> => {
public flush(): Promise<void> {
// check if a flush() operation is ongoing
if (this.flushPromise) {
if (this.flushAgainPromise) {
@@ -276,19 +258,20 @@ export class IndexedDBLogStore {
reject(new Error("No connected database"));
return;
}
const lines = this.loggerInstance.popLogs();
const lines = this.logger.flush();
if (lines.length === 0) {
resolve();
return;
}
const txn = this.db.transaction(["logs", "logslastmod"], "readwrite");
const objStore = txn.objectStore("logs");
txn.oncomplete = () => {
txn.oncomplete = (event) => {
resolve();
};
txn.onerror = (event) => {
logger.error("Failed to flush logs : ", event);
reject(new Error("Failed to write logs: " + txn.error.message));
// @ts-ignore
reject(new Error("Failed to write logs: " + event.target.errorCode));
};
objStore.add(this.generateLogEntry(lines));
const lastModStore = txn.objectStore("logslastmod");
@@ -297,7 +280,7 @@ export class IndexedDBLogStore {
this.flushPromise = null;
});
return this.flushPromise;
};
}
/**
* Consume the most recent logs and return them. Older logs which are not
@@ -324,11 +307,13 @@ export class IndexedDBLogStore {
.index("id")
.openCursor(IDBKeyRange.only(id), "prev");
let lines = "";
query.onerror = () => {
reject(new Error("Query failed: " + query.error.message));
query.onerror = (event) => {
// @ts-ignore
reject(new Error("Query failed: " + event.target.errorCode));
};
query.onsuccess = () => {
const cursor = query.result;
query.onsuccess = (event) => {
// @ts-ignore
const cursor = event.target.result;
if (!cursor) {
resolve(lines);
return; // end of results
@@ -370,8 +355,9 @@ export class IndexedDBLogStore {
const o = txn.objectStore("logs");
// only load the key path, not the data which may be huge
const query = o.index("id").openKeyCursor(IDBKeyRange.only(id));
query.onsuccess = () => {
const cursor = query.result;
query.onsuccess = (event) => {
// @ts-ignore
const cursor = event.target.result;
if (!cursor) {
return;
}
@@ -381,10 +367,12 @@ export class IndexedDBLogStore {
txn.oncomplete = () => {
resolve();
};
txn.onerror = () => {
txn.onerror = (event) => {
reject(
new Error(
"Failed to delete logs for " + `'${id}' : ${txn.error.message}`
"Failed to delete logs for " +
// @ts-ignore
`'${id}' : ${event.target.errorCode}`
)
);
};
@@ -468,12 +456,14 @@ function selectQuery<T>(
const query = store.openCursor(keyRange);
return new Promise((resolve, reject) => {
const results = [];
query.onerror = () => {
reject(new Error("Query failed: " + query.error.message));
query.onerror = (event) => {
// @ts-ignore
reject(new Error("Query failed: " + event.target.errorCode));
};
// collect results
query.onsuccess = () => {
const cursor = query.result;
query.onsuccess = (event) => {
// @ts-ignore
const cursor = event.target.result;
if (!cursor) {
resolve(results);
return; // end of results
@@ -489,6 +479,8 @@ declare global {
// eslint-disable-next-line no-var, camelcase
var mx_rage_logger: ConsoleLogger;
// eslint-disable-next-line no-var, camelcase
var mx_rage_initPromise: Promise<void>;
// eslint-disable-next-line no-var, camelcase
var mx_rage_initStoragePromise: Promise<void>;
}
@@ -499,11 +491,19 @@ declare global {
* be set up immediately for the logs.
* @return {Promise} Resolves when set up.
*/
export function init(): Promise<void> {
export function init(setUpPersistence = true): Promise<void> {
if (global.mx_rage_initPromise) {
return global.mx_rage_initPromise;
}
global.mx_rage_logger = new ConsoleLogger();
global.mx_rage_logger.monkeyPatch(window.console);
return tryInitStorage();
if (setUpPersistence) {
return tryInitStorage();
}
global.mx_rage_initPromise = Promise.resolve();
return global.mx_rage_initPromise;
}
/**
@@ -573,7 +573,7 @@ export async function getLogsForReport(): Promise<LogEntry[]> {
} else {
return [
{
lines: global.mx_rage_logger.peekLogs(),
lines: global.mx_rage_logger.flush(true),
id: "-",
},
];

View File

@@ -1,93 +0,0 @@
/*
Copyright 2022-2023 New Vector Ltd
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 { useCallback, useState } from "react";
import { getSetting } from "./settings/useSetting";
import { useEventTarget } from "./useEvents";
export function useCallViewKeyboardShortcuts(
enabled: boolean,
toggleMicrophoneMuted: () => void,
toggleLocalVideoMuted: () => void,
setMicrophoneMuted: (muted: boolean) => void
) {
const [spacebarHeld, setSpacebarHeld] = useState(false);
useEventTarget(
window,
"keydown",
useCallback(
(event: KeyboardEvent) => {
if (!enabled) return;
// Check if keyboard shortcuts are enabled
const keyboardShortcuts = getSetting("keyboard-shortcuts", true);
if (!keyboardShortcuts) {
return;
}
if (event.key === "m") {
toggleMicrophoneMuted();
} else if (event.key == "v") {
toggleLocalVideoMuted();
} else if (event.key === " " && !spacebarHeld) {
setSpacebarHeld(true);
setMicrophoneMuted(false);
}
},
[
enabled,
spacebarHeld,
toggleLocalVideoMuted,
toggleMicrophoneMuted,
setMicrophoneMuted,
setSpacebarHeld,
]
)
);
useEventTarget(
window,
"keyup",
useCallback(
(event: KeyboardEvent) => {
if (!enabled) return;
// Check if keyboard shortcuts are enabled
const keyboardShortcuts = getSetting("keyboard-shortcuts", true);
if (!keyboardShortcuts) {
return;
}
if (event.key === " ") {
setSpacebarHeld(false);
setMicrophoneMuted(true);
}
},
[enabled, setMicrophoneMuted, setSpacebarHeld]
)
);
useEventTarget(
window,
"blur",
useCallback(() => {
if (spacebarHeld) {
setSpacebarHeld(false);
setMicrophoneMuted(true);
}
}, [setMicrophoneMuted, setSpacebarHeld, spacebarHeld])
);
}

View File

@@ -31,14 +31,15 @@ export const AudioSink: React.FC<Props> = ({
tileDescriptor,
audioOutput,
}: Props) => {
const { audioMuted, localVolume, stream } = useCallFeed(
tileDescriptor.callFeed
);
const { localVolume, stream } = useCallFeed(tileDescriptor.callFeed);
const audioElementRef = useMediaStream(
stream,
audioOutput,
audioMuted,
// We don't compare the audioMuted flag of useCallFeed here, since unmuting
// depends on to-device messages which may lag behind the audio actually
// starting to flow over the stream
tileDescriptor.isLocal,
localVolume
);

View File

@@ -36,6 +36,7 @@ interface Props {
mediaRef?: React.RefObject<MediaElement>;
onOptionsPress?: () => void;
localVolume?: number;
hasAudio?: boolean;
maximised?: boolean;
fullscreen?: boolean;
onFullscreen?: () => void;
@@ -58,6 +59,7 @@ export const VideoTile = forwardRef<HTMLDivElement, Props>(
mediaRef,
onOptionsPress,
localVolume,
hasAudio,
maximised,
fullscreen,
onFullscreen,
@@ -74,14 +76,16 @@ export const VideoTile = forwardRef<HTMLDivElement, Props>(
const toolbarButtons: JSX.Element[] = [];
if (connectionState == ConnectionState.Connected && !isLocal) {
toolbarButtons.push(
<AudioButton
key="localVolume"
className={styles.button}
volume={localVolume}
onPress={onOptionsPress}
/>
);
if (hasAudio) {
toolbarButtons.push(
<AudioButton
key="localVolume"
className={styles.button}
volume={localVolume}
onPress={onOptionsPress}
/>
);
}
if (screenshare) {
toolbarButtons.push(
@@ -137,7 +141,13 @@ export const VideoTile = forwardRef<HTMLDivElement, Props>(
</div>
) : (
<div className={classNames(styles.infoBubble, styles.memberName)}>
{audioMuted && !videoMuted && <MicMutedIcon />}
{
/* If the user is speaking, it's safe to say they're unmuted.
Mute state is currently sent over to-device messages, which
aren't quite real-time, so this is an important kludge to make
sure no one appears muted when they've clearly begun talking. */
audioMuted && !videoMuted && !speaking && <MicMutedIcon />
}
{videoMuted && <VideoMutedIcon />}
<span title={caption}>{caption}</span>
</div>

View File

@@ -62,6 +62,7 @@ export function VideoTileContainer({
audioMuted,
videoMuted,
localVolume,
hasAudio,
speaking,
stream,
purpose,
@@ -109,6 +110,7 @@ export function VideoTileContainer({
avatar={getAvatar && getAvatar(item.member, width, height)}
onOptionsPress={onOptionsPress}
localVolume={localVolume}
hasAudio={hasAudio}
maximised={maximised}
fullscreen={fullscreen}
onFullscreen={onFullscreenCallback}

View File

@@ -25,6 +25,7 @@ interface CallFeedState {
videoMuted: boolean;
audioMuted: boolean;
localVolume: number;
hasAudio: boolean;
disposed: boolean | undefined;
stream: MediaStream | undefined;
purpose: SDPStreamMetadataPurpose | undefined;
@@ -38,6 +39,7 @@ function getCallFeedState(callFeed: CallFeed | undefined): CallFeedState {
videoMuted: callFeed ? callFeed.isVideoMuted() : true,
audioMuted: callFeed ? callFeed.isAudioMuted() : true,
localVolume: callFeed ? callFeed.getLocalVolume() : 0,
hasAudio: callFeed ? callFeed.stream.getAudioTracks().length >= 1 : false,
disposed: callFeed ? callFeed.disposed : undefined,
stream: callFeed ? callFeed.stream : undefined,
purpose: callFeed ? callFeed.purpose : undefined,

View File

@@ -1826,9 +1826,9 @@
resolved "https://registry.yarnpkg.com/@matrix-org/matrix-sdk-crypto-js/-/matrix-sdk-crypto-js-0.1.0-alpha.2.tgz#a09d0fea858e817da971a3c9f904632ef7b49eb6"
integrity sha512-oVkBCh9YP7H9i4gAoQbZzswniczfo/aIptNa4dxRi4Ff9lSvUCFv6Hvzi7C+90c0/PWZLXjIDTIAWZYmwyd2fA==
"@matrix-org/olm@https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.8.tgz":
version "3.2.8"
resolved "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.8.tgz#8d53636d045e1776e2a2ec6613e57330dd9ce856"
"@matrix-org/olm@https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.14.tgz":
version "3.2.14"
resolved "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.14.tgz#acd96c00a881d0f462e1f97a56c73742c8dbc984"
"@mdx-js/mdx@^1.6.22":
version "1.6.22"
@@ -2418,13 +2418,13 @@
"@sentry/utils" "6.19.7"
tslib "^1.9.3"
"@sentry/core@7.31.0":
version "7.31.0"
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.31.0.tgz#2c1ec086cd86c097fc5c60703d33c848089597c7"
integrity sha512-IZS1MZznyBOPw7UEpZwq3t3aaaVhFB+r3KM4JYFSJRr7Ky9TjldXA3hadNUTztjYGgEC3u8kB9jXoRvNXM2hqA==
"@sentry/core@7.28.1":
version "7.28.1"
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.28.1.tgz#c712ce17469b18b01606108817be24a99ed2116e"
integrity sha512-7wvnuvn/mrAfcugWoCG/3pqDIrUgH5t+HisMJMGw0h9Tc33KqrmqMDCQVvjlrr2pWrw/vuUCFdm8CbUHJ832oQ==
dependencies:
"@sentry/types" "7.31.0"
"@sentry/utils" "7.31.0"
"@sentry/types" "7.28.1"
"@sentry/utils" "7.28.1"
tslib "^1.9.3"
"@sentry/hub@6.19.7":
@@ -2446,13 +2446,13 @@
tslib "^1.9.3"
"@sentry/node@^7.19.0":
version "7.31.0"
resolved "https://registry.yarnpkg.com/@sentry/node/-/node-7.31.0.tgz#d6b6776f7c1daee0a41e7f97fc15f058435f1b1c"
integrity sha512-DBjPfThZ5CIC2G9/CVFRlSOP/QqF1IoZXNpTUPZkhQ1cjShJeERT64jMkTdk+RAStSTpEfF6J0rUy1NIyHHEoQ==
version "7.28.1"
resolved "https://registry.yarnpkg.com/@sentry/node/-/node-7.28.1.tgz#fc53675a048c29c86e5a8cd3ba570c454f492c18"
integrity sha512-n7AbpJqZJjWPpKNGc55mP7AdQ+XSomS9MZJuZ+Xt2AU52aVwGPI4z9aHUJFSDGaMHHiu/toyPnoUES+XZf6/hw==
dependencies:
"@sentry/core" "7.31.0"
"@sentry/types" "7.31.0"
"@sentry/utils" "7.31.0"
"@sentry/core" "7.28.1"
"@sentry/types" "7.28.1"
"@sentry/utils" "7.28.1"
cookie "^0.4.1"
https-proxy-agent "^5.0.0"
lru_map "^0.3.3"
@@ -2482,13 +2482,13 @@
tslib "^1.9.3"
"@sentry/tracing@^7.19.0":
version "7.31.0"
resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-7.31.0.tgz#c37f1930aba05a4c461f655dc258eaa851e9a305"
integrity sha512-p/b9sOw2wwcDLp8p0bJ0oetgeEB1q/ueZaXDeBeSh+3GNKx6J4S3pcFpbMXDK8d2Ayd3P9Gvrm7y9Hc2ueJteg==
version "7.28.1"
resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-7.28.1.tgz#d276e4d17a79190a88112696c73de12c209607a1"
integrity sha512-uWspnuz+7FyW8ES5lRaVA7O/YJSzMlSkvBFtgzaoKmdaueokU/sRLwlCsrdgwavG1wpm79df7R1iiSeqhaXDlw==
dependencies:
"@sentry/core" "7.31.0"
"@sentry/types" "7.31.0"
"@sentry/utils" "7.31.0"
"@sentry/core" "7.28.1"
"@sentry/types" "7.28.1"
"@sentry/utils" "7.28.1"
tslib "^1.9.3"
"@sentry/types@6.19.7":
@@ -2496,10 +2496,10 @@
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.19.7.tgz#c6b337912e588083fc2896eb012526cf7cfec7c7"
integrity sha512-jH84pDYE+hHIbVnab3Hr+ZXr1v8QABfhx39KknxqKWr2l0oEItzepV0URvbEhB446lk/S/59230dlUUIBGsXbg==
"@sentry/types@7.31.0":
version "7.31.0"
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.31.0.tgz#c028519a660d76c4e8f18ba9e44ede45d314fcae"
integrity sha512-nFqo7wyMnapdSEdw1MD+cavDtD9x5QQmh/bwLEOb/euM0cHFJHYyD7CveY/mQng4HyEVWY+DCtX/7E3GcQ7Bdw==
"@sentry/types@7.28.1":
version "7.28.1"
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.28.1.tgz#9018b4c152b475de9bedd267237393d3c9b1253d"
integrity sha512-DvSplMVrVEmOzR2M161V5+B8Up3vR71xMqJOpWTzE9TqtFJRGPtqT/5OBsNJJw1+/j2ssMcnKwbEo9Q2EGeS6g==
"@sentry/types@^7.2.0":
version "7.13.0"
@@ -2514,12 +2514,12 @@
"@sentry/types" "6.19.7"
tslib "^1.9.3"
"@sentry/utils@7.31.0":
version "7.31.0"
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.31.0.tgz#507290984c941fca8f5c2621a4b8b0aca8fbfc10"
integrity sha512-B1KkvdfwlaqM7sDp3/yk2No7WsbMuLEywGRVOLzXeTqTLSBRBWyyYIudqPtx2LDds9anlUHj21zs9FKY+S3eiA==
"@sentry/utils@7.28.1":
version "7.28.1"
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.28.1.tgz#0a7b6aa4b09e91e4d1aded2a8c8dbaf818cee96e"
integrity sha512-75/jzLUO9HH09iC9TslNimGbxOP3jgn89P+q7uR+rp2fJfRExHVeKJZQdK0Ij4/SmE7TJ3Uh2r154N0INZEx1g==
dependencies:
"@sentry/types" "7.31.0"
"@sentry/types" "7.28.1"
tslib "^1.9.3"
"@sentry/vite-plugin@^0.3.0":
@@ -10362,9 +10362,9 @@ matrix-events-sdk@0.0.1:
resolved "https://registry.yarnpkg.com/matrix-events-sdk/-/matrix-events-sdk-0.0.1.tgz#c8c38911e2cb29023b0bbac8d6f32e0de2c957dd"
integrity sha512-1QEOsXO+bhyCroIe2/A5OwaxHvBm7EsSQ46DEDn8RBIfQwN5HWBpFvyWWR4QY0KHPPnnJdI99wgRiAl7Ad5qaA==
"matrix-js-sdk@github:matrix-org/matrix-js-sdk#a34d06c7c24c3523a77f70c01d4e31c45e92aa6b":
"matrix-js-sdk@github:matrix-org/matrix-js-sdk#79575ef3760bb6085b7b770f241b3d32756d5b96":
version "23.0.0"
resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/a34d06c7c24c3523a77f70c01d4e31c45e92aa6b"
resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/79575ef3760bb6085b7b770f241b3d32756d5b96"
dependencies:
"@babel/runtime" "^7.12.5"
"@matrix-org/matrix-sdk-crypto-js" "^0.1.0-alpha.2"