@oxy.so/services 12.3.0 → 12.5.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/README.md +1 -1
- package/lib/commonjs/index.js +17 -2
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/types.js +14 -8
- package/lib/commonjs/ui/components/authChooser/types.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/OxyAuthenticatorPanel.js +11 -10
- package/lib/commonjs/ui/components/signIn/OxyAuthenticatorPanel.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/OxyDeleteAccountPanel.js +19 -18
- package/lib/commonjs/ui/components/signIn/OxyDeleteAccountPanel.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/OxyEmailPanel.js +251 -0
- package/lib/commonjs/ui/components/signIn/OxyEmailPanel.js.map +1 -0
- package/lib/commonjs/ui/components/signIn/OxyLinkCommonsPanel.js +40 -33
- package/lib/commonjs/ui/components/signIn/OxyLinkCommonsPanel.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/OxyPasswordPanel.js +51 -19
- package/lib/commonjs/ui/components/signIn/OxyPasswordPanel.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/OxySignInSecurityPanel.js +154 -0
- package/lib/commonjs/ui/components/signIn/OxySignInSecurityPanel.js.map +1 -0
- package/lib/commonjs/ui/components/signIn/ReauthStep.js +6 -4
- package/lib/commonjs/ui/components/signIn/ReauthStep.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/SessionsSection.js +200 -0
- package/lib/commonjs/ui/components/signIn/SessionsSection.js.map +1 -0
- package/lib/commonjs/ui/components/signIn/accountFlowParts.js +6 -0
- package/lib/commonjs/ui/components/signIn/accountFlowParts.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/accountSecurityState.js +74 -0
- package/lib/commonjs/ui/components/signIn/accountSecurityState.js.map +1 -0
- package/lib/commonjs/ui/navigation/routes.js +1 -1
- package/lib/commonjs/ui/navigation/routes.js.map +1 -1
- package/lib/commonjs/ui/navigation/screenComponents.js +4 -0
- package/lib/commonjs/ui/navigation/screenComponents.js.map +1 -1
- package/lib/commonjs/ui/navigation/surfaceRegistry.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountSecurityPanelScreen.js +1 -1
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js +3 -12
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/CreateAccountScreen.js +3 -12
- package/lib/commonjs/ui/screens/CreateAccountScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/EditProfileFieldScreen.js +7 -3
- package/lib/commonjs/ui/screens/EditProfileFieldScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/ManageAccountScreen.js +8 -196
- package/lib/commonjs/ui/screens/ManageAccountScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/SignInEmailScreen.js +19 -0
- package/lib/commonjs/ui/screens/SignInEmailScreen.js.map +1 -0
- package/lib/commonjs/ui/screens/SignInSecurityScreen.js +30 -0
- package/lib/commonjs/ui/screens/SignInSecurityScreen.js.map +1 -0
- package/lib/module/index.js +5 -2
- package/lib/module/index.js.map +1 -1
- package/lib/module/ui/components/authChooser/types.js +15 -8
- package/lib/module/ui/components/authChooser/types.js.map +1 -1
- package/lib/module/ui/components/signIn/OxyAuthenticatorPanel.js +12 -11
- package/lib/module/ui/components/signIn/OxyAuthenticatorPanel.js.map +1 -1
- package/lib/module/ui/components/signIn/OxyDeleteAccountPanel.js +20 -19
- package/lib/module/ui/components/signIn/OxyDeleteAccountPanel.js.map +1 -1
- package/lib/module/ui/components/signIn/OxyEmailPanel.js +246 -0
- package/lib/module/ui/components/signIn/OxyEmailPanel.js.map +1 -0
- package/lib/module/ui/components/signIn/OxyLinkCommonsPanel.js +40 -33
- package/lib/module/ui/components/signIn/OxyLinkCommonsPanel.js.map +1 -1
- package/lib/module/ui/components/signIn/OxyPasswordPanel.js +52 -20
- package/lib/module/ui/components/signIn/OxyPasswordPanel.js.map +1 -1
- package/lib/module/ui/components/signIn/OxySignInSecurityPanel.js +149 -0
- package/lib/module/ui/components/signIn/OxySignInSecurityPanel.js.map +1 -0
- package/lib/module/ui/components/signIn/ReauthStep.js +6 -4
- package/lib/module/ui/components/signIn/ReauthStep.js.map +1 -1
- package/lib/module/ui/components/signIn/SessionsSection.js +194 -0
- package/lib/module/ui/components/signIn/SessionsSection.js.map +1 -0
- package/lib/module/ui/components/signIn/accountFlowParts.js +6 -0
- package/lib/module/ui/components/signIn/accountFlowParts.js.map +1 -1
- package/lib/module/ui/components/signIn/accountSecurityState.js +69 -0
- package/lib/module/ui/components/signIn/accountSecurityState.js.map +1 -0
- package/lib/module/ui/navigation/routes.js +1 -1
- package/lib/module/ui/navigation/routes.js.map +1 -1
- package/lib/module/ui/navigation/screenComponents.js +4 -0
- package/lib/module/ui/navigation/screenComponents.js.map +1 -1
- package/lib/module/ui/navigation/surfaceRegistry.js.map +1 -1
- package/lib/module/ui/screens/AccountSecurityPanelScreen.js +1 -1
- package/lib/module/ui/screens/AccountSettingsScreen.js +4 -13
- package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/module/ui/screens/CreateAccountScreen.js +3 -13
- package/lib/module/ui/screens/CreateAccountScreen.js.map +1 -1
- package/lib/module/ui/screens/EditProfileFieldScreen.js +8 -4
- package/lib/module/ui/screens/EditProfileFieldScreen.js.map +1 -1
- package/lib/module/ui/screens/ManageAccountScreen.js +9 -197
- package/lib/module/ui/screens/ManageAccountScreen.js.map +1 -1
- package/lib/module/ui/screens/SignInEmailScreen.js +17 -0
- package/lib/module/ui/screens/SignInEmailScreen.js.map +1 -0
- package/lib/module/ui/screens/SignInSecurityScreen.js +26 -0
- package/lib/module/ui/screens/SignInSecurityScreen.js.map +1 -0
- package/lib/typescript/commonjs/index.d.ts +4 -0
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/types.d.ts +1 -3
- package/lib/typescript/commonjs/ui/components/authChooser/types.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 +3 -2
- package/lib/typescript/commonjs/ui/components/signIn/OxyDeleteAccountPanel.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/signIn/OxyEmailPanel.d.ts +21 -0
- package/lib/typescript/commonjs/ui/components/signIn/OxyEmailPanel.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/signIn/OxyLinkCommonsPanel.d.ts +2 -1
- package/lib/typescript/commonjs/ui/components/signIn/OxyLinkCommonsPanel.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/signIn/OxyPasswordPanel.d.ts +5 -4
- package/lib/typescript/commonjs/ui/components/signIn/OxyPasswordPanel.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/signIn/OxySignInSecurityPanel.d.ts +25 -0
- package/lib/typescript/commonjs/ui/components/signIn/OxySignInSecurityPanel.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/signIn/ReauthStep.d.ts +8 -2
- package/lib/typescript/commonjs/ui/components/signIn/ReauthStep.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/signIn/SessionsSection.d.ts +9 -0
- package/lib/typescript/commonjs/ui/components/signIn/SessionsSection.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/signIn/accountFlowParts.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/signIn/accountSecurityState.d.ts +26 -0
- package/lib/typescript/commonjs/ui/components/signIn/accountSecurityState.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts +3 -2
- package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.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/navigation/screenComponents.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/navigation/surfaceRegistry.d.ts +2 -0
- package/lib/typescript/commonjs/ui/navigation/surfaceRegistry.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/AccountSecurityPanelScreen.d.ts +1 -1
- package/lib/typescript/commonjs/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/CreateAccountScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/EditProfileFieldScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/ManageAccountScreen.d.ts +3 -2
- package/lib/typescript/commonjs/ui/screens/ManageAccountScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/SignInEmailScreen.d.ts +6 -0
- package/lib/typescript/commonjs/ui/screens/SignInEmailScreen.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/screens/SignInSecurityScreen.d.ts +6 -0
- package/lib/typescript/commonjs/ui/screens/SignInSecurityScreen.d.ts.map +1 -0
- package/lib/typescript/module/index.d.ts +4 -0
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/types.d.ts +1 -3
- package/lib/typescript/module/ui/components/authChooser/types.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 +3 -2
- package/lib/typescript/module/ui/components/signIn/OxyDeleteAccountPanel.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/signIn/OxyEmailPanel.d.ts +21 -0
- package/lib/typescript/module/ui/components/signIn/OxyEmailPanel.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/signIn/OxyLinkCommonsPanel.d.ts +2 -1
- package/lib/typescript/module/ui/components/signIn/OxyLinkCommonsPanel.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/signIn/OxyPasswordPanel.d.ts +5 -4
- package/lib/typescript/module/ui/components/signIn/OxyPasswordPanel.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/signIn/OxySignInSecurityPanel.d.ts +25 -0
- package/lib/typescript/module/ui/components/signIn/OxySignInSecurityPanel.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/signIn/ReauthStep.d.ts +8 -2
- package/lib/typescript/module/ui/components/signIn/ReauthStep.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/signIn/SessionsSection.d.ts +9 -0
- package/lib/typescript/module/ui/components/signIn/SessionsSection.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/signIn/accountFlowParts.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/signIn/accountSecurityState.d.ts +26 -0
- package/lib/typescript/module/ui/components/signIn/accountSecurityState.d.ts.map +1 -0
- package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts +3 -2
- package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.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/navigation/screenComponents.d.ts.map +1 -1
- package/lib/typescript/module/ui/navigation/surfaceRegistry.d.ts +2 -0
- package/lib/typescript/module/ui/navigation/surfaceRegistry.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/AccountSecurityPanelScreen.d.ts +1 -1
- package/lib/typescript/module/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/CreateAccountScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/EditProfileFieldScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/ManageAccountScreen.d.ts +3 -2
- package/lib/typescript/module/ui/screens/ManageAccountScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/SignInEmailScreen.d.ts +6 -0
- package/lib/typescript/module/ui/screens/SignInEmailScreen.d.ts.map +1 -0
- package/lib/typescript/module/ui/screens/SignInSecurityScreen.d.ts +6 -0
- package/lib/typescript/module/ui/screens/SignInSecurityScreen.d.ts.map +1 -0
- package/package.json +6 -6
- package/src/index.ts +7 -2
- package/src/ui/components/authChooser/__tests__/profileColors.test.ts +13 -0
- package/src/ui/components/authChooser/types.ts +16 -15
- package/src/ui/components/signIn/OxyAuthenticatorPanel.tsx +11 -8
- package/src/ui/components/signIn/OxyDeleteAccountPanel.tsx +15 -16
- package/src/ui/components/signIn/OxyEmailPanel.tsx +258 -0
- package/src/ui/components/signIn/OxyLinkCommonsPanel.tsx +27 -15
- package/src/ui/components/signIn/OxyPasswordPanel.tsx +49 -14
- package/src/ui/components/signIn/OxySignInSecurityPanel.tsx +147 -0
- package/src/ui/components/signIn/ReauthStep.tsx +13 -4
- package/src/ui/components/signIn/SessionsSection.tsx +218 -0
- package/src/ui/components/signIn/accountFlowParts.tsx +6 -0
- package/src/ui/components/signIn/accountSecurityState.tsx +75 -0
- package/src/ui/navigation/routes.ts +5 -1
- package/src/ui/navigation/screenComponents.ts +4 -0
- package/src/ui/navigation/surfaceRegistry.ts +2 -0
- package/src/ui/screens/AccountSecurityPanelScreen.tsx +1 -1
- package/src/ui/screens/AccountSettingsScreen.tsx +10 -29
- package/src/ui/screens/CreateAccountScreen.tsx +9 -27
- package/src/ui/screens/EditProfileFieldScreen.tsx +10 -8
- package/src/ui/screens/ManageAccountScreen.tsx +8 -241
- package/src/ui/screens/SignInEmailScreen.tsx +13 -0
- package/src/ui/screens/SignInSecurityScreen.tsx +19 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The frame the account's own security panels render in when presented as a
|
|
3
|
-
* surface (`showBottomSheet('DeleteAccount' | 'LinkCommons' | 'SignInPassword'
|
|
3
|
+
* surface (`showBottomSheet('DeleteAccount' | 'LinkCommons' | 'SignInEmail' | 'SignInPassword'
|
|
4
4
|
* | 'SignInAuthenticator')`): the panel draws its own large header, so the bar
|
|
5
5
|
* holds only the way back, and the body keeps the dialog's screen gutter.
|
|
6
6
|
*/
|
|
@@ -1,9 +1,4 @@
|
|
|
1
1
|
import type React from 'react';
|
|
2
|
-
import {
|
|
3
|
-
ORGANIZATION_NAME_MAX_LENGTH,
|
|
4
|
-
organizationNameCheck,
|
|
5
|
-
checkContract,
|
|
6
|
-
} from '@oxy.so/contracts/client';
|
|
7
2
|
import { OrganizationSettings } from '../components/organizations/OrganizationSettings';
|
|
8
3
|
import { useCallback, useMemo, useState } from 'react';
|
|
9
4
|
import { View } from 'react-native';
|
|
@@ -16,10 +11,11 @@ import { Button } from '@oxy.so/bloom/button';
|
|
|
16
11
|
import { TextField, TextFieldInput } from '@oxy.so/bloom/text-field';
|
|
17
12
|
import { GroupedSection, GroupedSectionItem } from '@oxy.so/bloom/grouped-section';
|
|
18
13
|
import {
|
|
19
|
-
|
|
14
|
+
accountNameInvalidKey,
|
|
15
|
+
accountNameInvalidMessage,
|
|
16
|
+
accountNameMaxLength,
|
|
20
17
|
getNormalizedUserHandle,
|
|
21
|
-
|
|
22
|
-
MAX_DISPLAY_NAME_LENGTH,
|
|
18
|
+
isValidAccountName,
|
|
23
19
|
type UpdateAccountInput,
|
|
24
20
|
} from '@oxy.so/core';
|
|
25
21
|
import type { BaseScreenProps } from '../types/navigation';
|
|
@@ -29,7 +25,6 @@ import { useI18n } from '../hooks/useI18n';
|
|
|
29
25
|
import { useSurfaceHeader } from '../hooks/useSurfaceHeader';
|
|
30
26
|
import { clearedFieldsFromAccountUpdate, upsertCachedUser } from '../hooks/queries/userCache';
|
|
31
27
|
|
|
32
|
-
const DISPLAY_NAME_MAX = MAX_DISPLAY_NAME_LENGTH;
|
|
33
28
|
const BIO_MAX = 160;
|
|
34
29
|
|
|
35
30
|
const errorMessage = (error: unknown, fallback: string): string =>
|
|
@@ -158,22 +153,12 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
158
153
|
setDisplayNameError('');
|
|
159
154
|
return;
|
|
160
155
|
}
|
|
161
|
-
|
|
162
|
-
setDisplayNameError(
|
|
163
|
-
checkContract(organizationNameCheck, trimmed) !== null
|
|
164
|
-
? ''
|
|
165
|
-
: t('organizationDirectory.invalidName'),
|
|
166
|
-
);
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
const nameParts = trimmed.split(/\s+/).filter(Boolean);
|
|
170
|
-
const first = nameParts[0] || '';
|
|
171
|
-
const last = nameParts.length > 1 ? nameParts.slice(1).join(' ') : '';
|
|
172
|
-
const invalidPart = [first, last].find((part) => part && !isValidDisplayName(part));
|
|
156
|
+
const kind = node?.account.kind;
|
|
173
157
|
setDisplayNameError(
|
|
174
|
-
|
|
175
|
-
?
|
|
176
|
-
:
|
|
158
|
+
isValidAccountName(trimmed, kind)
|
|
159
|
+
? ''
|
|
160
|
+
: t(`accounts.settings.displayName.${accountNameInvalidKey(kind)}`) ||
|
|
161
|
+
accountNameInvalidMessage(kind),
|
|
177
162
|
);
|
|
178
163
|
},
|
|
179
164
|
[node?.account.kind, t],
|
|
@@ -235,11 +220,7 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
235
220
|
value={displayName}
|
|
236
221
|
onChangeText={handleDisplayNameChange}
|
|
237
222
|
isInvalid={Boolean(displayNameError)}
|
|
238
|
-
maxLength={
|
|
239
|
-
node?.account.kind === 'organization'
|
|
240
|
-
? ORGANIZATION_NAME_MAX_LENGTH
|
|
241
|
-
: DISPLAY_NAME_MAX
|
|
242
|
-
}
|
|
223
|
+
maxLength={accountNameMaxLength(node?.account.kind)}
|
|
243
224
|
/>
|
|
244
225
|
</TextField>
|
|
245
226
|
{displayNameError ? (
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import type React from 'react';
|
|
2
2
|
import type { OrganizationType } from '@oxy.so/contracts';
|
|
3
|
-
import {
|
|
4
|
-
ORGANIZATION_NAME_MAX_LENGTH,
|
|
5
|
-
organizationNameCheck,
|
|
6
|
-
checkContract,
|
|
7
|
-
} from '@oxy.so/contracts/client';
|
|
8
3
|
import { OrganizationDirectorySearch } from '../components/organizations/OrganizationDirectorySearch';
|
|
9
4
|
import { OrganizationTypePicker } from '../components/organizations/OrganizationTypePicker';
|
|
10
5
|
import { useState, useCallback, useRef, useEffect } from 'react';
|
|
@@ -13,10 +8,11 @@ import Ionicons from '../icons/Ionicons';
|
|
|
13
8
|
import type { AccountCategoryId, AccountKind, CreateAccountInput } from '@oxy.so/core';
|
|
14
9
|
import {
|
|
15
10
|
accountCategoryLabel,
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
accountNameInvalidKey,
|
|
12
|
+
accountNameInvalidMessage,
|
|
13
|
+
accountNameMaxLength,
|
|
14
|
+
isValidAccountName,
|
|
18
15
|
MAX_ACCOUNT_CATEGORIES,
|
|
19
|
-
MAX_DISPLAY_NAME_LENGTH,
|
|
20
16
|
SELECTABLE_ACCOUNT_CATEGORY_IDS,
|
|
21
17
|
} from '@oxy.so/core';
|
|
22
18
|
import {
|
|
@@ -61,7 +57,6 @@ type UsernameStatus = 'idle' | 'checking' | 'available' | 'taken' | 'invalid';
|
|
|
61
57
|
type CreatableAccountKind = Extract<AccountKind, 'organization' | 'project' | 'bot'>;
|
|
62
58
|
|
|
63
59
|
const DEBOUNCE_MS = 400;
|
|
64
|
-
const DISPLAY_NAME_MAX = MAX_DISPLAY_NAME_LENGTH;
|
|
65
60
|
const BIO_MAX = 160;
|
|
66
61
|
|
|
67
62
|
interface KindOption {
|
|
@@ -286,22 +281,11 @@ const CreateAccountScreen: React.FC<BaseScreenProps> = ({
|
|
|
286
281
|
setDisplayNameError('');
|
|
287
282
|
return;
|
|
288
283
|
}
|
|
289
|
-
if (kind === 'organization') {
|
|
290
|
-
setDisplayNameError(
|
|
291
|
-
checkContract(organizationNameCheck, trimmed) !== null
|
|
292
|
-
? ''
|
|
293
|
-
: t('organizationDirectory.invalidName'),
|
|
294
|
-
);
|
|
295
|
-
return;
|
|
296
|
-
}
|
|
297
|
-
const nameParts = trimmed.split(/\s+/);
|
|
298
|
-
const firstName = nameParts[0] || '';
|
|
299
|
-
const lastName = nameParts.length > 1 ? nameParts.slice(1).join(' ') : '';
|
|
300
|
-
const invalidPart = [firstName, lastName].find((part) => part && !isValidDisplayName(part));
|
|
301
284
|
setDisplayNameError(
|
|
302
|
-
|
|
303
|
-
?
|
|
304
|
-
:
|
|
285
|
+
isValidAccountName(trimmed, kind)
|
|
286
|
+
? ''
|
|
287
|
+
: t(`accounts.create.displayName.${accountNameInvalidKey(kind)}`) ||
|
|
288
|
+
accountNameInvalidMessage(kind),
|
|
305
289
|
);
|
|
306
290
|
},
|
|
307
291
|
[kind, t],
|
|
@@ -548,9 +532,7 @@ const CreateAccountScreen: React.FC<BaseScreenProps> = ({
|
|
|
548
532
|
value={displayName}
|
|
549
533
|
onChangeText={handleDisplayNameChange}
|
|
550
534
|
isInvalid={Boolean(displayNameError)}
|
|
551
|
-
maxLength={
|
|
552
|
-
kind === 'organization' ? ORGANIZATION_NAME_MAX_LENGTH : DISPLAY_NAME_MAX
|
|
553
|
-
}
|
|
535
|
+
maxLength={accountNameMaxLength(kind)}
|
|
554
536
|
/>
|
|
555
537
|
</TextField>
|
|
556
538
|
{displayNameError ? (
|
|
@@ -22,7 +22,7 @@ import { useSurfaceHeader } from '../hooks/useSurfaceHeader';
|
|
|
22
22
|
import { useOxy } from '../context/oxyRuntimeContext';
|
|
23
23
|
import { useProfileEditing } from '../hooks/useProfileEditing';
|
|
24
24
|
import { toast } from '@oxy.so/bloom/toast';
|
|
25
|
-
import {
|
|
25
|
+
import { accountNameInvalidKey, accountNameInvalidMessage, isValidAccountName } from '@oxy.so/core';
|
|
26
26
|
import { getLinkTitle, getLinkDescription, linksToListItems } from './linkFormat';
|
|
27
27
|
|
|
28
28
|
/**
|
|
@@ -217,6 +217,12 @@ const EditProfileFieldScreen: React.FC<EditProfileFieldScreenProps> = ({
|
|
|
217
217
|
fieldType === 'username',
|
|
218
218
|
);
|
|
219
219
|
|
|
220
|
+
// The name policy is the account's kind's: an organization a person switched
|
|
221
|
+
// into is named by the title policy, not held to a person's.
|
|
222
|
+
const kind = user?.kind;
|
|
223
|
+
const invalidNameMessage =
|
|
224
|
+
t(`editProfile.items.displayName.${accountNameInvalidKey(kind)}`) ||
|
|
225
|
+
accountNameInvalidMessage(kind);
|
|
220
226
|
// Get field configuration based on fieldType
|
|
221
227
|
const fieldConfig = useMemo((): FieldConfig => {
|
|
222
228
|
switch (fieldType) {
|
|
@@ -233,9 +239,7 @@ const EditProfileFieldScreen: React.FC<EditProfileFieldScreenProps> = ({
|
|
|
233
239
|
placeholder:
|
|
234
240
|
t('editProfile.items.displayName.firstNamePlaceholder') || 'Enter first name',
|
|
235
241
|
validation: (value) =>
|
|
236
|
-
|
|
237
|
-
? undefined
|
|
238
|
-
: t('editProfile.items.displayName.invalidChars') || DISPLAY_NAME_INVALID_MESSAGE,
|
|
242
|
+
isValidAccountName(value, kind) ? undefined : invalidNameMessage,
|
|
239
243
|
},
|
|
240
244
|
{
|
|
241
245
|
key: 'lastName',
|
|
@@ -244,9 +248,7 @@ const EditProfileFieldScreen: React.FC<EditProfileFieldScreenProps> = ({
|
|
|
244
248
|
t('editProfile.items.displayName.lastNamePlaceholder') ||
|
|
245
249
|
'Enter last name (optional)',
|
|
246
250
|
validation: (value) =>
|
|
247
|
-
|
|
248
|
-
? undefined
|
|
249
|
-
: t('editProfile.items.displayName.invalidChars') || DISPLAY_NAME_INVALID_MESSAGE,
|
|
251
|
+
isValidAccountName(value, kind) ? undefined : invalidNameMessage,
|
|
250
252
|
},
|
|
251
253
|
],
|
|
252
254
|
};
|
|
@@ -360,7 +362,7 @@ const EditProfileFieldScreen: React.FC<EditProfileFieldScreenProps> = ({
|
|
|
360
362
|
fields: [],
|
|
361
363
|
};
|
|
362
364
|
}
|
|
363
|
-
}, [fieldType, t]);
|
|
365
|
+
}, [fieldType, t, kind, invalidNameMessage]);
|
|
364
366
|
|
|
365
367
|
// Field change handler
|
|
366
368
|
const handleFieldChange = useCallback(
|
|
@@ -4,7 +4,6 @@ import { SpinnerIcon } from '@oxy.so/bloom/loading';
|
|
|
4
4
|
import type React from 'react';
|
|
5
5
|
import { useCallback, useMemo, useState } from 'react';
|
|
6
6
|
import { View, StyleSheet, Linking, Platform } from 'react-native';
|
|
7
|
-
import Ionicons from '../icons/Ionicons';
|
|
8
7
|
import { toast } from '@oxy.so/bloom/toast';
|
|
9
8
|
import { surfaces } from '@oxy.so/bloom/surfaces';
|
|
10
9
|
import { useTheme } from '@oxy.so/bloom/theme';
|
|
@@ -16,7 +15,6 @@ import {
|
|
|
16
15
|
getNormalizedUserHandle,
|
|
17
16
|
logger as loggerUtil,
|
|
18
17
|
packageInfo,
|
|
19
|
-
type DeviceLinkedSession,
|
|
20
18
|
} from '@oxy.so/core';
|
|
21
19
|
import type { BaseScreenProps } from '../types/navigation';
|
|
22
20
|
import ProfileSummaryCard from '../components/ProfileSummaryCard';
|
|
@@ -29,46 +27,14 @@ import { useSurfaceHeader } from '../hooks/useSurfaceHeader';
|
|
|
29
27
|
import { isWebBrowser } from '../utils/isWebBrowser';
|
|
30
28
|
import { useCurrentUser } from '../hooks/queries/useAccountQueries';
|
|
31
29
|
import { useUserSubscription } from '../hooks/queries/usePaymentQueries';
|
|
32
|
-
import { useDeviceSessions } from '../hooks/queries/useServicesQueries';
|
|
33
|
-
import { deviceSessionTitle } from '../utils/deviceSessionTitle';
|
|
34
|
-
|
|
35
|
-
type DeviceSessionRow = DeviceLinkedSession;
|
|
36
|
-
|
|
37
|
-
const formatRelative = (dateString?: string): string => {
|
|
38
|
-
if (!dateString) {
|
|
39
|
-
return '—';
|
|
40
|
-
}
|
|
41
|
-
const date = new Date(dateString);
|
|
42
|
-
const now = new Date();
|
|
43
|
-
const diffMs = date.getTime() - now.getTime();
|
|
44
|
-
const absMin = Math.abs(diffMs) / 60000;
|
|
45
|
-
const isFuture = diffMs > 0;
|
|
46
|
-
if (absMin < 1) {
|
|
47
|
-
return isFuture ? 'in moments' : 'just now';
|
|
48
|
-
}
|
|
49
|
-
if (absMin < 60) {
|
|
50
|
-
const v = Math.floor(absMin);
|
|
51
|
-
return isFuture ? `in ${v}m` : `${v}m ago`;
|
|
52
|
-
}
|
|
53
|
-
const hrs = absMin / 60;
|
|
54
|
-
if (hrs < 24) {
|
|
55
|
-
const v = Math.floor(hrs);
|
|
56
|
-
return isFuture ? `in ${v}h` : `${v}h ago`;
|
|
57
|
-
}
|
|
58
|
-
const days = hrs / 24;
|
|
59
|
-
if (days < 7) {
|
|
60
|
-
const v = Math.floor(days);
|
|
61
|
-
return isFuture ? `in ${v}d` : `${v}d ago`;
|
|
62
|
-
}
|
|
63
|
-
return date.toLocaleDateString();
|
|
64
|
-
};
|
|
65
30
|
|
|
66
31
|
/**
|
|
67
32
|
* Unified "Manage your Oxy Account" screen.
|
|
68
33
|
*
|
|
69
34
|
* Replaces AccountOverview + AccountSettings + the per-account half of
|
|
70
|
-
* SessionManagement. Lists ONLY the active user's profile,
|
|
71
|
-
*
|
|
35
|
+
* SessionManagement. Lists ONLY the active user's profile, its sign-in and
|
|
36
|
+
* security page (`SignInSecurity`: methods and sessions), and the destructive
|
|
37
|
+
* actions for THIS account. Multi-account
|
|
72
38
|
* surface lives in the unified `OxyAccountDialogScreen` — keep these concerns separate.
|
|
73
39
|
*/
|
|
74
40
|
interface ManageAccountScreenProps extends BaseScreenProps {
|
|
@@ -91,7 +57,6 @@ const ManageAccountScreen: React.FC<ManageAccountScreenProps> = ({
|
|
|
91
57
|
user: contextUser,
|
|
92
58
|
isAuthenticated,
|
|
93
59
|
oxyServices,
|
|
94
|
-
activeSessionId,
|
|
95
60
|
logout,
|
|
96
61
|
openAvatarPicker,
|
|
97
62
|
accounts,
|
|
@@ -110,14 +75,6 @@ const ManageAccountScreen: React.FC<ManageAccountScreenProps> = ({
|
|
|
110
75
|
const user = userFromQuery ?? contextUser;
|
|
111
76
|
|
|
112
77
|
const { data: subscription } = useUserSubscription({ enabled: isAuthenticated });
|
|
113
|
-
const {
|
|
114
|
-
data: deviceSessions,
|
|
115
|
-
isLoading: deviceSessionsLoading,
|
|
116
|
-
refetch: refetchDeviceSessions,
|
|
117
|
-
} = useDeviceSessions({ enabled: isAuthenticated && !!activeSessionId });
|
|
118
|
-
|
|
119
|
-
const [removingDeviceId, setRemovingDeviceId] = useState<string | null>(null);
|
|
120
|
-
const [signingOutAllDevices, setSigningOutAllDevices] = useState(false);
|
|
121
78
|
const [signingOut, setSigningOut] = useState(false);
|
|
122
79
|
|
|
123
80
|
const displayName = useMemo(
|
|
@@ -159,91 +116,6 @@ const ManageAccountScreen: React.FC<ManageAccountScreenProps> = ({
|
|
|
159
116
|
}
|
|
160
117
|
}, [signingOut, logout, t, onClose]);
|
|
161
118
|
|
|
162
|
-
const confirmRemoveDevice = useCallback(
|
|
163
|
-
async (device: DeviceSessionRow) => {
|
|
164
|
-
if (!activeSessionId) {
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
const confirmed = await surfaces.confirm({
|
|
168
|
-
title: t('manageAccount.confirms.removeDeviceTitle') || 'Remove device',
|
|
169
|
-
description: t('manageAccount.confirms.removeDevice', {
|
|
170
|
-
name: deviceSessionTitle(device, t),
|
|
171
|
-
}),
|
|
172
|
-
confirmLabel: t('common.remove') || 'Remove',
|
|
173
|
-
cancelLabel: t('common.cancel') || 'Cancel',
|
|
174
|
-
destructive: true,
|
|
175
|
-
});
|
|
176
|
-
if (!confirmed) {
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
setRemovingDeviceId(device.sessionId);
|
|
180
|
-
try {
|
|
181
|
-
await oxyServices.session.logout(activeSessionId, device.sessionId);
|
|
182
|
-
await refetchDeviceSessions();
|
|
183
|
-
toast.success(
|
|
184
|
-
t('manageAccount.toasts.deviceRemoved', { name: deviceSessionTitle(device, t) }),
|
|
185
|
-
);
|
|
186
|
-
} catch (error) {
|
|
187
|
-
loggerUtil.warn(
|
|
188
|
-
'Remove device failed',
|
|
189
|
-
{ component: 'ManageAccountScreen' },
|
|
190
|
-
error as unknown,
|
|
191
|
-
);
|
|
192
|
-
toast.error(t('manageAccount.toasts.deviceRemoveFailed') || 'Failed to remove device');
|
|
193
|
-
} finally {
|
|
194
|
-
setRemovingDeviceId(null);
|
|
195
|
-
}
|
|
196
|
-
},
|
|
197
|
-
[activeSessionId, oxyServices, refetchDeviceSessions, t],
|
|
198
|
-
);
|
|
199
|
-
|
|
200
|
-
const handleSignOutAllDevices = useCallback(async () => {
|
|
201
|
-
if (!activeSessionId || signingOutAllDevices) {
|
|
202
|
-
return;
|
|
203
|
-
}
|
|
204
|
-
const otherDeviceCount = ((deviceSessions ?? []) as DeviceSessionRow[]).filter(
|
|
205
|
-
(device) => !device.isCurrent,
|
|
206
|
-
).length;
|
|
207
|
-
const confirmed = await surfaces.confirm({
|
|
208
|
-
title: t('manageAccount.confirms.signOutAllDevicesTitle') || 'Sign out of all other devices',
|
|
209
|
-
description:
|
|
210
|
-
t('manageAccount.confirms.signOutAllDevices', { count: otherDeviceCount }) ||
|
|
211
|
-
`End ${otherDeviceCount} other device session(s)? This won't sign you out here.`,
|
|
212
|
-
confirmLabel: t('common.actions.signOut') || 'Sign out',
|
|
213
|
-
cancelLabel: t('common.cancel') || 'Cancel',
|
|
214
|
-
destructive: true,
|
|
215
|
-
});
|
|
216
|
-
if (!confirmed) {
|
|
217
|
-
return;
|
|
218
|
-
}
|
|
219
|
-
setSigningOutAllDevices(true);
|
|
220
|
-
try {
|
|
221
|
-
await oxyServices.devices.logoutAll(activeSessionId);
|
|
222
|
-
await refetchDeviceSessions();
|
|
223
|
-
toast.success(
|
|
224
|
-
t('manageAccount.toasts.allDevicesSignedOut') || 'Signed out from all other devices',
|
|
225
|
-
);
|
|
226
|
-
} catch (error) {
|
|
227
|
-
loggerUtil.warn(
|
|
228
|
-
'Sign out all devices failed',
|
|
229
|
-
{ component: 'ManageAccountScreen' },
|
|
230
|
-
error as unknown,
|
|
231
|
-
);
|
|
232
|
-
toast.error(
|
|
233
|
-
t('manageAccount.toasts.allDevicesFailed') || 'Failed to sign out from other devices',
|
|
234
|
-
);
|
|
235
|
-
} finally {
|
|
236
|
-
setSigningOutAllDevices(false);
|
|
237
|
-
}
|
|
238
|
-
}, [
|
|
239
|
-
activeSessionId,
|
|
240
|
-
signingOutAllDevices,
|
|
241
|
-
deviceSessions,
|
|
242
|
-
oxyServices,
|
|
243
|
-
refetchDeviceSessions,
|
|
244
|
-
t,
|
|
245
|
-
]);
|
|
246
|
-
|
|
247
119
|
// The signed, open-format export (`GET /users/me/export`): one JSON bundle
|
|
248
120
|
// carrying Oxy's provenance attestation — the "credible exit" snapshot.
|
|
249
121
|
const handleDownloadData = useCallback(async () => {
|
|
@@ -347,9 +219,6 @@ const ManageAccountScreen: React.FC<ManageAccountScreenProps> = ({
|
|
|
347
219
|
);
|
|
348
220
|
}
|
|
349
221
|
|
|
350
|
-
const deviceRows: DeviceSessionRow[] = (deviceSessions ?? []) as DeviceSessionRow[];
|
|
351
|
-
const otherDevices = deviceRows.filter((d) => !d.isCurrent);
|
|
352
|
-
|
|
353
222
|
return (
|
|
354
223
|
<>
|
|
355
224
|
<View className="px-screen-margin pb-space-24">
|
|
@@ -432,117 +301,15 @@ const ManageAccountScreen: React.FC<ManageAccountScreenProps> = ({
|
|
|
432
301
|
</GroupedSection>
|
|
433
302
|
) : null}
|
|
434
303
|
|
|
435
|
-
{/*
|
|
436
|
-
<GroupedSection title={t('manageAccount.sections.sessions') || 'Sessions & devices'}>
|
|
437
|
-
{deviceSessionsLoading ? (
|
|
438
|
-
<GroupedSectionItem
|
|
439
|
-
icon={<SettingsIcon name="sync" color={bloomTheme.colors.primary} />}
|
|
440
|
-
title={t('manageAccount.sessions.loading') || 'Loading sessions…'}
|
|
441
|
-
rightElement={null}
|
|
442
|
-
showChevron={false}
|
|
443
|
-
disabled
|
|
444
|
-
/>
|
|
445
|
-
) : deviceRows.length === 0 ? (
|
|
446
|
-
<GroupedSectionItem
|
|
447
|
-
icon={<SettingsIcon name="cellphone" color={bloomTheme.colors.textTertiary} />}
|
|
448
|
-
title={t('manageAccount.sessions.empty') || 'No active sessions'}
|
|
449
|
-
showChevron={false}
|
|
450
|
-
disabled
|
|
451
|
-
/>
|
|
452
|
-
) : (
|
|
453
|
-
deviceRows.map((device) => (
|
|
454
|
-
<GroupedSectionItem
|
|
455
|
-
key={`device-${device.sessionId}`}
|
|
456
|
-
icon={
|
|
457
|
-
<SettingsIcon
|
|
458
|
-
name={device.isCurrent ? 'cellphone' : 'cellphone-basic'}
|
|
459
|
-
color={
|
|
460
|
-
device.isCurrent ? bloomTheme.colors.success : bloomTheme.colors.primary
|
|
461
|
-
}
|
|
462
|
-
/>
|
|
463
|
-
}
|
|
464
|
-
title={deviceSessionTitle(device, t)}
|
|
465
|
-
description={
|
|
466
|
-
t('manageAccount.sessions.lastActive', {
|
|
467
|
-
relative: formatRelative(device.lastActive),
|
|
468
|
-
}) || `Last active ${formatRelative(device.lastActive)}`
|
|
469
|
-
}
|
|
470
|
-
onPress={device.isCurrent ? undefined : () => confirmRemoveDevice(device)}
|
|
471
|
-
disabled={device.isCurrent || removingDeviceId === device.sessionId}
|
|
472
|
-
rightElement={
|
|
473
|
-
!device.isCurrent ? (
|
|
474
|
-
removingDeviceId === device.sessionId ? (
|
|
475
|
-
<SpinnerIcon color={bloomTheme.colors.error} size={16} />
|
|
476
|
-
) : (
|
|
477
|
-
<Ionicons
|
|
478
|
-
name="log-out-outline"
|
|
479
|
-
size={18}
|
|
480
|
-
color={bloomTheme.colors.error}
|
|
481
|
-
/>
|
|
482
|
-
)
|
|
483
|
-
) : undefined
|
|
484
|
-
}
|
|
485
|
-
showChevron={false}
|
|
486
|
-
/>
|
|
487
|
-
))
|
|
488
|
-
)}
|
|
489
|
-
{otherDevices.length > 0 ? (
|
|
490
|
-
<GroupedSectionItem
|
|
491
|
-
icon={<SettingsIcon name="logout" color={bloomTheme.colors.error} />}
|
|
492
|
-
title={
|
|
493
|
-
t('manageAccount.sessions.signOutAllOnThisDevice') ||
|
|
494
|
-
'Sign out of all other devices'
|
|
495
|
-
}
|
|
496
|
-
description={
|
|
497
|
-
t('manageAccount.sessions.signOutAllSubtitle', {
|
|
498
|
-
count: otherDevices.length,
|
|
499
|
-
}) || `End ${otherDevices.length} other device session(s)`
|
|
500
|
-
}
|
|
501
|
-
onPress={handleSignOutAllDevices}
|
|
502
|
-
destructive
|
|
503
|
-
showChevron={false}
|
|
504
|
-
disabled={signingOutAllDevices}
|
|
505
|
-
rightElement={
|
|
506
|
-
signingOutAllDevices ? (
|
|
507
|
-
<SpinnerIcon color={bloomTheme.colors.error} size={16} />
|
|
508
|
-
) : undefined
|
|
509
|
-
}
|
|
510
|
-
/>
|
|
511
|
-
) : null}
|
|
512
|
-
</GroupedSection>
|
|
513
|
-
|
|
514
|
-
{/* Security section */}
|
|
304
|
+
{/* Security: one page for how the account signs in and where. */}
|
|
515
305
|
<GroupedSection title={t('manageAccount.sections.security') || 'Security'}>
|
|
516
306
|
<GroupedSectionItem
|
|
517
307
|
icon={<SettingsIcon name="shield-check" color={bloomTheme.colors.primary} />}
|
|
518
|
-
title={t('
|
|
519
|
-
description={
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
onPress={() => navigate?.('PrivacySettings')}
|
|
308
|
+
title={t('signInSecurity.title')}
|
|
309
|
+
description={t('signInSecurity.subtitle')}
|
|
310
|
+
onPress={() => navigate?.('SignInSecurity')}
|
|
311
|
+
testID="manage-account-sign-in-security"
|
|
523
312
|
/>
|
|
524
|
-
{user && !user.publicKey ? (
|
|
525
|
-
<>
|
|
526
|
-
<GroupedSectionItem
|
|
527
|
-
icon={<SettingsIcon name="key" color={bloomTheme.colors.primary} />}
|
|
528
|
-
title={t('signInSecurity.password.title')}
|
|
529
|
-
description={t('signInSecurity.password.row')}
|
|
530
|
-
onPress={() => navigate?.('SignInPassword')}
|
|
531
|
-
/>
|
|
532
|
-
<GroupedSectionItem
|
|
533
|
-
icon={<SettingsIcon name="shield-check" color={bloomTheme.colors.primary} />}
|
|
534
|
-
title={t('signInSecurity.totp.title')}
|
|
535
|
-
description={t('signInSecurity.totp.row')}
|
|
536
|
-
onPress={() => navigate?.('SignInAuthenticator')}
|
|
537
|
-
/>
|
|
538
|
-
<GroupedSectionItem
|
|
539
|
-
icon={<SettingsIcon name="link" color={bloomTheme.colors.primary} />}
|
|
540
|
-
title={t('linkCommons.title')}
|
|
541
|
-
description={t('linkCommons.row')}
|
|
542
|
-
onPress={() => navigate?.('LinkCommons')}
|
|
543
|
-
/>
|
|
544
|
-
</>
|
|
545
|
-
) : null}
|
|
546
313
|
{user?.isPremium || subscription?.status === 'active' ? (
|
|
547
314
|
<GroupedSectionItem
|
|
548
315
|
icon={<SettingsIcon name="credit-card" color={bloomTheme.colors.success} />}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import { OxyEmailPanel } from '../components/signIn/OxyEmailPanel';
|
|
3
|
+
import type { BaseScreenProps } from '../types/navigation';
|
|
4
|
+
import { AccountSecurityPanelFrame } from './AccountSecurityPanelScreen';
|
|
5
|
+
|
|
6
|
+
/** `SignInEmail`: add, change or remove the account's email. */
|
|
7
|
+
const SignInEmailScreen: React.FC<BaseScreenProps> = ({ onClose, goBack }) => (
|
|
8
|
+
<AccountSecurityPanelFrame>
|
|
9
|
+
<OxyEmailPanel onDone={goBack ?? onClose} />
|
|
10
|
+
</AccountSecurityPanelFrame>
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
export default SignInEmailScreen;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import { View } from 'react-native';
|
|
3
|
+
import { OxySignInSecurityPanel } from '../components/signIn/OxySignInSecurityPanel';
|
|
4
|
+
import { useI18n } from '../hooks/useI18n';
|
|
5
|
+
import { useSurfaceHeader } from '../hooks/useSurfaceHeader';
|
|
6
|
+
import type { BaseScreenProps } from '../types/navigation';
|
|
7
|
+
|
|
8
|
+
/** `SignInSecurity`: how the account signs in, and where it is signed in. */
|
|
9
|
+
const SignInSecurityScreen: React.FC<BaseScreenProps> = ({ navigate }) => {
|
|
10
|
+
const { t } = useI18n();
|
|
11
|
+
useSurfaceHeader({ title: t('signInSecurity.title') });
|
|
12
|
+
return (
|
|
13
|
+
<View className="px-screen-margin pb-space-24">
|
|
14
|
+
<OxySignInSecurityPanel onOpen={navigate ? (route) => navigate(route) : undefined} />
|
|
15
|
+
</View>
|
|
16
|
+
);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export default SignInSecurityScreen;
|