1
0
Fork 0

Merge pull request #2284 from eriese/eriese-fix-memo

fix measurement gist issues with better state management  #2281
This commit is contained in:
Joost De Cock 2022-06-23 09:15:50 +02:00 committed by GitHub
commit 4bdda5062c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 114 additions and 76 deletions

View file

@ -11,25 +11,26 @@ import measurementAsMm from '@freesewing/utils/measurementAsMm'
* m holds the measurement name. It's just so long to type
* measurement and I always have some typo in it because dyslexia.
*/
const MeasurementInput = ({ m, gist, app, updateMeasurements }) => {
const MeasurementInput = ({ m, gist, app, updateMeasurements, focus }) => {
const { t } = useTranslation(['app', 'measurements'])
const prefix = (app.site === 'org') ? '' : 'https://freesewing.org'
const title = t(`measurements:${m}`)
const isDegree = isDegreeMeasurement(m);
const factor = useMemo(() => (isDegree ? 1 : (gist?.units == 'imperial' ? 25.4 : 10)), [gist?.units])
const factor = useMemo(() => (isDegree ? 1 : (gist.units == 'imperial' ? 25.4 : 10)), [gist.units])
const isValValid = val => (typeof val === 'undefined' || val === '')
? null
: val !== false && !isNaN(val)
: val != false && !isNaN(val)
const isValid = (newVal) => (typeof newVal === 'undefined')
? isValValid(val)
: isValValid(newVal)
const [val, setVal] = useState(gist?.measurements?.[m] / factor || '')
const [val, setVal] = useState(gist.measurements?.[m] / factor || '')
// keep a single reference to a debounce timer
const debounceTimeout = useRef(null);
const input = useRef(null);
// onChange
const update = useCallback((evt) => {
@ -38,7 +39,7 @@ const MeasurementInput = ({ m, gist, app, updateMeasurements }) => {
// set Val immediately so that the input reflects it
setVal(evtVal)
let useVal = isDegree ? evtVal : measurementAsMm(evtVal, gist?.units);
let useVal = isDegree ? evtVal : measurementAsMm(evtVal, gist.units);
const ok = isValid(useVal)
// only set to the gist if it's valid
if (ok) {
@ -50,14 +51,14 @@ const MeasurementInput = ({ m, gist, app, updateMeasurements }) => {
updateMeasurements(useVal, m)
}, 500);
}
}, [gist?.units])
}, [gist.units])
// use this for better update efficiency
// FIXME: This breaks gist updates.
// See: https://github.com/freesewing/freesewing/issues/2281
const memoVal = gist?.measurements?.[m] //useMemo(() => gist?.measurements?.[m], [gist])
const memoVal = useMemo(() => gist.measurements?.[m], [gist])
// track validity against the value and the units
const valid = useMemo(() => isValid(isDegree ? val : measurementAsMm(val, gist?.units)), [val, gist?.units])
const valid = useMemo(() => isValid(isDegree ? val : measurementAsMm(val, gist.units)), [val, gist.units])
// hook to update the value or format when the gist changes
useEffect(() => {
@ -68,8 +69,17 @@ const MeasurementInput = ({ m, gist, app, updateMeasurements }) => {
}
}, [memoVal, factor])
// focus when prompted by parent
useEffect(() => {
if (focus) {
input.current.focus();
}
}, [focus])
// cleanup
useEffect(() => clearTimeout(debounceTimeout.current), [])
useEffect(() => {
clearTimeout(debounceTimeout.current)
}, [])
if (!m) return null
@ -89,6 +99,7 @@ const MeasurementInput = ({ m, gist, app, updateMeasurements }) => {
<label className="input-group input-group-lg">
<input
key={`input-${m}`}
ref={input}
type="text"
placeholder={title}
className={`

View file

@ -1,4 +1,4 @@
import React from 'react'
import React, {useMemo, useEffect, useState} from 'react'
import MeasurementInput from '../inputs/measurement.js'
import { withBreasts, withoutBreasts } from '@freesewing/models'
import nonHuman from './non-human.js'
@ -7,8 +7,6 @@ import WithoutBreastsIcon from 'shared/components/icons/without-breasts.js'
import { useTranslation } from 'next-i18next'
import Setting from '../menu/core-settings/setting';
import {settings} from '../menu/core-settings/index';
import Popout from 'shared/components/popout'
import Code from 'shared/components/code'
const groups = {
people: {
@ -29,7 +27,7 @@ const icons = {
without: <WithoutBreastsIcon />,
}
const WorkbenchMeasurements = ({ app, design, gist, updateGist }) => {
const WorkbenchMeasurements = ({ app, design, gist, updateGist, gistReady }) => {
const { t } = useTranslation(['app', 'cfp'])
// Method to handle measurement updates
@ -39,13 +37,26 @@ const WorkbenchMeasurements = ({ app, design, gist, updateGist }) => {
updateGist('measurements', value)
} else {
// Set one measurement
const newValues = {...gist.measurements}
newValues[m] = value
updateGist('measurements', newValues)
updateGist(['measurements', m], value)
}
}
const [firstInvalid, setFirstInvalid] = useState(undefined)
useEffect(() => {
if (!gistReady) { return }
for (const m of design.config?.measurements || []) {
if (!gist?.measurements?.[m]) {
setFirstInvalid(m);
return;
}
setFirstInvalid(undefined)
}
}, [gistReady])
// Save us some typing
const inputProps = { app, updateMeasurements, gist }
const inputProps = useMemo(() => ({ app, updateMeasurements, gist }), [app, gist])
return (
<div className="m-auto max-w-2xl">
@ -54,11 +65,6 @@ const WorkbenchMeasurements = ({ app, design, gist, updateGist }) => {
{design.config.name}:
</span> {t('measurements')}
</h1>
<Popout fixme>
<h5>Debug for issue <a href="https://github.com/freesewing/freesewing/issues/2281">#2281</a></h5>
<p>Current value of <Code>gist.measurements</Code></p>
<pre>{JSON.stringify(gist.measurements, null ,2)}</pre>
</Popout>
<details open className="my-2">
<summary><h2 className="inline pl-1">{t('cfp:preloadMeasurements')}</h2></summary>
<div className="ml-2 pl-4 border-l-2">
@ -94,7 +100,7 @@ const WorkbenchMeasurements = ({ app, design, gist, updateGist }) => {
</div>
</details>
<details className="my-2">
<details open className="my-2">
<summary><h2 className="inline pl-2">{t('cfp:enterMeasurements')}</h2></summary>
<Setting key={'units'} setting={'units'} config={settings.units} updateGist={updateGist} {...inputProps} />
<div className="ml-2 pl-4 border-l-2">
@ -102,7 +108,7 @@ const WorkbenchMeasurements = ({ app, design, gist, updateGist }) => {
<>
<h3>{t('requiredMeasurements')}</h3>
{design.config.measurements.map(m => (
<MeasurementInput key={m} m={m} {...inputProps} />
<MeasurementInput key={m} m={m} focus={m == firstInvalid} {...inputProps} />
))}
</>
)}

View file

@ -14,12 +14,15 @@ 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 [
if (g.design !== undefined && g.design !== pattern.config.name) return [
false, `You tried loading a configuration for ${g.design} into a ${pattern.config.name} development environment`
]
return g
}
// TODO notify people of these errors
else return [false, 'This gist does not seem to be a valid pattern configuration']
}
}