@dubsdotapp/expo 0.1.3 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -37,12 +37,19 @@ __export(index_exports, {
37
37
  DubsApiError: () => DubsApiError,
38
38
  DubsClient: () => DubsClient,
39
39
  DubsProvider: () => DubsProvider,
40
+ GamePoster: () => GamePoster,
41
+ JoinGameButton: () => JoinGameButton,
42
+ LivePoolsCard: () => LivePoolsCard,
40
43
  MwaWalletAdapter: () => MwaWalletAdapter,
44
+ NETWORK_CONFIG: () => NETWORK_CONFIG,
45
+ PickWinnerCard: () => PickWinnerCard,
46
+ PlayersCard: () => PlayersCard,
41
47
  SOLANA_PROGRAM_ERRORS: () => SOLANA_PROGRAM_ERRORS,
48
+ STORAGE_KEYS: () => STORAGE_KEYS,
42
49
  SettingsSheet: () => SettingsSheet,
43
50
  UserProfileCard: () => UserProfileCard,
51
+ createSecureStoreStorage: () => createSecureStoreStorage,
44
52
  parseSolanaError: () => parseSolanaError,
45
- pollTransactionConfirmation: () => pollTransactionConfirmation,
46
53
  signAndSendBase64Transaction: () => signAndSendBase64Transaction,
47
54
  useAuth: () => useAuth,
48
55
  useClaim: () => useClaim,
@@ -60,6 +67,18 @@ module.exports = __toCommonJS(index_exports);
60
67
  // src/constants.ts
61
68
  var DEFAULT_BASE_URL = "https://dubs-server-prod-9c91d3f01199.herokuapp.com/api/developer/v1";
62
69
  var DEFAULT_RPC_URL = "https://api.mainnet-beta.solana.com";
70
+ var NETWORK_CONFIG = {
71
+ "mainnet-beta": {
72
+ baseUrl: DEFAULT_BASE_URL,
73
+ rpcUrl: DEFAULT_RPC_URL,
74
+ cluster: "mainnet-beta"
75
+ },
76
+ devnet: {
77
+ baseUrl: "https://dubs-server-dev-55d1fba09a97.herokuapp.com/api/developer/v1",
78
+ rpcUrl: "https://api.devnet.solana.com",
79
+ cluster: "devnet"
80
+ }
81
+ };
63
82
 
64
83
  // src/errors.ts
65
84
  var DubsApiError = class extends Error {
@@ -193,12 +212,20 @@ var DubsClient = class {
193
212
  if (this._token) {
194
213
  headers["Authorization"] = `Bearer ${this._token}`;
195
214
  }
215
+ console.log(`[DubsClient] ${method} ${url}`, body ? JSON.stringify(body).slice(0, 200) : "");
196
216
  const res = await fetch(url, {
197
217
  method,
198
218
  headers,
199
219
  body: body ? JSON.stringify(body) : void 0
200
220
  });
201
- const json = await res.json();
221
+ const text = await res.text();
222
+ console.log(`[DubsClient] ${method} ${path} \u2192 ${res.status}`, text.slice(0, 300));
223
+ let json;
224
+ try {
225
+ json = JSON.parse(text);
226
+ } catch {
227
+ throw new DubsApiError("parse_error", `Invalid JSON response: ${text.slice(0, 100)}`, res.status);
228
+ }
202
229
  if (!json.success) {
203
230
  const err = json.error;
204
231
  if (typeof err === "object" && err !== null) {
@@ -318,6 +345,13 @@ var DubsClient = class {
318
345
  );
319
346
  return res.game;
320
347
  }
348
+ async getLiveScore(gameId) {
349
+ const res = await this.request(
350
+ "GET",
351
+ `/games/${encodeURIComponent(gameId)}/live-score`
352
+ );
353
+ return res.liveScore;
354
+ }
321
355
  async getGames(params) {
322
356
  const qs = new URLSearchParams();
323
357
  if (params?.wallet) qs.set("wallet", params.wallet);
@@ -335,6 +369,7 @@ var DubsClient = class {
335
369
  async getNetworkGames(params) {
336
370
  const qs = new URLSearchParams();
337
371
  if (params?.league) qs.set("league", params.league);
372
+ if (params?.exclude_wallet) qs.set("exclude_wallet", params.exclude_wallet);
338
373
  if (params?.limit != null) qs.set("limit", String(params.limit));
339
374
  if (params?.offset != null) qs.set("offset", String(params.offset));
340
375
  const query = qs.toString();
@@ -443,35 +478,56 @@ var DubsClient = class {
443
478
  }
444
479
  };
445
480
 
446
- // src/provider.tsx
447
- var import_react = require("react");
448
- var import_web3 = require("@solana/web3.js");
449
- var import_jsx_runtime = require("react/jsx-runtime");
450
- var DubsContext = (0, import_react.createContext)(null);
451
- function DubsProvider({ apiKey, baseUrl, rpcUrl, wallet, children }) {
452
- const value = (0, import_react.useMemo)(() => {
453
- const client = new DubsClient({ apiKey, baseUrl });
454
- const connection = new import_web3.Connection(rpcUrl || DEFAULT_RPC_URL, { commitment: "confirmed" });
455
- return { client, wallet, connection };
456
- }, [apiKey, baseUrl, rpcUrl, wallet]);
457
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DubsContext.Provider, { value, children });
458
- }
459
- function useDubs() {
460
- const ctx = (0, import_react.useContext)(DubsContext);
461
- if (!ctx) {
462
- throw new Error("useDubs must be used within a <DubsProvider>");
481
+ // src/storage.ts
482
+ var STORAGE_KEYS = {
483
+ MWA_AUTH_TOKEN: "dubs_mwa_auth_token",
484
+ JWT_TOKEN: "dubs_jwt_token"
485
+ };
486
+ function createSecureStoreStorage() {
487
+ let SecureStore = null;
488
+ function getStore() {
489
+ if (!SecureStore) {
490
+ try {
491
+ SecureStore = require("expo-secure-store");
492
+ } catch {
493
+ throw new Error(
494
+ "@dubsdotapp/expo: expo-secure-store is required for default token storage. Install it with: npx expo install expo-secure-store \u2014 or pass a custom tokenStorage prop to <DubsProvider>."
495
+ );
496
+ }
497
+ }
498
+ return SecureStore;
463
499
  }
464
- return ctx;
500
+ return {
501
+ async getItem(key) {
502
+ const store = getStore();
503
+ return store.getItemAsync(key);
504
+ },
505
+ async setItem(key, value) {
506
+ const store = getStore();
507
+ await store.setItemAsync(key, value);
508
+ },
509
+ async deleteItem(key) {
510
+ const store = getStore();
511
+ await store.deleteItemAsync(key);
512
+ }
513
+ };
465
514
  }
466
515
 
516
+ // src/provider.tsx
517
+ var import_react12 = require("react");
518
+ var import_web33 = require("@solana/web3.js");
519
+
520
+ // src/managed-wallet.tsx
521
+ var import_react = require("react");
522
+
467
523
  // src/wallet/mwa-adapter.ts
468
- var import_web32 = require("@solana/web3.js");
524
+ var import_web3 = require("@solana/web3.js");
469
525
  function toPublicKey(address) {
470
526
  if (address instanceof Uint8Array) {
471
- return new import_web32.PublicKey(address);
527
+ return new import_web3.PublicKey(address);
472
528
  }
473
529
  const bytes = Uint8Array.from(atob(address), (c) => c.charCodeAt(0));
474
- return new import_web32.PublicKey(bytes);
530
+ return new import_web3.PublicKey(bytes);
475
531
  }
476
532
  var MwaWalletAdapter = class {
477
533
  constructor(config) {
@@ -487,6 +543,12 @@ var MwaWalletAdapter = class {
487
543
  get connected() {
488
544
  return this._connected;
489
545
  }
546
+ get authToken() {
547
+ return this._authToken;
548
+ }
549
+ setAuthToken(token) {
550
+ this._authToken = token;
551
+ }
490
552
  /**
491
553
  * Connect to a mobile wallet. Call this before any signing.
492
554
  */
@@ -550,17 +612,274 @@ var MwaWalletAdapter = class {
550
612
  return result[0];
551
613
  });
552
614
  if (signature instanceof Uint8Array) {
553
- const bs582 = await import("@solana/web3.js").then(() => {
554
- return new import_web32.PublicKey(signature).toBase58();
555
- }).catch(() => {
556
- return Buffer.from(signature).toString("base64");
557
- });
558
- return bs582;
615
+ return new import_web3.PublicKey(signature).toBase58();
559
616
  }
560
617
  return String(signature);
561
618
  }
562
619
  };
563
620
 
621
+ // src/ui/ConnectWalletScreen.tsx
622
+ var import_react_native2 = require("react-native");
623
+
624
+ // src/ui/theme.ts
625
+ var import_react_native = require("react-native");
626
+ var dark = {
627
+ background: "#08080D",
628
+ surface: "#111118",
629
+ surfaceActive: "#7C3AED",
630
+ border: "#1A1A24",
631
+ text: "#FFFFFF",
632
+ textSecondary: "#E0E0EE",
633
+ textMuted: "#666666",
634
+ textDim: "#555555",
635
+ accent: "#7C3AED",
636
+ success: "#22C55E",
637
+ live: "#EF4444",
638
+ errorText: "#F87171",
639
+ errorBg: "#1A0A0A",
640
+ errorBorder: "#3A1515"
641
+ };
642
+ var light = {
643
+ background: "#FFFFFF",
644
+ surface: "#F0F0F5",
645
+ surfaceActive: "#7C3AED",
646
+ border: "#E0E0E8",
647
+ text: "#111118",
648
+ textSecondary: "#333333",
649
+ textMuted: "#888888",
650
+ textDim: "#999999",
651
+ accent: "#7C3AED",
652
+ success: "#16A34A",
653
+ live: "#DC2626",
654
+ errorText: "#DC2626",
655
+ errorBg: "#FEF2F2",
656
+ errorBorder: "#FECACA"
657
+ };
658
+ function useDubsTheme() {
659
+ const scheme = (0, import_react_native.useColorScheme)();
660
+ return scheme === "light" ? light : dark;
661
+ }
662
+
663
+ // src/ui/ConnectWalletScreen.tsx
664
+ var import_jsx_runtime = require("react/jsx-runtime");
665
+ function ConnectWalletScreen({
666
+ onConnect,
667
+ connecting = false,
668
+ error = null,
669
+ appName = "Dubs"
670
+ }) {
671
+ const t = useDubsTheme();
672
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.View, { style: [styles.container, { backgroundColor: t.background }], children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native2.View, { style: styles.content, children: [
673
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native2.View, { style: styles.brandingSection, children: [
674
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.View, { style: [styles.logoCircle, { backgroundColor: t.accent }], children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.Text, { style: styles.logoText, children: "D" }) }),
675
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.Text, { style: [styles.appName, { color: t.text }], children: appName }),
676
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.Text, { style: [styles.subtitle, { color: t.textMuted }], children: "Connect your Solana wallet to get started" })
677
+ ] }),
678
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native2.View, { style: styles.actionSection, children: [
679
+ error ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
680
+ import_react_native2.View,
681
+ {
682
+ style: [
683
+ styles.errorBox,
684
+ { backgroundColor: t.errorBg, borderColor: t.errorBorder }
685
+ ],
686
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.Text, { style: [styles.errorText, { color: t.errorText }], children: error })
687
+ }
688
+ ) : null,
689
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
690
+ import_react_native2.TouchableOpacity,
691
+ {
692
+ style: [styles.connectButton, { backgroundColor: t.accent }],
693
+ onPress: onConnect,
694
+ disabled: connecting,
695
+ activeOpacity: 0.8,
696
+ children: connecting ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.ActivityIndicator, { color: "#FFFFFF", size: "small" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.Text, { style: styles.connectButtonText, children: "Connect Wallet" })
697
+ }
698
+ ),
699
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.Text, { style: [styles.hint, { color: t.textDim }], children: "Phantom, Solflare, or any Solana wallet" })
700
+ ] })
701
+ ] }) });
702
+ }
703
+ var styles = import_react_native2.StyleSheet.create({
704
+ container: {
705
+ flex: 1,
706
+ justifyContent: "center"
707
+ },
708
+ content: {
709
+ flex: 1,
710
+ justifyContent: "space-between",
711
+ paddingHorizontal: 32,
712
+ paddingTop: 120,
713
+ paddingBottom: 80
714
+ },
715
+ brandingSection: {
716
+ alignItems: "center",
717
+ gap: 12
718
+ },
719
+ logoCircle: {
720
+ width: 80,
721
+ height: 80,
722
+ borderRadius: 40,
723
+ justifyContent: "center",
724
+ alignItems: "center",
725
+ marginBottom: 8
726
+ },
727
+ logoText: {
728
+ fontSize: 36,
729
+ fontWeight: "800",
730
+ color: "#FFFFFF"
731
+ },
732
+ appName: {
733
+ fontSize: 32,
734
+ fontWeight: "800"
735
+ },
736
+ subtitle: {
737
+ fontSize: 16,
738
+ textAlign: "center",
739
+ lineHeight: 22
740
+ },
741
+ actionSection: {
742
+ gap: 16
743
+ },
744
+ errorBox: {
745
+ borderWidth: 1,
746
+ borderRadius: 12,
747
+ paddingHorizontal: 16,
748
+ paddingVertical: 12
749
+ },
750
+ errorText: {
751
+ fontSize: 14,
752
+ textAlign: "center"
753
+ },
754
+ connectButton: {
755
+ height: 56,
756
+ borderRadius: 16,
757
+ justifyContent: "center",
758
+ alignItems: "center"
759
+ },
760
+ connectButtonText: {
761
+ color: "#FFFFFF",
762
+ fontSize: 18,
763
+ fontWeight: "700"
764
+ },
765
+ hint: {
766
+ fontSize: 13,
767
+ textAlign: "center"
768
+ }
769
+ });
770
+
771
+ // src/managed-wallet.tsx
772
+ var import_jsx_runtime2 = require("react/jsx-runtime");
773
+ var DisconnectContext = (0, import_react.createContext)(null);
774
+ function useDisconnect() {
775
+ return (0, import_react.useContext)(DisconnectContext);
776
+ }
777
+ function ManagedWalletProvider({
778
+ appName,
779
+ cluster,
780
+ storage,
781
+ renderConnectScreen,
782
+ children
783
+ }) {
784
+ const [connected, setConnected] = (0, import_react.useState)(false);
785
+ const [connecting, setConnecting] = (0, import_react.useState)(false);
786
+ const [isReady, setIsReady] = (0, import_react.useState)(false);
787
+ const [error, setError] = (0, import_react.useState)(null);
788
+ const adapterRef = (0, import_react.useRef)(null);
789
+ const transactRef = (0, import_react.useRef)(null);
790
+ if (!adapterRef.current) {
791
+ adapterRef.current = new MwaWalletAdapter({
792
+ transact: (...args) => {
793
+ if (!transactRef.current) {
794
+ throw new Error(
795
+ "@dubsdotapp/expo: @solana-mobile/mobile-wallet-adapter-protocol-web3js is required. Install it with: npm install @solana-mobile/mobile-wallet-adapter-protocol-web3js"
796
+ );
797
+ }
798
+ return transactRef.current(...args);
799
+ },
800
+ appIdentity: { name: appName },
801
+ cluster,
802
+ onAuthTokenChange: (token) => {
803
+ if (token) {
804
+ storage.setItem(STORAGE_KEYS.MWA_AUTH_TOKEN, token).catch(() => {
805
+ });
806
+ } else {
807
+ storage.deleteItem(STORAGE_KEYS.MWA_AUTH_TOKEN).catch(() => {
808
+ });
809
+ }
810
+ }
811
+ });
812
+ }
813
+ const adapter = adapterRef.current;
814
+ (0, import_react.useEffect)(() => {
815
+ let cancelled = false;
816
+ (async () => {
817
+ try {
818
+ const mwa = await import("@solana-mobile/mobile-wallet-adapter-protocol-web3js");
819
+ if (cancelled) return;
820
+ transactRef.current = mwa.transact;
821
+ } catch {
822
+ }
823
+ try {
824
+ const savedToken = await storage.getItem(STORAGE_KEYS.MWA_AUTH_TOKEN);
825
+ if (savedToken && !cancelled) {
826
+ adapter.setAuthToken(savedToken);
827
+ await adapter.connect();
828
+ if (!cancelled) setConnected(true);
829
+ }
830
+ } catch {
831
+ } finally {
832
+ if (!cancelled) setIsReady(true);
833
+ }
834
+ })();
835
+ return () => {
836
+ cancelled = true;
837
+ };
838
+ }, [adapter, storage]);
839
+ const handleConnect = (0, import_react.useCallback)(async () => {
840
+ setConnecting(true);
841
+ setError(null);
842
+ try {
843
+ await adapter.connect();
844
+ setConnected(true);
845
+ } catch (err) {
846
+ const message = err instanceof Error ? err.message : "Connection failed";
847
+ setError(message);
848
+ } finally {
849
+ setConnecting(false);
850
+ }
851
+ }, [adapter]);
852
+ const disconnect = (0, import_react.useCallback)(async () => {
853
+ adapter.disconnect();
854
+ await storage.deleteItem(STORAGE_KEYS.MWA_AUTH_TOKEN).catch(() => {
855
+ });
856
+ await storage.deleteItem(STORAGE_KEYS.JWT_TOKEN).catch(() => {
857
+ });
858
+ setConnected(false);
859
+ }, [adapter, storage]);
860
+ if (!isReady) return null;
861
+ if (!connected) {
862
+ if (renderConnectScreen === false) {
863
+ return null;
864
+ }
865
+ const connectProps = {
866
+ onConnect: handleConnect,
867
+ connecting,
868
+ error,
869
+ appName
870
+ };
871
+ if (renderConnectScreen) {
872
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: renderConnectScreen(connectProps) });
873
+ }
874
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ConnectWalletScreen, { ...connectProps });
875
+ }
876
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(DisconnectContext.Provider, { value: disconnect, children: children(adapter) });
877
+ }
878
+
879
+ // src/ui/AuthGate.tsx
880
+ var import_react11 = __toESM(require("react"));
881
+ var import_react_native3 = require("react-native");
882
+
564
883
  // src/hooks/useEvents.ts
