@oxy.so/services 12.4.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.
Files changed (36) hide show
  1. package/lib/commonjs/ui/components/authChooser/types.js +14 -8
  2. package/lib/commonjs/ui/components/authChooser/types.js.map +1 -1
  3. package/lib/commonjs/ui/screens/AccountSettingsScreen.js +3 -12
  4. package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
  5. package/lib/commonjs/ui/screens/CreateAccountScreen.js +3 -12
  6. package/lib/commonjs/ui/screens/CreateAccountScreen.js.map +1 -1
  7. package/lib/commonjs/ui/screens/EditProfileFieldScreen.js +7 -3
  8. package/lib/commonjs/ui/screens/EditProfileFieldScreen.js.map +1 -1
  9. package/lib/module/ui/components/authChooser/types.js +15 -8
  10. package/lib/module/ui/components/authChooser/types.js.map +1 -1
  11. package/lib/module/ui/screens/AccountSettingsScreen.js +4 -13
  12. package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
  13. package/lib/module/ui/screens/CreateAccountScreen.js +3 -13
  14. package/lib/module/ui/screens/CreateAccountScreen.js.map +1 -1
  15. package/lib/module/ui/screens/EditProfileFieldScreen.js +8 -4
  16. package/lib/module/ui/screens/EditProfileFieldScreen.js.map +1 -1
  17. package/lib/typescript/commonjs/ui/components/authChooser/types.d.ts +1 -3
  18. package/lib/typescript/commonjs/ui/components/authChooser/types.d.ts.map +1 -1
  19. package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts +3 -2
  20. package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  21. package/lib/typescript/commonjs/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
  22. package/lib/typescript/commonjs/ui/screens/CreateAccountScreen.d.ts.map +1 -1
  23. package/lib/typescript/commonjs/ui/screens/EditProfileFieldScreen.d.ts.map +1 -1
  24. package/lib/typescript/module/ui/components/authChooser/types.d.ts +1 -3
  25. package/lib/typescript/module/ui/components/authChooser/types.d.ts.map +1 -1
  26. package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts +3 -2
  27. package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  28. package/lib/typescript/module/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
  29. package/lib/typescript/module/ui/screens/CreateAccountScreen.d.ts.map +1 -1
  30. package/lib/typescript/module/ui/screens/EditProfileFieldScreen.d.ts.map +1 -1
  31. package/package.json +5 -5
  32. package/src/ui/components/authChooser/__tests__/profileColors.test.ts +13 -0
  33. package/src/ui/components/authChooser/types.ts +16 -15
  34. package/src/ui/screens/AccountSettingsScreen.tsx +10 -29
  35. package/src/ui/screens/CreateAccountScreen.tsx +9 -27
  36. package/src/ui/screens/EditProfileFieldScreen.tsx +10 -8
@@ -1,3 +1,4 @@
1
+ import { APP_COLOR_NAMES } from '@oxy.so/bloom/color-presets';
1
2
  /**
2
3
  * Shared types + per-account color resolution for the auth chooser's views.
3
4
  *
@@ -8,12 +9,7 @@
8
9
  */
9
10
 
10
11
  import type { AccountStorageUsageResponse } from '@oxy.so/core';
11
- import {
12
- APP_COLOR_NAMES,
13
- APP_COLOR_PRESETS,
14
- type AppColorName,
15
- type useTheme,
16
- } from '@oxy.so/bloom/theme';
12
+ import { APP_COLOR_PRESETS, type useTheme } from '@oxy.so/bloom/theme';
17
13
  import type { AccountDialogMenuSection } from '../../navigation/accountDialogManager';
18
14
  import type { useI18n } from '../../hooks/useI18n';
19
15
 
@@ -132,14 +128,19 @@ export interface SignInAlternatives {
132
128
  * renders in EVERY consumer regardless of NativeWind availability.
133
129
  */
134
130
  export function resolveAccentHex(color: string | null, fallback: string): string {
135
- const preset = toPreset(color);
136
- return preset ? APP_COLOR_PRESETS[preset].hex : fallback;
137
- }
131
+ const normalized = color?.trim().toLowerCase();
132
+ if (!normalized) return fallback;
133
+
134
+ if (/^#[0-9a-f]{3}$/i.test(normalized)) {
135
+ return `#${normalized[1]}${normalized[1]}${normalized[2]}${normalized[2]}${normalized[3]}${normalized[3]}`;
136
+ }
137
+ if (/^#[0-9a-f]{6}$/i.test(normalized)) return normalized;
138
138
 
139
- /** Narrow a stored color string to a known `AppColorName`, or `undefined`. */
140
- export function toPreset(color: string | null): AppColorName | undefined {
141
- if (!color) return undefined;
142
- return (APP_COLOR_NAMES as readonly string[]).includes(color)
143
- ? (color as AppColorName)
144
- : undefined;
139
+ // `amber` was the retired name for `yellow`; existing user records are
140
+ // migrated, but inboxes can still render cached account data during rollout.
141
+ const preset = normalized === 'amber' ? 'yellow' : normalized;
142
+ if (APP_COLOR_NAMES.includes(preset as (typeof APP_COLOR_NAMES)[number])) {
143
+ return APP_COLOR_PRESETS[preset as (typeof APP_COLOR_NAMES)[number]].hex;
144
+ }
145
+ return fallback;
145
146
  }
@@ -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
- DISPLAY_NAME_INVALID_MESSAGE,
14
+ accountNameInvalidKey,
15
+ accountNameInvalidMessage,
16
+ accountNameMaxLength,
20
17
  getNormalizedUserHandle,
21
- isValidDisplayName,
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
- if (node?.account.kind === 'organization') {
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
- invalidPart
175
- ? t('accounts.settings.displayName.invalidChars') || DISPLAY_NAME_INVALID_MESSAGE
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
- DISPLAY_NAME_INVALID_MESSAGE,
17
- isValidDisplayName,
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
- invalidPart
303
- ? t('accounts.create.displayName.invalidChars') || DISPLAY_NAME_INVALID_MESSAGE
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 { DISPLAY_NAME_INVALID_MESSAGE, isValidDisplayName } from '@oxy.so/core';
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
- isValidDisplayName(value)
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
- isValidDisplayName(value)
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(