@dubsdotapp/expo 0.5.4 → 0.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +49 -31
- package/dist/index.d.ts +49 -31
- package/dist/index.js +63 -54
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +199 -191
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/hooks/useArcadeBridge.ts +9 -4
- package/src/index.ts +1 -1
- package/src/ui/AuthGate.tsx +27 -0
- package/src/ui/ConnectWalletButton.tsx +74 -79
- package/src/ui/game/EnterArcadePoolSheet.tsx +3 -2
- package/src/ui/game/JoinGameSheet.tsx +5 -2
- package/src/ui/index.ts +1 -1
package/package.json
CHANGED
|
@@ -94,6 +94,10 @@ export function useArcadeBridge({
|
|
|
94
94
|
const sessionTokenRef = useRef<string | null>(null);
|
|
95
95
|
const gameStartTimeRef = useRef<number>(0);
|
|
96
96
|
|
|
97
|
+
// Keep canPlay in a ref so TAP_PLAY handler always sees the latest value
|
|
98
|
+
const canPlayRef = useRef(canPlay);
|
|
99
|
+
canPlayRef.current = canPlay;
|
|
100
|
+
|
|
97
101
|
const [lastResult, setLastResult] = useState<SubmitScoreResult | null>(null);
|
|
98
102
|
const [bridgeLoading, setBridgeLoading] = useState(false);
|
|
99
103
|
|
|
@@ -139,12 +143,13 @@ export function useArcadeBridge({
|
|
|
139
143
|
return; // non-JSON postMessages from the game's own code — ignore
|
|
140
144
|
}
|
|
141
145
|
|
|
142
|
-
//
|
|
143
|
-
|
|
146
|
+
// If dubsArcade version is present it must match — if absent, accept anyway
|
|
147
|
+
// (backward-compat: old game builds don't include the envelope yet)
|
|
148
|
+
if (data.dubsArcade !== undefined && data.dubsArcade !== PROTOCOL_VERSION) return;
|
|
144
149
|
|
|
145
150
|
switch (data.type) {
|
|
146
151
|
case 'TAP_PLAY': {
|
|
147
|
-
if (
|
|
152
|
+
if (canPlayRef.current) {
|
|
148
153
|
triggerPlay();
|
|
149
154
|
}
|
|
150
155
|
return;
|
|
@@ -186,7 +191,7 @@ export function useArcadeBridge({
|
|
|
186
191
|
return;
|
|
187
192
|
}
|
|
188
193
|
},
|
|
189
|
-
[
|
|
194
|
+
[triggerPlay, submitScore, onScoreSubmitted, onError],
|
|
190
195
|
);
|
|
191
196
|
|
|
192
197
|
return { webviewRef, handleMessage, triggerPlay, lastResult, bridgeLoading };
|
package/src/index.ts
CHANGED
|
@@ -131,7 +131,7 @@ export type {
|
|
|
131
131
|
|
|
132
132
|
// UI
|
|
133
133
|
export { AuthGate, ConnectWalletScreen, ConnectWalletButton, UserProfileCard, SettingsSheet, UserProfileSheet, useDubsTheme, mergeTheme } from './ui';
|
|
134
|
-
export type { AuthGateProps, RegistrationScreenProps, ConnectWalletScreenProps, ConnectWalletButtonProps, UserProfileCardProps, SettingsSheetProps, UserProfileSheetProps, DubsTheme } from './ui';
|
|
134
|
+
export type { AuthGateProps, RegistrationScreenProps, ConnectWalletScreenProps, ConnectWalletButtonProps, AuthGateConnectWalletProps, UserProfileCardProps, SettingsSheetProps, UserProfileSheetProps, DubsTheme } from './ui';
|
|
135
135
|
|
|
136
136
|
// Game widgets
|
|
137
137
|
export { GamePoster, LivePoolsCard, PickWinnerCard, PlayersCard, JoinGameButton, CreateCustomGameSheet, JoinGameSheet, ClaimPrizeSheet, ClaimButton, EnterArcadePoolSheet, ArcadeLeaderboardSheet } from './ui';
|
package/src/ui/AuthGate.tsx
CHANGED
|
@@ -31,6 +31,15 @@ export interface RegistrationScreenProps {
|
|
|
31
31
|
client: DubsClient;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
export interface ConnectWalletScreenProps {
|
|
35
|
+
/** Call this to trigger the wallet connection flow */
|
|
36
|
+
onConnect: () => void;
|
|
37
|
+
/** True while the wallet is connecting/signing */
|
|
38
|
+
connecting: boolean;
|
|
39
|
+
/** Error from a failed connection attempt */
|
|
40
|
+
error: Error | null;
|
|
41
|
+
}
|
|
42
|
+
|
|
34
43
|
export interface AuthGateProps {
|
|
35
44
|
children: React.ReactNode;
|
|
36
45
|
onSaveToken: (token: string | null) => void | Promise<void>;
|
|
@@ -38,6 +47,12 @@ export interface AuthGateProps {
|
|
|
38
47
|
renderLoading?: (status: AuthStatus) => React.ReactNode;
|
|
39
48
|
renderError?: (error: Error, retry: () => void) => React.ReactNode;
|
|
40
49
|
renderRegistration?: (props: RegistrationScreenProps) => React.ReactNode;
|
|
50
|
+
/**
|
|
51
|
+
* Render your own connect-wallet screen.
|
|
52
|
+
* Receives { onConnect, connecting, error }.
|
|
53
|
+
* If omitted, the default ConnectWalletScreen is shown.
|
|
54
|
+
*/
|
|
55
|
+
renderConnectWallet?: (props: ConnectWalletScreenProps) => React.ReactNode;
|
|
41
56
|
appName?: string;
|
|
42
57
|
/** Override accent color for registration screens (from developer UI config) */
|
|
43
58
|
accentColor?: string;
|
|
@@ -52,6 +67,7 @@ export function AuthGate({
|
|
|
52
67
|
renderLoading,
|
|
53
68
|
renderError,
|
|
54
69
|
renderRegistration,
|
|
70
|
+
renderConnectWallet,
|
|
55
71
|
appName = 'Dubs',
|
|
56
72
|
accentColor,
|
|
57
73
|
}: AuthGateProps) {
|
|
@@ -162,6 +178,17 @@ export function AuthGate({
|
|
|
162
178
|
return <DefaultErrorScreen error={auth.error} onRetry={retry} appName={appName} accentColor={accentColor} />;
|
|
163
179
|
}
|
|
164
180
|
|
|
181
|
+
// idle = waiting for user to connect — show connect wallet screen
|
|
182
|
+
if (auth.status === 'idle') {
|
|
183
|
+
const connectProps: ConnectWalletScreenProps = {
|
|
184
|
+
onConnect: auth.authenticate,
|
|
185
|
+
connecting: false,
|
|
186
|
+
error: null,
|
|
187
|
+
};
|
|
188
|
+
if (renderConnectWallet) return <>{renderConnectWallet(connectProps)}</>;
|
|
189
|
+
// Fall through to default loading (existing behaviour if no renderConnectWallet)
|
|
190
|
+
}
|
|
191
|
+
|
|
165
192
|
if (renderLoading) return <>{renderLoading(auth.status)}</>;
|
|
166
193
|
return <DefaultLoadingScreen status={auth.status} appName={appName} accentColor={accentColor} />;
|
|
167
194
|
}
|
|
@@ -1,112 +1,107 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import {
|
|
3
|
-
Text,
|
|
4
3
|
TouchableOpacity,
|
|
4
|
+
Text,
|
|
5
5
|
ActivityIndicator,
|
|
6
6
|
StyleSheet,
|
|
7
7
|
View,
|
|
8
|
-
type ViewStyle,
|
|
9
8
|
} from 'react-native';
|
|
9
|
+
import { useAuth } from '../hooks/useAuth';
|
|
10
10
|
import { useDubsTheme } from './theme';
|
|
11
11
|
|
|
12
12
|
export interface ConnectWalletButtonProps {
|
|
13
|
-
/**
|
|
14
|
-
onConnect: () => void | Promise<void>;
|
|
15
|
-
/** Show a loading spinner while connecting */
|
|
16
|
-
connecting?: boolean;
|
|
17
|
-
/** Error message to display below the button */
|
|
18
|
-
error?: string | null;
|
|
19
|
-
/** Override accent color (e.g. from developer UI config) */
|
|
20
|
-
accentColor?: string;
|
|
21
|
-
/** Override button label. Defaults to "Connect Wallet" */
|
|
13
|
+
/** Button label. Defaults to "Connect Wallet" */
|
|
22
14
|
label?: string;
|
|
23
|
-
/**
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
|
|
15
|
+
/** Override accent/background color */
|
|
16
|
+
accentColor?: string;
|
|
17
|
+
/** Override text color. Defaults to #000 */
|
|
18
|
+
textColor?: string;
|
|
19
|
+
/** Override border radius */
|
|
20
|
+
borderRadius?: number;
|
|
21
|
+
/** Override padding vertical */
|
|
22
|
+
paddingVertical?: number;
|
|
23
|
+
/** Override font size */
|
|
24
|
+
fontSize?: number;
|
|
25
|
+
/** Full width. Defaults to true */
|
|
26
|
+
fullWidth?: boolean;
|
|
27
|
+
/** Called after authenticate() is triggered (not after it completes) */
|
|
28
|
+
onPress?: () => void;
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
/**
|
|
30
|
-
*
|
|
31
|
-
* connection flow as ConnectWalletScreen, but renders as a single button
|
|
32
|
-
* instead of a full-screen layout.
|
|
32
|
+
* ConnectWalletButton
|
|
33
33
|
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
34
|
+
* A minimal, customisable button that fires useAuth().authenticate().
|
|
35
|
+
* Use this when you want to own the connect-wallet screen UI (e.g. inside
|
|
36
|
+
* your own onboarding) but still hand off the wallet picker, signing, and
|
|
37
|
+
* registration flow to the SDK.
|
|
36
38
|
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
39
|
+
* The SDK will handle everything after this tap:
|
|
40
|
+
* wallet picker → sign message → (registration/avatar if new user) → authenticated
|
|
41
|
+
*
|
|
42
|
+
* Example:
|
|
43
|
+
* <ConnectWalletButton
|
|
44
|
+
* label="Let's go"
|
|
45
|
+
* accentColor="#22C55E"
|
|
46
|
+
* textColor="#000"
|
|
47
|
+
* />
|
|
46
48
|
*/
|
|
47
49
|
export function ConnectWalletButton({
|
|
48
|
-
onConnect,
|
|
49
|
-
connecting = false,
|
|
50
|
-
error = null,
|
|
51
|
-
accentColor,
|
|
52
50
|
label = 'Connect Wallet',
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
accentColor,
|
|
52
|
+
textColor = '#000000',
|
|
53
|
+
borderRadius = 16,
|
|
54
|
+
paddingVertical = 16,
|
|
55
|
+
fontSize = 17,
|
|
56
|
+
fullWidth = true,
|
|
57
|
+
onPress,
|
|
55
58
|
}: ConnectWalletButtonProps) {
|
|
56
59
|
const t = useDubsTheme();
|
|
60
|
+
const { authenticate, status } = useAuth();
|
|
61
|
+
|
|
62
|
+
const connecting = status === 'authenticating' || status === 'signing' || status === 'verifying';
|
|
57
63
|
const accent = accentColor || t.accent;
|
|
58
64
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
style={[
|
|
64
|
-
styles.errorBox,
|
|
65
|
-
{ backgroundColor: t.errorBg, borderColor: t.errorBorder },
|
|
66
|
-
]}
|
|
67
|
-
>
|
|
68
|
-
<Text style={[styles.errorText, { color: t.errorText }]}>{error}</Text>
|
|
69
|
-
</View>
|
|
70
|
-
) : null}
|
|
65
|
+
const handlePress = () => {
|
|
66
|
+
onPress?.();
|
|
67
|
+
authenticate();
|
|
68
|
+
};
|
|
71
69
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
70
|
+
return (
|
|
71
|
+
<TouchableOpacity
|
|
72
|
+
onPress={handlePress}
|
|
73
|
+
disabled={connecting}
|
|
74
|
+
activeOpacity={0.85}
|
|
75
|
+
style={[
|
|
76
|
+
styles.btn,
|
|
77
|
+
{
|
|
78
|
+
backgroundColor: accent,
|
|
79
|
+
borderRadius,
|
|
80
|
+
paddingVertical,
|
|
81
|
+
width: fullWidth ? '100%' : undefined,
|
|
82
|
+
opacity: connecting ? 0.8 : 1,
|
|
83
|
+
},
|
|
84
|
+
]}
|
|
85
|
+
>
|
|
86
|
+
{connecting ? (
|
|
87
|
+
<ActivityIndicator color={textColor} size="small" />
|
|
88
|
+
) : (
|
|
89
|
+
<Text style={[styles.label, { color: textColor, fontSize }]}>
|
|
90
|
+
{label}
|
|
91
|
+
</Text>
|
|
92
|
+
)}
|
|
93
|
+
</TouchableOpacity>
|
|
85
94
|
);
|
|
86
95
|
}
|
|
87
96
|
|
|
88
97
|
const styles = StyleSheet.create({
|
|
89
|
-
|
|
90
|
-
height: 56,
|
|
91
|
-
borderRadius: 16,
|
|
92
|
-
justifyContent: 'center',
|
|
98
|
+
btn: {
|
|
93
99
|
alignItems: 'center',
|
|
100
|
+
justifyContent: 'center',
|
|
94
101
|
paddingHorizontal: 24,
|
|
95
102
|
},
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
fontWeight: '700',
|
|
100
|
-
},
|
|
101
|
-
errorBox: {
|
|
102
|
-
borderWidth: 1,
|
|
103
|
-
borderRadius: 12,
|
|
104
|
-
paddingHorizontal: 16,
|
|
105
|
-
paddingVertical: 12,
|
|
106
|
-
marginBottom: 12,
|
|
107
|
-
},
|
|
108
|
-
errorText: {
|
|
109
|
-
fontSize: 14,
|
|
110
|
-
textAlign: 'center',
|
|
103
|
+
label: {
|
|
104
|
+
fontWeight: '800',
|
|
105
|
+
letterSpacing: -0.2,
|
|
111
106
|
},
|
|
112
107
|
});
|
|
@@ -82,9 +82,10 @@ export function EnterArcadePoolSheet({
|
|
|
82
82
|
}, [mutation.status, mutation.error]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
83
83
|
|
|
84
84
|
const buyInSol = (pool.buy_in_lamports / 1_000_000_000).toFixed(4);
|
|
85
|
-
const totalPlayers = stats?.total_entries ??
|
|
85
|
+
const totalPlayers = stats?.total_entries ?? 0;
|
|
86
|
+
const totalBuyIns = pool.total_entries ?? totalPlayers;
|
|
86
87
|
const topScore = stats?.top_score ?? 0;
|
|
87
|
-
const potSol = ((pool.buy_in_lamports * Number(
|
|
88
|
+
const potSol = ((pool.buy_in_lamports * Number(totalBuyIns)) / 1_000_000_000).toFixed(4);
|
|
88
89
|
|
|
89
90
|
const isMutating = mutation.status !== 'idle' && mutation.status !== 'success' && mutation.status !== 'error';
|
|
90
91
|
const canJoin = !isMutating && mutation.status !== 'success';
|
|
@@ -30,6 +30,8 @@ export interface JoinGameSheetProps {
|
|
|
30
30
|
/** Callbacks */
|
|
31
31
|
onSuccess?: (result: JoinGameMutationResult) => void;
|
|
32
32
|
onError?: (error: Error) => void;
|
|
33
|
+
/** Called when the user taps a team card (useful for sound/haptics) */
|
|
34
|
+
onTeamSelect?: (team: 'home' | 'away') => void;
|
|
33
35
|
/** Pool mode: hides team selection, auto-assigns team, shows "Join Pool" labels */
|
|
34
36
|
isPoolModeEnabled?: boolean;
|
|
35
37
|
}
|
|
@@ -53,6 +55,7 @@ export function JoinGameSheet({
|
|
|
53
55
|
awayColor = '#EF4444',
|
|
54
56
|
onSuccess,
|
|
55
57
|
onError,
|
|
58
|
+
onTeamSelect,
|
|
56
59
|
isPoolModeEnabled = false,
|
|
57
60
|
}: JoinGameSheetProps) {
|
|
58
61
|
const t = useDubsTheme();
|
|
@@ -194,7 +197,7 @@ export function JoinGameSheet({
|
|
|
194
197
|
bets={homeBets}
|
|
195
198
|
color={homeColor}
|
|
196
199
|
selected={selectedTeam === 'home'}
|
|
197
|
-
onPress={() => setSelectedTeam('home')}
|
|
200
|
+
onPress={() => { setSelectedTeam('home'); onTeamSelect?.('home'); }}
|
|
198
201
|
ImageComponent={ImageComponent}
|
|
199
202
|
t={t}
|
|
200
203
|
/>
|
|
@@ -205,7 +208,7 @@ export function JoinGameSheet({
|
|
|
205
208
|
bets={awayBets}
|
|
206
209
|
color={awayColor}
|
|
207
210
|
selected={selectedTeam === 'away'}
|
|
208
|
-
onPress={() => setSelectedTeam('away')}
|
|
211
|
+
onPress={() => { setSelectedTeam('away'); onTeamSelect?.('away'); }}
|
|
209
212
|
ImageComponent={ImageComponent}
|
|
210
213
|
t={t}
|
|
211
214
|
/>
|
package/src/ui/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { AuthGate } from './AuthGate';
|
|
2
|
-
export type { AuthGateProps, RegistrationScreenProps } from './AuthGate';
|
|
2
|
+
export type { AuthGateProps, RegistrationScreenProps, ConnectWalletScreenProps as AuthGateConnectWalletProps } from './AuthGate';
|
|
3
3
|
export { ConnectWalletScreen } from './ConnectWalletScreen';
|
|
4
4
|
export type { ConnectWalletScreenProps } from './ConnectWalletScreen';
|
|
5
5
|
export { ConnectWalletButton } from './ConnectWalletButton';
|