@oxyhq/services 5.10.15 → 5.10.16
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 +541 -128
- package/lib/commonjs/core/OxyServices.js +6 -1
- package/lib/commonjs/core/OxyServices.js.map +1 -1
- package/lib/commonjs/core/index.js +7 -0
- package/lib/commonjs/core/index.js.map +1 -1
- package/lib/commonjs/index.js +7 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/node/index.js +7 -0
- package/lib/commonjs/node/index.js.map +1 -1
- package/lib/module/core/OxyServices.js +5 -0
- package/lib/module/core/OxyServices.js.map +1 -1
- package/lib/module/core/index.js +1 -1
- package/lib/module/core/index.js.map +1 -1
- package/lib/module/index.js +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/node/index.js +2 -2
- package/lib/module/node/index.js.map +1 -1
- package/lib/typescript/core/OxyServices.d.ts +1 -0
- package/lib/typescript/core/OxyServices.d.ts.map +1 -1
- package/lib/typescript/core/index.d.ts +1 -1
- package/lib/typescript/core/index.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +1 -1
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/node/index.d.ts +2 -2
- package/lib/typescript/node/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/core/OxyServices.ts +4 -1
- package/src/core/index.ts +1 -1
- package/src/index.ts +1 -1
- package/src/node/index.ts +2 -2
package/README.md
CHANGED
|
@@ -1,242 +1,655 @@
|
|
|
1
1
|
# OxyHQServices
|
|
2
2
|
|
|
3
|
-
A TypeScript client library for the Oxy API providing authentication, user management, and UI components for React and
|
|
3
|
+
A comprehensive TypeScript client library for the Oxy API providing authentication, user management, and UI components for React Native, Expo, and Node.js applications.
|
|
4
4
|
|
|
5
|
-
## Table of Contents
|
|
5
|
+
## 📋 Table of Contents
|
|
6
6
|
|
|
7
7
|
- [Features](#features)
|
|
8
|
+
- [Installation](#installation)
|
|
8
9
|
- [Quick Start](#quick-start)
|
|
9
|
-
- [
|
|
10
|
+
- [Usage Patterns](#usage-patterns)
|
|
11
|
+
- [Frontend (React/React Native)](#frontend-reactreact-native)
|
|
12
|
+
- [Backend (Node.js)](#backend-nodejs)
|
|
13
|
+
- [Mixed Applications](#mixed-applications)
|
|
14
|
+
- [API Reference](#api-reference)
|
|
15
|
+
- [Configuration](#configuration)
|
|
16
|
+
- [Authentication](#authentication)
|
|
10
17
|
- [UI Components](#ui-components)
|
|
11
|
-
- [
|
|
18
|
+
- [Troubleshooting](#troubleshooting)
|
|
12
19
|
- [Requirements](#requirements)
|
|
13
|
-
- [Development](#development)
|
|
14
|
-
- [Integration](#integration)
|
|
15
|
-
- [License](#license)
|
|
16
20
|
|
|
17
|
-
## Features
|
|
21
|
+
## ✨ Features
|
|
18
22
|
|
|
19
|
-
- 🔐 **
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
- 📱 **
|
|
24
|
-
- 🔧 **TypeScript**: Full type safety and IntelliSense support
|
|
25
|
-
- 🚀 **Performance
|
|
26
|
-
-
|
|
23
|
+
- 🔐 **Zero-Config Authentication**: Automatic token management and refresh
|
|
24
|
+
- 📱 **React Native First**: Optimized for React Native and Expo applications
|
|
25
|
+
- 🎨 **UI Components**: Pre-built React Native components with built-in bottom sheet
|
|
26
|
+
- 🔄 **Cross-Platform**: Works seamlessly in React Native, Expo, and Node.js
|
|
27
|
+
- 📱 **Multi-Session Support**: Manage multiple user sessions simultaneously
|
|
28
|
+
- 🔧 **TypeScript First**: Full type safety and IntelliSense support
|
|
29
|
+
- 🚀 **Performance Optimized**: Automatic caching and state management
|
|
30
|
+
- 🛡️ **Production Ready**: Error handling, retry logic, and security best practices
|
|
27
31
|
|
|
28
|
-
##
|
|
32
|
+
## 📦 Installation
|
|
29
33
|
|
|
30
34
|
```bash
|
|
31
35
|
npm install @oxyhq/services
|
|
32
36
|
```
|
|
33
37
|
|
|
34
|
-
###
|
|
38
|
+
### React Native/Expo Setup
|
|
35
39
|
|
|
36
|
-
|
|
40
|
+
For React Native and Expo projects, add the polyfill import at the very top of your entry file:
|
|
37
41
|
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
+
```javascript
|
|
43
|
+
// index.js or App.js (very first line)
|
|
44
|
+
import 'react-native-url-polyfill/auto';
|
|
45
|
+
```
|
|
42
46
|
|
|
43
|
-
|
|
44
|
-
await oxy.signIn('username', 'password');
|
|
45
|
-
await oxy.signUp('username', 'email', 'password');
|
|
47
|
+
## 🚀 Quick Start
|
|
46
48
|
|
|
47
|
-
|
|
48
|
-
const user = await oxy.getCurrentUser();
|
|
49
|
-
await oxy.updateProfile({ name: 'John Doe' });
|
|
50
|
-
await oxy.followUser('user123');
|
|
49
|
+
### React Native/Expo
|
|
51
50
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const notifications = await oxy.getNotifications();
|
|
51
|
+
```typescript
|
|
52
|
+
import { OxyProvider, useOxy } from '@oxyhq/services';
|
|
55
53
|
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
function App() {
|
|
55
|
+
return (
|
|
56
|
+
<OxyProvider baseURL="https://cloud.oxy.so">
|
|
57
|
+
<YourApp />
|
|
58
|
+
</OxyProvider>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
58
61
|
|
|
59
|
-
|
|
60
|
-
const
|
|
62
|
+
function UserProfile() {
|
|
63
|
+
const { oxyServices, user, isAuthenticated } = useOxy();
|
|
64
|
+
|
|
65
|
+
if (!isAuthenticated) {
|
|
66
|
+
return <Text>Please sign in</Text>;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return <Text>Welcome, {user?.name}!</Text>;
|
|
70
|
+
}
|
|
71
|
+
```
|
|
61
72
|
|
|
62
|
-
|
|
63
|
-
await oxy.updateLocation(40.7128, -74.0060);
|
|
64
|
-
const nearby = await oxy.getNearbyUsers();
|
|
73
|
+
### Backend (Node.js)
|
|
65
74
|
|
|
66
|
-
|
|
67
|
-
|
|
75
|
+
```typescript
|
|
76
|
+
import { oxyClient } from '@oxyhq/services';
|
|
68
77
|
|
|
69
|
-
//
|
|
78
|
+
// Ready to use - no configuration needed!
|
|
79
|
+
app.get('/api/user', async (req, res) => {
|
|
80
|
+
const user = await oxyClient.getCurrentUser();
|
|
81
|
+
res.json(user);
|
|
82
|
+
});
|
|
70
83
|
```
|
|
71
84
|
|
|
72
|
-
|
|
85
|
+
## 📖 Usage Patterns
|
|
86
|
+
|
|
87
|
+
### React Native/Expo
|
|
88
|
+
|
|
89
|
+
#### 1. **OxyProvider + useOxy Hook (Recommended)**
|
|
90
|
+
|
|
91
|
+
This pattern provides full React Native integration with automatic state management, UI components, and authentication flow.
|
|
73
92
|
|
|
74
93
|
```typescript
|
|
75
94
|
import { OxyProvider, useOxy } from '@oxyhq/services';
|
|
76
95
|
|
|
96
|
+
// App.tsx - Setup the provider
|
|
77
97
|
function App() {
|
|
78
98
|
return (
|
|
79
|
-
<OxyProvider
|
|
99
|
+
<OxyProvider
|
|
100
|
+
baseURL="https://cloud.oxy.so"
|
|
101
|
+
onAuthStateChange={(user) => {
|
|
102
|
+
console.log('Auth state changed:', user ? 'logged in' : 'logged out');
|
|
103
|
+
}}
|
|
104
|
+
>
|
|
80
105
|
<YourApp />
|
|
81
106
|
</OxyProvider>
|
|
82
107
|
);
|
|
83
108
|
}
|
|
84
109
|
|
|
110
|
+
// Component.tsx - Use the hook
|
|
85
111
|
function UserProfile() {
|
|
86
|
-
const {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
//
|
|
90
|
-
|
|
91
|
-
|
|
112
|
+
const {
|
|
113
|
+
oxyServices, // OxyServices instance
|
|
114
|
+
user, // Current user data
|
|
115
|
+
isAuthenticated, // Authentication state
|
|
116
|
+
login, // Login method
|
|
117
|
+
logout, // Logout method
|
|
118
|
+
showBottomSheet // UI methods
|
|
119
|
+
} = useOxy();
|
|
120
|
+
|
|
121
|
+
const handleLogin = async () => {
|
|
122
|
+
try {
|
|
123
|
+
await login('username', 'password');
|
|
124
|
+
} catch (error) {
|
|
125
|
+
console.error('Login failed:', error);
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const openSignIn = () => {
|
|
130
|
+
showBottomSheet('SignIn');
|
|
92
131
|
};
|
|
132
|
+
|
|
133
|
+
return (
|
|
134
|
+
<View>
|
|
135
|
+
{isAuthenticated ? (
|
|
136
|
+
<View>
|
|
137
|
+
<Text style={styles.title}>Welcome, {user?.name}!</Text>
|
|
138
|
+
<TouchableOpacity onPress={logout} style={styles.button}>
|
|
139
|
+
<Text>Sign Out</Text>
|
|
140
|
+
</TouchableOpacity>
|
|
141
|
+
</View>
|
|
142
|
+
) : (
|
|
143
|
+
<TouchableOpacity onPress={openSignIn} style={styles.button}>
|
|
144
|
+
<Text>Sign In</Text>
|
|
145
|
+
</TouchableOpacity>
|
|
146
|
+
)}
|
|
147
|
+
</View>
|
|
148
|
+
);
|
|
93
149
|
}
|
|
94
150
|
```
|
|
95
151
|
|
|
96
|
-
|
|
152
|
+
#### 2. **Direct Import (Non-React Files)**
|
|
153
|
+
|
|
154
|
+
For utility functions, services, or non-React Native files:
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
import { oxyClient } from '@oxyhq/services';
|
|
158
|
+
|
|
159
|
+
// utils/api.ts
|
|
160
|
+
export const userUtils = {
|
|
161
|
+
async fetchUserProfile(userId: string) {
|
|
162
|
+
return await oxyClient.getUserById(userId);
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
async updateUserProfile(updates: any) {
|
|
166
|
+
return await oxyClient.updateProfile(updates);
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Backend (Node.js)
|
|
172
|
+
|
|
173
|
+
#### 1. **Pre-configured Client (Recommended)**
|
|
174
|
+
|
|
175
|
+
Use the pre-configured `oxyClient` for immediate access:
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
import { oxyClient } from '@oxyhq/services';
|
|
179
|
+
|
|
180
|
+
// routes/auth.ts
|
|
181
|
+
export const signIn = async (req, res) => {
|
|
182
|
+
try {
|
|
183
|
+
const { username, password } = req.body;
|
|
184
|
+
const response = await oxyClient.signIn(username, password);
|
|
185
|
+
res.json(response);
|
|
186
|
+
} catch (error) {
|
|
187
|
+
res.status(401).json({ error: error.message });
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
// routes/users.ts
|
|
192
|
+
export const getUserProfile = async (req, res) => {
|
|
193
|
+
try {
|
|
194
|
+
const { userId } = req.params;
|
|
195
|
+
const user = await oxyClient.getUserById(userId);
|
|
196
|
+
res.json(user);
|
|
197
|
+
} catch (error) {
|
|
198
|
+
res.status(500).json({ error: error.message });
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
// routes/social.ts
|
|
203
|
+
export const getFollowers = async (req, res) => {
|
|
204
|
+
try {
|
|
205
|
+
const { userId } = req.params;
|
|
206
|
+
const followers = await oxyClient.getUserFollowers(userId);
|
|
207
|
+
res.json(followers);
|
|
208
|
+
} catch (error) {
|
|
209
|
+
res.status(500).json({ error: error.message });
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
#### 2. **Custom Configuration**
|
|
215
|
+
|
|
216
|
+
Create your own instance with custom settings:
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
import { OxyServices, OXY_CLOUD_URL } from '@oxyhq/services';
|
|
220
|
+
|
|
221
|
+
const oxy = new OxyServices({
|
|
222
|
+
baseURL: process.env.OXY_API_URL || OXY_CLOUD_URL
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
export { oxy };
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Mixed Applications (React Native + Backend)
|
|
97
229
|
|
|
98
|
-
|
|
230
|
+
You can use both patterns in the same application:
|
|
99
231
|
|
|
100
232
|
```typescript
|
|
101
|
-
|
|
233
|
+
// App.tsx - React Native setup
|
|
234
|
+
import { OxyProvider } from '@oxyhq/services';
|
|
102
235
|
|
|
103
236
|
function App() {
|
|
104
237
|
return (
|
|
105
|
-
<OxyProvider
|
|
106
|
-
baseURL="https://api.example.com"
|
|
107
|
-
initialScreen="SignIn"
|
|
108
|
-
theme="light"
|
|
109
|
-
>
|
|
238
|
+
<OxyProvider baseURL="https://cloud.oxy.so">
|
|
110
239
|
<YourApp />
|
|
111
240
|
</OxyProvider>
|
|
112
241
|
);
|
|
113
242
|
}
|
|
114
243
|
|
|
244
|
+
// utils/api.ts - Direct import
|
|
245
|
+
import { oxyClient } from '@oxyhq/services';
|
|
246
|
+
|
|
247
|
+
export const apiUtils = {
|
|
248
|
+
async fetchData() {
|
|
249
|
+
return await oxyClient.getCurrentUser();
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
// Component.tsx - React Native hook
|
|
254
|
+
import { useOxy } from '@oxyhq/services';
|
|
255
|
+
|
|
115
256
|
function Component() {
|
|
116
|
-
const {
|
|
117
|
-
|
|
118
|
-
const openSignIn = () => {
|
|
119
|
-
showBottomSheet('SignIn'); // Works automatically!
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
return (
|
|
123
|
-
<div>
|
|
124
|
-
<button onClick={openSignIn}>Sign In</button>
|
|
125
|
-
<OxySignInButton /> {/* Also works automatically! */}
|
|
126
|
-
</div>
|
|
127
|
-
);
|
|
257
|
+
const { oxyServices } = useOxy();
|
|
258
|
+
// Both oxyServices and oxyClient share the same tokens!
|
|
128
259
|
}
|
|
129
260
|
```
|
|
130
261
|
|
|
131
|
-
|
|
262
|
+
## 🔧 API Reference
|
|
263
|
+
|
|
264
|
+
### Core Exports
|
|
132
265
|
|
|
133
266
|
```typescript
|
|
134
|
-
import {
|
|
267
|
+
import {
|
|
268
|
+
OxyServices, // Main service class
|
|
269
|
+
oxyClient, // Pre-configured instance
|
|
270
|
+
OXY_CLOUD_URL, // Default API URL
|
|
271
|
+
OxyAuthenticationError,
|
|
272
|
+
OxyAuthenticationTimeoutError
|
|
273
|
+
} from '@oxyhq/services';
|
|
274
|
+
```
|
|
135
275
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
276
|
+
### React Native Exports
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
import {
|
|
280
|
+
OxyProvider, // Context provider
|
|
281
|
+
useOxy, // React Native hook
|
|
282
|
+
OxySignInButton, // UI components
|
|
283
|
+
Avatar,
|
|
284
|
+
FollowButton
|
|
285
|
+
} from '@oxyhq/services';
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### OxyServices Methods
|
|
139
289
|
|
|
290
|
+
```typescript
|
|
140
291
|
// Authentication
|
|
141
|
-
|
|
292
|
+
await oxyClient.signIn(username, password);
|
|
293
|
+
await oxyClient.signUp(username, email, password);
|
|
294
|
+
await oxyClient.logout();
|
|
142
295
|
|
|
143
|
-
// User
|
|
144
|
-
const user = await
|
|
145
|
-
await
|
|
146
|
-
await
|
|
296
|
+
// User Management
|
|
297
|
+
const user = await oxyClient.getCurrentUser();
|
|
298
|
+
await oxyClient.updateProfile({ name: 'John Doe' });
|
|
299
|
+
const userById = await oxyClient.getUserById('user123');
|
|
147
300
|
|
|
148
|
-
// Social
|
|
149
|
-
|
|
150
|
-
const
|
|
301
|
+
// Social Features
|
|
302
|
+
await oxyClient.followUser('user123');
|
|
303
|
+
const followers = await oxyClient.getUserFollowers('user123');
|
|
304
|
+
const notifications = await oxyClient.getNotifications();
|
|
305
|
+
|
|
306
|
+
// File Management
|
|
307
|
+
const fileData = await oxyClient.uploadFile(file);
|
|
308
|
+
const downloadUrl = oxyClient.getFileDownloadUrl(fileId);
|
|
309
|
+
|
|
310
|
+
// Payments
|
|
311
|
+
const payment = await oxyClient.createPayment(paymentData);
|
|
151
312
|
|
|
152
|
-
//
|
|
153
|
-
|
|
154
|
-
const
|
|
313
|
+
// Location Services
|
|
314
|
+
await oxyClient.updateLocation(40.7128, -74.0060);
|
|
315
|
+
const nearby = await oxyClient.getNearbyUsers();
|
|
155
316
|
|
|
156
|
-
//
|
|
317
|
+
// Analytics
|
|
318
|
+
await oxyClient.trackEvent('user_action', { action: 'click' });
|
|
157
319
|
```
|
|
158
320
|
|
|
159
|
-
|
|
321
|
+
### useOxy Hook
|
|
160
322
|
|
|
161
|
-
|
|
323
|
+
```typescript
|
|
324
|
+
const {
|
|
325
|
+
// Service instance
|
|
326
|
+
oxyServices,
|
|
327
|
+
|
|
328
|
+
// Authentication state
|
|
329
|
+
user,
|
|
330
|
+
isAuthenticated,
|
|
331
|
+
isLoading,
|
|
332
|
+
error,
|
|
333
|
+
|
|
334
|
+
// Authentication methods
|
|
335
|
+
login,
|
|
336
|
+
logout,
|
|
337
|
+
signUp,
|
|
338
|
+
|
|
339
|
+
// Session management
|
|
340
|
+
sessions,
|
|
341
|
+
activeSessionId,
|
|
342
|
+
switchSession,
|
|
343
|
+
removeSession,
|
|
344
|
+
|
|
345
|
+
// UI methods
|
|
346
|
+
showBottomSheet,
|
|
347
|
+
hideBottomSheet
|
|
348
|
+
} = useOxy();
|
|
349
|
+
```
|
|
162
350
|
|
|
163
|
-
##
|
|
351
|
+
## ⚙️ Configuration
|
|
164
352
|
|
|
165
|
-
|
|
353
|
+
### OxyProvider Props
|
|
166
354
|
|
|
167
355
|
```typescript
|
|
168
|
-
|
|
356
|
+
<OxyProvider
|
|
357
|
+
baseURL="https://cloud.oxy.so" // API base URL
|
|
358
|
+
storageKeyPrefix="oxy_session" // Storage key prefix
|
|
359
|
+
onAuthStateChange={(user) => {}} // Auth state callback
|
|
360
|
+
onError={(error) => {}} // Error callback
|
|
361
|
+
bottomSheetRef={bottomSheetRef} // Bottom sheet ref
|
|
362
|
+
>
|
|
363
|
+
{children}
|
|
364
|
+
</OxyProvider>
|
|
169
365
|
```
|
|
170
366
|
|
|
171
|
-
|
|
367
|
+
### Environment Variables
|
|
368
|
+
|
|
369
|
+
```bash
|
|
370
|
+
# .env
|
|
371
|
+
OXY_API_URL=https://cloud.oxy.so
|
|
372
|
+
NODE_ENV=production
|
|
373
|
+
```
|
|
172
374
|
|
|
173
|
-
|
|
375
|
+
### Custom Configuration
|
|
174
376
|
|
|
175
377
|
```typescript
|
|
176
|
-
// Unified OxyServices class (recommended)
|
|
177
378
|
import { OxyServices } from '@oxyhq/services';
|
|
178
379
|
|
|
179
|
-
|
|
180
|
-
|
|
380
|
+
const oxy = new OxyServices({
|
|
381
|
+
baseURL: process.env.OXY_API_URL || 'https://cloud.oxy.so'
|
|
382
|
+
});
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
## 🔐 Authentication
|
|
386
|
+
|
|
387
|
+
### Automatic Token Management
|
|
388
|
+
|
|
389
|
+
The library handles authentication automatically:
|
|
181
390
|
|
|
182
|
-
|
|
183
|
-
|
|
391
|
+
- **Token Storage**: Secure storage across sessions
|
|
392
|
+
- **Token Refresh**: Automatic refresh before expiration
|
|
393
|
+
- **Session Management**: Multi-session support
|
|
394
|
+
- **Error Handling**: Graceful handling of auth errors
|
|
395
|
+
|
|
396
|
+
### Manual Token Management
|
|
397
|
+
|
|
398
|
+
```typescript
|
|
399
|
+
import { oxyClient } from '@oxyhq/services';
|
|
400
|
+
|
|
401
|
+
// Set tokens manually
|
|
402
|
+
oxyClient.setTokens(accessToken, refreshToken);
|
|
403
|
+
|
|
404
|
+
// Clear tokens
|
|
405
|
+
oxyClient.clearTokens();
|
|
406
|
+
|
|
407
|
+
// Check authentication
|
|
408
|
+
const isAuthenticated = oxyClient.hasValidToken();
|
|
184
409
|
```
|
|
185
410
|
|
|
411
|
+
## 🎨 UI Components
|
|
412
|
+
|
|
413
|
+
### Built-in Components
|
|
414
|
+
|
|
415
|
+
```typescript
|
|
416
|
+
import {
|
|
417
|
+
OxySignInButton,
|
|
418
|
+
Avatar,
|
|
419
|
+
FollowButton,
|
|
420
|
+
OxyLogo
|
|
421
|
+
} from '@oxyhq/services';
|
|
422
|
+
|
|
423
|
+
function MyComponent() {
|
|
424
|
+
return (
|
|
425
|
+
<View style={styles.container}>
|
|
426
|
+
<OxyLogo />
|
|
427
|
+
<Avatar userId="user123" size={40} />
|
|
428
|
+
<FollowButton userId="user123" />
|
|
429
|
+
<OxySignInButton />
|
|
430
|
+
</View>
|
|
431
|
+
);
|
|
432
|
+
}
|
|
186
433
|
|
|
434
|
+
const styles = StyleSheet.create({
|
|
435
|
+
container: {
|
|
436
|
+
flex: 1,
|
|
437
|
+
padding: 16,
|
|
438
|
+
},
|
|
439
|
+
});
|
|
440
|
+
```
|
|
187
441
|
|
|
188
|
-
|
|
442
|
+
### Bottom Sheet Integration
|
|
189
443
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
- **React Native** 0.60+ (for mobile components)
|
|
193
|
-
- **TypeScript** 4.0+ (optional but recommended)
|
|
444
|
+
```typescript
|
|
445
|
+
import { useOxy } from '@oxyhq/services';
|
|
194
446
|
|
|
195
|
-
|
|
447
|
+
function MyComponent() {
|
|
448
|
+
const { showBottomSheet } = useOxy();
|
|
449
|
+
|
|
450
|
+
const openSignIn = () => {
|
|
451
|
+
showBottomSheet('SignIn');
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
const openProfile = () => {
|
|
455
|
+
showBottomSheet('Profile');
|
|
456
|
+
};
|
|
457
|
+
|
|
458
|
+
return (
|
|
459
|
+
<View style={styles.container}>
|
|
460
|
+
<TouchableOpacity onPress={openSignIn} style={styles.button}>
|
|
461
|
+
<Text>Sign In</Text>
|
|
462
|
+
</TouchableOpacity>
|
|
463
|
+
<TouchableOpacity onPress={openProfile} style={styles.button}>
|
|
464
|
+
<Text>Profile</Text>
|
|
465
|
+
</TouchableOpacity>
|
|
466
|
+
</View>
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
```
|
|
196
470
|
|
|
197
|
-
|
|
471
|
+
## 🛠️ Troubleshooting
|
|
198
472
|
|
|
199
|
-
|
|
473
|
+
### Common Issues
|
|
200
474
|
|
|
201
|
-
**
|
|
202
|
-
- The library handles this automatically - no additional setup required
|
|
203
|
-
- File uploads will work with both native FormData (when available) and the polyfilled version
|
|
204
|
-
- Ensure you're using the latest version of the package
|
|
475
|
+
#### 1. **"useOxy must be used within an OxyContextProvider"**
|
|
205
476
|
|
|
206
|
-
|
|
477
|
+
**Solution**: Wrap your app with `OxyProvider`
|
|
207
478
|
|
|
208
|
-
|
|
479
|
+
```typescript
|
|
480
|
+
import { OxyProvider } from '@oxyhq/services';
|
|
209
481
|
|
|
210
|
-
|
|
211
|
-
|
|
482
|
+
function App() {
|
|
483
|
+
return (
|
|
484
|
+
<OxyProvider baseURL="https://cloud.oxy.so">
|
|
485
|
+
<YourApp />
|
|
486
|
+
</OxyProvider>
|
|
487
|
+
);
|
|
488
|
+
}
|
|
212
489
|
```
|
|
213
490
|
|
|
214
|
-
|
|
491
|
+
#### 2. **FormData Issues in React Native/Expo**
|
|
215
492
|
|
|
216
|
-
|
|
493
|
+
**Solution**: Add polyfill import at the very top of your entry file
|
|
217
494
|
|
|
218
|
-
```
|
|
495
|
+
```javascript
|
|
496
|
+
// index.js or App.js (very first line)
|
|
219
497
|
import 'react-native-url-polyfill/auto';
|
|
220
498
|
```
|
|
221
499
|
|
|
222
|
-
|
|
500
|
+
#### 3. **Authentication Not Persisting**
|
|
501
|
+
|
|
502
|
+
**Solution**: Check storage configuration
|
|
223
503
|
|
|
504
|
+
```typescript
|
|
505
|
+
<OxyProvider
|
|
506
|
+
baseURL="https://cloud.oxy.so"
|
|
507
|
+
storageKeyPrefix="my_app_oxy" // Custom storage key
|
|
508
|
+
>
|
|
509
|
+
{children}
|
|
510
|
+
</OxyProvider>
|
|
224
511
|
```
|
|
225
|
-
|
|
512
|
+
|
|
513
|
+
### Error Handling
|
|
514
|
+
|
|
515
|
+
```typescript
|
|
516
|
+
import { OxyAuthenticationError } from '@oxyhq/services';
|
|
517
|
+
|
|
518
|
+
try {
|
|
519
|
+
await oxyClient.getCurrentUser();
|
|
520
|
+
} catch (error) {
|
|
521
|
+
if (error instanceof OxyAuthenticationError) {
|
|
522
|
+
// Handle authentication errors
|
|
523
|
+
console.log('Auth error:', error.message);
|
|
524
|
+
} else {
|
|
525
|
+
// Handle other errors
|
|
526
|
+
console.log('Other error:', error.message);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
226
529
|
```
|
|
227
530
|
|
|
228
|
-
|
|
531
|
+
## 📋 Requirements
|
|
532
|
+
|
|
533
|
+
- **Node.js**: 16+ (for backend usage)
|
|
534
|
+
- **React Native**: 0.60+ (for mobile components)
|
|
535
|
+
- **Expo**: 44+ (recommended)
|
|
536
|
+
- **TypeScript**: 4.0+ (optional but recommended)
|
|
229
537
|
|
|
230
|
-
|
|
538
|
+
### Peer Dependencies
|
|
539
|
+
|
|
540
|
+
For React Native/Expo projects:
|
|
231
541
|
|
|
232
542
|
```bash
|
|
233
|
-
|
|
234
|
-
|
|
543
|
+
npm install axios jwt-decode invariant react-native-url-polyfill
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
## 📚 Examples
|
|
547
|
+
|
|
548
|
+
### Complete React Native App
|
|
549
|
+
|
|
550
|
+
```typescript
|
|
551
|
+
// App.tsx
|
|
552
|
+
import { OxyProvider } from '@oxyhq/services';
|
|
553
|
+
|
|
554
|
+
function App() {
|
|
555
|
+
return (
|
|
556
|
+
<OxyProvider baseURL="https://cloud.oxy.so">
|
|
557
|
+
<UserDashboard />
|
|
558
|
+
</OxyProvider>
|
|
559
|
+
);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
// UserDashboard.tsx
|
|
563
|
+
import { useOxy } from '@oxyhq/services';
|
|
564
|
+
import { View, Text, StyleSheet } from 'react-native';
|
|
235
565
|
|
|
236
|
-
|
|
237
|
-
|
|
566
|
+
function UserDashboard() {
|
|
567
|
+
const { user, isAuthenticated, oxyServices } = useOxy();
|
|
568
|
+
|
|
569
|
+
const [followers, setFollowers] = useState([]);
|
|
570
|
+
|
|
571
|
+
useEffect(() => {
|
|
572
|
+
if (isAuthenticated && user) {
|
|
573
|
+
oxyServices.getUserFollowers(user.id).then(setFollowers);
|
|
574
|
+
}
|
|
575
|
+
}, [isAuthenticated, user]);
|
|
576
|
+
|
|
577
|
+
if (!isAuthenticated) {
|
|
578
|
+
return <Text>Please sign in</Text>;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
return (
|
|
582
|
+
<View style={styles.container}>
|
|
583
|
+
<Text style={styles.title}>Welcome, {user?.name}!</Text>
|
|
584
|
+
<Text>Followers: {followers.length}</Text>
|
|
585
|
+
</View>
|
|
586
|
+
);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
const styles = StyleSheet.create({
|
|
590
|
+
container: {
|
|
591
|
+
flex: 1,
|
|
592
|
+
padding: 16,
|
|
593
|
+
justifyContent: 'center',
|
|
594
|
+
},
|
|
595
|
+
title: {
|
|
596
|
+
fontSize: 24,
|
|
597
|
+
fontWeight: 'bold',
|
|
598
|
+
marginBottom: 16,
|
|
599
|
+
},
|
|
600
|
+
});
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
### Complete Backend API
|
|
604
|
+
|
|
605
|
+
```typescript
|
|
606
|
+
// server.ts
|
|
607
|
+
import express from 'express';
|
|
608
|
+
import { oxyClient } from '@oxyhq/services';
|
|
609
|
+
|
|
610
|
+
const app = express();
|
|
611
|
+
app.use(express.json());
|
|
612
|
+
|
|
613
|
+
// Auth routes
|
|
614
|
+
app.post('/api/auth/signin', async (req, res) => {
|
|
615
|
+
try {
|
|
616
|
+
const { username, password } = req.body;
|
|
617
|
+
const response = await oxyClient.signIn(username, password);
|
|
618
|
+
res.json(response);
|
|
619
|
+
} catch (error) {
|
|
620
|
+
res.status(401).json({ error: error.message });
|
|
621
|
+
}
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
// User routes
|
|
625
|
+
app.get('/api/users/:userId', async (req, res) => {
|
|
626
|
+
try {
|
|
627
|
+
const { userId } = req.params;
|
|
628
|
+
const user = await oxyClient.getUserById(userId);
|
|
629
|
+
res.json(user);
|
|
630
|
+
} catch (error) {
|
|
631
|
+
res.status(500).json({ error: error.message });
|
|
632
|
+
}
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
// Social routes
|
|
636
|
+
app.get('/api/users/:userId/followers', async (req, res) => {
|
|
637
|
+
try {
|
|
638
|
+
const { userId } = req.params;
|
|
639
|
+
const followers = await oxyClient.getUserFollowers(userId);
|
|
640
|
+
res.json(followers);
|
|
641
|
+
} catch (error) {
|
|
642
|
+
res.status(500).json({ error: error.message });
|
|
643
|
+
}
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
app.listen(3000, () => {
|
|
647
|
+
console.log('Server running on port 3000');
|
|
648
|
+
});
|
|
238
649
|
```
|
|
239
650
|
|
|
240
|
-
|
|
651
|
+
---
|
|
652
|
+
|
|
653
|
+
## 📄 License
|
|
241
654
|
|
|
242
655
|
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
|