@oxyhq/services 22.1.0 → 22.3.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 +6 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/ui/components/FollowButton.js +24 -19
- package/lib/commonjs/ui/components/FollowButton.js.map +1 -1
- package/lib/commonjs/ui/components/OxyAuthChooser.js +107 -106
- package/lib/commonjs/ui/components/OxyAuthChooser.js.map +1 -1
- package/lib/commonjs/ui/components/oxyAuthChooserStyles.js +0 -19
- package/lib/commonjs/ui/components/oxyAuthChooserStyles.js.map +1 -1
- package/lib/commonjs/ui/components/passkeyHubPopup.js +33 -0
- package/lib/commonjs/ui/components/passkeyHubPopup.js.map +1 -0
- package/lib/commonjs/ui/context/OxyContext.js +7 -1
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/context/useOxyAccountGraph.js +2 -0
- package/lib/commonjs/ui/context/useOxyAccountGraph.js.map +1 -1
- package/lib/commonjs/ui/hooks/useFollow.js +53 -10
- package/lib/commonjs/ui/hooks/useFollow.js.map +1 -1
- package/lib/commonjs/ui/hooks/useSessionManagement.js +2 -0
- package/lib/commonjs/ui/hooks/useSessionManagement.js.map +1 -1
- package/lib/commonjs/ui/index.js +6 -0
- package/lib/commonjs/ui/index.js.map +1 -1
- package/lib/commonjs/ui/stores/followStore.js +276 -92
- package/lib/commonjs/ui/stores/followStore.js.map +1 -1
- package/lib/commonjs/ui/stores/resetSessionScopedStores.js +14 -0
- package/lib/commonjs/ui/stores/resetSessionScopedStores.js.map +1 -0
- package/lib/module/index.js +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/ui/components/FollowButton.js +24 -19
- package/lib/module/ui/components/FollowButton.js.map +1 -1
- package/lib/module/ui/components/OxyAuthChooser.js +108 -107
- package/lib/module/ui/components/OxyAuthChooser.js.map +1 -1
- package/lib/module/ui/components/oxyAuthChooserStyles.js +0 -19
- package/lib/module/ui/components/oxyAuthChooserStyles.js.map +1 -1
- package/lib/module/ui/components/passkeyHubPopup.js +29 -0
- package/lib/module/ui/components/passkeyHubPopup.js.map +1 -0
- package/lib/module/ui/context/OxyContext.js +7 -1
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/context/useOxyAccountGraph.js +2 -0
- package/lib/module/ui/context/useOxyAccountGraph.js.map +1 -1
- package/lib/module/ui/hooks/useFollow.js +51 -9
- package/lib/module/ui/hooks/useFollow.js.map +1 -1
- package/lib/module/ui/hooks/useSessionManagement.js +2 -0
- package/lib/module/ui/hooks/useSessionManagement.js.map +1 -1
- package/lib/module/ui/index.js +1 -1
- package/lib/module/ui/index.js.map +1 -1
- package/lib/module/ui/stores/followStore.js +274 -91
- package/lib/module/ui/stores/followStore.js.map +1 -1
- package/lib/module/ui/stores/resetSessionScopedStores.js +11 -0
- package/lib/module/ui/stores/resetSessionScopedStores.js.map +1 -0
- package/lib/typescript/commonjs/index.d.ts +1 -1
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/FollowButton.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/OxyAuthChooser.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/oxyAuthChooserStyles.d.ts +0 -19
- package/lib/typescript/commonjs/ui/components/oxyAuthChooserStyles.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/passkeyHubPopup.d.ts +23 -0
- package/lib/typescript/commonjs/ui/components/passkeyHubPopup.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/useOxyAccountGraph.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useFollow.d.ts +13 -2
- package/lib/typescript/commonjs/ui/hooks/useFollow.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useSessionManagement.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/index.d.ts +1 -1
- package/lib/typescript/commonjs/ui/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/stores/followStore.d.ts +4 -1
- package/lib/typescript/commonjs/ui/stores/followStore.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/stores/resetSessionScopedStores.d.ts +3 -0
- package/lib/typescript/commonjs/ui/stores/resetSessionScopedStores.d.ts.map +1 -0
- package/lib/typescript/module/index.d.ts +1 -1
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/FollowButton.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/OxyAuthChooser.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/oxyAuthChooserStyles.d.ts +0 -19
- package/lib/typescript/module/ui/components/oxyAuthChooserStyles.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/passkeyHubPopup.d.ts +23 -0
- package/lib/typescript/module/ui/components/passkeyHubPopup.d.ts.map +1 -0
- package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/useOxyAccountGraph.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useFollow.d.ts +13 -2
- package/lib/typescript/module/ui/hooks/useFollow.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useSessionManagement.d.ts.map +1 -1
- package/lib/typescript/module/ui/index.d.ts +1 -1
- package/lib/typescript/module/ui/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/stores/followStore.d.ts +4 -1
- package/lib/typescript/module/ui/stores/followStore.d.ts.map +1 -1
- package/lib/typescript/module/ui/stores/resetSessionScopedStores.d.ts +3 -0
- package/lib/typescript/module/ui/stores/resetSessionScopedStores.d.ts.map +1 -0
- package/package.json +12 -12
- package/src/index.ts +1 -1
- package/src/ui/components/FollowButton.tsx +22 -20
- package/src/ui/components/OxyAuthChooser.tsx +125 -89
- package/src/ui/components/oxyAuthChooserStyles.ts +0 -19
- package/src/ui/components/passkeyHubPopup.ts +28 -0
- package/src/ui/context/OxyContext.tsx +6 -0
- package/src/ui/context/useOxyAccountGraph.ts +2 -0
- package/src/ui/hooks/useFollow.ts +51 -9
- package/src/ui/hooks/useSessionManagement.ts +2 -0
- package/src/ui/index.ts +1 -1
- package/src/ui/stores/__tests__/followStore.test.ts +178 -0
- package/src/ui/stores/followStore.ts +277 -106
- package/src/ui/stores/resetSessionScopedStores.ts +8 -0
package/lib/commonjs/index.js
CHANGED
|
@@ -501,6 +501,12 @@ Object.defineProperty(exports, "useSecurityInfo", {
|
|
|
501
501
|
return _useServicesQueries.useSecurityInfo;
|
|
502
502
|
}
|
|
503
503
|
});
|
|
504
|
+
Object.defineProperty(exports, "useSeedFollowStatuses", {
|
|
505
|
+
enumerable: true,
|
|
506
|
+
get: function () {
|
|
507
|
+
return _useFollow.useSeedFollowStatuses;
|
|
508
|
+
}
|
|
509
|
+
});
|
|
504
510
|
Object.defineProperty(exports, "useSession", {
|
|
505
511
|
enumerable: true,
|
|
506
512
|
get: function () {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_core","require","_reactNative","_OxyProvider","_interopRequireDefault","_OxyContext","_useAuth","_FontLoader","_authStore","_accountStore","_assetStore","_useAssets","_useFileDownloadUrl","_useFollow","_useStorage","_useAccountQueries","_useServicesQueries","_useSecurityQueries","_useAuthMethods","_usePaymentQueries","_useAccountMutations","_useServicesMutations","_mutationFactory","_mutationKeys","_queryKeys","_useMutationStatus","_useOnlineStatus","_useOxyEvent","_errorHandlers","_useFileFiltering","_OxySignInButton","_OxyAuthPrompt","_OxyOAuthCallback","_OxyConsentScreen","_RequireOxyAuth","_FollowButton","_OxyPayButton","_LogoIcon","_LogoText","_ProfileButton","_OxyAuthChooser","_useSwitchableAccounts","_ProfileScreen","_ManageAccountScreen","_NotificationsScreen","_PreferencesScreen","_ConnectedAppsScreen","_CreateAccountScreen","_AccountMembersScreen","_AccountSettingsScreen","_bottomSheetManager","_accountDialogManager","e","__esModule","default","setPlatformOS","Platform","OS"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":"
|
|
1
|
+
{"version":3,"names":["_core","require","_reactNative","_OxyProvider","_interopRequireDefault","_OxyContext","_useAuth","_FontLoader","_authStore","_accountStore","_assetStore","_useAssets","_useFileDownloadUrl","_useFollow","_useStorage","_useAccountQueries","_useServicesQueries","_useSecurityQueries","_useAuthMethods","_usePaymentQueries","_useAccountMutations","_useServicesMutations","_mutationFactory","_mutationKeys","_queryKeys","_useMutationStatus","_useOnlineStatus","_useOxyEvent","_errorHandlers","_useFileFiltering","_OxySignInButton","_OxyAuthPrompt","_OxyOAuthCallback","_OxyConsentScreen","_RequireOxyAuth","_FollowButton","_OxyPayButton","_LogoIcon","_LogoText","_ProfileButton","_OxyAuthChooser","_useSwitchableAccounts","_ProfileScreen","_ManageAccountScreen","_NotificationsScreen","_PreferencesScreen","_ConnectedAppsScreen","_CreateAccountScreen","_AccountMembersScreen","_AccountSettingsScreen","_bottomSheetManager","_accountDialogManager","e","__esModule","default","setPlatformOS","Platform","OS"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -32,7 +32,7 @@ const isMultiMode = props => 'userIds' in props && Array.isArray(props.userIds);
|
|
|
32
32
|
const FollowButtonInner = /*#__PURE__*/(0, _react.memo)(function FollowButtonInner({
|
|
33
33
|
userId,
|
|
34
34
|
oxyServices,
|
|
35
|
-
initiallyFollowing
|
|
35
|
+
initiallyFollowing,
|
|
36
36
|
size = 'medium',
|
|
37
37
|
onFollowChange,
|
|
38
38
|
style,
|
|
@@ -46,17 +46,18 @@ const FollowButtonInner = /*#__PURE__*/(0, _react.memo)(function FollowButtonInn
|
|
|
46
46
|
} = (0, _theme.useTheme)();
|
|
47
47
|
const {
|
|
48
48
|
isFollowing,
|
|
49
|
+
isKnown,
|
|
49
50
|
isLoading,
|
|
50
51
|
toggleFollow,
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
} = (0, _useFollow.useFollowForButton)(userId, oxyServices);
|
|
52
|
+
resolveStatus
|
|
53
|
+
} = (0, _useFollow.useFollowForButton)(userId, oxyServices, initiallyFollowing);
|
|
54
54
|
const handlePress = (0, _react.useCallback)(async event => {
|
|
55
55
|
if (preventParentActions && event?.preventDefault) {
|
|
56
56
|
event.preventDefault();
|
|
57
57
|
event.stopPropagation?.();
|
|
58
58
|
}
|
|
59
|
-
|
|
59
|
+
// Ignore presses while a mutation is in flight or the status is still unknown.
|
|
60
|
+
if (disabled || isLoading || !isKnown) return;
|
|
60
61
|
try {
|
|
61
62
|
await toggleFollow();
|
|
62
63
|
onFollowChange?.(!isFollowing);
|
|
@@ -64,32 +65,36 @@ const FollowButtonInner = /*#__PURE__*/(0, _react.memo)(function FollowButtonInn
|
|
|
64
65
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
65
66
|
_bloom.toast.error(error.message || 'Failed to update follow status');
|
|
66
67
|
}
|
|
67
|
-
}, [disabled, isLoading, toggleFollow, onFollowChange, isFollowing, preventParentActions]);
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}, [userId, initiallyFollowing]);
|
|
68
|
+
}, [disabled, isLoading, isKnown, toggleFollow, onFollowChange, isFollowing, preventParentActions]);
|
|
69
|
+
|
|
70
|
+
// Enqueue a batched status fetch ONLY when the status is genuinely unknown
|
|
71
|
+
// (not seeded from `initiallyFollowing`, not already resolved). All buttons
|
|
72
|
+
// that enqueue in the same commit coalesce into a single bulk request; known/
|
|
73
|
+
// seeded ids never fetch. Once resolved, `isKnown` flips true and this no-ops.
|
|
74
74
|
(0, _react.useEffect)(() => {
|
|
75
|
-
if (
|
|
76
|
-
}, [
|
|
77
|
-
|
|
75
|
+
if (!isKnown) resolveStatus();
|
|
76
|
+
}, [isKnown, resolveStatus]);
|
|
77
|
+
|
|
78
|
+
// While the status is genuinely unknown (being resolved), present a NEUTRAL,
|
|
79
|
+
// non-interactive state rather than a definitive "Follow".
|
|
80
|
+
const isBusy = isLoading || !isKnown;
|
|
81
|
+
const showFollowing = isKnown && isFollowing;
|
|
82
|
+
const showSpinner = showLoadingState && isBusy;
|
|
78
83
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_button.Button, {
|
|
79
|
-
variant:
|
|
84
|
+
variant: showFollowing ? 'secondary' : 'primary',
|
|
80
85
|
size: size,
|
|
81
86
|
onPress: () => {
|
|
82
87
|
void handlePress();
|
|
83
88
|
},
|
|
84
|
-
disabled: disabled ||
|
|
89
|
+
disabled: disabled || isBusy,
|
|
85
90
|
style: style,
|
|
86
91
|
textStyle: textStyle,
|
|
87
92
|
icon: showSpinner ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_loading.Loading, {
|
|
88
93
|
variant: "inline",
|
|
89
94
|
size: "small",
|
|
90
|
-
color:
|
|
95
|
+
color: showFollowing ? colors.text : colors.primaryForeground
|
|
91
96
|
}) : undefined,
|
|
92
|
-
children: showSpinner ? undefined : isFollowing ? 'Following' : 'Follow'
|
|
97
|
+
children: showSpinner ? undefined : isKnown ? isFollowing ? 'Following' : 'Follow' : undefined
|
|
93
98
|
});
|
|
94
99
|
});
|
|
95
100
|
const FollowButtonMultiInner = /*#__PURE__*/(0, _react.memo)(function FollowButtonMultiInner({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_react","require","_OxyContext","_bloom","_button","_loading","_useFollow","_followStore","_theme","_shallow","_jsxRuntime","DEFAULT_FOLLOW_ALL_LABEL","DEFAULT_FOLLOWED_ALL_LABEL","isMultiMode","props","Array","isArray","userIds","FollowButtonInner","memo","userId","oxyServices","initiallyFollowing","size","onFollowChange","style","textStyle","disabled","showLoadingState","preventParentActions","colors","useTheme","isFollowing","
|
|
1
|
+
{"version":3,"names":["_react","require","_OxyContext","_bloom","_button","_loading","_useFollow","_followStore","_theme","_shallow","_jsxRuntime","DEFAULT_FOLLOW_ALL_LABEL","DEFAULT_FOLLOWED_ALL_LABEL","isMultiMode","props","Array","isArray","userIds","FollowButtonInner","memo","userId","oxyServices","initiallyFollowing","size","onFollowChange","style","textStyle","disabled","showLoadingState","preventParentActions","colors","useTheme","isFollowing","isKnown","isLoading","toggleFollow","resolveStatus","useFollowForButton","handlePress","useCallback","event","preventDefault","stopPropagation","err","error","Error","String","toast","message","useEffect","isBusy","showFollowing","showSpinner","jsx","Button","variant","onPress","icon","Loading","color","text","primaryForeground","undefined","children","FollowButtonMultiInner","initiallyAllFollowing","followAllLabel","followedAllLabel","onBulkFollow","onBulkUnfollow","follow","useFollow","followAllUsers","unfollowAllUsers","isAnyLoading","initialFollowStatuses","useFollowStore","useShallow","state","statuses","uid","Object","prototype","hasOwnProperty","call","followingUsers","hasKnownNotFollowing","some","storeAllFollowing","allFollowing","fetchAllStatuses","isSubmitting","setIsSubmitting","useState","userIdsKey","useMemo","join","result","allAlreadyFollowing","followedCount","results","length","every","entry","alreadyFollowing","anyFollowed","success","FollowButton","canUsePrivateApi","user","currentUser","useOxy","currentUserId","id","trim","rawUserIds","multiUserIds","seen","Set","cleaned","raw","has","add","push","targetUserId","exports","_default","default"],"sourceRoot":"../../../../src","sources":["ui/components/FollowButton.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -10,6 +10,7 @@ var _vectorIcons = require("@expo/vector-icons");
|
|
|
10
10
|
var _reactNativeQrcodeSvg = _interopRequireDefault(require("react-native-qrcode-svg"));
|
|
11
11
|
var _avatar = require("@oxyhq/bloom/avatar");
|
|
12
12
|
var _button = require("@oxyhq/bloom/button");
|
|
13
|
+
var _bloom = require("@oxyhq/bloom");
|
|
13
14
|
var _typography = require("@oxyhq/bloom/typography");
|
|
14
15
|
var _theme = require("@oxyhq/bloom/theme");
|
|
15
16
|
var _core = require("@oxyhq/core");
|
|
@@ -114,46 +115,51 @@ const OxyAuthChooser = ({
|
|
|
114
115
|
} = (0, _useI18n.useI18n)();
|
|
115
116
|
const queryClient = (0, _reactQuery.useQueryClient)();
|
|
116
117
|
|
|
117
|
-
// A credential minted for `oxy.so` can only be
|
|
118
|
-
// dev host) —
|
|
119
|
-
//
|
|
120
|
-
//
|
|
121
|
-
//
|
|
122
|
-
|
|
118
|
+
// A credential minted for `oxy.so` can only be ASSERTED there (or a loopback
|
|
119
|
+
// dev host) — a hard WebAuthn RP-ID boundary the browser enforces, not
|
|
120
|
+
// feature detection. On a first-party Oxy web origin the ceremony runs
|
|
121
|
+
// directly ('direct'). On any OTHER web origin (b2) it can't run locally, so
|
|
122
|
+
// the passkey action instead opens a popup at the auth.oxy.so passkey hub —
|
|
123
|
+
// where the SAME ceremony IS first-party — and relays the resulting session
|
|
124
|
+
// back via `AccountDialogController.startPasskeyHubSignIn` ('hub'). Native
|
|
125
|
+
// has neither: Commons owns identity there ('none'). `isOxyRpOrigin()` reads
|
|
126
|
+
// `location` once and is stable for the component's lifetime.
|
|
127
|
+
const passkeyMode = (0, _react.useMemo)(() => {
|
|
128
|
+
if (!(0, _isWebBrowser.isWebBrowser)()) return 'none';
|
|
129
|
+
return (0, _core.isOxyRpOrigin)() ? 'direct' : 'hub';
|
|
130
|
+
}, []);
|
|
131
|
+
const passkeyAvailable = passkeyMode !== 'none';
|
|
123
132
|
const [signInPasskeyPending, setSignInPasskeyPending] = (0, _react.useState)(false);
|
|
124
|
-
const [signInPasskeyError, setSignInPasskeyError] = (0, _react.useState)(null);
|
|
125
133
|
const handleSignInWithPasskey = (0, _react.useCallback)(async () => {
|
|
126
134
|
if (signInPasskeyPending) return;
|
|
127
135
|
setSignInPasskeyPending(true);
|
|
128
|
-
setSignInPasskeyError(null);
|
|
129
136
|
try {
|
|
130
137
|
await signInWithPasskey();
|
|
131
138
|
onComplete?.();
|
|
132
139
|
} catch (error) {
|
|
133
140
|
// A cancelled/failed ceremony keeps the view open so the user can retry
|
|
134
|
-
// or fall back to the QR — surface a
|
|
135
|
-
|
|
141
|
+
// or fall back to the QR — surface the reason as a toast (owner mandate:
|
|
142
|
+
// errors never render inline inside the dialog), never swallow.
|
|
143
|
+
_bloom.toast.error(error instanceof Error && error.message ? error.message : t('accountSwitcher.toasts.passkeySignInFailed') || 'Passkey sign-in failed.');
|
|
136
144
|
} finally {
|
|
137
145
|
setSignInPasskeyPending(false);
|
|
138
146
|
}
|
|
139
|
-
}, [signInPasskeyPending, signInWithPasskey, onComplete]);
|
|
147
|
+
}, [signInPasskeyPending, signInWithPasskey, onComplete, t]);
|
|
140
148
|
const [createPasskeyPending, setCreatePasskeyPending] = (0, _react.useState)(false);
|
|
141
|
-
const [createPasskeyError, setCreatePasskeyError] = (0, _react.useState)(null);
|
|
142
149
|
const handleCreateWithPasskey = (0, _react.useCallback)(async username => {
|
|
143
150
|
if (createPasskeyPending) return;
|
|
144
151
|
setCreatePasskeyPending(true);
|
|
145
|
-
setCreatePasskeyError(null);
|
|
146
152
|
try {
|
|
147
153
|
await registerWithPasskey({
|
|
148
154
|
username
|
|
149
155
|
});
|
|
150
156
|
onComplete?.();
|
|
151
157
|
} catch (error) {
|
|
152
|
-
|
|
158
|
+
_bloom.toast.error(error instanceof Error && error.message ? error.message : t('signup.toasts.passkeyCreateFailed') || 'Account creation failed.');
|
|
153
159
|
} finally {
|
|
154
160
|
setCreatePasskeyPending(false);
|
|
155
161
|
}
|
|
156
|
-
}, [createPasskeyPending, registerWithPasskey, onComplete]);
|
|
162
|
+
}, [createPasskeyPending, registerWithPasskey, onComplete, t]);
|
|
157
163
|
|
|
158
164
|
// Bind the headless controller. `getSnapshot` returns a stable reference
|
|
159
165
|
// between changes, so it is `useSyncExternalStore`-safe. Guard the no-provider
|
|
@@ -165,6 +171,28 @@ const OxyAuthChooser = ({
|
|
|
165
171
|
view
|
|
166
172
|
} = snapshot;
|
|
167
173
|
|
|
174
|
+
// Sign-in device-flow failures land on `snapshot.signIn` asynchronously (poll /
|
|
175
|
+
// socket / popup cancel). Toast at the controller notification site — never an
|
|
176
|
+
// inline banner in the QR view (owner mandate, same contract as account-switch).
|
|
177
|
+
(0, _react.useEffect)(() => {
|
|
178
|
+
if (!controller) return;
|
|
179
|
+
let lastToastedSignInError = null;
|
|
180
|
+
const maybeToastSignInError = () => {
|
|
181
|
+
const {
|
|
182
|
+
signIn
|
|
183
|
+
} = controller.getSnapshot();
|
|
184
|
+
if (signIn.phase === 'error' && signIn.error && signIn.error !== lastToastedSignInError) {
|
|
185
|
+
lastToastedSignInError = signIn.error;
|
|
186
|
+
_bloom.toast.error(signIn.error || t('accountSwitcher.toasts.signInFailed') || 'Sign-in failed. Please try again.');
|
|
187
|
+
}
|
|
188
|
+
if (signIn.phase !== 'error') {
|
|
189
|
+
lastToastedSignInError = null;
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
maybeToastSignInError();
|
|
193
|
+
return controller.subscribe(maybeToastSignInError);
|
|
194
|
+
}, [controller, t]);
|
|
195
|
+
|
|
168
196
|
// Web: "Sign in with Oxy" auto-starts the instant the sign-in entry is
|
|
169
197
|
// reached — no button tap needed. `signInWithOxy()` tries the shared
|
|
170
198
|
// keychain first (silently, native-only) then falls to the QR handoff,
|
|
@@ -188,19 +216,24 @@ const OxyAuthChooser = ({
|
|
|
188
216
|
setSwitching(true);
|
|
189
217
|
try {
|
|
190
218
|
await controller.switchTo(accountId);
|
|
191
|
-
//
|
|
192
|
-
//
|
|
193
|
-
//
|
|
194
|
-
//
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
219
|
+
// `switchTo` never throws — it records a failure on the controller's
|
|
220
|
+
// snapshot. Read it back at the point the switch settles and surface it
|
|
221
|
+
// as a toast (event-driven, at the failure site — NOT an inline banner
|
|
222
|
+
// and NOT a snapshot-reaction). On success reload the app's account
|
|
223
|
+
// graph and drop cached account-scoped data so the new active identity
|
|
224
|
+
// re-fetches — the same side effects the context's own
|
|
225
|
+
// `switchToAccount` performs.
|
|
226
|
+
if (controller.getSnapshot().error) {
|
|
227
|
+
_bloom.toast.error(t('accountSwitcher.toasts.switchFailed') || 'There was a problem switching accounts. Please try again.');
|
|
228
|
+
return;
|
|
199
229
|
}
|
|
230
|
+
void refreshAccounts();
|
|
231
|
+
queryClient.invalidateQueries();
|
|
232
|
+
onComplete?.();
|
|
200
233
|
} finally {
|
|
201
234
|
setSwitching(false);
|
|
202
235
|
}
|
|
203
|
-
}, [controller, switching, snapshot.activeAccountId, onComplete, refreshAccounts, queryClient]);
|
|
236
|
+
}, [controller, switching, snapshot.activeAccountId, onComplete, refreshAccounts, queryClient, t]);
|
|
204
237
|
const handleManage = (0, _react.useCallback)(() => {
|
|
205
238
|
onComplete?.();
|
|
206
239
|
showBottomSheet?.('ManageAccount');
|
|
@@ -230,9 +263,13 @@ const OxyAuthChooser = ({
|
|
|
230
263
|
t: t,
|
|
231
264
|
onRetry: () => void controller.showQr(),
|
|
232
265
|
passkeyAvailable: passkeyAvailable,
|
|
233
|
-
onSignInWithPasskey: () => void handleSignInWithPasskey()
|
|
234
|
-
|
|
235
|
-
|
|
266
|
+
onSignInWithPasskey: passkeyMode === 'direct' ? () => void handleSignInWithPasskey() : () => void controller.startPasskeyHubSignIn()
|
|
267
|
+
// The hub-popup flow's pending state is `snapshot.signIn` (already
|
|
268
|
+
// rendered elsewhere in this view) — only the DIRECT ceremony uses this
|
|
269
|
+
// component-local pending flag. A ceremony FAILURE is surfaced as a
|
|
270
|
+
// toast by `handleSignInWithPasskey`, never inline.
|
|
271
|
+
,
|
|
272
|
+
passkeyPending: passkeyMode === 'direct' ? signInPasskeyPending : false,
|
|
236
273
|
onCreateAccount: () => controller.startSignup()
|
|
237
274
|
});
|
|
238
275
|
}
|
|
@@ -242,10 +279,10 @@ const OxyAuthChooser = ({
|
|
|
242
279
|
theme: theme,
|
|
243
280
|
t: t,
|
|
244
281
|
oxyServices: oxyServices,
|
|
245
|
-
|
|
282
|
+
passkeyMode: passkeyMode,
|
|
246
283
|
onCreateWithPasskey: handleCreateWithPasskey,
|
|
247
284
|
createPending: createPasskeyPending,
|
|
248
|
-
|
|
285
|
+
onOpenHub: () => void controller.startPasskeyHubSignIn(),
|
|
249
286
|
onBackToSignIn: () => controller.setView('signin')
|
|
250
287
|
});
|
|
251
288
|
}
|
|
@@ -264,33 +301,6 @@ const OxyAuthChooser = ({
|
|
|
264
301
|
// Accounts view
|
|
265
302
|
// ---------------------------------------------------------------------------
|
|
266
303
|
|
|
267
|
-
/**
|
|
268
|
-
* Inline error banner for `snapshot.error` (the account-list / switch-failure
|
|
269
|
-
* error — distinct from `snapshot.signIn.error`, which `QrView` already
|
|
270
|
-
* renders). `AccountsView` and `SignInView` previously swallowed it: a
|
|
271
|
-
* blocked/failed switch left the user with no feedback outside the QR view.
|
|
272
|
-
*/
|
|
273
|
-
const ErrorBanner = ({
|
|
274
|
-
error,
|
|
275
|
-
theme
|
|
276
|
-
}) => {
|
|
277
|
-
if (!error) return null;
|
|
278
|
-
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
279
|
-
style: [_oxyAuthChooserStyles.authChooserStyles.errorBanner, {
|
|
280
|
-
backgroundColor: theme.colors.negativeSubtle
|
|
281
|
-
}],
|
|
282
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_vectorIcons.MaterialCommunityIcons, {
|
|
283
|
-
name: "alert-circle-outline",
|
|
284
|
-
size: 18,
|
|
285
|
-
color: theme.colors.error
|
|
286
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_typography.Text, {
|
|
287
|
-
style: [_oxyAuthChooserStyles.authChooserStyles.errorBannerText, {
|
|
288
|
-
color: theme.colors.error
|
|
289
|
-
}],
|
|
290
|
-
children: error
|
|
291
|
-
})]
|
|
292
|
-
});
|
|
293
|
-
};
|
|
294
304
|
const AccountsView = ({
|
|
295
305
|
snapshot,
|
|
296
306
|
theme,
|
|
@@ -314,10 +324,7 @@ const AccountsView = ({
|
|
|
314
324
|
}
|
|
315
325
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
316
326
|
style: _oxyAuthChooserStyles.authChooserStyles.rows,
|
|
317
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(
|
|
318
|
-
error: snapshot.error,
|
|
319
|
-
theme: theme
|
|
320
|
-
}), snapshot.accounts.map(account => /*#__PURE__*/(0, _jsxRuntime.jsx)(AccountRow, {
|
|
327
|
+
children: [snapshot.accounts.map(account => /*#__PURE__*/(0, _jsxRuntime.jsx)(AccountRow, {
|
|
321
328
|
account: account,
|
|
322
329
|
theme: theme,
|
|
323
330
|
switching: snapshot.switchingAccountId === account.accountId,
|
|
@@ -443,10 +450,7 @@ const SignInView = ({
|
|
|
443
450
|
onCreateAccount
|
|
444
451
|
}) => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
445
452
|
style: _oxyAuthChooserStyles.authChooserStyles.signInBlock,
|
|
446
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.
|
|
447
|
-
error: snapshot.error,
|
|
448
|
-
theme: theme
|
|
449
|
-
}), snapshot.accounts.length > 0 ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
453
|
+
children: [snapshot.accounts.length > 0 ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
450
454
|
style: _oxyAuthChooserStyles.authChooserStyles.rows,
|
|
451
455
|
children: [snapshot.accounts.map(account => /*#__PURE__*/(0, _jsxRuntime.jsx)(AccountRow, {
|
|
452
456
|
account: account,
|
|
@@ -547,7 +551,6 @@ const QrView = ({
|
|
|
547
551
|
passkeyAvailable,
|
|
548
552
|
onSignInWithPasskey,
|
|
549
553
|
passkeyPending,
|
|
550
|
-
passkeyError,
|
|
551
554
|
onCreateAccount
|
|
552
555
|
}) => {
|
|
553
556
|
const {
|
|
@@ -555,25 +558,21 @@ const QrView = ({
|
|
|
555
558
|
commonsAvailability
|
|
556
559
|
} = snapshot;
|
|
557
560
|
const [showQrAnyway, setShowQrAnyway] = (0, _react.useState)(false);
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
})
|
|
571
|
-
}), passkeyError ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_typography.Text, {
|
|
572
|
-
style: [_oxyAuthChooserStyles.authChooserStyles.errorText, {
|
|
573
|
-
color: theme.colors.error
|
|
561
|
+
|
|
562
|
+
// A ceremony FAILURE is surfaced as a toast by `handleSignInWithPasskey` — the
|
|
563
|
+
// link itself never renders an inline error (owner mandate).
|
|
564
|
+
const passkeyLink = passkeyAvailable && signIn.phase !== 'authorized' ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
|
|
565
|
+
onPress: onSignInWithPasskey,
|
|
566
|
+
disabled: passkeyPending,
|
|
567
|
+
accessibilityRole: "button",
|
|
568
|
+
style: _oxyAuthChooserStyles.authChooserStyles.footerLink,
|
|
569
|
+
testID: "passkey-signin-link",
|
|
570
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_typography.Text, {
|
|
571
|
+
style: [_oxyAuthChooserStyles.authChooserStyles.linkText, {
|
|
572
|
+
color: theme.colors.textSecondary
|
|
574
573
|
}],
|
|
575
|
-
children:
|
|
576
|
-
})
|
|
574
|
+
children: passkeyPending ? t('accountSwitcher.passkeySigningIn') || 'Signing in…' : t('accountSwitcher.useIdentityOnDevice') || 'No Commons? Use a passkey on this device instead'
|
|
575
|
+
})
|
|
577
576
|
}) : null;
|
|
578
577
|
|
|
579
578
|
// Native, Commons confirmed NOT installed: a same-device QR is a dead
|
|
@@ -610,14 +609,11 @@ const QrView = ({
|
|
|
610
609
|
});
|
|
611
610
|
}
|
|
612
611
|
if (signIn.phase === 'error') {
|
|
612
|
+
// The failure reason is toasted by the controller subscription above — no
|
|
613
|
+
// inline error copy here (owner mandate).
|
|
613
614
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
614
615
|
style: _oxyAuthChooserStyles.authChooserStyles.centeredBlock,
|
|
615
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(
|
|
616
|
-
style: [_oxyAuthChooserStyles.authChooserStyles.errorText, {
|
|
617
|
-
color: theme.colors.error
|
|
618
|
-
}],
|
|
619
|
-
children: signIn.error || 'Sign-in failed. Please try again.'
|
|
620
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_button.Button, {
|
|
616
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_button.Button, {
|
|
621
617
|
variant: "secondary",
|
|
622
618
|
onPress: onRetry,
|
|
623
619
|
style: _oxyAuthChooserStyles.authChooserStyles.secondaryButton,
|
|
@@ -674,7 +670,7 @@ const QrView = ({
|
|
|
674
670
|
// ---------------------------------------------------------------------------
|
|
675
671
|
|
|
676
672
|
/** Debounced username-availability check over the existing SDK method. */
|
|
677
|
-
function useUsernameAvailability(oxyServices) {
|
|
673
|
+
function useUsernameAvailability(oxyServices, t) {
|
|
678
674
|
const [status, setStatus] = (0, _react.useState)('idle');
|
|
679
675
|
const timerRef = (0, _react.useRef)(null);
|
|
680
676
|
const requestSeqRef = (0, _react.useRef)(0);
|
|
@@ -692,10 +688,13 @@ function useUsernameAvailability(oxyServices) {
|
|
|
692
688
|
setStatus(result.available ? 'available' : 'taken');
|
|
693
689
|
}).catch(() => {
|
|
694
690
|
if (seq !== requestSeqRef.current) return;
|
|
695
|
-
|
|
691
|
+
// Reset the (now stale) verdict rather than block on it, and surface
|
|
692
|
+
// the failure as a toast — never an inline error inside the dialog.
|
|
693
|
+
setStatus('idle');
|
|
694
|
+
_bloom.toast.error(t('accounts.create.username.checkFailed') || 'Could not check availability');
|
|
696
695
|
});
|
|
697
696
|
}, 400);
|
|
698
|
-
}, [oxyServices]);
|
|
697
|
+
}, [oxyServices, t]);
|
|
699
698
|
return {
|
|
700
699
|
status,
|
|
701
700
|
check
|
|
@@ -735,17 +734,17 @@ const SignUpView = ({
|
|
|
735
734
|
theme,
|
|
736
735
|
t,
|
|
737
736
|
oxyServices,
|
|
738
|
-
|
|
737
|
+
passkeyMode,
|
|
739
738
|
onCreateWithPasskey,
|
|
740
739
|
createPending,
|
|
741
|
-
|
|
740
|
+
onOpenHub,
|
|
742
741
|
onBackToSignIn
|
|
743
742
|
}) => {
|
|
744
743
|
const [username, setUsername] = (0, _react.useState)('');
|
|
745
744
|
const {
|
|
746
745
|
status: usernameStatus,
|
|
747
746
|
check: checkUsername
|
|
748
|
-
} = useUsernameAvailability(oxyServices);
|
|
747
|
+
} = useUsernameAvailability(oxyServices, t);
|
|
749
748
|
const canSubmit = username.trim().length >= 3 && usernameStatus === 'available' && !createPending;
|
|
750
749
|
|
|
751
750
|
// Native — Commons owns identity creation. Mirrors the QR view's own
|
|
@@ -773,7 +772,7 @@ const SignUpView = ({
|
|
|
773
772
|
// passkey creation is the de-emphasized alternative underneath for someone
|
|
774
773
|
// who doesn't want to install anything — secondary button, introduced by
|
|
775
774
|
// its own "No Commons?" framing, same subordination as the QR view's link.
|
|
776
|
-
if (
|
|
775
|
+
if (passkeyMode === 'direct') {
|
|
777
776
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
778
777
|
style: _oxyAuthChooserStyles.authChooserStyles.signInBlock,
|
|
779
778
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_typography.Text, {
|
|
@@ -819,12 +818,7 @@ const SignUpView = ({
|
|
|
819
818
|
status: usernameStatus,
|
|
820
819
|
theme: theme,
|
|
821
820
|
t: t
|
|
822
|
-
}),
|
|
823
|
-
style: [_oxyAuthChooserStyles.authChooserStyles.errorText, {
|
|
824
|
-
color: theme.colors.error
|
|
825
|
-
}],
|
|
826
|
-
children: createError
|
|
827
|
-
}) : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(_button.Button, {
|
|
821
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_button.Button, {
|
|
828
822
|
variant: "secondary",
|
|
829
823
|
onPress: () => void onCreateWithPasskey(username.trim()),
|
|
830
824
|
disabled: !canSubmit,
|
|
@@ -839,16 +833,23 @@ const SignUpView = ({
|
|
|
839
833
|
});
|
|
840
834
|
}
|
|
841
835
|
|
|
842
|
-
// Web, non-Oxy origin: passkey creation here
|
|
843
|
-
//
|
|
844
|
-
//
|
|
836
|
+
// Web, non-Oxy origin (b2): passkey creation can't run locally here — the
|
|
837
|
+
// same WebAuthn RP-ID boundary as sign-in — so this opens the SAME
|
|
838
|
+
// auth.oxy.so passkey hub popup `QrView`'s "No Commons?" link uses.
|
|
839
|
+
// Completing EITHER a sign-in OR a fresh sign-up there relays the resulting
|
|
840
|
+
// session back via the SAME poll/socket engine `showQr` drives.
|
|
845
841
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
846
842
|
style: _oxyAuthChooserStyles.authChooserStyles.centeredBlock,
|
|
847
843
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_typography.Text, {
|
|
848
844
|
style: [_oxyAuthChooserStyles.authChooserStyles.mutedText, {
|
|
849
845
|
color: theme.colors.textSecondary
|
|
850
846
|
}],
|
|
851
|
-
children: t('signup.
|
|
847
|
+
children: t('signup.hubExplainer') || 'Create your Oxy ID in a secure window, then come right back.'
|
|
848
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_button.Button, {
|
|
849
|
+
variant: "primary",
|
|
850
|
+
onPress: onOpenHub,
|
|
851
|
+
style: _oxyAuthChooserStyles.authChooserStyles.primaryButton,
|
|
852
|
+
children: t('signup.continueInWindow') || 'Continue in a new window'
|
|
852
853
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(SignInFooterLink, {
|
|
853
854
|
theme: theme,
|
|
854
855
|
t: t,
|