@explorins/pers-sdk-react-native 1.5.36 → 2.0.1
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 +329 -0
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/useBusiness.d.ts +5 -5
- package/dist/hooks/useBusiness.d.ts.map +1 -1
- package/dist/hooks/useCampaigns.d.ts +8 -8
- package/dist/hooks/useCampaigns.d.ts.map +1 -1
- package/dist/hooks/useCampaigns.js +3 -3
- package/dist/hooks/useDonations.d.ts +2 -2
- package/dist/hooks/useDonations.d.ts.map +1 -1
- package/dist/hooks/useEvents.d.ts +178 -0
- package/dist/hooks/useEvents.d.ts.map +1 -0
- package/dist/hooks/useEvents.js +312 -0
- package/dist/hooks/usePurchases.d.ts +3 -3
- package/dist/hooks/usePurchases.d.ts.map +1 -1
- package/dist/hooks/useRedemptions.d.ts +6 -5
- package/dist/hooks/useRedemptions.d.ts.map +1 -1
- package/dist/hooks/useRedemptions.js +13 -19
- package/dist/hooks/useTenants.d.ts +2 -2
- package/dist/hooks/useTenants.d.ts.map +1 -1
- package/dist/hooks/useTokens.d.ts +4 -4
- package/dist/hooks/useTokens.d.ts.map +1 -1
- package/dist/hooks/useTransactionSigner.d.ts +2 -0
- package/dist/hooks/useTransactionSigner.d.ts.map +1 -1
- package/dist/hooks/useTransactionSigner.js +68 -0
- package/dist/hooks/useTransactions.d.ts +3 -3
- package/dist/hooks/useTransactions.d.ts.map +1 -1
- package/dist/hooks/useUserStatus.d.ts +3 -3
- package/dist/hooks/useUserStatus.d.ts.map +1 -1
- package/dist/hooks/useUsers.d.ts +3 -3
- package/dist/hooks/useUsers.d.ts.map +1 -1
- package/dist/index.d.ts +83 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2986 -653
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/hooks/index.ts +3 -1
- package/src/hooks/useBusiness.ts +5 -5
- package/src/hooks/useCampaigns.ts +12 -11
- package/src/hooks/useDonations.ts +2 -2
- package/src/hooks/useEvents.ts +360 -0
- package/src/hooks/usePurchases.ts +3 -3
- package/src/hooks/useRedemptions.ts +16 -22
- package/src/hooks/useTenants.ts +2 -2
- package/src/hooks/useTokens.ts +4 -4
- package/src/hooks/useTransactionSigner.ts +73 -0
- package/src/hooks/useTransactions.ts +4 -3
- package/src/hooks/useUserStatus.ts +3 -3
- package/src/hooks/useUsers.ts +3 -3
- package/src/index.ts +105 -2
package/README.md
CHANGED
|
@@ -331,8 +331,189 @@ const {
|
|
|
331
331
|
const {
|
|
332
332
|
getDonationTypes
|
|
333
333
|
} = useDonations();
|
|
334
|
+
|
|
335
|
+
// Event subscriptions (notifications, logging)
|
|
336
|
+
const {
|
|
337
|
+
subscribe, // Subscribe to SDK events
|
|
338
|
+
once, // One-time event listener
|
|
339
|
+
clear, // Clear all subscriptions
|
|
340
|
+
isAvailable, // Event system available
|
|
341
|
+
subscriberCount // Active subscriber count
|
|
342
|
+
} = useEvents();
|
|
334
343
|
```
|
|
335
344
|
|
|
345
|
+
## Event System
|
|
346
|
+
|
|
347
|
+
The `useEvents` hook provides access to SDK-wide events for showing notifications, logging, and reacting to SDK operations. All events include a `userMessage` field ready for UI display.
|
|
348
|
+
|
|
349
|
+
### Basic Usage
|
|
350
|
+
|
|
351
|
+
```typescript
|
|
352
|
+
import { useEvents } from '@explorins/pers-sdk-react-native';
|
|
353
|
+
import { useEffect } from 'react';
|
|
354
|
+
|
|
355
|
+
function NotificationHandler() {
|
|
356
|
+
const { subscribe, isAvailable } = useEvents();
|
|
357
|
+
|
|
358
|
+
useEffect(() => {
|
|
359
|
+
if (!isAvailable) return;
|
|
360
|
+
|
|
361
|
+
// Subscribe to all events
|
|
362
|
+
const unsubscribe = subscribe((event) => {
|
|
363
|
+
showNotification(event.userMessage, event.level);
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
return () => unsubscribe(); // Cleanup on unmount
|
|
367
|
+
}, [subscribe, isAvailable]);
|
|
368
|
+
}
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
### Filtering Events
|
|
372
|
+
|
|
373
|
+
```typescript
|
|
374
|
+
// Only transaction successes
|
|
375
|
+
subscribe(
|
|
376
|
+
(event) => {
|
|
377
|
+
playSuccessSound();
|
|
378
|
+
showConfetti();
|
|
379
|
+
},
|
|
380
|
+
{ domain: 'transaction', level: 'success' }
|
|
381
|
+
);
|
|
382
|
+
|
|
383
|
+
// Only errors (for logging)
|
|
384
|
+
subscribe(
|
|
385
|
+
(event) => {
|
|
386
|
+
logToSentry(event);
|
|
387
|
+
},
|
|
388
|
+
{ level: 'error' }
|
|
389
|
+
);
|
|
390
|
+
|
|
391
|
+
// One-time event (auto-unsubscribes)
|
|
392
|
+
once(
|
|
393
|
+
(event) => {
|
|
394
|
+
console.log('First transaction completed!');
|
|
395
|
+
},
|
|
396
|
+
{ domain: 'transaction', level: 'success' }
|
|
397
|
+
);
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
### Event Domains
|
|
401
|
+
|
|
402
|
+
| Domain | Events |
|
|
403
|
+
|--------|--------|
|
|
404
|
+
| `auth` | Login, logout, token refresh |
|
|
405
|
+
| `user` | Profile updates |
|
|
406
|
+
| `transaction` | Created, completed, failed |
|
|
407
|
+
| `campaign` | Claimed, activated |
|
|
408
|
+
| `redemption` | Redeemed, expired |
|
|
409
|
+
| `business` | Created, updated, membership |
|
|
410
|
+
| `api` | Network errors, validation errors |
|
|
411
|
+
|
|
412
|
+
### Event Structure
|
|
413
|
+
|
|
414
|
+
```typescript
|
|
415
|
+
interface PersEvent {
|
|
416
|
+
id: string; // Unique event ID
|
|
417
|
+
timestamp: number; // Unix timestamp (ms)
|
|
418
|
+
domain: string; // Event domain (transaction, auth, etc.)
|
|
419
|
+
type: string; // Event type within domain
|
|
420
|
+
level: 'success' | 'error';
|
|
421
|
+
userMessage: string; // Ready for UI display
|
|
422
|
+
action?: string; // Suggested action
|
|
423
|
+
details?: object; // Additional data
|
|
424
|
+
}
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
---
|
|
428
|
+
|
|
429
|
+
## POS Transaction Flow
|
|
430
|
+
|
|
431
|
+
For Point-of-Sale scenarios where a **business submits a transaction on behalf of a user**, use the `buildPOSTransferRequest` helper:
|
|
432
|
+
|
|
433
|
+
```typescript
|
|
434
|
+
import {
|
|
435
|
+
useTransactions,
|
|
436
|
+
buildPOSTransferRequest,
|
|
437
|
+
useEvents
|
|
438
|
+
} from '@explorins/pers-sdk-react-native';
|
|
439
|
+
|
|
440
|
+
function POSScreen() {
|
|
441
|
+
const { createTransaction, signingStatus } = useTransactions();
|
|
442
|
+
const { subscribe, isAvailable } = useEvents();
|
|
443
|
+
|
|
444
|
+
// Listen for transaction events
|
|
445
|
+
useEffect(() => {
|
|
446
|
+
if (!isAvailable) return;
|
|
447
|
+
|
|
448
|
+
const unsubscribe = subscribe(
|
|
449
|
+
(event) => {
|
|
450
|
+
if (event.level === 'success') {
|
|
451
|
+
Alert.alert('Success', event.userMessage);
|
|
452
|
+
}
|
|
453
|
+
},
|
|
454
|
+
{ domain: 'transaction' }
|
|
455
|
+
);
|
|
456
|
+
|
|
457
|
+
return () => unsubscribe();
|
|
458
|
+
}, [subscribe, isAvailable]);
|
|
459
|
+
|
|
460
|
+
const handlePOSTransaction = async (
|
|
461
|
+
userAccountId: string,
|
|
462
|
+
businessAccountId: string,
|
|
463
|
+
amount: number,
|
|
464
|
+
token: TokenDTO
|
|
465
|
+
) => {
|
|
466
|
+
// Build POS transfer request
|
|
467
|
+
const request = buildPOSTransferRequest({
|
|
468
|
+
amount,
|
|
469
|
+
contractAddress: token.contractAddress,
|
|
470
|
+
chainId: token.chainId,
|
|
471
|
+
userAccountId, // User sending tokens
|
|
472
|
+
businessAccountId // Business receiving & authorized to submit
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
// Create and sign transaction
|
|
476
|
+
const result = await createTransaction(request, (status, message) => {
|
|
477
|
+
console.log(`Signing: ${status} - ${message}`);
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
console.log('Transaction created:', result.transaction?.id);
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
### POS Authorization Fields
|
|
486
|
+
|
|
487
|
+
The `buildPOSTransferRequest` helper automatically sets:
|
|
488
|
+
|
|
489
|
+
| Field | Value | Purpose |
|
|
490
|
+
|-------|-------|---------|
|
|
491
|
+
| `engagedBusinessId` | Business ID | Business commercially involved (for reporting) |
|
|
492
|
+
| `authorizedSubmitterId` | Business ID | Entity authorized to submit the signed tx |
|
|
493
|
+
| `authorizedSubmitterType` | `BUSINESS` | Type of authorized submitter |
|
|
494
|
+
|
|
495
|
+
For custom scenarios, use `buildTransferRequest` with manual POS fields:
|
|
496
|
+
|
|
497
|
+
```typescript
|
|
498
|
+
import { buildTransferRequest, AccountOwnerType } from '@explorins/pers-sdk-react-native';
|
|
499
|
+
|
|
500
|
+
const request = buildTransferRequest({
|
|
501
|
+
amount: 100,
|
|
502
|
+
contractAddress: '0x...',
|
|
503
|
+
chainId: 137,
|
|
504
|
+
senderAccountId: 'user-123',
|
|
505
|
+
senderAccountType: AccountOwnerType.USER,
|
|
506
|
+
recipientAccountId: 'business-456',
|
|
507
|
+
recipientAccountType: AccountOwnerType.BUSINESS,
|
|
508
|
+
// POS authorization
|
|
509
|
+
engagedBusinessId: 'business-456',
|
|
510
|
+
authorizedSubmitterId: 'business-456',
|
|
511
|
+
authorizedSubmitterType: AccountOwnerType.BUSINESS
|
|
512
|
+
});
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
---
|
|
516
|
+
|
|
336
517
|
## Token Collection Helper Methods
|
|
337
518
|
|
|
338
519
|
The `useWeb3` hook includes helper methods for querying token balances from any blockchain address. These work with **all token standards** (ERC-20, ERC-721, ERC-1155).
|
|
@@ -598,6 +779,154 @@ function AnalyticsExample() {
|
|
|
598
779
|
}
|
|
599
780
|
```
|
|
600
781
|
|
|
782
|
+
## Migration Guide
|
|
783
|
+
|
|
784
|
+
### v2.0.0 Breaking Changes - Pagination
|
|
785
|
+
|
|
786
|
+
**Version 2.0.0 introduces standardized pagination** across all hooks that return lists. Previously, hooks returned raw arrays. Now they return `PaginatedResponseDTO<T>` with pagination metadata.
|
|
787
|
+
|
|
788
|
+
#### What Changed
|
|
789
|
+
|
|
790
|
+
All hooks returning lists now return paginated responses:
|
|
791
|
+
|
|
792
|
+
```typescript
|
|
793
|
+
// ❌ OLD (v1.x) - Direct array
|
|
794
|
+
const businesses: BusinessDTO[] = await getActiveBusinesses();
|
|
795
|
+
|
|
796
|
+
// ✅ NEW (v2.x) - Paginated response
|
|
797
|
+
const response: PaginatedResponseDTO<BusinessDTO> = await getActiveBusinesses();
|
|
798
|
+
const businesses: BusinessDTO[] = response.data;
|
|
799
|
+
```
|
|
800
|
+
|
|
801
|
+
#### Affected Hooks
|
|
802
|
+
|
|
803
|
+
| Hook | Method | Return Type |
|
|
804
|
+
|------|--------|-------------|
|
|
805
|
+
| `useBusiness` | `getActiveBusinesses()` | `PaginatedResponseDTO<BusinessDTO>` |
|
|
806
|
+
| `useBusiness` | `getBusinessTypes()` | `PaginatedResponseDTO<BusinessTypeDTO>` |
|
|
807
|
+
| `useCampaigns` | `getActiveCampaigns()` | `PaginatedResponseDTO<CampaignDTO>` |
|
|
808
|
+
| `useCampaigns` | `getUserClaims()` | `PaginatedResponseDTO<CampaignClaimDTO>` |
|
|
809
|
+
| `useTokens` | `getTokens()` | `PaginatedResponseDTO<TokenDTO>` |
|
|
810
|
+
| `useTokens` | `getRewardTokens()` | `PaginatedResponseDTO<TokenDTO>` |
|
|
811
|
+
| `useTokens` | `getStatusTokens()` | `PaginatedResponseDTO<TokenDTO>` |
|
|
812
|
+
| `useRedemptions` | `getActiveRedemptions()` | `PaginatedResponseDTO<RedemptionDTO>` |
|
|
813
|
+
| `useRedemptions` | `getUserRedemptions()` | `PaginatedResponseDTO<RedemptionDTO>` |
|
|
814
|
+
| `useTransactions` | `getUserTransactionHistory()` | `PaginatedResponseDTO<TransactionDTO>` |
|
|
815
|
+
| `usePurchases` | `getAllUserPurchases()` | `PaginatedResponseDTO<PurchaseDTO>` |
|
|
816
|
+
| `usePurchases` | `getActivePurchaseTokens()` | `PaginatedResponseDTO<PurchaseTokenDTO>` |
|
|
817
|
+
| `useDonations` | `getDonationTypes()` | `PaginatedResponseDTO<DonationTypeDTO>` |
|
|
818
|
+
|
|
819
|
+
#### PaginatedResponseDTO Structure
|
|
820
|
+
|
|
821
|
+
```typescript
|
|
822
|
+
import type { PaginatedResponseDTO } from '@explorins/pers-shared';
|
|
823
|
+
|
|
824
|
+
interface PaginatedResponseDTO<T> {
|
|
825
|
+
data: T[]; // Array of results
|
|
826
|
+
pagination: {
|
|
827
|
+
currentPage: number; // Current page number (1-indexed)
|
|
828
|
+
pageSize: number; // Items per page
|
|
829
|
+
totalItems: number; // Total number of items across all pages
|
|
830
|
+
totalPages: number; // Total number of pages
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
```
|
|
834
|
+
|
|
835
|
+
#### Migration Examples
|
|
836
|
+
|
|
837
|
+
**Before (v1.x):**
|
|
838
|
+
```typescript
|
|
839
|
+
const businesses = await getActiveBusinesses();
|
|
840
|
+
console.log('Business count:', businesses.length);
|
|
841
|
+
businesses.forEach(b => console.log(b.name));
|
|
842
|
+
```
|
|
843
|
+
|
|
844
|
+
**After (v2.x):**
|
|
845
|
+
```typescript
|
|
846
|
+
const response = await getActiveBusinesses();
|
|
847
|
+
console.log('Business count:', response.data.length);
|
|
848
|
+
console.log('Total businesses:', response.pagination.totalItems);
|
|
849
|
+
response.data.forEach(b => console.log(b.name));
|
|
850
|
+
```
|
|
851
|
+
|
|
852
|
+
**With Pagination Parameters:**
|
|
853
|
+
```typescript
|
|
854
|
+
// Fetch page 2 with 20 items per page
|
|
855
|
+
const response = await getActiveBusinesses({ page: 2, pageSize: 20 });
|
|
856
|
+
|
|
857
|
+
console.log(`Page ${response.pagination.currentPage} of ${response.pagination.totalPages}`);
|
|
858
|
+
console.log(`Showing ${response.data.length} businesses`);
|
|
859
|
+
```
|
|
860
|
+
|
|
861
|
+
#### React Native Component Example
|
|
862
|
+
|
|
863
|
+
```typescript
|
|
864
|
+
import { useState, useEffect } from 'react';
|
|
865
|
+
import { useBusiness } from '@explorins/pers-sdk-react-native';
|
|
866
|
+
import { View, Text, FlatList, ActivityIndicator } from 'react-native';
|
|
867
|
+
|
|
868
|
+
function BusinessListScreen() {
|
|
869
|
+
const { getActiveBusinesses } = useBusiness();
|
|
870
|
+
const [businesses, setBusinesses] = useState<BusinessDTO[]>([]);
|
|
871
|
+
const [pagination, setPagination] = useState(null);
|
|
872
|
+
const [loading, setLoading] = useState(true);
|
|
873
|
+
|
|
874
|
+
useEffect(() => {
|
|
875
|
+
loadBusinesses();
|
|
876
|
+
}, []);
|
|
877
|
+
|
|
878
|
+
const loadBusinesses = async (page = 1) => {
|
|
879
|
+
try {
|
|
880
|
+
setLoading(true);
|
|
881
|
+
const response = await getActiveBusinesses({ page, pageSize: 20 });
|
|
882
|
+
setBusinesses(response.data);
|
|
883
|
+
setPagination(response.pagination);
|
|
884
|
+
} catch (error) {
|
|
885
|
+
console.error('Failed to load businesses:', error);
|
|
886
|
+
} finally {
|
|
887
|
+
setLoading(false);
|
|
888
|
+
}
|
|
889
|
+
};
|
|
890
|
+
|
|
891
|
+
if (loading) return <ActivityIndicator />;
|
|
892
|
+
|
|
893
|
+
return (
|
|
894
|
+
<View>
|
|
895
|
+
<Text>
|
|
896
|
+
Showing {businesses.length} of {pagination?.totalItems} businesses
|
|
897
|
+
</Text>
|
|
898
|
+
<FlatList
|
|
899
|
+
data={businesses}
|
|
900
|
+
keyExtractor={(item) => item.id}
|
|
901
|
+
renderItem={({ item }) => <BusinessCard business={item} />}
|
|
902
|
+
/>
|
|
903
|
+
<Text>Page {pagination?.currentPage} of {pagination?.totalPages}</Text>
|
|
904
|
+
</View>
|
|
905
|
+
);
|
|
906
|
+
}
|
|
907
|
+
```
|
|
908
|
+
|
|
909
|
+
#### Quick Fix for Existing Code
|
|
910
|
+
|
|
911
|
+
If you want minimal code changes, extract `.data` immediately:
|
|
912
|
+
|
|
913
|
+
```typescript
|
|
914
|
+
// Quick adaptation in your hooks
|
|
915
|
+
const businesses = (await getActiveBusinesses()).data;
|
|
916
|
+
const campaigns = (await getActiveCampaigns()).data;
|
|
917
|
+
const tokens = (await getTokens()).data;
|
|
918
|
+
```
|
|
919
|
+
|
|
920
|
+
#### Benefits of Pagination
|
|
921
|
+
|
|
922
|
+
- **Performance**: Load only what you need, not entire datasets
|
|
923
|
+
- **Consistency**: All list endpoints follow the same pattern
|
|
924
|
+
- **Metadata**: Access total counts without loading all items
|
|
925
|
+
- **Better UX**: Build proper pagination UI components
|
|
926
|
+
- **Memory Efficiency**: Reduced memory footprint for large datasets
|
|
927
|
+
|
|
928
|
+
---
|
|
929
|
+
|
|
601
930
|
## Contributing
|
|
602
931
|
|
|
603
932
|
We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
|
package/dist/hooks/index.d.ts
CHANGED
|
@@ -13,7 +13,9 @@ export { useUserStatus } from './useUserStatus';
|
|
|
13
13
|
export { useFiles } from './useFiles';
|
|
14
14
|
export { useAnalytics } from './useAnalytics';
|
|
15
15
|
export { useDonations } from './useDonations';
|
|
16
|
+
export { useEvents } from './useEvents';
|
|
16
17
|
export type { RawUserData } from './useAuth';
|
|
17
18
|
export type { TransactionSignerHook, SubmissionResult, AuthenticatedUser, TransactionSigningResult, StatusUpdateData, OnStatusUpdateFn, SigningStatus as SigningStatusType } from './useTransactionSigner';
|
|
18
19
|
export type { AccountOwnedTokensResult, Web3Hook } from './useWeb3';
|
|
20
|
+
export type { EventsHook, PersEvent, EventHandler, EventFilter, Unsubscribe } from './useEvents';
|
|
19
21
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC7E,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC7E,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAGxC,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC7C,YAAY,EACV,qBAAqB,EACrB,gBAAgB,EAChB,iBAAiB,EACjB,wBAAwB,EACxB,gBAAgB,EAChB,gBAAgB,EAChB,aAAa,IAAI,iBAAiB,EACnC,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,wBAAwB,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACpE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/hooks/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BusinessDTO, BusinessTypeDTO, BusinessUpdateRequestDTO, BusinessToggleActiveRequestDTO } from '@explorins/pers-shared';
|
|
1
|
+
import type { BusinessDTO, BusinessTypeDTO, BusinessUpdateRequestDTO, BusinessToggleActiveRequestDTO, PaginatedResponseDTO } from '@explorins/pers-shared';
|
|
2
2
|
/**
|
|
3
3
|
* React hook for business operations in the PERS SDK
|
|
4
4
|
*
|
|
@@ -34,12 +34,12 @@ import type { BusinessDTO, BusinessTypeDTO, BusinessUpdateRequestDTO, BusinessTo
|
|
|
34
34
|
* ```
|
|
35
35
|
*/
|
|
36
36
|
export declare const useBusiness: () => {
|
|
37
|
-
getActiveBusinesses: () => Promise<BusinessDTO
|
|
38
|
-
getBusinessTypes: () => Promise<BusinessTypeDTO
|
|
39
|
-
getBusinesses: () => Promise<BusinessDTO
|
|
37
|
+
getActiveBusinesses: () => Promise<PaginatedResponseDTO<BusinessDTO>>;
|
|
38
|
+
getBusinessTypes: () => Promise<PaginatedResponseDTO<BusinessTypeDTO>>;
|
|
39
|
+
getBusinesses: () => Promise<PaginatedResponseDTO<BusinessDTO>>;
|
|
40
40
|
getBusinessById: (businessId: string) => Promise<BusinessDTO | null>;
|
|
41
41
|
getBusinessByAccount: (accountAddress: string) => Promise<BusinessDTO>;
|
|
42
|
-
getBusinessesByType: (typeId: string) => Promise<BusinessDTO
|
|
42
|
+
getBusinessesByType: (typeId: string) => Promise<PaginatedResponseDTO<BusinessDTO>>;
|
|
43
43
|
createBusiness: (displayName: string) => Promise<BusinessDTO>;
|
|
44
44
|
updateBusiness: (businessId: string, businessData: BusinessUpdateRequestDTO) => Promise<BusinessDTO>;
|
|
45
45
|
toggleBusinessStatus: (businessId: string, toggleData: BusinessToggleActiveRequestDTO) => Promise<BusinessDTO>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useBusiness.d.ts","sourceRoot":"","sources":["../../src/hooks/useBusiness.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,wBAAwB,EAAE,8BAA8B,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"useBusiness.d.ts","sourceRoot":"","sources":["../../src/hooks/useBusiness.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,wBAAwB,EAAE,8BAA8B,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAE3J;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,eAAO,MAAM,WAAW;+BAgB4B,QAAQ,qBAAqB,WAAW,CAAC,CAAC;4BA2B7C,QAAQ,qBAAqB,eAAe,CAAC,CAAC;yBA4BjD,QAAQ,qBAAqB,WAAW,CAAC,CAAC;kCAd/B,MAAM,KAAG,QAAQ,WAAW,GAAG,IAAI,CAAC;2CA4B3B,MAAM,KAAG,QAAQ,WAAW,CAAC;kCActC,MAAM,KAAG,QAAQ,qBAAqB,WAAW,CAAC,CAAC;kCA6BnD,MAAM,KAAG,QAAQ,WAAW,CAAC;iCAc9B,MAAM,gBAAgB,wBAAwB,KAAG,QAAQ,WAAW,CAAC;uCAc/D,MAAM,cAAc,8BAA8B,KAAG,QAAQ,WAAW,CAAC;;CA0BtI,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC"}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import type { CampaignClaimRequestDTO, CampaignDTO, CampaignClaimDTO, CampaignTriggerDTO } from '@explorins/pers-shared';
|
|
1
|
+
import type { CampaignClaimRequestDTO, CampaignDTO, CampaignClaimDTO, CampaignTriggerDTO, PaginatedResponseDTO } from '@explorins/pers-shared';
|
|
2
2
|
export declare const useCampaigns: () => {
|
|
3
|
-
getActiveCampaigns: () => Promise<CampaignDTO
|
|
3
|
+
getActiveCampaigns: () => Promise<PaginatedResponseDTO<CampaignDTO>>;
|
|
4
4
|
getCampaignById: (campaignId: string) => Promise<CampaignDTO | null>;
|
|
5
5
|
claimCampaign: (request: CampaignClaimRequestDTO) => Promise<CampaignClaimDTO | null>;
|
|
6
|
-
getUserClaims: () => Promise<CampaignClaimDTO
|
|
7
|
-
getCampaignTriggers: () => Promise<CampaignTriggerDTO
|
|
8
|
-
getAllCampaigns: (active?: boolean) => Promise<CampaignDTO
|
|
9
|
-
getCampaignClaims: () => Promise<CampaignClaimDTO
|
|
10
|
-
getCampaignClaimsByUserId: (userId: string) => Promise<CampaignClaimDTO
|
|
11
|
-
getCampaignClaimsByBusinessId: (businessId: string) => Promise<CampaignClaimDTO
|
|
6
|
+
getUserClaims: () => Promise<PaginatedResponseDTO<CampaignClaimDTO>>;
|
|
7
|
+
getCampaignTriggers: () => Promise<PaginatedResponseDTO<CampaignTriggerDTO>>;
|
|
8
|
+
getAllCampaigns: (active?: boolean) => Promise<PaginatedResponseDTO<CampaignDTO>>;
|
|
9
|
+
getCampaignClaims: () => Promise<PaginatedResponseDTO<CampaignClaimDTO>>;
|
|
10
|
+
getCampaignClaimsByUserId: (userId: string) => Promise<PaginatedResponseDTO<CampaignClaimDTO>>;
|
|
11
|
+
getCampaignClaimsByBusinessId: (businessId: string) => Promise<PaginatedResponseDTO<CampaignClaimDTO>>;
|
|
12
12
|
isAvailable: boolean;
|
|
13
13
|
};
|
|
14
14
|
export type CampaignHook = ReturnType<typeof useCampaigns>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCampaigns.d.ts","sourceRoot":"","sources":["../../src/hooks/useCampaigns.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,uBAAuB,EACvB,WAAW,EACX,gBAAgB,EAChB,kBAAkB,
|
|
1
|
+
{"version":3,"file":"useCampaigns.d.ts","sourceRoot":"","sources":["../../src/hooks/useCampaigns.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,uBAAuB,EACvB,WAAW,EACX,gBAAgB,EAChB,kBAAkB,EAIlB,oBAAoB,EACrB,MAAM,wBAAwB,CAAC;AAEhC,eAAO,MAAM,YAAY;8BAG0B,QAAQ,qBAAqB,WAAW,CAAC,CAAC;kCAcpC,MAAM,KAAG,QAAQ,WAAW,GAAG,IAAI,CAAC;6BAczC,uBAAuB,KAAG,QAAQ,gBAAgB,GAAG,IAAI,CAAC;yBAiBhE,QAAQ,qBAAqB,gBAAgB,CAAC,CAAC;+BAkBzC,QAAQ,qBAAqB,kBAAkB,CAAC,CAAC;+BAe/C,OAAO,KAAG,QAAQ,qBAAqB,WAAW,CAAC,CAAC;6BAcxD,QAAQ,qBAAqB,gBAAgB,CAAC,CAAC;wCAclC,MAAM,KAAG,QAAQ,qBAAqB,gBAAgB,CAAC,CAAC;gDAchD,MAAM,KAAG,QAAQ,qBAAqB,gBAAgB,CAAC,CAAC;;CA0B9H,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC"}
|
|
@@ -7,7 +7,7 @@ export const useCampaigns = () => {
|
|
|
7
7
|
throw new Error('SDK not initialized. Call initialize() first.');
|
|
8
8
|
}
|
|
9
9
|
try {
|
|
10
|
-
const result = await sdk.campaigns.
|
|
10
|
+
const result = await sdk.campaigns.getCampaigns({ active: true });
|
|
11
11
|
return result;
|
|
12
12
|
}
|
|
13
13
|
catch (error) {
|
|
@@ -50,7 +50,7 @@ export const useCampaigns = () => {
|
|
|
50
50
|
}
|
|
51
51
|
if (!isAuthenticated) {
|
|
52
52
|
console.warn('SDK not authenticated. getUserClaims requires authentication.');
|
|
53
|
-
return [];
|
|
53
|
+
return { data: [], pagination: { page: 1, limit: 0, total: 0, pages: 0, hasNext: false, hasPrev: false } };
|
|
54
54
|
}
|
|
55
55
|
try {
|
|
56
56
|
const result = await sdk.campaigns.getUserClaims();
|
|
@@ -80,7 +80,7 @@ export const useCampaigns = () => {
|
|
|
80
80
|
throw new Error('SDK not initialized. Call initialize() first.');
|
|
81
81
|
}
|
|
82
82
|
try {
|
|
83
|
-
const result = await sdk.campaigns.
|
|
83
|
+
const result = await sdk.campaigns.getCampaigns({ active });
|
|
84
84
|
return result;
|
|
85
85
|
}
|
|
86
86
|
catch (error) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DonationTypeDTO } from '@explorins/pers-shared';
|
|
1
|
+
import type { DonationTypeDTO, PaginatedResponseDTO } from '@explorins/pers-shared';
|
|
2
2
|
/**
|
|
3
3
|
* React hook for donation operations in the PERS SDK
|
|
4
4
|
*
|
|
@@ -26,7 +26,7 @@ import type { DonationTypeDTO } from '@explorins/pers-shared';
|
|
|
26
26
|
* ```
|
|
27
27
|
*/
|
|
28
28
|
export declare const useDonations: () => {
|
|
29
|
-
getDonationTypes: () => Promise<DonationTypeDTO
|
|
29
|
+
getDonationTypes: () => Promise<PaginatedResponseDTO<DonationTypeDTO>>;
|
|
30
30
|
isAvailable: boolean;
|
|
31
31
|
};
|
|
32
32
|
export type DonationHook = ReturnType<typeof useDonations>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useDonations.d.ts","sourceRoot":"","sources":["../../src/hooks/useDonations.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"useDonations.d.ts","sourceRoot":"","sources":["../../src/hooks/useDonations.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAEpF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,YAAY;4BAgBwB,QAAQ,qBAAqB,eAAe,CAAC,CAAC;;CAkB9F,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import type { PersEvent, EventHandler, EventFilter, Unsubscribe } from '@explorins/pers-sdk/core';
|
|
2
|
+
export type { PersEvent, EventHandler, EventFilter, Unsubscribe };
|
|
3
|
+
/**
|
|
4
|
+
* Return interface for the useEvents hook
|
|
5
|
+
*
|
|
6
|
+
* Provides event subscription capabilities for the PERS SDK event system.
|
|
7
|
+
* Use this to display notifications, update UI, or trigger side effects.
|
|
8
|
+
*
|
|
9
|
+
* @interface EventsHook
|
|
10
|
+
* @property {Function} subscribe - Subscribe to SDK events with optional filtering
|
|
11
|
+
* @property {Function} once - Subscribe to a single event (auto-unsubscribes)
|
|
12
|
+
* @property {Function} clear - Clear all subscriptions
|
|
13
|
+
* @property {boolean} isAvailable - Whether the event system is available
|
|
14
|
+
* @property {number} subscriberCount - Current number of active subscribers
|
|
15
|
+
*/
|
|
16
|
+
export interface EventsHook {
|
|
17
|
+
subscribe: (handler: EventHandler, filter?: EventFilter) => Unsubscribe;
|
|
18
|
+
once: (handler: EventHandler, filter?: EventFilter) => Unsubscribe;
|
|
19
|
+
clear: () => void;
|
|
20
|
+
isAvailable: boolean;
|
|
21
|
+
subscriberCount: number;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* React Native hook for PERS SDK event system
|
|
25
|
+
*
|
|
26
|
+
* This hook provides access to the platform-agnostic event system for subscribing to
|
|
27
|
+
* transaction, authentication, campaign, and system events. All events include a
|
|
28
|
+
* `userMessage` field ready for display to end users.
|
|
29
|
+
*
|
|
30
|
+
* **Event Domains:**
|
|
31
|
+
* - `auth` - Authentication events (login, logout, token refresh)
|
|
32
|
+
* - `user` - User profile events (update, create)
|
|
33
|
+
* - `transaction` - Transaction events (created, completed, failed)
|
|
34
|
+
* - `campaign` - Campaign events (claimed, activated)
|
|
35
|
+
* - `redemption` - Redemption events (redeemed, expired)
|
|
36
|
+
* - `business` - Business events (created, updated, membership)
|
|
37
|
+
* - `api` - API error events (network, validation, server errors)
|
|
38
|
+
*
|
|
39
|
+
* **Notification Levels:**
|
|
40
|
+
* - `success` - Operation completed successfully
|
|
41
|
+
* - `error` - Operation failed
|
|
42
|
+
* - `warning` - Operation completed with warnings
|
|
43
|
+
* - `info` - Informational event
|
|
44
|
+
*
|
|
45
|
+
* **Cleanup:**
|
|
46
|
+
* All subscriptions created through this hook are automatically cleaned up when
|
|
47
|
+
* the component unmounts, preventing memory leaks and stale event handlers.
|
|
48
|
+
*
|
|
49
|
+
* @returns {EventsHook} Hook interface with event subscription methods
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* **Basic Usage - Show All Notifications**
|
|
53
|
+
* ```typescript
|
|
54
|
+
* import { useEvents } from '@explorins/pers-sdk-react-native';
|
|
55
|
+
*
|
|
56
|
+
* function NotificationComponent() {
|
|
57
|
+
* const { subscribe, isAvailable } = useEvents();
|
|
58
|
+
*
|
|
59
|
+
* useEffect(() => {
|
|
60
|
+
* if (!isAvailable) return;
|
|
61
|
+
*
|
|
62
|
+
* const unsubscribe = subscribe((event) => {
|
|
63
|
+
* // userMessage is always present and UI-ready
|
|
64
|
+
* showNotification(event.userMessage, event.level);
|
|
65
|
+
* });
|
|
66
|
+
*
|
|
67
|
+
* return () => unsubscribe();
|
|
68
|
+
* }, [subscribe, isAvailable]);
|
|
69
|
+
*
|
|
70
|
+
* return <View />;
|
|
71
|
+
* }
|
|
72
|
+
* ```
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* **Filter by Domain and Level**
|
|
76
|
+
* ```typescript
|
|
77
|
+
* function TransactionListener() {
|
|
78
|
+
* const { subscribe, isAvailable } = useEvents();
|
|
79
|
+
*
|
|
80
|
+
* useEffect(() => {
|
|
81
|
+
* if (!isAvailable) return;
|
|
82
|
+
*
|
|
83
|
+
* // Only listen to successful transactions
|
|
84
|
+
* const unsubscribe = subscribe(
|
|
85
|
+
* (event) => {
|
|
86
|
+
* if (event.level === 'success') {
|
|
87
|
+
* playSuccessSound();
|
|
88
|
+
* showConfetti();
|
|
89
|
+
* }
|
|
90
|
+
* },
|
|
91
|
+
* { domain: 'transaction' } // Filter
|
|
92
|
+
* );
|
|
93
|
+
*
|
|
94
|
+
* return () => unsubscribe();
|
|
95
|
+
* }, [subscribe, isAvailable]);
|
|
96
|
+
* }
|
|
97
|
+
* ```
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* **Filter Errors for Logging**
|
|
101
|
+
* ```typescript
|
|
102
|
+
* function ErrorLogger() {
|
|
103
|
+
* const { subscribe, isAvailable } = useEvents();
|
|
104
|
+
*
|
|
105
|
+
* useEffect(() => {
|
|
106
|
+
* if (!isAvailable) return;
|
|
107
|
+
*
|
|
108
|
+
* const unsubscribe = subscribe(
|
|
109
|
+
* (event) => {
|
|
110
|
+
* // Log errors to crash reporting
|
|
111
|
+
* logToSentry({
|
|
112
|
+
* message: event.userMessage,
|
|
113
|
+
* domain: event.domain,
|
|
114
|
+
* type: event.type,
|
|
115
|
+
* });
|
|
116
|
+
* },
|
|
117
|
+
* { level: 'error' } // Only errors
|
|
118
|
+
* );
|
|
119
|
+
*
|
|
120
|
+
* return () => unsubscribe();
|
|
121
|
+
* }, [subscribe, isAvailable]);
|
|
122
|
+
* }
|
|
123
|
+
* ```
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* **One-time Event**
|
|
127
|
+
* ```typescript
|
|
128
|
+
* function FirstTransactionHandler() {
|
|
129
|
+
* const { once, isAvailable } = useEvents();
|
|
130
|
+
*
|
|
131
|
+
* useEffect(() => {
|
|
132
|
+
* if (!isAvailable) return;
|
|
133
|
+
*
|
|
134
|
+
* // Auto-unsubscribe after first transaction event
|
|
135
|
+
* const unsubscribe = once(
|
|
136
|
+
* (event) => {
|
|
137
|
+
* console.log('First transaction event:', event.type);
|
|
138
|
+
* showOnboardingComplete();
|
|
139
|
+
* },
|
|
140
|
+
* { domain: 'transaction', level: 'success' }
|
|
141
|
+
* );
|
|
142
|
+
*
|
|
143
|
+
* return () => unsubscribe();
|
|
144
|
+
* }, [once, isAvailable]);
|
|
145
|
+
* }
|
|
146
|
+
* ```
|
|
147
|
+
*
|
|
148
|
+
* @example
|
|
149
|
+
* **Combined Domain and Level Filter**
|
|
150
|
+
* ```typescript
|
|
151
|
+
* function AuthErrorHandler() {
|
|
152
|
+
* const { subscribe, isAvailable } = useEvents();
|
|
153
|
+
*
|
|
154
|
+
* useEffect(() => {
|
|
155
|
+
* if (!isAvailable) return;
|
|
156
|
+
*
|
|
157
|
+
* const unsubscribe = subscribe(
|
|
158
|
+
* (event) => {
|
|
159
|
+
* Alert.alert('Authentication Error', event.userMessage);
|
|
160
|
+
* navigation.navigate('Login');
|
|
161
|
+
* },
|
|
162
|
+
* { domain: 'auth', level: 'error' }
|
|
163
|
+
* );
|
|
164
|
+
*
|
|
165
|
+
* return () => unsubscribe();
|
|
166
|
+
* }, [subscribe, isAvailable]);
|
|
167
|
+
* }
|
|
168
|
+
* ```
|
|
169
|
+
*
|
|
170
|
+
* @see {@link PersEvent} for event structure
|
|
171
|
+
* @see {@link EventFilter} for filtering options
|
|
172
|
+
* @see {@link EventHandler} for handler function type
|
|
173
|
+
*
|
|
174
|
+
* @since 1.6.49
|
|
175
|
+
*/
|
|
176
|
+
export declare const useEvents: () => EventsHook;
|
|
177
|
+
export type EventsHook_ = ReturnType<typeof useEvents>;
|
|
178
|
+
//# sourceMappingURL=useEvents.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useEvents.d.ts","sourceRoot":"","sources":["../../src/hooks/useEvents.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,SAAS,EACT,YAAY,EACZ,WAAW,EACX,WAAW,EACZ,MAAM,0BAA0B,CAAC;AAGlC,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC;AAElE;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,CAAC,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,WAAW,CAAC;IACxE,IAAI,EAAE,CAAC,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,WAAW,CAAC;IACnE,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwJG;AACH,eAAO,MAAM,SAAS,QAAO,UA2K5B,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC"}
|