@dubsdotapp/expo 0.5.12 → 0.5.14

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dubsdotapp/expo",
3
- "version": "0.5.12",
3
+ "version": "0.5.14",
4
4
  "description": "React Native SDK for the Dubs betting platform",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -17,6 +17,7 @@ import { useCreateGame } from '../../hooks/useCreateGame';
17
17
  import type { CreateGameMutationResult } from '../../hooks/useCreateGame';
18
18
  import type { UnifiedEvent } from '../../types';
19
19
  import { SolSlider } from './SolSlider';
20
+ import { TeamButton } from './TeamButton';
20
21
 
21
22
  export interface CreateGameSheetProps {
22
23
  visible: boolean;
@@ -198,59 +199,30 @@ export function CreateGameSheet({
198
199
  </TouchableOpacity>
199
200
  </View>
200
201
 
201
- {/* Matchup banner */}
202
- <View style={[styles.matchupBanner, { borderColor: t.border }]}>
203
- <View style={styles.matchupTeam}>
204
- {opponents[0]?.imageUrl ? (
205
- <Image source={{ uri: opponents[0].imageUrl }} style={styles.matchupLogo} resizeMode="contain" />
206
- ) : (
207
- <View style={[styles.matchupPlaceholder, { backgroundColor: homeColor + '20' }]}>
208
- <Text style={[styles.matchupInitial, { color: homeColor }]}>{homeName.charAt(0)}</Text>
209
- </View>
210
- )}
211
- <Text style={[styles.matchupName, { color: t.text }]} numberOfLines={1}>{homeName}</Text>
212
- </View>
213
- <Text style={[styles.matchupVs, { color: t.textMuted }]}>vs</Text>
214
- <View style={styles.matchupTeam}>
215
- {opponents[1]?.imageUrl ? (
216
- <Image source={{ uri: opponents[1].imageUrl }} style={styles.matchupLogo} resizeMode="contain" />
217
- ) : (
218
- <View style={[styles.matchupPlaceholder, { backgroundColor: awayColor + '20' }]}>
219
- <Text style={[styles.matchupInitial, { color: awayColor }]}>{awayName.charAt(0)}</Text>
220
- </View>
221
- )}
222
- <Text style={[styles.matchupName, { color: t.text }]} numberOfLines={1}>{awayName}</Text>
223
- </View>
224
- </View>
225
-
226
202
  {/* Team Selection */}
227
203
  <View style={styles.section}>
228
204
  <Text style={[styles.sectionLabel, { color: t.textSecondary }]}>Pick Your Side</Text>
229
205
  <View style={styles.teamsRow}>
230
- <TouchableOpacity
231
- style={[styles.teamOption, { borderColor: selectedTeam === 'home' ? homeColor : t.border, backgroundColor: selectedTeam === 'home' ? homeColor + '15' : t.background }]}
206
+ <TeamButton
207
+ name={homeName}
208
+ imageUrl={opponents[0]?.imageUrl}
209
+ odds="—"
210
+ bets={0}
211
+ color={homeColor}
212
+ selected={selectedTeam === 'home'}
232
213
  onPress={() => { setSelectedTeam('home'); onTeamSelect?.('home'); }}
233
- activeOpacity={0.7}
234
- >
235
- <Text style={[styles.teamLabel, { color: t.text }]}>{homeName}</Text>
236
- {selectedTeam === 'home' && (
237
- <View style={[styles.teamBadge, { backgroundColor: homeColor }]}>
238
- <Text style={styles.teamBadgeText}>Selected</Text>
239
- </View>
240
- )}
241
- </TouchableOpacity>
242
- <TouchableOpacity
243
- style={[styles.teamOption, { borderColor: selectedTeam === 'away' ? awayColor : t.border, backgroundColor: selectedTeam === 'away' ? awayColor + '15' : t.background }]}
214
+ t={t}
215
+ />
216
+ <TeamButton
217
+ name={awayName}
218
+ imageUrl={opponents[1]?.imageUrl}
219
+ odds="—"
220
+ bets={0}
221
+ color={awayColor}
222
+ selected={selectedTeam === 'away'}
244
223
  onPress={() => { setSelectedTeam('away'); onTeamSelect?.('away'); }}
245
- activeOpacity={0.7}
246
- >
247
- <Text style={[styles.teamLabel, { color: t.text }]}>{awayName}</Text>
248
- {selectedTeam === 'away' && (
249
- <View style={[styles.teamBadge, { backgroundColor: awayColor }]}>
250
- <Text style={styles.teamBadgeText}>Selected</Text>
251
- </View>
252
- )}
253
- </TouchableOpacity>
224
+ t={t}
225
+ />
254
226
  </View>
255
227
  </View>
256
228
 
@@ -329,21 +301,9 @@ const styles = StyleSheet.create({
329
301
  headerSub: { fontSize: 13, marginTop: 2 },
330
302
  closeButton: { fontSize: 20, padding: 4 },
331
303
 
332
- matchupBanner: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', paddingVertical: 16, borderBottomWidth: 1, gap: 16 },
333
- matchupTeam: { flex: 1, alignItems: 'center', gap: 6 },
334
- matchupLogo: { width: 40, height: 40 },
335
- matchupPlaceholder: { width: 40, height: 40, borderRadius: 20, alignItems: 'center', justifyContent: 'center' },
336
- matchupInitial: { fontSize: 18, fontWeight: '800' },
337
- matchupName: { fontSize: 13, fontWeight: '600', textAlign: 'center' },
338
- matchupVs: { fontSize: 13, fontWeight: '600' },
339
-
340
304
  section: { gap: 10, paddingTop: 12 },
341
305
  sectionLabel: { fontSize: 14, fontWeight: '600' },
342
- teamsRow: { flexDirection: 'row', gap: 10 },
343
- teamOption: { flex: 1, borderWidth: 2, borderRadius: 14, paddingVertical: 14, alignItems: 'center', gap: 6 },
344
- teamLabel: { fontSize: 15, fontWeight: '700' },
345
- teamBadge: { borderRadius: 8, paddingHorizontal: 10, paddingVertical: 3 },
346
- teamBadgeText: { color: '#FFF', fontSize: 11, fontWeight: '700' },
306
+ teamsRow: { flexDirection: 'row', gap: 12 },
347
307
 
348
308
  summaryCard: { marginTop: 12, borderRadius: 14, borderWidth: 1, overflow: 'hidden' },
349
309
  summaryRow: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingHorizontal: 16, paddingVertical: 12 },
@@ -17,6 +17,7 @@ import { useJoinGame } from '../../hooks/useJoinGame';
17
17
  import type { JoinGameMutationResult } from '../../hooks/useJoinGame';
18
18
  import type { GameDetail } from '../../types';
19
19
  import { SolSlider } from './SolSlider';
20
+ import { TeamButton } from './TeamButton';
20
21
 
21
22
  export interface JoinGameSheetProps {
22
23
  visible: boolean;
@@ -412,41 +413,6 @@ export function JoinGameSheet({
412
413
  );
413
414
  }
414
415
 
415
- // ── TeamButton (internal, for sports/esports games) ──
416
-
417
- function TeamButton({
418
- name, imageUrl, odds, bets, color, selected, onPress, ImageComponent, t,
419
- }: {
420
- name: string; imageUrl?: string | null; odds: string; bets: number;
421
- color: string; selected: boolean; onPress: () => void;
422
- ImageComponent?: React.ComponentType<any>; t: any;
423
- }) {
424
- const [imgFailed, setImgFailed] = useState(false);
425
- const Img = ImageComponent || require('react-native').Image;
426
- const showImage = imageUrl && !imgFailed;
427
-
428
- return (
429
- <TouchableOpacity
430
- style={[styles.teamOption, { borderColor: selected ? color : t.border, backgroundColor: selected ? color + '15' : t.background }]}
431
- onPress={onPress}
432
- activeOpacity={0.7}
433
- >
434
- {showImage ? (
435
- <Img source={{ uri: imageUrl }} style={styles.teamLogo} resizeMode="contain" onError={() => setImgFailed(true)} />
436
- ) : (
437
- <View style={[styles.teamLogo, styles.teamLogoPlaceholder]} />
438
- )}
439
- <Text style={[styles.teamName, { color: t.text }]} numberOfLines={1}>{name}</Text>
440
- <Text style={[styles.teamOdds, { color }]}>{odds}x</Text>
441
- <Text style={[styles.teamBets, { color: t.textMuted }]}>{bets} {bets === 1 ? 'bet' : 'bets'}</Text>
442
- {selected && (
443
- <View style={[styles.teamBadge, { backgroundColor: color }]}>
444
- <Text style={styles.teamBadgeText}>Selected</Text>
445
- </View>
446
- )}
447
- </TouchableOpacity>
448
- );
449
- }
450
416
 
451
417
  const styles = StyleSheet.create({
452
418
  overlay: {
@@ -642,43 +608,4 @@ const styles = StyleSheet.create({
642
608
  alignItems: 'center',
643
609
  gap: 10,
644
610
  },
645
- // Team button styles
646
- teamOption: {
647
- flex: 1,
648
- borderWidth: 2,
649
- borderRadius: 16,
650
- padding: 16,
651
- alignItems: 'center',
652
- gap: 8,
653
- },
654
- teamLogo: {
655
- width: 48,
656
- height: 48,
657
- borderRadius: 24,
658
- },
659
- teamLogoPlaceholder: {
660
- backgroundColor: 'rgba(128,128,128,0.2)',
661
- },
662
- teamName: {
663
- fontSize: 15,
664
- fontWeight: '700',
665
- },
666
- teamOdds: {
667
- fontSize: 20,
668
- fontWeight: '800',
669
- },
670
- teamBets: {
671
- fontSize: 12,
672
- },
673
- teamBadge: {
674
- borderRadius: 8,
675
- paddingHorizontal: 12,
676
- paddingVertical: 4,
677
- marginTop: 4,
678
- },
679
- teamBadgeText: {
680
- color: '#FFF',
681
- fontSize: 12,
682
- fontWeight: '700',
683
- },
684
611
  });
@@ -0,0 +1,85 @@
1
+ import React, { useState } from 'react';
2
+ import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
3
+
4
+ export interface TeamButtonProps {
5
+ name: string;
6
+ imageUrl?: string | null;
7
+ odds: string;
8
+ bets: number;
9
+ color: string;
10
+ selected: boolean;
11
+ onPress: () => void;
12
+ ImageComponent?: React.ComponentType<any>;
13
+ t: any;
14
+ }
15
+
16
+ export function TeamButton({
17
+ name, imageUrl, odds, bets, color, selected, onPress, ImageComponent, t,
18
+ }: TeamButtonProps) {
19
+ const [imgFailed, setImgFailed] = useState(false);
20
+ const Img = ImageComponent || require('react-native').Image;
21
+ const showImage = imageUrl && !imgFailed;
22
+
23
+ return (
24
+ <TouchableOpacity
25
+ style={[styles.teamOption, { borderColor: selected ? color : t.border, backgroundColor: selected ? color + '15' : t.background }]}
26
+ onPress={onPress}
27
+ activeOpacity={0.7}
28
+ >
29
+ {showImage ? (
30
+ <Img source={{ uri: imageUrl }} style={styles.teamLogo} resizeMode="contain" onError={() => setImgFailed(true)} />
31
+ ) : (
32
+ <View style={[styles.teamLogo, styles.teamLogoPlaceholder]} />
33
+ )}
34
+ <Text style={[styles.teamName, { color: t.text }]} numberOfLines={1}>{name}</Text>
35
+ {odds !== '—' && <Text style={[styles.teamOdds, { color }]}>{odds}x</Text>}
36
+ {bets > 0 && <Text style={[styles.teamBets, { color: t.textMuted }]}>{bets} {bets === 1 ? 'bet' : 'bets'}</Text>}
37
+ {selected && (
38
+ <View style={[styles.teamBadge, { backgroundColor: color }]}>
39
+ <Text style={styles.teamBadgeText}>Selected</Text>
40
+ </View>
41
+ )}
42
+ </TouchableOpacity>
43
+ );
44
+ }
45
+
46
+ const styles = StyleSheet.create({
47
+ teamOption: {
48
+ flex: 1,
49
+ borderWidth: 2,
50
+ borderRadius: 16,
51
+ padding: 16,
52
+ alignItems: 'center',
53
+ gap: 8,
54
+ },
55
+ teamLogo: {
56
+ width: 48,
57
+ height: 48,
58
+ borderRadius: 24,
59
+ },
60
+ teamLogoPlaceholder: {
61
+ backgroundColor: 'rgba(128,128,128,0.2)',
62
+ },
63
+ teamName: {
64
+ fontSize: 15,
65
+ fontWeight: '700',
66
+ },
67
+ teamOdds: {
68
+ fontSize: 20,
69
+ fontWeight: '800',
70
+ },
71
+ teamBets: {
72
+ fontSize: 12,
73
+ },
74
+ teamBadge: {
75
+ borderRadius: 8,
76
+ paddingHorizontal: 12,
77
+ paddingVertical: 4,
78
+ marginTop: 4,
79
+ },
80
+ teamBadgeText: {
81
+ color: '#FFF',
82
+ fontSize: 12,
83
+ fontWeight: '700',
84
+ },
85
+ });