@oxy.so/services 8.3.0 → 9.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/ui/components/BenefitList.js +1 -2
- package/lib/commonjs/ui/components/BenefitList.js.map +1 -1
- package/lib/commonjs/ui/components/BloomLocaleBridge.js +37 -0
- package/lib/commonjs/ui/components/BloomLocaleBridge.js.map +1 -0
- package/lib/commonjs/ui/components/OxyProvider.js +11 -6
- package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
- package/lib/commonjs/ui/components/OxySignInRequestSurface.js +0 -1
- package/lib/commonjs/ui/components/OxySignInRequestSurface.js.map +1 -1
- package/lib/commonjs/ui/components/SurfaceScreen.js +4 -10
- package/lib/commonjs/ui/components/SurfaceScreen.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/TroubleDisclosure.js +0 -1
- package/lib/commonjs/ui/components/authChooser/TroubleDisclosure.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/primitives.js +9 -11
- package/lib/commonjs/ui/components/authChooser/primitives.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/requestSurfaces.js +13 -10
- package/lib/commonjs/ui/components/authChooser/requestSurfaces.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/styles.js +4 -1
- package/lib/commonjs/ui/components/authChooser/styles.js.map +1 -1
- package/lib/commonjs/ui/components/payment/PaymentDetailsStep.js +2 -2
- package/lib/commonjs/ui/components/payment/PaymentDetailsStep.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/InlineCommonsQr.js +5 -2
- package/lib/commonjs/ui/components/signIn/InlineCommonsQr.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/OxyAccountPicker.js +13 -17
- package/lib/commonjs/ui/components/signIn/OxyAccountPicker.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/OxyAuthScreen.js +9 -8
- package/lib/commonjs/ui/components/signIn/OxyAuthScreen.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/OxyAuthenticatorPanel.js +38 -42
- package/lib/commonjs/ui/components/signIn/OxyAuthenticatorPanel.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/OxyDeleteAccountPanel.js +21 -6
- package/lib/commonjs/ui/components/signIn/OxyDeleteAccountPanel.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/OxyLinkCommonsPanel.js +4 -2
- package/lib/commonjs/ui/components/signIn/OxyLinkCommonsPanel.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/OxyPasswordPanel.js +5 -16
- package/lib/commonjs/ui/components/signIn/OxyPasswordPanel.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/OxySignInPanel.js +19 -20
- package/lib/commonjs/ui/components/signIn/OxySignInPanel.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/OxySignUpPanel.js +12 -8
- package/lib/commonjs/ui/components/signIn/OxySignUpPanel.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/ReauthStep.js +2 -7
- package/lib/commonjs/ui/components/signIn/ReauthStep.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/accountFlowParts.js +64 -13
- package/lib/commonjs/ui/components/signIn/accountFlowParts.js.map +1 -1
- package/lib/commonjs/ui/screens/AppInfoScreen.js +2 -1
- package/lib/commonjs/ui/screens/AppInfoScreen.js.map +1 -1
- package/lib/commonjs/ui/utils/clipboard.js +21 -0
- package/lib/commonjs/ui/utils/clipboard.js.map +1 -0
- package/lib/commonjs/ui/utils/clipboard.native.js +28 -0
- package/lib/commonjs/ui/utils/clipboard.native.js.map +1 -0
- package/lib/module/ui/components/BenefitList.js +1 -2
- package/lib/module/ui/components/BenefitList.js.map +1 -1
- package/lib/module/ui/components/BloomLocaleBridge.js +33 -0
- package/lib/module/ui/components/BloomLocaleBridge.js.map +1 -0
- package/lib/module/ui/components/OxyProvider.js +11 -6
- package/lib/module/ui/components/OxyProvider.js.map +1 -1
- package/lib/module/ui/components/OxySignInRequestSurface.js +0 -1
- package/lib/module/ui/components/OxySignInRequestSurface.js.map +1 -1
- package/lib/module/ui/components/SurfaceScreen.js +5 -11
- package/lib/module/ui/components/SurfaceScreen.js.map +1 -1
- package/lib/module/ui/components/authChooser/TroubleDisclosure.js +0 -1
- package/lib/module/ui/components/authChooser/TroubleDisclosure.js.map +1 -1
- package/lib/module/ui/components/authChooser/primitives.js +11 -12
- package/lib/module/ui/components/authChooser/primitives.js.map +1 -1
- package/lib/module/ui/components/authChooser/requestSurfaces.js +15 -10
- package/lib/module/ui/components/authChooser/requestSurfaces.js.map +1 -1
- package/lib/module/ui/components/authChooser/styles.js +4 -1
- package/lib/module/ui/components/authChooser/styles.js.map +1 -1
- package/lib/module/ui/components/payment/PaymentDetailsStep.js +3 -3
- package/lib/module/ui/components/payment/PaymentDetailsStep.js.map +1 -1
- package/lib/module/ui/components/signIn/InlineCommonsQr.js +6 -3
- package/lib/module/ui/components/signIn/InlineCommonsQr.js.map +1 -1
- package/lib/module/ui/components/signIn/OxyAccountPicker.js +13 -17
- package/lib/module/ui/components/signIn/OxyAccountPicker.js.map +1 -1
- package/lib/module/ui/components/signIn/OxyAuthScreen.js +10 -9
- package/lib/module/ui/components/signIn/OxyAuthScreen.js.map +1 -1
- package/lib/module/ui/components/signIn/OxyAuthenticatorPanel.js +40 -44
- package/lib/module/ui/components/signIn/OxyAuthenticatorPanel.js.map +1 -1
- package/lib/module/ui/components/signIn/OxyDeleteAccountPanel.js +22 -7
- package/lib/module/ui/components/signIn/OxyDeleteAccountPanel.js.map +1 -1
- package/lib/module/ui/components/signIn/OxyLinkCommonsPanel.js +5 -3
- package/lib/module/ui/components/signIn/OxyLinkCommonsPanel.js.map +1 -1
- package/lib/module/ui/components/signIn/OxyPasswordPanel.js +6 -17
- package/lib/module/ui/components/signIn/OxyPasswordPanel.js.map +1 -1
- package/lib/module/ui/components/signIn/OxySignInPanel.js +20 -21
- package/lib/module/ui/components/signIn/OxySignInPanel.js.map +1 -1
- package/lib/module/ui/components/signIn/OxySignUpPanel.js +13 -9
- package/lib/module/ui/components/signIn/OxySignUpPanel.js.map +1 -1
- package/lib/module/ui/components/signIn/ReauthStep.js +3 -8
- package/lib/module/ui/components/signIn/ReauthStep.js.map +1 -1
- package/lib/module/ui/components/signIn/accountFlowParts.js +60 -11
- package/lib/module/ui/components/signIn/accountFlowParts.js.map +1 -1
- package/lib/module/ui/screens/AppInfoScreen.js +3 -2
- package/lib/module/ui/screens/AppInfoScreen.js.map +1 -1
- package/lib/module/ui/utils/clipboard.js +17 -0
- package/lib/module/ui/utils/clipboard.js.map +1 -0
- package/lib/module/ui/utils/clipboard.native.js +23 -0
- package/lib/module/ui/utils/clipboard.native.js.map +1 -0
- package/lib/typescript/commonjs/ui/components/BloomLocaleBridge.d.ts +19 -0
- package/lib/typescript/commonjs/ui/components/BloomLocaleBridge.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/OxyProvider.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/OxySignInRequestSurface.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/SurfaceScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/TroubleDisclosure.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/primitives.d.ts +4 -2
- package/lib/typescript/commonjs/ui/components/authChooser/primitives.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/requestSurfaces.d.ts +4 -4
- package/lib/typescript/commonjs/ui/components/authChooser/requestSurfaces.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/styles.d.ts +2 -1
- package/lib/typescript/commonjs/ui/components/authChooser/styles.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/payment/PaymentDetailsStep.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/signIn/InlineCommonsQr.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/signIn/OxyAccountPicker.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/signIn/OxyAuthScreen.d.ts +1 -5
- package/lib/typescript/commonjs/ui/components/signIn/OxyAuthScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/signIn/OxyAuthenticatorPanel.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/signIn/OxyDeleteAccountPanel.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/signIn/OxyLinkCommonsPanel.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/signIn/OxyPasswordPanel.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/signIn/OxySignInPanel.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/signIn/OxySignUpPanel.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/signIn/ReauthStep.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/signIn/accountFlowParts.d.ts +29 -2
- package/lib/typescript/commonjs/ui/components/signIn/accountFlowParts.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/icons/MaterialCommunityIcons.d.ts +1 -1
- package/lib/typescript/commonjs/ui/screens/AppInfoScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/utils/clipboard.d.ts +11 -0
- package/lib/typescript/commonjs/ui/utils/clipboard.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/utils/clipboard.native.d.ts +18 -0
- package/lib/typescript/commonjs/ui/utils/clipboard.native.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/BloomLocaleBridge.d.ts +19 -0
- package/lib/typescript/module/ui/components/BloomLocaleBridge.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/OxyProvider.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/OxySignInRequestSurface.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/SurfaceScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/TroubleDisclosure.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/primitives.d.ts +4 -2
- package/lib/typescript/module/ui/components/authChooser/primitives.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/requestSurfaces.d.ts +4 -4
- package/lib/typescript/module/ui/components/authChooser/requestSurfaces.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/styles.d.ts +2 -1
- package/lib/typescript/module/ui/components/authChooser/styles.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/payment/PaymentDetailsStep.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/signIn/InlineCommonsQr.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/signIn/OxyAccountPicker.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/signIn/OxyAuthScreen.d.ts +1 -5
- package/lib/typescript/module/ui/components/signIn/OxyAuthScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/signIn/OxyAuthenticatorPanel.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/signIn/OxyDeleteAccountPanel.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/signIn/OxyLinkCommonsPanel.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/signIn/OxyPasswordPanel.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/signIn/OxySignInPanel.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/signIn/OxySignUpPanel.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/signIn/ReauthStep.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/signIn/accountFlowParts.d.ts +29 -2
- package/lib/typescript/module/ui/components/signIn/accountFlowParts.d.ts.map +1 -1
- package/lib/typescript/module/ui/icons/MaterialCommunityIcons.d.ts +1 -1
- package/lib/typescript/module/ui/screens/AppInfoScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/utils/clipboard.d.ts +11 -0
- package/lib/typescript/module/ui/utils/clipboard.d.ts.map +1 -0
- package/lib/typescript/module/ui/utils/clipboard.native.d.ts +18 -0
- package/lib/typescript/module/ui/utils/clipboard.native.d.ts.map +1 -0
- package/package.json +11 -6
- package/src/ui/components/BenefitList.tsx +1 -1
- package/src/ui/components/BloomLocaleBridge.tsx +23 -0
- package/src/ui/components/OxyProvider.tsx +13 -8
- package/src/ui/components/OxySignInRequestSurface.tsx +0 -1
- package/src/ui/components/SurfaceScreen.tsx +3 -9
- package/src/ui/components/__tests__/BloomLocaleBridge.test.tsx +68 -0
- package/src/ui/components/authChooser/TroubleDisclosure.tsx +0 -1
- package/src/ui/components/authChooser/primitives.tsx +11 -9
- package/src/ui/components/authChooser/requestSurfaces.tsx +9 -8
- package/src/ui/components/authChooser/styles.ts +4 -1
- package/src/ui/components/payment/PaymentDetailsStep.tsx +5 -3
- package/src/ui/components/signIn/InlineCommonsQr.tsx +5 -2
- package/src/ui/components/signIn/OxyAccountPicker.tsx +14 -13
- package/src/ui/components/signIn/OxyAuthScreen.tsx +5 -8
- package/src/ui/components/signIn/OxyAuthenticatorPanel.tsx +33 -45
- package/src/ui/components/signIn/OxyDeleteAccountPanel.tsx +17 -3
- package/src/ui/components/signIn/OxyLinkCommonsPanel.tsx +3 -2
- package/src/ui/components/signIn/OxyPasswordPanel.tsx +6 -12
- package/src/ui/components/signIn/OxySignInPanel.tsx +18 -15
- package/src/ui/components/signIn/OxySignUpPanel.tsx +13 -5
- package/src/ui/components/signIn/ReauthStep.tsx +4 -7
- package/src/ui/components/signIn/accountFlowParts.tsx +61 -7
- package/src/ui/screens/AppInfoScreen.tsx +2 -2
- package/src/ui/utils/clipboard.native.ts +20 -0
- package/src/ui/utils/clipboard.ts +14 -0
|
@@ -98,9 +98,12 @@ export const authChooserStyles = StyleSheet.create({
|
|
|
98
98
|
alignItems: 'center',
|
|
99
99
|
justifyContent: 'center',
|
|
100
100
|
},
|
|
101
|
+
// Longhands: Bloom's web `Button` (`SubtleLink`) is a raw DOM <button>,
|
|
102
|
+
// where `paddingVertical` is no CSS property and is silently dropped.
|
|
101
103
|
footerLink: {
|
|
102
104
|
alignSelf: 'center',
|
|
103
|
-
|
|
105
|
+
paddingTop: 10,
|
|
106
|
+
paddingBottom: 10,
|
|
104
107
|
marginTop: 12,
|
|
105
108
|
},
|
|
106
109
|
linkText: {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type React from 'react';
|
|
2
2
|
import { useMemo } from 'react';
|
|
3
|
-
import { View, Animated, TouchableOpacity,
|
|
3
|
+
import { View, Animated, TouchableOpacity, Linking } from 'react-native';
|
|
4
4
|
import { Text } from '@oxy.so/bloom/typography';
|
|
5
5
|
import Ionicons from '../../icons/Ionicons';
|
|
6
6
|
import { Button } from '@oxy.so/bloom/button';
|
|
@@ -8,6 +8,7 @@ import { TextField, TextFieldInput } from '@oxy.so/bloom/text-field';
|
|
|
8
8
|
import FAIRWalletIcon from '../icon/FAIRWalletIcon';
|
|
9
9
|
import { createPaymentStyles } from './paymentStyles';
|
|
10
10
|
import { toast } from '@oxy.so/bloom/toast';
|
|
11
|
+
import { copyText } from '../../utils/clipboard';
|
|
11
12
|
import type { CardDetails, PaymentColors, PaymentStepAnimations } from './types';
|
|
12
13
|
import { useI18n } from '../../hooks/useI18n';
|
|
13
14
|
|
|
@@ -43,8 +44,9 @@ const PaymentDetailsStep: React.FC<PaymentDetailsStepProps> = ({
|
|
|
43
44
|
const { fadeAnim, slideAnim, scaleAnim } = animations;
|
|
44
45
|
|
|
45
46
|
const handleCopyAddress = () => {
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
copyText(faircoinAddress)
|
|
48
|
+
.then(() => toast(t('payment.details.addressCopied')))
|
|
49
|
+
.catch(() => toast.error(t('appInfo.toasts.copyFailed')));
|
|
48
50
|
};
|
|
49
51
|
|
|
50
52
|
const handleOpenFairWallet = () => {
|
|
@@ -15,11 +15,12 @@
|
|
|
15
15
|
|
|
16
16
|
import type React from 'react';
|
|
17
17
|
import { useEffect, useState } from 'react';
|
|
18
|
-
import {
|
|
18
|
+
import { StyleSheet, View } from 'react-native';
|
|
19
19
|
import QRCode from 'react-native-qrcode-svg';
|
|
20
20
|
import { Button } from '@oxy.so/bloom/button';
|
|
21
21
|
import { RiCheckboxCircleLine } from '@oxy.so/bloom/icons/RiCheckboxCircleLine';
|
|
22
22
|
import { RiRefreshLine } from '@oxy.so/bloom/icons/RiRefreshLine';
|
|
23
|
+
import { SpinnerIcon } from '@oxy.so/bloom/loading';
|
|
23
24
|
import { useTheme } from '@oxy.so/bloom/theme';
|
|
24
25
|
import { Text } from '@oxy.so/bloom/typography';
|
|
25
26
|
import type { AccountDialogController } from '@oxy.so/core/session';
|
|
@@ -32,6 +33,8 @@ const QR_FOREGROUND = '#000000';
|
|
|
32
33
|
const QR_SIZE = 112;
|
|
33
34
|
const PLATE_PADDING = 8;
|
|
34
35
|
const PLATE_SIZE = QR_SIZE + PLATE_PADDING * 2;
|
|
36
|
+
/** The spinner while a code is on its way: React Native's `large` indicator size. */
|
|
37
|
+
const SPINNER_SIZE = 36;
|
|
35
38
|
|
|
36
39
|
export const InlineCommonsQr: React.FC<{ controller: AccountDialogController | null }> = ({ controller }) => {
|
|
37
40
|
const theme = useTheme();
|
|
@@ -70,7 +73,7 @@ export const InlineCommonsQr: React.FC<{ controller: AccountDialogController | n
|
|
|
70
73
|
} else if (live && signIn.qrPayload && signIn.progress !== 'confirming-identity') {
|
|
71
74
|
plate = <QRCode value={signIn.qrPayload} size={QR_SIZE} backgroundColor={QR_PLATE_BG} color={QR_FOREGROUND} />;
|
|
72
75
|
} else if (live || expired || (signIn.inline && signIn.phase === 'completed')) {
|
|
73
|
-
plate = <
|
|
76
|
+
plate = <SpinnerIcon size={SPINNER_SIZE} color={QR_FOREGROUND} />;
|
|
74
77
|
} else {
|
|
75
78
|
// Failed for a reason renewing will not fix by itself, or superseded and
|
|
76
79
|
// then withdrawn: one press for a new code.
|
|
@@ -23,8 +23,8 @@ import { Pressable, View } from 'react-native-css/components';
|
|
|
23
23
|
import { Avatar } from '@oxy.so/bloom/avatar';
|
|
24
24
|
import { RiArrowRightSLine } from '@oxy.so/bloom/icons/RiArrowRightSLine';
|
|
25
25
|
import { RiCheckLine } from '@oxy.so/bloom/icons/RiCheckLine';
|
|
26
|
-
import { RiLoader4Line } from '@oxy.so/bloom/icons/RiLoader4Line';
|
|
27
26
|
import { RiUserAddLine } from '@oxy.so/bloom/icons/RiUserAddLine';
|
|
27
|
+
import { SpinnerIcon } from '@oxy.so/bloom/loading';
|
|
28
28
|
import { useTheme } from '@oxy.so/bloom/theme';
|
|
29
29
|
import { Text } from '@oxy.so/bloom/typography';
|
|
30
30
|
import { showsPrincipalHeaders, type SwitcherContextRow, type SwitcherPrincipalRow } from '@oxy.so/core/session';
|
|
@@ -134,24 +134,25 @@ export const OxyAccountPicker: React.FC<OxyAccountPickerProps> = ({
|
|
|
134
134
|
) : null}
|
|
135
135
|
</View>
|
|
136
136
|
<View className="flex-row items-center gap-[8px] shrink-0">
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
{current
|
|
137
|
+
{/* The current account: Avatar's own verified slot (bottom-right,
|
|
138
|
+
above the image), holding the accent check badge. */}
|
|
139
|
+
<Avatar
|
|
140
|
+
source={context.avatarUrl ?? undefined}
|
|
141
|
+
variant="thumb"
|
|
142
|
+
name={context.displayName}
|
|
143
|
+
size={AVATAR_SIZE}
|
|
144
|
+
verified={current}
|
|
145
|
+
verifiedIcon={
|
|
145
146
|
<View
|
|
146
|
-
className="
|
|
147
|
+
className="items-center justify-center rounded-full border-2 border-background"
|
|
147
148
|
style={{ width: 18, height: 18, backgroundColor: accent }}
|
|
148
149
|
>
|
|
149
150
|
<RiCheckLine size="xs" fill="#ffffff" />
|
|
150
151
|
</View>
|
|
151
|
-
|
|
152
|
-
|
|
152
|
+
}
|
|
153
|
+
/>
|
|
153
154
|
{activating ? (
|
|
154
|
-
<
|
|
155
|
+
<SpinnerIcon size={20} color={accent} />
|
|
155
156
|
) : (
|
|
156
157
|
<RiArrowRightSLine size="sm" fill={theme.colors.textSecondary} />
|
|
157
158
|
)}
|
|
@@ -17,8 +17,9 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import type React from 'react';
|
|
20
|
-
import {
|
|
20
|
+
import { Linking, Text as RNText } from 'react-native';
|
|
21
21
|
import { View } from 'react-native-css/components';
|
|
22
|
+
import { Loading } from '@oxy.so/bloom/loading';
|
|
22
23
|
import { useTheme } from '@oxy.so/bloom/theme';
|
|
23
24
|
import { Text } from '@oxy.so/bloom/typography';
|
|
24
25
|
import { useI18n } from '../../hooks/useI18n';
|
|
@@ -97,16 +98,12 @@ export const OxyAuthSplit: React.FC<{ children: React.ReactNode; aside: React.Re
|
|
|
97
98
|
);
|
|
98
99
|
};
|
|
99
100
|
|
|
100
|
-
/**
|
|
101
|
-
* The screen while there is nothing to show yet. React Native's own indicator:
|
|
102
|
-
* `@oxy.so/bloom/loading` can tree-shake to `undefined` in a rolldown-vite
|
|
103
|
-
* production bundle when co-imported with `@oxy.so/bloom/button`.
|
|
104
|
-
*/
|
|
101
|
+
/** The screen while there is nothing to show yet: Bloom's spinner, named for assistive technology. */
|
|
105
102
|
export const OxyAuthLoading: React.FC = () => {
|
|
106
|
-
const
|
|
103
|
+
const { t } = useI18n();
|
|
107
104
|
return (
|
|
108
105
|
<View className="min-h-[300px] items-center justify-center">
|
|
109
|
-
<
|
|
106
|
+
<Loading variant="spinner" size="large" accessibilityLabel={t('common.status.loading')} testID="oxy-auth-loading" />
|
|
110
107
|
</View>
|
|
111
108
|
);
|
|
112
109
|
};
|
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
|
|
14
14
|
import type React from 'react';
|
|
15
15
|
import { useState } from 'react';
|
|
16
|
-
import {
|
|
16
|
+
import { StyleSheet, View } from 'react-native';
|
|
17
17
|
import QRCode from 'react-native-qrcode-svg';
|
|
18
|
-
import {
|
|
18
|
+
import { CodeBlock } from '@oxy.so/bloom/code';
|
|
19
19
|
import { useTheme } from '@oxy.so/bloom/theme';
|
|
20
20
|
import { toast } from '@oxy.so/bloom/toast';
|
|
21
21
|
import { Text } from '@oxy.so/bloom/typography';
|
|
@@ -25,6 +25,7 @@ import { useOxy } from '../../context/OxyContext';
|
|
|
25
25
|
import { useSignInMethods } from '../../hooks/queries/useAuthMethods';
|
|
26
26
|
import { queryKeys } from '../../hooks/queries/queryKeys';
|
|
27
27
|
import { useI18n } from '../../hooks/useI18n';
|
|
28
|
+
import { copyText } from '../../utils/clipboard';
|
|
28
29
|
import { SubtleLink } from '../authChooser/primitives';
|
|
29
30
|
import { OxyAuthLoading, OxyAuthScreen, OxyAuthScreenHeader } from './OxyAuthScreen';
|
|
30
31
|
import { ReauthStep } from './ReauthStep';
|
|
@@ -33,6 +34,7 @@ import {
|
|
|
33
34
|
AccountFlowCodeField,
|
|
34
35
|
AccountFlowErrorLine,
|
|
35
36
|
AccountFlowNote,
|
|
37
|
+
AccountFlowProgress,
|
|
36
38
|
describeSignInError,
|
|
37
39
|
} from './accountFlowParts';
|
|
38
40
|
|
|
@@ -46,7 +48,8 @@ type Step =
|
|
|
46
48
|
| { name: 'enroll'; enrollment: TotpEnrollResponse }
|
|
47
49
|
| { name: 'regenerate' }
|
|
48
50
|
| { name: 'disable' }
|
|
49
|
-
|
|
51
|
+
/** `enrolling`: the last step of turning it on, rather than new codes for one that is on. */
|
|
52
|
+
| { name: 'codes'; codes: string[]; enrolling: boolean };
|
|
50
53
|
|
|
51
54
|
export interface OxyAuthenticatorPanelProps {
|
|
52
55
|
/** The person is done here (after saving backup codes, or turning it off). */
|
|
@@ -92,34 +95,36 @@ export const OxyAuthenticatorPanel: React.FC<OxyAuthenticatorPanelProps> = ({ on
|
|
|
92
95
|
};
|
|
93
96
|
|
|
94
97
|
const back = { label: t('common.cancel'), onPress: () => setStep({ name: 'status' }) };
|
|
98
|
+
// Turning it on is two steps: scan and confirm, then save the backup codes.
|
|
99
|
+
const enrolSteps = [t('signInSecurity.totp.title'), t('signInSecurity.totp.backupTitle')];
|
|
100
|
+
|
|
101
|
+
const copyCodes = async (codes: string) => {
|
|
102
|
+
try {
|
|
103
|
+
await copyText(codes);
|
|
104
|
+
} catch (reason) {
|
|
105
|
+
toast.error(t('appInfo.toasts.copyFailed'));
|
|
106
|
+
// Rethrown so the copy button keeps its copy glyph: nothing was copied.
|
|
107
|
+
throw reason;
|
|
108
|
+
}
|
|
109
|
+
toast.success(t('signInSecurity.totp.copied'));
|
|
110
|
+
};
|
|
95
111
|
|
|
96
112
|
switch (step.name) {
|
|
97
113
|
case 'codes':
|
|
98
114
|
return (
|
|
99
115
|
<OxyAuthScreen>
|
|
100
116
|
<OxyAuthScreenHeader title={t('signInSecurity.totp.backupTitle')} description={t('signInSecurity.totp.backupDescription')} />
|
|
101
|
-
<
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
)
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
fullWidth
|
|
113
|
-
onPress={() => {
|
|
114
|
-
Promise.resolve()
|
|
115
|
-
.then(() => Clipboard.setString(step.codes.join('\n')))
|
|
116
|
-
.then(() => toast.success(t('signInSecurity.totp.copied')))
|
|
117
|
-
.catch(() => undefined);
|
|
118
|
-
}}
|
|
119
|
-
testID="totp-copy-codes"
|
|
120
|
-
>
|
|
121
|
-
{t('signInSecurity.totp.copy')}
|
|
122
|
-
</Button>
|
|
117
|
+
{step.enrolling ? <AccountFlowProgress steps={enrolSteps} current={1} testID="totp-progress" /> : null}
|
|
118
|
+
{/* One code per line, with the block's own copy button (it shows a
|
|
119
|
+
check once the codes are on the clipboard). */}
|
|
120
|
+
<CodeBlock
|
|
121
|
+
code={step.codes.join('\n')}
|
|
122
|
+
filename={t('signInSecurity.totp.remaining', { count: step.codes.length })}
|
|
123
|
+
lineNumbers={false}
|
|
124
|
+
labels={{ copy: t('signInSecurity.totp.copy'), copied: t('signInSecurity.totp.copied') }}
|
|
125
|
+
onCopy={copyCodes}
|
|
126
|
+
testID="totp-backup-codes"
|
|
127
|
+
/>
|
|
123
128
|
<AccountFlowAction
|
|
124
129
|
label={t('signInSecurity.totp.savedThem')}
|
|
125
130
|
onPress={() => {
|
|
@@ -146,10 +151,11 @@ export const OxyAuthenticatorPanel: React.FC<OxyAuthenticatorPanelProps> = ({ on
|
|
|
146
151
|
const codes = await oxyServices.auth.totp.confirm(code.trim(), reauth);
|
|
147
152
|
refresh();
|
|
148
153
|
toast.success(t('signInSecurity.totp.enabled'));
|
|
149
|
-
setStep({ name: 'codes', codes });
|
|
154
|
+
setStep({ name: 'codes', codes, enrolling: true });
|
|
150
155
|
}}
|
|
151
156
|
secondary={back}
|
|
152
157
|
>
|
|
158
|
+
<AccountFlowProgress steps={enrolSteps} current={0} testID="totp-progress" />
|
|
153
159
|
<View style={styles.plateRow}>
|
|
154
160
|
<View style={[styles.plate, { borderColor: theme.colors.border }]} testID="totp-qr">
|
|
155
161
|
<QRCode value={enrollment.otpauthUri} size={QR_SIZE} backgroundColor={QR_PLATE_BG} color={QR_FOREGROUND} />
|
|
@@ -184,7 +190,7 @@ export const OxyAuthenticatorPanel: React.FC<OxyAuthenticatorPanelProps> = ({ on
|
|
|
184
190
|
onSubmit={async (reauth) => {
|
|
185
191
|
const codes = await oxyServices.auth.totp.regenerateBackupCodes(reauth);
|
|
186
192
|
refresh();
|
|
187
|
-
setStep({ name: 'codes', codes });
|
|
193
|
+
setStep({ name: 'codes', codes, enrolling: false });
|
|
188
194
|
}}
|
|
189
195
|
secondary={back}
|
|
190
196
|
/>
|
|
@@ -229,7 +235,6 @@ export const OxyAuthenticatorPanel: React.FC<OxyAuthenticatorPanelProps> = ({ on
|
|
|
229
235
|
/>
|
|
230
236
|
<SubtleLink
|
|
231
237
|
label={t('signInSecurity.totp.disable')}
|
|
232
|
-
theme={theme}
|
|
233
238
|
onPress={() => setStep({ name: 'disable' })}
|
|
234
239
|
testID="totp-disable"
|
|
235
240
|
/>
|
|
@@ -270,21 +275,4 @@ const styles = StyleSheet.create({
|
|
|
270
275
|
letterSpacing: 1,
|
|
271
276
|
textAlign: 'center',
|
|
272
277
|
},
|
|
273
|
-
codes: {
|
|
274
|
-
flexDirection: 'row',
|
|
275
|
-
flexWrap: 'wrap',
|
|
276
|
-
gap: 8,
|
|
277
|
-
padding: 16,
|
|
278
|
-
borderRadius: 16,
|
|
279
|
-
borderWidth: StyleSheet.hairlineWidth,
|
|
280
|
-
justifyContent: 'center',
|
|
281
|
-
},
|
|
282
|
-
code: {
|
|
283
|
-
width: '45%',
|
|
284
|
-
fontSize: 16,
|
|
285
|
-
lineHeight: 24,
|
|
286
|
-
fontWeight: '600',
|
|
287
|
-
letterSpacing: 1,
|
|
288
|
-
textAlign: 'center',
|
|
289
|
-
},
|
|
290
278
|
});
|
|
@@ -12,12 +12,13 @@
|
|
|
12
12
|
|
|
13
13
|
import type React from 'react';
|
|
14
14
|
import { useState } from 'react';
|
|
15
|
+
import { surfaces } from '@oxy.so/bloom/surfaces';
|
|
15
16
|
import { useOxy } from '../../context/OxyContext';
|
|
16
17
|
import { useSignInMethods } from '../../hooks/queries/useAuthMethods';
|
|
17
18
|
import { useI18n } from '../../hooks/useI18n';
|
|
18
19
|
import { OxyAuthLoading, OxyAuthScreen, OxyAuthScreenHeader } from './OxyAuthScreen';
|
|
19
20
|
import { ReauthStep } from './ReauthStep';
|
|
20
|
-
import { AccountFlowField,
|
|
21
|
+
import { AccountFlowField, AccountFlowNotice } from './accountFlowParts';
|
|
21
22
|
|
|
22
23
|
export interface OxyDeleteAccountPanelProps {
|
|
23
24
|
/** The account is gone and this origin signed out of it. */
|
|
@@ -45,7 +46,8 @@ export const OxyDeleteAccountPanel: React.FC<OxyDeleteAccountPanelProps> = ({ on
|
|
|
45
46
|
if (keyed) {
|
|
46
47
|
return (
|
|
47
48
|
<OxyAuthScreen>
|
|
48
|
-
<OxyAuthScreenHeader title={t('deleteAccount.handoff.commonsTitle')}
|
|
49
|
+
<OxyAuthScreenHeader title={t('deleteAccount.handoff.commonsTitle')} />
|
|
50
|
+
<AccountFlowNotice testID="delete-account-keyed">{t('deleteAccount.keyless.keyed')}</AccountFlowNotice>
|
|
49
51
|
</OxyAuthScreen>
|
|
50
52
|
);
|
|
51
53
|
}
|
|
@@ -65,6 +67,16 @@ export const OxyDeleteAccountPanel: React.FC<OxyDeleteAccountPanelProps> = ({ on
|
|
|
65
67
|
validate={() => (confirmText.trim() === username ? null : t('deleteAccount.confirmLabel', { username }))}
|
|
66
68
|
onSubmit={async (proof) => {
|
|
67
69
|
if (!proof.emailCode) throw new Error(t('reauth.errors.invalid'));
|
|
70
|
+
// The last word before it is gone: the proof is ready, and nothing has
|
|
71
|
+
// been sent yet. Cancelling keeps the screen as it is, code and all.
|
|
72
|
+
const confirmed = await surfaces.confirm({
|
|
73
|
+
title: t('deleteAccount.title'),
|
|
74
|
+
description: t('deleteAccount.warning'),
|
|
75
|
+
confirmLabel: t('deleteAccount.confirm'),
|
|
76
|
+
cancelLabel: t('common.cancel'),
|
|
77
|
+
destructive: true,
|
|
78
|
+
});
|
|
79
|
+
if (!confirmed) return;
|
|
68
80
|
await oxyServices.users.deleteMe(confirmText.trim(), {
|
|
69
81
|
reauth: {
|
|
70
82
|
emailCode: proof.emailCode,
|
|
@@ -77,7 +89,9 @@ export const OxyDeleteAccountPanel: React.FC<OxyDeleteAccountPanelProps> = ({ on
|
|
|
77
89
|
}}
|
|
78
90
|
secondary={onCancel ? { label: t('common.cancel'), onPress: onCancel } : undefined}
|
|
79
91
|
>
|
|
80
|
-
<
|
|
92
|
+
<AccountFlowNotice type="warning" testID="delete-account-warning">
|
|
93
|
+
{t('deleteAccount.warning')}
|
|
94
|
+
</AccountFlowNotice>
|
|
81
95
|
<AccountFlowField
|
|
82
96
|
label={t('deleteAccount.confirmLabel', { username })}
|
|
83
97
|
value={confirmText}
|
|
@@ -13,9 +13,10 @@
|
|
|
13
13
|
|
|
14
14
|
import type React from 'react';
|
|
15
15
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
16
|
-
import {
|
|
16
|
+
import { StyleSheet, View } from 'react-native';
|
|
17
17
|
import QRCode from 'react-native-qrcode-svg';
|
|
18
18
|
import { Button } from '@oxy.so/bloom/button';
|
|
19
|
+
import { Loading } from '@oxy.so/bloom/loading';
|
|
19
20
|
import { useTheme } from '@oxy.so/bloom/theme';
|
|
20
21
|
import { Text } from '@oxy.so/bloom/typography';
|
|
21
22
|
import { deriveIdentityLinkCode } from '@oxy.so/core/crypto';
|
|
@@ -203,7 +204,7 @@ export const OxyLinkCommonsPanel: React.FC<OxyLinkCommonsPanelProps> = ({ onLink
|
|
|
203
204
|
<OxyAuthScreen>
|
|
204
205
|
<OxyAuthScreenHeader title={t('linkCommons.title')} description={t('linkCommons.subtitle')} />
|
|
205
206
|
<View style={styles.plateRow}>
|
|
206
|
-
<
|
|
207
|
+
<Loading variant="spinner" size="large" accessibilityLabel={t('common.status.loading')} />
|
|
207
208
|
</View>
|
|
208
209
|
</OxyAuthScreen>
|
|
209
210
|
);
|
|
@@ -7,11 +7,10 @@
|
|
|
7
7
|
|
|
8
8
|
import type React from 'react';
|
|
9
9
|
import { useState } from 'react';
|
|
10
|
-
import { StyleSheet
|
|
10
|
+
import { StyleSheet } from 'react-native';
|
|
11
|
+
import { Field } from '@oxy.so/bloom/field';
|
|
11
12
|
import { Switch } from '@oxy.so/bloom/switch';
|
|
12
|
-
import { useTheme } from '@oxy.so/bloom/theme';
|
|
13
13
|
import { toast } from '@oxy.so/bloom/toast';
|
|
14
|
-
import { Text } from '@oxy.so/bloom/typography';
|
|
15
14
|
import { PASSWORD_MAX_LENGTH, PASSWORD_MIN_LENGTH } from '@oxy.so/contracts';
|
|
16
15
|
import { useQueryClient } from '@tanstack/react-query';
|
|
17
16
|
import { useOxy } from '../../context/OxyContext';
|
|
@@ -30,7 +29,6 @@ export interface OxyPasswordPanelProps {
|
|
|
30
29
|
}
|
|
31
30
|
|
|
32
31
|
export const OxyPasswordPanel: React.FC<OxyPasswordPanelProps> = ({ onDone, onCancel }) => {
|
|
33
|
-
const theme = useTheme();
|
|
34
32
|
const { t } = useI18n();
|
|
35
33
|
const { oxyServices, user } = useOxy();
|
|
36
34
|
const queryClient = useQueryClient();
|
|
@@ -94,10 +92,11 @@ export const OxyPasswordPanel: React.FC<OxyPasswordPanelProps> = ({ onDone, onCa
|
|
|
94
92
|
autoFocus={false}
|
|
95
93
|
testID="password-repeat"
|
|
96
94
|
/>
|
|
97
|
-
|
|
98
|
-
|
|
95
|
+
{/* The field names the switch ("Sign out everywhere else, switch, off")
|
|
96
|
+
and, on web, its label toggles it. One row: label, then the switch. */}
|
|
97
|
+
<Field label={t('signInSecurity.password.signOutOthers')} style={styles.row}>
|
|
99
98
|
<Switch value={signOutOthers} onValueChange={setSignOutOthers} testID="password-sign-out-others" />
|
|
100
|
-
</
|
|
99
|
+
</Field>
|
|
101
100
|
</ReauthStep>
|
|
102
101
|
);
|
|
103
102
|
};
|
|
@@ -109,9 +108,4 @@ const styles = StyleSheet.create({
|
|
|
109
108
|
justifyContent: 'space-between',
|
|
110
109
|
gap: 12,
|
|
111
110
|
},
|
|
112
|
-
label: {
|
|
113
|
-
flex: 1,
|
|
114
|
-
fontSize: 16,
|
|
115
|
-
lineHeight: 24,
|
|
116
|
-
},
|
|
117
111
|
});
|
|
@@ -60,7 +60,7 @@ import {
|
|
|
60
60
|
AccountFlowAction,
|
|
61
61
|
AccountFlowCodeField,
|
|
62
62
|
AccountFlowField,
|
|
63
|
-
|
|
63
|
+
AccountFlowNotice,
|
|
64
64
|
BACKUP_CODE_LENGTH,
|
|
65
65
|
describeSignInError,
|
|
66
66
|
errorCode,
|
|
@@ -440,7 +440,14 @@ export const OxySignInPanel: React.FC<OxySignInPanelProps> = ({
|
|
|
440
440
|
);
|
|
441
441
|
}
|
|
442
442
|
|
|
443
|
-
|
|
443
|
+
// A 429 is not something the person typed wrong: it is a wait, counted down
|
|
444
|
+
// in a notice above the action it holds back, never the field's error.
|
|
445
|
+
const rateLimitNotice =
|
|
446
|
+
rateLimitSeconds > 0 ? (
|
|
447
|
+
<AccountFlowNotice type="warning" testID="signin-rate-limit">
|
|
448
|
+
{t('signin.errors.rateLimited', { seconds: rateLimitSeconds })}
|
|
449
|
+
</AccountFlowNotice>
|
|
450
|
+
) : null;
|
|
444
451
|
const clearError = () => {
|
|
445
452
|
if (error) setError(null);
|
|
446
453
|
};
|
|
@@ -464,7 +471,7 @@ export const OxySignInPanel: React.FC<OxySignInPanelProps> = ({
|
|
|
464
471
|
clearError();
|
|
465
472
|
}}
|
|
466
473
|
onComplete={submitCode}
|
|
467
|
-
error={
|
|
474
|
+
error={error}
|
|
468
475
|
disabled={blocked}
|
|
469
476
|
length={longCode ? EMAIL_SIGNIN_LONG_CODE_LENGTH : EMAIL_CODE_LENGTH}
|
|
470
477
|
type={longCode ? 'alphanumeric' : 'numeric'}
|
|
@@ -474,7 +481,6 @@ export const OxySignInPanel: React.FC<OxySignInPanelProps> = ({
|
|
|
474
481
|
/>
|
|
475
482
|
<SubtleLink
|
|
476
483
|
label={longCode ? t('signin.checkEmail.codeDigitsOnly') : t('signin.checkEmail.codeHasLetters')}
|
|
477
|
-
theme={theme}
|
|
478
484
|
onPress={() => {
|
|
479
485
|
setLongCode(!longCode);
|
|
480
486
|
setCode('');
|
|
@@ -483,7 +489,8 @@ export const OxySignInPanel: React.FC<OxySignInPanelProps> = ({
|
|
|
483
489
|
disabled={pending}
|
|
484
490
|
testID="signin-code-letters"
|
|
485
491
|
/>
|
|
486
|
-
{notice ? <
|
|
492
|
+
{notice ? <AccountFlowNotice testID="signin-notice">{notice}</AccountFlowNotice> : null}
|
|
493
|
+
{rateLimitNotice}
|
|
487
494
|
<AccountFlowAction
|
|
488
495
|
label={t('signin.actions.continue')}
|
|
489
496
|
onPress={() => submitCode(code)}
|
|
@@ -498,21 +505,18 @@ export const OxySignInPanel: React.FC<OxySignInPanelProps> = ({
|
|
|
498
505
|
? t('signin.checkEmail.resendIn', { seconds: resendSeconds })
|
|
499
506
|
: t('signin.checkEmail.resend')
|
|
500
507
|
}
|
|
501
|
-
theme={theme}
|
|
502
508
|
onPress={() => startEmail(request.identifier)}
|
|
503
509
|
disabled={blocked || resendSeconds > 0}
|
|
504
510
|
testID="signin-resend"
|
|
505
511
|
/>
|
|
506
512
|
<SubtleLink
|
|
507
513
|
label={t('signin.checkEmail.usePassword')}
|
|
508
|
-
theme={theme}
|
|
509
514
|
onPress={() => toPassword(request.identifier)}
|
|
510
515
|
disabled={pending}
|
|
511
516
|
testID="signin-use-password"
|
|
512
517
|
/>
|
|
513
518
|
<SubtleLink
|
|
514
519
|
label={t('signin.checkEmail.differentAccount')}
|
|
515
|
-
theme={theme}
|
|
516
520
|
onPress={backToIdentifier}
|
|
517
521
|
disabled={pending}
|
|
518
522
|
testID="signin-different-account"
|
|
@@ -535,12 +539,13 @@ export const OxySignInPanel: React.FC<OxySignInPanelProps> = ({
|
|
|
535
539
|
clearError();
|
|
536
540
|
}}
|
|
537
541
|
onSubmit={submitPassword}
|
|
538
|
-
error={
|
|
542
|
+
error={error}
|
|
539
543
|
disabled={blocked}
|
|
540
544
|
autoComplete="current-password"
|
|
541
545
|
secureTextEntry
|
|
542
546
|
testID="signin-password"
|
|
543
547
|
/>
|
|
548
|
+
{rateLimitNotice}
|
|
544
549
|
<AccountFlowAction
|
|
545
550
|
label={t('signin.actions.continue')}
|
|
546
551
|
onPress={submitPassword}
|
|
@@ -551,14 +556,12 @@ export const OxySignInPanel: React.FC<OxySignInPanelProps> = ({
|
|
|
551
556
|
<View style={styles.links}>
|
|
552
557
|
<SubtleLink
|
|
553
558
|
label={t('signin.password.forgot')}
|
|
554
|
-
theme={theme}
|
|
555
559
|
onPress={() => startEmail(request.identifier)}
|
|
556
560
|
disabled={blocked}
|
|
557
561
|
testID="signin-password-forgot"
|
|
558
562
|
/>
|
|
559
563
|
<SubtleLink
|
|
560
564
|
label={t('signin.checkEmail.differentAccount')}
|
|
561
|
-
theme={theme}
|
|
562
565
|
onPress={backToIdentifier}
|
|
563
566
|
disabled={pending}
|
|
564
567
|
testID="signin-different-account"
|
|
@@ -584,13 +587,14 @@ export const OxySignInPanel: React.FC<OxySignInPanelProps> = ({
|
|
|
584
587
|
clearError();
|
|
585
588
|
}}
|
|
586
589
|
onComplete={submitSecondFactor}
|
|
587
|
-
error={
|
|
590
|
+
error={error}
|
|
588
591
|
disabled={blocked}
|
|
589
592
|
length={useBackupCode ? BACKUP_CODE_LENGTH : TOTP_DIGITS}
|
|
590
593
|
type={useBackupCode ? 'alphanumeric' : 'numeric'}
|
|
591
594
|
groupEvery={useBackupCode ? BACKUP_CODE_LENGTH / 2 : undefined}
|
|
592
595
|
testID="signin-second-factor"
|
|
593
596
|
/>
|
|
597
|
+
{rateLimitNotice}
|
|
594
598
|
<AccountFlowAction
|
|
595
599
|
label={t('signin.actions.continue')}
|
|
596
600
|
onPress={() => submitSecondFactor(secondFactorCode)}
|
|
@@ -601,7 +605,6 @@ export const OxySignInPanel: React.FC<OxySignInPanelProps> = ({
|
|
|
601
605
|
<View style={styles.links}>
|
|
602
606
|
<SubtleLink
|
|
603
607
|
label={useBackupCode ? t('signin.secondFactor.useAuthenticator') : t('signin.secondFactor.useBackup')}
|
|
604
|
-
theme={theme}
|
|
605
608
|
onPress={() => {
|
|
606
609
|
setUseBackupCode(!useBackupCode);
|
|
607
610
|
setSecondFactorCode('');
|
|
@@ -612,7 +615,6 @@ export const OxySignInPanel: React.FC<OxySignInPanelProps> = ({
|
|
|
612
615
|
/>
|
|
613
616
|
<SubtleLink
|
|
614
617
|
label={t('signin.checkEmail.differentAccount')}
|
|
615
|
-
theme={theme}
|
|
616
618
|
onPress={backToIdentifier}
|
|
617
619
|
disabled={pending}
|
|
618
620
|
testID="signin-different-account"
|
|
@@ -675,7 +677,7 @@ export const OxySignInPanel: React.FC<OxySignInPanelProps> = ({
|
|
|
675
677
|
clearError();
|
|
676
678
|
}}
|
|
677
679
|
onSubmit={submitIdentifier}
|
|
678
|
-
error={
|
|
680
|
+
error={error}
|
|
679
681
|
disabled={blocked}
|
|
680
682
|
placeholder={t('signin.identifier.placeholder')}
|
|
681
683
|
autoComplete="username"
|
|
@@ -694,6 +696,7 @@ export const OxySignInPanel: React.FC<OxySignInPanelProps> = ({
|
|
|
694
696
|
{t('signin.createAccount')}
|
|
695
697
|
</RNText>
|
|
696
698
|
</Text>
|
|
699
|
+
{rateLimitNotice}
|
|
697
700
|
<AccountFlowAction
|
|
698
701
|
label={t('signin.actions.continue')}
|
|
699
702
|
onPress={submitIdentifier}
|
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
import type React from 'react';
|
|
15
15
|
import { useEffect, useState } from 'react';
|
|
16
16
|
import { Linking, Platform } from 'react-native';
|
|
17
|
-
import { useTheme } from '@oxy.so/bloom/theme';
|
|
18
17
|
import { toast } from '@oxy.so/bloom/toast';
|
|
19
18
|
import { SIGN_IN_ERROR_CODES, emailAddressSchema, type EmailVerificationConfirmResponse } from '@oxy.so/contracts';
|
|
20
19
|
import { useOxy } from '../../context/OxyContext';
|
|
@@ -29,6 +28,7 @@ import {
|
|
|
29
28
|
AccountFlowAction,
|
|
30
29
|
AccountFlowField,
|
|
31
30
|
AccountFlowNote,
|
|
31
|
+
AccountFlowProgress,
|
|
32
32
|
EmailCodeStep,
|
|
33
33
|
describeSignInError,
|
|
34
34
|
errorCode,
|
|
@@ -61,7 +61,6 @@ interface SavedSignUpFlow {
|
|
|
61
61
|
const SIGN_UP_FLOW_KEY = 'signup';
|
|
62
62
|
|
|
63
63
|
export const OxySignUpPanel: React.FC<OxySignUpPanelProps> = ({ onSignedIn, onSignIn, host = 'page' }) => {
|
|
64
|
-
const theme = useTheme();
|
|
65
64
|
const { t } = useI18n();
|
|
66
65
|
const { oxyServices, handleWebSession, accountDialogController } = useOxy();
|
|
67
66
|
const snapshot = useAccountDialogSnapshot(accountDialogController);
|
|
@@ -149,6 +148,12 @@ export const OxySignUpPanel: React.FC<OxySignUpPanelProps> = ({ onSignedIn, onSi
|
|
|
149
148
|
.catch(() => toast.error(t('accountSwitcher.linkOpenFailed')));
|
|
150
149
|
};
|
|
151
150
|
|
|
151
|
+
// username → email → code, drawn as one segmented bar under each header.
|
|
152
|
+
const progressSteps = [t('signup.username.label'), t('signup.email.label'), t('emailCode.title')];
|
|
153
|
+
const progress = (current: number) => (
|
|
154
|
+
<AccountFlowProgress steps={progressSteps} current={current} testID="signup-progress" />
|
|
155
|
+
);
|
|
156
|
+
|
|
152
157
|
switch (step.name) {
|
|
153
158
|
case 'code':
|
|
154
159
|
return (
|
|
@@ -158,12 +163,14 @@ export const OxySignUpPanel: React.FC<OxySignUpPanelProps> = ({ onSignedIn, onSi
|
|
|
158
163
|
onConfirmed={createAccount}
|
|
159
164
|
onResend={() => sendCode(email)}
|
|
160
165
|
back={{ label: t('emailCode.changeEmail'), onPress: () => setStep({ name: 'email' }) }}
|
|
166
|
+
progress={progress(2)}
|
|
161
167
|
/>
|
|
162
168
|
);
|
|
163
169
|
case 'email':
|
|
164
170
|
return (
|
|
165
171
|
<OxyAuthScreen>
|
|
166
172
|
<OxyAuthScreenHeader title={t('signup.email.title')} description={t('signup.email.subtitle')} />
|
|
173
|
+
{progress(1)}
|
|
167
174
|
<AccountFlowField
|
|
168
175
|
label={t('signup.email.label')}
|
|
169
176
|
value={email}
|
|
@@ -181,7 +188,7 @@ export const OxySignUpPanel: React.FC<OxySignUpPanelProps> = ({ onSignedIn, onSi
|
|
|
181
188
|
/>
|
|
182
189
|
<AccountFlowNote>{t('signup.laterNote')}</AccountFlowNote>
|
|
183
190
|
<AccountFlowAction label={t('signin.actions.continue')} onPress={submitEmail} pending={pending} testID="signup-email-continue" />
|
|
184
|
-
<SubtleLink label={t('signin.actions.back')}
|
|
191
|
+
<SubtleLink label={t('signin.actions.back')} onPress={() => setStep({ name: 'username' })} testID="signup-back" />
|
|
185
192
|
<OxyAuthTerms />
|
|
186
193
|
</OxyAuthScreen>
|
|
187
194
|
);
|
|
@@ -189,6 +196,7 @@ export const OxySignUpPanel: React.FC<OxySignUpPanelProps> = ({ onSignedIn, onSi
|
|
|
189
196
|
return (
|
|
190
197
|
<OxyAuthScreen>
|
|
191
198
|
<OxyAuthScreenHeader title={t('signup.title')} description={t('signup.subtitle')} />
|
|
199
|
+
{progress(0)}
|
|
192
200
|
<AccountFlowField
|
|
193
201
|
label={t('signup.username.label')}
|
|
194
202
|
value={username}
|
|
@@ -204,8 +212,8 @@ export const OxySignUpPanel: React.FC<OxySignUpPanelProps> = ({ onSignedIn, onSi
|
|
|
204
212
|
testID="signup-username"
|
|
205
213
|
/>
|
|
206
214
|
<AccountFlowAction label={t('signin.actions.continue')} onPress={submitUsername} pending={pending} testID="signup-username-continue" />
|
|
207
|
-
<SubtleLink label={t('signup.createInCommons')}
|
|
208
|
-
<SubtleLink label={t('signup.backToSignInLink')}
|
|
215
|
+
<SubtleLink label={t('signup.createInCommons')} onPress={createInCommons} testID="signup-commons-instead" />
|
|
216
|
+
<SubtleLink label={t('signup.backToSignInLink')} onPress={onSignIn} testID="back-to-sign-in" />
|
|
209
217
|
<OxyAuthTerms />
|
|
210
218
|
</OxyAuthScreen>
|
|
211
219
|
);
|