565
884
  var import_react2 = require("react");
566
885
  function useEvents(params) {
@@ -669,44 +988,24 @@ function useNetworkGames(params) {
669
988
  var import_react6 = require("react");
670
989
 
671
990
  // src/utils/transaction.ts
672
- var import_web33 = require("@solana/web3.js");
673
- async function signAndSendBase64Transaction(base64Tx, wallet, connection) {
991
+ var import_web32 = require("@solana/web3.js");
992
+ async function signAndSendBase64Transaction(base64Tx, wallet) {
674
993
  if (!wallet.publicKey) throw new Error("Wallet not connected");
675
- const txBuffer = Buffer.from(base64Tx, "base64");
676
- const transaction = import_web33.Transaction.from(txBuffer);
994
+ const binaryStr = atob(base64Tx);
995
+ const bytes = new Uint8Array(binaryStr.length);
996
+ for (let i = 0; i < binaryStr.length; i++) {
997
+ bytes[i] = binaryStr.charCodeAt(i);
998
+ }
999
+ const transaction = import_web32.Transaction.from(bytes);
677
1000
  if (wallet.signAndSendTransaction) {
678
1001
  return wallet.signAndSendTransaction(transaction);
679
1002
  }
680
- const signed = await wallet.signTransaction(transaction);
681
- const signature = await connection.sendRawTransaction(signed.serialize(), {
682
- skipPreflight: true
683
- });
684
- return signature;
685
- }
686
- async function pollTransactionConfirmation(signature, connection, commitment = "confirmed", timeout = 6e4, interval = 1500) {
687
- const start = Date.now();
688
- const confirmationOrder = ["processed", "confirmed", "finalized"];
689
- const targetIndex = confirmationOrder.indexOf(commitment);
690
- while (Date.now() - start < timeout) {
691
- const statuses = await connection.getSignatureStatuses([signature]);
692
- const status = statuses?.value?.[0];
693
- if (status?.err) {
694
- throw new Error(`Transaction failed: ${JSON.stringify(status.err)}`);
695
- }
696
- if (status?.confirmationStatus) {
697
- const currentIndex = confirmationOrder.indexOf(status.confirmationStatus);
698
- if (currentIndex >= targetIndex) {
699
- return;
700
- }
701
- }
702
- await new Promise((resolve) => setTimeout(resolve, interval));
703
- }
704
- throw new Error(`Transaction confirmation timeout after ${timeout}ms`);
1003
+ throw new Error("Wallet does not support signAndSendTransaction");
705
1004
  }
706
1005
 
707
1006
  // src/hooks/useCreateGame.ts
708
1007
  function useCreateGame() {
709
- const { client, wallet, connection } = useDubs();
1008
+ const { client, wallet } = useDubs();
710
1009
  const [status, setStatus] = (0, import_react6.useState)("idle");
711
1010
  const [error, setError] = (0, import_react6.useState)(null);
712
1011
  const [data, setData] = (0, import_react6.useState)(null);
@@ -720,17 +1019,18 @@ function useCreateGame() {
720
1019
  setError(null);
721
1020
  setData(null);
722
1021
  try {
1022
+ console.log("[useCreateGame] Step 1: Building transaction...");
723
1023
  const createResult = await client.createGame(params);
1024
+ console.log("[useCreateGame] Step 1 done:", { gameId: createResult.gameId, gameAddress: createResult.gameAddress });
724
1025
  setStatus("signing");
1026
+ console.log("[useCreateGame] Step 2: Signing and sending...");
725
1027
  const signature = await signAndSendBase64Transaction(
726
1028
  createResult.transaction,
727
- wallet,
728
- connection
1029
+ wallet
729
1030
  );
1031
+ console.log("[useCreateGame] Step 2 done. Signature:", signature);
730
1032
  setStatus("confirming");
731
- await pollTransactionConfirmation(signature, connection);
732
- setStatus("saving");
733
- const explorerUrl = `https://solscan.io/tx/${signature}`;
1033
+ console.log("[useCreateGame] Step 3: Confirming with backend...");
734
1034
  await client.confirmGame({
735
1035
  gameId: createResult.gameId,
736
1036
  playerWallet: params.playerWallet,
@@ -740,6 +1040,8 @@ function useCreateGame() {
740
1040
  role: "creator",
741
1041
  gameAddress: createResult.gameAddress
742
1042
  });
1043
+ console.log("[useCreateGame] Step 3 done.");
1044
+ const explorerUrl = `https://solscan.io/tx/${signature}`;
743
1045
  const result = {
744
1046
  gameId: createResult.gameId,
745
1047
  gameAddress: createResult.gameAddress,
@@ -748,21 +1050,23 @@ function useCreateGame() {
748
1050
  };
749
1051
  setData(result);
750
1052
  setStatus("success");
1053
+ console.log("[useCreateGame] Complete!");
751
1054
  return result;
752
1055
  } catch (err) {
1056
+ console.error("[useCreateGame] FAILED:", err);
753
1057
  const error2 = err instanceof Error ? err : new Error(String(err));
754
1058
  setError(error2);
755
1059
  setStatus("error");
756
1060
  throw error2;
757
1061
  }
758
- }, [client, wallet, connection]);
1062
+ }, [client, wallet]);
759
1063
  return { execute, status, error, data, reset };
760
1064
  }
761
1065
 
762
1066
  // src/hooks/useJoinGame.ts
763
1067
  var import_react7 = require("react");
764
1068
  function useJoinGame() {
765
- const { client, wallet, connection } = useDubs();
1069
+ const { client, wallet } = useDubs();
766
1070
  const [status, setStatus] = (0, import_react7.useState)("idle");
767
1071
  const [error, setError] = (0, import_react7.useState)(null);
768
1072
  const [data, setData] = (0, import_react7.useState)(null);
@@ -776,18 +1080,18 @@ function useJoinGame() {
776
1080
  setError(null);
777
1081
  setData(null);
778
1082
  try {
1083
+ console.log("[useJoinGame] Step 1: Building transaction...", { gameId: params.gameId, playerWallet: params.playerWallet, teamChoice: params.teamChoice, amount: params.amount });
779
1084
  const joinResult = await client.joinGame(params);
1085
+ console.log("[useJoinGame] Step 1 done:", { gameId: joinResult.gameId, gameAddress: joinResult.gameAddress, hasTx: !!joinResult.transaction });
780
1086
  setStatus("signing");
1087
+ console.log("[useJoinGame] Step 2: Signing and sending transaction...");
781
1088
  const signature = await signAndSendBase64Transaction(
782
1089
  joinResult.transaction,
783
- wallet,
784
- connection
1090
+ wallet
785
1091
  );
1092
+ console.log("[useJoinGame] Step 2 done. Signature:", signature);
786
1093
  setStatus("confirming");
787
- await pollTransactionConfirmation(signature, connection);
788
- setStatus("saving");
789
- const explorerUrl = `https://solscan.io/tx/${signature}`;
790
- await client.confirmGame({
1094
+ const confirmParams = {
791
1095
  gameId: params.gameId,
792
1096
  playerWallet: params.playerWallet,
793
1097
  signature,
@@ -795,7 +1099,11 @@ function useJoinGame() {
795
1099
  wagerAmount: params.amount,
796
1100
  role: "joiner",
797
1101
  gameAddress: joinResult.gameAddress
798
- });
1102
+ };
1103
+ console.log("[useJoinGame] Step 3: Confirming with backend...", confirmParams);
1104
+ await client.confirmGame(confirmParams);
1105
+ console.log("[useJoinGame] Step 3 done. Backend confirmed.");
1106
+ const explorerUrl = `https://solscan.io/tx/${signature}`;
799
1107
  const result = {
800
1108
  gameId: params.gameId,
801
1109
  gameAddress: joinResult.gameAddress,
@@ -804,21 +1112,23 @@ function useJoinGame() {
804
1112
  };
805
1113
  setData(result);
806
1114
  setStatus("success");
1115
+ console.log("[useJoinGame] Complete!");
807
1116
  return result;
808
1117
  } catch (err) {
1118
+ console.error("[useJoinGame] FAILED at status:", status, err);
809
1119
  const error2 = err instanceof Error ? err : new Error(String(err));
810
1120
  setError(error2);
811
1121
  setStatus("error");
812
1122
  throw error2;
813
1123
  }
814
- }, [client, wallet, connection]);
1124
+ }, [client, wallet]);
815
1125
  return { execute, status, error, data, reset };
816
1126
  }
817
1127
 
818
1128
  // src/hooks/useClaim.ts
819
1129
  var import_react8 = require("react");
820
1130
  function useClaim() {
821
- const { client, wallet, connection } = useDubs();
1131
+ const { client, wallet } = useDubs();
822
1132
  const [status, setStatus] = (0, import_react8.useState)("idle");
823
1133
  const [error, setError] = (0, import_react8.useState)(null);
824
1134
  const [data, setData] = (0, import_react8.useState)(null);
@@ -832,15 +1142,16 @@ function useClaim() {
832
1142
  setError(null);
833
1143
  setData(null);
834
1144
  try {
1145
+ console.log("[useClaim] Step 1: Building claim transaction...");
835
1146
  const claimResult = await client.buildClaimTransaction(params);
1147
+ console.log("[useClaim] Step 1 done.");
836
1148
  setStatus("signing");
1149
+ console.log("[useClaim] Step 2: Signing and sending...");
837
1150
  const signature = await signAndSendBase64Transaction(
838
1151
  claimResult.transaction,
839
- wallet,
840
- connection
1152
+ wallet
841
1153
  );
842
- setStatus("confirming");
843
- await pollTransactionConfirmation(signature, connection);
1154
+ console.log("[useClaim] Step 2 done. Signature:", signature);
844
1155
  const explorerUrl = `https://solscan.io/tx/${signature}`;
845
1156
  const result = {
846
1157
  gameId: params.gameId,
@@ -849,28 +1160,37 @@ function useClaim() {
849
1160
  };
850
1161
  setData(result);
851
1162
  setStatus("success");
1163
+ console.log("[useClaim] Complete!");
852
1164
  return result;
853
1165
  } catch (err) {
1166
+ console.error("[useClaim] FAILED:", err);
854
1167
  const error2 = err instanceof Error ? err : new Error(String(err));
855
1168
  setError(error2);
856
1169
  setStatus("error");
857
1170
  throw error2;
858
1171
  }
859
- }, [client, wallet, connection]);
1172
+ }, [client, wallet]);
860
1173
  return { execute, status, error, data, reset };
