From 4e0f396090e6e2806c4af921490e73315b244950 Mon Sep 17 00:00:00 2001 From: Tom Milewski Date: Wed, 25 Sep 2024 10:01:14 -0400 Subject: [PATCH] refactor(elements): Create sign-up actors --- .../machines/sign-in/actors/start-attempt.ts | 2 +- .../sign-up/actors/start-attempt-web3.ts | 21 +++++++ .../machines/sign-up/actors/start-attempt.ts | 16 +++++ .../actors/verification-attempt-email-link.ts | 63 +++++++++++++++++++ .../sign-up/actors/verification-attempt.ts | 10 +++ .../sign-up/actors/verification-prepare.ts | 15 +++++ .../machines/sign-up/start.machine.ts | 2 - 7 files changed, 126 insertions(+), 3 deletions(-) create mode 100644 packages/elements/src/internals/machines/sign-up/actors/start-attempt-web3.ts create mode 100644 packages/elements/src/internals/machines/sign-up/actors/start-attempt.ts create mode 100644 packages/elements/src/internals/machines/sign-up/actors/verification-attempt-email-link.ts create mode 100644 packages/elements/src/internals/machines/sign-up/actors/verification-attempt.ts create mode 100644 packages/elements/src/internals/machines/sign-up/actors/verification-prepare.ts diff --git a/packages/elements/src/internals/machines/sign-in/actors/start-attempt.ts b/packages/elements/src/internals/machines/sign-in/actors/start-attempt.ts index 665d49a4ea..55143a8a8a 100644 --- a/packages/elements/src/internals/machines/sign-in/actors/start-attempt.ts +++ b/packages/elements/src/internals/machines/sign-in/actors/start-attempt.ts @@ -6,7 +6,7 @@ import type { FormFields } from '../../form'; export type StartAttemptInput = { clerk: LoadedClerk; fields: FormFields }; export type StartAttemptOutput = SignInResource; -export const startAttempt = fromPromise(({ input }) => { +export const startAttempt = fromPromise(({ input }) => { const { clerk, fields } = input; const password = fields.get('password'); diff --git a/packages/elements/src/internals/machines/sign-up/actors/start-attempt-web3.ts b/packages/elements/src/internals/machines/sign-up/actors/start-attempt-web3.ts new file mode 100644 index 0000000000..f0cbc68861 --- /dev/null +++ b/packages/elements/src/internals/machines/sign-up/actors/start-attempt-web3.ts @@ -0,0 +1,21 @@ +import type { LoadedClerk, SignUpResource, Web3Strategy } from '@clerk/types'; +import { fromPromise } from 'xstate'; + +import { ClerkElementsRuntimeError } from '~/internals/errors'; + +export type StartAttemptWeb3Input = { clerk: LoadedClerk; strategy: Web3Strategy }; +export type StartAttemptWeb3Output = SignUpResource; + +export const startAttempt = fromPromise(({ input }) => { + const { clerk, strategy } = input; + + if (strategy === 'web3_metamask_signature') { + return clerk.client.signUp.authenticateWithMetamask(); + } + + if (strategy === 'web3_coinbase_wallet_signature') { + return clerk.client.signUp.authenticateWithCoinbaseWallet(); + } + + throw new ClerkElementsRuntimeError(`Unsupported Web3 strategy: ${strategy}`); +}); diff --git a/packages/elements/src/internals/machines/sign-up/actors/start-attempt.ts b/packages/elements/src/internals/machines/sign-up/actors/start-attempt.ts new file mode 100644 index 0000000000..9181a182c6 --- /dev/null +++ b/packages/elements/src/internals/machines/sign-up/actors/start-attempt.ts @@ -0,0 +1,16 @@ +import type { LoadedClerk, SignUpResource } from '@clerk/types'; +import { fromPromise } from 'xstate'; + +import { fieldsToSignUpParams } from '~/internals/machines/sign-up/utils/fields-to-params'; + +import type { FormFields } from '../../form'; + +export type StartAttemptParams = { strategy: 'ticket'; ticket: string } | { strategy?: never; ticket?: never }; +export type StartAttemptInput = { clerk: LoadedClerk; fields: FormFields; params?: StartAttemptParams }; +export type StartAttemptOutput = SignUpResource; + +// TODO: Also used for continueAttempt +export const startAttempt = fromPromise(({ input }) => { + const { clerk, fields, params } = input; + return clerk.client.signUp.create({ ...fieldsToSignUpParams(fields), ...params }); +}); diff --git a/packages/elements/src/internals/machines/sign-up/actors/verification-attempt-email-link.ts b/packages/elements/src/internals/machines/sign-up/actors/verification-attempt-email-link.ts new file mode 100644 index 0000000000..e04f809987 --- /dev/null +++ b/packages/elements/src/internals/machines/sign-up/actors/verification-attempt-email-link.ts @@ -0,0 +1,63 @@ +import { Poller } from '@clerk/shared/poller'; +import type { LoadedClerk } from '@clerk/types'; +import { fromCallback } from 'xstate'; + +import { ClerkElementsError, ClerkElementsRuntimeError } from '~/internals/errors'; + +export type VerificationAttemptEmailLinkInput = { clerk: LoadedClerk }; +export type VerificationAttemptEmailLinkEvents = { type: 'STOP' }; + +export const verificationAttemptEmailLink = fromCallback< + VerificationAttemptEmailLinkEvents, + VerificationAttemptEmailLinkInput +>(({ receive, sendBack, input: { clerk } }) => { + const { run, stop } = Poller(); + + void run(async () => + clerk.client.signUp + .reload() + .then(resource => { + const signInStatus = resource.status; + const verificationStatus = resource.verifications.emailAddress.status; + + // Short-circuit if the sign-up resource is already complete + if (signInStatus === 'complete') { + return sendBack({ type: 'EMAIL_LINK.VERIFIED', resource }); + } + + switch (verificationStatus) { + case 'verified': + case 'transferable': + case 'expired': { + sendBack({ type: `EMAIL_LINK.${verificationStatus.toUpperCase()}`, resource }); + break; + } + case 'failed': { + sendBack({ + type: 'EMAIL_LINK.FAILED', + error: new ClerkElementsError('email-link-verification-failed', 'Email verification failed'), + resource, + }); + break; + } + // case 'unverified': + default: + return; + } + + stop(); + }) + .catch(error => { + stop(); + new ClerkElementsRuntimeError(error); + }), + ); + + receive(event => { + if (event.type === 'STOP') { + stop(); + } + }); + + return () => stop(); +}); diff --git a/packages/elements/src/internals/machines/sign-up/actors/verification-attempt.ts b/packages/elements/src/internals/machines/sign-up/actors/verification-attempt.ts new file mode 100644 index 0000000000..260bd2193c --- /dev/null +++ b/packages/elements/src/internals/machines/sign-up/actors/verification-attempt.ts @@ -0,0 +1,10 @@ +import type { AttemptVerificationParams, LoadedClerk, SignUpResource } from '@clerk/types'; +import { fromPromise } from 'xstate'; + +export type VerificationAttemptInput = { clerk: LoadedClerk; params: AttemptVerificationParams }; +export type VerificationAttemptOutput = SignUpResource; + +export const verificationAttempt = fromPromise(({ input }) => { + const { clerk, params } = input; + return clerk.client.signUp.attemptVerification(params); +}); diff --git a/packages/elements/src/internals/machines/sign-up/actors/verification-prepare.ts b/packages/elements/src/internals/machines/sign-up/actors/verification-prepare.ts new file mode 100644 index 0000000000..4af8bb5d03 --- /dev/null +++ b/packages/elements/src/internals/machines/sign-up/actors/verification-prepare.ts @@ -0,0 +1,15 @@ +import type { LoadedClerk, PrepareVerificationParams, SignUpResource } from '@clerk/types'; +import { fromPromise } from 'xstate'; + +export type VerificationPrepareInput = { clerk: LoadedClerk; params: PrepareVerificationParams }; +export type VerificationPrepareOutput = SignUpResource; + +export const verificationPrepare = fromPromise(({ input }) => { + const { clerk, params } = input; + + if (params.strategy === 'email_link' && params.redirectUrl) { + params.redirectUrl = clerk.buildUrlWithAuth(params.redirectUrl); + } + + return clerk.client.signUp.prepareVerification(params); +}); diff --git a/packages/elements/src/internals/machines/sign-up/start.machine.ts b/packages/elements/src/internals/machines/sign-up/start.machine.ts index 3bec675f0e..54f6ce555a 100644 --- a/packages/elements/src/internals/machines/sign-up/start.machine.ts +++ b/packages/elements/src/internals/machines/sign-up/start.machine.ts @@ -6,7 +6,6 @@ import { ClerkElementsRuntimeError } from '~/internals/errors'; import type { FormFields } from '~/internals/machines/form'; import { sendToLoading } from '~/internals/machines/shared'; import { fieldsToSignUpParams } from '~/internals/machines/sign-up/utils'; -import { ThirdPartyMachine } from '~/internals/machines/third-party'; import { assertActorEventError } from '~/internals/machines/utils/assert'; import type { SignInRouterMachineActorRef } from './router.types'; @@ -45,7 +44,6 @@ export const SignUpStartMachine = setup({ throw new ClerkElementsRuntimeError(`Unsupported Web3 strategy: ${strategy}`); }, ), - thirdParty: ThirdPartyMachine, }, actions: { sendToNext: ({ context }) => context.parent.send({ type: 'NEXT' }),