-
Notifications
You must be signed in to change notification settings - Fork 330
/
Copy pathAlternativeMethods.tsx
127 lines (117 loc) · 4.96 KB
/
AlternativeMethods.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import type { SessionVerificationFirstFactor, SignInFactor } from '@clerk/types';
import React from 'react';
import type { LocalizationKey } from '../../customizables';
import { Col, descriptors, Flex, Flow, localizationKeys } from '../../customizables';
import { ArrowBlockButton, BackLink, Card, Header } from '../../elements';
import { useCardState } from '../../elements/contexts';
import { useAlternativeStrategies } from '../../hooks/useAlternativeStrategies';
import { ChatAltIcon, Email, LockClosedIcon } from '../../icons';
import { formatSafeIdentifier } from '../../utils';
import { useUserVerificationSession } from './useUserVerificationSession';
import { withHavingTrouble } from './withHavingTrouble';
export type AlternativeMethodsProps = {
onBackLinkClick: React.MouseEventHandler | undefined;
onFactorSelected: (factor: SignInFactor) => void;
currentFactor: SignInFactor | undefined | null;
};
export type AlternativeMethodListProps = AlternativeMethodsProps & { onHavingTroubleClick: React.MouseEventHandler };
export const AlternativeMethods = (props: AlternativeMethodsProps) => {
return withHavingTrouble(AlternativeMethodsList, {
...props,
});
};
const AlternativeMethodsList = (props: AlternativeMethodListProps) => {
const { onBackLinkClick, onHavingTroubleClick, onFactorSelected } = props;
const card = useCardState();
const { data } = useUserVerificationSession();
const { firstPartyFactors, hasAnyStrategy } = useAlternativeStrategies<SessionVerificationFirstFactor>({
filterOutFactor: props?.currentFactor,
supportedFirstFactors: data?.supportedFirstFactors,
});
return (
<Flow.Part part={'alternativeMethods'}>
<Card.Root>
<Card.Content>
<Header.Root showLogo>
<Header.Title localizationKey={localizationKeys('reverification.alternativeMethods.title')} />
<Header.Subtitle localizationKey={localizationKeys('reverification.alternativeMethods.subtitle')} />
</Header.Root>
<Card.Alert>{card.error}</Card.Alert>
{/*TODO: extract main in its own component */}
<Flex
direction='col'
elementDescriptor={descriptors.main}
gap={6}
>
<Col gap={4}>
{hasAnyStrategy && (
<Flex
elementDescriptor={descriptors.alternativeMethods}
direction='col'
gap={2}
>
{firstPartyFactors.map((factor, i) => (
<ArrowBlockButton
leftIcon={getButtonIcon(factor)}
textLocalizationKey={getButtonLabel(factor)}
elementDescriptor={descriptors.alternativeMethodsBlockButton}
textElementDescriptor={descriptors.alternativeMethodsBlockButtonText}
arrowElementDescriptor={descriptors.alternativeMethodsBlockButtonArrow}
key={i}
textVariant='buttonLarge'
isDisabled={card.isLoading}
onClick={() => {
card.setError(undefined);
onFactorSelected(factor);
}}
/>
))}
</Flex>
)}
{onBackLinkClick && (
<BackLink
boxElementDescriptor={descriptors.backRow}
linkElementDescriptor={descriptors.backLink}
onClick={onBackLinkClick}
/>
)}
</Col>
</Flex>
</Card.Content>
<Card.Footer>
<Card.Action elementId='havingTrouble'>
<Card.ActionText localizationKey={localizationKeys('reverification.alternativeMethods.actionText')} />
<Card.ActionLink
localizationKey={localizationKeys('reverification.alternativeMethods.actionLink')}
onClick={onHavingTroubleClick}
/>
</Card.Action>
</Card.Footer>
</Card.Root>
</Flow.Part>
);
};
export function getButtonLabel(factor: SessionVerificationFirstFactor): LocalizationKey {
switch (factor.strategy) {
case 'email_code':
return localizationKeys('reverification.alternativeMethods.blockButton__emailCode', {
identifier: formatSafeIdentifier(factor.safeIdentifier) || '',
});
case 'phone_code':
return localizationKeys('reverification.alternativeMethods.blockButton__phoneCode', {
identifier: formatSafeIdentifier(factor.safeIdentifier) || '',
});
case 'password':
return localizationKeys('reverification.alternativeMethods.blockButton__password');
default:
throw `Invalid sign in strategy: "${(factor as any).strategy}"`;
}
}
export function getButtonIcon(factor: SessionVerificationFirstFactor) {
const icons = {
email_code: Email,
phone_code: ChatAltIcon,
password: LockClosedIcon,
} as const;
return icons[factor.strategy as keyof typeof icons];
}