refactor(captcha): simplify interval handling in hook (#10974)
Close #10708 --------- Co-authored-by: Félix Malfait <felix@twenty.com>
This commit is contained in:
@ -1,16 +1,19 @@
|
|||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { useRequestFreshCaptchaToken } from '@/captcha/hooks/useRequestFreshCaptchaToken';
|
||||||
import { isCaptchaScriptLoadedState } from '@/captcha/states/isCaptchaScriptLoadedState';
|
import { isCaptchaScriptLoadedState } from '@/captcha/states/isCaptchaScriptLoadedState';
|
||||||
import { getCaptchaUrlByProvider } from '@/captcha/utils/getCaptchaUrlByProvider';
|
import { getCaptchaUrlByProvider } from '@/captcha/utils/getCaptchaUrlByProvider';
|
||||||
import { captchaState } from '@/client-config/states/captchaState';
|
import { captchaState } from '@/client-config/states/captchaState';
|
||||||
import { CaptchaDriverType } from '~/generated/graphql';
|
import { CaptchaDriverType } from '~/generated/graphql';
|
||||||
|
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||||
|
|
||||||
export const CaptchaProviderScriptLoaderEffect = () => {
|
export const CaptchaProviderScriptLoaderEffect = () => {
|
||||||
const captcha = useRecoilValue(captchaState);
|
const captcha = useRecoilValue(captchaState);
|
||||||
const setIsCaptchaScriptLoaded = useSetRecoilState(
|
const setIsCaptchaScriptLoaded = useSetRecoilState(
|
||||||
isCaptchaScriptLoadedState,
|
isCaptchaScriptLoadedState,
|
||||||
);
|
);
|
||||||
|
const { requestFreshCaptchaToken } = useRequestFreshCaptchaToken();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!captcha?.provider || !captcha.siteKey) {
|
if (!captcha?.provider || !captcha.siteKey) {
|
||||||
@ -43,6 +46,29 @@ export const CaptchaProviderScriptLoaderEffect = () => {
|
|||||||
document.body.appendChild(scriptElement);
|
document.body.appendChild(scriptElement);
|
||||||
}
|
}
|
||||||
}, [captcha?.provider, captcha?.siteKey, setIsCaptchaScriptLoaded]);
|
}, [captcha?.provider, captcha?.siteKey, setIsCaptchaScriptLoaded]);
|
||||||
|
useEffect(() => {
|
||||||
|
if (isUndefinedOrNull(captcha?.provider)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let refreshInterval: NodeJS.Timeout;
|
||||||
|
|
||||||
|
switch (captcha.provider) {
|
||||||
|
case CaptchaDriverType.GoogleRecaptcha:
|
||||||
|
// Google reCAPTCHA tokens expire after 120 seconds, refresh at 110 seconds
|
||||||
|
refreshInterval = setInterval(requestFreshCaptchaToken, 110 * 1000);
|
||||||
|
break;
|
||||||
|
case CaptchaDriverType.Turnstile:
|
||||||
|
// Cloudflare Turnstile tokens expire after 500 seconds, refresh at 480 seconds
|
||||||
|
refreshInterval = setInterval(requestFreshCaptchaToken, 480 * 1000);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Note: hCaptcha has a callback system for expiration that we're not implementing now
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => clearInterval(refreshInterval);
|
||||||
|
}, [captcha?.provider, requestFreshCaptchaToken]);
|
||||||
|
|
||||||
return <></>;
|
return <></>;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,14 +1,13 @@
|
|||||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||||
import { MULTI_WORKSPACE_DROPDOWN_ID } from '@/ui/navigation/navigation-drawer/constants/MultiWorkspaceDropdownId';
|
|
||||||
import { NavigationDrawerHotKeyScope } from '@/ui/navigation/navigation-drawer/types/NavigationDrawerHotKeyScope';
|
|
||||||
import { multiWorkspaceDropdownState } from '@/ui/navigation/navigation-drawer/states/multiWorkspaceDropdownState';
|
|
||||||
import { useRecoilState } from 'recoil';
|
|
||||||
import { useMemo } from 'react';
|
|
||||||
import { MultiWorkspaceDropdownClickableComponent } from '@/ui/navigation/navigation-drawer/components/MultiWorkspaceDropdown/internal/MultiWorkspaceDropdownClickableComponent';
|
import { MultiWorkspaceDropdownClickableComponent } from '@/ui/navigation/navigation-drawer/components/MultiWorkspaceDropdown/internal/MultiWorkspaceDropdownClickableComponent';
|
||||||
import { MultiWorkspaceDropdownDefaultComponents } from '@/ui/navigation/navigation-drawer/components/MultiWorkspaceDropdown/internal/MultiWorkspaceDropdownDefaultComponents';
|
import { MultiWorkspaceDropdownDefaultComponents } from '@/ui/navigation/navigation-drawer/components/MultiWorkspaceDropdown/internal/MultiWorkspaceDropdownDefaultComponents';
|
||||||
import { MultiWorkspaceDropdownThemesComponents } from '@/ui/navigation/navigation-drawer/components/MultiWorkspaceDropdown/internal/MultiWorkspaceDropdownThemesComponents';
|
import { MultiWorkspaceDropdownThemesComponents } from '@/ui/navigation/navigation-drawer/components/MultiWorkspaceDropdown/internal/MultiWorkspaceDropdownThemesComponents';
|
||||||
import { MultiWorkspaceDropdownWorkspacesListComponents } from '@/ui/navigation/navigation-drawer/components/MultiWorkspaceDropdown/internal/MultiWorkspaceDropdownWorkspacesListComponents';
|
import { MultiWorkspaceDropdownWorkspacesListComponents } from '@/ui/navigation/navigation-drawer/components/MultiWorkspaceDropdown/internal/MultiWorkspaceDropdownWorkspacesListComponents';
|
||||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
import { MULTI_WORKSPACE_DROPDOWN_ID } from '@/ui/navigation/navigation-drawer/constants/MultiWorkspaceDropdownId';
|
||||||
|
import { multiWorkspaceDropdownState } from '@/ui/navigation/navigation-drawer/states/multiWorkspaceDropdownState';
|
||||||
|
import { NavigationDrawerHotKeyScope } from '@/ui/navigation/navigation-drawer/types/NavigationDrawerHotKeyScope';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
import { useRecoilState } from 'recoil';
|
||||||
|
|
||||||
export const MultiWorkspaceDropdownButton = () => {
|
export const MultiWorkspaceDropdownButton = () => {
|
||||||
const [multiWorkspaceDropdown, setMultiWorkspaceDropdown] = useRecoilState(
|
const [multiWorkspaceDropdown, setMultiWorkspaceDropdown] = useRecoilState(
|
||||||
@ -26,8 +25,6 @@ export const MultiWorkspaceDropdownButton = () => {
|
|||||||
}
|
}
|
||||||
}, [multiWorkspaceDropdown]);
|
}, [multiWorkspaceDropdown]);
|
||||||
|
|
||||||
const { isDropdownOpen } = useDropdown(MULTI_WORKSPACE_DROPDOWN_ID);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
dropdownId={MULTI_WORKSPACE_DROPDOWN_ID}
|
dropdownId={MULTI_WORKSPACE_DROPDOWN_ID}
|
||||||
@ -35,11 +32,7 @@ export const MultiWorkspaceDropdownButton = () => {
|
|||||||
scope: NavigationDrawerHotKeyScope.MultiWorkspaceDropdownButton,
|
scope: NavigationDrawerHotKeyScope.MultiWorkspaceDropdownButton,
|
||||||
}}
|
}}
|
||||||
dropdownOffset={{ y: -35, x: -5 }}
|
dropdownOffset={{ y: -35, x: -5 }}
|
||||||
clickableComponent={
|
clickableComponent={<MultiWorkspaceDropdownClickableComponent />}
|
||||||
<MultiWorkspaceDropdownClickableComponent
|
|
||||||
isDropdownOpen={isDropdownOpen}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
dropdownComponents={<DropdownComponents />}
|
dropdownComponents={<DropdownComponents />}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
setMultiWorkspaceDropdown('default');
|
setMultiWorkspaceDropdown('default');
|
||||||
|
|||||||
@ -11,11 +11,7 @@ import { useRecoilValue } from 'recoil';
|
|||||||
import { useTheme } from '@emotion/react';
|
import { useTheme } from '@emotion/react';
|
||||||
import { isNavigationDrawerExpandedState } from '@/ui/navigation/states/isNavigationDrawerExpanded';
|
import { isNavigationDrawerExpandedState } from '@/ui/navigation/states/isNavigationDrawerExpanded';
|
||||||
|
|
||||||
export const MultiWorkspaceDropdownClickableComponent = ({
|
export const MultiWorkspaceDropdownClickableComponent = () => {
|
||||||
isDropdownOpen,
|
|
||||||
}: {
|
|
||||||
isDropdownOpen?: boolean;
|
|
||||||
}) => {
|
|
||||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
@ -36,7 +32,6 @@ export const MultiWorkspaceDropdownClickableComponent = ({
|
|||||||
</NavigationDrawerAnimatedCollapseWrapper>
|
</NavigationDrawerAnimatedCollapseWrapper>
|
||||||
<NavigationDrawerAnimatedCollapseWrapper>
|
<NavigationDrawerAnimatedCollapseWrapper>
|
||||||
<StyledIconChevronDown
|
<StyledIconChevronDown
|
||||||
isDropdownOpen={isDropdownOpen}
|
|
||||||
size={theme.icon.size.md}
|
size={theme.icon.size.md}
|
||||||
stroke={theme.icon.stroke.sm}
|
stroke={theme.icon.stroke.sm}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -32,12 +32,9 @@ export const StyledLabel = styled.div`
|
|||||||
|
|
||||||
export const StyledIconChevronDown = styled(IconChevronDown)<{
|
export const StyledIconChevronDown = styled(IconChevronDown)<{
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
isDropdownOpen?: boolean;
|
|
||||||
}>`
|
}>`
|
||||||
align-items: center;
|
align-items: center;
|
||||||
color: ${({ disabled, theme }) =>
|
color: ${({ disabled, theme }) =>
|
||||||
disabled ? theme.font.color.extraLight : theme.font.color.tertiary};
|
disabled ? theme.font.color.extraLight : theme.font.color.tertiary};
|
||||||
display: flex;
|
display: flex;
|
||||||
rotate: ${({ isDropdownOpen }) => (isDropdownOpen ? '-180deg' : '0deg')};
|
|
||||||
transition: rotate 0.1s ease-in-out;
|
|
||||||
`;
|
`;
|
||||||
|
|||||||
Reference in New Issue
Block a user