@oxyhq/services 5.18.1 → 5.18.3

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 (157) hide show
  1. package/lib/commonjs/core/mixins/index.js +36 -13
  2. package/lib/commonjs/core/mixins/index.js.map +1 -1
  3. package/lib/commonjs/index.js +8 -0
  4. package/lib/commonjs/index.js.map +1 -1
  5. package/lib/commonjs/ui/client.js +170 -0
  6. package/lib/commonjs/ui/client.js.map +1 -0
  7. package/lib/commonjs/ui/components/profile/EditFieldModal.js +412 -0
  8. package/lib/commonjs/ui/components/profile/EditFieldModal.js.map +1 -0
  9. package/lib/commonjs/ui/context/OxyContext.js +63 -1
  10. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  11. package/lib/commonjs/ui/hooks/useAuth.js +115 -0
  12. package/lib/commonjs/ui/hooks/useAuth.js.map +1 -0
  13. package/lib/commonjs/ui/hooks/useSettingToggle.js +7 -1
  14. package/lib/commonjs/ui/hooks/useSettingToggle.js.map +1 -1
  15. package/lib/commonjs/ui/hooks/useWebSSO.js +75 -0
  16. package/lib/commonjs/ui/hooks/useWebSSO.js.map +1 -0
  17. package/lib/commonjs/ui/index.js +17 -2
  18. package/lib/commonjs/ui/index.js.map +1 -1
  19. package/lib/commonjs/ui/screens/PrivacySettingsScreen.js +59 -65
  20. package/lib/commonjs/ui/screens/PrivacySettingsScreen.js.map +1 -1
  21. package/lib/commonjs/ui/screens/SearchSettingsScreen.js +38 -58
  22. package/lib/commonjs/ui/screens/SearchSettingsScreen.js.map +1 -1
  23. package/lib/commonjs/ui/server.js +105 -0
  24. package/lib/commonjs/ui/server.js.map +1 -0
  25. package/lib/commonjs/ui/utils/iconNames.js +133 -0
  26. package/lib/commonjs/ui/utils/iconNames.js.map +1 -0
  27. package/lib/commonjs/ui/utils/sessionHelpers.js +7 -0
  28. package/lib/commonjs/ui/utils/sessionHelpers.js.map +1 -1
  29. package/lib/commonjs/utils/hookUtils.js +31 -13
  30. package/lib/commonjs/utils/hookUtils.js.map +1 -1
  31. package/lib/commonjs/utils/requestUtils.js +4 -3
  32. package/lib/commonjs/utils/requestUtils.js.map +1 -1
  33. package/lib/module/core/mixins/index.js +36 -13
  34. package/lib/module/core/mixins/index.js.map +1 -1
  35. package/lib/module/index.js +2 -2
  36. package/lib/module/index.js.map +1 -1
  37. package/lib/module/ui/client.js +47 -0
  38. package/lib/module/ui/client.js.map +1 -0
  39. package/lib/module/ui/components/profile/EditFieldModal.js +406 -0
  40. package/lib/module/ui/components/profile/EditFieldModal.js.map +1 -0
  41. package/lib/module/ui/context/OxyContext.js +63 -1
  42. package/lib/module/ui/context/OxyContext.js.map +1 -1
  43. package/lib/module/ui/hooks/useAuth.js +106 -0
  44. package/lib/module/ui/hooks/useAuth.js.map +1 -0
  45. package/lib/module/ui/hooks/useSettingToggle.js +7 -1
  46. package/lib/module/ui/hooks/useSettingToggle.js.map +1 -1
  47. package/lib/module/ui/hooks/useWebSSO.js +71 -0
  48. package/lib/module/ui/hooks/useWebSSO.js.map +1 -0
  49. package/lib/module/ui/index.js +17 -3
  50. package/lib/module/ui/index.js.map +1 -1
  51. package/lib/module/ui/screens/PrivacySettingsScreen.js +59 -65
  52. package/lib/module/ui/screens/PrivacySettingsScreen.js.map +1 -1
  53. package/lib/module/ui/screens/SearchSettingsScreen.js +39 -59
  54. package/lib/module/ui/screens/SearchSettingsScreen.js.map +1 -1
  55. package/lib/module/ui/server.js +65 -0
  56. package/lib/module/ui/server.js.map +1 -0
  57. package/lib/module/ui/utils/iconNames.js +124 -0
  58. package/lib/module/ui/utils/iconNames.js.map +1 -0
  59. package/lib/module/ui/utils/sessionHelpers.js +7 -0
  60. package/lib/module/ui/utils/sessionHelpers.js.map +1 -1
  61. package/lib/module/utils/hookUtils.js +31 -13
  62. package/lib/module/utils/hookUtils.js.map +1 -1
  63. package/lib/module/utils/requestUtils.js +4 -2
  64. package/lib/module/utils/requestUtils.js.map +1 -1
  65. package/lib/typescript/commonjs/core/mixins/index.d.ts +18 -1115
  66. package/lib/typescript/commonjs/core/mixins/index.d.ts.map +1 -1
  67. package/lib/typescript/commonjs/index.d.ts +2 -0
  68. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  69. package/lib/typescript/commonjs/ui/client.d.ts +33 -0
  70. package/lib/typescript/commonjs/ui/client.d.ts.map +1 -0
  71. package/lib/typescript/commonjs/ui/components/profile/EditFieldModal.d.ts +110 -0
  72. package/lib/typescript/commonjs/ui/components/profile/EditFieldModal.d.ts.map +1 -0
  73. package/lib/typescript/commonjs/ui/context/OxyContext.d.ts +3 -0
  74. package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
  75. package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts +3 -3
  76. package/lib/typescript/commonjs/ui/hooks/queries/useAccountQueries.d.ts +6 -10
  77. package/lib/typescript/commonjs/ui/hooks/queries/useAccountQueries.d.ts.map +1 -1
  78. package/lib/typescript/commonjs/ui/hooks/queries/useSecurityQueries.d.ts +1 -1
  79. package/lib/typescript/commonjs/ui/hooks/queries/useSecurityQueries.d.ts.map +1 -1
  80. package/lib/typescript/commonjs/ui/hooks/queries/useServicesQueries.d.ts +3 -5
  81. package/lib/typescript/commonjs/ui/hooks/queries/useServicesQueries.d.ts.map +1 -1
  82. package/lib/typescript/commonjs/ui/hooks/useAssets.d.ts +1 -1
  83. package/lib/typescript/commonjs/ui/hooks/useAuth.d.ts +69 -0
  84. package/lib/typescript/commonjs/ui/hooks/useAuth.d.ts.map +1 -0
  85. package/lib/typescript/commonjs/ui/hooks/useSettingToggle.d.ts +4 -2
  86. package/lib/typescript/commonjs/ui/hooks/useSettingToggle.d.ts.map +1 -1
  87. package/lib/typescript/commonjs/ui/hooks/useWebSSO.d.ts +34 -0
  88. package/lib/typescript/commonjs/ui/hooks/useWebSSO.d.ts.map +1 -0
  89. package/lib/typescript/commonjs/ui/index.d.ts +2 -2
  90. package/lib/typescript/commonjs/ui/index.d.ts.map +1 -1
  91. package/lib/typescript/commonjs/ui/screens/PrivacySettingsScreen.d.ts.map +1 -1
  92. package/lib/typescript/commonjs/ui/screens/SearchSettingsScreen.d.ts.map +1 -1
  93. package/lib/typescript/commonjs/ui/server.d.ts +43 -0
  94. package/lib/typescript/commonjs/ui/server.d.ts.map +1 -0
  95. package/lib/typescript/commonjs/ui/utils/iconNames.d.ts +112 -0
  96. package/lib/typescript/commonjs/ui/utils/iconNames.d.ts.map +1 -0
  97. package/lib/typescript/commonjs/ui/utils/sessionHelpers.d.ts +8 -3
  98. package/lib/typescript/commonjs/ui/utils/sessionHelpers.d.ts.map +1 -1
  99. package/lib/typescript/commonjs/utils/hookUtils.d.ts +8 -8
  100. package/lib/typescript/commonjs/utils/hookUtils.d.ts.map +1 -1
  101. package/lib/typescript/commonjs/utils/requestUtils.d.ts +3 -1
  102. package/lib/typescript/commonjs/utils/requestUtils.d.ts.map +1 -1
  103. package/lib/typescript/module/core/mixins/index.d.ts +18 -1115
  104. package/lib/typescript/module/core/mixins/index.d.ts.map +1 -1
  105. package/lib/typescript/module/index.d.ts +2 -0
  106. package/lib/typescript/module/index.d.ts.map +1 -1
  107. package/lib/typescript/module/ui/client.d.ts +33 -0
  108. package/lib/typescript/module/ui/client.d.ts.map +1 -0
  109. package/lib/typescript/module/ui/components/profile/EditFieldModal.d.ts +110 -0
  110. package/lib/typescript/module/ui/components/profile/EditFieldModal.d.ts.map +1 -0
  111. package/lib/typescript/module/ui/context/OxyContext.d.ts +3 -0
  112. package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
  113. package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts +3 -3
  114. package/lib/typescript/module/ui/hooks/queries/useAccountQueries.d.ts +6 -10
  115. package/lib/typescript/module/ui/hooks/queries/useAccountQueries.d.ts.map +1 -1
  116. package/lib/typescript/module/ui/hooks/queries/useSecurityQueries.d.ts +1 -1
  117. package/lib/typescript/module/ui/hooks/queries/useSecurityQueries.d.ts.map +1 -1
  118. package/lib/typescript/module/ui/hooks/queries/useServicesQueries.d.ts +3 -5
  119. package/lib/typescript/module/ui/hooks/queries/useServicesQueries.d.ts.map +1 -1
  120. package/lib/typescript/module/ui/hooks/useAssets.d.ts +1 -1
  121. package/lib/typescript/module/ui/hooks/useAuth.d.ts +69 -0
  122. package/lib/typescript/module/ui/hooks/useAuth.d.ts.map +1 -0
  123. package/lib/typescript/module/ui/hooks/useSettingToggle.d.ts +4 -2
  124. package/lib/typescript/module/ui/hooks/useSettingToggle.d.ts.map +1 -1
  125. package/lib/typescript/module/ui/hooks/useWebSSO.d.ts +34 -0
  126. package/lib/typescript/module/ui/hooks/useWebSSO.d.ts.map +1 -0
  127. package/lib/typescript/module/ui/index.d.ts +2 -2
  128. package/lib/typescript/module/ui/index.d.ts.map +1 -1
  129. package/lib/typescript/module/ui/screens/PrivacySettingsScreen.d.ts.map +1 -1
  130. package/lib/typescript/module/ui/screens/SearchSettingsScreen.d.ts.map +1 -1
  131. package/lib/typescript/module/ui/server.d.ts +43 -0
  132. package/lib/typescript/module/ui/server.d.ts.map +1 -0
  133. package/lib/typescript/module/ui/utils/iconNames.d.ts +112 -0
  134. package/lib/typescript/module/ui/utils/iconNames.d.ts.map +1 -0
  135. package/lib/typescript/module/ui/utils/sessionHelpers.d.ts +8 -3
  136. package/lib/typescript/module/ui/utils/sessionHelpers.d.ts.map +1 -1
  137. package/lib/typescript/module/utils/hookUtils.d.ts +8 -8
  138. package/lib/typescript/module/utils/hookUtils.d.ts.map +1 -1
  139. package/lib/typescript/module/utils/requestUtils.d.ts +3 -1
  140. package/lib/typescript/module/utils/requestUtils.d.ts.map +1 -1
  141. package/package.json +1 -1
  142. package/src/core/mixins/index.ts +57 -43
  143. package/src/index.ts +3 -1
  144. package/src/ui/client.ts +55 -0
  145. package/src/ui/components/profile/EditFieldModal.tsx +465 -0
  146. package/src/ui/context/OxyContext.tsx +69 -0
  147. package/src/ui/hooks/useAuth.ts +159 -0
  148. package/src/ui/hooks/useSettingToggle.ts +7 -3
  149. package/src/ui/hooks/useWebSSO.ts +93 -0
  150. package/src/ui/index.ts +17 -2
  151. package/src/ui/screens/PrivacySettingsScreen.tsx +54 -63
  152. package/src/ui/screens/SearchSettingsScreen.tsx +42 -64
  153. package/src/ui/server.ts +70 -0
  154. package/src/ui/utils/iconNames.ts +136 -0
  155. package/src/ui/utils/sessionHelpers.ts +10 -3
  156. package/src/utils/hookUtils.ts +38 -14
  157. package/src/utils/requestUtils.ts +10 -7
@@ -1,43 +1,67 @@
1
- import React, { useState, useCallback, useEffect } from 'react';
1
+ import React, { useState, useEffect } from 'react';
2
2
  import {
3
3
  View,
4
4
  StyleSheet,
5
5
  ScrollView,
6
6
  } from 'react-native';
7
7
  import type { BaseScreenProps } from '../types/navigation';
8
- import { toast } from '../../lib/sonner';
9
8
  import { Header, Section, LoadingState, SettingRow } from '../components';
10
9
  import { useI18n } from '../hooks/useI18n';
11
10
  import { useThemeStyles } from '../hooks/useThemeStyles';
11
+ import { useSettingToggles } from '../hooks/useSettingToggle';
12
12
  import { normalizeTheme } from '../utils/themeUtils';
13
13
  import { useOxy } from '../context/OxyContext';
14
+ import type { User } from '../../models/interfaces';
15
+
16
+ interface SearchSettings {
17
+ safeSearch: boolean;
18
+ searchPersonalization: boolean;
19
+ }
14
20
 
15
21
  const SearchSettingsScreen: React.FC<BaseScreenProps> = ({
16
22
  onClose,
17
23
  theme,
18
24
  goBack,
19
25
  }) => {
20
- // Use useOxy() hook for OxyContext values
21
26
  const { oxyServices, user } = useOxy();
22
27
  const { t } = useI18n();
23
- const [safeSearch, setSafeSearch] = useState(false);
24
- const [searchPersonalization, setSearchPersonalization] = useState(true);
25
28
  const [isLoading, setIsLoading] = useState(true);
26
- const [isSaving, setIsSaving] = useState(false);
27
29
 
28
- // Load settings
30
+ // Use the existing useSettingToggles hook for toggle management
31
+ const { values: settings, toggle, savingKeys, setValues } = useSettingToggles<SearchSettings>({
32
+ initialValues: { safeSearch: false, searchPersonalization: true },
33
+ onSave: async (key, value) => {
34
+ if (!user?.id || !oxyServices) return;
35
+
36
+ const fieldMap: Record<keyof SearchSettings, string> = {
37
+ safeSearch: 'autoFilter',
38
+ searchPersonalization: 'dataSharing',
39
+ };
40
+
41
+ await oxyServices.updateProfile({
42
+ privacySettings: {
43
+ [fieldMap[key]]: value,
44
+ },
45
+ });
46
+ },
47
+ errorMessage: (key) => t(`searchSettings.${key}.error`) || `Failed to update ${key}`,
48
+ });
49
+
50
+ const isSaving = savingKeys.size > 0;
51
+
52
+ // Load initial settings
29
53
  useEffect(() => {
30
54
  const loadSettings = async () => {
31
55
  try {
32
56
  setIsLoading(true);
33
57
  if (user?.id && oxyServices) {
34
- // Load from user's privacy settings
35
- const userData = await oxyServices.getCurrentUser();
36
- const privacySettings = (userData as any)?.privacySettings || {};
58
+ const userData = await oxyServices.getCurrentUser() as User & { privacySettings?: { autoFilter?: boolean; dataSharing?: boolean } };
59
+ const privacySettings = userData?.privacySettings || {};
37
60
 
38
- // SafeSearch is typically stored in privacySettings.autoFilter or a separate field
39
- setSafeSearch(privacySettings.autoFilter ?? false);
40
- setSearchPersonalization(privacySettings.dataSharing ?? true);
61
+ setValues({
62
+ safeSearch: privacySettings.autoFilter ?? false,
63
+ searchPersonalization: privacySettings.dataSharing ?? true,
64
+ });
41
65
  }
42
66
  } catch (error) {
43
67
  console.error('Failed to load search settings:', error);
@@ -47,53 +71,7 @@ const SearchSettingsScreen: React.FC<BaseScreenProps> = ({
47
71
  };
48
72
 
49
73
  loadSettings();
50
- }, [user?.id, oxyServices]);
51
-
52
- const handleSafeSearchToggle = useCallback(async (value: boolean) => {
53
- try {
54
- setIsSaving(true);
55
- setSafeSearch(value);
56
-
57
- if (user?.id && oxyServices) {
58
- // Update privacy settings
59
- await oxyServices.updateProfile({
60
- privacySettings: {
61
- autoFilter: value,
62
- },
63
- });
64
- toast.success(t('searchSettings.safeSearch.updated') || 'SafeSearch setting updated');
65
- }
66
- } catch (error) {
67
- console.error('Failed to update SafeSearch:', error);
68
- toast.error(t('searchSettings.safeSearch.error') || 'Failed to update SafeSearch');
69
- setSafeSearch(!value); // Revert on error
70
- } finally {
71
- setIsSaving(false);
72
- }
73
- }, [user?.id, oxyServices, t]);
74
-
75
- const handlePersonalizationToggle = useCallback(async (value: boolean) => {
76
- try {
77
- setIsSaving(true);
78
- setSearchPersonalization(value);
79
-
80
- if (user?.id && oxyServices) {
81
- // Update privacy settings
82
- await oxyServices.updateProfile({
83
- privacySettings: {
84
- dataSharing: value,
85
- },
86
- });
87
- toast.success(t('searchSettings.personalization.updated') || 'Search personalization updated');
88
- }
89
- } catch (error) {
90
- console.error('Failed to update personalization:', error);
91
- toast.error(t('searchSettings.personalization.error') || 'Failed to update personalization');
92
- setSearchPersonalization(!value); // Revert on error
93
- } finally {
94
- setIsSaving(false);
95
- }
96
- }, [user?.id, oxyServices, t]);
74
+ }, [user?.id, oxyServices, setValues]);
97
75
 
98
76
  const normalizedTheme = normalizeTheme(theme);
99
77
  const themeStyles = useThemeStyles(normalizedTheme);
@@ -128,8 +106,8 @@ const SearchSettingsScreen: React.FC<BaseScreenProps> = ({
128
106
  <SettingRow
129
107
  title={t('searchSettings.safeSearch.label') || 'Enable SafeSearch'}
130
108
  description={t('searchSettings.safeSearch.description') || 'Filter out explicit content from search results'}
131
- value={safeSearch}
132
- onValueChange={handleSafeSearchToggle}
109
+ value={settings.safeSearch}
110
+ onValueChange={() => toggle('safeSearch')}
133
111
  disabled={isSaving}
134
112
  textColor={themeStyles.textColor}
135
113
  mutedTextColor={themeStyles.mutedTextColor}
@@ -142,8 +120,8 @@ const SearchSettingsScreen: React.FC<BaseScreenProps> = ({
142
120
  <SettingRow
143
121
  title={t('searchSettings.personalization.label') || 'Personalized Search'}
144
122
  description={t('searchSettings.personalization.description') || 'Use your activity to improve search results'}
145
- value={searchPersonalization}
146
- onValueChange={handlePersonalizationToggle}
123
+ value={settings.searchPersonalization}
124
+ onValueChange={() => toggle('searchPersonalization')}
147
125
  disabled={isSaving}
148
126
  textColor={themeStyles.textColor}
149
127
  mutedTextColor={themeStyles.mutedTextColor}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Server-safe UI exports (noops)
3
+ *
4
+ * Import from this module for SSR environments where React components
5
+ * shouldn't be rendered on the server.
6
+ *
7
+ * @example
8
+ * import { OxyProvider, useOxy } from '@oxyhq/services/ui/server';
9
+ */
10
+
11
+ // Noop utilities
12
+ const noopComponent = () => null;
13
+ const noopHook = () => ({});
14
+ const noopStorageResult = { storage: null, isReady: false };
15
+
16
+ // Components (all render null)
17
+ export const OxyProvider = noopComponent;
18
+ export const OxySignInButton = noopComponent;
19
+ export const OxyLogo = noopComponent;
20
+ export const Avatar = noopComponent;
21
+ export const FollowButton = noopComponent;
22
+ export const OxyPayButton = noopComponent;
23
+ export const FontLoader = noopComponent;
24
+ export const setupFonts = () => {};
25
+ export const OxyIcon = noopComponent;
26
+
27
+ // Context
28
+ export const useOxy = noopHook;
29
+
30
+ // Hooks (all return empty objects)
31
+ export const useAuth = noopHook;
32
+ export const useFollow = noopHook;
33
+ export const useStorage = () => noopStorageResult;
34
+
35
+ // Screens (render null)
36
+ export const ProfileScreen = noopComponent;
37
+
38
+ // Stores (return empty objects)
39
+ export const useAuthStore = noopHook;
40
+ export const useAccountStore = noopHook;
41
+
42
+ // Styles (empty objects)
43
+ export const fontFamilies = {};
44
+ export const fontStyles = {};
45
+
46
+ // Toast (noop)
47
+ export const toast = Object.assign(
48
+ () => {},
49
+ {
50
+ success: () => {},
51
+ error: () => {},
52
+ info: () => {},
53
+ warning: () => {},
54
+ loading: () => {},
55
+ dismiss: () => {},
56
+ }
57
+ );
58
+
59
+ // Core re-exports (these work in both environments)
60
+ export { OxyServices } from '../core';
61
+ export type { User, LoginResponse, ApiError } from '../models/interfaces';
62
+
63
+ // Error handler utilities (pure functions work everywhere)
64
+ export {
65
+ handleAuthError,
66
+ isInvalidSessionError,
67
+ isTimeoutOrNetworkError,
68
+ extractErrorMessage,
69
+ } from './utils/errorHandlers';
70
+ export type { HandleAuthErrorOptions } from './utils/errorHandlers';
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Type-safe icon name utilities
3
+ *
4
+ * Provides centralized, typed icon name mappings to eliminate `as any` casts
5
+ * when using dynamic icon names with @expo/vector-icons.
6
+ */
7
+
8
+ /**
9
+ * Profile field icons (MaterialCommunityIcons)
10
+ */
11
+ export const PROFILE_FIELD_ICONS = {
12
+ displayName: 'account-outline',
13
+ username: 'at',
14
+ email: 'email-outline',
15
+ bio: 'text-box-outline',
16
+ location: 'map-marker-outline',
17
+ links: 'link-variant',
18
+ website: 'web',
19
+ phone: 'phone-outline',
20
+ birthday: 'cake-variant-outline',
21
+ } as const;
22
+
23
+ export type ProfileFieldIconKey = keyof typeof PROFILE_FIELD_ICONS;
24
+
25
+ /**
26
+ * Get icon name for a profile field
27
+ */
28
+ export function getProfileFieldIcon(field: string): string {
29
+ return PROFILE_FIELD_ICONS[field as ProfileFieldIconKey] ?? 'account-outline';
30
+ }
31
+
32
+ /**
33
+ * Settings section icons (MaterialCommunityIcons)
34
+ */
35
+ export const SETTINGS_ICONS = {
36
+ account: 'account-cog-outline',
37
+ privacy: 'shield-account-outline',
38
+ security: 'lock-outline',
39
+ notifications: 'bell-outline',
40
+ language: 'translate',
41
+ appearance: 'palette-outline',
42
+ storage: 'folder-outline',
43
+ help: 'help-circle-outline',
44
+ about: 'information-outline',
45
+ logout: 'logout',
46
+ } as const;
47
+
48
+ export type SettingsIconKey = keyof typeof SETTINGS_ICONS;
49
+
50
+ /**
51
+ * Get icon name for a settings section
52
+ */
53
+ export function getSettingsIcon(section: string): string {
54
+ return SETTINGS_ICONS[section as SettingsIconKey] ?? 'cog-outline';
55
+ }
56
+
57
+ /**
58
+ * File type icons (MaterialCommunityIcons)
59
+ */
60
+ export const FILE_TYPE_ICONS = {
61
+ image: 'image-outline',
62
+ video: 'video-outline',
63
+ audio: 'music-note-outline',
64
+ document: 'file-document-outline',
65
+ pdf: 'file-pdf-box',
66
+ archive: 'folder-zip-outline',
67
+ code: 'code-tags',
68
+ spreadsheet: 'file-excel-outline',
69
+ presentation: 'file-presentation-outline',
70
+ text: 'file-document-edit-outline',
71
+ unknown: 'file-outline',
72
+ } as const;
73
+
74
+ export type FileTypeIconKey = keyof typeof FILE_TYPE_ICONS;
75
+
76
+ /**
77
+ * Get icon name for a file type
78
+ */
79
+ export function getFileTypeIcon(type: string): string {
80
+ return FILE_TYPE_ICONS[type as FileTypeIconKey] ?? FILE_TYPE_ICONS.unknown;
81
+ }
82
+
83
+ /**
84
+ * Action icons (Ionicons)
85
+ */
86
+ export const ACTION_ICONS = {
87
+ close: 'close',
88
+ back: 'chevron-back',
89
+ forward: 'chevron-forward',
90
+ add: 'add',
91
+ remove: 'remove',
92
+ delete: 'trash-outline',
93
+ edit: 'pencil-outline',
94
+ save: 'checkmark',
95
+ cancel: 'close',
96
+ search: 'search-outline',
97
+ filter: 'filter-outline',
98
+ sort: 'swap-vertical-outline',
99
+ refresh: 'refresh-outline',
100
+ share: 'share-outline',
101
+ copy: 'copy-outline',
102
+ download: 'download-outline',
103
+ upload: 'cloud-upload-outline',
104
+ } as const;
105
+
106
+ export type ActionIconKey = keyof typeof ACTION_ICONS;
107
+
108
+ /**
109
+ * Get icon name for an action
110
+ */
111
+ export function getActionIcon(action: string): string {
112
+ return ACTION_ICONS[action as ActionIconKey] ?? 'ellipsis-horizontal';
113
+ }
114
+
115
+ /**
116
+ * Status icons (Ionicons)
117
+ */
118
+ export const STATUS_ICONS = {
119
+ success: 'checkmark-circle',
120
+ error: 'alert-circle',
121
+ warning: 'warning',
122
+ info: 'information-circle',
123
+ loading: 'hourglass-outline',
124
+ pending: 'time-outline',
125
+ online: 'ellipse',
126
+ offline: 'ellipse-outline',
127
+ } as const;
128
+
129
+ export type StatusIconKey = keyof typeof STATUS_ICONS;
130
+
131
+ /**
132
+ * Get icon name for a status
133
+ */
134
+ export function getStatusIcon(status: string): string {
135
+ return STATUS_ICONS[status as StatusIconKey] ?? 'help-circle-outline';
136
+ }
@@ -1,4 +1,3 @@
1
- import type { OxyServices } from '../../core';
2
1
  import type { ClientSession } from '../../models/session';
3
2
 
4
3
  interface DeviceSession {
@@ -12,6 +11,14 @@ interface DeviceSession {
12
11
  isCurrent?: boolean;
13
12
  }
14
13
 
14
+ /**
15
+ * Service type for session helpers.
16
+ * Uses 'any' to work around TypeScript mixin composition type inference issues.
17
+ * The OxyServices class has these methods but TypeScript can't see them due to the mixin pattern.
18
+ */
19
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
+ type OxyServicesAny = any;
21
+
15
22
  export interface FetchSessionsWithFallbackOptions {
16
23
  fallbackDeviceId?: string;
17
24
  fallbackUserId?: string;
@@ -68,7 +75,7 @@ export const mapSessionsToClient = (
68
75
  * @param options - Optional fallback options
69
76
  */
70
77
  export const fetchSessionsWithFallback = async (
71
- oxyServices: Pick<OxyServices, 'getDeviceSessions' | 'getSessionsBySessionId'>,
78
+ oxyServices: OxyServicesAny,
72
79
  sessionId: string,
73
80
  {
74
81
  fallbackDeviceId,
@@ -97,7 +104,7 @@ export const fetchSessionsWithFallback = async (
97
104
  * @param options - Validation options
98
105
  */
99
106
  export const validateSessionBatch = async (
100
- oxyServices: Pick<OxyServices, 'validateSession'>,
107
+ oxyServices: OxyServicesAny,
101
108
  sessionIds: string[],
102
109
  { useHeaderValidation = true, maxConcurrency = 5 }: ValidateSessionBatchOptions = {},
103
110
  ): Promise<SessionValidationResult[]> => {
@@ -4,6 +4,12 @@
4
4
 
5
5
  import { useState, useEffect, useCallback, useRef, useMemo } from 'react';
6
6
 
7
+ // Platform detection for SSR/Node.js/React Native safety
8
+ const isBrowser = typeof window !== 'undefined';
9
+ const hasLocalStorage = isBrowser && typeof window.localStorage !== 'undefined';
10
+ const hasSessionStorage = isBrowser && typeof window.sessionStorage !== 'undefined';
11
+ const hasNavigator = typeof navigator !== 'undefined';
12
+
7
13
  /**
8
14
  * Hook for managing async operations with loading, error, and data states
9
15
  */
@@ -158,10 +164,11 @@ export function useCounter(initialValue = 0) {
158
164
  }
159
165
 
160
166
  /**
161
- * Hook for local storage
167
+ * Hook for local storage (platform-safe)
162
168
  */
163
169
  export function useLocalStorage<T>(key: string, initialValue: T) {
164
170
  const [storedValue, setStoredValue] = useState<T>(() => {
171
+ if (!hasLocalStorage) return initialValue;
165
172
  try {
166
173
  const item = window.localStorage.getItem(key);
167
174
  return item ? JSON.parse(item) : initialValue;
@@ -175,7 +182,9 @@ export function useLocalStorage<T>(key: string, initialValue: T) {
175
182
  try {
176
183
  const valueToStore = value instanceof Function ? value(storedValue) : value;
177
184
  setStoredValue(valueToStore);
178
- window.localStorage.setItem(key, JSON.stringify(valueToStore));
185
+ if (hasLocalStorage) {
186
+ window.localStorage.setItem(key, JSON.stringify(valueToStore));
187
+ }
179
188
  } catch (error) {
180
189
  console.error(`Error setting localStorage key "${key}":`, error);
181
190
  }
@@ -185,10 +194,11 @@ export function useLocalStorage<T>(key: string, initialValue: T) {
185
194
  }
186
195
 
187
196
  /**
188
- * Hook for session storage
197
+ * Hook for session storage (platform-safe)
189
198
  */
190
199
  export function useSessionStorage<T>(key: string, initialValue: T) {
191
200
  const [storedValue, setStoredValue] = useState<T>(() => {
201
+ if (!hasSessionStorage) return initialValue;
192
202
  try {
193
203
  const item = window.sessionStorage.getItem(key);
194
204
  return item ? JSON.parse(item) : initialValue;
@@ -202,7 +212,9 @@ export function useSessionStorage<T>(key: string, initialValue: T) {
202
212
  try {
203
213
  const valueToStore = value instanceof Function ? value(storedValue) : value;
204
214
  setStoredValue(valueToStore);
205
- window.sessionStorage.setItem(key, JSON.stringify(valueToStore));
215
+ if (hasSessionStorage) {
216
+ window.sessionStorage.setItem(key, JSON.stringify(valueToStore));
217
+ }
206
218
  } catch (error) {
207
219
  console.error(`Error setting sessionStorage key "${key}":`, error);
208
220
  }
@@ -212,15 +224,17 @@ export function useSessionStorage<T>(key: string, initialValue: T) {
212
224
  }
213
225
 
214
226
  /**
215
- * Hook for window size
227
+ * Hook for window size (platform-safe)
216
228
  */
217
229
  export function useWindowSize() {
218
230
  const [windowSize, setWindowSize] = useState({
219
- width: window.innerWidth,
220
- height: window.innerHeight,
231
+ width: isBrowser ? window.innerWidth : 0,
232
+ height: isBrowser ? window.innerHeight : 0,
221
233
  });
222
234
 
223
235
  useEffect(() => {
236
+ if (!isBrowser) return;
237
+
224
238
  const handleResize = () => {
225
239
  setWindowSize({
226
240
  width: window.innerWidth,
@@ -236,12 +250,14 @@ export function useWindowSize() {
236
250
  }
237
251
 
238
252
  /**
239
- * Hook for scroll position
253
+ * Hook for scroll position (platform-safe)
240
254
  */
241
255
  export function useScrollPosition() {
242
256
  const [scrollPosition, setScrollPosition] = useState(0);
243
257
 
244
258
  useEffect(() => {
259
+ if (!isBrowser) return;
260
+
245
261
  const handleScroll = () => {
246
262
  setScrollPosition(window.pageYOffset);
247
263
  };
@@ -254,12 +270,14 @@ export function useScrollPosition() {
254
270
  }
255
271
 
256
272
  /**
257
- * Hook for online/offline status
273
+ * Hook for online/offline status (platform-safe)
258
274
  */
259
275
  export function useOnlineStatus() {
260
- const [isOnline, setIsOnline] = useState(navigator.onLine);
276
+ const [isOnline, setIsOnline] = useState(hasNavigator ? navigator.onLine : true);
261
277
 
262
278
  useEffect(() => {
279
+ if (!isBrowser) return;
280
+
263
281
  const handleOnline = () => setIsOnline(true);
264
282
  const handleOffline = () => setIsOnline(false);
265
283
 
@@ -276,12 +294,14 @@ export function useOnlineStatus() {
276
294
  }
277
295
 
278
296
  /**
279
- * Hook for media queries
297
+ * Hook for media queries (platform-safe)
280
298
  */
281
299
  export function useMediaQuery(query: string): boolean {
282
300
  const [matches, setMatches] = useState(false);
283
301
 
284
302
  useEffect(() => {
303
+ if (!isBrowser) return;
304
+
285
305
  const media = window.matchMedia(query);
286
306
  if (media.matches !== matches) {
287
307
  setMatches(media.matches);
@@ -289,7 +309,7 @@ export function useMediaQuery(query: string): boolean {
289
309
 
290
310
  const listener = () => setMatches(media.matches);
291
311
  media.addEventListener('change', listener);
292
-
312
+
293
313
  return () => media.removeEventListener('change', listener);
294
314
  }, [matches, query]);
295
315
 
@@ -297,12 +317,14 @@ export function useMediaQuery(query: string): boolean {
297
317
  }
298
318
 
299
319
  /**
300
- * Hook for keyboard events
320
+ * Hook for keyboard events (platform-safe)
301
321
  */
302
322
  export function useKeyPress(targetKey: string): boolean {
303
323
  const [keyPressed, setKeyPressed] = useState(false);
304
324
 
305
325
  useEffect(() => {
326
+ if (!isBrowser) return;
327
+
306
328
  const downHandler = ({ key }: KeyboardEvent) => {
307
329
  if (key === targetKey) {
308
330
  setKeyPressed(true);
@@ -328,10 +350,12 @@ export function useKeyPress(targetKey: string): boolean {
328
350
  }
329
351
 
330
352
  /**
331
- * Hook for click outside detection
353
+ * Hook for click outside detection (platform-safe)
332
354
  */
333
355
  export function useClickOutside(ref: React.RefObject<HTMLElement>, handler: () => void) {
334
356
  useEffect(() => {
357
+ if (!isBrowser) return;
358
+
335
359
  const listener = (event: MouseEvent | TouchEvent) => {
336
360
  if (!ref.current || ref.current.contains(event.target as Node)) {
337
361
  return;
@@ -162,12 +162,15 @@ export class RequestQueue {
162
162
  }
163
163
  }
164
164
 
165
+ /** Log level type for SimpleLogger */
166
+ export type LogLevel = 'none' | 'error' | 'warn' | 'info' | 'debug';
167
+
165
168
  /**
166
169
  * Simple logger with level support
167
- *
170
+ *
168
171
  * Lightweight logger for HTTP clients and utilities.
169
172
  * For more advanced logging, use loggerUtils.ts
170
- *
173
+ *
171
174
  * @example
172
175
  * ```typescript
173
176
  * const logger = new SimpleLogger(true, 'debug');
@@ -178,7 +181,7 @@ export class RequestQueue {
178
181
  */
179
182
  export class SimpleLogger {
180
183
  private enabled: boolean;
181
- private level: 'none' | 'error' | 'warn' | 'info' | 'debug';
184
+ private level: LogLevel;
182
185
  private prefix: string;
183
186
 
184
187
  /**
@@ -189,17 +192,17 @@ export class SimpleLogger {
189
192
  */
190
193
  constructor(
191
194
  enabled: boolean = false,
192
- level: string = 'error',
195
+ level: LogLevel = 'error',
193
196
  prefix: string = ''
194
197
  ) {
195
198
  this.enabled = enabled;
196
- this.level = level as any;
199
+ this.level = level;
197
200
  this.prefix = prefix;
198
201
  }
199
202
 
200
- private shouldLog(level: string): boolean {
203
+ private shouldLog(level: LogLevel): boolean {
201
204
  if (!this.enabled || this.level === 'none') return false;
202
- const levels = ['none', 'error', 'warn', 'info', 'debug'];
205
+ const levels: LogLevel[] = ['none', 'error', 'warn', 'info', 'debug'];
203
206
  return levels.indexOf(level) <= levels.indexOf(this.level);
204
207
  }
205
208