mirror of
https://github.com/vector-im/element-call.git
synced 2024-11-24 00:38:31 +08:00
Add debug log inspector / rageshake
This commit is contained in:
parent
91366585ff
commit
76b2e8b29e
3
.env
3
.env
@ -7,6 +7,9 @@
|
|||||||
# Used for determining the homeserver to use for short urls etc.
|
# Used for determining the homeserver to use for short urls etc.
|
||||||
# VITE_DEFAULT_HOMESERVER=http://localhost:8008
|
# VITE_DEFAULT_HOMESERVER=http://localhost:8008
|
||||||
|
|
||||||
|
# Used for submitting debug logs to an external rageshake server
|
||||||
|
# VITE_RAGESHAKE_SUBMIT_URL=http://localhost:9110/api/submit
|
||||||
|
|
||||||
# The Sentry DSN to use for error reporting. Leave undefined to disable.
|
# The Sentry DSN to use for error reporting. Leave undefined to disable.
|
||||||
# VITE_SENTRY_DSN=https://examplePublicKey@o0.ingest.sentry.io/0
|
# VITE_SENTRY_DSN=https://examplePublicKey@o0.ingest.sentry.io/0
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
"matrix-react-sdk": "github:matrix-org/matrix-react-sdk#robertlong/group-call",
|
"matrix-react-sdk": "github:matrix-org/matrix-react-sdk#robertlong/group-call",
|
||||||
"mermaid": "^8.13.8",
|
"mermaid": "^8.13.8",
|
||||||
"normalize.css": "^8.0.1",
|
"normalize.css": "^8.0.1",
|
||||||
|
"pako": "^2.0.4",
|
||||||
"postcss-preset-env": "^6.7.0",
|
"postcss-preset-env": "^6.7.0",
|
||||||
"re-resizable": "^6.9.0",
|
"re-resizable": "^6.9.0",
|
||||||
"react": "^17.0.0",
|
"react": "^17.0.0",
|
||||||
|
@ -25,6 +25,7 @@ import { RoomPage } from "./room/RoomPage";
|
|||||||
import { RoomRedirect } from "./room/RoomRedirect";
|
import { RoomRedirect } from "./room/RoomRedirect";
|
||||||
import { ClientProvider } from "./ClientContext";
|
import { ClientProvider } from "./ClientContext";
|
||||||
import { usePageFocusStyle } from "./usePageFocusStyle";
|
import { usePageFocusStyle } from "./usePageFocusStyle";
|
||||||
|
import { SequenceDiagramViewerPage } from "./SequenceDiagramViewerPage";
|
||||||
|
|
||||||
const SentryRoute = Sentry.withSentryRouting(Route);
|
const SentryRoute = Sentry.withSentryRouting(Route);
|
||||||
|
|
||||||
@ -48,6 +49,9 @@ export default function App({ history }) {
|
|||||||
<SentryRoute path="/room/:roomId?">
|
<SentryRoute path="/room/:roomId?">
|
||||||
<RoomPage />
|
<RoomPage />
|
||||||
</SentryRoute>
|
</SentryRoute>
|
||||||
|
<SentryRoute path="/inspector">
|
||||||
|
<SequenceDiagramViewerPage />
|
||||||
|
</SentryRoute>
|
||||||
<SentryRoute path="*">
|
<SentryRoute path="*">
|
||||||
<RoomRedirect />
|
<RoomRedirect />
|
||||||
</SentryRoute>
|
</SentryRoute>
|
||||||
|
38
src/SequenceDiagramViewerPage.jsx
Normal file
38
src/SequenceDiagramViewerPage.jsx
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import React, { useCallback, useState } from "react";
|
||||||
|
import { SequenceDiagramViewer } from "./room/GroupCallInspector";
|
||||||
|
import { FieldRow, InputField } from "./input/Input";
|
||||||
|
|
||||||
|
export function SequenceDiagramViewerPage() {
|
||||||
|
const [debugLog, setDebugLog] = useState();
|
||||||
|
const [selectedUserId, setSelectedUserId] = useState();
|
||||||
|
const onChangeDebugLog = useCallback((e) => {
|
||||||
|
if (e.target.files && e.target.files.length > 0) {
|
||||||
|
e.target.files[0].text().then((text) => {
|
||||||
|
setDebugLog(JSON.parse(text));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ marginTop: 20 }}>
|
||||||
|
<FieldRow>
|
||||||
|
<InputField
|
||||||
|
type="file"
|
||||||
|
id="debugLog"
|
||||||
|
name="debugLog"
|
||||||
|
label="Debug Log"
|
||||||
|
onChange={onChangeDebugLog}
|
||||||
|
/>
|
||||||
|
</FieldRow>
|
||||||
|
{debugLog && (
|
||||||
|
<SequenceDiagramViewer
|
||||||
|
localUserId={debugLog.localUserId}
|
||||||
|
selectedUserId={selectedUserId}
|
||||||
|
onSelectUserId={setSelectedUserId}
|
||||||
|
remoteUserIds={debugLog.remoteUserIds}
|
||||||
|
events={debugLog.eventsByUserId[selectedUserId]}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -22,6 +22,10 @@ import App from "./App";
|
|||||||
import * as Sentry from "@sentry/react";
|
import * as Sentry from "@sentry/react";
|
||||||
import { Integrations } from "@sentry/tracing";
|
import { Integrations } from "@sentry/tracing";
|
||||||
import { ErrorView } from "./FullScreenView";
|
import { ErrorView } from "./FullScreenView";
|
||||||
|
import * as rageshake from "matrix-react-sdk/src/rageshake/rageshake";
|
||||||
|
import { InspectorContextProvider } from "./room/GroupCallInspector";
|
||||||
|
|
||||||
|
rageshake.init();
|
||||||
|
|
||||||
if (import.meta.env.VITE_CUSTOM_THEME) {
|
if (import.meta.env.VITE_CUSTOM_THEME) {
|
||||||
const style = document.documentElement.style;
|
const style = document.documentElement.style;
|
||||||
@ -59,7 +63,9 @@ Sentry.init({
|
|||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<Sentry.ErrorBoundary fallback={ErrorView}>
|
<Sentry.ErrorBoundary fallback={ErrorView}>
|
||||||
<App history={history} />
|
<InspectorContextProvider>
|
||||||
|
<App history={history} />
|
||||||
|
</InspectorContextProvider>
|
||||||
</Sentry.ErrorBoundary>
|
</Sentry.ErrorBoundary>
|
||||||
</React.StrictMode>,
|
</React.StrictMode>,
|
||||||
document.getElementById("root")
|
document.getElementById("root")
|
||||||
|
@ -1,5 +1,12 @@
|
|||||||
import { Resizable } from "re-resizable";
|
import { Resizable } from "re-resizable";
|
||||||
import React, { useEffect, useState, useReducer, useRef } from "react";
|
import React, {
|
||||||
|
useEffect,
|
||||||
|
useState,
|
||||||
|
useReducer,
|
||||||
|
useRef,
|
||||||
|
createContext,
|
||||||
|
useContext,
|
||||||
|
} from "react";
|
||||||
import ReactJson from "react-json-view";
|
import ReactJson from "react-json-view";
|
||||||
import mermaid from "mermaid";
|
import mermaid from "mermaid";
|
||||||
import styles from "./GroupCallInspector.module.css";
|
import styles from "./GroupCallInspector.module.css";
|
||||||
@ -90,7 +97,18 @@ function formatTimestamp(timestamp) {
|
|||||||
return dateFormatter.format(timestamp);
|
return dateFormatter.format(timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
function SequenceDiagramViewer({
|
export const InspectorContext = createContext();
|
||||||
|
|
||||||
|
export function InspectorContextProvider({ children }) {
|
||||||
|
const context = useState({});
|
||||||
|
return (
|
||||||
|
<InspectorContext.Provider value={context}>
|
||||||
|
{children}
|
||||||
|
</InspectorContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SequenceDiagramViewer({
|
||||||
localUserId,
|
localUserId,
|
||||||
remoteUserIds,
|
remoteUserIds,
|
||||||
selectedUserId,
|
selectedUserId,
|
||||||
@ -168,16 +186,16 @@ function reducer(state, action) {
|
|||||||
const fromId = event.getStateKey();
|
const fromId = event.getStateKey();
|
||||||
|
|
||||||
remoteUserIds =
|
remoteUserIds =
|
||||||
fromId === state.localUserId || eventsByUserId.has(fromId)
|
fromId === state.localUserId || eventsByUserId[fromId]
|
||||||
? state.remoteUserIds
|
? state.remoteUserIds
|
||||||
: [...state.remoteUserIds, fromId];
|
: [...state.remoteUserIds, fromId];
|
||||||
|
|
||||||
eventsByUserId = new Map(state.eventsByUserId);
|
eventsByUserId = { ...state.eventsByUserId };
|
||||||
|
|
||||||
if (event.getStateKey() === state.localUserId) {
|
if (event.getStateKey() === state.localUserId) {
|
||||||
for (const userId in eventsByUserId) {
|
for (const userId in eventsByUserId) {
|
||||||
eventsByUserId.set(userId, [
|
eventsByUserId[userId] = [
|
||||||
...(eventsByUserId.get(userId) || []),
|
...(eventsByUserId[userId] || []),
|
||||||
{
|
{
|
||||||
from: fromId,
|
from: fromId,
|
||||||
to: "Room",
|
to: "Room",
|
||||||
@ -186,11 +204,11 @@ function reducer(state, action) {
|
|||||||
timestamp: event.getTs() || Date.now(),
|
timestamp: event.getTs() || Date.now(),
|
||||||
ignored: false,
|
ignored: false,
|
||||||
},
|
},
|
||||||
]);
|
];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
eventsByUserId.set(fromId, [
|
eventsByUserId[fromId] = [
|
||||||
...(eventsByUserId.get(fromId) || []),
|
...(eventsByUserId[fromId] || []),
|
||||||
{
|
{
|
||||||
from: fromId,
|
from: fromId,
|
||||||
to: "Room",
|
to: "Room",
|
||||||
@ -199,7 +217,7 @@ function reducer(state, action) {
|
|||||||
timestamp: event.getTs() || Date.now(),
|
timestamp: event.getTs() || Date.now(),
|
||||||
ignored: false,
|
ignored: false,
|
||||||
},
|
},
|
||||||
]);
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,17 +233,17 @@ function reducer(state, action) {
|
|||||||
}
|
}
|
||||||
case "receive_to_device_event": {
|
case "receive_to_device_event": {
|
||||||
const event = action.event;
|
const event = action.event;
|
||||||
const eventsByUserId = new Map(state.eventsByUserId);
|
const eventsByUserId = { ...state.eventsByUserId };
|
||||||
const fromId = event.getSender();
|
const fromId = event.getSender();
|
||||||
const toId = state.localUserId;
|
const toId = state.localUserId;
|
||||||
const content = event.getContent();
|
const content = event.getContent();
|
||||||
|
|
||||||
const remoteUserIds = eventsByUserId.has(fromId)
|
const remoteUserIds = eventsByUserId[fromId]
|
||||||
? state.remoteUserIds
|
? state.remoteUserIds
|
||||||
: [...state.remoteUserIds, fromId];
|
: [...state.remoteUserIds, fromId];
|
||||||
|
|
||||||
eventsByUserId.set(fromId, [
|
eventsByUserId[fromId] = [
|
||||||
...(eventsByUserId.get(fromId) || []),
|
...(eventsByUserId[fromId] || []),
|
||||||
{
|
{
|
||||||
from: fromId,
|
from: fromId,
|
||||||
to: toId,
|
to: toId,
|
||||||
@ -234,22 +252,22 @@ function reducer(state, action) {
|
|||||||
timestamp: event.getTs() || Date.now(),
|
timestamp: event.getTs() || Date.now(),
|
||||||
ignored: state.localSessionId !== content.dest_session_id,
|
ignored: state.localSessionId !== content.dest_session_id,
|
||||||
},
|
},
|
||||||
]);
|
];
|
||||||
|
|
||||||
return { ...state, eventsByUserId, remoteUserIds };
|
return { ...state, eventsByUserId, remoteUserIds };
|
||||||
}
|
}
|
||||||
case "send_voip_event": {
|
case "send_voip_event": {
|
||||||
const event = action.event;
|
const event = action.event;
|
||||||
const eventsByUserId = new Map(state.eventsByUserId);
|
const eventsByUserId = { ...state.eventsByUserId };
|
||||||
const fromId = state.localUserId;
|
const fromId = state.localUserId;
|
||||||
const toId = event.userId;
|
const toId = event.userId;
|
||||||
|
|
||||||
const remoteUserIds = eventsByUserId.has(toId)
|
const remoteUserIds = eventsByUserId[toId]
|
||||||
? state.remoteUserIds
|
? state.remoteUserIds
|
||||||
: [...state.remoteUserIds, toId];
|
: [...state.remoteUserIds, toId];
|
||||||
|
|
||||||
eventsByUserId.set(toId, [
|
eventsByUserId[toId] = [
|
||||||
...(eventsByUserId.get(toId) || []),
|
...(eventsByUserId[toId] || []),
|
||||||
{
|
{
|
||||||
from: fromId,
|
from: fromId,
|
||||||
to: toId,
|
to: toId,
|
||||||
@ -258,7 +276,7 @@ function reducer(state, action) {
|
|||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
ignored: false,
|
ignored: false,
|
||||||
},
|
},
|
||||||
]);
|
];
|
||||||
|
|
||||||
return { ...state, eventsByUserId, remoteUserIds };
|
return { ...state, eventsByUserId, remoteUserIds };
|
||||||
}
|
}
|
||||||
@ -271,7 +289,7 @@ function useGroupCallState(client, groupCall, pollCallStats) {
|
|||||||
const [state, dispatch] = useReducer(reducer, {
|
const [state, dispatch] = useReducer(reducer, {
|
||||||
localUserId: client.getUserId(),
|
localUserId: client.getUserId(),
|
||||||
localSessionId: client.getSessionId(),
|
localSessionId: client.getSessionId(),
|
||||||
eventsByUserId: new Map(),
|
eventsByUserId: {},
|
||||||
remoteUserIds: [],
|
remoteUserIds: [],
|
||||||
callStateEvent: null,
|
callStateEvent: null,
|
||||||
memberStateEvents: {},
|
memberStateEvents: {},
|
||||||
@ -399,6 +417,12 @@ export function GroupCallInspector({ client, groupCall, show }) {
|
|||||||
const [selectedUserId, setSelectedUserId] = useState();
|
const [selectedUserId, setSelectedUserId] = useState();
|
||||||
const state = useGroupCallState(client, groupCall, show);
|
const state = useGroupCallState(client, groupCall, show);
|
||||||
|
|
||||||
|
const [_, setState] = useContext(InspectorContext);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setState({ json: state });
|
||||||
|
}, [setState, state]);
|
||||||
|
|
||||||
if (!show) {
|
if (!show) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -421,16 +445,13 @@ export function GroupCallInspector({ client, groupCall, show }) {
|
|||||||
selectedUserId={selectedUserId}
|
selectedUserId={selectedUserId}
|
||||||
onSelectUserId={setSelectedUserId}
|
onSelectUserId={setSelectedUserId}
|
||||||
remoteUserIds={state.remoteUserIds}
|
remoteUserIds={state.remoteUserIds}
|
||||||
events={state.eventsByUserId.get(selectedUserId)}
|
events={state.eventsByUserId[selectedUserId]}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{currentTab === "inspector" && (
|
{currentTab === "inspector" && (
|
||||||
<ReactJson
|
<ReactJson
|
||||||
theme="monokai"
|
theme="monokai"
|
||||||
src={{
|
src={state}
|
||||||
...state,
|
|
||||||
eventsByUserId: Object.fromEntries(state.eventsByUserId),
|
|
||||||
}}
|
|
||||||
name={null}
|
name={null}
|
||||||
indentWidth={2}
|
indentWidth={2}
|
||||||
shouldCollapse={shouldCollapse}
|
shouldCollapse={shouldCollapse}
|
||||||
|
@ -9,6 +9,8 @@ import { SelectInput } from "../input/SelectInput";
|
|||||||
import { Item } from "@react-stately/collections";
|
import { Item } from "@react-stately/collections";
|
||||||
import { useMediaHandler } from "./useMediaHandler";
|
import { useMediaHandler } from "./useMediaHandler";
|
||||||
import { FieldRow, InputField } from "../input/Input";
|
import { FieldRow, InputField } from "../input/Input";
|
||||||
|
import { Button } from "../button";
|
||||||
|
import { useSubmitRageshake } from "./useSubmitRageshake";
|
||||||
|
|
||||||
export function SettingsModal({
|
export function SettingsModal({
|
||||||
client,
|
client,
|
||||||
@ -25,6 +27,8 @@ export function SettingsModal({
|
|||||||
setVideoInput,
|
setVideoInput,
|
||||||
} = useMediaHandler(client);
|
} = useMediaHandler(client);
|
||||||
|
|
||||||
|
const { submitRageshake, downloadDebugLog } = useSubmitRageshake();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
title="Settings"
|
title="Settings"
|
||||||
@ -88,6 +92,12 @@ export function SettingsModal({
|
|||||||
onChange={(e) => setShowInspector(e.target.checked)}
|
onChange={(e) => setShowInspector(e.target.checked)}
|
||||||
/>
|
/>
|
||||||
</FieldRow>
|
</FieldRow>
|
||||||
|
<FieldRow>
|
||||||
|
<Button onPress={submitRageshake}>Send Debug Logs</Button>
|
||||||
|
</FieldRow>
|
||||||
|
<FieldRow>
|
||||||
|
<Button onPress={downloadDebugLog}>Download Debug Logs</Button>
|
||||||
|
</FieldRow>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
</TabContainer>
|
</TabContainer>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
209
src/settings/useSubmitRageshake.js
Normal file
209
src/settings/useSubmitRageshake.js
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
import { useCallback, useContext } from "react";
|
||||||
|
import * as rageshake from "matrix-react-sdk/src/rageshake/rageshake";
|
||||||
|
import pako from "pako";
|
||||||
|
import { useClient } from "../ClientContext";
|
||||||
|
import { InspectorContext } from "../room/GroupCallInspector";
|
||||||
|
|
||||||
|
export function useSubmitRageshake() {
|
||||||
|
const { client } = useClient();
|
||||||
|
const [{ json, svg }] = useContext(InspectorContext);
|
||||||
|
|
||||||
|
const submitRageshake = useCallback(
|
||||||
|
async (opts) => {
|
||||||
|
let userAgent = "UNKNOWN";
|
||||||
|
if (window.navigator && window.navigator.userAgent) {
|
||||||
|
userAgent = window.navigator.userAgent;
|
||||||
|
}
|
||||||
|
|
||||||
|
let touchInput = "UNKNOWN";
|
||||||
|
try {
|
||||||
|
// MDN claims broad support across browsers
|
||||||
|
touchInput = String(window.matchMedia("(pointer: coarse)").matches);
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
const body = new FormData();
|
||||||
|
body.append(
|
||||||
|
"text",
|
||||||
|
opts.description || "User did not supply any additional text."
|
||||||
|
);
|
||||||
|
body.append("app", "matrix-video-chat");
|
||||||
|
body.append("version", "dev");
|
||||||
|
body.append("user_agent", userAgent);
|
||||||
|
body.append("installed_pwa", false);
|
||||||
|
body.append("touch_input", touchInput);
|
||||||
|
|
||||||
|
if (client) {
|
||||||
|
body.append("user_id", client.credentials.userId);
|
||||||
|
body.append("device_id", client.deviceId);
|
||||||
|
|
||||||
|
if (client.isCryptoEnabled()) {
|
||||||
|
const keys = [`ed25519:${client.getDeviceEd25519Key()}`];
|
||||||
|
if (client.getDeviceCurve25519Key) {
|
||||||
|
keys.push(`curve25519:${client.getDeviceCurve25519Key()}`);
|
||||||
|
}
|
||||||
|
body.append("device_keys", keys.join(", "));
|
||||||
|
body.append("cross_signing_key", client.getCrossSigningId());
|
||||||
|
|
||||||
|
// add cross-signing status information
|
||||||
|
const crossSigning = client.crypto.crossSigningInfo;
|
||||||
|
const secretStorage = client.crypto.secretStorage;
|
||||||
|
|
||||||
|
body.append(
|
||||||
|
"cross_signing_ready",
|
||||||
|
String(await client.isCrossSigningReady())
|
||||||
|
);
|
||||||
|
body.append(
|
||||||
|
"cross_signing_supported_by_hs",
|
||||||
|
String(
|
||||||
|
await client.doesServerSupportUnstableFeature(
|
||||||
|
"org.matrix.e2e_cross_signing"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
body.append("cross_signing_key", crossSigning.getId());
|
||||||
|
body.append(
|
||||||
|
"cross_signing_privkey_in_secret_storage",
|
||||||
|
String(
|
||||||
|
!!(await crossSigning.isStoredInSecretStorage(secretStorage))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const pkCache = client.getCrossSigningCacheCallbacks();
|
||||||
|
body.append(
|
||||||
|
"cross_signing_master_privkey_cached",
|
||||||
|
String(
|
||||||
|
!!(pkCache && (await pkCache.getCrossSigningKeyCache("master")))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
body.append(
|
||||||
|
"cross_signing_self_signing_privkey_cached",
|
||||||
|
String(
|
||||||
|
!!(
|
||||||
|
pkCache &&
|
||||||
|
(await pkCache.getCrossSigningKeyCache("self_signing"))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
body.append(
|
||||||
|
"cross_signing_user_signing_privkey_cached",
|
||||||
|
String(
|
||||||
|
!!(
|
||||||
|
pkCache &&
|
||||||
|
(await pkCache.getCrossSigningKeyCache("user_signing"))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
body.append(
|
||||||
|
"secret_storage_ready",
|
||||||
|
String(await client.isSecretStorageReady())
|
||||||
|
);
|
||||||
|
body.append(
|
||||||
|
"secret_storage_key_in_account",
|
||||||
|
String(!!(await secretStorage.hasKey()))
|
||||||
|
);
|
||||||
|
|
||||||
|
body.append(
|
||||||
|
"session_backup_key_in_secret_storage",
|
||||||
|
String(!!(await client.isKeyBackupKeyStored()))
|
||||||
|
);
|
||||||
|
const sessionBackupKeyFromCache =
|
||||||
|
await client.crypto.getSessionBackupPrivateKey();
|
||||||
|
body.append(
|
||||||
|
"session_backup_key_cached",
|
||||||
|
String(!!sessionBackupKeyFromCache)
|
||||||
|
);
|
||||||
|
body.append(
|
||||||
|
"session_backup_key_well_formed",
|
||||||
|
String(sessionBackupKeyFromCache instanceof Uint8Array)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.label) {
|
||||||
|
body.append("label", opts.label);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add storage persistence/quota information
|
||||||
|
if (navigator.storage && navigator.storage.persisted) {
|
||||||
|
try {
|
||||||
|
body.append(
|
||||||
|
"storageManager_persisted",
|
||||||
|
String(await navigator.storage.persisted())
|
||||||
|
);
|
||||||
|
} catch (e) {}
|
||||||
|
} else if (document.hasStorageAccess) {
|
||||||
|
// Safari
|
||||||
|
try {
|
||||||
|
body.append(
|
||||||
|
"storageManager_persisted",
|
||||||
|
String(await document.hasStorageAccess())
|
||||||
|
);
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (navigator.storage && navigator.storage.estimate) {
|
||||||
|
try {
|
||||||
|
const estimate = await navigator.storage.estimate();
|
||||||
|
body.append("storageManager_quota", String(estimate.quota));
|
||||||
|
body.append("storageManager_usage", String(estimate.usage));
|
||||||
|
if (estimate.usageDetails) {
|
||||||
|
Object.keys(estimate.usageDetails).forEach((k) => {
|
||||||
|
body.append(
|
||||||
|
`storageManager_usage_${k}`,
|
||||||
|
String(estimate.usageDetails[k])
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
const logs = await rageshake.getLogsForReport();
|
||||||
|
|
||||||
|
for (const entry of logs) {
|
||||||
|
// encode as UTF-8
|
||||||
|
let buf = new TextEncoder().encode(entry.lines);
|
||||||
|
|
||||||
|
// compress
|
||||||
|
buf = pako.gzip(buf);
|
||||||
|
|
||||||
|
body.append("compressed-log", new Blob([buf]), entry.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json) {
|
||||||
|
body.append(
|
||||||
|
"file",
|
||||||
|
new Blob([JSON.stringify(json)], { type: "text/plain" }),
|
||||||
|
"groupcall.txt"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await fetch(
|
||||||
|
import.meta.env.VITE_RAGESHAKE_SUBMIT_URL ||
|
||||||
|
"https://element.io/bugreports/submit",
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
body,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[client]
|
||||||
|
);
|
||||||
|
|
||||||
|
const downloadDebugLog = useCallback(() => {
|
||||||
|
const blob = new Blob([JSON.stringify(json)], { type: "application/json" });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const el = document.createElement("a");
|
||||||
|
el.href = url;
|
||||||
|
el.download = "groupcall.json";
|
||||||
|
el.style.display = "none";
|
||||||
|
document.body.appendChild(el);
|
||||||
|
el.click();
|
||||||
|
setTimeout(() => {
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
el.parentNode.removeChild(el);
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
return { submitRageshake, downloadDebugLog };
|
||||||
|
}
|
@ -9273,7 +9273,7 @@ p-try@^2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
|
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
|
||||||
integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
|
integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
|
||||||
|
|
||||||
pako@^2.0.3:
|
pako@^2.0.3, pako@^2.0.4:
|
||||||
version "2.0.4"
|
version "2.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/pako/-/pako-2.0.4.tgz#6cebc4bbb0b6c73b0d5b8d7e8476e2b2fbea576d"
|
resolved "https://registry.yarnpkg.com/pako/-/pako-2.0.4.tgz#6cebc4bbb0b6c73b0d5b8d7e8476e2b2fbea576d"
|
||||||
integrity sha512-v8tweI900AUkZN6heMU/4Uy4cXRc2AYNRggVmTR+dEncawDJgCdLMximOVA2p4qO57WMynangsfGRb5WD6L1Bg==
|
integrity sha512-v8tweI900AUkZN6heMU/4Uy4cXRc2AYNRggVmTR+dEncawDJgCdLMximOVA2p4qO57WMynangsfGRb5WD6L1Bg==
|
||||||
|
Loading…
Reference in New Issue
Block a user