Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f20fc78bd7 | ||
|
|
741233909d | ||
|
|
4e0f4a8dc7 | ||
|
|
0d151452ba | ||
|
|
4fd76f9599 | ||
|
|
d123793deb | ||
|
|
449c1c9d79 | ||
|
|
de5b58792e | ||
|
|
7769074410 | ||
|
|
881054e265 | ||
|
|
767f9cdc4a | ||
|
|
946f564f84 | ||
|
|
468e389324 | ||
|
|
62e98f6c47 | ||
|
|
de31c099e3 | ||
|
|
49cae76387 | ||
|
|
d45ea78ddb | ||
|
|
dcbc3ed865 | ||
|
|
ff19135d4e | ||
|
|
de7343d16a | ||
|
|
c09fec5f88 |
@@ -2,24 +2,9 @@ server {
|
|||||||
listen 8080;
|
listen 8080;
|
||||||
server_name localhost;
|
server_name localhost;
|
||||||
|
|
||||||
root /app;
|
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
# disable cache entriely by default (apart from Etag which is accurate enough)
|
root /app;
|
||||||
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 "";
|
|
||||||
|
|
||||||
try_files $uri /$uri /index.html;
|
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";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@juggle/resize-observer": "^3.3.1",
|
"@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/button": "^3.3.4",
|
||||||
"@react-aria/dialog": "^3.1.4",
|
"@react-aria/dialog": "^3.1.4",
|
||||||
"@react-aria/focus": "^3.5.0",
|
"@react-aria/focus": "^3.5.0",
|
||||||
@@ -45,8 +45,7 @@
|
|||||||
"i18next": "^21.10.0",
|
"i18next": "^21.10.0",
|
||||||
"i18next-browser-languagedetector": "^6.1.8",
|
"i18next-browser-languagedetector": "^6.1.8",
|
||||||
"i18next-http-backend": "^1.4.4",
|
"i18next-http-backend": "^1.4.4",
|
||||||
"lodash": "^4.17.21",
|
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#79575ef3760bb6085b7b770f241b3d32756d5b96",
|
||||||
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#a34d06c7c24c3523a77f70c01d4e31c45e92aa6b",
|
|
||||||
"matrix-widget-api": "^1.0.0",
|
"matrix-widget-api": "^1.0.0",
|
||||||
"mermaid": "^8.13.8",
|
"mermaid": "^8.13.8",
|
||||||
"normalize.css": "^8.0.1",
|
"normalize.css": "^8.0.1",
|
||||||
|
|||||||
@@ -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.",
|
"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",
|
"Single-key keyboard shortcuts": "Ein-Tasten-Tastenkürzel",
|
||||||
"{{name}} (Waiting for video...)": "{{name}} (Warte auf Video …)",
|
"{{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>"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.",
|
"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",
|
"Single-key keyboard shortcuts": "Üheklahvilised kiirklahvid",
|
||||||
"{{name}} (Waiting for video...)": "{{name}} (Ootame videovoo algust...)",
|
"{{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>"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.",
|
"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",
|
"Single-key keyboard shortcuts": "Raccourcis clavier en une touche",
|
||||||
"{{name}} (Waiting for video...)": "{{name}} (En attente de vidéo…)",
|
"{{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 s’est mal passé.</0>"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.",
|
"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",
|
"Single-key keyboard shortcuts": "Pintasan papan ketik satu tombol",
|
||||||
"{{name}} (Waiting for video...)": "{{name}} (Menunggu video...)",
|
"{{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>"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -137,5 +137,7 @@
|
|||||||
"{{displayName}}, your call is now ended": "{{displayName}}, váš hovor je teraz ukončený",
|
"{{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|other": "{{count}} osôb pripojených",
|
||||||
"{{count}} people connected|one": "{{count}} osoba pripojená",
|
"{{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>"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -137,5 +137,7 @@
|
|||||||
"Whether to enable single-key keyboard shortcuts, e.g. 'm' to mute/unmute the mic.": "Чи вмикати/вимикати мікрофон однією клавішею, наприклад, «m» для ввімкнення/вимкнення мікрофона.",
|
"Whether to enable single-key keyboard shortcuts, e.g. 'm' to mute/unmute the mic.": "Чи вмикати/вимикати мікрофон однією клавішею, наприклад, «m» для ввімкнення/вимкнення мікрофона.",
|
||||||
"Single-key keyboard shortcuts": "Одноклавішні комбінації клавіш",
|
"Single-key keyboard shortcuts": "Одноклавішні комбінації клавіш",
|
||||||
"{{name}} (Waiting for video...)": "{{name}} (Очікування на відео...)",
|
"{{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>"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import {
|
|||||||
SignupTracker,
|
SignupTracker,
|
||||||
MuteCameraTracker,
|
MuteCameraTracker,
|
||||||
MuteMicrophoneTracker,
|
MuteMicrophoneTracker,
|
||||||
UndecryptableToDeviceEventTracker,
|
|
||||||
} from "./PosthogEvents";
|
} from "./PosthogEvents";
|
||||||
import { Config } from "./config/Config";
|
import { Config } from "./config/Config";
|
||||||
import { getUrlParams } from "./UrlParams";
|
import { getUrlParams } from "./UrlParams";
|
||||||
@@ -416,5 +415,4 @@ export class PosthogAnalytics {
|
|||||||
public eventLogin = new LoginTracker();
|
public eventLogin = new LoginTracker();
|
||||||
public eventMuteMicrophone = new MuteMicrophoneTracker();
|
public eventMuteMicrophone = new MuteMicrophoneTracker();
|
||||||
public eventMuteCamera = new MuteCameraTracker();
|
public eventMuteCamera = new MuteCameraTracker();
|
||||||
public eventUndecryptableToDevice = new UndecryptableToDeviceEventTracker();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -94,17 +94,12 @@ export async function initClient(
|
|||||||
|
|
||||||
const storeOpts = {} as ICreateClientOpts;
|
const storeOpts = {} as ICreateClientOpts;
|
||||||
|
|
||||||
if (indexedDB && localStorage) {
|
if (indexedDB && localStorage && !import.meta.env.DEV) {
|
||||||
storeOpts.store = new IndexedDBStore({
|
storeOpts.store = new IndexedDBStore({
|
||||||
indexedDB: window.indexedDB,
|
indexedDB: window.indexedDB,
|
||||||
localStorage,
|
localStorage,
|
||||||
dbName: SYNC_STORE_NAME,
|
dbName: SYNC_STORE_NAME,
|
||||||
// We can't use the worker in dev mode because Vite simply doesn't bundle workers
|
workerFactory: () => new IndexedDBWorker(),
|
||||||
// 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(),
|
|
||||||
});
|
});
|
||||||
} else if (localStorage) {
|
} else if (localStorage) {
|
||||||
storeOpts.store = new MemoryStore({ localStorage });
|
storeOpts.store = new MemoryStore({ localStorage });
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as Sentry from "@sentry/react";
|
|
||||||
import { Resizable } from "re-resizable";
|
import { Resizable } from "re-resizable";
|
||||||
import React, {
|
import React, {
|
||||||
useEffect,
|
useEffect,
|
||||||
@@ -35,7 +34,6 @@ import { CallEvent } from "matrix-js-sdk/src/webrtc/call";
|
|||||||
|
|
||||||
import styles from "./GroupCallInspector.module.css";
|
import styles from "./GroupCallInspector.module.css";
|
||||||
import { SelectInput } from "../input/SelectInput";
|
import { SelectInput } from "../input/SelectInput";
|
||||||
import { PosthogAnalytics } from "../PosthogAnalytics";
|
|
||||||
|
|
||||||
interface InspectorContextState {
|
interface InspectorContextState {
|
||||||
eventsByUserId?: { [userId: string]: SequenceDiagramMatrixEvent[] };
|
eventsByUserId?: { [userId: string]: SequenceDiagramMatrixEvent[] };
|
||||||
@@ -110,19 +108,6 @@ function formatTimestamp(timestamp: number | Date) {
|
|||||||
return dateFormatter.format(timestamp);
|
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 =
|
export const InspectorContext =
|
||||||
createContext<
|
createContext<
|
||||||
[
|
[
|
||||||
@@ -202,7 +187,21 @@ export function SequenceDiagramViewer({
|
|||||||
participant ${getUserName(localUserId)}
|
participant ${getUserName(localUserId)}
|
||||||
participant Room
|
participant Room
|
||||||
participant ${selectedUserId ? getUserName(selectedUserId) : "unknown"}
|
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) => {
|
mermaid.mermaidAPI.render("mermaid", graphDefinition, (svgCode: string) => {
|
||||||
@@ -390,23 +389,12 @@ function useGroupCallState(
|
|||||||
function onSendVoipEvent(event: Record<string, unknown>) {
|
function onSendVoipEvent(event: Record<string, unknown>) {
|
||||||
dispatch({ type: CallEvent.SendVoipEvent, rawEvent: event });
|
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);
|
client.on(RoomStateEvent.Events, onUpdateRoomState);
|
||||||
//groupCall.on("calls_changed", onCallsChanged);
|
//groupCall.on("calls_changed", onCallsChanged);
|
||||||
groupCall.on(CallEvent.SendVoipEvent, onSendVoipEvent);
|
groupCall.on(CallEvent.SendVoipEvent, onSendVoipEvent);
|
||||||
//client.on("state", onCallsChanged);
|
//client.on("state", onCallsChanged);
|
||||||
//client.on("hangup", onCallHangup);
|
//client.on("hangup", onCallHangup);
|
||||||
client.on(ClientEvent.ReceivedVoipEvent, onReceivedVoipEvent);
|
client.on(ClientEvent.ReceivedVoipEvent, onReceivedVoipEvent);
|
||||||
client.on(ClientEvent.UndecryptableToDeviceEvent, onUndecryptableToDevice);
|
|
||||||
|
|
||||||
onUpdateRoomState();
|
onUpdateRoomState();
|
||||||
|
|
||||||
@@ -417,10 +405,6 @@ function useGroupCallState(
|
|||||||
//client.removeListener("state", onCallsChanged);
|
//client.removeListener("state", onCallsChanged);
|
||||||
//client.removeListener("hangup", onCallHangup);
|
//client.removeListener("hangup", onCallHangup);
|
||||||
client.removeListener(ClientEvent.ReceivedVoipEvent, onReceivedVoipEvent);
|
client.removeListener(ClientEvent.ReceivedVoipEvent, onReceivedVoipEvent);
|
||||||
client.removeListener(
|
|
||||||
ClientEvent.UndecryptableToDeviceEvent,
|
|
||||||
onUndecryptableToDevice
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
}, [client, groupCall]);
|
}, [client, groupCall]);
|
||||||
|
|
||||||
|
|||||||
@@ -75,7 +75,6 @@ export function GroupCallView({
|
|||||||
toggleLocalVideoMuted,
|
toggleLocalVideoMuted,
|
||||||
toggleMicrophoneMuted,
|
toggleMicrophoneMuted,
|
||||||
toggleScreensharing,
|
toggleScreensharing,
|
||||||
setMicrophoneMuted,
|
|
||||||
requestingScreenshare,
|
requestingScreenshare,
|
||||||
isScreensharing,
|
isScreensharing,
|
||||||
screenshareFeeds,
|
screenshareFeeds,
|
||||||
@@ -252,7 +251,6 @@ export function GroupCallView({
|
|||||||
localVideoMuted={localVideoMuted}
|
localVideoMuted={localVideoMuted}
|
||||||
toggleLocalVideoMuted={toggleLocalVideoMuted}
|
toggleLocalVideoMuted={toggleLocalVideoMuted}
|
||||||
toggleMicrophoneMuted={toggleMicrophoneMuted}
|
toggleMicrophoneMuted={toggleMicrophoneMuted}
|
||||||
setMicrophoneMuted={setMicrophoneMuted}
|
|
||||||
userMediaFeeds={userMediaFeeds}
|
userMediaFeeds={userMediaFeeds}
|
||||||
activeSpeaker={activeSpeaker}
|
activeSpeaker={activeSpeaker}
|
||||||
onLeave={onLeave}
|
onLeave={onLeave}
|
||||||
|
|||||||
@@ -63,7 +63,6 @@ import { usePrefersReducedMotion } from "../usePrefersReducedMotion";
|
|||||||
import { ParticipantInfo } from "./useGroupCall";
|
import { ParticipantInfo } from "./useGroupCall";
|
||||||
import { TileDescriptor } from "../video-grid/TileDescriptor";
|
import { TileDescriptor } from "../video-grid/TileDescriptor";
|
||||||
import { AudioSink } from "../video-grid/AudioSink";
|
import { AudioSink } from "../video-grid/AudioSink";
|
||||||
import { useCallViewKeyboardShortcuts } from "../useCallViewKeyboardShortcuts";
|
|
||||||
|
|
||||||
const canScreenshare = "getDisplayMedia" in (navigator.mediaDevices ?? {});
|
const canScreenshare = "getDisplayMedia" in (navigator.mediaDevices ?? {});
|
||||||
// There is currently a bug in Safari our our code with cloning and sending MediaStreams
|
// There is currently a bug in Safari our our code with cloning and sending MediaStreams
|
||||||
@@ -82,7 +81,6 @@ interface Props {
|
|||||||
toggleLocalVideoMuted: () => void;
|
toggleLocalVideoMuted: () => void;
|
||||||
toggleMicrophoneMuted: () => void;
|
toggleMicrophoneMuted: () => void;
|
||||||
toggleScreensharing: () => void;
|
toggleScreensharing: () => void;
|
||||||
setMicrophoneMuted: (muted: boolean) => void;
|
|
||||||
userMediaFeeds: CallFeed[];
|
userMediaFeeds: CallFeed[];
|
||||||
activeSpeaker: CallFeed | null;
|
activeSpeaker: CallFeed | null;
|
||||||
onLeave: () => void;
|
onLeave: () => void;
|
||||||
@@ -103,7 +101,6 @@ export function InCallView({
|
|||||||
localVideoMuted,
|
localVideoMuted,
|
||||||
toggleLocalVideoMuted,
|
toggleLocalVideoMuted,
|
||||||
toggleMicrophoneMuted,
|
toggleMicrophoneMuted,
|
||||||
setMicrophoneMuted,
|
|
||||||
userMediaFeeds,
|
userMediaFeeds,
|
||||||
activeSpeaker,
|
activeSpeaker,
|
||||||
onLeave,
|
onLeave,
|
||||||
@@ -144,13 +141,6 @@ export function InCallView({
|
|||||||
|
|
||||||
const { hideScreensharing } = useUrlParams();
|
const { hideScreensharing } = useUrlParams();
|
||||||
|
|
||||||
useCallViewKeyboardShortcuts(
|
|
||||||
!feedbackModalState.isOpen,
|
|
||||||
toggleMicrophoneMuted,
|
|
||||||
toggleLocalVideoMuted,
|
|
||||||
setMicrophoneMuted
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
widget?.api.transport.send(
|
widget?.api.transport.send(
|
||||||
layout === "freedom"
|
layout === "freedom"
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ import { usePageUnload } from "./usePageUnload";
|
|||||||
import { PosthogAnalytics } from "../PosthogAnalytics";
|
import { PosthogAnalytics } from "../PosthogAnalytics";
|
||||||
import { TranslatedError, translatedError } from "../TranslatedError";
|
import { TranslatedError, translatedError } from "../TranslatedError";
|
||||||
import { ElementWidgetActions, ScreenshareStartData, widget } from "../widget";
|
import { ElementWidgetActions, ScreenshareStartData, widget } from "../widget";
|
||||||
|
import { getSetting } from "../settings/useSetting";
|
||||||
|
import { useEventTarget } from "../useEvents";
|
||||||
|
|
||||||
export enum ConnectionState {
|
export enum ConnectionState {
|
||||||
EstablishingCall = "establishing call", // call hasn't been established yet
|
EstablishingCall = "establishing call", // call hasn't been established yet
|
||||||
@@ -58,7 +60,6 @@ export interface UseGroupCallReturnType {
|
|||||||
toggleLocalVideoMuted: () => void;
|
toggleLocalVideoMuted: () => void;
|
||||||
toggleMicrophoneMuted: () => void;
|
toggleMicrophoneMuted: () => void;
|
||||||
toggleScreensharing: () => void;
|
toggleScreensharing: () => void;
|
||||||
setMicrophoneMuted: (muted: boolean) => void;
|
|
||||||
requestingScreenshare: boolean;
|
requestingScreenshare: boolean;
|
||||||
isScreensharing: boolean;
|
isScreensharing: boolean;
|
||||||
screenshareFeeds: CallFeed[];
|
screenshareFeeds: CallFeed[];
|
||||||
@@ -471,6 +472,68 @@ export function useGroupCall(groupCall: GroupCall): UseGroupCallReturnType {
|
|||||||
}
|
}
|
||||||
}, [t, updateState]);
|
}, [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 {
|
return {
|
||||||
state,
|
state,
|
||||||
localCallFeed,
|
localCallFeed,
|
||||||
@@ -485,7 +548,6 @@ export function useGroupCall(groupCall: GroupCall): UseGroupCallReturnType {
|
|||||||
toggleLocalVideoMuted,
|
toggleLocalVideoMuted,
|
||||||
toggleMicrophoneMuted,
|
toggleMicrophoneMuted,
|
||||||
toggleScreensharing,
|
toggleScreensharing,
|
||||||
setMicrophoneMuted,
|
|
||||||
requestingScreenshare,
|
requestingScreenshare,
|
||||||
isScreensharing,
|
isScreensharing,
|
||||||
screenshareFeeds,
|
screenshareFeeds,
|
||||||
|
|||||||
@@ -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 2017 OpenMarket Ltd
|
||||||
Copyright 2018 New Vector Ltd
|
Copyright 2018 New Vector Ltd
|
||||||
Copyright 2019 The 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
|
// actually timestamps. We then purge the remaining logs. We also do this
|
||||||
// purge on startup to prevent logs from accumulating.
|
// 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 { logger } from "matrix-js-sdk/src/logger";
|
||||||
import { randomString } from "matrix-js-sdk/src/randomstring";
|
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)
|
// the length of log data we keep in indexeddb (and include in the reports)
|
||||||
const MAX_LOG_SIZE = 1024 * 1024 * 5; // 5 MB
|
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 = (
|
type LogFunction = (
|
||||||
...args: (Error | DOMException | object | string)[]
|
...args: (Error | DOMException | object | string)[]
|
||||||
) => void;
|
) => void;
|
||||||
@@ -67,7 +76,7 @@ interface LogEntry {
|
|||||||
index?: number;
|
index?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ConsoleLogger extends EventEmitter {
|
export class ConsoleLogger {
|
||||||
private logs = "";
|
private logs = "";
|
||||||
private originalFunctions: { [key in LogFunctionName]?: LogFunction } = {};
|
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(
|
public log(
|
||||||
level: string,
|
level: string,
|
||||||
...args: (Error | DOMException | object | string)[]
|
...args: (Error | DOMException | object | string)[]
|
||||||
@@ -121,27 +137,23 @@ export class ConsoleLogger extends EventEmitter {
|
|||||||
// Using + really is the quickest way in JS
|
// Using + really is the quickest way in JS
|
||||||
// http://jsperf.com/concat-vs-plus-vs-join
|
// http://jsperf.com/concat-vs-plus-vs-join
|
||||||
this.logs += line;
|
this.logs += line;
|
||||||
|
|
||||||
this.emit(ConsoleLoggerEvent.Log);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the log lines to flush to disk and empties the internal log buffer
|
* Retrieve log lines to flush to disk.
|
||||||
* @return {string} \n delimited log lines
|
* @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;
|
const logsToFlush = this.logs;
|
||||||
this.logs = "";
|
this.logs = "";
|
||||||
return logsToFlush;
|
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.
|
// A class which stores log lines in an IndexedDB instance.
|
||||||
@@ -152,14 +164,8 @@ export class IndexedDBLogStore {
|
|||||||
private flushAgainPromise: Promise<void> = null;
|
private flushAgainPromise: Promise<void> = null;
|
||||||
private id: string;
|
private id: string;
|
||||||
|
|
||||||
constructor(
|
constructor(private indexedDB: IDBFactory, private logger: ConsoleLogger) {
|
||||||
private indexedDB: IDBFactory,
|
|
||||||
private loggerInstance: ConsoleLogger
|
|
||||||
) {
|
|
||||||
this.id = "instance-" + randomString(16);
|
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> {
|
public connect(): Promise<void> {
|
||||||
const req = this.indexedDB.open("logs");
|
const req = this.indexedDB.open("logs");
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
req.onsuccess = () => {
|
req.onsuccess = (event: Event) => {
|
||||||
this.db = req.result;
|
// @ts-ignore
|
||||||
|
this.db = event.target.result;
|
||||||
|
// Periodically flush logs to local storage / indexeddb
|
||||||
|
setInterval(this.flush.bind(this), FLUSH_RATE_MS);
|
||||||
resolve();
|
resolve();
|
||||||
};
|
};
|
||||||
|
|
||||||
req.onerror = () => {
|
req.onerror = (event) => {
|
||||||
const err = "Failed to open log database: " + req.error.name;
|
const err =
|
||||||
|
// @ts-ignore
|
||||||
|
"Failed to open log database: " + event.target.error.name;
|
||||||
logger.error(err);
|
logger.error(err);
|
||||||
reject(new Error(err));
|
reject(new Error(err));
|
||||||
};
|
};
|
||||||
|
|
||||||
// First time: Setup the object store
|
// First time: Setup the object store
|
||||||
req.onupgradeneeded = () => {
|
req.onupgradeneeded = (event) => {
|
||||||
const db = req.result;
|
// @ts-ignore
|
||||||
// This is the log entries themselves. Each entry is a chunk of
|
const db = event.target.result;
|
||||||
// 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.
|
|
||||||
const logObjStore = db.createObjectStore("logs", {
|
const logObjStore = db.createObjectStore("logs", {
|
||||||
keyPath: ["id", "index"],
|
keyPath: ["id", "index"],
|
||||||
});
|
});
|
||||||
// Keys in the database look like: [ "instance-148938490", 0 ]
|
// 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.
|
// Later on we need to query everything based on an instance id.
|
||||||
// In order to do this, we need to set up indexes "id".
|
// In order to do this, we need to set up indexes "id".
|
||||||
logObjStore.createIndex("id", "id", { unique: false });
|
logObjStore.createIndex("id", "id", { unique: false });
|
||||||
@@ -201,9 +206,6 @@ export class IndexedDBLogStore {
|
|||||||
this.generateLogEntry(new Date() + " ::: Log database was created.")
|
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", {
|
const lastModifiedStore = db.createObjectStore("logslastmod", {
|
||||||
keyPath: "id",
|
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.
|
* Flush logs to disk.
|
||||||
*
|
*
|
||||||
@@ -251,7 +233,7 @@ export class IndexedDBLogStore {
|
|||||||
*
|
*
|
||||||
* @return {Promise} Resolved when the logs have been flushed.
|
* @return {Promise} Resolved when the logs have been flushed.
|
||||||
*/
|
*/
|
||||||
public flush = (): Promise<void> => {
|
public flush(): Promise<void> {
|
||||||
// check if a flush() operation is ongoing
|
// check if a flush() operation is ongoing
|
||||||
if (this.flushPromise) {
|
if (this.flushPromise) {
|
||||||
if (this.flushAgainPromise) {
|
if (this.flushAgainPromise) {
|
||||||
@@ -276,19 +258,20 @@ export class IndexedDBLogStore {
|
|||||||
reject(new Error("No connected database"));
|
reject(new Error("No connected database"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const lines = this.loggerInstance.popLogs();
|
const lines = this.logger.flush();
|
||||||
if (lines.length === 0) {
|
if (lines.length === 0) {
|
||||||
resolve();
|
resolve();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const txn = this.db.transaction(["logs", "logslastmod"], "readwrite");
|
const txn = this.db.transaction(["logs", "logslastmod"], "readwrite");
|
||||||
const objStore = txn.objectStore("logs");
|
const objStore = txn.objectStore("logs");
|
||||||
txn.oncomplete = () => {
|
txn.oncomplete = (event) => {
|
||||||
resolve();
|
resolve();
|
||||||
};
|
};
|
||||||
txn.onerror = (event) => {
|
txn.onerror = (event) => {
|
||||||
logger.error("Failed to flush logs : ", 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));
|
objStore.add(this.generateLogEntry(lines));
|
||||||
const lastModStore = txn.objectStore("logslastmod");
|
const lastModStore = txn.objectStore("logslastmod");
|
||||||
@@ -297,7 +280,7 @@ export class IndexedDBLogStore {
|
|||||||
this.flushPromise = null;
|
this.flushPromise = null;
|
||||||
});
|
});
|
||||||
return this.flushPromise;
|
return this.flushPromise;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Consume the most recent logs and return them. Older logs which are not
|
* Consume the most recent logs and return them. Older logs which are not
|
||||||
@@ -324,11 +307,13 @@ export class IndexedDBLogStore {
|
|||||||
.index("id")
|
.index("id")
|
||||||
.openCursor(IDBKeyRange.only(id), "prev");
|
.openCursor(IDBKeyRange.only(id), "prev");
|
||||||
let lines = "";
|
let lines = "";
|
||||||
query.onerror = () => {
|
query.onerror = (event) => {
|
||||||
reject(new Error("Query failed: " + query.error.message));
|
// @ts-ignore
|
||||||
|
reject(new Error("Query failed: " + event.target.errorCode));
|
||||||
};
|
};
|
||||||
query.onsuccess = () => {
|
query.onsuccess = (event) => {
|
||||||
const cursor = query.result;
|
// @ts-ignore
|
||||||
|
const cursor = event.target.result;
|
||||||
if (!cursor) {
|
if (!cursor) {
|
||||||
resolve(lines);
|
resolve(lines);
|
||||||
return; // end of results
|
return; // end of results
|
||||||
@@ -370,8 +355,9 @@ export class IndexedDBLogStore {
|
|||||||
const o = txn.objectStore("logs");
|
const o = txn.objectStore("logs");
|
||||||
// only load the key path, not the data which may be huge
|
// only load the key path, not the data which may be huge
|
||||||
const query = o.index("id").openKeyCursor(IDBKeyRange.only(id));
|
const query = o.index("id").openKeyCursor(IDBKeyRange.only(id));
|
||||||
query.onsuccess = () => {
|
query.onsuccess = (event) => {
|
||||||
const cursor = query.result;
|
// @ts-ignore
|
||||||
|
const cursor = event.target.result;
|
||||||
if (!cursor) {
|
if (!cursor) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -381,10 +367,12 @@ export class IndexedDBLogStore {
|
|||||||
txn.oncomplete = () => {
|
txn.oncomplete = () => {
|
||||||
resolve();
|
resolve();
|
||||||
};
|
};
|
||||||
txn.onerror = () => {
|
txn.onerror = (event) => {
|
||||||
reject(
|
reject(
|
||||||
new Error(
|
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);
|
const query = store.openCursor(keyRange);
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const results = [];
|
const results = [];
|
||||||
query.onerror = () => {
|
query.onerror = (event) => {
|
||||||
reject(new Error("Query failed: " + query.error.message));
|
// @ts-ignore
|
||||||
|
reject(new Error("Query failed: " + event.target.errorCode));
|
||||||
};
|
};
|
||||||
// collect results
|
// collect results
|
||||||
query.onsuccess = () => {
|
query.onsuccess = (event) => {
|
||||||
const cursor = query.result;
|
// @ts-ignore
|
||||||
|
const cursor = event.target.result;
|
||||||
if (!cursor) {
|
if (!cursor) {
|
||||||
resolve(results);
|
resolve(results);
|
||||||
return; // end of results
|
return; // end of results
|
||||||
@@ -489,6 +479,8 @@ declare global {
|
|||||||
// eslint-disable-next-line no-var, camelcase
|
// eslint-disable-next-line no-var, camelcase
|
||||||
var mx_rage_logger: ConsoleLogger;
|
var mx_rage_logger: ConsoleLogger;
|
||||||
// eslint-disable-next-line no-var, camelcase
|
// 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>;
|
var mx_rage_initStoragePromise: Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -499,11 +491,19 @@ declare global {
|
|||||||
* be set up immediately for the logs.
|
* be set up immediately for the logs.
|
||||||
* @return {Promise} Resolves when set up.
|
* @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 = new ConsoleLogger();
|
||||||
global.mx_rage_logger.monkeyPatch(window.console);
|
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 {
|
} else {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
lines: global.mx_rage_logger.peekLogs(),
|
lines: global.mx_rage_logger.flush(true),
|
||||||
id: "-",
|
id: "-",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -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])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -31,14 +31,15 @@ export const AudioSink: React.FC<Props> = ({
|
|||||||
tileDescriptor,
|
tileDescriptor,
|
||||||
audioOutput,
|
audioOutput,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { audioMuted, localVolume, stream } = useCallFeed(
|
const { localVolume, stream } = useCallFeed(tileDescriptor.callFeed);
|
||||||
tileDescriptor.callFeed
|
|
||||||
);
|
|
||||||
|
|
||||||
const audioElementRef = useMediaStream(
|
const audioElementRef = useMediaStream(
|
||||||
stream,
|
stream,
|
||||||
audioOutput,
|
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
|
localVolume
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ interface Props {
|
|||||||
mediaRef?: React.RefObject<MediaElement>;
|
mediaRef?: React.RefObject<MediaElement>;
|
||||||
onOptionsPress?: () => void;
|
onOptionsPress?: () => void;
|
||||||
localVolume?: number;
|
localVolume?: number;
|
||||||
|
hasAudio?: boolean;
|
||||||
maximised?: boolean;
|
maximised?: boolean;
|
||||||
fullscreen?: boolean;
|
fullscreen?: boolean;
|
||||||
onFullscreen?: () => void;
|
onFullscreen?: () => void;
|
||||||
@@ -58,6 +59,7 @@ export const VideoTile = forwardRef<HTMLDivElement, Props>(
|
|||||||
mediaRef,
|
mediaRef,
|
||||||
onOptionsPress,
|
onOptionsPress,
|
||||||
localVolume,
|
localVolume,
|
||||||
|
hasAudio,
|
||||||
maximised,
|
maximised,
|
||||||
fullscreen,
|
fullscreen,
|
||||||
onFullscreen,
|
onFullscreen,
|
||||||
@@ -74,14 +76,16 @@ export const VideoTile = forwardRef<HTMLDivElement, Props>(
|
|||||||
|
|
||||||
const toolbarButtons: JSX.Element[] = [];
|
const toolbarButtons: JSX.Element[] = [];
|
||||||
if (connectionState == ConnectionState.Connected && !isLocal) {
|
if (connectionState == ConnectionState.Connected && !isLocal) {
|
||||||
toolbarButtons.push(
|
if (hasAudio) {
|
||||||
<AudioButton
|
toolbarButtons.push(
|
||||||
key="localVolume"
|
<AudioButton
|
||||||
className={styles.button}
|
key="localVolume"
|
||||||
volume={localVolume}
|
className={styles.button}
|
||||||
onPress={onOptionsPress}
|
volume={localVolume}
|
||||||
/>
|
onPress={onOptionsPress}
|
||||||
);
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (screenshare) {
|
if (screenshare) {
|
||||||
toolbarButtons.push(
|
toolbarButtons.push(
|
||||||
@@ -137,7 +141,13 @@ export const VideoTile = forwardRef<HTMLDivElement, Props>(
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className={classNames(styles.infoBubble, styles.memberName)}>
|
<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 />}
|
{videoMuted && <VideoMutedIcon />}
|
||||||
<span title={caption}>{caption}</span>
|
<span title={caption}>{caption}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ export function VideoTileContainer({
|
|||||||
audioMuted,
|
audioMuted,
|
||||||
videoMuted,
|
videoMuted,
|
||||||
localVolume,
|
localVolume,
|
||||||
|
hasAudio,
|
||||||
speaking,
|
speaking,
|
||||||
stream,
|
stream,
|
||||||
purpose,
|
purpose,
|
||||||
@@ -109,6 +110,7 @@ export function VideoTileContainer({
|
|||||||
avatar={getAvatar && getAvatar(item.member, width, height)}
|
avatar={getAvatar && getAvatar(item.member, width, height)}
|
||||||
onOptionsPress={onOptionsPress}
|
onOptionsPress={onOptionsPress}
|
||||||
localVolume={localVolume}
|
localVolume={localVolume}
|
||||||
|
hasAudio={hasAudio}
|
||||||
maximised={maximised}
|
maximised={maximised}
|
||||||
fullscreen={fullscreen}
|
fullscreen={fullscreen}
|
||||||
onFullscreen={onFullscreenCallback}
|
onFullscreen={onFullscreenCallback}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ interface CallFeedState {
|
|||||||
videoMuted: boolean;
|
videoMuted: boolean;
|
||||||
audioMuted: boolean;
|
audioMuted: boolean;
|
||||||
localVolume: number;
|
localVolume: number;
|
||||||
|
hasAudio: boolean;
|
||||||
disposed: boolean | undefined;
|
disposed: boolean | undefined;
|
||||||
stream: MediaStream | undefined;
|
stream: MediaStream | undefined;
|
||||||
purpose: SDPStreamMetadataPurpose | undefined;
|
purpose: SDPStreamMetadataPurpose | undefined;
|
||||||
@@ -38,6 +39,7 @@ function getCallFeedState(callFeed: CallFeed | undefined): CallFeedState {
|
|||||||
videoMuted: callFeed ? callFeed.isVideoMuted() : true,
|
videoMuted: callFeed ? callFeed.isVideoMuted() : true,
|
||||||
audioMuted: callFeed ? callFeed.isAudioMuted() : true,
|
audioMuted: callFeed ? callFeed.isAudioMuted() : true,
|
||||||
localVolume: callFeed ? callFeed.getLocalVolume() : 0,
|
localVolume: callFeed ? callFeed.getLocalVolume() : 0,
|
||||||
|
hasAudio: callFeed ? callFeed.stream.getAudioTracks().length >= 1 : false,
|
||||||
disposed: callFeed ? callFeed.disposed : undefined,
|
disposed: callFeed ? callFeed.disposed : undefined,
|
||||||
stream: callFeed ? callFeed.stream : undefined,
|
stream: callFeed ? callFeed.stream : undefined,
|
||||||
purpose: callFeed ? callFeed.purpose : undefined,
|
purpose: callFeed ? callFeed.purpose : undefined,
|
||||||
|
|||||||
64
yarn.lock
64
yarn.lock
@@ -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"
|
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==
|
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":
|
"@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.8"
|
version "3.2.14"
|
||||||
resolved "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.8.tgz#8d53636d045e1776e2a2ec6613e57330dd9ce856"
|
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":
|
"@mdx-js/mdx@^1.6.22":
|
||||||
version "1.6.22"
|
version "1.6.22"
|
||||||
@@ -2418,13 +2418,13 @@
|
|||||||
"@sentry/utils" "6.19.7"
|
"@sentry/utils" "6.19.7"
|
||||||
tslib "^1.9.3"
|
tslib "^1.9.3"
|
||||||
|
|
||||||
"@sentry/core@7.31.0":
|
"@sentry/core@7.28.1":
|
||||||
version "7.31.0"
|
version "7.28.1"
|
||||||
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.31.0.tgz#2c1ec086cd86c097fc5c60703d33c848089597c7"
|
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.28.1.tgz#c712ce17469b18b01606108817be24a99ed2116e"
|
||||||
integrity sha512-IZS1MZznyBOPw7UEpZwq3t3aaaVhFB+r3KM4JYFSJRr7Ky9TjldXA3hadNUTztjYGgEC3u8kB9jXoRvNXM2hqA==
|
integrity sha512-7wvnuvn/mrAfcugWoCG/3pqDIrUgH5t+HisMJMGw0h9Tc33KqrmqMDCQVvjlrr2pWrw/vuUCFdm8CbUHJ832oQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@sentry/types" "7.31.0"
|
"@sentry/types" "7.28.1"
|
||||||
"@sentry/utils" "7.31.0"
|
"@sentry/utils" "7.28.1"
|
||||||
tslib "^1.9.3"
|
tslib "^1.9.3"
|
||||||
|
|
||||||
"@sentry/hub@6.19.7":
|
"@sentry/hub@6.19.7":
|
||||||
@@ -2446,13 +2446,13 @@
|
|||||||
tslib "^1.9.3"
|
tslib "^1.9.3"
|
||||||
|
|
||||||
"@sentry/node@^7.19.0":
|
"@sentry/node@^7.19.0":
|
||||||
version "7.31.0"
|
version "7.28.1"
|
||||||
resolved "https://registry.yarnpkg.com/@sentry/node/-/node-7.31.0.tgz#d6b6776f7c1daee0a41e7f97fc15f058435f1b1c"
|
resolved "https://registry.yarnpkg.com/@sentry/node/-/node-7.28.1.tgz#fc53675a048c29c86e5a8cd3ba570c454f492c18"
|
||||||
integrity sha512-DBjPfThZ5CIC2G9/CVFRlSOP/QqF1IoZXNpTUPZkhQ1cjShJeERT64jMkTdk+RAStSTpEfF6J0rUy1NIyHHEoQ==
|
integrity sha512-n7AbpJqZJjWPpKNGc55mP7AdQ+XSomS9MZJuZ+Xt2AU52aVwGPI4z9aHUJFSDGaMHHiu/toyPnoUES+XZf6/hw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@sentry/core" "7.31.0"
|
"@sentry/core" "7.28.1"
|
||||||
"@sentry/types" "7.31.0"
|
"@sentry/types" "7.28.1"
|
||||||
"@sentry/utils" "7.31.0"
|
"@sentry/utils" "7.28.1"
|
||||||
cookie "^0.4.1"
|
cookie "^0.4.1"
|
||||||
https-proxy-agent "^5.0.0"
|
https-proxy-agent "^5.0.0"
|
||||||
lru_map "^0.3.3"
|
lru_map "^0.3.3"
|
||||||
@@ -2482,13 +2482,13 @@
|
|||||||
tslib "^1.9.3"
|
tslib "^1.9.3"
|
||||||
|
|
||||||
"@sentry/tracing@^7.19.0":
|
"@sentry/tracing@^7.19.0":
|
||||||
version "7.31.0"
|
version "7.28.1"
|
||||||
resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-7.31.0.tgz#c37f1930aba05a4c461f655dc258eaa851e9a305"
|
resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-7.28.1.tgz#d276e4d17a79190a88112696c73de12c209607a1"
|
||||||
integrity sha512-p/b9sOw2wwcDLp8p0bJ0oetgeEB1q/ueZaXDeBeSh+3GNKx6J4S3pcFpbMXDK8d2Ayd3P9Gvrm7y9Hc2ueJteg==
|
integrity sha512-uWspnuz+7FyW8ES5lRaVA7O/YJSzMlSkvBFtgzaoKmdaueokU/sRLwlCsrdgwavG1wpm79df7R1iiSeqhaXDlw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@sentry/core" "7.31.0"
|
"@sentry/core" "7.28.1"
|
||||||
"@sentry/types" "7.31.0"
|
"@sentry/types" "7.28.1"
|
||||||
"@sentry/utils" "7.31.0"
|
"@sentry/utils" "7.28.1"
|
||||||
tslib "^1.9.3"
|
tslib "^1.9.3"
|
||||||
|
|
||||||
"@sentry/types@6.19.7":
|
"@sentry/types@6.19.7":
|
||||||
@@ -2496,10 +2496,10 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.19.7.tgz#c6b337912e588083fc2896eb012526cf7cfec7c7"
|
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.19.7.tgz#c6b337912e588083fc2896eb012526cf7cfec7c7"
|
||||||
integrity sha512-jH84pDYE+hHIbVnab3Hr+ZXr1v8QABfhx39KknxqKWr2l0oEItzepV0URvbEhB446lk/S/59230dlUUIBGsXbg==
|
integrity sha512-jH84pDYE+hHIbVnab3Hr+ZXr1v8QABfhx39KknxqKWr2l0oEItzepV0URvbEhB446lk/S/59230dlUUIBGsXbg==
|
||||||
|
|
||||||
"@sentry/types@7.31.0":
|
"@sentry/types@7.28.1":
|
||||||
version "7.31.0"
|
version "7.28.1"
|
||||||
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.31.0.tgz#c028519a660d76c4e8f18ba9e44ede45d314fcae"
|
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.28.1.tgz#9018b4c152b475de9bedd267237393d3c9b1253d"
|
||||||
integrity sha512-nFqo7wyMnapdSEdw1MD+cavDtD9x5QQmh/bwLEOb/euM0cHFJHYyD7CveY/mQng4HyEVWY+DCtX/7E3GcQ7Bdw==
|
integrity sha512-DvSplMVrVEmOzR2M161V5+B8Up3vR71xMqJOpWTzE9TqtFJRGPtqT/5OBsNJJw1+/j2ssMcnKwbEo9Q2EGeS6g==
|
||||||
|
|
||||||
"@sentry/types@^7.2.0":
|
"@sentry/types@^7.2.0":
|
||||||
version "7.13.0"
|
version "7.13.0"
|
||||||
@@ -2514,12 +2514,12 @@
|
|||||||
"@sentry/types" "6.19.7"
|
"@sentry/types" "6.19.7"
|
||||||
tslib "^1.9.3"
|
tslib "^1.9.3"
|
||||||
|
|
||||||
"@sentry/utils@7.31.0":
|
"@sentry/utils@7.28.1":
|
||||||
version "7.31.0"
|
version "7.28.1"
|
||||||
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.31.0.tgz#507290984c941fca8f5c2621a4b8b0aca8fbfc10"
|
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.28.1.tgz#0a7b6aa4b09e91e4d1aded2a8c8dbaf818cee96e"
|
||||||
integrity sha512-B1KkvdfwlaqM7sDp3/yk2No7WsbMuLEywGRVOLzXeTqTLSBRBWyyYIudqPtx2LDds9anlUHj21zs9FKY+S3eiA==
|
integrity sha512-75/jzLUO9HH09iC9TslNimGbxOP3jgn89P+q7uR+rp2fJfRExHVeKJZQdK0Ij4/SmE7TJ3Uh2r154N0INZEx1g==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@sentry/types" "7.31.0"
|
"@sentry/types" "7.28.1"
|
||||||
tslib "^1.9.3"
|
tslib "^1.9.3"
|
||||||
|
|
||||||
"@sentry/vite-plugin@^0.3.0":
|
"@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"
|
resolved "https://registry.yarnpkg.com/matrix-events-sdk/-/matrix-events-sdk-0.0.1.tgz#c8c38911e2cb29023b0bbac8d6f32e0de2c957dd"
|
||||||
integrity sha512-1QEOsXO+bhyCroIe2/A5OwaxHvBm7EsSQ46DEDn8RBIfQwN5HWBpFvyWWR4QY0KHPPnnJdI99wgRiAl7Ad5qaA==
|
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"
|
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:
|
dependencies:
|
||||||
"@babel/runtime" "^7.12.5"
|
"@babel/runtime" "^7.12.5"
|
||||||
"@matrix-org/matrix-sdk-crypto-js" "^0.1.0-alpha.2"
|
"@matrix-org/matrix-sdk-crypto-js" "^0.1.0-alpha.2"
|
||||||
|
|||||||
Reference in New Issue
Block a user