@dubsdotapp/expo 0.5.17 → 0.5.19

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.mjs CHANGED
@@ -594,6 +594,151 @@ var DubsClient = class {
594
594
  );
595
595
  return res.entry;
596
596
  }
597
+ // ── Jackpot ──
598
+ /** Get current jackpot round + last winner */
599
+ async getJackpotCurrent() {
600
+ const res = await this.request(
601
+ "GET",
602
+ "/jackpot/current"
603
+ );
604
+ return { round: res.round, lastWinner: res.lastWinner };
605
+ }
606
+ /** Get current round entries with odds */
607
+ async getJackpotEntries() {
608
+ const res = await this.request(
609
+ "GET",
610
+ "/jackpot/entries"
611
+ );
612
+ return { roundId: res.roundId, entries: res.entries, totalEntries: res.totalEntries };
613
+ }
614
+ /** Get recent jackpot round results */
615
+ async getJackpotHistory(limit) {
616
+ const qs = limit ? `?limit=${limit}` : "";
617
+ const res = await this.request(
618
+ "GET",
619
+ `/jackpot/history${qs}`
620
+ );
621
+ return res.rounds;
622
+ }
623
+ /** Get jackpot protocol config */
624
+ async getJackpotConfig() {
625
+ const res = await this.request(
626
+ "GET",
627
+ "/jackpot/config"
628
+ );
629
+ return res.config;
630
+ }
631
+ /** Build unsigned jackpot enter transaction */
632
+ async buildJackpotEnter(playerAddress, amount) {
633
+ const res = await this.request(
634
+ "POST",
635
+ "/jackpot/build-enter",
636
+ { playerAddress, amount }
637
+ );
638
+ return { transaction: res.transaction, roundId: res.roundId, amount: res.amount, amountSol: res.amountSol };
639
+ }
640
+ /** Confirm jackpot entry after wallet signs (creates developer attribution) */
641
+ async confirmJackpotEnter(params) {
642
+ const res = await this.request(
643
+ "POST",
644
+ "/jackpot/confirm-enter",
645
+ params
646
+ );
647
+ return { attributed: res.attributed, appId: res.appId, signature: res.signature };
648
+ }
649
+ // ── Chat ──
650
+ /** Get recent global chat messages */
651
+ async getChatMessages(params) {
652
+ const qs = new URLSearchParams();
653
+ if (params?.limit) qs.set("limit", String(params.limit));
654
+ if (params?.before) qs.set("before", String(params.before));
655
+ const query = qs.toString();
656
+ return this.request("GET", `/chat/messages${query ? `?${query}` : ""}`);
657
+ }
658
+ /** Send a message to global chat */
659
+ async sendChatMessage(params) {
660
+ return this.request("POST", "/chat/message", params);
661
+ }
662
+ /** Edit your own message */
663
+ async editChatMessage(id, message) {
664
+ return this.request("PUT", `/chat/message/${id}`, { message });
665
+ }
666
+ /** Delete your own message */
667
+ async deleteChatMessage(id) {
668
+ return this.request("DELETE", `/chat/message/${id}`);
669
+ }
670
+ /** Block a user */
671
+ async blockUser(targetUserId) {
672
+ await this.request("POST", `/chat/block/${targetUserId}`);
673
+ }
674
+ /** Unblock a user */
675
+ async unblockUser(targetUserId) {
676
+ await this.request("DELETE", `/chat/block/${targetUserId}`);
677
+ }
678
+ // ── DMs ──
679
+ /** Get DM conversations inbox */
680
+ async getConversations(limit) {
681
+ const qs = limit ? `?limit=${limit}` : "";
682
+ return this.request("GET", `/dm/conversations${qs}`);
683
+ }
684
+ /** Get conversation history with a specific user */
685
+ async getConversation(walletAddress, params) {
686
+ const qs = new URLSearchParams();
687
+ if (params?.limit) qs.set("limit", String(params.limit));
688
+ if (params?.beforeId) qs.set("beforeId", String(params.beforeId));
689
+ const query = qs.toString();
690
+ return this.request("GET", `/dm/conversation/${encodeURIComponent(walletAddress)}${query ? `?${query}` : ""}`);
691
+ }
692
+ /** Send a direct message */
693
+ async sendDirectMessage(params) {
694
+ return this.request("POST", "/dm/send", params);
695
+ }
696
+ /** Mark all DMs from a user as read */
697
+ async markDMRead(walletAddress) {
698
+ return this.request("POST", `/dm/read/${encodeURIComponent(walletAddress)}`);
699
+ }
700
+ /** Get unread DM count */
701
+ async getDMUnreadCount() {
702
+ return this.request("GET", "/dm/unread");
703
+ }
704
+ /** Delete a DM conversation (soft delete) */
705
+ async deleteConversation(walletAddress) {
706
+ await this.request("DELETE", `/dm/conversation/${encodeURIComponent(walletAddress)}`);
707
+ }
708
+ // ── Social ──
709
+ /** Search users by username */
710
+ async searchUsers(query) {
711
+ const qs = query ? `?q=${encodeURIComponent(query)}` : "";
712
+ return this.request("GET", `/social/search${qs}`);
713
+ }
714
+ /** Send a friend request */
715
+ async sendFriendRequest(targetUserId) {
716
+ return this.request("POST", `/social/friend-request/${targetUserId}`);
717
+ }
718
+ /** Get pending friend requests (received) */
719
+ async getPendingFriendRequests() {
720
+ return this.request("GET", "/social/friend-requests");
721
+ }
722
+ /** Accept a friend request */
723
+ async acceptFriendRequest(requestId) {
724
+ await this.request("POST", `/social/request/${requestId}/accept`);
725
+ }
726
+ /** Reject a friend request */
727
+ async rejectFriendRequest(requestId) {
728
+ await this.request("POST", `/social/request/${requestId}/reject`);
729
+ }
730
+ /** Get friends list */
731
+ async getFriends() {
732
+ return this.request("GET", "/social/friends");
733
+ }
734
+ /** Remove a friend */
735
+ async removeFriend(targetUserId) {
736
+ await this.request("DELETE", `/social/friend/${targetUserId}`);
737
+ }
738
+ /** Get blocked users list */
739
+ async getBlockedUsers() {
740
+ return this.request("GET", "/social/blocked");
741
+ }
597
742
  // ── App Config ──
598
743
  /** Fetch the app's UI customization config (accent color, icon, tagline, environment) */
