-
Notifications
You must be signed in to change notification settings - Fork 268
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Website: Add error report modal #1102
Changes from 44 commits
b100814
5b03727
16295b6
bc874a5
db6bde2
0c1ac32
20d5190
a9dd3a9
669757e
da44dd0
f295b8c
24f4d0d
c3d4941
6520a2b
f447ca9
f8694b8
416e40c
298e241
12418ef
6e5f8e0
29d0553
e473349
16a9ed1
9762186
8891776
7bb6157
93e28c0
8f6c9d3
fc3c28b
a7f2347
9c5b595
be0148c
a9aa605
88d497f
74eecae
212e72d
f46d37b
65b683f
009bc98
0e0a441
22c3fac
ee0bd6f
978dec8
ac9f9cd
8c33769
38e8889
74f6c7a
c5e1ca5
b2dfafc
559a603
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,13 +33,13 @@ function response($ok, $error = null) | |
function validate_message($message) | ||
{ | ||
// Validate description. Description is required | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wouldn't this be simpler and more reliable if the data was transmitted not as a text blob but as separate POST fields? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was thinking the same things while working on the validation but ended up overcomplicating it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done, this is definitely much cleaner. |
||
preg_match('/(?<=What happened\?\n\n)(.*?)(?=\n\nLogs)/s', $message, $description); | ||
preg_match('/(?<=What happened\?\n\n)(.+?)(?=\n\nLogs)/s', $message, $description); | ||
if (empty($description)) { | ||
return false; | ||
} | ||
|
||
// Validate logs if exists. Logs need to match the PHP error log format | ||
preg_match('/(?<=Logs\n\n)(.*?)(?=\n\nUrl)/s', $message, $logs); | ||
preg_match('/(?<=Logs\n\n)(.+?)(?=\n\nUrl)/s', $message, $logs); | ||
if (!empty($logs)) { | ||
$logs = $logs[0]; | ||
if (preg_match('/\[\d{2}-[A-Za-z]{3}-\d{4} \d{2}:\d{2}:\d{2} UTC\](.*)/s', $logs) !== 1) { | ||
|
@@ -48,7 +48,11 @@ function validate_message($message) | |
} | ||
|
||
// Validate URL if exists | ||
preg_match('/(?<=Url\n\n)(.*)/s', $message, $url); | ||
preg_match( | ||
'/(?<=Url\n\n)(.+)/s', | ||
$message, | ||
$url | ||
); | ||
if (!empty($url)) { | ||
$url = $url[0]; | ||
if (filter_var($url, FILTER_VALIDATE_URL) === false) { | ||
|
@@ -66,12 +70,14 @@ function validate_message($message) | |
if (!isset($_POST['message'])) { | ||
response(false, 'No message provided'); | ||
} | ||
$text = $_POST['message']; | ||
$message = urldecode($_POST['message']); | ||
$message = str_replace("\r\n", "\n", $message); // Replace \r\n with \n | ||
bgrgicak marked this conversation as resolved.
Show resolved
Hide resolved
|
||
$message = str_replace("\r", "\n", $message); // Replace remaining \r with \n | ||
|
||
if (!validate_message($text)) { | ||
if (!validate_message($message)) { | ||
response(false, 'Invalid message'); | ||
} | ||
$text = urlencode($text); | ||
$text = urlencode($message); | ||
|
||
$ch = curl_init(); | ||
curl_setopt($ch, CURLOPT_URL, "https://slack.com/api/chat.postMessage?channel=$channel&text=$text"); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
import { useEffect, useState } from 'react'; | ||
import Modal from '../modal'; | ||
import { addFatalErrorListener, logger } from '@php-wasm/logger'; | ||
import { Button, TextareaControl, TextControl } from '@wordpress/components'; | ||
|
||
import css from './style.module.css'; | ||
|
||
export function ErrorReportModal() { | ||
const [showModal, setShowModal] = useState(false); | ||
const [loading, setLoading] = useState(false); | ||
const [text, setText] = useState(''); | ||
const [logs, setLogs] = useState(''); | ||
const [url, setUrl] = useState(''); | ||
const [submitted, setSubmitted] = useState(false); | ||
const [submitError, setSubmitError] = useState(''); | ||
|
||
useEffect(() => { | ||
addFatalErrorListener(logger, (e) => { | ||
const error = e as CustomEvent; | ||
if (error.detail?.source === 'php-wasm') { | ||
setShowModal(true); | ||
setText(''); | ||
setLogs(error.detail.logs.join('')); | ||
setUrl(window.location.href); | ||
} | ||
}); | ||
}, []); | ||
|
||
function resetForm() { | ||
setText(''); | ||
setLogs(''); | ||
setUrl(''); | ||
} | ||
|
||
function resetSubmission() { | ||
setSubmitted(false); | ||
setSubmitError(''); | ||
} | ||
|
||
function onClose() { | ||
setShowModal(false); | ||
resetForm(); | ||
resetSubmission(); | ||
} | ||
|
||
async function onSubmit() { | ||
setLoading(true); | ||
const formdata = new FormData(); | ||
formdata.append( | ||
'message', | ||
['What happened?', text, 'Logs', logs, 'Url', url].join('\n\n') | ||
); | ||
try { | ||
const response = await fetch( | ||
'https://playground.wordpress.net/logger.php', | ||
{ | ||
method: 'POST', | ||
body: formdata, | ||
} | ||
); | ||
setSubmitted(true); | ||
|
||
const body = await response.json(); | ||
if (!body.ok) { | ||
throw new Error(body.error); | ||
} | ||
|
||
setSubmitError(''); | ||
resetForm(); | ||
} catch (e) { | ||
setSubmitError((e as Error).message); | ||
} finally { | ||
setLoading(false); | ||
} | ||
} | ||
|
||
function getTitle() { | ||
if (!submitted) { | ||
return 'Report error'; | ||
} else if (submitError) { | ||
return 'Failed to report the error'; | ||
} else { | ||
return 'Thank you for reporting the error'; | ||
} | ||
} | ||
|
||
function getContent() { | ||
if (!submitted) { | ||
return `Playground crashed because of an error. You can help | ||
resolve the issue by sharing the error details with | ||
us.`; | ||
bgrgicak marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} else if (submitError) { | ||
return ( | ||
<> | ||
We were unable to submit the error report. Please try again | ||
or open an{' '} | ||
<a href="https://github.com/WordPress/wordpress-playground/issues/"> | ||
issue on GitHub. | ||
</a> | ||
</> | ||
); | ||
} else { | ||
return ( | ||
<> | ||
Your report has been submitted to the{' '} | ||
<a href="https://wordpress.slack.com/archives/C06Q5DCKZ3L"> | ||
Making WordPress #playground-logs Slack channel | ||
</a>{' '} | ||
and will be reviewed by the team. | ||
</> | ||
); | ||
} | ||
} | ||
|
||
/** | ||
* Show the form if the error has not been submitted or if there was an error submitting it. | ||
* | ||
* @return {boolean} | ||
*/ | ||
function showForm() { | ||
return !submitted || submitError; | ||
} | ||
|
||
return ( | ||
<Modal isOpen={showModal} onRequestClose={onClose}> | ||
<header className={css.errorReportModalHeader}> | ||
<h2>{getTitle()}</h2> | ||
<p>{getContent()}</p> | ||
</header> | ||
{showForm() && ( | ||
<> | ||
<main> | ||
<TextareaControl | ||
label="What happened?" | ||
help="Describe what caused the error and how can we reproduce it." | ||
value={text} | ||
onChange={setText} | ||
className={css.errorReportModalTextarea} | ||
required={true} | ||
/> | ||
<TextareaControl | ||
label="Logs" | ||
value={logs} | ||
onChange={setLogs} | ||
className={css.errorReportModalTextarea} | ||
/> | ||
|
||
<TextControl | ||
label="Url" | ||
value={url} | ||
onChange={setUrl} | ||
/> | ||
</main> | ||
<footer className={css.errorReportModalFooter}> | ||
<Button | ||
variant="primary" | ||
onClick={onSubmit} | ||
isBusy={loading} | ||
disabled={loading || !text} | ||
> | ||
Report error | ||
</Button> | ||
<Button onClick={onClose}>Cancel</Button> | ||
</footer> | ||
</> | ||
)} | ||
</Modal> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
.error-report-modal__header h2 { | ||
font-size: 20px; | ||
} | ||
|
||
.error-report-modal__header p { | ||
font-size: 13px; | ||
} | ||
|
||
.error-report-modal__textarea textarea { | ||
resize: vertical; | ||
width: 100% !important; | ||
} | ||
|
||
.error-report-modal__footer { | ||
margin-top: 20px; | ||
} | ||
.error-report-modal__error { | ||
margin: 16px 0 0; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,5 +20,4 @@ | |
padding: 15px; | ||
text-align: left; | ||
max-height: 90vh; | ||
overflow: auto; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,6 +27,11 @@ import { ExportFormValues } from './github/github-export-form/form'; | |
import { joinPaths } from '@php-wasm/util'; | ||
import { PlaygroundContext } from './playground-context'; | ||
import { collectWindowErrors, logger } from '@php-wasm/logger'; | ||
import { ErrorReportModal } from './components/error-report-modal'; | ||
|
||
collectWindowErrors(logger); | ||
|
||
collectWindowErrors(logger); | ||
|
||
collectWindowErrors(logger); | ||
bgrgicak marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
|
@@ -87,6 +92,7 @@ function Main() { | |
|
||
return ( | ||
<PlaygroundContext.Provider value={{ storage }}> | ||
<ErrorReportModal /> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Follow-up idea – add a "Report an error" button to trigger the modal manually. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done b2dfafc |
||
<PlaygroundViewport | ||
storage={storage} | ||
displayMode={displayMode} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this for Playground integrators?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is for localhost, but it's also required if we want to allow an integrator to send error reports.