@dubsdotapp/expo 0.1.3 → 0.2.1
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/README.md +80 -19
- package/dist/index.d.mts +207 -46
- package/dist/index.d.ts +207 -46
- package/dist/index.js +1266 -411
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1299 -444
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -2
- package/src/auth-context.ts +9 -0
- package/src/client.ts +30 -1
- package/src/constants.ts +15 -0
- package/src/hooks/useAuth.ts +13 -2
- package/src/hooks/useClaim.ts +11 -9
- package/src/hooks/useCreateGame.ts +15 -12
- package/src/hooks/useJoinGame.ts +18 -14
- package/src/index.ts +22 -3
- package/src/managed-wallet.tsx +158 -0
- package/src/provider.tsx +245 -9
- package/src/storage.ts +57 -0
- package/src/types.ts +47 -4
- package/src/ui/AuthGate.tsx +20 -11
- package/src/ui/ConnectWalletScreen.tsx +31 -5
- package/src/ui/game/GamePoster.tsx +182 -0
- package/src/ui/game/JoinGameButton.tsx +73 -0
- package/src/ui/game/LivePoolsCard.tsx +88 -0
- package/src/ui/game/PickWinnerCard.tsx +126 -0
- package/src/ui/game/PlayersCard.tsx +108 -0
- package/src/ui/game/index.ts +10 -0
- package/src/ui/index.ts +11 -1
- package/src/ui/theme.ts +5 -0
- package/src/utils/transaction.ts +8 -49
- package/src/wallet/mwa-adapter.ts +9 -6
- package/src/wallet/types.ts +6 -0
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,25 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
1
8
|
// src/constants.ts
|
|
2
9
|
var DEFAULT_BASE_URL = "https://dubs-server-prod-9c91d3f01199.herokuapp.com/api/developer/v1";
|
|
3
10
|
var DEFAULT_RPC_URL = "https://api.mainnet-beta.solana.com";
|
|
11
|
+
var NETWORK_CONFIG = {
|
|
12
|
+
"mainnet-beta": {
|
|
13
|
+
baseUrl: DEFAULT_BASE_URL,
|
|
14
|
+
rpcUrl: DEFAULT_RPC_URL,
|
|
15
|
+
cluster: "mainnet-beta"
|
|
16
|
+
},
|
|
17
|
+
devnet: {
|
|
18
|
+
baseUrl: "https://dubs-server-dev-55d1fba09a97.herokuapp.com/api/developer/v1",
|
|
19
|
+
rpcUrl: "https://api.devnet.solana.com",
|
|
20
|
+
cluster: "devnet"
|
|
21
|
+
}
|
|
22
|
+
};
|
|
4
23
|
|
|
5
24
|
// src/errors.ts
|
|
6
25
|
var DubsApiError = class extends Error {
|
|
@@ -134,12 +153,20 @@ var DubsClient = class {
|
|
|
134
153
|
if (this._token) {
|
|
135
154
|
headers["Authorization"] = `Bearer ${this._token}`;
|
|
136
155
|
}
|
|
156
|
+
console.log(`[DubsClient] ${method} ${url}`, body ? JSON.stringify(body).slice(0, 200) : "");
|
|
137
157
|
const res = await fetch(url, {
|
|
138
158
|
method,
|
|
139
159
|
headers,
|
|
140
160
|
body: body ? JSON.stringify(body) : void 0
|
|
141
161
|
});
|
|
142
|
-
const
|
|
162
|
+
const text = await res.text();
|
|
163
|
+
console.log(`[DubsClient] ${method} ${path} \u2192 ${res.status}`, text.slice(0, 300));
|
|
164
|
+
let json;
|
|
165
|
+
try {
|
|
166
|
+
json = JSON.parse(text);
|
|
167
|
+
} catch {
|
|
168
|
+
throw new DubsApiError("parse_error", `Invalid JSON response: ${text.slice(0, 100)}`, res.status);
|
|
169
|
+
}
|
|
143
170
|
if (!json.success) {
|
|
144
171
|
const err = json.error;
|
|
145
172
|
if (typeof err === "object" && err !== null) {
|
|
@@ -259,6 +286,13 @@ var DubsClient = class {
|
|
|
259
286
|
);
|
|
260
287
|
return res.game;
|
|
261
288
|
}
|
|
289
|
+
async getLiveScore(gameId) {
|
|
290
|
+
const res = await this.request(
|
|
291
|
+
"GET",
|
|
292
|
+
`/games/${encodeURIComponent(gameId)}/live-score`
|
|
293
|
+
);
|
|
294
|
+
return res.liveScore;
|
|
295
|
+
}
|
|
262
296
|
async getGames(params) {
|
|
263
297
|
const qs = new URLSearchParams();
|
|
264
298
|
if (params?.wallet) qs.set("wallet", params.wallet);
|
|
@@ -276,6 +310,7 @@ var DubsClient = class {
|
|
|
276
310
|
async getNetworkGames(params) {
|
|
277
311
|
const qs = new URLSearchParams();
|
|
278
312
|
if (params?.league) qs.set("league", params.league);
|
|
313
|
+
if (params?.exclude_wallet) qs.set("exclude_wallet", params.exclude_wallet);
|
|
279
314
|
if (params?.limit != null) qs.set("limit", String(params.limit));
|
|
280
315
|
if (params?.offset != null) qs.set("offset", String(params.offset));
|
|
281
316
|
const query = qs.toString();
|
|
@@ -382,29 +417,56 @@ var DubsClient = class {
|
|
|
382
417
|
getErrorCodesLocal() {
|
|
383
418
|
return { ...SOLANA_PROGRAM_ERRORS };
|
|
384
419
|
}
|
|
420
|
+
// ── App Config ──
|
|
421
|
+
/** Fetch the app's UI customization config (accent color, icon, tagline) */
|
|
422
|
+
async getAppConfig() {
|
|
423
|
+
const res = await this.request("GET", "/apps/config");
|
|
424
|
+
return res.uiConfig || {};
|
|
425
|
+
}
|
|
385
426
|
};
|
|
386
427
|
|
|
387
|
-
// src/
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
function
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
428
|
+
// src/storage.ts
|
|
429
|
+
var STORAGE_KEYS = {
|
|
430
|
+
MWA_AUTH_TOKEN: "dubs_mwa_auth_token",
|
|
431
|
+
JWT_TOKEN: "dubs_jwt_token"
|
|
432
|
+
};
|
|
433
|
+
function createSecureStoreStorage() {
|
|
434
|
+
let SecureStore = null;
|
|
435
|
+
function getStore() {
|
|
436
|
+
if (!SecureStore) {
|
|
437
|
+
try {
|
|
438
|
+
SecureStore = __require("expo-secure-store");
|
|
439
|
+
} catch {
|
|
440
|
+
throw new Error(
|
|
441
|
+
"@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>."
|
|
442
|
+
);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
return SecureStore;
|
|
404
446
|
}
|
|
405
|
-
return
|
|
447
|
+
return {
|
|
448
|
+
async getItem(key) {
|
|
449
|
+
const store = getStore();
|
|
450
|
+
return store.getItemAsync(key);
|
|
451
|
+
},
|
|
452
|
+
async setItem(key, value) {
|
|
453
|
+
const store = getStore();
|
|
454
|
+
await store.setItemAsync(key, value);
|
|
455
|
+
},
|
|
456
|
+
async deleteItem(key) {
|
|
457
|
+
const store = getStore();
|
|
458
|
+
await store.deleteItemAsync(key);
|
|
459
|
+
}
|
|
460
|
+
};
|
|
406
461
|
}
|
|
407
462
|
|
|
463
|
+
// src/provider.tsx
|
|
464
|
+
import { createContext as createContext3, useContext as useContext3, useMemo, useCallback as useCallback11, useState as useState11, useEffect as useEffect7 } from "react";
|
|
465
|
+
import { Connection } from "@solana/web3.js";
|
|
466
|
+
|
|
467
|
+
// src/managed-wallet.tsx
|
|
468
|
+
import { createContext, useContext, useState, useEffect, useRef, useCallback } from "react";
|
|
469
|
+
|
|
408
470
|
// src/wallet/mwa-adapter.ts
|
|
409
471
|
import { PublicKey } from "@solana/web3.js";
|
|
410
472
|
function toPublicKey(address) {
|
|
@@ -428,6 +490,12 @@ var MwaWalletAdapter = class {
|
|
|
428
490
|
get connected() {
|
|
429
491
|
return this._connected;
|
|
430
492
|
}
|
|
493
|
+
get authToken() {
|
|
494
|
+
return this._authToken;
|
|
495
|
+
}
|
|
496
|
+
setAuthToken(token) {
|
|
497
|
+
this._authToken = token;
|
|
498
|
+
}
|
|
431
499
|
/**
|
|
432
500
|
* Connect to a mobile wallet. Call this before any signing.
|
|
433
501
|
*/
|
|
@@ -491,26 +559,328 @@ var MwaWalletAdapter = class {
|
|
|
491
559
|
return result[0];
|
|
492
560
|
});
|
|
493
561
|
if (signature instanceof Uint8Array) {
|
|
494
|
-
|
|
495
|
-
return new PublicKey(signature).toBase58();
|
|
496
|
-
}).catch(() => {
|
|
497
|
-
return Buffer.from(signature).toString("base64");
|
|
498
|
-
});
|
|
499
|
-
return bs582;
|
|
562
|
+
return new PublicKey(signature).toBase58();
|
|
500
563
|
}
|
|
501
564
|
return String(signature);
|
|
502
565
|
}
|
|
503
566
|
};
|
|
504
567
|
|
|
568
|
+
// src/ui/ConnectWalletScreen.tsx
|
|
569
|
+
import {
|
|
570
|
+
View,
|
|
571
|
+
Text,
|
|
572
|
+
TouchableOpacity,
|
|
573
|
+
ActivityIndicator,
|
|
574
|
+
StyleSheet,
|
|
575
|
+
Image
|
|
576
|
+
} from "react-native";
|
|
577
|
+
|
|
578
|
+
// src/ui/theme.ts
|
|
579
|
+
import { useColorScheme } from "react-native";
|
|
580
|
+
var dark = {
|
|
581
|
+
background: "#08080D",
|
|
582
|
+
surface: "#111118",
|
|
583
|
+
surfaceActive: "#7C3AED",
|
|
584
|
+
border: "#1A1A24",
|
|
585
|
+
text: "#FFFFFF",
|
|
586
|
+
textSecondary: "#E0E0EE",
|
|
587
|
+
textMuted: "#666666",
|
|
588
|
+
textDim: "#555555",
|
|
589
|
+
accent: "#7C3AED",
|
|
590
|
+
success: "#22C55E",
|
|
591
|
+
live: "#EF4444",
|
|
592
|
+
errorText: "#F87171",
|
|
593
|
+
errorBg: "#1A0A0A",
|
|
594
|
+
errorBorder: "#3A1515"
|
|
595
|
+
};
|
|
596
|
+
var light = {
|
|
597
|
+
background: "#FFFFFF",
|
|
598
|
+
surface: "#F0F0F5",
|
|
599
|
+
surfaceActive: "#7C3AED",
|
|
600
|
+
border: "#E0E0E8",
|
|
601
|
+
text: "#111118",
|
|
602
|
+
textSecondary: "#333333",
|
|
603
|
+
textMuted: "#888888",
|
|
604
|
+
textDim: "#999999",
|
|
605
|
+
accent: "#7C3AED",
|
|
606
|
+
success: "#16A34A",
|
|
607
|
+
live: "#DC2626",
|
|
608
|
+
errorText: "#DC2626",
|
|
609
|
+
errorBg: "#FEF2F2",
|
|
610
|
+
errorBorder: "#FECACA"
|
|
611
|
+
};
|
|
612
|
+
function useDubsTheme() {
|
|
613
|
+
const scheme = useColorScheme();
|
|
614
|
+
return scheme === "light" ? light : dark;
|
|
615
|
+
}
|
|
616
|
+
function mergeTheme(base, overrides) {
|
|
617
|
+
return { ...base, ...overrides };
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
// src/ui/ConnectWalletScreen.tsx
|
|
621
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
622
|
+
function ConnectWalletScreen({
|
|
623
|
+
onConnect,
|
|
624
|
+
connecting = false,
|
|
625
|
+
error = null,
|
|
626
|
+
appName = "Dubs",
|
|
627
|
+
accentColor,
|
|
628
|
+
appIcon,
|
|
629
|
+
tagline
|
|
630
|
+
}) {
|
|
631
|
+
const t = useDubsTheme();
|
|
632
|
+
const accent = accentColor || t.accent;
|
|
633
|
+
return /* @__PURE__ */ jsx(View, { style: [styles.container, { backgroundColor: t.background }], children: /* @__PURE__ */ jsxs(View, { style: styles.content, children: [
|
|
634
|
+
/* @__PURE__ */ jsxs(View, { style: styles.brandingSection, children: [
|
|
635
|
+
appIcon ? /* @__PURE__ */ jsx(
|
|
636
|
+
Image,
|
|
637
|
+
{
|
|
638
|
+
source: { uri: appIcon },
|
|
639
|
+
style: styles.logoImage
|
|
640
|
+
}
|
|
641
|
+
) : /* @__PURE__ */ jsx(View, { style: [styles.logoCircle, { backgroundColor: accent }], children: /* @__PURE__ */ jsx(Text, { style: styles.logoText, children: appName.charAt(0).toUpperCase() }) }),
|
|
642
|
+
/* @__PURE__ */ jsx(Text, { style: [styles.appName, { color: t.text }], children: appName }),
|
|
643
|
+
/* @__PURE__ */ jsx(Text, { style: [styles.subtitle, { color: t.textMuted }], children: tagline || "Connect your Solana wallet to get started" })
|
|
644
|
+
] }),
|
|
645
|
+
/* @__PURE__ */ jsxs(View, { style: styles.actionSection, children: [
|
|
646
|
+
error ? /* @__PURE__ */ jsx(
|
|
647
|
+
View,
|
|
648
|
+
{
|
|
649
|
+
style: [
|
|
650
|
+
styles.errorBox,
|
|
651
|
+
{ backgroundColor: t.errorBg, borderColor: t.errorBorder }
|
|
652
|
+
],
|
|
653
|
+
children: /* @__PURE__ */ jsx(Text, { style: [styles.errorText, { color: t.errorText }], children: error })
|
|
654
|
+
}
|
|
655
|
+
) : null,
|
|
656
|
+
/* @__PURE__ */ jsx(
|
|
657
|
+
TouchableOpacity,
|
|
658
|
+
{
|
|
659
|
+
style: [styles.connectButton, { backgroundColor: accent }],
|
|
660
|
+
onPress: onConnect,
|
|
661
|
+
disabled: connecting,
|
|
662
|
+
activeOpacity: 0.8,
|
|
663
|
+
children: connecting ? /* @__PURE__ */ jsx(ActivityIndicator, { color: "#FFFFFF", size: "small" }) : /* @__PURE__ */ jsx(Text, { style: styles.connectButtonText, children: "Connect Wallet" })
|
|
664
|
+
}
|
|
665
|
+
),
|
|
666
|
+
/* @__PURE__ */ jsx(Text, { style: [styles.hint, { color: t.textDim }], children: "Phantom, Solflare, or any Solana wallet" })
|
|
667
|
+
] })
|
|
668
|
+
] }) });
|
|
669
|
+
}
|
|
670
|
+
var styles = StyleSheet.create({
|
|
671
|
+
container: {
|
|
672
|
+
flex: 1,
|
|
673
|
+
justifyContent: "center"
|
|
674
|
+
},
|
|
675
|
+
content: {
|
|
676
|
+
flex: 1,
|
|
677
|
+
justifyContent: "space-between",
|
|
678
|
+
paddingHorizontal: 32,
|
|
679
|
+
paddingTop: 120,
|
|
680
|
+
paddingBottom: 80
|
|
681
|
+
},
|
|
682
|
+
brandingSection: {
|
|
683
|
+
alignItems: "center",
|
|
684
|
+
gap: 12
|
|
685
|
+
},
|
|
686
|
+
logoCircle: {
|
|
687
|
+
width: 80,
|
|
688
|
+
height: 80,
|
|
689
|
+
borderRadius: 40,
|
|
690
|
+
justifyContent: "center",
|
|
691
|
+
alignItems: "center",
|
|
692
|
+
marginBottom: 8
|
|
693
|
+
},
|
|
694
|
+
logoImage: {
|
|
695
|
+
width: 80,
|
|
696
|
+
height: 80,
|
|
697
|
+
borderRadius: 16,
|
|
698
|
+
marginBottom: 8
|
|
699
|
+
},
|
|
700
|
+
logoText: {
|
|
701
|
+
fontSize: 36,
|
|
702
|
+
fontWeight: "800",
|
|
703
|
+
color: "#FFFFFF"
|
|
704
|
+
},
|
|
705
|
+
appName: {
|
|
706
|
+
fontSize: 32,
|
|
707
|
+
fontWeight: "800"
|
|
708
|
+
},
|
|
709
|
+
subtitle: {
|
|
710
|
+
fontSize: 16,
|
|
711
|
+
textAlign: "center",
|
|
712
|
+
lineHeight: 22
|
|
713
|
+
},
|
|
714
|
+
actionSection: {
|
|
715
|
+
gap: 16
|
|
716
|
+
},
|
|
717
|
+
errorBox: {
|
|
718
|
+
borderWidth: 1,
|
|
719
|
+
borderRadius: 12,
|
|
720
|
+
paddingHorizontal: 16,
|
|
721
|
+
paddingVertical: 12
|
|
722
|
+
},
|
|
723
|
+
errorText: {
|
|
724
|
+
fontSize: 14,
|
|
725
|
+
textAlign: "center"
|
|
726
|
+
},
|
|
727
|
+
connectButton: {
|
|
728
|
+
height: 56,
|
|
729
|
+
borderRadius: 16,
|
|
730
|
+
justifyContent: "center",
|
|
731
|
+
alignItems: "center"
|
|
732
|
+
},
|
|
733
|
+
connectButtonText: {
|
|
734
|
+
color: "#FFFFFF",
|
|
735
|
+
fontSize: 18,
|
|
736
|
+
fontWeight: "700"
|
|
737
|
+
},
|
|
738
|
+
hint: {
|
|
739
|
+
fontSize: 13,
|
|
740
|
+
textAlign: "center"
|
|
741
|
+
}
|
|
742
|
+
});
|
|
743
|
+
|
|
744
|
+
// src/managed-wallet.tsx
|
|
745
|
+
import { Fragment, jsx as jsx2 } from "react/jsx-runtime";
|
|
746
|
+
var DisconnectContext = createContext(null);
|
|
747
|
+
function useDisconnect() {
|
|
748
|
+
return useContext(DisconnectContext);
|
|
749
|
+
}
|
|
750
|
+
function ManagedWalletProvider({
|
|
751
|
+
appName,
|
|
752
|
+
cluster,
|
|
753
|
+
storage,
|
|
754
|
+
renderConnectScreen,
|
|
755
|
+
accentColor,
|
|
756
|
+
appIcon,
|
|
757
|
+
tagline,
|
|
758
|
+
children
|
|
759
|
+
}) {
|
|
760
|
+
const [connected, setConnected] = useState(false);
|
|
761
|
+
const [connecting, setConnecting] = useState(false);
|
|
762
|
+
const [isReady, setIsReady] = useState(false);
|
|
763
|
+
const [error, setError] = useState(null);
|
|
764
|
+
const adapterRef = useRef(null);
|
|
765
|
+
const transactRef = useRef(null);
|
|
766
|
+
if (!adapterRef.current) {
|
|
767
|
+
adapterRef.current = new MwaWalletAdapter({
|
|
768
|
+
transact: (...args) => {
|
|
769
|
+
if (!transactRef.current) {
|
|
770
|
+
throw new Error(
|
|
771
|
+
"@dubsdotapp/expo: @solana-mobile/mobile-wallet-adapter-protocol-web3js is required. Install it with: npm install @solana-mobile/mobile-wallet-adapter-protocol-web3js"
|
|
772
|
+
);
|
|
773
|
+
}
|
|
774
|
+
return transactRef.current(...args);
|
|
775
|
+
},
|
|
776
|
+
appIdentity: { name: appName },
|
|
777
|
+
cluster,
|
|
778
|
+
onAuthTokenChange: (token) => {
|
|
779
|
+
if (token) {
|
|
780
|
+
storage.setItem(STORAGE_KEYS.MWA_AUTH_TOKEN, token).catch(() => {
|
|
781
|
+
});
|
|
782
|
+
} else {
|
|
783
|
+
storage.deleteItem(STORAGE_KEYS.MWA_AUTH_TOKEN).catch(() => {
|
|
784
|
+
});
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
const adapter = adapterRef.current;
|
|
790
|
+
useEffect(() => {
|
|
791
|
+
let cancelled = false;
|
|
792
|
+
(async () => {
|
|
793
|
+
try {
|
|
794
|
+
const mwa = await import("@solana-mobile/mobile-wallet-adapter-protocol-web3js");
|
|
795
|
+
if (cancelled) return;
|
|
796
|
+
transactRef.current = mwa.transact;
|
|
797
|
+
} catch {
|
|
798
|
+
}
|
|
799
|
+
try {
|
|
800
|
+
const savedToken = await storage.getItem(STORAGE_KEYS.MWA_AUTH_TOKEN);
|
|
801
|
+
if (savedToken && !cancelled) {
|
|
802
|
+
adapter.setAuthToken(savedToken);
|
|
803
|
+
await adapter.connect();
|
|
804
|
+
if (!cancelled) setConnected(true);
|
|
805
|
+
}
|
|
806
|
+
} catch {
|
|
807
|
+
} finally {
|
|
808
|
+
if (!cancelled) setIsReady(true);
|
|
809
|
+
}
|
|
810
|
+
})();
|
|
811
|
+
return () => {
|
|
812
|
+
cancelled = true;
|
|
813
|
+
};
|
|
814
|
+
}, [adapter, storage]);
|
|
815
|
+
const handleConnect = useCallback(async () => {
|
|
816
|
+
setConnecting(true);
|
|
817
|
+
setError(null);
|
|
818
|
+
try {
|
|
819
|
+
await adapter.connect();
|
|
820
|
+
setConnected(true);
|
|
821
|
+
} catch (err) {
|
|
822
|
+
const message = err instanceof Error ? err.message : "Connection failed";
|
|
823
|
+
setError(message);
|
|
824
|
+
} finally {
|
|
825
|
+
setConnecting(false);
|
|
826
|
+
}
|
|
827
|
+
}, [adapter]);
|
|
828
|
+
const disconnect = useCallback(async () => {
|
|
829
|
+
adapter.disconnect();
|
|
830
|
+
await storage.deleteItem(STORAGE_KEYS.MWA_AUTH_TOKEN).catch(() => {
|
|
831
|
+
});
|
|
832
|
+
await storage.deleteItem(STORAGE_KEYS.JWT_TOKEN).catch(() => {
|
|
833
|
+
});
|
|
834
|
+
setConnected(false);
|
|
835
|
+
}, [adapter, storage]);
|
|
836
|
+
if (!isReady) return null;
|
|
837
|
+
if (!connected) {
|
|
838
|
+
if (renderConnectScreen === false) {
|
|
839
|
+
return null;
|
|
840
|
+
}
|
|
841
|
+
const connectProps = {
|
|
842
|
+
onConnect: handleConnect,
|
|
843
|
+
connecting,
|
|
844
|
+
error,
|
|
845
|
+
appName,
|
|
846
|
+
accentColor,
|
|
847
|
+
appIcon,
|
|
848
|
+
tagline
|
|
849
|
+
};
|
|
850
|
+
if (renderConnectScreen) {
|
|
851
|
+
return /* @__PURE__ */ jsx2(Fragment, { children: renderConnectScreen(connectProps) });
|
|
852
|
+
}
|
|
853
|
+
return /* @__PURE__ */ jsx2(ConnectWalletScreen, { ...connectProps });
|
|
854
|
+
}
|
|
855
|
+
return /* @__PURE__ */ jsx2(DisconnectContext.Provider, { value: disconnect, children: children(adapter) });
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
// src/ui/AuthGate.tsx
|
|
859
|
+
import React2, { useState as useState10, useEffect as useEffect6, useRef as useRef3, useCallback as useCallback10 } from "react";
|
|
860
|
+
import {
|
|
861
|
+
View as View2,
|
|
862
|
+
Text as Text2,
|
|
863
|
+
TextInput,
|
|
864
|
+
TouchableOpacity as TouchableOpacity2,
|
|
865
|
+
ActivityIndicator as ActivityIndicator2,
|
|
866
|
+
StyleSheet as StyleSheet2,
|
|
867
|
+
Keyboard,
|
|
868
|
+
KeyboardAvoidingView,
|
|
869
|
+
Platform,
|
|
870
|
+
Image as Image2,
|
|
871
|
+
Animated,
|
|
872
|
+
ScrollView
|
|
873
|
+
} from "react-native";
|
|
874
|
+
|
|
505
875
|
// src/hooks/useEvents.ts
|
|
506
|
-
import { useState, useEffect, useCallback } from "react";
|
|
876
|
+
import { useState as useState2, useEffect as useEffect2, useCallback as useCallback2 } from "react";
|
|
507
877
|
function useEvents(params) {
|
|
508
878
|
const { client } = useDubs();
|
|
509
|
-
const [data, setData] =
|
|
510
|
-
const [loading, setLoading] =
|
|
511
|
-
const [error, setError] =
|
|
879
|
+
const [data, setData] = useState2(null);
|
|
880
|
+
const [loading, setLoading] = useState2(true);
|
|
881
|
+
const [error, setError] = useState2(null);
|
|
512
882
|
const paramKey = JSON.stringify(params ?? {});
|
|
513
|
-
const fetchData =
|
|
883
|
+
const fetchData = useCallback2(async () => {
|
|
514
884
|
setLoading(true);
|
|
515
885
|
setError(null);
|
|
516
886
|
try {
|
|
@@ -522,20 +892,20 @@ function useEvents(params) {
|
|
|
522
892
|
setLoading(false);
|
|
523
893
|
}
|
|
524
894
|
}, [client, paramKey]);
|
|
525
|
-
|
|
895
|
+
useEffect2(() => {
|
|
526
896
|
fetchData();
|
|
527
897
|
}, [fetchData]);
|
|
528
898
|
return { data, loading, error, refetch: fetchData };
|
|
529
899
|
}
|
|
530
900
|
|
|
531
901
|
// src/hooks/useGame.ts
|
|
532
|
-
import { useState as
|
|
902
|
+
import { useState as useState3, useEffect as useEffect3, useCallback as useCallback3 } from "react";
|
|
533
903
|
function useGame(gameId) {
|
|
534
904
|
const { client } = useDubs();
|
|
535
|
-
const [data, setData] =
|
|
536
|
-
const [loading, setLoading] =
|
|
537
|
-
const [error, setError] =
|
|
538
|
-
const fetchData =
|
|
905
|
+
const [data, setData] = useState3(null);
|
|
906
|
+
const [loading, setLoading] = useState3(!!gameId);
|
|
907
|
+
const [error, setError] = useState3(null);
|
|
908
|
+
const fetchData = useCallback3(async () => {
|
|
539
909
|
if (!gameId) return;
|
|
540
910
|
setLoading(true);
|
|
541
911
|
setError(null);
|
|
@@ -548,21 +918,21 @@ function useGame(gameId) {
|
|
|
548
918
|
setLoading(false);
|
|
549
919
|
}
|
|
550
920
|
}, [client, gameId]);
|
|
551
|
-
|
|
921
|
+
useEffect3(() => {
|
|
552
922
|
fetchData();
|
|
553
923
|
}, [fetchData]);
|
|
554
924
|
return { data, loading, error, refetch: fetchData };
|
|
555
925
|
}
|
|
556
926
|
|
|
557
927
|
// src/hooks/useGames.ts
|
|
558
|
-
import { useState as
|
|
928
|
+
import { useState as useState4, useEffect as useEffect4, useCallback as useCallback4 } from "react";
|
|
559
929
|
function useGames(params) {
|
|
560
930
|
const { client } = useDubs();
|
|
561
|
-
const [data, setData] =
|
|
562
|
-
const [loading, setLoading] =
|
|
563
|
-
const [error, setError] =
|
|
931
|
+
const [data, setData] = useState4(null);
|
|
932
|
+
const [loading, setLoading] = useState4(true);
|
|
933
|
+
const [error, setError] = useState4(null);
|
|
564
934
|
const paramKey = JSON.stringify(params ?? {});
|
|
565
|
-
const fetchData =
|
|
935
|
+
const fetchData = useCallback4(async () => {
|
|
566
936
|
setLoading(true);
|
|
567
937
|
setError(null);
|
|
568
938
|
try {
|
|
@@ -574,21 +944,21 @@ function useGames(params) {
|
|
|
574
944
|
setLoading(false);
|
|
575
945
|
}
|
|
576
946
|
}, [client, paramKey]);
|
|
577
|
-
|
|
947
|
+
useEffect4(() => {
|
|
578
948
|
fetchData();
|
|
579
949
|
}, [fetchData]);
|
|
580
950
|
return { data, loading, error, refetch: fetchData };
|
|
581
951
|
}
|
|
582
952
|
|
|
583
953
|
// src/hooks/useNetworkGames.ts
|
|
584
|
-
import { useState as
|
|
954
|
+
import { useState as useState5, useEffect as useEffect5, useCallback as useCallback5 } from "react";
|
|
585
955
|
function useNetworkGames(params) {
|
|
586
956
|
const { client } = useDubs();
|
|
587
|
-
const [data, setData] =
|
|
588
|
-
const [loading, setLoading] =
|
|
589
|
-
const [error, setError] =
|
|
957
|
+
const [data, setData] = useState5(null);
|
|
958
|
+
const [loading, setLoading] = useState5(true);
|
|
959
|
+
const [error, setError] = useState5(null);
|
|
590
960
|
const paramKey = JSON.stringify(params ?? {});
|
|
591
|
-
const fetchData =
|
|
961
|
+
const fetchData = useCallback5(async () => {
|
|
592
962
|
setLoading(true);
|
|
593
963
|
setError(null);
|
|
594
964
|
try {
|
|
@@ -600,78 +970,59 @@ function useNetworkGames(params) {
|
|
|
600
970
|
setLoading(false);
|
|
601
971
|
}
|
|
602
972
|
}, [client, paramKey]);
|
|
603
|
-
|
|
973
|
+
useEffect5(() => {
|
|
604
974
|
fetchData();
|
|
605
975
|
}, [fetchData]);
|
|
606
976
|
return { data, loading, error, refetch: fetchData };
|
|
607
977
|
}
|
|
608
978
|
|
|
609
979
|
// src/hooks/useCreateGame.ts
|
|
610
|
-
import { useState as
|
|
980
|
+
import { useState as useState6, useCallback as useCallback6 } from "react";
|
|
611
981
|
|
|
612
982
|
// src/utils/transaction.ts
|
|
613
983
|
import { Transaction as Transaction2 } from "@solana/web3.js";
|
|
614
|
-
async function signAndSendBase64Transaction(base64Tx, wallet
|
|
984
|
+
async function signAndSendBase64Transaction(base64Tx, wallet) {
|
|
615
985
|
if (!wallet.publicKey) throw new Error("Wallet not connected");
|
|
616
|
-
const
|
|
617
|
-
const
|
|
986
|
+
const binaryStr = atob(base64Tx);
|
|
987
|
+
const bytes = new Uint8Array(binaryStr.length);
|
|
988
|
+
for (let i = 0; i < binaryStr.length; i++) {
|
|
989
|
+
bytes[i] = binaryStr.charCodeAt(i);
|
|
990
|
+
}
|
|
991
|
+
const transaction = Transaction2.from(bytes);
|
|
618
992
|
if (wallet.signAndSendTransaction) {
|
|
619
993
|
return wallet.signAndSendTransaction(transaction);
|
|
620
994
|
}
|
|
621
|
-
|
|
622
|
-
const signature = await connection.sendRawTransaction(signed.serialize(), {
|
|
623
|
-
skipPreflight: true
|
|
624
|
-
});
|
|
625
|
-
return signature;
|
|
626
|
-
}
|
|
627
|
-
async function pollTransactionConfirmation(signature, connection, commitment = "confirmed", timeout = 6e4, interval = 1500) {
|
|
628
|
-
const start = Date.now();
|
|
629
|
-
const confirmationOrder = ["processed", "confirmed", "finalized"];
|
|
630
|
-
const targetIndex = confirmationOrder.indexOf(commitment);
|
|
631
|
-
while (Date.now() - start < timeout) {
|
|
632
|
-
const statuses = await connection.getSignatureStatuses([signature]);
|
|
633
|
-
const status = statuses?.value?.[0];
|
|
634
|
-
if (status?.err) {
|
|
635
|
-
throw new Error(`Transaction failed: ${JSON.stringify(status.err)}`);
|
|
636
|
-
}
|
|
637
|
-
if (status?.confirmationStatus) {
|
|
638
|
-
const currentIndex = confirmationOrder.indexOf(status.confirmationStatus);
|
|
639
|
-
if (currentIndex >= targetIndex) {
|
|
640
|
-
return;
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
644
|
-
}
|
|
645
|
-
throw new Error(`Transaction confirmation timeout after ${timeout}ms`);
|
|
995
|
+
throw new Error("Wallet does not support signAndSendTransaction");
|
|
646
996
|
}
|
|
647
997
|
|
|
648
998
|
// src/hooks/useCreateGame.ts
|
|
649
999
|
function useCreateGame() {
|
|
650
|
-
const { client, wallet
|
|
651
|
-
const [status, setStatus] =
|
|
652
|
-
const [error, setError] =
|
|
653
|
-
const [data, setData] =
|
|
654
|
-
const reset =
|
|
1000
|
+
const { client, wallet } = useDubs();
|
|
1001
|
+
const [status, setStatus] = useState6("idle");
|
|
1002
|
+
const [error, setError] = useState6(null);
|
|
1003
|
+
const [data, setData] = useState6(null);
|
|
1004
|
+
const reset = useCallback6(() => {
|
|
655
1005
|
setStatus("idle");
|
|
656
1006
|
setError(null);
|
|
657
1007
|
setData(null);
|
|
658
1008
|
}, []);
|
|
659
|
-
const execute =
|
|
1009
|
+
const execute = useCallback6(async (params) => {
|
|
660
1010
|
setStatus("building");
|
|
661
1011
|
setError(null);
|
|
662
1012
|
setData(null);
|
|
663
1013
|
try {
|
|
1014
|
+
console.log("[useCreateGame] Step 1: Building transaction...");
|
|
664
1015
|
const createResult = await client.createGame(params);
|
|
1016
|
+
console.log("[useCreateGame] Step 1 done:", { gameId: createResult.gameId, gameAddress: createResult.gameAddress });
|
|
665
1017
|
setStatus("signing");
|
|
1018
|
+
console.log("[useCreateGame] Step 2: Signing and sending...");
|
|
666
1019
|
const signature = await signAndSendBase64Transaction(
|
|
667
1020
|
createResult.transaction,
|
|
668
|
-
wallet
|
|
669
|
-
connection
|
|
1021
|
+
wallet
|
|
670
1022
|
);
|
|
1023
|
+
console.log("[useCreateGame] Step 2 done. Signature:", signature);
|
|
671
1024
|
setStatus("confirming");
|
|
672
|
-
|
|
673
|
-
setStatus("saving");
|
|
674
|
-
const explorerUrl = `https://solscan.io/tx/${signature}`;
|
|
1025
|
+
console.log("[useCreateGame] Step 3: Confirming with backend...");
|
|
675
1026
|
await client.confirmGame({
|
|
676
1027
|
gameId: createResult.gameId,
|
|
677
1028
|
playerWallet: params.playerWallet,
|
|
@@ -681,6 +1032,8 @@ function useCreateGame() {
|
|
|
681
1032
|
role: "creator",
|
|
682
1033
|
gameAddress: createResult.gameAddress
|
|
683
1034
|
});
|
|
1035
|
+
console.log("[useCreateGame] Step 3 done.");
|
|
1036
|
+
const explorerUrl = `https://solscan.io/tx/${signature}`;
|
|
684
1037
|
const result = {
|
|
685
1038
|
gameId: createResult.gameId,
|
|
686
1039
|
gameAddress: createResult.gameAddress,
|
|
@@ -689,46 +1042,48 @@ function useCreateGame() {
|
|
|
689
1042
|
};
|
|
690
1043
|
setData(result);
|
|
691
1044
|
setStatus("success");
|
|
1045
|
+
console.log("[useCreateGame] Complete!");
|
|
692
1046
|
return result;
|
|
693
1047
|
} catch (err) {
|
|
1048
|
+
console.error("[useCreateGame] FAILED:", err);
|
|
694
1049
|
const error2 = err instanceof Error ? err : new Error(String(err));
|
|
695
1050
|
setError(error2);
|
|
696
1051
|
setStatus("error");
|
|
697
1052
|
throw error2;
|
|
698
1053
|
}
|
|
699
|
-
}, [client, wallet
|
|
1054
|
+
}, [client, wallet]);
|
|
700
1055
|
return { execute, status, error, data, reset };
|
|
701
1056
|
}
|
|
702
1057
|
|
|
703
1058
|
// src/hooks/useJoinGame.ts
|
|
704
|
-
import { useState as
|
|
1059
|
+
import { useState as useState7, useCallback as useCallback7 } from "react";
|
|
705
1060
|
function useJoinGame() {
|
|
706
|
-
const { client, wallet
|
|
707
|
-
const [status, setStatus] =
|
|
708
|
-
const [error, setError] =
|
|
709
|
-
const [data, setData] =
|
|
710
|
-
const reset =
|
|
1061
|
+
const { client, wallet } = useDubs();
|
|
1062
|
+
const [status, setStatus] = useState7("idle");
|
|
1063
|
+
const [error, setError] = useState7(null);
|
|
1064
|
+
const [data, setData] = useState7(null);
|
|
1065
|
+
const reset = useCallback7(() => {
|
|
711
1066
|
setStatus("idle");
|
|
712
1067
|
setError(null);
|
|
713
1068
|
setData(null);
|
|
714
1069
|
}, []);
|
|
715
|
-
const execute =
|
|
1070
|
+
const execute = useCallback7(async (params) => {
|
|
716
1071
|
setStatus("building");
|
|
717
1072
|
setError(null);
|
|
718
1073
|
setData(null);
|
|
719
1074
|
try {
|
|
1075
|
+
console.log("[useJoinGame] Step 1: Building transaction...", { gameId: params.gameId, playerWallet: params.playerWallet, teamChoice: params.teamChoice, amount: params.amount });
|
|
720
1076
|
const joinResult = await client.joinGame(params);
|
|
1077
|
+
console.log("[useJoinGame] Step 1 done:", { gameId: joinResult.gameId, gameAddress: joinResult.gameAddress, hasTx: !!joinResult.transaction });
|
|
721
1078
|
setStatus("signing");
|
|
1079
|
+
console.log("[useJoinGame] Step 2: Signing and sending transaction...");
|
|
722
1080
|
const signature = await signAndSendBase64Transaction(
|
|
723
1081
|
joinResult.transaction,
|
|
724
|
-
wallet
|
|
725
|
-
connection
|
|
1082
|
+
wallet
|
|
726
1083
|
);
|
|
1084
|
+
console.log("[useJoinGame] Step 2 done. Signature:", signature);
|
|
727
1085
|
setStatus("confirming");
|
|
728
|
-
|
|
729
|
-
setStatus("saving");
|
|
730
|
-
const explorerUrl = `https://solscan.io/tx/${signature}`;
|
|
731
|
-
await client.confirmGame({
|
|
1086
|
+
const confirmParams = {
|
|
732
1087
|
gameId: params.gameId,
|
|
733
1088
|
playerWallet: params.playerWallet,
|
|
734
1089
|
signature,
|
|
@@ -736,7 +1091,11 @@ function useJoinGame() {
|
|
|
736
1091
|
wagerAmount: params.amount,
|
|
737
1092
|
role: "joiner",
|
|
738
1093
|
gameAddress: joinResult.gameAddress
|
|
739
|
-
}
|
|
1094
|
+
};
|
|
1095
|
+
console.log("[useJoinGame] Step 3: Confirming with backend...", confirmParams);
|
|
1096
|
+
await client.confirmGame(confirmParams);
|
|
1097
|
+
console.log("[useJoinGame] Step 3 done. Backend confirmed.");
|
|
1098
|
+
const explorerUrl = `https://solscan.io/tx/${signature}`;
|
|
740
1099
|
const result = {
|
|
741
1100
|
gameId: params.gameId,
|
|
742
1101
|
gameAddress: joinResult.gameAddress,
|
|
@@ -745,43 +1104,46 @@ function useJoinGame() {
|
|
|
745
1104
|
};
|
|
746
1105
|
setData(result);
|
|
747
1106
|
setStatus("success");
|
|
1107
|
+
console.log("[useJoinGame] Complete!");
|
|
748
1108
|
return result;
|
|
749
1109
|
} catch (err) {
|
|
1110
|
+
console.error("[useJoinGame] FAILED at status:", status, err);
|
|
750
1111
|
const error2 = err instanceof Error ? err : new Error(String(err));
|
|
751
1112
|
setError(error2);
|
|
752
1113
|
setStatus("error");
|
|
753
1114
|
throw error2;
|
|
754
1115
|
}
|
|
755
|
-
}, [client, wallet
|
|
1116
|
+
}, [client, wallet]);
|
|
756
1117
|
return { execute, status, error, data, reset };
|
|
757
1118
|
}
|
|
758
1119
|
|
|
759
1120
|
// src/hooks/useClaim.ts
|
|
760
|
-
import { useState as
|
|
1121
|
+
import { useState as useState8, useCallback as useCallback8 } from "react";
|
|
761
1122
|
function useClaim() {
|
|
762
|
-
const { client, wallet
|
|
763
|
-
const [status, setStatus] =
|
|
764
|
-
const [error, setError] =
|
|
765
|
-
const [data, setData] =
|
|
766
|
-
const reset =
|
|
1123
|
+
const { client, wallet } = useDubs();
|
|
1124
|
+
const [status, setStatus] = useState8("idle");
|
|
1125
|
+
const [error, setError] = useState8(null);
|
|
1126
|
+
const [data, setData] = useState8(null);
|
|
1127
|
+
const reset = useCallback8(() => {
|
|
767
1128
|
setStatus("idle");
|
|
768
1129
|
setError(null);
|
|
769
1130
|
setData(null);
|
|
770
1131
|
}, []);
|
|
771
|
-
const execute =
|
|
1132
|
+
const execute = useCallback8(async (params) => {
|
|
772
1133
|
setStatus("building");
|
|
773
1134
|
setError(null);
|
|
774
1135
|
setData(null);
|
|
775
1136
|
try {
|
|
1137
|
+
console.log("[useClaim] Step 1: Building claim transaction...");
|
|
776
1138
|
const claimResult = await client.buildClaimTransaction(params);
|
|
1139
|
+
console.log("[useClaim] Step 1 done.");
|
|
777
1140
|
setStatus("signing");
|
|
1141
|
+
console.log("[useClaim] Step 2: Signing and sending...");
|
|
778
1142
|
const signature = await signAndSendBase64Transaction(
|
|
779
1143
|
claimResult.transaction,
|
|
780
|
-
wallet
|
|
781
|
-
connection
|
|
1144
|
+
wallet
|
|
782
1145
|
);
|
|
783
|
-
|
|
784
|
-
await pollTransactionConfirmation(signature, connection);
|
|
1146
|
+
console.log("[useClaim] Step 2 done. Signature:", signature);
|
|
785
1147
|
const explorerUrl = `https://solscan.io/tx/${signature}`;
|
|
786
1148
|
const result = {
|
|
787
1149
|
gameId: params.gameId,
|
|
@@ -790,28 +1152,37 @@ function useClaim() {
|
|
|
790
1152
|
};
|
|
791
1153
|
setData(result);
|
|
792
1154
|
setStatus("success");
|
|
1155
|
+
console.log("[useClaim] Complete!");
|
|
793
1156
|
return result;
|
|
794
1157
|
} catch (err) {
|
|
1158
|
+
console.error("[useClaim] FAILED:", err);
|
|
795
1159
|
const error2 = err instanceof Error ? err : new Error(String(err));
|
|
796
1160
|
setError(error2);
|
|
797
1161
|
setStatus("error");
|
|
798
1162
|
throw error2;
|
|
799
1163
|
}
|
|
800
|
-
}, [client, wallet
|
|
1164
|
+
}, [client, wallet]);
|
|
801
1165
|
return { execute, status, error, data, reset };
|
|
802
1166
|
}
|
|
803
1167
|
|
|
804
1168
|
// src/hooks/useAuth.ts
|
|
805
|
-
import { useState as
|
|
1169
|
+
import { useState as useState9, useCallback as useCallback9, useRef as useRef2, useContext as useContext2 } from "react";
|
|
806
1170
|
import bs58 from "bs58";
|
|
1171
|
+
|
|
1172
|
+
// src/auth-context.ts
|
|
1173
|
+
import { createContext as createContext2 } from "react";
|
|
1174
|
+
var AuthContext = createContext2(null);
|
|
1175
|
+
|
|
1176
|
+
// src/hooks/useAuth.ts
|
|
807
1177
|
function useAuth() {
|
|
1178
|
+
const sharedAuth = useContext2(AuthContext);
|
|
808
1179
|
const { client, wallet } = useDubs();
|
|
809
|
-
const [status, setStatus] =
|
|
810
|
-
const [user, setUser] =
|
|
811
|
-
const [token, setToken] =
|
|
812
|
-
const [error, setError] =
|
|
813
|
-
const pendingAuth =
|
|
814
|
-
const reset =
|
|
1180
|
+
const [status, setStatus] = useState9("idle");
|
|
1181
|
+
const [user, setUser] = useState9(null);
|
|
1182
|
+
const [token, setToken] = useState9(null);
|
|
1183
|
+
const [error, setError] = useState9(null);
|
|
1184
|
+
const pendingAuth = useRef2(null);
|
|
1185
|
+
const reset = useCallback9(() => {
|
|
815
1186
|
setStatus("idle");
|
|
816
1187
|
setUser(null);
|
|
817
1188
|
setToken(null);
|
|
@@ -819,7 +1190,7 @@ function useAuth() {
|
|
|
819
1190
|
pendingAuth.current = null;
|
|
820
1191
|
client.setToken(null);
|
|
821
1192
|
}, [client]);
|
|
822
|
-
const authenticate =
|
|
1193
|
+
const authenticate = useCallback9(async () => {
|
|
823
1194
|
try {
|
|
824
1195
|
if (!wallet.publicKey) {
|
|
825
1196
|
throw new Error("Wallet not connected");
|
|
@@ -850,7 +1221,7 @@ function useAuth() {
|
|
|
850
1221
|
setStatus("error");
|
|
851
1222
|
}
|
|
852
1223
|
}, [client, wallet]);
|
|
853
|
-
const register =
|
|
1224
|
+
const register = useCallback9(async (username, referralCode, avatarUrl) => {
|
|
854
1225
|
try {
|
|
855
1226
|
const pending = pendingAuth.current;
|
|
856
1227
|
if (!pending) {
|
|
@@ -867,7 +1238,8 @@ function useAuth() {
|
|
|
867
1238
|
avatarUrl
|
|
868
1239
|
});
|
|
869
1240
|
pendingAuth.current = null;
|
|
870
|
-
|
|
1241
|
+
const user2 = avatarUrl && !result.user.avatar ? { ...result.user, avatar: avatarUrl } : result.user;
|
|
1242
|
+
setUser(user2);
|
|
871
1243
|
setToken(result.token);
|
|
872
1244
|
setStatus("authenticated");
|
|
873
1245
|
} catch (err) {
|
|
@@ -875,7 +1247,7 @@ function useAuth() {
|
|
|
875
1247
|
setStatus("error");
|
|
876
1248
|
}
|
|
877
1249
|
}, [client]);
|
|
878
|
-
const logout =
|
|
1250
|
+
const logout = useCallback9(async () => {
|
|
879
1251
|
try {
|
|
880
1252
|
await client.logout();
|
|
881
1253
|
} catch {
|
|
@@ -886,7 +1258,7 @@ function useAuth() {
|
|
|
886
1258
|
setError(null);
|
|
887
1259
|
pendingAuth.current = null;
|
|
888
1260
|
}, [client]);
|
|
889
|
-
const restoreSession =
|
|
1261
|
+
const restoreSession = useCallback9(async (savedToken) => {
|
|
890
1262
|
try {
|
|
891
1263
|
client.setToken(savedToken);
|
|
892
1264
|
const me = await client.getMe();
|
|
@@ -902,6 +1274,7 @@ function useAuth() {
|
|
|
902
1274
|
return false;
|
|
903
1275
|
}
|
|
904
1276
|
}, [client]);
|
|
1277
|
+
if (sharedAuth) return sharedAuth;
|
|
905
1278
|
return {
|
|
906
1279
|
status,
|
|
907
1280
|
user,
|
|
@@ -917,63 +1290,7 @@ function useAuth() {
|
|
|
917
1290
|
}
|
|
918
1291
|
|
|
919
1292
|
// src/ui/AuthGate.tsx
|
|
920
|
-
import
|
|
921
|
-
import {
|
|
922
|
-
View,
|
|
923
|
-
Text,
|
|
924
|
-
TextInput,
|
|
925
|
-
TouchableOpacity,
|
|
926
|
-
ActivityIndicator,
|
|
927
|
-
StyleSheet,
|
|
928
|
-
Keyboard,
|
|
929
|
-
KeyboardAvoidingView,
|
|
930
|
-
Platform,
|
|
931
|
-
Image,
|
|
932
|
-
Animated,
|
|
933
|
-
ScrollView
|
|
934
|
-
} from "react-native";
|
|
935
|
-
|
|
936
|
-
// src/ui/theme.ts
|
|
937
|
-
import { useColorScheme } from "react-native";
|
|
938
|
-
var dark = {
|
|
939
|
-
background: "#08080D",
|
|
940
|
-
surface: "#111118",
|
|
941
|
-
surfaceActive: "#7C3AED",
|
|
942
|
-
border: "#1A1A24",
|
|
943
|
-
text: "#FFFFFF",
|
|
944
|
-
textSecondary: "#E0E0EE",
|
|
945
|
-
textMuted: "#666666",
|
|
946
|
-
textDim: "#555555",
|
|
947
|
-
accent: "#7C3AED",
|
|
948
|
-
success: "#22C55E",
|
|
949
|
-
live: "#EF4444",
|
|
950
|
-
errorText: "#F87171",
|
|
951
|
-
errorBg: "#1A0A0A",
|
|
952
|
-
errorBorder: "#3A1515"
|
|
953
|
-
};
|
|
954
|
-
var light = {
|
|
955
|
-
background: "#FFFFFF",
|
|
956
|
-
surface: "#F0F0F5",
|
|
957
|
-
surfaceActive: "#7C3AED",
|
|
958
|
-
border: "#E0E0E8",
|
|
959
|
-
text: "#111118",
|
|
960
|
-
textSecondary: "#333333",
|
|
961
|
-
textMuted: "#888888",
|
|
962
|
-
textDim: "#999999",
|
|
963
|
-
accent: "#7C3AED",
|
|
964
|
-
success: "#16A34A",
|
|
965
|
-
live: "#DC2626",
|
|
966
|
-
errorText: "#DC2626",
|
|
967
|
-
errorBg: "#FEF2F2",
|
|
968
|
-
errorBorder: "#FECACA"
|
|
969
|
-
};
|
|
970
|
-
function useDubsTheme() {
|
|
971
|
-
const scheme = useColorScheme();
|
|
972
|
-
return scheme === "light" ? light : dark;
|
|
973
|
-
}
|
|
974
|
-
|
|
975
|
-
// src/ui/AuthGate.tsx
|
|
976
|
-
import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
1293
|
+
import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
977
1294
|
var DICEBEAR_STYLES = [
|
|
978
1295
|
"adventurer",
|
|
979
1296
|
"avataaars",
|
|
@@ -995,13 +1312,14 @@ function AuthGate({
|
|
|
995
1312
|
renderLoading,
|
|
996
1313
|
renderError,
|
|
997
1314
|
renderRegistration,
|
|
998
|
-
appName = "Dubs"
|
|
1315
|
+
appName = "Dubs",
|
|
1316
|
+
accentColor
|
|
999
1317
|
}) {
|
|
1000
1318
|
const { client } = useDubs();
|
|
1001
1319
|
const auth = useAuth();
|
|
1002
|
-
const [phase, setPhase] =
|
|
1003
|
-
const [registrationPhase, setRegistrationPhase] =
|
|
1004
|
-
|
|
1320
|
+
const [phase, setPhase] = useState10("init");
|
|
1321
|
+
const [registrationPhase, setRegistrationPhase] = useState10(false);
|
|
1322
|
+
useEffect6(() => {
|
|
1005
1323
|
let cancelled = false;
|
|
1006
1324
|
(async () => {
|
|
1007
1325
|
try {
|
|
@@ -1027,51 +1345,54 @@ function AuthGate({
|
|
|
1027
1345
|
cancelled = true;
|
|
1028
1346
|
};
|
|
1029
1347
|
}, []);
|
|
1030
|
-
|
|
1348
|
+
useEffect6(() => {
|
|
1031
1349
|
if (auth.status === "needsRegistration") setRegistrationPhase(true);
|
|
1032
1350
|
}, [auth.status]);
|
|
1033
|
-
|
|
1351
|
+
useEffect6(() => {
|
|
1034
1352
|
if (auth.token) onSaveToken(auth.token);
|
|
1035
1353
|
}, [auth.token]);
|
|
1036
|
-
const retry =
|
|
1354
|
+
const retry = useCallback10(() => {
|
|
1037
1355
|
setRegistrationPhase(false);
|
|
1038
1356
|
auth.reset();
|
|
1039
1357
|
auth.authenticate();
|
|
1040
1358
|
}, [auth]);
|
|
1041
|
-
const handleRegister =
|
|
1359
|
+
const handleRegister = useCallback10(
|
|
1042
1360
|
(username, referralCode, avatarUrl) => {
|
|
1043
1361
|
auth.register(username, referralCode, avatarUrl);
|
|
1044
1362
|
},
|
|
1045
1363
|
[auth]
|
|
1046
1364
|
);
|
|
1047
1365
|
if (phase === "init") {
|
|
1048
|
-
if (renderLoading) return /* @__PURE__ */
|
|
1049
|
-
return /* @__PURE__ */
|
|
1366
|
+
if (renderLoading) return /* @__PURE__ */ jsx3(Fragment2, { children: renderLoading("authenticating") });
|
|
1367
|
+
return /* @__PURE__ */ jsx3(DefaultLoadingScreen, { status: "authenticating", appName });
|
|
1368
|
+
}
|
|
1369
|
+
if (auth.status === "authenticated") {
|
|
1370
|
+
return /* @__PURE__ */ jsx3(AuthContext.Provider, { value: auth, children });
|
|
1050
1371
|
}
|
|
1051
|
-
if (auth.status === "authenticated") return /* @__PURE__ */ jsx2(Fragment, { children });
|
|
1052
1372
|
if (registrationPhase) {
|
|
1053
1373
|
const isRegistering = auth.status === "registering";
|
|
1054
1374
|
const regError = auth.status === "error" ? auth.error : null;
|
|
1055
1375
|
if (renderRegistration) {
|
|
1056
|
-
return /* @__PURE__ */
|
|
1376
|
+
return /* @__PURE__ */ jsx3(Fragment2, { children: renderRegistration({ onRegister: handleRegister, registering: isRegistering, error: regError, client }) });
|
|
1057
1377
|
}
|
|
1058
|
-
return /* @__PURE__ */
|
|
1378
|
+
return /* @__PURE__ */ jsx3(
|
|
1059
1379
|
DefaultRegistrationScreen,
|
|
1060
1380
|
{
|
|
1061
1381
|
onRegister: handleRegister,
|
|
1062
1382
|
registering: isRegistering,
|
|
1063
1383
|
error: regError,
|
|
1064
1384
|
client,
|
|
1065
|
-
appName
|
|
1385
|
+
appName,
|
|
1386
|
+
accentColor
|
|
1066
1387
|
}
|
|
1067
1388
|
);
|
|
1068
1389
|
}
|
|
1069
1390
|
if (auth.status === "error" && auth.error) {
|
|
1070
|
-
if (renderError) return /* @__PURE__ */
|
|
1071
|
-
return /* @__PURE__ */
|
|
1391
|
+
if (renderError) return /* @__PURE__ */ jsx3(Fragment2, { children: renderError(auth.error, retry) });
|
|
1392
|
+
return /* @__PURE__ */ jsx3(DefaultErrorScreen, { error: auth.error, onRetry: retry, appName });
|
|
1072
1393
|
}
|
|
1073
|
-
if (renderLoading) return /* @__PURE__ */
|
|
1074
|
-
return /* @__PURE__ */
|
|
1394
|
+
if (renderLoading) return /* @__PURE__ */ jsx3(Fragment2, { children: renderLoading(auth.status) });
|
|
1395
|
+
return /* @__PURE__ */ jsx3(DefaultLoadingScreen, { status: auth.status, appName });
|
|
1075
1396
|
}
|
|
1076
1397
|
function DefaultLoadingScreen({ status, appName }) {
|
|
1077
1398
|
const t = useDubsTheme();
|
|
@@ -1085,43 +1406,43 @@ function DefaultLoadingScreen({ status, appName }) {
|
|
|
1085
1406
|
authenticated: "Ready!",
|
|
1086
1407
|
error: "Something went wrong"
|
|
1087
1408
|
};
|
|
1088
|
-
return /* @__PURE__ */
|
|
1089
|
-
/* @__PURE__ */
|
|
1090
|
-
/* @__PURE__ */
|
|
1091
|
-
/* @__PURE__ */
|
|
1409
|
+
return /* @__PURE__ */ jsx3(View2, { style: [s.container, { backgroundColor: t.background }], children: /* @__PURE__ */ jsxs2(View2, { style: s.centerContent, children: [
|
|
1410
|
+
/* @__PURE__ */ jsxs2(View2, { style: s.brandingSection, children: [
|
|
1411
|
+
/* @__PURE__ */ jsx3(View2, { style: [s.logoCircle, { backgroundColor: t.accent }], children: /* @__PURE__ */ jsx3(Text2, { style: s.logoText, children: "D" }) }),
|
|
1412
|
+
/* @__PURE__ */ jsx3(Text2, { style: [s.appNameText, { color: t.text }], children: appName })
|
|
1092
1413
|
] }),
|
|
1093
|
-
/* @__PURE__ */
|
|
1094
|
-
/* @__PURE__ */
|
|
1095
|
-
/* @__PURE__ */
|
|
1414
|
+
/* @__PURE__ */ jsxs2(View2, { style: s.loadingSection, children: [
|
|
1415
|
+
/* @__PURE__ */ jsx3(ActivityIndicator2, { size: "large", color: t.accent }),
|
|
1416
|
+
/* @__PURE__ */ jsx3(Text2, { style: [s.statusText, { color: t.textMuted }], children: statusText[status] || "Loading..." })
|
|
1096
1417
|
] })
|
|
1097
1418
|
] }) });
|
|
1098
1419
|
}
|
|
1099
1420
|
function DefaultErrorScreen({ error, onRetry, appName }) {
|
|
1100
1421
|
const t = useDubsTheme();
|
|
1101
|
-
return /* @__PURE__ */
|
|
1102
|
-
/* @__PURE__ */
|
|
1103
|
-
/* @__PURE__ */
|
|
1104
|
-
/* @__PURE__ */
|
|
1422
|
+
return /* @__PURE__ */ jsx3(View2, { style: [s.container, { backgroundColor: t.background }], children: /* @__PURE__ */ jsxs2(View2, { style: s.spreadContent, children: [
|
|
1423
|
+
/* @__PURE__ */ jsxs2(View2, { style: s.brandingSection, children: [
|
|
1424
|
+
/* @__PURE__ */ jsx3(View2, { style: [s.logoCircle, { backgroundColor: t.accent }], children: /* @__PURE__ */ jsx3(Text2, { style: s.logoText, children: "D" }) }),
|
|
1425
|
+
/* @__PURE__ */ jsx3(Text2, { style: [s.appNameText, { color: t.text }], children: appName })
|
|
1105
1426
|
] }),
|
|
1106
|
-
/* @__PURE__ */
|
|
1107
|
-
/* @__PURE__ */
|
|
1108
|
-
/* @__PURE__ */
|
|
1427
|
+
/* @__PURE__ */ jsxs2(View2, { style: { gap: 16 }, children: [
|
|
1428
|
+
/* @__PURE__ */ jsx3(View2, { style: [s.errorBox, { backgroundColor: t.errorBg, borderColor: t.errorBorder }], children: /* @__PURE__ */ jsx3(Text2, { style: [s.errorText, { color: t.errorText }], children: error.message }) }),
|
|
1429
|
+
/* @__PURE__ */ jsx3(TouchableOpacity2, { style: [s.primaryBtn, { backgroundColor: t.accent }], onPress: onRetry, activeOpacity: 0.8, children: /* @__PURE__ */ jsx3(Text2, { style: s.primaryBtnText, children: "Try Again" }) })
|
|
1109
1430
|
] })
|
|
1110
1431
|
] }) });
|
|
1111
1432
|
}
|
|
1112
1433
|
function StepIndicator({ currentStep }) {
|
|
1113
1434
|
const t = useDubsTheme();
|
|
1114
1435
|
const steps = [0, 1, 2, 3];
|
|
1115
|
-
return /* @__PURE__ */
|
|
1116
|
-
i > 0 && /* @__PURE__ */
|
|
1117
|
-
/* @__PURE__ */
|
|
1118
|
-
|
|
1436
|
+
return /* @__PURE__ */ jsx3(View2, { style: s.stepRow, children: steps.map((i) => /* @__PURE__ */ jsxs2(React2.Fragment, { children: [
|
|
1437
|
+
i > 0 && /* @__PURE__ */ jsx3(View2, { style: [s.stepLine, { backgroundColor: i <= currentStep ? t.success : t.border }] }),
|
|
1438
|
+
/* @__PURE__ */ jsx3(
|
|
1439
|
+
View2,
|
|
1119
1440
|
{
|
|
1120
1441
|
style: [
|
|
1121
1442
|
s.stepCircle,
|
|
1122
1443
|
i < currentStep ? { backgroundColor: t.success } : i === currentStep ? { backgroundColor: t.accent } : { backgroundColor: "transparent", borderWidth: 2, borderColor: t.border }
|
|
1123
1444
|
],
|
|
1124
|
-
children: i < currentStep ? /* @__PURE__ */
|
|
1445
|
+
children: i < currentStep ? /* @__PURE__ */ jsx3(Text2, { style: s.stepCheck, children: "\u2713" }) : /* @__PURE__ */ jsx3(Text2, { style: [s.stepNum, { color: i === currentStep ? "#FFF" : t.textMuted }], children: i + 1 })
|
|
1125
1446
|
}
|
|
1126
1447
|
)
|
|
1127
1448
|
] }, i)) });
|
|
@@ -1131,22 +1452,24 @@ function DefaultRegistrationScreen({
|
|
|
1131
1452
|
registering,
|
|
1132
1453
|
error,
|
|
1133
1454
|
client,
|
|
1134
|
-
appName
|
|
1455
|
+
appName,
|
|
1456
|
+
accentColor
|
|
1135
1457
|
}) {
|
|
1136
1458
|
const t = useDubsTheme();
|
|
1137
|
-
const
|
|
1138
|
-
const [
|
|
1139
|
-
const [
|
|
1140
|
-
const [
|
|
1141
|
-
const [
|
|
1142
|
-
const [
|
|
1143
|
-
const [
|
|
1144
|
-
const [
|
|
1145
|
-
const
|
|
1146
|
-
const
|
|
1147
|
-
const
|
|
1459
|
+
const accent = accentColor || t.accent;
|
|
1460
|
+
const [step, setStep] = useState10(0);
|
|
1461
|
+
const [avatarSeed, setAvatarSeed] = useState10(generateSeed);
|
|
1462
|
+
const [avatarStyle, setAvatarStyle] = useState10("adventurer");
|
|
1463
|
+
const [showStyles, setShowStyles] = useState10(false);
|
|
1464
|
+
const [username, setUsername] = useState10("");
|
|
1465
|
+
const [referralCode, setReferralCode] = useState10("");
|
|
1466
|
+
const [checking, setChecking] = useState10(false);
|
|
1467
|
+
const [availability, setAvailability] = useState10(null);
|
|
1468
|
+
const debounceRef = useRef3(null);
|
|
1469
|
+
const fadeAnim = useRef3(new Animated.Value(1)).current;
|
|
1470
|
+
const slideAnim = useRef3(new Animated.Value(0)).current;
|
|
1148
1471
|
const avatarUrl = getAvatarUrl(avatarStyle, avatarSeed);
|
|
1149
|
-
|
|
1472
|
+
useEffect6(() => {
|
|
1150
1473
|
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
1151
1474
|
const trimmed = username.trim();
|
|
1152
1475
|
if (trimmed.length < 3) {
|
|
@@ -1169,7 +1492,7 @@ function DefaultRegistrationScreen({
|
|
|
1169
1492
|
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
1170
1493
|
};
|
|
1171
1494
|
}, [username, client]);
|
|
1172
|
-
const animateToStep =
|
|
1495
|
+
const animateToStep = useCallback10((newStep) => {
|
|
1173
1496
|
const dir = newStep > step ? 1 : -1;
|
|
1174
1497
|
Keyboard.dismiss();
|
|
1175
1498
|
Animated.parallel([
|
|
@@ -1189,76 +1512,76 @@ function DefaultRegistrationScreen({
|
|
|
1189
1512
|
Keyboard.dismiss();
|
|
1190
1513
|
onRegister(username.trim(), referralCode.trim() || void 0, avatarUrl);
|
|
1191
1514
|
};
|
|
1192
|
-
const renderAvatarStep = () => /* @__PURE__ */
|
|
1193
|
-
/* @__PURE__ */
|
|
1194
|
-
/* @__PURE__ */
|
|
1195
|
-
/* @__PURE__ */
|
|
1196
|
-
/* @__PURE__ */
|
|
1197
|
-
/* @__PURE__ */
|
|
1198
|
-
/* @__PURE__ */
|
|
1199
|
-
/* @__PURE__ */
|
|
1515
|
+
const renderAvatarStep = () => /* @__PURE__ */ jsxs2(View2, { style: s.stepContainer, children: [
|
|
1516
|
+
/* @__PURE__ */ jsxs2(View2, { style: s.stepTop, children: [
|
|
1517
|
+
/* @__PURE__ */ jsx3(Text2, { style: [s.title, { color: t.text }], children: "Choose Your Avatar" }),
|
|
1518
|
+
/* @__PURE__ */ jsx3(Text2, { style: [s.subtitle, { color: t.textMuted }], children: "Pick a look that represents you" }),
|
|
1519
|
+
/* @__PURE__ */ jsx3(StepIndicator, { currentStep: 0 }),
|
|
1520
|
+
/* @__PURE__ */ jsx3(View2, { style: s.avatarCenter, children: /* @__PURE__ */ jsxs2(View2, { style: [s.avatarFrame, { borderColor: accent }], children: [
|
|
1521
|
+
/* @__PURE__ */ jsx3(Image2, { source: { uri: avatarUrl }, style: s.avatarLarge }),
|
|
1522
|
+
/* @__PURE__ */ jsx3(View2, { style: [s.checkBadge, { backgroundColor: t.success }], children: /* @__PURE__ */ jsx3(Text2, { style: s.checkBadgeText, children: "\u2713" }) })
|
|
1200
1523
|
] }) }),
|
|
1201
|
-
/* @__PURE__ */
|
|
1202
|
-
/* @__PURE__ */
|
|
1203
|
-
|
|
1524
|
+
/* @__PURE__ */ jsxs2(View2, { style: s.avatarActions, children: [
|
|
1525
|
+
/* @__PURE__ */ jsx3(
|
|
1526
|
+
TouchableOpacity2,
|
|
1204
1527
|
{
|
|
1205
1528
|
style: [s.outlineBtn, { borderColor: t.border }],
|
|
1206
1529
|
onPress: () => setAvatarSeed(generateSeed()),
|
|
1207
1530
|
activeOpacity: 0.7,
|
|
1208
|
-
children: /* @__PURE__ */
|
|
1531
|
+
children: /* @__PURE__ */ jsx3(Text2, { style: [s.outlineBtnText, { color: t.text }], children: "\u21BB Shuffle" })
|
|
1209
1532
|
}
|
|
1210
1533
|
),
|
|
1211
|
-
/* @__PURE__ */
|
|
1212
|
-
|
|
1534
|
+
/* @__PURE__ */ jsx3(
|
|
1535
|
+
TouchableOpacity2,
|
|
1213
1536
|
{
|
|
1214
|
-
style: [s.outlineBtn, { borderColor:
|
|
1537
|
+
style: [s.outlineBtn, { borderColor: accent, backgroundColor: accent + "15" }],
|
|
1215
1538
|
onPress: () => setShowStyles(!showStyles),
|
|
1216
1539
|
activeOpacity: 0.7,
|
|
1217
|
-
children: /* @__PURE__ */
|
|
1540
|
+
children: /* @__PURE__ */ jsx3(Text2, { style: [s.outlineBtnText, { color: accent }], children: "\u263A Customize" })
|
|
1218
1541
|
}
|
|
1219
1542
|
)
|
|
1220
1543
|
] }),
|
|
1221
|
-
showStyles && /* @__PURE__ */
|
|
1222
|
-
|
|
1544
|
+
showStyles && /* @__PURE__ */ jsx3(ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, style: s.styleScroll, children: DICEBEAR_STYLES.map((st) => /* @__PURE__ */ jsx3(
|
|
1545
|
+
TouchableOpacity2,
|
|
1223
1546
|
{
|
|
1224
1547
|
onPress: () => setAvatarStyle(st),
|
|
1225
|
-
style: [s.styleThumbWrap, { borderColor: st === avatarStyle ?
|
|
1226
|
-
children: /* @__PURE__ */
|
|
1548
|
+
style: [s.styleThumbWrap, { borderColor: st === avatarStyle ? accent : t.border }],
|
|
1549
|
+
children: /* @__PURE__ */ jsx3(Image2, { source: { uri: getAvatarUrl(st, avatarSeed, 80) }, style: s.styleThumb })
|
|
1227
1550
|
},
|
|
1228
1551
|
st
|
|
1229
1552
|
)) })
|
|
1230
1553
|
] }),
|
|
1231
|
-
/* @__PURE__ */
|
|
1232
|
-
|
|
1554
|
+
/* @__PURE__ */ jsx3(View2, { style: s.bottomRow, children: /* @__PURE__ */ jsx3(
|
|
1555
|
+
TouchableOpacity2,
|
|
1233
1556
|
{
|
|
1234
|
-
style: [s.primaryBtn, { backgroundColor:
|
|
1557
|
+
style: [s.primaryBtn, { backgroundColor: accent, flex: 1 }],
|
|
1235
1558
|
onPress: () => animateToStep(1),
|
|
1236
1559
|
activeOpacity: 0.8,
|
|
1237
|
-
children: /* @__PURE__ */
|
|
1560
|
+
children: /* @__PURE__ */ jsx3(Text2, { style: s.primaryBtnText, children: "Continue \u203A" })
|
|
1238
1561
|
}
|
|
1239
1562
|
) })
|
|
1240
1563
|
] });
|
|
1241
|
-
const renderUsernameStep = () => /* @__PURE__ */
|
|
1242
|
-
/* @__PURE__ */
|
|
1243
|
-
/* @__PURE__ */
|
|
1244
|
-
/* @__PURE__ */
|
|
1245
|
-
/* @__PURE__ */
|
|
1564
|
+
const renderUsernameStep = () => /* @__PURE__ */ jsxs2(View2, { style: s.stepContainer, children: [
|
|
1565
|
+
/* @__PURE__ */ jsxs2(View2, { style: s.stepTop, children: [
|
|
1566
|
+
/* @__PURE__ */ jsxs2(View2, { style: s.headerRow, children: [
|
|
1567
|
+
/* @__PURE__ */ jsx3(TouchableOpacity2, { onPress: () => animateToStep(0), hitSlop: { top: 12, bottom: 12, left: 12, right: 12 }, children: /* @__PURE__ */ jsx3(Text2, { style: [s.backChevron, { color: t.text }], children: "\u2039" }) }),
|
|
1568
|
+
/* @__PURE__ */ jsx3(Text2, { style: [s.titleInline, { color: t.text }], children: "Pick a Username" })
|
|
1246
1569
|
] }),
|
|
1247
|
-
/* @__PURE__ */
|
|
1248
|
-
/* @__PURE__ */
|
|
1249
|
-
/* @__PURE__ */
|
|
1250
|
-
/* @__PURE__ */
|
|
1251
|
-
/* @__PURE__ */
|
|
1570
|
+
/* @__PURE__ */ jsx3(Text2, { style: [s.subtitle, { color: t.textMuted }], children: "This is how others will see you" }),
|
|
1571
|
+
/* @__PURE__ */ jsx3(StepIndicator, { currentStep: 1 }),
|
|
1572
|
+
/* @__PURE__ */ jsx3(View2, { style: s.avatarCenter, children: /* @__PURE__ */ jsxs2(View2, { style: [s.avatarFrameSmall, { borderColor: accent }], children: [
|
|
1573
|
+
/* @__PURE__ */ jsx3(Image2, { source: { uri: avatarUrl }, style: s.avatarSmall }),
|
|
1574
|
+
/* @__PURE__ */ jsx3(View2, { style: [s.checkBadgeSm, { backgroundColor: t.success }], children: /* @__PURE__ */ jsx3(Text2, { style: s.checkBadgeTextSm, children: "\u2713" }) })
|
|
1252
1575
|
] }) }),
|
|
1253
|
-
/* @__PURE__ */
|
|
1254
|
-
/* @__PURE__ */
|
|
1576
|
+
/* @__PURE__ */ jsxs2(View2, { style: s.inputGroup, children: [
|
|
1577
|
+
/* @__PURE__ */ jsxs2(Text2, { style: [s.inputLabel, { color: t.text }], children: [
|
|
1255
1578
|
"Username ",
|
|
1256
|
-
/* @__PURE__ */
|
|
1579
|
+
/* @__PURE__ */ jsx3(Text2, { style: { color: t.errorText }, children: "*" })
|
|
1257
1580
|
] }),
|
|
1258
|
-
/* @__PURE__ */
|
|
1581
|
+
/* @__PURE__ */ jsx3(
|
|
1259
1582
|
TextInput,
|
|
1260
1583
|
{
|
|
1261
|
-
style: [s.input, { backgroundColor: t.surface, color: t.text, borderColor:
|
|
1584
|
+
style: [s.input, { backgroundColor: t.surface, color: t.text, borderColor: accent }],
|
|
1262
1585
|
placeholder: "Enter username",
|
|
1263
1586
|
placeholderTextColor: t.textDim,
|
|
1264
1587
|
value: username,
|
|
@@ -1268,62 +1591,62 @@ function DefaultRegistrationScreen({
|
|
|
1268
1591
|
autoFocus: true
|
|
1269
1592
|
}
|
|
1270
1593
|
),
|
|
1271
|
-
checking ? /* @__PURE__ */
|
|
1594
|
+
checking ? /* @__PURE__ */ jsx3(Text2, { style: [s.hint, { color: t.textDim }], children: "Checking..." }) : availability ? /* @__PURE__ */ jsx3(Text2, { 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__ */ jsx3(Text2, { style: [s.hint, { color: t.textDim }], children: "At least 3 characters" }) : null
|
|
1272
1595
|
] })
|
|
1273
1596
|
] }),
|
|
1274
|
-
/* @__PURE__ */
|
|
1275
|
-
/* @__PURE__ */
|
|
1276
|
-
|
|
1597
|
+
/* @__PURE__ */ jsxs2(View2, { style: s.bottomRow, children: [
|
|
1598
|
+
/* @__PURE__ */ jsx3(
|
|
1599
|
+
TouchableOpacity2,
|
|
1277
1600
|
{
|
|
1278
1601
|
style: [s.secondaryBtn, { borderColor: t.border }],
|
|
1279
1602
|
onPress: () => animateToStep(0),
|
|
1280
1603
|
activeOpacity: 0.7,
|
|
1281
|
-
children: /* @__PURE__ */
|
|
1604
|
+
children: /* @__PURE__ */ jsx3(Text2, { style: [s.secondaryBtnText, { color: t.text }], children: "\u2039 Back" })
|
|
1282
1605
|
}
|
|
1283
1606
|
),
|
|
1284
|
-
/* @__PURE__ */
|
|
1285
|
-
|
|
1607
|
+
/* @__PURE__ */ jsx3(
|
|
1608
|
+
TouchableOpacity2,
|
|
1286
1609
|
{
|
|
1287
|
-
style: [s.primaryBtn, { backgroundColor:
|
|
1610
|
+
style: [s.primaryBtn, { backgroundColor: accent, flex: 1, opacity: canContinueUsername ? 1 : 0.4 }],
|
|
1288
1611
|
onPress: () => animateToStep(2),
|
|
1289
1612
|
disabled: !canContinueUsername,
|
|
1290
1613
|
activeOpacity: 0.8,
|
|
1291
|
-
children: /* @__PURE__ */
|
|
1614
|
+
children: /* @__PURE__ */ jsx3(Text2, { style: s.primaryBtnText, children: "Continue \u203A" })
|
|
1292
1615
|
}
|
|
1293
1616
|
)
|
|
1294
1617
|
] })
|
|
1295
1618
|
] });
|
|
1296
|
-
const renderReferralStep = () => /* @__PURE__ */
|
|
1297
|
-
/* @__PURE__ */
|
|
1298
|
-
/* @__PURE__ */
|
|
1299
|
-
/* @__PURE__ */
|
|
1300
|
-
/* @__PURE__ */
|
|
1619
|
+
const renderReferralStep = () => /* @__PURE__ */ jsxs2(View2, { style: s.stepContainer, children: [
|
|
1620
|
+
/* @__PURE__ */ jsxs2(View2, { style: s.stepTop, children: [
|
|
1621
|
+
/* @__PURE__ */ jsxs2(View2, { style: s.headerRow, children: [
|
|
1622
|
+
/* @__PURE__ */ jsx3(TouchableOpacity2, { onPress: () => animateToStep(1), hitSlop: { top: 12, bottom: 12, left: 12, right: 12 }, children: /* @__PURE__ */ jsx3(Text2, { style: [s.backChevron, { color: t.text }], children: "\u2039" }) }),
|
|
1623
|
+
/* @__PURE__ */ jsx3(Text2, { style: [s.titleInline, { color: t.text }], children: "Almost There!" })
|
|
1301
1624
|
] }),
|
|
1302
|
-
/* @__PURE__ */
|
|
1303
|
-
/* @__PURE__ */
|
|
1304
|
-
/* @__PURE__ */
|
|
1305
|
-
/* @__PURE__ */
|
|
1306
|
-
/* @__PURE__ */
|
|
1307
|
-
/* @__PURE__ */
|
|
1308
|
-
/* @__PURE__ */
|
|
1309
|
-
/* @__PURE__ */
|
|
1625
|
+
/* @__PURE__ */ jsx3(Text2, { style: [s.subtitle, { color: t.textMuted }], children: "Got a referral code? (optional)" }),
|
|
1626
|
+
/* @__PURE__ */ jsx3(StepIndicator, { currentStep: 2 }),
|
|
1627
|
+
/* @__PURE__ */ jsxs2(View2, { style: [s.profileCard, { borderColor: t.border }], children: [
|
|
1628
|
+
/* @__PURE__ */ jsx3(Text2, { style: [s.profileLabel, { color: t.textMuted }], children: "Your Profile" }),
|
|
1629
|
+
/* @__PURE__ */ jsxs2(View2, { style: s.profileRow, children: [
|
|
1630
|
+
/* @__PURE__ */ jsx3(Image2, { source: { uri: avatarUrl }, style: s.profileAvatar }),
|
|
1631
|
+
/* @__PURE__ */ jsxs2(View2, { style: { gap: 4 }, children: [
|
|
1632
|
+
/* @__PURE__ */ jsxs2(Text2, { style: [s.profileUsername, { color: t.text }], children: [
|
|
1310
1633
|
"@",
|
|
1311
1634
|
username
|
|
1312
1635
|
] }),
|
|
1313
|
-
/* @__PURE__ */
|
|
1636
|
+
/* @__PURE__ */ jsxs2(Text2, { style: [s.profileReady, { color: t.success }], children: [
|
|
1314
1637
|
"\u2713",
|
|
1315
1638
|
" Ready to go!"
|
|
1316
1639
|
] })
|
|
1317
1640
|
] })
|
|
1318
1641
|
] })
|
|
1319
1642
|
] }),
|
|
1320
|
-
error ? /* @__PURE__ */
|
|
1321
|
-
/* @__PURE__ */
|
|
1322
|
-
/* @__PURE__ */
|
|
1643
|
+
error ? /* @__PURE__ */ jsx3(View2, { style: [s.errorBox, { backgroundColor: t.errorBg, borderColor: t.errorBorder }], children: /* @__PURE__ */ jsx3(Text2, { style: [s.errorText, { color: t.errorText }], children: error.message }) }) : null,
|
|
1644
|
+
/* @__PURE__ */ jsxs2(View2, { style: s.inputGroup, children: [
|
|
1645
|
+
/* @__PURE__ */ jsxs2(Text2, { style: [s.inputLabel, { color: t.text }], children: [
|
|
1323
1646
|
"Referral Code ",
|
|
1324
|
-
/* @__PURE__ */
|
|
1647
|
+
/* @__PURE__ */ jsx3(Text2, { style: { color: t.textMuted }, children: "(optional)" })
|
|
1325
1648
|
] }),
|
|
1326
|
-
/* @__PURE__ */
|
|
1649
|
+
/* @__PURE__ */ jsx3(
|
|
1327
1650
|
TextInput,
|
|
1328
1651
|
{
|
|
1329
1652
|
style: [s.input, { backgroundColor: t.surface, color: t.text, borderColor: t.border }],
|
|
@@ -1336,31 +1659,31 @@ function DefaultRegistrationScreen({
|
|
|
1336
1659
|
editable: !registering
|
|
1337
1660
|
}
|
|
1338
1661
|
),
|
|
1339
|
-
/* @__PURE__ */
|
|
1662
|
+
/* @__PURE__ */ jsxs2(Text2, { style: [s.hint, { color: t.textMuted }], children: [
|
|
1340
1663
|
"\u{1F381}",
|
|
1341
1664
|
" If a friend invited you, enter their code to give them credit!"
|
|
1342
1665
|
] })
|
|
1343
1666
|
] })
|
|
1344
1667
|
] }),
|
|
1345
|
-
/* @__PURE__ */
|
|
1346
|
-
/* @__PURE__ */
|
|
1347
|
-
|
|
1668
|
+
/* @__PURE__ */ jsxs2(View2, { style: s.bottomRow, children: [
|
|
1669
|
+
/* @__PURE__ */ jsx3(
|
|
1670
|
+
TouchableOpacity2,
|
|
1348
1671
|
{
|
|
1349
1672
|
style: [s.secondaryBtn, { borderColor: t.border }],
|
|
1350
1673
|
onPress: () => animateToStep(1),
|
|
1351
1674
|
disabled: registering,
|
|
1352
1675
|
activeOpacity: 0.7,
|
|
1353
|
-
children: /* @__PURE__ */
|
|
1676
|
+
children: /* @__PURE__ */ jsx3(Text2, { style: [s.secondaryBtnText, { color: t.text }], children: "\u2039 Back" })
|
|
1354
1677
|
}
|
|
1355
1678
|
),
|
|
1356
|
-
/* @__PURE__ */
|
|
1357
|
-
|
|
1679
|
+
/* @__PURE__ */ jsx3(
|
|
1680
|
+
TouchableOpacity2,
|
|
1358
1681
|
{
|
|
1359
|
-
style: [s.primaryBtn, { backgroundColor:
|
|
1682
|
+
style: [s.primaryBtn, { backgroundColor: accent, flex: 1, opacity: registering ? 0.7 : 1 }],
|
|
1360
1683
|
onPress: handleSubmit,
|
|
1361
1684
|
disabled: registering,
|
|
1362
1685
|
activeOpacity: 0.8,
|
|
1363
|
-
children: registering ? /* @__PURE__ */
|
|
1686
|
+
children: registering ? /* @__PURE__ */ jsx3(ActivityIndicator2, { color: "#FFFFFF", size: "small" }) : /* @__PURE__ */ jsx3(Text2, { style: s.primaryBtnText, children: "Create Account" })
|
|
1364
1687
|
}
|
|
1365
1688
|
)
|
|
1366
1689
|
] })
|
|
@@ -1377,18 +1700,18 @@ function DefaultRegistrationScreen({
|
|
|
1377
1700
|
return null;
|
|
1378
1701
|
}
|
|
1379
1702
|
};
|
|
1380
|
-
return /* @__PURE__ */
|
|
1703
|
+
return /* @__PURE__ */ jsx3(
|
|
1381
1704
|
KeyboardAvoidingView,
|
|
1382
1705
|
{
|
|
1383
1706
|
style: [s.container, { backgroundColor: t.background }],
|
|
1384
1707
|
behavior: Platform.OS === "ios" ? "padding" : void 0,
|
|
1385
|
-
children: /* @__PURE__ */
|
|
1708
|
+
children: /* @__PURE__ */ jsx3(
|
|
1386
1709
|
ScrollView,
|
|
1387
1710
|
{
|
|
1388
1711
|
contentContainerStyle: { flexGrow: 1 },
|
|
1389
1712
|
keyboardShouldPersistTaps: "handled",
|
|
1390
1713
|
bounces: false,
|
|
1391
|
-
children: /* @__PURE__ */
|
|
1714
|
+
children: /* @__PURE__ */ jsx3(
|
|
1392
1715
|
Animated.View,
|
|
1393
1716
|
{
|
|
1394
1717
|
style: [
|
|
@@ -1403,7 +1726,7 @@ function DefaultRegistrationScreen({
|
|
|
1403
1726
|
}
|
|
1404
1727
|
);
|
|
1405
1728
|
}
|
|
1406
|
-
var s =
|
|
1729
|
+
var s = StyleSheet2.create({
|
|
1407
1730
|
container: { flex: 1 },
|
|
1408
1731
|
// Loading / Error
|
|
1409
1732
|
centerContent: { flex: 1, justifyContent: "center", alignItems: "center", paddingHorizontal: 32, gap: 48 },
|
|
@@ -1466,125 +1789,179 @@ var s = StyleSheet.create({
|
|
|
1466
1789
|
secondaryBtnText: { fontSize: 16, fontWeight: "600" }
|
|
1467
1790
|
});
|
|
1468
1791
|
|
|
1469
|
-
// src/
|
|
1470
|
-
import {
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1792
|
+
// src/provider.tsx
|
|
1793
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
1794
|
+
var DubsContext = createContext3(null);
|
|
1795
|
+
function DubsProvider({
|
|
1796
|
+
apiKey,
|
|
1797
|
+
children,
|
|
1798
|
+
appName = "Dubs",
|
|
1799
|
+
network = "mainnet-beta",
|
|
1800
|
+
wallet: externalWallet,
|
|
1801
|
+
tokenStorage,
|
|
1802
|
+
baseUrl: baseUrlOverride,
|
|
1803
|
+
rpcUrl: rpcUrlOverride,
|
|
1804
|
+
renderConnectScreen,
|
|
1805
|
+
renderLoading,
|
|
1806
|
+
renderError,
|
|
1807
|
+
renderRegistration,
|
|
1808
|
+
managed = true
|
|
1483
1809
|
}) {
|
|
1484
|
-
const
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1810
|
+
const config = NETWORK_CONFIG[network];
|
|
1811
|
+
const baseUrl = baseUrlOverride || config.baseUrl;
|
|
1812
|
+
const rpcUrl = rpcUrlOverride || config.rpcUrl;
|
|
1813
|
+
const cluster = config.cluster;
|
|
1814
|
+
const client = useMemo(() => new DubsClient({ apiKey, baseUrl }), [apiKey, baseUrl]);
|
|
1815
|
+
const connection = useMemo(() => new Connection(rpcUrl, { commitment: "confirmed" }), [rpcUrl]);
|
|
1816
|
+
const storage = useMemo(() => tokenStorage || createSecureStoreStorage(), [tokenStorage]);
|
|
1817
|
+
const [uiConfig, setUiConfig] = useState11({});
|
|
1818
|
+
useEffect7(() => {
|
|
1819
|
+
client.getAppConfig().then(setUiConfig).catch(() => {
|
|
1820
|
+
});
|
|
1821
|
+
}, [client]);
|
|
1822
|
+
if (externalWallet) {
|
|
1823
|
+
return /* @__PURE__ */ jsx4(
|
|
1824
|
+
ExternalWalletProvider,
|
|
1825
|
+
{
|
|
1826
|
+
client,
|
|
1827
|
+
connection,
|
|
1828
|
+
wallet: externalWallet,
|
|
1829
|
+
appName: uiConfig.appName || appName,
|
|
1830
|
+
network,
|
|
1831
|
+
storage,
|
|
1832
|
+
managed,
|
|
1833
|
+
renderLoading,
|
|
1834
|
+
renderError,
|
|
1835
|
+
renderRegistration,
|
|
1836
|
+
accentColor: uiConfig.accentColor,
|
|
1837
|
+
children
|
|
1838
|
+
}
|
|
1839
|
+
);
|
|
1840
|
+
}
|
|
1841
|
+
return /* @__PURE__ */ jsx4(
|
|
1842
|
+
ManagedWalletProvider,
|
|
1843
|
+
{
|
|
1844
|
+
appName: uiConfig.appName || appName,
|
|
1845
|
+
cluster,
|
|
1846
|
+
storage,
|
|
1847
|
+
renderConnectScreen,
|
|
1848
|
+
accentColor: uiConfig.accentColor,
|
|
1849
|
+
appIcon: uiConfig.appIcon,
|
|
1850
|
+
tagline: uiConfig.tagline,
|
|
1851
|
+
children: (adapter) => /* @__PURE__ */ jsx4(
|
|
1852
|
+
ManagedInner,
|
|
1504
1853
|
{
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1854
|
+
client,
|
|
1855
|
+
connection,
|
|
1856
|
+
wallet: adapter,
|
|
1857
|
+
appName: uiConfig.appName || appName,
|
|
1858
|
+
network,
|
|
1859
|
+
storage,
|
|
1860
|
+
renderLoading,
|
|
1861
|
+
renderError,
|
|
1862
|
+
renderRegistration,
|
|
1863
|
+
accentColor: uiConfig.accentColor,
|
|
1864
|
+
children
|
|
1510
1865
|
}
|
|
1511
|
-
)
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
] }) });
|
|
1866
|
+
)
|
|
1867
|
+
}
|
|
1868
|
+
);
|
|
1515
1869
|
}
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
}
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1870
|
+
function ManagedInner({
|
|
1871
|
+
client,
|
|
1872
|
+
connection,
|
|
1873
|
+
wallet,
|
|
1874
|
+
appName,
|
|
1875
|
+
network,
|
|
1876
|
+
storage,
|
|
1877
|
+
renderLoading,
|
|
1878
|
+
renderError,
|
|
1879
|
+
renderRegistration,
|
|
1880
|
+
accentColor,
|
|
1881
|
+
children
|
|
1882
|
+
}) {
|
|
1883
|
+
const managedDisconnect = useDisconnect();
|
|
1884
|
+
const disconnect = useCallback11(async () => {
|
|
1885
|
+
client.setToken(null);
|
|
1886
|
+
await managedDisconnect?.();
|
|
1887
|
+
}, [client, managedDisconnect]);
|
|
1888
|
+
const value = useMemo(
|
|
1889
|
+
() => ({ client, wallet, connection, appName, network, disconnect }),
|
|
1890
|
+
[client, wallet, connection, appName, network, disconnect]
|
|
1891
|
+
);
|
|
1892
|
+
return /* @__PURE__ */ jsx4(DubsContext.Provider, { value, children: /* @__PURE__ */ jsx4(
|
|
1893
|
+
AuthGate,
|
|
1894
|
+
{
|
|
1895
|
+
onSaveToken: (token) => {
|
|
1896
|
+
if (token) return storage.setItem(STORAGE_KEYS.JWT_TOKEN, token);
|
|
1897
|
+
return storage.deleteItem(STORAGE_KEYS.JWT_TOKEN);
|
|
1898
|
+
},
|
|
1899
|
+
onLoadToken: () => storage.getItem(STORAGE_KEYS.JWT_TOKEN),
|
|
1900
|
+
renderLoading,
|
|
1901
|
+
renderError,
|
|
1902
|
+
renderRegistration,
|
|
1903
|
+
appName,
|
|
1904
|
+
accentColor,
|
|
1905
|
+
children
|
|
1906
|
+
}
|
|
1907
|
+
) });
|
|
1908
|
+
}
|
|
1909
|
+
function ExternalWalletProvider({
|
|
1910
|
+
client,
|
|
1911
|
+
connection,
|
|
1912
|
+
wallet,
|
|
1913
|
+
appName,
|
|
1914
|
+
network,
|
|
1915
|
+
storage,
|
|
1916
|
+
managed,
|
|
1917
|
+
renderLoading,
|
|
1918
|
+
renderError,
|
|
1919
|
+
renderRegistration,
|
|
1920
|
+
accentColor,
|
|
1921
|
+
children
|
|
1922
|
+
}) {
|
|
1923
|
+
const disconnect = useCallback11(async () => {
|
|
1924
|
+
client.setToken(null);
|
|
1925
|
+
await storage.deleteItem(STORAGE_KEYS.JWT_TOKEN).catch(() => {
|
|
1926
|
+
});
|
|
1927
|
+
await wallet.disconnect?.();
|
|
1928
|
+
}, [client, storage, wallet]);
|
|
1929
|
+
const value = useMemo(
|
|
1930
|
+
() => ({ client, wallet, connection, appName, network, disconnect }),
|
|
1931
|
+
[client, wallet, connection, appName, network, disconnect]
|
|
1932
|
+
);
|
|
1933
|
+
if (!managed) {
|
|
1934
|
+
return /* @__PURE__ */ jsx4(DubsContext.Provider, { value, children });
|
|
1581
1935
|
}
|
|
1582
|
-
|
|
1936
|
+
return /* @__PURE__ */ jsx4(DubsContext.Provider, { value, children: /* @__PURE__ */ jsx4(
|
|
1937
|
+
AuthGate,
|
|
1938
|
+
{
|
|
1939
|
+
onSaveToken: (token) => {
|
|
1940
|
+
if (token) return storage.setItem(STORAGE_KEYS.JWT_TOKEN, token);
|
|
1941
|
+
return storage.deleteItem(STORAGE_KEYS.JWT_TOKEN);
|
|
1942
|
+
},
|
|
1943
|
+
onLoadToken: () => storage.getItem(STORAGE_KEYS.JWT_TOKEN),
|
|
1944
|
+
renderLoading,
|
|
1945
|
+
renderError,
|
|
1946
|
+
renderRegistration,
|
|
1947
|
+
appName,
|
|
1948
|
+
accentColor,
|
|
1949
|
+
children
|
|
1950
|
+
}
|
|
1951
|
+
) });
|
|
1952
|
+
}
|
|
1953
|
+
function useDubs() {
|
|
1954
|
+
const ctx = useContext3(DubsContext);
|
|
1955
|
+
if (!ctx) {
|
|
1956
|
+
throw new Error("useDubs must be used within a <DubsProvider>");
|
|
1957
|
+
}
|
|
1958
|
+
return ctx;
|
|
1959
|
+
}
|
|
1583
1960
|
|
|
1584
1961
|
// src/ui/UserProfileCard.tsx
|
|
1585
1962
|
import { useMemo as useMemo2 } from "react";
|
|
1586
|
-
import { View as View3, Text as Text3, Image as
|
|
1587
|
-
import { jsx as
|
|
1963
|
+
import { View as View3, Text as Text3, Image as Image3, StyleSheet as StyleSheet3 } from "react-native";
|
|
1964
|
+
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1588
1965
|
function truncateAddress(address, chars = 4) {
|
|
1589
1966
|
if (address.length <= chars * 2 + 3) return address;
|
|
1590
1967
|
return `${address.slice(0, chars)}...${address.slice(-chars)}`;
|
|
@@ -1607,11 +1984,11 @@ function UserProfileCard({
|
|
|
1607
1984
|
[avatarUrl, walletAddress]
|
|
1608
1985
|
);
|
|
1609
1986
|
return /* @__PURE__ */ jsxs3(View3, { style: [styles2.card, { backgroundColor: t.surface, borderColor: t.border }], children: [
|
|
1610
|
-
/* @__PURE__ */
|
|
1987
|
+
/* @__PURE__ */ jsx5(Image3, { source: { uri: imageUri }, style: styles2.avatar }),
|
|
1611
1988
|
/* @__PURE__ */ jsxs3(View3, { style: styles2.info, children: [
|
|
1612
|
-
username ? /* @__PURE__ */
|
|
1613
|
-
/* @__PURE__ */
|
|
1614
|
-
memberSince ? /* @__PURE__ */
|
|
1989
|
+
username ? /* @__PURE__ */ jsx5(Text3, { style: [styles2.username, { color: t.text }], children: username }) : null,
|
|
1990
|
+
/* @__PURE__ */ jsx5(Text3, { style: [styles2.address, { color: t.textMuted }], children: truncateAddress(walletAddress) }),
|
|
1991
|
+
memberSince ? /* @__PURE__ */ jsx5(Text3, { style: [styles2.memberSince, { color: t.textDim }], children: formatMemberSince(memberSince) }) : null
|
|
1615
1992
|
] })
|
|
1616
1993
|
] });
|
|
1617
1994
|
}
|
|
@@ -1657,7 +2034,7 @@ import {
|
|
|
1657
2034
|
ActivityIndicator as ActivityIndicator3,
|
|
1658
2035
|
StyleSheet as StyleSheet4
|
|
1659
2036
|
} from "react-native";
|
|
1660
|
-
import { Fragment as
|
|
2037
|
+
import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1661
2038
|
function truncateAddress2(address, chars = 4) {
|
|
1662
2039
|
if (address.length <= chars * 2 + 3) return address;
|
|
1663
2040
|
return `${address.slice(0, chars)}...${address.slice(-chars)}`;
|
|
@@ -1680,7 +2057,7 @@ function SettingsSheet({
|
|
|
1680
2057
|
style: [styles3.container, { backgroundColor: t.background }],
|
|
1681
2058
|
contentContainerStyle: styles3.content,
|
|
1682
2059
|
children: [
|
|
1683
|
-
/* @__PURE__ */
|
|
2060
|
+
/* @__PURE__ */ jsx6(
|
|
1684
2061
|
UserProfileCard,
|
|
1685
2062
|
{
|
|
1686
2063
|
walletAddress,
|
|
@@ -1698,15 +2075,15 @@ function SettingsSheet({
|
|
|
1698
2075
|
activeOpacity: 0.7,
|
|
1699
2076
|
children: [
|
|
1700
2077
|
/* @__PURE__ */ jsxs4(View4, { style: styles3.actionRowLeft, children: [
|
|
1701
|
-
/* @__PURE__ */
|
|
1702
|
-
/* @__PURE__ */
|
|
2078
|
+
/* @__PURE__ */ jsx6(Text4, { style: [styles3.actionLabel, { color: t.text }], children: "Wallet Address" }),
|
|
2079
|
+
/* @__PURE__ */ jsx6(Text4, { style: [styles3.actionValue, { color: t.textMuted }], children: truncateAddress2(walletAddress) })
|
|
1703
2080
|
] }),
|
|
1704
|
-
/* @__PURE__ */
|
|
2081
|
+
/* @__PURE__ */ jsx6(Text4, { style: [styles3.copyLabel, { color: t.accent }], children: "Copy" })
|
|
1705
2082
|
]
|
|
1706
2083
|
}
|
|
1707
2084
|
) : null,
|
|
1708
|
-
onSupport ? /* @__PURE__ */ jsxs4(
|
|
1709
|
-
onCopyAddress ? /* @__PURE__ */
|
|
2085
|
+
onSupport ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
2086
|
+
onCopyAddress ? /* @__PURE__ */ jsx6(View4, { style: [styles3.separator, { backgroundColor: t.border }] }) : null,
|
|
1710
2087
|
/* @__PURE__ */ jsxs4(
|
|
1711
2088
|
TouchableOpacity3,
|
|
1712
2089
|
{
|
|
@@ -1714,21 +2091,21 @@ function SettingsSheet({
|
|
|
1714
2091
|
onPress: onSupport,
|
|
1715
2092
|
activeOpacity: 0.7,
|
|
1716
2093
|
children: [
|
|
1717
|
-
/* @__PURE__ */
|
|
1718
|
-
/* @__PURE__ */
|
|
2094
|
+
/* @__PURE__ */ jsx6(Text4, { style: [styles3.actionLabel, { color: t.text }], children: "Help & Support" }),
|
|
2095
|
+
/* @__PURE__ */ jsx6(Text4, { style: [styles3.chevron, { color: t.textMuted }], children: "\u203A" })
|
|
1719
2096
|
]
|
|
1720
2097
|
}
|
|
1721
2098
|
)
|
|
1722
2099
|
] }) : null
|
|
1723
2100
|
] }),
|
|
1724
|
-
/* @__PURE__ */
|
|
2101
|
+
/* @__PURE__ */ jsx6(
|
|
1725
2102
|
TouchableOpacity3,
|
|
1726
2103
|
{
|
|
1727
2104
|
style: [styles3.logoutButton, { borderColor: t.live }],
|
|
1728
2105
|
onPress: onLogout,
|
|
1729
2106
|
disabled: loggingOut,
|
|
1730
2107
|
activeOpacity: 0.7,
|
|
1731
|
-
children: loggingOut ? /* @__PURE__ */
|
|
2108
|
+
children: loggingOut ? /* @__PURE__ */ jsx6(ActivityIndicator3, { color: t.live, size: "small" }) : /* @__PURE__ */ jsx6(Text4, { style: [styles3.logoutText, { color: t.live }], children: "Log Out" })
|
|
1732
2109
|
}
|
|
1733
2110
|
),
|
|
1734
2111
|
appVersion ? /* @__PURE__ */ jsxs4(Text4, { style: [styles3.version, { color: t.textDim }], children: [
|
|
@@ -1800,6 +2177,476 @@ var styles3 = StyleSheet4.create({
|
|
|
1800
2177
|
textAlign: "center"
|
|
1801
2178
|
}
|
|
1802
2179
|
});
|
|
2180
|
+
|
|
2181
|
+
// src/ui/game/GamePoster.tsx
|
|
2182
|
+
import { useState as useState12 } from "react";
|
|
2183
|
+
import { StyleSheet as StyleSheet5, View as View5, Text as Text5 } from "react-native";
|
|
2184
|
+
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
2185
|
+
function computeCountdown(lockTimestamp) {
|
|
2186
|
+
if (!lockTimestamp) return "";
|
|
2187
|
+
const ts = typeof lockTimestamp === "string" ? parseInt(lockTimestamp) : lockTimestamp;
|
|
2188
|
+
const diff = ts * 1e3 - Date.now();
|
|
2189
|
+
if (diff <= 0) return "LIVE";
|
|
2190
|
+
const days = Math.floor(diff / 864e5);
|
|
2191
|
+
const hours = Math.floor(diff % 864e5 / 36e5);
|
|
2192
|
+
const mins = Math.floor(diff % 36e5 / 6e4);
|
|
2193
|
+
if (days > 0) return `${days}d ${hours}h`;
|
|
2194
|
+
if (hours > 0) return `${hours}h ${mins}m`;
|
|
2195
|
+
return `${mins}m`;
|
|
2196
|
+
}
|
|
2197
|
+
function GamePoster({ game, ImageComponent }) {
|
|
2198
|
+
const t = useDubsTheme();
|
|
2199
|
+
const Img = ImageComponent || __require("react-native").Image;
|
|
2200
|
+
const opponents = game.opponents || [];
|
|
2201
|
+
const home = opponents[0];
|
|
2202
|
+
const away = opponents[1];
|
|
2203
|
+
const countdown = computeCountdown(game.lockTimestamp);
|
|
2204
|
+
const isLive = countdown === "LIVE";
|
|
2205
|
+
return /* @__PURE__ */ jsxs5(View5, { style: styles4.container, children: [
|
|
2206
|
+
game.media?.poster ? /* @__PURE__ */ jsx7(
|
|
2207
|
+
Img,
|
|
2208
|
+
{
|
|
2209
|
+
source: { uri: game.media.poster },
|
|
2210
|
+
style: styles4.image,
|
|
2211
|
+
resizeMode: "cover"
|
|
2212
|
+
}
|
|
2213
|
+
) : /* @__PURE__ */ jsx7(View5, { style: [styles4.image, { backgroundColor: t.surface }], children: /* @__PURE__ */ jsxs5(View5, { style: styles4.fallback, children: [
|
|
2214
|
+
/* @__PURE__ */ jsx7(TeamLogoInternal, { url: home?.imageUrl, size: 56, Img }),
|
|
2215
|
+
/* @__PURE__ */ jsx7(Text5, { style: styles4.vs, children: "VS" }),
|
|
2216
|
+
/* @__PURE__ */ jsx7(TeamLogoInternal, { url: away?.imageUrl, size: 56, Img })
|
|
2217
|
+
] }) }),
|
|
2218
|
+
/* @__PURE__ */ jsx7(View5, { style: styles4.overlay }),
|
|
2219
|
+
/* @__PURE__ */ jsxs5(View5, { style: styles4.teamNames, children: [
|
|
2220
|
+
/* @__PURE__ */ jsx7(Text5, { style: styles4.teamNameText, numberOfLines: 1, children: home?.name || "Home" }),
|
|
2221
|
+
/* @__PURE__ */ jsx7(Text5, { style: styles4.teamNameVs, children: "vs" }),
|
|
2222
|
+
/* @__PURE__ */ jsx7(Text5, { style: styles4.teamNameText, numberOfLines: 1, children: away?.name || "Away" })
|
|
2223
|
+
] }),
|
|
2224
|
+
countdown ? /* @__PURE__ */ jsx7(View5, { style: styles4.countdownPill, children: /* @__PURE__ */ jsx7(Text5, { style: [styles4.countdownText, isLive && styles4.countdownLive], children: countdown }) }) : null,
|
|
2225
|
+
/* @__PURE__ */ jsx7(View5, { style: styles4.poolPill, children: /* @__PURE__ */ jsxs5(Text5, { style: styles4.poolText, children: [
|
|
2226
|
+
game.totalPool || 0,
|
|
2227
|
+
" SOL"
|
|
2228
|
+
] }) })
|
|
2229
|
+
] });
|
|
2230
|
+
}
|
|
2231
|
+
function TeamLogoInternal({ url, size, Img }) {
|
|
2232
|
+
const [failed, setFailed] = useState12(false);
|
|
2233
|
+
if (!url || failed) {
|
|
2234
|
+
return /* @__PURE__ */ jsx7(View5, { style: [styles4.logoPlaceholder, { width: size, height: size, borderRadius: size / 2 }] });
|
|
2235
|
+
}
|
|
2236
|
+
return /* @__PURE__ */ jsx7(
|
|
2237
|
+
Img,
|
|
2238
|
+
{
|
|
2239
|
+
source: { uri: url },
|
|
2240
|
+
style: { width: size, height: size, borderRadius: size / 2 },
|
|
2241
|
+
resizeMode: "contain",
|
|
2242
|
+
onError: () => setFailed(true)
|
|
2243
|
+
}
|
|
2244
|
+
);
|
|
2245
|
+
}
|
|
2246
|
+
var styles4 = StyleSheet5.create({
|
|
2247
|
+
container: {
|
|
2248
|
+
height: 200,
|
|
2249
|
+
borderRadius: 16,
|
|
2250
|
+
overflow: "hidden",
|
|
2251
|
+
position: "relative"
|
|
2252
|
+
},
|
|
2253
|
+
image: {
|
|
2254
|
+
...StyleSheet5.absoluteFillObject,
|
|
2255
|
+
justifyContent: "center",
|
|
2256
|
+
alignItems: "center"
|
|
2257
|
+
},
|
|
2258
|
+
overlay: {
|
|
2259
|
+
...StyleSheet5.absoluteFillObject,
|
|
2260
|
+
backgroundColor: "rgba(0,0,0,0.35)"
|
|
2261
|
+
},
|
|
2262
|
+
fallback: {
|
|
2263
|
+
flexDirection: "row",
|
|
2264
|
+
alignItems: "center",
|
|
2265
|
+
gap: 24,
|
|
2266
|
+
zIndex: 2
|
|
2267
|
+
},
|
|
2268
|
+
vs: {
|
|
2269
|
+
color: "#FFF",
|
|
2270
|
+
fontSize: 24,
|
|
2271
|
+
fontWeight: "900",
|
|
2272
|
+
zIndex: 2
|
|
2273
|
+
},
|
|
2274
|
+
logoPlaceholder: {
|
|
2275
|
+
backgroundColor: "rgba(255,255,255,0.15)",
|
|
2276
|
+
zIndex: 2
|
|
2277
|
+
},
|
|
2278
|
+
teamNames: {
|
|
2279
|
+
position: "absolute",
|
|
2280
|
+
top: 12,
|
|
2281
|
+
left: 12,
|
|
2282
|
+
right: 12,
|
|
2283
|
+
flexDirection: "row",
|
|
2284
|
+
alignItems: "center",
|
|
2285
|
+
justifyContent: "center",
|
|
2286
|
+
gap: 8
|
|
2287
|
+
},
|
|
2288
|
+
teamNameText: {
|
|
2289
|
+
color: "#FFF",
|
|
2290
|
+
fontSize: 14,
|
|
2291
|
+
fontWeight: "700",
|
|
2292
|
+
maxWidth: "40%"
|
|
2293
|
+
},
|
|
2294
|
+
teamNameVs: {
|
|
2295
|
+
color: "rgba(255,255,255,0.6)",
|
|
2296
|
+
fontSize: 12,
|
|
2297
|
+
fontWeight: "600"
|
|
2298
|
+
},
|
|
2299
|
+
countdownPill: {
|
|
2300
|
+
position: "absolute",
|
|
2301
|
+
bottom: 12,
|
|
2302
|
+
left: 12,
|
|
2303
|
+
backgroundColor: "rgba(0,0,0,0.65)",
|
|
2304
|
+
borderRadius: 8,
|
|
2305
|
+
paddingHorizontal: 10,
|
|
2306
|
+
paddingVertical: 5
|
|
2307
|
+
},
|
|
2308
|
+
countdownText: {
|
|
2309
|
+
color: "#FFF",
|
|
2310
|
+
fontSize: 13,
|
|
2311
|
+
fontWeight: "700"
|
|
2312
|
+
},
|
|
2313
|
+
countdownLive: {
|
|
2314
|
+
color: "#EF4444"
|
|
2315
|
+
},
|
|
2316
|
+
poolPill: {
|
|
2317
|
+
position: "absolute",
|
|
2318
|
+
bottom: 12,
|
|
2319
|
+
right: 12,
|
|
2320
|
+
backgroundColor: "#7C3AED",
|
|
2321
|
+
borderRadius: 8,
|
|
2322
|
+
paddingHorizontal: 12,
|
|
2323
|
+
paddingVertical: 5
|
|
2324
|
+
},
|
|
2325
|
+
poolText: {
|
|
2326
|
+
color: "#FFF",
|
|
2327
|
+
fontSize: 13,
|
|
2328
|
+
fontWeight: "800"
|
|
2329
|
+
}
|
|
2330
|
+
});
|
|
2331
|
+
|
|
2332
|
+
// src/ui/game/LivePoolsCard.tsx
|
|
2333
|
+
import { useMemo as useMemo3 } from "react";
|
|
2334
|
+
import { StyleSheet as StyleSheet6, View as View6, Text as Text6 } from "react-native";
|
|
2335
|
+
import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
2336
|
+
function LivePoolsCard({
|
|
2337
|
+
game,
|
|
2338
|
+
shortName,
|
|
2339
|
+
homeColor = "#3B82F6",
|
|
2340
|
+
awayColor = "#EF4444"
|
|
2341
|
+
}) {
|
|
2342
|
+
const t = useDubsTheme();
|
|
2343
|
+
const opponents = game.opponents || [];
|
|
2344
|
+
const homeName = shortName ? shortName(opponents[0]?.name) : opponents[0]?.name || "Home";
|
|
2345
|
+
const awayName = shortName ? shortName(opponents[1]?.name) : opponents[1]?.name || "Away";
|
|
2346
|
+
const homePool = game.homePool || 0;
|
|
2347
|
+
const awayPool = game.awayPool || 0;
|
|
2348
|
+
const totalPool = game.totalPool || 0;
|
|
2349
|
+
const { homePercent, awayPercent, homeOdds, awayOdds } = useMemo3(() => {
|
|
2350
|
+
return {
|
|
2351
|
+
homePercent: totalPool > 0 ? homePool / totalPool * 100 : 50,
|
|
2352
|
+
awayPercent: totalPool > 0 ? awayPool / totalPool * 100 : 50,
|
|
2353
|
+
homeOdds: homePool > 0 ? (totalPool / homePool).toFixed(2) : "\u2014",
|
|
2354
|
+
awayOdds: awayPool > 0 ? (totalPool / awayPool).toFixed(2) : "\u2014"
|
|
2355
|
+
};
|
|
2356
|
+
}, [homePool, awayPool, totalPool]);
|
|
2357
|
+
return /* @__PURE__ */ jsxs6(View6, { style: [styles5.card, { backgroundColor: t.surface, borderColor: t.border }], children: [
|
|
2358
|
+
/* @__PURE__ */ jsx8(Text6, { style: [styles5.title, { color: t.text }], children: "Live Pools" }),
|
|
2359
|
+
/* @__PURE__ */ jsxs6(Text6, { style: [styles5.total, { color: t.accent }], children: [
|
|
2360
|
+
totalPool,
|
|
2361
|
+
" SOL total"
|
|
2362
|
+
] }),
|
|
2363
|
+
/* @__PURE__ */ jsxs6(View6, { style: styles5.bars, children: [
|
|
2364
|
+
/* @__PURE__ */ jsx8(PoolBar, { name: homeName, amount: homePool, percent: homePercent, color: homeColor, t }),
|
|
2365
|
+
/* @__PURE__ */ jsx8(PoolBar, { name: awayName, amount: awayPool, percent: awayPercent, color: awayColor, t })
|
|
2366
|
+
] }),
|
|
2367
|
+
/* @__PURE__ */ jsxs6(View6, { style: styles5.oddsRow, children: [
|
|
2368
|
+
/* @__PURE__ */ jsxs6(Text6, { style: [styles5.oddsText, { color: t.textMuted }], children: [
|
|
2369
|
+
homeName,
|
|
2370
|
+
": ",
|
|
2371
|
+
/* @__PURE__ */ jsxs6(Text6, { style: { color: t.text, fontWeight: "700" }, children: [
|
|
2372
|
+
homeOdds,
|
|
2373
|
+
"x"
|
|
2374
|
+
] })
|
|
2375
|
+
] }),
|
|
2376
|
+
/* @__PURE__ */ jsxs6(Text6, { style: [styles5.oddsText, { color: t.textMuted }], children: [
|
|
2377
|
+
awayName,
|
|
2378
|
+
": ",
|
|
2379
|
+
/* @__PURE__ */ jsxs6(Text6, { style: { color: t.text, fontWeight: "700" }, children: [
|
|
2380
|
+
awayOdds,
|
|
2381
|
+
"x"
|
|
2382
|
+
] })
|
|
2383
|
+
] })
|
|
2384
|
+
] })
|
|
2385
|
+
] });
|
|
2386
|
+
}
|
|
2387
|
+
function PoolBar({ name, amount, percent, color, t }) {
|
|
2388
|
+
return /* @__PURE__ */ jsxs6(View6, { style: styles5.barRow, children: [
|
|
2389
|
+
/* @__PURE__ */ jsx8(Text6, { style: [styles5.barLabel, { color: t.textSecondary }], numberOfLines: 1, children: name }),
|
|
2390
|
+
/* @__PURE__ */ jsx8(View6, { style: [styles5.barTrack, { backgroundColor: t.border }], children: /* @__PURE__ */ jsx8(View6, { style: [styles5.barFill, { width: `${Math.max(percent, 2)}%`, backgroundColor: color }] }) }),
|
|
2391
|
+
/* @__PURE__ */ jsxs6(Text6, { style: [styles5.barAmount, { color: t.text }], children: [
|
|
2392
|
+
amount,
|
|
2393
|
+
" SOL"
|
|
2394
|
+
] })
|
|
2395
|
+
] });
|
|
2396
|
+
}
|
|
2397
|
+
var styles5 = StyleSheet6.create({
|
|
2398
|
+
card: { borderRadius: 16, borderWidth: 1, padding: 16 },
|
|
2399
|
+
title: { fontSize: 17, fontWeight: "700", marginBottom: 4 },
|
|
2400
|
+
total: { fontSize: 14, fontWeight: "600", marginBottom: 14 },
|
|
2401
|
+
bars: { gap: 10 },
|
|
2402
|
+
barRow: { flexDirection: "row", alignItems: "center", gap: 10 },
|
|
2403
|
+
barLabel: { width: 80, fontSize: 13, fontWeight: "600" },
|
|
2404
|
+
barTrack: { flex: 1, height: 10, borderRadius: 5, overflow: "hidden" },
|
|
2405
|
+
barFill: { height: "100%", borderRadius: 5 },
|
|
2406
|
+
barAmount: { width: 70, textAlign: "right", fontSize: 13, fontWeight: "700" },
|
|
2407
|
+
oddsRow: { flexDirection: "row", justifyContent: "space-between", marginTop: 12 },
|
|
2408
|
+
oddsText: { fontSize: 12 }
|
|
2409
|
+
});
|
|
2410
|
+
|
|
2411
|
+
// src/ui/game/PickWinnerCard.tsx
|
|
2412
|
+
import { useState as useState13, useMemo as useMemo4 } from "react";
|
|
2413
|
+
import { StyleSheet as StyleSheet7, View as View7, Text as Text7, TouchableOpacity as TouchableOpacity4 } from "react-native";
|
|
2414
|
+
import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2415
|
+
function PickWinnerCard({
|
|
2416
|
+
game,
|
|
2417
|
+
selectedTeam,
|
|
2418
|
+
onSelect,
|
|
2419
|
+
shortName,
|
|
2420
|
+
homeColor = "#3B82F6",
|
|
2421
|
+
awayColor = "#EF4444",
|
|
2422
|
+
ImageComponent
|
|
2423
|
+
}) {
|
|
2424
|
+
const t = useDubsTheme();
|
|
2425
|
+
const opponents = game.opponents || [];
|
|
2426
|
+
const bettors = game.bettors || [];
|
|
2427
|
+
const totalPool = game.totalPool || 0;
|
|
2428
|
+
const homePool = game.homePool || 0;
|
|
2429
|
+
const awayPool = game.awayPool || 0;
|
|
2430
|
+
const { homeOdds, awayOdds, homeBets, awayBets } = useMemo4(() => ({
|
|
2431
|
+
homeOdds: homePool > 0 ? (totalPool / homePool).toFixed(2) : "\u2014",
|
|
2432
|
+
awayOdds: awayPool > 0 ? (totalPool / awayPool).toFixed(2) : "\u2014",
|
|
2433
|
+
homeBets: bettors.filter((b) => b.team === "home").length,
|
|
2434
|
+
awayBets: bettors.filter((b) => b.team === "away").length
|
|
2435
|
+
}), [totalPool, homePool, awayPool, bettors]);
|
|
2436
|
+
const homeName = shortName ? shortName(opponents[0]?.name) : opponents[0]?.name || "Home";
|
|
2437
|
+
const awayName = shortName ? shortName(opponents[1]?.name) : opponents[1]?.name || "Away";
|
|
2438
|
+
return /* @__PURE__ */ jsxs7(View7, { style: [styles6.card, { backgroundColor: t.surface, borderColor: t.border }], children: [
|
|
2439
|
+
/* @__PURE__ */ jsx9(Text7, { style: [styles6.title, { color: t.text }], children: "Pick Your Winner" }),
|
|
2440
|
+
/* @__PURE__ */ jsxs7(View7, { style: styles6.row, children: [
|
|
2441
|
+
/* @__PURE__ */ jsx9(
|
|
2442
|
+
TeamOption,
|
|
2443
|
+
{
|
|
2444
|
+
name: homeName,
|
|
2445
|
+
imageUrl: opponents[0]?.imageUrl,
|
|
2446
|
+
odds: homeOdds,
|
|
2447
|
+
bets: homeBets,
|
|
2448
|
+
color: homeColor,
|
|
2449
|
+
selected: selectedTeam === "home",
|
|
2450
|
+
onPress: () => onSelect("home"),
|
|
2451
|
+
ImageComponent,
|
|
2452
|
+
t
|
|
2453
|
+
}
|
|
2454
|
+
),
|
|
2455
|
+
/* @__PURE__ */ jsx9(
|
|
2456
|
+
TeamOption,
|
|
2457
|
+
{
|
|
2458
|
+
name: awayName,
|
|
2459
|
+
imageUrl: opponents[1]?.imageUrl,
|
|
2460
|
+
odds: awayOdds,
|
|
2461
|
+
bets: awayBets,
|
|
2462
|
+
color: awayColor,
|
|
2463
|
+
selected: selectedTeam === "away",
|
|
2464
|
+
onPress: () => onSelect("away"),
|
|
2465
|
+
ImageComponent,
|
|
2466
|
+
t
|
|
2467
|
+
}
|
|
2468
|
+
)
|
|
2469
|
+
] })
|
|
2470
|
+
] });
|
|
2471
|
+
}
|
|
2472
|
+
function TeamOption({
|
|
2473
|
+
name,
|
|
2474
|
+
imageUrl,
|
|
2475
|
+
odds,
|
|
2476
|
+
bets,
|
|
2477
|
+
color,
|
|
2478
|
+
selected,
|
|
2479
|
+
onPress,
|
|
2480
|
+
ImageComponent,
|
|
2481
|
+
t
|
|
2482
|
+
}) {
|
|
2483
|
+
const [imgFailed, setImgFailed] = useState13(false);
|
|
2484
|
+
const Img = ImageComponent || __require("react-native").Image;
|
|
2485
|
+
const showImage = imageUrl && !imgFailed;
|
|
2486
|
+
return /* @__PURE__ */ jsxs7(
|
|
2487
|
+
TouchableOpacity4,
|
|
2488
|
+
{
|
|
2489
|
+
style: [styles6.option, { borderColor: selected ? color : t.border, backgroundColor: selected ? color + "15" : t.background }],
|
|
2490
|
+
onPress,
|
|
2491
|
+
activeOpacity: 0.7,
|
|
2492
|
+
children: [
|
|
2493
|
+
showImage ? /* @__PURE__ */ jsx9(Img, { source: { uri: imageUrl }, style: styles6.logo, resizeMode: "contain", onError: () => setImgFailed(true) }) : /* @__PURE__ */ jsx9(View7, { style: [styles6.logo, styles6.logoPlaceholder] }),
|
|
2494
|
+
/* @__PURE__ */ jsx9(Text7, { style: [styles6.name, { color: t.text }], numberOfLines: 1, children: name }),
|
|
2495
|
+
/* @__PURE__ */ jsxs7(Text7, { style: [styles6.odds, { color }], children: [
|
|
2496
|
+
odds,
|
|
2497
|
+
"x"
|
|
2498
|
+
] }),
|
|
2499
|
+
/* @__PURE__ */ jsxs7(Text7, { style: [styles6.bets, { color: t.textMuted }], children: [
|
|
2500
|
+
bets,
|
|
2501
|
+
" ",
|
|
2502
|
+
bets === 1 ? "bet" : "bets"
|
|
2503
|
+
] }),
|
|
2504
|
+
selected && /* @__PURE__ */ jsx9(View7, { style: [styles6.badge, { backgroundColor: color }], children: /* @__PURE__ */ jsx9(Text7, { style: styles6.badgeText, children: "Selected" }) })
|
|
2505
|
+
]
|
|
2506
|
+
}
|
|
2507
|
+
);
|
|
2508
|
+
}
|
|
2509
|
+
var styles6 = StyleSheet7.create({
|
|
2510
|
+
card: { borderRadius: 16, borderWidth: 1, padding: 16 },
|
|
2511
|
+
title: { fontSize: 17, fontWeight: "700", marginBottom: 12 },
|
|
2512
|
+
row: { flexDirection: "row", gap: 12 },
|
|
2513
|
+
option: { flex: 1, borderWidth: 2, borderRadius: 16, padding: 16, alignItems: "center", gap: 8 },
|
|
2514
|
+
logo: { width: 48, height: 48, borderRadius: 24 },
|
|
2515
|
+
logoPlaceholder: { backgroundColor: "rgba(128,128,128,0.2)" },
|
|
2516
|
+
name: { fontSize: 15, fontWeight: "700" },
|
|
2517
|
+
odds: { fontSize: 20, fontWeight: "800" },
|
|
2518
|
+
bets: { fontSize: 12 },
|
|
2519
|
+
badge: { borderRadius: 8, paddingHorizontal: 12, paddingVertical: 4, marginTop: 4 },
|
|
2520
|
+
badgeText: { color: "#FFF", fontSize: 12, fontWeight: "700" }
|
|
2521
|
+
});
|
|
2522
|
+
|
|
2523
|
+
// src/ui/game/PlayersCard.tsx
|
|
2524
|
+
import { useState as useState14 } from "react";
|
|
2525
|
+
import { StyleSheet as StyleSheet8, View as View8, Text as Text8 } from "react-native";
|
|
2526
|
+
import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2527
|
+
function truncateWallet(addr, chars) {
|
|
2528
|
+
if (addr.length <= chars * 2 + 3) return addr;
|
|
2529
|
+
return `${addr.slice(0, chars)}...${addr.slice(-chars)}`;
|
|
2530
|
+
}
|
|
2531
|
+
function PlayersCard({
|
|
2532
|
+
game,
|
|
2533
|
+
truncateChars = 4,
|
|
2534
|
+
homeColor = "#3B82F6",
|
|
2535
|
+
awayColor = "#EF4444",
|
|
2536
|
+
drawColor = "#A855F7",
|
|
2537
|
+
ImageComponent
|
|
2538
|
+
}) {
|
|
2539
|
+
const t = useDubsTheme();
|
|
2540
|
+
const bettors = game.bettors || [];
|
|
2541
|
+
const dotColor = (team) => {
|
|
2542
|
+
if (team === "home") return homeColor;
|
|
2543
|
+
if (team === "away") return awayColor;
|
|
2544
|
+
return drawColor;
|
|
2545
|
+
};
|
|
2546
|
+
return /* @__PURE__ */ jsxs8(View8, { style: [styles7.card, { backgroundColor: t.surface, borderColor: t.border }], children: [
|
|
2547
|
+
/* @__PURE__ */ jsxs8(Text8, { style: [styles7.title, { color: t.text }], children: [
|
|
2548
|
+
"Players",
|
|
2549
|
+
bettors.length > 0 ? ` (${bettors.length})` : ""
|
|
2550
|
+
] }),
|
|
2551
|
+
bettors.length === 0 ? /* @__PURE__ */ jsx10(Text8, { style: [styles7.empty, { color: t.textMuted }], children: "No players yet \u2014 be the first!" }) : bettors.map((b, i) => /* @__PURE__ */ jsx10(
|
|
2552
|
+
BettorRow,
|
|
2553
|
+
{
|
|
2554
|
+
bettor: b,
|
|
2555
|
+
dotColor: dotColor(b.team),
|
|
2556
|
+
truncateChars,
|
|
2557
|
+
isFirst: i === 0,
|
|
2558
|
+
ImageComponent,
|
|
2559
|
+
t
|
|
2560
|
+
},
|
|
2561
|
+
`${b.wallet}-${i}`
|
|
2562
|
+
))
|
|
2563
|
+
] });
|
|
2564
|
+
}
|
|
2565
|
+
function BettorRow({
|
|
2566
|
+
bettor,
|
|
2567
|
+
dotColor,
|
|
2568
|
+
truncateChars,
|
|
2569
|
+
isFirst,
|
|
2570
|
+
ImageComponent,
|
|
2571
|
+
t
|
|
2572
|
+
}) {
|
|
2573
|
+
const [imgFailed, setImgFailed] = useState14(false);
|
|
2574
|
+
const Img = ImageComponent || __require("react-native").Image;
|
|
2575
|
+
const showAvatar = bettor.avatar && !imgFailed;
|
|
2576
|
+
return /* @__PURE__ */ jsxs8(View8, { style: [styles7.row, !isFirst && { borderTopColor: t.border, borderTopWidth: 1 }], children: [
|
|
2577
|
+
/* @__PURE__ */ jsx10(View8, { style: [styles7.dot, { backgroundColor: dotColor }] }),
|
|
2578
|
+
showAvatar ? /* @__PURE__ */ jsx10(Img, { source: { uri: bettor.avatar }, style: styles7.avatar, resizeMode: "cover", onError: () => setImgFailed(true) }) : /* @__PURE__ */ jsx10(View8, { style: [styles7.avatar, styles7.avatarPlaceholder] }),
|
|
2579
|
+
/* @__PURE__ */ jsx10(View8, { style: styles7.nameCol, children: /* @__PURE__ */ jsx10(Text8, { style: [styles7.username, { color: t.text }], numberOfLines: 1, children: bettor.username || truncateWallet(bettor.wallet, truncateChars) }) }),
|
|
2580
|
+
/* @__PURE__ */ jsxs8(Text8, { style: [styles7.amount, { color: t.textSecondary }], children: [
|
|
2581
|
+
bettor.amount,
|
|
2582
|
+
" SOL"
|
|
2583
|
+
] })
|
|
2584
|
+
] });
|
|
2585
|
+
}
|
|
2586
|
+
var styles7 = StyleSheet8.create({
|
|
2587
|
+
card: { borderRadius: 16, borderWidth: 1, padding: 16 },
|
|
2588
|
+
title: { fontSize: 17, fontWeight: "700", marginBottom: 12 },
|
|
2589
|
+
empty: { fontSize: 14, textAlign: "center", paddingVertical: 16 },
|
|
2590
|
+
row: { flexDirection: "row", alignItems: "center", paddingVertical: 10, gap: 10 },
|
|
2591
|
+
dot: { width: 8, height: 8, borderRadius: 4 },
|
|
2592
|
+
avatar: { width: 28, height: 28, borderRadius: 14 },
|
|
2593
|
+
avatarPlaceholder: { backgroundColor: "rgba(128,128,128,0.2)" },
|
|
2594
|
+
nameCol: { flex: 1 },
|
|
2595
|
+
username: { fontSize: 14, fontWeight: "600" },
|
|
2596
|
+
amount: { fontSize: 13, fontWeight: "700" }
|
|
2597
|
+
});
|
|
2598
|
+
|
|
2599
|
+
// src/ui/game/JoinGameButton.tsx
|
|
2600
|
+
import { useMemo as useMemo5 } from "react";
|
|
2601
|
+
import { StyleSheet as StyleSheet9, View as View9, Text as Text9, TouchableOpacity as TouchableOpacity5, ActivityIndicator as ActivityIndicator4 } from "react-native";
|
|
2602
|
+
import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2603
|
+
var STATUS_LABELS = {
|
|
2604
|
+
building: "Building transaction...",
|
|
2605
|
+
signing: "Approve in wallet...",
|
|
2606
|
+
confirming: "Confirming on-chain...",
|
|
2607
|
+
saving: "Saving..."
|
|
2608
|
+
};
|
|
2609
|
+
function JoinGameButton({ game, walletAddress, selectedTeam, status, onJoin }) {
|
|
2610
|
+
const t = useDubsTheme();
|
|
2611
|
+
const alreadyJoined = useMemo5(() => {
|
|
2612
|
+
if (!walletAddress) return false;
|
|
2613
|
+
return (game.bettors || []).some((b) => b.wallet === walletAddress);
|
|
2614
|
+
}, [game.bettors, walletAddress]);
|
|
2615
|
+
if (alreadyJoined || game.isLocked || game.isResolved) return null;
|
|
2616
|
+
const isJoining = status !== "idle" && status !== "success" && status !== "error";
|
|
2617
|
+
const statusLabel = STATUS_LABELS[status] || "";
|
|
2618
|
+
return /* @__PURE__ */ jsxs9(View9, { style: [styles8.bar, { backgroundColor: t.background, borderTopColor: t.border }], children: [
|
|
2619
|
+
/* @__PURE__ */ jsxs9(View9, { style: styles8.buyInRow, children: [
|
|
2620
|
+
/* @__PURE__ */ jsx11(Text9, { style: [styles8.buyInLabel, { color: t.textMuted }], children: "Buy-in" }),
|
|
2621
|
+
/* @__PURE__ */ jsxs9(Text9, { style: [styles8.buyInValue, { color: t.text }], children: [
|
|
2622
|
+
game.buyIn,
|
|
2623
|
+
" SOL"
|
|
2624
|
+
] })
|
|
2625
|
+
] }),
|
|
2626
|
+
/* @__PURE__ */ jsx11(
|
|
2627
|
+
TouchableOpacity5,
|
|
2628
|
+
{
|
|
2629
|
+
style: [styles8.button, { backgroundColor: selectedTeam ? "#22D3EE" : t.border }],
|
|
2630
|
+
disabled: !selectedTeam || isJoining,
|
|
2631
|
+
onPress: onJoin,
|
|
2632
|
+
activeOpacity: 0.8,
|
|
2633
|
+
children: isJoining ? /* @__PURE__ */ jsxs9(View9, { style: styles8.joiningRow, children: [
|
|
2634
|
+
/* @__PURE__ */ jsx11(ActivityIndicator4, { size: "small", color: "#000" }),
|
|
2635
|
+
/* @__PURE__ */ jsx11(Text9, { style: styles8.buttonText, children: statusLabel })
|
|
2636
|
+
] }) : /* @__PURE__ */ jsx11(Text9, { style: [styles8.buttonText, !selectedTeam && { color: t.textMuted }], children: selectedTeam ? `Join Bet \u2014 ${game.buyIn} SOL` : "Pick a team to bet" })
|
|
2637
|
+
}
|
|
2638
|
+
)
|
|
2639
|
+
] });
|
|
2640
|
+
}
|
|
2641
|
+
var styles8 = StyleSheet9.create({
|
|
2642
|
+
bar: { position: "absolute", bottom: 0, left: 0, right: 0, paddingHorizontal: 16, paddingTop: 12, paddingBottom: 36, borderTopWidth: 1 },
|
|
2643
|
+
buyInRow: { flexDirection: "row", alignItems: "center", justifyContent: "space-between", marginBottom: 10 },
|
|
2644
|
+
buyInLabel: { fontSize: 13 },
|
|
2645
|
+
buyInValue: { fontSize: 15, fontWeight: "800" },
|
|
2646
|
+
button: { borderRadius: 14, paddingVertical: 16, alignItems: "center" },
|
|
2647
|
+
buttonText: { color: "#000", fontSize: 16, fontWeight: "800" },
|
|
2648
|
+
joiningRow: { flexDirection: "row", alignItems: "center", gap: 10 }
|
|
2649
|
+
});
|
|
1803
2650
|
export {
|
|
1804
2651
|
AuthGate,
|
|
1805
2652
|
ConnectWalletScreen,
|
|
@@ -1808,12 +2655,20 @@ export {
|
|
|
1808
2655
|
DubsApiError,
|
|
1809
2656
|
DubsClient,
|
|
1810
2657
|
DubsProvider,
|
|
2658
|
+
GamePoster,
|
|
2659
|
+
JoinGameButton,
|
|
2660
|
+
LivePoolsCard,
|
|
1811
2661
|
MwaWalletAdapter,
|
|
2662
|
+
NETWORK_CONFIG,
|
|
2663
|
+
PickWinnerCard,
|
|
2664
|
+
PlayersCard,
|
|
1812
2665
|
SOLANA_PROGRAM_ERRORS,
|
|
2666
|
+
STORAGE_KEYS,
|
|
1813
2667
|
SettingsSheet,
|
|
1814
2668
|
UserProfileCard,
|
|
2669
|
+
createSecureStoreStorage,
|
|
2670
|
+
mergeTheme,
|
|
1815
2671
|
parseSolanaError,
|
|
1816
|
-
pollTransactionConfirmation,
|
|
1817
2672
|
signAndSendBase64Transaction,
|
|
1818
2673
|
useAuth,
|
|
1819
2674
|
useClaim,
|