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

Feature/email subscription api #4489

Closed
wants to merge 19 commits into from
Closed
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: '[email protected]' }) : '',
},
}
},
},
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()
},
}

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'), '[email protected]')
await waitFor(() => expect(button.closest('button')).toBeEnabled())
await userEvent.click(button)
expect(modal.getByText('Pending transaction'))
},
}
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,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'
Expand All @@ -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<EmailSubscriptionModalCall>()

const [, setMembersEmail] = useLocalStorage<Record<string, string>>('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 (
<EmailSubscriptionFormModal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ export const EmailSubscriptionMachine = createMachine<Partial<Context>, EmailSub
metaMessages: {
error: 'There was a problem during the email subscription.',
},
cancel: {
target: 'canceled',
action: 'prepare',
},
}),
},
}
Expand Down
6 changes: 5 additions & 1 deletion packages/ui/src/mocks/providers/accounts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading