@dubsdotapp/expo 0.5.32 → 0.5.33
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 +16 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +82 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +82 -4
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/client.ts +25 -0
- package/src/hooks/useJoinGame.ts +3 -0
- package/src/types.ts +16 -0
- package/src/ui/game/JoinGameSheet.tsx +77 -4
package/package.json
CHANGED
package/src/client.ts
CHANGED
|
@@ -215,6 +215,31 @@ export class DubsClient {
|
|
|
215
215
|
}
|
|
216
216
|
|
|
217
217
|
async joinGame(params: JoinGameParams): Promise<JoinGameResult> {
|
|
218
|
+
// Sponsored path: spend an active streak credit instead of buy-in.
|
|
219
|
+
// Server picks the user's oldest active credit (FIFO); we just flag
|
|
220
|
+
// the intent. Result includes promoCode for the confirm step to
|
|
221
|
+
// mark as used.
|
|
222
|
+
if (params.useCredit) {
|
|
223
|
+
const res = await this.request<{
|
|
224
|
+
success: true;
|
|
225
|
+
gameId: string;
|
|
226
|
+
transaction: string;
|
|
227
|
+
gameAddress: string;
|
|
228
|
+
promoCode: string;
|
|
229
|
+
sponsorWallet?: string;
|
|
230
|
+
}>('POST', '/games/join-sponsored', {
|
|
231
|
+
gameId: params.gameId,
|
|
232
|
+
teamChoice: params.teamChoice,
|
|
233
|
+
});
|
|
234
|
+
return {
|
|
235
|
+
gameId: res.gameId,
|
|
236
|
+
transaction: res.transaction,
|
|
237
|
+
gameAddress: res.gameAddress,
|
|
238
|
+
promoCode: res.promoCode,
|
|
239
|
+
sponsorWallet: res.sponsorWallet,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
218
243
|
const res = await this.request<{ success: true } & JoinGameResult>(
|
|
219
244
|
'POST',
|
|
220
245
|
'/games/join',
|
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
|
@@ -130,12 +130,26 @@ export interface JoinGameParams {
|
|
|
130
130
|
gameId: string;
|
|
131
131
|
teamChoice: 'home' | 'away' | 'draw';
|
|
132
132
|
amount: number;
|
|
133
|
+
/**
|
|
134
|
+
* When true, spend one of the user's active 0.01 SOL streak credits
|
|
135
|
+
* instead of paying buy-in from the player's own wallet. The SDK
|
|
136
|
+
* picks the oldest credit (FIFO). The treasury covers buy-in + tx
|
|
137
|
+
* fee; the player only signs to consent. No-op if the user has no
|
|
138
|
+
* active credits — call useCredits() to check before enabling.
|
|
139
|
+
*/
|
|
140
|
+
useCredit?: boolean;
|
|
133
141
|
}
|
|
134
142
|
|
|
135
143
|
export interface JoinGameResult {
|
|
136
144
|
gameId: string;
|
|
137
145
|
transaction: string;
|
|
138
146
|
gameAddress: string;
|
|
147
|
+
/**
|
|
148
|
+
* Set when this join was sponsored. The mutation hook echoes this
|
|
149
|
+
* back to confirmGame so the server can mark the credit as used.
|
|
150
|
+
*/
|
|
151
|
+
promoCode?: string;
|
|
152
|
+
sponsorWallet?: string;
|
|
139
153
|
}
|
|
140
154
|
|
|
141
155
|
// ── Confirm Game ──
|
|
@@ -148,6 +162,8 @@ export interface ConfirmGameParams {
|
|
|
148
162
|
wagerAmount?: number;
|
|
149
163
|
role?: 'creator' | 'joiner';
|
|
150
164
|
gameAddress?: string;
|
|
165
|
+
/** Echoed back from a sponsored joinGame so the server marks the credit as used. */
|
|
166
|
+
promoCode?: string;
|
|
151
167
|
}
|
|
152
168
|
|
|
153
169
|
export interface ConfirmGameResult {
|
|
@@ -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,20 @@ 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
|
+
const [useCredit, setUseCredit] = useState(false);
|
|
101
|
+
const oldestCredit = credits.length > 0 ? credits[0] : null;
|
|
102
|
+
const canUseCredit = !!oldestCredit && oldestCredit.amountSOL >= game.buyIn;
|
|
94
103
|
|
|
95
104
|
const overlayOpacity = useRef(new Animated.Value(0)).current;
|
|
96
105
|
const successScale = useRef(new Animated.Value(0)).current;
|
|
@@ -213,12 +222,18 @@ export function JoinGameSheet({
|
|
|
213
222
|
playerWallet: wallet.publicKey.toBase58(),
|
|
214
223
|
gameId: game.gameId,
|
|
215
224
|
teamChoice: selectedTeam,
|
|
216
|
-
|
|
225
|
+
// When useCredit is on, the on-chain instruction transfers
|
|
226
|
+
// exactly the credit's amount; pass that as the wager.
|
|
227
|
+
amount: useCredit && oldestCredit ? oldestCredit.amountSOL : wager,
|
|
228
|
+
useCredit: useCredit && canUseCredit ? true : undefined,
|
|
217
229
|
});
|
|
230
|
+
// The credit was just spent — refresh the list so the toggle
|
|
231
|
+
// disappears (or rolls to the next FIFO credit).
|
|
232
|
+
if (useCredit) refetchCredits();
|
|
218
233
|
} catch {
|
|
219
234
|
// Error is already captured in mutation state
|
|
220
235
|
}
|
|
221
|
-
}, [selectedTeam, wallet.publicKey, mutation.execute, game.gameId, wager]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
236
|
+
}, [selectedTeam, wallet.publicKey, mutation.execute, game.gameId, wager, useCredit, oldestCredit, canUseCredit, refetchCredits]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
222
237
|
|
|
223
238
|
const statusLabel = STATUS_LABELS[mutation.status] || '';
|
|
224
239
|
|
|
@@ -430,8 +445,31 @@ export function JoinGameSheet({
|
|
|
430
445
|
)}
|
|
431
446
|
</View>
|
|
432
447
|
|
|
433
|
-
{/*
|
|
434
|
-
{selectedTeam && !
|
|
448
|
+
{/* Credit toggle — only when user has a credit ≥ buy-in */}
|
|
449
|
+
{selectedTeam && !alreadyJoined && canUseCredit && oldestCredit && (
|
|
450
|
+
<TouchableOpacity
|
|
451
|
+
style={[styles.creditRow, { backgroundColor: useCredit ? '#22C55E18' : t.surface, borderColor: useCredit ? '#22C55E' : t.border }]}
|
|
452
|
+
activeOpacity={0.8}
|
|
453
|
+
onPress={() => setUseCredit(v => !v)}
|
|
454
|
+
>
|
|
455
|
+
<View style={styles.creditRowText}>
|
|
456
|
+
<Text style={[styles.creditTitle, { color: useCredit ? '#22C55E' : t.text }]}>
|
|
457
|
+
🎁 Use my {formatSol(oldestCredit.amountSOL)} SOL credit
|
|
458
|
+
</Text>
|
|
459
|
+
<Text style={[styles.creditSub, { color: t.textMuted }]}>
|
|
460
|
+
{useCredit
|
|
461
|
+
? 'Treasury covers this bet — wager locked to credit amount'
|
|
462
|
+
: `${credits.length} credit${credits.length === 1 ? '' : 's'} available from your streak`}
|
|
463
|
+
</Text>
|
|
464
|
+
</View>
|
|
465
|
+
<View style={[styles.creditCheckbox, useCredit && { backgroundColor: '#22C55E', borderColor: '#22C55E' }]}>
|
|
466
|
+
{useCredit && <Text style={styles.creditCheck}>✓</Text>}
|
|
467
|
+
</View>
|
|
468
|
+
</TouchableOpacity>
|
|
469
|
+
)}
|
|
470
|
+
|
|
471
|
+
{/* SOL Slider — hidden when sponsoring with a credit */}
|
|
472
|
+
{selectedTeam && !isPoolModeEnabled && !alreadyJoined && !useCredit && (
|
|
435
473
|
<SolSlider
|
|
436
474
|
value={wager}
|
|
437
475
|
min={game.buyIn}
|
|
@@ -804,4 +842,39 @@ const styles = StyleSheet.create({
|
|
|
804
842
|
alignItems: 'center',
|
|
805
843
|
gap: 10,
|
|
806
844
|
},
|
|
845
|
+
creditRow: {
|
|
846
|
+
flexDirection: 'row',
|
|
847
|
+
alignItems: 'center',
|
|
848
|
+
gap: 12,
|
|
849
|
+
marginTop: 12,
|
|
850
|
+
padding: 12,
|
|
851
|
+
borderRadius: 12,
|
|
852
|
+
borderWidth: 1.5,
|
|
853
|
+
},
|
|
854
|
+
creditRowText: {
|
|
855
|
+
flex: 1,
|
|
856
|
+
gap: 2,
|
|
857
|
+
},
|
|
858
|
+
creditTitle: {
|
|
859
|
+
fontSize: 14,
|
|
860
|
+
fontWeight: '700',
|
|
861
|
+
},
|
|
862
|
+
creditSub: {
|
|
863
|
+
fontSize: 12,
|
|
864
|
+
fontWeight: '500',
|
|
865
|
+
},
|
|
866
|
+
creditCheckbox: {
|
|
867
|
+
width: 22,
|
|
868
|
+
height: 22,
|
|
869
|
+
borderRadius: 11,
|
|
870
|
+
borderWidth: 2,
|
|
871
|
+
borderColor: '#3A3A3C',
|
|
872
|
+
alignItems: 'center',
|
|
873
|
+
justifyContent: 'center',
|
|
874
|
+
},
|
|
875
|
+
creditCheck: {
|
|
876
|
+
color: '#FFFFFF',
|
|
877
|
+
fontSize: 14,
|
|
878
|
+
fontWeight: '900',
|
|
879
|
+
},
|
|
807
880
|
});
|