Force post options open when dirty or on errors (#1129)
* wip submit will open options * fix: options show on error discussions * lint * feat: all types check for dirty or errors * lint * fix ordering * dirty and error useEffects * use formik context * update dirty checks on forms * revert dirty logic * simplify handle error functions * lint * add myself to contributors and update awards * use Formik context in adv-post-form * move all logic into accordian item * move logic up to adv-post-form * lint * errors open options every time * refine dirty form accordians --------- Co-authored-by: Keyan <34140557+huumn@users.noreply.github.com> Co-authored-by: keyan <keyan.kousha+huumn@gmail.com>
This commit is contained in:
parent
77c87dae80
commit
57fbab31b3
@ -79,5 +79,6 @@ felipebueno,pr,#1094,,,,2,,80k,felipebueno@getalby.com,2024-05-06
|
||||
benalleng,helpfulness,#1127,#927,good-first-issue,,,,2k,benalleng@mutiny.plus,2024-05-04
|
||||
itsrealfake,pr,#1135,#1016,good-first-issue,,,nonideal solution,10k,itsrealfake2@stacker.news,2024-05-06
|
||||
SatsAllDay,issue,#1135,#1016,good-first-issue,,,,1k,weareallsatoshi@getalby.com,2024-05-04
|
||||
s373nZ,issue,#1136,#1107,medium,high,,,50k,???,???
|
||||
SatsAllDay,pr,#1145,#717,medium,,,,250k,weareallsatoshi@zeuspay.com,???
|
||||
s373nZ,issue,#1136,#1107,medium,high,,,50k,se7enz@minibits.cash,2024-05-05
|
||||
benalleng,pr,#1129,#1045,good-first-issue,,,paid for advice out of band,20k,benalleng@mutiny.plus,???
|
||||
benalleng,pr,#1129,#491,good-first-issue,,,,20k,benalleng@mutiny.plus,???
|
||||
|
|
@ -3,12 +3,19 @@ import AccordionContext from 'react-bootstrap/AccordionContext'
|
||||
import { useAccordionButton } from 'react-bootstrap/AccordionButton'
|
||||
import ArrowRight from '@/svgs/arrow-right-s-fill.svg'
|
||||
import ArrowDown from '@/svgs/arrow-down-s-fill.svg'
|
||||
import { useContext } from 'react'
|
||||
import { useContext, useEffect } from 'react'
|
||||
|
||||
function ContextAwareToggle ({ children, headerColor = 'var(--theme-grey)', eventKey }) {
|
||||
function ContextAwareToggle ({ children, headerColor = 'var(--theme-grey)', eventKey, show }) {
|
||||
const { activeEventKey } = useContext(AccordionContext)
|
||||
const decoratedOnClick = useAccordionButton(eventKey)
|
||||
|
||||
useEffect(() => {
|
||||
// if we want to show the accordian and it's not open, open it
|
||||
if (show && activeEventKey !== eventKey) {
|
||||
decoratedOnClick()
|
||||
}
|
||||
}, [show])
|
||||
|
||||
const isCurrentEventKey = activeEventKey === eventKey
|
||||
|
||||
return (
|
||||
@ -24,7 +31,7 @@ function ContextAwareToggle ({ children, headerColor = 'var(--theme-grey)', even
|
||||
export default function AccordianItem ({ header, body, headerColor = 'var(--theme-grey)', show }) {
|
||||
return (
|
||||
<Accordion defaultActiveKey={show ? '0' : undefined}>
|
||||
<ContextAwareToggle eventKey='0'><div style={{ color: headerColor }}>{header}</div></ContextAwareToggle>
|
||||
<ContextAwareToggle show={show} eventKey='0'><div style={{ color: headerColor }}>{header}</div></ContextAwareToggle>
|
||||
<Accordion.Collapse eventKey='0' className='mt-2'>
|
||||
<div>{body}</div>
|
||||
</Accordion.Collapse>
|
||||
|
@ -10,6 +10,7 @@ import styles from './adv-post-form.module.css'
|
||||
import { useMe } from './me'
|
||||
import { useFeeButton } from './fee-button'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useFormikContext } from 'formik'
|
||||
|
||||
const EMPTY_FORWARD = { nym: '', pct: '' }
|
||||
|
||||
@ -20,11 +21,43 @@ export function AdvPostInitial ({ forward, boost }) {
|
||||
}
|
||||
}
|
||||
|
||||
export default function AdvPostForm ({ children, item }) {
|
||||
export default function AdvPostForm ({ children, item, storageKeyPrefix }) {
|
||||
const me = useMe()
|
||||
const { merge } = useFeeButton()
|
||||
const router = useRouter()
|
||||
const [itemType, setItemType] = useState()
|
||||
const formik = useFormikContext()
|
||||
const [show, setShow] = useState(undefined)
|
||||
|
||||
useEffect(() => {
|
||||
const isDirty = formik?.values.forward?.[0].nym !== '' || formik?.values.forward?.[0].pct !== '' ||
|
||||
formik?.values.boost !== '' || (router.query?.type === 'link' && formik?.values.text !== '')
|
||||
|
||||
// if the adv post form is dirty on first render, show the accordian
|
||||
if (isDirty) {
|
||||
setShow(true)
|
||||
}
|
||||
|
||||
// HACK ... TODO: we should generically handle this kind of local storage stuff
|
||||
// in the form component, overriding the initial values
|
||||
if (storageKeyPrefix) {
|
||||
for (let i = 0; i < MAX_FORWARDS; i++) {
|
||||
['nym', 'pct'].forEach(key => {
|
||||
const value = window.localStorage.getItem(`${storageKeyPrefix}-forward[${i}].${key}`)
|
||||
if (value) {
|
||||
formik?.setFieldValue(`forward[${i}].${key}`, value)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}, [formik?.values, storageKeyPrefix])
|
||||
|
||||
useEffect(() => {
|
||||
// force show the accordian if there is an error and the form is submitting
|
||||
const hasError = !!formik?.errors?.boost || formik?.errors?.forward?.length > 0
|
||||
// if it's open we don't want to collapse on submit
|
||||
setShow(show => show || (hasError && formik?.isSubmitting))
|
||||
}, [formik?.isSubmitting])
|
||||
|
||||
useEffect(() => {
|
||||
const determineItemType = () => {
|
||||
@ -69,6 +102,7 @@ export default function AdvPostForm ({ children, item }) {
|
||||
return (
|
||||
<AccordianItem
|
||||
header={<div style={{ fontWeight: 'bold', fontSize: '92%' }}>options</div>}
|
||||
show={show}
|
||||
body={
|
||||
<>
|
||||
{children}
|
||||
|
@ -93,6 +93,8 @@ export function BountyForm ({
|
||||
}, [upsertBounty, router]
|
||||
)
|
||||
|
||||
const storageKeyPrefix = item ? undefined : 'bounty'
|
||||
|
||||
return (
|
||||
<Form
|
||||
initial={{
|
||||
@ -109,7 +111,7 @@ export function BountyForm ({
|
||||
handleSubmit ||
|
||||
onSubmit
|
||||
}
|
||||
storageKeyPrefix={item ? undefined : 'bounty'}
|
||||
storageKeyPrefix={storageKeyPrefix}
|
||||
>
|
||||
{children}
|
||||
<Input
|
||||
@ -143,7 +145,7 @@ export function BountyForm ({
|
||||
: null
|
||||
}
|
||||
/>
|
||||
<AdvPostForm edit={!!item} item={item} />
|
||||
<AdvPostForm storageKeyPrefix={storageKeyPrefix} item={item} />
|
||||
<ItemButtonBar itemId={item?.id} canDelete={false} />
|
||||
</Form>
|
||||
)
|
||||
|
@ -84,7 +84,7 @@ export function DiscussionForm ({
|
||||
}`)
|
||||
|
||||
const related = relatedData?.related?.items || []
|
||||
|
||||
const storageKeyPrefix = item ? undefined : 'discussion'
|
||||
return (
|
||||
<Form
|
||||
initial={{
|
||||
@ -97,7 +97,7 @@ export function DiscussionForm ({
|
||||
schema={schema}
|
||||
invoiceable
|
||||
onSubmit={handleSubmit || onSubmit}
|
||||
storageKeyPrefix={item ? undefined : 'discussion'}
|
||||
storageKeyPrefix={storageKeyPrefix}
|
||||
>
|
||||
{children}
|
||||
<Input
|
||||
@ -124,7 +124,7 @@ export function DiscussionForm ({
|
||||
? <div className='text-muted fw-bold'><Countdown date={editThreshold} /></div>
|
||||
: null}
|
||||
/>
|
||||
<AdvPostForm edit={!!item} item={item} />
|
||||
<AdvPostForm storageKeyPrefix={storageKeyPrefix} item={item} />
|
||||
<ItemButtonBar itemId={item?.id} />
|
||||
{!item &&
|
||||
<div className={`mt-3 ${related.length > 0 ? '' : 'invisible'}`}>
|
||||
|
@ -19,6 +19,7 @@ import { MAX_TITLE_LENGTH, MEDIA_URL } from '@/lib/constants'
|
||||
import { useToast } from './toast'
|
||||
import { toastDeleteScheduled } from '@/lib/form'
|
||||
import { ItemButtonBar } from './post'
|
||||
import { useFormikContext } from 'formik'
|
||||
|
||||
function satsMin2Mo (minute) {
|
||||
return minute * 30 * 24 * 60
|
||||
@ -166,6 +167,8 @@ export default function JobForm ({ item, sub }) {
|
||||
}
|
||||
|
||||
function PromoteJob ({ item, sub }) {
|
||||
const formik = useFormikContext()
|
||||
const [show, setShow] = useState(undefined)
|
||||
const [monthly, setMonthly] = useState(satsMin2Mo(item?.maxBid || 0))
|
||||
const [getAuctionPosition, { data }] = useLazyQuery(gql`
|
||||
query AuctionPosition($id: ID, $bid: Int!) {
|
||||
@ -180,9 +183,21 @@ function PromoteJob ({ item, sub }) {
|
||||
setMonthly(satsMin2Mo(initialMaxBid))
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (formik?.values?.maxBid !== 0) {
|
||||
setShow(true)
|
||||
}
|
||||
}, [formik?.values])
|
||||
|
||||
useEffect(() => {
|
||||
const hasMaxBidError = !!formik?.errors?.maxBid
|
||||
// if it's open we don't want to collapse on submit
|
||||
setShow(show => show || (hasMaxBidError && formik?.isSubmitting))
|
||||
}, [formik?.isSubmitting])
|
||||
|
||||
return (
|
||||
<AccordianItem
|
||||
show={item?.maxBid > 0}
|
||||
show={show}
|
||||
header={<div style={{ fontWeight: 'bold', fontSize: '92%' }}>promote</div>}
|
||||
body={
|
||||
<>
|
||||
|
@ -129,6 +129,8 @@ export function LinkForm ({ item, sub, editThreshold, children }) {
|
||||
const [postDisabled, setPostDisabled] = useState(false)
|
||||
const [titleOverride, setTitleOverride] = useState()
|
||||
|
||||
const storageKeyPrefix = item ? undefined : 'link'
|
||||
|
||||
return (
|
||||
<Form
|
||||
initial={{
|
||||
@ -142,7 +144,7 @@ export function LinkForm ({ item, sub, editThreshold, children }) {
|
||||
schema={schema}
|
||||
invoiceable
|
||||
onSubmit={onSubmit}
|
||||
storageKeyPrefix={item ? undefined : 'link'}
|
||||
storageKeyPrefix={storageKeyPrefix}
|
||||
>
|
||||
{children}
|
||||
<Input
|
||||
@ -196,7 +198,7 @@ export function LinkForm ({ item, sub, editThreshold, children }) {
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<AdvPostForm edit={!!item} item={item}>
|
||||
<AdvPostForm storageKeyPrefix={storageKeyPrefix} item={item}>
|
||||
<MarkdownInput
|
||||
label='context'
|
||||
name='text'
|
||||
|
@ -71,6 +71,8 @@ export function PollForm ({ item, sub, editThreshold, children }) {
|
||||
|
||||
const initialOptions = item?.poll?.options.map(i => i.option)
|
||||
|
||||
const storageKeyPrefix = item ? undefined : 'poll'
|
||||
|
||||
return (
|
||||
<Form
|
||||
initial={{
|
||||
@ -85,7 +87,7 @@ export function PollForm ({ item, sub, editThreshold, children }) {
|
||||
schema={schema}
|
||||
invoiceable
|
||||
onSubmit={onSubmit}
|
||||
storageKeyPrefix={item ? undefined : 'poll'}
|
||||
storageKeyPrefix={storageKeyPrefix}
|
||||
>
|
||||
{children}
|
||||
<Input
|
||||
@ -111,7 +113,7 @@ export function PollForm ({ item, sub, editThreshold, children }) {
|
||||
: null}
|
||||
maxLength={MAX_POLL_CHOICE_LENGTH}
|
||||
/>
|
||||
<AdvPostForm edit={!!item} item={item}>
|
||||
<AdvPostForm storageKeyPrefix={storageKeyPrefix} item={item}>
|
||||
<DateTimeInput
|
||||
isClearable
|
||||
label='poll expiration'
|
||||
|
@ -9,3 +9,4 @@ stargut
|
||||
mz
|
||||
btcbagehot
|
||||
felipe
|
||||
benalleng
|
||||
|
Loading…
x
Reference in New Issue
Block a user