From 8ab68ed8c809e4623f5ad2e8e2abbe93664ed3a4 Mon Sep 17 00:00:00 2001 From: Robert Long Date: Tue, 1 Feb 2022 15:39:45 -0800 Subject: [PATCH] Add rageshake submit state --- src/settings/SettingsModal.jsx | 20 +- src/settings/useSubmitRageshake.js | 351 +++++++++++++++-------------- 2 files changed, 201 insertions(+), 170 deletions(-) diff --git a/src/settings/SettingsModal.jsx b/src/settings/SettingsModal.jsx index 964973e9..3d82000b 100644 --- a/src/settings/SettingsModal.jsx +++ b/src/settings/SettingsModal.jsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useState } from "react"; import { Modal } from "../Modal"; import styles from "./SettingsModal.module.css"; import { TabContainer, TabItem } from "../tabs/Tabs"; @@ -8,7 +8,7 @@ import { ReactComponent as DeveloperIcon } from "../icons/Developer.svg"; import { SelectInput } from "../input/SelectInput"; import { Item } from "@react-stately/collections"; import { useMediaHandler } from "./useMediaHandler"; -import { FieldRow, InputField } from "../input/Input"; +import { FieldRow, InputField, ErrorMessage } from "../input/Input"; import { Button } from "../button"; import { useSubmitRageshake } from "./useSubmitRageshake"; @@ -27,7 +27,8 @@ export function SettingsModal({ setVideoInput, } = useMediaHandler(client); - const { submitRageshake, downloadDebugLog } = useSubmitRageshake(); + const { submitRageshake, sending, sent, error, downloadDebugLog } = + useSubmitRageshake(); return ( - + + {error && ( + + {error.message} + + )} diff --git a/src/settings/useSubmitRageshake.js b/src/settings/useSubmitRageshake.js index 2507e900..0c32b8b2 100644 --- a/src/settings/useSubmitRageshake.js +++ b/src/settings/useSubmitRageshake.js @@ -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 pako from "pako"; import { useClient } from "../ClientContext"; @@ -6,186 +6,205 @@ import { InspectorContext } from "../room/GroupCallInspector"; export function useSubmitRageshake() { 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( async (opts) => { - let userAgent = "UNKNOWN"; - if (window.navigator && window.navigator.userAgent) { - userAgent = window.navigator.userAgent; + if (sending) { + return; } - let touchInput = "UNKNOWN"; try { - // MDN claims broad support across browsers - touchInput = String(window.matchMedia("(pointer: coarse)").matches); - } catch (e) {} + setState({ sending: true, sent: false, error: null }); - 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) - ); + let userAgent = "UNKNOWN"; + if (window.navigator && window.navigator.userAgent) { + userAgent = window.navigator.userAgent; } - } - if (opts.label) { - body.append("label", opts.label); - } - - // add storage persistence/quota information - if (navigator.storage && navigator.storage.persisted) { + let touchInput = "UNKNOWN"; try { - body.append( - "storageManager_persisted", - String(await navigator.storage.persisted()) - ); + // MDN claims broad support across browsers + touchInput = String(window.matchMedia("(pointer: coarse)").matches); } 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) { + const body = new FormData(); body.append( - "file", - new Blob([JSON.stringify(json)], { type: "text/plain" }), - "groupcall.txt" + "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); - await fetch( - import.meta.env.VITE_RAGESHAKE_SUBMIT_URL || - "https://element.io/bugreports/submit", - { - method: "POST", - body, + 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, + } + ); + + setState({ sending: false, sent: true, error: null }); + } catch (error) { + setState({ sending: false, sent: false, error }); + console.error(error); + } }, [client] ); @@ -205,5 +224,5 @@ export function useSubmitRageshake() { }, 0); }); - return { submitRageshake, downloadDebugLog }; + return { submitRageshake, sending, sent, error, downloadDebugLog }; }