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

Conversation

vrrayz
Copy link
Contributor

@vrrayz vrrayz commented Jun 21, 2023

Ref to this issue #2365

Create Proposal Scope Completed

  • Added "Pay multiple" toggle which when triggered addds a text are input
  • Added prompt on the format of adding accounts
  • Added Textarea for Destination accounts and transfer amounts
  • Added CSV Format Validation for the TextArea
  • Added Maximum accounts Validation
  • Added Max payment validation
  • Added Address validation
  • Added Preview Modal with Validation

@vercel
Copy link

vercel bot commented Jun 21, 2023

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Updated (UTC)
dao ✅ Ready (Inspect) Visit Preview Aug 11, 2023 2:41pm
pioneer-2 ✅ Ready (Inspect) Visit Preview Aug 11, 2023 2:41pm
pioneer-2-storybook ✅ Ready (Inspect) Visit Preview Aug 11, 2023 2:41pm

Copy link
Contributor

@traumschule traumschule left a comment

Choose a reason for hiding this comment

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

This looks great and works perfectly up to proposal creation: For some reason tx fails with

proposalsCodex.InvalidFundingRequestProposalBalance
txfailed
multitx
Is the total amount submitted correctly?

Editor

100 for alice and bob each
multi-alicebob

Validation

multicsv
multivalidate

Preview

multi-preview

@traumschule traumschule added community-dev issue suitable for community-dev pipeline qa-tests-failed builders-wg-code-review labels Jun 21, 2023
@vrrayz
Copy link
Contributor Author

vrrayz commented Jun 21, 2023

@traumschule I forgot to add in the new parameters that support multiple funding requests. My bad!.
I just added a new commit now to fix that here

Copy link
Contributor

@traumschule traumschule left a comment

Choose a reason for hiding this comment

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

Copy link
Member

@thesan thesan left a comment

Choose a reason for hiding this comment

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

@vrrayz thank you for this PR and sorry it took me so long to finally get to review it. You did a really nice work with this proposal that's really not an easy one !

However there is a bunch of changes I believe this feature needs. In addition to those change requests there is the main reason it took me so long to get to this PR: which is that this proposal should get at least one automated test. Please add a new "Specific Parameters Multiple Recipients Funding Request" test story right after this one: https://pioneer-2-storybook-joystream.vercel.app/?path=/story/pages-proposals-proposallist-current--specific-parameters-funding-request

Comment on lines 244 to 257
.test(duplicateAccounts('Duplicate accounts are not allowed'))
.test(isValidCSV('Not valid CSV format'))
.test(
maxFundingAmount(
'Maximal amount allowed is ${max}',
api?.consts.proposalsCodex.fundingRequestProposalMaxTotalAmount
)
)
.test(
maxAccounts(
'Maximum allowed accounts is ${max}',
api?.consts.proposalsCodex.fundingRequestProposalMaxAccounts.toNumber()
)
)
Copy link
Member

Choose a reason for hiding this comment

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

If I understand the design correctly, all these validations except for isValidCSV should be shown in the PreviewAndValidateModal for clarity. So the form validation should only check whether the csv format is correct.

Suggested change
.test(duplicateAccounts('Duplicate accounts are not allowed'))
.test(isValidCSV('Not valid CSV format'))
.test(
maxFundingAmount(
'Maximal amount allowed is ${max}',
api?.consts.proposalsCodex.fundingRequestProposalMaxTotalAmount
)
)
.test(
maxAccounts(
'Maximum allowed accounts is ${max}',
api?.consts.proposalsCodex.fundingRequestProposalMaxAccounts.toNumber()
)
)
.test(isValidCSV('Not valid CSV format'))

is: false,
then: (schema) =>
schema
.test(moreThanMixed(0, '')) // todo: change funding request to allow upload request in file
Copy link
Member

Choose a reason for hiding this comment

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

This comment can be removed now:

Suggested change
.test(moreThanMixed(0, '')) // todo: change funding request to allow upload request in file
.test(moreThanMixed(0, ''))

const decimals = new BN(10).pow(new BN(JOY_DECIMAL_PLACES))
return input?.split(';\n').map((item) => {
const [address, amount] = item.split(',')
const formattedAmount = new BN(amount).mul(decimals)
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't work when the amount has a decimal part because bn.js doesn't work with decimals see:

> new BN('1.5').muln(2).toNumber()
2

There is already the joy helper function for this:

Suggested change
const formattedAmount = new BN(amount).mul(decimals)
const formattedAmount = new BN(joy(amount))

Comment on lines 96 to 103
setErrorMessages((prev) =>
totalInvalidAccounts > 0 ? [...prev, 'Incorrect destination accounts detected'] : [...prev]
)
setErrorMessages((prev) =>
maxAllowedAccounts && previewAccounts?.length > maxAllowedAccounts
? [...prev, 'Maximum allowed accounts exceeded']
: [...prev]
)
Copy link
Member

Choose a reason for hiding this comment

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

Please avoid calling set state dispatchers when no data change is needed because it creates useless re-renders. Instead just call them conditionally:

Suggested change
setErrorMessages((prev) =>
totalInvalidAccounts > 0 ? [...prev, 'Incorrect destination accounts detected'] : [...prev]
)
setErrorMessages((prev) =>
maxAllowedAccounts && previewAccounts?.length > maxAllowedAccounts
? [...prev, 'Maximum allowed accounts exceeded']
: [...prev]
)
if (totalInvalidAccounts > 0) {
setErrorMessages((prev) => [...prev, 'Incorrect destination accounts detected'])
}
if (maxAllowedAccounts && previewAccounts?.length > maxAllowedAccounts) {
setErrorMessages((prev) => [...prev, 'Maximum allowed accounts exceeded'])
}

Also are you sure calling these 2 setErrorMessages in the same useEffect works ? meaning the prev value gets updated between the 2 calls ? It probably does but I have a small doubt. Just in case you haven't tested that part already.

Comment on lines 45 to 47
setIsPreviewModalShown,
previewModalData,
setValue,
Copy link
Member

Choose a reason for hiding this comment

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

You shouldn't need to pass these as props. I think you can access these with:

const { setValue, getValues } = useFormContext()

just like in the FundingRequest form.

Comment on lines 32 to 35
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])

packages/ui/src/common/components/Modal/Modal.tsx Outdated Show resolved Hide resolved
Copy link
Member

@thesan thesan left a comment

Choose a reason for hiding this comment

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

Thank you, the tests are perfect !

There is just one last thing to change in term of UX:
Screenshot from 2023-08-09 17-40-29

When the csv field is empty or invalid please:

  1. hide the "Please preview and validate the inputs to proceed" message
  2. disable the "Preview and Validate" button

Also I just realized that a followup to this PR will be needed because ATM the preview page only shows 1 of the payments. I setup a temporary PR to visualize this case in storybook: https://pioneer-2-storybook-git-fork-thesan-feature-mu-ca321d-joystream.vercel.app/?path=/story/pages-proposals-proposalpreview--funding-request-multiple-recipients

So I'll retarget this PR to a new branch.

@thesan thesan changed the base branch from dev to feature/multi-recipient-proposal August 9, 2023 16:09
Comment on lines 57 to 58
customModalSize?: any
marginRight?: any
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.

@@ -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

Comment on lines 12 to 79
export const maxAccounts = (message: string, max: number | undefined): Yup.TestConfig<any, AnyObject> => ({
message,
name: 'maxAccounts',
params: { max },
exclusive: false,
test(value: string) {
const pairs = value.split('\n')
return max ? pairs.length <= max : false
},
})

export const duplicateAccounts = (message: string): Yup.TestConfig<any, AnyObject> => ({
message,
name: 'duplicateAccounts',
exclusive: false,
test(value: string) {
const pairs = value.split('\n')
const addresses: string[] = []

for (const pair of pairs) {
const [address] = pair.split(',')
if (addresses.indexOf(address) >= 0) return false
addresses.push(address)
}

return true
},
})

export const isValidCSV = (message: string): Yup.TestConfig<any, AnyObject> => ({
message,
name: 'isValidCSV',
exclusive: false,
test(value: string) {
if (!CSV_PATTERN.test(value)) return false

const pairs = value.split('\n')

for (const pair of pairs) {
const [, amount] = pair.split(',')
if (!Number(amount)) return false
}

return true
},
})

export const maxFundingAmount = (
message: string,
max: Reference<number | BN> | number | BN | undefined,
isJoyValue = true
): Yup.TestConfig<any, AnyObject> => ({
message,
name: 'maxFundingAmount',
params: { max: isJoyValue && isBn(max) ? formatJoyValue(max, { precision: 2 }) : max },
exclusive: false,
test(value: string) {
const pairs = value.split('\n')
let total = new BN(0)
const decimals = new BN(10).pow(new BN(JOY_DECIMAL_PLACES))
for (const pair of pairs) {
const [, amount] = pair.split(',')
total = total.add(new BN(amount))
}
total = total.mul(decimals)
return !total || typeof max === 'undefined' || !isBn(total) || total.lte(new BN(this.resolve(max)))
},
})
Copy link
Member

Choose a reason for hiding this comment

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

Except for isValidCSV are any of these functions still in use ? If not please remove them

Comment on lines 38 to 41
const shouldDisablePreviewButton = () => {
const input = getFieldState('fundingRequest.csvInput')
return input.error !== undefined || !input.isDirty
}
Copy link
Member

Choose a reason for hiding this comment

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

Since this function is called on every render, it would be simpler to just have a variable:

Suggested change
const shouldDisablePreviewButton = () => {
const input = getFieldState('fundingRequest.csvInput')
return input.error !== undefined || !input.isDirty
}
const canPreviewInput = csvInput.isDirty && !csvInput.error

{shouldPreview() && <ErrorPrompt>Please preview and validate the inputs to proceed</ErrorPrompt>}
<ButtonPrimary
size="medium"
disabled={shouldDisablePreviewButton()}
Copy link
Member

Choose a reason for hiding this comment

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

So this becomes:

Suggested change
disabled={shouldDisablePreviewButton()}
disabled={!canPreviewInput}

/>
</InputComponent>
<HiddenCheckBox name="fundingRequest.hasPreviewedInput" checked={hasPreviewedInput} />
{shouldPreview() && <ErrorPrompt>Please preview and validate the inputs to proceed</ErrorPrompt>}
Copy link
Member

Choose a reason for hiding this comment

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

And this:

Suggested change
{shouldPreview() && <ErrorPrompt>Please preview and validate the inputs to proceed</ErrorPrompt>}
{canPreviewInput && !hasPreviewedInput && <ErrorPrompt>Please preview and validate the inputs to proceed</ErrorPrompt>}

I might have missed something but AFAICS this should be enough.

Copy link
Member

@thesan thesan left a comment

Choose a reason for hiding this comment

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

It's all good. Great work 🙇

@thesan thesan merged commit 83b4ef3 into Joystream:feature/multi-recipient-proposal Aug 16, 2023
3 checks passed
@chrlschwb chrlschwb removed the qa-task label Sep 22, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
community-dev issue suitable for community-dev pipeline
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants