@oxyhq/services 19.2.0 → 20.0.1

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 (126) hide show
  1. package/lib/commonjs/ui/components/FollowButton.js +1 -2
  2. package/lib/commonjs/ui/components/FollowButton.js.map +1 -1
  3. package/lib/commonjs/ui/components/SettingsIcon.js +1 -2
  4. package/lib/commonjs/ui/components/SettingsIcon.js.map +1 -1
  5. package/lib/commonjs/ui/components/logo/LogoIcon.js +0 -2
  6. package/lib/commonjs/ui/components/logo/LogoIcon.js.map +1 -1
  7. package/lib/commonjs/ui/components/logo/LogoText.js +0 -2
  8. package/lib/commonjs/ui/components/logo/LogoText.js.map +1 -1
  9. package/lib/commonjs/ui/context/OxyContext.js +9 -8
  10. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  11. package/lib/commonjs/ui/context/commitSessionFlow.js +10 -0
  12. package/lib/commonjs/ui/context/commitSessionFlow.js.map +1 -1
  13. package/lib/commonjs/ui/context/hooks/useAuthOperations.js +1 -3
  14. package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
  15. package/lib/commonjs/ui/hooks/useLanguageManagement.js +109 -28
  16. package/lib/commonjs/ui/hooks/useLanguageManagement.js.map +1 -1
  17. package/lib/commonjs/ui/hooks/useSessionManagement.js +1 -3
  18. package/lib/commonjs/ui/hooks/useSessionManagement.js.map +1 -1
  19. package/lib/commonjs/ui/screens/LanguageSelectorScreen.js +151 -88
  20. package/lib/commonjs/ui/screens/LanguageSelectorScreen.js.map +1 -1
  21. package/lib/commonjs/ui/screens/PreferencesScreen.js +5 -6
  22. package/lib/commonjs/ui/screens/PreferencesScreen.js.map +1 -1
  23. package/lib/module/ui/components/FollowButton.js +1 -1
  24. package/lib/module/ui/components/FollowButton.js.map +1 -1
  25. package/lib/module/ui/components/SettingsIcon.js +1 -1
  26. package/lib/module/ui/components/SettingsIcon.js.map +1 -1
  27. package/lib/module/ui/components/logo/LogoIcon.js +0 -1
  28. package/lib/module/ui/components/logo/LogoIcon.js.map +1 -1
  29. package/lib/module/ui/components/logo/LogoText.js +0 -1
  30. package/lib/module/ui/components/logo/LogoText.js.map +1 -1
  31. package/lib/module/ui/context/OxyContext.js +9 -8
  32. package/lib/module/ui/context/OxyContext.js.map +1 -1
  33. package/lib/module/ui/context/commitSessionFlow.js +10 -0
  34. package/lib/module/ui/context/commitSessionFlow.js.map +1 -1
  35. package/lib/module/ui/context/hooks/useAuthOperations.js +1 -3
  36. package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
  37. package/lib/module/ui/hooks/useLanguageManagement.js +107 -29
  38. package/lib/module/ui/hooks/useLanguageManagement.js.map +1 -1
  39. package/lib/module/ui/hooks/useSessionManagement.js +1 -3
  40. package/lib/module/ui/hooks/useSessionManagement.js.map +1 -1
  41. package/lib/module/ui/screens/FeedbackScreen.js.map +1 -1
  42. package/lib/module/ui/screens/LanguageSelectorScreen.js +153 -91
  43. package/lib/module/ui/screens/LanguageSelectorScreen.js.map +1 -1
  44. package/lib/module/ui/screens/PreferencesScreen.js +5 -6
  45. package/lib/module/ui/screens/PreferencesScreen.js.map +1 -1
  46. package/lib/typescript/commonjs/ui/components/FollowButton.d.ts +2 -2
  47. package/lib/typescript/commonjs/ui/components/FollowButton.d.ts.map +1 -1
  48. package/lib/typescript/commonjs/ui/components/OxyPayButton.d.ts +1 -1
  49. package/lib/typescript/commonjs/ui/components/OxyPayButton.d.ts.map +1 -1
  50. package/lib/typescript/commonjs/ui/components/SettingsIcon.d.ts +1 -1
  51. package/lib/typescript/commonjs/ui/components/SettingsIcon.d.ts.map +1 -1
  52. package/lib/typescript/commonjs/ui/components/logo/LogoIcon.d.ts +1 -1
  53. package/lib/typescript/commonjs/ui/components/logo/LogoIcon.d.ts.map +1 -1
  54. package/lib/typescript/commonjs/ui/components/logo/LogoText.d.ts +1 -1
  55. package/lib/typescript/commonjs/ui/components/logo/LogoText.d.ts.map +1 -1
  56. package/lib/typescript/commonjs/ui/context/OxyContext.d.ts +1 -1
  57. package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
  58. package/lib/typescript/commonjs/ui/context/commitSessionFlow.d.ts.map +1 -1
  59. package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts +1 -2
  60. package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
  61. package/lib/typescript/commonjs/ui/context/oxyContextTypes.d.ts +3 -0
  62. package/lib/typescript/commonjs/ui/context/oxyContextTypes.d.ts.map +1 -1
  63. package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts +5 -3
  64. package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  65. package/lib/typescript/commonjs/ui/hooks/useFollow.d.ts +1 -1
  66. package/lib/typescript/commonjs/ui/hooks/useFollow.d.ts.map +1 -1
  67. package/lib/typescript/commonjs/ui/hooks/useLanguageManagement.d.ts +52 -8
  68. package/lib/typescript/commonjs/ui/hooks/useLanguageManagement.d.ts.map +1 -1
  69. package/lib/typescript/commonjs/ui/hooks/useSessionManagement.d.ts +1 -2
  70. package/lib/typescript/commonjs/ui/hooks/useSessionManagement.d.ts.map +1 -1
  71. package/lib/typescript/commonjs/ui/screens/LanguageSelectorScreen.d.ts.map +1 -1
  72. package/lib/typescript/commonjs/ui/screens/PreferencesScreen.d.ts.map +1 -1
  73. package/lib/typescript/commonjs/ui/screens/fileManagement/FileListSection.d.ts +1 -1
  74. package/lib/typescript/commonjs/ui/screens/fileManagement/FileListSection.d.ts.map +1 -1
  75. package/lib/typescript/commonjs/ui/screens/fileManagement/hooks/useFileUploadState.d.ts +1 -1
  76. package/lib/typescript/commonjs/ui/screens/fileManagement/hooks/useFileUploadState.d.ts.map +1 -1
  77. package/lib/typescript/module/ui/components/FollowButton.d.ts +2 -2
  78. package/lib/typescript/module/ui/components/FollowButton.d.ts.map +1 -1
  79. package/lib/typescript/module/ui/components/OxyPayButton.d.ts +1 -1
  80. package/lib/typescript/module/ui/components/OxyPayButton.d.ts.map +1 -1
  81. package/lib/typescript/module/ui/components/SettingsIcon.d.ts +1 -1
  82. package/lib/typescript/module/ui/components/SettingsIcon.d.ts.map +1 -1
  83. package/lib/typescript/module/ui/components/logo/LogoIcon.d.ts +1 -1
  84. package/lib/typescript/module/ui/components/logo/LogoIcon.d.ts.map +1 -1
  85. package/lib/typescript/module/ui/components/logo/LogoText.d.ts +1 -1
  86. package/lib/typescript/module/ui/components/logo/LogoText.d.ts.map +1 -1
  87. package/lib/typescript/module/ui/context/OxyContext.d.ts +1 -1
  88. package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
  89. package/lib/typescript/module/ui/context/commitSessionFlow.d.ts.map +1 -1
  90. package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts +1 -2
  91. package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
  92. package/lib/typescript/module/ui/context/oxyContextTypes.d.ts +3 -0
  93. package/lib/typescript/module/ui/context/oxyContextTypes.d.ts.map +1 -1
  94. package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts +5 -3
  95. package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  96. package/lib/typescript/module/ui/hooks/useFollow.d.ts +1 -1
  97. package/lib/typescript/module/ui/hooks/useFollow.d.ts.map +1 -1
  98. package/lib/typescript/module/ui/hooks/useLanguageManagement.d.ts +52 -8
  99. package/lib/typescript/module/ui/hooks/useLanguageManagement.d.ts.map +1 -1
  100. package/lib/typescript/module/ui/hooks/useSessionManagement.d.ts +1 -2
  101. package/lib/typescript/module/ui/hooks/useSessionManagement.d.ts.map +1 -1
  102. package/lib/typescript/module/ui/screens/LanguageSelectorScreen.d.ts.map +1 -1
  103. package/lib/typescript/module/ui/screens/PreferencesScreen.d.ts.map +1 -1
  104. package/lib/typescript/module/ui/screens/fileManagement/FileListSection.d.ts +1 -1
  105. package/lib/typescript/module/ui/screens/fileManagement/FileListSection.d.ts.map +1 -1
  106. package/lib/typescript/module/ui/screens/fileManagement/hooks/useFileUploadState.d.ts +1 -1
  107. package/lib/typescript/module/ui/screens/fileManagement/hooks/useFileUploadState.d.ts.map +1 -1
  108. package/package.json +18 -17
  109. package/src/ui/components/FollowButton.tsx +6 -5
  110. package/src/ui/components/OxyPayButton.tsx +1 -1
  111. package/src/ui/components/SettingsIcon.tsx +2 -1
  112. package/src/ui/components/logo/LogoIcon.tsx +2 -1
  113. package/src/ui/components/logo/LogoText.tsx +2 -1
  114. package/src/ui/context/OxyContext.tsx +13 -11
  115. package/src/ui/context/__tests__/commitSessionFlow.test.ts +27 -0
  116. package/src/ui/context/commitSessionFlow.ts +10 -0
  117. package/src/ui/context/hooks/useAuthOperations.ts +1 -5
  118. package/src/ui/context/oxyContextTypes.ts +3 -0
  119. package/src/ui/hooks/useFollow.ts +1 -1
  120. package/src/ui/hooks/useLanguageManagement.ts +144 -42
  121. package/src/ui/hooks/useSessionManagement.ts +1 -4
  122. package/src/ui/screens/FeedbackScreen.tsx +1 -1
  123. package/src/ui/screens/LanguageSelectorScreen.tsx +195 -104
  124. package/src/ui/screens/PreferencesScreen.tsx +4 -6
  125. package/src/ui/screens/fileManagement/FileListSection.tsx +1 -1
  126. package/src/ui/screens/fileManagement/hooks/useFileUploadState.ts +1 -1
@@ -1,11 +1,15 @@
1
1
  import { useCallback, useEffect, useMemo, useState } from 'react';
2
- import type { ApiError, User } from '@oxyhq/core';
2
+ import type { ApiError, User, SupportedLanguage } from '@oxyhq/core';
3
3
  import {
4
+ FALLBACK_LOCALE,
5
+ SUPPORTED_LANGUAGES,
6
+ getBaseLanguage,
4
7
  getLanguageMetadata,
5
8
  getLanguageName,
6
9
  getNativeLanguageName,
7
- normalizeLanguageCode,
8
- type LanguageMetadata,
10
+ getPrimaryLanguage,
11
+ getUserLanguages,
12
+ normalizeLocale,
9
13
  } from '@oxyhq/core';
10
14
  import type { StorageInterface } from '../utils/storageHelpers';
11
15
  import { extractErrorMessage } from '../utils/errorHandlers';
@@ -13,34 +17,143 @@ import { extractErrorMessage } from '../utils/errorHandlers';
13
17
  export interface UseLanguageManagementOptions {
14
18
  storage: StorageInterface | null;
15
19
  languageKey: string;
20
+ /**
21
+ * The active account, when signed in. When present, the account's primary
22
+ * locale (`languages[0]`) is authoritative for the app's UI locale; when
23
+ * `null` the device locale (or a locally-chosen guest override) is used.
24
+ */
25
+ user: User | null;
16
26
  onError?: (error: ApiError) => void;
17
27
  logger?: (message: string, error?: unknown) => void;
18
28
  }
