@dubsdotapp/expo 0.3.2 → 0.3.4

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.js CHANGED
@@ -40,6 +40,7 @@ __export(index_exports, {
40
40
  DubsApiError: () => DubsApiError,
41
41
  DubsClient: () => DubsClient,
42
42
  DubsProvider: () => DubsProvider,
43
+ EnterArcadePoolSheet: () => EnterArcadePoolSheet,
43
44
  GamePoster: () => GamePoster,
44
45
  JoinGameButton: () => JoinGameButton,
45
46
  JoinGameSheet: () => JoinGameSheet,
@@ -62,12 +63,16 @@ __export(index_exports, {
62
63
  parseSolanaError: () => parseSolanaError,
63
64
  signAndSendBase64Transaction: () => signAndSendBase64Transaction,
64
65
  useAppConfig: () => useAppConfig,
66
+ useArcadeGame: () => useArcadeGame,
67
+ useArcadePool: () => useArcadePool,
68
+ useArcadePools: () => useArcadePools,
65
69
  useAuth: () => useAuth,
66
70
  useClaim: () => useClaim,
67
71
  useCreateCustomGame: () => useCreateCustomGame,
68
72
  useCreateGame: () => useCreateGame,
69
73
  useDubs: () => useDubs,
70
74
  useDubsTheme: () => useDubsTheme,
75
+ useEnterArcadePool: () => useEnterArcadePool,
71
76
  useEvents: () => useEvents,
72
77
  useGame: () => useGame,
73
78
  useGames: () => useGames,
@@ -585,6 +590,81 @@ var DubsClient = class {
585
590
  getErrorCodesLocal() {
586
591
  return { ...SOLANA_PROGRAM_ERRORS };
587
592
  }
593
+ // ── Arcade Pools ──
594
+ async getArcadePools(params) {
595
+ const qs = new URLSearchParams();
596
+ if (params?.gameSlug) qs.set("gameSlug", params.gameSlug);
597
+ if (params?.status) qs.set("status", params.status);
598
+ const query = qs.toString();
599
+ const res = await this.request(
600
+ "GET",
601
+ `/arcade/pools${query ? `?${query}` : ""}`
602
+ );
603
+ return res.pools;
604
+ }
605
+ async getArcadePool(poolId) {
606
+ const res = await this.request(
607
+ "GET",
608
+ `/arcade/pools/${poolId}`
609
+ );
610
+ return { pool: res.pool, stats: res.stats };
611
+ }
612
+ /** Build unsigned entry transaction for an arcade pool */
613
+ async buildArcadeEntry(poolId, walletAddress) {
614
+ const res = await this.request(
615
+ "POST",
616
+ `/arcade/pools/${poolId}/build-enter`,
617
+ { walletAddress }
618
+ );
619
+ return {
620
+ transaction: res.transaction,
621
+ poolId: res.poolId,
622
+ amount: res.amount,
623
+ destination: res.destination
624
+ };
625
+ }
626
+ async enterArcadePool(poolId, params) {
627
+ const res = await this.request(
628
+ "POST",
629
+ `/arcade/pools/${poolId}/enter`,
630
+ params
631
+ );
632
+ return res.entry;
633
+ }
634
+ async startArcadeAttempt(poolId, walletAddress) {
635
+ const res = await this.request(
636
+ "POST",
637
+ `/arcade/pools/${poolId}/start-attempt`,
638
+ { walletAddress }
639
+ );
640
+ return { sessionToken: res.sessionToken, attemptNumber: res.attemptNumber, livesRemaining: res.livesRemaining };
641
+ }
642
+ async submitArcadeScore(poolId, params) {
643
+ const res = await this.request(
644
+ "POST",
645
+ `/arcade/pools/${poolId}/submit-score`,
646
+ params
647
+ );
648
+ return { score: res.score, bestScore: res.bestScore, livesUsed: res.livesUsed, isNewBest: res.isNewBest };
649
+ }
650
+ async getArcadeLeaderboard(poolId, params) {
651
+ const qs = new URLSearchParams();
652
+ if (params?.limit != null) qs.set("limit", String(params.limit));
653
+ if (params?.offset != null) qs.set("offset", String(params.offset));
654
+ const query = qs.toString();
655
+ const res = await this.request(
656
+ "GET",
657
+ `/arcade/pools/${poolId}/leaderboard${query ? `?${query}` : ""}`
658
+ );
659
+ return res.leaderboard;
660
+ }
661
+ async getArcadeEntry(poolId, walletAddress) {
662
+ const res = await this.request(
663
+ "GET",
664
+ `/arcade/pools/${poolId}/my-entry?walletAddress=${encodeURIComponent(walletAddress)}`
665
+ );
666
+ return res.entry;
667
+ }
588
668
  // ── App Config ──
589
669
  /** Fetch the app's UI customization config (accent color, icon, tagline, environment) */
590
670
  async getAppConfig() {
@@ -636,7 +716,7 @@ function createSecureStoreStorage() {
636
716
  }
637
717
 
638
718
  // src/provider.tsx
639
- var import_react18 = require("react");
719
+ var import_react22 = require("react");
640
720
 
641
721
  // src/ui/theme.ts
642
722
  var import_react = require("react");
@@ -1694,7 +1774,7 @@ function ManagedWalletProvider({
1694
1774
  }
1695
1775
 
1696
1776
  // src/ui/AuthGate.tsx
1697
- var import_react17 = __toESM(require("react"));
1777
+ var import_react21 = __toESM(require("react"));
1698
1778
  var import_react_native8 = require("react-native");
1699
1779
 
1700
1780
  // src/hooks/useEvents.ts
@@ -2526,6 +2606,196 @@ function usePushNotifications() {
2526
2606
  };
2527
2607
  }
2528
2608
 
2609
+ // src/hooks/useArcadePools.ts
2610
+ var import_react17 = require("react");
2611
+ function useArcadePools(gameSlug) {
2612
+ const { client } = useDubs();
2613
+ const [pools, setPools] = (0, import_react17.useState)([]);
2614
+ const [loading, setLoading] = (0, import_react17.useState)(false);
2615
+ const [error, setError] = (0, import_react17.useState)(null);
2616
+ const fetch2 = (0, import_react17.useCallback)(async () => {
2617
+ setLoading(true);
2618
+ setError(null);
2619
+ try {
2620
+ const result = await client.getArcadePools({ gameSlug });
2621
+ setPools(result);
2622
+ } catch (err) {
2623
+ setError(err instanceof Error ? err : new Error(String(err)));
2624
+ } finally {
2625
+ setLoading(false);
2626
+ }
2627
+ }, [client, gameSlug]);
2628
+ (0, import_react17.useEffect)(() => {
2629
+ fetch2();
2630
+ }, [fetch2]);
2631
+ return { pools, loading, error, refetch: fetch2 };
2632
+ }
2633
+
2634
+ // src/hooks/useArcadePool.ts
2635
+ var import_react18 = require("react");
2636
+ function useArcadePool(poolId) {
2637
+ const { client } = useDubs();
2638
+ const [pool, setPool] = (0, import_react18.useState)(null);
2639
+ const [stats, setStats] = (0, import_react18.useState)(null);
2640
+ const [leaderboard, setLeaderboard] = (0, import_react18.useState)([]);
2641
+ const [loading, setLoading] = (0, import_react18.useState)(false);
2642
+ const [error, setError] = (0, import_react18.useState)(null);
2643
+ const fetch2 = (0, import_react18.useCallback)(async () => {
2644
+ if (!poolId) return;
2645
+ setLoading(true);
2646
+ setError(null);
2647
+ try {
2648
+ const [poolRes, lb] = await Promise.all([
2649
+ client.getArcadePool(poolId),
2650
+ client.getArcadeLeaderboard(poolId)
2651
+ ]);
2652
+ setPool(poolRes.pool);
2653
+ setStats(poolRes.stats);
2654
+ setLeaderboard(lb);
2655
+ } catch (err) {
2656
+ setError(err instanceof Error ? err : new Error(String(err)));
2657
+ } finally {
2658
+ setLoading(false);
2659
+ }
2660
+ }, [client, poolId]);
2661
+ (0, import_react18.useEffect)(() => {
2662
+ fetch2();
2663
+ }, [fetch2]);
2664
+ return { pool, stats, leaderboard, loading, error, refetch: fetch2 };
2665
+ }
2666
+
2667
+ // src/hooks/useArcadeGame.ts
2668
+ var import_react19 = require("react");
2669
+ function useArcadeGame(poolId, maxLives = 3) {
2670
+ const { client } = useDubs();
2671
+ const { user } = useAuth();
2672
+ const [entry, setEntry] = (0, import_react19.useState)(null);
2673
+ const [loading, setLoading] = (0, import_react19.useState)(false);
2674
+ const [error, setError] = (0, import_react19.useState)(null);
2675
+ const walletAddress = user?.walletAddress || "";
2676
+ const refreshEntry = (0, import_react19.useCallback)(async () => {
2677
+ if (!poolId || !walletAddress) return;
2678
+ setLoading(true);
2679
+ setError(null);
2680
+ try {
2681
+ const e = await client.getArcadeEntry(poolId, walletAddress);
2682
+ setEntry(e);
2683
+ } catch (err) {
2684
+ if (err instanceof Error && err.message.includes("No entry found")) {
2685
+ setEntry(null);
2686
+ } else {
2687
+ setError(err instanceof Error ? err : new Error(String(err)));
2688
+ }
2689
+ } finally {
2690
+ setLoading(false);
2691
+ }
2692
+ }, [client, poolId, walletAddress]);
2693
+ const startAttempt = (0, import_react19.useCallback)(async () => {
2694
+ if (!poolId || !walletAddress) throw new Error("Not ready");
2695
+ setError(null);
2696
+ try {
2697
+ const result = await client.startArcadeAttempt(poolId, walletAddress);
2698
+ return result;
2699
+ } catch (err) {
2700
+ const e = err instanceof Error ? err : new Error(String(err));
2701
+ setError(e);
2702
+ throw e;
2703
+ }
2704
+ }, [client, poolId, walletAddress]);
2705
+ const submitScore = (0, import_react19.useCallback)(async (sessionToken, score, durationMs) => {
2706
+ if (!poolId || !walletAddress) throw new Error("Not ready");
2707
+ setError(null);
2708
+ try {
2709
+ const result = await client.submitArcadeScore(poolId, {
2710
+ walletAddress,
2711
+ sessionToken,
2712
+ score,
2713
+ durationMs
2714
+ });
2715
+ setEntry((prev) => prev ? {
2716
+ ...prev,
2717
+ best_score: result.bestScore,
2718
+ lives_used: result.livesUsed
2719
+ } : prev);
2720
+ return result;
2721
+ } catch (err) {
2722
+ const e = err instanceof Error ? err : new Error(String(err));
2723
+ setError(e);
2724
+ throw e;
2725
+ }
2726
+ }, [client, poolId, walletAddress]);
2727
+ const livesRemaining = entry ? maxLives - entry.lives_used : maxLives;
2728
+ const bestScore = entry?.best_score || 0;
2729
+ return {
2730
+ entry,
2731
+ livesRemaining,
2732
+ bestScore,
2733
+ loading,
2734
+ error,
2735
+ refreshEntry,
2736
+ startAttempt,
2737
+ submitScore
2738
+ };
2739
+ }
2740
+
2741
+ // src/hooks/useEnterArcadePool.ts
2742
+ var import_react20 = require("react");
2743
+ function useEnterArcadePool() {
2744
+ const { client, wallet, connection } = useDubs();
2745
+ const { user } = useAuth();
2746
+ const [status, setStatus] = (0, import_react20.useState)("idle");
2747
+ const [error, setError] = (0, import_react20.useState)(null);
2748
+ const [data, setData] = (0, import_react20.useState)(null);
2749
+ const reset = (0, import_react20.useCallback)(() => {
2750
+ setStatus("idle");
2751
+ setError(null);
2752
+ setData(null);
2753
+ }, []);
2754
+ const execute = (0, import_react20.useCallback)(async (poolId) => {
2755
+ if (!wallet.publicKey) throw new Error("Wallet not connected");
2756
+ const walletAddress = wallet.publicKey.toBase58();
2757
+ setStatus("building");
2758
+ setError(null);
2759
+ setData(null);
2760
+ try {
2761
+ console.log("[useEnterArcadePool] Step 1: Building transaction...", { poolId, walletAddress });
2762
+ const buildResult = await client.buildArcadeEntry(poolId, walletAddress);
2763
+ console.log("[useEnterArcadePool] Step 1 done:", { poolId: buildResult.poolId, amount: buildResult.amount });
2764
+ setStatus("signing");
2765
+ console.log("[useEnterArcadePool] Step 2: Signing and sending...");
2766
+ const signature = await signAndSendBase64Transaction(
2767
+ buildResult.transaction,
2768
+ wallet,
2769
+ connection
2770
+ );
2771
+ console.log("[useEnterArcadePool] Step 2 done. Signature:", signature);
2772
+ setStatus("confirming");
2773
+ console.log("[useEnterArcadePool] Step 3: Confirming with backend...");
2774
+ const entry = await client.enterArcadePool(poolId, {
2775
+ walletAddress,
2776
+ txSignature: signature
2777
+ });
2778
+ console.log("[useEnterArcadePool] Step 3 done. Entry recorded.");
2779
+ const result = {
2780
+ poolId,
2781
+ signature,
2782
+ entry
2783
+ };
2784
+ setData(result);
2785
+ setStatus("success");
2786
+ console.log("[useEnterArcadePool] Complete!");
2787
+ return result;
2788
+ } catch (err) {
2789
+ console.error("[useEnterArcadePool] FAILED at status:", status, err);
2790
+ const e = err instanceof Error ? err : new Error(String(err));
2791
+ setError(e);
2792
+ setStatus("error");
2793
+ throw e;
2794
+ }
2795
+ }, [client, wallet, connection]);
2796
+ return { execute, status, error, data, reset };
2797
+ }
2798
+
2529
2799
  // src/ui/AvatarEditor.tsx
2530
2800
  var import_react_native7 = require("react-native");
2531
2801
  var import_jsx_runtime3 = require("react/jsx-runtime");
@@ -2686,11 +2956,11 @@ function AuthGate({
2686
2956
  }) {
2687
2957
  const { client, pushEnabled } = useDubs();
2688
2958
  const auth = useAuth();
2689
- const [phase, setPhase] = (0, import_react17.useState)("init");
2690
- const [registrationPhase, setRegistrationPhase] = (0, import_react17.useState)(false);
2691
- const [showPushSetup, setShowPushSetup] = (0, import_react17.useState)(false);
2692
- const [isRestoredSession, setIsRestoredSession] = (0, import_react17.useState)(false);
2693
- (0, import_react17.useEffect)(() => {
2959
+ const [phase, setPhase] = (0, import_react21.useState)("init");
2960
+ const [registrationPhase, setRegistrationPhase] = (0, import_react21.useState)(false);
2961
+ const [showPushSetup, setShowPushSetup] = (0, import_react21.useState)(false);
2962
+ const [isRestoredSession, setIsRestoredSession] = (0, import_react21.useState)(false);
2963
+ (0, import_react21.useEffect)(() => {
2694
2964
  let cancelled = false;
2695
2965
  (async () => {
2696
2966
  try {
@@ -2717,23 +2987,23 @@ function AuthGate({
2717
2987
  cancelled = true;
2718
2988
  };
2719
2989
  }, []);
2720
- (0, import_react17.useEffect)(() => {
2990
+ (0, import_react21.useEffect)(() => {
2721
2991
  if (auth.status === "needsRegistration") setRegistrationPhase(true);
2722
2992
  }, [auth.status]);
2723
- (0, import_react17.useEffect)(() => {
2993
+ (0, import_react21.useEffect)(() => {
2724
2994
  if (pushEnabled && auth.status === "authenticated" && registrationPhase && !isRestoredSession) {
2725
2995
  setShowPushSetup(true);
2726
2996
  }
2727
2997
  }, [pushEnabled, auth.status, registrationPhase, isRestoredSession]);
2728
- (0, import_react17.useEffect)(() => {
2998
+ (0, import_react21.useEffect)(() => {
2729
2999
  if (auth.token) onSaveToken(auth.token);
2730
3000
  }, [auth.token]);
2731
- const retry = (0, import_react17.useCallback)(() => {
3001
+ const retry = (0, import_react21.useCallback)(() => {
2732
3002
  setRegistrationPhase(false);
2733
3003
  auth.reset();
2734
3004
  auth.authenticate();
2735
3005
  }, [auth]);
2736
- const handleRegister = (0, import_react17.useCallback)(
3006
+ const handleRegister = (0, import_react21.useCallback)(
2737
3007
  (username, referralCode, avatarUrl) => {
2738
3008
  auth.register(username, referralCode, avatarUrl);
2739
3009
  },
@@ -2825,7 +3095,7 @@ function DefaultErrorScreen({ error, onRetry, appName, accentColor }) {
2825
3095
  function StepIndicator({ currentStep }) {
2826
3096
  const t = useDubsTheme();
2827
3097
  const steps = [0, 1, 2, 3];
2828
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native8.View, { style: s.stepRow, children: steps.map((i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react17.default.Fragment, { children: [
3098
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native8.View, { style: s.stepRow, children: steps.map((i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react21.default.Fragment, { children: [
2829
3099
  i > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native8.View, { style: [s.stepLine, { backgroundColor: i <= currentStep ? t.success : t.border }] }),
2830
3100
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2831
3101
  import_react_native8.View,
@@ -2849,20 +3119,20 @@ function DefaultRegistrationScreen({
2849
3119
  }) {
2850
3120
  const t = useDubsTheme();
2851
3121
  const accent = accentColor || t.accent;
2852
- const [step, setStep] = (0, import_react17.useState)(0);
2853
- const [avatarSeed, setAvatarSeed] = (0, import_react17.useState)(generateSeed);
2854
- const [avatarStyle, setAvatarStyle] = (0, import_react17.useState)("adventurer");
2855
- const [avatarBg, setAvatarBg] = (0, import_react17.useState)("1a1a2e");
2856
- const [showStyles, setShowStyles] = (0, import_react17.useState)(false);
2857
- const [username, setUsername] = (0, import_react17.useState)("");
2858
- const [referralCode, setReferralCode] = (0, import_react17.useState)("");
2859
- const [checking, setChecking] = (0, import_react17.useState)(false);
2860
- const [availability, setAvailability] = (0, import_react17.useState)(null);
2861
- const debounceRef = (0, import_react17.useRef)(null);
2862
- const fadeAnim = (0, import_react17.useRef)(new import_react_native8.Animated.Value(1)).current;
2863
- const slideAnim = (0, import_react17.useRef)(new import_react_native8.Animated.Value(0)).current;
3122
+ const [step, setStep] = (0, import_react21.useState)(0);
3123
+ const [avatarSeed, setAvatarSeed] = (0, import_react21.useState)(generateSeed);
3124
+ const [avatarStyle, setAvatarStyle] = (0, import_react21.useState)("adventurer");
3125
+ const [avatarBg, setAvatarBg] = (0, import_react21.useState)("1a1a2e");
3126
+ const [showStyles, setShowStyles] = (0, import_react21.useState)(false);
3127
+ const [username, setUsername] = (0, import_react21.useState)("");
3128
+ const [referralCode, setReferralCode] = (0, import_react21.useState)("");
3129
+ const [checking, setChecking] = (0, import_react21.useState)(false);
3130
+ const [availability, setAvailability] = (0, import_react21.useState)(null);
3131
+ const debounceRef = (0, import_react21.useRef)(null);
3132
+ const fadeAnim = (0, import_react21.useRef)(new import_react_native8.Animated.Value(1)).current;
3133
+ const slideAnim = (0, import_react21.useRef)(new import_react_native8.Animated.Value(0)).current;
2864
3134
  const avatarUrl = getAvatarUrl(avatarStyle, avatarSeed, avatarBg);
2865
- (0, import_react17.useEffect)(() => {
3135
+ (0, import_react21.useEffect)(() => {
2866
3136
  if (debounceRef.current) clearTimeout(debounceRef.current);
2867
3137
  const trimmed = username.trim();
2868
3138
  if (trimmed.length < 3) {
@@ -2885,7 +3155,7 @@ function DefaultRegistrationScreen({
2885
3155
  if (debounceRef.current) clearTimeout(debounceRef.current);
2886
3156
  };
2887
3157
  }, [username, client]);
2888
- const animateToStep = (0, import_react17.useCallback)((newStep) => {
3158
+ const animateToStep = (0, import_react21.useCallback)((newStep) => {
2889
3159
  const dir = newStep > step ? 1 : -1;
2890
3160
  import_react_native8.Keyboard.dismiss();
2891
3161
  import_react_native8.Animated.parallel([
@@ -3124,8 +3394,8 @@ function DefaultRegistrationScreen({
3124
3394
  }
3125
3395
  function PushTokenRestorer() {
3126
3396
  const push = usePushNotifications();
3127
- const restored = (0, import_react17.useRef)(false);
3128
- (0, import_react17.useEffect)(() => {
3397
+ const restored = (0, import_react21.useRef)(false);
3398
+ (0, import_react21.useEffect)(() => {
3129
3399
  if (restored.current) return;
3130
3400
  restored.current = true;
3131
3401
  push.restoreIfGranted();
@@ -3140,9 +3410,9 @@ function PushSetupScreen({
3140
3410
  const t = useDubsTheme();
3141
3411
  const accent = accentColor || t.accent;
3142
3412
  const push = usePushNotifications();
3143
- const fadeAnim = (0, import_react17.useRef)(new import_react_native8.Animated.Value(0)).current;
3144
- const slideAnim = (0, import_react17.useRef)(new import_react_native8.Animated.Value(30)).current;
3145
- (0, import_react17.useEffect)(() => {
3413
+ const fadeAnim = (0, import_react21.useRef)(new import_react_native8.Animated.Value(0)).current;
3414
+ const slideAnim = (0, import_react21.useRef)(new import_react_native8.Animated.Value(30)).current;
3415
+ (0, import_react21.useEffect)(() => {
3146
3416
  import_react_native8.Animated.parallel([
3147
3417
  import_react_native8.Animated.timing(fadeAnim, { toValue: 1, duration: 300, useNativeDriver: true }),
3148
3418
  import_react_native8.Animated.timing(slideAnim, { toValue: 0, duration: 300, useNativeDriver: true })
@@ -3278,7 +3548,7 @@ var s = import_react_native8.StyleSheet.create({
3278
3548
 
3279
3549
  // src/provider.tsx
3280
3550
  var import_jsx_runtime5 = require("react/jsx-runtime");
3281
- var DubsContext = (0, import_react18.createContext)(null);
3551
+ var DubsContext = (0, import_react22.createContext)(null);
3282
3552
  function DubsProvider({
3283
3553
  apiKey,
3284
3554
  children,
@@ -3300,11 +3570,11 @@ function DubsProvider({
3300
3570
  const config = NETWORK_CONFIG[network];
3301
3571
  const baseUrl = baseUrlOverride || config.baseUrl;
3302
3572
  const rpcUrl = rpcUrlOverride || config.rpcUrl;
3303
- const client = (0, import_react18.useMemo)(() => new DubsClient({ apiKey, baseUrl }), [apiKey, baseUrl]);
3304
- const storage = (0, import_react18.useMemo)(() => tokenStorage || createSecureStoreStorage(), [tokenStorage]);
3305
- const [uiConfig, setUiConfig] = (0, import_react18.useState)(null);
3306
- const [resolvedNetwork, setResolvedNetwork] = (0, import_react18.useState)(network);
3307
- (0, import_react18.useEffect)(() => {
3573
+ const client = (0, import_react22.useMemo)(() => new DubsClient({ apiKey, baseUrl }), [apiKey, baseUrl]);
3574
+ const storage = (0, import_react22.useMemo)(() => tokenStorage || createSecureStoreStorage(), [tokenStorage]);
3575
+ const [uiConfig, setUiConfig] = (0, import_react22.useState)(null);
3576
+ const [resolvedNetwork, setResolvedNetwork] = (0, import_react22.useState)(network);
3577
+ (0, import_react22.useEffect)(() => {
3308
3578
  client.getAppConfig().then((cfg) => {
3309
3579
  console.log("[DubsProvider] UI config loaded:", JSON.stringify(cfg));
3310
3580
  setUiConfig(cfg);
@@ -3320,7 +3590,7 @@ function DubsProvider({
3320
3590
  const resolvedConfig = NETWORK_CONFIG[resolvedNetwork];
3321
3591
  const resolvedRpcUrl = rpcUrlOverride || resolvedConfig.rpcUrl;
3322
3592
  const cluster = resolvedConfig.cluster;
3323
- const connection = (0, import_react18.useMemo)(() => new import_web34.Connection(resolvedRpcUrl, { commitment: "confirmed" }), [resolvedRpcUrl]);
3593
+ const connection = (0, import_react22.useMemo)(() => new import_web34.Connection(resolvedRpcUrl, { commitment: "confirmed" }), [resolvedRpcUrl]);
3324
3594
  if (uiConfig === null) return null;
3325
3595
  const themeOverrides = {};
3326
3596
  if (uiConfig.accentColor) {
@@ -3396,11 +3666,11 @@ function ManagedInner({
3396
3666
  children
3397
3667
  }) {
3398
3668
  const managedDisconnect = useDisconnect();
3399
- const disconnect = (0, import_react18.useCallback)(async () => {
3669
+ const disconnect = (0, import_react22.useCallback)(async () => {
3400
3670
  client.setToken(null);
3401
3671
  await managedDisconnect?.();
3402
3672
  }, [client, managedDisconnect]);
3403
- const value = (0, import_react18.useMemo)(
3673
+ const value = (0, import_react22.useMemo)(
3404
3674
  () => ({ client, wallet, connection, appName, network, disconnect, uiConfig, pushEnabled }),
3405
3675
  [client, wallet, connection, appName, network, disconnect, uiConfig, pushEnabled]
3406
3676
  );
@@ -3437,13 +3707,13 @@ function ExternalWalletProvider({
3437
3707
  pushEnabled,
3438
3708
  children
3439
3709
  }) {
3440
- const disconnect = (0, import_react18.useCallback)(async () => {
3710
+ const disconnect = (0, import_react22.useCallback)(async () => {
3441
3711
  client.setToken(null);
3442
3712
  await storage.deleteItem(STORAGE_KEYS.JWT_TOKEN).catch(() => {
3443
3713
  });
3444
3714
  await wallet.disconnect?.();
3445
3715
  }, [client, storage, wallet]);
3446
- const value = (0, import_react18.useMemo)(
3716
+ const value = (0, import_react22.useMemo)(
3447
3717
  () => ({ client, wallet, connection, appName, network, disconnect, uiConfig, pushEnabled }),
3448
3718
  [client, wallet, connection, appName, network, disconnect, uiConfig, pushEnabled]
3449
3719
  );
@@ -3468,19 +3738,19 @@ function ExternalWalletProvider({
3468
3738
  ) });
3469
3739
  }
3470
3740
  function useDubs() {
3471
- const ctx = (0, import_react18.useContext)(DubsContext);
3741
+ const ctx = (0, import_react22.useContext)(DubsContext);
3472
3742
  if (!ctx) {
3473
3743
  throw new Error("useDubs must be used within a <DubsProvider>");
3474
3744
  }
3475
3745
  return ctx;
3476
3746
  }
3477
3747
  function useAppConfig() {
3478
- const ctx = (0, import_react18.useContext)(DubsContext);
3748
+ const ctx = (0, import_react22.useContext)(DubsContext);
3479
3749
  return ctx?.uiConfig || {};
3480
3750
  }
3481
3751
 
3482
3752
  // src/ui/UserProfileCard.tsx
3483
- var import_react19 = require("react");
3753
+ var import_react23 = require("react");
3484
3754
  var import_react_native9 = require("react-native");
3485
3755
  var import_jsx_runtime6 = require("react/jsx-runtime");
3486
3756
  function truncateAddress(address, chars = 4) {
@@ -3500,7 +3770,7 @@ function UserProfileCard({
3500
3770
  memberSince
3501
3771
  }) {
3502
3772
  const t = useDubsTheme();
3503
- const imageUri = (0, import_react19.useMemo)(
3773
+ const imageUri = (0, import_react23.useMemo)(
3504
3774
  () => ensurePngAvatar(avatarUrl) || `https://api.dicebear.com/9.x/avataaars/png?seed=${walletAddress}&size=128`,
3505
3775
  [avatarUrl, walletAddress]
3506
3776
  );
@@ -3693,7 +3963,7 @@ var styles4 = import_react_native10.StyleSheet.create({
3693
3963
  });
3694
3964
 
3695
3965
  // src/ui/UserProfileSheet.tsx
3696
- var import_react20 = require("react");
3966
+ var import_react24 = require("react");
3697
3967
  var import_react_native11 = require("react-native");
3698
3968
  var import_jsx_runtime8 = require("react/jsx-runtime");
3699
3969
  function truncateAddress3(address, chars = 4) {
@@ -3711,31 +3981,31 @@ function UserProfileSheet({
3711
3981
  const { client } = useDubs();
3712
3982
  const { refreshUser } = useAuth();
3713
3983
  const push = usePushNotifications();
3714
- const overlayOpacity = (0, import_react20.useRef)(new import_react_native11.Animated.Value(0)).current;
3715
- const parsed = (0, import_react20.useMemo)(() => parseAvatarUrl(user.avatar), [user.avatar]);
3716
- const [avatarStyle, setAvatarStyle] = (0, import_react20.useState)(parsed.style);
3717
- const [avatarSeed, setAvatarSeed] = (0, import_react20.useState)(parsed.seed);
3718
- const [bgColor, setBgColor] = (0, import_react20.useState)(parsed.bg);
3719
- const [saving, setSaving] = (0, import_react20.useState)(false);
3720
- const [error, setError] = (0, import_react20.useState)(null);
3721
- (0, import_react20.useEffect)(() => {
3984
+ const overlayOpacity = (0, import_react24.useRef)(new import_react_native11.Animated.Value(0)).current;
3985
+ const parsed = (0, import_react24.useMemo)(() => parseAvatarUrl(user.avatar), [user.avatar]);
3986
+ const [avatarStyle, setAvatarStyle] = (0, import_react24.useState)(parsed.style);
3987
+ const [avatarSeed, setAvatarSeed] = (0, import_react24.useState)(parsed.seed);
3988
+ const [bgColor, setBgColor] = (0, import_react24.useState)(parsed.bg);
3989
+ const [saving, setSaving] = (0, import_react24.useState)(false);
3990
+ const [error, setError] = (0, import_react24.useState)(null);
3991
+ (0, import_react24.useEffect)(() => {
3722
3992
  const p = parseAvatarUrl(user.avatar);
3723
3993
  setAvatarStyle(p.style);
3724
3994
  setAvatarSeed(p.seed);
3725
3995
  setBgColor(p.bg);
3726
3996
  }, [user.avatar]);
3727
- (0, import_react20.useEffect)(() => {
3997
+ (0, import_react24.useEffect)(() => {
3728
3998
  import_react_native11.Animated.timing(overlayOpacity, {
3729
3999
  toValue: visible ? 1 : 0,
3730
4000
  duration: 250,
3731
4001
  useNativeDriver: true
3732
4002
  }).start();
3733
4003
  }, [visible, overlayOpacity]);
3734
- (0, import_react20.useEffect)(() => {
4004
+ (0, import_react24.useEffect)(() => {
3735
4005
  if (visible) setError(null);
3736
4006
  }, [visible]);
3737
4007
  const currentAvatarUrl = getAvatarUrl(avatarStyle, avatarSeed, bgColor);
3738
- const saveAvatar = (0, import_react20.useCallback)(async (newUrl) => {
4008
+ const saveAvatar = (0, import_react24.useCallback)(async (newUrl) => {
3739
4009
  setSaving(true);
3740
4010
  setError(null);
3741
4011
  try {
@@ -3748,16 +4018,16 @@ function UserProfileSheet({
3748
4018
  setSaving(false);
3749
4019
  }
3750
4020
  }, [client, refreshUser, onAvatarUpdated]);
3751
- const handleStyleChange = (0, import_react20.useCallback)((style) => {
4021
+ const handleStyleChange = (0, import_react24.useCallback)((style) => {
3752
4022
  setAvatarStyle(style);
3753
4023
  saveAvatar(getAvatarUrl(style, avatarSeed, bgColor));
3754
4024
  }, [avatarSeed, bgColor, saveAvatar]);
3755
- const handleShuffle = (0, import_react20.useCallback)(() => {
4025
+ const handleShuffle = (0, import_react24.useCallback)(() => {
3756
4026
  const newSeed = generateSeed();
3757
4027
  setAvatarSeed(newSeed);
3758
4028
  saveAvatar(getAvatarUrl(avatarStyle, newSeed, bgColor));
3759
4029
  }, [avatarStyle, bgColor, saveAvatar]);
3760
- const handleBgChange = (0, import_react20.useCallback)((color) => {
4030
+ const handleBgChange = (0, import_react24.useCallback)((color) => {
3761
4031
  setBgColor(color);
3762
4032
  saveAvatar(getAvatarUrl(avatarStyle, avatarSeed, color));
3763
4033
  }, [avatarStyle, avatarSeed, saveAvatar]);
@@ -4037,7 +4307,7 @@ var styles5 = import_react_native11.StyleSheet.create({
4037
4307
  });
4038
4308
 
4039
4309
  // src/ui/game/GamePoster.tsx
4040
- var import_react21 = require("react");
4310
+ var import_react25 = require("react");
4041
4311
  var import_react_native12 = require("react-native");
4042
4312
  var import_jsx_runtime9 = require("react/jsx-runtime");
4043
4313
  function computeCountdown(lockTimestamp) {
@@ -4087,7 +4357,7 @@ function GamePoster({ game, ImageComponent }) {
4087
4357
  ] });
4088
4358
  }
4089
4359
  function TeamLogoInternal({ url, size, Img }) {
4090
- const [failed, setFailed] = (0, import_react21.useState)(false);
4360
+ const [failed, setFailed] = (0, import_react25.useState)(false);
4091
4361
  if (!url || failed) {
4092
4362
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native12.View, { style: [styles6.logoPlaceholder, { width: size, height: size, borderRadius: size / 2 }] });
4093
4363
  }
@@ -4188,7 +4458,7 @@ var styles6 = import_react_native12.StyleSheet.create({
4188
4458
  });
4189
4459
 
4190
4460
  // src/ui/game/LivePoolsCard.tsx
4191
- var import_react22 = require("react");
4461
+ var import_react26 = require("react");
4192
4462
  var import_react_native13 = require("react-native");
4193
4463
  var import_jsx_runtime10 = require("react/jsx-runtime");
4194
4464
  function LivePoolsCard({
@@ -4204,7 +4474,7 @@ function LivePoolsCard({
4204
4474
  const homePool = game.homePool || 0;
4205
4475
  const awayPool = game.awayPool || 0;
4206
4476
  const totalPool = game.totalPool || 0;
4207
- const { homePercent, awayPercent, homeOdds, awayOdds } = (0, import_react22.useMemo)(() => {
4477
+ const { homePercent, awayPercent, homeOdds, awayOdds } = (0, import_react26.useMemo)(() => {
4208
4478
  return {
4209
4479
  homePercent: totalPool > 0 ? homePool / totalPool * 100 : 50,
4210
4480
  awayPercent: totalPool > 0 ? awayPool / totalPool * 100 : 50,
@@ -4267,7 +4537,7 @@ var styles7 = import_react_native13.StyleSheet.create({
4267
4537
  });
4268
4538
 
4269
4539
  // src/ui/game/PickWinnerCard.tsx
4270
- var import_react23 = require("react");
4540
+ var import_react27 = require("react");
4271
4541
  var import_react_native14 = require("react-native");
4272
4542
  var import_jsx_runtime11 = require("react/jsx-runtime");
4273
4543
  function PickWinnerCard({
@@ -4285,7 +4555,7 @@ function PickWinnerCard({
4285
4555
  const totalPool = game.totalPool || 0;
4286
4556
  const homePool = game.homePool || 0;
4287
4557
  const awayPool = game.awayPool || 0;
4288
- const { homeOdds, awayOdds, homeBets, awayBets } = (0, import_react23.useMemo)(() => ({
4558
+ const { homeOdds, awayOdds, homeBets, awayBets } = (0, import_react27.useMemo)(() => ({
4289
4559
  homeOdds: homePool > 0 ? (totalPool / homePool).toFixed(2) : "\u2014",
4290
4560
  awayOdds: awayPool > 0 ? (totalPool / awayPool).toFixed(2) : "\u2014",
4291
4561
  homeBets: bettors.filter((b) => b.team === "home").length,
@@ -4338,7 +4608,7 @@ function TeamOption({
4338
4608
  ImageComponent,
4339
4609
  t
4340
4610
  }) {
4341
- const [imgFailed, setImgFailed] = (0, import_react23.useState)(false);
4611
+ const [imgFailed, setImgFailed] = (0, import_react27.useState)(false);
4342
4612
  const Img = ImageComponent || require("react-native").Image;
4343
4613
  const showImage = imageUrl && !imgFailed;
4344
4614
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
@@ -4379,7 +4649,7 @@ var styles8 = import_react_native14.StyleSheet.create({
4379
4649
  });
4380
4650
 
4381
4651
  // src/ui/game/PlayersCard.tsx
4382
- var import_react24 = require("react");
4652
+ var import_react28 = require("react");
4383
4653
  var import_react_native15 = require("react-native");
4384
4654
  var import_jsx_runtime12 = require("react/jsx-runtime");
4385
4655
  function truncateWallet(addr, chars) {
@@ -4428,7 +4698,7 @@ function BettorRow({
4428
4698
  ImageComponent,
4429
4699
  t
4430
4700
  }) {
4431
- const [imgFailed, setImgFailed] = (0, import_react24.useState)(false);
4701
+ const [imgFailed, setImgFailed] = (0, import_react28.useState)(false);
4432
4702
  const Img = ImageComponent || require("react-native").Image;
4433
4703
  const showAvatar = bettor.avatar && !imgFailed;
4434
4704
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react_native15.View, { style: [styles9.row, !isFirst && { borderTopColor: t.border, borderTopWidth: 1 }], children: [
@@ -4455,7 +4725,7 @@ var styles9 = import_react_native15.StyleSheet.create({
4455
4725
  });
4456
4726
 
4457
4727
  // src/ui/game/JoinGameButton.tsx
4458
- var import_react25 = require("react");
4728
+ var import_react29 = require("react");
4459
4729
  var import_react_native16 = require("react-native");
4460
4730
  var import_jsx_runtime13 = require("react/jsx-runtime");
4461
4731
  var STATUS_LABELS = {
@@ -4466,7 +4736,7 @@ var STATUS_LABELS = {
4466
4736
  };
4467
4737
  function JoinGameButton({ game, walletAddress, selectedTeam, status, onJoin }) {
4468
4738
  const t = useDubsTheme();
4469
- const alreadyJoined = (0, import_react25.useMemo)(() => {
4739
+ const alreadyJoined = (0, import_react29.useMemo)(() => {
4470
4740
  if (!walletAddress) return false;
4471
4741
  return (game.bettors || []).some((b) => b.wallet === walletAddress);
4472
4742
  }, [game.bettors, walletAddress]);
@@ -4507,7 +4777,7 @@ var styles10 = import_react_native16.StyleSheet.create({
4507
4777
  });
4508
4778
 
4509
4779
  // src/ui/game/CreateCustomGameSheet.tsx
4510
- var import_react26 = require("react");
4780
+ var import_react30 = require("react");
4511
4781
  var import_react_native17 = require("react-native");
4512
4782
  var import_jsx_runtime14 = require("react/jsx-runtime");
4513
4783
  var STATUS_LABELS2 = {
@@ -4533,18 +4803,18 @@ function CreateCustomGameSheet({
4533
4803
  const t = useDubsTheme();
4534
4804
  const { wallet } = useDubs();
4535
4805
  const mutation = useCreateCustomGame();
4536
- const [selectedAmount, setSelectedAmount] = (0, import_react26.useState)(null);
4537
- const [customAmount, setCustomAmount] = (0, import_react26.useState)("");
4538
- const [isCustom, setIsCustom] = (0, import_react26.useState)(false);
4539
- const overlayOpacity = (0, import_react26.useRef)(new import_react_native17.Animated.Value(0)).current;
4540
- (0, import_react26.useEffect)(() => {
4806
+ const [selectedAmount, setSelectedAmount] = (0, import_react30.useState)(null);
4807
+ const [customAmount, setCustomAmount] = (0, import_react30.useState)("");
4808
+ const [isCustom, setIsCustom] = (0, import_react30.useState)(false);
4809
+ const overlayOpacity = (0, import_react30.useRef)(new import_react_native17.Animated.Value(0)).current;
4810
+ (0, import_react30.useEffect)(() => {
4541
4811
  import_react_native17.Animated.timing(overlayOpacity, {
4542
4812
  toValue: visible ? 1 : 0,
4543
4813
  duration: 250,
4544
4814
  useNativeDriver: true
4545
4815
  }).start();
4546
4816
  }, [visible, overlayOpacity]);
4547
- (0, import_react26.useEffect)(() => {
4817
+ (0, import_react30.useEffect)(() => {
4548
4818
  if (visible) {
4549
4819
  setSelectedAmount(defaultAmount ?? null);
4550
4820
  setCustomAmount("");
@@ -4552,7 +4822,7 @@ function CreateCustomGameSheet({
4552
4822
  mutation.reset();
4553
4823
  }
4554
4824
  }, [visible]);
4555
- (0, import_react26.useEffect)(() => {
4825
+ (0, import_react30.useEffect)(() => {
4556
4826
  if (mutation.status === "success" && mutation.data) {
4557
4827
  onSuccess?.(mutation.data);
4558
4828
  const timer = setTimeout(() => {
@@ -4561,23 +4831,23 @@ function CreateCustomGameSheet({
4561
4831
  return () => clearTimeout(timer);
4562
4832
  }
4563
4833
  }, [mutation.status, mutation.data]);
4564
- (0, import_react26.useEffect)(() => {
4834
+ (0, import_react30.useEffect)(() => {
4565
4835
  if (mutation.status === "error" && mutation.error) {
4566
4836
  onError?.(mutation.error);
4567
4837
  }
4568
4838
  }, [mutation.status, mutation.error]);
4569
- const handlePresetSelect = (0, import_react26.useCallback)((amount) => {
4839
+ const handlePresetSelect = (0, import_react30.useCallback)((amount) => {
4570
4840
  setSelectedAmount(amount);
4571
4841
  setIsCustom(false);
4572
4842
  setCustomAmount("");
4573
4843
  onAmountChange?.(amount);
4574
4844
  }, [onAmountChange]);
4575
- const handleCustomSelect = (0, import_react26.useCallback)(() => {
4845
+ const handleCustomSelect = (0, import_react30.useCallback)(() => {
4576
4846
  setIsCustom(true);
4577
4847
  setSelectedAmount(null);
4578
4848
  onAmountChange?.(null);
4579
4849
  }, [onAmountChange]);
4580
- const handleCustomAmountChange = (0, import_react26.useCallback)((text) => {
4850
+ const handleCustomAmountChange = (0, import_react30.useCallback)((text) => {
4581
4851
  const cleaned = text.replace(/[^0-9.]/g, "").replace(/(\..*?)\..*/g, "$1");
4582
4852
  setCustomAmount(cleaned);
4583
4853
  const parsed = parseFloat(cleaned);
@@ -4592,7 +4862,7 @@ function CreateCustomGameSheet({
4592
4862
  const winnerTakes = pot * (1 - fee / 100);
4593
4863
  const isMutating = mutation.status !== "idle" && mutation.status !== "success" && mutation.status !== "error";
4594
4864
  const canCreate = finalAmount !== null && finalAmount > 0 && !isMutating && mutation.status !== "success";
4595
- const handleCreate = (0, import_react26.useCallback)(async () => {
4865
+ const handleCreate = (0, import_react30.useCallback)(async () => {
4596
4866
  if (!finalAmount || !wallet.publicKey) return;
4597
4867
  try {
4598
4868
  await mutation.execute({
@@ -4861,7 +5131,7 @@ var styles11 = import_react_native17.StyleSheet.create({
4861
5131
  });
4862
5132
 
4863
5133
  // src/ui/game/JoinGameSheet.tsx
4864
- var import_react27 = require("react");
5134
+ var import_react31 = require("react");
4865
5135
  var import_react_native18 = require("react-native");
4866
5136
  var import_jsx_runtime15 = require("react/jsx-runtime");
4867
5137
  var STATUS_LABELS3 = {
@@ -4887,22 +5157,22 @@ function JoinGameSheet({
4887
5157
  const { wallet } = useDubs();
4888
5158
  const mutation = useJoinGame();
4889
5159
  const isCustomGame = game.gameMode === CUSTOM_GAME_MODE;
4890
- const [selectedTeam, setSelectedTeam] = (0, import_react27.useState)(null);
4891
- const overlayOpacity = (0, import_react27.useRef)(new import_react_native18.Animated.Value(0)).current;
4892
- (0, import_react27.useEffect)(() => {
5160
+ const [selectedTeam, setSelectedTeam] = (0, import_react31.useState)(null);
5161
+ const overlayOpacity = (0, import_react31.useRef)(new import_react_native18.Animated.Value(0)).current;
5162
+ (0, import_react31.useEffect)(() => {
4893
5163
  import_react_native18.Animated.timing(overlayOpacity, {
4894
5164
  toValue: visible ? 1 : 0,
4895
5165
  duration: 250,
4896
5166
  useNativeDriver: true
4897
5167
  }).start();
4898
5168
  }, [visible, overlayOpacity]);
4899
- (0, import_react27.useEffect)(() => {
5169
+ (0, import_react31.useEffect)(() => {
4900
5170
  if (visible) {
4901
5171
  setSelectedTeam(isPoolModeEnabled ? "home" : isCustomGame ? "away" : null);
4902
5172
  mutation.reset();
4903
5173
  }
4904
5174
  }, [visible]);
4905
- (0, import_react27.useEffect)(() => {
5175
+ (0, import_react31.useEffect)(() => {
4906
5176
  if (mutation.status === "success" && mutation.data) {
4907
5177
  onSuccess?.(mutation.data);
4908
5178
  const timer = setTimeout(() => {
@@ -4911,7 +5181,7 @@ function JoinGameSheet({
4911
5181
  return () => clearTimeout(timer);
4912
5182
  }
4913
5183
  }, [mutation.status, mutation.data]);
4914
- (0, import_react27.useEffect)(() => {
5184
+ (0, import_react31.useEffect)(() => {
4915
5185
  if (mutation.status === "error" && mutation.error) {
4916
5186
  onError?.(mutation.error);
4917
5187
  }
@@ -4922,7 +5192,7 @@ function JoinGameSheet({
4922
5192
  const homePool = game.homePool || 0;
4923
5193
  const awayPool = game.awayPool || 0;
4924
5194
  const buyIn = game.buyIn;
4925
- const { homeOdds, awayOdds, homeBets, awayBets } = (0, import_react27.useMemo)(() => ({
5195
+ const { homeOdds, awayOdds, homeBets, awayBets } = (0, import_react31.useMemo)(() => ({
4926
5196
  homeOdds: homePool > 0 ? (totalPool / homePool).toFixed(2) : "\u2014",
4927
5197
  awayOdds: awayPool > 0 ? (totalPool / awayPool).toFixed(2) : "\u2014",
4928
5198
  homeBets: bettors.filter((b) => b.team === "home").length,
@@ -4934,14 +5204,14 @@ function JoinGameSheet({
4934
5204
  const homeName = shortName ? shortName(opponents[0]?.name) : opponents[0]?.name || "Home";
4935
5205
  const awayName = shortName ? shortName(opponents[1]?.name) : opponents[1]?.name || "Away";
4936
5206
  const selectedName = selectedTeam === "home" ? homeName : selectedTeam === "away" ? awayName : "\u2014";
4937
- const alreadyJoined = (0, import_react27.useMemo)(() => {
5207
+ const alreadyJoined = (0, import_react31.useMemo)(() => {
4938
5208
  if (!wallet.publicKey) return false;
4939
5209
  const addr = wallet.publicKey.toBase58();
4940
5210
  return bettors.some((b) => b.wallet === addr);
4941
5211
  }, [bettors, wallet.publicKey]);
4942
5212
  const isMutating = mutation.status !== "idle" && mutation.status !== "success" && mutation.status !== "error";
4943
5213
  const canJoin = selectedTeam !== null && !isMutating && mutation.status !== "success" && !alreadyJoined;
4944
- const handleJoin = (0, import_react27.useCallback)(async () => {
5214
+ const handleJoin = (0, import_react31.useCallback)(async () => {
4945
5215
  if (!selectedTeam || !wallet.publicKey) return;
4946
5216
  try {
4947
5217
  await mutation.execute({
@@ -5085,7 +5355,7 @@ function TeamButton({
5085
5355
  ImageComponent,
5086
5356
  t
5087
5357
  }) {
5088
- const [imgFailed, setImgFailed] = (0, import_react27.useState)(false);
5358
+ const [imgFailed, setImgFailed] = (0, import_react31.useState)(false);
5089
5359
  const Img = ImageComponent || require("react-native").Image;
5090
5360
  const showImage = imageUrl && !imgFailed;
5091
5361
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
@@ -5262,7 +5532,7 @@ var styles12 = import_react_native18.StyleSheet.create({
5262
5532
  });
5263
5533
 
5264
5534
  // src/ui/game/ClaimPrizeSheet.tsx
5265
- var import_react28 = require("react");
5535
+ var import_react32 = require("react");
5266
5536
  var import_react_native19 = require("react-native");
5267
5537
  var import_jsx_runtime16 = require("react/jsx-runtime");
5268
5538
  var STATUS_LABELS4 = {
@@ -5283,18 +5553,18 @@ function ClaimPrizeSheet({
5283
5553
  const t = useDubsTheme();
5284
5554
  const { wallet } = useDubs();
5285
5555
  const mutation = useClaim();
5286
- const overlayOpacity = (0, import_react28.useRef)(new import_react_native19.Animated.Value(0)).current;
5287
- const celebrationScale = (0, import_react28.useRef)(new import_react_native19.Animated.Value(0)).current;
5288
- const celebrationOpacity = (0, import_react28.useRef)(new import_react_native19.Animated.Value(0)).current;
5289
- const [showCelebration, setShowCelebration] = (0, import_react28.useState)(false);
5290
- (0, import_react28.useEffect)(() => {
5556
+ const overlayOpacity = (0, import_react32.useRef)(new import_react_native19.Animated.Value(0)).current;
5557
+ const celebrationScale = (0, import_react32.useRef)(new import_react_native19.Animated.Value(0)).current;
5558
+ const celebrationOpacity = (0, import_react32.useRef)(new import_react_native19.Animated.Value(0)).current;
5559
+ const [showCelebration, setShowCelebration] = (0, import_react32.useState)(false);
5560
+ (0, import_react32.useEffect)(() => {
5291
5561
  import_react_native19.Animated.timing(overlayOpacity, {
5292
5562
  toValue: visible ? 1 : 0,
5293
5563
  duration: 250,
5294
5564
  useNativeDriver: true
5295
5565
  }).start();
5296
5566
  }, [visible, overlayOpacity]);
5297
- (0, import_react28.useEffect)(() => {
5567
+ (0, import_react32.useEffect)(() => {
5298
5568
  if (visible) {
5299
5569
  mutation.reset();
5300
5570
  setShowCelebration(false);
@@ -5302,7 +5572,7 @@ function ClaimPrizeSheet({
5302
5572
  celebrationOpacity.setValue(0);
5303
5573
  }
5304
5574
  }, [visible]);
5305
- (0, import_react28.useEffect)(() => {
5575
+ (0, import_react32.useEffect)(() => {
5306
5576
  if (mutation.status === "success" && mutation.data) {
5307
5577
  setShowCelebration(true);
5308
5578
  import_react_native19.Animated.parallel([
@@ -5325,14 +5595,14 @@ function ClaimPrizeSheet({
5325
5595
  return () => clearTimeout(timer);
5326
5596
  }
5327
5597
  }, [mutation.status, mutation.data]);
5328
- (0, import_react28.useEffect)(() => {
5598
+ (0, import_react32.useEffect)(() => {
5329
5599
  if (mutation.status === "error" && mutation.error) {
5330
5600
  onError?.(mutation.error);
5331
5601
  }
5332
5602
  }, [mutation.status, mutation.error]);
5333
5603
  const isMutating = mutation.status !== "idle" && mutation.status !== "success" && mutation.status !== "error";
5334
5604
  const canClaim = !isMutating && mutation.status !== "success" && !!wallet.publicKey;
5335
- const handleClaim = (0, import_react28.useCallback)(async () => {
5605
+ const handleClaim = (0, import_react32.useCallback)(async () => {
5336
5606
  if (!wallet.publicKey) return;
5337
5607
  try {
5338
5608
  await mutation.execute({
@@ -5565,7 +5835,7 @@ var styles13 = import_react_native19.StyleSheet.create({
5565
5835
  });
5566
5836
 
5567
5837
  // src/ui/game/ClaimButton.tsx
5568
- var import_react29 = require("react");
5838
+ var import_react33 = require("react");
5569
5839
  var import_react_native20 = require("react-native");
5570
5840
  var import_jsx_runtime17 = require("react/jsx-runtime");
5571
5841
  function ClaimButton({ gameId, style, onSuccess, onError }) {
@@ -5573,9 +5843,9 @@ function ClaimButton({ gameId, style, onSuccess, onError }) {
5573
5843
  const { wallet } = useDubs();
5574
5844
  const game = useGame(gameId);
5575
5845
  const claimStatus = useHasClaimed(gameId);
5576
- const [sheetVisible, setSheetVisible] = (0, import_react29.useState)(false);
5846
+ const [sheetVisible, setSheetVisible] = (0, import_react33.useState)(false);
5577
5847
  const walletAddress = wallet.publicKey?.toBase58() ?? null;
5578
- const myBet = (0, import_react29.useMemo)(() => {
5848
+ const myBet = (0, import_react33.useMemo)(() => {
5579
5849
  if (!walletAddress || !game.data?.bettors) return null;
5580
5850
  return game.data.bettors.find((b) => b.wallet === walletAddress) ?? null;
5581
5851
  }, [walletAddress, game.data?.bettors]);
@@ -5584,7 +5854,7 @@ function ClaimButton({ gameId, style, onSuccess, onError }) {
5584
5854
  const isWinner = isResolved && myBet != null && myBet.team === game.data?.winnerSide;
5585
5855
  const isEligible = myBet != null && isResolved && (isWinner || isRefund);
5586
5856
  const prizeAmount = isRefund ? myBet?.amount ?? 0 : game.data?.totalPool ?? 0;
5587
- const handleSuccess = (0, import_react29.useCallback)(
5857
+ const handleSuccess = (0, import_react33.useCallback)(
5588
5858
  (result) => {
5589
5859
  claimStatus.refetch();
5590
5860
  onSuccess?.(result);
@@ -5669,6 +5939,206 @@ var styles14 = import_react_native20.StyleSheet.create({
5669
5939
  fontWeight: "700"
5670
5940
  }
5671
5941
  });
5942
+
5943
+ // src/ui/game/EnterArcadePoolSheet.tsx
5944
+ var import_react34 = require("react");
5945
+ var import_react_native21 = require("react-native");
5946
+ var import_jsx_runtime18 = require("react/jsx-runtime");
5947
+ var STATUS_LABELS5 = {
5948
+ building: "Building transaction...",
5949
+ signing: "Approve in wallet...",
5950
+ confirming: "Confirming...",
5951
+ success: "Joined!"
5952
+ };
5953
+ function EnterArcadePoolSheet({
5954
+ visible,
5955
+ onDismiss,
5956
+ pool,
5957
+ stats,
5958
+ onSuccess,
5959
+ onError
5960
+ }) {
5961
+ const t = useDubsTheme();
5962
+ const { wallet } = useDubs();
5963
+ const mutation = useEnterArcadePool();
5964
+ const overlayOpacity = (0, import_react34.useRef)(new import_react_native21.Animated.Value(0)).current;
5965
+ (0, import_react34.useEffect)(() => {
5966
+ import_react_native21.Animated.timing(overlayOpacity, {
5967
+ toValue: visible ? 1 : 0,
5968
+ duration: 250,
5969
+ useNativeDriver: true
5970
+ }).start();
5971
+ }, [visible, overlayOpacity]);
5972
+ (0, import_react34.useEffect)(() => {
5973
+ if (visible) {
5974
+ mutation.reset();
5975
+ }
5976
+ }, [visible]);
5977
+ (0, import_react34.useEffect)(() => {
5978
+ if (mutation.status === "success" && mutation.data) {
5979
+ onSuccess?.(mutation.data);
5980
+ const timer = setTimeout(() => {
5981
+ onDismiss();
5982
+ }, 1500);
5983
+ return () => clearTimeout(timer);
5984
+ }
5985
+ }, [mutation.status, mutation.data]);
5986
+ (0, import_react34.useEffect)(() => {
5987
+ if (mutation.status === "error" && mutation.error) {
5988
+ onError?.(mutation.error);
5989
+ }
5990
+ }, [mutation.status, mutation.error]);
5991
+ const buyInSol = (pool.buy_in_lamports / 1e9).toFixed(4);
5992
+ const totalPlayers = stats?.total_entries ?? pool.total_entries ?? 0;
5993
+ const topScore = stats?.top_score ?? 0;
5994
+ const potSol = (pool.buy_in_lamports * Number(totalPlayers) / 1e9).toFixed(4);
5995
+ const isMutating = mutation.status !== "idle" && mutation.status !== "success" && mutation.status !== "error";
5996
+ const canJoin = !isMutating && mutation.status !== "success";
5997
+ const handleJoin = (0, import_react34.useCallback)(async () => {
5998
+ if (!wallet.publicKey) return;
5999
+ try {
6000
+ await mutation.execute(pool.id);
6001
+ } catch {
6002
+ }
6003
+ }, [wallet.publicKey, mutation.execute, pool.id]);
6004
+ const statusLabel = STATUS_LABELS5[mutation.status] || "";
6005
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
6006
+ import_react_native21.Modal,
6007
+ {
6008
+ visible,
6009
+ animationType: "slide",
6010
+ transparent: true,
6011
+ onRequestClose: onDismiss,
6012
+ children: [
6013
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native21.Animated.View, { style: [styles15.overlay, { opacity: overlayOpacity }], children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native21.TouchableOpacity, { style: styles15.overlayTap, activeOpacity: 1, onPress: onDismiss }) }),
6014
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
6015
+ import_react_native21.KeyboardAvoidingView,
6016
+ {
6017
+ style: styles15.keyboardView,
6018
+ behavior: import_react_native21.Platform.OS === "ios" ? "padding" : void 0,
6019
+ children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native21.View, { style: styles15.sheetPositioner, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_react_native21.View, { style: [styles15.sheet, { backgroundColor: t.background }], children: [
6020
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native21.View, { style: styles15.handleRow, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native21.View, { style: [styles15.handle, { backgroundColor: t.textMuted }] }) }),
6021
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_react_native21.View, { style: styles15.header, children: [
6022
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native21.Text, { style: [styles15.headerTitle, { color: t.text }], children: "Join Pool" }),
6023
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native21.TouchableOpacity, { onPress: onDismiss, activeOpacity: 0.8, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native21.Text, { style: [styles15.closeButton, { color: t.textMuted }], children: "\u2715" }) })
6024
+ ] }),
6025
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native21.Text, { style: [styles15.poolName, { color: t.textSecondary }], children: pool.name }),
6026
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_react_native21.View, { style: [styles15.summaryCard, { backgroundColor: t.surface, borderColor: t.border }], children: [
6027
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_react_native21.View, { style: styles15.summaryRow, children: [
6028
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native21.Text, { style: [styles15.summaryLabel, { color: t.textMuted }], children: "Buy-in" }),
6029
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_react_native21.Text, { style: [styles15.summaryValue, { color: t.text }], children: [
6030
+ buyInSol,
6031
+ " SOL"
6032
+ ] })
6033
+ ] }),
6034
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native21.View, { style: [styles15.summarySep, { backgroundColor: t.border }] }),
6035
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_react_native21.View, { style: styles15.summaryRow, children: [
6036
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native21.Text, { style: [styles15.summaryLabel, { color: t.textMuted }], children: "Players in" }),
6037
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native21.Text, { style: [styles15.summaryValue, { color: t.text }], children: totalPlayers })
6038
+ ] }),
6039
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native21.View, { style: [styles15.summarySep, { backgroundColor: t.border }] }),
6040
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_react_native21.View, { style: styles15.summaryRow, children: [
6041
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native21.Text, { style: [styles15.summaryLabel, { color: t.textMuted }], children: "Current pot" }),
6042
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_react_native21.Text, { style: [styles15.summaryValue, { color: t.success }], children: [
6043
+ potSol,
6044
+ " SOL"
6045
+ ] })
6046
+ ] }),
6047
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native21.View, { style: [styles15.summarySep, { backgroundColor: t.border }] }),
6048
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_react_native21.View, { style: styles15.summaryRow, children: [
6049
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native21.Text, { style: [styles15.summaryLabel, { color: t.textMuted }], children: "Lives" }),
6050
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native21.Text, { style: [styles15.summaryValue, { color: t.text }], children: pool.max_lives })
6051
+ ] }),
6052
+ topScore > 0 && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
6053
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native21.View, { style: [styles15.summarySep, { backgroundColor: t.border }] }),
6054
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_react_native21.View, { style: styles15.summaryRow, children: [
6055
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native21.Text, { style: [styles15.summaryLabel, { color: t.textMuted }], children: "Top score" }),
6056
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native21.Text, { style: [styles15.summaryValue, { color: t.text }], children: topScore })
6057
+ ] })
6058
+ ] })
6059
+ ] }),
6060
+ mutation.error && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native21.View, { style: [styles15.errorBox, { backgroundColor: t.errorBg, borderColor: t.errorBorder }], children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native21.Text, { style: [styles15.errorText, { color: t.errorText }], children: mutation.error.message }) }),
6061
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
6062
+ import_react_native21.TouchableOpacity,
6063
+ {
6064
+ style: [
6065
+ styles15.ctaButton,
6066
+ { backgroundColor: canJoin ? t.accent : t.border }
6067
+ ],
6068
+ disabled: !canJoin,
6069
+ onPress: handleJoin,
6070
+ activeOpacity: 0.8,
6071
+ children: isMutating ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_react_native21.View, { style: styles15.ctaLoading, children: [
6072
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native21.ActivityIndicator, { size: "small", color: "#FFFFFF" }),
6073
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native21.Text, { style: styles15.ctaText, children: statusLabel })
6074
+ ] }) : mutation.status === "success" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native21.Text, { style: styles15.ctaText, children: "Joined!" }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native21.Text, { style: [styles15.ctaText, !canJoin && { opacity: 0.5 }], children: `Join Pool \u2014 ${buyInSol} SOL` })
6075
+ }
6076
+ )
6077
+ ] }) })
6078
+ }
6079
+ )
6080
+ ]
6081
+ }
6082
+ );
6083
+ }
6084
+ var styles15 = import_react_native21.StyleSheet.create({
6085
+ overlay: {
6086
+ ...import_react_native21.StyleSheet.absoluteFillObject,
6087
+ backgroundColor: "rgba(0,0,0,0.5)"
6088
+ },
6089
+ overlayTap: { flex: 1 },
6090
+ keyboardView: { flex: 1, justifyContent: "flex-end" },
6091
+ sheetPositioner: { justifyContent: "flex-end" },
6092
+ sheet: {
6093
+ borderTopLeftRadius: 24,
6094
+ borderTopRightRadius: 24,
6095
+ paddingHorizontal: 20,
6096
+ paddingBottom: 40
6097
+ },
6098
+ handleRow: { alignItems: "center", paddingTop: 10, paddingBottom: 8 },
6099
+ handle: { width: 36, height: 4, borderRadius: 2, opacity: 0.4 },
6100
+ header: {
6101
+ flexDirection: "row",
6102
+ alignItems: "center",
6103
+ justifyContent: "space-between",
6104
+ paddingVertical: 12
6105
+ },
6106
+ headerTitle: { fontSize: 20, fontWeight: "700" },
6107
+ closeButton: { fontSize: 20, padding: 4 },
6108
+ poolName: { fontSize: 15, fontWeight: "600", marginBottom: 8 },
6109
+ summaryCard: {
6110
+ marginTop: 12,
6111
+ borderRadius: 16,
6112
+ borderWidth: 1,
6113
+ overflow: "hidden"
6114
+ },
6115
+ summaryRow: {
6116
+ flexDirection: "row",
6117
+ alignItems: "center",
6118
+ justifyContent: "space-between",
6119
+ paddingHorizontal: 16,
6120
+ paddingVertical: 14
6121
+ },
6122
+ summaryLabel: { fontSize: 14 },
6123
+ summaryValue: { fontSize: 15, fontWeight: "700" },
6124
+ summarySep: { height: 1, marginHorizontal: 16 },
6125
+ errorBox: {
6126
+ marginTop: 16,
6127
+ borderRadius: 12,
6128
+ borderWidth: 1,
6129
+ padding: 12
6130
+ },
6131
+ errorText: { fontSize: 13, fontWeight: "500" },
6132
+ ctaButton: {
6133
+ marginTop: 20,
6134
+ height: 56,
6135
+ borderRadius: 14,
6136
+ justifyContent: "center",
6137
+ alignItems: "center"
6138
+ },
6139
+ ctaText: { color: "#FFFFFF", fontSize: 16, fontWeight: "700" },
6140
+ ctaLoading: { flexDirection: "row", alignItems: "center", gap: 10 }
6141
+ });
5672
6142
  // Annotate the CommonJS export names for ESM import in node:
5673
6143
  0 && (module.exports = {
5674
6144
  AuthGate,
@@ -5681,6 +6151,7 @@ var styles14 = import_react_native20.StyleSheet.create({
5681
6151
  DubsApiError,
5682
6152
  DubsClient,
5683
6153
  DubsProvider,
6154
+ EnterArcadePoolSheet,
5684
6155
  GamePoster,
5685
6156
  JoinGameButton,
5686
6157
  JoinGameSheet,
@@ -5703,12 +6174,16 @@ var styles14 = import_react_native20.StyleSheet.create({
5703
6174
  parseSolanaError,
5704
6175
  signAndSendBase64Transaction,
5705
6176
  useAppConfig,
6177
+ useArcadeGame,
6178
+ useArcadePool,
6179
+ useArcadePools,
5706
6180
  useAuth,
5707
6181
  useClaim,
5708
6182
  useCreateCustomGame,
5709
6183
  useCreateGame,
5710
6184
  useDubs,
5711
6185
  useDubsTheme,
6186
+ useEnterArcadePool,
5712
6187
  useEvents,
5713
6188
  useGame,
5714
6189
  useGames,