@oxyhq/services 5.18.2 → 5.18.4
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 +16 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/ui/client.js +177 -0
- package/lib/commonjs/ui/client.js.map +1 -0
- package/lib/commonjs/ui/components/WebOxyProvider.js +98 -0
- package/lib/commonjs/ui/components/WebOxyProvider.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/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 +5 -2
- package/lib/module/index.js.map +1 -1
- package/lib/module/ui/client.js +48 -0
- package/lib/module/ui/client.js.map +1 -0
- package/lib/module/ui/components/WebOxyProvider.js +94 -0
- package/lib/module/ui/components/WebOxyProvider.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/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 +3 -0
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/client.d.ts +34 -0
- package/lib/typescript/commonjs/ui/client.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/WebOxyProvider.d.ts +44 -0
- package/lib/typescript/commonjs/ui/components/WebOxyProvider.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/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 +3 -0
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/client.d.ts +34 -0
- package/lib/typescript/module/ui/client.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/WebOxyProvider.d.ts +44 -0
- package/lib/typescript/module/ui/components/WebOxyProvider.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/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 +6 -1
- package/src/ui/client.ts +56 -0
- package/src/ui/components/WebOxyProvider.tsx +109 -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/requestUtils.ts +10 -7
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified Auth Hook
|
|
3
|
+
*
|
|
4
|
+
* Provides a clean, standard interface for authentication across all platforms.
|
|
5
|
+
* This is the recommended way to access auth state in Oxy apps.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* ```tsx
|
|
9
|
+
* import { useAuth } from '@oxyhq/services';
|
|
10
|
+
*
|
|
11
|
+
* function MyComponent() {
|
|
12
|
+
* const { user, isAuthenticated, isLoading, signIn, signOut } = useAuth();
|
|
13
|
+
*
|
|
14
|
+
* if (isLoading) return <Loading />;
|
|
15
|
+
* if (!isAuthenticated) return <SignInButton onClick={() => signIn()} />;
|
|
16
|
+
* return <Welcome user={user} />;
|
|
17
|
+
* }
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { useCallback } from 'react';
|
|
22
|
+
import { useOxy } from '../context/OxyContext';
|
|
23
|
+
import type { User } from '../../models/interfaces';
|
|
24
|
+
|
|
25
|
+
export interface AuthState {
|
|
26
|
+
/** Current authenticated user, null if not authenticated */
|
|
27
|
+
user: User | null;
|
|
28
|
+
|
|
29
|
+
/** Whether user is authenticated */
|
|
30
|
+
isAuthenticated: boolean;
|
|
31
|
+
|
|
32
|
+
/** Whether auth state is being determined (initial load) */
|
|
33
|
+
isLoading: boolean;
|
|
34
|
+
|
|
35
|
+
/** Whether the auth token is ready for API calls */
|
|
36
|
+
isReady: boolean;
|
|
37
|
+
|
|
38
|
+
/** Current error message, if any */
|
|
39
|
+
error: string | null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface AuthActions {
|
|
43
|
+
/**
|
|
44
|
+
* Sign in with cryptographic identity
|
|
45
|
+
* On native: Uses device keychain
|
|
46
|
+
* On web: Opens auth popup/redirect
|
|
47
|
+
*/
|
|
48
|
+
signIn: (publicKey?: string) => Promise<User>;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Sign out current session
|
|
52
|
+
*/
|
|
53
|
+
signOut: () => Promise<void>;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Sign out all sessions across all devices
|
|
57
|
+
*/
|
|
58
|
+
signOutAll: () => Promise<void>;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Refresh auth state (re-check session validity)
|
|
62
|
+
*/
|
|
63
|
+
refresh: () => Promise<void>;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface UseAuthReturn extends AuthState, AuthActions {
|
|
67
|
+
/** Access to full OxyServices instance for advanced usage */
|
|
68
|
+
oxyServices: ReturnType<typeof useOxy>['oxyServices'];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Unified auth hook for all Oxy apps
|
|
73
|
+
*
|
|
74
|
+
* Features:
|
|
75
|
+
* - Zero config: Just wrap with OxyProvider and use
|
|
76
|
+
* - Cross-platform: Same API on native and web
|
|
77
|
+
* - Auto SSO: Web apps automatically check for cross-domain sessions
|
|
78
|
+
* - Type-safe: Full TypeScript support
|
|
79
|
+
*/
|
|
80
|
+
export function useAuth(): UseAuthReturn {
|
|
81
|
+
const {
|
|
82
|
+
user,
|
|
83
|
+
isAuthenticated,
|
|
84
|
+
isLoading,
|
|
85
|
+
isTokenReady,
|
|
86
|
+
error,
|
|
87
|
+
signIn: oxySignIn,
|
|
88
|
+
logout,
|
|
89
|
+
logoutAll,
|
|
90
|
+
refreshSessions,
|
|
91
|
+
oxyServices,
|
|
92
|
+
hasIdentity,
|
|
93
|
+
getPublicKey,
|
|
94
|
+
showBottomSheet,
|
|
95
|
+
} = useOxy();
|
|
96
|
+
|
|
97
|
+
const signIn = useCallback(async (publicKey?: string): Promise<User> => {
|
|
98
|
+
// If public key provided, use it directly
|
|
99
|
+
if (publicKey) {
|
|
100
|
+
return oxySignIn(publicKey);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Try to get existing identity
|
|
104
|
+
const hasExisting = await hasIdentity();
|
|
105
|
+
|
|
106
|
+
if (hasExisting) {
|
|
107
|
+
const existingKey = await getPublicKey();
|
|
108
|
+
if (existingKey) {
|
|
109
|
+
return oxySignIn(existingKey);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// No identity - show auth UI
|
|
114
|
+
// On native: shows bottom sheet for identity creation
|
|
115
|
+
// On web: could trigger popup auth
|
|
116
|
+
showBottomSheet?.('OxyAuth');
|
|
117
|
+
|
|
118
|
+
// Return a promise that resolves when auth completes
|
|
119
|
+
// This is a simplified version - real implementation would
|
|
120
|
+
// wait for the auth flow to complete
|
|
121
|
+
return new Promise((resolve, reject) => {
|
|
122
|
+
// For now, just reject - the bottom sheet handles the flow
|
|
123
|
+
reject(new Error('Please complete sign-in in the auth sheet'));
|
|
124
|
+
});
|
|
125
|
+
}, [oxySignIn, hasIdentity, getPublicKey, showBottomSheet]);
|
|
126
|
+
|
|
127
|
+
const signOut = useCallback(async (): Promise<void> => {
|
|
128
|
+
await logout();
|
|
129
|
+
}, [logout]);
|
|
130
|
+
|
|
131
|
+
const signOutAll = useCallback(async (): Promise<void> => {
|
|
132
|
+
await logoutAll();
|
|
133
|
+
}, [logoutAll]);
|
|
134
|
+
|
|
135
|
+
const refresh = useCallback(async (): Promise<void> => {
|
|
136
|
+
await refreshSessions();
|
|
137
|
+
}, [refreshSessions]);
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
// State
|
|
141
|
+
user,
|
|
142
|
+
isAuthenticated,
|
|
143
|
+
isLoading,
|
|
144
|
+
isReady: isTokenReady,
|
|
145
|
+
error,
|
|
146
|
+
|
|
147
|
+
// Actions
|
|
148
|
+
signIn,
|
|
149
|
+
signOut,
|
|
150
|
+
signOutAll,
|
|
151
|
+
refresh,
|
|
152
|
+
|
|
153
|
+
// Advanced
|
|
154
|
+
oxyServices,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Re-export useOxy for backward compatibility and advanced usage
|
|
159
|
+
export { useOxy } from '../context/OxyContext';
|
|
@@ -91,7 +91,7 @@ export function useSettingToggle(options: UseSettingToggleOptions): UseSettingTo
|
|
|
91
91
|
* Hook for managing multiple toggle settings at once.
|
|
92
92
|
* Useful when you have several related boolean settings.
|
|
93
93
|
*/
|
|
94
|
-
export function useSettingToggles<T extends
|
|
94
|
+
export function useSettingToggles<T extends { [K in keyof T]: boolean }>(options: {
|
|
95
95
|
initialValues: T;
|
|
96
96
|
onSave: (key: keyof T, value: boolean) => Promise<void>;
|
|
97
97
|
errorMessage?: string | ((key: keyof T) => string);
|
|
@@ -100,7 +100,7 @@ export function useSettingToggles<T extends Record<string, boolean>>(options: {
|
|
|
100
100
|
values: T;
|
|
101
101
|
savingKeys: Set<keyof T>;
|
|
102
102
|
toggle: (key: keyof T) => Promise<void>;
|
|
103
|
-
setValues: (values: T) => void;
|
|
103
|
+
setValues: (values: Partial<T>) => void;
|
|
104
104
|
} {
|
|
105
105
|
const { initialValues, onSave, errorMessage = 'Failed to save setting', revertOnError = true } = options;
|
|
106
106
|
|
|
@@ -141,7 +141,11 @@ export function useSettingToggles<T extends Record<string, boolean>>(options: {
|
|
|
141
141
|
}
|
|
142
142
|
}, [values, onSave, errorMessage, revertOnError]);
|
|
143
143
|
|
|
144
|
-
|
|
144
|
+
const setValuesExternal = useCallback((newValues: Partial<T>) => {
|
|
145
|
+
setValues(prev => ({ ...prev, ...newValues }));
|
|
146
|
+
}, []);
|
|
147
|
+
|
|
148
|
+
return { values, savingKeys, toggle, setValues: setValuesExternal };
|
|
145
149
|
}
|
|
146
150
|
|
|
147
151
|
export default useSettingToggle;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web SSO Hook
|
|
3
|
+
*
|
|
4
|
+
* Automatically handles cross-domain SSO for web apps.
|
|
5
|
+
* Uses the OxyServices.silentSignIn() method which loads a hidden iframe
|
|
6
|
+
* to check for existing session at auth.oxy.so.
|
|
7
|
+
*
|
|
8
|
+
* This is called automatically by OxyContext on web platforms.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { useEffect, useRef, useCallback } from 'react';
|
|
12
|
+
import type { OxyServices } from '../../core/OxyServices';
|
|
13
|
+
import type { SessionLoginResponse } from '../../models/session';
|
|
14
|
+
|
|
15
|
+
interface UseWebSSOOptions {
|
|
16
|
+
oxyServices: OxyServices;
|
|
17
|
+
onSessionFound: (session: SessionLoginResponse) => Promise<void>;
|
|
18
|
+
onError?: (error: Error) => void;
|
|
19
|
+
enabled?: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface UseWebSSOResult {
|
|
23
|
+
checkSSO: () => Promise<SessionLoginResponse | null>;
|
|
24
|
+
isChecking: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Check if we're running in a web browser environment (not React Native)
|
|
29
|
+
*/
|
|
30
|
+
function isWebBrowser(): boolean {
|
|
31
|
+
// Check for browser globals and that we have a real DOM (React Native has window but not documentElement)
|
|
32
|
+
return typeof window !== 'undefined' &&
|
|
33
|
+
typeof document !== 'undefined' &&
|
|
34
|
+
typeof document.documentElement !== 'undefined';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Hook for automatic web SSO
|
|
39
|
+
*
|
|
40
|
+
* Automatically checks for existing cross-domain session on mount.
|
|
41
|
+
* Only runs on web platforms. Uses OxyServices.silentSignIn() internally.
|
|
42
|
+
*/
|
|
43
|
+
export function useWebSSO({
|
|
44
|
+
oxyServices,
|
|
45
|
+
onSessionFound,
|
|
46
|
+
onError,
|
|
47
|
+
enabled = true,
|
|
48
|
+
}: UseWebSSOOptions): UseWebSSOResult {
|
|
49
|
+
const isCheckingRef = useRef(false);
|
|
50
|
+
const hasCheckedRef = useRef(false);
|
|
51
|
+
|
|
52
|
+
const checkSSO = useCallback(async (): Promise<SessionLoginResponse | null> => {
|
|
53
|
+
if (!isWebBrowser() || isCheckingRef.current) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
isCheckingRef.current = true;
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
// Use the existing silentSignIn method from OxyServices
|
|
61
|
+
// which handles iframe creation, postMessage, and token storage
|
|
62
|
+
const session = await (oxyServices as any).silentSignIn?.();
|
|
63
|
+
|
|
64
|
+
if (session) {
|
|
65
|
+
await onSessionFound(session);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return session;
|
|
69
|
+
} catch (error) {
|
|
70
|
+
onError?.(error instanceof Error ? error : new Error(String(error)));
|
|
71
|
+
return null;
|
|
72
|
+
} finally {
|
|
73
|
+
isCheckingRef.current = false;
|
|
74
|
+
}
|
|
75
|
+
}, [oxyServices, onSessionFound, onError]);
|
|
76
|
+
|
|
77
|
+
// Auto-check SSO on mount (web only)
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
if (!enabled || !isWebBrowser() || hasCheckedRef.current) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
hasCheckedRef.current = true;
|
|
84
|
+
checkSSO();
|
|
85
|
+
}, [enabled, checkSSO]);
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
checkSSO,
|
|
89
|
+
isChecking: isCheckingRef.current,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export { isWebBrowser };
|
package/src/ui/index.ts
CHANGED
|
@@ -1,13 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* UI Component exports - Frontend Only (with backend-safe fallbacks)
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* This module exports all React/React Native UI components and hooks.
|
|
5
5
|
* In backend, all exports are no-ops or empty objects.
|
|
6
|
+
*
|
|
7
|
+
* NOTE: This entry point uses runtime detection which prevents tree-shaking.
|
|
8
|
+
* For better bundle optimization, use:
|
|
9
|
+
* - '@oxyhq/services/ui/client' for client bundles (tree-shakeable)
|
|
10
|
+
* - '@oxyhq/services/ui/server' for SSR environments (all noops)
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* // Client bundle (tree-shakeable)
|
|
14
|
+
* import { OxyProvider, useOxy } from '@oxyhq/services/ui/client';
|
|
15
|
+
*
|
|
16
|
+
* // SSR (noops)
|
|
17
|
+
* import { OxyProvider, useOxy } from '@oxyhq/services/ui/server';
|
|
6
18
|
*/
|
|
7
19
|
import isFrontend from './isFrontend';
|
|
8
20
|
|
|
9
21
|
// UI exports
|
|
10
|
-
let OxyProvider, OxySignInButton, OxyLogo, Avatar, FollowButton, OxyPayButton, FontLoader, setupFonts, OxyIcon, useOxy, useFollow, ProfileScreen, useAuthStore, useAccountStore, fontFamilies, fontStyles, toast, useStorage;
|
|
22
|
+
let OxyProvider, OxySignInButton, OxyLogo, Avatar, FollowButton, OxyPayButton, FontLoader, setupFonts, OxyIcon, useOxy, useAuth, useFollow, ProfileScreen, useAuthStore, useAccountStore, fontFamilies, fontStyles, toast, useStorage;
|
|
11
23
|
|
|
12
24
|
if (isFrontend) {
|
|
13
25
|
OxyProvider = require('./components/OxyProvider').default;
|
|
@@ -20,6 +32,7 @@ if (isFrontend) {
|
|
|
20
32
|
setupFonts = require('./components/FontLoader').setupFonts;
|
|
21
33
|
OxyIcon = require('./components/icon').OxyIcon;
|
|
22
34
|
useOxy = require('./context/OxyContext').useOxy;
|
|
35
|
+
useAuth = require('./hooks/useAuth').useAuth;
|
|
23
36
|
useFollow = require('./hooks').useFollow;
|
|
24
37
|
ProfileScreen = require('./screens/ProfileScreen').default;
|
|
25
38
|
useAuthStore = require('./stores/authStore').useAuthStore;
|
|
@@ -44,6 +57,7 @@ if (isFrontend) {
|
|
|
44
57
|
setupFonts = () => {};
|
|
45
58
|
OxyIcon = noopComponent;
|
|
46
59
|
useOxy = noopHook;
|
|
60
|
+
useAuth = noopHook;
|
|
47
61
|
useFollow = noopHook;
|
|
48
62
|
ProfileScreen = noopComponent;
|
|
49
63
|
useAuthStore = noopHook;
|
|
@@ -65,6 +79,7 @@ export {
|
|
|
65
79
|
setupFonts,
|
|
66
80
|
OxyIcon,
|
|
67
81
|
useOxy,
|
|
82
|
+
useAuth,
|
|
68
83
|
useFollow,
|
|
69
84
|
ProfileScreen,
|
|
70
85
|
useAuthStore,
|
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
Text,
|
|
5
5
|
StyleSheet,
|
|
6
6
|
ScrollView,
|
|
7
|
-
ActivityIndicator,
|
|
8
7
|
TouchableOpacity,
|
|
9
8
|
} from 'react-native';
|
|
10
9
|
import type { BaseScreenProps } from '../types/navigation';
|
|
@@ -12,6 +11,7 @@ import { toast } from '../../lib/sonner';
|
|
|
12
11
|
import { Header, Section, Avatar, SettingRow, LoadingState, EmptyState, GroupedSection } from '../components';
|
|
13
12
|
import { useI18n } from '../hooks/useI18n';
|
|
14
13
|
import { useThemeStyles } from '../hooks/useThemeStyles';
|
|
14
|
+
import { useSettingToggles } from '../hooks/useSettingToggle';
|
|
15
15
|
import { normalizeTheme } from '../utils/themeUtils';
|
|
16
16
|
import type { BlockedUser, RestrictedUser } from '../../models/interfaces';
|
|
17
17
|
import { useOxy } from '../context/OxyContext';
|
|
@@ -38,42 +38,53 @@ interface PrivacySettings {
|
|
|
38
38
|
muteKeywords: boolean;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
const DEFAULT_PRIVACY_SETTINGS: PrivacySettings = {
|
|
42
|
+
isPrivateAccount: false,
|
|
43
|
+
hideOnlineStatus: false,
|
|
44
|
+
hideLastSeen: false,
|
|
45
|
+
profileVisibility: true,
|
|
46
|
+
loginAlerts: true,
|
|
47
|
+
blockScreenshots: false,
|
|
48
|
+
login: true,
|
|
49
|
+
biometricLogin: false,
|
|
50
|
+
showActivity: true,
|
|
51
|
+
allowTagging: true,
|
|
52
|
+
allowMentions: true,
|
|
53
|
+
hideReadReceipts: false,
|
|
54
|
+
allowDirectMessages: true,
|
|
55
|
+
dataSharing: true,
|
|
56
|
+
locationSharing: false,
|
|
57
|
+
analyticsSharing: true,
|
|
58
|
+
sensitiveContent: false,
|
|
59
|
+
autoFilter: true,
|
|
60
|
+
muteKeywords: false,
|
|
61
|
+
};
|
|
62
|
+
|
|
41
63
|
const PrivacySettingsScreen: React.FC<BaseScreenProps> = ({
|
|
42
64
|
onClose,
|
|
43
65
|
theme,
|
|
44
66
|
goBack,
|
|
45
67
|
}) => {
|
|
46
|
-
// Use useOxy() hook for OxyContext values
|
|
47
68
|
const { oxyServices, user } = useOxy();
|
|
48
69
|
const { t } = useI18n();
|
|
49
|
-
const [settings, setSettings] = useState<PrivacySettings>({
|
|
50
|
-
isPrivateAccount: false,
|
|
51
|
-
hideOnlineStatus: false,
|
|
52
|
-
hideLastSeen: false,
|
|
53
|
-
profileVisibility: true,
|
|
54
|
-
loginAlerts: true,
|
|
55
|
-
blockScreenshots: false,
|
|
56
|
-
login: true,
|
|
57
|
-
biometricLogin: false,
|
|
58
|
-
showActivity: true,
|
|
59
|
-
allowTagging: true,
|
|
60
|
-
allowMentions: true,
|
|
61
|
-
hideReadReceipts: false,
|
|
62
|
-
allowDirectMessages: true,
|
|
63
|
-
dataSharing: true,
|
|
64
|
-
locationSharing: false,
|
|
65
|
-
analyticsSharing: true,
|
|
66
|
-
sensitiveContent: false,
|
|
67
|
-
autoFilter: true,
|
|
68
|
-
muteKeywords: false,
|
|
69
|
-
});
|
|
70
70
|
const [isLoading, setIsLoading] = useState(true);
|
|
71
|
-
const [isSaving, setIsSaving] = useState(false);
|
|
72
71
|
const [blockedUsers, setBlockedUsers] = useState<BlockedUser[]>([]);
|
|
73
72
|
const [restrictedUsers, setRestrictedUsers] = useState<RestrictedUser[]>([]);
|
|
74
73
|
const [isLoadingUsers, setIsLoadingUsers] = useState(false);
|
|
75
74
|
|
|
76
|
-
//
|
|
75
|
+
// Use the existing useSettingToggles hook for toggle management
|
|
76
|
+
const { values: settings, toggle, savingKeys, setValues } = useSettingToggles<PrivacySettings>({
|
|
77
|
+
initialValues: DEFAULT_PRIVACY_SETTINGS,
|
|
78
|
+
onSave: async (key, value) => {
|
|
79
|
+
if (!user?.id || !oxyServices) return;
|
|
80
|
+
await oxyServices.updatePrivacySettings({ [key]: value }, user.id);
|
|
81
|
+
},
|
|
82
|
+
errorMessage: t('privacySettings.updateError') || 'Failed to update privacy setting',
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const isSaving = savingKeys.size > 0;
|
|
86
|
+
|
|
87
|
+
// Load settings
|
|
77
88
|
useEffect(() => {
|
|
78
89
|
const loadSettings = async () => {
|
|
79
90
|
try {
|
|
@@ -81,7 +92,7 @@ const PrivacySettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
81
92
|
if (user?.id && oxyServices) {
|
|
82
93
|
const privacySettings = await oxyServices.getPrivacySettings(user.id);
|
|
83
94
|
if (privacySettings) {
|
|
84
|
-
|
|
95
|
+
setValues(privacySettings);
|
|
85
96
|
}
|
|
86
97
|
}
|
|
87
98
|
} catch (error) {
|
|
@@ -93,7 +104,7 @@ const PrivacySettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
93
104
|
};
|
|
94
105
|
|
|
95
106
|
loadSettings();
|
|
96
|
-
}, [user?.id, oxyServices, t]);
|
|
107
|
+
}, [user?.id, oxyServices, t, setValues]);
|
|
97
108
|
|
|
98
109
|
// Load blocked and restricted users
|
|
99
110
|
useEffect(() => {
|
|
@@ -117,26 +128,6 @@ const PrivacySettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
117
128
|
loadUsers();
|
|
118
129
|
}, [oxyServices]);
|
|
119
130
|
|
|
120
|
-
const updateSetting = useCallback(async (key: keyof PrivacySettings, value: boolean) => {
|
|
121
|
-
try {
|
|
122
|
-
setIsSaving(true);
|
|
123
|
-
const newSettings = { ...settings, [key]: value };
|
|
124
|
-
setSettings(newSettings);
|
|
125
|
-
|
|
126
|
-
if (user?.id && oxyServices) {
|
|
127
|
-
await oxyServices.updatePrivacySettings({ [key]: value }, user.id);
|
|
128
|
-
toast.success(t('privacySettings.updated') || 'Privacy settings updated');
|
|
129
|
-
}
|
|
130
|
-
} catch (error) {
|
|
131
|
-
console.error(`Failed to update ${key}:`, error);
|
|
132
|
-
toast.error(t('privacySettings.updateError') || 'Failed to update privacy setting');
|
|
133
|
-
// Revert on error
|
|
134
|
-
setSettings(settings);
|
|
135
|
-
} finally {
|
|
136
|
-
setIsSaving(false);
|
|
137
|
-
}
|
|
138
|
-
}, [settings, user?.id, oxyServices, t]);
|
|
139
|
-
|
|
140
131
|
const handleUnblock = useCallback(async (userId: string) => {
|
|
141
132
|
if (!oxyServices) return;
|
|
142
133
|
try {
|
|
@@ -295,7 +286,7 @@ const PrivacySettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
295
286
|
title={t('privacySettings.isPrivateAccount') || 'Private Account'}
|
|
296
287
|
description={t('privacySettings.isPrivateAccountDesc') || 'Only approved followers can see your posts'}
|
|
297
288
|
value={settings.isPrivateAccount}
|
|
298
|
-
onValueChange={(
|
|
289
|
+
onValueChange={() => toggle('isPrivateAccount')}
|
|
299
290
|
disabled={isSaving}
|
|
300
291
|
textColor={themeStyles.textColor}
|
|
301
292
|
mutedTextColor={themeStyles.mutedTextColor}
|
|
@@ -305,7 +296,7 @@ const PrivacySettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
305
296
|
title={t('privacySettings.profileVisibility') || 'Profile Visibility'}
|
|
306
297
|
description={t('privacySettings.profileVisibilityDesc') || 'Control who can view your profile'}
|
|
307
298
|
value={settings.profileVisibility}
|
|
308
|
-
onValueChange={(
|
|
299
|
+
onValueChange={() => toggle('profileVisibility')}
|
|
309
300
|
disabled={isSaving}
|
|
310
301
|
textColor={themeStyles.textColor}
|
|
311
302
|
mutedTextColor={themeStyles.mutedTextColor}
|
|
@@ -315,7 +306,7 @@ const PrivacySettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
315
306
|
title={t('privacySettings.hideOnlineStatus') || 'Hide Online Status'}
|
|
316
307
|
description={t('privacySettings.hideOnlineStatusDesc') || 'Don\'t show when you\'re online'}
|
|
317
308
|
value={settings.hideOnlineStatus}
|
|
318
|
-
onValueChange={(
|
|
309
|
+
onValueChange={() => toggle('hideOnlineStatus')}
|
|
319
310
|
disabled={isSaving}
|
|
320
311
|
textColor={themeStyles.textColor}
|
|
321
312
|
mutedTextColor={themeStyles.mutedTextColor}
|
|
@@ -325,7 +316,7 @@ const PrivacySettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
325
316
|
title={t('privacySettings.hideLastSeen') || 'Hide Last Seen'}
|
|
326
317
|
description={t('privacySettings.hideLastSeenDesc') || 'Don\'t show when you were last active'}
|
|
327
318
|
value={settings.hideLastSeen}
|
|
328
|
-
onValueChange={(
|
|
319
|
+
onValueChange={() => toggle('hideLastSeen')}
|
|
329
320
|
disabled={isSaving}
|
|
330
321
|
textColor={themeStyles.textColor}
|
|
331
322
|
mutedTextColor={themeStyles.mutedTextColor}
|
|
@@ -339,7 +330,7 @@ const PrivacySettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
339
330
|
title={t('privacySettings.allowTagging') || 'Allow Tagging'}
|
|
340
331
|
description={t('privacySettings.allowTaggingDesc') || 'Let others tag you in posts'}
|
|
341
332
|
value={settings.allowTagging}
|
|
342
|
-
onValueChange={(
|
|
333
|
+
onValueChange={() => toggle('allowTagging')}
|
|
343
334
|
disabled={isSaving}
|
|
344
335
|
textColor={themeStyles.textColor}
|
|
345
336
|
mutedTextColor={themeStyles.mutedTextColor}
|
|
@@ -349,7 +340,7 @@ const PrivacySettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
349
340
|
title={t('privacySettings.allowMentions') || 'Allow Mentions'}
|
|
350
341
|
description={t('privacySettings.allowMentionsDesc') || 'Let others mention you'}
|
|
351
342
|
value={settings.allowMentions}
|
|
352
|
-
onValueChange={(
|
|
343
|
+
onValueChange={() => toggle('allowMentions')}
|
|
353
344
|
disabled={isSaving}
|
|
354
345
|
textColor={themeStyles.textColor}
|
|
355
346
|
mutedTextColor={themeStyles.mutedTextColor}
|
|
@@ -359,7 +350,7 @@ const PrivacySettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
359
350
|
title={t('privacySettings.allowDirectMessages') || 'Allow Direct Messages'}
|
|
360
351
|
description={t('privacySettings.allowDirectMessagesDesc') || 'Let others send you direct messages'}
|
|
361
352
|
value={settings.allowDirectMessages}
|
|
362
|
-
onValueChange={(
|
|
353
|
+
onValueChange={() => toggle('allowDirectMessages')}
|
|
363
354
|
disabled={isSaving}
|
|
364
355
|
textColor={themeStyles.textColor}
|
|
365
356
|
mutedTextColor={themeStyles.mutedTextColor}
|
|
@@ -369,7 +360,7 @@ const PrivacySettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
369
360
|
title={t('privacySettings.hideReadReceipts') || 'Hide Read Receipts'}
|
|
370
361
|
description={t('privacySettings.hideReadReceiptsDesc') || 'Don\'t show read receipts in messages'}
|
|
371
362
|
value={settings.hideReadReceipts}
|
|
372
|
-
onValueChange={(
|
|
363
|
+
onValueChange={() => toggle('hideReadReceipts')}
|
|
373
364
|
disabled={isSaving}
|
|
374
365
|
textColor={themeStyles.textColor}
|
|
375
366
|
mutedTextColor={themeStyles.mutedTextColor}
|
|
@@ -383,7 +374,7 @@ const PrivacySettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
383
374
|
title={t('privacySettings.showActivity') || 'Show Activity Status'}
|
|
384
375
|
description={t('privacySettings.showActivityDesc') || 'Display your activity on your profile'}
|
|
385
376
|
value={settings.showActivity}
|
|
386
|
-
onValueChange={(
|
|
377
|
+
onValueChange={() => toggle('showActivity')}
|
|
387
378
|
disabled={isSaving}
|
|
388
379
|
textColor={themeStyles.textColor}
|
|
389
380
|
mutedTextColor={themeStyles.mutedTextColor}
|
|
@@ -393,7 +384,7 @@ const PrivacySettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
393
384
|
title={t('privacySettings.dataSharing') || 'Data Sharing'}
|
|
394
385
|
description={t('privacySettings.dataSharingDesc') || 'Allow sharing data for personalization'}
|
|
395
386
|
value={settings.dataSharing}
|
|
396
|
-
onValueChange={(
|
|
387
|
+
onValueChange={() => toggle('dataSharing')}
|
|
397
388
|
disabled={isSaving}
|
|
398
389
|
textColor={themeStyles.textColor}
|
|
399
390
|
mutedTextColor={themeStyles.mutedTextColor}
|
|
@@ -403,7 +394,7 @@ const PrivacySettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
403
394
|
title={t('privacySettings.locationSharing') || 'Location Sharing'}
|
|
404
395
|
description={t('privacySettings.locationSharingDesc') || 'Share your location'}
|
|
405
396
|
value={settings.locationSharing}
|
|
406
|
-
onValueChange={(
|
|
397
|
+
onValueChange={() => toggle('locationSharing')}
|
|
407
398
|
disabled={isSaving}
|
|
408
399
|
textColor={themeStyles.textColor}
|
|
409
400
|
mutedTextColor={themeStyles.mutedTextColor}
|
|
@@ -413,7 +404,7 @@ const PrivacySettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
413
404
|
title={t('privacySettings.analyticsSharing') || 'Analytics Sharing'}
|
|
414
405
|
description={t('privacySettings.analyticsSharingDesc') || 'Allow analytics data collection'}
|
|
415
406
|
value={settings.analyticsSharing}
|
|
416
|
-
onValueChange={(
|
|
407
|
+
onValueChange={() => toggle('analyticsSharing')}
|
|
417
408
|
disabled={isSaving}
|
|
418
409
|
textColor={themeStyles.textColor}
|
|
419
410
|
mutedTextColor={themeStyles.mutedTextColor}
|
|
@@ -427,7 +418,7 @@ const PrivacySettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
427
418
|
title={t('privacySettings.sensitiveContent') || 'Show Sensitive Content'}
|
|
428
419
|
description={t('privacySettings.sensitiveContentDesc') || 'Allow sensitive or explicit content'}
|
|
429
420
|
value={settings.sensitiveContent}
|
|
430
|
-
onValueChange={(
|
|
421
|
+
onValueChange={() => toggle('sensitiveContent')}
|
|
431
422
|
disabled={isSaving}
|
|
432
423
|
textColor={themeStyles.textColor}
|
|
433
424
|
mutedTextColor={themeStyles.mutedTextColor}
|
|
@@ -437,7 +428,7 @@ const PrivacySettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
437
428
|
title={t('privacySettings.autoFilter') || 'Auto Filter'}
|
|
438
429
|
description={t('privacySettings.autoFilterDesc') || 'Automatically filter inappropriate content'}
|
|
439
430
|
value={settings.autoFilter}
|
|
440
|
-
onValueChange={(
|
|
431
|
+
onValueChange={() => toggle('autoFilter')}
|
|
441
432
|
disabled={isSaving}
|
|
442
433
|
textColor={themeStyles.textColor}
|
|
443
434
|
mutedTextColor={themeStyles.mutedTextColor}
|
|
@@ -447,7 +438,7 @@ const PrivacySettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
447
438
|
title={t('privacySettings.muteKeywords') || 'Mute Keywords'}
|
|
448
439
|
description={t('privacySettings.muteKeywordsDesc') || 'Hide posts containing muted keywords'}
|
|
449
440
|
value={settings.muteKeywords}
|
|
450
|
-
onValueChange={(
|
|
441
|
+
onValueChange={() => toggle('muteKeywords')}
|
|
451
442
|
disabled={isSaving}
|
|
452
443
|
textColor={themeStyles.textColor}
|
|
453
444
|
mutedTextColor={themeStyles.mutedTextColor}
|
|
@@ -457,7 +448,7 @@ const PrivacySettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
457
448
|
title={t('privacySettings.blockScreenshots') || 'Block Screenshots'}
|
|
458
449
|
description={t('privacySettings.blockScreenshotsDesc') || 'Prevent screenshots of your content'}
|
|
459
450
|
value={settings.blockScreenshots}
|
|
460
|
-
onValueChange={(
|
|
451
|
+
onValueChange={() => toggle('blockScreenshots')}
|
|
461
452
|
disabled={isSaving}
|
|
462
453
|
textColor={themeStyles.textColor}
|
|
463
454
|
mutedTextColor={themeStyles.mutedTextColor}
|