@oxyhq/auth 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -0
- package/dist/cjs/WebOxyProvider.js +287 -0
- package/dist/cjs/hooks/mutations/index.js +23 -0
- package/dist/cjs/hooks/mutations/mutationFactory.js +126 -0
- package/dist/cjs/hooks/mutations/useAccountMutations.js +275 -0
- package/dist/cjs/hooks/mutations/useServicesMutations.js +149 -0
- package/dist/cjs/hooks/queries/index.js +35 -0
- package/dist/cjs/hooks/queries/queryKeys.js +82 -0
- package/dist/cjs/hooks/queries/useAccountQueries.js +141 -0
- package/dist/cjs/hooks/queries/useSecurityQueries.js +45 -0
- package/dist/cjs/hooks/queries/useServicesQueries.js +113 -0
- package/dist/cjs/hooks/queryClient.js +110 -0
- package/dist/cjs/hooks/useAssets.js +225 -0
- package/dist/cjs/hooks/useFileDownloadUrl.js +91 -0
- package/dist/cjs/hooks/useFileFiltering.js +81 -0
- package/dist/cjs/hooks/useFollow.js +159 -0
- package/dist/cjs/hooks/useFollow.types.js +4 -0
- package/dist/cjs/hooks/useQueryClient.js +16 -0
- package/dist/cjs/hooks/useSessionSocket.js +215 -0
- package/dist/cjs/hooks/useWebSSO.js +146 -0
- package/dist/cjs/index.js +115 -0
- package/dist/cjs/stores/accountStore.js +226 -0
- package/dist/cjs/stores/assetStore.js +192 -0
- package/dist/cjs/stores/authStore.js +47 -0
- package/dist/cjs/stores/followStore.js +154 -0
- package/dist/cjs/utils/authHelpers.js +154 -0
- package/dist/cjs/utils/avatarUtils.js +77 -0
- package/dist/cjs/utils/errorHandlers.js +128 -0
- package/dist/cjs/utils/sessionHelpers.js +90 -0
- package/dist/cjs/utils/storageHelpers.js +147 -0
- package/dist/esm/WebOxyProvider.js +282 -0
- package/dist/esm/hooks/mutations/index.js +10 -0
- package/dist/esm/hooks/mutations/mutationFactory.js +122 -0
- package/dist/esm/hooks/mutations/useAccountMutations.js +267 -0
- package/dist/esm/hooks/mutations/useServicesMutations.js +141 -0
- package/dist/esm/hooks/queries/index.js +14 -0
- package/dist/esm/hooks/queries/queryKeys.js +76 -0
- package/dist/esm/hooks/queries/useAccountQueries.js +131 -0
- package/dist/esm/hooks/queries/useSecurityQueries.js +40 -0
- package/dist/esm/hooks/queries/useServicesQueries.js +105 -0
- package/dist/esm/hooks/queryClient.js +104 -0
- package/dist/esm/hooks/useAssets.js +220 -0
- package/dist/esm/hooks/useFileDownloadUrl.js +86 -0
- package/dist/esm/hooks/useFileFiltering.js +78 -0
- package/dist/esm/hooks/useFollow.js +154 -0
- package/dist/esm/hooks/useFollow.types.js +3 -0
- package/dist/esm/hooks/useQueryClient.js +12 -0
- package/dist/esm/hooks/useSessionSocket.js +209 -0
- package/dist/esm/hooks/useWebSSO.js +143 -0
- package/dist/esm/index.js +48 -0
- package/dist/esm/stores/accountStore.js +219 -0
- package/dist/esm/stores/assetStore.js +180 -0
- package/dist/esm/stores/authStore.js +44 -0
- package/dist/esm/stores/followStore.js +151 -0
- package/dist/esm/utils/authHelpers.js +145 -0
- package/dist/esm/utils/avatarUtils.js +72 -0
- package/dist/esm/utils/errorHandlers.js +121 -0
- package/dist/esm/utils/sessionHelpers.js +84 -0
- package/dist/esm/utils/storageHelpers.js +108 -0
- package/dist/types/WebOxyProvider.d.ts +97 -0
- package/dist/types/hooks/mutations/index.d.ts +8 -0
- package/dist/types/hooks/mutations/mutationFactory.d.ts +75 -0
- package/dist/types/hooks/mutations/useAccountMutations.d.ts +68 -0
- package/dist/types/hooks/mutations/useServicesMutations.d.ts +22 -0
- package/dist/types/hooks/queries/index.d.ts +10 -0
- package/dist/types/hooks/queries/queryKeys.d.ts +64 -0
- package/dist/types/hooks/queries/useAccountQueries.d.ts +42 -0
- package/dist/types/hooks/queries/useSecurityQueries.d.ts +14 -0
- package/dist/types/hooks/queries/useServicesQueries.d.ts +31 -0
- package/dist/types/hooks/queryClient.d.ts +18 -0
- package/dist/types/hooks/useAssets.d.ts +34 -0
- package/dist/types/hooks/useFileDownloadUrl.d.ts +18 -0
- package/dist/types/hooks/useFileFiltering.d.ts +28 -0
- package/dist/types/hooks/useFollow.d.ts +61 -0
- package/dist/types/hooks/useFollow.types.d.ts +32 -0
- package/dist/types/hooks/useQueryClient.d.ts +6 -0
- package/dist/types/hooks/useSessionSocket.d.ts +13 -0
- package/dist/types/hooks/useWebSSO.d.ts +57 -0
- package/dist/types/index.d.ts +46 -0
- package/dist/types/stores/accountStore.d.ts +33 -0
- package/dist/types/stores/assetStore.d.ts +53 -0
- package/dist/types/stores/authStore.d.ts +16 -0
- package/dist/types/stores/followStore.d.ts +24 -0
- package/dist/types/utils/authHelpers.d.ts +98 -0
- package/dist/types/utils/avatarUtils.d.ts +33 -0
- package/dist/types/utils/errorHandlers.d.ts +34 -0
- package/dist/types/utils/sessionHelpers.d.ts +63 -0
- package/dist/types/utils/storageHelpers.d.ts +27 -0
- package/package.json +71 -0
- package/src/WebOxyProvider.tsx +372 -0
- package/src/global.d.ts +1 -0
- package/src/hooks/mutations/index.ts +25 -0
- package/src/hooks/mutations/mutationFactory.ts +215 -0
- package/src/hooks/mutations/useAccountMutations.ts +344 -0
- package/src/hooks/mutations/useServicesMutations.ts +164 -0
- package/src/hooks/queries/index.ts +36 -0
- package/src/hooks/queries/queryKeys.ts +88 -0
- package/src/hooks/queries/useAccountQueries.ts +152 -0
- package/src/hooks/queries/useSecurityQueries.ts +64 -0
- package/src/hooks/queries/useServicesQueries.ts +126 -0
- package/src/hooks/queryClient.ts +112 -0
- package/src/hooks/useAssets.ts +291 -0
- package/src/hooks/useFileDownloadUrl.ts +118 -0
- package/src/hooks/useFileFiltering.ts +115 -0
- package/src/hooks/useFollow.ts +175 -0
- package/src/hooks/useFollow.types.ts +33 -0
- package/src/hooks/useQueryClient.ts +17 -0
- package/src/hooks/useSessionSocket.ts +233 -0
- package/src/hooks/useWebSSO.ts +187 -0
- package/src/index.ts +144 -0
- package/src/stores/accountStore.ts +296 -0
- package/src/stores/assetStore.ts +281 -0
- package/src/stores/authStore.ts +63 -0
- package/src/stores/followStore.ts +181 -0
- package/src/utils/authHelpers.ts +183 -0
- package/src/utils/avatarUtils.ts +103 -0
- package/src/utils/errorHandlers.ts +194 -0
- package/src/utils/sessionHelpers.ts +151 -0
- package/src/utils/storageHelpers.ts +130 -0
package/README.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# @oxyhq/auth
|
|
2
|
+
|
|
3
|
+
OxyHQ Web Auth SDK. Headless React hooks for web applications. Zero React Native or Expo dependencies.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @oxyhq/auth
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
### Peer Dependencies
|
|
12
|
+
|
|
13
|
+
- `@oxyhq/core`
|
|
14
|
+
- `react`
|
|
15
|
+
|
|
16
|
+
### Dependencies
|
|
17
|
+
|
|
18
|
+
- `@tanstack/react-query`
|
|
19
|
+
- `zustand`
|
|
20
|
+
- `socket.io-client`
|
|
21
|
+
- `sonner`
|
|
22
|
+
|
|
23
|
+
## Contents
|
|
24
|
+
|
|
25
|
+
- **WebOxyProvider** — React context provider with auth state
|
|
26
|
+
- **useAuth** — hook for signIn, signOut, user, isAuthenticated
|
|
27
|
+
- **useWebOxy** — full context access including sessions, switchSession, clearSessionState
|
|
28
|
+
- **Query hooks** — useCurrentUser, useUserProfile, usePrivacySettings, useSecurityActivity, and more
|
|
29
|
+
- **Mutation hooks** — useUpdateProfile, useUploadAvatar, useSwitchSession, useLogoutSession, and more
|
|
30
|
+
- **Stores** — authStore, assetStore, accountStore, followStore (zustand)
|
|
31
|
+
- **Session management utilities**
|
|
32
|
+
|
|
33
|
+
## Usage
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
import { WebOxyProvider, useAuth } from '@oxyhq/auth';
|
|
37
|
+
import type { User } from '@oxyhq/core';
|
|
38
|
+
|
|
39
|
+
function App() {
|
|
40
|
+
return (
|
|
41
|
+
<WebOxyProvider baseURL="https://api.oxy.so">
|
|
42
|
+
<YourApp />
|
|
43
|
+
</WebOxyProvider>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function YourApp() {
|
|
48
|
+
const { user, isAuthenticated, signIn, signOut } = useAuth();
|
|
49
|
+
|
|
50
|
+
if (!isAuthenticated) {
|
|
51
|
+
return <button onClick={() => signIn()}>Sign In</button>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return <p>Welcome, {user?.name}</p>;
|
|
55
|
+
}
|
|
56
|
+
```
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WebOxyProvider = WebOxyProvider;
|
|
4
|
+
exports.useWebOxy = useWebOxy;
|
|
5
|
+
exports.useAuth = useAuth;
|
|
6
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
7
|
+
/**
|
|
8
|
+
* @oxyhq/auth — Web Authentication Provider
|
|
9
|
+
*
|
|
10
|
+
* Clean implementation with ZERO React Native dependencies.
|
|
11
|
+
* Provides FedCM, popup, and redirect authentication methods.
|
|
12
|
+
* Uses centralized AuthManager for token and session management.
|
|
13
|
+
*/
|
|
14
|
+
const react_1 = require("react");
|
|
15
|
+
const core_1 = require("@oxyhq/core");
|
|
16
|
+
const react_query_1 = require("@tanstack/react-query");
|
|
17
|
+
const queryClient_1 = require("./hooks/queryClient");
|
|
18
|
+
const WebOxyContext = (0, react_1.createContext)(null);
|
|
19
|
+
/**
|
|
20
|
+
* Web-only Oxy Provider
|
|
21
|
+
*
|
|
22
|
+
* Provides authentication context for pure web applications (React, Next.js, Vite).
|
|
23
|
+
* Supports FedCM, popup, and redirect authentication methods.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```tsx
|
|
27
|
+
* import { WebOxyProvider, useAuth } from '@oxyhq/auth';
|
|
28
|
+
*
|
|
29
|
+
* function App() {
|
|
30
|
+
* return (
|
|
31
|
+
* <WebOxyProvider baseURL="https://api.oxy.so">
|
|
32
|
+
* <YourApp />
|
|
33
|
+
* </WebOxyProvider>
|
|
34
|
+
* );
|
|
35
|
+
* }
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
function WebOxyProvider({ children, baseURL, authWebUrl, onAuthStateChange, onError, preferredAuthMethod = 'auto', skipAutoCheck = false, }) {
|
|
39
|
+
const [oxyServices] = (0, react_1.useState)(() => new core_1.OxyServices({ baseURL, authWebUrl }));
|
|
40
|
+
const [crossDomainAuth] = (0, react_1.useState)(() => new core_1.CrossDomainAuth(oxyServices));
|
|
41
|
+
const [authManager] = (0, react_1.useState)(() => (0, core_1.createAuthManager)(oxyServices, { autoRefresh: true }));
|
|
42
|
+
const [queryClient] = (0, react_1.useState)(() => (0, queryClient_1.createQueryClient)());
|
|
43
|
+
// Auth state
|
|
44
|
+
const [user, setUser] = (0, react_1.useState)(null);
|
|
45
|
+
const [isLoading, setIsLoading] = (0, react_1.useState)(!skipAutoCheck);
|
|
46
|
+
const [error, setError] = (0, react_1.useState)(null);
|
|
47
|
+
const [activeSessionId, setActiveSessionId] = (0, react_1.useState)(null);
|
|
48
|
+
const [sessions, setSessions] = (0, react_1.useState)([]);
|
|
49
|
+
const isAuthenticated = !!user;
|
|
50
|
+
const handleAuthSuccess = (0, react_1.useCallback)(async (session, method = 'credentials') => {
|
|
51
|
+
await authManager.handleAuthSuccess(session, method);
|
|
52
|
+
// Set active session
|
|
53
|
+
if (session.sessionId) {
|
|
54
|
+
setActiveSessionId(session.sessionId);
|
|
55
|
+
}
|
|
56
|
+
// Fetch full user profile
|
|
57
|
+
try {
|
|
58
|
+
const fullUser = await oxyServices.getCurrentUser();
|
|
59
|
+
if (fullUser) {
|
|
60
|
+
setUser(fullUser);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
setUser(session.user);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
setUser(session.user);
|
|
68
|
+
}
|
|
69
|
+
setError(null);
|
|
70
|
+
setIsLoading(false);
|
|
71
|
+
}, [authManager, oxyServices]);
|
|
72
|
+
const handleAuthError = (0, react_1.useCallback)((err) => {
|
|
73
|
+
const errorMessage = err instanceof Error ? err.message : 'Authentication failed';
|
|
74
|
+
setError(errorMessage);
|
|
75
|
+
setIsLoading(false);
|
|
76
|
+
onError?.(err instanceof Error ? err : new Error(errorMessage));
|
|
77
|
+
}, [onError]);
|
|
78
|
+
// Initialize
|
|
79
|
+
(0, react_1.useEffect)(() => {
|
|
80
|
+
if (skipAutoCheck)
|
|
81
|
+
return;
|
|
82
|
+
let mounted = true;
|
|
83
|
+
const initAuth = async () => {
|
|
84
|
+
try {
|
|
85
|
+
const callbackSession = crossDomainAuth.handleRedirectCallback();
|
|
86
|
+
if (callbackSession && mounted) {
|
|
87
|
+
await handleAuthSuccess(callbackSession, 'redirect');
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const restoredUser = await authManager.initialize();
|
|
91
|
+
if (restoredUser && mounted) {
|
|
92
|
+
try {
|
|
93
|
+
const currentUser = await oxyServices.getCurrentUser();
|
|
94
|
+
if (mounted && currentUser) {
|
|
95
|
+
setUser(currentUser);
|
|
96
|
+
setIsLoading(false);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
await authManager.signOut();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
const session = await crossDomainAuth.silentSignIn();
|
|
106
|
+
if (mounted && session?.user) {
|
|
107
|
+
await handleAuthSuccess(session, 'fedcm');
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
// Silent sign-in failed
|
|
113
|
+
}
|
|
114
|
+
if (mounted)
|
|
115
|
+
setIsLoading(false);
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
if (mounted)
|
|
119
|
+
setIsLoading(false);
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
initAuth();
|
|
123
|
+
return () => { mounted = false; };
|
|
124
|
+
}, [oxyServices, crossDomainAuth, authManager, skipAutoCheck, handleAuthSuccess]);
|
|
125
|
+
(0, react_1.useEffect)(() => {
|
|
126
|
+
onAuthStateChange?.(user);
|
|
127
|
+
}, [user, onAuthStateChange]);
|
|
128
|
+
const signIn = (0, react_1.useCallback)(async () => {
|
|
129
|
+
setError(null);
|
|
130
|
+
setIsLoading(true);
|
|
131
|
+
let selectedMethod = 'popup';
|
|
132
|
+
try {
|
|
133
|
+
const session = await crossDomainAuth.signIn({
|
|
134
|
+
method: preferredAuthMethod,
|
|
135
|
+
onMethodSelected: (method) => {
|
|
136
|
+
selectedMethod = method;
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
if (session) {
|
|
140
|
+
await handleAuthSuccess(session, selectedMethod);
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
setIsLoading(false);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
catch (err) {
|
|
147
|
+
handleAuthError(err);
|
|
148
|
+
}
|
|
149
|
+
}, [crossDomainAuth, preferredAuthMethod, handleAuthSuccess, handleAuthError]);
|
|
150
|
+
const signInWithFedCM = (0, react_1.useCallback)(async () => {
|
|
151
|
+
setError(null);
|
|
152
|
+
setIsLoading(true);
|
|
153
|
+
try {
|
|
154
|
+
const session = await crossDomainAuth.signInWithFedCM();
|
|
155
|
+
await handleAuthSuccess(session, 'fedcm');
|
|
156
|
+
}
|
|
157
|
+
catch (err) {
|
|
158
|
+
handleAuthError(err);
|
|
159
|
+
}
|
|
160
|
+
}, [crossDomainAuth, handleAuthSuccess, handleAuthError]);
|
|
161
|
+
const signInWithPopup = (0, react_1.useCallback)(async () => {
|
|
162
|
+
setError(null);
|
|
163
|
+
setIsLoading(true);
|
|
164
|
+
try {
|
|
165
|
+
const session = await crossDomainAuth.signInWithPopup();
|
|
166
|
+
await handleAuthSuccess(session, 'popup');
|
|
167
|
+
}
|
|
168
|
+
catch (err) {
|
|
169
|
+
handleAuthError(err);
|
|
170
|
+
}
|
|
171
|
+
}, [crossDomainAuth, handleAuthSuccess, handleAuthError]);
|
|
172
|
+
const signInWithRedirect = (0, react_1.useCallback)(() => {
|
|
173
|
+
setError(null);
|
|
174
|
+
crossDomainAuth.signInWithRedirect({
|
|
175
|
+
redirectUri: typeof window !== 'undefined' ? window.location.href : undefined,
|
|
176
|
+
});
|
|
177
|
+
}, [crossDomainAuth]);
|
|
178
|
+
const isFedCMSupported = (0, react_1.useCallback)(() => {
|
|
179
|
+
return crossDomainAuth.isFedCMSupported();
|
|
180
|
+
}, [crossDomainAuth]);
|
|
181
|
+
const signOut = (0, react_1.useCallback)(async () => {
|
|
182
|
+
setError(null);
|
|
183
|
+
try {
|
|
184
|
+
await authManager.signOut();
|
|
185
|
+
setUser(null);
|
|
186
|
+
setActiveSessionId(null);
|
|
187
|
+
setSessions([]);
|
|
188
|
+
}
|
|
189
|
+
catch (err) {
|
|
190
|
+
const errorMessage = err instanceof Error ? err.message : 'Sign out failed';
|
|
191
|
+
setError(errorMessage);
|
|
192
|
+
onError?.(err instanceof Error ? err : new Error(errorMessage));
|
|
193
|
+
}
|
|
194
|
+
}, [authManager, onError]);
|
|
195
|
+
const switchSession = (0, react_1.useCallback)(async (sessionId) => {
|
|
196
|
+
try {
|
|
197
|
+
const result = await oxyServices.getTokenBySession(sessionId);
|
|
198
|
+
if (result) {
|
|
199
|
+
setActiveSessionId(sessionId);
|
|
200
|
+
const currentUser = await oxyServices.getCurrentUser();
|
|
201
|
+
if (currentUser)
|
|
202
|
+
setUser(currentUser);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
catch (err) {
|
|
206
|
+
handleAuthError(err);
|
|
207
|
+
}
|
|
208
|
+
}, [oxyServices, handleAuthError]);
|
|
209
|
+
const clearSessionState = (0, react_1.useCallback)(async () => {
|
|
210
|
+
await authManager.signOut();
|
|
211
|
+
setUser(null);
|
|
212
|
+
setActiveSessionId(null);
|
|
213
|
+
setSessions([]);
|
|
214
|
+
}, [authManager]);
|
|
215
|
+
(0, react_1.useEffect)(() => {
|
|
216
|
+
return () => { authManager.destroy(); };
|
|
217
|
+
}, [authManager]);
|
|
218
|
+
const contextValue = (0, react_1.useMemo)(() => ({
|
|
219
|
+
user,
|
|
220
|
+
isAuthenticated,
|
|
221
|
+
isLoading,
|
|
222
|
+
error,
|
|
223
|
+
activeSessionId,
|
|
224
|
+
sessions,
|
|
225
|
+
oxyServices,
|
|
226
|
+
crossDomainAuth,
|
|
227
|
+
authManager,
|
|
228
|
+
signIn,
|
|
229
|
+
signInWithFedCM,
|
|
230
|
+
signInWithPopup,
|
|
231
|
+
signInWithRedirect,
|
|
232
|
+
signOut,
|
|
233
|
+
isFedCMSupported,
|
|
234
|
+
switchSession,
|
|
235
|
+
clearSessionState,
|
|
236
|
+
}), [
|
|
237
|
+
user, isAuthenticated, isLoading, error, activeSessionId, sessions,
|
|
238
|
+
oxyServices, crossDomainAuth, authManager,
|
|
239
|
+
signIn, signInWithFedCM, signInWithPopup, signInWithRedirect,
|
|
240
|
+
signOut, isFedCMSupported, switchSession, clearSessionState,
|
|
241
|
+
]);
|
|
242
|
+
return ((0, jsx_runtime_1.jsx)(react_query_1.QueryClientProvider, { client: queryClient, children: (0, jsx_runtime_1.jsx)(WebOxyContext.Provider, { value: contextValue, children: children }) }));
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Hook to access the full Web Oxy context.
|
|
246
|
+
*/
|
|
247
|
+
function useWebOxy() {
|
|
248
|
+
const context = (0, react_1.useContext)(WebOxyContext);
|
|
249
|
+
if (!context) {
|
|
250
|
+
throw new Error('useWebOxy must be used within WebOxyProvider');
|
|
251
|
+
}
|
|
252
|
+
return context;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Hook for authentication in web apps.
|
|
256
|
+
*
|
|
257
|
+
* @example
|
|
258
|
+
* ```tsx
|
|
259
|
+
* function LoginPage() {
|
|
260
|
+
* const { user, isAuthenticated, signIn, signOut } = useAuth();
|
|
261
|
+
* if (!isAuthenticated) return <button onClick={signIn}>Sign in</button>;
|
|
262
|
+
* return <button onClick={signOut}>Sign out</button>;
|
|
263
|
+
* }
|
|
264
|
+
* ```
|
|
265
|
+
*/
|
|
266
|
+
function useAuth() {
|
|
267
|
+
const ctx = useWebOxy();
|
|
268
|
+
return {
|
|
269
|
+
user: ctx.user,
|
|
270
|
+
isAuthenticated: ctx.isAuthenticated,
|
|
271
|
+
isLoading: ctx.isLoading,
|
|
272
|
+
isReady: !ctx.isLoading,
|
|
273
|
+
error: ctx.error,
|
|
274
|
+
activeSessionId: ctx.activeSessionId,
|
|
275
|
+
sessions: ctx.sessions,
|
|
276
|
+
signIn: ctx.signIn,
|
|
277
|
+
signInWithFedCM: ctx.signInWithFedCM,
|
|
278
|
+
signInWithPopup: ctx.signInWithPopup,
|
|
279
|
+
signInWithRedirect: ctx.signInWithRedirect,
|
|
280
|
+
signOut: ctx.signOut,
|
|
281
|
+
isFedCMSupported: ctx.isFedCMSupported,
|
|
282
|
+
switchSession: ctx.switchSession,
|
|
283
|
+
oxyServices: ctx.oxyServices,
|
|
284
|
+
authManager: ctx.authManager,
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
exports.default = WebOxyProvider;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Mutation Hooks
|
|
4
|
+
*
|
|
5
|
+
* TanStack Query mutation hooks for updating Oxy services data.
|
|
6
|
+
* All mutations handle authentication, error handling, and query invalidation.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.useRemoveDevice = exports.useUpdateDeviceName = exports.useLogoutAll = exports.useLogoutSession = exports.useSwitchSession = exports.useUploadFile = exports.useUpdatePrivacySettings = exports.useUpdateAccountSettings = exports.useUploadAvatar = exports.useUpdateProfile = void 0;
|
|
10
|
+
// Account mutation hooks
|
|
11
|
+
var useAccountMutations_1 = require("./useAccountMutations");
|
|
12
|
+
Object.defineProperty(exports, "useUpdateProfile", { enumerable: true, get: function () { return useAccountMutations_1.useUpdateProfile; } });
|
|
13
|
+
Object.defineProperty(exports, "useUploadAvatar", { enumerable: true, get: function () { return useAccountMutations_1.useUploadAvatar; } });
|
|
14
|
+
Object.defineProperty(exports, "useUpdateAccountSettings", { enumerable: true, get: function () { return useAccountMutations_1.useUpdateAccountSettings; } });
|
|
15
|
+
Object.defineProperty(exports, "useUpdatePrivacySettings", { enumerable: true, get: function () { return useAccountMutations_1.useUpdatePrivacySettings; } });
|
|
16
|
+
Object.defineProperty(exports, "useUploadFile", { enumerable: true, get: function () { return useAccountMutations_1.useUploadFile; } });
|
|
17
|
+
// Service mutation hooks (sessions, devices)
|
|
18
|
+
var useServicesMutations_1 = require("./useServicesMutations");
|
|
19
|
+
Object.defineProperty(exports, "useSwitchSession", { enumerable: true, get: function () { return useServicesMutations_1.useSwitchSession; } });
|
|
20
|
+
Object.defineProperty(exports, "useLogoutSession", { enumerable: true, get: function () { return useServicesMutations_1.useLogoutSession; } });
|
|
21
|
+
Object.defineProperty(exports, "useLogoutAll", { enumerable: true, get: function () { return useServicesMutations_1.useLogoutAll; } });
|
|
22
|
+
Object.defineProperty(exports, "useUpdateDeviceName", { enumerable: true, get: function () { return useServicesMutations_1.useUpdateDeviceName; } });
|
|
23
|
+
Object.defineProperty(exports, "useRemoveDevice", { enumerable: true, get: function () { return useServicesMutations_1.useRemoveDevice; } });
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Mutation Factory - Creates standardized mutations with optimistic updates
|
|
4
|
+
*
|
|
5
|
+
* This factory reduces boilerplate code for mutations that follow the common pattern:
|
|
6
|
+
* 1. Cancel outgoing queries
|
|
7
|
+
* 2. Snapshot previous data
|
|
8
|
+
* 3. Apply optimistic update
|
|
9
|
+
* 4. On error: rollback and show toast
|
|
10
|
+
* 5. On success: update cache, stores, and invalidate queries
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.createProfileMutation = createProfileMutation;
|
|
14
|
+
exports.createGenericMutation = createGenericMutation;
|
|
15
|
+
const queryKeys_1 = require("../queries/queryKeys");
|
|
16
|
+
const sonner_1 = require("sonner");
|
|
17
|
+
const authStore_1 = require("../../stores/authStore");
|
|
18
|
+
/**
|
|
19
|
+
* Creates a standard profile mutation with optimistic updates
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* const updateProfile = createProfileMutation({
|
|
24
|
+
* mutationFn: (updates) => oxyServices.updateProfile(updates),
|
|
25
|
+
* optimisticUpdate: (user, updates) => updates,
|
|
26
|
+
* errorMessage: 'Failed to update profile',
|
|
27
|
+
* });
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
function createProfileMutation(config, queryClient, activeSessionId) {
|
|
31
|
+
const { mutationFn, cancelQueryKeys = [], optimisticUpdate, errorMessage = 'Operation failed', successMessage, updateAuthStore = true, invalidateUserQueries: shouldInvalidateUserQueries = true, invalidateAccountQueries: shouldInvalidateAccountQueries = true, onSuccess: customOnSuccess, } = config;
|
|
32
|
+
return {
|
|
33
|
+
mutationFn,
|
|
34
|
+
onMutate: async (variables) => {
|
|
35
|
+
// Cancel queries that might conflict
|
|
36
|
+
await queryClient.cancelQueries({ queryKey: queryKeys_1.queryKeys.accounts.current() });
|
|
37
|
+
for (const key of cancelQueryKeys) {
|
|
38
|
+
await queryClient.cancelQueries({ queryKey: key });
|
|
39
|
+
}
|
|
40
|
+
// Snapshot previous user data
|
|
41
|
+
const previousUser = queryClient.getQueryData(queryKeys_1.queryKeys.accounts.current());
|
|
42
|
+
// Apply optimistic update if provided
|
|
43
|
+
if (previousUser && optimisticUpdate) {
|
|
44
|
+
const updates = optimisticUpdate(previousUser, variables);
|
|
45
|
+
const optimisticUser = { ...previousUser, ...updates };
|
|
46
|
+
queryClient.setQueryData(queryKeys_1.queryKeys.accounts.current(), optimisticUser);
|
|
47
|
+
if (activeSessionId) {
|
|
48
|
+
queryClient.setQueryData(queryKeys_1.queryKeys.users.profile(activeSessionId), optimisticUser);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return { previousUser };
|
|
52
|
+
},
|
|
53
|
+
onError: (error, _variables, context) => {
|
|
54
|
+
// Rollback optimistic update
|
|
55
|
+
if (context?.previousUser) {
|
|
56
|
+
queryClient.setQueryData(queryKeys_1.queryKeys.accounts.current(), context.previousUser);
|
|
57
|
+
if (activeSessionId) {
|
|
58
|
+
queryClient.setQueryData(queryKeys_1.queryKeys.users.profile(activeSessionId), context.previousUser);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// Show error toast
|
|
62
|
+
const message = typeof errorMessage === 'function'
|
|
63
|
+
? errorMessage(error)
|
|
64
|
+
: (error instanceof Error ? error.message : errorMessage);
|
|
65
|
+
sonner_1.toast.error(message);
|
|
66
|
+
},
|
|
67
|
+
onSuccess: (data, variables) => {
|
|
68
|
+
// Update cache with server response
|
|
69
|
+
queryClient.setQueryData(queryKeys_1.queryKeys.accounts.current(), data);
|
|
70
|
+
if (activeSessionId) {
|
|
71
|
+
queryClient.setQueryData(queryKeys_1.queryKeys.users.profile(activeSessionId), data);
|
|
72
|
+
}
|
|
73
|
+
// Update authStore for immediate UI updates
|
|
74
|
+
if (updateAuthStore) {
|
|
75
|
+
authStore_1.useAuthStore.getState().setUser(data);
|
|
76
|
+
}
|
|
77
|
+
// Invalidate related queries
|
|
78
|
+
if (shouldInvalidateUserQueries) {
|
|
79
|
+
(0, queryKeys_1.invalidateUserQueries)(queryClient);
|
|
80
|
+
}
|
|
81
|
+
if (shouldInvalidateAccountQueries) {
|
|
82
|
+
(0, queryKeys_1.invalidateAccountQueries)(queryClient);
|
|
83
|
+
}
|
|
84
|
+
// Show success toast if configured
|
|
85
|
+
if (successMessage) {
|
|
86
|
+
sonner_1.toast.success(successMessage);
|
|
87
|
+
}
|
|
88
|
+
// Call custom onSuccess handler
|
|
89
|
+
if (customOnSuccess) {
|
|
90
|
+
customOnSuccess(data, variables, queryClient);
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Creates a generic mutation with optimistic updates
|
|
97
|
+
*/
|
|
98
|
+
function createGenericMutation(config, queryClient) {
|
|
99
|
+
const { mutationFn, queryKey, optimisticData, errorMessage = 'Operation failed', successMessage, invalidateQueries = [], } = config;
|
|
100
|
+
return {
|
|
101
|
+
mutationFn,
|
|
102
|
+
onMutate: async (variables) => {
|
|
103
|
+
await queryClient.cancelQueries({ queryKey });
|
|
104
|
+
const previous = queryClient.getQueryData(queryKey);
|
|
105
|
+
if (optimisticData) {
|
|
106
|
+
queryClient.setQueryData(queryKey, optimisticData(previous, variables));
|
|
107
|
+
}
|
|
108
|
+
return { previous };
|
|
109
|
+
},
|
|
110
|
+
onError: (error, _variables, context) => {
|
|
111
|
+
if (context?.previous !== undefined) {
|
|
112
|
+
queryClient.setQueryData(queryKey, context.previous);
|
|
113
|
+
}
|
|
114
|
+
sonner_1.toast.error(error instanceof Error ? error.message : errorMessage);
|
|
115
|
+
},
|
|
116
|
+
onSuccess: (data) => {
|
|
117
|
+
queryClient.setQueryData(queryKey, data);
|
|
118
|
+
for (const key of invalidateQueries) {
|
|
119
|
+
queryClient.invalidateQueries({ queryKey: key });
|
|
120
|
+
}
|
|
121
|
+
if (successMessage) {
|
|
122
|
+
sonner_1.toast.success(successMessage);
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
}
|