Skip to content
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

Funding Request Proposal for Multiple Recipients #2365 #4453

Merged
merged 26 commits into from
Aug 16, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
254936f
preview multiple receipients modal
vrrayz Jun 20, 2023
dead262
multiple funding request validation
vrrayz Jun 20, 2023
4d0bfa0
csv reg exp pattern
vrrayz Jun 20, 2023
30651dc
Preview And Validate Modal validation
vrrayz Jun 21, 2023
e7517cc
lint fix
vrrayz Jun 21, 2023
e7d4b82
Tx fail fix
vrrayz Jun 21, 2023
51ddf50
suggestions
vrrayz Jul 24, 2023
dc0fb3d
Merge branch 'dev' of https://github.com/vrrayz/pioneer into issue-2365
vrrayz Jul 24, 2023
383089d
lint fix
vrrayz Jul 24, 2023
d06d0ca
side drawer
vrrayz Jul 25, 2023
1abf1d0
csv pattern change and cleanup
vrrayz Jul 25, 2023
283dd51
Multiple funding request test
vrrayz Jul 28, 2023
91cb882
lint
vrrayz Jul 28, 2023
f4477df
Merge branch 'Joystream:dev' into issue-2365
vrrayz Jul 28, 2023
e018470
Merge branch 'issue-2365' of https://github.com/vrrayz/pioneer into i…
vrrayz Jul 28, 2023
855aa76
Update packages/ui/src/app/pages/Proposals/CurrentProposals.stories.tsx
vrrayz Aug 6, 2023
44dcd9a
requested changes
vrrayz Aug 7, 2023
8fdbc0b
Merge branch 'Joystream:dev' into issue-2365
vrrayz Aug 7, 2023
172518f
lint
vrrayz Aug 7, 2023
617ab12
disable preview button
vrrayz Aug 11, 2023
18b3cf5
Merge branch 'dev' of https://github.com/vrrayz/pioneer into issue-2365
vrrayz Aug 11, 2023
904d370
Merge branch 'Joystream:dev' into issue-2365
vrrayz Aug 11, 2023
59f10f2
updated storybook test for multiple funding request
vrrayz Aug 11, 2023
9a3d8bb
Merge branch 'issue-2365' of https://github.com/vrrayz/pioneer into i…
vrrayz Aug 11, 2023
313eb98
requested changes
vrrayz Aug 11, 2023
f58a6fb
requested changes
vrrayz Aug 11, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions packages/ui/src/common/components/Modal/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,20 @@ interface ModalProps {
children: ReactNode
isDark?: boolean
className?: any
customModalSize?: any
marginRight?: any
thesan marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just realized that the changes to this file are now unnecessary. Please run: git checkout dev -- packages/ui/src/common/components/Modal/Modal.tsx to reverse these changes.

}

