Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
51561e2f4e | ||
|
|
4168540017 | ||
|
|
942630c2fc | ||
|
|
9251cd9964 | ||
|
|
145826d1f3 | ||
|
|
5e42881c5c | ||
|
|
0824bfb4ed | ||
|
|
6ec9e4b666 |
@@ -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
3
src/icons/Feedback.svg
Normal 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 |
76
src/room/FeedbackModal.jsx
Normal file
76
src/room/FeedbackModal.jsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
45
src/room/RageshakeRequestModal.jsx
Normal file
45
src/room/RageshakeRequestModal.jsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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 } };
|
||||
}
|
||||
Reference in New Issue
Block a user