19
29
 
20
30
  export interface UseLanguageManagementResult {
31
+ /**
32
+ * The active UI locale (a canonical `language-REGION` tag). Derived, never
33
+ * stored: the account's primary locale when signed in, otherwise the
34
+ * locally-chosen guest override, otherwise the device locale.
35
+ */
21
36
  currentLanguage: string;
22
- metadata: LanguageMetadata | null;
37
+ /**
38
+ * The ordered account locales (primary first) when signed in, or the single
39
+ * guest override when signed out. Empty only before the guest override loads
40
+ * and no device locale resolves.
41
+ */
42
+ languages: string[];
43
+ metadata: SupportedLanguage | null;
23
44
  languageName: string;
24
45
  nativeLanguageName: string;
25
- setLanguage: (languageId: string) => Promise<void>;
26
- applyLanguagePreference: (user: User | null) => Promise<void>;
46
+ /**
47
+ * Persist a single locally-owned locale (guest / device override) and mark it
48
+ * primary. For signed-in accounts, locale changes are written to the account
49
+ * (`updateProfile({ languages })`) instead — the derived `currentLanguage`
50
+ * then follows the refreshed `user`.
51
+ */
52
+ setLanguage: (locale: string) => Promise<void>;
27
53
  hydrateLanguage: () => Promise<void>;
28
54
  }