861
1174
  }
862
1175
 
863
1176
  // src/hooks/useAuth.ts
864
- var import_react9 = require("react");
1177
+ var import_react10 = require("react");
865
1178
  var import_bs58 = __toESM(require("bs58"));
1179
+
1180
+ // src/auth-context.ts
1181
+ var import_react9 = require("react");
1182
+ var AuthContext = (0, import_react9.createContext)(null);
1183
+
1184
+ // src/hooks/useAuth.ts
866
1185
  function useAuth() {
1186
+ const sharedAuth = (0, import_react10.useContext)(AuthContext);
867
1187
  const { client, wallet } = useDubs();
868
- const [status, setStatus] = (0, import_react9.useState)("idle");
869
- const [user, setUser] = (0, import_react9.useState)(null);
870
- const [token, setToken] = (0, import_react9.useState)(null);
871
- const [error, setError] = (0, import_react9.useState)(null);
872
- const pendingAuth = (0, import_react9.useRef)(null);
873
- const reset = (0, import_react9.useCallback)(() => {
1188
+ const [status, setStatus] = (0, import_react10.useState)("idle");
1189
+ const [user, setUser] = (0, import_react10.useState)(null);
1190
+ const [token, setToken] = (0, import_react10.useState)(null);
1191
+ const [error, setError] = (0, import_react10.useState)(null);
1192
+ const pendingAuth = (0, import_react10.useRef)(null);
1193
+ const reset = (0, import_react10.useCallback)(() => {
874
1194
  setStatus("idle");
875
1195
  setUser(null);
876
1196
  setToken(null);
@@ -878,7 +1198,7 @@ function useAuth() {
878
1198
  pendingAuth.current = null;
879
1199
  client.setToken(null);
880
1200
  }, [client]);
881
- const authenticate = (0, import_react9.useCallback)(async () => {
1201
+ const authenticate = (0, import_react10.useCallback)(async () => {
882
1202
  try {
883
1203
  if (!wallet.publicKey) {
884
1204
  throw new Error("Wallet not connected");
@@ -909,7 +1229,7 @@ function useAuth() {
909
1229
  setStatus("error");
910
1230
  }
911
1231
  }, [client, wallet]);
912
- const register = (0, import_react9.useCallback)(async (username, referralCode, avatarUrl) => {
1232
+ const register = (0, import_react10.useCallback)(async (username, referralCode, avatarUrl) => {
913
1233
  try {
914
1234
  const pending = pendingAuth.current;
915
1235
  if (!pending) {
@@ -926,7 +1246,8 @@ function useAuth() {
926
1246
  avatarUrl
927
1247
  });
928
1248
  pendingAuth.current = null;
929
- setUser(result.user);
1249
+ const user2 = avatarUrl && !result.user.avatar ? { ...result.user, avatar: avatarUrl } : result.user;
1250
+ setUser(user2);
930
1251
  setToken(result.token);
931
1252
  setStatus("authenticated");
932
1253
  } catch (err) {
@@ -934,7 +1255,7 @@ function useAuth() {
934
1255
  setStatus("error");
935
1256
  }
936
1257
  }, [client]);
937
- const logout = (0, import_react9.useCallback)(async () => {
1258
+ const logout = (0, import_react10.useCallback)(async () => {
938
1259
  try {
939
1260
  await client.logout();
940
1261
  } catch {
@@ -945,7 +1266,7 @@ function useAuth() {
945
1266
  setError(null);
946
1267
  pendingAuth.current = null;
947
1268
  }, [client]);
948
- const restoreSession = (0, import_react9.useCallback)(async (savedToken) => {
1269
+ const restoreSession = (0, import_react10.useCallback)(async (savedToken) => {
949
1270
  try {
950
1271
  client.setToken(savedToken);
951
1272
  const me = await client.getMe();
@@ -961,6 +1282,7 @@ function useAuth() {
961
1282
  return false;
962
1283
  }
963
1284
  }, [client]);
1285
+ if (sharedAuth) return sharedAuth;
964
1286
  return {
965
1287
  status,
966
1288
  user,
@@ -976,50 +1298,7 @@ function useAuth() {
976
1298
  }
977
1299
 
978
1300
  // src/ui/AuthGate.tsx
979
- var import_react10 = __toESM(require("react"));
980
- var import_react_native2 = require("react-native");
981
-
982
- // src/ui/theme.ts
983
- var import_react_native = require("react-native");
984
- var dark = {
985
- background: "#08080D",
986
- surface: "#111118",
987
- surfaceActive: "#7C3AED",
988
- border: "#1A1A24",
989
- text: "#FFFFFF",
990
- textSecondary: "#E0E0EE",
991
- textMuted: "#666666",
992
- textDim: "#555555",
993
- accent: "#7C3AED",
994
- success: "#22C55E",
995
- live: "#EF4444",
996
- errorText: "#F87171",
997
- errorBg: "#1A0A0A",
998
- errorBorder: "#3A1515"
999
- };
1000
- var light = {
1001
- background: "#FFFFFF",
1002
- surface: "#F0F0F5",
1003
- surfaceActive: "#7C3AED",
1004
- border: "#E0E0E8",
1005
- text: "#111118",
1006
- textSecondary: "#333333",
1007
- textMuted: "#888888",
1008
- textDim: "#999999",
1009
- accent: "#7C3AED",
1010
- success: "#16A34A",
1011
- live: "#DC2626",
1012
- errorText: "#DC2626",
1013
- errorBg: "#FEF2F2",
1014
- errorBorder: "#FECACA"
1015
- };
1016
- function useDubsTheme() {
1017
- const scheme = (0, import_react_native.useColorScheme)();
1018
- return scheme === "light" ? light : dark;
1019
- }
1020
-
1021
- // src/ui/AuthGate.tsx
1022
- var import_jsx_runtime2 = require("react/jsx-runtime");
1301
+ var import_jsx_runtime3 = require("react/jsx-runtime");
1023
1302
  var DICEBEAR_STYLES = [
1024
1303
  "adventurer",
1025
1304
  "avataaars",
@@ -1045,9 +1324,9 @@ function AuthGate({
1045
1324
  }) {
1046
1325
  const { client } = useDubs();
1047
1326
  const auth = useAuth();
1048
- const [phase, setPhase] = (0, import_react10.useState)("init");
1049
- const [registrationPhase, setRegistrationPhase] = (0, import_react10.useState)(false);
1050
- (0, import_react10.useEffect)(() => {
1327
+ const [phase, setPhase] = (0, import_react11.useState)("init");
1328
+ const [registrationPhase, setRegistrationPhase] = (0, import_react11.useState)(false);
1329
+ (0, import_react11.useEffect)(() => {
1051
1330
  let cancelled = false;
1052
1331
  (async () => {
1053
1332
  try {
@@ -1073,35 +1352,37 @@ function AuthGate({
1073
1352
  cancelled = true;
1074
1353
  };
1075
1354
  }, []);
1076
- (0, import_react10.useEffect)(() => {
1355
+ (0, import_react11.useEffect)(() => {
1077
1356
  if (auth.status === "needsRegistration") setRegistrationPhase(true);
1078
1357
  }, [auth.status]);
1079
- (0, import_react10.useEffect)(() => {
1358
+ (0, import_react11.useEffect)(() => {
1080
1359
  if (auth.token) onSaveToken(auth.token);
1081
1360
  }, [auth.token]);
1082
- const retry = (0, import_react10.useCallback)(() => {
1361
+ const retry = (0, import_react11.useCallback)(() => {
1083
1362
  setRegistrationPhase(false);
1084
1363
  auth.reset();
1085
1364
  auth.authenticate();
1086
1365
  }, [auth]);
1087
- const handleRegister = (0, import_react10.useCallback)(
1366
+ const handleRegister = (0, import_react11.useCallback)(
1088
1367
  (username, referralCode, avatarUrl) => {
1089
1368
  auth.register(username, referralCode, avatarUrl);
1090
1369
  },
1091
1370
  [auth]
1092
1371
  );
1093
1372
  if (phase === "init") {
1094
- if (renderLoading) return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: renderLoading("authenticating") });
1095
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(DefaultLoadingScreen, { status: "authenticating", appName });
1373
+ if (renderLoading) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children: renderLoading("authenticating") });
1374
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(DefaultLoadingScreen, { status: "authenticating", appName });
1375
+ }
1376
+ if (auth.status === "authenticated") {
1377
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(AuthContext.Provider, { value: auth, children });
1096
1378
  }
1097
- if (auth.status === "authenticated") return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children });
1098
1379
  if (registrationPhase) {
1099
1380
  const isRegistering = auth.status === "registering";
1100
1381
  const regError = auth.status === "error" ? auth.error : null;
1101
1382
  if (renderRegistration) {
1102
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: renderRegistration({ onRegister: handleRegister, registering: isRegistering, error: regError, client }) });
1383
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children: renderRegistration({ onRegister: handleRegister, registering: isRegistering, error: regError, client }) });
1103
1384
  }
1104
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1385
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1105
1386
  DefaultRegistrationScreen,
1106
1387
  {
1107
1388
  onRegister: handleRegister,
@@ -1113,11 +1394,11 @@ function AuthGate({
1113
1394
  );
1114
1395
  }
1115
1396
  if (auth.status === "error" && auth.error) {
1116
- if (renderError) return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: renderError(auth.error, retry) });
1117
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(DefaultErrorScreen, { error: auth.error, onRetry: retry, appName });
1397
+ if (renderError) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children: renderError(auth.error, retry) });
1398
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(DefaultErrorScreen, { error: auth.error, onRetry: retry, appName });
1118
1399
  }
1119
- if (renderLoading) return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: renderLoading(auth.status) });
1120
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(DefaultLoadingScreen, { status: auth.status, appName });
1400
+ if (renderLoading) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children: renderLoading(auth.status) });
1401
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(DefaultLoadingScreen, { status: auth.status, appName });
1121
1402
  }
1122
1403
  function DefaultLoadingScreen({ status, appName }) {
1123
1404
  const t = useDubsTheme();
@@ -1131,43 +1412,43 @@ function DefaultLoadingScreen({ status, appName }) {
1131
1412
  authenticated: "Ready!",
1132
1413
  error: "Something went wrong"
1133
1414
  };
1134
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.View, { style: [s.container, { backgroundColor: t.background }], children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: s.centerContent, children: [
1135
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: s.brandingSection, children: [
1136
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.View, { style: [s.logoCircle, { backgroundColor: t.accent }], children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: s.logoText, children: "D" }) }),
1137
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: [s.appNameText, { color: t.text }], children: appName })
1415
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.View, { style: [s.container, { backgroundColor: t.background }], children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: s.centerContent, children: [
1416
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: s.brandingSection, children: [
1417
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.View, { style: [s.logoCircle, { backgroundColor: t.accent }], children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: s.logoText, children: "D" }) }),
1418
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [s.appNameText, { color: t.text }], children: appName })
1138
1419
  ] }),
1139
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: s.loadingSection, children: [
1140
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.ActivityIndicator, { size: "large", color: t.accent }),
1141
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: [s.statusText, { color: t.textMuted }], children: statusText[status] || "Loading..." })
1420
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: s.loadingSection, children: [
1421
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.ActivityIndicator, { size: "large", color: t.accent }),
1422
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [s.statusText, { color: t.textMuted }], children: statusText[status] || "Loading..." })
1142
1423
  ] })
1143
1424
  ] }) });
