Compare commits

..

8 Commits

Author SHA1 Message Date
Robert Long
51561e2f4e Set rageshake submit url for prod 2022-02-07 16:16:51 -08:00
Robert Long
4168540017 Added group_call_rageshake_request_id for rageshake grouping 2022-02-07 15:24:43 -08:00
Robert Long
942630c2fc Merge pull request #207 from vector-im/revert-206-michaelk/rename_groupcall.txt
Revert "Rename groupcall.txt -> groupcall.json."
2022-02-07 15:23:14 -08:00
Robert Long
9251cd9964 Revert "Rename groupcall.txt -> groupcall.json." 2022-02-07 15:23:00 -08:00
Robert Long
145826d1f3 Merge pull request #206 from michaelkaye/michaelk/rename_groupcall.txt
Rename groupcall.txt -> groupcall.json.
2022-02-07 15:09:13 -08:00
Michael Kaye
5e42881c5c Rename groupcall.txt -> groupcall.json.
This will stop groupcall.txt being handled as a 'log file' and instead
indicate it's an artifact to be stored alongside the rageshake.

The file will still be stored on the rageshake server but the extension
will indicate it's not a log file.
2022-02-07 15:28:46 +00:00
Robert Long
0824bfb4ed Update copy and feedback icon 2022-02-04 17:00:58 -08:00
Robert Long
6ec9e4b666 Add rageshake request modal 2022-02-04 16:55:57 -08:00
8 changed files with 241 additions and 48 deletions

View File

@@ -4,6 +4,7 @@ set -ex
export VITE_DEFAULT_HOMESERVER=https://call.ems.host
export VITE_SENTRY_DSN=https://b1e328d49be3402ba96101338989fb35@sentry.matrix.org/41
export VITE_RAGESHAKE_SUBMIT_URL=https://element.io/bugreports/submit
git clone https://github.com/matrix-org/matrix-js-sdk.git
cd matrix-js-sdk

3
src/icons/Feedback.svg Normal file
View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.283 21.4401C17.6495 21.4401 21.9999 17.0881 21.9999 11.7196C21.9999 6.3511 17.6495 1.99908 12.283 1.99908C6.91643 1.99908 2.566 6.3511 2.566 11.7196C2.566 13.2234 2.90739 14.6476 3.51687 15.9186L2.04468 20.7049C1.80806 21.4742 2.5308 22.1936 3.29898 21.9535L8.04564 20.4696C9.32625 21.0914 10.7639 21.4401 12.283 21.4401Z" fill="#ffffff"/>
</svg>

After

Width:  |  Height:  |  Size: 496 B

View File

@@ -0,0 +1,76 @@
import React, { useCallback, useEffect } from "react";
import { Modal, ModalContent } from "../Modal";
import { Button } from "../button";
import { FieldRow, InputField, ErrorMessage } from "../input/Input";
import { useSubmitRageshake, useRageshakeRequest } from "../settings/rageshake";
import { Body } from "../typography/Typography";
import { randomString } from "matrix-js-sdk/src/randomstring";
export function FeedbackModal({ inCall, roomId, ...rest }) {
const { submitRageshake, sending, sent, error } = useSubmitRageshake();
const sendRageshakeRequest = useRageshakeRequest();
const onSubmitFeedback = useCallback(
(e) => {
e.preventDefault();
const data = new FormData(e.target);
const description = data.get("description");
const sendLogs = data.get("sendLogs");
const rageshakeRequestId = randomString(16);
submitRageshake({
description,
sendLogs,
rageshakeRequestId,
});
if (inCall && sendLogs) {
sendRageshakeRequest(roomId, rageshakeRequestId);
}
},
[inCall, submitRageshake, roomId, sendRageshakeRequest]
);
useEffect(() => {
if (sent) {
rest.onClose();
}
}, [sent, rest.onClose]);
return (
<Modal title="Submit Feedback" isDismissable {...rest}>
<ModalContent>
<Body>Having trouble? Help us fix it.</Body>
<form onSubmit={onSubmitFeedback}>
<FieldRow>
<InputField
id="description"
name="description"
label="Description (optional)"
type="text"
/>
</FieldRow>
<FieldRow>
<InputField
id="sendLogs"
name="sendLogs"
label="Include Debug Logs"
type="checkbox"
defaultChecked
/>
</FieldRow>
{error && (
<FieldRow>
<ErrorMessage>{error.message}</ErrorMessage>
</FieldRow>
)}
<FieldRow>
<Button type="submit" disabled={sending}>
{sending ? "Submitting feedback..." : "Submit Feedback"}
</Button>
</FieldRow>
</form>
</ModalContent>
</Modal>
);
}

View File

