@beta-gamer/react-native 0.1.19 → 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,134 +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
+ ```
86
+
87
+ **3. Game screen — drop in `ChessBoard`**
64
88
 
65
- <ChessBoard style={styles.board} />
66
- <ChessMoveHistory style={styles.history} textStyle={styles.move} />
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();
67
97
 
68
- <PlayerCard player="self" style={styles.card} nameStyle={styles.name} />
69
- <Timer player="self" style={styles.timer} textStyle={styles.clock} />
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
 
82
- ## Components
120
+ That's it. The board handles everything — no extra components needed.
83
121
 
84
- ### Shared (all games)
122
+ ---
85
123
 
86
- | Component | Props | Description |
87
- |-----------|-------|-------------|
88
- | `BetaGamerProvider` | `token`, `serverUrl?`, `socketPath?` | 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 |
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
+
135
+ ## Components
91
136
 
92
- ### Chess
137
+ ### Chess, Checkers, Connect4, Tictactoe (fully native — no WebView)
93
138
 
94
139
  | Component | Props | Description |
95
140
  |-----------|-------|-------------|
96
- | `ChessBoard` | `style?`, `showAfkWarning?` | Interactive chess board (WebView) |
141
+ | `ChessBoard` | `style?`, `showAfkWarning?`, `onLeave?` | Full chess game — socket, board, clocks, AFK, promotion, game-over all built in |
97
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 |
98
146
 
99
- ### Other games
147
+ ### Subway Runner (WebView-based)
100
148
 
101
149
  | Component | Props | Game |
102
150
  |-----------|-------|------|
103
- | `CheckersBoard` | `style?`, `showAfkWarning?` | checkers |
104
- | `Connect4Board` | `style?`, `showAfkWarning?` | connect4 |
105
- | `TictactoeBoard` | `style?`, `showAfkWarning?` | tictactoe |
106
151
  | `SubwayRunnerGame`, `SubwayRunnerScore`, `SubwayRunnerLives` | `style?` | subway-runner |
107
152
 
108
- > **`showAfkWarning`** (default `true`) — set to `false` to hide the built-in AFK countdown banner and implement your own using the `{game}:afk_warning` / `{game}:afk_warning_cleared` socket events.
153
+ ### Shared
154
+
155
+ | Component | Props | Description |
156
+ |-----------|-------|-------------|
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
+ ---
173
+
174
+ ## `showAfkWarning` — custom AFK UI
175
+
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
+ ---
109
206
 
110
207
  ## Hooks
111
208
 
209
+ Available when `connectSocket={true}` (default) and building a fully custom game UI:
210
+
112
211
  ```tsx
113
- import { useGameState, useSession, useSocket, useTheme } from '@beta-gamer/react-native';
212
+ import { useGameState, useSession, useSocket } from '@beta-gamer/react-native';
114
213
 
115
214
  const { status, winner, reason } = useGameState();
