@oxyhq/services 26.0.4 → 26.1.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.
Files changed (40) hide show
  1. package/lib/commonjs/ui/components/OxyAuthChooser.js +11 -11
  2. package/lib/commonjs/ui/components/OxyAuthChooser.js.map +1 -1
  3. package/lib/commonjs/ui/components/OxySignInButton.js +34 -2
  4. package/lib/commonjs/ui/components/OxySignInButton.js.map +1 -1
  5. package/lib/commonjs/ui/components/ProfileButton.js +1 -1
  6. package/lib/commonjs/ui/components/ProfileButton.js.map +1 -1
  7. package/lib/commonjs/ui/navigation/accountDialogManager.js +17 -0
  8. package/lib/commonjs/ui/navigation/accountDialogManager.js.map +1 -1
  9. package/lib/commonjs/ui/screens/AccountSettingsScreen.js +3 -5
  10. package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
  11. package/lib/module/ui/components/OxyAuthChooser.js +12 -12
  12. package/lib/module/ui/components/OxyAuthChooser.js.map +1 -1
  13. package/lib/module/ui/components/OxySignInButton.js +34 -2
  14. package/lib/module/ui/components/OxySignInButton.js.map +1 -1
  15. package/lib/module/ui/components/ProfileButton.js +1 -1
  16. package/lib/module/ui/components/ProfileButton.js.map +1 -1
  17. package/lib/module/ui/navigation/accountDialogManager.js +16 -0
  18. package/lib/module/ui/navigation/accountDialogManager.js.map +1 -1
  19. package/lib/module/ui/screens/AccountSettingsScreen.js +3 -5
  20. package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
  21. package/lib/typescript/commonjs/ui/components/OxyAuthChooser.d.ts.map +1 -1
  22. package/lib/typescript/commonjs/ui/components/OxySignInButton.d.ts.map +1 -1
  23. package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts +4 -0
  24. package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  25. package/lib/typescript/commonjs/ui/navigation/accountDialogManager.d.ts +2 -0
  26. package/lib/typescript/commonjs/ui/navigation/accountDialogManager.d.ts.map +1 -1
  27. package/lib/typescript/commonjs/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
  28. package/lib/typescript/module/ui/components/OxyAuthChooser.d.ts.map +1 -1
  29. package/lib/typescript/module/ui/components/OxySignInButton.d.ts.map +1 -1
  30. package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts +4 -0
  31. package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  32. package/lib/typescript/module/ui/navigation/accountDialogManager.d.ts +2 -0
  33. package/lib/typescript/module/ui/navigation/accountDialogManager.d.ts.map +1 -1
  34. package/lib/typescript/module/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
  35. package/package.json +4 -4
  36. package/src/ui/components/OxyAuthChooser.tsx +19 -12
  37. package/src/ui/components/OxySignInButton.tsx +43 -1
  38. package/src/ui/components/ProfileButton.tsx +1 -1
  39. package/src/ui/navigation/accountDialogManager.ts +17 -0
  40. package/src/ui/screens/AccountSettingsScreen.tsx +3 -4
@@ -12,6 +12,7 @@ import { useAuthStore } from '../stores/authStore';
12
12
  import { useShallow } from 'zustand/react/shallow';
13
13
  import { useTheme } from '@oxyhq/bloom/theme';
14
14
  import { Button, type ButtonVariant } from '@oxyhq/bloom/button';
15
+ import { toast } from '@oxyhq/bloom/toast';
15
16
  import { useOxy } from '../context/OxyContext';
16
17
  import { useI18n } from '../hooks/useI18n';
17
18
  import { LogoIcon } from './logo/LogoIcon';
@@ -201,6 +202,24 @@ export const OxySignInButton: React.FC<OxySignInButtonProps> = ({
201
202
  return promise;
202
203
  }, [clientId, oxyServices]);
203
204
 
205
+ // A press that cannot reach a sign-in surface must SAY so. Without this the
206
+ // only trace of an aborted sign-in was a console line, so the button read as
207
+ // simply dead — the SDK owns this so no relying party has to wire it up.
208
+ // `ToastOutlet` is already mounted by `OxyProvider`.
209
+ const notifyNotConfigured = useCallback(
210
+ (appName: string) => {
211
+ toast.error(t('signin.errors.notConfigured'), {
212
+ description: t('signin.errors.notConfiguredDescription', { app: appName }),
213
+ });
214
+ },
215
+ [t],
216
+ );
217
+ const notifyFailed = useCallback(() => {
218
+ toast.error(t('signin.errors.failed'), {
219
+ description: t('signin.errors.failedDescription'),
220
+ });
221
+ }, [t]);
222
+
204
223
  // Official / first-party surface: the in-app account + sign-in dialog.
