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

signature for email modal #4474

Merged
merged 18 commits into from
Aug 7, 2023
69 changes: 68 additions & 1 deletion packages/ui/src/app/App.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type Args = {
hasAccounts: boolean
hasWallet: boolean
isRPCNodeConnected: boolean
hasRegisteredEmail: boolean
onBuyMembership: CallableFunction
onTransfer: CallableFunction
}
Expand Down Expand Up @@ -66,6 +67,7 @@ export default {
hasFunds: true,
hasWallet: true,
isRPCNodeConnected: true,
hasRegisteredEmail: true,
},

parameters: {
Expand All @@ -76,7 +78,6 @@ export default {
balances: args.hasFunds ? parameters.totalBalance : 0,
...(args.hasMemberships ? { member } : { account: { name: member.handle, address: member.controllerAccount } }),
})

return {
accounts: {
active: args.isLoggedIn ? 'alice' : undefined,
Expand Down Expand Up @@ -117,6 +118,10 @@ export default {
data: { membershipByUniqueInput: { ...bob, ...MEMBER_DATA, invitees: [] } },
},
],

localStorage: {
membersEmail: args.hasRegisteredEmail ? JSON.stringify({ 0: 'alice@example.com' }) : '',
},
}
},
},
Expand Down Expand Up @@ -408,3 +413,65 @@ export const BuyMembershipTxFailure: Story = {
expect(await modal.findByText('Some error message'))
},
}

// ----------------------------------------------------------------------------
// Test Emil Subsciption Modal
// ----------------------------------------------------------------------------
export const EmailSubscriptionModalDecline: Story = {
args: {
isLoggedIn: true,
hasMemberships: true,
hasAccounts: true,
hasFunds: true,
hasWallet: true,
isRPCNodeConnected: true,
hasRegisteredEmail: false,
},
play: async ({ canvasElement }) => {
const modal = withinModal(canvasElement)
const element = await modal.getByText('Sign up to email notifications')
expect(element)
await userEvent.click(modal.getByText('Not now'))
expect(element).not.toBeInTheDocument()
Copy link
Member

Choose a reason for hiding this comment

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

Nice one 👍

},
}

export const EmailSubscriptionModalWrongEmail: Story = {
args: {
isLoggedIn: true,
hasMemberships: true,
hasAccounts: true,
hasFunds: true,
hasWallet: true,
isRPCNodeConnected: true,
hasRegisteredEmail: false,
},
play: async ({ canvasElement }) => {
const modal = withinModal(canvasElement)
const button = modal.getByText(/^Sign and Authorize Email/i).closest('button')
expect(button).toBeDisabled()
await userEvent.type(modal.getByPlaceholderText('Add email for notifications here'), 'test@email')
expect(button).toBeDisabled()
},
}

export const EmailSubscriptionModalSubscribe: Story = {
args: {
isLoggedIn: true,
hasMemberships: true,
hasAccounts: true,
hasFunds: true,
hasWallet: true,
isRPCNodeConnected: true,
hasRegisteredEmail: false,
},
play: async ({ canvasElement }) => {
const modal = withinModal(canvasElement)
const button = modal.getByText(/^Sign and Authorize Email/i)
expect(button.closest('button')).toBeDisabled()
await userEvent.type(modal.getByPlaceholderText('Add email for notifications here'), 'test@email.com')
await waitFor(() => expect(button.closest('button')).toBeEnabled())
await userEvent.click(button)
IlyaSmiyukha marked this conversation as resolved.
Show resolved Hide resolved
expect(modal.getByText('Transaction was canceled.'))
Copy link
Member

Choose a reason for hiding this comment

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

At this point the machine gets in the cancel state because wallet? signer.signPayload is undefined, the code tries to call it, and all errors are assumed to be the user cancelling the signature. So this is not correct. Instead please change the accounts mock so you can pass it a storybook action called "sign" which will be added to the mock wallet. Currently WALLET is a constant in the module scope so this should be changed.

},
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const EmailSubscriptionSchema = Yup.object().shape({
})

export const EmailSubscriptionFormModal = ({ onClose, onSubmit, member }: Props) => {
const [, setMembersEmail] = useLocalStorage<Record<string, string>>('memberEmail')
const [, setMembersEmail] = useLocalStorage<Record<string, string>>('membersEmail')

const form = useForm({
resolver: useYupValidationResolver<EmailSubscriptionForm>(EmailSubscriptionSchema),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useEffect } from 'react'

// import { useApi } from '@/api/hooks/useApi'
import { useMyAccounts } from '@/accounts/hooks/useMyAccounts'
import { FailureModal } from '@/common/components/FailureModal'
import { ResultText } from '@/common/components/Modal'
import { WaitModal } from '@/common/components/WaitModal'
Expand All @@ -13,18 +13,32 @@ import { EmailSubscriptionMachine } from './machine'
import { EmailSubscriptionForm } from './types'

export const EmailSubscriptionModal = () => {
// const { api } = useApi()
const {
hideModal,
modalData: { member },
} = useModal<EmailSubscriptionModalCall>()

const { wallet } = useMyAccounts()
const [state, send] = useMachine(EmailSubscriptionMachine)

const signEmail = async () => {
const timestamp = Date.now()
try {
const signature = await wallet?.signer.signPayload({
address: member.controllerAccount,
data: `${member.id}:${timestamp}`,
})
if (signature) {
send('SIGNED', { signature: signature.signature, timestamp })
}
} catch (error) {
send('CANCEL')
}
}

useEffect(() => {
if (state.matches('signature')) {
// const timestamp = new Date()
// api?.sign(member.controllerAccount, `${member.id}:${timestamp}`)
signEmail()
}
}, [state])

Expand All @@ -40,11 +54,17 @@ export const EmailSubscriptionModal = () => {
)
}

if (state.matches('error')) {
if (state.matches('error') || state.matches('canceled')) {
Copy link
Member

Choose a reason for hiding this comment

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

Also if the "cancel" state just goes back to the "prepare" state it shouldn't show the failure modal.

return (
<FailureModal onClose={hideModal}>
There was a problem registering your email.
<ResultText>We could not register your email at the moment! Please, try again later!</ResultText>
{state.matches('canceled') ? (
<ResultText>Transaction was canceled.</ResultText>
) : (
<>
There was a problem registering your email.
<ResultText>We could not register your email at the moment! Please, try again later!</ResultText>
</>
)}
</FailureModal>
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type EmailSubscriptionState =
| { value: 'transaction'; context: Context }
| { value: 'success'; context: Context }
| { value: 'error'; context: Context }
| { value: 'canceled'; context: EmptyObject }
Copy link
Member

Choose a reason for hiding this comment

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

The state is "cancel" but I don't think it needs to be defined since it just goes back to the "prepare" state


export type EmailSubscriptionEvent =
| { type: 'DONE'; email: string }
Expand Down Expand Up @@ -67,6 +68,10 @@ export const EmailSubscriptionMachine = createMachine<Partial<Context>, EmailSub
metaMessages: {
error: 'There was a problem during the email subscription.',
},
cancel: {
target: 'canceled',
action: 'BACK',
Copy link
Member

Choose a reason for hiding this comment

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

For this to work the BACK action should be define in the EmailSubscriptionEvent and also the target should be prepare

},
}),
},
}
Expand Down