599
744
  async getAppConfig() {
@@ -645,7 +790,7 @@ function createSecureStoreStorage() {
645
790
  }
646
791
 
647
792
  // src/provider.tsx
648
- import { createContext as createContext4, useContext as useContext4, useMemo as useMemo3, useCallback as useCallback22, useState as useState24, useEffect as useEffect16 } from "react";
793
+ import { createContext as createContext4, useContext as useContext4, useMemo as useMemo3, useCallback as useCallback25, useState as useState27, useEffect as useEffect18 } from "react";
649
794
 
650
795
  // src/ui/theme.ts
651
796
  import { createContext, useContext } from "react";
@@ -1710,7 +1855,7 @@ function ManagedWalletProvider({
1710
1855
  }
1711
1856
 
1712
1857
  // src/ui/AuthGate.tsx
1713
- import React2, { useState as useState23, useEffect as useEffect15, useRef as useRef6, useCallback as useCallback21 } from "react";
1858
+ import React2, { useState as useState26, useEffect as useEffect17, useRef as useRef6, useCallback as useCallback24 } from "react";
1714
1859
  import {
1715
1860
  View as View3,
1716
1861
  Text as Text3,
@@ -2934,6 +3079,117 @@ function useArcadeBridge({
2934
3079
  return { webviewRef, handleMessage, triggerPlay, lastResult, bridgeLoading };
2935
3080
  }
2936
3081
 
3082
+ // src/hooks/useJackpot.ts
3083
+ import { useState as useState23, useEffect as useEffect15, useCallback as useCallback21 } from "react";
3084
+ function useJackpot() {
3085
+ const { client } = useDubs();
3086
+ const [round, setRound] = useState23(null);
3087
+ const [lastWinner, setLastWinner] = useState23(null);
3088
+ const [loading, setLoading] = useState23(false);
3089
+ const [error, setError] = useState23(null);
3090
+ const fetch2 = useCallback21(async () => {
3091
+ setLoading(true);
3092
+ setError(null);
3093
+ try {
3094
+ const result = await client.getJackpotCurrent();
3095
+ setRound(result.round);
3096
+ setLastWinner(result.lastWinner);
3097
+ } catch (err) {
3098
+ setError(err instanceof Error ? err : new Error(String(err)));
3099
+ } finally {
3100
+ setLoading(false);
3101
+ }
3102
+ }, [client]);
3103
+ useEffect15(() => {
3104
+ fetch2();
3105
+ }, [fetch2]);
3106
+ return { round, lastWinner, loading, error, refetch: fetch2 };
3107
+ }
3108
+
3109
+ // src/hooks/useJackpotHistory.ts
3110
+ import { useState as useState24, useEffect as useEffect16, useCallback as useCallback22 } from "react";
3111
+ function useJackpotHistory(limit) {
3112
+ const { client } = useDubs();
3113
+ const [rounds, setRounds] = useState24([]);
3114
+ const [loading, setLoading] = useState24(false);
3115
+ const [error, setError] = useState24(null);
3116
+ const fetch2 = useCallback22(async () => {
3117
+ setLoading(true);
3118
+ setError(null);
3119
+ try {
3120
+ const result = await client.getJackpotHistory(limit);
3121
+ setRounds(result);
3122
+ } catch (err) {
3123
+ setError(err instanceof Error ? err : new Error(String(err)));
3124
+ } finally {
3125
+ setLoading(false);
3126
+ }
3127
+ }, [client, limit]);
3128
+ useEffect16(() => {
3129
+ fetch2();
3130
+ }, [fetch2]);
3131
+ return { rounds, loading, error, refetch: fetch2 };
3132
+ }
3133
+
3134
+ // src/hooks/useEnterJackpot.ts
3135
+ import { useState as useState25, useCallback as useCallback23 } from "react";
3136
+ function useEnterJackpot() {
3137
+ const { client, wallet, connection } = useDubs();
3138
+ const [status, setStatus] = useState25("idle");
3139
+ const [error, setError] = useState25(null);
3140
+ const [data, setData] = useState25(null);
3141
+ const reset = useCallback23(() => {
3142
+ setStatus("idle");
3143
+ setError(null);
3144
+ setData(null);
3145
+ }, []);
3146
+ const execute = useCallback23(async (amountLamports) => {
3147
+ if (!wallet.publicKey) throw new Error("Wallet not connected");
3148
+ const walletAddress = wallet.publicKey.toBase58();
3149
+ setStatus("building");
3150
+ setError(null);
3151
+ setData(null);
3152
+ try {
3153
+ console.log("[useEnterJackpot] Step 1: Building transaction...", { walletAddress, amountLamports });
3154
+ const buildResult = await client.buildJackpotEnter(walletAddress, amountLamports);
3155
+ console.log("[useEnterJackpot] Step 1 done:", { roundId: buildResult.roundId, amount: buildResult.amount });
3156
+ setStatus("signing");
3157
+ console.log("[useEnterJackpot] Step 2: Signing and sending...");
3158
+ const signature = await signAndSendBase64Transaction(
3159
+ buildResult.transaction,
3160
+ wallet,
3161
+ connection
3162
+ );
3163
+ console.log("[useEnterJackpot] Step 2 done. Signature:", signature);
3164
+ setStatus("confirming");
3165
+ console.log("[useEnterJackpot] Step 3: Confirming with backend...");
3166
+ await client.confirmJackpotEnter({
3167
+ playerAddress: walletAddress,
3168
+ roundId: buildResult.roundId,
3169
+ amount: amountLamports,
3170
+ signature
3171
+ });
3172
+ console.log("[useEnterJackpot] Step 3 done. Entry confirmed + attributed.");
3173
+ const result = {
3174
+ roundId: buildResult.roundId,
3175
+ amount: buildResult.amount,
3176
+ amountSol: buildResult.amountSol,
3177
+ signature
3178
+ };
3179
+ setData(result);
3180
+ setStatus("success");
3181
+ return result;
3182
+ } catch (err) {
3183
+ console.error("[useEnterJackpot] FAILED:", err);
3184
+ const e = err instanceof Error ? err : new Error(String(err));
3185
+ setError(e);
3186
+ setStatus("error");
3187
+ throw e;
3188
+ }
3189
+ }, [client, wallet, connection]);
3190
+ return { execute, status, error, data, reset };
3191
+ }
3192
+
2937
3193
  // src/ui/AvatarEditor.tsx
2938
3194
  import {
2939
3195
  View as View2,
@@ -3102,11 +3358,11 @@ function AuthGate({
3102
3358
  }) {
3103
3359
  const { client, pushEnabled } = useDubs();
3104
3360
  const auth = useAuth();
3105
- const [phase, setPhase] = useState23("init");
3106
- const [registrationPhase, setRegistrationPhase] = useState23(false);
3107
- const [showPushSetup, setShowPushSetup] = useState23(false);
3108
- const [isRestoredSession, setIsRestoredSession] = useState23(false);
3109
- useEffect15(() => {
3361
+ const [phase, setPhase] = useState26("init");
3362
+ const [registrationPhase, setRegistrationPhase] = useState26(false);
3363
+ const [showPushSetup, setShowPushSetup] = useState26(false);
3364
+ const [isRestoredSession, setIsRestoredSession] = useState26(false);
3365
+ useEffect17(() => {
3110
3366
  let cancelled = false;
3111
3367
  (async () => {
3112
3368
  try {
@@ -3133,23 +3389,23 @@ function AuthGate({
3133
3389
  cancelled = true;
3134
3390
  };
3135
3391
  }, []);
3136
- useEffect15(() => {
3392
+ useEffect17(() => {
3137
3393
  if (auth.status === "needsRegistration") setRegistrationPhase(true);
3138
3394
  }, [auth.status]);
3139
- useEffect15(() => {
3395
+ useEffect17(() => {
3140
3396
  if (pushEnabled && auth.status === "authenticated" && registrationPhase && !isRestoredSession) {
3141
3397
  setShowPushSetup(true);
3142
3398
  }
3143
3399
  }, [pushEnabled, auth.status, registrationPhase, isRestoredSession]);
3144
- useEffect15(() => {
3400
+ useEffect17(() => {
3145
3401
  if (auth.token) onSaveToken(auth.token);
3146
3402
  }, [auth.token]);
3147
- const retry = useCallback21(() => {
3403
+ const retry = useCallback24(() => {
3148
3404
  setRegistrationPhase(false);
3149
3405
  auth.reset();
3150
3406
  auth.authenticate();
3151
3407
  }, [auth]);
3152
- const handleRegister = useCallback21(
3408
+ const handleRegister = useCallback24(
3153
3409
  (username, referralCode, avatarUrl) => {
3154
3410
  auth.register(username, referralCode, avatarUrl);
3155
3411
  },
@@ -3273,20 +3529,20 @@ function DefaultRegistrationScreen({
3273
3529
  }) {
3274
3530
  const t = useDubsTheme();
3275
3531
  const accent = accentColor || t.accent;
3276
- const [step, setStep] = useState23(0);
3277
- const [avatarSeed, setAvatarSeed] = useState23(generateSeed);
3278
- const [avatarStyle, setAvatarStyle] = useState23("adventurer");
3279
- const [avatarBg, setAvatarBg] = useState23("1a1a2e");
3280
- const [showStyles, setShowStyles] = useState23(false);
3281
- const [username, setUsername] = useState23("");
3282
- const [referralCode, setReferralCode] = useState23("");
3283
- const [checking, setChecking] = useState23(false);
3284
- const [availability, setAvailability] = useState23(null);
3532
+ const [step, setStep] = useState26(0);
3533
+ const [avatarSeed, setAvatarSeed] = useState26(generateSeed);
3534
+ const [avatarStyle, setAvatarStyle] = useState26("adventurer");
3535
+ const [avatarBg, setAvatarBg] = useState26("1a1a2e");
3536
+ const [showStyles, setShowStyles] = useState26(false);
3537
+ const [username, setUsername] = useState26("");
3538
+ const [referralCode, setReferralCode] = useState26("");
3539
+ const [checking, setChecking] = useState26(false);
3540
+ const [availability, setAvailability] = useState26(null);
3285
3541
  const debounceRef = useRef6(null);
3286
3542
  const fadeAnim = useRef6(new Animated.Value(1)).current;
3287
3543
  const slideAnim = useRef6(new Animated.Value(0)).current;
3288
3544
  const avatarUrl = getAvatarUrl(avatarStyle, avatarSeed, avatarBg);
3289
- useEffect15(() => {
3545
+ useEffect17(() => {
3290
3546
  if (debounceRef.current) clearTimeout(debounceRef.current);
3291
3547
  const trimmed = username.trim();
3292
3548
  if (trimmed.length < 3) {
@@ -3309,7 +3565,7 @@ function DefaultRegistrationScreen({
3309
3565
  if (debounceRef.current) clearTimeout(debounceRef.current);
3310
3566
  };
3311
3567
  }, [username, client]);
3312
- const animateToStep = useCallback21((newStep) => {
3568
+ const animateToStep = useCallback24((newStep) => {
3313
3569
  const dir = newStep > step ? 1 : -1;
3314
3570
  Keyboard.dismiss();
3315
3571
  Animated.parallel([
@@ -3549,7 +3805,7 @@ function DefaultRegistrationScreen({
3549
3805
  function PushTokenRestorer() {
3550
3806
  const push = usePushNotifications();
3551
3807
  const restored = useRef6(false);
3552
- useEffect15(() => {
3808
+ useEffect17(() => {
3553
3809
  if (restored.current) return;
3554
3810
  restored.current = true;
3555
3811
  push.restoreIfGranted();
@@ -3566,7 +3822,7 @@ function PushSetupScreen({
3566
3822
  const push = usePushNotifications();
3567
3823
  const fadeAnim = useRef6(new Animated.Value(0)).current;
3568
3824
  const slideAnim = useRef6(new Animated.Value(30)).current;
3569
- useEffect15(() => {
3825
+ useEffect17(() => {
3570
3826
  Animated.parallel([
3571
3827
  Animated.timing(fadeAnim, { toValue: 1, duration: 300, useNativeDriver: true }),
3572
3828
  Animated.timing(slideAnim, { toValue: 0, duration: 300, useNativeDriver: true })
@@ -3726,9 +3982,9 @@ function DubsProvider({
3726
3982
  const rpcUrl = rpcUrlOverride || config.rpcUrl;
3727
3983
  const client = useMemo3(() => new DubsClient({ apiKey, baseUrl }), [apiKey, baseUrl]);
3728
3984
  const storage = useMemo3(() => tokenStorage || createSecureStoreStorage(), [tokenStorage]);
3729
- const [uiConfig, setUiConfig] = useState24(null);
3730
- const [resolvedNetwork, setResolvedNetwork] = useState24(network);
3731
- useEffect16(() => {
3985
+ const [uiConfig, setUiConfig] = useState27(null);
3986
+ const [resolvedNetwork, setResolvedNetwork] = useState27(network);
3987
+ useEffect18(() => {
3732
3988
  client.getAppConfig().then((cfg) => {
3733
3989
  console.log("[DubsProvider] UI config loaded:", JSON.stringify(cfg));
3734
3990
  setUiConfig(cfg);
@@ -3820,7 +4076,7 @@ function ManagedInner({
3820
4076
  children
3821
4077
  }) {
3822
4078
  const managedDisconnect = useDisconnect();
3823
- const disconnect = useCallback22(async () => {
4079
+ const disconnect = useCallback25(async () => {
3824
4080
  client.setToken(null);
3825
4081
  await managedDisconnect?.();
3826
4082
  }, [client, managedDisconnect]);
@@ -3861,7 +4117,7 @@ function ExternalWalletProvider({
3861
4117
  pushEnabled,
3862
4118
  children
3863
4119
  }) {
3864
- const disconnect = useCallback22(async () => {
4120
+ const disconnect = useCallback25(async () => {
3865
4121
  client.setToken(null);
3866
4122
  await storage.deleteItem(STORAGE_KEYS.JWT_TOKEN).catch(() => {
3867
4123
  });
@@ -4182,7 +4438,7 @@ var styles5 = StyleSheet6.create({
4182
4438
  });
4183
4439
 
4184
4440
  // src/ui/UserProfileSheet.tsx
4185
- import { useState as useState25, useEffect as useEffect17, useRef as useRef7, useCallback as useCallback23, useMemo as useMemo5 } from "react";
4441
+ import { useState as useState28, useEffect as useEffect19, useRef as useRef7, useCallback as useCallback26, useMemo as useMemo5 } from "react";
4186
4442
  import {
4187
4443
  View as View7,
4188
4444
  Text as Text7,
@@ -4215,29 +4471,29 @@ function UserProfileSheet({
4215
4471
  const push = usePushNotifications();
4216
4472
  const overlayOpacity = useRef7(new Animated2.Value(0)).current;
4217
4473
  const parsed = useMemo5(() => parseAvatarUrl(user.avatar), [user.avatar]);
4218
- const [avatarStyle, setAvatarStyle] = useState25(parsed.style);
4219
- const [avatarSeed, setAvatarSeed] = useState25(parsed.seed);
4220
- const [bgColor, setBgColor] = useState25(parsed.bg);
4221
- const [saving, setSaving] = useState25(false);
4222
- const [error, setError] = useState25(null);
4223
- useEffect17(() => {
4474
+ const [avatarStyle, setAvatarStyle] = useState28(parsed.style);
4475
+ const [avatarSeed, setAvatarSeed] = useState28(parsed.seed);
4476
+ const [bgColor, setBgColor] = useState28(parsed.bg);
4477
+ const [saving, setSaving] = useState28(false);
4478
+ const [error, setError] = useState28(null);
4479
+ useEffect19(() => {
4224
4480
  const p = parseAvatarUrl(user.avatar);
4225
4481
  setAvatarStyle(p.style);
4226
4482
  setAvatarSeed(p.seed);
4227
4483
  setBgColor(p.bg);
4228
4484
  }, [user.avatar]);
4229
- useEffect17(() => {
4485
+ useEffect19(() => {
4230
4486
  Animated2.timing(overlayOpacity, {
4231
4487
  toValue: visible ? 1 : 0,
4232
4488
  duration: 250,
4233
4489
  useNativeDriver: true
4234
4490
  }).start();
4235
4491
  }, [visible, overlayOpacity]);
4236
- useEffect17(() => {
4492
+ useEffect19(() => {
4237
4493
  if (visible) setError(null);
4238
4494
  }, [visible]);
4239
4495
  const currentAvatarUrl = getAvatarUrl(avatarStyle, avatarSeed, bgColor);
4240
- const saveAvatar = useCallback23(async (newUrl) => {
4496
+ const saveAvatar = useCallback26(async (newUrl) => {
4241
4497
  setSaving(true);
4242
4498
  setError(null);
4243
4499
  try {
@@ -4250,16 +4506,16 @@ function UserProfileSheet({
4250
4506
  setSaving(false);
4251
4507
  }
4252
4508
  }, [client, refreshUser, onAvatarUpdated]);
4253
- const handleStyleChange = useCallback23((style) => {
4509
+ const handleStyleChange = useCallback26((style) => {
4254
4510
  setAvatarStyle(style);
4255
4511
  saveAvatar(getAvatarUrl(style, avatarSeed, bgColor));
4256
4512
  }, [avatarSeed, bgColor, saveAvatar]);
4257
- const handleShuffle = useCallback23(() => {
4513
+ const handleShuffle = useCallback26(() => {
4258
4514
  const newSeed = generateSeed();
4259
4515
  setAvatarSeed(newSeed);
4260
4516
  saveAvatar(getAvatarUrl(avatarStyle, newSeed, bgColor));
4261
4517
  }, [avatarStyle, bgColor, saveAvatar]);
4262
- const handleBgChange = useCallback23((color) => {
4518
+ const handleBgChange = useCallback26((color) => {
4263
4519
  setBgColor(color);
4264
4520
  saveAvatar(getAvatarUrl(avatarStyle, avatarSeed, color));
4265
4521
  }, [avatarStyle, avatarSeed, saveAvatar]);
@@ -4539,7 +4795,7 @@ var styles6 = StyleSheet7.create({
4539
4795
  });
4540
4796
 
4541
4797
  // src/ui/game/GamePoster.tsx
4542
- import { useState as useState26 } from "react";
4798
+ import { useState as useState29 } from "react";
4543
4799
  import { StyleSheet as StyleSheet8, View as View8, Text as Text8 } from "react-native";
4544
4800
  import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
4545
4801
  function computeCountdown(lockTimestamp) {
@@ -4589,7 +4845,7 @@ function GamePoster({ game, ImageComponent }) {
4589
4845
  ] });
4590
4846
  }
4591
4847
  function TeamLogoInternal({ url, size, Img }) {
4592
- const [failed, setFailed] = useState26(false);
4848
+ const [failed, setFailed] = useState29(false);
4593
4849
  if (!url || failed) {
4594
4850
  return /* @__PURE__ */ jsx10(View8, { style: [styles7.logoPlaceholder, { width: size, height: size, borderRadius: size / 2 }] });
4595
4851
  }
@@ -4769,7 +5025,7 @@ var styles8 = StyleSheet9.create({
4769
5025
  });
4770
5026
 
4771
5027
  // src/ui/game/PickWinnerCard.tsx
4772
- import { useState as useState27, useMemo as useMemo7 } from "react";
5028
+ import { useState as useState30, useMemo as useMemo7 } from "react";
4773
5029
  import { StyleSheet as StyleSheet10, View as View10, Text as Text10, TouchableOpacity as TouchableOpacity7 } from "react-native";
4774
5030
  import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
4775
5031
  function PickWinnerCard({
@@ -4840,7 +5096,7 @@ function TeamOption({
4840
5096
  ImageComponent,
4841
5097
  t
4842
5098
  }) {
4843
- const [imgFailed, setImgFailed] = useState27(false);
5099
+ const [imgFailed, setImgFailed] = useState30(false);
4844
5100
  const Img = ImageComponent || __require("react-native").Image;
4845
5101
  const showImage = imageUrl && !imgFailed;
4846
5102
  return /* @__PURE__ */ jsxs9(
@@ -4881,7 +5137,7 @@ var styles9 = StyleSheet10.create({
4881
5137
  });
4882
5138
 
4883
5139
  // src/ui/game/PlayersCard.tsx
4884
- import { useState as useState28 } from "react";
5140
+ import { useState as useState31 } from "react";
4885
5141
  import { StyleSheet as StyleSheet11, View as View11, Text as Text11 } from "react-native";
4886
5142
  import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
4887
5143
  function truncateWallet(addr, chars) {
@@ -4930,7 +5186,7 @@ function BettorRow({
4930
5186
  ImageComponent,
4931
5187
  t
4932
5188
  }) {
4933
- const [imgFailed, setImgFailed] = useState28(false);
5189
+ const [imgFailed, setImgFailed] = useState31(false);
4934
5190
  const Img = ImageComponent || __require("react-native").Image;
4935
5191
  const showAvatar = bettor.avatar && !imgFailed;
4936
5192
  return /* @__PURE__ */ jsxs10(View11, { style: [styles10.row, !isFirst && { borderTopColor: t.border, borderTopWidth: 1 }], children: [
@@ -5009,7 +5265,7 @@ var styles11 = StyleSheet12.create({
5009
5265
  });
5010
5266
 
5011
5267
  // src/ui/game/CreateCustomGameSheet.tsx
5012
- import { useState as useState29, useEffect as useEffect18, useRef as useRef8, useCallback as useCallback24 } from "react";
5268
+ import { useState as useState32, useEffect as useEffect20, useRef as useRef8, useCallback as useCallback27 } from "react";
5013
5269
  import {
5014
5270
  View as View13,
5015
5271
  Text as Text13,
@@ -5046,18 +5302,18 @@ function CreateCustomGameSheet({
5046
5302
  const t = useDubsTheme();
5047
5303
  const { wallet } = useDubs();
5048
5304
  const mutation = useCreateCustomGame();
5049
- const [selectedAmount, setSelectedAmount] = useState29(null);
5050
- const [customAmount, setCustomAmount] = useState29("");
5051
- const [isCustom, setIsCustom] = useState29(false);
5305
+ const [selectedAmount, setSelectedAmount] = useState32(null);
5306
+ const [customAmount, setCustomAmount] = useState32("");
5307
+ const [isCustom, setIsCustom] = useState32(false);
5052
5308
  const overlayOpacity = useRef8(new Animated3.Value(0)).current;
5053
- useEffect18(() => {
5309
+ useEffect20(() => {
5054
5310
  Animated3.timing(overlayOpacity, {
5055
5311
  toValue: visible ? 1 : 0,
5056
5312
  duration: 250,
5057
5313
  useNativeDriver: true
5058
5314
  }).start();
5059
5315
  }, [visible, overlayOpacity]);
5060
- useEffect18(() => {
5316
+ useEffect20(() => {
5061
5317
  if (visible) {
5062
5318
  setSelectedAmount(defaultAmount ?? null);
5063
5319
  setCustomAmount("");
@@ -5065,7 +5321,7 @@ function CreateCustomGameSheet({
5065
5321
  mutation.reset();
5066
5322
  }
5067
5323
  }, [visible]);
5068
- useEffect18(() => {
5324
+ useEffect20(() => {
5069
5325
  if (mutation.status === "success" && mutation.data) {
5070
5326
  onSuccess?.(mutation.data);
5071
5327
  const timer = setTimeout(() => {
@@ -5074,23 +5330,23 @@ function CreateCustomGameSheet({
5074
5330
  return () => clearTimeout(timer);
5075
5331
  }
5076
5332
  }, [mutation.status, mutation.data]);
5077
- useEffect18(() => {
5333
+ useEffect20(() => {
5078
5334
  if (mutation.status === "error" && mutation.error) {
5079
5335
  onError?.(mutation.error);
5080
5336
  }
5081
5337
  }, [mutation.status, mutation.error]);
5082
- const handlePresetSelect = useCallback24((amount) => {
5338
+ const handlePresetSelect = useCallback27((amount) => {
5083
5339
  setSelectedAmount(amount);
5084
5340
  setIsCustom(false);
5085
5341
  setCustomAmount("");
5086
5342
  onAmountChange?.(amount);
5087
5343
  }, [onAmountChange]);
5088
- const handleCustomSelect = useCallback24(() => {
5344
+ const handleCustomSelect = useCallback27(() => {
5089
5345
  setIsCustom(true);
5090
5346
  setSelectedAmount(null);
5091
5347
  onAmountChange?.(null);
5092
5348
  }, [onAmountChange]);
5093
- const handleCustomAmountChange = useCallback24((text) => {
5349
+ const handleCustomAmountChange = useCallback27((text) => {
5094
5350
  const cleaned = text.replace(/[^0-9.]/g, "").replace(/(\..*?)\..*/g, "$1");
5095
5351
  setCustomAmount(cleaned);
5096
5352
  const parsed = parseFloat(cleaned);
@@ -5105,7 +5361,7 @@ function CreateCustomGameSheet({
5105
5361
  const winnerTakes = pot * (1 - fee / 100);
5106
5362
  const isMutating = mutation.status !== "idle" && mutation.status !== "success" && mutation.status !== "error";
5107
5363
  const canCreate = finalAmount !== null && finalAmount > 0 && !isMutating && mutation.status !== "success";
5108
- const handleCreate = useCallback24(async () => {
5364
+ const handleCreate = useCallback27(async () => {
5109
5365
  if (!finalAmount || !wallet.publicKey) return;
5110
5366
  try {
5111
5367
  await mutation.execute({
@@ -5374,7 +5630,7 @@ var styles12 = StyleSheet13.create({
5374
5630
  });
5375
5631
 
5376
5632
  // src/ui/game/JoinGameSheet.tsx
5377
- import { useState as useState31, useEffect as useEffect19, useRef as useRef10, useCallback as useCallback26, useMemo as useMemo9 } from "react";
5633
+ import { useState as useState34, useEffect as useEffect21, useRef as useRef10, useCallback as useCallback29, useMemo as useMemo9 } from "react";
5378
5634
  import {
5379
5635
  View as View16,
5380
5636
  Text as Text16,
@@ -5584,7 +5840,7 @@ var styles13 = StyleSheet14.create({
5584
5840
  });
5585
5841
 
5586
5842
  // src/ui/game/TeamButton.tsx
5587
- import { useState as useState30 } from "react";
5843
+ import { useState as useState33 } from "react";
5588
5844
  import { View as View15, Text as Text15, TouchableOpacity as TouchableOpacity10, StyleSheet as StyleSheet15 } from "react-native";
5589
5845
  import { jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
5590
5846
  function TeamButton({
@@ -5598,7 +5854,7 @@ function TeamButton({
5598
5854
  ImageComponent,
5599
5855
  t
5600
5856
  }) {
5601
- const [imgFailed, setImgFailed] = useState30(false);
5857
+ const [imgFailed, setImgFailed] = useState33(false);
5602
5858
  const Img = ImageComponent || __require("react-native").Image;
5603
5859
  const showImage = imageUrl && !imgFailed;
5604
5860
  return /* @__PURE__ */ jsxs14(
@@ -5702,20 +5958,20 @@ function JoinGameSheet({
5702
5958
  const { wallet } = useDubs();
5703
5959
  const mutation = useJoinGame();
5704
5960
  const isCustomGame = game.gameMode === CUSTOM_GAME_MODE;
5705
- const [selectedTeam, setSelectedTeam] = useState31(null);
5706
- const [wager, setWager] = useState31(game.buyIn);
5707
- const [showSuccess, setShowSuccess] = useState31(false);
5961
+ const [selectedTeam, setSelectedTeam] = useState34(null);
5962
+ const [wager, setWager] = useState34(game.buyIn);
5963
+ const [showSuccess, setShowSuccess] = useState34(false);
5708
5964
  const overlayOpacity = useRef10(new Animated4.Value(0)).current;
5709
5965
  const successScale = useRef10(new Animated4.Value(0)).current;
5710
5966
  const successOpacity = useRef10(new Animated4.Value(0)).current;
5711
- useEffect19(() => {
5967
+ useEffect21(() => {
5712
5968
  Animated4.timing(overlayOpacity, {
5713
5969
  toValue: visible ? 1 : 0,
5714
5970
  duration: 250,
5715
5971
  useNativeDriver: true
5716
5972
  }).start();
5717
5973
  }, [visible, overlayOpacity]);
5718
- useEffect19(() => {
5974
+ useEffect21(() => {
5719
5975
  if (visible) {
5720
5976
  setSelectedTeam(isPoolModeEnabled ? "home" : isCustomGame ? "away" : null);
5721
5977
  setWager(game.buyIn);
@@ -5725,7 +5981,7 @@ function JoinGameSheet({
5725
5981
  mutation.reset();
5726
5982
  }
5727
5983
  }, [visible]);
5728
- useEffect19(() => {
5984
+ useEffect21(() => {
5729
5985
  if (mutation.status === "success" && mutation.data) {
5730
5986
  setShowSuccess(true);
5731
5987
  onSuccess?.(mutation.data);
@@ -5742,7 +5998,7 @@ function JoinGameSheet({
5742
5998
  return () => clearTimeout(timer);
5743
5999
  }
5744
6000
  }, [mutation.status, mutation.data]);
5745
- useEffect19(() => {
6001
+ useEffect21(() => {
5746
6002
  if (mutation.status === "error" && mutation.error) {
5747
6003
  onError?.(mutation.error);
5748
6004
  }
@@ -5786,7 +6042,7 @@ function JoinGameSheet({
5786
6042
  const alreadyJoined = myBet !== null;
5787
6043
  const isMutating = mutation.status !== "idle" && mutation.status !== "success" && mutation.status !== "error";
5788
6044
  const canJoin = selectedTeam !== null && !isMutating && mutation.status !== "success" && !alreadyJoined;
5789
- const handleJoin = useCallback26(async () => {
6045
+ const handleJoin = useCallback29(async () => {
5790
6046
  if (!selectedTeam || !wallet.publicKey) return;
5791
6047
  try {
5792
6048
  await mutation.execute({
@@ -6049,7 +6305,7 @@ function PickRow({
6049
6305
  ImageComponent,
6050
6306
  t
6051
6307
  }) {
6052
- const [imgFailed, setImgFailed] = useState31(false);
6308
+ const [imgFailed, setImgFailed] = useState34(false);
6053
6309
  const Img = ImageComponent || __require("react-native").Image;
6054
6310
  const showImage = imageUrl && !imgFailed;
6055
6311
  return /* @__PURE__ */ jsxs15(
@@ -6342,7 +6598,7 @@ var styles15 = StyleSheet16.create({
6342
6598
  });
6343
6599
 
6344
6600
  // src/ui/game/ClaimPrizeSheet.tsx
6345
- import { useState as useState32, useEffect as useEffect20, useRef as useRef11, useCallback as useCallback27 } from "react";
6601
+ import { useState as useState35, useEffect as useEffect22, useRef as useRef11, useCallback as useCallback30 } from "react";
6346
6602
  import {
6347
6603
  View as View17,
6348
6604
  Text as Text17,
@@ -6376,15 +6632,15 @@ function ClaimPrizeSheet({
6376
6632
  const overlayOpacity = useRef11(new Animated5.Value(0)).current;
6377
6633
  const celebrationScale = useRef11(new Animated5.Value(0)).current;
6378
6634
  const celebrationOpacity = useRef11(new Animated5.Value(0)).current;
6379
- const [showCelebration, setShowCelebration] = useState32(false);
6380
- useEffect20(() => {
6635
+ const [showCelebration, setShowCelebration] = useState35(false);
6636
+ useEffect22(() => {
6381
6637
  Animated5.timing(overlayOpacity, {
6382
6638
  toValue: visible ? 1 : 0,
6383
6639
  duration: 250,
6384
6640
  useNativeDriver: true
6385
6641
  }).start();
6386
6642
  }, [visible, overlayOpacity]);
6387
- useEffect20(() => {
6643
+ useEffect22(() => {
6388
6644
  if (visible) {
6389
6645
  mutation.reset();
6390
6646
  setShowCelebration(false);
@@ -6392,7 +6648,7 @@ function ClaimPrizeSheet({
6392
6648
  celebrationOpacity.setValue(0);
6393
6649
  }
6394
6650
  }, [visible]);
6395
- useEffect20(() => {
6651
+ useEffect22(() => {
6396
6652
  if (mutation.status === "success" && mutation.data) {
6397
6653
  setShowCelebration(true);
6398
6654
  Animated5.parallel([
@@ -6415,14 +6671,14 @@ function ClaimPrizeSheet({
6415
6671
  return () => clearTimeout(timer);
6416
6672
  }
6417
6673
  }, [mutation.status, mutation.data]);
6418
- useEffect20(() => {
6674
+ useEffect22(() => {
6419
6675
  if (mutation.status === "error" && mutation.error) {
6420
6676
  onError?.(mutation.error);
6421
6677
  }
6422
6678
  }, [mutation.status, mutation.error]);
6423
6679
  const isMutating = mutation.status !== "idle" && mutation.status !== "success" && mutation.status !== "error";
6424
6680
  const canClaim = !isMutating && mutation.status !== "success" && !!wallet.publicKey;
6425
- const handleClaim = useCallback27(async () => {
6681
+ const handleClaim = useCallback30(async () => {
6426
6682
  if (!wallet.publicKey) return;
6427
6683
  try {
6428
6684
  await mutation.execute({
@@ -6655,7 +6911,7 @@ var styles16 = StyleSheet17.create({
6655
6911
  });
6656
6912
 
6657
6913
  // src/ui/game/ClaimButton.tsx
6658
- import { useState as useState33, useMemo as useMemo10, useCallback as useCallback28 } from "react";
6914
+ import { useState as useState36, useMemo as useMemo10, useCallback as useCallback31 } from "react";
6659
6915
  import { StyleSheet as StyleSheet18, Text as Text18, TouchableOpacity as TouchableOpacity13 } from "react-native";
6660
6916
  import { Fragment as Fragment5, jsx as jsx20, jsxs as jsxs17 } from "react/jsx-runtime";
6661
6917
  function ClaimButton({ gameId, style, onSuccess, onError }) {
@@ -6663,7 +6919,7 @@ function ClaimButton({ gameId, style, onSuccess, onError }) {
6663
6919
  const { wallet } = useDubs();
6664
6920
  const game = useGame(gameId);
6665
6921
  const claimStatus = useHasClaimed(gameId);
6666
- const [sheetVisible, setSheetVisible] = useState33(false);
6922
+ const [sheetVisible, setSheetVisible] = useState36(false);
6667
6923
  const walletAddress = wallet.publicKey?.toBase58() ?? null;
6668
6924
  const myBet = useMemo10(() => {
6669
6925
  if (!walletAddress || !game.data?.bettors) return null;
@@ -6674,7 +6930,7 @@ function ClaimButton({ gameId, style, onSuccess, onError }) {
6674
6930
  const isWinner = isResolved && myBet != null && myBet.team === game.data?.winnerSide;
6675
6931
  const isEligible = myBet != null && isResolved && (isWinner || isRefund);
6676
6932
  const prizeAmount = isRefund ? myBet?.amount ?? 0 : game.data?.totalPool ?? 0;
6677
- const handleSuccess = useCallback28(
6933
+ const handleSuccess = useCallback31(
6678
6934
  (result) => {
6679
6935
  claimStatus.refetch();
6680
6936
  onSuccess?.(result);
@@ -6761,7 +7017,7 @@ var styles17 = StyleSheet18.create({
6761
7017
  });
6762
7018
 
6763
7019
  // src/ui/game/EnterArcadePoolSheet.tsx
6764
- import { useEffect as useEffect21, useRef as useRef12, useCallback as useCallback29 } from "react";
7020
+ import { useEffect as useEffect23, useRef as useRef12, useCallback as useCallback32 } from "react";
6765
7021
  import {
6766
7022
  View as View18,
6767
7023
  Text as Text19,
@@ -6793,19 +7049,19 @@ function EnterArcadePoolSheet({
6793
7049
  const { wallet } = useDubs();
6794
7050
  const mutation = useEnterArcadePool();
6795
7051
  const overlayOpacity = useRef12(new Animated6.Value(0)).current;
6796
- useEffect21(() => {
7052
+ useEffect23(() => {
6797
7053
  Animated6.timing(overlayOpacity, {
6798
7054
  toValue: visible ? 1 : 0,
6799
7055
  duration: 250,
6800
7056
  useNativeDriver: true
6801
7057
  }).start();
6802
7058
  }, [visible, overlayOpacity]);
6803
- useEffect21(() => {
7059
+ useEffect23(() => {
6804
7060
  if (visible) {
6805
7061
  mutation.reset();
6806
7062
  }
6807
7063
  }, [visible]);
6808
- useEffect21(() => {
7064
+ useEffect23(() => {
6809
7065
  if (mutation.status === "success" && mutation.data) {
6810
7066
  onSuccess?.(mutation.data);
6811
7067
  const timer = setTimeout(() => {
@@ -6814,7 +7070,7 @@ function EnterArcadePoolSheet({
6814
7070
  return () => clearTimeout(timer);
6815
7071
  }
6816
7072
  }, [mutation.status, mutation.data]);
6817
- useEffect21(() => {
7073
+ useEffect23(() => {
6818
7074
  if (mutation.status === "error" && mutation.error) {
6819
7075
  onError?.(mutation.error);
6820
7076
  }
@@ -6826,7 +7082,7 @@ function EnterArcadePoolSheet({
6826
7082
  const potSol = (pool.buy_in_lamports * Number(totalBuyIns) / 1e9).toFixed(4);
6827
7083
  const isMutating = mutation.status !== "idle" && mutation.status !== "success" && mutation.status !== "error";
6828
7084
  const canJoin = !isMutating && mutation.status !== "success";
6829
- const handleJoin = useCallback29(async () => {
7085
+ const handleJoin = useCallback32(async () => {
6830
7086
  if (!wallet.publicKey) return;
6831
7087
  try {
6832
7088
  await mutation.execute(pool.id);
@@ -6977,7 +7233,7 @@ var styles18 = StyleSheet19.create({
6977
7233
  });
6978
7234
 
6979
7235
  // src/ui/game/ArcadeLeaderboardSheet.tsx
6980
- import { useEffect as useEffect22, useRef as useRef13 } from "react";
7236
+ import { useEffect as useEffect24, useRef as useRef13 } from "react";
6981
7237
  import {
6982
7238
  View as View19,
6983
7239
  Text as Text20,
@@ -7006,14 +7262,14 @@ function ArcadeLeaderboardSheet({
7006
7262
  const t = useDubsTheme();
7007
7263
  const { pool, leaderboard, stats, loading, refetch } = useArcadePool(poolId);
7008
7264
  const overlayOpacity = useRef13(new Animated7.Value(0)).current;
7009
- useEffect22(() => {
7265
+ useEffect24(() => {
7010
7266
  Animated7.timing(overlayOpacity, {
7011
7267
  toValue: visible ? 1 : 0,
7012
7268
  duration: 250,
7013
7269
  useNativeDriver: true
7014
7270
  }).start();
7015
7271
  }, [visible, overlayOpacity]);
7016
- useEffect22(() => {
7272
+ useEffect24(() => {
7017
7273
  if (visible) refetch();
7018
7274
  }, [visible]);
7019
7275
  const renderItem = ({ item, index }) => {
@@ -7160,7 +7416,7 @@ var styles19 = StyleSheet20.create({
7160
7416
  });
7161
7417
 
7162
7418
  // src/ui/game/CreateGameSheet.tsx
7163
- import { useState as useState35, useEffect as useEffect23, useRef as useRef14, useCallback as useCallback30 } from "react";
7419
+ import { useState as useState38, useEffect as useEffect25, useRef as useRef14, useCallback as useCallback33 } from "react";
7164
7420
  import {
7165
7421
  View as View20,
7166
7422
  Text as Text21,
@@ -7199,20 +7455,20 @@ function CreateGameSheet({
7199
7455
  const t = useDubsTheme();
7200
7456
  const { wallet } = useDubs();
7201
7457
  const mutation = useCreateGame();
7202
- const [selectedTeam, setSelectedTeam] = useState35(null);
7203
- const [wager, setWager] = useState35(0.01);
7204
- const [showSuccess, setShowSuccess] = useState35(false);
7458
+ const [selectedTeam, setSelectedTeam] = useState38(null);
7459
+ const [wager, setWager] = useState38(0.01);
7460
+ const [showSuccess, setShowSuccess] = useState38(false);
7205
7461
  const overlayOpacity = useRef14(new Animated8.Value(0)).current;
7206
7462
  const successScale = useRef14(new Animated8.Value(0)).current;
7207
7463
  const successOpacity = useRef14(new Animated8.Value(0)).current;
7208
- useEffect23(() => {
7464
+ useEffect25(() => {
7209
7465
  Animated8.timing(overlayOpacity, {
7210
7466
  toValue: visible ? 1 : 0,
7211
7467
  duration: 250,
7212
7468
  useNativeDriver: true
7213
7469
  }).start();
7214
7470
  }, [visible]);
7215
- useEffect23(() => {
7471
+ useEffect25(() => {
7216
7472
  if (visible) {
7217
7473
  setSelectedTeam(null);
7218
7474
  setWager(0.01);
@@ -7222,7 +7478,7 @@ function CreateGameSheet({
7222
7478
  mutation.reset();
7223
7479
  }
7224
7480
  }, [visible]);
7225
- useEffect23(() => {
7481
+ useEffect25(() => {
7226
7482
  if (mutation.status === "success" && mutation.data) {
7227
7483
  setShowSuccess(true);
7228
7484
  onSuccess?.(mutation.data);
@@ -7239,7 +7495,7 @@ function CreateGameSheet({
7239
7495
  return () => clearTimeout(timer);
7240
7496
  }
7241
7497
  }, [mutation.status, mutation.data]);
7242
- useEffect23(() => {
7498
+ useEffect25(() => {
7243
7499
  if (mutation.status === "error" && mutation.error) {
7244
7500
  onError?.(mutation.error);
7245
7501
  }
@@ -7249,7 +7505,7 @@ function CreateGameSheet({
7249
7505
  const awayName = shortName ? shortName(opponents[1]?.name) : opponents[1]?.name || "Away";
7250
7506
  const isMutating = mutation.status !== "idle" && mutation.status !== "success" && mutation.status !== "error";
7251
7507
  const canCreate = selectedTeam !== null && !isMutating && mutation.status !== "success";
7252
- const handleCreate = useCallback30(async () => {
7508
+ const handleCreate = useCallback33(async () => {
7253
7509
  if (!selectedTeam || !wallet.publicKey) return;
7254
7510
  try {
7255
7511
  await mutation.execute({
@@ -7395,9 +7651,1500 @@ var styles20 = StyleSheet21.create({
7395
7651
  successTitle: { color: "#FFFFFF", fontSize: 28, fontWeight: "900" },
7396
7652
  successSub: { color: "#8E8E93", fontSize: 16 }
7397
7653
  });
7654
+
7655
+ // src/ui/jackpot/JackpotCard.tsx
7656
+ import { useEffect as useEffect26, useRef as useRef15 } from "react";
7657
+ import {
7658
+ View as View21,
7659
+ Text as Text22,
7660
+ TouchableOpacity as TouchableOpacity17,
7661
+ Animated as Animated9,
7662
+ StyleSheet as StyleSheet22
7663
+ } from "react-native";
7664
+ import { jsx as jsx24, jsxs as jsxs21 } from "react/jsx-runtime";
7665
+ function formatSOL(lamports) {
7666
+ const val = typeof lamports === "string" ? parseInt(lamports, 10) : lamports;
7667
+ if (isNaN(val) || val === 0) return "0";
7668
+ const sol = val / 1e9;
7669
+ if (sol >= 100) return sol.toFixed(0);
7670
+ if (sol >= 1) return sol.toFixed(2);
7671
+ if (sol >= 0.01) return sol.toFixed(3);
7672
+ return sol.toFixed(4);
7673
+ }
7674
+ function truncateWallet2(addr) {
7675
+ if (!addr || addr.length < 8) return addr || "";
7676
+ return `${addr.slice(0, 4)}...${addr.slice(-4)}`;
7677
+ }
7678
+ function JackpotCard({ round, lastWinner, entries, onPress, style }) {
7679
+ const shimmerAnim = useRef15(new Animated9.Value(-1)).current;
7680
+ const pulseAnim = useRef15(new Animated9.Value(0.4)).current;
7681
+ useEffect26(() => {
7682
+ const shimmer = Animated9.loop(
7683
+ Animated9.sequence([
7684
+ Animated9.timing(shimmerAnim, { toValue: 1, duration: 4e3, useNativeDriver: true }),
7685
+ Animated9.delay(500),
7686
+ Animated9.timing(shimmerAnim, { toValue: -1, duration: 0, useNativeDriver: true })
7687
+ ])
7688
+ );
7689
+ shimmer.start();
7690
+ const pulse = Animated9.loop(
7691
+ Animated9.sequence([
7692
+ Animated9.timing(pulseAnim, { toValue: 1, duration: 1e3, useNativeDriver: true }),
7693
+ Animated9.timing(pulseAnim, { toValue: 0.4, duration: 1e3, useNativeDriver: true })
7694
+ ])
7695
+ );
7696
+ pulse.start();
7697
+ return () => {
7698
+ shimmer.stop();
7699
+ pulse.stop();
7700
+ };
7701
+ }, [shimmerAnim, pulseAnim]);
7702
+ const potSol = round ? formatSOL(round.totalPotLamports) : "0";
7703
+ const isOpen = round?.status === "Open";
7704
+ const entryCount = round?.entryCount ?? 0;
7705
+ const totalWeight = round ? Number(BigInt(round.totalWeight || "0")) : 0;
7706
+ return /* @__PURE__ */ jsxs21(
7707
+ TouchableOpacity17,
7708
+ {
7709
+ activeOpacity: 0.9,
7710
+ onPress,
7711
+ style: [styles21.card, style],
7712
+ children: [
7713
+ /* @__PURE__ */ jsx24(View21, { style: styles21.gradientBg }),
7714
+ /* @__PURE__ */ jsx24(
7715
+ Animated9.View,
7716
+ {
7717
+ style: [
7718
+ styles21.shimmer,
7719
+ {
7720
+ transform: [{
7721
+ translateX: shimmerAnim.interpolate({
7722
+ inputRange: [-1, 1],
7723
+ outputRange: [-400, 400]
7724
+ })
7725
+ }]
7726
+ }
7727
+ ]
7728
+ }
7729
+ ),
7730
+ /* @__PURE__ */ jsx24(View21, { style: styles21.accentBar }),
7731
+ /* @__PURE__ */ jsxs21(View21, { style: styles21.content, children: [
7732
+ /* @__PURE__ */ jsxs21(View21, { style: styles21.statusRow, children: [
7733
+ /* @__PURE__ */ jsxs21(View21, { style: [styles21.statusBadge, isOpen ? styles21.statusOpen : styles21.statusClosed], children: [
7734
+ isOpen && /* @__PURE__ */ jsx24(Animated9.View, { style: [styles21.statusDot, { opacity: pulseAnim }] }),
7735
+ /* @__PURE__ */ jsx24(Text22, { style: [styles21.statusText, { color: isOpen ? "#22c55e" : "#9ca3af" }], children: isOpen ? "Open" : round?.status ?? "Loading" })
7736
+ ] }),
7737
+ /* @__PURE__ */ jsxs21(Text22, { style: styles21.entryCountText, children: [
7738
+ entryCount,
7739
+ " player",
7740
+ entryCount !== 1 ? "s" : ""
7741
+ ] })
7742
+ ] }),
7743
+ /* @__PURE__ */ jsxs21(View21, { style: styles21.heroSection, children: [
7744
+ /* @__PURE__ */ jsxs21(View21, { style: styles21.potInfo, children: [
7745
+ /* @__PURE__ */ jsx24(Text22, { style: styles21.jackpotLabel, children: "JACKPOT" }),
7746
+ /* @__PURE__ */ jsxs21(Text22, { style: styles21.potValue, children: [
7747
+ potSol,
7748
+ " SOL"
7749
+ ] })
7750
+ ] }),
7751
+ /* @__PURE__ */ jsx24(Text22, { style: styles21.potEmoji, children: "\u{1F911}" })
7752
+ ] }),
7753
+ /* @__PURE__ */ jsxs21(View21, { style: styles21.infoGrid, children: [
7754
+ /* @__PURE__ */ jsxs21(View21, { style: styles21.infoCard, children: [
7755
+ /* @__PURE__ */ jsx24(Text22, { style: styles21.infoLabel, children: "PLAYERS" }),
7756
+ /* @__PURE__ */ jsx24(Text22, { style: styles21.infoValue, children: entryCount })
7757
+ ] }),
7758
+ /* @__PURE__ */ jsxs21(View21, { style: styles21.infoCard, children: [
7759
+ /* @__PURE__ */ jsx24(Text22, { style: styles21.infoLabel, children: "TOTAL POT" }),
7760
+ /* @__PURE__ */ jsx24(Text22, { style: [styles21.infoValue, { color: "#4ade80" }], children: potSol })
7761
+ ] }),
7762
+ /* @__PURE__ */ jsxs21(View21, { style: styles21.infoCard, children: [
7763
+ /* @__PURE__ */ jsx24(Text22, { style: styles21.infoLabel, children: "LAST WIN" }),
7764
+ /* @__PURE__ */ jsx24(Text22, { style: [styles21.infoValue, { color: "#22c55e" }], children: lastWinner ? formatSOL(lastWinner.winAmount) : "\u2014" })
7765
+ ] })
7766
+ ] }),
7767
+ entries && entries.length > 0 && /* @__PURE__ */ jsxs21(View21, { style: styles21.playersSection, children: [
7768
+ /* @__PURE__ */ jsxs21(View21, { style: styles21.playersSectionHeader, children: [
7769
+ /* @__PURE__ */ jsx24(Text22, { style: styles21.playersSectionTitle, children: "Players in Round" }),
7770
+ /* @__PURE__ */ jsx24(View21, { style: styles21.activeCountBadge, children: /* @__PURE__ */ jsx24(Text22, { style: styles21.activeCountText, children: entries.length }) })
7771
+ ] }),
7772
+ /* @__PURE__ */ jsxs21(View21, { style: styles21.playersCarousel, children: [
7773
+ entries.slice(0, 8).map((entry, i) => {
7774
+ const odds = entry.oddsPercent;
7775
+ return /* @__PURE__ */ jsxs21(View21, { style: styles21.playerCard, children: [
7776
+ /* @__PURE__ */ jsx24(View21, { style: styles21.playerAvatar, children: /* @__PURE__ */ jsx24(Text22, { style: styles21.playerAvatarText, children: entry.player.slice(0, 2).toUpperCase() }) }),
7777
+ /* @__PURE__ */ jsx24(Text22, { style: styles21.playerWallet, numberOfLines: 1, children: truncateWallet2(entry.player) }),
7778
+ /* @__PURE__ */ jsxs21(Text22, { style: styles21.playerWager, children: [
7779
+ entry.weightSol.toFixed(2),
7780
+ " SOL"
7781
+ ] }),
7782
+ /* @__PURE__ */ jsxs21(Text22, { style: styles21.playerOdds, children: [
7783
+ odds,
7784
+ "%"
7785
+ ] })
7786
+ ] }, `${entry.player}-${i}`);
7787
+ }),
7788
+ entries.length > 8 && /* @__PURE__ */ jsx24(View21, { style: styles21.playerCardMore, children: /* @__PURE__ */ jsxs21(Text22, { style: styles21.playerMoreText, children: [
7789
+ "+",
7790
+ entries.length - 8
7791
+ ] }) })
7792
+ ] })
7793
+ ] }),
7794
+ /* @__PURE__ */ jsx24(
7795
+ TouchableOpacity17,
7796
+ {
7797
+ style: styles21.placeBetButton,
7798
+ activeOpacity: 0.85,
7799
+ onPress,
7800
+ children: /* @__PURE__ */ jsx24(Text22, { style: styles21.placeBetText, children: "Place Bet" })
7801
+ }
7802
+ )
7803
+ ] })
7804
+ ]
7805
+ }
7806
+ );
7807
+ }
7808
+ var styles21 = StyleSheet22.create({
7809
+ card: {
7810
+ borderRadius: 16,
7811
+ borderWidth: 1,
7812
+ borderColor: "rgba(34, 197, 94, 0.2)",
7813
+ backgroundColor: "#0c0c14",
7814
+ overflow: "hidden",
7815
+ position: "relative"
7816
+ },
7817
+ gradientBg: {
7818
+ ...StyleSheet22.absoluteFillObject,
7819
+ backgroundColor: "rgba(34, 197, 94, 0.04)"
7820
+ },
7821
+ shimmer: {
7822
+ position: "absolute",
7823
+ top: 0,
7824
+ bottom: 0,
7825
+ width: 120,
7826
+ backgroundColor: "rgba(34, 197, 94, 0.08)"
7827
+ },
7828
+ accentBar: {
7829
+ position: "absolute",
7830
+ bottom: 0,
7831
+ left: 0,
7832
+ right: "40%",
7833
+ height: 2,
7834
+ backgroundColor: "#22c55e"
7835
+ },
7836
+ content: {
7837
+ padding: 16,
7838
+ gap: 12
7839
+ },
7840
+ // Status row
7841
+ statusRow: {
7842
+ flexDirection: "row",
7843
+ alignItems: "center",
7844
+ gap: 8
7845
+ },
7846
+ statusBadge: {
7847
+ flexDirection: "row",
7848
+ alignItems: "center",
7849
+ gap: 6,
7850
+ paddingHorizontal: 10,
7851
+ paddingVertical: 4,
7852
+ borderRadius: 100
7853
+ },
7854
+ statusOpen: {
7855
+ backgroundColor: "rgba(34, 197, 94, 0.15)"
7856
+ },
7857
+ statusClosed: {
7858
+ backgroundColor: "rgba(156, 163, 175, 0.15)"
7859
+ },
7860
+ statusDot: {
7861
+ width: 6,
7862
+ height: 6,
7863
+ borderRadius: 3,
7864
+ backgroundColor: "#22c55e"
7865
+ },
7866
+ statusText: {
7867
+ fontSize: 12,
7868
+ fontWeight: "700"
7869
+ },
7870
+ entryCountText: {
7871
+ fontSize: 12,
7872
+ color: "#6b6b6b",
7873
+ fontWeight: "500"
7874
+ },
7875
+ // Hero pot
7876
+ heroSection: {
7877
+ flexDirection: "row",
7878
+ alignItems: "center",
7879
+ justifyContent: "space-between"
7880
+ },
7881
+ potInfo: {
7882
+ flex: 1
7883
+ },
7884
+ jackpotLabel: {
7885
+ fontSize: 10,
7886
+ fontWeight: "600",
7887
+ color: "#6b6b6b",
7888
+ letterSpacing: 3,
7889
+ textTransform: "uppercase",
7890
+ marginBottom: 4
7891
+ },
7892
+ potValue: {
7893
+ fontSize: 36,
7894
+ fontWeight: "900",
7895
+ color: "#4ade80",
7896
+ letterSpacing: -1
7897
+ },
7898
+ potEmoji: {
7899
+ fontSize: 40,
7900
+ opacity: 0.2
7901
+ },
7902
+ // Info grid
7903
+ infoGrid: {
7904
+ flexDirection: "row",
7905
+ gap: 8
7906
+ },
7907
+ infoCard: {
7908
+ flex: 1,
7909
+ borderRadius: 12,
7910
+ borderWidth: 1,
7911
+ borderColor: "#1e1e2a",
7912
+ backgroundColor: "#0c0c14",
7913
+ padding: 12
7914
+ },
7915
+ infoLabel: {
7916
+ fontSize: 9,
7917
+ fontWeight: "600",
7918
+ color: "#6b6b6b",
7919
+ letterSpacing: 2,
7920
+ textTransform: "uppercase",
7921
+ marginBottom: 4
7922
+ },
7923
+ infoValue: {
7924
+ fontSize: 18,
7925
+ fontWeight: "700",
7926
+ color: "#FFFFFF"
7927
+ },
7928
+ // Players section
7929
+ playersSection: {
7930
+ borderRadius: 12,
7931
+ borderWidth: 1,
7932
+ borderColor: "#1e1e2a",
7933
+ backgroundColor: "rgba(139, 92, 246, 0.04)",
7934
+ padding: 12,
7935
+ overflow: "hidden"
7936
+ },
7937
+ playersSectionHeader: {
7938
+ flexDirection: "row",
7939
+ alignItems: "center",
7940
+ justifyContent: "space-between",
7941
+ marginBottom: 10
7942
+ },
7943
+ playersSectionTitle: {
7944
+ fontSize: 13,
7945
+ fontWeight: "600",
7946
+ color: "#a0a0a0"
7947
+ },
7948
+ activeCountBadge: {
7949
+ backgroundColor: "rgba(34, 197, 94, 0.15)",
7950
+ paddingHorizontal: 8,
7951
+ paddingVertical: 2,
7952
+ borderRadius: 100
7953
+ },
7954
+ activeCountText: {
7955
+ fontSize: 11,
7956
+ fontWeight: "700",
7957
+ color: "#22c55e"
7958
+ },
7959
+ playersCarousel: {
7960
+ flexDirection: "row",
7961
+ gap: 10
7962
+ },
7963
+ playerCard: {
7964
+ width: 96,
7965
+ borderRadius: 12,
7966
+ borderWidth: 1.5,
7967
+ borderColor: "rgba(139, 92, 246, 0.4)",
7968
+ backgroundColor: "rgba(139, 92, 246, 0.08)",
7969
+ padding: 10,
7970
+ alignItems: "center",
7971
+ gap: 4
7972
+ },
7973
+ playerAvatar: {
7974
+ width: 40,
7975
+ height: 40,
7976
+ borderRadius: 20,
7977
+ borderWidth: 1.5,
7978
+ borderColor: "#8b5cf6",
7979
+ backgroundColor: "rgba(139, 92, 246, 0.2)",
7980
+ alignItems: "center",
7981
+ justifyContent: "center"
7982
+ },
7983
+ playerAvatarText: {
7984
+ fontSize: 14,
7985
+ fontWeight: "700",
7986
+ color: "#a78bfa"
7987
+ },
7988
+ playerWallet: {
7989
+ fontSize: 10,
7990
+ fontWeight: "500",
7991
+ color: "#a0a0a0",
7992
+ width: "100%",
7993
+ textAlign: "center"
7994
+ },
7995
+ playerWager: {
7996
+ fontSize: 12,
7997
+ fontWeight: "600",
7998
+ color: "#a78bfa"
7999
+ },
8000
+ playerOdds: {
8001
+ fontSize: 10,
8002
+ fontWeight: "700",
8003
+ color: "#22c55e"
8004
+ },
8005
+ playerCardMore: {
8006
+ width: 96,
8007
+ borderRadius: 12,
8008
+ borderWidth: 1.5,
8009
+ borderColor: "#1e1e2a",
8010
+ backgroundColor: "#14141e",
8011
+ alignItems: "center",
8012
+ justifyContent: "center"
8013
+ },
8014
+ playerMoreText: {
8015
+ fontSize: 16,
8016
+ fontWeight: "700",
8017
+ color: "#6b6b6b"
8018
+ },
8019
+ // Place bet CTA
8020
+ placeBetButton: {
8021
+ height: 52,
8022
+ borderRadius: 12,
8023
+ alignItems: "center",
8024
+ justifyContent: "center",
8025
+ backgroundColor: "#22c55e",
8026
+ shadowColor: "#22c55e",
8027
+ shadowOffset: { width: 0, height: 4 },
8028
+ shadowOpacity: 0.25,
8029
+ shadowRadius: 12,
8030
+ elevation: 6
8031
+ },
8032
+ placeBetText: {
8033
+ color: "#FFFFFF",
8034
+ fontSize: 16,
8035
+ fontWeight: "700"
8036
+ }
8037
+ });
8038
+
8039
+ // src/ui/jackpot/JackpotSheet.tsx
8040
+ import { useState as useState39, useEffect as useEffect27, useRef as useRef16, useCallback as useCallback34 } from "react";
8041
+ import {
8042
+ View as View22,
8043
+ Text as Text23,
8044
+ TouchableOpacity as TouchableOpacity18,
8045
+ ActivityIndicator as ActivityIndicator12,
8046
+ Modal as Modal8,
8047
+ Animated as Animated10,
8048
+ StyleSheet as StyleSheet23,
8049
+ ScrollView as ScrollView5,
8050
+ FlatList as FlatList2,
8051
+ TextInput as TextInput3,
8052
+ Platform as Platform13
8053
+ } from "react-native";
8054
+ import { jsx as jsx25, jsxs as jsxs22 } from "react/jsx-runtime";
8055
+ var STATUS_LABELS7 = {
8056
+ building: "Building transaction...",
8057
+ signing: "Approve in wallet...",
8058
+ confirming: "Confirming entry...",
8059
+ success: "You're in! \u{1F3C6}"
8060
+ };
8061
+ var QUICK_AMOUNTS = [0.1, 0.5, 1];
8062
+ function formatSOL2(lamports) {
8063
+ const val = typeof lamports === "string" ? parseInt(lamports, 10) : lamports;
8064
+ if (isNaN(val) || val === 0) return "0";
8065
+ const sol = val / 1e9;
8066
+ if (sol >= 100) return sol.toFixed(0);
8067
+ if (sol >= 1) return sol.toFixed(2);
8068
+ if (sol >= 0.01) return sol.toFixed(3);
8069
+ return sol.toFixed(4);
8070
+ }
8071
+ function truncateWallet3(addr) {
8072
+ if (!addr || addr.length < 8) return addr || "";
8073
+ return `${addr.slice(0, 4)}...${addr.slice(-4)}`;
8074
+ }
8075
+ function JackpotSheet({
8076
+ visible,
8077
+ onDismiss,
8078
+ onSuccess,
8079
+ onError,
8080
+ minEntry = 0.01,
8081
+ maxEntry = 10
8082
+ }) {
8083
+ const t = useDubsTheme();
8084
+ const { wallet, client } = useDubs();
8085
+ const { round, lastWinner, refetch } = useJackpot();
8086
+ const mutation = useEnterJackpot();
8087
+ const [betAmount, setBetAmount] = useState39("0.1");
8088
+ const [entries, setEntries] = useState39([]);
8089
+ const overlayOpacity = useRef16(new Animated10.Value(0)).current;
8090
+ useEffect27(() => {
8091
+ Animated10.timing(overlayOpacity, {
8092
+ toValue: visible ? 1 : 0,
8093
+ duration: 250,
8094
+ useNativeDriver: true
8095
+ }).start();
8096
+ }, [visible, overlayOpacity]);
8097
+ useEffect27(() => {
8098
+ if (visible) {
8099
+ mutation.reset();
8100
+ setBetAmount("0.1");
8101
+ refetch();
8102
+ client.getJackpotEntries().then((res) => setEntries(res.entries)).catch(() => {
8103
+ });
8104
+ }
8105
+ }, [visible]);
8106
+ useEffect27(() => {
8107
+ if (mutation.status === "success" && mutation.data) {
8108
+ onSuccess?.(mutation.data);
8109
+ refetch();
8110
+ client.getJackpotEntries().then((res) => setEntries(res.entries)).catch(() => {
8111
+ });
8112
+ const timer = setTimeout(() => onDismiss(), 2500);
8113
+ return () => clearTimeout(timer);
8114
+ }
8115
+ }, [mutation.status, mutation.data]);
8116
+ useEffect27(() => {
8117
+ if (mutation.status === "error" && mutation.error) {
8118
+ onError?.(mutation.error);
8119
+ }
8120
+ }, [mutation.status, mutation.error]);
8121
+ const potSol = round ? Number(BigInt(round.totalPotLamports || "0")) / 1e9 : 0;
8122
+ const totalWeight = round ? Number(BigInt(round.totalWeight || "0")) : 0;
8123
+ const betSol = parseFloat(betAmount) || 0;
8124
+ const entryLamports = Math.round(betSol * 1e9);
8125
+ const userOdds = totalWeight > 0 ? (entryLamports / (totalWeight + entryLamports) * 100).toFixed(1) : entries.length === 0 ? "100.0" : "0.0";
8126
+ const isMutating = mutation.status !== "idle" && mutation.status !== "success" && mutation.status !== "error";
8127
+ const canEnter = !isMutating && mutation.status !== "success" && round?.status === "Open" && betSol >= minEntry;
8128
+ const handleQuickAdd = useCallback34((amount) => {
8129
+ setBetAmount((prev) => {
8130
+ const current = parseFloat(prev) || 0;
8131
+ const next = Math.min(current + amount, maxEntry);
8132
+ return next.toFixed(2);
8133
+ });
8134
+ }, [maxEntry]);
8135
+ const handleEnter = useCallback34(async () => {
8136
+ if (!wallet.publicKey || entryLamports < 1e4) return;
8137
+ try {
8138
+ await mutation.execute(entryLamports);
8139
+ } catch {
8140
+ }
8141
+ }, [wallet.publicKey, mutation.execute, entryLamports]);
8142
+ const statusLabel = STATUS_LABELS7[mutation.status] || "";
8143
+ const renderPlayerCard = ({ item, index }) => /* @__PURE__ */ jsxs22(View22, { style: styles22.playerCard, children: [
8144
+ /* @__PURE__ */ jsx25(View22, { style: styles22.playerAvatar, children: /* @__PURE__ */ jsx25(Text23, { style: styles22.playerAvatarText, children: item.player.slice(0, 2).toUpperCase() }) }),
8145
+ /* @__PURE__ */ jsxs22(Text23, { style: styles22.playerUsername, numberOfLines: 1, children: [
8146
+ "@",
8147
+ truncateWallet3(item.player)
8148
+ ] }),
8149
+ /* @__PURE__ */ jsx25(View22, { style: styles22.playerWagerRow, children: /* @__PURE__ */ jsxs22(Text23, { style: styles22.playerWagerAmount, children: [
8150
+ item.weightSol.toFixed(2),
8151
+ " SOL"
8152
+ ] }) }),
8153
+ /* @__PURE__ */ jsxs22(Text23, { style: styles22.playerOdds, children: [
8154
+ item.oddsPercent,
8155
+ "%"
8156
+ ] })
8157
+ ] });
8158
+ return /* @__PURE__ */ jsxs22(
8159
+ Modal8,
8160
+ {
8161
+ visible,
8162
+ animationType: "slide",
8163
+ transparent: true,
8164
+ onRequestClose: onDismiss,
8165
+ children: [
8166
+ /* @__PURE__ */ jsx25(Animated10.View, { style: [styles22.overlay, { opacity: overlayOpacity }], children: /* @__PURE__ */ jsx25(TouchableOpacity18, { style: styles22.overlayTap, activeOpacity: 1, onPress: onDismiss }) }),
8167
+ /* @__PURE__ */ jsx25(View22, { style: styles22.sheetPositioner, children: /* @__PURE__ */ jsxs22(View22, { style: styles22.sheet, children: [
8168
+ /* @__PURE__ */ jsx25(View22, { style: styles22.handleRow, children: /* @__PURE__ */ jsx25(View22, { style: styles22.handle }) }),
8169
+ /* @__PURE__ */ jsxs22(
8170
+ ScrollView5,
8171
+ {
8172
+ style: styles22.scrollView,
8173
+ contentContainerStyle: styles22.scrollContent,
8174
+ showsVerticalScrollIndicator: false,
8175
+ bounces: true,
8176
+ children: [
8177
+ /* @__PURE__ */ jsxs22(View22, { style: styles22.header, children: [
8178
+ /* @__PURE__ */ jsx25(Text23, { style: styles22.headerTitle, children: "Jackpot" }),
8179
+ /* @__PURE__ */ jsx25(TouchableOpacity18, { onPress: onDismiss, activeOpacity: 0.8, style: styles22.closeBtn, children: /* @__PURE__ */ jsx25(Text23, { style: styles22.closeBtnText, children: "\u2715" }) })
8180
+ ] }),
8181
+ /* @__PURE__ */ jsxs22(View22, { style: styles22.heroCard, children: [
8182
+ /* @__PURE__ */ jsx25(View22, { style: styles22.heroGradient }),
8183
+ /* @__PURE__ */ jsx25(Text23, { style: styles22.heroLabel, children: "JACKPOT" }),
8184
+ /* @__PURE__ */ jsxs22(View22, { style: styles22.heroPotRow, children: [
8185
+ /* @__PURE__ */ jsxs22(Text23, { style: styles22.heroPotValue, children: [
8186
+ potSol.toFixed(4),
8187
+ " SOL"
8188
+ ] }),
8189
+ /* @__PURE__ */ jsx25(Text23, { style: styles22.heroEmoji, children: "\u{1F911}" })
8190
+ ] }),
8191
+ /* @__PURE__ */ jsx25(View22, { style: styles22.heroAccentBar })
8192
+ ] }),
8193
+ /* @__PURE__ */ jsxs22(View22, { style: styles22.infoRow, children: [
8194
+ /* @__PURE__ */ jsxs22(View22, { style: styles22.infoCard, children: [
8195
+ /* @__PURE__ */ jsx25(Text23, { style: styles22.infoCardLabel, children: "YOUR WAGER" }),
8196
+ /* @__PURE__ */ jsx25(Text23, { style: styles22.infoCardValue, children: betSol.toFixed(2) })
8197
+ ] }),
8198
+ /* @__PURE__ */ jsxs22(View22, { style: styles22.infoCard, children: [
8199
+ /* @__PURE__ */ jsx25(Text23, { style: styles22.infoCardLabel, children: "YOUR CHANCE" }),
8200
+ /* @__PURE__ */ jsxs22(Text23, { style: [styles22.infoCardValue, { color: "#22c55e" }], children: [
8201
+ userOdds,
8202
+ "%"
8203
+ ] })
8204
+ ] }),
8205
+ /* @__PURE__ */ jsxs22(View22, { style: styles22.infoCard, children: [
8206
+ /* @__PURE__ */ jsx25(Text23, { style: styles22.infoCardLabel, children: "PLAYERS" }),
8207
+ /* @__PURE__ */ jsx25(Text23, { style: styles22.infoCardValue, children: entries.length })
8208
+ ] })
8209
+ ] }),
8210
+ entries.length > 0 && /* @__PURE__ */ jsxs22(View22, { style: styles22.playersSection, children: [
8211
+ /* @__PURE__ */ jsxs22(View22, { style: styles22.playersSectionHeader, children: [
8212
+ /* @__PURE__ */ jsx25(Text23, { style: styles22.playersSectionTitle, children: "Players in Round" }),
8213
+ /* @__PURE__ */ jsxs22(View22, { style: styles22.playersBadge, children: [
8214
+ /* @__PURE__ */ jsx25(View22, { style: styles22.playersBadgeDot }),
8215
+ /* @__PURE__ */ jsxs22(Text23, { style: styles22.playersBadgeText, children: [
8216
+ entries.length,
8217
+ " active"
8218
+ ] })
8219
+ ] })
8220
+ ] }),
8221
+ /* @__PURE__ */ jsx25(
8222
+ FlatList2,
8223
+ {
8224
+ data: entries,
8225
+ renderItem: renderPlayerCard,
8226
+ keyExtractor: (item, i) => `${item.player}-${i}`,
8227
+ horizontal: true,
8228
+ showsHorizontalScrollIndicator: false,
8229
+ contentContainerStyle: styles22.playersListContent,
8230
+ ItemSeparatorComponent: () => /* @__PURE__ */ jsx25(View22, { style: { width: 10 } })
8231
+ }
8232
+ )
8233
+ ] }),
8234
+ entries.length === 0 && /* @__PURE__ */ jsxs22(View22, { style: styles22.emptyState, children: [
8235
+ /* @__PURE__ */ jsx25(Text23, { style: styles22.emptyEmoji, children: "\u{1F911}" }),
8236
+ /* @__PURE__ */ jsx25(Text23, { style: styles22.emptyTitle, children: "No players yet" }),
8237
+ /* @__PURE__ */ jsx25(Text23, { style: styles22.emptySubtitle, children: "Be the first to join!" })
8238
+ ] }),
8239
+ /* @__PURE__ */ jsxs22(View22, { style: styles22.betSection, children: [
8240
+ /* @__PURE__ */ jsx25(Text23, { style: styles22.betLabel, children: "Bet Amount" }),
8241
+ /* @__PURE__ */ jsxs22(View22, { style: styles22.betInputRow, children: [
8242
+ /* @__PURE__ */ jsx25(View22, { style: styles22.solIcon, children: /* @__PURE__ */ jsx25(Text23, { style: styles22.solIconText, children: "\u25CE" }) }),
8243
+ /* @__PURE__ */ jsx25(
8244
+ TextInput3,
8245
+ {
8246
+ style: styles22.betInput,
8247
+ value: betAmount,
8248
+ onChangeText: setBetAmount,
8249
+ keyboardType: "decimal-pad",
8250
+ placeholder: "0.00",
8251
+ placeholderTextColor: "#6b6b6b",
8252
+ selectTextOnFocus: true
8253
+ }
8254
+ ),
8255
+ /* @__PURE__ */ jsx25(Text23, { style: styles22.betInputSuffix, children: "SOL" })
8256
+ ] }),
8257
+ /* @__PURE__ */ jsx25(View22, { style: styles22.quickBetRow, children: QUICK_AMOUNTS.map((amount) => /* @__PURE__ */ jsx25(
8258
+ TouchableOpacity18,
8259
+ {
8260
+ style: styles22.quickBetBtn,
8261
+ onPress: () => handleQuickAdd(amount),
8262
+ activeOpacity: 0.7,
8263
+ children: /* @__PURE__ */ jsxs22(Text23, { style: styles22.quickBetText, children: [
8264
+ "+",
8265
+ amount,
8266
+ " SOL"
8267
+ ] })
8268
+ },
8269
+ amount
8270
+ )) })
8271
+ ] }),
8272
+ mutation.error && /* @__PURE__ */ jsx25(View22, { style: styles22.errorBox, children: /* @__PURE__ */ jsx25(Text23, { style: styles22.errorText, children: mutation.error.message }) }),
8273
+ /* @__PURE__ */ jsx25(
8274
+ TouchableOpacity18,
8275
+ {
8276
+ style: [
8277
+ styles22.placeBetBtn,
8278
+ !canEnter && styles22.placeBetBtnDisabled,
8279
+ mutation.status === "success" && styles22.placeBetBtnSuccess
8280
+ ],
8281
+ disabled: !canEnter,
8282
+ onPress: handleEnter,
8283
+ activeOpacity: 0.85,
8284
+ children: isMutating ? /* @__PURE__ */ jsxs22(View22, { style: styles22.placeBetLoading, children: [
8285
+ /* @__PURE__ */ jsx25(ActivityIndicator12, { size: "small", color: "#FFFFFF" }),
8286
+ /* @__PURE__ */ jsx25(Text23, { style: styles22.placeBetText, children: statusLabel })
8287
+ ] }) : mutation.status === "success" ? /* @__PURE__ */ jsx25(Text23, { style: styles22.placeBetText, children: "You're in! \u{1F3C6}" }) : /* @__PURE__ */ jsxs22(Text23, { style: [styles22.placeBetText, !canEnter && { opacity: 0.5 }], children: [
8288
+ "Place Bet \u2014 ",
8289
+ betSol.toFixed(2),
8290
+ " SOL"
8291
+ ] })
8292
+ }
8293
+ ),
8294
+ lastWinner && /* @__PURE__ */ jsxs22(View22, { style: styles22.lastWinnerSection, children: [
8295
+ /* @__PURE__ */ jsx25(Text23, { style: styles22.lastWinnerLabel, children: "Last Winner" }),
8296
+ /* @__PURE__ */ jsxs22(View22, { style: styles22.lastWinnerRow, children: [
8297
+ /* @__PURE__ */ jsx25(Text23, { style: styles22.lastWinnerWallet, children: truncateWallet3(lastWinner.winner) }),
8298
+ /* @__PURE__ */ jsxs22(Text23, { style: styles22.lastWinnerAmount, children: [
8299
+ "won ",
8300
+ formatSOL2(lastWinner.winAmount),
8301
+ " SOL"
8302
+ ] })
8303
+ ] })
8304
+ ] })
8305
+ ]
8306
+ }
8307
+ )
8308
+ ] }) })
8309
+ ]
8310
+ }
8311
+ );
8312
+ }
8313
+ var styles22 = StyleSheet23.create({
8314
+ overlay: {
8315
+ ...StyleSheet23.absoluteFillObject,
8316
+ backgroundColor: "rgba(0,0,0,0.65)"
8317
+ },
8318
+ overlayTap: { flex: 1 },
8319
+ sheetPositioner: {
8320
+ flex: 1,
8321
+ justifyContent: "flex-end"
8322
+ },
8323
+ sheet: {
8324
+ backgroundColor: "#08080e",
8325
+ borderTopLeftRadius: 24,
8326
+ borderTopRightRadius: 24,
8327
+ maxHeight: "92%",
8328
+ borderWidth: 1,
8329
+ borderColor: "#1e1e2a",
8330
+ borderBottomWidth: 0
8331
+ },
8332
+ handleRow: { alignItems: "center", paddingTop: 10, paddingBottom: 4 },
8333
+ handle: { width: 36, height: 4, borderRadius: 2, backgroundColor: "#2a2a3a" },
8334
+ scrollView: { flexGrow: 0 },
8335
+ scrollContent: {
8336
+ paddingHorizontal: 16,
8337
+ paddingBottom: Platform13.OS === "ios" ? 40 : 24
8338
+ },
8339
+ // Header
8340
+ header: {
8341
+ flexDirection: "row",
8342
+ alignItems: "center",
8343
+ justifyContent: "space-between",
8344
+ paddingVertical: 12
8345
+ },
8346
+ headerTitle: { fontSize: 20, fontWeight: "700", color: "#FFFFFF" },
8347
+ closeBtn: { padding: 4 },
8348
+ closeBtnText: { fontSize: 20, color: "#6b6b6b" },
8349
+ // Hero pot card
8350
+ heroCard: {
8351
+ borderRadius: 16,
8352
+ borderWidth: 1,
8353
+ borderColor: "rgba(34, 197, 94, 0.2)",
8354
+ padding: 16,
8355
+ overflow: "hidden",
8356
+ position: "relative"
8357
+ },
8358
+ heroGradient: {
8359
+ ...StyleSheet23.absoluteFillObject,
8360
+ backgroundColor: "rgba(34, 197, 94, 0.04)"
8361
+ },
8362
+ heroLabel: {
8363
+ fontSize: 10,
8364
+ fontWeight: "600",
8365
+ color: "#6b6b6b",
8366
+ letterSpacing: 3,
8367
+ marginBottom: 4
8368
+ },
8369
+ heroPotRow: {
8370
+ flexDirection: "row",
8371
+ alignItems: "center",
8372
+ justifyContent: "space-between"
8373
+ },
8374
+ heroPotValue: {
8375
+ fontSize: 32,
8376
+ fontWeight: "900",
8377
+ color: "#4ade80",
8378
+ letterSpacing: -1
8379
+ },
8380
+ heroEmoji: {
8381
+ fontSize: 36,
8382
+ opacity: 0.2
8383
+ },
8384
+ heroAccentBar: {
8385
+ position: "absolute",
8386
+ bottom: 0,
8387
+ left: 0,
8388
+ width: "60%",
8389
+ height: 2,
8390
+ backgroundColor: "#22c55e"
8391
+ },
8392
+ // Info row
8393
+ infoRow: {
8394
+ flexDirection: "row",
8395
+ gap: 8,
8396
+ marginTop: 12
8397
+ },
8398
+ infoCard: {
8399
+ flex: 1,
8400
+ borderRadius: 12,
8401
+ borderWidth: 1,
8402
+ borderColor: "#1e1e2a",
8403
+ backgroundColor: "#0c0c14",
8404
+ padding: 12
8405
+ },
8406
+ infoCardLabel: {
8407
+ fontSize: 9,
8408
+ fontWeight: "600",
8409
+ color: "#6b6b6b",
8410
+ letterSpacing: 2,
8411
+ marginBottom: 4
8412
+ },
8413
+ infoCardValue: {
8414
+ fontSize: 18,
8415
+ fontWeight: "700",
8416
+ color: "#FFFFFF"
8417
+ },
8418
+ // Players section
8419
+ playersSection: {
8420
+ marginTop: 16,
8421
+ borderRadius: 16,
8422
+ borderWidth: 1,
8423
+ borderColor: "#1e1e2a",
8424
+ backgroundColor: "rgba(139, 92, 246, 0.03)",
8425
+ padding: 12,
8426
+ overflow: "hidden"
8427
+ },
8428
+ playersSectionHeader: {
8429
+ flexDirection: "row",
8430
+ alignItems: "center",
8431
+ justifyContent: "space-between",
8432
+ marginBottom: 12
8433
+ },
8434
+ playersSectionTitle: {
8435
+ fontSize: 13,
8436
+ fontWeight: "600",
8437
+ color: "#a0a0a0"
8438
+ },
8439
+ playersBadge: {
8440
+ flexDirection: "row",
8441
+ alignItems: "center",
8442
+ gap: 6,
8443
+ backgroundColor: "rgba(34, 197, 94, 0.15)",
8444
+ paddingHorizontal: 8,
8445
+ paddingVertical: 3,
8446
+ borderRadius: 100
8447
+ },
8448
+ playersBadgeDot: {
8449
+ width: 6,
8450
+ height: 6,
8451
+ borderRadius: 3,
8452
+ backgroundColor: "#22c55e"
8453
+ },
8454
+ playersBadgeText: {
8455
+ fontSize: 11,
8456
+ fontWeight: "700",
8457
+ color: "#22c55e"
8458
+ },
8459
+ playersListContent: {
8460
+ paddingRight: 4
8461
+ },
8462
+ // Player cards (carousel items)
8463
+ playerCard: {
8464
+ width: 110,
8465
+ borderRadius: 12,
8466
+ borderWidth: 1.5,
8467
+ borderColor: "rgba(139, 92, 246, 0.4)",
8468
+ backgroundColor: "rgba(139, 92, 246, 0.08)",
8469
+ padding: 12,
8470
+ alignItems: "center",
8471
+ gap: 6
8472
+ },
8473
+ playerAvatar: {
8474
+ width: 48,
8475
+ height: 48,
8476
+ borderRadius: 24,
8477
+ borderWidth: 2,
8478
+ borderColor: "#8b5cf6",
8479
+ backgroundColor: "rgba(139, 92, 246, 0.15)",
8480
+ alignItems: "center",
8481
+ justifyContent: "center"
8482
+ },
8483
+ playerAvatarText: {
8484
+ fontSize: 16,
8485
+ fontWeight: "700",
8486
+ color: "#a78bfa"
8487
+ },
8488
+ playerUsername: {
8489
+ fontSize: 11,
8490
+ fontWeight: "500",
8491
+ color: "#a0a0a0",
8492
+ textAlign: "center"
8493
+ },
8494
+ playerWagerRow: {
8495
+ flexDirection: "row",
8496
+ alignItems: "center",
8497
+ gap: 4
8498
+ },
8499
+ playerWagerAmount: {
8500
+ fontSize: 13,
8501
+ fontWeight: "600",
8502
+ color: "#a78bfa"
8503
+ },
8504
+ playerOdds: {
8505
+ fontSize: 11,
8506
+ fontWeight: "700",
8507
+ color: "#22c55e"
8508
+ },
8509
+ // Empty state
8510
+ emptyState: {
8511
+ marginTop: 16,
8512
+ alignItems: "center",
8513
+ paddingVertical: 24,
8514
+ borderRadius: 16,
8515
+ borderWidth: 1,
8516
+ borderColor: "#1e1e2a",
8517
+ backgroundColor: "#0c0c14"
8518
+ },
8519
+ emptyEmoji: { fontSize: 40, marginBottom: 8 },
8520
+ emptyTitle: { fontSize: 16, fontWeight: "600", color: "#FFFFFF" },
8521
+ emptySubtitle: { fontSize: 13, color: "#6b6b6b", marginTop: 4 },
8522
+ // Bet section
8523
+ betSection: {
8524
+ marginTop: 16
8525
+ },
8526
+ betLabel: {
8527
+ fontSize: 13,
8528
+ fontWeight: "500",
8529
+ color: "#6b6b6b",
8530
+ marginBottom: 8
8531
+ },
8532
+ betInputRow: {
8533
+ flexDirection: "row",
8534
+ alignItems: "center",
8535
+ gap: 8,
8536
+ backgroundColor: "#08080e",
8537
+ borderWidth: 1,
8538
+ borderColor: "#2a2a3a",
8539
+ borderRadius: 12,
8540
+ paddingHorizontal: 14,
8541
+ paddingVertical: Platform13.OS === "ios" ? 14 : 10
8542
+ },
8543
+ solIcon: {
8544
+ width: 32,
8545
+ height: 32,
8546
+ borderRadius: 16,
8547
+ backgroundColor: "rgba(139, 92, 246, 0.15)",
8548
+ alignItems: "center",
8549
+ justifyContent: "center"
8550
+ },
8551
+ solIconText: {
8552
+ fontSize: 18,
8553
+ color: "#a78bfa",
8554
+ fontWeight: "700"
8555
+ },
8556
+ betInput: {
8557
+ flex: 1,
8558
+ fontSize: 24,
8559
+ fontWeight: "700",
8560
+ color: "#FFFFFF",
8561
+ padding: 0
8562
+ },
8563
+ betInputSuffix: {
8564
+ fontSize: 14,
8565
+ fontWeight: "500",
8566
+ color: "#6b6b6b"
8567
+ },
8568
+ quickBetRow: {
8569
+ flexDirection: "row",
8570
+ gap: 8,
8571
+ marginTop: 10
8572
+ },
8573
+ quickBetBtn: {
8574
+ flex: 1,
8575
+ paddingVertical: 12,
8576
+ borderRadius: 10,
8577
+ borderWidth: 1,
8578
+ borderColor: "#2a2a3a",
8579
+ backgroundColor: "#08080e",
8580
+ alignItems: "center"
8581
+ },
8582
+ quickBetText: {
8583
+ fontSize: 13,
8584
+ fontWeight: "600",
8585
+ color: "#FFFFFF"
8586
+ },
8587
+ // Error
8588
+ errorBox: {
8589
+ marginTop: 12,
8590
+ borderRadius: 12,
8591
+ borderWidth: 1,
8592
+ borderColor: "#3A1515",
8593
+ backgroundColor: "#1A0A0A",
8594
+ padding: 12
8595
+ },
8596
+ errorText: { fontSize: 13, fontWeight: "500", color: "#F87171" },
8597
+ // Place Bet CTA
8598
+ placeBetBtn: {
8599
+ marginTop: 16,
8600
+ height: 56,
8601
+ borderRadius: 14,
8602
+ justifyContent: "center",
8603
+ alignItems: "center",
8604
+ backgroundColor: "#22c55e",
8605
+ shadowColor: "#22c55e",
8606
+ shadowOffset: { width: 0, height: 4 },
8607
+ shadowOpacity: 0.25,
8608
+ shadowRadius: 12,
8609
+ elevation: 6
8610
+ },
8611
+ placeBetBtnDisabled: {
8612
+ backgroundColor: "#1e1e2a",
8613
+ shadowOpacity: 0,
8614
+ elevation: 0
8615
+ },
8616
+ placeBetBtnSuccess: {
8617
+ backgroundColor: "#16a34a"
8618
+ },
8619
+ placeBetText: { color: "#FFFFFF", fontSize: 16, fontWeight: "700" },
8620
+ placeBetLoading: { flexDirection: "row", alignItems: "center", gap: 10 },
8621
+ // Last winner
8622
+ lastWinnerSection: {
8623
+ marginTop: 14,
8624
+ paddingTop: 14,
8625
+ borderTopWidth: 1,
8626
+ borderTopColor: "#1e1e2a"
8627
+ },
8628
+ lastWinnerLabel: {
8629
+ fontSize: 11,
8630
+ fontWeight: "600",
8631
+ color: "#6b6b6b",
8632
+ letterSpacing: 1,
8633
+ textTransform: "uppercase",
8634
+ marginBottom: 4
8635
+ },
8636
+ lastWinnerRow: {
8637
+ flexDirection: "row",
8638
+ alignItems: "center",
8639
+ gap: 6
8640
+ },
8641
+ lastWinnerWallet: {
8642
+ fontSize: 13,
8643
+ fontWeight: "500",
8644
+ color: "#a0a0a0"
8645
+ },
8646
+ lastWinnerAmount: {
8647
+ fontSize: 13,
8648
+ fontWeight: "600",
8649
+ color: "#22c55e"
8650
+ }
8651
+ });
8652
+
8653
+ // src/ui/jackpot/JackpotWidget.tsx
8654
+ import { useState as useState40, useEffect as useEffect28 } from "react";
8655
+ import { Fragment as Fragment7, jsx as jsx26, jsxs as jsxs23 } from "react/jsx-runtime";
8656
+ function JackpotWidget({
8657
+ style,
8658
+ minEntry,
8659
+ maxEntry,
8660
+ onEntry,
8661
+ onError
8662
+ }) {
8663
+ const [sheetVisible, setSheetVisible] = useState40(false);
8664
+ const { round, lastWinner } = useJackpot();
8665
+ const { client } = useDubs();
8666
+ const [entries, setEntries] = useState40([]);
8667
+ useEffect28(() => {
8668
+ client.getJackpotEntries().then((res) => setEntries(res.entries)).catch(() => {
8669
+ });
8670
+ }, [client, round?.entryCount]);
8671
+ return /* @__PURE__ */ jsxs23(Fragment7, { children: [
8672
+ /* @__PURE__ */ jsx26(
8673
+ JackpotCard,
8674
+ {
8675
+ round,
8676
+ lastWinner,
8677
+ entries,
8678
+ onPress: () => setSheetVisible(true),
8679
+ style
8680
+ }
8681
+ ),
8682
+ /* @__PURE__ */ jsx26(
8683
+ JackpotSheet,
8684
+ {
8685
+ visible: sheetVisible,
8686
+ onDismiss: () => setSheetVisible(false),
8687
+ onSuccess: (result) => {
8688
+ onEntry?.(result);
8689
+ client.getJackpotEntries().then((res) => setEntries(res.entries)).catch(() => {
8690
+ });
8691
+ },
8692
+ onError,
8693
+ minEntry,
8694
+ maxEntry
8695
+ }
8696
+ )
8697
+ ] });
8698
+ }
8699
+
8700
+ // src/chat/provider.tsx
8701
+ import { createContext as createContext5, useContext as useContext5, useEffect as useEffect29, useRef as useRef17, useState as useState41, useCallback as useCallback35, useMemo as useMemo11 } from "react";
8702
+ import { AppState } from "react-native";
8703
+
8704
+ // src/chat/socket.ts
8705
+ import { io } from "socket.io-client";
8706
+ var ChatSocket = class {
8707
+ constructor() {
8708
+ this.socket = null;
8709
+ this.listeners = {};
8710
+ }
8711
+ /** Set event listeners. Call before or after connect. */
8712
+ setListeners(listeners) {
8713
+ this.listeners = listeners;
8714
+ }
8715
+ /** Connect to the /chat namespace */
8716
+ connect(config) {
8717
+ if (this.socket?.connected) {
8718
+ console.log("[Dubs:ChatSocket] Already connected");
8719
+ return;
8720
+ }
8721
+ this.listeners.onConnectionChange?.("connecting");
8722
+ console.log(`[Dubs:ChatSocket] Connecting to ${config.host}/chat`);
8723
+ this.socket = io(`${config.host}/chat`, {
8724
+ auth: { token: config.token },
8725
+ transports: ["websocket"],
8726
+ reconnection: true,
8727
+ reconnectionDelay: 1e3,
8728
+ reconnectionAttempts: 10
8729
+ });
8730
+ this.socket.on("connect", () => {
8731
+ console.log("[Dubs:ChatSocket] Connected");
8732
+ this.listeners.onConnectionChange?.("connected");
8733
+ });
8734
+ this.socket.on("disconnect", (reason) => {
8735
+ console.log("[Dubs:ChatSocket] Disconnected:", reason);
8736
+ this.listeners.onConnectionChange?.("disconnected");
8737
+ });
8738
+ this.socket.on("connect_error", (error) => {
8739
+ console.error("[Dubs:ChatSocket] Connection error:", error.message);
8740
+ this.listeners.onConnectionChange?.("error");
8741
+ });
8742
+ this.socket.on("new_message", (msg) => this.listeners.onNewMessage?.(msg));
8743
+ this.socket.on("online_users", (users) => this.listeners.onOnlineUsers?.(users));
8744
+ this.socket.on("online_count", (count) => this.listeners.onOnlineCount?.(count));
8745
+ this.socket.on("user_typing", (data) => this.listeners.onTyping?.(data));
8746
+ this.socket.on("reaction_added", (data) => this.listeners.onReactionAdded?.(data));
8747
+ this.socket.on("reaction_removed", (data) => this.listeners.onReactionRemoved?.(data));
8748
+ this.socket.on("notification", (n) => this.listeners.onNotification?.(n));
8749
+ this.socket.on("unread_count", (count) => this.listeners.onUnreadCount?.(count));
8750
+ this.socket.on("dm:new_message", (msg) => this.listeners.onDMNewMessage?.(msg));
8751
+ this.socket.on("dm:message_sent", (msg) => this.listeners.onDMMessageSent?.(msg));
8752
+ this.socket.on("dm:notification", (data) => this.listeners.onDMNotification?.(data));
8753
+ this.socket.on("dm:messages_read", (data) => this.listeners.onDMMessagesRead?.(data));
8754
+ this.socket.on("friend_request_accepted", (data) => this.listeners.onFriendRequestAccepted?.(data));
8755
+ this.socket.on("friend_request_declined", (data) => this.listeners.onFriendRequestDeclined?.(data));
8756
+ this.socket.on("friend_removed", (data) => this.listeners.onFriendRemoved?.(data));
8757
+ this.socket.on("error", (err) => this.listeners.onError?.(err));
8758
+ }
8759
+ /** Disconnect from the chat namespace */
8760
+ disconnect() {
8761
+ if (this.socket) {
8762
+ console.log("[Dubs:ChatSocket] Disconnecting");
8763
+ this.socket.removeAllListeners();
8764
+ this.socket.disconnect();
8765
+ this.socket = null;
8766
+ }
8767
+ }
8768
+ /** Whether the socket is currently connected */
8769
+ isConnected() {
8770
+ return this.socket?.connected ?? false;
8771
+ }
8772
+ // ── Global Chat Actions ──
8773
+ /** Send a message to global chat */
8774
+ sendMessage(params) {
8775
+ this.socket?.emit("send_message", params);
8776
+ }
8777
+ /** Emit typing indicator */
8778
+ sendTyping(isTyping) {
8779
+ this.socket?.emit("typing", isTyping);
8780
+ }
8781
+ /** Add an emoji reaction to a message */
8782
+ addReaction(messageId, reaction) {
8783
+ this.socket?.emit("add_reaction", { messageId, reaction });
8784
+ }
8785
+ /** Remove an emoji reaction from a message */
8786
+ removeReaction(messageId, reaction) {
8787
+ this.socket?.emit("remove_reaction", { messageId, reaction });
8788
+ }
8789
+ // ── DM Actions ──
8790
+ /** Join a DM room to receive messages in real-time */
8791
+ joinDM(recipientWallet) {
8792
+ this.socket?.emit("dm:join", { recipientWallet });
8793
+ }
8794
+ /** Leave a DM room */
8795
+ leaveDM(recipientWallet) {
8796
+ this.socket?.emit("dm:leave", { recipientWallet });
8797
+ }
8798
+ /** Send a direct message via socket */
8799
+ sendDM(params) {
8800
+ this.socket?.emit("dm:send", params);
8801
+ }
8802
+ /** Mark DMs from a sender as read */
8803
+ markDMRead(senderWallet) {
8804
+ this.socket?.emit("dm:mark_read", { senderWallet });
8805
+ }
8806
+ };
8807
+
8808
+ // src/chat/provider.tsx
8809
+ import { jsx as jsx27 } from "react/jsx-runtime";
8810
+ var ChatContext = createContext5(null);
8811
+ function ChatProvider({ children, autoConnect = true }) {
8812
+ const { client } = useDubs();
8813
+ const socketRef = useRef17(new ChatSocket());
8814
+ const [status, setStatus] = useState41("disconnected");
8815
+ const [messages, setMessages] = useState41([]);
8816
+ const [onlineUsers, setOnlineUsers] = useState41([]);
8817
+ const [onlineCount, setOnlineCount] = useState41(0);
8818
+ const [unreadCount, setUnreadCount] = useState41(0);
8819
+ const [conversations, setConversations] = useState41([]);
8820
+ const [friends, setFriends] = useState41([]);
8821
+ const [pendingRequests, setPendingRequests] = useState41([]);
8822
+ const refreshMessages = useCallback35(async () => {
8823
+ try {
8824
+ const res = await client.getChatMessages({ limit: 30 });
8825
+ setMessages(res.messages);
8826
+ } catch (err) {
8827
+ console.error("[Dubs:ChatProvider] Failed to load messages:", err);
8828
+ }
8829
+ }, [client]);
8830
+ const refreshConversations = useCallback35(async () => {
8831
+ try {
8832
+ const res = await client.getConversations();
8833
+ setConversations(res.conversations);
8834
+ } catch (_) {
8835
+ }
8836
+ }, [client]);
8837
+ const refreshFriends = useCallback35(async () => {
8838
+ try {
8839
+ const res = await client.getFriends();
8840
+ setFriends(res.friends);
8841
+ } catch (_) {
8842
+ }
8843
+ }, [client]);
8844
+ const refreshPendingRequests = useCallback35(async () => {
8845
+ try {
8846
+ const res = await client.getPendingFriendRequests();
8847
+ setPendingRequests(res.requests);
8848
+ } catch (_) {
8849
+ }
8850
+ }, [client]);
8851
+ useEffect29(() => {
8852
+ const token = client.getToken();
8853
+ if (!autoConnect || !token) return;
8854
+ const chatSocket = socketRef.current;
8855
+ const baseUrl = client.baseUrl;
8856
+ const host = new URL(baseUrl).origin;
8857
+ chatSocket.setListeners({
8858
+ onConnectionChange: setStatus,
8859
+ // Global chat
8860
+ onNewMessage: (msg) => {
8861
+ setMessages((prev) => {
8862
+ if (prev.some((m) => m.id === msg.id)) return prev;
8863
+ return [msg, ...prev];
8864
+ });
8865
+ },
8866
+ onOnlineUsers: setOnlineUsers,
8867
+ onOnlineCount: setOnlineCount,
8868
+ onUnreadCount: setUnreadCount,
8869
+ // Notifications trigger conversation refresh
8870
+ onDMNotification: () => {
8871
+ refreshConversations();
8872
+ },
8873
+ onNotification: (n) => {
8874
+ setUnreadCount((prev) => prev + 1);
8875
+ if (n.type === "friend_request") refreshPendingRequests();
8876
+ if (n.type === "friend_request_accepted") refreshFriends();
8877
+ },
8878
+ onFriendRequestAccepted: () => refreshFriends(),
8879
+ onFriendRemoved: () => refreshFriends()
8880
+ });
8881
+ chatSocket.connect({ host, token });
8882
+ refreshMessages();
8883
+ refreshFriends().catch(() => {
8884
+ });
8885
+ refreshPendingRequests().catch(() => {
8886
+ });
8887
+ return () => {
8888
+ chatSocket.disconnect();
8889
+ };
8890
+ }, [client, autoConnect, refreshMessages, refreshConversations, refreshFriends, refreshPendingRequests]);
8891
+ useEffect29(() => {
8892
+ const handleAppState = (nextState) => {
8893
+ if (nextState === "active") {
8894
+ const chatSocket = socketRef.current;
8895
+ if (!chatSocket.isConnected()) {
8896
+ const token = client.getToken();
8897
+ if (token) {
8898
+ const baseUrl = client.baseUrl;
8899
+ const host = new URL(baseUrl).origin;
8900
+ chatSocket.connect({ host, token });
8901
+ }
8902
+ }
8903
+ refreshMessages();
8904
+ }
8905
+ };
8906
+ const sub = AppState.addEventListener("change", handleAppState);
8907
+ return () => sub.remove();
8908
+ }, [client, refreshMessages, refreshConversations]);
8909
+ const value = useMemo11(
8910
+ () => ({
8911
+ socket: socketRef.current,
8912
+ status,
8913
+ messages,
8914
+ onlineUsers,
8915
+ onlineCount,
8916
+ unreadCount,
8917
+ conversations,
8918
+ friends,
8919
+ pendingRequests,
8920
+ refreshMessages,
8921
+ refreshConversations,
8922
+ refreshFriends,
8923
+ refreshPendingRequests
8924
+ }),
8925
+ [status, messages, onlineUsers, onlineCount, unreadCount, conversations, friends, pendingRequests, refreshMessages, refreshConversations, refreshFriends, refreshPendingRequests]
8926
+ );
8927
+ return /* @__PURE__ */ jsx27(ChatContext.Provider, { value, children });
8928
+ }
8929
+ function useChatContext() {
8930
+ const ctx = useContext5(ChatContext);
8931
+ if (!ctx) {
8932
+ throw new Error("useChatContext must be used inside a <ChatProvider>");
8933
+ }
8934
+ return ctx;
8935
+ }
8936
+
8937
+ // src/chat/hooks.ts
8938
+ import { useState as useState42, useCallback as useCallback36, useEffect as useEffect30, useRef as useRef18 } from "react";
8939
+ function useChatStatus() {
8940
+ return useChatContext().status;
8941
+ }
8942
+ function useChatMessages() {
8943
+ const { messages, refreshMessages } = useChatContext();
8944
+ const [loading, setLoading] = useState42(false);
8945
+ const refetch = useCallback36(async () => {
8946
+ setLoading(true);
8947
+ await refreshMessages();
8948
+ setLoading(false);
8949
+ }, [refreshMessages]);
8950
+ return { messages, loading, refetch };
8951
+ }
8952
+ function useSendMessage() {
8953
+ const { socket } = useChatContext();
8954
+ const { client } = useDubs();
8955
+ const send = useCallback36(
8956
+ (params) => {
8957
+ socket.sendMessage(params);
8958
+ },
8959
+ [socket]
8960
+ );
8961
+ const sendViaREST = useCallback36(
8962
+ async (params) => {
8963
+ await client.sendChatMessage(params);
8964
+ },
8965
+ [client]
8966
+ );
8967
+ return { send, sendViaREST };
8968
+ }
8969
+ function useOnlineUsers() {
8970
+ const { onlineUsers, onlineCount } = useChatContext();
8971
+ return { users: onlineUsers, count: onlineCount };
8972
+ }
8973
+ function useUnreadCount() {
8974
+ return useChatContext().unreadCount;
8975
+ }
8976
+ function useConversations() {
8977
+ const { conversations, refreshConversations } = useChatContext();
8978
+ const [loading, setLoading] = useState42(false);
8979
+ const refetch = useCallback36(async () => {
8980
+ setLoading(true);
8981
+ await refreshConversations();
8982
+ setLoading(false);
8983
+ }, [refreshConversations]);
8984
+ return { conversations, loading, refetch };
8985
+ }
8986
+ function useDirectMessages(recipientWallet) {
8987
+ const { client } = useDubs();
8988
+ const { socket, refreshConversations } = useChatContext();
8989
+ const [messages, setMessages] = useState42([]);
8990
+ const [otherUser, setOtherUser] = useState42(null);
8991
+ const [loading, setLoading] = useState42(true);
8992
+ const refetch = useCallback36(async () => {
8993
+ try {
8994
+ setLoading(true);
8995
+ const res = await client.getConversation(recipientWallet);
8996
+ setMessages(res.messages);
8997
+ setOtherUser(res.otherUser);
8998
+ } catch (err) {
8999
+ console.error("[Dubs:useDirectMessages] Error loading:", err);
9000
+ } finally {
9001
+ setLoading(false);
9002
+ }
9003
+ }, [client, recipientWallet]);
9004
+ useEffect30(() => {
9005
+ refetch();
9006
+ socket.joinDM(recipientWallet);
9007
+ return () => {
9008
+ socket.leaveDM(recipientWallet);
9009
+ };
9010
+ }, [recipientWallet, refetch, socket]);
9011
+ const socketRef = useRef18(socket);
9012
+ socketRef.current = socket;
9013
+ useEffect30(() => {
9014
+ const currentSocket = socketRef.current;
9015
+ const prevListeners = currentSocket.listeners;
9016
+ const originalOnDMNew = prevListeners?.onDMNewMessage;
9017
+ currentSocket.setListeners({
9018
+ ...prevListeners,
9019
+ onDMNewMessage: (msg) => {
9020
+ originalOnDMNew?.(msg);
9021
+ if (msg.senderWallet === recipientWallet || msg.isOwn) {
9022
+ setMessages((prev) => {
9023
+ if (prev.some((m) => m.id === msg.id)) return prev;
9024
+ return [...prev, msg];
9025
+ });
9026
+ }
9027
+ },
9028
+ onDMMessageSent: (msg) => {
9029
+ setMessages((prev) => {
9030
+ if (prev.some((m) => m.id === msg.id)) return prev;
9031
+ return [...prev, { ...msg, isOwn: true }];
9032
+ });
9033
+ }
9034
+ });
9035
+ }, [recipientWallet]);
9036
+ const send = useCallback36(
9037
+ (message) => {
9038
+ socket.sendDM({ recipientWallet, message });
9039
+ },
9040
+ [socket, recipientWallet]
9041
+ );
9042
+ const sendViaREST = useCallback36(
9043
+ async (message) => {
9044
+ await client.sendDirectMessage({ recipientWallet, message });
9045
+ await refetch();
9046
+ await refreshConversations();
9047
+ },
9048
+ [client, recipientWallet, refetch, refreshConversations]
9049
+ );
9050
+ const markRead = useCallback36(() => {
9051
+ socket.markDMRead(recipientWallet);
9052
+ }, [socket, recipientWallet]);
9053
+ return { messages, loading, otherUser, send, sendViaREST, markRead, refetch };
9054
+ }
9055
+ function useFriends() {
9056
+ const { friends, refreshFriends } = useChatContext();
9057
+ const [loading, setLoading] = useState42(false);
9058
+ const refetch = useCallback36(async () => {
9059
+ setLoading(true);
9060
+ await refreshFriends();
9061
+ setLoading(false);
9062
+ }, [refreshFriends]);
9063
+ return { friends, loading, refetch };
9064
+ }
9065
+ function useFriendRequests() {
9066
+ const { pendingRequests, refreshPendingRequests } = useChatContext();
9067
+ const [loading, setLoading] = useState42(false);
9068
+ const refetch = useCallback36(async () => {
9069
+ setLoading(true);
9070
+ await refreshPendingRequests();
9071
+ setLoading(false);
9072
+ }, [refreshPendingRequests]);
9073
+ return { requests: pendingRequests, loading, refetch };
9074
+ }
9075
+ function useSearchUsers() {
9076
+ const { client } = useDubs();
9077
+ const [results, setResults] = useState42([]);
9078
+ const [loading, setLoading] = useState42(false);
9079
+ const search = useCallback36(
9080
+ async (query) => {
9081
+ setLoading(true);
9082
+ try {
9083
+ const res = await client.searchUsers(query);
9084
+ setResults(res.results);
9085
+ } catch (err) {
9086
+ console.error("[Dubs:useSearchUsers] Error:", err);
9087
+ } finally {
9088
+ setLoading(false);
9089
+ }
9090
+ },
9091
+ [client]
9092
+ );
9093
+ const clear = useCallback36(() => setResults([]), []);
9094
+ return { results, loading, search, clear };
9095
+ }
9096
+ function useSendFriendRequest() {
9097
+ const { client } = useDubs();
9098
+ const { refreshPendingRequests } = useChatContext();
9099
+ const [loading, setLoading] = useState42(false);
9100
+ const send = useCallback36(
9101
+ async (targetUserId) => {
9102
+ setLoading(true);
9103
+ try {
9104
+ await client.sendFriendRequest(targetUserId);
9105
+ } finally {
9106
+ setLoading(false);
9107
+ }
9108
+ },
9109
+ [client, refreshPendingRequests]
9110
+ );
9111
+ return { send, loading };
9112
+ }
9113
+ function useRespondToFriendRequest() {
9114
+ const { client } = useDubs();
9115
+ const { refreshFriends, refreshPendingRequests } = useChatContext();
9116
+ const [loading, setLoading] = useState42(false);
9117
+ const accept = useCallback36(
9118
+ async (requestId) => {
9119
+ setLoading(true);
9120
+ try {
9121
+ await client.acceptFriendRequest(requestId);
9122
+ await Promise.all([refreshFriends(), refreshPendingRequests()]);
9123
+ } finally {
9124
+ setLoading(false);
9125
+ }
9126
+ },
9127
+ [client, refreshFriends, refreshPendingRequests]
9128
+ );
9129
+ const reject = useCallback36(
9130
+ async (requestId) => {
9131
+ setLoading(true);
9132
+ try {
9133
+ await client.rejectFriendRequest(requestId);
9134
+ await refreshPendingRequests();
9135
+ } finally {
9136
+ setLoading(false);
9137
+ }
9138
+ },
9139
+ [client, refreshPendingRequests]
9140
+ );
9141
+ return { accept, reject, loading };
9142
+ }
7398
9143
  export {
7399
9144
  ArcadeLeaderboardSheet,
7400
9145
  AuthGate,
9146
+ ChatProvider,
9147
+ ChatSocket,
7401
9148
  ClaimButton,
7402
9149
  ClaimPrizeSheet,
7403
9150
  ConnectWalletButton,
@@ -7411,6 +9158,9 @@ export {
7411
9158
  DubsProvider,
7412
9159
  EnterArcadePoolSheet,
7413
9160
  GamePoster,
9161
+ JackpotCard,
9162
+ JackpotSheet,
9163
+ JackpotWidget,
7414
9164
  JoinGameButton,
7415
9165
  JoinGameSheet,
7416
9166
  LivePoolsCard,
@@ -7439,22 +9189,38 @@ export {
7439
9189
  useArcadePool,
7440
9190
  useArcadePools,
7441
9191
  useAuth,
9192
+ useChatContext,
9193
+ useChatMessages,
9194
+ useChatStatus,
7442
9195
  useClaim,
9196
+ useConversations,
7443
9197
  useCreateCustomGame,
7444
9198
  useCreateGame,
9199
+ useDirectMessages,
7445
9200
  useDubs,
7446
9201
  useDubsTheme,
7447
9202
  useEnterArcadePool,
9203
+ useEnterJackpot,
7448
9204
  useEvents,
9205
+ useFriendRequests,
9206
+ useFriends,
7449
9207
  useGame,
7450
9208
  useGames,
7451
9209
  useHasClaimed,
7452
9210
  useHighlights,
9211
+ useJackpot,
9212
+ useJackpotHistory,
7453
9213
  useJoinGame,
7454
9214
  useNetworkGames,
9215
+ useOnlineUsers,
7455
9216
  usePushNotifications,
9217
+ useRespondToFriendRequest,
9218
+ useSearchUsers,
9219
+ useSendFriendRequest,
9220
+ useSendMessage,
7456
9221
  useShorts,
7457
9222
  useUFCFightCard,
7458
- useUFCFighterDetail
9223
+ useUFCFighterDetail,
9224
+ useUnreadCount
7459
9225
  };
7460
9226
  //# sourceMappingURL=index.mjs.map