@explorins/pers-sdk-react-native 2.1.3 → 2.1.6
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 +7 -7
- package/dist/hooks/index.d.ts +6 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/useAnalytics.d.ts +37 -14
- package/dist/hooks/useAnalytics.d.ts.map +1 -1
- package/dist/hooks/useAnalytics.js +239 -19
- package/dist/hooks/useCampaigns.d.ts +14 -6
- package/dist/hooks/useCampaigns.d.ts.map +1 -1
- package/dist/hooks/useCampaigns.js +144 -10
- package/dist/hooks/useEvents.d.ts +17 -5
- package/dist/hooks/useEvents.d.ts.map +1 -1
- package/dist/hooks/useEvents.js +17 -5
- package/dist/hooks/useRedemptions.d.ts +5 -2
- package/dist/hooks/useRedemptions.d.ts.map +1 -1
- package/dist/hooks/useRedemptions.js +53 -2
- package/dist/hooks/useTokenBalances.d.ts +24 -0
- package/dist/hooks/useTokenBalances.d.ts.map +1 -1
- package/dist/hooks/useTokenBalances.js +42 -2
- package/dist/hooks/useTransactions.d.ts +8 -5
- package/dist/hooks/useTransactions.d.ts.map +1 -1
- package/dist/hooks/useTransactions.js +70 -27
- package/dist/hooks/useTriggerSources.d.ts +76 -0
- package/dist/hooks/useTriggerSources.d.ts.map +1 -0
- package/dist/hooks/useTriggerSources.js +272 -0
- package/dist/hooks/useUsers.d.ts +5 -4
- package/dist/hooks/useUsers.d.ts.map +1 -1
- package/dist/hooks/useUsers.js +47 -8
- package/dist/hooks/useWeb3.d.ts +6 -5
- package/dist/hooks/useWeb3.d.ts.map +1 -1
- package/dist/hooks/useWeb3.js +23 -10
- package/dist/index.d.ts +12 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5500 -893
- package/dist/index.js.map +1 -1
- package/dist/providers/PersSDKProvider.d.ts +38 -0
- package/dist/providers/PersSDKProvider.d.ts.map +1 -1
- package/dist/providers/PersSDKProvider.js +29 -1
- package/package.json +3 -2
- package/src/hooks/index.ts +17 -1
- package/src/hooks/useAnalytics.ts +268 -21
- package/src/hooks/useCampaigns.ts +176 -14
- package/src/hooks/useEvents.ts +17 -5
- package/src/hooks/useRedemptions.ts +66 -3
- package/src/hooks/useTokenBalances.ts +60 -2
- package/src/hooks/useTransactions.ts +84 -29
- package/src/hooks/useTriggerSources.ts +301 -0
- package/src/hooks/useUsers.ts +51 -9
- package/src/hooks/useWeb3.ts +28 -13
- package/src/index.ts +33 -3
- package/src/providers/PersSDKProvider.tsx +38 -1
|
@@ -5,11 +5,14 @@ import type {
|
|
|
5
5
|
CampaignDTO,
|
|
6
6
|
CampaignClaimDTO,
|
|
7
7
|
CampaignTriggerDTO,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
PaginatedResponseDTO
|
|
8
|
+
PaginatedResponseDTO,
|
|
9
|
+
CampaignClaimIncludeRelation,
|
|
10
|
+
CampaignIncludeRelation
|
|
12
11
|
} from '@explorins/pers-shared';
|
|
12
|
+
import type { CampaignClaimFilters } from '@explorins/pers-sdk/campaign';
|
|
13
|
+
|
|
14
|
+
// Re-export for consumers
|
|
15
|
+
export type { CampaignClaimFilters } from '@explorins/pers-sdk/campaign';
|
|
13
16
|
|
|
14
17
|
export const useCampaigns = () => {
|
|
15
18
|
const { sdk, isInitialized, isAuthenticated } = usePersSDK();
|
|
@@ -28,13 +31,30 @@ export const useCampaigns = () => {
|
|
|
28
31
|
}
|
|
29
32
|
}, [sdk, isInitialized]);
|
|
30
33
|
|
|
31
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Get campaign by ID with optional include relations
|
|
36
|
+
*
|
|
37
|
+
* @param campaignId - The campaign ID
|
|
38
|
+
* @param include - Relations to include: 'triggerSources', 'businesses'
|
|
39
|
+
* @returns Promise resolving to campaign data
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```typescript
|
|
43
|
+
* // Get campaign with all related data
|
|
44
|
+
* const campaign = await getCampaignById('campaign-123', ['triggerSources', 'businesses']);
|
|
45
|
+
* console.log('Trigger sources:', campaign.included?.triggerSources);
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
const getCampaignById = useCallback(async (
|
|
49
|
+
campaignId: string,
|
|
50
|
+
include?: CampaignIncludeRelation[]
|
|
51
|
+
): Promise<CampaignDTO | null> => {
|
|
32
52
|
if (!isInitialized || !sdk) {
|
|
33
53
|
throw new Error('SDK not initialized. Call initialize() first.');
|
|
34
54
|
}
|
|
35
55
|
|
|
36
56
|
try {
|
|
37
|
-
const result = await sdk.campaigns.getCampaignById(campaignId);
|
|
57
|
+
const result = await sdk.campaigns.getCampaignById(campaignId, include);
|
|
38
58
|
return result;
|
|
39
59
|
} catch (error) {
|
|
40
60
|
console.error('Failed to fetch campaign:', error);
|
|
@@ -59,7 +79,29 @@ export const useCampaigns = () => {
|
|
|
59
79
|
}
|
|
60
80
|
}, [sdk, isInitialized, isAuthenticated]);
|
|
61
81
|
|
|
62
|
-
|
|
82
|
+
/**
|
|
83
|
+
* Get user's campaign claims with optional pagination and include relations
|
|
84
|
+
*
|
|
85
|
+
* @param options - Optional options including pagination and include relations
|
|
86
|
+
* @returns Promise resolving to paginated campaign claims
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```typescript
|
|
90
|
+
* // Get user's claims with campaign details
|
|
91
|
+
* const { data: claims } = await getUserClaims({ include: ['campaign', 'business'] });
|
|
92
|
+
* claims.forEach(claim => {
|
|
93
|
+
* console.log('Campaign:', claim.included?.campaign?.title);
|
|
94
|
+
* });
|
|
95
|
+
*
|
|
96
|
+
* // With pagination
|
|
97
|
+
* const { data } = await getUserClaims({ page: 1, limit: 10, include: ['campaign'] });
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
const getUserClaims = useCallback(async (options?: {
|
|
101
|
+
page?: number;
|
|
102
|
+
limit?: number;
|
|
103
|
+
include?: CampaignClaimIncludeRelation[];
|
|
104
|
+
}): Promise<PaginatedResponseDTO<CampaignClaimDTO>> => {
|
|
63
105
|
if (!isInitialized || !sdk) {
|
|
64
106
|
throw new Error('SDK not initialized. Call initialize() first.');
|
|
65
107
|
}
|
|
@@ -69,7 +111,7 @@ export const useCampaigns = () => {
|
|
|
69
111
|
}
|
|
70
112
|
|
|
71
113
|
try {
|
|
72
|
-
const result = await sdk.campaigns.getUserClaims();
|
|
114
|
+
const result = await sdk.campaigns.getUserClaims(options);
|
|
73
115
|
return result;
|
|
74
116
|
} catch (error) {
|
|
75
117
|
console.error('Failed to fetch user claims:', error);
|
|
@@ -106,13 +148,32 @@ export const useCampaigns = () => {
|
|
|
106
148
|
}
|
|
107
149
|
}, [sdk, isInitialized]);
|
|
108
150
|
|
|
109
|
-
|
|
151
|
+
/**
|
|
152
|
+
* Admin: Get campaign claims with optional filters and include relations
|
|
153
|
+
*
|
|
154
|
+
* @param filters - Optional filters for campaign, user, or business
|
|
155
|
+
* @param include - Relations to include: 'campaign', 'user', 'business'
|
|
156
|
+
* @returns Promise resolving to paginated campaign claims
|
|
157
|
+
*
|
|
158
|
+
* @example
|
|
159
|
+
* ```typescript
|
|
160
|
+
* // Get all claims with user details
|
|
161
|
+
* const { data: claims } = await getCampaignClaims({}, ['user']);
|
|
162
|
+
*
|
|
163
|
+
* // Get claims for a specific campaign
|
|
164
|
+
* const { data } = await getCampaignClaims({ campaignId: 'campaign-123' }, ['user', 'business']);
|
|
165
|
+
* ```
|
|
166
|
+
*/
|
|
167
|
+
const getCampaignClaims = useCallback(async (
|
|
168
|
+
filters?: CampaignClaimFilters,
|
|
169
|
+
include?: CampaignClaimIncludeRelation[]
|
|
170
|
+
): Promise<PaginatedResponseDTO<CampaignClaimDTO>> => {
|
|
110
171
|
if (!isInitialized || !sdk) {
|
|
111
172
|
throw new Error('SDK not initialized. Call initialize() first.');
|
|
112
173
|
}
|
|
113
174
|
|
|
114
175
|
try {
|
|
115
|
-
const result = await sdk.campaigns.getCampaignClaims();
|
|
176
|
+
const result = await sdk.campaigns.getCampaignClaims(filters, include);
|
|
116
177
|
return result;
|
|
117
178
|
} catch (error) {
|
|
118
179
|
console.error('Failed to fetch campaign claims:', error);
|
|
@@ -120,13 +181,23 @@ export const useCampaigns = () => {
|
|
|
120
181
|
}
|
|
121
182
|
}, [sdk, isInitialized]);
|
|
122
183
|
|
|
123
|
-
|
|
184
|
+
/**
|
|
185
|
+
* Admin: Get campaign claims by user ID with optional include relations
|
|
186
|
+
*
|
|
187
|
+
* @param userId - The user ID
|
|
188
|
+
* @param include - Relations to include: 'campaign', 'user', 'business'
|
|
189
|
+
* @returns Promise resolving to paginated campaign claims
|
|
190
|
+
*/
|
|
191
|
+
const getCampaignClaimsByUserId = useCallback(async (
|
|
192
|
+
userId: string,
|
|
193
|
+
include?: CampaignClaimIncludeRelation[]
|
|
194
|
+
): Promise<PaginatedResponseDTO<CampaignClaimDTO>> => {
|
|
124
195
|
if (!isInitialized || !sdk) {
|
|
125
196
|
throw new Error('SDK not initialized. Call initialize() first.');
|
|
126
197
|
}
|
|
127
198
|
|
|
128
199
|
try {
|
|
129
|
-
const result = await sdk.campaigns.getCampaignClaimsByUserId(userId);
|
|
200
|
+
const result = await sdk.campaigns.getCampaignClaimsByUserId(userId, undefined, include);
|
|
130
201
|
return result;
|
|
131
202
|
} catch (error) {
|
|
132
203
|
console.error('Failed to fetch campaign claims by user ID:', error);
|
|
@@ -134,13 +205,23 @@ export const useCampaigns = () => {
|
|
|
134
205
|
}
|
|
135
206
|
}, [sdk, isInitialized]);
|
|
136
207
|
|
|
137
|
-
|
|
208
|
+
/**
|
|
209
|
+
* Admin: Get campaign claims by business ID with optional include relations
|
|
210
|
+
*
|
|
211
|
+
* @param businessId - The business ID
|
|
212
|
+
* @param include - Relations to include: 'campaign', 'user', 'business'
|
|
213
|
+
* @returns Promise resolving to paginated campaign claims
|
|
214
|
+
*/
|
|
215
|
+
const getCampaignClaimsByBusinessId = useCallback(async (
|
|
216
|
+
businessId: string,
|
|
217
|
+
include?: CampaignClaimIncludeRelation[]
|
|
218
|
+
): Promise<PaginatedResponseDTO<CampaignClaimDTO>> => {
|
|
138
219
|
if (!isInitialized || !sdk) {
|
|
139
220
|
throw new Error('SDK not initialized. Call initialize() first.');
|
|
140
221
|
}
|
|
141
222
|
|
|
142
223
|
try {
|
|
143
|
-
const result = await sdk.campaigns.getCampaignClaimsByBusinessId(businessId);
|
|
224
|
+
const result = await sdk.campaigns.getCampaignClaimsByBusinessId(businessId, undefined, include);
|
|
144
225
|
return result;
|
|
145
226
|
} catch (error) {
|
|
146
227
|
console.error('Failed to fetch campaign claims by business ID:', error);
|
|
@@ -148,6 +229,85 @@ export const useCampaigns = () => {
|
|
|
148
229
|
}
|
|
149
230
|
}, [sdk, isInitialized]);
|
|
150
231
|
|
|
232
|
+
// ==========================================
|
|
233
|
+
// TRIGGER SOURCE ASSIGNMENT (Admin)
|
|
234
|
+
// Note: TriggerSource CRUD is in useTriggerSources hook
|
|
235
|
+
// ==========================================
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Admin: Assign a trigger source to a campaign
|
|
239
|
+
*
|
|
240
|
+
* Associates a trigger source (QR code, NFC tag, etc.) with a campaign.
|
|
241
|
+
* A campaign can have multiple trigger sources.
|
|
242
|
+
*
|
|
243
|
+
* Note: To create/update/delete trigger sources, use `useTriggerSources` hook.
|
|
244
|
+
*
|
|
245
|
+
* @param campaignId - Campaign UUID
|
|
246
|
+
* @param triggerSourceId - Trigger source UUID
|
|
247
|
+
* @returns Promise resolving to updated campaign
|
|
248
|
+
*
|
|
249
|
+
* @example
|
|
250
|
+
* ```typescript
|
|
251
|
+
* const { create } = useTriggerSources();
|
|
252
|
+
* const { assignTriggerSource } = useCampaigns();
|
|
253
|
+
*
|
|
254
|
+
* // Create trigger source first
|
|
255
|
+
* const source = await create({ type: 'QR_CODE', name: 'Store QR' });
|
|
256
|
+
*
|
|
257
|
+
* // Then assign to campaign
|
|
258
|
+
* const updated = await assignTriggerSource('campaign-123', source.id);
|
|
259
|
+
* ```
|
|
260
|
+
*/
|
|
261
|
+
const assignTriggerSource = useCallback(async (
|
|
262
|
+
campaignId: string,
|
|
263
|
+
triggerSourceId: string
|
|
264
|
+
): Promise<CampaignDTO> => {
|
|
265
|
+
if (!isInitialized || !sdk) {
|
|
266
|
+
throw new Error('SDK not initialized. Call initialize() first.');
|
|
267
|
+
}
|
|
268
|
+
if (!isAuthenticated) {
|
|
269
|
+
throw new Error('SDK not authenticated. assignTriggerSource requires admin authentication.');
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
try {
|
|
273
|
+
const result = await sdk.campaigns.assignTriggerSource(campaignId, triggerSourceId);
|
|
274
|
+
return result;
|
|
275
|
+
} catch (error) {
|
|
276
|
+
console.error('Failed to assign trigger source:', error);
|
|
277
|
+
throw error;
|
|
278
|
+
}
|
|
279
|
+
}, [sdk, isInitialized, isAuthenticated]);
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Admin: Remove a trigger source from a campaign
|
|
283
|
+
*
|
|
284
|
+
* Removes the association between a trigger source and a campaign.
|
|
285
|
+
* The trigger source itself is not deleted.
|
|
286
|
+
*
|
|
287
|
+
* @param campaignId - Campaign UUID
|
|
288
|
+
* @param triggerSourceId - Trigger source UUID
|
|
289
|
+
* @returns Promise resolving to updated campaign
|
|
290
|
+
*/
|
|
291
|
+
const removeTriggerSource = useCallback(async (
|
|
292
|
+
campaignId: string,
|
|
293
|
+
triggerSourceId: string
|
|
294
|
+
): Promise<CampaignDTO> => {
|
|
295
|
+
if (!isInitialized || !sdk) {
|
|
296
|
+
throw new Error('SDK not initialized. Call initialize() first.');
|
|
297
|
+
}
|
|
298
|
+
if (!isAuthenticated) {
|
|
299
|
+
throw new Error('SDK not authenticated. removeTriggerSource requires admin authentication.');
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
try {
|
|
303
|
+
const result = await sdk.campaigns.removeTriggerSource(campaignId, triggerSourceId);
|
|
304
|
+
return result;
|
|
305
|
+
} catch (error) {
|
|
306
|
+
console.error('Failed to remove trigger source:', error);
|
|
307
|
+
throw error;
|
|
308
|
+
}
|
|
309
|
+
}, [sdk, isInitialized, isAuthenticated]);
|
|
310
|
+
|
|
151
311
|
return {
|
|
152
312
|
getActiveCampaigns,
|
|
153
313
|
getCampaignById,
|
|
@@ -158,6 +318,8 @@ export const useCampaigns = () => {
|
|
|
158
318
|
getCampaignClaims,
|
|
159
319
|
getCampaignClaimsByUserId,
|
|
160
320
|
getCampaignClaimsByBusinessId,
|
|
321
|
+
assignTriggerSource,
|
|
322
|
+
removeTriggerSource,
|
|
161
323
|
isAvailable: isInitialized && !!sdk?.campaigns,
|
|
162
324
|
};
|
|
163
325
|
};
|
package/src/hooks/useEvents.ts
CHANGED
|
@@ -35,23 +35,35 @@ export interface EventsHook {
|
|
|
35
35
|
* React Native hook for PERS SDK event system
|
|
36
36
|
*
|
|
37
37
|
* This hook provides access to the platform-agnostic event system for subscribing to
|
|
38
|
-
* transaction, authentication, campaign, and system events. All events
|
|
39
|
-
* `userMessage` field ready for display to end users.
|
|
38
|
+
* transaction, authentication, campaign, blockchain, and system events. All events
|
|
39
|
+
* include a `userMessage` field ready for display to end users.
|
|
40
40
|
*
|
|
41
41
|
* **Event Domains:**
|
|
42
42
|
* - `auth` - Authentication events (login, logout, token refresh)
|
|
43
43
|
* - `user` - User profile events (update, create)
|
|
44
|
-
* - `transaction` - Transaction events (created,
|
|
44
|
+
* - `transaction` - Transaction events (created, submitted, confirmed)
|
|
45
45
|
* - `campaign` - Campaign events (claimed, activated)
|
|
46
46
|
* - `redemption` - Redemption events (redeemed, expired)
|
|
47
47
|
* - `business` - Business events (created, updated, membership)
|
|
48
|
+
* - `wallet` - Real-time blockchain events (Transfer, Approval, etc.) - Auto-enabled on auth
|
|
48
49
|
* - `api` - API error events (network, validation, server errors)
|
|
49
50
|
*
|
|
51
|
+
* **Blockchain Events (Wallet Domain):**
|
|
52
|
+
* When `sdk.connectWalletEvents()` is called (or auto-enabled via `captureWalletEvents: true`),
|
|
53
|
+
* real-time blockchain events are automatically routed through the same event system:
|
|
54
|
+
* ```typescript
|
|
55
|
+
* const { subscribe } = useEvents();
|
|
56
|
+
*
|
|
57
|
+
* subscribe((event) => {
|
|
58
|
+
* if (event.domain === 'wallet') {
|
|
59
|
+
* console.log('Blockchain event:', event.type); // wallet_transfer, wallet_approval, etc.
|
|
60
|
+
* }
|
|
61
|
+
* });
|
|
62
|
+
* ```
|
|
63
|
+
*
|
|
50
64
|
* **Notification Levels:**
|
|
51
65
|
* - `success` - Operation completed successfully
|
|
52
66
|
* - `error` - Operation failed
|
|
53
|
-
* - `warning` - Operation completed with warnings
|
|
54
|
-
* - `info` - Informational event
|
|
55
67
|
*
|
|
56
68
|
* **Cleanup:**
|
|
57
69
|
* All subscriptions created through this hook are automatically cleaned up when
|
|
@@ -7,8 +7,13 @@ import type {
|
|
|
7
7
|
RedemptionRedeemDTO,
|
|
8
8
|
RedemptionRedeemRequestResponseDTO,
|
|
9
9
|
RedemptionTypeDTO,
|
|
10
|
-
PaginatedResponseDTO
|
|
10
|
+
PaginatedResponseDTO,
|
|
11
|
+
RedemptionRedeemIncludeRelation
|
|
11
12
|
} from '@explorins/pers-shared';
|
|
13
|
+
import type { RedemptionRedeemFilters } from '@explorins/pers-sdk/redemption';
|
|
14
|
+
|
|
15
|
+
// Re-export for consumers
|
|
16
|
+
export type { RedemptionRedeemFilters } from '@explorins/pers-sdk/redemption';
|
|
12
17
|
|
|
13
18
|
export const useRedemptions = () => {
|
|
14
19
|
const { sdk, isInitialized, isAuthenticated } = usePersSDK();
|
|
@@ -36,7 +41,25 @@ export const useRedemptions = () => {
|
|
|
36
41
|
}
|
|
37
42
|
}, [sdk, isInitialized]);
|
|
38
43
|
|
|
39
|
-
|
|
44
|
+
/**
|
|
45
|
+
* Get user's redemption history with optional include relations
|
|
46
|
+
*
|
|
47
|
+
* @param include - Relations to include: 'redemption', 'user', 'business'
|
|
48
|
+
* @returns Promise resolving to paginated redemption history
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```typescript
|
|
52
|
+
* // Get user redemptions with full redemption details
|
|
53
|
+
* const { data: redeems } = await getUserRedemptions(['redemption', 'business']);
|
|
54
|
+
* redeems.forEach(redeem => {
|
|
55
|
+
* console.log('Redemption:', redeem.included?.redemption?.title);
|
|
56
|
+
* console.log('Business:', redeem.included?.business?.displayName);
|
|
57
|
+
* });
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
const getUserRedemptions = useCallback(async (
|
|
61
|
+
include?: RedemptionRedeemIncludeRelation[]
|
|
62
|
+
): Promise<PaginatedResponseDTO<RedemptionRedeemDTO>> => {
|
|
40
63
|
if (!isInitialized || !sdk) {
|
|
41
64
|
throw new Error('SDK not initialized. Call initialize() first.');
|
|
42
65
|
}
|
|
@@ -46,7 +69,7 @@ export const useRedemptions = () => {
|
|
|
46
69
|
}
|
|
47
70
|
|
|
48
71
|
try {
|
|
49
|
-
const result = await sdk.redemptions.getUserRedemptions();
|
|
72
|
+
const result = await sdk.redemptions.getUserRedemptions(undefined, include);
|
|
50
73
|
return result;
|
|
51
74
|
} catch (error) {
|
|
52
75
|
console.error('Failed to fetch user redemptions:', error);
|
|
@@ -100,6 +123,45 @@ export const useRedemptions = () => {
|
|
|
100
123
|
}, [sdk, isInitialized, isAuthenticated, signAndSubmitTransactionWithJWT, isSignerAvailable]);
|
|
101
124
|
|
|
102
125
|
// Admin methods
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Admin: Get all redemption redeems with filters and include relations
|
|
129
|
+
*
|
|
130
|
+
* Retrieves all redemption redeems across the platform with filtering.
|
|
131
|
+
*
|
|
132
|
+
* @param filters - Optional filters for user, redemption, and pagination
|
|
133
|
+
* @param include - Relations to include: 'redemption', 'user', 'business'
|
|
134
|
+
* @returns Promise resolving to paginated redemption redeems
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```typescript
|
|
138
|
+
* // Get all redeems with user details
|
|
139
|
+
* const { data: redeems } = await getRedemptionRedeems({}, ['user', 'redemption']);
|
|
140
|
+
*
|
|
141
|
+
* // Filter by specific user
|
|
142
|
+
* const { data: userRedeems } = await getRedemptionRedeems(
|
|
143
|
+
* { userId: 'user-123' },
|
|
144
|
+
* ['redemption']
|
|
145
|
+
* );
|
|
146
|
+
* ```
|
|
147
|
+
*/
|
|
148
|
+
const getRedemptionRedeems = useCallback(async (
|
|
149
|
+
filters?: RedemptionRedeemFilters,
|
|
150
|
+
include?: RedemptionRedeemIncludeRelation[]
|
|
151
|
+
): Promise<PaginatedResponseDTO<RedemptionRedeemDTO>> => {
|
|
152
|
+
if (!isInitialized || !sdk) {
|
|
153
|
+
throw new Error('SDK not initialized. Call initialize() first.');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
try {
|
|
157
|
+
const result = await sdk.redemptions.getRedemptionRedeems(filters, include);
|
|
158
|
+
return result;
|
|
159
|
+
} catch (error) {
|
|
160
|
+
console.error('Failed to fetch redemption redeems:', error);
|
|
161
|
+
throw error;
|
|
162
|
+
}
|
|
163
|
+
}, [sdk, isInitialized]);
|
|
164
|
+
|
|
103
165
|
const createRedemption = useCallback(async (redemptionData: RedemptionCreateRequestDTO): Promise<RedemptionDTO> => {
|
|
104
166
|
if (!isInitialized || !sdk) {
|
|
105
167
|
throw new Error('SDK not initialized. Call initialize() first.');
|
|
@@ -161,6 +223,7 @@ export const useRedemptions = () => {
|
|
|
161
223
|
getUserRedemptions,
|
|
162
224
|
redeem,
|
|
163
225
|
getRedemptionTypes,
|
|
226
|
+
getRedemptionRedeems,
|
|
164
227
|
createRedemption,
|
|
165
228
|
updateRedemption,
|
|
166
229
|
toggleRedemptionStatus,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useCallback, useEffect, useState } from 'react';
|
|
1
|
+
import { useCallback, useEffect, useState, useRef } from 'react';
|
|
2
2
|
import { usePersSDK } from '../providers/PersSDKProvider';
|
|
3
3
|
import { useWeb3 } from './useWeb3';
|
|
4
4
|
import { NativeTokenTypes, type TokenDTO } from '@explorins/pers-shared';
|
|
@@ -36,6 +36,18 @@ export interface UseTokenBalancesOptions {
|
|
|
36
36
|
autoLoad?: boolean;
|
|
37
37
|
/** Optional refresh interval in milliseconds (0 = disabled) */
|
|
38
38
|
refreshInterval?: number;
|
|
39
|
+
/**
|
|
40
|
+
* Auto-refresh balances when wallet events are received (Transfer, Approval, etc.)
|
|
41
|
+
* Requires `captureWalletEvents: true` in SDK config (default)
|
|
42
|
+
* @default true
|
|
43
|
+
*/
|
|
44
|
+
refreshOnWalletEvents?: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Debounce time for wallet event-triggered refreshes (milliseconds)
|
|
47
|
+
* Prevents rapid refresh calls during batch transfers
|
|
48
|
+
* @default 1000
|
|
49
|
+
*/
|
|
50
|
+
walletEventDebounceMs?: number;
|
|
39
51
|
}
|
|
40
52
|
|
|
41
53
|
/**
|
|
@@ -126,6 +138,18 @@ export interface UseTokenBalancesResult {
|
|
|
126
138
|
* ```
|
|
127
139
|
*
|
|
128
140
|
* @example
|
|
141
|
+
* **With Wallet Events (Real-time):**
|
|
142
|
+
* ```typescript
|
|
143
|
+
* // Auto-refresh on Transfer, Approval, and other blockchain events
|
|
144
|
+
* const { tokenBalances } = useTokenBalances({
|
|
145
|
+
* accountAddress: walletAddress!,
|
|
146
|
+
* availableTokens,
|
|
147
|
+
* refreshOnWalletEvents: true, // Enable real-time refresh (default: true)
|
|
148
|
+
* walletEventDebounceMs: 1000 // Debounce rapid events (default: 1000ms)
|
|
149
|
+
* });
|
|
150
|
+
* ```
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
129
153
|
* **Multi-Wallet Support:**
|
|
130
154
|
* ```typescript
|
|
131
155
|
* function MultiWalletBalances() {
|
|
@@ -148,7 +172,9 @@ export function useTokenBalances(options: UseTokenBalancesOptions): UseTokenBala
|
|
|
148
172
|
accountAddress,
|
|
149
173
|
availableTokens = [],
|
|
150
174
|
autoLoad = true,
|
|
151
|
-
refreshInterval = 0
|
|
175
|
+
refreshInterval = 0,
|
|
176
|
+
refreshOnWalletEvents = true,
|
|
177
|
+
walletEventDebounceMs = 1000
|
|
152
178
|
} = options;
|
|
153
179
|
|
|
154
180
|
const { isAuthenticated, sdk } = usePersSDK();
|
|
@@ -157,6 +183,9 @@ export function useTokenBalances(options: UseTokenBalancesOptions): UseTokenBala
|
|
|
157
183
|
const [tokenBalances, setTokenBalances] = useState<TokenBalanceWithToken[]>([]);
|
|
158
184
|
const [isLoading, setIsLoading] = useState(false);
|
|
159
185
|
const [error, setError] = useState<string | null>(null);
|
|
186
|
+
|
|
187
|
+
// Debounce ref for wallet events
|
|
188
|
+
const walletEventDebounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
160
189
|
|
|
161
190
|
// Check if the hook is available for use
|
|
162
191
|
const isAvailable = web3.isAvailable && isAuthenticated && !!accountAddress;
|
|
@@ -293,6 +322,35 @@ export function useTokenBalances(options: UseTokenBalancesOptions): UseTokenBala
|
|
|
293
322
|
};
|
|
294
323
|
}, [sdk, refreshInterval, isAvailable, loadBalances]);
|
|
295
324
|
|
|
325
|
+
// Wallet events refresh: listen for real-time blockchain events
|
|
326
|
+
// Refreshes on ANY wallet domain event (Transfer, TransferSingle, etc.)
|
|
327
|
+
// Debouncing prevents excessive API calls during rapid events
|
|
328
|
+
useEffect(() => {
|
|
329
|
+
if (!sdk || !refreshOnWalletEvents || !isAvailable) return;
|
|
330
|
+
|
|
331
|
+
const unsubscribe = sdk.events.subscribe((event) => {
|
|
332
|
+
if (event.domain === 'wallet') {
|
|
333
|
+
// Debounce rapid events (batch transfers, etc.)
|
|
334
|
+
if (walletEventDebounceRef.current) {
|
|
335
|
+
clearTimeout(walletEventDebounceRef.current);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
walletEventDebounceRef.current = setTimeout(() => {
|
|
339
|
+
console.log(`[useTokenBalances] Wallet event (${event.type}), refreshing balances...`);
|
|
340
|
+
loadBalances();
|
|
341
|
+
walletEventDebounceRef.current = null;
|
|
342
|
+
}, walletEventDebounceMs);
|
|
343
|
+
}
|
|
344
|
+
}, { domains: ['wallet'] });
|
|
345
|
+
|
|
346
|
+
return () => {
|
|
347
|
+
unsubscribe();
|
|
348
|
+
if (walletEventDebounceRef.current) {
|
|
349
|
+
clearTimeout(walletEventDebounceRef.current);
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
}, [sdk, refreshOnWalletEvents, walletEventDebounceMs, isAvailable, loadBalances]);
|
|
353
|
+
|
|
296
354
|
return {
|
|
297
355
|
tokenBalances,
|
|
298
356
|
isLoading,
|