@dubsdotapp/expo 0.5.32 → 0.5.34
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/dist/index.d.mts +29 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +143 -11
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +143 -11
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/client.ts +56 -0
- package/src/hooks/useCreateGame.ts +10 -3
- package/src/hooks/useJoinGame.ts +3 -0
- package/src/types.ts +29 -0
- package/src/ui/game/CreateGameSheet.tsx +49 -4
- package/src/ui/game/JoinGameSheet.tsx +82 -4
package/package.json
CHANGED
package/src/client.ts
CHANGED
|
@@ -200,6 +200,37 @@ export class DubsClient {
|
|
|
200
200
|
}
|
|
201
201
|
|
|
202
202
|
async createGame(params: CreateGameParams): Promise<CreateGameResult> {
|
|
203
|
+
// Sponsored path: spend an active streak credit instead of paying
|
|
204
|
+
// rent + buy-in. Server picks oldest FIFO credit, returns the
|
|
205
|
+
// partial-signed compound tx + promoCode so confirmGame can mark
|
|
206
|
+
// the credit as used after the on-chain confirm.
|
|
207
|
+
if (params.useCredit) {
|
|
208
|
+
const res = await this.request<{
|
|
209
|
+
success: true;
|
|
210
|
+
gameId: string;
|
|
211
|
+
gameAddress: string;
|
|
212
|
+
transaction: string;
|
|
213
|
+
lockTimestamp: number;
|
|
214
|
+
event: UnifiedEvent;
|
|
215
|
+
promoCode: string;
|
|
216
|
+
sponsorWallet?: string;
|
|
217
|
+
wagerAmount: number;
|
|
218
|
+
}>('POST', '/games/create-sponsored', {
|
|
219
|
+
id: params.id,
|
|
220
|
+
teamChoice: params.teamChoice,
|
|
221
|
+
});
|
|
222
|
+
return {
|
|
223
|
+
gameId: res.gameId,
|
|
224
|
+
gameAddress: res.gameAddress,
|
|
225
|
+
transaction: res.transaction,
|
|
226
|
+
lockTimestamp: res.lockTimestamp,
|
|
227
|
+
event: res.event,
|
|
228
|
+
promoCode: res.promoCode,
|
|
229
|
+
sponsorWallet: res.sponsorWallet,
|
|
230
|
+
wagerAmount: res.wagerAmount,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
203
234
|
const res = await this.request<{ success: true } & CreateGameResult>(
|
|
204
235
|
'POST',
|
|
205
236
|
'/games/create',
|
|
@@ -215,6 +246,31 @@ export class DubsClient {
|
|
|
215
246
|
}
|
|
216
247
|
|
|
217
248
|
async joinGame(params: JoinGameParams): Promise<JoinGameResult> {
|
|
249
|
+
// Sponsored path: spend an active streak credit instead of buy-in.
|
|
250
|
+
// Server picks the user's oldest active credit (FIFO); we just flag
|
|
251
|
+
// the intent. Result includes promoCode for the confirm step to
|
|
252
|
+
// mark as used.
|
|
253
|
+
if (params.useCredit) {
|
|
254
|
+
const res = await this.request<{
|
|
255
|
+
success: true;
|
|
256
|
+
gameId: string;
|
|
257
|
+
transaction: string;
|
|
258
|
+
gameAddress: string;
|
|
259
|
+
promoCode: string;
|
|
260
|
+
sponsorWallet?: string;
|
|
261
|
+
}>('POST', '/games/join-sponsored', {
|
|
262
|
+
gameId: params.gameId,
|
|
263
|
+
teamChoice: params.teamChoice,
|
|
264
|
+
});
|
|
265
|
+
return {
|
|
266
|
+
gameId: res.gameId,
|
|
267
|
+
transaction: res.transaction,
|
|
268
|
+
gameAddress: res.gameAddress,
|
|
269
|
+
promoCode: res.promoCode,
|
|
270
|
+
sponsorWallet: res.sponsorWallet,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
218
274
|
const res = await this.request<{ success: true } & JoinGameResult>(
|
|
219
275
|
'POST',
|
|
220
276
|
'/games/join',
|
|
@@ -58,14 +58,21 @@ export function useCreateGame() {
|
|
|
58
58
|
// 3. Confirm with backend (server handles on-chain verification)
|
|
59
59
|
setStatus('confirming');
|
|
60
60
|
console.log('[useCreateGame] Step 3: Confirming with backend...');
|
|
61
|
+
// For sponsored creates, the server forces wager to the credit
|
|
62
|
+
// amount — use that, not whatever was in params, so the saved
|
|
63
|
+
// game record is consistent with the on-chain bet.
|
|
64
|
+
const wagerAmount = createResult.wagerAmount ?? params.wagerAmount;
|
|
61
65
|
const confirmResult = await client.confirmGame({
|
|
62
66
|
gameId: createResult.gameId,
|
|
63
67
|
playerWallet: params.playerWallet,
|
|
64
68
|
signature,
|
|
65
69
|
teamChoice: params.teamChoice,
|
|
66
|
-
wagerAmount
|
|
70
|
+
wagerAmount,
|
|
67
71
|
role: 'creator',
|
|
68
72
|
gameAddress: createResult.gameAddress,
|
|
73
|
+
// Echo the credit code through so the server marks it as used
|
|
74
|
+
// after the on-chain confirm succeeds.
|
|
75
|
+
promoCode: createResult.promoCode,
|
|
69
76
|
});
|
|
70
77
|
console.log('[useCreateGame] Step 3 done.');
|
|
71
78
|
|
|
@@ -94,14 +101,14 @@ export function useCreateGame() {
|
|
|
94
101
|
params.teamChoice === 'away' ? teamNickname(away) :
|
|
95
102
|
'Draw';
|
|
96
103
|
|
|
97
|
-
const message = `I just placed a ${
|
|
104
|
+
const message = `I just placed a ${wagerAmount} SOL bet on ${teamLabel} - Join me!`;
|
|
98
105
|
const gameInvite = {
|
|
99
106
|
gameId: createResult.gameId,
|
|
100
107
|
gameAddress: createResult.gameAddress,
|
|
101
108
|
title: event?.title,
|
|
102
109
|
league: event?.league,
|
|
103
110
|
gameType: 'sports',
|
|
104
|
-
buyIn:
|
|
111
|
+
buyIn: wagerAmount,
|
|
105
112
|
status: 'waiting',
|
|
106
113
|
homeTeam: home,
|
|
107
114
|
awayTeam: away,
|
package/src/hooks/useJoinGame.ts
CHANGED
|
@@ -53,6 +53,9 @@ export function useJoinGame() {
|
|
|
53
53
|
wagerAmount: params.amount,
|
|
54
54
|
role: 'joiner' as const,
|
|
55
55
|
gameAddress: joinResult.gameAddress,
|
|
56
|
+
// Pass through when this was a sponsored join — server marks
|
|
57
|
+
// the credit as used after the on-chain confirm succeeds.
|
|
58
|
+
promoCode: joinResult.promoCode,
|
|
56
59
|
};
|
|
57
60
|
console.log('[useJoinGame] Step 3: Confirming with backend...', confirmParams);
|
|
58
61
|
const confirmResult = await client.confirmGame(confirmParams);
|
package/src/types.ts
CHANGED
|
@@ -95,6 +95,14 @@ export interface CreateGameParams {
|
|
|
95
95
|
playerWallet: string;
|
|
96
96
|
teamChoice: 'home' | 'away' | 'draw';
|
|
97
97
|
wagerAmount: number;
|
|
98
|
+
/**
|
|
99
|
+
* When true, fund create+join with one of the user's active 0.01 SOL
|
|
100
|
+
* streak credits instead of paying rent + buy-in from their own
|
|
101
|
+
* wallet. SDK picks the oldest credit (FIFO). Treasury covers rent
|
|
102
|
+
* + buy-in; player only signs to consent. Buy-in is forced to the
|
|
103
|
+
* credit amount.
|
|
104
|
+
*/
|
|
105
|
+
useCredit?: boolean;
|
|
98
106
|
}
|
|
99
107
|
|
|
100
108
|
export interface CreateGameResult {
|
|
@@ -103,6 +111,11 @@ export interface CreateGameResult {
|
|
|
103
111
|
transaction: string;
|
|
104
112
|
lockTimestamp: number;
|
|
105
113
|
event: UnifiedEvent;
|
|
114
|
+
/** Set when sponsored — echoed back to confirmGame for mark-as-used. */
|
|
115
|
+
promoCode?: string;
|
|
116
|
+
sponsorWallet?: string;
|
|
117
|
+
/** Server-confirmed wager amount (== credit amount when sponsored). */
|
|
118
|
+
wagerAmount?: number;
|
|
106
119
|
}
|
|
107
120
|
|
|
108
121
|
// ── Custom Game (game_mode=6) ──
|
|
@@ -130,12 +143,26 @@ export interface JoinGameParams {
|
|
|
130
143
|
gameId: string;
|
|
131
144
|
teamChoice: 'home' | 'away' | 'draw';
|
|
132
145
|
amount: number;
|
|
146
|
+
/**
|
|
147
|
+
* When true, spend one of the user's active 0.01 SOL streak credits
|
|
148
|
+
* instead of paying buy-in from the player's own wallet. The SDK
|
|
149
|
+
* picks the oldest credit (FIFO). The treasury covers buy-in + tx
|
|
150
|
+
* fee; the player only signs to consent. No-op if the user has no
|
|
151
|
+
* active credits — call useCredits() to check before enabling.
|
|
152
|
+
*/
|
|
153
|
+
useCredit?: boolean;
|
|
133
154
|
}
|
|
134
155
|
|
|
135
156
|
export interface JoinGameResult {
|
|
136
157
|
gameId: string;
|
|
137
158
|
transaction: string;
|
|
138
159
|
gameAddress: string;
|
|
160
|
+
/**
|
|
161
|
+
* Set when this join was sponsored. The mutation hook echoes this
|
|
162
|
+
* back to confirmGame so the server can mark the credit as used.
|
|
163
|
+
*/
|
|
164
|
+
promoCode?: string;
|
|
165
|
+
sponsorWallet?: string;
|
|
139
166
|
}
|
|
140
167
|
|
|
141
168
|
// ── Confirm Game ──
|
|
@@ -148,6 +175,8 @@ export interface ConfirmGameParams {
|
|
|
148
175
|
wagerAmount?: number;
|
|
149
176
|
role?: 'creator' | 'joiner';
|
|
150
177
|
gameAddress?: string;
|
|
178
|
+
/** Echoed back from a sponsored joinGame so the server marks the credit as used. */
|
|
179
|
+
promoCode?: string;
|
|
151
180
|
}
|
|
152
181
|
|
|
153
182
|
export interface ConfirmGameResult {
|
|
@@ -15,6 +15,7 @@ import { useDubsTheme } from '../theme';
|
|
|
15
15
|
import { useDubs } from '../../provider';
|
|
16
16
|
import { useCreateGame } from '../../hooks/useCreateGame';
|
|
17
17
|
import type { CreateGameMutationResult } from '../../hooks/useCreateGame';
|
|
18
|
+
import { useCredits } from '../../hooks/useCredits';
|
|
18
19
|
import type { UnifiedEvent } from '../../types';
|
|
19
20
|
import { SolSlider } from './SolSlider';
|
|
20
21
|
import { TeamButton } from './TeamButton';
|
|
@@ -74,10 +75,19 @@ export function CreateGameSheet({
|
|
|
74
75
|
const t = useDubsTheme();
|
|
75
76
|
const { wallet } = useDubs();
|
|
76
77
|
const mutation = useCreateGame();
|
|
78
|
+
const { credits, refetch: refetchCredits } = useCredits();
|
|
77
79
|
|
|
78
80
|
const [selectedTeam, setSelectedTeam] = useState<'home' | 'away' | null>(null);
|
|
79
81
|
const [wager, setWager] = useState(0.01);
|
|
80
82
|
const [showSuccess, setShowSuccess] = useState(false);
|
|
83
|
+
const [useCredit, setUseCredit] = useState(false);
|
|
84
|
+
|
|
85
|
+
const oldestCredit = credits.length > 0 ? credits[0] : null;
|
|
86
|
+
// Compare in lamports to avoid float drift between credit.amountSOL and
|
|
87
|
+
// the slider's wager value. The on-chain instruction requires exact
|
|
88
|
+
// match anyway — if either side has a sub-lamport rounding error we'd
|
|
89
|
+
// get a confusing on-chain failure instead of a clean toggle gate.
|
|
90
|
+
const canUseCredit = !!oldestCredit; // Any 0.01 SOL credit covers the 0.01 SOL minimum buy-in
|
|
81
91
|
|
|
82
92
|
const overlayOpacity = useRef(new Animated.Value(0)).current;
|
|
83
93
|
const successScale = useRef(new Animated.Value(0)).current;
|
|
@@ -144,12 +154,17 @@ export function CreateGameSheet({
|
|
|
144
154
|
id: event.id,
|
|
145
155
|
playerWallet: wallet.publicKey.toBase58(),
|
|
146
156
|
teamChoice: selectedTeam,
|
|
147
|
-
|
|
157
|
+
// When sponsoring, the on-chain instruction forces buy-in to
|
|
158
|
+
// the credit's amount; pass that so client-side wager state
|
|
159
|
+
// matches what gets recorded.
|
|
160
|
+
wagerAmount: useCredit && oldestCredit ? oldestCredit.amountSOL : wager,
|
|
161
|
+
useCredit: useCredit && canUseCredit ? true : undefined,
|
|
148
162
|
});
|
|
163
|
+
if (useCredit) refetchCredits();
|
|
149
164
|
} catch {
|
|
150
165
|
// Error captured in mutation state
|
|
151
166
|
}
|
|
152
|
-
}, [selectedTeam, wallet.publicKey, mutation.execute, event.id, wager]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
167
|
+
}, [selectedTeam, wallet.publicKey, mutation.execute, event.id, wager, useCredit, oldestCredit, canUseCredit, refetchCredits]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
153
168
|
|
|
154
169
|
const statusLabel = STATUS_LABELS[mutation.status] || '';
|
|
155
170
|
|
|
@@ -239,8 +254,31 @@ export function CreateGameSheet({
|
|
|
239
254
|
</View>
|
|
240
255
|
</View>
|
|
241
256
|
|
|
242
|
-
{/*
|
|
243
|
-
{selectedTeam && (
|
|
257
|
+
{/* Credit toggle — only when user has at least one credit */}
|
|
258
|
+
{selectedTeam && canUseCredit && oldestCredit && (
|
|
259
|
+
<TouchableOpacity
|
|
260
|
+
style={[styles.creditRow, { backgroundColor: useCredit ? '#22C55E18' : t.surface, borderColor: useCredit ? '#22C55E' : t.border }]}
|
|
261
|
+
activeOpacity={0.8}
|
|
262
|
+
onPress={() => setUseCredit(v => !v)}
|
|
263
|
+
>
|
|
264
|
+
<View style={styles.creditRowText}>
|
|
265
|
+
<Text style={[styles.creditTitle, { color: useCredit ? '#22C55E' : t.text }]}>
|
|
266
|
+
🎁 Use my {formatSol(oldestCredit.amountSOL)} SOL credit
|
|
267
|
+
</Text>
|
|
268
|
+
<Text style={[styles.creditSub, { color: t.textMuted }]}>
|
|
269
|
+
{useCredit
|
|
270
|
+
? 'Treasury covers rent + buy-in'
|
|
271
|
+
: `${credits.length} credit${credits.length === 1 ? '' : 's'} from your streak`}
|
|
272
|
+
</Text>
|
|
273
|
+
</View>
|
|
274
|
+
<View style={[styles.creditCheckbox, useCredit && { backgroundColor: '#22C55E', borderColor: '#22C55E' }]}>
|
|
275
|
+
{useCredit && <Text style={styles.creditCheck}>✓</Text>}
|
|
276
|
+
</View>
|
|
277
|
+
</TouchableOpacity>
|
|
278
|
+
)}
|
|
279
|
+
|
|
280
|
+
{/* SOL Slider — hidden when sponsoring (wager locked to credit) */}
|
|
281
|
+
{selectedTeam && !useCredit && (
|
|
244
282
|
<SolSlider
|
|
245
283
|
value={wager}
|
|
246
284
|
min={0.01}
|
|
@@ -318,6 +356,13 @@ const styles = StyleSheet.create({
|
|
|
318
356
|
ctaText: { color: '#FFFFFF', fontSize: 16, fontWeight: '700' },
|
|
319
357
|
ctaLoading: { flexDirection: 'row', alignItems: 'center', gap: 10 },
|
|
320
358
|
|
|
359
|
+
creditRow: { flexDirection: 'row', alignItems: 'center', gap: 12, marginTop: 12, padding: 12, borderRadius: 12, borderWidth: 1.5 },
|
|
360
|
+
creditRowText: { flex: 1, gap: 2 },
|
|
361
|
+
creditTitle: { fontSize: 14, fontWeight: '700' },
|
|
362
|
+
creditSub: { fontSize: 12, fontWeight: '500' },
|
|
363
|
+
creditCheckbox: { width: 22, height: 22, borderRadius: 11, borderWidth: 2, borderColor: '#3A3A3C', alignItems: 'center', justifyContent: 'center' },
|
|
364
|
+
creditCheck: { color: '#FFFFFF', fontSize: 14, fontWeight: '900' },
|
|
365
|
+
|
|
321
366
|
successOverlay: { ...StyleSheet.absoluteFillObject, zIndex: 100, alignItems: 'center', justifyContent: 'center', backgroundColor: 'rgba(0,0,0,0.85)' },
|
|
322
367
|
successContent: { alignItems: 'center', gap: 12 },
|
|
323
368
|
successEmoji: { fontSize: 64 },
|
|
@@ -15,6 +15,7 @@ import { useDubsTheme } from '../theme';
|
|
|
15
15
|
import { useDubs } from '../../provider';
|
|
16
16
|
import { useJoinGame } from '../../hooks/useJoinGame';
|
|
17
17
|
import type { JoinGameMutationResult } from '../../hooks/useJoinGame';
|
|
18
|
+
import { useCredits } from '../../hooks/useCredits';
|
|
18
19
|
import type { GameDetail } from '../../types';
|
|
19
20
|
import { SolSlider } from './SolSlider';
|
|
20
21
|
import { TeamButton } from './TeamButton';
|
|
@@ -85,12 +86,25 @@ export function JoinGameSheet({
|
|
|
85
86
|
const t = useDubsTheme();
|
|
86
87
|
const { wallet } = useDubs();
|
|
87
88
|
const mutation = useJoinGame();
|
|
89
|
+
const { credits, refetch: refetchCredits } = useCredits();
|
|
88
90
|
|
|
89
91
|
const isCustomGame = game.gameMode === CUSTOM_GAME_MODE;
|
|
90
92
|
|
|
91
93
|
const [selectedTeam, setSelectedTeam] = useState<'home' | 'away' | 'draw' | null>(null);
|
|
92
94
|
const [wager, setWager] = useState(game.buyIn);
|
|
93
95
|
const [showSuccess, setShowSuccess] = useState(false);
|
|
96
|
+
// "Use my streak credit" toggle. Only meaningful when the user has at
|
|
97
|
+
// least one active credit AND the credit's amount covers buy-in. The
|
|
98
|
+
// sponsored on-chain instruction transfers exactly the credit amount,
|
|
99
|
+
// so when this is on we lock wager to the credit value.
|
|
100
|
+
//
|
|
101
|
+
// Compare in lamports to dodge float drift between credit.amountSOL
|
|
102
|
+
// (server-rounded to 5dp) and game.buyIn — a 0.01 SOL credit must
|
|
103
|
+
// visibly cover a 0.01 SOL buy-in, which `>=` on raw floats can miss.
|
|
104
|
+
const [useCredit, setUseCredit] = useState(false);
|
|
105
|
+
const oldestCredit = credits.length > 0 ? credits[0] : null;
|
|
106
|
+
const canUseCredit = !!oldestCredit
|
|
107
|
+
&& Math.round(oldestCredit.amountSOL * 1e9) >= Math.round(game.buyIn * 1e9);
|
|
94
108
|
|
|
95
109
|
const overlayOpacity = useRef(new Animated.Value(0)).current;
|
|
96
110
|
const successScale = useRef(new Animated.Value(0)).current;
|
|
@@ -213,12 +227,18 @@ export function JoinGameSheet({
|
|
|
213
227
|
playerWallet: wallet.publicKey.toBase58(),
|
|
214
228
|
gameId: game.gameId,
|
|
215
229
|
teamChoice: selectedTeam,
|
|
216
|
-
|
|
230
|
+
// When useCredit is on, the on-chain instruction transfers
|
|
231
|
+
// exactly the credit's amount; pass that as the wager.
|
|
232
|
+
amount: useCredit && oldestCredit ? oldestCredit.amountSOL : wager,
|
|
233
|
+
useCredit: useCredit && canUseCredit ? true : undefined,
|
|
217
234
|
});
|
|
235
|
+
// The credit was just spent — refresh the list so the toggle
|
|
236
|
+
// disappears (or rolls to the next FIFO credit).
|
|
237
|
+
if (useCredit) refetchCredits();
|
|
218
238
|
} catch {
|
|
219
239
|
// Error is already captured in mutation state
|
|
220
240
|
}
|
|
221
|
-
}, [selectedTeam, wallet.publicKey, mutation.execute, game.gameId, wager]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
241
|
+
}, [selectedTeam, wallet.publicKey, mutation.execute, game.gameId, wager, useCredit, oldestCredit, canUseCredit, refetchCredits]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
222
242
|
|
|
223
243
|
const statusLabel = STATUS_LABELS[mutation.status] || '';
|
|
224
244
|
|
|
@@ -430,8 +450,31 @@ export function JoinGameSheet({
|
|
|
430
450
|
)}
|
|
431
451
|
</View>
|
|
432
452
|
|
|
433
|
-
{/*
|
|
434
|
-
{selectedTeam && !
|
|
453
|
+
{/* Credit toggle — only when user has a credit ≥ buy-in */}
|
|
454
|
+
{selectedTeam && !alreadyJoined && canUseCredit && oldestCredit && (
|
|
455
|
+
<TouchableOpacity
|
|
456
|
+
style={[styles.creditRow, { backgroundColor: useCredit ? '#22C55E18' : t.surface, borderColor: useCredit ? '#22C55E' : t.border }]}
|
|
457
|
+
activeOpacity={0.8}
|
|
458
|
+
onPress={() => setUseCredit(v => !v)}
|
|
459
|
+
>
|
|
460
|
+
<View style={styles.creditRowText}>
|
|
461
|
+
<Text style={[styles.creditTitle, { color: useCredit ? '#22C55E' : t.text }]}>
|
|
462
|
+
🎁 Use my {formatSol(oldestCredit.amountSOL)} SOL credit
|
|
463
|
+
</Text>
|
|
464
|
+
<Text style={[styles.creditSub, { color: t.textMuted }]}>
|
|
465
|
+
{useCredit
|
|
466
|
+
? 'Treasury covers this bet — wager locked to credit amount'
|
|
467
|
+
: `${credits.length} credit${credits.length === 1 ? '' : 's'} available from your streak`}
|
|
468
|
+
</Text>
|
|
469
|
+
</View>
|
|
470
|
+
<View style={[styles.creditCheckbox, useCredit && { backgroundColor: '#22C55E', borderColor: '#22C55E' }]}>
|
|
471
|
+
{useCredit && <Text style={styles.creditCheck}>✓</Text>}
|
|
472
|
+
</View>
|
|
473
|
+
</TouchableOpacity>
|
|
474
|
+
)}
|
|
475
|
+
|
|
476
|
+
{/* SOL Slider — hidden when sponsoring with a credit */}
|
|
477
|
+
{selectedTeam && !isPoolModeEnabled && !alreadyJoined && !useCredit && (
|
|
435
478
|
<SolSlider
|
|
436
479
|
value={wager}
|
|
437
480
|
min={game.buyIn}
|
|
@@ -804,4 +847,39 @@ const styles = StyleSheet.create({
|
|
|
804
847
|
alignItems: 'center',
|
|
805
848
|
gap: 10,
|
|
806
849
|
},
|
|
850
|
+
creditRow: {
|
|
851
|
+
flexDirection: 'row',
|
|
852
|
+
alignItems: 'center',
|
|
853
|
+
gap: 12,
|
|
854
|
+
marginTop: 12,
|
|
855
|
+
padding: 12,
|
|
856
|
+
borderRadius: 12,
|
|
857
|
+
borderWidth: 1.5,
|
|
858
|
+
},
|
|
859
|
+
creditRowText: {
|
|
860
|
+
flex: 1,
|
|
861
|
+
gap: 2,
|
|
862
|
+
},
|
|
863
|
+
creditTitle: {
|
|
864
|
+
fontSize: 14,
|
|
865
|
+
fontWeight: '700',
|
|
866
|
+
},
|
|
867
|
+
creditSub: {
|
|
868
|
+
fontSize: 12,
|
|
869
|
+
fontWeight: '500',
|
|
870
|
+
},
|
|
871
|
+
creditCheckbox: {
|
|
872
|
+
width: 22,
|
|
873
|
+
height: 22,
|
|
874
|
+
borderRadius: 11,
|
|
875
|
+
borderWidth: 2,
|
|
876
|
+
borderColor: '#3A3A3C',
|
|
877
|
+
alignItems: 'center',
|
|
878
|
+
justifyContent: 'center',
|
|
879
|
+
},
|
|
880
|
+
creditCheck: {
|
|
881
|
+
color: '#FFFFFF',
|
|
882
|
+
fontSize: 14,
|
|
883
|
+
fontWeight: '900',
|
|
884
|
+
},
|
|
807
885
|
});
|