export const Modal = ({ onClose, modalHeight = 'm', children, modalSize, isDark, className }: ModalProps) => {
export const Modal = ({
onClose,
modalHeight = 'm',
children,
modalSize,
isDark,
className,
customModalSize,
marginRight,
}: ModalProps) => {
function onBackgroundClick(e: React.MouseEvent<HTMLDivElement, MouseEvent>) {
if (e.target === e.currentTarget) {
onClose()
Expand All @@ -68,7 +79,14 @@ export const Modal = ({ onClose, modalHeight = 'm', children, modalSize, isDark,
return (
<>
<ModalGlass onClick={onBackgroundClick} />
<ModalWrap modalMaxSize={modalSize} modalHeight={modalHeight} isDark={isDark} role="modal" className={className}>
<ModalWrap
modalMaxSize={customModalSize || modalSize}
modalHeight={modalHeight}
isDark={isDark}
role="modal"
className={className}
marginRight={marginRight}
>
{children}
</ModalWrap>
</>
Expand Down Expand Up @@ -203,13 +221,15 @@ interface ModalWrapProps {
modalMaxSize: string
isDark?: boolean
modalHeight?: ModalHeight
marginRight?: string
}

export const ModalWrap = styled.section<ModalWrapProps>`
z-index: ${ZIndex.modal};
position: absolute;
inset: 0;
margin: auto auto;
margin-right: ${({ marginRight }) => (marginRight ? `${marginRight}px` : 'auto')};
display: grid;
@media only screen and (max-height: 700px) {
max-height: 100%;
Expand All @@ -233,6 +253,8 @@ export const ModalWrap = styled.section<ModalWrapProps>`
return '904px'
case 'l':
return '1240px'
default:
return `${modalMaxSize}px`
}
}};
height: ${({ modalHeight }) => (modalHeight === 'xl' ? '90vh' : 'min-content')};
Expand Down
1 change: 1 addition & 0 deletions packages/ui/src/proposals/constants/regExp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const CSV_PATTERN = /^([^,:;]+),([^,:;]+)(;\n[^,:;]+,[^,:;]+)*(;\n)?$/
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { TextMedium, TokenValue } from '@/common/components/typography'
import { BN_ZERO } from '@/common/constants'
import { camelCaseToText } from '@/common/helpers'
import { useCurrentBlockNumber } from '@/common/hooks/useCurrentBlockNumber'
import { useKeyring } from '@/common/hooks/useKeyring'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing for this file since the validation doesn't happens in the useYupValidationResolver anymore

import { useLocalStorage } from '@/common/hooks/useLocalStorage'
import { useMachine } from '@/common/hooks/useMachine'
import { useModal } from '@/common/hooks/useModal'
Expand Down Expand Up @@ -87,6 +88,9 @@ export const AddNewProposalModal = () => {
const schema = useMemo(() => schemaFactory(api), [!api])

const path = useMemo(() => machineStateConverter(state.value), [state.value])

const keyring = useKeyring()

const form = useForm<AddNewProposalForm>({
resolver: useYupValidationResolver<AddNewProposalForm>(schema, path),
mode: 'onChange',
Expand All @@ -105,6 +109,7 @@ export const AddNewProposalModal = () => {
minTriggerBlock: currentBlock
? currentBlock.addn(constants?.votingPeriod ?? 0).addn(constants?.gracePeriod ?? 0)
: BN_ZERO,
keyring,
} as IStakingAccountSchema,
defaultValues: defaultProposalValues,
})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,47 @@
import React from 'react'
import React, { useCallback, useState } from 'react'
import { useFormContext } from 'react-hook-form'
import styled from 'styled-components'

import { SelectAccount } from '@/accounts/components/SelectAccount'
import { CurrencyName } from '@/app/constants/currency'
import { InputComponent, TokenInput } from '@/common/components/forms'
import { ButtonPrimary } from '@/common/components/buttons'
import {
InlineToggleWrap,
InputComponent,
Label,
ToggleCheckbox,
TokenInput,
InputTextarea,
} from '@/common/components/forms'
import { Arrow } from '@/common/components/icons'
import { Row } from '@/common/components/Modal'
import { RowGapBlock } from '@/common/components/page/PageContent'
import { TextMedium } from '@/common/components/typography'
import { Tooltip, TooltipDefault } from '@/common/components/Tooltip'
import { TextMedium, TextSmall, TextInlineSmall } from '@/common/components/typography'
import { CSV_PATTERN } from '@/proposals/constants/regExp'

import { PreviewAndValidateModal } from './modals/PreviewAndValidate'
import { ErrorPrompt, Prompt } from './Prompt'

export const FundingRequest = () => {
const { watch, setValue, getValues } = useFormContext()
const [isPreviewModalShown, setIsPreviewModalShown] = useState(false)
const [previewModalData, setPreviewModalData] = useState<string[]>([])
const [payMultiple] = watch(['fundingRequest.payMultiple'])
const [hasPreviewedInput] = watch(['fundingRequest.hasPreviewedInput'], { 'fundingRequest.hasPreviewedInput': true })
const verifyInput = useCallback((input: string) => {
setValue('fundingRequest.hasPreviewedInput', false, { shouldValidate: true })
setValue('fundingRequest.accountsAndAmounts', input, { shouldValidate: true })
}, [])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a dangerous hack to me. Instead please watch the csv value (currently accountsAndAmounts) and reset what should be reset (currently hasPreviewedInput) in a useEffect.

So with the current accountsAndAmounts and hasPreviewedInput it would be something like:

const accountsAndAmounts = watch('accountsAndAmounts')
useEffect(() => {
  if (getFieldState('hasPreviewedInput')) {
    setData('fundingRequest.hasPreviewedInput', false, { shouldValidate: true })
  }
}, [accountsAndAmounts])

const previewInput = useCallback(() => {
const input = getValues('fundingRequest.accountsAndAmounts')
if (CSV_PATTERN.test(input)) {
const inputSplit = input.split(';\n')
setValue('fundingRequest.hasPreviewedInput', true, { shouldValidate: true })
setIsPreviewModalShown(true)
setPreviewModalData(inputSplit)
}
}, [])
return (
<RowGapBlock gap={24}>
<Row>
Expand All @@ -17,22 +51,96 @@ export const FundingRequest = () => {
</RowGapBlock>
</Row>
<Row>
<RowGapBlock gap={20}>
<InputComponent
label="Amount"
tight
units={CurrencyName.integerValue}
required
message="Amount must be greater than zero"
name="fundingRequest.amount"
>
<TokenInput id="amount-input" placeholder="0" name="fundingRequest.amount" />
</InputComponent>
<InputComponent label="Recipient account" required inputSize="l">
<SelectAccount name="fundingRequest.account" />
</InputComponent>
<RowGapBlock gap={payMultiple ? 6 : 24}>
<Row>
<InlineToggleWrap>
<Label>Pay multiple</Label>
<Tooltip
tooltipTitle="Pay multiple"
tooltipText="For multiple accounts and amounts, follow this CSV pattern:<br/>
account1, amount1<br/>
account2, amount2<br/>
...<br/>
account20, amount20"
>
<TooltipDefault />
</Tooltip>
</InlineToggleWrap>
<ToggleCheckbox falseLabel="No" trueLabel="Yes" name="fundingRequest.payMultiple" />
</Row>
{payMultiple && (
<Row>
<Prompt>
<TextSmall>
For <TextInlineSmall bold>multiple accounts and amounts</TextInlineSmall>, follow this CSV pattern:
<br />
account1, amount1;
<br />
account2, amount2;
<br />
...
<br />
account20, amount20
</TextSmall>
</Prompt>
</Row>
)}
</RowGapBlock>
</Row>
<Row>
{payMultiple ? (
<RowGapBlock gap={12}>
<InputComponent
label="Destination accounts and transfer amounts"
required
message={'You can select up to 20 recipients'}
name="fundingRequest.accountsAndAmounts"
id="accounts-amounts"
inputSize="xl"
>
<InputTextarea
id="accounts-amounts"
name="fundingRequest.accountsAndAmounts"
placeholder="Destination account address and amount"
onInput={(event) => verifyInput(event.currentTarget.value)}
/>
</InputComponent>
<HiddenCheckBox name="fundingRequest.hasPreviewedInput" checked={hasPreviewedInput} />
{!hasPreviewedInput && <ErrorPrompt>Please preview and validate the inputs to proceed</ErrorPrompt>}
<ButtonPrimary size="medium" onClick={() => previewInput()}>
Preview and Validate <Arrow direction="right" />
</ButtonPrimary>
</RowGapBlock>
) : (
<RowGapBlock gap={20}>
<InputComponent
label="Amount"
tight
units={CurrencyName.integerValue}
required
message="Amount must be greater than zero"
name="fundingRequest.amount"
>
<TokenInput id="amount-input" placeholder="0" name="fundingRequest.amount" />
</InputComponent>
<InputComponent label="Recipient account" required inputSize="l">
<SelectAccount name="fundingRequest.account" />
</InputComponent>
</RowGapBlock>
)}
</Row>
{isPreviewModalShown && (
<PreviewAndValidateModal
previewModalData={previewModalData}
setIsPreviewModalShown={setIsPreviewModalShown}
setValue={setValue}
/>
)}
</RowGapBlock>
)
}
const HiddenCheckBox = styled.input.attrs({ type: 'checkbox' })`
margin-top: -12px;
height: 0px;
visibility: hidden;
`
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import React from 'react'
import styled from 'styled-components'

import { QuestionIcon } from '@/common/components/icons'
import { BorderRad, Colors } from '@/common/constants'

export interface PromptProps {
children: React.ReactNode
}

export const Prompt = ({ children }: PromptProps) => {
return (
<PromptContainer>
<IconSection>
<PromptQuestion>
<QuestionIcon />
</PromptQuestion>
</IconSection>
{children}
</PromptContainer>
)
}
const PromptContainer = styled.div`
display: grid;
grid-template-columns: 48px 1fr;
border-left: 4px solid ${Colors.Blue[200]};
color: ${Colors.Black[500]};
`
const IconSection = styled.div`
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
`
const PromptQuestion = styled.div`
display: flex;
position: relative;
justify-content: center;
align-items: center;
width: 16px;
height: 16px;
border-radius: ${BorderRad.full};
border: 1px solid ${Colors.Black[900]};
`
export const ErrorPrompt = styled.div`
display: flex;
justify-content: center;
flex-direction: column;
border-left: 4px solid ${Colors.Red[200]};
padding-left: 10.78px;
background-color: ${Colors.Red[50]};
color: ${Colors.Red[400]};
width: 504px;
height: 56px;
border-radius: 2px;
font-weight: 400;
`
Loading