diff --git a/.changeset/bright-mangos-pump.md b/.changeset/bright-mangos-pump.md new file mode 100644 index 0000000000..fb7ed12de5 --- /dev/null +++ b/.changeset/bright-mangos-pump.md @@ -0,0 +1,5 @@ +--- +'@clerk/clerk-js': patch +--- + +Revalidate environment on window focus for Keyless. diff --git a/packages/clerk-js/src/ui/components/KeylessPrompt/index.tsx b/packages/clerk-js/src/ui/components/KeylessPrompt/index.tsx index 5d3bef3aef..7e0e732149 100644 --- a/packages/clerk-js/src/ui/components/KeylessPrompt/index.tsx +++ b/packages/clerk-js/src/ui/components/KeylessPrompt/index.tsx @@ -3,12 +3,12 @@ import { useClerk } from '@clerk/shared/react'; import { css } from '@emotion/react'; import { useState } from 'react'; -import { useEnvironment } from '../../contexts'; import { descriptors, Flex, Link, Spinner } from '../../customizables'; import { Portal } from '../../elements/Portal'; import { InternalThemeProvider } from '../../styledSystem'; import { ClerkLogoIcon } from './ClerkLogoIcon'; import { KeySlashIcon } from './KeySlashIcon'; +import { useRevalidateEnvironment } from './use-revalidate-environment'; type KeylessPromptProps = { claimUrl: string; @@ -20,7 +20,7 @@ const _KeylessPrompt = (_props: KeylessPromptProps) => { const [isLoading, setIsLoading] = useState(false); const handleFocus = () => setIsExpanded(true); - const claimed = Boolean(useEnvironment().authConfig.claimedAt); + const claimed = Boolean(useRevalidateEnvironment().authConfig.claimedAt); const clerk = useClerk(); return ( diff --git a/packages/clerk-js/src/ui/components/KeylessPrompt/use-revalidate-environment.ts b/packages/clerk-js/src/ui/components/KeylessPrompt/use-revalidate-environment.ts new file mode 100644 index 0000000000..12bd1ab84b --- /dev/null +++ b/packages/clerk-js/src/ui/components/KeylessPrompt/use-revalidate-environment.ts @@ -0,0 +1,62 @@ +import { useClerk } from '@clerk/shared/react'; +import { useEffect, useReducer } from 'react'; + +import type { Clerk } from '../../../core/clerk'; +import type { Environment } from '../../../core/resources'; +import { useEnvironment } from '../../contexts'; + +/** + * Revalidates environment on focus, highly optimized for Keyless mode. + * Attention: this is not a generic solution, and should not be used for revalidating environment inside UI components that are end-user facing (e.g. SignIn) + */ +function useRevalidateEnvironment() { + const clerk = useClerk(); + const [, forceUpdate] = useReducer(v => v + 1, 0); + + useEffect(() => { + const controller = new AbortController(); + window.addEventListener( + 'focus', + + async () => { + const environment = (clerk as Clerk).__unstable__environment as Environment | undefined; + + if (!environment) { + return; + } + + if (environment.authConfig.claimedAt !== null) { + return controller.abort(); + } + + if (document.visibilityState !== 'visible') { + return; + } + + const maxRetries = 2; + + for (let i = 0; i < maxRetries; i++) { + const { + authConfig: { claimedAt }, + } = await environment.fetch(); + + if (claimedAt !== null) { + forceUpdate(); + break; + } + } + }, + { + signal: controller.signal, + }, + ); + + return () => { + controller.abort(); + }; + }, []); + + return useEnvironment(); +} + +export { useRevalidateEnvironment };