205
224
  const startOfficialSignIn = useCallback(() => {
206
225
  openAccountDialog('signin');
@@ -227,6 +246,7 @@ export const OxySignInButton: React.FC<OxySignInButtonProps> = ({
227
246
  undefined,
228
247
  { component: 'OxySignInButton', clientId, application: app.name },
229
248
  );
249
+ notifyNotConfigured(app.name);
230
250
  return;
231
251
  }
232
252
 
@@ -245,6 +265,7 @@ export const OxySignInButton: React.FC<OxySignInButtonProps> = ({
245
265
  { component: 'OxySignInButton', application: app.name },
246
266
  result.description,
247
267
  );
268
+ notifyFailed();
248
269
  }
249
270
  return;
250
271
  }
@@ -267,6 +288,7 @@ export const OxySignInButton: React.FC<OxySignInButtonProps> = ({
267
288
  'OxySignInButton: native third-party sign-in cannot complete without an `onOAuthResult` handler; the code exchange is the RP\'s responsibility (state + code_verifier were not surfaced)',
268
289
  { component: 'OxySignInButton', application: app.name },
269
290
  );
291
+ notifyNotConfigured(app.name);
270
292
  },
271
293
  [
272
294
  clientId,
@@ -275,6 +297,8 @@ export const OxySignInButton: React.FC<OxySignInButtonProps> = ({
275
297
  startOfficialSignIn,
276
298
  startWebOAuthSignIn,
277
299
  shouldPreOpenPopup,
300
+ notifyNotConfigured,
301
+ notifyFailed,
278
302
  ],
279
303
  );
280
304
 
@@ -316,11 +340,29 @@ export const OxySignInButton: React.FC<OxySignInButtonProps> = ({
316
340
  }
317
341
  closeOAuthPopup(popup);
318
342
  startOfficialSignIn();
343
+ } catch (error) {
344
+ // `handlePress` fires this without awaiting, so an unexpected
345
+ // throw (a rejected native auth session, a crypto failure) would
346
+ // otherwise vanish into an unhandled rejection with the button
347
+ // left looking inert.
348
+ closeOAuthPopup(popup);
349
+ logger.error(
350
+ 'OxySignInButton: sign-in routing threw; sign-in aborted',
351
+ error instanceof Error ? error : new Error(String(error)),
352
+ { component: 'OxySignInButton', clientId },
353
+ );
354
+ notifyFailed();
319
355
  } finally {
320
356
  routingRef.current = false;
321
357
  }
322
358
  },
323
- [resolvePublicApplication, startOfficialSignIn, startThirdPartyOAuth, clientId],
359
+ [
360
+ resolvePublicApplication,
361
+ startOfficialSignIn,
362
+ startThirdPartyOAuth,
363
+ clientId,
364
+ notifyFailed,
365
+ ],
324
366
  );
325
367
 
326
368
  // Defer to a caller-supplied handler, otherwise route by application type.
@@ -134,7 +134,7 @@ const ProfileButton: React.FC<ProfileButtonProps> = ({
134
134
  }, [openAccountDialog]);
135
135
 
136
136
  useEffect(() => {
137
- if (!onNavigateManage && !onAddAccount && !onNavigateProfile) {
137
+ if (!onNavigateManage && !onAddAccount && !onNavigateProfile && !menuItems?.length) {
138
138
  return undefined;
139
139
  }
140
140
  return registerAccountDialogConsumerHooks({
@@ -45,6 +45,13 @@ export interface AccountDialogConsumerHooks {
45
45
  let controls: AccountDialogControls | null = null;
46
46
  let consumerHooks: AccountDialogConsumerHooks | null = null;
47
47
  const visibilityListeners = new Set<(visible: boolean) => void>();
48
+ const consumerHookListeners = new Set<() => void>();
49
+
50
+ function notifyConsumerHookListeners(): void {
51
+ for (const listener of consumerHookListeners) {
52
+ listener();
53
+ }
54
+ }
48
55
 
49
56
  export function registerAccountDialogControls(next: AccountDialogControls): () => void {
50
57
  controls = next;
@@ -60,9 +67,11 @@ export function registerAccountDialogConsumerHooks(
60
67
  next: AccountDialogConsumerHooks | null,
61
68
  ): () => void {
62
69
  consumerHooks = next;
70
+ notifyConsumerHookListeners();
63
71
  return () => {
64
72
  if (consumerHooks === next) {
65
73
  consumerHooks = null;
74
+ notifyConsumerHookListeners();
66
75
  }
67
76
  };
68
77
  }
@@ -71,6 +80,14 @@ export function getAccountDialogConsumerHooks(): AccountDialogConsumerHooks | nu
71
80
  return consumerHooks;
72
81
  }
73
82
 
83
+ /** Subscribe to consumer-hook registration changes (menu items, manage/add overrides). */
84
+ export function subscribeToAccountDialogConsumerHooks(listener: () => void): () => void {
85
+ consumerHookListeners.add(listener);
86
+ return () => {
87
+ consumerHookListeners.delete(listener);
88
+ };
89
+ }
90
+
74
91
  /** Open the account dialog on `view` (default `accounts`). No-op before mount. */
75
92
  export function openAccountDialog(view?: AccountDialogView): void {
76
93
  controls?.open(view);
@@ -143,11 +143,10 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({ onClose, goBack, nav
143
143
  const handleSave = useCallback(() => {
144
144
  const trimmed = displayName.trim();
145
145
  if (!trimmed || displayNameError) return;
146
- const nameParts = trimmed.split(/\s+/).filter(Boolean);
147
- const first = nameParts[0] || '';
148
- const last = nameParts.length > 1 ? nameParts.slice(1).join(' ') : undefined;
146
+ // Managed accounts (org / project / bot / channel) have a title, not a
147
+ // given-and-family name persist the explicit displayName column.
149
148
  updateMutation.mutate({
150
- name: { first, last },
149
+ name: { displayName: trimmed },
151
150
  bio: bio.trim() ? bio.trim() : null,
152
151
  });
153
152
  }, [displayName, displayNameError, bio, updateMutation]);