@dubsdotapp/expo 0.5.16 → 0.5.18

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/src/index.ts CHANGED
@@ -77,6 +77,13 @@ export type {
77
77
  BuildArcadeEntryResult,
78
78
  EnterArcadePoolResult,
79
79
  ArcadePoolResult,
80
+ JackpotRound,
81
+ JackpotLastWinner,
82
+ JackpotEntry,
83
+ JackpotRoundResult,
84
+ JackpotConfig,
85
+ BuildJackpotEnterResult,
86
+ ConfirmJackpotEnterResult,
80
87
  } from './types';
81
88
 
82
89
  // Provider
@@ -111,6 +118,9 @@ export {
111
118
  useEnterArcadePool,
112
119
  useArcadeCountdown,
113
120
  useArcadeBridge,
121
+ useJackpot,
122
+ useJackpotHistory,
123
+ useEnterJackpot,
114
124
  } from './hooks';
115
125
  export type {
116
126
  CreateGameMutationResult,
@@ -129,6 +139,9 @@ export type {
129
139
  UseArcadeBridgeResult,
130
140
  HighlightVideo,
131
141
  ShortVideo,
142
+ UseJackpotResult,
143
+ UseJackpotHistoryResult,
144
+ EnterJackpotMutationResult,
132
145
  } from './hooks';
133
146
 
134
147
  export { useHighlights, useShorts } from './hooks';
@@ -155,6 +168,48 @@ export type {
155
168
  ArcadeLeaderboardSheetProps,
156
169
  } from './ui';
157
170
 
171
+ // Jackpot widgets
172
+ export { JackpotCard, JackpotSheet, JackpotWidget } from './ui';
173
+ export type { JackpotCardProps, JackpotSheetProps, JackpotWidgetProps } from './ui';
174
+
175
+ // Chat module
176
+ export {
177
+ ChatProvider,
178
+ useChatContext,
179
+ ChatSocket,
180
+ useChatStatus,
181
+ useChatMessages,
182
+ useSendMessage,
183
+ useOnlineUsers,
184
+ useUnreadCount,
185
+ useConversations,
186
+ useDirectMessages,
187
+ useFriends,
188
+ useFriendRequests,
189
+ useSearchUsers,
190
+ useSendFriendRequest,
191
+ useRespondToFriendRequest,
192
+ } from './chat';
193
+ export type {
194
+ ChatProviderProps,
195
+ ChatContextValue,
196
+ ChatSocketConfig,
197
+ ChatSocketListeners,
198
+ ChatMessage,
199
+ ChatMention,
200
+ ChatPayment,
201
+ DirectMessage,
202
+ Conversation,
203
+ FriendUser,
204
+ FriendRequest,
205
+ OnlineUser,
206
+ TypingEvent,
207
+ ChatNotification,
208
+ ChatConnectionStatus,
209
+ SendMessageParams,
210
+ SendDMParams,
211
+ } from './chat';
212
+
158
213
  // Utils
159
214
  export { signAndSendBase64Transaction } from './utils/transaction';
160
215
  export { getDeviceInfo, isSolanaSeeker } from './utils/device';
package/src/types.ts CHANGED
@@ -581,6 +581,66 @@ export interface ArcadePoolResult {
581
581
  best_score: number;
582
582
  }
583
583
 
584
+ // ── Jackpot ──
585
+
586
+ export interface JackpotRound {
587
+ roundId: string;
588
+ status: 'Open' | 'Locked' | 'Resolved';
589
+ totalPotLamports: string;
590
+ totalPotSol: number;
591
+ entryCount: number;
592
+ totalWeight: string;
593
+ timeRemainingSlots: number;
594
+ }
595
+
596
+ export interface JackpotLastWinner {
597
+ roundId: string;
598
+ winner: string;
599
+ winAmount: string;
600
+ winAmountSol: number;
601
+ totalPot: string;
602
+ entryCount: number;
603
+ timestamp: string | null;
604
+ }
605
+
606
+ export interface JackpotEntry {
607
+ player: string;
608
+ weight: string;
609
+ weightSol: number;
610
+ oddsPercent: string;
611
+ }
612
+
613
+ export interface JackpotRoundResult {
614
+ roundId: string;
615
+ winner: string;
616
+ winAmount: string;
617
+ winAmountSol: number;
618
+ totalPot: string;
619
+ totalPotSol: number;
620
+ entryCount: number;
621
+ timestamp: string;
622
+ }
623
+
624
+ export interface JackpotConfig {
625
+ feeBasisPoints: number;
626
+ roundDurationSlots: string;
627
+ minEntryLamports: number;
628
+ minEntrySol: number;
629
+ }
630
+
631
+ export interface BuildJackpotEnterResult {
632
+ transaction: string;
633
+ roundId: string;
634
+ amount: string;
635
+ amountSol: number;
636
+ }
637
+
638
+ export interface ConfirmJackpotEnterResult {
639
+ attributed: boolean;
640
+ appId: number;
641
+ signature: string;
642
+ }
643
+
584
644
  // ── UI Config (developer branding) ──
585
645
 
586
646
  export interface UiConfig {
@@ -88,7 +88,7 @@ export function JoinGameSheet({
88
88
 
89
89
  const isCustomGame = game.gameMode === CUSTOM_GAME_MODE;
90
90
 
91
- const [selectedTeam, setSelectedTeam] = useState<'home' | 'away' | null>(null);
91
+ const [selectedTeam, setSelectedTeam] = useState<'home' | 'away' | 'draw' | null>(null);
92
92
  const [wager, setWager] = useState(game.buyIn);
93
93
  const [showSuccess, setShowSuccess] = useState(false);
94
94
 
@@ -288,11 +288,14 @@ export function JoinGameSheet({
288
288
 
289
289
  {/* Team Selection — hidden in pool mode and custom games */}
290
290
  {!isCustomGame && !isPoolModeEnabled && !alreadyJoined && (
291
- <View style={styles.section}>
292
- <Text style={[styles.sectionLabel, { color: t.textSecondary }]}>Pick Your Side</Text>
293
- <View style={styles.teamsRow}>
294
- <TeamButton
291
+ hasDrawOption ? (
292
+ /* ── 3-way layout: Home / VS / Away / OR / Draw ── */
293
+ <View style={styles.section}>
294
+ <Text style={[styles.sectionLabel, { color: t.textSecondary }]}>Who will win?</Text>
295
+
296
+ <PickRow
295
297
  name={homeName}
298
+ subtitle="Home Team"
296
299
  imageUrl={opponents[0]?.imageUrl}
297
300
  odds={homeOdds}
298
301
  bets={homeBets}
@@ -302,8 +305,16 @@ export function JoinGameSheet({
302
305
  ImageComponent={ImageComponent}
303
306
  t={t}
304
307
  />
305
- <TeamButton
308
+
309
+ <View style={styles.dividerRow}>
310
+ <View style={[styles.dividerLine, { backgroundColor: t.border }]} />
311
+ <Text style={[styles.dividerText, { color: t.textMuted }]}>VS</Text>
312
+ <View style={[styles.dividerLine, { backgroundColor: t.border }]} />
313
+ </View>
314
+
315
+ <PickRow
306
316
  name={awayName}
317
+ subtitle="Away Team"
307
318
  imageUrl={opponents[1]?.imageUrl}
308
319
  odds={awayOdds}
309
320
  bets={awayBets}
@@ -313,21 +324,54 @@ export function JoinGameSheet({
313
324
  ImageComponent={ImageComponent}
314
325
  t={t}
315
326
  />
327
+
328
+ <View style={styles.dividerRow}>
329
+ <View style={[styles.dividerLine, { backgroundColor: t.border }]} />
330
+ <Text style={[styles.dividerText, { color: t.textMuted }]}>OR</Text>
331
+ <View style={[styles.dividerLine, { backgroundColor: t.border }]} />
332
+ </View>
333
+
334
+ <PickRow
335
+ name="Draw"
336
+ subtitle="Match ends in a tie"
337
+ odds={drawOdds}
338
+ bets={drawBets}
339
+ color={drawColor}
340
+ selected={selectedTeam === 'draw'}
341
+ onPress={() => { setSelectedTeam('draw' as any); onTeamSelect?.('draw' as any); }}
342
+ t={t}
343
+ />
316
344
  </View>
317
- {hasDrawOption && (
318
- <View style={styles.drawRow}>
345
+ ) : (
346
+ /* ── 2-way layout: side by side ── */
347
+ <View style={styles.section}>
348
+ <Text style={[styles.sectionLabel, { color: t.textSecondary }]}>Pick Your Side</Text>
349
+ <View style={styles.teamsRow}>
319
350
  <TeamButton
320
- name="Draw"
321
- odds={drawOdds}
322
- bets={drawBets}
323
- color={drawColor}
324
- selected={selectedTeam === 'draw'}
325
- onPress={() => { setSelectedTeam('draw' as any); onTeamSelect?.('draw' as any); }}
351
+ name={homeName}
352
+ imageUrl={opponents[0]?.imageUrl}
353
+ odds={homeOdds}
354
+ bets={homeBets}
355
+ color={homeColor}
356
+ selected={selectedTeam === 'home'}
357
+ onPress={() => { setSelectedTeam('home'); onTeamSelect?.('home'); }}
358
+ ImageComponent={ImageComponent}
359
+ t={t}
360
+ />
361
+ <TeamButton
362
+ name={awayName}
363
+ imageUrl={opponents[1]?.imageUrl}
364
+ odds={awayOdds}
365
+ bets={awayBets}
366
+ color={awayColor}
367
+ selected={selectedTeam === 'away'}
368
+ onPress={() => { setSelectedTeam('away'); onTeamSelect?.('away'); }}
369
+ ImageComponent={ImageComponent}
326
370
  t={t}
327
371
  />
328
372
  </View>
329
- )}
330
- </View>
373
+ </View>
374
+ )
331
375
  )}
332
376
 
333
377
  {/* Already joined — show which side */}
@@ -443,6 +487,105 @@ export function JoinGameSheet({
443
487
  }
444
488
 
445
489
 
490
+ // ── PickRow (vertical 3-way layout for draw-supported leagues) ──
491
+
492
+ function PickRow({
493
+ name, subtitle, imageUrl, odds, bets, color, selected, onPress, ImageComponent, t,
494
+ }: {
495
+ name: string; subtitle?: string; imageUrl?: string | null; odds: string; bets: number;
496
+ color: string; selected: boolean; onPress: () => void;
497
+ ImageComponent?: React.ComponentType<any>; t: any;
498
+ }) {
499
+ const [imgFailed, setImgFailed] = useState(false);
500
+ const Img = ImageComponent || require('react-native').Image;
501
+ const showImage = imageUrl && !imgFailed;
502
+
503
+ return (
504
+ <TouchableOpacity
505
+ style={[pickStyles.row, { borderColor: selected ? color : t.border, backgroundColor: selected ? color + '12' : t.background }]}
506
+ onPress={onPress}
507
+ activeOpacity={0.7}
508
+ >
509
+ {showImage ? (
510
+ <Img source={{ uri: imageUrl }} style={pickStyles.logo} resizeMode="contain" onError={() => setImgFailed(true)} />
511
+ ) : (
512
+ <View style={[pickStyles.logoPlaceholder, { backgroundColor: color + '20' }]}>
513
+ <Text style={[pickStyles.logoEmoji, { color }]}>{name === 'Draw' ? '🤝' : name.charAt(0)}</Text>
514
+ </View>
515
+ )}
516
+ <View style={pickStyles.info}>
517
+ <Text style={[pickStyles.name, { color: t.text }]}>{name}</Text>
518
+ {subtitle && <Text style={[pickStyles.subtitle, { color: t.textMuted }]}>{subtitle}</Text>}
519
+ </View>
520
+ {odds !== '—' && <Text style={[pickStyles.odds, { color }]}>{odds}x</Text>}
521
+ {bets > 0 && <Text style={[pickStyles.bets, { color: t.textMuted }]}>{bets}</Text>}
522
+ {selected && (
523
+ <View style={[pickStyles.check, { backgroundColor: color }]}>
524
+ <Text style={pickStyles.checkText}>✓</Text>
525
+ </View>
526
+ )}
527
+ </TouchableOpacity>
528
+ );
529
+ }
530
+
531
+ const pickStyles = StyleSheet.create({
532
+ row: {
533
+ flexDirection: 'row',
534
+ alignItems: 'center',
535
+ borderWidth: 1.5,
536
+ borderRadius: 14,
537
+ paddingVertical: 12,
538
+ paddingHorizontal: 14,
539
+ gap: 12,
540
+ },
541
+ logo: {
542
+ width: 36,
543
+ height: 36,
544
+ borderRadius: 18,
545
+ },
546
+ logoPlaceholder: {
547
+ width: 36,
548
+ height: 36,
549
+ borderRadius: 18,
550
+ alignItems: 'center',
551
+ justifyContent: 'center',
552
+ },
553
+ logoEmoji: {
554
+ fontSize: 16,
555
+ fontWeight: '800',
556
+ },
557
+ info: {
558
+ flex: 1,
559
+ },
560
+ name: {
561
+ fontSize: 15,
562
+ fontWeight: '700',
563
+ },
564
+ subtitle: {
565
+ fontSize: 12,
566
+ marginTop: 1,
567
+ },
568
+ odds: {
569
+ fontSize: 16,
570
+ fontWeight: '800',
571
+ },
572
+ bets: {
573
+ fontSize: 12,
574
+ },
575
+ check: {
576
+ width: 22,
577
+ height: 22,
578
+ borderRadius: 11,
579
+ alignItems: 'center',
580
+ justifyContent: 'center',
581
+ },
582
+ checkText: {
583
+ color: '#FFF',
584
+ fontSize: 13,
585
+ fontWeight: '800',
586
+ },
587
+ });
588
+
446
589
  const styles = StyleSheet.create({
447
590
  overlay: {
448
591
  ...StyleSheet.absoluteFillObject,
@@ -568,6 +711,20 @@ const styles = StyleSheet.create({
568
711
  drawRow: {
569
712
  marginTop: 8,
570
713
  },
714
+ dividerRow: {
715
+ flexDirection: 'row',
716
+ alignItems: 'center',
717
+ gap: 12,
718
+ marginVertical: 6,
719
+ },
720
+ dividerLine: {
721
+ flex: 1,
722
+ height: 1,
723
+ },
724
+ dividerText: {
725
+ fontSize: 12,
726
+ fontWeight: '700',
727
+ },
571
728
  summaryCard: {
572
729
  marginTop: 20,
573
730
  borderRadius: 16,
package/src/ui/index.ts CHANGED
@@ -15,6 +15,10 @@ export type { DubsTheme } from './theme';
15
15
 
16
16
  // Game widgets
17
17
  export { GamePoster, LivePoolsCard, PickWinnerCard, PlayersCard, JoinGameButton, CreateCustomGameSheet, CreateGameSheet, JoinGameSheet, ClaimPrizeSheet, ClaimButton, EnterArcadePoolSheet, ArcadeLeaderboardSheet, SolSlider } from './game';
18
+
19
+ // Jackpot widgets
20
+ export { JackpotCard, JackpotSheet, JackpotWidget } from './jackpot';
21
+ export type { JackpotCardProps, JackpotSheetProps, JackpotWidgetProps } from './jackpot';
18
22
  export type {
19
23
  GamePosterProps,
20
24
  LivePoolsCardProps,