diff --git a/packages/ui/src/app/App.stories.tsx b/packages/ui/src/app/App.stories.tsx index 23008abecd..40d8f7a9e9 100644 --- a/packages/ui/src/app/App.stories.tsx +++ b/packages/ui/src/app/App.stories.tsx @@ -24,6 +24,7 @@ type Args = { hasAccounts: boolean hasWallet: boolean isRPCNodeConnected: boolean + hasRegisteredEmail: boolean onBuyMembership: CallableFunction onTransfer: CallableFunction } @@ -66,6 +67,7 @@ export default { hasFunds: true, hasWallet: true, isRPCNodeConnected: true, + hasRegisteredEmail: true, }, parameters: { @@ -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, @@ -117,6 +118,10 @@ export default { data: { membershipByUniqueInput: { ...bob, ...MEMBER_DATA, invitees: [] } }, }, ], + + localStorage: { + membersEmail: args.hasRegisteredEmail ? JSON.stringify({ 0: 'alice@example.com' }) : '', + }, } }, }, @@ -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() + }, +} + +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) + expect(modal.getByText('Pending transaction')) + }, +} diff --git a/packages/ui/src/memberships/modals/EmailSubscriptionModal/EmaiSubscriptionFormModal.tsx b/packages/ui/src/memberships/modals/EmailSubscriptionModal/EmaiSubscriptionFormModal.tsx index 31248bac5c..93fcddd90c 100644 --- a/packages/ui/src/memberships/modals/EmailSubscriptionModal/EmaiSubscriptionFormModal.tsx +++ b/packages/ui/src/memberships/modals/EmailSubscriptionModal/EmaiSubscriptionFormModal.tsx @@ -26,7 +26,7 @@ const EmailSubscriptionSchema = Yup.object().shape({ }) export const EmailSubscriptionFormModal = ({ onClose, onSubmit, member }: Props) => { - const [, setMembersEmail] = useLocalStorage>('memberEmail') + const [, setMembersEmail] = useLocalStorage>('membersEmail') const form = useForm({ resolver: useYupValidationResolver(EmailSubscriptionSchema), diff --git a/packages/ui/src/memberships/modals/EmailSubscriptionModal/EmailSubscriptionModal.tsx b/packages/ui/src/memberships/modals/EmailSubscriptionModal/EmailSubscriptionModal.tsx index 6016bbf991..b13a3adb9f 100644 --- a/packages/ui/src/memberships/modals/EmailSubscriptionModal/EmailSubscriptionModal.tsx +++ b/packages/ui/src/memberships/modals/EmailSubscriptionModal/EmailSubscriptionModal.tsx @@ -1,9 +1,12 @@ +import { gql } from '@apollo/client' 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' +import { useBackend } from '@/common/hooks/useBackend' +import { useLocalStorage } from '@/common/hooks/useLocalStorage' import { useMachine } from '@/common/hooks/useMachine' import { useModal } from '@/common/hooks/useModal' import { EmailSubscriptionModalCall } from '@/memberships/modals/EmailSubscriptionModal/index' @@ -12,22 +15,75 @@ import { EmailSubscriptionFormModal } from './EmaiSubscriptionFormModal' import { EmailSubscriptionMachine } from './machine' import { EmailSubscriptionForm } from './types' +const SIGNUP_MUTATION = gql` + mutation signup($memberId: Int!, $name: String!, $email: String!, $signature: String!, $timestamp: BigInt!) { + signup(memberId: $memberId, name: $name, email: $email, signature: $signature, timestamp: $timestamp) + } +` + export const EmailSubscriptionModal = () => { - // const { api } = useApi() const { hideModal, modalData: { member }, } = useModal() - + const [, setMembersEmail] = useLocalStorage>('membersEmail') + const { wallet } = useMyAccounts() const [state, send] = useMachine(EmailSubscriptionMachine) + const { + data, + error, + send: mutate, + } = useBackend({ + mutation: SIGNUP_MUTATION, + }) + + 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') + } + } + + const signUp = async () => { + if (state.event.type === 'SIGNED') { + await mutate({ + memberId: parseFloat(member.id), + name: member.name, + email: state.context.email, + signature: state.event.signature, + timestamp: state.event.timestamp, + }) + } + } useEffect(() => { if (state.matches('signature')) { - // const timestamp = new Date() - // api?.sign(member.controllerAccount, `${member.id}:${timestamp}`) + signEmail() + } + if (state.matches('transaction')) { + signUp() } }, [state]) + useEffect(() => { + if (data) { + send('SUCCESS') + setMembersEmail((emails) => ({ ...emails, [member.id]: state.context.email || '' })) + hideModal() + } + if (error) { + send('ERROR') + } + }, [data, error]) + if (state.matches('prepare')) { return ( , EmailSub metaMessages: { error: 'There was a problem during the email subscription.', }, + cancel: { + target: 'canceled', + action: 'prepare', + }, }), }, } diff --git a/packages/ui/src/mocks/providers/accounts.tsx b/packages/ui/src/mocks/providers/accounts.tsx index 2e2bb9857f..e5a01b98a0 100644 --- a/packages/ui/src/mocks/providers/accounts.tsx +++ b/packages/ui/src/mocks/providers/accounts.tsx @@ -153,7 +153,11 @@ const WALLET: Wallet = { title: 'bar', installUrl: 'http://example.com', logo: { src: PolkadotLogo, alt: 'Wallet logo' }, - signer: {}, + signer: { + signPayload: async () => ({ + signature: '0x123', + }), + }, extension: {}, getAccounts: async () => [], subscribeAccounts: () => undefined,