wip(lab): Working on crash reporting in the new dev env
This commit is contained in:
parent
f100691a92
commit
8a2a13f240
7 changed files with 164 additions and 61 deletions
|
@ -1,8 +1,13 @@
|
|||
import { useState } from 'react'
|
||||
import SvgWrapper from './svg-wrapper'
|
||||
import Error from './error.js'
|
||||
import Popout from 'shared/components/popout.js'
|
||||
import Robot from 'shared/components/robot/index.js'
|
||||
|
||||
const LabDraft = props => {
|
||||
const { app, draft, pattern, gist, updateGist, unsetGist } = props
|
||||
const { app, draft, pattern, gist, updateGist, unsetGist, feedback } = props
|
||||
|
||||
const [share, setShare] = useState(false)
|
||||
if (!draft) return null
|
||||
|
||||
if (gist?.renderer === 'svg') {
|
||||
|
@ -24,17 +29,66 @@ const LabDraft = props => {
|
|||
return <Error error={error} {...props} />
|
||||
}
|
||||
|
||||
return (
|
||||
// Handle broken drafts
|
||||
let error = null
|
||||
if (patternProps.events.error.length > 0) {
|
||||
error = (
|
||||
<Popout warning>
|
||||
<div className="flex flex-row justify-between">
|
||||
<div>
|
||||
<SvgWrapper
|
||||
draft={draft}
|
||||
patternProps={patternProps}
|
||||
gist={gist}
|
||||
updateGist={updateGist}
|
||||
unsetGist={unsetGist}
|
||||
app={app}
|
||||
/>
|
||||
<h3>Got {patternProps.events.error.length} problems and a stitch ain't one</h3>
|
||||
<p>Don't be alarmed, but we ran into some trouble while drafting this pattern.</p>
|
||||
<h4>Help us make FreeSewing better</h4>
|
||||
<p>
|
||||
If you like puzzles, you can try to figure out what went wrong:
|
||||
</p>
|
||||
<ul className="list-disc list-inside ml-4 text-xl">
|
||||
<li>
|
||||
Check the <button className="btn-link" onClick={() => updateGist(['_state', 'view'], 'events')}>
|
||||
<strong>{patternProps.events.error.length} errors</strong> and <strong>
|
||||
{patternProps.events.warning.length} warnings</strong></button>
|
||||
</li>
|
||||
<li>Check the partially rendered pattern below to see which areas are problematic</li>
|
||||
</ul>
|
||||
<p>
|
||||
Alternatively, you can escalate this. Which means that:
|
||||
</p>
|
||||
<ul className="list-disc list-inside ml-4 text-xl">
|
||||
<li>
|
||||
We will compile a <strong>crash report</strong> that contains everything needed <strong>to recreate this problem</strong>
|
||||
</li>
|
||||
<li>
|
||||
We will include <strong>personal data</strong> such as your <strong>username</strong>, <strong>
|
||||
email address</strong> and <strong>measurements</strong>
|
||||
</li>
|
||||
<li>
|
||||
We will share this report and the data in it with <a className="text-primary font-bold"
|
||||
href="https://github.com/orgs/freesewing/teams/bughunters">FreeSewing's bughunters team</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div className="form-control">
|
||||
<label className="cursor-pointer flex flex-row gap-4 my-4">
|
||||
<input type="checkbox" checked={share} className="checkbox checkbox-primary" onChange={() => setShare(!share)}/>
|
||||
<span className="label-text text-xl">I agree to the use of my personal data for this purpose</span>
|
||||
</label>
|
||||
</div>
|
||||
<p>
|
||||
<button disabled={!share} className="btn btn-primary">Escalate this</button>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
<Robot pose='fail' />
|
||||
</div>
|
||||
</Popout>
|
||||
)
|
||||
}
|
||||
console.log(patternProps)
|
||||
|
||||
return (
|
||||
<>
|
||||
{error}
|
||||
<SvgWrapper {...{ draft, patternProps, gist, updateGist, unsetGist, app, feedback }} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,32 @@
|
|||
import React from 'react'
|
||||
import Path from '../path'
|
||||
import Point from '../point'
|
||||
import Snippet from '../snippet'
|
||||
import { getProps } from '../utils'
|
||||
|
||||
class ErrorBoundary extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { hasError: false };
|
||||
}
|
||||
|
||||
static getDerivedStateFromError(error) {
|
||||
console.log(error)
|
||||
return { hasError: true }
|
||||
}
|
||||
componentDidCatch(error, errorInfo) {
|
||||
console.log(error, errorInfo)
|
||||
}
|
||||
render() {
|
||||
if (this.state.hasError) {
|
||||
console.log('in error boundary', props)
|
||||
return <text>Something went wrong.</text>
|
||||
}
|
||||
|
||||
return this.props.children
|
||||
}
|
||||
}
|
||||
|
||||
const XrayPart = props => {
|
||||
// Don't bother if this is the only part on display
|
||||
if (props.gist.only && props.gist.only.length === 1) return null
|
||||
|
@ -77,5 +101,14 @@ const Part = props => {
|
|||
</g>
|
||||
)
|
||||
}
|
||||
/*
|
||||
<ErrorBoundary
|
||||
x={part.topLeft.x}
|
||||
y={part.topLeft.y}
|
||||
width={part.width}
|
||||
height={part.height}
|
||||
>
|
||||
</ErrorBoundary>
|
||||
*/
|
||||
|
||||
export default Part
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import React from 'react'
|
||||
import TextOnPath from '../text-on-path'
|
||||
import { getProps } from '../utils'
|
||||
|
||||
|
@ -21,9 +22,15 @@ const Path = props => {
|
|||
if (!path.render) return null
|
||||
const output = []
|
||||
const pathId = 'path-' + partName + '-' + pathName
|
||||
output.push(
|
||||
<path id={pathId} key={pathId} d={path.asPathstring()} {...getProps(path)} />
|
||||
)
|
||||
let d = ''
|
||||
try { d = path.asPathstring() }
|
||||
catch (err) {
|
||||
// Bail out
|
||||
console.log(`Failed to generate pathstring for path ${pathId} in part ${partName}`, err)
|
||||
return null
|
||||
}
|
||||
|
||||
output.push(<path id={pathId} key={pathId} d={d} {...getProps(path)} />)
|
||||
if (path.attributes.get('data-text'))
|
||||
output.push(<TextOnPath key={'text-on-path-' + name} pathId={pathId} {...props} />)
|
||||
if (props.gist._state?.xray?.enabled) output.push(<XrayPath {...props} key={'xpath'+pathId} />)
|
||||
|
|
|
@ -40,14 +40,10 @@ const SvgWrapper = props => {
|
|||
<style>{`:root { --pattern-scale: ${gist.scale || 1}}`}</style>
|
||||
<g>
|
||||
{Object.keys(patternProps.parts).map((name) => (
|
||||
<Part
|
||||
<Part {...{ app, gist, updateGist, unsetGist }}
|
||||
key={name}
|
||||
partName={name}
|
||||
part={patternProps.parts[name]}
|
||||
app={app}
|
||||
gist={gist}
|
||||
updateGist={updateGist}
|
||||
unsetGist={unsetGist}
|
||||
/>
|
||||
))}
|
||||
</g>
|
||||
|
|
|
@ -152,7 +152,7 @@ const Part = props => {
|
|||
const partLayout= layout.parts[name]
|
||||
|
||||
// Don't just assume this makes sense
|
||||
if (typeof layout.parts[name].move?.x === 'undefined') return null
|
||||
if (typeof layout.parts?.[name]?.move?.x === 'undefined') return null
|
||||
|
||||
// Use a ref for direct DOM manipulation
|
||||
const partRef = useRef(null)
|
||||
|
|
|
@ -15,7 +15,7 @@ const preload = {
|
|||
if (result.data.files['pattern.yaml'].content) {
|
||||
let g = yaml.load(result.data.files['pattern.yaml'].content)
|
||||
if (g.design !== pattern.config.name) return [
|
||||
false, `You tried loading a configuration for ${g.design} into a ${design} development environment`
|
||||
false, `You tried loading a configuration for ${g.design} into a ${pattern.config.name} development environment`
|
||||
]
|
||||
|
||||
return g
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { useEffect } from 'react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import useLocalStorage from 'shared/hooks/useLocalStorage.js'
|
||||
import Layout from 'shared/components/layouts/default'
|
||||
import Menu from 'shared/components/workbench/menu/index.js'
|
||||
|
@ -62,6 +62,7 @@ const WorkbenchWrapper = ({ app, pattern, preload=false, from=false }) => {
|
|||
|
||||
// State for gist
|
||||
const [gist, setGist] = useLocalStorage(`${pattern.config.name}_gist`, defaultGist(pattern, app.locale))
|
||||
const [messages, setMessages] = useState([])
|
||||
|
||||
// If we don't have the required measurements,
|
||||
// force view to measurements
|
||||
|
@ -76,7 +77,7 @@ const WorkbenchWrapper = ({ app, pattern, preload=false, from=false }) => {
|
|||
useEffect(async () => {
|
||||
if (preload && from && preloaders[from]) {
|
||||
const g = await preloaders[from](preload, pattern)
|
||||
// FIXME: Continue here
|
||||
setGist({ ...gist, ...g.settings })
|
||||
}
|
||||
}, [preload, from])
|
||||
|
||||
|
@ -91,6 +92,17 @@ const WorkbenchWrapper = ({ app, pattern, preload=false, from=false }) => {
|
|||
unset(newGist, path)
|
||||
setGist(newGist)
|
||||
}
|
||||
// Helper methods to handle messages
|
||||
const feedback = {
|
||||
add: msg => {
|
||||
const newMsgs = [...messages]
|
||||
if (Array.isArray(msg)) newMsgs.push(...msg)
|
||||
else newMsgs.push(msg)
|
||||
setMessages(newMsgs)
|
||||
},
|
||||
set: setMessages,
|
||||
clear: () => setMessages([]),
|
||||
}
|
||||
|
||||
// Generate the draft here so we can pass it down
|
||||
let draft = false
|
||||
|
@ -107,7 +119,7 @@ const WorkbenchWrapper = ({ app, pattern, preload=false, from=false }) => {
|
|||
}
|
||||
|
||||
// Props to pass down
|
||||
const componentProps = { app, pattern, gist, updateGist, unsetGist, setGist, draft }
|
||||
const componentProps = { app, pattern, gist, updateGist, unsetGist, setGist, draft, feedback }
|
||||
// Required props for layout
|
||||
const layoutProps = {
|
||||
app: app,
|
||||
|
@ -121,6 +133,7 @@ const WorkbenchWrapper = ({ app, pattern, preload=false, from=false }) => {
|
|||
: views.welcome
|
||||
|
||||
return <Layout {...layoutProps}>
|
||||
{messages}
|
||||
<Component {...componentProps} />
|
||||
</Layout>
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue