@oxyhq/services 14.0.1 → 15.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/index.js +0 -17
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/ui/components/SignInAccountChooser.js +189 -0
- package/lib/commonjs/ui/components/SignInAccountChooser.js.map +1 -0
- package/lib/commonjs/ui/components/SignInModal.js +350 -131
- package/lib/commonjs/ui/components/SignInModal.js.map +1 -1
- package/lib/commonjs/ui/context/OxyContext.js +340 -1547
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js +61 -64
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/commonjs/ui/hooks/useAuth.js +14 -33
- package/lib/commonjs/ui/hooks/useAuth.js.map +1 -1
- package/lib/commonjs/ui/hooks/usePasswordSignIn.js +170 -0
- package/lib/commonjs/ui/hooks/usePasswordSignIn.js.map +1 -0
- package/lib/commonjs/ui/hooks/useProfileEditing.js +2 -5
- package/lib/commonjs/ui/hooks/useProfileEditing.js.map +1 -1
- package/lib/commonjs/ui/hooks/useSessionManagement.js +2 -2
- package/lib/commonjs/ui/hooks/useSessionManagement.js.map +1 -1
- package/lib/commonjs/ui/navigation/routes.js +1 -0
- package/lib/commonjs/ui/navigation/routes.js.map +1 -1
- package/lib/commonjs/ui/screens/EditProfileFieldScreen.js +93 -68
- package/lib/commonjs/ui/screens/EditProfileFieldScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/EditProfileScreen.js +187 -0
- package/lib/commonjs/ui/screens/EditProfileScreen.js.map +1 -0
- package/lib/commonjs/ui/screens/ManageAccountScreen.js +3 -7
- package/lib/commonjs/ui/screens/ManageAccountScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/OxyAuthScreen.js +231 -95
- package/lib/commonjs/ui/screens/OxyAuthScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/ProfileScreen.js +15 -2
- package/lib/commonjs/ui/screens/ProfileScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/linkFormat.js +38 -0
- package/lib/commonjs/ui/screens/linkFormat.js.map +1 -0
- package/lib/commonjs/ui/session/authStore.js +146 -0
- package/lib/commonjs/ui/session/authStore.js.map +1 -0
- package/lib/commonjs/ui/session/createSessionClient.js +25 -10
- package/lib/commonjs/ui/session/createSessionClient.js.map +1 -1
- package/lib/commonjs/ui/session/index.js +13 -7
- package/lib/commonjs/ui/session/index.js.map +1 -1
- package/lib/commonjs/ui/session/tokenTransport.js +19 -31
- package/lib/commonjs/ui/session/tokenTransport.js.map +1 -1
- package/lib/commonjs/ui/utils/isWebBrowser.js +13 -0
- package/lib/commonjs/ui/utils/isWebBrowser.js.map +1 -0
- package/lib/commonjs/utils/deviceFlowSignIn.js +13 -16
- package/lib/commonjs/utils/deviceFlowSignIn.js.map +1 -1
- package/lib/module/index.js +0 -5
- package/lib/module/index.js.map +1 -1
- package/lib/module/ui/components/SignInAccountChooser.js +183 -0
- package/lib/module/ui/components/SignInAccountChooser.js.map +1 -0
- package/lib/module/ui/components/SignInModal.js +352 -134
- package/lib/module/ui/components/SignInModal.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +342 -1551
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/context/hooks/useAuthOperations.js +60 -63
- package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/module/ui/hooks/useAuth.js +14 -33
- package/lib/module/ui/hooks/useAuth.js.map +1 -1
- package/lib/module/ui/hooks/usePasswordSignIn.js +165 -0
- package/lib/module/ui/hooks/usePasswordSignIn.js.map +1 -0
- package/lib/module/ui/hooks/useProfileEditing.js +2 -5
- package/lib/module/ui/hooks/useProfileEditing.js.map +1 -1
- package/lib/module/ui/hooks/useSessionManagement.js +1 -1
- package/lib/module/ui/hooks/useSessionManagement.js.map +1 -1
- package/lib/module/ui/navigation/routes.js +1 -0
- package/lib/module/ui/navigation/routes.js.map +1 -1
- package/lib/module/ui/screens/EditProfileFieldScreen.js +91 -66
- package/lib/module/ui/screens/EditProfileFieldScreen.js.map +1 -1
- package/lib/module/ui/screens/EditProfileScreen.js +182 -0
- package/lib/module/ui/screens/EditProfileScreen.js.map +1 -0
- package/lib/module/ui/screens/ManageAccountScreen.js +3 -7
- package/lib/module/ui/screens/ManageAccountScreen.js.map +1 -1
- package/lib/module/ui/screens/OxyAuthScreen.js +233 -97
- package/lib/module/ui/screens/OxyAuthScreen.js.map +1 -1
- package/lib/module/ui/screens/ProfileScreen.js +15 -2
- package/lib/module/ui/screens/ProfileScreen.js.map +1 -1
- package/lib/module/ui/screens/linkFormat.js +31 -0
- package/lib/module/ui/screens/linkFormat.js.map +1 -0
- package/lib/module/ui/session/authStore.js +143 -0
- package/lib/module/ui/session/authStore.js.map +1 -0
- package/lib/module/ui/session/createSessionClient.js +25 -10
- package/lib/module/ui/session/createSessionClient.js.map +1 -1
- package/lib/module/ui/session/index.js +9 -9
- package/lib/module/ui/session/index.js.map +1 -1
- package/lib/module/ui/session/tokenTransport.js +20 -33
- package/lib/module/ui/session/tokenTransport.js.map +1 -1
- package/lib/module/ui/utils/isWebBrowser.js +11 -0
- package/lib/module/ui/utils/isWebBrowser.js.map +1 -0
- package/lib/module/utils/deviceFlowSignIn.js +14 -17
- package/lib/module/utils/deviceFlowSignIn.js.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +0 -1
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/SignInAccountChooser.d.ts +29 -0
- package/lib/typescript/commonjs/ui/components/SignInAccountChooser.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/SignInModal.d.ts +15 -10
- package/lib/typescript/commonjs/ui/components/SignInModal.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/OxyContext.d.ts +36 -42
- package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts +18 -23
- package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts +0 -2
- package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useAuth.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/usePasswordSignIn.d.ts +31 -0
- package/lib/typescript/commonjs/ui/hooks/usePasswordSignIn.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/hooks/useProfileEditing.d.ts +0 -1
- package/lib/typescript/commonjs/ui/hooks/useProfileEditing.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/navigation/routes.d.ts +1 -1
- package/lib/typescript/commonjs/ui/navigation/routes.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/EditProfileFieldScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/EditProfileScreen.d.ts +14 -0
- package/lib/typescript/commonjs/ui/screens/EditProfileScreen.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/screens/ManageAccountScreen.d.ts +1 -1
- package/lib/typescript/commonjs/ui/screens/ManageAccountScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/OxyAuthScreen.d.ts +13 -12
- package/lib/typescript/commonjs/ui/screens/OxyAuthScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/ProfileScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/linkFormat.d.ts +23 -0
- package/lib/typescript/commonjs/ui/screens/linkFormat.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/session/authStore.d.ts +33 -0
- package/lib/typescript/commonjs/ui/session/authStore.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/session/createSessionClient.d.ts +20 -9
- package/lib/typescript/commonjs/ui/session/createSessionClient.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/session/index.d.ts +9 -9
- package/lib/typescript/commonjs/ui/session/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/session/tokenTransport.d.ts +13 -19
- package/lib/typescript/commonjs/ui/session/tokenTransport.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/utils/isWebBrowser.d.ts +9 -0
- package/lib/typescript/commonjs/ui/utils/isWebBrowser.d.ts.map +1 -0
- package/lib/typescript/commonjs/utils/deviceFlowSignIn.d.ts +15 -13
- package/lib/typescript/commonjs/utils/deviceFlowSignIn.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +0 -1
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/SignInAccountChooser.d.ts +29 -0
- package/lib/typescript/module/ui/components/SignInAccountChooser.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/SignInModal.d.ts +15 -10
- package/lib/typescript/module/ui/components/SignInModal.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/OxyContext.d.ts +36 -42
- package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts +18 -23
- package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts +0 -2
- package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useAuth.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/usePasswordSignIn.d.ts +31 -0
- package/lib/typescript/module/ui/hooks/usePasswordSignIn.d.ts.map +1 -0
- package/lib/typescript/module/ui/hooks/useProfileEditing.d.ts +0 -1
- package/lib/typescript/module/ui/hooks/useProfileEditing.d.ts.map +1 -1
- package/lib/typescript/module/ui/navigation/routes.d.ts +1 -1
- package/lib/typescript/module/ui/navigation/routes.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/EditProfileFieldScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/EditProfileScreen.d.ts +14 -0
- package/lib/typescript/module/ui/screens/EditProfileScreen.d.ts.map +1 -0
- package/lib/typescript/module/ui/screens/ManageAccountScreen.d.ts +1 -1
- package/lib/typescript/module/ui/screens/ManageAccountScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/OxyAuthScreen.d.ts +13 -12
- package/lib/typescript/module/ui/screens/OxyAuthScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/ProfileScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/linkFormat.d.ts +23 -0
- package/lib/typescript/module/ui/screens/linkFormat.d.ts.map +1 -0
- package/lib/typescript/module/ui/session/authStore.d.ts +33 -0
- package/lib/typescript/module/ui/session/authStore.d.ts.map +1 -0
- package/lib/typescript/module/ui/session/createSessionClient.d.ts +20 -9
- package/lib/typescript/module/ui/session/createSessionClient.d.ts.map +1 -1
- package/lib/typescript/module/ui/session/index.d.ts +9 -9
- package/lib/typescript/module/ui/session/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/session/tokenTransport.d.ts +13 -19
- package/lib/typescript/module/ui/session/tokenTransport.d.ts.map +1 -1
- package/lib/typescript/module/ui/utils/isWebBrowser.d.ts +9 -0
- package/lib/typescript/module/ui/utils/isWebBrowser.d.ts.map +1 -0
- package/lib/typescript/module/utils/deviceFlowSignIn.d.ts +15 -13
- package/lib/typescript/module/utils/deviceFlowSignIn.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/index.ts +0 -4
- package/src/ui/components/SignInAccountChooser.tsx +162 -0
- package/src/ui/components/SignInModal.tsx +309 -139
- package/src/ui/context/OxyContext.tsx +532 -1785
- package/src/ui/context/hooks/useAuthOperations.ts +65 -76
- package/src/ui/hooks/useAuth.ts +14 -35
- package/src/ui/hooks/usePasswordSignIn.ts +207 -0
- package/src/ui/hooks/useProfileEditing.ts +2 -8
- package/src/ui/hooks/useSessionManagement.ts +1 -1
- package/src/ui/navigation/routes.ts +2 -0
- package/src/ui/screens/EditProfileFieldScreen.tsx +102 -66
- package/src/ui/screens/EditProfileScreen.tsx +155 -0
- package/src/ui/screens/ManageAccountScreen.tsx +3 -7
- package/src/ui/screens/OxyAuthScreen.tsx +259 -112
- package/src/ui/screens/ProfileScreen.tsx +15 -2
- package/src/ui/screens/__tests__/linkFormat.test.ts +73 -0
- package/src/ui/screens/linkFormat.ts +37 -0
- package/src/ui/session/__tests__/createSessionClient.test.ts +49 -6
- package/src/ui/session/__tests__/tokenTransport.test.ts +37 -49
- package/src/ui/session/authStore.ts +164 -0
- package/src/ui/session/createSessionClient.ts +32 -11
- package/src/ui/session/index.ts +10 -9
- package/src/ui/session/tokenTransport.ts +26 -36
- package/src/ui/utils/isWebBrowser.ts +8 -0
- package/src/utils/__tests__/deviceFlowSignIn.test.ts +50 -131
- package/src/utils/deviceFlowSignIn.ts +19 -26
- package/lib/commonjs/ui/context/inSessionTokenRefresh.js +0 -243
- package/lib/commonjs/ui/context/inSessionTokenRefresh.js.map +0 -1
- package/lib/commonjs/ui/context/silentSessionRestore.js +0 -56
- package/lib/commonjs/ui/context/silentSessionRestore.js.map +0 -1
- package/lib/commonjs/ui/hooks/useWebSSO.js +0 -28
- package/lib/commonjs/ui/hooks/useWebSSO.js.map +0 -1
- package/lib/commonjs/ui/session/projectSessionState.js +0 -86
- package/lib/commonjs/ui/session/projectSessionState.js.map +0 -1
- package/lib/commonjs/ui/session/sessionClientHost.js +0 -30
- package/lib/commonjs/ui/session/sessionClientHost.js.map +0 -1
- package/lib/commonjs/ui/utils/activeAuthuser.js +0 -142
- package/lib/commonjs/ui/utils/activeAuthuser.js.map +0 -1
- package/lib/commonjs/utils/crossApex.js +0 -74
- package/lib/commonjs/utils/crossApex.js.map +0 -1
- package/lib/module/ui/context/inSessionTokenRefresh.js +0 -238
- package/lib/module/ui/context/inSessionTokenRefresh.js.map +0 -1
- package/lib/module/ui/context/silentSessionRestore.js +0 -53
- package/lib/module/ui/context/silentSessionRestore.js.map +0 -1
- package/lib/module/ui/hooks/useWebSSO.js +0 -24
- package/lib/module/ui/hooks/useWebSSO.js.map +0 -1
- package/lib/module/ui/session/projectSessionState.js +0 -79
- package/lib/module/ui/session/projectSessionState.js.map +0 -1
- package/lib/module/ui/session/sessionClientHost.js +0 -26
- package/lib/module/ui/session/sessionClientHost.js.map +0 -1
- package/lib/module/ui/utils/activeAuthuser.js +0 -134
- package/lib/module/ui/utils/activeAuthuser.js.map +0 -1
- package/lib/module/utils/crossApex.js +0 -69
- package/lib/module/utils/crossApex.js.map +0 -1
- package/lib/typescript/commonjs/ui/context/inSessionTokenRefresh.d.ts +0 -106
- package/lib/typescript/commonjs/ui/context/inSessionTokenRefresh.d.ts.map +0 -1
- package/lib/typescript/commonjs/ui/context/silentSessionRestore.d.ts +0 -36
- package/lib/typescript/commonjs/ui/context/silentSessionRestore.d.ts.map +0 -1
- package/lib/typescript/commonjs/ui/hooks/useWebSSO.d.ts +0 -19
- package/lib/typescript/commonjs/ui/hooks/useWebSSO.d.ts.map +0 -1
- package/lib/typescript/commonjs/ui/session/projectSessionState.d.ts +0 -45
- package/lib/typescript/commonjs/ui/session/projectSessionState.d.ts.map +0 -1
- package/lib/typescript/commonjs/ui/session/sessionClientHost.d.ts +0 -14
- package/lib/typescript/commonjs/ui/session/sessionClientHost.d.ts.map +0 -1
- package/lib/typescript/commonjs/ui/utils/activeAuthuser.d.ts +0 -67
- package/lib/typescript/commonjs/ui/utils/activeAuthuser.d.ts.map +0 -1
- package/lib/typescript/commonjs/utils/crossApex.d.ts +0 -55
- package/lib/typescript/commonjs/utils/crossApex.d.ts.map +0 -1
- package/lib/typescript/module/ui/context/inSessionTokenRefresh.d.ts +0 -106
- package/lib/typescript/module/ui/context/inSessionTokenRefresh.d.ts.map +0 -1
- package/lib/typescript/module/ui/context/silentSessionRestore.d.ts +0 -36
- package/lib/typescript/module/ui/context/silentSessionRestore.d.ts.map +0 -1
- package/lib/typescript/module/ui/hooks/useWebSSO.d.ts +0 -19
- package/lib/typescript/module/ui/hooks/useWebSSO.d.ts.map +0 -1
- package/lib/typescript/module/ui/session/projectSessionState.d.ts +0 -45
- package/lib/typescript/module/ui/session/projectSessionState.d.ts.map +0 -1
- package/lib/typescript/module/ui/session/sessionClientHost.d.ts +0 -14
- package/lib/typescript/module/ui/session/sessionClientHost.d.ts.map +0 -1
- package/lib/typescript/module/ui/utils/activeAuthuser.d.ts +0 -67
- package/lib/typescript/module/ui/utils/activeAuthuser.d.ts.map +0 -1
- package/lib/typescript/module/utils/crossApex.d.ts +0 -55
- package/lib/typescript/module/utils/crossApex.d.ts.map +0 -1
- package/src/ui/context/inSessionTokenRefresh.ts +0 -264
- package/src/ui/context/silentSessionRestore.ts +0 -53
- package/src/ui/hooks/useWebSSO.ts +0 -23
- package/src/ui/session/__tests__/projectSessionState.test.ts +0 -114
- package/src/ui/session/__tests__/sessionClientHost.test.ts +0 -43
- package/src/ui/session/projectSessionState.ts +0 -85
- package/src/ui/session/sessionClientHost.ts +0 -27
- package/src/ui/utils/activeAuthuser.ts +0 -142
- package/src/utils/crossApex.ts +0 -75
|
@@ -1,36 +1,47 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* SignInModal
|
|
2
|
+
* SignInModal — web-first centered sign-in modal.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Two phases, Google-style:
|
|
5
|
+
* 1. Account chooser (FRONT screen, shown when the device/user already has
|
|
6
|
+
* accounts): pick an account to continue as — one tap switches through the
|
|
7
|
+
* SAME `switchToAccount` path the account switcher uses — or "Use another
|
|
8
|
+
* account" to reveal the sign-in options.
|
|
9
|
+
* 2. Sign-in options: the first-party password flow (identifier → password →
|
|
10
|
+
* optional 2FA, `usePasswordSignIn`) as the PRIMARY action, with the
|
|
11
|
+
* cross-app device flow (same-device deep-link + "sign in on another device"
|
|
12
|
+
* QR) as a SECONDARY option below an "or" divider.
|
|
7
13
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* Animates with a fade + scale effect.
|
|
14
|
+
* When there are no accounts the modal opens straight on the sign-in options.
|
|
15
|
+
* The device-flow machinery lives in the shared `useOxyAuthSession` hook (the
|
|
16
|
+
* native `OxyAuthScreen` consumes it too — neither container re-implements the
|
|
17
|
+
* transport). Animates with a fade + scale; per-phase content cross-fades and
|
|
18
|
+
* respects reduced motion.
|
|
14
19
|
*/
|
|
15
20
|
|
|
16
21
|
import type React from 'react';
|
|
17
22
|
import { useState, useEffect, useCallback } from 'react';
|
|
18
|
-
import { View, Text, TouchableOpacity, StyleSheet, Modal } from 'react-native';
|
|
23
|
+
import { View, Text, TextInput, TouchableOpacity, StyleSheet, Modal, Linking, AccessibilityInfo } from 'react-native';
|
|
19
24
|
import Animated, {
|
|
20
25
|
useSharedValue,
|
|
21
26
|
useAnimatedStyle,
|
|
22
27
|
withTiming,
|
|
28
|
+
FadeIn,
|
|
23
29
|
} from 'react-native-reanimated';
|
|
24
30
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
25
31
|
import { useTheme } from '@oxyhq/bloom/theme';
|
|
26
32
|
import { Button } from '@oxyhq/bloom/button';
|
|
27
33
|
import { Loading } from '@oxyhq/bloom/loading';
|
|
28
|
-
import {
|
|
34
|
+
import { toast } from '@oxyhq/bloom';
|
|
35
|
+
import { Ionicons } from '@expo/vector-icons';
|
|
36
|
+
import { isDev, logger as loggerUtil } from '@oxyhq/core';
|
|
29
37
|
import { useOxy } from '../context/OxyContext';
|
|
30
38
|
import OxyLogo from './OxyLogo';
|
|
31
39
|
import AnotherDeviceQR from './AnotherDeviceQR';
|
|
40
|
+
import SignInAccountChooser from './SignInAccountChooser';
|
|
41
|
+
import { useSwitchableAccounts, type SwitchableAccount } from '../hooks/useSwitchableAccounts';
|
|
42
|
+
import { useI18n } from '../hooks/useI18n';
|
|
32
43
|
import { useOxyAuthSession, OXY_ACCOUNTS_WEB_URL } from '../hooks/useOxyAuthSession';
|
|
33
|
-
import {
|
|
44
|
+
import { usePasswordSignIn } from '../hooks/usePasswordSignIn';
|
|
34
45
|
|
|
35
46
|
// Store for modal visibility with subscription support
|
|
36
47
|
let modalVisible = false;
|
|
@@ -59,10 +70,8 @@ export const subscribeToSignInModal = (listener: (visible: boolean) => void): ((
|
|
|
59
70
|
|
|
60
71
|
const SignInModal: React.FC = () => {
|
|
61
72
|
const [visible, setVisible] = useState(false);
|
|
62
|
-
|
|
63
73
|
const insets = useSafeAreaInsets();
|
|
64
74
|
const theme = useTheme();
|
|
65
|
-
const { oxyServices, handleWebSession, clientId } = useOxy();
|
|
66
75
|
|
|
67
76
|
// Register the imperative visibility callback.
|
|
68
77
|
useEffect(() => {
|
|
@@ -74,75 +83,88 @@ const SignInModal: React.FC = () => {
|
|
|
74
83
|
|
|
75
84
|
if (!visible) return null;
|
|
76
85
|
|
|
77
|
-
//
|
|
78
|
-
//
|
|
79
|
-
|
|
80
|
-
// flips false, which runs the hook's unmount cleanup.
|
|
81
|
-
return (
|
|
82
|
-
<SignInModalContent
|
|
83
|
-
theme={theme}
|
|
84
|
-
insets={insets}
|
|
85
|
-
oxyServices={oxyServices}
|
|
86
|
-
handleWebSession={handleWebSession}
|
|
87
|
-
clientId={clientId}
|
|
88
|
-
/>
|
|
89
|
-
);
|
|
86
|
+
// Mounting the content only while visible preserves the session-created-on-open
|
|
87
|
+
// / cleaned-up-on-close behaviour and resets the phase state on every reopen.
|
|
88
|
+
return <SignInModalContent theme={theme} insets={insets} />;
|
|
90
89
|
};
|
|
91
90
|
|
|
92
91
|
interface SignInModalContentProps {
|
|
93
92
|
theme: ReturnType<typeof useTheme>;
|
|
94
93
|
insets: ReturnType<typeof useSafeAreaInsets>;
|
|
95
|
-
oxyServices: ReturnType<typeof useOxy>['oxyServices'];
|
|
96
|
-
handleWebSession: ReturnType<typeof useOxy>['handleWebSession'];
|
|
97
|
-
clientId: ReturnType<typeof useOxy>['clientId'];
|
|
98
94
|
}
|
|
99
95
|
|
|
100
|
-
const SignInModalContent: React.FC<SignInModalContentProps> = ({
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const
|
|
96
|
+
const SignInModalContent: React.FC<SignInModalContentProps> = ({ theme, insets }) => {
|
|
97
|
+
const { oxyServices, handleWebSession, clientId, switchToAccount } = useOxy();
|
|
98
|
+
const { t } = useI18n();
|
|
99
|
+
const { accounts } = useSwitchableAccounts();
|
|
100
|
+
|
|
101
|
+
// Phase: the chooser is the front screen whenever accounts exist AND the user
|
|
102
|
+
// has not tapped "Use another account" this session.
|
|
103
|
+
const [useAnother, setUseAnother] = useState(false);
|
|
104
|
+
const [switchingId, setSwitchingId] = useState<string | null>(null);
|
|
105
|
+
const showChooser = !useAnother && accounts.length > 0;
|
|
106
|
+
|
|
107
|
+
const { qrData, qrPayload, isLoading, error, isWaiting, openSameDeviceApproval, retry } = useOxyAuthSession(
|
|
108
108
|
oxyServices,
|
|
109
109
|
clientId,
|
|
110
110
|
handleWebSession,
|
|
111
111
|
{ onSignedIn: hideSignInModal },
|
|
112
112
|
);
|
|
113
113
|
|
|
114
|
-
//
|
|
115
|
-
|
|
116
|
-
// + cross-device QR) approve OUTSIDE the browser, so they leave no IdP
|
|
117
|
-
// session and the user would be logged out on reload — hide them there.
|
|
118
|
-
const crossApexWeb = isCrossApexWeb();
|
|
114
|
+
// First-party password sign-in — the PRIMARY option once past the chooser.
|
|
115
|
+
const pw = usePasswordSignIn({ onSignedIn: hideSignInModal });
|
|
119
116
|
|
|
120
|
-
// Entrance animation.
|
|
117
|
+
// Entrance animation (respects reduced motion).
|
|
121
118
|
const opacity = useSharedValue(0);
|
|
122
119
|
const scale = useSharedValue(0.96);
|
|
123
|
-
|
|
124
|
-
// biome-ignore lint/correctness/useExhaustiveDependencies: opacity/scale are Reanimated SharedValues (stable refs) and must not be listed as deps; this runs once on mount to play the entrance.
|
|
120
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: opacity/scale are Reanimated SharedValues (stable refs), not deps; runs once on mount.
|
|
125
121
|
useEffect(() => {
|
|
126
|
-
|
|
127
|
-
|
|
122
|
+
let cancelled = false;
|
|
123
|
+
AccessibilityInfo.isReduceMotionEnabled().then((reduced) => {
|
|
124
|
+
if (cancelled) return;
|
|
125
|
+
const duration = reduced ? 0 : 250;
|
|
126
|
+
opacity.value = withTiming(1, { duration });
|
|
127
|
+
scale.value = withTiming(1, { duration });
|
|
128
|
+
});
|
|
129
|
+
return () => {
|
|
130
|
+
cancelled = true;
|
|
131
|
+
};
|
|
128
132
|
}, []);
|
|
129
133
|
|
|
130
|
-
const backdropStyle = useAnimatedStyle(() => ({
|
|
131
|
-
opacity: opacity.value,
|
|
132
|
-
}));
|
|
133
|
-
|
|
134
|
+
const backdropStyle = useAnimatedStyle(() => ({ opacity: opacity.value }));
|
|
134
135
|
const contentStyle = useAnimatedStyle(() => ({
|
|
135
136
|
opacity: opacity.value,
|
|
136
137
|
transform: [{ scale: scale.value }],
|
|
137
138
|
}));
|
|
138
139
|
|
|
139
|
-
const handleClose = useCallback(() =>
|
|
140
|
-
|
|
141
|
-
}, []);
|
|
140
|
+
const handleClose = useCallback(() => hideSignInModal(), []);
|
|
141
|
+
const handleCreateAccount = useCallback(() => Linking.openURL(OXY_ACCOUNTS_WEB_URL), []);
|
|
142
142
|
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
143
|
+
const handleSelectAccount = useCallback(async (account: SwitchableAccount) => {
|
|
144
|
+
// The active account is already signed in — selecting it just continues.
|
|
145
|
+
if (account.isCurrent) {
|
|
146
|
+
hideSignInModal();
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
if (switchingId) return;
|
|
150
|
+
setSwitchingId(account.accountId);
|
|
151
|
+
try {
|
|
152
|
+
await switchToAccount(account.accountId);
|
|
153
|
+
hideSignInModal();
|
|
154
|
+
} catch (switchError) {
|
|
155
|
+
if (isDev()) {
|
|
156
|
+
loggerUtil.warn('SignInModal: switch account failed', { component: 'SignInModal' }, switchError as unknown);
|
|
157
|
+
}
|
|
158
|
+
toast.error(t('accountSwitcher.toasts.switchFailed') || 'Failed to switch account');
|
|
159
|
+
} finally {
|
|
160
|
+
setSwitchingId(null);
|
|
161
|
+
}
|
|
162
|
+
}, [switchingId, switchToAccount, t]);
|
|
163
|
+
|
|
164
|
+
const title = showChooser ? t('signin.chooser.title') : 'Sign in to Oxy';
|
|
165
|
+
const subtitle = showChooser
|
|
166
|
+
? t('signin.chooser.subtitle')
|
|
167
|
+
: 'Continue with your Oxy identity to sign in securely.';
|
|
146
168
|
|
|
147
169
|
return (
|
|
148
170
|
<Modal visible transparent animationType="none" statusBarTranslucent onRequestClose={handleClose}>
|
|
@@ -152,87 +174,182 @@ const SignInModalContent: React.FC<SignInModalContentProps> = ({
|
|
|
152
174
|
<Animated.View
|
|
153
175
|
style={[
|
|
154
176
|
styles.card,
|
|
155
|
-
{ backgroundColor: theme.colors.card, paddingTop: insets.top +
|
|
177
|
+
{ backgroundColor: theme.colors.card, paddingTop: insets.top + 28, paddingBottom: insets.bottom + 24 },
|
|
156
178
|
contentStyle,
|
|
157
179
|
]}
|
|
158
180
|
>
|
|
159
|
-
{/* Close button */}
|
|
160
181
|
<TouchableOpacity
|
|
161
182
|
style={[styles.closeButton, { backgroundColor: theme.colors.backgroundSecondary }]}
|
|
162
183
|
onPress={handleClose}
|
|
163
184
|
accessibilityRole="button"
|
|
164
185
|
accessibilityLabel="Close sign in"
|
|
165
186
|
>
|
|
166
|
-
<
|
|
187
|
+
<Ionicons name="close" size={22} color={theme.colors.textSecondary} />
|
|
167
188
|
</TouchableOpacity>
|
|
168
189
|
|
|
169
190
|
{/* Branded header */}
|
|
170
191
|
<View style={styles.header}>
|
|
171
|
-
<OxyLogo variant="icon" size={
|
|
172
|
-
<Text className="text-foreground" style={styles.title}>
|
|
173
|
-
<Text className="text-muted-foreground" style={styles.subtitle}>
|
|
174
|
-
Continue with your Oxy identity to sign in securely
|
|
175
|
-
</Text>
|
|
192
|
+
<OxyLogo variant="icon" size={52} />
|
|
193
|
+
<Text className="text-foreground" style={styles.title}>{title}</Text>
|
|
194
|
+
<Text className="text-muted-foreground" style={styles.subtitle}>{subtitle}</Text>
|
|
176
195
|
</View>
|
|
177
196
|
|
|
178
|
-
{
|
|
179
|
-
<View style={styles.
|
|
180
|
-
<
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
<Button variant="primary" onPress={retry} style={styles.primaryButton}>Try Again</Button>
|
|
189
|
-
</View>
|
|
197
|
+
{showChooser ? (
|
|
198
|
+
<Animated.View key="chooser" entering={FadeIn.duration(180)} style={styles.phase}>
|
|
199
|
+
<SignInAccountChooser
|
|
200
|
+
accounts={accounts}
|
|
201
|
+
onSelectAccount={handleSelectAccount}
|
|
202
|
+
onUseAnother={() => setUseAnother(true)}
|
|
203
|
+
pendingAccountId={switchingId}
|
|
204
|
+
disabled={switchingId !== null}
|
|
205
|
+
/>
|
|
206
|
+
</Animated.View>
|
|
190
207
|
) : (
|
|
191
|
-
|
|
192
|
-
{/*
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
{
|
|
208
|
+
<Animated.View key="signIn" entering={FadeIn.duration(180)} style={styles.phase}>
|
|
209
|
+
{/* Back to the chooser when accounts exist. */}
|
|
210
|
+
{accounts.length > 0 && (
|
|
211
|
+
<TouchableOpacity
|
|
212
|
+
onPress={() => setUseAnother(false)}
|
|
213
|
+
accessibilityRole="button"
|
|
214
|
+
style={styles.backRow}
|
|
215
|
+
>
|
|
216
|
+
<Ionicons name="chevron-back" size={18} color={theme.colors.textSecondary} />
|
|
217
|
+
<Text style={[styles.backText, { color: theme.colors.textSecondary }]}>{t('signin.chooser.title')}</Text>
|
|
218
|
+
</TouchableOpacity>
|
|
219
|
+
)}
|
|
220
|
+
|
|
221
|
+
{/* PRIMARY — first-party password sign-in. Always usable; the
|
|
222
|
+
device-flow loading/error state below never gates it. */}
|
|
223
|
+
<View style={styles.form}>
|
|
224
|
+
{pw.step === 'identifier' && (
|
|
225
|
+
<TextInput
|
|
226
|
+
style={[styles.input, { borderColor: theme.colors.border, color: theme.colors.text, backgroundColor: theme.colors.backgroundSecondary }]}
|
|
227
|
+
value={pw.identifier}
|
|
228
|
+
onChangeText={pw.setIdentifier}
|
|
229
|
+
onSubmitEditing={pw.submitIdentifier}
|
|
230
|
+
placeholder="Username or email"
|
|
231
|
+
placeholderTextColor={theme.colors.textSecondary}
|
|
232
|
+
autoCapitalize="none"
|
|
233
|
+
autoCorrect={false}
|
|
234
|
+
keyboardType="email-address"
|
|
235
|
+
returnKeyType="next"
|
|
236
|
+
accessibilityLabel="Username or email"
|
|
237
|
+
/>
|
|
238
|
+
)}
|
|
239
|
+
|
|
240
|
+
{pw.step === 'password' && (
|
|
241
|
+
<>
|
|
242
|
+
<Text style={[styles.contextText, { color: theme.colors.textSecondary }]}>{pw.identifier}</Text>
|
|
243
|
+
<TextInput
|
|
244
|
+
style={[styles.input, { borderColor: theme.colors.border, color: theme.colors.text, backgroundColor: theme.colors.backgroundSecondary }]}
|
|
245
|
+
value={pw.password}
|
|
246
|
+
onChangeText={pw.setPassword}
|
|
247
|
+
onSubmitEditing={pw.submitPassword}
|
|
248
|
+
placeholder="Password"
|
|
249
|
+
placeholderTextColor={theme.colors.textSecondary}
|
|
250
|
+
secureTextEntry
|
|
251
|
+
autoCapitalize="none"
|
|
252
|
+
autoCorrect={false}
|
|
253
|
+
returnKeyType="go"
|
|
254
|
+
accessibilityLabel="Password"
|
|
255
|
+
/>
|
|
256
|
+
</>
|
|
257
|
+
)}
|
|
258
|
+
|
|
259
|
+
{pw.step === 'twoFactor' && (
|
|
260
|
+
<TextInput
|
|
261
|
+
style={[styles.input, { borderColor: theme.colors.border, color: theme.colors.text, backgroundColor: theme.colors.backgroundSecondary }]}
|
|
262
|
+
value={pw.code}
|
|
263
|
+
onChangeText={pw.setCode}
|
|
264
|
+
onSubmitEditing={pw.submitTwoFactor}
|
|
265
|
+
placeholder={pw.useBackupCode ? 'Backup code' : '6-digit code'}
|
|
266
|
+
placeholderTextColor={theme.colors.textSecondary}
|
|
267
|
+
autoCapitalize="none"
|
|
268
|
+
autoCorrect={false}
|
|
269
|
+
keyboardType={pw.useBackupCode ? 'default' : 'number-pad'}
|
|
270
|
+
returnKeyType="go"
|
|
271
|
+
accessibilityLabel={pw.useBackupCode ? 'Backup code' : 'Two-factor code'}
|
|
272
|
+
/>
|
|
273
|
+
)}
|
|
274
|
+
|
|
207
275
|
<Button
|
|
208
|
-
variant="
|
|
209
|
-
onPress={
|
|
210
|
-
|
|
211
|
-
|
|
276
|
+
variant="primary"
|
|
277
|
+
onPress={
|
|
278
|
+
pw.step === 'identifier' ? pw.submitIdentifier
|
|
279
|
+
: pw.step === 'password' ? pw.submitPassword
|
|
280
|
+
: pw.submitTwoFactor
|
|
281
|
+
}
|
|
282
|
+
loading={pw.isSubmitting}
|
|
283
|
+
disabled={pw.isSubmitting}
|
|
284
|
+
style={styles.primaryButton}
|
|
212
285
|
>
|
|
213
|
-
Sign in
|
|
286
|
+
{pw.step === 'identifier' ? 'Continue' : pw.step === 'password' ? 'Sign in' : 'Verify'}
|
|
214
287
|
</Button>
|
|
215
|
-
)}
|
|
216
288
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
289
|
+
{pw.error && (
|
|
290
|
+
<Text className="text-destructive" style={styles.formError}>{pw.error}</Text>
|
|
291
|
+
)}
|
|
292
|
+
|
|
293
|
+
{pw.step === 'twoFactor' && (
|
|
294
|
+
<TouchableOpacity onPress={() => pw.setUseBackupCode(!pw.useBackupCode)} accessibilityRole="button" style={styles.linkButton}>
|
|
295
|
+
<Text style={[styles.linkText, { color: theme.colors.primary }]}>
|
|
296
|
+
{pw.useBackupCode ? 'Use authenticator code' : 'Use a backup code'}
|
|
297
|
+
</Text>
|
|
298
|
+
</TouchableOpacity>
|
|
299
|
+
)}
|
|
300
|
+
|
|
301
|
+
{pw.step !== 'identifier' && (
|
|
302
|
+
<TouchableOpacity onPress={pw.back} accessibilityRole="button" style={styles.linkButton}>
|
|
303
|
+
<Text style={[styles.linkText, { color: theme.colors.textSecondary }]}>Back</Text>
|
|
304
|
+
</TouchableOpacity>
|
|
305
|
+
)}
|
|
306
|
+
</View>
|
|
307
|
+
|
|
308
|
+
{/* "or" divider */}
|
|
309
|
+
<View style={styles.dividerRow}>
|
|
310
|
+
<View style={[styles.dividerLine, { backgroundColor: theme.colors.border }]} />
|
|
311
|
+
<Text style={[styles.dividerText, { color: theme.colors.textSecondary }]}>or</Text>
|
|
312
|
+
<View style={[styles.dividerLine, { backgroundColor: theme.colors.border }]} />
|
|
313
|
+
</View>
|
|
314
|
+
|
|
315
|
+
{/* SECONDARY — cross-app device flow. Its loading/error state gates
|
|
316
|
+
ONLY this section; the password form above is always usable. */}
|
|
317
|
+
{isLoading ? (
|
|
318
|
+
<View style={styles.deviceLoading}>
|
|
220
319
|
<Loading size="small" style={styles.statusSpinner} />
|
|
221
|
-
<Text style={[styles.statusText, { color: theme.colors.textSecondary }]}>
|
|
222
|
-
Waiting for authorization...
|
|
223
|
-
</Text>
|
|
320
|
+
<Text style={[styles.statusText, { color: theme.colors.textSecondary }]}>Preparing sign in…</Text>
|
|
224
321
|
</View>
|
|
225
|
-
)
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
the device-flow claim, which plants no `fedcm_session`. */}
|
|
230
|
-
{!crossApexWeb && (
|
|
231
|
-
<View style={styles.qrSection}>
|
|
232
|
-
<AnotherDeviceQR qrData={qrData} qrPayload={qrPayload} />
|
|
322
|
+
) : error ? (
|
|
323
|
+
<View style={styles.errorContainer}>
|
|
324
|
+
<Text className="text-destructive" style={styles.errorText}>{error}</Text>
|
|
325
|
+
<Button variant="secondary" onPress={retry} style={styles.secondaryButton}>Try Again</Button>
|
|
233
326
|
</View>
|
|
327
|
+
) : (
|
|
328
|
+
<>
|
|
329
|
+
{qrPayload && (
|
|
330
|
+
<Button
|
|
331
|
+
variant="secondary"
|
|
332
|
+
onPress={openSameDeviceApproval}
|
|
333
|
+
icon={<OxyLogo variant="icon" size={20} fillColor={theme.colors.text} style={styles.buttonIcon} />}
|
|
334
|
+
style={styles.secondaryButton}
|
|
335
|
+
>
|
|
336
|
+
Sign in with the Oxy app
|
|
337
|
+
</Button>
|
|
338
|
+
)}
|
|
339
|
+
|
|
340
|
+
{isWaiting && (
|
|
341
|
+
<View style={styles.statusContainer}>
|
|
342
|
+
<Loading size="small" style={styles.statusSpinner} />
|
|
343
|
+
<Text style={[styles.statusText, { color: theme.colors.textSecondary }]}>Waiting for authorization…</Text>
|
|
344
|
+
</View>
|
|
345
|
+
)}
|
|
346
|
+
|
|
347
|
+
<View style={styles.qrSection}>
|
|
348
|
+
<AnotherDeviceQR qrData={qrData} qrPayload={qrPayload} />
|
|
349
|
+
</View>
|
|
350
|
+
</>
|
|
234
351
|
)}
|
|
235
|
-
|
|
352
|
+
</Animated.View>
|
|
236
353
|
)}
|
|
237
354
|
|
|
238
355
|
{/* Footer — create an account */}
|
|
@@ -241,9 +358,7 @@ const SignInModalContent: React.FC<SignInModalContentProps> = ({
|
|
|
241
358
|
Don't have an Oxy account?{' '}
|
|
242
359
|
</Text>
|
|
243
360
|
<TouchableOpacity onPress={handleCreateAccount} accessibilityRole="link">
|
|
244
|
-
<Text style={styles.footerLink} className="text-primary">
|
|
245
|
-
Create one
|
|
246
|
-
</Text>
|
|
361
|
+
<Text style={styles.footerLink} className="text-primary">Create one</Text>
|
|
247
362
|
</TouchableOpacity>
|
|
248
363
|
</View>
|
|
249
364
|
</Animated.View>
|
|
@@ -260,42 +375,54 @@ const styles = StyleSheet.create({
|
|
|
260
375
|
},
|
|
261
376
|
card: {
|
|
262
377
|
width: '100%',
|
|
263
|
-
maxWidth:
|
|
378
|
+
maxWidth: 420,
|
|
264
379
|
alignItems: 'center',
|
|
265
380
|
paddingHorizontal: 24,
|
|
266
|
-
borderRadius:
|
|
381
|
+
borderRadius: 28,
|
|
267
382
|
},
|
|
268
383
|
closeButton: {
|
|
269
384
|
position: 'absolute',
|
|
270
385
|
top: 16,
|
|
271
386
|
right: 16,
|
|
272
|
-
width:
|
|
273
|
-
height:
|
|
274
|
-
borderRadius:
|
|
387
|
+
width: 36,
|
|
388
|
+
height: 36,
|
|
389
|
+
borderRadius: 18,
|
|
275
390
|
justifyContent: 'center',
|
|
276
391
|
alignItems: 'center',
|
|
277
392
|
zIndex: 10,
|
|
278
393
|
},
|
|
279
|
-
closeButtonText: {
|
|
280
|
-
fontSize: 28,
|
|
281
|
-
fontWeight: '300',
|
|
282
|
-
lineHeight: 32,
|
|
283
|
-
},
|
|
284
394
|
header: {
|
|
285
395
|
alignItems: 'center',
|
|
286
|
-
marginBottom:
|
|
396
|
+
marginBottom: 24,
|
|
287
397
|
},
|
|
288
398
|
title: {
|
|
289
399
|
fontSize: 26,
|
|
290
|
-
fontWeight: '
|
|
400
|
+
fontWeight: '800',
|
|
401
|
+
letterSpacing: -0.5,
|
|
291
402
|
marginTop: 16,
|
|
292
403
|
textAlign: 'center',
|
|
293
404
|
},
|
|
294
405
|
subtitle: {
|
|
295
406
|
fontSize: 15,
|
|
407
|
+
lineHeight: 21,
|
|
296
408
|
marginTop: 8,
|
|
297
409
|
textAlign: 'center',
|
|
298
410
|
},
|
|
411
|
+
phase: {
|
|
412
|
+
width: '100%',
|
|
413
|
+
},
|
|
414
|
+
backRow: {
|
|
415
|
+
flexDirection: 'row',
|
|
416
|
+
alignItems: 'center',
|
|
417
|
+
gap: 4,
|
|
418
|
+
alignSelf: 'flex-start',
|
|
419
|
+
paddingVertical: 6,
|
|
420
|
+
marginBottom: 8,
|
|
421
|
+
},
|
|
422
|
+
backText: {
|
|
423
|
+
fontSize: 14,
|
|
424
|
+
fontWeight: '600',
|
|
425
|
+
},
|
|
299
426
|
primaryButton: {
|
|
300
427
|
width: '100%',
|
|
301
428
|
borderRadius: 12,
|
|
@@ -308,6 +435,57 @@ const styles = StyleSheet.create({
|
|
|
308
435
|
buttonIcon: {
|
|
309
436
|
marginRight: 10,
|
|
310
437
|
},
|
|
438
|
+
form: {
|
|
439
|
+
width: '100%',
|
|
440
|
+
},
|
|
441
|
+
input: {
|
|
442
|
+
width: '100%',
|
|
443
|
+
borderWidth: 1,
|
|
444
|
+
borderRadius: 12,
|
|
445
|
+
paddingHorizontal: 16,
|
|
446
|
+
paddingVertical: 13,
|
|
447
|
+
fontSize: 15,
|
|
448
|
+
marginBottom: 12,
|
|
449
|
+
},
|
|
450
|
+
contextText: {
|
|
451
|
+
fontSize: 14,
|
|
452
|
+
textAlign: 'center',
|
|
453
|
+
marginBottom: 12,
|
|
454
|
+
},
|
|
455
|
+
formError: {
|
|
456
|
+
fontSize: 14,
|
|
457
|
+
textAlign: 'center',
|
|
458
|
+
marginTop: 12,
|
|
459
|
+
},
|
|
460
|
+
linkButton: {
|
|
461
|
+
alignSelf: 'center',
|
|
462
|
+
paddingVertical: 8,
|
|
463
|
+
marginTop: 8,
|
|
464
|
+
},
|
|
465
|
+
linkText: {
|
|
466
|
+
fontSize: 14,
|
|
467
|
+
fontWeight: '600',
|
|
468
|
+
},
|
|
469
|
+
dividerRow: {
|
|
470
|
+
flexDirection: 'row',
|
|
471
|
+
alignItems: 'center',
|
|
472
|
+
width: '100%',
|
|
473
|
+
marginVertical: 20,
|
|
474
|
+
},
|
|
475
|
+
dividerLine: {
|
|
476
|
+
flex: 1,
|
|
477
|
+
height: 1,
|
|
478
|
+
},
|
|
479
|
+
dividerText: {
|
|
480
|
+
marginHorizontal: 12,
|
|
481
|
+
fontSize: 13,
|
|
482
|
+
},
|
|
483
|
+
deviceLoading: {
|
|
484
|
+
flexDirection: 'row',
|
|
485
|
+
alignItems: 'center',
|
|
486
|
+
justifyContent: 'center',
|
|
487
|
+
paddingVertical: 12,
|
|
488
|
+
},
|
|
311
489
|
statusContainer: {
|
|
312
490
|
flexDirection: 'row',
|
|
313
491
|
alignItems: 'center',
|
|
@@ -324,14 +502,6 @@ const styles = StyleSheet.create({
|
|
|
324
502
|
width: '100%',
|
|
325
503
|
marginTop: 24,
|
|
326
504
|
},
|
|
327
|
-
loadingContainer: {
|
|
328
|
-
alignItems: 'center',
|
|
329
|
-
paddingVertical: 40,
|
|
330
|
-
},
|
|
331
|
-
loadingText: {
|
|
332
|
-
marginTop: 16,
|
|
333
|
-
fontSize: 14,
|
|
334
|
-
},
|
|
335
505
|
errorContainer: {
|
|
336
506
|
alignItems: 'center',
|
|
337
507
|
paddingVertical: 20,
|