Add rageshake submit state

This commit is contained in:
Robert Long 2022-02-01 15:39:45 -08:00
commit 8ab68ed8c8
2 changed files with 201 additions and 170 deletions

View file

@ -1,4 +1,4 @@
import React from "react"; import React, { useState } from "react";
import { Modal } from "../Modal"; import { Modal } from "../Modal";
import styles from "./SettingsModal.module.css"; import styles from "./SettingsModal.module.css";
import { TabContainer, TabItem } from "../tabs/Tabs"; import { TabContainer, TabItem } from "../tabs/Tabs";
@ -8,7 +8,7 @@ import { ReactComponent as DeveloperIcon } from "../icons/Developer.svg";
import { SelectInput } from "../input/SelectInput"; 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, ErrorMessage } from "../input/Input";
import { Button } from "../button"; import { Button } from "../button";
import { useSubmitRageshake } from "./useSubmitRageshake"; import { useSubmitRageshake } from "./useSubmitRageshake";
@ -27,7 +27,8 @@ export function SettingsModal({
setVideoInput, setVideoInput,
} = useMediaHandler(client); } = useMediaHandler(client);
const { submitRageshake, downloadDebugLog } = useSubmitRageshake(); const { submitRageshake, sending, sent, error, downloadDebugLog } =
useSubmitRageshake();
return ( return (
<Modal <Modal
@ -93,8 +94,19 @@ export function SettingsModal({
/> />
</FieldRow> </FieldRow>
<FieldRow> <FieldRow>
<Button onPress={submitRageshake}>Send Debug Logs</Button> <Button onPress={submitRageshake}>
{sent
? "Debug Logs Sent"
: sending
? "Sending Debug Logs..."
: "Send Debug Logs"}
</Button>
</FieldRow> </FieldRow>
{error && (
<FieldRow>
<ErrorMessage>{error.message}</ErrorMessage>
</FieldRow>
)}
<FieldRow> <FieldRow>
<Button onPress={downloadDebugLog}>Download Debug Logs</Button> <Button onPress={downloadDebugLog}>Download Debug Logs</Button>
</FieldRow> </FieldRow>

View file

@ -1,4 +1,4 @@
import { useCallback, useContext } from "react"; import { useCallback, useContext, useState } from "react";
import * as rageshake from "matrix-react-sdk/src/rageshake/rageshake"; import * as rageshake from "matrix-react-sdk/src/rageshake/rageshake";
import pako from "pako"; import pako from "pako";
import { useClient } from "../ClientContext"; import { useClient } from "../ClientContext";
@ -6,186 +6,205 @@ import { InspectorContext } from "../room/GroupCallInspector";
export function useSubmitRageshake() { export function useSubmitRageshake() {
const { client } = useClient(); const { client } = useClient();
const [{ json, svg }] = useContext(InspectorContext); const [{ json }] = useContext(InspectorContext);
const [{ sending, sent, error }, setState] = useState({
sending: false,
sent: false,
error: null,
});
const submitRageshake = useCallback( const submitRageshake = useCallback(
async (opts) => { async (opts) => {
let userAgent = "UNKNOWN"; if (sending) {
if (window.navigator && window.navigator.userAgent) { return;
userAgent = window.navigator.userAgent;
} }
let touchInput = "UNKNOWN";
try { try {
// MDN claims broad support across browsers setState({ sending: true, sent: false, error: null });
touchInput = String(window.matchMedia("(pointer: coarse)").matches);
} catch (e) {}
const body = new FormData(); let userAgent = "UNKNOWN";
body.append( if (window.navigator && window.navigator.userAgent) {
"text", userAgent = window.navigator.userAgent;
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) { let touchInput = "UNKNOWN";
body.append("label", opts.label);
}
// add storage persistence/quota information
if (navigator.storage && navigator.storage.persisted) {
try { try {
body.append( // MDN claims broad support across browsers
"storageManager_persisted", touchInput = String(window.matchMedia("(pointer: coarse)").matches);
String(await navigator.storage.persisted())
);
} catch (e) {} } catch (e) {}
} else if (document.hasStorageAccess) {
// Safari
try {
body.append(
"storageManager_persisted",
String(await document.hasStorageAccess())
);
} catch (e) {}
}
if (navigator.storage && navigator.storage.estimate) { const body = new FormData();
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( body.append(
"file", "text",
new Blob([JSON.stringify(json)], { type: "text/plain" }), opts.description || "User did not supply any additional text."
"groupcall.txt"
); );
} 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);
await fetch( if (client) {
import.meta.env.VITE_RAGESHAKE_SUBMIT_URL || body.append("user_id", client.credentials.userId);
"https://element.io/bugreports/submit", body.append("device_id", client.deviceId);
{
method: "POST", if (client.isCryptoEnabled()) {
body, 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,
}
);
setState({ sending: false, sent: true, error: null });
} catch (error) {
setState({ sending: false, sent: false, error });
console.error(error);
}
}, },
[client] [client]
); );
@ -205,5 +224,5 @@ export function useSubmitRageshake() {
}, 0); }, 0);
}); });
return { submitRageshake, downloadDebugLog }; return { submitRageshake, sending, sent, error, downloadDebugLog };
} }