zopassport 0.1.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/LICENSE +22 -0
- package/README.md +407 -0
- package/app/.env.example +15 -0
- package/app/README.md +28 -0
- package/app/package.json +24 -0
- package/app/reanimated-mock.js +102 -0
- package/app/reanimated-mock.jsx +97 -0
- package/app/src/App.tsx +331 -0
- package/app/src/components/FounderBadge.tsx +26 -0
- package/app/src/components/OTPInput.tsx +149 -0
- package/app/src/components/PhoneInput.tsx +109 -0
- package/app/src/components/ZoAuth.tsx +320 -0
- package/app/src/components/ZoAvatar.tsx +87 -0
- package/app/src/components/ZoLanding.tsx +231 -0
- package/app/src/components/ZoOnboarding.tsx +524 -0
- package/app/src/components/ZoPassportCard.tsx +183 -0
- package/app/src/components/ZoProgressRing.tsx +57 -0
- package/app/src/components/index.ts +16 -0
- package/app/src/components/wallet/MovingShine.tsx +43 -0
- package/app/src/components/wallet/TransactionItem.tsx +84 -0
- package/app/src/components/wallet/TransactionList.tsx +65 -0
- package/app/src/components/wallet/WalletCard.tsx +152 -0
- package/app/src/components/wallet/WalletScreen.tsx +190 -0
- package/app/src/components/wallet/ZoToken.tsx +69 -0
- package/app/src/components/wallet/index.ts +8 -0
- package/app/src/components/wallet/styles/index.ts +4 -0
- package/app/src/components/wallet/styles/walletStyles.ts +210 -0
- package/app/src/sdk/ZoPassportSDK.ts +277 -0
- package/app/src/sdk/lib/api/auth.ts +223 -0
- package/app/src/sdk/lib/api/avatar.ts +155 -0
- package/app/src/sdk/lib/api/client.ts +135 -0
- package/app/src/sdk/lib/api/index.ts +8 -0
- package/app/src/sdk/lib/api/profile.ts +80 -0
- package/app/src/sdk/lib/api/wallet.ts +59 -0
- package/app/src/sdk/lib/types/auth.ts +78 -0
- package/app/src/sdk/lib/types/avatar.ts +22 -0
- package/app/src/sdk/lib/types/index.ts +8 -0
- package/app/src/sdk/lib/types/profile.ts +18 -0
- package/app/src/sdk/lib/types/wallet.ts +103 -0
- package/app/src/sdk/lib/types.ts +205 -0
- package/app/src/sdk/lib/utils/index.ts +6 -0
- package/app/src/sdk/lib/utils/phone.ts +71 -0
- package/app/src/sdk/lib/utils/storage.ts +116 -0
- package/app/src/sdk/lib/utils/wallet.ts +73 -0
- package/app/src/sdk/types.ts +205 -0
- package/app/src/styles.css +154 -0
- package/app/svg-mock.js +125 -0
- package/app/svg-mock.jsx +120 -0
- package/app/vite.config.ts +70 -0
- package/assets/ASSETS_MANIFEST.md +124 -0
- package/assets/bae.png +0 -0
- package/assets/bro.png +0 -0
- package/assets/cultural-stickers/Business.png +0 -0
- package/assets/cultural-stickers/Default (2).jpg +0 -0
- package/assets/cultural-stickers/Design.png +0 -0
- package/assets/cultural-stickers/FollowYourHeart.png +0 -0
- package/assets/cultural-stickers/Food.png +0 -0
- package/assets/cultural-stickers/Game.png +0 -0
- package/assets/cultural-stickers/Health&Fitness.png +0 -0
- package/assets/cultural-stickers/Home&Lifestyle.png +0 -0
- package/assets/cultural-stickers/Law.png +0 -0
- package/assets/cultural-stickers/Literature&Stories.png +0 -0
- package/assets/cultural-stickers/Music&Entertainment.png +0 -0
- package/assets/cultural-stickers/Nature&Wildlife.png +0 -0
- package/assets/cultural-stickers/Photography.png +0 -0
- package/assets/cultural-stickers/Science&Technology.png +0 -0
- package/assets/cultural-stickers/Spiritual.png +0 -0
- package/assets/cultural-stickers/Sport.png +0 -0
- package/assets/cultural-stickers/Stories&Journal.png +0 -0
- package/assets/cultural-stickers/Television&Cinema.png +0 -0
- package/assets/cultural-stickers/Travel&Adventure.png +0 -0
- package/assets/cultural-stickers/z.jpg (1).jpg +0 -0
- package/assets/figma-assets/landing-zo-logo.png +0 -0
- package/assets/images/rank1.jpeg +0 -0
- package/assets/index.ts +76 -0
- package/assets/lotties/loader.json +1216 -0
- package/assets/lotties/spinner.json +1 -0
- package/assets/videos/loading-screen-background.mp4 +0 -0
- package/assets/videos/opening-disks.mp4 +0 -0
- package/assets/wallet/constants.ts +38 -0
- package/assets/zo-coin.gif +0 -0
- package/assets/zo-fallback.png +0 -0
- package/dist/assets/index.d.mts +136 -0
- package/dist/assets/index.d.ts +136 -0
- package/dist/assets/index.js +133 -0
- package/dist/assets/index.js.map +1 -0
- package/dist/assets/index.mjs +100 -0
- package/dist/assets/index.mjs.map +1 -0
- package/dist/index.d.mts +789 -0
- package/dist/index.d.ts +789 -0
- package/dist/index.js +1118 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1060 -0
- package/dist/index.mjs.map +1 -0
- package/dist/react-native.d.mts +537 -0
- package/dist/react-native.d.ts +537 -0
- package/dist/react-native.js +1617 -0
- package/dist/react-native.js.map +1 -0
- package/dist/react-native.mjs +1588 -0
- package/dist/react-native.mjs.map +1 -0
- package/dist/react.d.mts +824 -0
- package/dist/react.d.ts +824 -0
- package/dist/react.js +3856 -0
- package/dist/react.js.map +1 -0
- package/dist/react.mjs +3801 -0
- package/dist/react.mjs.map +1 -0
- package/package.json +112 -0
- package/scripts/init.js +196 -0
- package/scripts/postinstall.js +174 -0
- package/scripts/verify-build.js +121 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// src/lib/api/avatar.ts
|
|
2
|
+
// ZO API avatar generation functions
|
|
3
|
+
|
|
4
|
+
import { ZoApiClient } from './client';
|
|
5
|
+
import { StorageAdapter } from '../utils/storage';
|
|
6
|
+
import type {
|
|
7
|
+
ZoAvatarGenerateRequest,
|
|
8
|
+
ZoAvatarGenerateResponse,
|
|
9
|
+
ZoAvatarStatusResponse,
|
|
10
|
+
ZoErrorResponse,
|
|
11
|
+
} from '../types';
|
|
12
|
+
|
|
13
|
+
export class ZoAvatar {
|
|
14
|
+
constructor(
|
|
15
|
+
private client: ZoApiClient,
|
|
16
|
+
private storage: StorageAdapter
|
|
17
|
+
) {}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Generate avatar for user
|
|
21
|
+
*/
|
|
22
|
+
async generateAvatar(
|
|
23
|
+
accessToken: string,
|
|
24
|
+
bodyType: 'bro' | 'bae'
|
|
25
|
+
): Promise<{
|
|
26
|
+
success: boolean;
|
|
27
|
+
task_id?: string;
|
|
28
|
+
status?: string;
|
|
29
|
+
error?: string;
|
|
30
|
+
}> {
|
|
31
|
+
try {
|
|
32
|
+
const payload: ZoAvatarGenerateRequest = {
|
|
33
|
+
body_type: bodyType,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const response = await this.client.axiosInstance.post<ZoAvatarGenerateResponse>(
|
|
37
|
+
'/api/v1/avatar/generate/',
|
|
38
|
+
payload,
|
|
39
|
+
{
|
|
40
|
+
headers: {
|
|
41
|
+
Authorization: `Bearer ${accessToken}`,
|
|
42
|
+
},
|
|
43
|
+
}
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
success: true,
|
|
48
|
+
task_id: response.data.task_id,
|
|
49
|
+
status: response.data.status,
|
|
50
|
+
};
|
|
51
|
+
} catch (error: any) {
|
|
52
|
+
const errorData = error.response?.data as ZoErrorResponse;
|
|
53
|
+
return {
|
|
54
|
+
success: false,
|
|
55
|
+
error: errorData?.detail || errorData?.message || 'Failed to generate avatar',
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Check avatar generation status
|
|
62
|
+
*/
|
|
63
|
+
async getAvatarStatus(
|
|
64
|
+
accessToken: string,
|
|
65
|
+
taskId: string
|
|
66
|
+
): Promise<{
|
|
67
|
+
success: boolean;
|
|
68
|
+
status?: 'pending' | 'processing' | 'completed' | 'failed';
|
|
69
|
+
avatarUrl?: string;
|
|
70
|
+
error?: string;
|
|
71
|
+
}> {
|
|
72
|
+
try {
|
|
73
|
+
const response = await this.client.axiosInstance.get<ZoAvatarStatusResponse>(
|
|
74
|
+
`/api/v1/avatar/status/${taskId}/`,
|
|
75
|
+
{
|
|
76
|
+
headers: {
|
|
77
|
+
Authorization: `Bearer ${accessToken}`,
|
|
78
|
+
},
|
|
79
|
+
}
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
success: true,
|
|
84
|
+
status: response.data.status,
|
|
85
|
+
avatarUrl: response.data.result?.avatar_url,
|
|
86
|
+
};
|
|
87
|
+
} catch (error: any) {
|
|
88
|
+
const errorData = error.response?.data as ZoErrorResponse;
|
|
89
|
+
return {
|
|
90
|
+
success: false,
|
|
91
|
+
error: errorData?.detail || errorData?.message || 'Failed to get avatar status',
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Poll avatar status until completion
|
|
98
|
+
*/
|
|
99
|
+
async pollAvatarStatus(
|
|
100
|
+
accessToken: string,
|
|
101
|
+
taskId: string,
|
|
102
|
+
options: {
|
|
103
|
+
onProgress?: (status: string) => void;
|
|
104
|
+
onComplete?: (avatarUrl: string) => void;
|
|
105
|
+
onError?: (error: string) => void;
|
|
106
|
+
maxAttempts?: number;
|
|
107
|
+
interval?: number;
|
|
108
|
+
} = {}
|
|
109
|
+
): Promise<void> {
|
|
110
|
+
const {
|
|
111
|
+
onProgress,
|
|
112
|
+
onComplete,
|
|
113
|
+
onError,
|
|
114
|
+
maxAttempts = 30,
|
|
115
|
+
interval = 2000,
|
|
116
|
+
} = options;
|
|
117
|
+
|
|
118
|
+
let attempts = 0;
|
|
119
|
+
|
|
120
|
+
const poll = async () => {
|
|
121
|
+
attempts++;
|
|
122
|
+
|
|
123
|
+
if (attempts > maxAttempts) {
|
|
124
|
+
const timeoutError = 'Avatar generation timed out';
|
|
125
|
+
onError?.(timeoutError);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const result = await this.getAvatarStatus(accessToken, taskId);
|
|
130
|
+
|
|
131
|
+
if (!result.success) {
|
|
132
|
+
onError?.(result.error || 'Unknown error');
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
onProgress?.(result.status || 'unknown');
|
|
137
|
+
|
|
138
|
+
if (result.status === 'completed' && result.avatarUrl) {
|
|
139
|
+
onComplete?.(result.avatarUrl);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (result.status === 'failed') {
|
|
144
|
+
onError?.('Avatar generation failed');
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Continue polling
|
|
149
|
+
setTimeout(poll, interval);
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
poll();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// src/lib/api/client.ts
|
|
2
|
+
// ZO API HTTP client configuration
|
|
3
|
+
|
|
4
|
+
import axios, { AxiosInstance } from 'axios';
|
|
5
|
+
import { StorageAdapter, STORAGE_KEYS, LocalStorageAdapter } from '../utils/storage';
|
|
6
|
+
|
|
7
|
+
export interface ZoPassportConfig {
|
|
8
|
+
/** Your ZO client key (required) */
|
|
9
|
+
clientKey: string;
|
|
10
|
+
/** API base URL (default: https://api.io.zo.xyz) */
|
|
11
|
+
baseUrl?: string;
|
|
12
|
+
/** Request timeout in ms (default: 10000) */
|
|
13
|
+
timeout?: number;
|
|
14
|
+
/** Storage adapter for tokens (default: LocalStorageAdapter) */
|
|
15
|
+
storageAdapter?: StorageAdapter;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Generate new device credentials
|
|
20
|
+
*/
|
|
21
|
+
function generateDeviceCredentials(): { deviceId: string; deviceSecret: string } {
|
|
22
|
+
const deviceId = `web-${Date.now()}-${Math.random().toString(36).substring(7)}`;
|
|
23
|
+
const deviceSecret = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
|
|
24
|
+
return { deviceId, deviceSecret };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export class ZoApiClient {
|
|
28
|
+
private client: AxiosInstance;
|
|
29
|
+
private config: ZoPassportConfig;
|
|
30
|
+
private storage: StorageAdapter;
|
|
31
|
+
|
|
32
|
+
constructor(config: ZoPassportConfig) {
|
|
33
|
+
this.config = config;
|
|
34
|
+
this.storage = config.storageAdapter || new LocalStorageAdapter();
|
|
35
|
+
|
|
36
|
+
this.client = axios.create({
|
|
37
|
+
baseURL: config.baseUrl || 'https://api.io.zo.xyz',
|
|
38
|
+
timeout: config.timeout || 10000,
|
|
39
|
+
headers: {
|
|
40
|
+
'Content-Type': 'application/json',
|
|
41
|
+
'Accept': 'application/json',
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
this.setupInterceptors();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private async setupInterceptors() {
|
|
49
|
+
// Request interceptor: Add required headers
|
|
50
|
+
this.client.interceptors.request.use(async (config) => {
|
|
51
|
+
// Always add client key
|
|
52
|
+
config.headers['client-key'] = this.config.clientKey;
|
|
53
|
+
|
|
54
|
+
// Get or generate device credentials
|
|
55
|
+
const credentials = await this.getOrCreateDeviceCredentials();
|
|
56
|
+
config.headers['client-device-id'] = credentials.deviceId;
|
|
57
|
+
config.headers['client-device-secret'] = credentials.deviceSecret;
|
|
58
|
+
|
|
59
|
+
// Add auth token if available
|
|
60
|
+
const token = await this.storage.getItem(STORAGE_KEYS.ACCESS_TOKEN);
|
|
61
|
+
if (token) {
|
|
62
|
+
config.headers['Authorization'] = `Bearer ${token}`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return config;
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// Response interceptor: Handle token refresh on 401
|
|
69
|
+
this.client.interceptors.response.use(
|
|
70
|
+
(response) => response,
|
|
71
|
+
async (error) => {
|
|
72
|
+
const originalRequest = error.config;
|
|
73
|
+
|
|
74
|
+
// If 401 and we haven't retried yet
|
|
75
|
+
if (error.response?.status === 401 && !originalRequest._retry) {
|
|
76
|
+
originalRequest._retry = true;
|
|
77
|
+
|
|
78
|
+
const refreshToken = await this.storage.getItem(STORAGE_KEYS.REFRESH_TOKEN);
|
|
79
|
+
if (refreshToken) {
|
|
80
|
+
try {
|
|
81
|
+
const response = await this.client.post('/api/v1/auth/token/refresh/', {
|
|
82
|
+
refresh_token: refreshToken,
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
if (response.data?.access) {
|
|
86
|
+
await this.storage.setItem(STORAGE_KEYS.ACCESS_TOKEN, response.data.access);
|
|
87
|
+
if (response.data.refresh) {
|
|
88
|
+
await this.storage.setItem(STORAGE_KEYS.REFRESH_TOKEN, response.data.refresh);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Retry original request with new token
|
|
92
|
+
originalRequest.headers['Authorization'] = `Bearer ${response.data.access}`;
|
|
93
|
+
return this.client(originalRequest);
|
|
94
|
+
}
|
|
95
|
+
} catch (refreshError) {
|
|
96
|
+
// Refresh failed - clear session
|
|
97
|
+
await this.storage.removeItem(STORAGE_KEYS.ACCESS_TOKEN);
|
|
98
|
+
await this.storage.removeItem(STORAGE_KEYS.REFRESH_TOKEN);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return Promise.reject(error);
|
|
104
|
+
}
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
private async getOrCreateDeviceCredentials(): Promise<{ deviceId: string; deviceSecret: string }> {
|
|
109
|
+
// Try to get from storage
|
|
110
|
+
const storedId = await this.storage.getItem(STORAGE_KEYS.CLIENT_DEVICE_ID);
|
|
111
|
+
const storedSecret = await this.storage.getItem(STORAGE_KEYS.CLIENT_DEVICE_SECRET);
|
|
112
|
+
|
|
113
|
+
if (storedId && storedSecret) {
|
|
114
|
+
return { deviceId: storedId, deviceSecret: storedSecret };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Generate new credentials
|
|
118
|
+
const credentials = generateDeviceCredentials();
|
|
119
|
+
|
|
120
|
+
// Save to storage
|
|
121
|
+
await this.storage.setItem(STORAGE_KEYS.CLIENT_DEVICE_ID, credentials.deviceId);
|
|
122
|
+
await this.storage.setItem(STORAGE_KEYS.CLIENT_DEVICE_SECRET, credentials.deviceSecret);
|
|
123
|
+
|
|
124
|
+
return credentials;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
get axiosInstance(): AxiosInstance {
|
|
128
|
+
return this.client;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
getStorage(): StorageAdapter {
|
|
132
|
+
return this.storage;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// src/lib/api/profile.ts
|
|
2
|
+
// ZO API profile functions
|
|
3
|
+
|
|
4
|
+
import { ZoApiClient } from './client';
|
|
5
|
+
import { StorageAdapter } from '../utils/storage';
|
|
6
|
+
import type { ZoProfileResponse, ZoProfileUpdatePayload, ZoErrorResponse } from '../types';
|
|
7
|
+
|
|
8
|
+
export class ZoProfile {
|
|
9
|
+
constructor(
|
|
10
|
+
private client: ZoApiClient,
|
|
11
|
+
private storage: StorageAdapter
|
|
12
|
+
) {}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Get user profile
|
|
16
|
+
*/
|
|
17
|
+
async getProfile(accessToken: string): Promise<{
|
|
18
|
+
success: boolean;
|
|
19
|
+
profile?: ZoProfileResponse;
|
|
20
|
+
error?: string;
|
|
21
|
+
}> {
|
|
22
|
+
try {
|
|
23
|
+
const response = await this.client.axiosInstance.get<ZoProfileResponse>(
|
|
24
|
+
'/api/v1/profile/me/',
|
|
25
|
+
{
|
|
26
|
+
headers: {
|
|
27
|
+
Authorization: `Bearer ${accessToken}`,
|
|
28
|
+
},
|
|
29
|
+
}
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
success: true,
|
|
34
|
+
profile: response.data,
|
|
35
|
+
};
|
|
36
|
+
} catch (error: any) {
|
|
37
|
+
const errorData = error.response?.data as ZoErrorResponse;
|
|
38
|
+
return {
|
|
39
|
+
success: false,
|
|
40
|
+
error: errorData?.detail || errorData?.message || 'Failed to fetch profile',
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Update user profile (partial updates supported)
|
|
47
|
+
*/
|
|
48
|
+
async updateProfile(
|
|
49
|
+
accessToken: string,
|
|
50
|
+
updates: ZoProfileUpdatePayload
|
|
51
|
+
): Promise<{
|
|
52
|
+
success: boolean;
|
|
53
|
+
profile?: ZoProfileResponse;
|
|
54
|
+
error?: string;
|
|
55
|
+
}> {
|
|
56
|
+
try {
|
|
57
|
+
const response = await this.client.axiosInstance.post<ZoProfileResponse>(
|
|
58
|
+
'/api/v1/profile/me/',
|
|
59
|
+
updates,
|
|
60
|
+
{
|
|
61
|
+
headers: {
|
|
62
|
+
Authorization: `Bearer ${accessToken}`,
|
|
63
|
+
},
|
|
64
|
+
}
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
success: true,
|
|
69
|
+
profile: response.data,
|
|
70
|
+
};
|
|
71
|
+
} catch (error: any) {
|
|
72
|
+
const errorData = error.response?.data as ZoErrorResponse;
|
|
73
|
+
return {
|
|
74
|
+
success: false,
|
|
75
|
+
error: errorData?.detail || errorData?.message || 'Failed to update profile',
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// src/lib/api/wallet.ts
|
|
2
|
+
// Framework-agnostic Wallet API
|
|
3
|
+
|
|
4
|
+
import { ZoApiClient } from './client';
|
|
5
|
+
import type { BalanceResponse, TransactionsResponse, Transaction } from '../types/wallet';
|
|
6
|
+
|
|
7
|
+
export class ZoWallet {
|
|
8
|
+
private client: ZoApiClient;
|
|
9
|
+
|
|
10
|
+
constructor(client: ZoApiClient) {
|
|
11
|
+
this.client = client;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Get wallet balance
|
|
16
|
+
* @returns Wallet balance amount
|
|
17
|
+
*/
|
|
18
|
+
async getBalance(): Promise<number> {
|
|
19
|
+
try {
|
|
20
|
+
const response = await this.client.axiosInstance.get<BalanceResponse>(
|
|
21
|
+
'/api/v1/web3/token/airdrops/summary'
|
|
22
|
+
);
|
|
23
|
+
return response.data?.data?.total_amount ?? 0;
|
|
24
|
+
} catch (error) {
|
|
25
|
+
console.error('[ZoWallet] Failed to fetch balance:', error);
|
|
26
|
+
throw error;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Get transaction history
|
|
32
|
+
* @param page - Optional page number for pagination
|
|
33
|
+
* @returns Array of transactions
|
|
34
|
+
*/
|
|
35
|
+
async getTransactions(page?: number): Promise<{
|
|
36
|
+
transactions: Transaction[];
|
|
37
|
+
next?: string;
|
|
38
|
+
previous?: string;
|
|
39
|
+
count: number;
|
|
40
|
+
}> {
|
|
41
|
+
try {
|
|
42
|
+
const url = page
|
|
43
|
+
? `/api/v1/profile/completion-grants/claims?page=${page}`
|
|
44
|
+
: '/api/v1/profile/completion-grants/claims';
|
|
45
|
+
|
|
46
|
+
const response = await this.client.axiosInstance.get<TransactionsResponse>(url);
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
transactions: response.data?.data?.results ?? [],
|
|
50
|
+
next: response.data?.data?.next,
|
|
51
|
+
previous: response.data?.data?.previous,
|
|
52
|
+
count: response.data?.data?.count ?? 0,
|
|
53
|
+
};
|
|
54
|
+
} catch (error) {
|
|
55
|
+
console.error('[ZoWallet] Failed to fetch transactions:', error);
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// src/lib/types/auth.ts
|
|
2
|
+
// Authentication-related types
|
|
3
|
+
|
|
4
|
+
export interface ZoAuthOTPRequest {
|
|
5
|
+
mobile_country_code: string;
|
|
6
|
+
mobile_number: string;
|
|
7
|
+
message_channel?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface ZoAuthOTPVerifyRequest {
|
|
11
|
+
mobile_country_code: string;
|
|
12
|
+
mobile_number: string;
|
|
13
|
+
otp: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface ZoAuthTokens {
|
|
17
|
+
access: string;
|
|
18
|
+
refresh: string;
|
|
19
|
+
access_expiry: string;
|
|
20
|
+
refresh_expiry: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ZoAuthResponse {
|
|
24
|
+
// Legacy fields (for backward compatibility)
|
|
25
|
+
token: string; // Same as access_token
|
|
26
|
+
valid_till: string; // Same as access_token_expiry
|
|
27
|
+
|
|
28
|
+
// Current fields
|
|
29
|
+
access_token: string;
|
|
30
|
+
access_token_expiry: string;
|
|
31
|
+
refresh_token: string;
|
|
32
|
+
refresh_token_expiry: string;
|
|
33
|
+
client_key: string;
|
|
34
|
+
device_id: string;
|
|
35
|
+
device_secret: string;
|
|
36
|
+
device_info: Record<string, any>;
|
|
37
|
+
user: ZoUser;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface ZoUser {
|
|
41
|
+
id: string; // UUID
|
|
42
|
+
pid: string; // PID123
|
|
43
|
+
first_name: string;
|
|
44
|
+
last_name: string;
|
|
45
|
+
mobile_number: string;
|
|
46
|
+
email_address: string;
|
|
47
|
+
date_of_birth: string | null;
|
|
48
|
+
bio: string;
|
|
49
|
+
pfp_image: string;
|
|
50
|
+
wallet_address: string; // Auto-provisioned during auth
|
|
51
|
+
membership: 'founder' | 'citizen' | 'none';
|
|
52
|
+
body_type: 'bro' | 'bae';
|
|
53
|
+
place_name: string;
|
|
54
|
+
home_location: {
|
|
55
|
+
lat: number;
|
|
56
|
+
lng: number;
|
|
57
|
+
} | null;
|
|
58
|
+
cultures: Array<{
|
|
59
|
+
key: string;
|
|
60
|
+
name: string;
|
|
61
|
+
icon: string;
|
|
62
|
+
description: string;
|
|
63
|
+
}>;
|
|
64
|
+
founder_tokens: string[]; // Array of token IDs like ["523", "204"]
|
|
65
|
+
avatar?: {
|
|
66
|
+
image: string;
|
|
67
|
+
status: 'pending' | 'processing' | 'completed' | 'failed';
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface ZoErrorResponse {
|
|
72
|
+
detail?: string;
|
|
73
|
+
error?: string;
|
|
74
|
+
message?: string;
|
|
75
|
+
errors?: string[];
|
|
76
|
+
success?: boolean;
|
|
77
|
+
}
|
|
78
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// src/lib/types/avatar.ts
|
|
2
|
+
// Avatar-related types
|
|
3
|
+
|
|
4
|
+
export interface ZoAvatarGenerateRequest {
|
|
5
|
+
body_type: 'bro' | 'bae';
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface ZoAvatarGenerateResponse {
|
|
9
|
+
task_id: string;
|
|
10
|
+
status: 'pending' | 'processing' | 'completed' | 'failed';
|
|
11
|
+
message: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface ZoAvatarStatusResponse {
|
|
15
|
+
task_id: string;
|
|
16
|
+
status: 'pending' | 'processing' | 'completed' | 'failed';
|
|
17
|
+
result?: {
|
|
18
|
+
avatar_url: string;
|
|
19
|
+
};
|
|
20
|
+
error?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// src/lib/types/profile.ts
|
|
2
|
+
// Profile-related types
|
|
3
|
+
|
|
4
|
+
import { ZoUser } from './auth';
|
|
5
|
+
|
|
6
|
+
export interface ZoProfileResponse extends ZoUser {
|
|
7
|
+
mobile_country_code: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface ZoProfileUpdatePayload {
|
|
11
|
+
first_name?: string;
|
|
12
|
+
last_name?: string;
|
|
13
|
+
bio?: string;
|
|
14
|
+
date_of_birth?: string;
|
|
15
|
+
place_name?: string;
|
|
16
|
+
body_type?: 'bro' | 'bae';
|
|
17
|
+
}
|
|
18
|
+
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// src/lib/types/wallet.ts
|
|
2
|
+
// Wallet-related types
|
|
3
|
+
|
|
4
|
+
// Existing wallet types
|
|
5
|
+
export interface ZoWallet {
|
|
6
|
+
address: string;
|
|
7
|
+
network: 'base' | 'avalanche';
|
|
8
|
+
balance?: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface ZoTokenBalanceResponse {
|
|
12
|
+
balance: number;
|
|
13
|
+
currency: {
|
|
14
|
+
name: string;
|
|
15
|
+
symbol: string;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Wallet balance and transaction types
|
|
20
|
+
export interface WalletBalance {
|
|
21
|
+
total_amount: number;
|
|
22
|
+
currency: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface Transaction {
|
|
26
|
+
id: string;
|
|
27
|
+
created_at: string;
|
|
28
|
+
updated_at: string;
|
|
29
|
+
amount: number;
|
|
30
|
+
description: string;
|
|
31
|
+
claimed_at: string;
|
|
32
|
+
grant: {
|
|
33
|
+
id: string;
|
|
34
|
+
name: string;
|
|
35
|
+
description: string;
|
|
36
|
+
};
|
|
37
|
+
action: 'deposit' | 'spend';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface WalletUser {
|
|
41
|
+
avatar?: {
|
|
42
|
+
image: string;
|
|
43
|
+
};
|
|
44
|
+
first_name: string;
|
|
45
|
+
nickname?: string;
|
|
46
|
+
wallet_address: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// API Response types
|
|
50
|
+
export interface BalanceResponse {
|
|
51
|
+
data: {
|
|
52
|
+
total_amount: number;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface TransactionsResponse {
|
|
57
|
+
data: {
|
|
58
|
+
results: Transaction[];
|
|
59
|
+
next?: string;
|
|
60
|
+
previous?: string;
|
|
61
|
+
count: number;
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Formatted types for display
|
|
66
|
+
export interface FormattedTransaction {
|
|
67
|
+
id: string;
|
|
68
|
+
description: string;
|
|
69
|
+
amount: string;
|
|
70
|
+
date: string;
|
|
71
|
+
timestamp: string;
|
|
72
|
+
action: 'deposit' | 'spend';
|
|
73
|
+
color: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// React Component Props (optional, for React components)
|
|
77
|
+
export interface WalletCardProps {
|
|
78
|
+
balance: number;
|
|
79
|
+
user: WalletUser;
|
|
80
|
+
isOpen?: boolean;
|
|
81
|
+
onToggle?: () => void;
|
|
82
|
+
isLoading?: boolean;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface TransactionItemProps {
|
|
86
|
+
transaction: Transaction;
|
|
87
|
+
showDate?: boolean;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface TransactionListProps {
|
|
91
|
+
transactions: Transaction[];
|
|
92
|
+
isLoading?: boolean;
|
|
93
|
+
onEndReached?: () => void;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export interface MovingShineProps {
|
|
97
|
+
duration?: number;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface WalletScreenProps {
|
|
101
|
+
onBack?: () => void;
|
|
102
|
+
}
|
|
103
|
+
|