29
55
 
30
- const DEFAULT_LANGUAGE = 'en-US';
56
+ /**
57
+ * Append `locale` to an ordered locale list if not already present. Canonical
58
+ * order is preserved; the new locale lands last (non-primary).
59
+ */
60
+ export function addLocale(languages: readonly string[], locale: string): string[] {
61
+ const canonical = normalizeLocale(locale);
62
+ if (!canonical) return [...languages];
63
+ if (languages.includes(canonical)) return [...languages];
64
+ return [...languages, canonical];
65
+ }
66
+
67
+ /**
68
+ * Remove `locale` from an ordered locale list. The list's new first element
69
+ * becomes the primary locale.
70
+ */
71
+ export function removeLocale(languages: readonly string[], locale: string): string[] {
72
+ const canonical = normalizeLocale(locale);
73
+ if (!canonical) return [...languages];
74
+ return languages.filter((entry) => entry !== canonical);
75
+ }
76
+
77
+ /**
78
+ * Move `locale` to the front of an ordered locale list, making it primary.
79
+ * Adds it if it was not already present.
80
+ */
81
+ export function setPrimaryLocale(languages: readonly string[], locale: string): string[] {
82
+ const canonical = normalizeLocale(locale);
83
+ if (!canonical) return [...languages];
84
+ return [canonical, ...languages.filter((entry) => entry !== canonical)];
85
+ }
86
+
87
+ /**
88
+ * Resolve the device/browser locale, mapped to a supported catalog locale.
89
+ *
90
+ * Reads the platform's ordered locale preferences (`navigator.languages` /
91
+ * `navigator.language` on web, `Intl.DateTimeFormat().resolvedOptions().locale`
92
+ * on Hermes) and returns the first that resolves to a supported locale —
93
+ * either exactly or by base-language match (`fr-BE` → `fr-FR`). Falls back to
94
+ * {@link FALLBACK_LOCALE} when nothing resolves. Pure and side-effect free.
95
+ */
96
+ function resolveDeviceLocale(): string {
97
+ const candidates: string[] = [];
98
+
99
+ if (typeof navigator !== 'undefined') {
100
+ const nav = navigator as Navigator & { languages?: readonly string[] };
101
+ if (Array.isArray(nav.languages)) {
102
+ candidates.push(...nav.languages);
103
+ }
104
+ if (typeof nav.language === 'string') {
105
+ candidates.push(nav.language);
106
+ }
107
+ }
108
+
109
+ try {
110
+ const intlLocale = Intl.DateTimeFormat().resolvedOptions().locale;
111
+ if (intlLocale) {
112
+ candidates.push(intlLocale);
113
+ }
114
+ } catch {
115
+ // Intl is unavailable or incomplete on this runtime — fall through to the
116
+ // navigator-provided candidates (or the fallback locale below).
117
+ }
118
+
119
+ for (const candidate of candidates) {
120
+ const exact = normalizeLocale(candidate);
121
+ if (exact) return exact;
122
+ const base = getBaseLanguage(candidate);
123
+ const byBase = SUPPORTED_LANGUAGES.find((entry) => entry.language === base);
124
+ if (byBase) return byBase.code;
125
+ }
126
+
127
+ return FALLBACK_LOCALE;
128
+ }
31
129
 
