@umituz/react-native-subscription 2.14.97 → 2.14.98
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 +21 -0
- package/README.md +462 -0
- package/package.json +1 -3
- package/src/application/README.md +229 -0
- package/src/application/ports/README.md +103 -0
- package/src/domain/README.md +402 -0
- package/src/domain/constants/README.md +80 -0
- package/src/domain/entities/README.md +176 -0
- package/src/domain/errors/README.md +307 -0
- package/src/domain/value-objects/README.md +186 -0
- package/src/domains/config/README.md +390 -0
- package/src/domains/paywall/README.md +371 -0
- package/src/domains/paywall/components/PaywallHeader.tsx +8 -11
- package/src/domains/paywall/components/README.md +185 -0
- package/src/domains/paywall/entities/README.md +199 -0
- package/src/domains/paywall/hooks/README.md +129 -0
- package/src/domains/wallet/README.md +292 -0
- package/src/domains/wallet/domain/README.md +108 -0
- package/src/domains/wallet/domain/entities/README.md +122 -0
- package/src/domains/wallet/domain/errors/README.md +157 -0
- package/src/domains/wallet/infrastructure/README.md +96 -0
- package/src/domains/wallet/presentation/components/BalanceCard.tsx +6 -12
- package/src/domains/wallet/presentation/components/README.md +231 -0
- package/src/domains/wallet/presentation/hooks/README.md +255 -0
- package/src/infrastructure/README.md +514 -0
- package/src/infrastructure/mappers/README.md +34 -0
- package/src/infrastructure/models/README.md +26 -0
- package/src/infrastructure/repositories/README.md +385 -0
- package/src/infrastructure/services/README.md +374 -0
- package/src/presentation/README.md +410 -0
- package/src/presentation/components/README.md +183 -0
- package/src/presentation/components/details/CreditRow.md +337 -0
- package/src/presentation/components/details/DetailRow.md +283 -0
- package/src/presentation/components/details/PremiumDetailsCard.md +266 -0
- package/src/presentation/components/details/PremiumStatusBadge.md +266 -0
- package/src/presentation/components/details/README.md +449 -0
- package/src/presentation/components/feedback/PaywallFeedbackModal.md +314 -0
- package/src/presentation/components/feedback/README.md +447 -0
- package/src/presentation/components/paywall/PaywallModal.md +444 -0
- package/src/presentation/components/paywall/README.md +190 -0
- package/src/presentation/components/sections/README.md +468 -0
- package/src/presentation/components/sections/SubscriptionSection.md +246 -0
- package/src/presentation/hooks/README.md +743 -0
- package/src/presentation/hooks/useAuthAwarePurchase.md +359 -0
- package/src/presentation/hooks/useAuthGate.md +403 -0
- package/src/presentation/hooks/useAuthSubscriptionSync.md +398 -0
- package/src/presentation/hooks/useCreditChecker.md +407 -0
- package/src/presentation/hooks/useCredits.md +342 -0
- package/src/presentation/hooks/useCreditsGate.md +346 -0
- package/src/presentation/hooks/useDeductCredit.md +160 -0
- package/src/presentation/hooks/useDevTestCallbacks.md +422 -0
- package/src/presentation/hooks/useFeatureGate.md +157 -0
- package/src/presentation/hooks/useInitializeCredits.md +458 -0
- package/src/presentation/hooks/usePaywall.md +334 -0
- package/src/presentation/hooks/usePaywallOperations.md +486 -0
- package/src/presentation/hooks/usePaywallVisibility.md +344 -0
- package/src/presentation/hooks/usePremium.md +230 -0
- package/src/presentation/hooks/usePremiumGate.md +423 -0
- package/src/presentation/hooks/usePremiumWithCredits.md +429 -0
- package/src/presentation/hooks/useSubscription.md +450 -0
- package/src/presentation/hooks/useSubscriptionDetails.md +438 -0
- package/src/presentation/hooks/useSubscriptionGate.md +168 -0
- package/src/presentation/hooks/useSubscriptionSettingsConfig.md +374 -0
- package/src/presentation/hooks/useSubscriptionStatus.md +424 -0
- package/src/presentation/hooks/useUserTier.md +356 -0
- package/src/presentation/hooks/useUserTierWithRepository.md +452 -0
- package/src/presentation/screens/README.md +194 -0
- package/src/presentation/types/README.md +38 -0
- package/src/presentation/utils/README.md +52 -0
- package/src/revenuecat/README.md +523 -0
- package/src/revenuecat/domain/README.md +147 -0
- package/src/revenuecat/domain/errors/README.md +197 -0
- package/src/revenuecat/infrastructure/config/README.md +40 -0
- package/src/revenuecat/infrastructure/managers/README.md +49 -0
- package/src/revenuecat/presentation/hooks/README.md +56 -0
- package/src/utils/README.md +529 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Wallet Domain Entities
|
|
2
|
+
|
|
3
|
+
Core entities for credits and transaction management.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This directory contains domain entities representing credits, transactions, and wallet-related business concepts.
|
|
8
|
+
|
|
9
|
+
## Entities
|
|
10
|
+
|
|
11
|
+
### UserCredits
|
|
12
|
+
Represents user's credit balance and metadata.
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
interface UserCredits {
|
|
16
|
+
credits: number; // Current credit balance
|
|
17
|
+
purchasedAt: Date; // When credits were purchased
|
|
18
|
+
lastUpdatedAt: Date; // Last update timestamp
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface CreditsResult {
|
|
22
|
+
success: boolean;
|
|
23
|
+
data?: UserCredits;
|
|
24
|
+
error?: {
|
|
25
|
+
code: string;
|
|
26
|
+
message: string;
|
|
27
|
+
};
|
|
28
|
+
duplicate?: boolean; // If this was a duplicate operation
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Usage:**
|
|
33
|
+
```typescript
|
|
34
|
+
const credits: UserCredits = {
|
|
35
|
+
credits: 100,
|
|
36
|
+
purchasedAt: new Date('2024-01-01'),
|
|
37
|
+
lastUpdatedAt: new Date('2024-01-15'),
|
|
38
|
+
};
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Transaction
|
|
42
|
+
Represents a credit transaction record.
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
interface Transaction {
|
|
46
|
+
id: string;
|
|
47
|
+
amount: number; // Positive for additions, negative for deductions
|
|
48
|
+
reason: string; // Description of transaction
|
|
49
|
+
timestamp: Date; // When transaction occurred
|
|
50
|
+
type: TransactionType; // Transaction category
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
type TransactionType =
|
|
54
|
+
| 'purchase' // Initial purchase
|
|
55
|
+
| 'deduction' // Feature usage
|
|
56
|
+
| 'bonus' // Bonus credits
|
|
57
|
+
| 'renewal' // Subscription renewal
|
|
58
|
+
| 'adjustment'; // Manual adjustment
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**Usage:**
|
|
62
|
+
```typescript
|
|
63
|
+
const transaction: Transaction = {
|
|
64
|
+
id: 'tx_123',
|
|
65
|
+
amount: -5,
|
|
66
|
+
reason: 'ai_generation',
|
|
67
|
+
timestamp: new Date(),
|
|
68
|
+
type: 'deduction',
|
|
69
|
+
};
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### CreditPackage
|
|
73
|
+
Represents a credit package for purchase.
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
interface CreditPackage {
|
|
77
|
+
id: string;
|
|
78
|
+
amount: number; // Number of credits
|
|
79
|
+
price: number; // Price in currency units
|
|
80
|
+
currency: string; // Currency code (USD, EUR, etc.)
|
|
81
|
+
description?: string;
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Key Operations
|
|
86
|
+
|
|
87
|
+
### Check Balance
|
|
88
|
+
```typescript
|
|
89
|
+
function hasEnoughCredits(credits: UserCredits, required: number): boolean {
|
|
90
|
+
return credits.credits >= required;
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Calculate Remaining
|
|
95
|
+
```typescript
|
|
96
|
+
function calculateRemaining(credits: UserCredits, spent: number): number {
|
|
97
|
+
return Math.max(0, credits.credits - spent);
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Validate Credits
|
|
102
|
+
```typescript
|
|
103
|
+
function validateCredits(amount: number): void {
|
|
104
|
+
if (amount < 0) {
|
|
105
|
+
throw new Error('Credits cannot be negative');
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Best Practices
|
|
111
|
+
|
|
112
|
+
1. **Immutability**: Treat entities as immutable values
|
|
113
|
+
2. **Validation**: Validate in entity methods
|
|
114
|
+
3. **Type Safety**: Use strict TypeScript types
|
|
115
|
+
4. **Business Rules**: Keep business logic in entities
|
|
116
|
+
5. **Serialization**: Handle date serialization properly
|
|
117
|
+
|
|
118
|
+
## Related
|
|
119
|
+
|
|
120
|
+
- [Wallet Domain](../README.md)
|
|
121
|
+
- [Credits Repository](../../infrastructure/repositories/README.md)
|
|
122
|
+
- [useCredits Hook](../../../../presentation/hooks/useCredits.md)
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# Wallet Domain Errors
|
|
2
|
+
|
|
3
|
+
Domain-specific errors for wallet and credit operations.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This directory contains custom error classes representing wallet-related error conditions.
|
|
8
|
+
|
|
9
|
+
## Error Types
|
|
10
|
+
|
|
11
|
+
### CreditsExhaustedError
|
|
12
|
+
Thrown when user doesn't have enough credits for an operation.
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
class CreditsExhaustedError extends Error {
|
|
16
|
+
constructor(
|
|
17
|
+
public currentBalance: number,
|
|
18
|
+
public required: number
|
|
19
|
+
) {
|
|
20
|
+
super(
|
|
21
|
+
`Insufficient credits: ${currentBalance}/${required} required`,
|
|
22
|
+
'CREDITS_EXHAUSTED'
|
|
23
|
+
);
|
|
24
|
+
this.name = 'CreditsExhaustedError';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**Usage:**
|
|
30
|
+
```typescript
|
|
31
|
+
import { CreditsExhaustedError } from './errors/CreditsExhaustedError';
|
|
32
|
+
|
|
33
|
+
function deductCredits(credits: UserCredits, amount: number) {
|
|
34
|
+
if (credits.credits < amount) {
|
|
35
|
+
throw new CreditsExhaustedError(credits.credits, amount);
|
|
36
|
+
}
|
|
37
|
+
// Proceed with deduction
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### DuplicatePurchaseError
|
|
42
|
+
Thrown when attempting to add credits for a duplicate purchase ID.
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
class DuplicatePurchaseError extends Error {
|
|
46
|
+
constructor(
|
|
47
|
+
public purchaseId: string,
|
|
48
|
+
public existingCredits: UserCredits
|
|
49
|
+
) {
|
|
50
|
+
super(
|
|
51
|
+
`Duplicate purchase ID: ${purchaseId}`,
|
|
52
|
+
'DUPLICATE_PURCHASE'
|
|
53
|
+
);
|
|
54
|
+
this.name = 'DuplicatePurchaseError';
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Usage:**
|
|
60
|
+
```typescript
|
|
61
|
+
try {
|
|
62
|
+
await repository.initializeCredits(userId, purchaseId);
|
|
63
|
+
} catch (error) {
|
|
64
|
+
if (error instanceof DuplicatePurchaseError) {
|
|
65
|
+
console.log('Credits already added for this purchase');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### CreditsValidationError
|
|
71
|
+
Thrown when credit validation fails.
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
class CreditsValidationError extends Error {
|
|
75
|
+
constructor(
|
|
76
|
+
message: string,
|
|
77
|
+
public code: string
|
|
78
|
+
) {
|
|
79
|
+
super(message, code);
|
|
80
|
+
this.name = 'CreditsValidationError';
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**Usage:**
|
|
86
|
+
```typescript
|
|
87
|
+
function validateCreditAmount(amount: number) {
|
|
88
|
+
if (amount < 0) {
|
|
89
|
+
throw new CreditsValidationError(
|
|
90
|
+
'Credit amount cannot be negative',
|
|
91
|
+
'INVALID_AMOUNT'
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
if (amount > 10000) {
|
|
95
|
+
throw new CreditsValidationError(
|
|
96
|
+
'Credit amount exceeds maximum',
|
|
97
|
+
'EXCEEDS_MAXIMUM'
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Error Handling Pattern
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
import {
|
|
107
|
+
CreditsExhaustedError,
|
|
108
|
+
DuplicatePurchaseError,
|
|
109
|
+
CreditsValidationError
|
|
110
|
+
} from './errors';
|
|
111
|
+
|
|
112
|
+
async function handleCreditOperation(userId: string, cost: number) {
|
|
113
|
+
try {
|
|
114
|
+
const result = await repository.deductCredit(userId, cost);
|
|
115
|
+
return result;
|
|
116
|
+
} catch (error) {
|
|
117
|
+
if (error instanceof CreditsExhaustedError) {
|
|
118
|
+
// Show paywall or upgrade prompt
|
|
119
|
+
showPaywall({ required: cost, current: error.currentBalance });
|
|
120
|
+
} else if (error instanceof DuplicatePurchaseError) {
|
|
121
|
+
// Log and continue (not a critical error)
|
|
122
|
+
console.warn('Duplicate purchase detected');
|
|
123
|
+
} else if (error instanceof CreditsValidationError) {
|
|
124
|
+
// Log validation error
|
|
125
|
+
console.error('Validation failed:', error.message);
|
|
126
|
+
} else {
|
|
127
|
+
// Unexpected error
|
|
128
|
+
console.error('Unexpected error:', error);
|
|
129
|
+
}
|
|
130
|
+
throw error;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Error Codes Reference
|
|
136
|
+
|
|
137
|
+
| Code | Description | Recovery |
|
|
138
|
+
|------|-------------|----------|
|
|
139
|
+
| `CREDITS_EXHAUSTED` | Not enough credits | Show paywall/upgrade |
|
|
140
|
+
| `DUPLICATE_PURCHASE` | Duplicate purchase ID | Ignore (idempotent) |
|
|
141
|
+
| `INVALID_AMOUNT` | Invalid credit amount | Validate input |
|
|
142
|
+
| `EXCEEDS_MAXIMUM` | Amount too large | Cap at maximum |
|
|
143
|
+
| `USER_NOT_FOUND` | User doesn't exist | Create user record |
|
|
144
|
+
| `INITIALIZATION_FAILED` | Credit init failed | Retry operation |
|
|
145
|
+
|
|
146
|
+
## Best Practices
|
|
147
|
+
|
|
148
|
+
1. **Specific Errors**: Use specific error types for different scenarios
|
|
149
|
+
2. **Error Context**: Include relevant data in error properties
|
|
150
|
+
3. **Graceful Handling**: Handle errors appropriately at boundaries
|
|
151
|
+
4. **Logging**: Log errors for debugging
|
|
152
|
+
5. **User Feedback**: Convert errors to user-friendly messages
|
|
153
|
+
|
|
154
|
+
## Related
|
|
155
|
+
|
|
156
|
+
- [Wallet Entities](../entities/README.md)
|
|
157
|
+
- [Credits Repository](../../infrastructure/repositories/README.md)
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Wallet Infrastructure
|
|
2
|
+
|
|
3
|
+
Infrastructure layer for wallet and credits functionality.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This directory contains implementations for credit persistence, transactions, and credit operations.
|
|
8
|
+
|
|
9
|
+
## Structure
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
infrastructure/
|
|
13
|
+
├── repositories/ # Data persistence implementations
|
|
14
|
+
│ └── CreditsRepository.ts
|
|
15
|
+
└── services/ # External service integrations
|
|
16
|
+
└── CreditsService.ts
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Credits Repository
|
|
20
|
+
|
|
21
|
+
Handles credit data persistence with duplicate protection.
|
|
22
|
+
|
|
23
|
+
### Key Features
|
|
24
|
+
|
|
25
|
+
- **Duplicate Protection**: Prevents duplicate credit allocations
|
|
26
|
+
- **Optimistic Updates**: Immediate UI updates with rollback
|
|
27
|
+
- **Transactional**: Atomic credit operations
|
|
28
|
+
- **ACCUMULATE Mode**: Adds credits on renewal
|
|
29
|
+
|
|
30
|
+
### Core Methods
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
class CreditsRepository implements ICreditsRepository {
|
|
34
|
+
async getCredits(userId: string): Promise<UserCredits | null>;
|
|
35
|
+
async initializeCredits(
|
|
36
|
+
userId: string,
|
|
37
|
+
purchaseId?: string,
|
|
38
|
+
productId?: string
|
|
39
|
+
): Promise<CreditsResult>;
|
|
40
|
+
async deductCredit(userId: string, amount: number): Promise<CreditsResult>;
|
|
41
|
+
async addCredits(userId: string, amount: number): Promise<CreditsResult>;
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Usage
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
import { CreditsRepository } from './repositories/CreditsRepository';
|
|
49
|
+
import { getCreditsRepository } from './repositories/CreditsRepositoryProvider';
|
|
50
|
+
|
|
51
|
+
const repository = getCreditsRepository();
|
|
52
|
+
|
|
53
|
+
// Initialize credits
|
|
54
|
+
const result = await repository.initializeCredits('user-123', 'purchase-456', 'premium_monthly');
|
|
55
|
+
if (result.success) {
|
|
56
|
+
console.log('Credits:', result.data.credits);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Deduct credits
|
|
60
|
+
const deductResult = await repository.deductCredit('user-123', 5);
|
|
61
|
+
if (deductResult.success) {
|
|
62
|
+
console.log('Credits deducted successfully');
|
|
63
|
+
} else if (deductResult.error?.code === 'CREDITS_EXHAUSTED') {
|
|
64
|
+
console.log('Not enough credits');
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Duplicate Protection
|
|
69
|
+
|
|
70
|
+
The repository prevents duplicate credit allocations based on `purchaseId`:
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
// First call
|
|
74
|
+
await repository.initializeCredits(userId, 'renewal-123', 'premium');
|
|
75
|
+
// Returns: { success: true, data: { credits: 100 } }
|
|
76
|
+
|
|
77
|
+
// Second call with same purchaseId
|
|
78
|
+
await repository.initializeCredits(userId, 'renewal-123', 'premium');
|
|
79
|
+
// Returns: { success: true, duplicate: true, data: { credits: 100 } }
|
|
80
|
+
// Credits not added again
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Best Practices
|
|
84
|
+
|
|
85
|
+
1. **Check Before Deduct**: Always verify credit balance
|
|
86
|
+
2. **Handle Errors**: Catch and handle credit errors appropriately
|
|
87
|
+
3. **Use Transactions**: Ensure data consistency
|
|
88
|
+
4. **Log Operations**: Track credit operations for debugging
|
|
89
|
+
5. **Test Edge Cases**: Zero credits, max credits, duplicates
|
|
90
|
+
6. **Validate Input**: Validate all input parameters
|
|
91
|
+
|
|
92
|
+
## Related
|
|
93
|
+
|
|
94
|
+
- [Wallet Domain](../domain/README.md)
|
|
95
|
+
- [Credits Entity](../domain/entities/README.md)
|
|
96
|
+
- [useCredits Hook](../../presentation/hooks/useCredits.md)
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Balance Card Component
|
|
3
3
|
*
|
|
4
|
-
* Displays user's credit balance with
|
|
4
|
+
* Displays user's credit balance with solid background.
|
|
5
5
|
* Props-driven for full customization.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import React from "react";
|
|
9
9
|
import { View, StyleSheet } from "react-native";
|
|
10
|
-
|
|
10
|
+
|
|
11
11
|
import {
|
|
12
12
|
useAppDesignTokens,
|
|
13
13
|
AtomicText,
|
|
@@ -31,17 +31,11 @@ export const BalanceCard: React.FC<BalanceCardProps> = ({
|
|
|
31
31
|
iconName = "wallet",
|
|
32
32
|
}) => {
|
|
33
33
|
const tokens = useAppDesignTokens();
|
|
34
|
-
|
|
35
|
-
tokens.colors.primary,
|
|
36
|
-
tokens.colors.primaryDark || tokens.colors.primary,
|
|
37
|
-
] as const;
|
|
34
|
+
|
|
38
35
|
|
|
39
36
|
return (
|
|
40
|
-
<
|
|
41
|
-
|
|
42
|
-
start={{ x: 0, y: 0 }}
|
|
43
|
-
end={{ x: 1, y: 1 }}
|
|
44
|
-
style={styles.container}
|
|
37
|
+
<View
|
|
38
|
+
style={[styles.container, { backgroundColor: tokens.colors.primary }]}
|
|
45
39
|
>
|
|
46
40
|
<View style={styles.content}>
|
|
47
41
|
<View style={styles.textContainer}>
|
|
@@ -73,7 +67,7 @@ export const BalanceCard: React.FC<BalanceCardProps> = ({
|
|
|
73
67
|
<AtomicIcon name={iconName} size="xl" color="onPrimary" />
|
|
74
68
|
</View>
|
|
75
69
|
</View>
|
|
76
|
-
</
|
|
70
|
+
</View>
|
|
77
71
|
);
|
|
78
72
|
};
|
|
79
73
|
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# Wallet Presentation Components
|
|
2
|
+
|
|
3
|
+
UI components for wallet and credit display.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This directory contains React Native components for displaying credits, transactions, and wallet information.
|
|
8
|
+
|
|
9
|
+
## Components
|
|
10
|
+
|
|
11
|
+
### CreditBalanceCard
|
|
12
|
+
Card displaying current credit balance.
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
interface CreditBalanceCardProps {
|
|
16
|
+
credits: number;
|
|
17
|
+
balance: number;
|
|
18
|
+
isLoading?: boolean;
|
|
19
|
+
onPress?: () => void;
|
|
20
|
+
onRefresh?: () => void;
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**Usage:**
|
|
25
|
+
```typescript
|
|
26
|
+
<CreditBalanceCard
|
|
27
|
+
credits={50}
|
|
28
|
+
balance={0.50}
|
|
29
|
+
onPress={() => navigation.navigate('CreditHistory')}
|
|
30
|
+
onRefresh={() => refetch()}
|
|
31
|
+
/>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### TransactionItem
|
|
35
|
+
Individual transaction display component.
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
interface TransactionItemProps {
|
|
39
|
+
transaction: Transaction;
|
|
40
|
+
format?: 'full' | 'compact';
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Usage:**
|
|
45
|
+
```typescript
|
|
46
|
+
<TransactionItem
|
|
47
|
+
transaction={{
|
|
48
|
+
id: 'tx_123',
|
|
49
|
+
amount: -5,
|
|
50
|
+
reason: 'AI Generation',
|
|
51
|
+
timestamp: new Date(),
|
|
52
|
+
type: 'deduction',
|
|
53
|
+
}}
|
|
54
|
+
format="full"
|
|
55
|
+
/>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### CreditProgressBar
|
|
59
|
+
Progress bar showing credit usage.
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
interface CreditProgressBarProps {
|
|
63
|
+
current: number;
|
|
64
|
+
total: number;
|
|
65
|
+
showLabel?: boolean;
|
|
66
|
+
showPercentage?: boolean;
|
|
67
|
+
height?: number;
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Usage:**
|
|
72
|
+
```typescript
|
|
73
|
+
<CreditProgressBar
|
|
74
|
+
current={50}
|
|
75
|
+
total={100}
|
|
76
|
+
showLabel={true}
|
|
77
|
+
showPercentage={true}
|
|
78
|
+
height={8}
|
|
79
|
+
/>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### CreditPackageCard
|
|
83
|
+
Display card for purchasable credit packages.
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
interface CreditPackageCardProps {
|
|
87
|
+
package: {
|
|
88
|
+
id: string;
|
|
89
|
+
amount: number;
|
|
90
|
+
price: number;
|
|
91
|
+
bonus?: number;
|
|
92
|
+
currency: string;
|
|
93
|
+
};
|
|
94
|
+
onPress: () => void;
|
|
95
|
+
highlight?: boolean;
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**Usage:**
|
|
100
|
+
```typescript
|
|
101
|
+
<CreditPackageCard
|
|
102
|
+
package={{
|
|
103
|
+
id: 'premium_credits',
|
|
104
|
+
amount: 100,
|
|
105
|
+
price: 9.99,
|
|
106
|
+
bonus: 10,
|
|
107
|
+
currency: 'USD',
|
|
108
|
+
}}
|
|
109
|
+
onPress={() => purchaseCredits('premium_credits')}
|
|
110
|
+
highlight={true}
|
|
111
|
+
/>
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Usage Patterns
|
|
115
|
+
|
|
116
|
+
### Complete Credit Dashboard
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
function CreditDashboard() {
|
|
120
|
+
const { credits, transactions, isLoading, refetch } = useCredits();
|
|
121
|
+
|
|
122
|
+
return (
|
|
123
|
+
<ScrollView>
|
|
124
|
+
<CreditBalanceCard
|
|
125
|
+
credits={credits}
|
|
126
|
+
balance={calculateBalance(credits)}
|
|
127
|
+
onRefresh={refetch}
|
|
128
|
+
/>
|
|
129
|
+
|
|
130
|
+
<View style={styles.section}>
|
|
131
|
+
<Text style={styles.title}>Recent Transactions</Text>
|
|
132
|
+
{transactions.slice(0, 10).map(tx => (
|
|
133
|
+
<TransactionItem key={tx.id} transaction={tx} />
|
|
134
|
+
))}
|
|
135
|
+
</View>
|
|
136
|
+
|
|
137
|
+
<Button onPress={() => navigation.navigate('CreditPackages')}>
|
|
138
|
+
Get More Credits
|
|
139
|
+
</Button>
|
|
140
|
+
</ScrollView>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Credit Warning Banner
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
function LowCreditWarning() {
|
|
149
|
+
const { credits } = useCredits();
|
|
150
|
+
const warningThreshold = 20;
|
|
151
|
+
|
|
152
|
+
if (credits > warningThreshold) return null;
|
|
153
|
+
|
|
154
|
+
return (
|
|
155
|
+
<Banner type={credits === 0 ? 'error' : 'warning'}>
|
|
156
|
+
<Text>
|
|
157
|
+
{credits === 0
|
|
158
|
+
? 'No credits remaining'
|
|
159
|
+
: `Only ${credits} credits left`
|
|
160
|
+
}
|
|
161
|
+
</Text>
|
|
162
|
+
<Button onPress={() => navigation.navigate('CreditPackages')}>
|
|
163
|
+
Get More
|
|
164
|
+
</Button>
|
|
165
|
+
</Banner>
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Credit History List
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
function TransactionHistory() {
|
|
174
|
+
const { transactions } = useCredits();
|
|
175
|
+
|
|
176
|
+
return (
|
|
177
|
+
<FlatList
|
|
178
|
+
data={transactions}
|
|
179
|
+
keyExtractor={(item) => item.id}
|
|
180
|
+
renderItem={({ item }) => (
|
|
181
|
+
<TransactionItem transaction={item} format="compact" />
|
|
182
|
+
)}
|
|
183
|
+
ListHeaderComponent={() => (
|
|
184
|
+
<Text style={styles.title}>Transaction History</Text>
|
|
185
|
+
)}
|
|
186
|
+
ListEmptyComponent={() => (
|
|
187
|
+
<EmptyState
|
|
188
|
+
icon="receipt"
|
|
189
|
+
title="No transactions yet"
|
|
190
|
+
message="Your credit transaction history will appear here"
|
|
191
|
+
/>
|
|
192
|
+
)}
|
|
193
|
+
/>
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Styling
|
|
199
|
+
|
|
200
|
+
Components use design system:
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
import { useAppDesignTokens } from '@umituz/react-native-design-system';
|
|
204
|
+
|
|
205
|
+
const tokens = useAppDesignTokens();
|
|
206
|
+
|
|
207
|
+
const styles = StyleSheet.create({
|
|
208
|
+
card: {
|
|
209
|
+
backgroundColor: tokens.colors.surface,
|
|
210
|
+
borderRadius: tokens.radius.lg,
|
|
211
|
+
padding: tokens.spacing.lg,
|
|
212
|
+
marginBottom: tokens.spacing.md,
|
|
213
|
+
},
|
|
214
|
+
});
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Best Practices
|
|
218
|
+
|
|
219
|
+
1. **Visual Hierarchy**: Emphasize important information
|
|
220
|
+
2. **Color Coding**: Use colors to indicate status (low/warning/good)
|
|
221
|
+
3. **Loading States**: Show skeletons while loading
|
|
222
|
+
4. **Empty States**: Provide helpful empty state messages
|
|
223
|
+
5. **Refresh Capability**: Allow manual refresh
|
|
224
|
+
6. **Transactional History**: Show clear transaction history
|
|
225
|
+
7. **Purchase Links**: Easy access to purchase more credits
|
|
226
|
+
|
|
227
|
+
## Related
|
|
228
|
+
|
|
229
|
+
- [Credit Row](../../../presentation/components/details/CreditRow.md)
|
|
230
|
+
- [Credits Hook](../hooks/README.md)
|
|
231
|
+
- [Wallet Domain](../../domain/README.md)
|