@oxy.so/services 11.4.0 → 11.6.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 -28
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/ui/boot/runProviderColdBoot.js +47 -3
- package/lib/commonjs/ui/boot/runProviderColdBoot.js.map +1 -1
- package/lib/commonjs/ui/components/organizations/OrganizationClaimReview.js +2 -2
- package/lib/commonjs/ui/components/organizations/OrganizationClaimReview.js.map +1 -1
- package/lib/commonjs/ui/components/organizations/OrganizationDirectorySearch.js +5 -5
- package/lib/commonjs/ui/components/organizations/OrganizationDirectorySearch.js.map +1 -1
- package/lib/commonjs/ui/components/organizations/OrganizationSettings.js +4 -4
- package/lib/commonjs/ui/components/organizations/OrganizationSettings.js.map +1 -1
- package/lib/commonjs/ui/components/organizations/OrganizationTypePicker.js +2 -2
- package/lib/commonjs/ui/components/organizations/OrganizationTypePicker.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/OxyAuthenticatorPanel.js +3 -3
- package/lib/commonjs/ui/components/signIn/OxyAuthenticatorPanel.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/OxyEmailAccountChoice.js +113 -0
- package/lib/commonjs/ui/components/signIn/OxyEmailAccountChoice.js.map +1 -0
- package/lib/commonjs/ui/components/signIn/OxyLinkCommonsPanel.js +6 -2
- package/lib/commonjs/ui/components/signIn/OxyLinkCommonsPanel.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/OxyPasswordPanel.js +4 -4
- package/lib/commonjs/ui/components/signIn/OxyPasswordPanel.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/OxySignInPanel.js +76 -9
- package/lib/commonjs/ui/components/signIn/OxySignInPanel.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/OxySignUpPanel.js +6 -6
- package/lib/commonjs/ui/components/signIn/OxySignUpPanel.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/ReauthStep.js +4 -4
- package/lib/commonjs/ui/components/signIn/ReauthStep.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/accountFlowParts.js +24 -22
- package/lib/commonjs/ui/components/signIn/accountFlowParts.js.map +1 -1
- package/lib/commonjs/ui/context/OxyContext.js +7 -4
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js +5 -2
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/commonjs/ui/oauth/applicationSessionLane.js +2 -2
- package/lib/commonjs/ui/oauth/applicationSessionLane.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountMembersScreen.js +20 -63
- package/lib/commonjs/ui/screens/AccountMembersScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js +3 -3
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/CreateAccountScreen.js +12 -12
- package/lib/commonjs/ui/screens/CreateAccountScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/PreferencesScreen.js +3 -4
- package/lib/commonjs/ui/screens/PreferencesScreen.js.map +1 -1
- package/lib/commonjs/ui/utils/identityCrypto.js +19 -0
- package/lib/commonjs/ui/utils/identityCrypto.js.map +1 -0
- package/lib/module/index.js +0 -4
- package/lib/module/index.js.map +1 -1
- package/lib/module/ui/boot/runProviderColdBoot.js +47 -3
- package/lib/module/ui/boot/runProviderColdBoot.js.map +1 -1
- package/lib/module/ui/components/organizations/OrganizationClaimReview.js +2 -2
- package/lib/module/ui/components/organizations/OrganizationClaimReview.js.map +1 -1
- package/lib/module/ui/components/organizations/OrganizationDirectorySearch.js +5 -5
- package/lib/module/ui/components/organizations/OrganizationDirectorySearch.js.map +1 -1
- package/lib/module/ui/components/organizations/OrganizationSettings.js +4 -4
- package/lib/module/ui/components/organizations/OrganizationSettings.js.map +1 -1
- package/lib/module/ui/components/organizations/OrganizationTypePicker.js +1 -1
- package/lib/module/ui/components/organizations/OrganizationTypePicker.js.map +1 -1
- package/lib/module/ui/components/signIn/OxyAuthenticatorPanel.js +1 -1
- package/lib/module/ui/components/signIn/OxyAuthenticatorPanel.js.map +1 -1
- package/lib/module/ui/components/signIn/OxyEmailAccountChoice.js +110 -0
- package/lib/module/ui/components/signIn/OxyEmailAccountChoice.js.map +1 -0
- package/lib/module/ui/components/signIn/OxyLinkCommonsPanel.js +5 -1
- package/lib/module/ui/components/signIn/OxyLinkCommonsPanel.js.map +1 -1
- package/lib/module/ui/components/signIn/OxyPasswordPanel.js +1 -1
- package/lib/module/ui/components/signIn/OxyPasswordPanel.js.map +1 -1
- package/lib/module/ui/components/signIn/OxySignInPanel.js +71 -4
- package/lib/module/ui/components/signIn/OxySignInPanel.js.map +1 -1
- package/lib/module/ui/components/signIn/OxySignUpPanel.js +5 -5
- package/lib/module/ui/components/signIn/OxySignUpPanel.js.map +1 -1
- package/lib/module/ui/components/signIn/ReauthStep.js +1 -1
- package/lib/module/ui/components/signIn/ReauthStep.js.map +1 -1
- package/lib/module/ui/components/signIn/accountFlowParts.js +3 -1
- package/lib/module/ui/components/signIn/accountFlowParts.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +6 -3
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/context/hooks/useAuthOperations.js +4 -1
- package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/module/ui/oauth/applicationSessionLane.js +2 -2
- package/lib/module/ui/oauth/applicationSessionLane.js.map +1 -1
- package/lib/module/ui/screens/AccountMembersScreen.js +20 -63
- package/lib/module/ui/screens/AccountMembersScreen.js.map +1 -1
- package/lib/module/ui/screens/AccountSettingsScreen.js +2 -2
- package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/module/ui/screens/CreateAccountScreen.js +6 -6
- package/lib/module/ui/screens/CreateAccountScreen.js.map +1 -1
- package/lib/module/ui/screens/PreferencesScreen.js +3 -4
- package/lib/module/ui/screens/PreferencesScreen.js.map +1 -1
- package/lib/module/ui/utils/identityCrypto.js +13 -0
- package/lib/module/ui/utils/identityCrypto.js.map +1 -0
- package/lib/typescript/commonjs/index.d.ts +0 -4
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/boot/runProviderColdBoot.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/organizations/OrganizationClaimReview.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/organizations/OrganizationDirectorySearch.d.ts +1 -1
- package/lib/typescript/commonjs/ui/components/organizations/OrganizationDirectorySearch.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/organizations/OrganizationSettings.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/organizations/OrganizationTypePicker.d.ts +1 -1
- package/lib/typescript/commonjs/ui/components/organizations/OrganizationTypePicker.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/OxyEmailAccountChoice.d.ts +20 -0
- package/lib/typescript/commonjs/ui/components/signIn/OxyEmailAccountChoice.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/signIn/OxyLinkCommonsPanel.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/signIn/OxySignInPanel.d.ts +2 -0
- 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 +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 +1 -1
- package/lib/typescript/commonjs/ui/components/signIn/accountFlowParts.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts +1 -7
- package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/queries/usePersonalPlans.d.ts +1 -2
- package/lib/typescript/commonjs/ui/hooks/queries/usePersonalPlans.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/AccountMembersScreen.d.ts +1 -1
- package/lib/typescript/commonjs/ui/screens/AccountMembersScreen.d.ts.map +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/PreferencesScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/utils/identityCrypto.d.ts +43 -0
- package/lib/typescript/commonjs/ui/utils/identityCrypto.d.ts.map +1 -0
- package/lib/typescript/module/index.d.ts +0 -4
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/boot/runProviderColdBoot.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/organizations/OrganizationClaimReview.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/organizations/OrganizationDirectorySearch.d.ts +1 -1
- package/lib/typescript/module/ui/components/organizations/OrganizationDirectorySearch.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/organizations/OrganizationSettings.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/organizations/OrganizationTypePicker.d.ts +1 -1
- package/lib/typescript/module/ui/components/organizations/OrganizationTypePicker.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/OxyEmailAccountChoice.d.ts +20 -0
- package/lib/typescript/module/ui/components/signIn/OxyEmailAccountChoice.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/signIn/OxyLinkCommonsPanel.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/signIn/OxySignInPanel.d.ts +2 -0
- 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 +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 +1 -1
- package/lib/typescript/module/ui/components/signIn/accountFlowParts.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts +1 -7
- package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/queries/usePersonalPlans.d.ts +1 -2
- package/lib/typescript/module/ui/hooks/queries/usePersonalPlans.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/AccountMembersScreen.d.ts +1 -1
- package/lib/typescript/module/ui/screens/AccountMembersScreen.d.ts.map +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/PreferencesScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/utils/identityCrypto.d.ts +43 -0
- package/lib/typescript/module/ui/utils/identityCrypto.d.ts.map +1 -0
- package/package.json +6 -7
- package/src/index.ts +0 -5
- package/src/ui/boot/__tests__/coldBootNoIdpRedirect.test.ts +166 -3
- package/src/ui/boot/runProviderColdBoot.ts +49 -4
- package/src/ui/components/organizations/OrganizationClaimReview.tsx +3 -5
- package/src/ui/components/organizations/OrganizationDirectorySearch.tsx +12 -9
- package/src/ui/components/organizations/OrganizationSettings.tsx +6 -9
- package/src/ui/components/organizations/OrganizationTypePicker.tsx +2 -1
- package/src/ui/components/signIn/OxyAuthenticatorPanel.tsx +2 -1
- package/src/ui/components/signIn/OxyEmailAccountChoice.tsx +116 -0
- package/src/ui/components/signIn/OxyLinkCommonsPanel.tsx +3 -1
- package/src/ui/components/signIn/OxyPasswordPanel.tsx +1 -1
- package/src/ui/components/signIn/OxySignInPanel.tsx +93 -6
- package/src/ui/components/signIn/OxySignUpPanel.tsx +6 -9
- package/src/ui/components/signIn/ReauthStep.tsx +2 -6
- package/src/ui/components/signIn/accountFlowParts.tsx +4 -2
- package/src/ui/context/OxyContext.tsx +7 -3
- package/src/ui/context/hooks/useAuthOperations.ts +2 -1
- package/src/ui/oauth/applicationSessionLane.ts +2 -2
- package/src/ui/screens/AccountMembersScreen.tsx +27 -71
- package/src/ui/screens/AccountSettingsScreen.tsx +6 -2
- package/src/ui/screens/CreateAccountScreen.tsx +10 -9
- package/src/ui/screens/PreferencesScreen.tsx +0 -2
- package/src/ui/utils/identityCrypto.ts +10 -0
- package/lib/commonjs/ui/components/AccountInvitationAcceptance.js +0 -97
- package/lib/commonjs/ui/components/AccountInvitationAcceptance.js.map +0 -1
- package/lib/commonjs/ui/components/DisplayCurrencySelector.js +0 -49
- package/lib/commonjs/ui/components/DisplayCurrencySelector.js.map +0 -1
- package/lib/commonjs/ui/hooks/useDisplayCurrency.js +0 -77
- package/lib/commonjs/ui/hooks/useDisplayCurrency.js.map +0 -1
- package/lib/commonjs/ui/hooks/useDisplayPrice.js +0 -59
- package/lib/commonjs/ui/hooks/useDisplayPrice.js.map +0 -1
- package/lib/module/ui/components/AccountInvitationAcceptance.js +0 -94
- package/lib/module/ui/components/AccountInvitationAcceptance.js.map +0 -1
- package/lib/module/ui/components/DisplayCurrencySelector.js +0 -44
- package/lib/module/ui/components/DisplayCurrencySelector.js.map +0 -1
- package/lib/module/ui/hooks/useDisplayCurrency.js +0 -72
- package/lib/module/ui/hooks/useDisplayCurrency.js.map +0 -1
- package/lib/module/ui/hooks/useDisplayPrice.js +0 -56
- package/lib/module/ui/hooks/useDisplayPrice.js.map +0 -1
- package/lib/typescript/commonjs/ui/components/AccountInvitationAcceptance.d.ts +0 -5
- package/lib/typescript/commonjs/ui/components/AccountInvitationAcceptance.d.ts.map +0 -1
- package/lib/typescript/commonjs/ui/components/DisplayCurrencySelector.d.ts +0 -3
- package/lib/typescript/commonjs/ui/components/DisplayCurrencySelector.d.ts.map +0 -1
- package/lib/typescript/commonjs/ui/hooks/useDisplayCurrency.d.ts +0 -11
- package/lib/typescript/commonjs/ui/hooks/useDisplayCurrency.d.ts.map +0 -1
- package/lib/typescript/commonjs/ui/hooks/useDisplayPrice.d.ts +0 -6
- package/lib/typescript/commonjs/ui/hooks/useDisplayPrice.d.ts.map +0 -1
- package/lib/typescript/module/ui/components/AccountInvitationAcceptance.d.ts +0 -5
- package/lib/typescript/module/ui/components/AccountInvitationAcceptance.d.ts.map +0 -1
- package/lib/typescript/module/ui/components/DisplayCurrencySelector.d.ts +0 -3
- package/lib/typescript/module/ui/components/DisplayCurrencySelector.d.ts.map +0 -1
- package/lib/typescript/module/ui/hooks/useDisplayCurrency.d.ts +0 -11
- package/lib/typescript/module/ui/hooks/useDisplayCurrency.d.ts.map +0 -1
- package/lib/typescript/module/ui/hooks/useDisplayPrice.d.ts +0 -6
- package/lib/typescript/module/ui/hooks/useDisplayPrice.d.ts.map +0 -1
- package/src/ui/components/AccountInvitationAcceptance.tsx +0 -90
- package/src/ui/components/DisplayCurrencySelector.tsx +0 -38
- package/src/ui/components/__tests__/AccountInvitationAcceptance.test.tsx +0 -61
- package/src/ui/hooks/useDisplayCurrency.ts +0 -64
- package/src/ui/hooks/useDisplayPrice.ts +0 -40
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
* (the start response does not say which, by design); meanwhile
|
|
17
17
|
* the screen asks whether the link was opened in this browser,
|
|
18
18
|
* and signs in when it was.
|
|
19
|
+
* choose the email holds several accounts, and the code or the link just
|
|
20
|
+
* proved it: which one signs in (a username never gets here).
|
|
19
21
|
* password the alternative for an account that has one.
|
|
20
22
|
* second-factor the authenticator's code, or a backup code, when the account has one.
|
|
21
23
|
*
|
|
@@ -34,15 +36,19 @@ import { useTheme } from '@oxy.so/bloom/theme';
|
|
|
34
36
|
import { toast } from '@oxy.so/bloom/toast';
|
|
35
37
|
import { Text } from '@oxy.so/bloom/typography';
|
|
36
38
|
import type { SwitcherContextRow } from '@oxy.so/core/session';
|
|
39
|
+
import type {
|
|
40
|
+
EmailSignInAccountOption,
|
|
41
|
+
EmailSignInStepResult,
|
|
42
|
+
LoginResult,
|
|
43
|
+
} from '@oxy.so/contracts';
|
|
37
44
|
import {
|
|
38
45
|
EMAIL_CODE_LENGTH,
|
|
39
46
|
EMAIL_SIGNIN_LONG_CODE_LENGTH,
|
|
40
47
|
SIGN_IN_ERROR_CODES,
|
|
41
48
|
TOTP_DIGITS,
|
|
49
|
+
isEmailSignInChooseAccount,
|
|
42
50
|
isSecondFactorRequired,
|
|
43
|
-
|
|
44
|
-
type SignInStepResult,
|
|
45
|
-
} from '@oxy.so/contracts';
|
|
51
|
+
} from '@oxy.so/contracts/client';
|
|
46
52
|
import { useQueryClient } from '@tanstack/react-query';
|
|
47
53
|
import { useOxy } from '../../context/OxyContext';
|
|
48
54
|
import { useAccountDialogSnapshot } from '../../hooks/accountDialogSnapshot';
|
|
@@ -56,6 +62,7 @@ import { SIGN_IN_SLIDES } from './artwork';
|
|
|
56
62
|
import { InlineCommonsQr } from './InlineCommonsQr';
|
|
57
63
|
import { OxyAccountPicker } from './OxyAccountPicker';
|
|
58
64
|
import { OxyAuthScreen, OxyAuthScreenHeader, OxyAuthSplit, OxyAuthTerms } from './OxyAuthScreen';
|
|
65
|
+
import { OxyEmailAccountChoice } from './OxyEmailAccountChoice';
|
|
59
66
|
import {
|
|
60
67
|
AccountFlowAction,
|
|
61
68
|
AccountFlowCodeField,
|
|
@@ -94,6 +101,13 @@ const SIGN_IN_FLOW_KEY = 'signin';
|
|
|
94
101
|
type Step =
|
|
95
102
|
| { name: 'start' }
|
|
96
103
|
| { name: 'check-email'; identifier: string; requestId: string; requestSecret: string }
|
|
104
|
+
| {
|
|
105
|
+
name: 'choose';
|
|
106
|
+
identifier: string;
|
|
107
|
+
requestId: string;
|
|
108
|
+
requestSecret: string;
|
|
109
|
+
accounts: EmailSignInAccountOption[];
|
|
110
|
+
}
|
|
97
111
|
| { name: 'password'; identifier: string }
|
|
98
112
|
| { name: 'second-factor'; challengeId: string; identifier: string };
|
|
99
113
|
|
|
@@ -153,6 +167,7 @@ export const OxySignInPanel: React.FC<OxySignInPanelProps> = ({
|
|
|
153
167
|
const [useBackupCode, setUseBackupCode] = useState(saved?.useBackupCode ?? false);
|
|
154
168
|
const [longCode, setLongCode] = useState(saved?.longCode ?? false);
|
|
155
169
|
const [pending, setPending] = useState(false);
|
|
170
|
+
const [choosingId, setChoosingId] = useState<string | null>(null);
|
|
156
171
|
const [error, setError] = useState<string | null>(null);
|
|
157
172
|
const [notice, setNotice] = useState<string | null>(null);
|
|
158
173
|
const [rateLimitSeconds, setRateLimitSeconds] = useState(0);
|
|
@@ -225,8 +240,25 @@ export const OxySignInPanel: React.FC<OxySignInPanelProps> = ({
|
|
|
225
240
|
// One sign-in ends once: the typed code and the opened link can race.
|
|
226
241
|
const finishingRef = useRef(false);
|
|
227
242
|
const finish = useCallback(
|
|
228
|
-
async (
|
|
243
|
+
async (
|
|
244
|
+
result: EmailSignInStepResult,
|
|
245
|
+
from: string,
|
|
246
|
+
request?: { requestId: string; requestSecret: string },
|
|
247
|
+
): Promise<void> => {
|
|
229
248
|
if (finishingRef.current) return;
|
|
249
|
+
if (isEmailSignInChooseAccount(result)) {
|
|
250
|
+
if (!request) return;
|
|
251
|
+
setError(null);
|
|
252
|
+
setNotice(null);
|
|
253
|
+
setStep({
|
|
254
|
+
name: 'choose',
|
|
255
|
+
identifier: from,
|
|
256
|
+
requestId: request.requestId,
|
|
257
|
+
requestSecret: request.requestSecret,
|
|
258
|
+
accounts: result.accounts,
|
|
259
|
+
});
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
230
262
|
if (isSecondFactorRequired(result)) {
|
|
231
263
|
setError(null);
|
|
232
264
|
setSecondFactorCode('');
|
|
@@ -301,7 +333,7 @@ export const OxySignInPanel: React.FC<OxySignInPanelProps> = ({
|
|
|
301
333
|
requestSecret: request.requestSecret,
|
|
302
334
|
code: typed.trim(),
|
|
303
335
|
})
|
|
304
|
-
.then((result) => finish(result, request.identifier))
|
|
336
|
+
.then((result) => finish(result, request.identifier, request))
|
|
305
337
|
.catch((reason: unknown) => {
|
|
306
338
|
setCode('');
|
|
307
339
|
fail(reason);
|
|
@@ -309,6 +341,27 @@ export const OxySignInPanel: React.FC<OxySignInPanelProps> = ({
|
|
|
309
341
|
.finally(() => setPending(false));
|
|
310
342
|
};
|
|
311
343
|
|
|
344
|
+
const chooseAccount = (account: EmailSignInAccountOption) => {
|
|
345
|
+
if (step.name !== 'choose' || blocked) return;
|
|
346
|
+
const request = step;
|
|
347
|
+
const name = account.username ?? request.identifier;
|
|
348
|
+
setError(null);
|
|
349
|
+
setPending(true);
|
|
350
|
+
setChoosingId(account.id);
|
|
351
|
+
oxyServices.auth.email
|
|
352
|
+
.choose({
|
|
353
|
+
requestId: request.requestId,
|
|
354
|
+
requestSecret: request.requestSecret,
|
|
355
|
+
userId: account.id,
|
|
356
|
+
})
|
|
357
|
+
.then((result) => finish(result, name))
|
|
358
|
+
.catch(fail)
|
|
359
|
+
.finally(() => {
|
|
360
|
+
setPending(false);
|
|
361
|
+
setChoosingId(null);
|
|
362
|
+
});
|
|
363
|
+
};
|
|
364
|
+
|
|
312
365
|
const submitPassword = () => {
|
|
313
366
|
if (step.name !== 'password' || blocked) return;
|
|
314
367
|
if (!password) {
|
|
@@ -369,7 +422,7 @@ export const OxySignInPanel: React.FC<OxySignInPanelProps> = ({
|
|
|
369
422
|
try {
|
|
370
423
|
const result = await oxyServices.auth.email.collect({ requestId, requestSecret });
|
|
371
424
|
if (!('status' in result)) {
|
|
372
|
-
await finishRef.current(result, requestIdentifier);
|
|
425
|
+
await finishRef.current(result, requestIdentifier, { requestId, requestSecret });
|
|
373
426
|
return;
|
|
374
427
|
}
|
|
375
428
|
if (stopped) return;
|
|
@@ -550,6 +603,40 @@ export const OxySignInPanel: React.FC<OxySignInPanelProps> = ({
|
|
|
550
603
|
);
|
|
551
604
|
break;
|
|
552
605
|
}
|
|
606
|
+
case 'choose': {
|
|
607
|
+
const request = step;
|
|
608
|
+
form = (
|
|
609
|
+
<OxyAuthScreen className={splits ? 'md:max-w-none' : undefined}>
|
|
610
|
+
<OxyAuthScreenHeader
|
|
611
|
+
title={t('signin.emailAccounts.title')}
|
|
612
|
+
description={t('signin.emailAccounts.description', {
|
|
613
|
+
identifier: request.identifier,
|
|
614
|
+
})}
|
|
615
|
+
/>
|
|
616
|
+
<OxyEmailAccountChoice
|
|
617
|
+
accounts={request.accounts}
|
|
618
|
+
onChoose={chooseAccount}
|
|
619
|
+
pendingId={choosingId}
|
|
620
|
+
disabled={blocked}
|
|
621
|
+
/>
|
|
622
|
+
{error ? (
|
|
623
|
+
<AccountFlowNotice type="error" testID="signin-choose-error">
|
|
624
|
+
{error}
|
|
625
|
+
</AccountFlowNotice>
|
|
626
|
+
) : null}
|
|
627
|
+
{rateLimitNotice}
|
|
628
|
+
<View style={styles.links}>
|
|
629
|
+
<SubtleLink
|
|
630
|
+
label={t('signin.checkEmail.differentAccount')}
|
|
631
|
+
onPress={backToIdentifier}
|
|
632
|
+
disabled={pending}
|
|
633
|
+
testID="signin-different-account"
|
|
634
|
+
/>
|
|
635
|
+
</View>
|
|
636
|
+
</OxyAuthScreen>
|
|
637
|
+
);
|
|
638
|
+
break;
|
|
639
|
+
}
|
|
553
640
|
case 'password': {
|
|
554
641
|
const request = step;
|
|
555
642
|
form = (
|
|
@@ -15,11 +15,8 @@ import type React from 'react';
|
|
|
15
15
|
import { useEffect, useState } from 'react';
|
|
16
16
|
import { Linking, Platform } from 'react-native';
|
|
17
17
|
import { toast } from '@oxy.so/bloom/toast';
|
|
18
|
-
import {
|
|
19
|
-
|
|
20
|
-
emailAddressSchema,
|
|
21
|
-
type EmailVerificationConfirmResponse,
|
|
22
|
-
} from '@oxy.so/contracts';
|
|
18
|
+
import type { EmailVerificationConfirmResponse } from '@oxy.so/contracts';
|
|
19
|
+
import { SIGN_IN_ERROR_CODES, emailAddressCheck, checkContract } from '@oxy.so/contracts/client';
|
|
23
20
|
import { useOxy } from '../../context/OxyContext';
|
|
24
21
|
import { useAccountDialogSnapshot } from '../../hooks/accountDialogSnapshot';
|
|
25
22
|
import { useI18n } from '../../hooks/useI18n';
|
|
@@ -126,13 +123,13 @@ export const OxySignUpPanel: React.FC<OxySignUpPanelProps> = ({
|
|
|
126
123
|
};
|
|
127
124
|
|
|
128
125
|
const submitEmail = () => {
|
|
129
|
-
const
|
|
130
|
-
if (
|
|
126
|
+
const address = checkContract(emailAddressCheck, email);
|
|
127
|
+
if (address === null) {
|
|
131
128
|
setError(t('signup.email.invalid'));
|
|
132
129
|
return;
|
|
133
130
|
}
|
|
134
|
-
setEmail(
|
|
135
|
-
run(() => sendCode(
|
|
131
|
+
setEmail(address);
|
|
132
|
+
run(() => sendCode(address));
|
|
136
133
|
};
|
|
137
134
|
|
|
138
135
|
// The code step reports this promise's failure in place.
|
|
@@ -8,12 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
import type React from 'react';
|
|
10
10
|
import { useState } from 'react';
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
TOTP_DIGITS,
|
|
14
|
-
type ReauthAction,
|
|
15
|
-
type ReauthProof,
|
|
16
|
-
} from '@oxy.so/contracts';
|
|
11
|
+
import type { ReauthAction, ReauthProof } from '@oxy.so/contracts';
|
|
12
|
+
import { EMAIL_CODE_LENGTH, TOTP_DIGITS } from '@oxy.so/contracts/client';
|
|
17
13
|
import { useOxy } from '../../context/OxyContext';
|
|
18
14
|
import { useI18n } from '../../hooks/useI18n';
|
|
19
15
|
import { SubtleLink } from '../authChooser/primitives';
|
|
@@ -18,6 +18,7 @@ import { useTheme } from '@oxy.so/bloom/theme';
|
|
|
18
18
|
import { TextField, TextFieldHint, TextFieldInput, TextFieldLabel } from '@oxy.so/bloom/text-field';
|
|
19
19
|
import { Text } from '@oxy.so/bloom/typography';
|
|
20
20
|
import { WizardProgress } from '@oxy.so/bloom/wizard';
|
|
21
|
+
import type { EmailVerificationConfirmResponse } from '@oxy.so/contracts';
|
|
21
22
|
import {
|
|
22
23
|
EMAIL_CODE_LENGTH,
|
|
23
24
|
EMAIL_SIGNIN_LONG_CODE_ALPHABET,
|
|
@@ -25,8 +26,7 @@ import {
|
|
|
25
26
|
EMAIL_VERIFICATION_ERROR_CODES,
|
|
26
27
|
SIGN_IN_ERROR_CODES,
|
|
27
28
|
normalizeEmailSignInCode,
|
|
28
|
-
|
|
29
|
-
} from '@oxy.so/contracts';
|
|
29
|
+
} from '@oxy.so/contracts/client';
|
|
30
30
|
import { useOxy } from '../../context/OxyContext';
|
|
31
31
|
import { useI18n } from '../../hooks/useI18n';
|
|
32
32
|
import type { Translate } from '../authChooser/types';
|
|
@@ -85,6 +85,8 @@ export function describeSignInError(error: unknown, t: Translate): string {
|
|
|
85
85
|
return t('signin.errors.originNotAllowed');
|
|
86
86
|
case SIGN_IN_ERROR_CODES.usernameTaken:
|
|
87
87
|
return t('signup.username.taken');
|
|
88
|
+
case SIGN_IN_ERROR_CODES.emailAccountsFull:
|
|
89
|
+
return t('signin.errors.emailAccountsFull');
|
|
88
90
|
case EMAIL_VERIFICATION_ERROR_CODES.codeInvalid:
|
|
89
91
|
return t('emailCode.errors.codeInvalid');
|
|
90
92
|
case EMAIL_VERIFICATION_ERROR_CODES.tooManyAttempts:
|
|
@@ -29,7 +29,6 @@ import type {
|
|
|
29
29
|
AccountDialogController,
|
|
30
30
|
AccountDialogView,
|
|
31
31
|
} from '@oxy.so/core/session';
|
|
32
|
-
import { KeyManager } from '@oxy.so/core/crypto';
|
|
33
32
|
import {
|
|
34
33
|
refreshDeviceSecretArm,
|
|
35
34
|
establishIdentitySession,
|
|
@@ -60,6 +59,7 @@ import {
|
|
|
60
59
|
type RequestOAuthConsentOptions,
|
|
61
60
|
} from '../oauth/explicitOAuthConsent';
|
|
62
61
|
import { isWebBrowser } from '../utils/isWebBrowser';
|
|
62
|
+
import { loadIdentityCrypto } from '../utils/identityCrypto';
|
|
63
63
|
import { resolveDeliveryPlatform } from '../utils/deliveryPlatform';
|
|
64
64
|
import { runProviderColdBoot } from '../boot/runProviderColdBoot';
|
|
65
65
|
import { resolveApplicationSessionLane } from '../oauth/applicationSessionLane';
|
|
@@ -696,6 +696,7 @@ export const OxyRuntimeProvider: React.FC<OxyRuntimeProviderProps> = ({
|
|
|
696
696
|
try {
|
|
697
697
|
// The same keys the refresh handler's second arm signs with: the
|
|
698
698
|
// identity-bound client's own key, or the cross-app shared identity.
|
|
699
|
+
const { KeyManager } = await loadIdentityCrypto();
|
|
699
700
|
return identity ? await KeyManager.hasIdentity() : await KeyManager.hasSharedIdentity();
|
|
700
701
|
} catch {
|
|
701
702
|
return false;
|
|
@@ -1495,9 +1496,12 @@ export const OxyRuntimeProvider: React.FC<OxyRuntimeProviderProps> = ({
|
|
|
1495
1496
|
// — a locked keychain must never be misreported as "no identity". `hasIdentity`
|
|
1496
1497
|
// still resolves `false` and `getPublicKey` still resolves `null` for a genuine
|
|
1497
1498
|
// absence.
|
|
1498
|
-
const hasIdentity = useCallback(
|
|
1499
|
+
const hasIdentity = useCallback(
|
|
1500
|
+
async (): Promise<boolean> => (await loadIdentityCrypto()).KeyManager.hasIdentity(),
|
|
1501
|
+
[],
|
|
1502
|
+
);
|
|
1499
1503
|
const getPublicKey = useCallback(
|
|
1500
|
-
async (): Promise<string | null> => KeyManager.getPublicKey(),
|
|
1504
|
+
async (): Promise<string | null> => (await loadIdentityCrypto()).KeyManager.getPublicKey(),
|
|
1501
1505
|
[],
|
|
1502
1506
|
);
|
|
1503
1507
|
|
|
@@ -8,9 +8,9 @@ import type { LogoutResult } from '../oxyContextTypes';
|
|
|
8
8
|
import { DeviceManager } from '@oxy.so/core/session';
|
|
9
9
|
import { fetchSessionsWithFallback } from '../../utils/sessionHelpers';
|
|
10
10
|
import { handleAuthError, isInvalidSessionError } from '../../utils/errorHandlers';
|
|
11
|
+
import { loadIdentityCrypto } from '../../utils/identityCrypto';
|
|
11
12
|
import type { StorageInterface } from '../../utils/storageHelpers';
|
|
12
13
|
import type { OxyServices } from '@oxy.so/core';
|
|
13
|
-
import { SignatureService } from '@oxy.so/core/crypto';
|
|
14
14
|
|
|
15
15
|
export interface UseAuthOperationsOptions {
|
|
16
16
|
oxyServices: OxyServices;
|
|
@@ -126,6 +126,7 @@ export const useAuthOperations = ({
|
|
|
126
126
|
// in local storage as 'oxy_biometric_enabled' and can be checked there.
|
|
127
127
|
|
|
128
128
|
// Sign the challenge
|
|
129
|
+
const { SignatureService } = await loadIdentityCrypto();
|
|
129
130
|
const { challenge: signature, timestamp } = await SignatureService.signChallenge(challenge);
|
|
130
131
|
|
|
131
132
|
let fullUser: User;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { publicApplicationCheck, parseContract } from '@oxy.so/contracts/client';
|
|
2
2
|
import type { OxyServices } from '@oxy.so/core';
|
|
3
3
|
|
|
4
4
|
export function classifyApplicationSessionLane(metadata: unknown): 'device' | 'oauth' {
|
|
5
|
-
const application =
|
|
5
|
+
const application = parseContract(publicApplicationCheck, metadata);
|
|
6
6
|
return application.type === 'third_party' && !application.isOfficial && !application.isInternal
|
|
7
7
|
? 'oauth'
|
|
8
8
|
: 'device';
|
|
@@ -30,8 +30,13 @@ const ASSIGNABLE_ROLES: AssignableRole[] = ['admin', 'editor', 'developer', 'bil
|
|
|
30
30
|
const errorMessage = (error: unknown, fallback: string): string =>
|
|
31
31
|
error instanceof Error && error.message ? error.message : fallback;
|
|
32
32
|
|
|
33
|
+
const isUserNotFound = (error: unknown): boolean => {
|
|
34
|
+
const message = error instanceof Error ? error.message.toLowerCase() : '';
|
|
35
|
+
return message.includes('user not found') || message.includes('not found');
|
|
36
|
+
};
|
|
37
|
+
|
|
33
38
|
/**
|
|
34
|
-
* Manage the members of an account (invite by email, change roles,
|
|
39
|
+
* Manage the members of an account (invite by username/email, change roles,
|
|
35
40
|
* remove, transfer ownership). Gated on the caller's `callerMembership`
|
|
36
41
|
* permissions. Receives the target `accountId` via navigation props.
|
|
37
42
|
*/
|
|
@@ -81,27 +86,6 @@ const AccountMembersScreen: React.FC<BaseScreenProps> = ({ onClose, goBack, acco
|
|
|
81
86
|
queryFn: () => oxyServices.accounts.members.list(id),
|
|
82
87
|
enabled: canUsePrivateApi && id.length > 0 && canRead,
|
|
83
88
|
});
|
|
84
|
-
const invitationsQuery = useQuery({
|
|
85
|
-
queryKey: ['account-invitations', id],
|
|
86
|
-
queryFn: () => oxyServices.accounts.invitations.list(id),
|
|
87
|
-
enabled: canUsePrivateApi && !!id && canRead,
|
|
88
|
-
});
|
|
89
|
-
const invitationAction = useMutation({
|
|
90
|
-
mutationFn: async ({
|
|
91
|
-
invitationId,
|
|
92
|
-
action,
|
|
93
|
-
}: {
|
|
94
|
-
invitationId: string;
|
|
95
|
-
action: 'resend' | 'cancel';
|
|
96
|
-
}) => {
|
|
97
|
-
if (action === 'resend') await oxyServices.accounts.invitations.resend(id, invitationId);
|
|
98
|
-
else await oxyServices.accounts.invitations.cancel(id, invitationId);
|
|
99
|
-
},
|
|
100
|
-
onSuccess: () => {
|
|
101
|
-
void invitationsQuery.refetch();
|
|
102
|
-
},
|
|
103
|
-
onError: (error) => toast.error(errorMessage(error, 'Could not update invitation')),
|
|
104
|
-
});
|
|
105
89
|
const members = useMemo<AccountMember[]>(() => membersQuery.data ?? [], [membersQuery.data]);
|
|
106
90
|
const memberUserIds = useMemo(
|
|
107
91
|
() => [...new Set(members.map((member) => member.memberUserId).filter((id) => id.length > 0))],
|
|
@@ -142,7 +126,6 @@ const AccountMembersScreen: React.FC<BaseScreenProps> = ({ onClose, goBack, acco
|
|
|
142
126
|
// tables — mirror the SDK's per-account prefix sweep so every cached member
|
|
143
127
|
// list refetches, not only the account that was mutated.
|
|
144
128
|
queryClient.invalidateQueries({ queryKey: ['accounts', 'members'] });
|
|
145
|
-
queryClient.invalidateQueries({ queryKey: ['account-invitations', id] });
|
|
146
129
|
queryClient.invalidateQueries({ queryKey: ['accounts', 'detail', id] });
|
|
147
130
|
}, [queryClient, id]);
|
|
148
131
|
|
|
@@ -154,20 +137,24 @@ const AccountMembersScreen: React.FC<BaseScreenProps> = ({ onClose, goBack, acco
|
|
|
154
137
|
const inviteMutation = useMutation({
|
|
155
138
|
mutationKey: ['accounts', 'members', 'invite', id],
|
|
156
139
|
mutationFn: (input: { usernameOrEmail: string; role: AssignableRole }) =>
|
|
157
|
-
oxyServices.accounts.
|
|
158
|
-
email: input.usernameOrEmail,
|
|
159
|
-
role: input.role,
|
|
160
|
-
}),
|
|
140
|
+
oxyServices.accounts.members.invite(id, input),
|
|
161
141
|
onSuccess: () => {
|
|
162
142
|
setInviteIdentifier('');
|
|
163
143
|
setInviteRole('editor');
|
|
164
144
|
setInviteError(null);
|
|
165
145
|
invalidateMembers();
|
|
166
|
-
toast.success('
|
|
146
|
+
toast.success(t('accounts.members.toasts.added') || 'Member added');
|
|
167
147
|
},
|
|
168
148
|
onError: (error) => {
|
|
169
|
-
|
|
170
|
-
|
|
149
|
+
if (isUserNotFound(error)) {
|
|
150
|
+
const message = t('accounts.members.errors.userNotFound') || 'User not found';
|
|
151
|
+
setInviteError(message);
|
|
152
|
+
toast.error(message);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
toast.error(
|
|
156
|
+
errorMessage(error, t('accounts.members.toasts.addFailed') || 'Failed to add member'),
|
|
157
|
+
);
|
|
171
158
|
},
|
|
172
159
|
});
|
|
173
160
|
|
|
@@ -220,12 +207,14 @@ const AccountMembersScreen: React.FC<BaseScreenProps> = ({ onClose, goBack, acco
|
|
|
220
207
|
const handleInvite = useCallback(() => {
|
|
221
208
|
const usernameOrEmail = inviteIdentifier.trim();
|
|
222
209
|
if (!usernameOrEmail) {
|
|
223
|
-
setInviteError(
|
|
210
|
+
setInviteError(
|
|
211
|
+
t('accounts.members.errors.identifierRequired') || 'Enter a username or email to invite',
|
|
212
|
+
);
|
|
224
213
|
return;
|
|
225
214
|
}
|
|
226
215
|
setInviteError(null);
|
|
227
216
|
inviteMutation.mutate({ usernameOrEmail, role: inviteRole });
|
|
228
|
-
}, [inviteIdentifier, inviteRole, inviteMutation]);
|
|
217
|
+
}, [inviteIdentifier, inviteRole, inviteMutation, t]);
|
|
229
218
|
|
|
230
219
|
const handleChangeRole = useCallback(
|
|
231
220
|
(member: AccountMember, role: AccountRole) => {
|
|
@@ -293,11 +282,13 @@ const AccountMembersScreen: React.FC<BaseScreenProps> = ({ onClose, goBack, acco
|
|
|
293
282
|
{/* Invite form */}
|
|
294
283
|
{canInvite ? (
|
|
295
284
|
<View className="gap-space-12 p-space-16 rounded-radius-20 bg-fill">
|
|
296
|
-
<Text className="text-body font-semibold text-text">
|
|
285
|
+
<Text className="text-body font-semibold text-text">
|
|
286
|
+
{t('accounts.members.invite.title') || 'Add a member'}
|
|
287
|
+
</Text>
|
|
297
288
|
<TextField isInvalid={!!inviteError}>
|
|
298
289
|
<TextFieldInput
|
|
299
290
|
floatingLabel
|
|
300
|
-
label={'
|
|
291
|
+
label={t('accounts.members.invite.identifierLabel') || 'Username or email'}
|
|
301
292
|
value={inviteIdentifier}
|
|
302
293
|
onChangeText={(value) => {
|
|
303
294
|
setInviteIdentifier(value);
|
|
@@ -341,49 +332,14 @@ const AccountMembersScreen: React.FC<BaseScreenProps> = ({ onClose, goBack, acco
|
|
|
341
332
|
onPress={handleInvite}
|
|
342
333
|
disabled={inviteMutation.isPending || !inviteIdentifier.trim()}
|
|
343
334
|
loading={inviteMutation.isPending}
|
|
344
|
-
accessibilityLabel={'
|
|
335
|
+
accessibilityLabel={t('accounts.members.invite.submit') || 'Add member'}
|
|
345
336
|
className="w-full"
|
|
346
337
|
>
|
|
347
|
-
{'
|
|
338
|
+
{t('accounts.members.invite.submit') || 'Add member'}
|
|
348
339
|
</Button>
|
|
349
340
|
</View>
|
|
350
341
|
) : null}
|
|
351
342
|
|
|
352
|
-
{canRead ? (
|
|
353
|
-
<View className="gap-space-12">
|
|
354
|
-
<Text className="text-body font-semibold text-text">Pending invitations</Text>
|
|
355
|
-
{invitationsQuery.isError ? <Text>Could not load invitations.</Text> : null}
|
|
356
|
-
{(invitationsQuery.data ?? []).map((invitation) => (
|
|
357
|
-
<View key={invitation.id} className="gap-space-8 p-space-16 rounded-radius-20 bg-fill">
|
|
358
|
-
<Text>
|
|
359
|
-
{invitation.email} · {roleLabel(invitation.role)} · {invitation.status}
|
|
360
|
-
</Text>
|
|
361
|
-
<Text>Expires {new Date(invitation.expiresAt).toLocaleDateString()}</Text>
|
|
362
|
-
{canInvite ? (
|
|
363
|
-
<Button
|
|
364
|
-
disabled={invitationAction.isPending}
|
|
365
|
-
onPress={() =>
|
|
366
|
-
invitationAction.mutate({ invitationId: invitation.id, action: 'resend' })
|
|
367
|
-
}
|
|
368
|
-
>
|
|
369
|
-
Resend
|
|
370
|
-
</Button>
|
|
371
|
-
) : null}
|
|
372
|
-
{canRemove ? (
|
|
373
|
-
<Button
|
|
374
|
-
disabled={invitationAction.isPending}
|
|
375
|
-
onPress={() =>
|
|
376
|
-
invitationAction.mutate({ invitationId: invitation.id, action: 'cancel' })
|
|
377
|
-
}
|
|
378
|
-
>
|
|
379
|
-
Cancel invitation
|
|
380
|
-
</Button>
|
|
381
|
-
) : null}
|
|
382
|
-
</View>
|
|
383
|
-
))}
|
|
384
|
-
</View>
|
|
385
|
-
) : null}
|
|
386
|
-
|
|
387
343
|
{/* Member list */}
|
|
388
344
|
{canRead ? (
|
|
389
345
|
<View className="gap-space-8">
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import type React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
ORGANIZATION_NAME_MAX_LENGTH,
|
|
4
|
+
organizationNameCheck,
|
|
5
|
+
checkContract,
|
|
6
|
+
} from '@oxy.so/contracts/client';
|
|
3
7
|
import { OrganizationSettings } from '../components/organizations/OrganizationSettings';
|
|
4
8
|
import { useCallback, useMemo, useState } from 'react';
|
|
5
9
|
import { View, ActivityIndicator } from 'react-native';
|
|
@@ -152,7 +156,7 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
152
156
|
}
|
|
153
157
|
if (node?.account.kind === 'organization') {
|
|
154
158
|
setDisplayNameError(
|
|
155
|
-
|
|
159
|
+
checkContract(organizationNameCheck, trimmed) !== null
|
|
156
160
|
? ''
|
|
157
161
|
: t('organizationDirectory.invalidName'),
|
|
158
162
|
);
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type React from 'react';
|
|
2
|
+
import type { OrganizationType } from '@oxy.so/contracts';
|
|
2
3
|
import {
|
|
3
4
|
ORGANIZATION_NAME_MAX_LENGTH,
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
} from '@oxy.so/contracts';
|
|
5
|
+
organizationNameCheck,
|
|
6
|
+
checkContract,
|
|
7
|
+
} from '@oxy.so/contracts/client';
|
|
7
8
|
import { OrganizationDirectorySearch } from '../components/organizations/OrganizationDirectorySearch';
|
|
8
9
|
import { OrganizationTypePicker } from '../components/organizations/OrganizationTypePicker';
|
|
9
10
|
import { useState, useCallback, useRef, useEffect } from 'react';
|
|
@@ -21,12 +22,12 @@ import {
|
|
|
21
22
|
import {
|
|
22
23
|
applyBotUsernameSuffix,
|
|
23
24
|
stripDisallowedUsernameCharacters,
|
|
24
|
-
|
|
25
|
+
usernameCheckForAccountKind,
|
|
25
26
|
BOT_USERNAME_INVALID_MESSAGE,
|
|
26
27
|
USERNAME_INVALID_MESSAGE,
|
|
27
28
|
USERNAME_MAX_LENGTH,
|
|
28
29
|
USERNAME_MIN_LENGTH,
|
|
29
|
-
} from '@oxy.so/contracts';
|
|
30
|
+
} from '@oxy.so/contracts/client';
|
|
30
31
|
import type { BaseScreenProps } from '../types/navigation';
|
|
31
32
|
import { useI18n } from '../hooks/useI18n';
|
|
32
33
|
import { useSurfaceHeader } from '../hooks/useSurfaceHeader';
|
|
@@ -196,9 +197,9 @@ const CreateAccountScreen: React.FC<BaseScreenProps> = ({ onClose, goBack, paren
|
|
|
196
197
|
// somebody who typed `a.b` to append `bot` sends them to be refused a second
|
|
197
198
|
// time. The issue is read to choose between two LOCALIZED strings rather than
|
|
198
199
|
// shown directly — the schema's message is English, and this screen is not.
|
|
199
|
-
const
|
|
200
|
-
if (!
|
|
201
|
-
const failedTheLabel =
|
|
200
|
+
const checked = usernameCheckForAccountKind(forKind)(value, []);
|
|
201
|
+
if (!checked.ok) {
|
|
202
|
+
const failedTheLabel = checked.issue.message === BOT_USERNAME_INVALID_MESSAGE;
|
|
202
203
|
setUsernameStatus('invalid');
|
|
203
204
|
setUsernameMessage(
|
|
204
205
|
failedTheLabel
|
|
@@ -282,7 +283,7 @@ const CreateAccountScreen: React.FC<BaseScreenProps> = ({ onClose, goBack, paren
|
|
|
282
283
|
}
|
|
283
284
|
if (kind === 'organization') {
|
|
284
285
|
setDisplayNameError(
|
|
285
|
-
|
|
286
|
+
checkContract(organizationNameCheck, trimmed) !== null
|
|
286
287
|
? ''
|
|
287
288
|
: t('organizationDirectory.invalidName'),
|
|
288
289
|
);
|
|
@@ -5,7 +5,6 @@ import { Switch } from '@oxy.so/bloom/switch';
|
|
|
5
5
|
import { useTheme } from '@oxy.so/bloom/theme';
|
|
6
6
|
import type { UserPreferences } from '@oxy.so/core';
|
|
7
7
|
import { getNativeLanguageName } from '@oxy.so/core';
|
|
8
|
-
import { DisplayCurrencySelector } from '../components/DisplayCurrencySelector';
|
|
9
8
|
import type { BaseScreenProps } from '../types/navigation';
|
|
10
9
|
import { SettingsIcon } from '../components/SettingsIcon';
|
|
11
10
|
import { useI18n } from '../hooks/useI18n';
|
|
@@ -159,7 +158,6 @@ const PreferencesScreen: React.FC<BaseScreenProps> = ({ onClose, goBack, navigat
|
|
|
159
158
|
</SettingsListGroup>
|
|
160
159
|
|
|
161
160
|
<SettingsListGroup title={t('preferences.sections.region') || 'Region'}>
|
|
162
|
-
<DisplayCurrencySelector />
|
|
163
161
|
<SettingsListItem
|
|
164
162
|
icon={<SettingsIcon name="clock-outline" color={bloomTheme.colors.info} />}
|
|
165
163
|
title={t('preferences.items.timezone.title') || 'Timezone'}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The self-custody identity (`@oxy.so/core/crypto`: secp256k1, bip39, the AEAD
|
|
3
|
+
* cipher — ~32 KB gzipped on web), loaded where a key or signature is used.
|
|
4
|
+
*
|
|
5
|
+
* Importing it statically put it in every app's startup JavaScript, although a
|
|
6
|
+
* reader without an identity key never touches it before first paint. Core's
|
|
7
|
+
* `crypto` entry installs its polyfill before anything else, so loading it
|
|
8
|
+
* late keeps the order every key operation relies on.
|
|
9
|
+
*/
|
|
10
|
+
export const loadIdentityCrypto = () => import('@oxy.so/core/crypto');
|