mirror of
https://github.com/vector-im/element-call.git
synced 2024-11-15 00:04:59 +08:00
Add rageshake request modal
This commit is contained in:
parent
ec447429c5
commit
6ec9e4b666
69
src/room/FeedbackModal.jsx
Normal file
69
src/room/FeedbackModal.jsx
Normal file
@ -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 (
|
||||
<Modal title="Submit Feedback" isDismissable {...rest}>
|
||||
<ModalContent>
|
||||
<Body>Having trouble on this call? 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>
|
||||
);
|
||||
}
|
||||
|
@ -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({
|
||||
<SettingsIcon />
|
||||
<span>Settings</span>
|
||||
</Item>
|
||||
<Item key="feedback" textValue="Submit Feedback">
|
||||
<SettingsIcon />
|
||||
<span>Submit Feedback</span>
|
||||
</Item>
|
||||
</Menu>
|
||||
)}
|
||||
</PopoverMenuTrigger>
|
||||
@ -68,6 +80,13 @@ export function OverflowMenu({
|
||||
{inviteModalState.isOpen && (
|
||||
<InviteModal roomId={roomId} {...inviteModalProps} />
|
||||
)}
|
||||
{feedbackModalState.isOpen && (
|
||||
<FeedbackModal
|
||||
{...feedbackModalProps}
|
||||
roomId={groupCall?.room.roomId}
|
||||
inCall={inCall}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
40
src/room/RageshakeRequestModal.jsx
Normal file
40
src/room/RageshakeRequestModal.jsx
Normal file
@ -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 (
|
||||
<Modal title="Debug Log Request" isDismissable {...props}>
|
||||
<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 })}
|
||||
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,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 };
|
||||
}
|
Loading…
Reference in New Issue
Block a user