Add debug log inspector / rageshake
This commit is contained in:
		
					parent
					
						
							
								91366585ff
							
						
					
				
			
			
				commit
				
					
						76b2e8b29e
					
				
			
		
					 9 changed files with 320 additions and 28 deletions
				
			
		
							
								
								
									
										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…
	
	Add table
		Add a link
		
	
		Reference in a new issue