32
130
  /**
33
- * Manage UI language state, persistence, and metadata derivation.
131
+ * Resolve and manage the app's active UI locale.
132
+ *
133
+ * The active locale is DERIVED, not stored:
134
+ * - Signed in → the account's primary locale (`getPrimaryLanguage(user)`),
135
+ * falling back to {@link FALLBACK_LOCALE}. This is what makes every Oxy app
136
+ * follow the account's language automatically.
137
+ * - Signed out → a locally-chosen guest override if one exists, otherwise the
138
+ * device locale.
34
139
  *
35
- * @param options - Configuration for storage access and error reporting
140
+ * The only external system this hook synchronizes with is local storage (the
141
+ * guest override), which is loaded once on mount.
36
142
  */
37
143
  export const useLanguageManagement = ({
38
144
  storage,
39
145
  languageKey,
146
+ user,
40
147
  onError,
41
148
  logger,
42
149
  }: UseLanguageManagementOptions): UseLanguageManagementResult => {
43
- const [currentLanguage, setCurrentLanguage] = useState<string>(DEFAULT_LANGUAGE);
150
+ // The device locale is fixed for the lifetime of the runtime.
151
+ const deviceLanguage = useMemo(resolveDeviceLocale, []);
152
+
153
+ // The locally-owned guest override (a single locale). `null` until loaded
154
+ // from storage or explicitly set; the derived locale falls back to the device
155
+ // locale while it is `null`.
156
+ const [guestLanguage, setGuestLanguage] = useState<string | null>(null);
44
157
 
45
158
  const loadLanguageFromStorage = useCallback(async (): Promise<void> => {
46
159
  if (!storage) {
@@ -49,9 +162,9 @@ export const useLanguageManagement = ({
49
162
 
50
163
  try {
51
164
  const savedLanguageRaw = await storage.getItem(languageKey);
52
- const normalized = normalizeLanguageCode(savedLanguageRaw) || savedLanguageRaw;
165
+ const normalized = savedLanguageRaw ? normalizeLocale(savedLanguageRaw) : undefined;
53
166
  if (normalized) {
54
- setCurrentLanguage(normalized);
167
+ setGuestLanguage(normalized);
55
168
  }
56
169
  } catch (error) {
57
170
  const message = extractErrorMessage(error, 'Failed to load language preference');
@@ -77,16 +190,19 @@ export const useLanguageManagement = ({
77
190
  }, [loadLanguageFromStorage, logger]);
78
191
 
79
192
  const setLanguage = useCallback(
80
- async (languageId: string): Promise<void> => {
193
+ async (locale: string): Promise<void> => {
81
194
  if (!storage) {
82
195
  throw new Error('Storage not initialized');
83
196
  }
84
197
 
85
- const normalized = normalizeLanguageCode(languageId) || languageId;
198
+ const normalized = normalizeLocale(locale);
199
+ if (!normalized) {
200
+ throw new Error(`Unsupported locale: ${locale}`);
201
+ }
86
202
 
87
203
  try {
88
204
  await storage.setItem(languageKey, normalized);
89
- setCurrentLanguage(normalized);
205
+ setGuestLanguage(normalized);
90
206
  } catch (error) {
91
207
  const message = extractErrorMessage(error, 'Failed to save language preference');
92
208
  onError?.({
@@ -105,31 +221,19 @@ export const useLanguageManagement = ({
105
221
  [languageKey, logger, onError, storage],
106
222
  );
107
223
 
108
- const applyLanguagePreference = useCallback(
109
- async (user: User | null): Promise<void> => {
110
- if (!storage || !user) {
111
- return;
112
- }
113
-
114
- const userLanguage = (user as Record<string, unknown>)?.language as string | undefined;
115
- if (!userLanguage) {
116
- return;
117
- }
224
+ const languages = useMemo<string[]>(() => {
225
+ if (user) {
226
+ return getUserLanguages(user);
227
+ }
228
+ return guestLanguage ? [guestLanguage] : [];
229
+ }, [user, guestLanguage]);
118
230
 
119
- try {
120
- const normalized = normalizeLanguageCode(userLanguage) || userLanguage;
121
- await storage.setItem(languageKey, normalized);
122
- setCurrentLanguage(normalized);
123
- } catch (error) {
124
- if (logger) {
125
- logger('Failed to apply server language preference', error);
126
- } else if (__DEV__) {
127
- console.warn('Failed to apply server language preference', error);
128
- }
129
- }
130
- },
131
- [languageKey, logger, storage],
132
- );
231
+ const currentLanguage = useMemo<string>(() => {
232
+ if (user) {
233
+ return getPrimaryLanguage(user) ?? FALLBACK_LOCALE;
234
+ }
235
+ return guestLanguage ?? deviceLanguage;
236
+ }, [user, guestLanguage, deviceLanguage]);
133
237
 
134
238
  const metadata = useMemo(() => getLanguageMetadata(currentLanguage), [currentLanguage]);
135
239
  const languageName = useMemo(() => getLanguageName(currentLanguage), [currentLanguage]);
@@ -140,13 +244,11 @@ export const useLanguageManagement = ({
140
244
 
141
245
  return {
142
246
  currentLanguage,
247
+ languages,
143
248
  metadata,
144
249
  languageName,
145
250
  nativeLanguageName,
146
251
  setLanguage,
147
- applyLanguagePreference,
148
252
  hydrateLanguage: loadLanguageFromStorage,
149
253
  };
150
254
  };
151
-
152
-
@@ -17,7 +17,6 @@ export interface UseSessionManagementOptions {
17
17
  storageKeyPrefix?: string;
18
18
  loginSuccess: (user: User) => void;
19
19
  logoutStore: () => void;
20
- applyLanguagePreference: (user: User) => Promise<void>;
21
20
  onAuthStateChange?: (user: User | null) => void;
22
21
  onError?: (error: ApiError) => void;
23
22
  setAuthError?: (message: string | null) => void;
@@ -54,7 +53,6 @@ export const useSessionManagement = ({
54
53
  storageKeyPrefix,
55
54
  loginSuccess,
56
55
  logoutStore,
57
- applyLanguagePreference,
58
56
  onAuthStateChange,
59
57
  onError,
60
58
  setAuthError,
@@ -207,10 +205,9 @@ export const useSessionManagement = ({
207
205
  setActiveSessionId(sessionId);
208
206
  loginSuccess(user);
209
207
  await saveActiveSessionId(sessionId);
210
- await applyLanguagePreference(user);
211
208
  onAuthStateChange?.(user);
212
209
  },
213
- [applyLanguagePreference, loginSuccess, onAuthStateChange, saveActiveSessionId, setTokenReady],
210
+ [loginSuccess, onAuthStateChange, saveActiveSessionId, setTokenReady],
214
211
  );
215
212
 
216
213
  const removalTimerIdsRef = useRef<Set<ReturnType<typeof setTimeout>>>(new Set());
@@ -12,7 +12,7 @@ import type { BaseScreenProps } from '../types/navigation';
12
12
  import { normalizeTheme } from '@oxyhq/core';
13
13
  import { packageInfo } from '@oxyhq/core';
14
14
  import { useTheme } from '@oxyhq/bloom/theme';
15
- import { Ionicons, MaterialCommunityIcons } from '@expo/vector-icons';
15
+ import { Ionicons, type MaterialCommunityIcons } from '@expo/vector-icons';
16
16
  import { toast } from '@oxyhq/bloom';
17
17
  import { H2, Text } from '@oxyhq/bloom/typography';
18
18
  import { Button } from '@oxyhq/bloom/button';