@@ -19,6 +19,8 @@ import { OverflowMenu } from "./OverflowMenu";
import { GridLayoutMenu } from "./GridLayoutMenu";
import { Avatar } from "../Avatar";
import { UserMenuContainer } from "../UserMenuContainer";
import { useRageshakeRequestModal } from "../settings/rageshake";
import { RageshakeRequestModal } from "./RageshakeRequestModal";
const canScreenshare = "getDisplayMedia" in navigator.mediaDevices;
// There is currently a bug in Safari our our code with cloning and sending MediaStreams
@@ -120,6 +122,11 @@ export function InCallView({
[client]
);
const {
modalState: rageshakeRequestModalState,
modalProps: rageshakeRequestModalProps,
} = useRageshakeRequestModal(groupCall.room.roomId);
return (
<div className={styles.inRoom}>
<Header>
@@ -164,10 +171,12 @@ export function InCallView({
/>
)}
<OverflowMenu
inCall
roomId={roomId}
setShowInspector={setShowInspector}
showInspector={showInspector}
client={client}
groupCall={groupCall}
/>
<HangupButton onPress={onLeave} />
</div>
@@ -176,6 +185,9 @@ export function InCallView({
groupCall={groupCall}
show={showInspector}
/>
{rageshakeRequestModalState.isOpen && (
<RageshakeRequestModal {...rageshakeRequestModalProps} />
)}
</div>
);
}

View File

@@ -6,21 +6,27 @@ import { Item } from "@react-stately/collections";
import { ReactComponent as SettingsIcon } from "../icons/Settings.svg";
import { ReactComponent as AddUserIcon } from "../icons/AddUser.svg";
import { ReactComponent as OverflowIcon } from "../icons/Overflow.svg";
import { ReactComponent as FeedbackIcon } from "../icons/Feedback.svg";
import { useModalTriggerState } from "../Modal";
import { SettingsModal } from "../settings/SettingsModal";
import { InviteModal } from "./InviteModal";
import { Tooltip, TooltipTrigger } from "../Tooltip";
import { TooltipTrigger } from "../Tooltip";
import { FeedbackModal } from "./FeedbackModal";
export function OverflowMenu({
roomId,
setShowInspector,
showInspector,
client,
inCall,
groupCall,
}) {
const { modalState: inviteModalState, modalProps: inviteModalProps } =
useModalTriggerState();
const { modalState: settingsModalState, modalProps: settingsModalProps } =
useModalTriggerState();
const { modalState: feedbackModalState, modalProps: feedbackModalProps } =
useModalTriggerState();
// TODO: On closing modal, focus should be restored to the trigger button
// https://github.com/adobe/react-spectrum/issues/2444
@@ -32,6 +38,9 @@ export function OverflowMenu({
case "settings":
settingsModalState.open();
break;
case "feedback":
feedbackModalState.open();
break;
}
});
@@ -54,6 +63,10 @@ export function OverflowMenu({
<SettingsIcon />
<span>Settings</span>
</Item>
<Item key="feedback" textValue="Submit Feedback">
<FeedbackIcon />
<span>Submit Feedback</span>
</Item>
</Menu>
)}
</PopoverMenuTrigger>
@@ -68,6 +81,13 @@ export function OverflowMenu({
{inviteModalState.isOpen && (
<InviteModal roomId={roomId} {...inviteModalProps} />
)}
{feedbackModalState.isOpen && (
<FeedbackModal
{...feedbackModalProps}
roomId={groupCall?.room.roomId}
inCall={inCall}
/>
)}
</>
);
}

View File

@@ -0,0 +1,45 @@
import React, { useEffect } from "react";
import { Modal, ModalContent } from "../Modal";
import { Button } from "../button";
import { FieldRow, ErrorMessage } from "../input/Input";
import { useSubmitRageshake } from "../settings/rageshake";
import { Body } from "../typography/Typography";
export function RageshakeRequestModal({ rageshakeRequestId, ...rest }) {
const { submitRageshake, sending, sent, error } = useSubmitRageshake();
useEffect(() => {
if (sent) {
rest.onClose();
}
}, [sent, rest.onClose]);
return (
<Modal title="Debug Log Request" isDismissable {...rest}>
<ModalContent>
<Body>
Another user on this call is having an issue. In order to better
diagnose these issues we'd like to collect a debug log.
</Body>
<FieldRow>
<Button
onPress={() =>
submitRageshake({
sendLogs: true,
rageshakeRequestId,
})
}
disabled={sending}
>
{sending ? "Sending debug log..." : "Send debug log"}
</Button>
</FieldRow>
{error && (
<FieldRow>
<ErrorMessage>{error.message}</ErrorMessage>
</FieldRow>
)}
</ModalContent>
</Modal>
);
}

View File

@@ -1,4 +1,4 @@
import React, { useState } from "react";
import React from "react";
import { Modal } from "../Modal";
import styles from "./SettingsModal.module.css";
import { TabContainer, TabItem } from "../tabs/Tabs";
@@ -8,10 +8,9 @@ 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, ErrorMessage } from "../input/Input";
import { FieldRow, InputField } from "../input/Input";
import { Button } from "../button";
import { useSubmitRageshake } from "./useSubmitRageshake";
import { Subtitle } from "../typography/Typography";
import { useDownloadDebugLog } from "./rageshake";
export function SettingsModal({
client,
@@ -28,10 +27,7 @@ export function SettingsModal({
setVideoInput,
} = useMediaHandler(client);
const [description, setDescription] = useState("");
const { submitRageshake, sending, sent, error, downloadDebugLog } =
useSubmitRageshake();
const downloadDebugLog = useDownloadDebugLog();
return (
<Modal
@@ -96,31 +92,6 @@ export function SettingsModal({
onChange={(e) => setShowInspector(e.target.checked)}
/>
</FieldRow>
<Subtitle>Feedback</Subtitle>
<FieldRow>
<InputField
id="description"
name="description"
label="Description"
type="text"
value={description}
onChange={(e) => setDescription(e.target.value)}
/>
</FieldRow>
<FieldRow>
<Button onPress={() => submitRageshake({ description })}>
{sent
? "Debug Logs Sent"
: sending
? "Sending Debug Logs..."
: "Send Debug Logs"}
</Button>
</FieldRow>
{error && (
<FieldRow>
<ErrorMessage>{error.message}</ErrorMessage>
</FieldRow>
)}
<FieldRow>
<Button onPress={downloadDebugLog}>Download Debug Logs</Button>
</FieldRow>

View File

@@ -1,8 +1,9 @@
import { useCallback, useContext, useState } from "react";
import { useCallback, useContext, useEffect, useState } 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";
import { useModalTriggerState } from "../Modal";
export function useSubmitRageshake() {
const { client } = useClient();
@@ -171,23 +172,32 @@ export function useSubmitRageshake() {
} catch (e) {}
}
const logs = await rageshake.getLogsForReport();
if (opts.sendLogs) {
const logs = await rageshake.getLogsForReport();
for (const entry of logs) {
// encode as UTF-8
let buf = new TextEncoder().encode(entry.lines);
for (const entry of logs) {
// encode as UTF-8
let buf = new TextEncoder().encode(entry.lines);
// compress
buf = pako.gzip(buf);
// compress
buf = pako.gzip(buf);
body.append("compressed-log", new Blob([buf]), entry.id);
body.append("compressed-log", new Blob([buf]), entry.id);
}
if (json) {
body.append(
"file",
new Blob([JSON.stringify(json)], { type: "text/plain" }),
"groupcall.txt"
);
}
}
if (json) {
if (opts.rageshakeRequestId) {
body.append(
"file",
new Blob([JSON.stringify(json)], { type: "text/plain" }),
"groupcall.txt"
"group_call_rageshake_request_id",
opts.rageshakeRequestId
);
}
@@ -209,6 +219,17 @@ export function useSubmitRageshake() {
[client]
);
return {
submitRageshake,
sending,
sent,
error,
};
}
export function useDownloadDebugLog() {
const [{ json }] = useContext(InspectorContext);
const downloadDebugLog = useCallback(() => {
const blob = new Blob([JSON.stringify(json)], { type: "application/json" });
const url = URL.createObjectURL(blob);
@@ -222,7 +243,51 @@ export function useSubmitRageshake() {
URL.revokeObjectURL(url);
el.parentNode.removeChild(el);
}, 0);
});
}, [json]);
return { submitRageshake, sending, sent, error, downloadDebugLog };
return downloadDebugLog;
}
export function useRageshakeRequest() {
const { client } = useClient();
const sendRageshakeRequest = useCallback(
(roomId, rageshakeRequestId) => {
client.sendEvent(roomId, "org.matrix.rageshake_request", {
request_id: rageshakeRequestId,
});
},
[client]
);
return sendRageshakeRequest;
}
export function useRageshakeRequestModal(roomId) {
const { modalState, modalProps } = useModalTriggerState();
const { client } = useClient();
const [rageshakeRequestId, setRageshakeRequestId] = useState();
useEffect(() => {
const onEvent = (event) => {
const type = event.getType();
if (
type === "org.matrix.rageshake_request" &&
roomId === event.getRoomId() &&
client.getUserId() !== event.getSender()
) {
setRageshakeRequestId(event.getContent().request_id);
modalState.open();
}
};
client.on("event", onEvent);
return () => {
client.removeListener("event", onEvent);
};
}, [modalState.open, roomId]);
return { modalState, modalProps: { ...modalProps, rageshakeRequestId } };
}