@pickleball/expo-sdk 0.1.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/CHANGELOG.md +14 -0
- package/LICENSE +21 -0
- package/README.md +75 -0
- package/app.plugin.js +6 -0
- package/dist/chunk-VHLUJQG5.js +17 -0
- package/dist/index.cjs +6284 -0
- package/dist/index.d.cts +1436 -0
- package/dist/index.d.ts +1436 -0
- package/dist/index.js +6186 -0
- package/dist/plugin.cjs +153 -0
- package/dist/plugin.d.cts +21 -0
- package/dist/plugin.d.ts +21 -0
- package/dist/plugin.js +131 -0
- package/dist/version.cjs +35 -0
- package/dist/version.d.cts +4 -0
- package/dist/version.d.ts +4 -0
- package/dist/version.js +6 -0
- package/package.json +126 -0
- package/src/contracts.ts +211 -0
- package/src/engine.ts +1332 -0
- package/src/errors.ts +128 -0
- package/src/http-session-provider.ts +259 -0
- package/src/index.ts +16 -0
- package/src/livekit-room-adapter.ts +353 -0
- package/src/livestream-screen.tsx +980 -0
- package/src/match/contracts.ts +137 -0
- package/src/match/engine.ts +702 -0
- package/src/match/persistence/default-store.native.ts +20 -0
- package/src/match/persistence/default-store.ts +11 -0
- package/src/match/persistence/index.ts +6 -0
- package/src/match/persistence/memory-driver.ts +255 -0
- package/src/match/persistence/migrations.ts +40 -0
- package/src/match/persistence/sqlite-driver.ts +348 -0
- package/src/match/persistence/store.ts +497 -0
- package/src/match/persistence/types.ts +98 -0
- package/src/match/provider.tsx +728 -0
- package/src/match/remote/deduplicator.ts +54 -0
- package/src/match/remote/encoding.ts +12 -0
- package/src/match/remote/hid-gesture.ts +61 -0
- package/src/match/remote/index.ts +5 -0
- package/src/match/remote/native-service.ts +62 -0
- package/src/match/remote/parser.ts +134 -0
- package/src/match/remote/types.ts +116 -0
- package/src/match/replay.ts +102 -0
- package/src/match/state.ts +72 -0
- package/src/match/validation.ts +375 -0
- package/src/match/voice/assets/court-left.wav +0 -0
- package/src/match/voice/assets/court-right.wav +0 -0
- package/src/match/voice/assets/court-switch.wav +0 -0
- package/src/match/voice/assets/game-point.wav +0 -0
- package/src/match/voice/assets/game-won.wav +0 -0
- package/src/match/voice/assets/match-won.wav +0 -0
- package/src/match/voice/assets/number-0.wav +0 -0
- package/src/match/voice/assets/number-1.wav +0 -0
- package/src/match/voice/assets/number-10.wav +0 -0
- package/src/match/voice/assets/number-11.wav +0 -0
- package/src/match/voice/assets/number-12.wav +0 -0
- package/src/match/voice/assets/number-13.wav +0 -0
- package/src/match/voice/assets/number-14.wav +0 -0
- package/src/match/voice/assets/number-15.wav +0 -0
- package/src/match/voice/assets/number-16.wav +0 -0
- package/src/match/voice/assets/number-17.wav +0 -0
- package/src/match/voice/assets/number-18.wav +0 -0
- package/src/match/voice/assets/number-19.wav +0 -0
- package/src/match/voice/assets/number-2.wav +0 -0
- package/src/match/voice/assets/number-20.wav +0 -0
- package/src/match/voice/assets/number-3.wav +0 -0
- package/src/match/voice/assets/number-30.wav +0 -0
- package/src/match/voice/assets/number-4.wav +0 -0
- package/src/match/voice/assets/number-40.wav +0 -0
- package/src/match/voice/assets/number-5.wav +0 -0
- package/src/match/voice/assets/number-50.wav +0 -0
- package/src/match/voice/assets/number-6.wav +0 -0
- package/src/match/voice/assets/number-60.wav +0 -0
- package/src/match/voice/assets/number-7.wav +0 -0
- package/src/match/voice/assets/number-70.wav +0 -0
- package/src/match/voice/assets/number-8.wav +0 -0
- package/src/match/voice/assets/number-80.wav +0 -0
- package/src/match/voice/assets/number-9.wav +0 -0
- package/src/match/voice/assets/number-90.wav +0 -0
- package/src/match/voice/assets/number-lam.wav +0 -0
- package/src/match/voice/assets/number-mot.wav +0 -0
- package/src/match/voice/assets/score-deu-0.wav +0 -0
- package/src/match/voice/assets/score-deu-1.wav +0 -0
- package/src/match/voice/assets/score-deu-10.wav +0 -0
- package/src/match/voice/assets/score-deu-11.wav +0 -0
- package/src/match/voice/assets/score-deu-12.wav +0 -0
- package/src/match/voice/assets/score-deu-13.wav +0 -0
- package/src/match/voice/assets/score-deu-14.wav +0 -0
- package/src/match/voice/assets/score-deu-15.wav +0 -0
- package/src/match/voice/assets/score-deu-16.wav +0 -0
- package/src/match/voice/assets/score-deu-17.wav +0 -0
- package/src/match/voice/assets/score-deu-18.wav +0 -0
- package/src/match/voice/assets/score-deu-19.wav +0 -0
- package/src/match/voice/assets/score-deu-2.wav +0 -0
- package/src/match/voice/assets/score-deu-20.wav +0 -0
- package/src/match/voice/assets/score-deu-3.wav +0 -0
- package/src/match/voice/assets/score-deu-30.wav +0 -0
- package/src/match/voice/assets/score-deu-4.wav +0 -0
- package/src/match/voice/assets/score-deu-40.wav +0 -0
- package/src/match/voice/assets/score-deu-5.wav +0 -0
- package/src/match/voice/assets/score-deu-50.wav +0 -0
- package/src/match/voice/assets/score-deu-6.wav +0 -0
- package/src/match/voice/assets/score-deu-60.wav +0 -0
- package/src/match/voice/assets/score-deu-7.wav +0 -0
- package/src/match/voice/assets/score-deu-70.wav +0 -0
- package/src/match/voice/assets/score-deu-8.wav +0 -0
- package/src/match/voice/assets/score-deu-80.wav +0 -0
- package/src/match/voice/assets/score-deu-9.wav +0 -0
- package/src/match/voice/assets/score-deu-90.wav +0 -0
- package/src/match/voice/assets/score-deu-lam.wav +0 -0
- package/src/match/voice/assets/score-deu-mot.wav +0 -0
- package/src/match/voice/assets/server-1.wav +0 -0
- package/src/match/voice/assets/server-2.wav +0 -0
- package/src/match/voice/assets/service-change.wav +0 -0
- package/src/match/voice/assets/test-speaker.wav +0 -0
- package/src/match/voice/audio.ts +231 -0
- package/src/match/voice/bundled-assets.native.ts +47 -0
- package/src/match/voice/bundled-assets.ts +3 -0
- package/src/match/voice/gemini-engine.ts +167 -0
- package/src/match/voice/index.ts +7 -0
- package/src/match/voice/phrases.ts +185 -0
- package/src/match/voice/scheduler.ts +205 -0
- package/src/match/voice/service.ts +147 -0
- package/src/match/voice/types.ts +118 -0
- package/src/native-runtime.ts +241 -0
- package/src/plugin.ts +145 -0
- package/src/provider.tsx +176 -0
- package/src/version.ts +4 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,1436 @@
|
|
|
1
|
+
import React, { ReactNode } from 'react';
|
|
2
|
+
import { ViewStyle } from 'react-native';
|
|
3
|
+
export { PICKLEBALL_EXPO_SDK_VERSION } from './version.js';
|
|
4
|
+
|
|
5
|
+
type LivestreamState = "idle" | "preparing" | "preview" | "connecting"
|
|
6
|
+
/** Đã vào phòng, chưa phát — chờ lệnh phát từ app hoặc dashboard. */
|
|
7
|
+
| "standby" | "live" | "reconnecting" | "ending" | "ended" | "error";
|
|
8
|
+
type SdkErrorCode = "PERMISSION_DENIED" | "NETWORK_UNAVAILABLE" | "TOKEN_EXPIRED" | "SDK_UPGRADE_REQUIRED" | "SESSION_CONFLICT" | "RECORDING_FAILED" | "CONSENT_REQUIRED" | "SDK_DISABLED" | "UNKNOWN";
|
|
9
|
+
interface SdkError {
|
|
10
|
+
code: SdkErrorCode;
|
|
11
|
+
message: string;
|
|
12
|
+
}
|
|
13
|
+
interface BootstrapInput {
|
|
14
|
+
sdkVersion: string;
|
|
15
|
+
platform: "ios" | "android";
|
|
16
|
+
bundleId: string;
|
|
17
|
+
installationId: string;
|
|
18
|
+
}
|
|
19
|
+
interface SdkRemoteConfig {
|
|
20
|
+
enabled: boolean;
|
|
21
|
+
protocolVersion: 1;
|
|
22
|
+
minSdkVersion: string;
|
|
23
|
+
recommendedSdkVersion: string;
|
|
24
|
+
videoQuality: 720 | 1080;
|
|
25
|
+
reconnectTimeoutMs: number;
|
|
26
|
+
backgroundGraceMs: number;
|
|
27
|
+
telemetryIntervalMs: number;
|
|
28
|
+
maxSessionDurationMs: number;
|
|
29
|
+
/** Tự kết thúc phiên nếu ngồi standby quá lâu không phát (mặc định 15 phút). */
|
|
30
|
+
standbyTimeoutMs?: number;
|
|
31
|
+
}
|
|
32
|
+
interface SdkBootstrap {
|
|
33
|
+
config: SdkRemoteConfig;
|
|
34
|
+
serverTime: number;
|
|
35
|
+
rolloutEnabled: boolean;
|
|
36
|
+
message?: string;
|
|
37
|
+
}
|
|
38
|
+
interface StartLivestreamInput {
|
|
39
|
+
externalSessionId: string;
|
|
40
|
+
title: string;
|
|
41
|
+
visibility?: "public" | "private";
|
|
42
|
+
consentVersion: string;
|
|
43
|
+
metadata?: Record<string, string>;
|
|
44
|
+
/**
|
|
45
|
+
* true: vào phòng ở chế độ chờ (không publish) — server giữ status
|
|
46
|
+
* "scheduled" và KHÔNG khởi động recording egress cho tới khi publish.
|
|
47
|
+
* Engine tự đặt cờ này theo options.autoPublish; app không cần truyền.
|
|
48
|
+
*/
|
|
49
|
+
standby?: boolean;
|
|
50
|
+
}
|
|
51
|
+
interface SdkTelemetryCredentials {
|
|
52
|
+
endpoint: string;
|
|
53
|
+
token: string;
|
|
54
|
+
expiresAt: number;
|
|
55
|
+
}
|
|
56
|
+
interface SdkSessionGrant {
|
|
57
|
+
sessionId: string;
|
|
58
|
+
serverUrl: string;
|
|
59
|
+
participantToken: string;
|
|
60
|
+
playbackUrl: string | null;
|
|
61
|
+
tokenExpiresAt: number;
|
|
62
|
+
telemetry: SdkTelemetryCredentials;
|
|
63
|
+
config: SdkRemoteConfig;
|
|
64
|
+
}
|
|
65
|
+
interface SdkSessionRefresh {
|
|
66
|
+
participantToken?: string;
|
|
67
|
+
tokenExpiresAt?: number;
|
|
68
|
+
telemetry?: SdkTelemetryCredentials;
|
|
69
|
+
config: SdkRemoteConfig;
|
|
70
|
+
}
|
|
71
|
+
interface EndLivestreamInput {
|
|
72
|
+
sessionId: string;
|
|
73
|
+
reason: "user" | "background_timeout" | "network_timeout" | "standby_timeout" | "error";
|
|
74
|
+
}
|
|
75
|
+
type EndReason = EndLivestreamInput["reason"];
|
|
76
|
+
declare const SDK_TELEMETRY_EVENT_NAMES: readonly ["state_changed", "connection_quality", "reconnect", "error", "heartbeat", "session_ended"];
|
|
77
|
+
type SdkTelemetryEventName = (typeof SDK_TELEMETRY_EVENT_NAMES)[number];
|
|
78
|
+
interface SdkTelemetryEvent {
|
|
79
|
+
sessionId: string;
|
|
80
|
+
at: number;
|
|
81
|
+
name: SdkTelemetryEventName;
|
|
82
|
+
data?: Record<string, unknown>;
|
|
83
|
+
}
|
|
84
|
+
interface LivestreamSessionProvider {
|
|
85
|
+
bootstrap(input: BootstrapInput): Promise<SdkBootstrap>;
|
|
86
|
+
start(input: StartLivestreamInput): Promise<SdkSessionGrant>;
|
|
87
|
+
refresh(sessionId: string): Promise<SdkSessionRefresh>;
|
|
88
|
+
end(input: EndLivestreamInput): Promise<void>;
|
|
89
|
+
/**
|
|
90
|
+
* Báo server bắt đầu phát thật (standby → live): server bật recording
|
|
91
|
+
* egress + chuyển status live TRƯỚC khi engine publish track. Optional để
|
|
92
|
+
* giữ tương thích provider cũ — thiếu thì engine bỏ qua bước server.
|
|
93
|
+
*/
|
|
94
|
+
publish?(sessionId: string): Promise<void>;
|
|
95
|
+
/** Báo server tạm dừng phát (live → standby): dừng egress, status về chờ. */
|
|
96
|
+
unpublish?(sessionId: string): Promise<void>;
|
|
97
|
+
}
|
|
98
|
+
declare function isSdkRemoteConfig(value: unknown): value is SdkRemoteConfig;
|
|
99
|
+
declare function compareSemver(left: string, right: string): -1 | 0 | 1;
|
|
100
|
+
declare function isSdkVersionSupported(version: string, minimum: string): boolean;
|
|
101
|
+
|
|
102
|
+
type MatchFormat = "singles" | "doubles";
|
|
103
|
+
type MatchScoringMode = "rally" | "sideOut";
|
|
104
|
+
type MatchTeamId = "A" | "B";
|
|
105
|
+
type CourtSide = "left" | "right";
|
|
106
|
+
type MatchInputSource = "local" | "remote" | "voice" | "ble" | "system";
|
|
107
|
+
interface MatchPlayer {
|
|
108
|
+
readonly id: string;
|
|
109
|
+
readonly name: string;
|
|
110
|
+
}
|
|
111
|
+
interface MatchTeam {
|
|
112
|
+
readonly id: MatchTeamId;
|
|
113
|
+
readonly name?: string;
|
|
114
|
+
/** Defaults to the first player in the roster. */
|
|
115
|
+
readonly initialServerId?: string;
|
|
116
|
+
readonly players: readonly MatchPlayer[];
|
|
117
|
+
}
|
|
118
|
+
interface MatchCreateInput {
|
|
119
|
+
readonly id: string;
|
|
120
|
+
readonly format: MatchFormat;
|
|
121
|
+
readonly scoringMode: MatchScoringMode;
|
|
122
|
+
readonly targetScore?: number;
|
|
123
|
+
readonly initialServingTeam?: MatchTeamId;
|
|
124
|
+
readonly teams: Readonly<Record<MatchTeamId, MatchTeam>>;
|
|
125
|
+
}
|
|
126
|
+
interface TeamScore {
|
|
127
|
+
readonly A: number;
|
|
128
|
+
readonly B: number;
|
|
129
|
+
}
|
|
130
|
+
interface ServeState {
|
|
131
|
+
readonly teamId: MatchTeamId;
|
|
132
|
+
readonly playerId: string;
|
|
133
|
+
readonly serverNumber: 1 | 2;
|
|
134
|
+
readonly court: CourtSide;
|
|
135
|
+
}
|
|
136
|
+
interface GameSnapshot {
|
|
137
|
+
readonly number: 1 | 2 | 3;
|
|
138
|
+
readonly score: TeamScore;
|
|
139
|
+
readonly startingServingTeam: MatchTeamId;
|
|
140
|
+
readonly winner: MatchTeamId | null;
|
|
141
|
+
readonly midGameSwitchCompleted: boolean;
|
|
142
|
+
}
|
|
143
|
+
type PendingCourtSwitch = {
|
|
144
|
+
readonly reason: "gameEnd";
|
|
145
|
+
readonly game: 1 | 2;
|
|
146
|
+
} | {
|
|
147
|
+
readonly reason: "gameThreeMidpoint";
|
|
148
|
+
readonly game: 3;
|
|
149
|
+
};
|
|
150
|
+
interface MatchSnapshot {
|
|
151
|
+
readonly id: string;
|
|
152
|
+
readonly format: MatchFormat;
|
|
153
|
+
readonly scoringMode: MatchScoringMode;
|
|
154
|
+
readonly targetScore: number;
|
|
155
|
+
readonly bestOf: 3;
|
|
156
|
+
readonly teams: Readonly<Record<MatchTeamId, MatchTeam>>;
|
|
157
|
+
readonly designatedServerByTeam: Readonly<Record<MatchTeamId, string>>;
|
|
158
|
+
readonly initialServingTeam: MatchTeamId;
|
|
159
|
+
readonly courtSides: Readonly<Record<CourtSide, MatchTeamId>>;
|
|
160
|
+
readonly playerCourts: Readonly<Record<string, CourtSide>>;
|
|
161
|
+
readonly games: readonly GameSnapshot[];
|
|
162
|
+
readonly currentGame: 1 | 2 | 3;
|
|
163
|
+
readonly gamesWon: TeamScore;
|
|
164
|
+
readonly serve: ServeState;
|
|
165
|
+
readonly pendingCourtSwitch: PendingCourtSwitch | null;
|
|
166
|
+
readonly status: "inProgress" | "completed" | "ended";
|
|
167
|
+
readonly winner: MatchTeamId | null;
|
|
168
|
+
readonly endReason: string | null;
|
|
169
|
+
readonly sequence: number;
|
|
170
|
+
}
|
|
171
|
+
interface MatchCorrection {
|
|
172
|
+
readonly score?: Partial<TeamScore>;
|
|
173
|
+
readonly serve?: ServeState;
|
|
174
|
+
readonly playerCourts?: Readonly<Record<string, CourtSide>>;
|
|
175
|
+
}
|
|
176
|
+
interface MatchEventBase {
|
|
177
|
+
readonly schemaVersion: 1;
|
|
178
|
+
readonly id: string;
|
|
179
|
+
readonly sequence: number;
|
|
180
|
+
readonly source: MatchInputSource;
|
|
181
|
+
}
|
|
182
|
+
interface MatchCreatedEvent extends MatchEventBase {
|
|
183
|
+
readonly type: "matchCreated";
|
|
184
|
+
readonly input: MatchCreateInput;
|
|
185
|
+
readonly snapshot: MatchSnapshot;
|
|
186
|
+
}
|
|
187
|
+
interface RallyAwardedEvent extends MatchEventBase {
|
|
188
|
+
readonly type: "rallyAwarded";
|
|
189
|
+
readonly displaySide: CourtSide;
|
|
190
|
+
readonly winnerTeamId: MatchTeamId;
|
|
191
|
+
}
|
|
192
|
+
interface CourtSwitchConfirmedEvent extends MatchEventBase {
|
|
193
|
+
readonly type: "courtSwitchConfirmed";
|
|
194
|
+
readonly pending: PendingCourtSwitch;
|
|
195
|
+
}
|
|
196
|
+
interface MatchCorrectedEvent extends MatchEventBase {
|
|
197
|
+
readonly type: "matchCorrected";
|
|
198
|
+
readonly correction: MatchCorrection;
|
|
199
|
+
}
|
|
200
|
+
interface MatchEndedEvent extends MatchEventBase {
|
|
201
|
+
readonly type: "matchEnded";
|
|
202
|
+
readonly reason: string;
|
|
203
|
+
}
|
|
204
|
+
interface MatchEventUndoneEvent extends MatchEventBase {
|
|
205
|
+
readonly type: "matchEventUndone";
|
|
206
|
+
readonly targetEventId: string;
|
|
207
|
+
readonly restoredSnapshot: MatchSnapshot;
|
|
208
|
+
}
|
|
209
|
+
type MatchEvent = MatchCreatedEvent | RallyAwardedEvent | CourtSwitchConfirmedEvent | MatchCorrectedEvent | MatchEndedEvent | MatchEventUndoneEvent;
|
|
210
|
+
interface MatchCreation {
|
|
211
|
+
readonly snapshot: MatchSnapshot;
|
|
212
|
+
readonly events: readonly [MatchCreatedEvent];
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
declare function createMatch(input: MatchCreateInput, source?: MatchInputSource): MatchCreation;
|
|
216
|
+
declare function applyMatchEvent(snapshot: MatchSnapshot, event: MatchEvent): MatchSnapshot;
|
|
217
|
+
declare function awardRally(snapshot: MatchSnapshot, displaySide: CourtSide, source?: MatchInputSource): RallyAwardedEvent | null;
|
|
218
|
+
declare function confirmCourtSwitch(snapshot: MatchSnapshot, source?: MatchInputSource): CourtSwitchConfirmedEvent;
|
|
219
|
+
declare function correctMatch(snapshot: MatchSnapshot, correction: MatchCorrection, source?: MatchInputSource): MatchCorrectedEvent;
|
|
220
|
+
declare function endMatch(snapshot: MatchSnapshot, reason: string, source?: MatchInputSource): MatchEndedEvent;
|
|
221
|
+
|
|
222
|
+
declare function replayMatchEvents(events: readonly unknown[]): MatchSnapshot;
|
|
223
|
+
declare function undoLastMatchEvent(events: readonly MatchEvent[], source?: MatchInputSource): MatchEventUndoneEvent | null;
|
|
224
|
+
|
|
225
|
+
declare const BTHOME_SERVICE_UUID: "FCD2";
|
|
226
|
+
type RemoteButtonAction = "single" | "double" | "long";
|
|
227
|
+
type RemoteInputMode = "bthome" | "hidVolume" | "both";
|
|
228
|
+
type RemoteMappedAction = "left" | "right" | "undo" | "none";
|
|
229
|
+
interface RemoteActionMapping {
|
|
230
|
+
readonly volumeUp: RemoteMappedAction;
|
|
231
|
+
readonly volumeDown: RemoteMappedAction;
|
|
232
|
+
readonly longPress: RemoteMappedAction;
|
|
233
|
+
}
|
|
234
|
+
declare const DEFAULT_REMOTE_ACTION_MAPPING: RemoteActionMapping;
|
|
235
|
+
interface RemoteHidVolumeEvent {
|
|
236
|
+
readonly source: "hidVolume";
|
|
237
|
+
readonly direction: "volumeUp" | "volumeDown";
|
|
238
|
+
readonly repeat: boolean;
|
|
239
|
+
readonly longPress: boolean;
|
|
240
|
+
readonly timestamp: number;
|
|
241
|
+
}
|
|
242
|
+
type RemoteHidGesture = "single" | "double" | "hold";
|
|
243
|
+
interface RemoteHidGestureMapping {
|
|
244
|
+
/** Raw HID direction emitted by the calibrated remote button. */
|
|
245
|
+
readonly direction: RemoteHidVolumeEvent["direction"];
|
|
246
|
+
/** Quiet window used to close one physical gesture burst. */
|
|
247
|
+
readonly settleMs: number;
|
|
248
|
+
/** Repeated volume changes required to classify a hold before the burst ends. */
|
|
249
|
+
readonly holdEventCount: number;
|
|
250
|
+
readonly single: RemoteMappedAction;
|
|
251
|
+
readonly double: RemoteMappedAction;
|
|
252
|
+
readonly hold: RemoteMappedAction;
|
|
253
|
+
}
|
|
254
|
+
interface RemoteBinding {
|
|
255
|
+
/** Stable identifier supplied by CoreBluetooth or BluetoothLeScanner. */
|
|
256
|
+
readonly deviceId: string;
|
|
257
|
+
readonly label?: string;
|
|
258
|
+
}
|
|
259
|
+
type RemotePermissionStatus = "unknown" | "granted" | "denied" | "restricted" | "unavailable";
|
|
260
|
+
type RemoteStatus = "unavailable" | "poweredOff" | "idle" | "scanning" | "bound" | "outOfRange";
|
|
261
|
+
interface RemoteCandidate {
|
|
262
|
+
readonly deviceId: string;
|
|
263
|
+
readonly label?: string;
|
|
264
|
+
readonly rssi: number;
|
|
265
|
+
readonly lastSeenAt: number;
|
|
266
|
+
}
|
|
267
|
+
interface RemoteState {
|
|
268
|
+
readonly status: RemoteStatus;
|
|
269
|
+
readonly permission: RemotePermissionStatus;
|
|
270
|
+
readonly binding: RemoteBinding | null;
|
|
271
|
+
readonly candidate: RemoteCandidate | null;
|
|
272
|
+
readonly lastSeenAt: number | null;
|
|
273
|
+
readonly error: RemoteError | null;
|
|
274
|
+
readonly hidCaptureArmed: boolean;
|
|
275
|
+
readonly lastInputSource: "bthome" | "hidVolume" | null;
|
|
276
|
+
readonly lastHidEvent: RemoteHidVolumeEvent | null;
|
|
277
|
+
}
|
|
278
|
+
type RemoteErrorCode = "permission_denied" | "permission_restricted" | "bluetooth_unavailable" | "bluetooth_powered_off" | "scan_failed" | "native_unavailable";
|
|
279
|
+
declare class RemoteError extends Error {
|
|
280
|
+
readonly code: RemoteErrorCode;
|
|
281
|
+
readonly name = "RemoteError";
|
|
282
|
+
constructor(code: RemoteErrorCode, message: string, options?: {
|
|
283
|
+
cause?: unknown;
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
interface RemoteScanAdvertisement {
|
|
287
|
+
readonly deviceId: string;
|
|
288
|
+
readonly name?: string;
|
|
289
|
+
readonly rssi: number;
|
|
290
|
+
readonly serviceUuid: string;
|
|
291
|
+
/** Base64 encoded FCD2 service-data payload, beginning with the device-info byte. */
|
|
292
|
+
readonly serviceData: string;
|
|
293
|
+
readonly timestamp: number;
|
|
294
|
+
}
|
|
295
|
+
type RemoteNativeState = {
|
|
296
|
+
readonly status: "permission";
|
|
297
|
+
readonly permission: RemotePermissionStatus;
|
|
298
|
+
} | {
|
|
299
|
+
readonly status: "poweredOff" | "unavailable" | "scanning" | "idle";
|
|
300
|
+
} | {
|
|
301
|
+
readonly status: "error";
|
|
302
|
+
readonly code: string;
|
|
303
|
+
readonly message: string;
|
|
304
|
+
};
|
|
305
|
+
interface RemoteService {
|
|
306
|
+
start(): Promise<void>;
|
|
307
|
+
stop(): Promise<void>;
|
|
308
|
+
dispose(): Promise<void>;
|
|
309
|
+
subscribeAdvertisement(listener: (event: RemoteScanAdvertisement) => void): () => void;
|
|
310
|
+
subscribeState(listener: (event: RemoteNativeState) => void): () => void;
|
|
311
|
+
startHidCapture(options?: {
|
|
312
|
+
consume?: boolean;
|
|
313
|
+
}): Promise<void>;
|
|
314
|
+
stopHidCapture(): Promise<void>;
|
|
315
|
+
subscribeHidVolume(listener: (event: RemoteHidVolumeEvent) => void): () => void;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
declare const MATCH_PERSISTENCE_SCHEMA_VERSION: 1;
|
|
319
|
+
interface StoredMatchRow {
|
|
320
|
+
readonly matchId: string;
|
|
321
|
+
readonly schemaVersion: number;
|
|
322
|
+
readonly snapshotJson: string;
|
|
323
|
+
readonly snapshotSequence: number;
|
|
324
|
+
readonly status: MatchSnapshot["status"];
|
|
325
|
+
readonly updatedAt: number;
|
|
326
|
+
}
|
|
327
|
+
interface StoredMatchEventRow {
|
|
328
|
+
readonly matchId: string;
|
|
329
|
+
readonly sequence: number;
|
|
330
|
+
readonly eventId: string;
|
|
331
|
+
readonly schemaVersion: number;
|
|
332
|
+
readonly eventJson: string;
|
|
333
|
+
readonly createdAt: number;
|
|
334
|
+
}
|
|
335
|
+
interface MatchPersistenceTransaction {
|
|
336
|
+
getMatch(matchId: string): Promise<StoredMatchRow | null>;
|
|
337
|
+
getEvents(matchId: string): Promise<readonly StoredMatchEventRow[]>;
|
|
338
|
+
insertMatch(row: StoredMatchRow): Promise<void>;
|
|
339
|
+
insertEvent(row: StoredMatchEventRow): Promise<void>;
|
|
340
|
+
updateSnapshot(matchId: string, expectedSequence: number, row: Pick<StoredMatchRow, "schemaVersion" | "snapshotJson" | "snapshotSequence" | "status" | "updatedAt">): Promise<boolean>;
|
|
341
|
+
setActiveMatch(matchId: string | null): Promise<void>;
|
|
342
|
+
clearActiveMatch(matchId: string): Promise<void>;
|
|
343
|
+
}
|
|
344
|
+
interface MatchPersistenceDriver {
|
|
345
|
+
migrate(): Promise<void>;
|
|
346
|
+
transaction<T>(work: (transaction: MatchPersistenceTransaction) => Promise<T>): Promise<T>;
|
|
347
|
+
readMatch(matchId: string): Promise<{
|
|
348
|
+
readonly match: StoredMatchRow | null;
|
|
349
|
+
readonly events: readonly StoredMatchEventRow[];
|
|
350
|
+
}>;
|
|
351
|
+
getActiveMatchId(): Promise<string | null>;
|
|
352
|
+
readActiveMatch(): Promise<{
|
|
353
|
+
readonly activeMatchId: string | null;
|
|
354
|
+
readonly match: StoredMatchRow | null;
|
|
355
|
+
readonly events: readonly StoredMatchEventRow[];
|
|
356
|
+
}>;
|
|
357
|
+
readMatchList(): Promise<{
|
|
358
|
+
readonly matches: readonly StoredMatchRow[];
|
|
359
|
+
readonly activeMatchId: string | null;
|
|
360
|
+
}>;
|
|
361
|
+
getRemoteBinding?(): Promise<RemoteBinding | null>;
|
|
362
|
+
setRemoteBinding?(binding: RemoteBinding | null): Promise<void>;
|
|
363
|
+
}
|
|
364
|
+
interface PersistedMatch {
|
|
365
|
+
readonly snapshot: MatchSnapshot;
|
|
366
|
+
readonly events: readonly MatchEvent[];
|
|
367
|
+
}
|
|
368
|
+
interface MatchSummary {
|
|
369
|
+
readonly id: string;
|
|
370
|
+
readonly sequence: number;
|
|
371
|
+
readonly status: MatchSnapshot["status"];
|
|
372
|
+
readonly updatedAt: number;
|
|
373
|
+
readonly isActive: boolean;
|
|
374
|
+
}
|
|
375
|
+
type MatchPersistenceErrorCode = "not_found" | "already_exists" | "conflict" | "corrupt_data" | "unsupported_schema" | "sequence_mismatch" | "snapshot_mismatch" | "storage_failure";
|
|
376
|
+
declare class MatchPersistenceError extends Error {
|
|
377
|
+
readonly code: MatchPersistenceErrorCode;
|
|
378
|
+
readonly name = "MatchPersistenceError";
|
|
379
|
+
readonly recoverable = true;
|
|
380
|
+
readonly retryable: boolean;
|
|
381
|
+
constructor(code: MatchPersistenceErrorCode, message: string, options?: {
|
|
382
|
+
cause?: unknown;
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
declare const MATCH_SQL_SCHEMA_VERSION: 2;
|
|
387
|
+
declare const MATCH_SQL_MIGRATIONS: readonly ["CREATE TABLE IF NOT EXISTS match_aggregates (\n match_id TEXT PRIMARY KEY NOT NULL,\n schema_version INTEGER NOT NULL CHECK (schema_version = 1),\n snapshot_json TEXT NOT NULL,\n snapshot_sequence INTEGER NOT NULL CHECK (snapshot_sequence >= 0),\n status TEXT NOT NULL CHECK (status IN ('inProgress', 'completed', 'ended')),\n updated_at INTEGER NOT NULL\n )", "CREATE TABLE IF NOT EXISTS match_events (\n match_id TEXT NOT NULL,\n sequence INTEGER NOT NULL CHECK (sequence >= 0),\n event_id TEXT NOT NULL UNIQUE,\n schema_version INTEGER NOT NULL CHECK (schema_version = 1),\n event_json TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n PRIMARY KEY (match_id, sequence),\n FOREIGN KEY (match_id) REFERENCES match_aggregates(match_id) ON DELETE RESTRICT\n )", "CREATE TABLE IF NOT EXISTS match_active_pointer (\n slot INTEGER PRIMARY KEY NOT NULL CHECK (slot = 1),\n match_id TEXT NOT NULL UNIQUE,\n FOREIGN KEY (match_id) REFERENCES match_aggregates(match_id) ON DELETE RESTRICT\n )", "CREATE INDEX IF NOT EXISTS match_aggregates_recent\n ON match_aggregates(updated_at DESC)", "CREATE TRIGGER IF NOT EXISTS match_events_no_update\n BEFORE UPDATE ON match_events\n BEGIN SELECT RAISE(ABORT, 'match events are immutable'); END", "CREATE TRIGGER IF NOT EXISTS match_events_no_delete\n BEFORE DELETE ON match_events\n BEGIN SELECT RAISE(ABORT, 'match events are immutable'); END", "CREATE TABLE IF NOT EXISTS remote_binding (\n slot INTEGER PRIMARY KEY NOT NULL CHECK (slot = 1),\n device_id TEXT NOT NULL CHECK (length(trim(device_id)) > 0),\n label TEXT\n )"];
|
|
388
|
+
|
|
389
|
+
interface MemoryState {
|
|
390
|
+
migrationVersion: number;
|
|
391
|
+
remoteBinding: RemoteBinding | null;
|
|
392
|
+
activeMatchId: string | null;
|
|
393
|
+
matches: StoredMatchRow[];
|
|
394
|
+
events: StoredMatchEventRow[];
|
|
395
|
+
nextOrdinal: number;
|
|
396
|
+
ordinals: Record<string, number>;
|
|
397
|
+
}
|
|
398
|
+
declare class MemoryMatchPersistenceDriver implements MatchPersistenceDriver {
|
|
399
|
+
private state;
|
|
400
|
+
private eventInsertFailure;
|
|
401
|
+
private snapshotUpdateFailure;
|
|
402
|
+
private transactionTail;
|
|
403
|
+
migrate(): Promise<void>;
|
|
404
|
+
getRemoteBinding(): Promise<{
|
|
405
|
+
deviceId: string;
|
|
406
|
+
label?: string;
|
|
407
|
+
} | null>;
|
|
408
|
+
setRemoteBinding(binding: RemoteBinding | null): Promise<void>;
|
|
409
|
+
getMigrationVersion(): number;
|
|
410
|
+
transaction<T>(work: (transaction: MatchPersistenceTransaction) => Promise<T>): Promise<T>;
|
|
411
|
+
readMatch(matchId: string): Promise<{
|
|
412
|
+
match: StoredMatchRow | null;
|
|
413
|
+
events: StoredMatchEventRow[];
|
|
414
|
+
}>;
|
|
415
|
+
getActiveMatchId(): Promise<string | null>;
|
|
416
|
+
readActiveMatch(): Promise<{
|
|
417
|
+
activeMatchId: null;
|
|
418
|
+
match: null;
|
|
419
|
+
events: never[];
|
|
420
|
+
} | {
|
|
421
|
+
activeMatchId: string;
|
|
422
|
+
match: StoredMatchRow | null;
|
|
423
|
+
events: StoredMatchEventRow[];
|
|
424
|
+
}>;
|
|
425
|
+
readMatchList(): Promise<{
|
|
426
|
+
matches: StoredMatchRow[];
|
|
427
|
+
activeMatchId: string | null;
|
|
428
|
+
}>;
|
|
429
|
+
inspect(): MemoryState;
|
|
430
|
+
failNextSnapshotUpdate(error: Error): void;
|
|
431
|
+
failNextEventInsert(error: Error): void;
|
|
432
|
+
corruptSnapshotJson(matchId: string, snapshotJson: string): void;
|
|
433
|
+
corruptEventJson(matchId: string, sequence: number, eventJson: string): void;
|
|
434
|
+
corruptEventSchema(matchId: string, sequence: number, schemaVersion: number): void;
|
|
435
|
+
corruptEventSequence(matchId: string, sequence: number): void;
|
|
436
|
+
duplicateEventRow(matchId: string, sequence: number): void;
|
|
437
|
+
removeEventRow(matchId: string, sequence: number): void;
|
|
438
|
+
private mutateMatch;
|
|
439
|
+
private mutateEvent;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
interface SqlRunResult {
|
|
443
|
+
readonly changes: number;
|
|
444
|
+
readonly lastInsertRowId: number;
|
|
445
|
+
}
|
|
446
|
+
interface SqlExecutor {
|
|
447
|
+
runAsync(sql: string, ...params: unknown[]): Promise<SqlRunResult>;
|
|
448
|
+
getFirstAsync<T>(sql: string, ...params: unknown[]): Promise<T | null>;
|
|
449
|
+
getAllAsync<T>(sql: string, ...params: unknown[]): Promise<T[]>;
|
|
450
|
+
}
|
|
451
|
+
interface ExpoSqliteDatabaseLike extends SqlExecutor {
|
|
452
|
+
execAsync(sql: string): Promise<void>;
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Serializes adapters sharing this exact connection object. Separate
|
|
456
|
+
* connections to the same file rely on SQLite locking plus busy_timeout.
|
|
457
|
+
*/
|
|
458
|
+
declare function createExpoSqliteMatchDriver(database: ExpoSqliteDatabaseLike): MatchPersistenceDriver;
|
|
459
|
+
|
|
460
|
+
interface MatchEventStoreOptions {
|
|
461
|
+
readonly now?: () => number;
|
|
462
|
+
}
|
|
463
|
+
declare class MatchEventStore {
|
|
464
|
+
private readonly driver;
|
|
465
|
+
private initialized;
|
|
466
|
+
private readonly now;
|
|
467
|
+
constructor(driver: MatchPersistenceDriver, options?: MatchEventStoreOptions);
|
|
468
|
+
initialize(): Promise<void>;
|
|
469
|
+
getRemoteBinding(): Promise<RemoteBinding | null>;
|
|
470
|
+
setRemoteBinding(binding: RemoteBinding | null): Promise<void>;
|
|
471
|
+
createMatch(input: MatchCreateInput, source?: MatchInputSource): Promise<PersistedMatch>;
|
|
472
|
+
appendEvent(currentSnapshot: MatchSnapshot, event: MatchEvent): Promise<PersistedMatch>;
|
|
473
|
+
resumeActiveMatch(): Promise<PersistedMatch | null>;
|
|
474
|
+
resumeMatch(matchId: string): Promise<PersistedMatch | null>;
|
|
475
|
+
listMatches(): Promise<readonly MatchSummary[]>;
|
|
476
|
+
archiveMatch(matchId: string): Promise<void>;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
declare function createDefaultMatchEventStore(_databaseName?: string): Promise<MatchEventStore>;
|
|
480
|
+
|
|
481
|
+
type AnnouncementKind = "score" | "serviceChange" | "gamePoint" | "gameWon" | "courtSwitch" | "matchWon";
|
|
482
|
+
type FallbackVoiceToken = `number-${number}` | "number-mot" | "number-lam" | `score-deu-${number}` | "score-deu-mot" | "score-deu-lam" | "server-1" | "server-2" | "court-left" | "court-right" | "service-change" | "game-point" | "game-won" | "court-switch" | "match-won" | "test-speaker";
|
|
483
|
+
interface AnnouncementPlan {
|
|
484
|
+
readonly kind: AnnouncementKind;
|
|
485
|
+
readonly text: string;
|
|
486
|
+
readonly score?: readonly [number, number];
|
|
487
|
+
readonly fallbackTokens: readonly FallbackVoiceToken[];
|
|
488
|
+
}
|
|
489
|
+
type VoiceAudioRoute = "system" | "bluetoothA2DP" | "bluetoothHFP" | "speaker" | "headphones" | "unknown";
|
|
490
|
+
type VoiceDisconnectReason = "oldDeviceUnavailable" | "becomingNoisy" | "unknown";
|
|
491
|
+
interface VoiceRouteState {
|
|
492
|
+
readonly route: VoiceAudioRoute;
|
|
493
|
+
readonly interrupted: boolean;
|
|
494
|
+
readonly disconnectedReason: VoiceDisconnectReason | null;
|
|
495
|
+
}
|
|
496
|
+
type VoiceAudioMode = "offline" | "live";
|
|
497
|
+
interface AudioPlayback {
|
|
498
|
+
configure(mode: VoiceAudioMode): Promise<void>;
|
|
499
|
+
play(source: string): Promise<void>;
|
|
500
|
+
stop(): Promise<void>;
|
|
501
|
+
dispose(): Promise<void>;
|
|
502
|
+
getRouteState(): VoiceRouteState;
|
|
503
|
+
subscribeToRouteChanges?(listener: (state: VoiceRouteState) => void): () => void;
|
|
504
|
+
}
|
|
505
|
+
type NeuralVoiceAvailability = "available" | "unavailable";
|
|
506
|
+
interface NeuralVoiceEngine {
|
|
507
|
+
availability(): Promise<NeuralVoiceAvailability>;
|
|
508
|
+
prewarm(commonPhrases: readonly string[]): Promise<void>;
|
|
509
|
+
synthesize(text: string, signal: AbortSignal): Promise<string>;
|
|
510
|
+
cancel(): Promise<void>;
|
|
511
|
+
release?(filePath: string): Promise<void>;
|
|
512
|
+
dispose(): Promise<void>;
|
|
513
|
+
}
|
|
514
|
+
/** Cloud TTS endpoint the SDK posts announcement text to (see apps/proxy /voice/tts). */
|
|
515
|
+
interface VoiceTtsOptions {
|
|
516
|
+
readonly url: string;
|
|
517
|
+
/** Extra request headers, e.g. partner bearer tokens. Re-evaluated per request. */
|
|
518
|
+
readonly headers?: () => Readonly<Record<string, string>> | Promise<Readonly<Record<string, string>>>;
|
|
519
|
+
readonly voiceName?: string;
|
|
520
|
+
readonly languageCode?: string;
|
|
521
|
+
readonly requestTimeoutMs?: number;
|
|
522
|
+
}
|
|
523
|
+
type VoiceErrorCode = "engine_unavailable" | "network_unavailable" | "synthesis_failed" | "storage_failure" | "playback_failed";
|
|
524
|
+
declare class VoiceError extends Error {
|
|
525
|
+
readonly code: VoiceErrorCode;
|
|
526
|
+
readonly name = "VoiceError";
|
|
527
|
+
constructor(code: VoiceErrorCode, message: string, options?: {
|
|
528
|
+
cause?: unknown;
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
interface MatchVoiceService {
|
|
532
|
+
readonly audioState: VoiceRouteState;
|
|
533
|
+
readonly lastError?: Error | null;
|
|
534
|
+
initialize(): Promise<void>;
|
|
535
|
+
subscribe(listener: () => void): () => void;
|
|
536
|
+
announce(previous: MatchSnapshot, current: MatchSnapshot, event: MatchEvent): void;
|
|
537
|
+
replayAnnouncement(): Promise<void>;
|
|
538
|
+
testSpeaker(): Promise<void>;
|
|
539
|
+
configureAudioMode?(mode: VoiceAudioMode): Promise<void>;
|
|
540
|
+
dispose(): Promise<void>;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
interface PickleballMatchStore {
|
|
544
|
+
initialize(): Promise<void>;
|
|
545
|
+
createMatch(input: MatchCreateInput, source?: MatchInputSource): Promise<PersistedMatch>;
|
|
546
|
+
appendEvent(currentSnapshot: MatchSnapshot, event: MatchEvent): Promise<PersistedMatch>;
|
|
547
|
+
resumeMatch(matchId: string): Promise<PersistedMatch | null>;
|
|
548
|
+
resumeActiveMatch(): Promise<PersistedMatch | null>;
|
|
549
|
+
listMatches(): Promise<readonly MatchSummary[]>;
|
|
550
|
+
archiveMatch(matchId: string): Promise<void>;
|
|
551
|
+
getRemoteBinding?(): Promise<RemoteBinding | null>;
|
|
552
|
+
setRemoteBinding?(binding: RemoteBinding | null): Promise<void>;
|
|
553
|
+
}
|
|
554
|
+
declare class NoActiveMatchError extends Error {
|
|
555
|
+
readonly name = "NoActiveMatchError";
|
|
556
|
+
readonly code = "NO_ACTIVE_MATCH";
|
|
557
|
+
constructor();
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* State-changing actions reject with NoActiveMatchError when no match is loaded.
|
|
561
|
+
* Commands the engine deems ineligible are ignored and resolve the current snapshot.
|
|
562
|
+
*/
|
|
563
|
+
interface PickleballMatchContextValue {
|
|
564
|
+
readonly snapshot: MatchSnapshot | null;
|
|
565
|
+
readonly events: readonly MatchEvent[];
|
|
566
|
+
readonly loading: boolean;
|
|
567
|
+
readonly error: Error | null;
|
|
568
|
+
readonly voiceError: Error | null;
|
|
569
|
+
readonly audioState: VoiceRouteState;
|
|
570
|
+
readonly remoteState: RemoteState;
|
|
571
|
+
createMatch(input: MatchCreateInput, source?: MatchInputSource): Promise<MatchSnapshot>;
|
|
572
|
+
resumeMatch(matchId?: string): Promise<MatchSnapshot | null>;
|
|
573
|
+
awardRallyToSide(side: CourtSide, source?: MatchInputSource): Promise<MatchSnapshot>;
|
|
574
|
+
undo(source?: MatchInputSource): Promise<MatchSnapshot>;
|
|
575
|
+
correctState(correction: MatchCorrection, source?: MatchInputSource): Promise<MatchSnapshot>;
|
|
576
|
+
confirmCourtSwitch(source?: MatchInputSource): Promise<MatchSnapshot>;
|
|
577
|
+
endMatch(reason: string, source?: MatchInputSource): Promise<MatchSnapshot>;
|
|
578
|
+
testSpeaker(): Promise<void>;
|
|
579
|
+
replayAnnouncement(): Promise<void>;
|
|
580
|
+
setVoiceAudioMode(mode: VoiceAudioMode): Promise<void>;
|
|
581
|
+
bindRemote(binding: RemoteBinding): Promise<void>;
|
|
582
|
+
unbindRemote(): Promise<void>;
|
|
583
|
+
startRemoteScan(): Promise<void>;
|
|
584
|
+
stopRemoteScan(): Promise<void>;
|
|
585
|
+
startHidCapture(): Promise<void>;
|
|
586
|
+
stopHidCapture(): Promise<void>;
|
|
587
|
+
}
|
|
588
|
+
interface PickleballMatchProviderProps {
|
|
589
|
+
readonly children?: ReactNode;
|
|
590
|
+
/** Fixed for this provider instance. Remount to use a different local store. */
|
|
591
|
+
readonly store?: PickleballMatchStore;
|
|
592
|
+
/** Fixed for this provider instance. Remount to use a different factory. */
|
|
593
|
+
readonly storeFactory?: () => Promise<PickleballMatchStore>;
|
|
594
|
+
/** Resumes the active local match on mount. Disable for hosts that resume explicitly. */
|
|
595
|
+
readonly autoResume?: boolean;
|
|
596
|
+
readonly onError?: (error: Error) => void | Promise<void>;
|
|
597
|
+
/** Fixed for this provider instance. Primarily useful for custom storage and tests. */
|
|
598
|
+
readonly voiceService?: MatchVoiceService;
|
|
599
|
+
/** Initial audio session mode. Defaults to live so mounting never disables capture. */
|
|
600
|
+
readonly voiceAudioMode?: VoiceAudioMode;
|
|
601
|
+
/** Cloud TTS endpoint for announcements. Without it only bundled WAVs play. Fixed for this provider instance. */
|
|
602
|
+
readonly voiceTts?: VoiceTtsOptions;
|
|
603
|
+
/** Fixed for this provider instance. Primarily useful for tests and custom native bridges. */
|
|
604
|
+
readonly remoteService?: RemoteService;
|
|
605
|
+
/** Foreground silence before a bound remote becomes outOfRange. */
|
|
606
|
+
readonly remoteOutOfRangeTimeoutMs?: number;
|
|
607
|
+
readonly remoteInputMode?: RemoteInputMode;
|
|
608
|
+
readonly remoteActionMapping?: Partial<RemoteActionMapping>;
|
|
609
|
+
/** Optional single/double/hold recognizer for one calibrated HID volume button. */
|
|
610
|
+
readonly hidGestureMapping?: RemoteHidGestureMapping;
|
|
611
|
+
readonly consumeHidVolumeKeys?: boolean;
|
|
612
|
+
}
|
|
613
|
+
declare function PickleballMatchProvider({ children, store, storeFactory, autoResume, onError, voiceService, voiceAudioMode, voiceTts, remoteService, remoteOutOfRangeTimeoutMs, remoteInputMode, remoteActionMapping, hidGestureMapping, consumeHidVolumeKeys, }: PickleballMatchProviderProps): React.JSX.Element;
|
|
614
|
+
declare function usePickleballMatch(): PickleballMatchContextValue;
|
|
615
|
+
|
|
616
|
+
type BTHomeRemoteParseErrorCode = "wrong_uuid" | "unsupported_version" | "encrypted" | "truncated" | "unknown_object" | "invalid_button_event" | "no_button";
|
|
617
|
+
declare class BTHomeRemoteParseError extends Error {
|
|
618
|
+
readonly code: BTHomeRemoteParseErrorCode;
|
|
619
|
+
readonly name = "BTHomeRemoteParseError";
|
|
620
|
+
constructor(code: BTHomeRemoteParseErrorCode, message: string);
|
|
621
|
+
}
|
|
622
|
+
interface BTHomeAdvertisementInput {
|
|
623
|
+
readonly deviceId: string;
|
|
624
|
+
readonly serviceUuid: string;
|
|
625
|
+
/** FCD2 service data only. The first byte must be BTHome device-info, not UUID bytes. */
|
|
626
|
+
readonly serviceData: Uint8Array;
|
|
627
|
+
}
|
|
628
|
+
interface ParsedBTHomeRemoteAdvertisement {
|
|
629
|
+
readonly deviceId: string;
|
|
630
|
+
readonly version: 2;
|
|
631
|
+
readonly encrypted: false;
|
|
632
|
+
readonly triggerBased: boolean;
|
|
633
|
+
readonly packetId?: number;
|
|
634
|
+
readonly button: {
|
|
635
|
+
readonly action: RemoteButtonAction;
|
|
636
|
+
readonly objectIndex: number;
|
|
637
|
+
readonly rawEvent: number;
|
|
638
|
+
};
|
|
639
|
+
readonly fingerprint: string;
|
|
640
|
+
}
|
|
641
|
+
declare function parseBTHomeV2Advertisement(input: BTHomeAdvertisementInput): ParsedBTHomeRemoteAdvertisement;
|
|
642
|
+
|
|
643
|
+
interface RemoteDeduplicationKey {
|
|
644
|
+
readonly packetId?: number;
|
|
645
|
+
readonly fingerprint: string;
|
|
646
|
+
}
|
|
647
|
+
interface BTHomeRemoteDeduplicatorOptions {
|
|
648
|
+
readonly fingerprintWindowMs?: number;
|
|
649
|
+
}
|
|
650
|
+
declare class BTHomeRemoteDeduplicator {
|
|
651
|
+
private readonly devices;
|
|
652
|
+
private readonly fingerprintWindowMs;
|
|
653
|
+
constructor(options?: BTHomeRemoteDeduplicatorOptions);
|
|
654
|
+
get trackedDeviceCount(): number;
|
|
655
|
+
accept(deviceId: string, key: RemoteDeduplicationKey, now?: number): boolean;
|
|
656
|
+
forget(deviceId: string): void;
|
|
657
|
+
clear(): void;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
declare function decodeBase64Bytes(value: string): Uint8Array;
|
|
661
|
+
|
|
662
|
+
interface HidVolumeGestureRecognizerOptions extends Pick<RemoteHidGestureMapping, "direction" | "settleMs" | "holdEventCount"> {
|
|
663
|
+
readonly onGesture: (gesture: RemoteHidGesture) => void;
|
|
664
|
+
}
|
|
665
|
+
declare class HidVolumeGestureRecognizer {
|
|
666
|
+
private readonly options;
|
|
667
|
+
private count;
|
|
668
|
+
private resolved;
|
|
669
|
+
private lastTimestamp;
|
|
670
|
+
private timer;
|
|
671
|
+
constructor(options: HidVolumeGestureRecognizerOptions);
|
|
672
|
+
push(event: RemoteHidVolumeEvent): void;
|
|
673
|
+
dispose(): void;
|
|
674
|
+
private finishBurst;
|
|
675
|
+
private clearTimer;
|
|
676
|
+
private reset;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
/** Pure phrase selection from the state before and after one committed event. */
|
|
680
|
+
declare function buildVietnameseAnnouncement(previous: MatchSnapshot, current: MatchSnapshot, event: MatchEvent): AnnouncementPlan;
|
|
681
|
+
declare function planBundledFallback(plan: AnnouncementPlan): readonly string[];
|
|
682
|
+
|
|
683
|
+
interface LatestAnnouncementSchedulerOptions {
|
|
684
|
+
readonly engine: NeuralVoiceEngine;
|
|
685
|
+
readonly audio: AudioPlayback;
|
|
686
|
+
readonly onError?: (error: Error) => void;
|
|
687
|
+
}
|
|
688
|
+
declare class LatestAnnouncementScheduler {
|
|
689
|
+
private readonly options;
|
|
690
|
+
private static readonly MAX_CACHE_ENTRIES;
|
|
691
|
+
private generation;
|
|
692
|
+
private activeController;
|
|
693
|
+
private lastPlan;
|
|
694
|
+
private readonly neuralCache;
|
|
695
|
+
private readonly operations;
|
|
696
|
+
private readonly prewarmControllers;
|
|
697
|
+
private lifecycle;
|
|
698
|
+
private disposePromise;
|
|
699
|
+
constructor(options: LatestAnnouncementSchedulerOptions);
|
|
700
|
+
prewarm(commonPhrases?: readonly string[]): Promise<void>;
|
|
701
|
+
enqueue(plan: AnnouncementPlan): Promise<void>;
|
|
702
|
+
private performEnqueue;
|
|
703
|
+
replayLast(): Promise<void>;
|
|
704
|
+
testSpeaker(): Promise<void>;
|
|
705
|
+
private performSpeakerTest;
|
|
706
|
+
dispose(): Promise<void>;
|
|
707
|
+
clearCache(): Promise<void>;
|
|
708
|
+
private cacheFile;
|
|
709
|
+
private touchCachedFile;
|
|
710
|
+
private track;
|
|
711
|
+
private report;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
interface ExpoAudioPlayer {
|
|
715
|
+
play(): void;
|
|
716
|
+
pause(): void;
|
|
717
|
+
remove(): void;
|
|
718
|
+
addListener(event: "playbackStatusUpdate", listener: (status: {
|
|
719
|
+
readonly didJustFinish?: boolean;
|
|
720
|
+
readonly isLoaded?: boolean;
|
|
721
|
+
readonly isBuffering?: boolean;
|
|
722
|
+
readonly error?: unknown;
|
|
723
|
+
readonly playbackState?: string;
|
|
724
|
+
}) => void): {
|
|
725
|
+
remove(): void;
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
interface ExpoAudioBindings {
|
|
729
|
+
setAudioModeAsync(options: {
|
|
730
|
+
readonly allowsRecording: boolean;
|
|
731
|
+
readonly playsInSilentMode: boolean;
|
|
732
|
+
}): Promise<void>;
|
|
733
|
+
createAudioPlayer(source: unknown): ExpoAudioPlayer;
|
|
734
|
+
}
|
|
735
|
+
interface ExpoAudioPlaybackOptions {
|
|
736
|
+
readonly load?: () => ExpoAudioBindings;
|
|
737
|
+
readonly loadNativeAudio?: () => NativeAudioSessionBindings;
|
|
738
|
+
readonly resolveSource?: (source: string) => unknown;
|
|
739
|
+
readonly playbackTimeoutMs?: number;
|
|
740
|
+
}
|
|
741
|
+
interface NativeAudioSessionBindings {
|
|
742
|
+
captureAudioSessionState(): Promise<void>;
|
|
743
|
+
configureAudioSession(mode: VoiceAudioMode): Promise<VoiceRouteState>;
|
|
744
|
+
getAudioRouteState(): VoiceRouteState | Promise<VoiceRouteState>;
|
|
745
|
+
addListener(event: "onAudioRouteChanged", listener: (state: VoiceRouteState) => void): {
|
|
746
|
+
remove(): void;
|
|
747
|
+
};
|
|
748
|
+
disposeAudioSession(): Promise<void>;
|
|
749
|
+
}
|
|
750
|
+
declare function createExpoAudioPlayback(options?: ExpoAudioPlaybackOptions): AudioPlayback;
|
|
751
|
+
|
|
752
|
+
/** Minimal file operations the engine needs; defaults to expo-file-system. */
|
|
753
|
+
interface GeneratedAudioFileSystem {
|
|
754
|
+
writeFile(path: string, bytes: Uint8Array): Promise<void>;
|
|
755
|
+
removeFile(path: string): Promise<void>;
|
|
756
|
+
}
|
|
757
|
+
interface GeminiVoiceEngineOptions extends VoiceTtsOptions {
|
|
758
|
+
/** Directory that receives generated WAV files. */
|
|
759
|
+
readonly generatedRoot: string;
|
|
760
|
+
readonly fetch?: typeof globalThis.fetch;
|
|
761
|
+
readonly fileSystem?: GeneratedAudioFileSystem;
|
|
762
|
+
}
|
|
763
|
+
/**
|
|
764
|
+
* Cloud announcement voice: posts the phrase to the partner proxy, which calls
|
|
765
|
+
* Vertex AI Gemini TTS and returns a WAV body. Failures surface as VoiceError
|
|
766
|
+
* so the scheduler drops to the bundled WAV fallback.
|
|
767
|
+
*/
|
|
768
|
+
declare function createGeminiVoiceEngine(options: GeminiVoiceEngineOptions): NeuralVoiceEngine;
|
|
769
|
+
/** Placeholder engine used when no TTS endpoint is configured: always falls back. */
|
|
770
|
+
declare function createUnavailableVoiceEngine(): NeuralVoiceEngine;
|
|
771
|
+
|
|
772
|
+
declare function resolveBundledVoiceAsset(source: string): string;
|
|
773
|
+
|
|
774
|
+
interface DefaultMatchVoiceServiceOptions {
|
|
775
|
+
/** Cloud TTS endpoint. Without it, announcements always use the bundled WAVs. */
|
|
776
|
+
readonly tts?: VoiceTtsOptions;
|
|
777
|
+
readonly audioMode?: VoiceAudioMode;
|
|
778
|
+
/** Exact cache directory for generated WAVs. Primarily useful for tests. */
|
|
779
|
+
readonly generatedRoot?: string;
|
|
780
|
+
/** Primarily useful for tests. Defaults to expo-file-system removal. */
|
|
781
|
+
readonly removeGeneratedDirectory?: (path: string) => Promise<void>;
|
|
782
|
+
/** Primarily useful for native integration tests. */
|
|
783
|
+
readonly audio?: AudioPlayback;
|
|
784
|
+
/** Primarily useful for native integration tests. */
|
|
785
|
+
readonly engine?: NeuralVoiceEngine;
|
|
786
|
+
}
|
|
787
|
+
declare function createDefaultMatchVoiceService(options?: DefaultMatchVoiceServiceOptions): MatchVoiceService;
|
|
788
|
+
|
|
789
|
+
type ExpoSdkErrorCode = SdkErrorCode | "CONFIGURATION_ERROR" | "UNSUPPORTED_RUNTIME" | "INVALID_RESPONSE" | "TIMEOUT";
|
|
790
|
+
declare class PickleballExpoError extends Error {
|
|
791
|
+
readonly code: ExpoSdkErrorCode;
|
|
792
|
+
constructor(message: string, code: ExpoSdkErrorCode, options?: ErrorOptions);
|
|
793
|
+
}
|
|
794
|
+
declare const PermissionDeniedError_base: {
|
|
795
|
+
new (message?: string, options?: ErrorOptions): {
|
|
796
|
+
readonly code: ExpoSdkErrorCode;
|
|
797
|
+
name: string;
|
|
798
|
+
message: string;
|
|
799
|
+
stack?: string;
|
|
800
|
+
cause?: unknown;
|
|
801
|
+
};
|
|
802
|
+
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
803
|
+
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
804
|
+
stackTraceLimit: number;
|
|
805
|
+
};
|
|
806
|
+
declare class PermissionDeniedError extends PermissionDeniedError_base {
|
|
807
|
+
}
|
|
808
|
+
declare const NetworkUnavailableError_base: {
|
|
809
|
+
new (message?: string, options?: ErrorOptions): {
|
|
810
|
+
readonly code: ExpoSdkErrorCode;
|
|
811
|
+
name: string;
|
|
812
|
+
message: string;
|
|
813
|
+
stack?: string;
|
|
814
|
+
cause?: unknown;
|
|
815
|
+
};
|
|
816
|
+
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
817
|
+
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
818
|
+
stackTraceLimit: number;
|
|
819
|
+
};
|
|
820
|
+
declare class NetworkUnavailableError extends NetworkUnavailableError_base {
|
|
821
|
+
}
|
|
822
|
+
declare const TokenExpiredError_base: {
|
|
823
|
+
new (message?: string, options?: ErrorOptions): {
|
|
824
|
+
readonly code: ExpoSdkErrorCode;
|
|
825
|
+
name: string;
|
|
826
|
+
message: string;
|
|
827
|
+
stack?: string;
|
|
828
|
+
cause?: unknown;
|
|
829
|
+
};
|
|
830
|
+
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
831
|
+
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
832
|
+
stackTraceLimit: number;
|
|
833
|
+
};
|
|
834
|
+
declare class TokenExpiredError extends TokenExpiredError_base {
|
|
835
|
+
}
|
|
836
|
+
declare const UnsupportedSdkError_base: {
|
|
837
|
+
new (message?: string, options?: ErrorOptions): {
|
|
838
|
+
readonly code: ExpoSdkErrorCode;
|
|
839
|
+
name: string;
|
|
840
|
+
message: string;
|
|
841
|
+
stack?: string;
|
|
842
|
+
cause?: unknown;
|
|
843
|
+
};
|
|
844
|
+
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
845
|
+
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
846
|
+
stackTraceLimit: number;
|
|
847
|
+
};
|
|
848
|
+
declare class UnsupportedSdkError extends UnsupportedSdkError_base {
|
|
849
|
+
}
|
|
850
|
+
declare const UnsupportedRuntimeError_base: {
|
|
851
|
+
new (message?: string, options?: ErrorOptions): {
|
|
852
|
+
readonly code: ExpoSdkErrorCode;
|
|
853
|
+
name: string;
|
|
854
|
+
message: string;
|
|
855
|
+
stack?: string;
|
|
856
|
+
cause?: unknown;
|
|
857
|
+
};
|
|
858
|
+
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
859
|
+
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
860
|
+
stackTraceLimit: number;
|
|
861
|
+
};
|
|
862
|
+
declare class UnsupportedRuntimeError extends UnsupportedRuntimeError_base {
|
|
863
|
+
}
|
|
864
|
+
declare const SessionConflictError_base: {
|
|
865
|
+
new (message?: string, options?: ErrorOptions): {
|
|
866
|
+
readonly code: ExpoSdkErrorCode;
|
|
867
|
+
name: string;
|
|
868
|
+
message: string;
|
|
869
|
+
stack?: string;
|
|
870
|
+
cause?: unknown;
|
|
871
|
+
};
|
|
872
|
+
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
873
|
+
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
874
|
+
stackTraceLimit: number;
|
|
875
|
+
};
|
|
876
|
+
declare class SessionConflictError extends SessionConflictError_base {
|
|
877
|
+
}
|
|
878
|
+
declare const RecordingFailureError_base: {
|
|
879
|
+
new (message?: string, options?: ErrorOptions): {
|
|
880
|
+
readonly code: ExpoSdkErrorCode;
|
|
881
|
+
name: string;
|
|
882
|
+
message: string;
|
|
883
|
+
stack?: string;
|
|
884
|
+
cause?: unknown;
|
|
885
|
+
};
|
|
886
|
+
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
887
|
+
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
888
|
+
stackTraceLimit: number;
|
|
889
|
+
};
|
|
890
|
+
declare class RecordingFailureError extends RecordingFailureError_base {
|
|
891
|
+
}
|
|
892
|
+
declare const ConsentRequiredError_base: {
|
|
893
|
+
new (message?: string, options?: ErrorOptions): {
|
|
894
|
+
readonly code: ExpoSdkErrorCode;
|
|
895
|
+
name: string;
|
|
896
|
+
message: string;
|
|
897
|
+
stack?: string;
|
|
898
|
+
cause?: unknown;
|
|
899
|
+
};
|
|
900
|
+
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
901
|
+
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
902
|
+
stackTraceLimit: number;
|
|
903
|
+
};
|
|
904
|
+
declare class ConsentRequiredError extends ConsentRequiredError_base {
|
|
905
|
+
}
|
|
906
|
+
declare const SdkDisabledError_base: {
|
|
907
|
+
new (message?: string, options?: ErrorOptions): {
|
|
908
|
+
readonly code: ExpoSdkErrorCode;
|
|
909
|
+
name: string;
|
|
910
|
+
message: string;
|
|
911
|
+
stack?: string;
|
|
912
|
+
cause?: unknown;
|
|
913
|
+
};
|
|
914
|
+
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
915
|
+
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
916
|
+
stackTraceLimit: number;
|
|
917
|
+
};
|
|
918
|
+
declare class SdkDisabledError extends SdkDisabledError_base {
|
|
919
|
+
}
|
|
920
|
+
declare const ExpoSdkConfigurationError_base: {
|
|
921
|
+
new (message?: string, options?: ErrorOptions): {
|
|
922
|
+
readonly code: ExpoSdkErrorCode;
|
|
923
|
+
name: string;
|
|
924
|
+
message: string;
|
|
925
|
+
stack?: string;
|
|
926
|
+
cause?: unknown;
|
|
927
|
+
};
|
|
928
|
+
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
929
|
+
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
930
|
+
stackTraceLimit: number;
|
|
931
|
+
};
|
|
932
|
+
declare class ExpoSdkConfigurationError extends ExpoSdkConfigurationError_base {
|
|
933
|
+
}
|
|
934
|
+
declare const InvalidResponseError_base: {
|
|
935
|
+
new (message?: string, options?: ErrorOptions): {
|
|
936
|
+
readonly code: ExpoSdkErrorCode;
|
|
937
|
+
name: string;
|
|
938
|
+
message: string;
|
|
939
|
+
stack?: string;
|
|
940
|
+
cause?: unknown;
|
|
941
|
+
};
|
|
942
|
+
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
943
|
+
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
944
|
+
stackTraceLimit: number;
|
|
945
|
+
};
|
|
946
|
+
declare class InvalidResponseError extends InvalidResponseError_base {
|
|
947
|
+
}
|
|
948
|
+
declare const ExpoSdkTimeoutError_base: {
|
|
949
|
+
new (message?: string, options?: ErrorOptions): {
|
|
950
|
+
readonly code: ExpoSdkErrorCode;
|
|
951
|
+
name: string;
|
|
952
|
+
message: string;
|
|
953
|
+
stack?: string;
|
|
954
|
+
cause?: unknown;
|
|
955
|
+
};
|
|
956
|
+
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
957
|
+
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
958
|
+
stackTraceLimit: number;
|
|
959
|
+
};
|
|
960
|
+
declare class ExpoSdkTimeoutError extends ExpoSdkTimeoutError_base {
|
|
961
|
+
}
|
|
962
|
+
declare const UnknownSdkError_base: {
|
|
963
|
+
new (message?: string, options?: ErrorOptions): {
|
|
964
|
+
readonly code: ExpoSdkErrorCode;
|
|
965
|
+
name: string;
|
|
966
|
+
message: string;
|
|
967
|
+
stack?: string;
|
|
968
|
+
cause?: unknown;
|
|
969
|
+
};
|
|
970
|
+
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
971
|
+
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
972
|
+
stackTraceLimit: number;
|
|
973
|
+
};
|
|
974
|
+
declare class UnknownSdkError extends UnknownSdkError_base {
|
|
975
|
+
}
|
|
976
|
+
declare function errorFromCode(code: string, message?: string): PickleballExpoError;
|
|
977
|
+
declare function normalizeError(error: unknown): PickleballExpoError;
|
|
978
|
+
|
|
979
|
+
interface RoomConnection {
|
|
980
|
+
serverUrl: string;
|
|
981
|
+
participantToken: string;
|
|
982
|
+
microphoneEnabled: boolean;
|
|
983
|
+
/** false: vào phòng nhưng chưa publish track (trạng thái standby). */
|
|
984
|
+
publish?: boolean;
|
|
985
|
+
}
|
|
986
|
+
type RoomEvent = {
|
|
987
|
+
type: "reconnecting";
|
|
988
|
+
} | {
|
|
989
|
+
type: "reconnected";
|
|
990
|
+
} | {
|
|
991
|
+
type: "disconnected";
|
|
992
|
+
reason?: string;
|
|
993
|
+
} | {
|
|
994
|
+
type: "media-error";
|
|
995
|
+
error: unknown;
|
|
996
|
+
}
|
|
997
|
+
/** Lệnh điều khiển từ dashboard qua LiveKit data channel ({"type":"cmd"}). */
|
|
998
|
+
| {
|
|
999
|
+
type: "command";
|
|
1000
|
+
action: string;
|
|
1001
|
+
};
|
|
1002
|
+
interface RoomAdapter {
|
|
1003
|
+
/** Bật/tắt publish track khi đã ở trong phòng (standby ↔ live). */
|
|
1004
|
+
setPublishing?(enabled: boolean): Promise<void>;
|
|
1005
|
+
setCaptureOrientation?(orientation: CaptureOrientation): Promise<void>;
|
|
1006
|
+
listCameras?(): Promise<CameraDeviceInfo[]>;
|
|
1007
|
+
selectCamera?(deviceId: string, facing: CameraDeviceInfo["facing"]): Promise<void>;
|
|
1008
|
+
preparePreview(quality: 720 | 1080): Promise<unknown>;
|
|
1009
|
+
connect(input: RoomConnection): Promise<void>;
|
|
1010
|
+
disconnect(stopTracks?: boolean): Promise<void>;
|
|
1011
|
+
switchCamera(): Promise<void>;
|
|
1012
|
+
setMicrophoneEnabled(enabled: boolean): Promise<void>;
|
|
1013
|
+
subscribe(listener: (event: RoomEvent) => void): () => void;
|
|
1014
|
+
dispose(): Promise<void>;
|
|
1015
|
+
}
|
|
1016
|
+
interface SdkRuntime {
|
|
1017
|
+
assertSupported(): void;
|
|
1018
|
+
requestPermissions(): Promise<boolean>;
|
|
1019
|
+
activateKeepAwake(): void | Promise<void>;
|
|
1020
|
+
deactivateKeepAwake(): void | Promise<void>;
|
|
1021
|
+
}
|
|
1022
|
+
interface SdkStorage {
|
|
1023
|
+
getItem(key: string): Promise<string | null>;
|
|
1024
|
+
setItem(key: string, value: string): Promise<void>;
|
|
1025
|
+
removeItem(key: string): Promise<void>;
|
|
1026
|
+
}
|
|
1027
|
+
interface SdkNetwork {
|
|
1028
|
+
isOnline(): boolean;
|
|
1029
|
+
subscribe(listener: (online: boolean) => void): () => void;
|
|
1030
|
+
}
|
|
1031
|
+
interface TelemetrySink {
|
|
1032
|
+
send(credentials: SdkTelemetryCredentials, events: readonly SdkTelemetryEvent[]): Promise<void>;
|
|
1033
|
+
dispose?(): void;
|
|
1034
|
+
}
|
|
1035
|
+
interface EngineDependencies {
|
|
1036
|
+
sessionProvider: LivestreamSessionProvider;
|
|
1037
|
+
bootstrapInput: BootstrapInput;
|
|
1038
|
+
roomFactory: () => RoomAdapter;
|
|
1039
|
+
runtime: SdkRuntime;
|
|
1040
|
+
storage: SdkStorage;
|
|
1041
|
+
network: SdkNetwork;
|
|
1042
|
+
telemetry?: TelemetrySink;
|
|
1043
|
+
/** Thu chỉ số thiết bị; gọi mỗi nhịp heartbeat khi đang phát. */
|
|
1044
|
+
deviceMetrics?: () => Promise<DeviceMetricsSample | null>;
|
|
1045
|
+
now?: () => number;
|
|
1046
|
+
}
|
|
1047
|
+
type CaptureOrientation = "portrait" | "landscape";
|
|
1048
|
+
interface CameraDeviceInfo {
|
|
1049
|
+
deviceId: string;
|
|
1050
|
+
label: string;
|
|
1051
|
+
facing: "front" | "environment" | "unknown";
|
|
1052
|
+
}
|
|
1053
|
+
/** Chỉ số thiết bị gửi kèm heartbeat (trường trùng schema deviceMetrics). */
|
|
1054
|
+
interface DeviceMetricsSample {
|
|
1055
|
+
batteryPct?: number;
|
|
1056
|
+
batteryTempC?: number;
|
|
1057
|
+
thermal?: number;
|
|
1058
|
+
ramFreeMb?: number;
|
|
1059
|
+
ramTotalMb?: number;
|
|
1060
|
+
storageFreeGb?: number;
|
|
1061
|
+
netType?: string;
|
|
1062
|
+
linkMbps?: number;
|
|
1063
|
+
rssi?: number;
|
|
1064
|
+
connQuality?: string;
|
|
1065
|
+
charging?: boolean;
|
|
1066
|
+
}
|
|
1067
|
+
interface LivestreamSnapshot {
|
|
1068
|
+
state: LivestreamState;
|
|
1069
|
+
error: PickleballExpoError | null;
|
|
1070
|
+
config: SdkRemoteConfig | null;
|
|
1071
|
+
sessionId: string | null;
|
|
1072
|
+
previewTrack: unknown;
|
|
1073
|
+
isMicrophoneEnabled: boolean;
|
|
1074
|
+
isCameraFront: boolean;
|
|
1075
|
+
/** Explicit pre-live choice; null follows the window orientation. */
|
|
1076
|
+
captureOrientation: CaptureOrientation | null;
|
|
1077
|
+
/** Mẫu chỉ số thiết bị mới nhất (cập nhật mỗi heartbeat khi live). */
|
|
1078
|
+
deviceMetrics: DeviceMetricsSample | null;
|
|
1079
|
+
/** Cameras reported by the platform after preview was prepared. */
|
|
1080
|
+
cameras: CameraDeviceInfo[];
|
|
1081
|
+
/** Explicitly chosen camera; null when following facing (front/back). */
|
|
1082
|
+
selectedCameraId: string | null;
|
|
1083
|
+
elapsedMs: number;
|
|
1084
|
+
isOnline: boolean;
|
|
1085
|
+
}
|
|
1086
|
+
declare const PENDING_END_KEY = "@pickleball/expo-sdk/pending-end";
|
|
1087
|
+
declare const LEGAL_LIVESTREAM_TRANSITIONS: Readonly<Record<LivestreamState, readonly LivestreamState[]>>;
|
|
1088
|
+
declare class LivestreamEngine {
|
|
1089
|
+
private readonly dependencies;
|
|
1090
|
+
private room?;
|
|
1091
|
+
private readonly now;
|
|
1092
|
+
private snapshot;
|
|
1093
|
+
private listeners;
|
|
1094
|
+
private preparePromise?;
|
|
1095
|
+
private startPromise?;
|
|
1096
|
+
private stopPromise?;
|
|
1097
|
+
private retryPromise?;
|
|
1098
|
+
private credentialRefreshPromise?;
|
|
1099
|
+
private pendingReplayPromise?;
|
|
1100
|
+
private startupRecoveryPromise?;
|
|
1101
|
+
private startupRecovered;
|
|
1102
|
+
private pendingReplayError?;
|
|
1103
|
+
private ownedSessionId?;
|
|
1104
|
+
private commandTail;
|
|
1105
|
+
private commandCount;
|
|
1106
|
+
private roomUnsubscribe;
|
|
1107
|
+
private networkUnsubscribe;
|
|
1108
|
+
private networkSubscribed;
|
|
1109
|
+
private latestAppState;
|
|
1110
|
+
private backgroundTimer?;
|
|
1111
|
+
private standbyTimer?;
|
|
1112
|
+
private reconnectTimer?;
|
|
1113
|
+
private heartbeatTimer?;
|
|
1114
|
+
private elapsedTimer?;
|
|
1115
|
+
private tokenRefreshTimer?;
|
|
1116
|
+
private maxSessionTimer?;
|
|
1117
|
+
private grant?;
|
|
1118
|
+
private startedAt?;
|
|
1119
|
+
/** Đang publish track (live) hay chỉ ngồi trong phòng (standby). */
|
|
1120
|
+
private publishing;
|
|
1121
|
+
/** Tổng thời gian đã phát trước lần tạm dừng gần nhất. */
|
|
1122
|
+
private pausedElapsedMs;
|
|
1123
|
+
private sessionDeadline?;
|
|
1124
|
+
private refreshRetryAttempt;
|
|
1125
|
+
private operationGeneration;
|
|
1126
|
+
private liveTimersStarted;
|
|
1127
|
+
private keepAwakeActive;
|
|
1128
|
+
private telemetryInFlight?;
|
|
1129
|
+
private pendingCriticalTelemetry?;
|
|
1130
|
+
private readonly sessionTerminations;
|
|
1131
|
+
private disposePromise?;
|
|
1132
|
+
private disposed;
|
|
1133
|
+
constructor(dependencies: EngineDependencies);
|
|
1134
|
+
getSnapshot: () => LivestreamSnapshot;
|
|
1135
|
+
subscribe: (listener: () => void) => () => boolean;
|
|
1136
|
+
initialize(): Promise<void>;
|
|
1137
|
+
prepare(): Promise<void>;
|
|
1138
|
+
private doPrepare;
|
|
1139
|
+
start(input: StartLivestreamInput, options?: {
|
|
1140
|
+
autoPublish?: boolean;
|
|
1141
|
+
}): Promise<void>;
|
|
1142
|
+
private doStart;
|
|
1143
|
+
stop(reason?: EndReason): Promise<void>;
|
|
1144
|
+
private doStop;
|
|
1145
|
+
retry(): Promise<void>;
|
|
1146
|
+
private doRetry;
|
|
1147
|
+
switchCamera(): Promise<void>;
|
|
1148
|
+
/** Chọn một ống kính cụ thể từ snapshot.cameras. */
|
|
1149
|
+
selectCamera(deviceId: string): Promise<void>;
|
|
1150
|
+
/** Chọn kiểu quay trước khi live; không đổi khi phiên đang hoạt động. */
|
|
1151
|
+
setCaptureOrientation(orientation: CaptureOrientation): Promise<void>;
|
|
1152
|
+
setMicrophoneEnabled(enabled: boolean): Promise<void>;
|
|
1153
|
+
handleRoomEvent(event: RoomEvent): void;
|
|
1154
|
+
handleAppState(state: "active" | "background" | "inactive"): void;
|
|
1155
|
+
private ensureBackgroundGraceTimer;
|
|
1156
|
+
dispose(): Promise<void>;
|
|
1157
|
+
private replayPendingEnd;
|
|
1158
|
+
private doReplayPendingEnd;
|
|
1159
|
+
private recoverStartupSession;
|
|
1160
|
+
private ensurePendingEndResolved;
|
|
1161
|
+
private readPendingEnd;
|
|
1162
|
+
private ownsInMemorySession;
|
|
1163
|
+
private startTimers;
|
|
1164
|
+
private clearTimers;
|
|
1165
|
+
private clearReconnectTimer;
|
|
1166
|
+
/** Heartbeat kèm chỉ số thiết bị (nếu collector được cung cấp). */
|
|
1167
|
+
private sendHeartbeat;
|
|
1168
|
+
private sendTelemetry;
|
|
1169
|
+
private startTelemetrySend;
|
|
1170
|
+
private refreshCredentials;
|
|
1171
|
+
private transition;
|
|
1172
|
+
private startStandbyTimer;
|
|
1173
|
+
private clearStandbyTimer;
|
|
1174
|
+
private fail;
|
|
1175
|
+
private recordError;
|
|
1176
|
+
private update;
|
|
1177
|
+
private ensureState;
|
|
1178
|
+
private ensureNotDisposed;
|
|
1179
|
+
private ensureRoom;
|
|
1180
|
+
private ensureNetworkSubscription;
|
|
1181
|
+
private requireRoom;
|
|
1182
|
+
private releaseRoom;
|
|
1183
|
+
private enqueueLifecycle;
|
|
1184
|
+
private scheduleTokenRefresh;
|
|
1185
|
+
private scheduleMaxSessionDuration;
|
|
1186
|
+
private restartHeartbeat;
|
|
1187
|
+
private enterSession;
|
|
1188
|
+
/** Bắt đầu phát khi đang standby (từ nút trên app hoặc lệnh dashboard). */
|
|
1189
|
+
startPublishing(): Promise<void>;
|
|
1190
|
+
/** Tạm dừng phát: ngắt kết nối LiveKit hoàn toàn về standby, KHÔNG kết thúc phiên. */
|
|
1191
|
+
stopPublishing(): Promise<void>;
|
|
1192
|
+
private ensureSessionTermination;
|
|
1193
|
+
private abandonGrant;
|
|
1194
|
+
private isOperationCurrent;
|
|
1195
|
+
private ensureOperation;
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
type Fetch = typeof globalThis.fetch;
|
|
1199
|
+
type HeadersInput = Record<string, string>;
|
|
1200
|
+
interface HttpSessionProviderPaths {
|
|
1201
|
+
bootstrap: string;
|
|
1202
|
+
sessions: string;
|
|
1203
|
+
refresh(sessionId: string): string;
|
|
1204
|
+
end(sessionId: string): string;
|
|
1205
|
+
publish(sessionId: string): string;
|
|
1206
|
+
unpublish(sessionId: string): string;
|
|
1207
|
+
}
|
|
1208
|
+
interface HttpSessionProviderOptions {
|
|
1209
|
+
/** Base URL of the partner-owned mobile proxy. Never the Pickleball API URL. */
|
|
1210
|
+
baseUrl: string;
|
|
1211
|
+
fetch?: Fetch;
|
|
1212
|
+
headers?: HeadersInput | (() => HeadersInput | Promise<HeadersInput>);
|
|
1213
|
+
timeoutMs?: number;
|
|
1214
|
+
paths?: Partial<HttpSessionProviderPaths>;
|
|
1215
|
+
}
|
|
1216
|
+
declare function createHttpSessionProvider(options: HttpSessionProviderOptions): LivestreamSessionProvider;
|
|
1217
|
+
|
|
1218
|
+
interface LocalTrackLike {
|
|
1219
|
+
kind: string;
|
|
1220
|
+
stop(): void;
|
|
1221
|
+
mute?(): Promise<unknown>;
|
|
1222
|
+
unmute?(): Promise<unknown>;
|
|
1223
|
+
/** livekit-client LocalVideoTrack restart; deterministic facing switch. */
|
|
1224
|
+
restartTrack?(options: {
|
|
1225
|
+
facingMode?: "user" | "environment";
|
|
1226
|
+
deviceId?: string;
|
|
1227
|
+
resolution?: {
|
|
1228
|
+
width: number;
|
|
1229
|
+
height: number;
|
|
1230
|
+
frameRate: number;
|
|
1231
|
+
};
|
|
1232
|
+
}): Promise<unknown>;
|
|
1233
|
+
mediaStreamTrack?: {
|
|
1234
|
+
readyState?: string;
|
|
1235
|
+
/**
|
|
1236
|
+
* react-native-webrtc: đổi thiết bị/facing TẠI CHỖ trên cùng track native
|
|
1237
|
+
* (mediaStreamTrackApplyConstraints) — preview và sender giữ nguyên.
|
|
1238
|
+
*/
|
|
1239
|
+
applyConstraints?(constraints: {
|
|
1240
|
+
facingMode?: "user" | "environment";
|
|
1241
|
+
deviceId?: string;
|
|
1242
|
+
width?: number;
|
|
1243
|
+
height?: number;
|
|
1244
|
+
frameRate?: number;
|
|
1245
|
+
}): Promise<void>;
|
|
1246
|
+
/** react-native-webrtc native camera flip; blind front/back toggle. */
|
|
1247
|
+
_switchCamera?(): void;
|
|
1248
|
+
};
|
|
1249
|
+
}
|
|
1250
|
+
interface RoomLike {
|
|
1251
|
+
state: string;
|
|
1252
|
+
localParticipant: {
|
|
1253
|
+
publishTrack(track: LocalTrackLike, options?: unknown): Promise<unknown>;
|
|
1254
|
+
unpublishTrack?(track: LocalTrackLike, stopOnUnpublish?: boolean): Promise<unknown> | unknown;
|
|
1255
|
+
};
|
|
1256
|
+
connect(serverUrl: string, token: string, options?: unknown): Promise<void>;
|
|
1257
|
+
disconnect(stopTracks?: boolean): Promise<void>;
|
|
1258
|
+
on(event: string, listener: (...args: unknown[]) => void): unknown;
|
|
1259
|
+
off(event: string, listener: (...args: unknown[]) => void): unknown;
|
|
1260
|
+
}
|
|
1261
|
+
interface NativeLiveKitBindings {
|
|
1262
|
+
registerGlobals(): void;
|
|
1263
|
+
createRoom(): RoomLike;
|
|
1264
|
+
createLocalTracks(options: {
|
|
1265
|
+
audio: true;
|
|
1266
|
+
video: {
|
|
1267
|
+
facingMode?: "user" | "environment";
|
|
1268
|
+
deviceId?: string;
|
|
1269
|
+
resolution: {
|
|
1270
|
+
width: number;
|
|
1271
|
+
height: number;
|
|
1272
|
+
frameRate: number;
|
|
1273
|
+
};
|
|
1274
|
+
};
|
|
1275
|
+
}): Promise<LocalTrackLike[]>;
|
|
1276
|
+
startAudioSession(): Promise<void> | void;
|
|
1277
|
+
stopAudioSession(): Promise<void> | void;
|
|
1278
|
+
/** Current window orientation; capture dimensions follow it. */
|
|
1279
|
+
isLandscape?(): boolean;
|
|
1280
|
+
/** Physical/virtual cameras reported by the platform. */
|
|
1281
|
+
listCameras?(): Promise<CameraDeviceInfo[]>;
|
|
1282
|
+
cameraSource?: unknown;
|
|
1283
|
+
events: {
|
|
1284
|
+
reconnecting: string;
|
|
1285
|
+
reconnected: string;
|
|
1286
|
+
disconnected: string;
|
|
1287
|
+
mediaDevicesError: string;
|
|
1288
|
+
/** LiveKit RoomEvent.DataReceived — nhận lệnh điều khiển từ dashboard. */
|
|
1289
|
+
dataReceived?: string;
|
|
1290
|
+
};
|
|
1291
|
+
}
|
|
1292
|
+
interface LocalPreviewReference {
|
|
1293
|
+
videoTrack: unknown;
|
|
1294
|
+
trackRef?: {
|
|
1295
|
+
participant: RoomLike["localParticipant"];
|
|
1296
|
+
publication: unknown;
|
|
1297
|
+
source?: unknown;
|
|
1298
|
+
};
|
|
1299
|
+
}
|
|
1300
|
+
declare class LiveKitRoomAdapter implements RoomAdapter {
|
|
1301
|
+
private readonly bindings;
|
|
1302
|
+
private readonly room;
|
|
1303
|
+
private readonly listeners;
|
|
1304
|
+
private tracks;
|
|
1305
|
+
private videoTrack?;
|
|
1306
|
+
private audioTrack?;
|
|
1307
|
+
private preview?;
|
|
1308
|
+
private isFront;
|
|
1309
|
+
private orientationOverride;
|
|
1310
|
+
private deviceOverride;
|
|
1311
|
+
private published;
|
|
1312
|
+
private disposed;
|
|
1313
|
+
private quality;
|
|
1314
|
+
private readonly nativeListeners;
|
|
1315
|
+
constructor(bindings: NativeLiveKitBindings);
|
|
1316
|
+
preparePreview(quality: 720 | 1080): Promise<LocalPreviewReference | undefined>;
|
|
1317
|
+
connect(input: RoomConnection): Promise<void>;
|
|
1318
|
+
/** Bật/tắt publish track khi đã trong phòng — standby ↔ live. */
|
|
1319
|
+
setPublishing(enabled: boolean): Promise<void>;
|
|
1320
|
+
disconnect(stopTracks?: boolean): Promise<void>;
|
|
1321
|
+
private captureResolution;
|
|
1322
|
+
setCaptureOrientation(orientation: "portrait" | "landscape"): Promise<void>;
|
|
1323
|
+
/**
|
|
1324
|
+
* livekit-client restart() chỉ giữ deviceId/facingMode khi gọi getUserMedia
|
|
1325
|
+
* (vứt width/height/frameRate) nên track mới rơi về format mặc định
|
|
1326
|
+
* 640x480. Native applyConstraints thì ngược lại: chỉ xử lý
|
|
1327
|
+
* width/height/frameRate. Sau mỗi restartTrack phải nắn lại format trên
|
|
1328
|
+
* track mới bằng applyConstraints.
|
|
1329
|
+
*/
|
|
1330
|
+
private reapplyCaptureFormat;
|
|
1331
|
+
switchCamera(): Promise<void>;
|
|
1332
|
+
listCameras(): Promise<CameraDeviceInfo[]>;
|
|
1333
|
+
selectCamera(deviceId: string, facing: CameraDeviceInfo["facing"]): Promise<void>;
|
|
1334
|
+
setMicrophoneEnabled(enabled: boolean): Promise<void>;
|
|
1335
|
+
subscribe(listener: (event: RoomEvent) => void): () => boolean;
|
|
1336
|
+
dispose(): Promise<void>;
|
|
1337
|
+
private releaseTracks;
|
|
1338
|
+
private ensurePreparedTracks;
|
|
1339
|
+
private emit;
|
|
1340
|
+
private ensureNotDisposed;
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
declare function createNativeLiveKitBindings(): NativeLiveKitBindings;
|
|
1344
|
+
declare function createDefaultRuntime(): SdkRuntime;
|
|
1345
|
+
declare function createDefaultStorage(): SdkStorage;
|
|
1346
|
+
declare function createDefaultNetwork(): SdkNetwork;
|
|
1347
|
+
declare function createDefaultTelemetry(fetchImplementation?: typeof fetch, timeoutMs?: number): TelemetrySink;
|
|
1348
|
+
declare function createDefaultRoomFactory(): EngineDependencies["roomFactory"];
|
|
1349
|
+
/**
|
|
1350
|
+
* Thu chỉ số thiết bị cho heartbeat. Mỗi nguồn là optional — thiếu package
|
|
1351
|
+
* (expo-battery/expo-device/expo-file-system) thì bỏ qua trường đó thay vì lỗi.
|
|
1352
|
+
*/
|
|
1353
|
+
declare function createDefaultDeviceMetrics(): NonNullable<EngineDependencies["deviceMetrics"]>;
|
|
1354
|
+
|
|
1355
|
+
interface LivestreamTheme {
|
|
1356
|
+
background: string;
|
|
1357
|
+
foreground: string;
|
|
1358
|
+
accent: string;
|
|
1359
|
+
danger: string;
|
|
1360
|
+
/** Chữ phụ mờ nhất (labels, chú thích). */
|
|
1361
|
+
muted: string;
|
|
1362
|
+
overlay: string;
|
|
1363
|
+
radius: number;
|
|
1364
|
+
/** Token FintechX — đồng bộ với dashboard web. */
|
|
1365
|
+
surface: string;
|
|
1366
|
+
elevated: string;
|
|
1367
|
+
hairline: string;
|
|
1368
|
+
/** Màu chữ đặt trên nền accent. */
|
|
1369
|
+
ink: string;
|
|
1370
|
+
textSecondary: string;
|
|
1371
|
+
radiusSm: number;
|
|
1372
|
+
fontFamily?: string;
|
|
1373
|
+
fontFamilyBold?: string;
|
|
1374
|
+
displayFontFamily?: string;
|
|
1375
|
+
}
|
|
1376
|
+
/** FintechX design tokens — trùng bộ token trong apps/web/globals.css. */
|
|
1377
|
+
declare const defaultLivestreamTheme: LivestreamTheme;
|
|
1378
|
+
interface PickleballLiveProviderProps {
|
|
1379
|
+
children?: ReactNode;
|
|
1380
|
+
sessionProvider: LivestreamSessionProvider;
|
|
1381
|
+
bootstrapInput: BootstrapInput;
|
|
1382
|
+
runtime?: SdkRuntime;
|
|
1383
|
+
storage?: SdkStorage;
|
|
1384
|
+
network?: SdkNetwork;
|
|
1385
|
+
telemetry?: TelemetrySink;
|
|
1386
|
+
roomFactory?: () => RoomAdapter;
|
|
1387
|
+
deviceMetrics?: EngineDependencies["deviceMetrics"];
|
|
1388
|
+
theme?: Partial<LivestreamTheme>;
|
|
1389
|
+
locale?: "vi" | "en";
|
|
1390
|
+
onError?: (error: PickleballExpoError) => void;
|
|
1391
|
+
}
|
|
1392
|
+
interface LivestreamContextValue extends LivestreamSnapshot {
|
|
1393
|
+
locale: "vi" | "en";
|
|
1394
|
+
theme: LivestreamTheme;
|
|
1395
|
+
prepare(): Promise<void>;
|
|
1396
|
+
start(input: StartLivestreamInput, options?: {
|
|
1397
|
+
autoPublish?: boolean;
|
|
1398
|
+
}): Promise<void>;
|
|
1399
|
+
/** Bắt đầu phát khi đang standby. */
|
|
1400
|
+
startPublishing(): Promise<void>;
|
|
1401
|
+
/** Tạm dừng phát về standby (không kết thúc phiên). */
|
|
1402
|
+
stopPublishing(): Promise<void>;
|
|
1403
|
+
stop(reason?: EndReason): Promise<void>;
|
|
1404
|
+
retry(): Promise<void>;
|
|
1405
|
+
switchCamera(): Promise<void>;
|
|
1406
|
+
setMicrophoneEnabled(enabled: boolean): Promise<void>;
|
|
1407
|
+
setCaptureOrientation(orientation: "portrait" | "landscape"): Promise<void>;
|
|
1408
|
+
selectCamera(deviceId: string): Promise<void>;
|
|
1409
|
+
}
|
|
1410
|
+
declare function PickleballLiveProvider(props: PickleballLiveProviderProps): React.JSX.Element;
|
|
1411
|
+
declare function useLivestream(): LivestreamContextValue;
|
|
1412
|
+
|
|
1413
|
+
type RenderState = ReturnType<typeof useLivestream>;
|
|
1414
|
+
interface LivestreamScreenProps {
|
|
1415
|
+
input: StartLivestreamInput;
|
|
1416
|
+
style?: ViewStyle;
|
|
1417
|
+
/**
|
|
1418
|
+
* Hướng máy vật lý (từ cảm biến, ví dụ expo-sensors DeviceMotion). Khi được
|
|
1419
|
+
* cung cấp, nút bắt đầu phát bị chặn nếu máy đang cầm lệch với khung
|
|
1420
|
+
* 9:16/16:9 đã chọn. Bỏ trống để tắt kiểm tra.
|
|
1421
|
+
*/
|
|
1422
|
+
physicalOrientation?: "portrait" | "landscape" | null;
|
|
1423
|
+
/**
|
|
1424
|
+
* Tự kết nối vào phòng khi mở màn hình nếu consent đã được lưu từ trước
|
|
1425
|
+
* (mặc định bật) — dashboard điều khiển được ngay từ lúc app bật.
|
|
1426
|
+
*/
|
|
1427
|
+
autoConnect?: boolean;
|
|
1428
|
+
renderPreview?: (state: RenderState) => ReactNode;
|
|
1429
|
+
renderHeader?: (state: RenderState) => ReactNode;
|
|
1430
|
+
renderControls?: (state: RenderState) => ReactNode;
|
|
1431
|
+
renderError?: (state: RenderState) => ReactNode;
|
|
1432
|
+
onEnded?: () => void;
|
|
1433
|
+
}
|
|
1434
|
+
declare function LivestreamScreen(props: LivestreamScreenProps): React.JSX.Element;
|
|
1435
|
+
|
|
1436
|
+
export { type AnnouncementKind, type AnnouncementPlan, type AudioPlayback, BTHOME_SERVICE_UUID, type BTHomeAdvertisementInput, BTHomeRemoteDeduplicator, type BTHomeRemoteDeduplicatorOptions, BTHomeRemoteParseError, type BTHomeRemoteParseErrorCode, type BootstrapInput, type CameraDeviceInfo, type CaptureOrientation, ConsentRequiredError, type CourtSide, type CourtSwitchConfirmedEvent, DEFAULT_REMOTE_ACTION_MAPPING, type DefaultMatchVoiceServiceOptions, type DeviceMetricsSample, type EndLivestreamInput, type EndReason, type EngineDependencies, type ExpoAudioBindings, type ExpoAudioPlaybackOptions, type ExpoAudioPlayer, ExpoSdkConfigurationError, type ExpoSdkErrorCode, ExpoSdkTimeoutError, type ExpoSqliteDatabaseLike, type FallbackVoiceToken, type GameSnapshot, type GeminiVoiceEngineOptions, type GeneratedAudioFileSystem, HidVolumeGestureRecognizer, type HidVolumeGestureRecognizerOptions, type HttpSessionProviderOptions, type HttpSessionProviderPaths, InvalidResponseError, LEGAL_LIVESTREAM_TRANSITIONS, LatestAnnouncementScheduler, type LatestAnnouncementSchedulerOptions, LiveKitRoomAdapter, type LivestreamContextValue, LivestreamEngine, LivestreamScreen, type LivestreamScreenProps, type LivestreamSessionProvider, type LivestreamSnapshot, type LivestreamState, type LivestreamTheme, type LocalPreviewReference, MATCH_PERSISTENCE_SCHEMA_VERSION, MATCH_SQL_MIGRATIONS, MATCH_SQL_SCHEMA_VERSION, type MatchCorrectedEvent, type MatchCorrection, type MatchCreateInput, type MatchCreatedEvent, type MatchCreation, type MatchEndedEvent, type MatchEvent, MatchEventStore, type MatchEventUndoneEvent, type MatchFormat, type MatchInputSource, type MatchPersistenceDriver, MatchPersistenceError, type MatchPersistenceErrorCode, type MatchPersistenceTransaction, type MatchPlayer, type MatchScoringMode, type MatchSnapshot, type MatchSummary, type MatchTeam, type MatchTeamId, type MatchVoiceService, MemoryMatchPersistenceDriver, type NativeAudioSessionBindings, type NativeLiveKitBindings, NetworkUnavailableError, type NeuralVoiceAvailability, type NeuralVoiceEngine, NoActiveMatchError, PENDING_END_KEY, type ParsedBTHomeRemoteAdvertisement, type PendingCourtSwitch, PermissionDeniedError, type PersistedMatch, PickleballExpoError, PickleballLiveProvider, type PickleballLiveProviderProps, type PickleballMatchContextValue, PickleballMatchProvider, type PickleballMatchProviderProps, type PickleballMatchStore, type RallyAwardedEvent, RecordingFailureError, type RemoteActionMapping, type RemoteBinding, type RemoteButtonAction, type RemoteCandidate, type RemoteDeduplicationKey, RemoteError, type RemoteErrorCode, type RemoteHidGesture, type RemoteHidGestureMapping, type RemoteHidVolumeEvent, type RemoteInputMode, type RemoteMappedAction, type RemoteNativeState, type RemotePermissionStatus, type RemoteScanAdvertisement, type RemoteService, type RemoteState, type RemoteStatus, type RoomAdapter, type RoomConnection, type RoomEvent, SDK_TELEMETRY_EVENT_NAMES, type SdkBootstrap, SdkDisabledError, type SdkError, type SdkErrorCode, type SdkNetwork, type SdkRemoteConfig, type SdkRuntime, type SdkSessionGrant, type SdkSessionRefresh, type SdkStorage, type SdkTelemetryCredentials, type SdkTelemetryEvent, type SdkTelemetryEventName, type ServeState, SessionConflictError, type StartLivestreamInput, type StoredMatchEventRow, type StoredMatchRow, type TeamScore, type TelemetrySink, TokenExpiredError, UnknownSdkError, UnsupportedRuntimeError, UnsupportedSdkError, type VoiceAudioMode, type VoiceAudioRoute, type VoiceDisconnectReason, VoiceError, type VoiceErrorCode, type VoiceRouteState, type VoiceTtsOptions, applyMatchEvent, awardRally, buildVietnameseAnnouncement, compareSemver, confirmCourtSwitch, correctMatch, createDefaultDeviceMetrics, createDefaultMatchEventStore, createDefaultMatchVoiceService, createDefaultNetwork, createDefaultRoomFactory, createDefaultRuntime, createDefaultStorage, createDefaultTelemetry, createExpoAudioPlayback, createExpoSqliteMatchDriver, createGeminiVoiceEngine, createHttpSessionProvider, createMatch, createNativeLiveKitBindings, createUnavailableVoiceEngine, decodeBase64Bytes, defaultLivestreamTheme, endMatch, errorFromCode, isSdkRemoteConfig, isSdkVersionSupported, normalizeError, parseBTHomeV2Advertisement, planBundledFallback, replayMatchEvents, resolveBundledVoiceAsset, undoLastMatchEvent, useLivestream, usePickleballMatch };
|