@beta-gamer/react-native 0.1.18 → 0.1.21

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 CHANGED
@@ -5,132 +5,228 @@ React Native SDK for [Beta Gamer](https://beta-gamer.com) — embed multiplayer
5
5
  ## Installation
6
6
 
7
7
  ```bash
8
- npm install @beta-gamer/react-native react-native-webview
8
+ npm install @beta-gamer/react-native
9
9
  ```
10
10
 
11
- Then link the native module:
11
+ > `react-native-webview` is only required for subway-runner. Chess, Checkers, Connect4, and Tictactoe are fully native.
12
12
 
13
13
  ```bash
14
- # iOS
15
- cd ios && pod install
16
-
17
- # Android — no extra steps needed
14
+ # Only if using subway-runner:
15
+ npx expo install react-native-webview # Expo
16
+ npm install react-native-webview && cd ios && pod install # Bare RN
18
17
  ```
19
18
 
20
19
  ## Requirements
21
20
 
22
- - React Native 0.73+
23
- - `react-native-webview` 13+
24
- - A Beta Gamer tenant account and API key → [beta-gamer.com](https://beta-gamer.com)
21
+ - React Native 0.73+ or Expo SDK 50+
22
+ - A Beta Gamer account and API key → [beta-gamer.com](https://beta-gamer.com)
23
+
24
+ ---
25
+
26
+ ## Quick start — Chess (fully native)
25
27
 
26
- ## Quick start
28
+ `ChessBoard` is a self-contained native component. It manages its own socket, renders with RN primitives, and handles matchmaking, clocks, AFK warnings, pawn promotion, and game-over automatically.
27
29
 
28
- **1. Create a session from your backend** (never expose your API key on the client)
30
+ **1. Create a session on your backend** (never call the Beta Gamer API from the app)
29
31
 
30
32
  ```ts
31
- const res = await fetch('https://api.beta-gamer.com/v1/sessions', {
32
- method: 'POST',
33
- headers: {
34
- 'Authorization': 'Bearer bg_live_xxxx',
35
- 'Content-Type': 'application/json',
36
- },
37
- body: JSON.stringify({
38
- game: 'chess',
39
- matchType: 'matchmaking',
40
- players: [{ id: 'user_123', displayName: 'Alex' }],
41
- }),
33
+ // Your server e.g. Express, Next.js API route, Cloudflare Worker
34
+ app.post('/api/game/start', async (req, res) => {
35
+ const { userId, userName, matchType } = req.body;
36
+ const response = await fetch('https://api.beta-gamer.com/v1/sessions', {
37
+ method: 'POST',
38
+ headers: {
39
+ 'Authorization': 'Bearer bg_live_xxxx',
40
+ 'Content-Type': 'application/json',
41
+ },
42
+ body: JSON.stringify({
43
+ game: 'chess',
44
+ matchType,
45
+ players: [{ id: userId, displayName: userName }],
46
+ }),
47
+ });
48
+ const data = await response.json();
49
+ res.json({ sessionToken: data.sessionToken });
42
50
  });
43
- const { sessionToken } = await res.json();
44
51
  ```
45
52
 
46
- **2. Pass the token to your screen and render**
53
+ > ⚠️ **Never call the Beta Gamer API from your app.** Your API key would be bundled into the binary and is trivially extractable from any APK or IPA.
47
54
 
48
- ```tsx
49
- import {
50
- BetaGamerProvider,
51
- ChessBoard,
52
- ChessMoveHistory,
53
- PlayerCard,
54
- Timer,
55
- } from '@beta-gamer/react-native';
56
- import { View, StyleSheet } from 'react-native';
55
+ **2. Lobby screen — fetch the token**
57
56
 
58
- export default function GameScreen({ sessionToken }: { sessionToken: string }) {
57
+ ```tsx
58
+ import { useState } from 'react';
59
+ import { View, Button, ActivityIndicator } from 'react-native';
60
+ import { useNavigation } from '@react-navigation/native';
61
+
62
+ export function ChessLobbyScreen() {
63
+ const navigation = useNavigation<any>();
64
+ const [loading, setLoading] = useState(false);
65
+
66
+ const startGame = async (matchType: 'matchmaking' | 'bot') => {
67
+ setLoading(true);
68
+ const res = await fetch('https://your-api.com/api/game/start', {
69
+ method: 'POST',
70
+ headers: { 'Content-Type': 'application/json' },
71
+ body: JSON.stringify({ game: 'chess', matchType }),
72
+ });
73
+ const { sessionToken } = await res.json();
74
+ navigation.navigate('ChessGame', { sessionToken });
75
+ };
76
+
77
+ if (loading) return <ActivityIndicator />;
59
78
  return (
60
- <BetaGamerProvider token={sessionToken}>
61
- <View style={styles.layout}>
62
- <PlayerCard player="opponent" style={styles.card} nameStyle={styles.name} />
63
- <Timer player="opponent" style={styles.timer} textStyle={styles.clock} />
79
+ <View>
80
+ <Button title="Find opponent" onPress={() => startGame('matchmaking')} />
81
+ <Button title="vs Bot" onPress={() => startGame('bot')} />
82
+ </View>
83
+ );
84
+ }
85
+ ```
64
86
 
65
- <ChessBoard style={styles.board} />
66
- <ChessMoveHistory style={styles.history} textStyle={styles.move} />
87
+ **3. Game screen — drop in `ChessBoard`**
67
88
 
68
- <PlayerCard player="self" style={styles.card} nameStyle={styles.name} />
69
- <Timer player="self" style={styles.timer} textStyle={styles.clock} />
89
+ ```tsx
90
+ import { View, StyleSheet } from 'react-native';
91
+ import { useRoute, useNavigation } from '@react-navigation/native';
92
+ import { BetaGamerProvider, ChessBoard } from '@beta-gamer/react-native';
93
+
94
+ export function ChessGameScreen() {
95
+ const { sessionToken } = useRoute<any>().params;
96
+ const navigation = useNavigation();
97
+
98
+ return (
99
+ <BetaGamerProvider
100
+ token={sessionToken}
101
+ serverUrl="https://api.beta-gamer.com"
102
+ connectSocket={false} // ChessBoard manages its own socket
103
+ >
104
+ <View style={styles.screen}>
105
+ <ChessBoard
106
+ style={styles.board}
107
+ onLeave={() => navigation.goBack()}
108
+ />
70
109
  </View>
71
110
  </BetaGamerProvider>
72
111
  );
73
112
  }
74
113
 
75
114
  const styles = StyleSheet.create({
76
- layout: { flex: 1, backgroundColor: '#0f0f0f' },
115
+ screen: { flex: 1, backgroundColor: '#0a0a0f' },
77
116
  board: { flex: 1 },
78
- // ... your styles
79
117
  });
80
118
  ```
81
119
 
120
+ That's it. The board handles everything — no extra components needed.
121
+
122
+ ---
123
+
124
+ ## `BetaGamerProvider` props
125
+
126
+ | Prop | Type | Default | Description |
127
+ |------|------|---------|-------------|
128
+ | `token` | `string` | required | Session token from your backend |
129
+ | `serverUrl` | `string` | `'https://api.beta-gamer.com'` | Your Beta Gamer server URL |
130
+ | `socketPath` | `string` | `'/socket.io'` | Socket.IO path (change if self-hosting) |
131
+ | `connectSocket` | `boolean` | `true` | Set `false` when using board components — they manage their own socket. Using `true` with a board component creates a duplicate connection causing AFK and turn bugs. |
132
+
133
+ ---
134
+
82
135
  ## Components
83
136
 
84
- ### Shared (all games)
137
+ ### Chess, Checkers, Connect4, Tictactoe (fully native — no WebView)
85
138
 
86
139
  | Component | Props | Description |
87
140
  |-----------|-------|-------------|
88
- | `BetaGamerProvider` | `token`, `serverUrl?` | Wrap your game UI with this. Handles socket connection and theme. |
89
- | `PlayerCard` | `player`, `style?`, `nameStyle?`, `indicatorStyle?` | Player name + active-turn indicator |
90
- | `Timer` | `player`, `initialSeconds?`, `style?`, `textStyle?` | Live countdown clock, syncs with server |
141
+ | `ChessBoard` | `style?`, `showAfkWarning?`, `onLeave?` | Full chess game socket, board, clocks, AFK, promotion, game-over all built in |
142
+ | `ChessMoveHistory` | `style?`, `textStyle?`, `rowStyle?` | Scrollable move list in algebraic notation |
143
+ | `CheckersBoard` | `style?`, `showAfkWarning?`, `onLeave?` | Full checkers game — piece selection, valid moves, captures, king promotion |
144
+ | `Connect4Board` | `style?`, `showAfkWarning?`, `onLeave?` | Full connect4 game — column drop, winning cell highlight |
145
+ | `TictactoeBoard` | `style?`, `showAfkWarning?`, `onLeave?` | Full tictactoe game — mark placement, winning line highlight |
146
+
147
+ ### Subway Runner (WebView-based)
148
+
149
+ | Component | Props | Game |
150
+ |-----------|-------|------|
151
+ | `SubwayRunnerGame`, `SubwayRunnerScore`, `SubwayRunnerLives` | `style?` | subway-runner |
91
152
 
92
- ### Chess
153
+ ### Shared
93
154
 
94
155
  | Component | Props | Description |
95
156
  |-----------|-------|-------------|
96
- | `ChessBoard` | `style?` | Interactive chess board (WebView) |
97
- | `ChessMoveHistory` | `style?`, `textStyle?`, `rowStyle?` | Scrollable move list in algebraic notation |
157
+ | `PlayerCard` | `player` (`'self'`\|`'opponent'`), `style?`, `nameStyle?` | Player name + active-turn indicator |
158
+ | `Timer` | `player`, `initialSeconds?`, `style?`, `textStyle?` | Live countdown clock |
159
+
160
+ ---
161
+
162
+ ## Board component props (all 4 games)
163
+
164
+ All native board components share the same props:
165
+
166
+ | Prop | Type | Default | Description |
167
+ |------|------|---------|-------------|
168
+ | `style` | `ViewStyle` | — | Style for the outer container |
169
+ | `showAfkWarning` | `boolean` | `true` | Show built-in AFK countdown banner. Set `false` to implement your own |
170
+ | `onLeave` | `() => void` | — | Called when player taps Leave or Play again after game over |
171
+
172
+ ---
98
173
 
99
- ### Other games
174
+ ## `showAfkWarning` — custom AFK UI
100
175
 
101
- | Component | Game |
102
- |-----------|------|
103
- | `CheckersBoard` | checkers |
104
- | `Connect4Board` | connect4 |
105
- | `TictactoeBoard` | tictactoe |
106
- | `SubwayRunnerGame`, `SubwayRunnerScore`, `SubwayRunnerLives` | subway-runner |
176
+ If you want to render your own AFK banner, pass `showAfkWarning={false}` and listen to socket events manually:
177
+
178
+ ```tsx
179
+ import { useEffect } from 'react';
180
+ import { useSocket } from '@beta-gamer/react-native';
181
+
182
+ function MyAfkBanner() {
183
+ const socket = useSocket();
184
+
185
+ useEffect(() => {
186
+ if (!socket) return;
187
+ socket.on('chess:afk_warning', ({ playerId, secondsRemaining }) => {
188
+ // show your own banner
189
+ });
190
+ socket.on('chess:afk_warning_cleared', () => {
191
+ // hide banner
192
+ });
193
+ return () => {
194
+ socket.off('chess:afk_warning');
195
+ socket.off('chess:afk_warning_cleared');
196
+ };
197
+ }, [socket]);
198
+
199
+ return null;
200
+ }
201
+ ```
202
+
203
+ > Note: custom AFK UI only works when using hooks (`useSocket`) with `connectSocket={true}`. `ChessBoard` manages its own internal socket — use `showAfkWarning={false}` on the component itself instead.
204
+
205
+ ---
107
206
 
108
207
  ## Hooks
109
208
 
209
+ Available when `connectSocket={true}` (default) and building a fully custom game UI:
210
+
110
211
  ```tsx
111
- import { useGameState, useSession, useSocket, useTheme } from '@beta-gamer/react-native';
212
+ import { useGameState, useSession, useSocket } from '@beta-gamer/react-native';
112
213
 
113
214
  const { status, winner, reason } = useGameState();
114
- const { game, mode, matchType, players } = useSession();
115
- const socket = useSocket(); // raw Socket.IO socket for custom events
116
- const theme = useTheme(); // active theme tokens
215
+ const { game, matchType, players } = useSession();
216
+ const socket = useSocket(); // raw Socket.IO socket
117
217
  ```
118
218
 
119
- ## Differences from `@beta-gamer/react`
120
-
121
- | React | React Native |
122
- |-------|-------------|
123
- | `className` prop | `style` / `textStyle` / `rowStyle` props |
124
- | CSS variables for theming | Theme injected via WebView script |
125
- | `<div>`, `<span>` | `<View>`, `<Text>` |
126
- | Move history scrolls via CSS | Wrapped in `<ScrollView>` |
219
+ ---
127
220
 
128
221
  ## Supported games
129
222
 
130
223
  `chess` · `checkers` · `connect4` · `tictactoe` · `subway-runner`
131
224
 
225
+ ---
226
+
132
227
  ## Links
133
228
 
134
229
  - [Documentation](https://beta-gamer.com/docs)
135
230
  - [Dashboard](https://beta-gamer.com/dashboard)
136
231
  - [React SDK](https://www.npmjs.com/package/@beta-gamer/react)
232
+ - [Angular SDK](https://www.npmjs.com/package/@beta-gamer/angular)
package/dist/index.d.mts CHANGED
@@ -52,9 +52,10 @@ interface BetaGamerProviderProps {
52
52
  token: string;
53
53
  serverUrl?: string;
54
54
  socketPath?: string;
55
+ connectSocket?: boolean;
55
56
  children: React.ReactNode;
56
57
  }
57
- declare function BetaGamerProvider({ token, serverUrl, socketPath, children }: BetaGamerProviderProps): react_jsx_runtime.JSX.Element;
58
+ declare function BetaGamerProvider({ token, serverUrl, socketPath, connectSocket, children }: BetaGamerProviderProps): react_jsx_runtime.JSX.Element;
58
59
  declare function useBetaGamer(): BetaGamerContextValue;
59
60
 
60
61
  /** Returns the current game state (status, players, winner, etc.) */
@@ -82,9 +83,12 @@ interface TimerProps {
82
83
  }
83
84
  declare function Timer({ player, initialSeconds, style, textStyle }: TimerProps): react_jsx_runtime.JSX.Element;
84
85
 
85
- declare function ChessBoard({ style }: {
86
+ interface Props$3 {
86
87
  style?: ViewStyle;
87
- }): react_jsx_runtime.JSX.Element;
88
+ showAfkWarning?: boolean;
89
+ onLeave?: () => void;
90
+ }
91
+ declare function ChessBoard({ style, showAfkWarning, onLeave }: Props$3): react_jsx_runtime.JSX.Element | null;
88
92
 
89
93
  interface ChessMoveHistoryProps {
90
94
  style?: ViewStyle;
@@ -93,17 +97,26 @@ interface ChessMoveHistoryProps {
93
97
  }
94
98
  declare function ChessMoveHistory({ style, rowStyle, textStyle }: ChessMoveHistoryProps): react_jsx_runtime.JSX.Element;
95
99
 
96
- declare function CheckersBoard({ style }: {
100
+ interface Props$2 {
97
101
  style?: ViewStyle;
98
- }): react_jsx_runtime.JSX.Element;
102
+ showAfkWarning?: boolean;
103
+ onLeave?: () => void;
104
+ }
105
+ declare function CheckersBoard({ style, showAfkWarning, onLeave }: Props$2): react_jsx_runtime.JSX.Element | null;
99
106
 
100
- declare function Connect4Board({ style }: {
107
+ interface Props$1 {
101
108
  style?: ViewStyle;
102
- }): react_jsx_runtime.JSX.Element;
109
+ showAfkWarning?: boolean;
110
+ onLeave?: () => void;
111
+ }
112
+ declare function Connect4Board({ style, showAfkWarning, onLeave }: Props$1): react_jsx_runtime.JSX.Element | null;
103
113
 
104
- declare function TictactoeBoard({ style }: {
114
+ interface Props {
105
115
  style?: ViewStyle;
106
- }): react_jsx_runtime.JSX.Element;
116
+ showAfkWarning?: boolean;
117
+ onLeave?: () => void;
118
+ }
119
+ declare function TictactoeBoard({ style, showAfkWarning, onLeave }: Props): react_jsx_runtime.JSX.Element | null;
107
120
 
108
121
  declare function SubwayRunnerGame({ style }: {
109
122
  style?: ViewStyle;
package/dist/index.d.ts CHANGED
@@ -52,9 +52,10 @@ interface BetaGamerProviderProps {
52
52
  token: string;
53
53
  serverUrl?: string;
54
54
  socketPath?: string;
55
+ connectSocket?: boolean;
55
56
  children: React.ReactNode;
56
57
  }
57
- declare function BetaGamerProvider({ token, serverUrl, socketPath, children }: BetaGamerProviderProps): react_jsx_runtime.JSX.Element;
58
+ declare function BetaGamerProvider({ token, serverUrl, socketPath, connectSocket, children }: BetaGamerProviderProps): react_jsx_runtime.JSX.Element;
58
59
  declare function useBetaGamer(): BetaGamerContextValue;
59
60
 
60
61
  /** Returns the current game state (status, players, winner, etc.) */
@@ -82,9 +83,12 @@ interface TimerProps {
82
83
  }
83
84
  declare function Timer({ player, initialSeconds, style, textStyle }: TimerProps): react_jsx_runtime.JSX.Element;
84
85
 
85
- declare function ChessBoard({ style }: {
86
+ interface Props$3 {
86
87
  style?: ViewStyle;
87
- }): react_jsx_runtime.JSX.Element;
88
+ showAfkWarning?: boolean;
89
+ onLeave?: () => void;
90
+ }
91
+ declare function ChessBoard({ style, showAfkWarning, onLeave }: Props$3): react_jsx_runtime.JSX.Element | null;
88
92
 
89
93
  interface ChessMoveHistoryProps {
90
94
  style?: ViewStyle;
@@ -93,17 +97,26 @@ interface ChessMoveHistoryProps {
93
97
  }
94
98
  declare function ChessMoveHistory({ style, rowStyle, textStyle }: ChessMoveHistoryProps): react_jsx_runtime.JSX.Element;
95
99
 
96
- declare function CheckersBoard({ style }: {
100
+ interface Props$2 {
97
101
  style?: ViewStyle;
98
- }): react_jsx_runtime.JSX.Element;
102
+ showAfkWarning?: boolean;
103
+ onLeave?: () => void;
104
+ }
105
+ declare function CheckersBoard({ style, showAfkWarning, onLeave }: Props$2): react_jsx_runtime.JSX.Element | null;
99
106
 
100
- declare function Connect4Board({ style }: {
107
+ interface Props$1 {
101
108
  style?: ViewStyle;
102
- }): react_jsx_runtime.JSX.Element;
109
+ showAfkWarning?: boolean;
110
+ onLeave?: () => void;
111
+ }
112
+ declare function Connect4Board({ style, showAfkWarning, onLeave }: Props$1): react_jsx_runtime.JSX.Element | null;
103
113
 
104
- declare function TictactoeBoard({ style }: {
114
+ interface Props {
105
115
  style?: ViewStyle;
106
- }): react_jsx_runtime.JSX.Element;
116
+ showAfkWarning?: boolean;
117
+ onLeave?: () => void;
118
+ }
119
+ declare function TictactoeBoard({ style, showAfkWarning, onLeave }: Props): react_jsx_runtime.JSX.Element | null;
107
120
 
108
121
  declare function SubwayRunnerGame({ style }: {
109
122
  style?: ViewStyle;