1144
1425
  }
1145
1426
  function DefaultErrorScreen({ error, onRetry, appName }) {
1146
1427
  const t = useDubsTheme();
1147
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.View, { style: [s.container, { backgroundColor: t.background }], children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: s.spreadContent, children: [
1148
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: s.brandingSection, children: [
1149
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.View, { style: [s.logoCircle, { backgroundColor: t.accent }], children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: s.logoText, children: "D" }) }),
1150
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: [s.appNameText, { color: t.text }], children: appName })
1428
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.View, { style: [s.container, { backgroundColor: t.background }], children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: s.spreadContent, children: [
1429
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: s.brandingSection, children: [
1430
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.View, { style: [s.logoCircle, { backgroundColor: t.accent }], children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: s.logoText, children: "D" }) }),
1431
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [s.appNameText, { color: t.text }], children: appName })
1151
1432
  ] }),
1152
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: { gap: 16 }, children: [
1153
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.View, { style: [s.errorBox, { backgroundColor: t.errorBg, borderColor: t.errorBorder }], children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: [s.errorText, { color: t.errorText }], children: error.message }) }),
1154
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.TouchableOpacity, { style: [s.primaryBtn, { backgroundColor: t.accent }], onPress: onRetry, activeOpacity: 0.8, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: s.primaryBtnText, children: "Try Again" }) })
1433
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: { gap: 16 }, children: [
1434
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.View, { style: [s.errorBox, { backgroundColor: t.errorBg, borderColor: t.errorBorder }], children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [s.errorText, { color: t.errorText }], children: error.message }) }),
1435
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.TouchableOpacity, { style: [s.primaryBtn, { backgroundColor: t.accent }], onPress: onRetry, activeOpacity: 0.8, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: s.primaryBtnText, children: "Try Again" }) })
1155
1436
  ] })
1156
1437
  ] }) });
1157
1438
  }
1158
1439
  function StepIndicator({ currentStep }) {
1159
1440
  const t = useDubsTheme();
1160
1441
  const steps = [0, 1, 2, 3];
1161
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.View, { style: s.stepRow, children: steps.map((i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react10.default.Fragment, { children: [
1162
- i > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.View, { style: [s.stepLine, { backgroundColor: i <= currentStep ? t.success : t.border }] }),
1163
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1164
- import_react_native2.View,
1442
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.View, { style: s.stepRow, children: steps.map((i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react11.default.Fragment, { children: [
1443
+ i > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.View, { style: [s.stepLine, { backgroundColor: i <= currentStep ? t.success : t.border }] }),
1444
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1445
+ import_react_native3.View,
1165
1446
  {
1166
1447
  style: [
1167
1448
  s.stepCircle,
1168
1449
  i < currentStep ? { backgroundColor: t.success } : i === currentStep ? { backgroundColor: t.accent } : { backgroundColor: "transparent", borderWidth: 2, borderColor: t.border }
1169
1450
  ],
1170
- children: i < currentStep ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: s.stepCheck, children: "\u2713" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: [s.stepNum, { color: i === currentStep ? "#FFF" : t.textMuted }], children: i + 1 })
1451
+ children: i < currentStep ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: s.stepCheck, children: "\u2713" }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [s.stepNum, { color: i === currentStep ? "#FFF" : t.textMuted }], children: i + 1 })
1171
1452
  }
1172
1453
  )
1173
1454
  ] }, i)) });
@@ -1180,19 +1461,19 @@ function DefaultRegistrationScreen({
1180
1461
  appName
1181
1462
  }) {
1182
1463
  const t = useDubsTheme();
1183
- const [step, setStep] = (0, import_react10.useState)(0);
1184
- const [avatarSeed, setAvatarSeed] = (0, import_react10.useState)(generateSeed);
1185
- const [avatarStyle, setAvatarStyle] = (0, import_react10.useState)("adventurer");
1186
- const [showStyles, setShowStyles] = (0, import_react10.useState)(false);
1187
- const [username, setUsername] = (0, import_react10.useState)("");
1188
- const [referralCode, setReferralCode] = (0, import_react10.useState)("");
1189
- const [checking, setChecking] = (0, import_react10.useState)(false);
1190
- const [availability, setAvailability] = (0, import_react10.useState)(null);
1191
- const debounceRef = (0, import_react10.useRef)(null);
1192
- const fadeAnim = (0, import_react10.useRef)(new import_react_native2.Animated.Value(1)).current;
1193
- const slideAnim = (0, import_react10.useRef)(new import_react_native2.Animated.Value(0)).current;
1464
+ const [step, setStep] = (0, import_react11.useState)(0);
1465
+ const [avatarSeed, setAvatarSeed] = (0, import_react11.useState)(generateSeed);
1466
+ const [avatarStyle, setAvatarStyle] = (0, import_react11.useState)("adventurer");
1467
+ const [showStyles, setShowStyles] = (0, import_react11.useState)(false);
1468
+ const [username, setUsername] = (0, import_react11.useState)("");
1469
+ const [referralCode, setReferralCode] = (0, import_react11.useState)("");
1470
+ const [checking, setChecking] = (0, import_react11.useState)(false);
1471
+ const [availability, setAvailability] = (0, import_react11.useState)(null);
1472
+ const debounceRef = (0, import_react11.useRef)(null);
1473
+ const fadeAnim = (0, import_react11.useRef)(new import_react_native3.Animated.Value(1)).current;
1474
+ const slideAnim = (0, import_react11.useRef)(new import_react_native3.Animated.Value(0)).current;
1194
1475
  const avatarUrl = getAvatarUrl(avatarStyle, avatarSeed);
1195
- (0, import_react10.useEffect)(() => {
1476
+ (0, import_react11.useEffect)(() => {
1196
1477
  if (debounceRef.current) clearTimeout(debounceRef.current);
1197
1478
  const trimmed = username.trim();
1198
1479
  if (trimmed.length < 3) {
@@ -1215,94 +1496,94 @@ function DefaultRegistrationScreen({
1215
1496
  if (debounceRef.current) clearTimeout(debounceRef.current);
1216
1497
  };
1217
1498
  }, [username, client]);
1218
- const animateToStep = (0, import_react10.useCallback)((newStep) => {
1499
+ const animateToStep = (0, import_react11.useCallback)((newStep) => {
1219
1500
  const dir = newStep > step ? 1 : -1;
1220
- import_react_native2.Keyboard.dismiss();
1221
- import_react_native2.Animated.parallel([
1222
- import_react_native2.Animated.timing(fadeAnim, { toValue: 0, duration: 120, useNativeDriver: true }),
1223
- import_react_native2.Animated.timing(slideAnim, { toValue: -dir * 40, duration: 120, useNativeDriver: true })
1501
+ import_react_native3.Keyboard.dismiss();
1502
+ import_react_native3.Animated.parallel([
1503
+ import_react_native3.Animated.timing(fadeAnim, { toValue: 0, duration: 120, useNativeDriver: true }),
1504
+ import_react_native3.Animated.timing(slideAnim, { toValue: -dir * 40, duration: 120, useNativeDriver: true })
1224
1505
  ]).start(() => {
1225
1506
  setStep(newStep);
1226
1507
  slideAnim.setValue(dir * 40);
1227
- import_react_native2.Animated.parallel([
1228
- import_react_native2.Animated.timing(fadeAnim, { toValue: 1, duration: 200, useNativeDriver: true }),
1229
- import_react_native2.Animated.timing(slideAnim, { toValue: 0, duration: 200, useNativeDriver: true })
1508
+ import_react_native3.Animated.parallel([
1509
+ import_react_native3.Animated.timing(fadeAnim, { toValue: 1, duration: 200, useNativeDriver: true }),
1510
+ import_react_native3.Animated.timing(slideAnim, { toValue: 0, duration: 200, useNativeDriver: true })
1230
1511
  ]).start();
1231
1512
  });
1232
1513
  }, [step, fadeAnim, slideAnim]);
1233
1514
  const canContinueUsername = username.trim().length >= 3 && availability?.available === true && !checking;
1234
1515
  const handleSubmit = () => {
1235
- import_react_native2.Keyboard.dismiss();
1516
+ import_react_native3.Keyboard.dismiss();
1236
1517
  onRegister(username.trim(), referralCode.trim() || void 0, avatarUrl);
1237
1518
  };
1238
- const renderAvatarStep = () => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: s.stepContainer, children: [
1239
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: s.stepTop, children: [
1240
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: [s.title, { color: t.text }], children: "Choose Your Avatar" }),
1241
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: [s.subtitle, { color: t.textMuted }], children: "Pick a look that represents you" }),
1242
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(StepIndicator, { currentStep: 0 }),
1243
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.View, { style: s.avatarCenter, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: [s.avatarFrame, { borderColor: t.accent }], children: [
1244
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Image, { source: { uri: avatarUrl }, style: s.avatarLarge }),
1245
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.View, { style: [s.checkBadge, { backgroundColor: t.success }], children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: s.checkBadgeText, children: "\u2713" }) })
1519
+ const renderAvatarStep = () => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: s.stepContainer, children: [
1520
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: s.stepTop, children: [
1521
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [s.title, { color: t.text }], children: "Choose Your Avatar" }),
1522
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [s.subtitle, { color: t.textMuted }], children: "Pick a look that represents you" }),
1523
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(StepIndicator, { currentStep: 0 }),
1524
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.View, { style: s.avatarCenter, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: [s.avatarFrame, { borderColor: t.accent }], children: [
1525
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Image, { source: { uri: avatarUrl }, style: s.avatarLarge }),
1526
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.View, { style: [s.checkBadge, { backgroundColor: t.success }], children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: s.checkBadgeText, children: "\u2713" }) })
1246
1527
  ] }) }),
1247
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: s.avatarActions, children: [
1248
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1249
- import_react_native2.TouchableOpacity,
1528
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: s.avatarActions, children: [
1529
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1530
+ import_react_native3.TouchableOpacity,
1250
1531
  {
1251
1532
  style: [s.outlineBtn, { borderColor: t.border }],
1252
1533
  onPress: () => setAvatarSeed(generateSeed()),
1253
1534
  activeOpacity: 0.7,
1254
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: [s.outlineBtnText, { color: t.text }], children: "\u21BB Shuffle" })
1535
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [s.outlineBtnText, { color: t.text }], children: "\u21BB Shuffle" })
1255
1536
  }
1256
1537
  ),
1257
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1258
- import_react_native2.TouchableOpacity,
1538
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1539
+ import_react_native3.TouchableOpacity,
1259
1540
  {
1260
1541
  style: [s.outlineBtn, { borderColor: t.accent, backgroundColor: t.accent + "15" }],
1261
1542
  onPress: () => setShowStyles(!showStyles),
1262
1543
  activeOpacity: 0.7,
1263
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: [s.outlineBtnText, { color: t.accent }], children: "\u263A Customize" })
1544
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [s.outlineBtnText, { color: t.accent }], children: "\u263A Customize" })
1264
1545
  }
1265
1546
  )
1266
1547
  ] }),
1267
- showStyles && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, style: s.styleScroll, children: DICEBEAR_STYLES.map((st) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1268
- import_react_native2.TouchableOpacity,
1548
+ showStyles && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, style: s.styleScroll, children: DICEBEAR_STYLES.map((st) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1549
+ import_react_native3.TouchableOpacity,
1269
1550
  {
1270
1551
  onPress: () => setAvatarStyle(st),
1271
1552
  style: [s.styleThumbWrap, { borderColor: st === avatarStyle ? t.accent : t.border }],
1272
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Image, { source: { uri: getAvatarUrl(st, avatarSeed, 80) }, style: s.styleThumb })
1553
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Image, { source: { uri: getAvatarUrl(st, avatarSeed, 80) }, style: s.styleThumb })
1273
1554
  },
1274
1555
  st
1275
1556
  )) })
1276
1557
  ] }),
1277
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.View, { style: s.bottomRow, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1278
- import_react_native2.TouchableOpacity,
1558
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.View, { style: s.bottomRow, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1559
+ import_react_native3.TouchableOpacity,
1279
1560
  {
1280
1561
  style: [s.primaryBtn, { backgroundColor: t.accent, flex: 1 }],
1281
1562
  onPress: () => animateToStep(1),
1282
1563
  activeOpacity: 0.8,
1283
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: s.primaryBtnText, children: "Continue \u203A" })
1564
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: s.primaryBtnText, children: "Continue \u203A" })
1284
1565
  }
1285
1566
  ) })
1286
1567
  ] });
1287
- const renderUsernameStep = () => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: s.stepContainer, children: [
1288
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: s.stepTop, children: [
1289
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: s.headerRow, children: [
1290
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.TouchableOpacity, { onPress: () => animateToStep(0), hitSlop: { top: 12, bottom: 12, left: 12, right: 12 }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: [s.backChevron, { color: t.text }], children: "\u2039" }) }),
1291
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: [s.titleInline, { color: t.text }], children: "Pick a Username" })
1568
+ const renderUsernameStep = () => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: s.stepContainer, children: [
1569
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: s.stepTop, children: [
1570
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: s.headerRow, children: [
1571
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.TouchableOpacity, { onPress: () => animateToStep(0), hitSlop: { top: 12, bottom: 12, left: 12, right: 12 }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [s.backChevron, { color: t.text }], children: "\u2039" }) }),
1572
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [s.titleInline, { color: t.text }], children: "Pick a Username" })
1292
1573
  ] }),
