-
Notifications
You must be signed in to change notification settings - Fork 708
/
stuck-popup.tsx
132 lines (123 loc) 路 3.82 KB
/
stuck-popup.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import { Signal, useSignal } from "@preact/signals";
import {
PersistenceState,
codeMirror,
editSessionLength,
errorLog,
} from "../../lib/state";
import Button from "../design-system/button";
import Textarea from "../design-system/textarea";
import styles from "../navbar.module.css";
interface StuckPopupProps {
persistenceState: Signal<PersistenceState>;
showStuckPopup: Signal<boolean>;
}
type StuckCategory = "Logic Error" | "Syntax Error" | "Other";
type StuckData = {
category: StuckCategory;
description: string;
};
export default function StuckPopup(props: StuckPopupProps) {
// we will accept the current user's
// - name,
// - the category of issue they
// - their description of the issue
const stuckData = useSignal<StuckData>({
category: "Other",
description: "",
});
// keep track of the submit status for "I'm stuck" requests
const isSubmitting = useSignal<boolean>(false);
return (
<>
<div class={styles.stuckPopup}>
<form
class={styles.stuckForm}
onSubmit={async (event) => {
event.preventDefault(); // prevent the browser from reloading after form submit
isSubmitting.value = true;
// 'from' and 'to' represent the index of character where the selection is started to where it's ended
// if 'from' and 'to' are equal, then it's the cursor position
// from && to being -1 means the cursor is not in the editor
const selectionRange = codeMirror.value?.state.selection
.ranges[0] ?? { from: -1, to: -1 };
// Store a copy of the user's code, currently active errors and the length of their editing session
// along with their description of the issue
const payload = {
selection: JSON.stringify({
from: selectionRange.from,
to: selectionRange.to,
}),
email: props.persistenceState.value.session?.user
.email,
code: codeMirror.value?.state.doc.toString(),
error: errorLog.value,
sessionLength:
(new Date().getTime() -
editSessionLength.value.getTime()) /
1000, // calculate the session length in seconds
...stuckData.value,
};
try {
const response = await fetch("/api/stuck-request", {
method: "POST",
body: JSON.stringify(payload),
});
// Let the user know we'll get back to them after we've receive their complaint
if (response.ok) {
alert(
"We received your request and will get back to you via email."
);
} else
alert(
"We couldn't send your request. Please make sure you're connected and try again."
);
} catch (err) {
console.error(err);
} finally {
isSubmitting.value = false;
}
}}
>
<label htmlFor="issue category">
What is the type of issue you're facing?
</label>
<select
value={stuckData.value.category}
onChange={(event) => {
stuckData.value = {
...stuckData.value,
category: (event.target! as HTMLSelectElement)
.value as StuckCategory,
};
}}
name=""
id=""
>
<option value={"Logic Error"}>Logic Error</option>
<option value={"Syntax Error"}>Syntax Error</option>
<option value={"Other"}>Other</option>
</select>
<label htmlFor="Description">
Please describe the issue you're facing below
</label>
<Textarea
required
value={stuckData.value.description}
onChange={(event) => {
stuckData.value = {
...stuckData.value,
description: event.target.value,
};
}}
placeholder="Example: After 2 seconds, the browser tab suddenly freezes and I do not know why."
/>
<br />
<Button type="submit" disabled={isSubmitting.value}>
{isSubmitting.value ? "Sending..." : "Send"}
</Button>
</form>
</div>
</>
);
}