@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.
- package/lib/commonjs/core/mixins/index.js +36 -13
- package/lib/commonjs/core/mixins/index.js.map +1 -1
- package/lib/commonjs/index.js +8 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/ui/client.js +170 -0
- package/lib/commonjs/ui/client.js.map +1 -0
- package/lib/commonjs/ui/components/profile/EditFieldModal.js +412 -0
- package/lib/commonjs/ui/components/profile/EditFieldModal.js.map +1 -0
- package/lib/commonjs/ui/context/OxyContext.js +63 -1
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/hooks/useAuth.js +115 -0
- package/lib/commonjs/ui/hooks/useAuth.js.map +1 -0
- package/lib/commonjs/ui/hooks/useSettingToggle.js +7 -1
- package/lib/commonjs/ui/hooks/useSettingToggle.js.map +1 -1
- package/lib/commonjs/ui/hooks/useWebSSO.js +75 -0
- package/lib/commonjs/ui/hooks/useWebSSO.js.map +1 -0
- package/lib/commonjs/ui/index.js +17 -2
- package/lib/commonjs/ui/index.js.map +1 -1
- package/lib/commonjs/ui/screens/PrivacySettingsScreen.js +59 -65
- package/lib/commonjs/ui/screens/PrivacySettingsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/SearchSettingsScreen.js +38 -58
- package/lib/commonjs/ui/screens/SearchSettingsScreen.js.map +1 -1
- package/lib/commonjs/ui/server.js +105 -0
- package/lib/commonjs/ui/server.js.map +1 -0
- package/lib/commonjs/ui/utils/iconNames.js +133 -0
- package/lib/commonjs/ui/utils/iconNames.js.map +1 -0
- package/lib/commonjs/ui/utils/sessionHelpers.js +7 -0
- package/lib/commonjs/ui/utils/sessionHelpers.js.map +1 -1
- package/lib/commonjs/utils/hookUtils.js +31 -13
- package/lib/commonjs/utils/hookUtils.js.map +1 -1
- package/lib/commonjs/utils/requestUtils.js +4 -3
- package/lib/commonjs/utils/requestUtils.js.map +1 -1
- package/lib/module/core/mixins/index.js +36 -13
- package/lib/module/core/mixins/index.js.map +1 -1
- package/lib/module/index.js +2 -2
- package/lib/module/index.js.map +1 -1
- package/lib/module/ui/client.js +47 -0
- package/lib/module/ui/client.js.map +1 -0
- package/lib/module/ui/components/profile/EditFieldModal.js +406 -0
- package/lib/module/ui/components/profile/EditFieldModal.js.map +1 -0
- package/lib/module/ui/context/OxyContext.js +63 -1
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/hooks/useAuth.js +106 -0
- package/lib/module/ui/hooks/useAuth.js.map +1 -0
- package/lib/module/ui/hooks/useSettingToggle.js +7 -1
- package/lib/module/ui/hooks/useSettingToggle.js.map +1 -1
- package/lib/module/ui/hooks/useWebSSO.js +71 -0
- package/lib/module/ui/hooks/useWebSSO.js.map +1 -0
- package/lib/module/ui/index.js +17 -3
- package/lib/module/ui/index.js.map +1 -1
- package/lib/module/ui/screens/PrivacySettingsScreen.js +59 -65
- package/lib/module/ui/screens/PrivacySettingsScreen.js.map +1 -1
- package/lib/module/ui/screens/SearchSettingsScreen.js +39 -59
- package/lib/module/ui/screens/SearchSettingsScreen.js.map +1 -1
- package/lib/module/ui/server.js +65 -0
- package/lib/module/ui/server.js.map +1 -0
- package/lib/module/ui/utils/iconNames.js +124 -0
- package/lib/module/ui/utils/iconNames.js.map +1 -0
- package/lib/module/ui/utils/sessionHelpers.js +7 -0
- package/lib/module/ui/utils/sessionHelpers.js.map +1 -1
- package/lib/module/utils/hookUtils.js +31 -13
- package/lib/module/utils/hookUtils.js.map +1 -1
- package/lib/module/utils/requestUtils.js +4 -2
- package/lib/module/utils/requestUtils.js.map +1 -1
- package/lib/typescript/commonjs/core/mixins/index.d.ts +18 -1115
- package/lib/typescript/commonjs/core/mixins/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +2 -0
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/client.d.ts +33 -0
- package/lib/typescript/commonjs/ui/client.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/profile/EditFieldModal.d.ts +110 -0
- package/lib/typescript/commonjs/ui/components/profile/EditFieldModal.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/context/OxyContext.d.ts +3 -0
- package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts +3 -3
- package/lib/typescript/commonjs/ui/hooks/queries/useAccountQueries.d.ts +6 -10
- package/lib/typescript/commonjs/ui/hooks/queries/useAccountQueries.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/queries/useSecurityQueries.d.ts +1 -1
- package/lib/typescript/commonjs/ui/hooks/queries/useSecurityQueries.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/queries/useServicesQueries.d.ts +3 -5
- package/lib/typescript/commonjs/ui/hooks/queries/useServicesQueries.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useAssets.d.ts +1 -1
- package/lib/typescript/commonjs/ui/hooks/useAuth.d.ts +69 -0
- package/lib/typescript/commonjs/ui/hooks/useAuth.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/hooks/useSettingToggle.d.ts +4 -2
- package/lib/typescript/commonjs/ui/hooks/useSettingToggle.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useWebSSO.d.ts +34 -0
- package/lib/typescript/commonjs/ui/hooks/useWebSSO.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/index.d.ts +2 -2
- package/lib/typescript/commonjs/ui/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/PrivacySettingsScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/SearchSettingsScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/server.d.ts +43 -0
- package/lib/typescript/commonjs/ui/server.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/utils/iconNames.d.ts +112 -0
- package/lib/typescript/commonjs/ui/utils/iconNames.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/utils/sessionHelpers.d.ts +8 -3
- package/lib/typescript/commonjs/ui/utils/sessionHelpers.d.ts.map +1 -1
- package/lib/typescript/commonjs/utils/hookUtils.d.ts +8 -8
- package/lib/typescript/commonjs/utils/hookUtils.d.ts.map +1 -1
- package/lib/typescript/commonjs/utils/requestUtils.d.ts +3 -1
- package/lib/typescript/commonjs/utils/requestUtils.d.ts.map +1 -1
- package/lib/typescript/module/core/mixins/index.d.ts +18 -1115
- package/lib/typescript/module/core/mixins/index.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +2 -0
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/client.d.ts +33 -0
- package/lib/typescript/module/ui/client.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/profile/EditFieldModal.d.ts +110 -0
- package/lib/typescript/module/ui/components/profile/EditFieldModal.d.ts.map +1 -0
- package/lib/typescript/module/ui/context/OxyContext.d.ts +3 -0
- package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts +3 -3
- package/lib/typescript/module/ui/hooks/queries/useAccountQueries.d.ts +6 -10
- package/lib/typescript/module/ui/hooks/queries/useAccountQueries.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/queries/useSecurityQueries.d.ts +1 -1
- package/lib/typescript/module/ui/hooks/queries/useSecurityQueries.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/queries/useServicesQueries.d.ts +3 -5
- package/lib/typescript/module/ui/hooks/queries/useServicesQueries.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useAssets.d.ts +1 -1
- package/lib/typescript/module/ui/hooks/useAuth.d.ts +69 -0
- package/lib/typescript/module/ui/hooks/useAuth.d.ts.map +1 -0
- package/lib/typescript/module/ui/hooks/useSettingToggle.d.ts +4 -2
- package/lib/typescript/module/ui/hooks/useSettingToggle.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useWebSSO.d.ts +34 -0
- package/lib/typescript/module/ui/hooks/useWebSSO.d.ts.map +1 -0
- package/lib/typescript/module/ui/index.d.ts +2 -2
- package/lib/typescript/module/ui/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/PrivacySettingsScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/SearchSettingsScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/server.d.ts +43 -0
- package/lib/typescript/module/ui/server.d.ts.map +1 -0
- package/lib/typescript/module/ui/utils/iconNames.d.ts +112 -0
- package/lib/typescript/module/ui/utils/iconNames.d.ts.map +1 -0
- package/lib/typescript/module/ui/utils/sessionHelpers.d.ts +8 -3
- package/lib/typescript/module/ui/utils/sessionHelpers.d.ts.map +1 -1
- package/lib/typescript/module/utils/hookUtils.d.ts +8 -8
- package/lib/typescript/module/utils/hookUtils.d.ts.map +1 -1
- package/lib/typescript/module/utils/requestUtils.d.ts +3 -1
- package/lib/typescript/module/utils/requestUtils.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/core/mixins/index.ts +57 -43
- package/src/index.ts +3 -1
- package/src/ui/client.ts +55 -0
- package/src/ui/components/profile/EditFieldModal.tsx +465 -0
- package/src/ui/context/OxyContext.tsx +69 -0
- package/src/ui/hooks/useAuth.ts +159 -0
- package/src/ui/hooks/useSettingToggle.ts +7 -3
- package/src/ui/hooks/useWebSSO.ts +93 -0
- package/src/ui/index.ts +17 -2
- package/src/ui/screens/PrivacySettingsScreen.tsx +54 -63
- package/src/ui/screens/SearchSettingsScreen.tsx +42 -64
- package/src/ui/server.ts +70 -0
- package/src/ui/utils/iconNames.ts +136 -0
- package/src/ui/utils/sessionHelpers.ts +10 -3
- package/src/utils/hookUtils.ts +38 -14
- package/src/utils/requestUtils.ts +10 -7
|
@@ -1,43 +1,67 @@
|
|
|
1
|
-
import React, { useState,
|
|
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
|
-
//
|
|
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
|
-
|
|
35
|
-
const
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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={
|
|
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={
|
|
123
|
+
value={settings.searchPersonalization}
|
|
124
|
+
onValueChange={() => toggle('searchPersonalization')}
|
|
147
125
|
disabled={isSaving}
|
|
148
126
|
textColor={themeStyles.textColor}
|
|
149
127
|
mutedTextColor={themeStyles.mutedTextColor}
|
package/src/ui/server.ts
ADDED
|
@@ -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:
|
|
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:
|
|
107
|
+
oxyServices: OxyServicesAny,
|
|
101
108
|
sessionIds: string[],
|
|
102
109
|
{ useHeaderValidation = true, maxConcurrency = 5 }: ValidateSessionBatchOptions = {},
|
|
103
110
|
): Promise<SessionValidationResult[]> => {
|
package/src/utils/hookUtils.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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:
|
|
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:
|
|
195
|
+
level: LogLevel = 'error',
|
|
193
196
|
prefix: string = ''
|
|
194
197
|
) {
|
|
195
198
|
this.enabled = enabled;
|
|
196
|
-
this.level = level
|
|
199
|
+
this.level = level;
|
|
197
200
|
this.prefix = prefix;
|
|
198
201
|
}
|
|
199
202
|
|
|
200
|
-
private shouldLog(level:
|
|
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
|
|