1293
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: [s.subtitle, { color: t.textMuted }], children: "This is how others will see you" }),
1294
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(StepIndicator, { currentStep: 1 }),
1295
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.View, { style: s.avatarCenter, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: [s.avatarFrameSmall, { borderColor: t.accent }], children: [
1296
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Image, { source: { uri: avatarUrl }, style: s.avatarSmall }),
1297
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.View, { style: [s.checkBadgeSm, { backgroundColor: t.success }], children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: s.checkBadgeTextSm, children: "\u2713" }) })
1574
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [s.subtitle, { color: t.textMuted }], children: "This is how others will see you" }),
1575
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(StepIndicator, { currentStep: 1 }),
1576
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.View, { style: s.avatarCenter, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: [s.avatarFrameSmall, { borderColor: t.accent }], children: [
1577
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Image, { source: { uri: avatarUrl }, style: s.avatarSmall }),
1578
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.View, { style: [s.checkBadgeSm, { backgroundColor: t.success }], children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: s.checkBadgeTextSm, children: "\u2713" }) })
1298
1579
  ] }) }),
1299
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: s.inputGroup, children: [
1300
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.Text, { style: [s.inputLabel, { color: t.text }], children: [
1580
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: s.inputGroup, children: [
1581
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.Text, { style: [s.inputLabel, { color: t.text }], children: [
1301
1582
  "Username ",
1302
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: { color: t.errorText }, children: "*" })
1583
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: { color: t.errorText }, children: "*" })
1303
1584
  ] }),
1304
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1305
- import_react_native2.TextInput,
1585
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1586
+ import_react_native3.TextInput,
1306
1587
  {
1307
1588
  style: [s.input, { backgroundColor: t.surface, color: t.text, borderColor: t.accent }],
1308
1589
  placeholder: "Enter username",
@@ -1314,63 +1595,63 @@ function DefaultRegistrationScreen({
1314
1595
  autoFocus: true
1315
1596
  }
1316
1597
  ),
1317
- checking ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: [s.hint, { color: t.textDim }], children: "Checking..." }) : availability ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: [s.hint, { color: availability.available ? t.success : t.errorText }], children: availability.available ? "\u2713 Available!" : availability.reason || "Username taken" }) : username.trim().length > 0 && username.trim().length < 3 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: [s.hint, { color: t.textDim }], children: "At least 3 characters" }) : null
1598
+ checking ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [s.hint, { color: t.textDim }], children: "Checking..." }) : availability ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [s.hint, { color: availability.available ? t.success : t.errorText }], children: availability.available ? "\u2713 Available!" : availability.reason || "Username taken" }) : username.trim().length > 0 && username.trim().length < 3 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [s.hint, { color: t.textDim }], children: "At least 3 characters" }) : null
1318
1599
  ] })
1319
1600
  ] }),
1320
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: s.bottomRow, children: [
1321
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1322
- import_react_native2.TouchableOpacity,
1601
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: s.bottomRow, children: [
1602
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1603
+ import_react_native3.TouchableOpacity,
1323
1604
  {
1324
1605
  style: [s.secondaryBtn, { borderColor: t.border }],
1325
1606
  onPress: () => animateToStep(0),
1326
1607
  activeOpacity: 0.7,
1327
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: [s.secondaryBtnText, { color: t.text }], children: "\u2039 Back" })
1608
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [s.secondaryBtnText, { color: t.text }], children: "\u2039 Back" })
1328
1609
  }
1329
1610
  ),
1330
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1331
- import_react_native2.TouchableOpacity,
1611
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1612
+ import_react_native3.TouchableOpacity,
1332
1613
  {
1333
1614
  style: [s.primaryBtn, { backgroundColor: t.accent, flex: 1, opacity: canContinueUsername ? 1 : 0.4 }],
1334
1615
  onPress: () => animateToStep(2),
1335
1616
  disabled: !canContinueUsername,
1336
1617
  activeOpacity: 0.8,
1337
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: s.primaryBtnText, children: "Continue \u203A" })
1618
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: s.primaryBtnText, children: "Continue \u203A" })
1338
1619
  }
1339
1620
  )
1340
1621
  ] })
1341
1622
  ] });
1342
- const renderReferralStep = () => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: s.stepContainer, children: [
1343
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: s.stepTop, children: [
1344
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: s.headerRow, children: [
1345
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.TouchableOpacity, { onPress: () => animateToStep(1), hitSlop: { top: 12, bottom: 12, left: 12, right: 12 }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: [s.backChevron, { color: t.text }], children: "\u2039" }) }),
1346
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: [s.titleInline, { color: t.text }], children: "Almost There!" })
1623
+ const renderReferralStep = () => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: s.stepContainer, children: [
1624
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: s.stepTop, children: [
1625
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: s.headerRow, children: [
1626
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.TouchableOpacity, { onPress: () => animateToStep(1), hitSlop: { top: 12, bottom: 12, left: 12, right: 12 }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [s.backChevron, { color: t.text }], children: "\u2039" }) }),
1627
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [s.titleInline, { color: t.text }], children: "Almost There!" })
1347
1628
  ] }),
1348
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: [s.subtitle, { color: t.textMuted }], children: "Got a referral code? (optional)" }),
1349
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(StepIndicator, { currentStep: 2 }),
1350
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: [s.profileCard, { borderColor: t.border }], children: [
1351
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: [s.profileLabel, { color: t.textMuted }], children: "Your Profile" }),
1352
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: s.profileRow, children: [
1353
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Image, { source: { uri: avatarUrl }, style: s.profileAvatar }),
1354
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: { gap: 4 }, children: [
1355
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.Text, { style: [s.profileUsername, { color: t.text }], children: [
1629
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [s.subtitle, { color: t.textMuted }], children: "Got a referral code? (optional)" }),
1630
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(StepIndicator, { currentStep: 2 }),
1631
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: [s.profileCard, { borderColor: t.border }], children: [
1632
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [s.profileLabel, { color: t.textMuted }], children: "Your Profile" }),
1633
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: s.profileRow, children: [
1634
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Image, { source: { uri: avatarUrl }, style: s.profileAvatar }),
1635
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: { gap: 4 }, children: [
1636
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.Text, { style: [s.profileUsername, { color: t.text }], children: [
1356
1637
  "@",
1357
1638
  username
1358
1639
  ] }),
1359
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.Text, { style: [s.profileReady, { color: t.success }], children: [
1640
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.Text, { style: [s.profileReady, { color: t.success }], children: [
1360
1641
  "\u2713",
1361
1642
  " Ready to go!"
1362
1643
  ] })
1363
1644
  ] })
1364
1645
  ] })
1365
1646
  ] }),
1366
- error ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.View, { style: [s.errorBox, { backgroundColor: t.errorBg, borderColor: t.errorBorder }], children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: [s.errorText, { color: t.errorText }], children: error.message }) }) : null,
1367
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: s.inputGroup, children: [
1368
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.Text, { style: [s.inputLabel, { color: t.text }], children: [
1647
+ error ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.View, { style: [s.errorBox, { backgroundColor: t.errorBg, borderColor: t.errorBorder }], children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [s.errorText, { color: t.errorText }], children: error.message }) }) : null,
1648
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: s.inputGroup, children: [
1649
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.Text, { style: [s.inputLabel, { color: t.text }], children: [
1369
1650
  "Referral Code ",
1370
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: { color: t.textMuted }, children: "(optional)" })
1651
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: { color: t.textMuted }, children: "(optional)" })
1371
1652
  ] }),
1372
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1373
- import_react_native2.TextInput,
1653
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1654
+ import_react_native3.TextInput,
1374
1655
  {
1375
1656
  style: [s.input, { backgroundColor: t.surface, color: t.text, borderColor: t.border }],
1376
1657
  placeholder: "Enter referral code",
@@ -1382,31 +1663,31 @@ function DefaultRegistrationScreen({
1382
1663
  editable: !registering
1383
1664
  }
1384
1665
  ),
1385
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.Text, { style: [s.hint, { color: t.textMuted }], children: [
1666
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.Text, { style: [s.hint, { color: t.textMuted }], children: [
1386
1667
  "\u{1F381}",
1387
1668
  " If a friend invited you, enter their code to give them credit!"
1388
1669
  ] })
1389
1670
  ] })
1390
1671
  ] }),
1391
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: s.bottomRow, children: [
1392
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1393
- import_react_native2.TouchableOpacity,
1672
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: s.bottomRow, children: [
1673
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1674
+ import_react_native3.TouchableOpacity,
1394
1675
  {
1395
1676
  style: [s.secondaryBtn, { borderColor: t.border }],
1396
1677
  onPress: () => animateToStep(1),
1397
1678
  disabled: registering,
1398
1679
  activeOpacity: 0.7,
1399
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: [s.secondaryBtnText, { color: t.text }], children: "\u2039 Back" })
1680
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [s.secondaryBtnText, { color: t.text }], children: "\u2039 Back" })
1400
1681
  }
1401
1682
  ),
1402
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1403
- import_react_native2.TouchableOpacity,
1683
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1684
+ import_react_native3.TouchableOpacity,
1404
1685
  {
1405
1686
  style: [s.primaryBtn, { backgroundColor: t.accent, flex: 1, opacity: registering ? 0.7 : 1 }],
1406
1687
  onPress: handleSubmit,
1407
1688
  disabled: registering,
1408
1689
  activeOpacity: 0.8,
1409
- children: registering ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.ActivityIndicator, { color: "#FFFFFF", size: "small" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: s.primaryBtnText, children: "Create Account" })
1690
+ children: registering ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.ActivityIndicator, { color: "#FFFFFF", size: "small" }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: s.primaryBtnText, children: "Create Account" })
1410
1691
  }
1411
1692
  )
1412
1693
  ] })
@@ -1423,19 +1704,19 @@ function DefaultRegistrationScreen({
1423
1704
  return null;
1424
1705
  }
1425
1706
  };
1426
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1427
- import_react_native2.KeyboardAvoidingView,
1707
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1708
+ import_react_native3.KeyboardAvoidingView,
1428
1709
  {
1429
1710
  style: [s.container, { backgroundColor: t.background }],
1430
- behavior: import_react_native2.Platform.OS === "ios" ? "padding" : void 0,
1431
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1432
- import_react_native2.ScrollView,
1711
+ behavior: import_react_native3.Platform.OS === "ios" ? "padding" : void 0,
1712
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1713
+ import_react_native3.ScrollView,
1433
1714
  {
1434
1715
  contentContainerStyle: { flexGrow: 1 },
1435
1716
  keyboardShouldPersistTaps: "handled",
1436
1717
  bounces: false,
1437
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1438
- import_react_native2.Animated.View,
1718
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1719
+ import_react_native3.Animated.View,
1439
1720
  {
1440
1721
  style: [
1441
1722
  { flex: 1 },
@@ -1449,7 +1730,7 @@ function DefaultRegistrationScreen({
1449
1730
  }
1450
1731
  );
1451
1732
  }
1452
- var s = import_react_native2.StyleSheet.create({
1733
+ var s = import_react_native3.StyleSheet.create({
1453
1734
  container: { flex: 1 },
1454
1735
  // Loading / Error
1455
1736
  centerContent: { flex: 1, justifyContent: "center", alignItems: "center", paddingHorizontal: 32, gap: 48 },
@@ -1512,119 +1793,165 @@ var s = import_react_native2.StyleSheet.create({
1512
1793
  secondaryBtnText: { fontSize: 16, fontWeight: "600" }
1513
1794
  });
1514
1795
 
1515
- // src/ui/ConnectWalletScreen.tsx
1516
- var import_react_native3 = require("react-native");
1517
- var import_jsx_runtime3 = require("react/jsx-runtime");
1518
- function ConnectWalletScreen({
1519
- onConnect,
1520
- connecting = false,
1521
- error = null,
1522
- appName = "Dubs"
1796
+ // src/provider.tsx
1797
+ var import_jsx_runtime4 = require("react/jsx-runtime");
1798
+ var DubsContext = (0, import_react12.createContext)(null);
1799
+ function DubsProvider({
1800
+ apiKey,
1801
+ children,
1802
+ appName = "Dubs",
1803
+ network = "mainnet-beta",
1804
+ wallet: externalWallet,
1805
+ tokenStorage,
1806
+ baseUrl: baseUrlOverride,
1807
+ rpcUrl: rpcUrlOverride,
1808
+ renderConnectScreen,
1809
+ renderLoading,
1810
+ renderError,
1811
+ renderRegistration,
1812
+ managed = true
1523
1813
  }) {
1524
- const t = useDubsTheme();
1525
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.View, { style: [styles.container, { backgroundColor: t.background }], children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: styles.content, children: [
1526
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: styles.brandingSection, children: [
1527
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.View, { style: [styles.logoCircle, { backgroundColor: t.accent }], children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: styles.logoText, children: "D" }) }),
1528
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [styles.appName, { color: t.text }], children: appName }),
1529
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [styles.subtitle, { color: t.textMuted }], children: "Connect your Solana wallet to get started" })
1530
- ] }),
1531
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: styles.actionSection, children: [
1532
- error ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1533
- import_react_native3.View,
1534
- {
1535
- style: [
1536
- styles.errorBox,
1537
- { backgroundColor: t.errorBg, borderColor: t.errorBorder }
1538
- ],
1539
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [styles.errorText, { color: t.errorText }], children: error })
1540
- }
1541
- ) : null,
1542
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1543
- import_react_native3.TouchableOpacity,
1814
+ const config = NETWORK_CONFIG[network];
1815
+ const baseUrl = baseUrlOverride || config.baseUrl;
1816
+ const rpcUrl = rpcUrlOverride || config.rpcUrl;
1817
+ const cluster = config.cluster;
1818
+ const client = (0, import_react12.useMemo)(() => new DubsClient({ apiKey, baseUrl }), [apiKey, baseUrl]);
1819
+ const connection = (0, import_react12.useMemo)(() => new import_web33.Connection(rpcUrl, { commitment: "confirmed" }), [rpcUrl]);
1820
+ const storage = (0, import_react12.useMemo)(() => tokenStorage || createSecureStoreStorage(), [tokenStorage]);
1821
+ if (externalWallet) {
1822
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1823
+ ExternalWalletProvider,
1824
+ {
1825
+ client,
1826
+ connection,
1827
+ wallet: externalWallet,
1828
+ appName,
1829
+ network,
1830
+ storage,
1831
+ managed,
1832
+ renderLoading,
1833
+ renderError,
1834
+ renderRegistration,
1835
+ children
1836
+ }
1837
+ );
1838
+ }
1839
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1840
+ ManagedWalletProvider,
1841
+ {
1842
+ appName,
1843
+ cluster,
1844
+ storage,
1845
+ renderConnectScreen,
1846
+ children: (adapter) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1847
+ ManagedInner,
1544
1848
  {
1545
- style: [styles.connectButton, { backgroundColor: t.accent }],
1546
- onPress: onConnect,
1547
- disabled: connecting,
1548
- activeOpacity: 0.8,
1549
- children: connecting ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.ActivityIndicator, { color: "#FFFFFF", size: "small" }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: styles.connectButtonText, children: "Connect Wallet" })
1849
+ client,
1850
+ connection,
1851
+ wallet: adapter,
1852
+ appName,
1853
+ network,
1854
+ storage,
1855
+ renderLoading,
1856
+ renderError,
1857
+ renderRegistration,
1858
+ children
1550
1859
  }
1551
- ),
1552
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [styles.hint, { color: t.textDim }], children: "Phantom, Solflare, or any Solana wallet" })
1553
- ] })
1554
- ] }) });
1860
+ )
1861
+ }
1862
+ );
1555
1863
  }
1556
- var styles = import_react_native3.StyleSheet.create({
1557
- container: {
1558
- flex: 1,
1559
- justifyContent: "center"
1560
- },
1561
- content: {
1562
- flex: 1,
1563
- justifyContent: "space-between",
1564
- paddingHorizontal: 32,
1565
- paddingTop: 120,
1566
- paddingBottom: 80
1567
- },
1568
- brandingSection: {
1569
- alignItems: "center",
1570
- gap: 12
1571
- },
1572
- logoCircle: {
1573
- width: 80,
1574
- height: 80,
1575
- borderRadius: 40,
1576
- justifyContent: "center",
1577
- alignItems: "center",
1578
- marginBottom: 8
1579
- },
1580
- logoText: {
1581
- fontSize: 36,
1582
- fontWeight: "800",
1583
- color: "#FFFFFF"
1584
- },
1585
- appName: {
1586
- fontSize: 32,
1587
- fontWeight: "800"
1588
- },
1589
- subtitle: {
1590
- fontSize: 16,
1591
- textAlign: "center",
1592
- lineHeight: 22
1593
- },
1594
- actionSection: {
1595
- gap: 16
1596
- },
1597
- errorBox: {
1598
- borderWidth: 1,
1599
- borderRadius: 12,
1600
- paddingHorizontal: 16,
1601
- paddingVertical: 12
1602
- },
1603
- errorText: {
1604
- fontSize: 14,
1605
- textAlign: "center"
1606
- },
1607
- connectButton: {
1608
- height: 56,
1609
- borderRadius: 16,
1610
- justifyContent: "center",
1611
- alignItems: "center"
1612
- },
1613
- connectButtonText: {
1614
- color: "#FFFFFF",
1615
- fontSize: 18,
1616
- fontWeight: "700"
1617
- },
1618
- hint: {
1619
- fontSize: 13,
1620
- textAlign: "center"
1864
+ function ManagedInner({
1865
+ client,
1866
+ connection,
1867
+ wallet,
1868
+ appName,
1869
+ network,
1870
+ storage,
1871
+ renderLoading,
1872
+ renderError,
1873
+ renderRegistration,
1874
+ children
1875
+ }) {
1876
+ const managedDisconnect = useDisconnect();
1877
+ const disconnect = (0, import_react12.useCallback)(async () => {
1878
+ client.setToken(null);
1879
+ await managedDisconnect?.();
1880
+ }, [client, managedDisconnect]);
1881
+ const value = (0, import_react12.useMemo)(
1882
+ () => ({ client, wallet, connection, appName, network, disconnect }),
1883
+ [client, wallet, connection, appName, network, disconnect]
1884
+ );
1885
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(DubsContext.Provider, { value, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1886
+ AuthGate,
1887
+ {
1888
+ onSaveToken: (token) => {
1889
+ if (token) return storage.setItem(STORAGE_KEYS.JWT_TOKEN, token);
1890
+ return storage.deleteItem(STORAGE_KEYS.JWT_TOKEN);
1891
+ },
1892
+ onLoadToken: () => storage.getItem(STORAGE_KEYS.JWT_TOKEN),
1893
+ renderLoading,
1894
+ renderError,
1895
+ renderRegistration,
1896
+ appName,
1897
+ children
1898
+ }
1899
+ ) });
1900
+ }
1901
+ function ExternalWalletProvider({
1902
+ client,
1903
+ connection,
1904
+ wallet,
1905
+ appName,
1906
+ network,
1907
+ storage,
1908
+ managed,
1909
+ renderLoading,
1910
+ renderError,
1911
+ renderRegistration,
1912
+ children
1913
+ }) {
1914
+ const disconnect = (0, import_react12.useCallback)(async () => {
1915
+ client.setToken(null);
1916
+ await storage.deleteItem(STORAGE_KEYS.JWT_TOKEN).catch(() => {
1917
+ });
1918
+ await wallet.disconnect?.();
1919
+ }, [client, storage, wallet]);
1920
+ const value = (0, import_react12.useMemo)(
1921
+ () => ({ client, wallet, connection, appName, network, disconnect }),
1922
+ [client, wallet, connection, appName, network, disconnect]
1923
+ );
1924
+ if (!managed) {
1925
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(DubsContext.Provider, { value, children });
1621
1926
  }
1622
- });
1927
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(DubsContext.Provider, { value, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1928
+ AuthGate,
1929
+ {
1930
+ onSaveToken: (token) => {
1931
+ if (token) return storage.setItem(STORAGE_KEYS.JWT_TOKEN, token);
1932
+ return storage.deleteItem(STORAGE_KEYS.JWT_TOKEN);
1933
+ },
1934
+ onLoadToken: () => storage.getItem(STORAGE_KEYS.JWT_TOKEN),
1935
+ renderLoading,
1936
+ renderError,
1937
+ renderRegistration,
1938
+ appName,
1939
+ children
1940
+ }
1941
+ ) });
1942
+ }
1943
+ function useDubs() {
1944
+ const ctx = (0, import_react12.useContext)(DubsContext);
1945
+ if (!ctx) {
1946
+ throw new Error("useDubs must be used within a <DubsProvider>");
1947
+ }
1948
+ return ctx;
1949
+ }
1623
1950
 
1624
1951
  // src/ui/UserProfileCard.tsx
1625
- var import_react11 = require("react");
1952
+ var import_react13 = require("react");
1626
1953
  var import_react_native4 = require("react-native");
1627
- var import_jsx_runtime4 = require("react/jsx-runtime");
1954
+ var import_jsx_runtime5 = require("react/jsx-runtime");
1628
1955
  function truncateAddress(address, chars = 4) {
1629
1956
  if (address.length <= chars * 2 + 3) return address;
1630
1957
  return `${address.slice(0, chars)}...${address.slice(-chars)}`;
@@ -1642,16 +1969,16 @@ function UserProfileCard({
1642
1969
  memberSince
1643
1970
  }) {
1644
1971
  const t = useDubsTheme();
1645
- const imageUri = (0, import_react11.useMemo)(
1972
+ const imageUri = (0, import_react13.useMemo)(
1646
1973
  () => avatarUrl || `https://api.dicebear.com/9.x/avataaars/png?seed=${walletAddress}&size=128`,
1647
1974
  [avatarUrl, walletAddress]
1648
1975
  );
1649
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native4.View, { style: [styles2.card, { backgroundColor: t.surface, borderColor: t.border }], children: [
1650
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.Image, { source: { uri: imageUri }, style: styles2.avatar }),
1651
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native4.View, { style: styles2.info, children: [
1652
- username ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.Text, { style: [styles2.username, { color: t.text }], children: username }) : null,
1653
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.Text, { style: [styles2.address, { color: t.textMuted }], children: truncateAddress(walletAddress) }),
1654
- memberSince ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.Text, { style: [styles2.memberSince, { color: t.textDim }], children: formatMemberSince(memberSince) }) : null
1976
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native4.View, { style: [styles2.card, { backgroundColor: t.surface, borderColor: t.border }], children: [
1977
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native4.Image, { source: { uri: imageUri }, style: styles2.avatar }),
1978
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native4.View, { style: styles2.info, children: [
1979
+ username ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native4.Text, { style: [styles2.username, { color: t.text }], children: username }) : null,
1980
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native4.Text, { style: [styles2.address, { color: t.textMuted }], children: truncateAddress(walletAddress) }),
1981
+ memberSince ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native4.Text, { style: [styles2.memberSince, { color: t.textDim }], children: formatMemberSince(memberSince) }) : null
1655
1982
  ] })
1656
1983
  ] });
1657
1984
  }
@@ -1690,7 +2017,7 @@ var styles2 = import_react_native4.StyleSheet.create({
1690
2017
 
1691
2018
  // src/ui/SettingsSheet.tsx
1692
2019
  var import_react_native5 = require("react-native");
1693
- var import_jsx_runtime5 = require("react/jsx-runtime");
2020
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1694
2021
  function truncateAddress2(address, chars = 4) {
1695
2022
  if (address.length <= chars * 2 + 3) return address;
1696
2023
  return `${address.slice(0, chars)}...${address.slice(-chars)}`;
@@ -1707,13 +2034,13 @@ function SettingsSheet({
1707
2034
  loggingOut = false
1708
2035
  }) {
1709
2036
  const t = useDubsTheme();
1710
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2037
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1711
2038
  import_react_native5.ScrollView,
1712
2039
  {
1713
2040
  style: [styles3.container, { backgroundColor: t.background }],
1714
2041
  contentContainerStyle: styles3.content,
1715
2042
  children: [
1716
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2043
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1717
2044
  UserProfileCard,
1718
2045
  {
1719
2046
  walletAddress,
@@ -1722,49 +2049,49 @@ function SettingsSheet({
1722
2049
  memberSince
1723
2050
  }
1724
2051
  ),
1725
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native5.View, { style: [styles3.actionsCard, { backgroundColor: t.surface, borderColor: t.border }], children: [
1726
- onCopyAddress ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2052
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react_native5.View, { style: [styles3.actionsCard, { backgroundColor: t.surface, borderColor: t.border }], children: [
2053
+ onCopyAddress ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1727
2054
  import_react_native5.TouchableOpacity,
1728
2055
  {
1729
2056
  style: styles3.actionRow,
1730
2057
  onPress: onCopyAddress,
1731
2058
  activeOpacity: 0.7,
1732
2059
  children: [
1733
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native5.View, { style: styles3.actionRowLeft, children: [
1734
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.Text, { style: [styles3.actionLabel, { color: t.text }], children: "Wallet Address" }),
1735
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.Text, { style: [styles3.actionValue, { color: t.textMuted }], children: truncateAddress2(walletAddress) })
2060
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react_native5.View, { style: styles3.actionRowLeft, children: [
2061
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native5.Text, { style: [styles3.actionLabel, { color: t.text }], children: "Wallet Address" }),
2062
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native5.Text, { style: [styles3.actionValue, { color: t.textMuted }], children: truncateAddress2(walletAddress) })
1736
2063
  ] }),
1737
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.Text, { style: [styles3.copyLabel, { color: t.accent }], children: "Copy" })
2064
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native5.Text, { style: [styles3.copyLabel, { color: t.accent }], children: "Copy" })
1738
2065
  ]
1739
2066
  }
1740
2067
  ) : null,
1741
- onSupport ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1742
- onCopyAddress ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.View, { style: [styles3.separator, { backgroundColor: t.border }] }) : null,
1743
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2068
+ onSupport ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
2069
+ onCopyAddress ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native5.View, { style: [styles3.separator, { backgroundColor: t.border }] }) : null,
2070
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1744
2071
  import_react_native5.TouchableOpacity,
1745
2072
  {
1746
2073
  style: styles3.actionRow,
1747
2074
  onPress: onSupport,
1748
2075
  activeOpacity: 0.7,
1749
2076
  children: [
1750
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.Text, { style: [styles3.actionLabel, { color: t.text }], children: "Help & Support" }),
1751
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.Text, { style: [styles3.chevron, { color: t.textMuted }], children: "\u203A" })
2077
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native5.Text, { style: [styles3.actionLabel, { color: t.text }], children: "Help & Support" }),
2078
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native5.Text, { style: [styles3.chevron, { color: t.textMuted }], children: "\u203A" })
1752
2079
  ]
