diff --git a/src/room/FeedbackModal.jsx b/src/room/FeedbackModal.jsx
new file mode 100644
index 00000000..ba07e2fb
--- /dev/null
+++ b/src/room/FeedbackModal.jsx
@@ -0,0 +1,69 @@
+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";
+
+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");
+ submitRageshake({ description, sendLogs });
+
+ if (inCall && sendLogs) {
+ sendRageshakeRequest(roomId);
+ }
+ },
+ [inCall, submitRageshake, roomId, sendRageshakeRequest]
+ );
+
+ useEffect(() => {
+ if (sent) {
+ rest.onClose();
+ }
+ }, [sent, rest.onClose]);
+
+ return (
+
+
+ Having trouble on this call? Help us fix it.
+
+
+
+ );
+}
diff --git a/src/room/InCallView.jsx b/src/room/InCallView.jsx
index 5a7db8b9..e28c7717 100644
--- a/src/room/InCallView.jsx
+++ b/src/room/InCallView.jsx
@@ -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 (
@@ -164,10 +171,12 @@ export function InCallView({
/>
)}
@@ -176,6 +185,9 @@ export function InCallView({
groupCall={groupCall}
show={showInspector}
/>
+ {rageshakeRequestModalState.isOpen && (
+
+ )}
);
}
diff --git a/src/room/OverflowMenu.jsx b/src/room/OverflowMenu.jsx
index e35538d2..b507449b 100644
--- a/src/room/OverflowMenu.jsx
+++ b/src/room/OverflowMenu.jsx
@@ -9,18 +9,23 @@ import { ReactComponent as OverflowIcon } from "../icons/Overflow.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 +37,9 @@ export function OverflowMenu({
case "settings":
settingsModalState.open();
break;
+ case "feedback":
+ feedbackModalState.open();
+ break;
}
});
@@ -54,6 +62,10 @@ export function OverflowMenu({
Settings
+ -
+
+ Submit Feedback
+
)}
@@ -68,6 +80,13 @@ export function OverflowMenu({
{inviteModalState.isOpen && (
)}
+ {feedbackModalState.isOpen && (
+
+ )}
>
);
}
diff --git a/src/room/RageshakeRequestModal.jsx b/src/room/RageshakeRequestModal.jsx
new file mode 100644
index 00000000..2aeb5441
--- /dev/null
+++ b/src/room/RageshakeRequestModal.jsx
@@ -0,0 +1,40 @@
+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(props) {
+ const { submitRageshake, sending, sent, error } = useSubmitRageshake();
+
+ useEffect(() => {
+ if (sent) {
+ props.onClose();
+ }
+ }, [sent, props.onClose]);
+
+ return (
+
+
+
+ Another user on this call is having an issue. In order to better
+ diagnose these issues we'd like to collect a debug log.
+
+
+
+
+ {error && (
+
+ {error.message}
+
+ )}
+
+
+ );
+}
diff --git a/src/settings/SettingsModal.jsx b/src/settings/SettingsModal.jsx
index 014f92a8..e456cff8 100644
--- a/src/settings/SettingsModal.jsx
+++ b/src/settings/SettingsModal.jsx
@@ -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 (
setShowInspector(e.target.checked)}
/>
- Feedback
-
- setDescription(e.target.value)}
- />
-
-
-
-
- {error && (
-
- {error.message}
-
- )}
diff --git a/src/settings/useSubmitRageshake.js b/src/settings/rageshake.js
similarity index 78%
rename from src/settings/useSubmitRageshake.js
rename to src/settings/rageshake.js
index 0c32b8b2..d1d69c81 100644
--- a/src/settings/useSubmitRageshake.js
+++ b/src/settings/rageshake.js
@@ -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,24 +172,26 @@ 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) {
+ body.append(
+ "file",
+ new Blob([JSON.stringify(json)], { type: "text/plain" }),
+ "groupcall.txt"
+ );
+ }
}
await fetch(
@@ -209,6 +212,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 +236,47 @@ 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) => {
+ client.sendEvent(roomId, "org.matrix.rageshake_request", {});
+ },
+ [client]
+ );
+
+ return sendRageshakeRequest;
+}
+
+export function useRageshakeRequestModal(roomId) {
+ const { modalState, modalProps } = useModalTriggerState();
+ const { client } = useClient();
+
+ useEffect(() => {
+ const onEvent = (event) => {
+ const type = event.getType();
+
+ if (
+ type === "org.matrix.rageshake_request" &&
+ roomId === event.getRoomId() &&
+ client.getUserId() !== event.getSender()
+ ) {
+ modalState.open();
+ }
+ };
+
+ client.on("event", onEvent);
+
+ return () => {
+ client.removeListener("event", onEvent);
+ };
+ }, [modalState.open, roomId]);
+
+ return { modalState, modalProps };
}