-
Notifications
You must be signed in to change notification settings - Fork 331
/
Copy pathUVFactorTwoAlternativeMethods.tsx
104 lines (95 loc) · 4.19 KB
/
UVFactorTwoAlternativeMethods.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
import type { SessionVerificationSecondFactor } from '@clerk/types';
import React from 'react';
import type { LocalizationKey } from '../../customizables';
import { Col, descriptors, Flow, localizationKeys } from '../../customizables';
import { ArrowBlockButton, Card, Header } from '../../elements';
import { useCardState } from '../../elements/contexts';
import { backupCodePrefFactorComparator, formatSafeIdentifier } from '../../utils';
import { HavingTrouble } from './HavingTrouble';
export type AlternativeMethodsProps = {
onBackLinkClick: React.MouseEventHandler | undefined;
onFactorSelected: (factor: SessionVerificationSecondFactor) => void;
supportedSecondFactors: SessionVerificationSecondFactor[] | null;
};
export const UVFactorTwoAlternativeMethods = (props: AlternativeMethodsProps) => {
const [showHavingTrouble, setShowHavingTrouble] = React.useState(false);
const toggleHavingTrouble = React.useCallback(() => setShowHavingTrouble(s => !s), [setShowHavingTrouble]);
if (showHavingTrouble) {
return <HavingTrouble onBackLinkClick={toggleHavingTrouble} />;
}
return (
<AlternativeMethodsList
supportedSecondFactors={props.supportedSecondFactors}
onBackLinkClick={props.onBackLinkClick}
onFactorSelected={props.onFactorSelected}
onHavingTroubleClick={toggleHavingTrouble}
/>
);
};
const AlternativeMethodsList = (props: AlternativeMethodsProps & { onHavingTroubleClick: React.MouseEventHandler }) => {
const { supportedSecondFactors, onHavingTroubleClick, onFactorSelected, onBackLinkClick } = props;
const card = useCardState();
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 */}
<Col
elementDescriptor={descriptors.main}
gap={3}
>
<Col gap={2}>
{supportedSecondFactors?.sort(backupCodePrefFactorComparator).map((factor, i) => (
<ArrowBlockButton
textLocalizationKey={getButtonLabel(factor)}
elementDescriptor={descriptors.alternativeMethodsBlockButton}
textElementDescriptor={descriptors.alternativeMethodsBlockButtonText}
arrowElementDescriptor={descriptors.alternativeMethodsBlockButtonArrow}
key={i}
isDisabled={card.isLoading}
onClick={() => onFactorSelected(factor)}
/>
))}
</Col>
<Card.Action elementId='alternativeMethods'>
{onBackLinkClick && (
<Card.ActionLink
localizationKey={localizationKeys('backButton')}
onClick={props.onBackLinkClick}
/>
)}
</Card.Action>
</Col>
</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: SessionVerificationSecondFactor): LocalizationKey {
switch (factor.strategy) {
case 'phone_code':
return localizationKeys('reverification.alternativeMethods.blockButton__phoneCode', {
identifier: formatSafeIdentifier(factor.safeIdentifier) || '',
});
case 'totp':
return localizationKeys('reverification.alternativeMethods.blockButton__totp');
case 'backup_code':
return localizationKeys('reverification.alternativeMethods.blockButton__backupCode');
default:
throw `Invalid verification strategy: "${(factor as any).strategy}"`;
}
}