116
- const { game, mode, matchType, players } = useSession();
117
- const socket = useSocket(); // raw Socket.IO socket for custom events
118
- const theme = useTheme(); // active theme tokens
215
+ const { game, matchType, players } = useSession();
216
+ const socket = useSocket(); // raw Socket.IO socket
119
217
  ```
120
218
 
121
- ## Differences from `@beta-gamer/react`
122
-
123
- | React | React Native |
124
- |-------|-------------|
125
- | `className` prop | `style` / `textStyle` / `rowStyle` props |
126
- | CSS variables for theming | Theme injected via WebView script |
127
- | `<div>`, `<span>` | `<View>`, `<Text>` |
128
- | Move history scrolls via CSS | Wrapped in `<ScrollView>` |
219
+ ---
129
220
 
130
221
  ## Supported games
131
222
 
132
223
  `chess` · `checkers` · `connect4` · `tictactoe` · `subway-runner`
133
224
 
225
+ ---
226
+
134
227
  ## Links
135
228
 
136
229
  - [Documentation](https://beta-gamer.com/docs)
137
230
  - [Dashboard](https://beta-gamer.com/dashboard)
138
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,10 +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, showAfkWarning }: {
86
+ interface Props$3 {
86
87
  style?: ViewStyle;
87
88
  showAfkWarning?: boolean;
88
- }): react_jsx_runtime.JSX.Element;
89
+ onLeave?: () => void;
90
+ }
91
+ declare function ChessBoard({ style, showAfkWarning, onLeave }: Props$3): react_jsx_runtime.JSX.Element | null;
89
92
 
90
93
  interface ChessMoveHistoryProps {
91
94
  style?: ViewStyle;
@@ -94,20 +97,26 @@ interface ChessMoveHistoryProps {
94
97
  }
95
98
  declare function ChessMoveHistory({ style, rowStyle, textStyle }: ChessMoveHistoryProps): react_jsx_runtime.JSX.Element;
96
99
 
97
- declare function CheckersBoard({ style, showAfkWarning }: {
100
+ interface Props$2 {
98
101
  style?: ViewStyle;
99
102
  showAfkWarning?: boolean;
100
- }): react_jsx_runtime.JSX.Element;
103
+ onLeave?: () => void;
104
+ }
105
+ declare function CheckersBoard({ style, showAfkWarning, onLeave }: Props$2): react_jsx_runtime.JSX.Element | null;
101
106
 
102
- declare function Connect4Board({ style, showAfkWarning }: {
107
+ interface Props$1 {
103
108
  style?: ViewStyle;
104
109
  showAfkWarning?: boolean;
105
- }): react_jsx_runtime.JSX.Element;
110
+ onLeave?: () => void;
111
+ }
112
+ declare function Connect4Board({ style, showAfkWarning, onLeave }: Props$1): react_jsx_runtime.JSX.Element | null;
106
113
 
107
- declare function TictactoeBoard({ style, showAfkWarning }: {
114
+ interface Props {
108
115
  style?: ViewStyle;
109
116
  showAfkWarning?: boolean;
110
- }): react_jsx_runtime.JSX.Element;
117
+ onLeave?: () => void;
118
+ }
119
+ declare function TictactoeBoard({ style, showAfkWarning, onLeave }: Props): react_jsx_runtime.JSX.Element | null;
111
120
 
112
121
  declare function SubwayRunnerGame({ style }: {
113
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,10 +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, showAfkWarning }: {
86
+ interface Props$3 {
86
87
  style?: ViewStyle;
87
88
  showAfkWarning?: boolean;
88
- }): react_jsx_runtime.JSX.Element;
89
+ onLeave?: () => void;
90
+ }
91
+ declare function ChessBoard({ style, showAfkWarning, onLeave }: Props$3): react_jsx_runtime.JSX.Element | null;
89
92
 
90
93
  interface ChessMoveHistoryProps {
91
94
  style?: ViewStyle;
@@ -94,20 +97,26 @@ interface ChessMoveHistoryProps {
94
97
  }
95
98
  declare function ChessMoveHistory({ style, rowStyle, textStyle }: ChessMoveHistoryProps): react_jsx_runtime.JSX.Element;
96
99
 
97
- declare function CheckersBoard({ style, showAfkWarning }: {
100
+ interface Props$2 {
98
101
  style?: ViewStyle;
99
102
  showAfkWarning?: boolean;
100
- }): react_jsx_runtime.JSX.Element;
103
+ onLeave?: () => void;
104
+ }
105
+ declare function CheckersBoard({ style, showAfkWarning, onLeave }: Props$2): react_jsx_runtime.JSX.Element | null;
101
106
 
102
- declare function Connect4Board({ style, showAfkWarning }: {
107
+ interface Props$1 {
103
108
  style?: ViewStyle;
104
109
  showAfkWarning?: boolean;
105
- }): react_jsx_runtime.JSX.Element;
110
+ onLeave?: () => void;
111
+ }
112
+ declare function Connect4Board({ style, showAfkWarning, onLeave }: Props$1): react_jsx_runtime.JSX.Element | null;
106
113
 
107
- declare function TictactoeBoard({ style, showAfkWarning }: {
114
+ interface Props {
108
115
  style?: ViewStyle;
109
116
  showAfkWarning?: boolean;
110
- }): react_jsx_runtime.JSX.Element;
117
+ onLeave?: () => void;
118
+ }
119
+ declare function TictactoeBoard({ style, showAfkWarning, onLeave }: Props): react_jsx_runtime.JSX.Element | null;
111
120
 
112
121
  declare function SubwayRunnerGame({ style }: {
113
122
  style?: ViewStyle;