1753
2080
  }
1754
2081
  )
1755
2082
  ] }) : null
1756
2083
  ] }),
1757
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2084
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1758
2085
  import_react_native5.TouchableOpacity,
1759
2086
  {
1760
2087
  style: [styles3.logoutButton, { borderColor: t.live }],
1761
2088
  onPress: onLogout,
1762
2089
  disabled: loggingOut,
1763
2090
  activeOpacity: 0.7,
1764
- children: loggingOut ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.ActivityIndicator, { color: t.live, size: "small" }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.Text, { style: [styles3.logoutText, { color: t.live }], children: "Log Out" })
2091
+ children: loggingOut ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native5.ActivityIndicator, { color: t.live, size: "small" }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native5.Text, { style: [styles3.logoutText, { color: t.live }], children: "Log Out" })
1765
2092
  }
1766
2093
  ),
1767
- appVersion ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native5.Text, { style: [styles3.version, { color: t.textDim }], children: [
2094
+ appVersion ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react_native5.Text, { style: [styles3.version, { color: t.textDim }], children: [
1768
2095
  "v",
1769
2096
  appVersion
1770
2097
  ] }) : null
@@ -1833,6 +2160,476 @@ var styles3 = import_react_native5.StyleSheet.create({
1833
2160
  textAlign: "center"
1834
2161
  }
1835
2162
  });
2163
+
2164
+ // src/ui/game/GamePoster.tsx
2165
+ var import_react14 = require("react");
2166
+ var import_react_native6 = require("react-native");
2167
+ var import_jsx_runtime7 = require("react/jsx-runtime");
2168
+ function computeCountdown(lockTimestamp) {
2169
+ if (!lockTimestamp) return "";
2170
+ const ts = typeof lockTimestamp === "string" ? parseInt(lockTimestamp) : lockTimestamp;
2171
+ const diff = ts * 1e3 - Date.now();
2172
+ if (diff <= 0) return "LIVE";
2173
+ const days = Math.floor(diff / 864e5);
2174
+ const hours = Math.floor(diff % 864e5 / 36e5);
2175
+ const mins = Math.floor(diff % 36e5 / 6e4);
2176
+ if (days > 0) return `${days}d ${hours}h`;
2177
+ if (hours > 0) return `${hours}h ${mins}m`;
2178
+ return `${mins}m`;
2179
+ }
2180
+ function GamePoster({ game, ImageComponent }) {
2181
+ const t = useDubsTheme();
2182
+ const Img = ImageComponent || require("react-native").Image;
2183
+ const opponents = game.opponents || [];
2184
+ const home = opponents[0];
2185
+ const away = opponents[1];
2186
+ const countdown = computeCountdown(game.lockTimestamp);
2187
+ const isLive = countdown === "LIVE";
2188
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native6.View, { style: styles4.container, children: [
2189
+ game.media?.poster ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2190
+ Img,
2191
+ {
2192
+ source: { uri: game.media.poster },
2193
+ style: styles4.image,
2194
+ resizeMode: "cover"
2195
+ }
2196
+ ) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native6.View, { style: [styles4.image, { backgroundColor: t.surface }], children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native6.View, { style: styles4.fallback, children: [
2197
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TeamLogoInternal, { url: home?.imageUrl, size: 56, Img }),
2198
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native6.Text, { style: styles4.vs, children: "VS" }),
2199
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TeamLogoInternal, { url: away?.imageUrl, size: 56, Img })
2200
+ ] }) }),
2201
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native6.View, { style: styles4.overlay }),
2202
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native6.View, { style: styles4.teamNames, children: [
2203
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native6.Text, { style: styles4.teamNameText, numberOfLines: 1, children: home?.name || "Home" }),
2204
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native6.Text, { style: styles4.teamNameVs, children: "vs" }),
2205
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native6.Text, { style: styles4.teamNameText, numberOfLines: 1, children: away?.name || "Away" })
2206
+ ] }),
2207
+ countdown ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native6.View, { style: styles4.countdownPill, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native6.Text, { style: [styles4.countdownText, isLive && styles4.countdownLive], children: countdown }) }) : null,
2208
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native6.View, { style: styles4.poolPill, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native6.Text, { style: styles4.poolText, children: [
2209
+ game.totalPool || 0,
2210
+ " SOL"
2211
+ ] }) })
2212
+ ] });
2213
+ }
2214
+ function TeamLogoInternal({ url, size, Img }) {
2215
+ const [failed, setFailed] = (0, import_react14.useState)(false);
2216
+ if (!url || failed) {
2217
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native6.View, { style: [styles4.logoPlaceholder, { width: size, height: size, borderRadius: size / 2 }] });
2218
+ }
2219
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2220
+ Img,
2221
+ {
2222
+ source: { uri: url },
2223
+ style: { width: size, height: size, borderRadius: size / 2 },
2224
+ resizeMode: "contain",
2225
+ onError: () => setFailed(true)
2226
+ }
2227
+ );
2228
+ }
2229
+ var styles4 = import_react_native6.StyleSheet.create({
2230
+ container: {
2231
+ height: 200,
2232
+ borderRadius: 16,
2233
+ overflow: "hidden",
2234
+ position: "relative"
2235
+ },
2236
+ image: {
2237
+ ...import_react_native6.StyleSheet.absoluteFillObject,
2238
+ justifyContent: "center",
2239
+ alignItems: "center"
2240
+ },
2241
+ overlay: {
2242
+ ...import_react_native6.StyleSheet.absoluteFillObject,
2243
+ backgroundColor: "rgba(0,0,0,0.35)"
2244
+ },
2245
+ fallback: {
2246
+ flexDirection: "row",
2247
+ alignItems: "center",
2248
+ gap: 24,
2249
+ zIndex: 2
2250
+ },
2251
+ vs: {
2252
+ color: "#FFF",
2253
+ fontSize: 24,
2254
+ fontWeight: "900",
2255
+ zIndex: 2
2256
+ },
2257
+ logoPlaceholder: {
2258
+ backgroundColor: "rgba(255,255,255,0.15)",
2259
+ zIndex: 2
2260
+ },
2261
+ teamNames: {
2262
+ position: "absolute",
2263
+ top: 12,
2264
+ left: 12,
2265
+ right: 12,
2266
+ flexDirection: "row",
2267
+ alignItems: "center",
2268
+ justifyContent: "center",
2269
+ gap: 8
2270
+ },
2271
+ teamNameText: {
2272
+ color: "#FFF",
2273
+ fontSize: 14,
2274
+ fontWeight: "700",
2275
+ maxWidth: "40%"
2276
+ },
2277
+ teamNameVs: {
2278
+ color: "rgba(255,255,255,0.6)",
2279
+ fontSize: 12,
2280
+ fontWeight: "600"
2281
+ },
2282
+ countdownPill: {
2283
+ position: "absolute",
2284
+ bottom: 12,
2285
+ left: 12,
2286
+ backgroundColor: "rgba(0,0,0,0.65)",
2287
+ borderRadius: 8,
2288
+ paddingHorizontal: 10,
2289
+ paddingVertical: 5
2290
+ },
2291
+ countdownText: {
2292
+ color: "#FFF",
2293
+ fontSize: 13,
2294
+ fontWeight: "700"
2295
+ },
2296
+ countdownLive: {
2297
+ color: "#EF4444"
2298
+ },
2299
+ poolPill: {
2300
+ position: "absolute",
2301
+ bottom: 12,
2302
+ right: 12,
2303
+ backgroundColor: "#7C3AED",
2304
+ borderRadius: 8,
2305
+ paddingHorizontal: 12,
2306
+ paddingVertical: 5
2307
+ },
2308
+ poolText: {
2309
+ color: "#FFF",
2310
+ fontSize: 13,
2311
+ fontWeight: "800"
2312
+ }
2313
+ });
2314
+
2315
+ // src/ui/game/LivePoolsCard.tsx
2316
+ var import_react15 = require("react");
2317
+ var import_react_native7 = require("react-native");
2318
+ var import_jsx_runtime8 = require("react/jsx-runtime");
2319
+ function LivePoolsCard({
2320
+ game,
2321
+ shortName,
2322
+ homeColor = "#3B82F6",
2323
+ awayColor = "#EF4444"
2324
+ }) {
2325
+ const t = useDubsTheme();
2326
+ const opponents = game.opponents || [];
2327
+ const homeName = shortName ? shortName(opponents[0]?.name) : opponents[0]?.name || "Home";
2328
+ const awayName = shortName ? shortName(opponents[1]?.name) : opponents[1]?.name || "Away";
2329
+ const homePool = game.homePool || 0;
2330
+ const awayPool = game.awayPool || 0;
2331
+ const totalPool = game.totalPool || 0;
2332
+ const { homePercent, awayPercent, homeOdds, awayOdds } = (0, import_react15.useMemo)(() => {
2333
+ return {
2334
+ homePercent: totalPool > 0 ? homePool / totalPool * 100 : 50,
2335
+ awayPercent: totalPool > 0 ? awayPool / totalPool * 100 : 50,
2336
+ homeOdds: homePool > 0 ? (totalPool / homePool).toFixed(2) : "\u2014",
2337
+ awayOdds: awayPool > 0 ? (totalPool / awayPool).toFixed(2) : "\u2014"
2338
+ };
2339
+ }, [homePool, awayPool, totalPool]);
2340
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native7.View, { style: [styles5.card, { backgroundColor: t.surface, borderColor: t.border }], children: [
2341
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native7.Text, { style: [styles5.title, { color: t.text }], children: "Live Pools" }),
2342
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native7.Text, { style: [styles5.total, { color: t.accent }], children: [
2343
+ totalPool,
2344
+ " SOL total"
2345
+ ] }),
2346
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native7.View, { style: styles5.bars, children: [
2347
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PoolBar, { name: homeName, amount: homePool, percent: homePercent, color: homeColor, t }),
2348
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PoolBar, { name: awayName, amount: awayPool, percent: awayPercent, color: awayColor, t })
2349
+ ] }),
2350
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native7.View, { style: styles5.oddsRow, children: [
2351
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native7.Text, { style: [styles5.oddsText, { color: t.textMuted }], children: [
2352
+ homeName,
2353
+ ": ",
2354
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native7.Text, { style: { color: t.text, fontWeight: "700" }, children: [
2355
+ homeOdds,
2356
+ "x"
2357
+ ] })
2358
+ ] }),
2359
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native7.Text, { style: [styles5.oddsText, { color: t.textMuted }], children: [
2360
+ awayName,
2361
+ ": ",
2362
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native7.Text, { style: { color: t.text, fontWeight: "700" }, children: [
2363
+ awayOdds,
2364
+ "x"
2365
+ ] })
2366
+ ] })
2367
+ ] })
2368
+ ] });
2369
+ }
2370
+ function PoolBar({ name, amount, percent, color, t }) {
2371
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native7.View, { style: styles5.barRow, children: [
2372
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native7.Text, { style: [styles5.barLabel, { color: t.textSecondary }], numberOfLines: 1, children: name }),
2373
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native7.View, { style: [styles5.barTrack, { backgroundColor: t.border }], children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native7.View, { style: [styles5.barFill, { width: `${Math.max(percent, 2)}%`, backgroundColor: color }] }) }),
2374
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native7.Text, { style: [styles5.barAmount, { color: t.text }], children: [
2375
+ amount,
2376
+ " SOL"
2377
+ ] })
2378
+ ] });
2379
+ }
2380
+ var styles5 = import_react_native7.StyleSheet.create({
2381
+ card: { borderRadius: 16, borderWidth: 1, padding: 16 },
2382
+ title: { fontSize: 17, fontWeight: "700", marginBottom: 4 },
2383
+ total: { fontSize: 14, fontWeight: "600", marginBottom: 14 },
2384
+ bars: { gap: 10 },
2385
+ barRow: { flexDirection: "row", alignItems: "center", gap: 10 },
2386
+ barLabel: { width: 80, fontSize: 13, fontWeight: "600" },
2387
+ barTrack: { flex: 1, height: 10, borderRadius: 5, overflow: "hidden" },
2388
+ barFill: { height: "100%", borderRadius: 5 },
2389
+ barAmount: { width: 70, textAlign: "right", fontSize: 13, fontWeight: "700" },
2390
+ oddsRow: { flexDirection: "row", justifyContent: "space-between", marginTop: 12 },
2391
+ oddsText: { fontSize: 12 }
2392
+ });
2393
+
2394
+ // src/ui/game/PickWinnerCard.tsx
2395
+ var import_react16 = require("react");
2396
+ var import_react_native8 = require("react-native");
2397
+ var import_jsx_runtime9 = require("react/jsx-runtime");
2398
+ function PickWinnerCard({
2399
+ game,
2400
+ selectedTeam,
2401
+ onSelect,
2402
+ shortName,
2403
+ homeColor = "#3B82F6",
2404
+ awayColor = "#EF4444",
2405
+ ImageComponent
2406
+ }) {
2407
+ const t = useDubsTheme();
2408
+ const opponents = game.opponents || [];
2409
+ const bettors = game.bettors || [];
2410
+ const totalPool = game.totalPool || 0;
2411
+ const homePool = game.homePool || 0;
2412
+ const awayPool = game.awayPool || 0;
2413
+ const { homeOdds, awayOdds, homeBets, awayBets } = (0, import_react16.useMemo)(() => ({
2414
+ homeOdds: homePool > 0 ? (totalPool / homePool).toFixed(2) : "\u2014",
2415
+ awayOdds: awayPool > 0 ? (totalPool / awayPool).toFixed(2) : "\u2014",
2416
+ homeBets: bettors.filter((b) => b.team === "home").length,
2417
+ awayBets: bettors.filter((b) => b.team === "away").length
2418
+ }), [totalPool, homePool, awayPool, bettors]);
2419
+ const homeName = shortName ? shortName(opponents[0]?.name) : opponents[0]?.name || "Home";
2420
+ const awayName = shortName ? shortName(opponents[1]?.name) : opponents[1]?.name || "Away";
2421
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native8.View, { style: [styles6.card, { backgroundColor: t.surface, borderColor: t.border }], children: [
2422
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native8.Text, { style: [styles6.title, { color: t.text }], children: "Pick Your Winner" }),
2423
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native8.View, { style: styles6.row, children: [
2424
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2425
+ TeamOption,
2426
+ {
2427
+ name: homeName,
2428
+ imageUrl: opponents[0]?.imageUrl,
2429
+ odds: homeOdds,
2430
+ bets: homeBets,
2431
+ color: homeColor,
2432
+ selected: selectedTeam === "home",
2433
+ onPress: () => onSelect("home"),
2434
+ ImageComponent,
2435
+ t
2436
+ }
2437
+ ),
2438
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2439
+ TeamOption,
2440
+ {
2441
+ name: awayName,
2442
+ imageUrl: opponents[1]?.imageUrl,
2443
+ odds: awayOdds,
2444
+ bets: awayBets,
2445
+ color: awayColor,
2446
+ selected: selectedTeam === "away",
2447
+ onPress: () => onSelect("away"),
2448
+ ImageComponent,
2449
+ t
2450
+ }
2451
+ )
2452
+ ] })
2453
+ ] });
2454
+ }
2455
+ function TeamOption({
2456
+ name,
2457
+ imageUrl,
2458
+ odds,
2459
+ bets,
2460
+ color,
2461
+ selected,
2462
+ onPress,
2463
+ ImageComponent,
2464
+ t
2465
+ }) {
2466
+ const [imgFailed, setImgFailed] = (0, import_react16.useState)(false);
2467
+ const Img = ImageComponent || require("react-native").Image;
2468
+ const showImage = imageUrl && !imgFailed;
2469
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2470
+ import_react_native8.TouchableOpacity,
2471
+ {
2472
+ style: [styles6.option, { borderColor: selected ? color : t.border, backgroundColor: selected ? color + "15" : t.background }],
2473
+ onPress,
2474
+ activeOpacity: 0.7,
2475
+ children: [
2476
+ showImage ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Img, { source: { uri: imageUrl }, style: styles6.logo, resizeMode: "contain", onError: () => setImgFailed(true) }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native8.View, { style: [styles6.logo, styles6.logoPlaceholder] }),
2477
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native8.Text, { style: [styles6.name, { color: t.text }], numberOfLines: 1, children: name }),
2478
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native8.Text, { style: [styles6.odds, { color }], children: [
2479
+ odds,
2480
+ "x"
2481
+ ] }),
2482
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native8.Text, { style: [styles6.bets, { color: t.textMuted }], children: [
2483
+ bets,
2484
+ " ",
2485
+ bets === 1 ? "bet" : "bets"
2486
+ ] }),
2487
+ selected && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native8.View, { style: [styles6.badge, { backgroundColor: color }], children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native8.Text, { style: styles6.badgeText, children: "Selected" }) })
2488
+ ]
2489
+ }
2490
+ );
2491
+ }
2492
+ var styles6 = import_react_native8.StyleSheet.create({
2493
+ card: { borderRadius: 16, borderWidth: 1, padding: 16 },
2494
+ title: { fontSize: 17, fontWeight: "700", marginBottom: 12 },
2495
+ row: { flexDirection: "row", gap: 12 },
2496
+ option: { flex: 1, borderWidth: 2, borderRadius: 16, padding: 16, alignItems: "center", gap: 8 },
2497
+ logo: { width: 48, height: 48, borderRadius: 24 },
2498
+ logoPlaceholder: { backgroundColor: "rgba(128,128,128,0.2)" },
2499
+ name: { fontSize: 15, fontWeight: "700" },
2500
+ odds: { fontSize: 20, fontWeight: "800" },
2501
+ bets: { fontSize: 12 },
2502
+ badge: { borderRadius: 8, paddingHorizontal: 12, paddingVertical: 4, marginTop: 4 },
2503
+ badgeText: { color: "#FFF", fontSize: 12, fontWeight: "700" }
2504
+ });
2505
+
2506
+ // src/ui/game/PlayersCard.tsx
2507
+ var import_react17 = require("react");
2508
+ var import_react_native9 = require("react-native");
2509
+ var import_jsx_runtime10 = require("react/jsx-runtime");
2510
+ function truncateWallet(addr, chars) {
2511
+ if (addr.length <= chars * 2 + 3) return addr;
2512
+ return `${addr.slice(0, chars)}...${addr.slice(-chars)}`;
2513
+ }
2514
+ function PlayersCard({
2515
+ game,
2516
+ truncateChars = 4,
2517
+ homeColor = "#3B82F6",
2518
+ awayColor = "#EF4444",
2519
+ drawColor = "#A855F7",
2520
+ ImageComponent
2521
+ }) {
2522
+ const t = useDubsTheme();
2523
+ const bettors = game.bettors || [];
2524
+ const dotColor = (team) => {
2525
+ if (team === "home") return homeColor;
2526
+ if (team === "away") return awayColor;
2527
+ return drawColor;
2528
+ };
2529
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native9.View, { style: [styles7.card, { backgroundColor: t.surface, borderColor: t.border }], children: [
2530
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native9.Text, { style: [styles7.title, { color: t.text }], children: [
2531
+ "Players",
2532
+ bettors.length > 0 ? ` (${bettors.length})` : ""
2533
+ ] }),
2534
+ bettors.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native9.Text, { style: [styles7.empty, { color: t.textMuted }], children: "No players yet \u2014 be the first!" }) : bettors.map((b, i) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2535
+ BettorRow,
2536
+ {
2537
+ bettor: b,
2538
+ dotColor: dotColor(b.team),
2539
+ truncateChars,
2540
+ isFirst: i === 0,
2541
+ ImageComponent,
2542
+ t
2543
+ },
2544
+ `${b.wallet}-${i}`
2545
+ ))
2546
+ ] });
2547
+ }
2548
+ function BettorRow({
2549
+ bettor,
2550
+ dotColor,
2551
+ truncateChars,
2552
+ isFirst,
2553
+ ImageComponent,
2554
+ t
2555
+ }) {
2556
+ const [imgFailed, setImgFailed] = (0, import_react17.useState)(false);
2557
+ const Img = ImageComponent || require("react-native").Image;
2558
+ const showAvatar = bettor.avatar && !imgFailed;
2559
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native9.View, { style: [styles7.row, !isFirst && { borderTopColor: t.border, borderTopWidth: 1 }], children: [
2560
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native9.View, { style: [styles7.dot, { backgroundColor: dotColor }] }),
2561
+ showAvatar ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Img, { source: { uri: bettor.avatar }, style: styles7.avatar, resizeMode: "cover", onError: () => setImgFailed(true) }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native9.View, { style: [styles7.avatar, styles7.avatarPlaceholder] }),
2562
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native9.View, { style: styles7.nameCol, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native9.Text, { style: [styles7.username, { color: t.text }], numberOfLines: 1, children: bettor.username || truncateWallet(bettor.wallet, truncateChars) }) }),
2563
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native9.Text, { style: [styles7.amount, { color: t.textSecondary }], children: [
2564
+ bettor.amount,
2565
+ " SOL"
2566
+ ] })
2567
+ ] });
2568
+ }
2569
+ var styles7 = import_react_native9.StyleSheet.create({
2570
+ card: { borderRadius: 16, borderWidth: 1, padding: 16 },
2571
+ title: { fontSize: 17, fontWeight: "700", marginBottom: 12 },
2572
+ empty: { fontSize: 14, textAlign: "center", paddingVertical: 16 },
2573
+ row: { flexDirection: "row", alignItems: "center", paddingVertical: 10, gap: 10 },
2574
+ dot: { width: 8, height: 8, borderRadius: 4 },
2575
+ avatar: { width: 28, height: 28, borderRadius: 14 },
2576
+ avatarPlaceholder: { backgroundColor: "rgba(128,128,128,0.2)" },
2577
+ nameCol: { flex: 1 },
2578
+ username: { fontSize: 14, fontWeight: "600" },
2579
+ amount: { fontSize: 13, fontWeight: "700" }
2580
+ });
2581
+
2582
+ // src/ui/game/JoinGameButton.tsx
2583
+ var import_react18 = require("react");
2584
+ var import_react_native10 = require("react-native");
2585
+ var import_jsx_runtime11 = require("react/jsx-runtime");
2586
+ var STATUS_LABELS = {
2587
+ building: "Building transaction...",
2588
+ signing: "Approve in wallet...",
2589
+ confirming: "Confirming on-chain...",
2590
+ saving: "Saving..."
2591
+ };
2592
+ function JoinGameButton({ game, walletAddress, selectedTeam, status, onJoin }) {
2593
+ const t = useDubsTheme();
2594
+ const alreadyJoined = (0, import_react18.useMemo)(() => {
2595
+ if (!walletAddress) return false;
2596
+ return (game.bettors || []).some((b) => b.wallet === walletAddress);
2597
+ }, [game.bettors, walletAddress]);
2598
+ if (alreadyJoined || game.isLocked || game.isResolved) return null;
2599
+ const isJoining = status !== "idle" && status !== "success" && status !== "error";
2600
+ const statusLabel = STATUS_LABELS[status] || "";
2601
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_react_native10.View, { style: [styles8.bar, { backgroundColor: t.background, borderTopColor: t.border }], children: [
2602
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_react_native10.View, { style: styles8.buyInRow, children: [
2603
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native10.Text, { style: [styles8.buyInLabel, { color: t.textMuted }], children: "Buy-in" }),
2604
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_react_native10.Text, { style: [styles8.buyInValue, { color: t.text }], children: [
2605
+ game.buyIn,
2606
+ " SOL"
2607
+ ] })
2608
+ ] }),
2609
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2610
+ import_react_native10.TouchableOpacity,
2611
+ {
2612
+ style: [styles8.button, { backgroundColor: selectedTeam ? "#22D3EE" : t.border }],
2613
+ disabled: !selectedTeam || isJoining,
2614
+ onPress: onJoin,
2615
+ activeOpacity: 0.8,
2616
+ children: isJoining ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_react_native10.View, { style: styles8.joiningRow, children: [
2617
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native10.ActivityIndicator, { size: "small", color: "#000" }),
2618
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native10.Text, { style: styles8.buttonText, children: statusLabel })
2619
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native10.Text, { style: [styles8.buttonText, !selectedTeam && { color: t.textMuted }], children: selectedTeam ? `Join Bet \u2014 ${game.buyIn} SOL` : "Pick a team to bet" })
2620
+ }
2621
+ )
2622
+ ] });
2623
+ }
2624
+ var styles8 = import_react_native10.StyleSheet.create({
2625
+ bar: { position: "absolute", bottom: 0, left: 0, right: 0, paddingHorizontal: 16, paddingTop: 12, paddingBottom: 36, borderTopWidth: 1 },
2626
+ buyInRow: { flexDirection: "row", alignItems: "center", justifyContent: "space-between", marginBottom: 10 },
2627
+ buyInLabel: { fontSize: 13 },
2628
+ buyInValue: { fontSize: 15, fontWeight: "800" },
2629
+ button: { borderRadius: 14, paddingVertical: 16, alignItems: "center" },
2630
+ buttonText: { color: "#000", fontSize: 16, fontWeight: "800" },
2631
+ joiningRow: { flexDirection: "row", alignItems: "center", gap: 10 }
2632
+ });
1836
2633
  // Annotate the CommonJS export names for ESM import in node:
1837
2634
  0 && (module.exports = {
1838
2635
  AuthGate,
@@ -1842,12 +2639,19 @@ var styles3 = import_react_native5.StyleSheet.create({
1842
2639
  DubsApiError,
1843
2640
  DubsClient,
1844
2641
  DubsProvider,
2642
+ GamePoster,
2643
+ JoinGameButton,
2644
+ LivePoolsCard,
1845
2645
  MwaWalletAdapter,
2646
+ NETWORK_CONFIG,
2647
+ PickWinnerCard,
2648
+ PlayersCard,
1846
2649
  SOLANA_PROGRAM_ERRORS,
2650
+ STORAGE_KEYS,
1847
2651
  SettingsSheet,
1848
2652
  UserProfileCard,
2653
+ createSecureStoreStorage,
1849
2654
  parseSolanaError,
1850
- pollTransactionConfirmation,
1851
2655
  signAndSendBase64Transaction,
1852
2656
  useAuth,
1853
2657
  useClaim,