@tastic/shell 0.1.1 → 0.2.0
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 +71 -12
- package/dist/index.d.mts +86 -14
- package/dist/index.d.ts +86 -14
- package/dist/index.js +317 -148
- package/dist/index.mjs +321 -153
- package/package.json +9 -9
- package/src/PackContext.tsx +2 -2
- package/src/RootLayout.tsx +110 -28
- package/src/components/GuideHost.tsx +62 -23
- package/src/components/Providers.tsx +17 -7
- package/src/components/SeatTwoPanel.tsx +2 -1
- package/src/components/SettingsDialog.tsx +6 -4
- package/src/components/UpdateDialog.tsx +1 -1
- package/src/hooks/useSeatColors.ts +54 -27
- package/src/index.ts +1 -1
- package/src/redux/createPackStore.ts +16 -6
- package/src/redux/shellState.ts +4 -2
- package/src/routes.ts +10 -0
- package/src/screens/GameRoute.tsx +4 -3
- package/src/screens/HomeScreen.tsx +145 -76
- package/src/screens/LoadoutScreen.tsx +13 -7
- package/src/screens/ProfilesRoute.tsx +26 -1
- package/src/screens/StatsScreen.tsx +138 -56
- package/src/types.ts +143 -10
package/dist/index.mjs
CHANGED
|
@@ -26,12 +26,13 @@ import { useAutoPaperTheme } from "@rific/auto-paper";
|
|
|
26
26
|
import { useHapticSettings } from "@rific/feedback-press";
|
|
27
27
|
import { Toaster, ToastProvider } from "@rific/toaster";
|
|
28
28
|
import { OrientationProvider, useRotationAwareStatusBar, useThemedRootBackground } from "@tastic/core";
|
|
29
|
-
import { RotatedFrame } from "@tastic/hud";
|
|
29
|
+
import { RotatedFrame } from "@tastic/hud/chrome";
|
|
30
|
+
import { ProfileColorProvider } from "@tastic/profile";
|
|
30
31
|
import * as Haptics from "expo-haptics";
|
|
31
|
-
import { Stack } from "expo-router";
|
|
32
|
+
import { DarkTheme, Stack, ThemeProvider as NavigationThemeProvider } from "expo-router";
|
|
32
33
|
import { DeviceMotion } from "expo-sensors";
|
|
33
|
-
import { useCallback as useCallback6 } from "react";
|
|
34
|
-
import { StyleSheet } from "react-native";
|
|
34
|
+
import { useCallback as useCallback6, useMemo as useMemo3 } from "react";
|
|
35
|
+
import { Platform as Platform2, StyleSheet, View } from "react-native";
|
|
35
36
|
import { GestureHandlerRootView } from "react-native-gesture-handler";
|
|
36
37
|
import * as RNPaper2 from "react-native-paper";
|
|
37
38
|
import { SafeAreaProvider } from "react-native-safe-area-context";
|
|
@@ -75,24 +76,35 @@ function seatTwoFor(saved, choices) {
|
|
|
75
76
|
function humanSeatsFor(mode) {
|
|
76
77
|
return mode === "twoPlayer" ? [1, 2] : [1];
|
|
77
78
|
}
|
|
79
|
+
var GUEST_PLAYER_ID = "guest";
|
|
78
80
|
|
|
79
81
|
// src/components/GuideHost.tsx
|
|
80
82
|
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
83
|
+
var PACK_GUIDE = "__pack__";
|
|
84
|
+
var NO_INPUT = {};
|
|
81
85
|
var GameGuideContext = createContext2(null);
|
|
82
86
|
function GuideHost({ children }) {
|
|
83
87
|
const pack = usePack();
|
|
84
|
-
const [
|
|
85
|
-
const
|
|
88
|
+
const [subject, setSubject] = useState(() => pack.guide ? PACK_GUIDE : pack.games.find((g) => g.guide)?.id ?? null);
|
|
89
|
+
const packGuide = subject === PACK_GUIDE ? pack.guide : void 0;
|
|
90
|
+
const game = subject === PACK_GUIDE ? void 0 : pack.games.find((g) => g.id === subject);
|
|
91
|
+
const gameGuide = game?.guide;
|
|
86
92
|
const rotation = useRotation();
|
|
87
93
|
const touch = useIsTouchPrimaryDevice();
|
|
88
94
|
const seatTwo = useSelector((state) => state.opponent.seatTwo);
|
|
89
|
-
const input = useSelector((state) =>
|
|
90
|
-
const seenVersion = useSelector((state) =>
|
|
95
|
+
const input = useSelector((state) => gameGuide ? gameGuide.selectInput(state) : NO_INPUT, shallowEqual);
|
|
96
|
+
const seenVersion = useSelector((state) => (packGuide ?? gameGuide)?.selectVersionSeen(state) ?? 0);
|
|
91
97
|
const dispatch = useDispatch();
|
|
92
|
-
const mode = roundModeFor(seatTwoFor(seatTwo, game
|
|
93
|
-
const steps = useMemo(() =>
|
|
94
|
-
const onSeen = useCallback(
|
|
95
|
-
|
|
98
|
+
const mode = roundModeFor(seatTwoFor(seatTwo, game?.seatTwo));
|
|
99
|
+
const steps = useMemo(() => packGuide ? packGuide.getSteps({ touch }) : gameGuide ? gameGuide.getSteps(input, { touch, mode }) : [], [packGuide, gameGuide, input, touch, mode]);
|
|
100
|
+
const onSeen = useCallback(
|
|
101
|
+
(version) => {
|
|
102
|
+
const guide = packGuide ?? gameGuide;
|
|
103
|
+
if (guide) dispatch(guide.markSeen(version));
|
|
104
|
+
},
|
|
105
|
+
[dispatch, packGuide, gameGuide]
|
|
106
|
+
);
|
|
107
|
+
return /* @__PURE__ */ jsx3(GuideProvider, { steps, currentVersion: (packGuide ?? gameGuide)?.version ?? 0, seenVersion, onSeen, rotation, children: /* @__PURE__ */ jsx3(GameGuideBridge, { onPick: setSubject, children }) });
|
|
96
108
|
}
|
|
97
109
|
function GameGuideBridge({ onPick, children }) {
|
|
98
110
|
const pack = usePack();
|
|
@@ -100,6 +112,7 @@ function GameGuideBridge({ onPick, children }) {
|
|
|
100
112
|
const { isOpen, open } = useGuide();
|
|
101
113
|
const pendingNext = useRef(null);
|
|
102
114
|
const wasOpen = useRef(false);
|
|
115
|
+
const packAutoShown = useRef(false);
|
|
103
116
|
useEffect(() => {
|
|
104
117
|
if (wasOpen.current && !isOpen && pendingNext.current) {
|
|
105
118
|
const next = pendingNext.current;
|
|
@@ -110,15 +123,16 @@ function GameGuideBridge({ onPick, children }) {
|
|
|
110
123
|
}, [isOpen]);
|
|
111
124
|
const openFor = useCallback(
|
|
112
125
|
(gameId) => {
|
|
126
|
+
if (!pack.games.find((g) => g.id === gameId)?.guide) return;
|
|
113
127
|
onPick(gameId);
|
|
114
128
|
open();
|
|
115
129
|
},
|
|
116
|
-
[onPick, open]
|
|
130
|
+
[pack, onPick, open]
|
|
117
131
|
);
|
|
118
132
|
const openFirstTime = useCallback(
|
|
119
133
|
(gameId, next) => {
|
|
120
|
-
const
|
|
121
|
-
if (!
|
|
134
|
+
const guide = pack.games.find((g) => g.id === gameId)?.guide;
|
|
135
|
+
if (!guide || guide.selectVersionSeen(store.getState()) >= guide.version) {
|
|
122
136
|
next();
|
|
123
137
|
return;
|
|
124
138
|
}
|
|
@@ -127,7 +141,18 @@ function GameGuideBridge({ onPick, children }) {
|
|
|
127
141
|
},
|
|
128
142
|
[pack, store, openFor]
|
|
129
143
|
);
|
|
130
|
-
const
|
|
144
|
+
const openPack = useCallback(() => {
|
|
145
|
+
if (!pack.guide) return;
|
|
146
|
+
onPick(PACK_GUIDE);
|
|
147
|
+
open();
|
|
148
|
+
}, [pack, onPick, open]);
|
|
149
|
+
const autoShowPack = useCallback(() => {
|
|
150
|
+
const guide = pack.guide;
|
|
151
|
+
if (!guide || packAutoShown.current || guide.selectVersionSeen(store.getState()) >= guide.version) return;
|
|
152
|
+
packAutoShown.current = true;
|
|
153
|
+
openPack();
|
|
154
|
+
}, [pack, store, openPack]);
|
|
155
|
+
const value = useMemo(() => ({ openFirstTime, openFor, openPack, autoShowPack }), [openFirstTime, openFor, openPack, autoShowPack]);
|
|
131
156
|
return /* @__PURE__ */ jsx3(GameGuideContext.Provider, { value, children });
|
|
132
157
|
}
|
|
133
158
|
function useGameGuide() {
|
|
@@ -147,15 +172,20 @@ import * as RNPaper from "react-native-paper";
|
|
|
147
172
|
import { Provider as ReduxProvider, useDispatch as useDispatch2, useSelector as useSelector2 } from "react-redux";
|
|
148
173
|
import { PersistGate } from "redux-persist/integration/react";
|
|
149
174
|
import { Fragment as Fragment2, jsx as jsx4 } from "react/jsx-runtime";
|
|
175
|
+
function useShellFeedbackSounds() {
|
|
176
|
+
const selection = useAudioPool(__require("./select-MLBNNA4N.wav"));
|
|
177
|
+
const notification = useAudioPool(__require("./notification-CGGFPYK7.wav"));
|
|
178
|
+
return { selection, notification };
|
|
179
|
+
}
|
|
150
180
|
var FeedbackBridge = ({ children }) => {
|
|
151
181
|
const haptic = useSelector2((state) => state.haptic);
|
|
152
182
|
const sound = useSelector2((state) => state.sound);
|
|
153
183
|
const dispatch = useDispatch2();
|
|
154
184
|
const onChange = useCallback2((next) => dispatch(hapticActions.initialize(next)), [dispatch]);
|
|
155
185
|
const onSoundChange = useCallback2((next) => dispatch(soundActions.initialize(next)), [dispatch]);
|
|
156
|
-
const
|
|
157
|
-
const
|
|
158
|
-
const bridgeProps = useFeedbackBridgeProps({ initialValue: haptic, onChange, soundInitialValue: sound, onSoundChange, sound:
|
|
186
|
+
const { useFeedbackSounds = useShellFeedbackSounds } = usePack();
|
|
187
|
+
const sounds = useFeedbackSounds();
|
|
188
|
+
const bridgeProps = useFeedbackBridgeProps({ initialValue: haptic, onChange, soundInitialValue: sound, onSoundChange, sound: sounds });
|
|
159
189
|
return /* @__PURE__ */ jsx4(FeedbackPressProvider, { ...bridgeProps, paper: RNPaper, children });
|
|
160
190
|
};
|
|
161
191
|
var EdgeGuardBridge = ({ children }) => {
|
|
@@ -198,7 +228,7 @@ function Theme({ children }) {
|
|
|
198
228
|
// src/components/UpdateDialog.tsx
|
|
199
229
|
import { useUpdateErrorToast } from "@rific/toaster";
|
|
200
230
|
import { useRotation as useRotation2 } from "@tastic/core";
|
|
201
|
-
import { UpdateDialog as SharedUpdateDialog } from "@tastic/hud";
|
|
231
|
+
import { UpdateDialog as SharedUpdateDialog } from "@tastic/hud/chrome";
|
|
202
232
|
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
203
233
|
function UpdateDialog() {
|
|
204
234
|
const onError = useUpdateErrorToast();
|
|
@@ -371,20 +401,41 @@ function useProfiles() {
|
|
|
371
401
|
}
|
|
372
402
|
|
|
373
403
|
// src/RootLayout.tsx
|
|
374
|
-
import { jsx as jsx9, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
404
|
+
import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
375
405
|
var PLAY_OPTIONS = { statusBarHidden: true };
|
|
406
|
+
var KEEP_STATUS_BAR_OPTIONS = {};
|
|
407
|
+
var TRANSPARENT_CONTENT = { backgroundColor: "transparent" };
|
|
408
|
+
var noScreenOptions = () => ({});
|
|
376
409
|
function AppStack() {
|
|
410
|
+
const pack = usePack();
|
|
411
|
+
const { Backdrop } = pack;
|
|
377
412
|
const { dark } = useAutoPaperTheme();
|
|
378
|
-
const rootBackground = useThemedRootBackground(dark, false);
|
|
413
|
+
const rootBackground = useThemedRootBackground(Backdrop ? true : dark, false);
|
|
379
414
|
const statusBar = useRotationAwareStatusBar();
|
|
380
|
-
|
|
415
|
+
const packOptions = (pack.useScreenOptions ?? noScreenOptions)();
|
|
416
|
+
const backdropOptions = Backdrop ? { contentStyle: TRANSPARENT_CONTENT, animation: Platform2.select({ android: "fade" }) } : void 0;
|
|
417
|
+
const playOptions = useCallback6(
|
|
418
|
+
({ route }) => {
|
|
419
|
+
const id = route.params?.game;
|
|
420
|
+
return pack.games.some((g) => g.id === id && g.keepStatusBar) ? KEEP_STATUS_BAR_OPTIONS : PLAY_OPTIONS;
|
|
421
|
+
},
|
|
422
|
+
[pack.games]
|
|
423
|
+
);
|
|
424
|
+
const navigationTheme = useMemo3(() => Backdrop ? { ...DarkTheme, colors: { ...DarkTheme.colors, background: "transparent" } } : null, [Backdrop]);
|
|
425
|
+
const stack = /* @__PURE__ */ jsxs2(Stack, { screenOptions: { ...rootBackground, ...statusBar, ...backdropOptions, ...packOptions }, children: [
|
|
381
426
|
/* @__PURE__ */ jsx9(Stack.Screen, { name: "index" }),
|
|
382
|
-
/* @__PURE__ */ jsx9(Stack.Screen, { name: "play/[game]", options:
|
|
427
|
+
/* @__PURE__ */ jsx9(Stack.Screen, { name: "play/[game]", options: playOptions })
|
|
428
|
+
] });
|
|
429
|
+
if (!Backdrop || !navigationTheme) return stack;
|
|
430
|
+
return /* @__PURE__ */ jsxs2(View, { style: styles.root, children: [
|
|
431
|
+
/* @__PURE__ */ jsx9(Backdrop, {}),
|
|
432
|
+
/* @__PURE__ */ jsx9(NavigationThemeProvider, { value: navigationTheme, children: stack })
|
|
383
433
|
] });
|
|
384
434
|
}
|
|
385
435
|
function OrientationBridge({ children }) {
|
|
386
436
|
const { settings, setSettings } = useAppSettings();
|
|
387
|
-
|
|
437
|
+
const { portraitWhileKeyboard } = usePack();
|
|
438
|
+
return /* @__PURE__ */ jsx9(OrientationProvider, { deviceMotion: DeviceMotion, freezeWhileLocked: true, lockInitialValue: { locked: settings.lockOrientation, rotation: settings.lockedRotation }, onLockChange: (next) => setSettings({ lockOrientation: next.locked, lockedRotation: next.rotation }), portraitWhileKeyboard, children });
|
|
388
439
|
}
|
|
389
440
|
function GatedApp({ children }) {
|
|
390
441
|
const { loaded } = useProfiles();
|
|
@@ -392,7 +443,8 @@ function GatedApp({ children }) {
|
|
|
392
443
|
}
|
|
393
444
|
function HapticGatedToastProvider({ children }) {
|
|
394
445
|
const { settings } = useHapticSettings();
|
|
395
|
-
|
|
446
|
+
const { Toaster: PackToasterComponent } = usePack();
|
|
447
|
+
return /* @__PURE__ */ jsx9(ToastProvider, { haptics: settings.vibrate ? Haptics : void 0, paper: PackToasterComponent ? void 0 : RNPaper2, children });
|
|
396
448
|
}
|
|
397
449
|
function RotatingToaster() {
|
|
398
450
|
const renderFrame = useCallback6(
|
|
@@ -401,15 +453,28 @@ function RotatingToaster() {
|
|
|
401
453
|
);
|
|
402
454
|
return /* @__PURE__ */ jsx9(Toaster, { historyButton: null, clearButton: null, limit: 3, renderFrame });
|
|
403
455
|
}
|
|
456
|
+
function PackToaster({ Toaster: PackToasterComponent }) {
|
|
457
|
+
return /* @__PURE__ */ jsx9(View, { style: StyleSheet.absoluteFill, pointerEvents: "box-none", children: /* @__PURE__ */ jsx9(RotatedFrame, { rotatedSafeArea: true, passThrough: true, children: /* @__PURE__ */ jsx9(PackToasterComponent, {}) }) });
|
|
458
|
+
}
|
|
404
459
|
function GameProviders({ games, children }) {
|
|
405
|
-
return games.reduceRight((inner, game) => /* @__PURE__ */ jsx9(game.Provider, { children: inner }, game.id), children);
|
|
460
|
+
return games.reduceRight((inner, game) => game.Provider ? /* @__PURE__ */ jsx9(game.Provider, { children: inner }, game.id) : inner, children);
|
|
461
|
+
}
|
|
462
|
+
function PreviewColors({ previewColor, children }) {
|
|
463
|
+
const { dark } = useAutoPaperTheme();
|
|
464
|
+
const preview = useCallback6((hex) => previewColor(hex, dark), [previewColor, dark]);
|
|
465
|
+
return /* @__PURE__ */ jsx9(ProfileColorProvider, { preview, children });
|
|
466
|
+
}
|
|
467
|
+
function PackRoot({ pack, children }) {
|
|
468
|
+
const { Provider, previewColor } = pack;
|
|
469
|
+
const inner = Provider ? /* @__PURE__ */ jsx9(Provider, { children }) : children;
|
|
470
|
+
return previewColor ? /* @__PURE__ */ jsx9(PreviewColors, { previewColor, children: inner }) : /* @__PURE__ */ jsx9(Fragment3, { children: inner });
|
|
406
471
|
}
|
|
407
472
|
function RootLayout({ pack, store, persistor }) {
|
|
408
|
-
return /* @__PURE__ */ jsx9(GestureHandlerRootView, { style: styles.root, children: /* @__PURE__ */ jsx9(SafeAreaProvider, { children: /* @__PURE__ */ jsx9(PackProvider, { pack, children: /* @__PURE__ */ jsx9(Fonts, { children: /* @__PURE__ */ jsx9(Providers, { store, persistor, children: /* @__PURE__ */ jsx9(Theme, { children: /* @__PURE__ */ jsx9(ProfilesProvider, { children: /* @__PURE__ */ jsx9(LastActiveGameProvider, { children: /* @__PURE__ */ jsx9(GameProviders, { games: pack.games, children: /* @__PURE__ */ jsx9(GatedApp, { children: /* @__PURE__ */ jsx9(OrientationBridge, { children: /* @__PURE__ */ jsxs2(HapticGatedToastProvider, { children: [
|
|
473
|
+
return /* @__PURE__ */ jsx9(GestureHandlerRootView, { style: styles.root, children: /* @__PURE__ */ jsx9(SafeAreaProvider, { children: /* @__PURE__ */ jsx9(PackProvider, { pack, children: /* @__PURE__ */ jsx9(Fonts, { children: /* @__PURE__ */ jsx9(Providers, { store, persistor, children: /* @__PURE__ */ jsx9(Theme, { children: /* @__PURE__ */ jsx9(ProfilesProvider, { children: /* @__PURE__ */ jsx9(LastActiveGameProvider, { children: /* @__PURE__ */ jsx9(GameProviders, { games: pack.games, children: /* @__PURE__ */ jsx9(PackRoot, { pack, children: /* @__PURE__ */ jsx9(GatedApp, { children: /* @__PURE__ */ jsx9(OrientationBridge, { children: /* @__PURE__ */ jsxs2(HapticGatedToastProvider, { children: [
|
|
409
474
|
/* @__PURE__ */ jsx9(GuideHost, { children: /* @__PURE__ */ jsx9(AppStack, {}) }),
|
|
410
|
-
/* @__PURE__ */ jsx9(RotatingToaster, {}),
|
|
475
|
+
pack.Toaster ? /* @__PURE__ */ jsx9(PackToaster, { Toaster: pack.Toaster }) : /* @__PURE__ */ jsx9(RotatingToaster, {}),
|
|
411
476
|
/* @__PURE__ */ jsx9(UpdateDialog, {})
|
|
412
|
-
] }) }) }) }) }) }) }) }) }) }) }) });
|
|
477
|
+
] }) }) }) }) }) }) }) }) }) }) }) }) });
|
|
413
478
|
}
|
|
414
479
|
var styles = StyleSheet.create({
|
|
415
480
|
root: { flex: 1 }
|
|
@@ -419,23 +484,30 @@ var styles = StyleSheet.create({
|
|
|
419
484
|
import { Redirect, useLocalSearchParams } from "expo-router";
|
|
420
485
|
import { useEffect as useEffect5 } from "react";
|
|
421
486
|
|
|
487
|
+
// src/routes.ts
|
|
488
|
+
var playHref = (game) => ({ pathname: "/play/[game]", params: { game } });
|
|
489
|
+
var loadoutHref = (game) => ({ pathname: "/loadout/[game]", params: { game } });
|
|
490
|
+
var STATS_HREF = "/stats";
|
|
491
|
+
var PROFILES_HREF = "/profiles";
|
|
492
|
+
|
|
422
493
|
// src/screens/LoadoutScreen.tsx
|
|
423
494
|
import { defaultColors, useAutoPaperTheme as useAutoPaperTheme3 } from "@rific/auto-paper";
|
|
424
495
|
import { FakeLandscapeView, getViewRotation, rotateInsets, useOrientationState } from "@tastic/core";
|
|
425
|
-
import { CornerActionButtons,
|
|
496
|
+
import { CornerActionButtons, PressAwayOverlay, usePopoverHost } from "@tastic/hud/chrome";
|
|
497
|
+
import { PlayerSetupPanel, ReadyButton as ReadyButton2, SharedActionBand } from "@tastic/hud/lobby";
|
|
426
498
|
import { DualZoneLayout, needsSharedNeutralZone, useDualZoneLayout } from "@tastic/split-screen";
|
|
427
499
|
import { router as router2, useFocusEffect as useFocusEffect2 } from "expo-router";
|
|
428
|
-
import { useCallback as useCallback9, useEffect as useEffect4, useMemo as
|
|
429
|
-
import { Platform as
|
|
500
|
+
import { useCallback as useCallback9, useEffect as useEffect4, useMemo as useMemo5, useRef as useRef4, useState as useState4 } from "react";
|
|
501
|
+
import { Platform as Platform3, StyleSheet as StyleSheet3, View as View3 } from "react-native";
|
|
430
502
|
import Animated from "react-native-reanimated";
|
|
431
503
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
432
504
|
import { useDispatch as useDispatch9 } from "react-redux";
|
|
433
505
|
|
|
434
506
|
// src/components/SeatTwoPanel.tsx
|
|
435
|
-
import { ReadyButton, useZoneClampedAlign } from "@tastic/hud";
|
|
507
|
+
import { ReadyButton, useZoneClampedAlign } from "@tastic/hud/lobby";
|
|
436
508
|
import { getOptionPickerContentSize, OptionPicker } from "@tastic/profile";
|
|
437
|
-
import { useMemo as
|
|
438
|
-
import { StyleSheet as StyleSheet2, View } from "react-native";
|
|
509
|
+
import { useMemo as useMemo4 } from "react";
|
|
510
|
+
import { StyleSheet as StyleSheet2, View as View2 } from "react-native";
|
|
439
511
|
import { jsx as jsx10, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
440
512
|
var CPU_PREFIX = "cpu:";
|
|
441
513
|
var PROFILE_PREFIX = "profile:";
|
|
@@ -451,7 +523,7 @@ function SeatTwoPanel({ idPrefix, host, choices, seatTwo, onSeatTwoChange, cpuDi
|
|
|
451
523
|
const isPerson = seatTwo === "person";
|
|
452
524
|
const selectedProfile = isPerson ? profiles.find((p) => p.id === selectedProfileId) ?? null : null;
|
|
453
525
|
const cpuOptions = cpuDifficulty?.options;
|
|
454
|
-
const options =
|
|
526
|
+
const options = useMemo4(() => {
|
|
455
527
|
const list = [];
|
|
456
528
|
if (choices.includes("cpu")) list.push(...cpuOptions ? cpuOptions.map((o) => ({ value: `${CPU_PREFIX}${o.value}`, label: o.label, icon: o.icon })) : [{ value: CPU_PREFIX, label: "CPU", icon: CPU_ICON }]);
|
|
457
529
|
if (choices.includes("nobody")) list.push({ value: NOBODY, label: "Nobody", icon: NOBODY_ICON });
|
|
@@ -480,9 +552,9 @@ function SeatTwoPanel({ idPrefix, host, choices, seatTwo, onSeatTwoChange, cpuDi
|
|
|
480
552
|
const align = useZoneClampedAlign(host.openId === pickerId, size.width, size.height);
|
|
481
553
|
const ownPopoverOpen = host.openId?.startsWith(`${idPrefix}-`) ?? false;
|
|
482
554
|
const pickerRowOpen = secondPickerId !== void 0 && host.openId === secondPickerId;
|
|
483
|
-
return /* @__PURE__ */ jsxs3(
|
|
555
|
+
return /* @__PURE__ */ jsxs3(View2, { style: [styles2.panel, ownPopoverOpen && styles2.panelOpen], children: [
|
|
484
556
|
/* @__PURE__ */ jsx10(OptionPicker, { id: pickerId, host, options, value, onChange: handleChange, color, dark, icon, colorSection, alignOverride: align }),
|
|
485
|
-
isPerson && secondPicker && /* @__PURE__ */ jsx10(
|
|
557
|
+
isPerson && secondPicker && /* @__PURE__ */ jsx10(View2, { style: [styles2.pickerRow, pickerRowOpen && styles2.pickerRowOpen], children: secondPicker }),
|
|
486
558
|
isPerson && showReadyButton && onToggleReady && /* @__PURE__ */ jsx10(ReadyButton, { color, ready: ready ?? false, onToggleReady })
|
|
487
559
|
] });
|
|
488
560
|
}
|
|
@@ -509,13 +581,14 @@ var styles2 = StyleSheet2.create({
|
|
|
509
581
|
|
|
510
582
|
// src/components/SettingsDialog.tsx
|
|
511
583
|
import { useUpdateErrorToast as useUpdateErrorToast2 } from "@rific/toaster";
|
|
512
|
-
import { BaseSettingsDialog } from "@tastic/hud";
|
|
584
|
+
import { BaseSettingsDialog } from "@tastic/hud/chrome";
|
|
513
585
|
import { jsx as jsx11, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
514
586
|
function SettingsDialog({ visible, onDismiss, rotation = 0, onShowHowToPlay, children }) {
|
|
515
587
|
const onUpdateError = useUpdateErrorToast2();
|
|
516
588
|
const { settings, setSettings } = useAppSettings();
|
|
517
|
-
const { otaVersion, SettingsSections } = usePack();
|
|
518
|
-
|
|
589
|
+
const { otaVersion, SettingsSections, edgeGuard = true } = usePack();
|
|
590
|
+
const edgeGuardProps = edgeGuard ? { deferBottomEdgeGestures: settings.deferBottomEdgeGestures, onDeferBottomEdgeGestures: (value) => setSettings({ deferBottomEdgeGestures: value }) } : {};
|
|
591
|
+
return /* @__PURE__ */ jsxs4(BaseSettingsDialog, { visible, onDismiss, rotation, version: otaVersion, lockOrientation: settings.lockOrientation, onLockOrientationChange: (value) => setSettings({ lockOrientation: value }), ...edgeGuardProps, onUpdateError, onShowHowToPlay, children: [
|
|
519
592
|
children,
|
|
520
593
|
SettingsSections && /* @__PURE__ */ jsx11(SettingsSections, {})
|
|
521
594
|
] });
|
|
@@ -560,13 +633,17 @@ function createSeatColorsReducer(seatColors) {
|
|
|
560
633
|
var seatColorsActions = createSeatColorsSlice("pack", defaultSeatColorsState({ 1: "", 2: "" })).actions;
|
|
561
634
|
|
|
562
635
|
// src/hooks/useSeatColors.ts
|
|
636
|
+
function sameTheme(current, next) {
|
|
637
|
+
const a = current.color;
|
|
638
|
+
const b = next.color;
|
|
639
|
+
const sameColor = typeof a === "string" || typeof b === "string" ? typeof a === typeof b && String(a).toLowerCase() === String(b).toLowerCase() : !!a && !!b && a.primary.toLowerCase() === b.primary.toLowerCase() && a.secondary.toLowerCase() === b.secondary.toLowerCase();
|
|
640
|
+
return sameColor && (next.harmony === void 0 || next.harmony === current.harmony);
|
|
641
|
+
}
|
|
563
642
|
function useSeatColors(p2IsHuman) {
|
|
564
|
-
const { colors: themeColors } = useAutoPaperTheme2();
|
|
565
|
-
const { set: setThemeColor } = useThemeSettings();
|
|
566
|
-
const p1Color = themeColors.primary;
|
|
567
|
-
const p2Color = themeColors.secondary;
|
|
643
|
+
const { dark, colors: themeColors } = useAutoPaperTheme2();
|
|
644
|
+
const { settings: themeSettings, set: setThemeColor } = useThemeSettings();
|
|
568
645
|
const { profiles, lastSelected } = useProfiles();
|
|
569
|
-
const { seatColors } = usePack();
|
|
646
|
+
const { seatColors, seatTheme } = usePack();
|
|
570
647
|
const seatDefaultColors = [seatColors[1], seatColors[2]];
|
|
571
648
|
const dispatch = useDispatch8();
|
|
572
649
|
const guestColors = useSelector7((state) => state.seatColors.lastGuestColor);
|
|
@@ -574,11 +651,21 @@ function useSeatColors(p2IsHuman) {
|
|
|
574
651
|
const profileColors = useSelector7((state) => state.seatColors.profileColors);
|
|
575
652
|
const p1Profile = profiles.find((p) => p.id === lastSelected[1]) ?? null;
|
|
576
653
|
const p2Profile = p2IsHuman ? profiles.find((p) => p.id === lastSelected[2]) ?? null : null;
|
|
654
|
+
const rememberedColors = (p1Prof, p2Prof, picks, guest, cpu) => {
|
|
655
|
+
const p1 = p1Prof ? profileSeatColor(p1Prof, picks) : guest[1];
|
|
656
|
+
const p2 = avoidSeatColorClash(p1, p2Prof ? profileSeatColor(p2Prof, picks) : p2IsHuman ? guest[2] : cpu, seatDefaultColors);
|
|
657
|
+
return { p1, p2 };
|
|
658
|
+
};
|
|
659
|
+
const remembered = rememberedColors(p1Profile, p2Profile, profileColors, guestColors, cpuColor);
|
|
660
|
+
const p1Color = seatTheme ? remembered.p1 : themeColors.primary;
|
|
661
|
+
const p2Color = seatTheme ? remembered.p2 : themeColors.secondary;
|
|
662
|
+
const themeFor = useCallback8((p1, p2) => seatTheme ? seatTheme({ 1: p1, 2: p2 }, dark) : { color: { primary: p1, secondary: p2, tertiary: getThirdColor(p1, p2) } }, [seatTheme, dark]);
|
|
577
663
|
const p1ProfileRef = useRef3(p1Profile);
|
|
578
664
|
const p2ProfileRef = useRef3(p2Profile);
|
|
579
665
|
const guestColorsRef = useRef3(guestColors);
|
|
580
666
|
const cpuColorRef = useRef3(cpuColor);
|
|
581
667
|
const profileColorsRef = useRef3(profileColors);
|
|
668
|
+
const themeForRef = useRef3(themeFor);
|
|
582
669
|
useEffect3(() => {
|
|
583
670
|
p1ProfileRef.current = p1Profile;
|
|
584
671
|
}, [p1Profile]);
|
|
@@ -595,21 +682,17 @@ function useSeatColors(p2IsHuman) {
|
|
|
595
682
|
profileColorsRef.current = profileColors;
|
|
596
683
|
}, [profileColors]);
|
|
597
684
|
useEffect3(() => {
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
685
|
+
themeForRef.current = themeFor;
|
|
686
|
+
}, [themeFor]);
|
|
687
|
+
useEffect3(() => {
|
|
688
|
+
const next = themeFor(remembered.p1, remembered.p2);
|
|
689
|
+
if (sameTheme(themeSettings, next)) return;
|
|
690
|
+
setThemeColor(next);
|
|
691
|
+
}, [lastSelected[1], lastSelected[2], p2IsHuman, guestColors, cpuColor, profileColors, profiles.length, dark]);
|
|
603
692
|
useFocusEffect(
|
|
604
693
|
useCallback8(() => {
|
|
605
|
-
const
|
|
606
|
-
|
|
607
|
-
const p1Prof = p1ProfileRef.current;
|
|
608
|
-
const p2Prof = p2ProfileRef.current;
|
|
609
|
-
const picks = profileColorsRef.current;
|
|
610
|
-
const nextP1 = p1Prof ? profileSeatColor(p1Prof, picks) : guest[1];
|
|
611
|
-
const nextP2 = avoidSeatColorClash(nextP1, p2Prof ? profileSeatColor(p2Prof, picks) : p2IsHuman ? guest[2] : cpu, seatDefaultColors);
|
|
612
|
-
setThemeColor({ color: { primary: nextP1, secondary: nextP2, tertiary: getThirdColor(nextP1, nextP2) } });
|
|
694
|
+
const next = rememberedColors(p1ProfileRef.current, p2ProfileRef.current, profileColorsRef.current, guestColorsRef.current, cpuColorRef.current);
|
|
695
|
+
setThemeColor(themeForRef.current(next.p1, next.p2));
|
|
613
696
|
}, [lastSelected[1], lastSelected[2], p2IsHuman, setThemeColor])
|
|
614
697
|
);
|
|
615
698
|
const persistSeatColor = useCallback8(
|
|
@@ -631,21 +714,21 @@ function useSeatColors(p2IsHuman) {
|
|
|
631
714
|
(hex) => {
|
|
632
715
|
const swapped = hex.toLowerCase() === p2Color.toLowerCase();
|
|
633
716
|
const nextP2 = swapped ? p1Color : p2Color;
|
|
634
|
-
setThemeColor(
|
|
717
|
+
setThemeColor(themeFor(hex, nextP2));
|
|
635
718
|
persistSeatColor(1, hex);
|
|
636
719
|
if (swapped) persistSeatColor(2, nextP2);
|
|
637
720
|
},
|
|
638
|
-
[setThemeColor, p1Color, p2Color, persistSeatColor]
|
|
721
|
+
[setThemeColor, themeFor, p1Color, p2Color, persistSeatColor]
|
|
639
722
|
);
|
|
640
723
|
const handleP2ColorChange = useCallback8(
|
|
641
724
|
(hex) => {
|
|
642
725
|
const swapped = hex.toLowerCase() === p1Color.toLowerCase();
|
|
643
726
|
const nextP1 = swapped ? p2Color : p1Color;
|
|
644
|
-
setThemeColor(
|
|
727
|
+
setThemeColor(themeFor(nextP1, hex));
|
|
645
728
|
persistSeatColor(2, hex);
|
|
646
729
|
if (swapped) persistSeatColor(1, nextP1);
|
|
647
730
|
},
|
|
648
|
-
[setThemeColor, p1Color, p2Color, persistSeatColor]
|
|
731
|
+
[setThemeColor, themeFor, p1Color, p2Color, persistSeatColor]
|
|
649
732
|
);
|
|
650
733
|
return { p1Color, p2Color, handleP1ColorChange, handleP2ColorChange };
|
|
651
734
|
}
|
|
@@ -671,16 +754,17 @@ function LoadoutScreen({ game }) {
|
|
|
671
754
|
const { seatTwo: savedSeatTwo, setSeatTwo } = useOpponent();
|
|
672
755
|
const seatTwo = seatTwoFor(savedSeatTwo, game.seatTwo);
|
|
673
756
|
const mode = roundModeFor(seatTwo);
|
|
674
|
-
const humanSeats =
|
|
757
|
+
const humanSeats = useMemo5(() => humanSeatsFor(mode), [mode]);
|
|
675
758
|
const p2IsHuman = mode === "twoPlayer";
|
|
676
759
|
const p2Exists = mode !== "solo";
|
|
677
760
|
const loadout = game.useLoadout(mode);
|
|
678
761
|
const { openFor } = useGameGuide();
|
|
679
762
|
const { profiles, lastSelected, selectProfile } = useProfiles();
|
|
680
763
|
const { orientationMode, p1OnRight, upsideDown, resolved: p1OnRightResolved } = useOrientationState();
|
|
681
|
-
const fixedSeats = p2IsHuman && (!!game.twoPlayerSeats?.faceToFaceOnWeb ||
|
|
764
|
+
const fixedSeats = p2IsHuman && (!!game.twoPlayerSeats?.faceToFaceOnWeb || Platform3.OS !== "web");
|
|
682
765
|
const fixedUpsideDown = !!game.twoPlayerSeats?.followUpsideDown && orientationMode === "faceToFace" && upsideDown;
|
|
683
766
|
const { panelLayout, panelFadeStyle } = useDualZoneLayout(fixedSeats ? "faceToFace" : orientationMode, fixedSeats ? true : p1OnRight, fixedSeats || p1OnRightResolved, fixedSeats ? fixedUpsideDown : upsideDown, PANEL_SWAP_FADE_MS);
|
|
767
|
+
const playersRowFade = panelFadeStyle;
|
|
684
768
|
const [settingsOpen, setSettingsOpen] = useState4(false);
|
|
685
769
|
const rotation = getViewRotation(panelLayout.orientationMode, panelLayout.p1OnRight, panelLayout.upsideDown);
|
|
686
770
|
const insets = rotateInsets(useSafeAreaInsets(), rotation);
|
|
@@ -709,7 +793,7 @@ function LoadoutScreen({ game }) {
|
|
|
709
793
|
useEffect4(() => {
|
|
710
794
|
if (!humanSeats.every((s) => ready[s])) return;
|
|
711
795
|
dispatch(liveplayActions.setActiveRound({ game: game.id, mode, settings: roundSettingsRef.current }));
|
|
712
|
-
router2.push(
|
|
796
|
+
router2.push(playHref(game.id));
|
|
713
797
|
}, [ready, humanSeats, game.id, mode, dispatch]);
|
|
714
798
|
const bg = dark ? "#000000" : "#FFFFFF";
|
|
715
799
|
const fg = dark ? "#FFFFFF" : "#000000";
|
|
@@ -737,10 +821,10 @@ function LoadoutScreen({ game }) {
|
|
|
737
821
|
const sharedControls = showMetaInSharedBand ? /* @__PURE__ */ jsx12(SharedActionBand, { onBack: safeBack, onSettings: () => setSettingsOpen(true), onRandomize: loadout.randomize, onReset: loadout.reset, fg, popoverOpen: optionsPopoverOpen, children: optionsRow }) : optionsRow;
|
|
738
822
|
const controlLook = { accentColor: themeColors.tertiary, mutedColor: fgMuted, dark, rotation };
|
|
739
823
|
const p1Control = loadout.seatControl(1, controlsHost, { ...controlLook, seatColor: p1Color });
|
|
740
|
-
const p1Panel = /* @__PURE__ */ jsx12(PlayerSetupPanel, { idPrefix: "p1", host: controlsHost, color: p1Color, onColorChange: handleP1ColorChange, swatches: defaultColors, takenColor: p2Exists ? p2Color : void 0, allowSwapTaken: mode === "vsCpu", colorCaption: pack.colorCaption, isHuman: true, ready: ready[1], onToggleReady: () => setReady((r) => ({ ...r, 1: !r[1] })), dark, showReadyButton, secondPicker: p1Control, secondPickerId: p1Control ? "p1-control" : void 0, profiles, selectedProfileId: lastSelected[1], takenProfileId: p2IsHuman ? lastSelected[2] : null, guestLabel: "P1", onProfileSelect: handleP1ProfileSelect, onManageProfiles: () => router2.push(
|
|
824
|
+
const p1Panel = /* @__PURE__ */ jsx12(PlayerSetupPanel, { idPrefix: "p1", host: controlsHost, color: p1Color, onColorChange: handleP1ColorChange, swatches: defaultColors, takenColor: p2Exists ? p2Color : void 0, allowSwapTaken: mode === "vsCpu", colorCaption: pack.colorCaption, isHuman: true, ready: ready[1], onToggleReady: () => setReady((r) => ({ ...r, 1: !r[1] })), dark, showReadyButton, secondPicker: p1Control, secondPickerId: p1Control ? "p1-control" : void 0, profiles, selectedProfileId: lastSelected[1], takenProfileId: p2IsHuman ? lastSelected[2] : null, guestLabel: "P1", onProfileSelect: handleP1ProfileSelect, onManageProfiles: () => router2.push(PROFILES_HREF) });
|
|
741
825
|
const p2Control = p2IsHuman ? loadout.seatControl(2, p2Host, { ...controlLook, seatColor: p2Color }) : void 0;
|
|
742
826
|
const p2Panel = game.seatTwo && /* @__PURE__ */ jsx12(SeatTwoPanel, { idPrefix: "p2", host: p2Host, choices: game.seatTwo, seatTwo, onSeatTwoChange: changeSeatTwo, cpuDifficulty: loadout.cpuDifficulty, profiles, selectedProfileId: lastSelected[2], onProfileSelect: handleP2ProfileSelect, color: p2Color, onColorChange: handleP2ColorChange, swatches: defaultColors, takenColor: p1Color, allowSwapTaken: mode === "vsCpu", colorCaption: pack.colorCaption, dark, secondPicker: p2Control, secondPickerId: p2Control ? "p2-control" : void 0, ready: ready[2], onToggleReady: () => setReady((r) => ({ ...r, 2: !r[2] })), showReadyButton });
|
|
743
|
-
return /* @__PURE__ */ jsxs5(
|
|
827
|
+
return /* @__PURE__ */ jsxs5(View3, { style: [styles3.container, { backgroundColor: bg }], children: [
|
|
744
828
|
/* @__PURE__ */ jsxs5(FakeLandscapeView, { orientationMode: panelLayout.orientationMode, p1OnRight: panelLayout.p1OnRight, upsideDown: panelLayout.upsideDown, style: styles3.rotatable, children: [
|
|
745
829
|
/* @__PURE__ */ jsx12(PressAwayOverlay, { active: controlsHost.openId !== null, onPress: controlsHost.close }),
|
|
746
830
|
p2ZoneStyle && /* @__PURE__ */ jsx12(PressAwayOverlay, { active: controlsHost.openId !== null || p2Host.openId !== null, onPress: p2Host.close, style: p2ZoneStyle }),
|
|
@@ -757,9 +841,9 @@ function LoadoutScreen({ game }) {
|
|
|
757
841
|
p2Elevated: p2Host.openId !== null,
|
|
758
842
|
sharedElevated: optionsPopoverOpen
|
|
759
843
|
}
|
|
760
|
-
) : /* @__PURE__ */ jsxs5(
|
|
844
|
+
) : /* @__PURE__ */ jsxs5(View3, { style: styles3.stackedZone, children: [
|
|
761
845
|
sharedControls,
|
|
762
|
-
/* @__PURE__ */ jsxs5(Animated.View, { style: [styles3.playersRow, anyPlayerPickerOpen && styles3.playersRowOpen,
|
|
846
|
+
/* @__PURE__ */ jsxs5(Animated.View, { style: [styles3.playersRow, anyPlayerPickerOpen && styles3.playersRowOpen, playersRowFade], children: [
|
|
763
847
|
!isPortrait && panelLayout.p1OnRight ? p2Panel : p1Panel,
|
|
764
848
|
!isPortrait && panelLayout.p1OnRight ? p1Panel : p2Panel
|
|
765
849
|
] }),
|
|
@@ -829,7 +913,7 @@ function LoadoutRoute() {
|
|
|
829
913
|
const { game: id } = useLocalSearchParams();
|
|
830
914
|
const game = usePackGame(id);
|
|
831
915
|
if (!game) return /* @__PURE__ */ jsx13(Redirect, { href: "/" });
|
|
832
|
-
if (!hasLoadout(game)) return /* @__PURE__ */ jsx13(Redirect, { href:
|
|
916
|
+
if (!hasLoadout(game)) return /* @__PURE__ */ jsx13(Redirect, { href: playHref(game.id) });
|
|
833
917
|
return /* @__PURE__ */ jsx13(LoadoutScreen, { game }, game.id);
|
|
834
918
|
}
|
|
835
919
|
function hasLoadout(game) {
|
|
@@ -853,15 +937,15 @@ function useResumePoint(gameId) {
|
|
|
853
937
|
}
|
|
854
938
|
|
|
855
939
|
// src/screens/HomeScreen.tsx
|
|
856
|
-
import { defaultColors as defaultColors2, useAutoPaperTheme as useAutoPaperTheme4 } from "@rific/auto-paper";
|
|
940
|
+
import { defaultColors as defaultColors2, FadingScrollView, useAutoPaperTheme as useAutoPaperTheme4 } from "@rific/auto-paper";
|
|
857
941
|
import { IconButton, Pressable } from "@rific/feedback-press";
|
|
858
942
|
import { FakeLandscapeView as FakeLandscapeView2, useRotation as useRotation4 } from "@tastic/core";
|
|
859
|
-
import { PressAwayOverlay as PressAwayOverlay2, usePopoverHost as usePopoverHost2, useWellColors, WELL_SHADOW } from "@tastic/hud";
|
|
860
|
-
import { ProfilePicker } from "@tastic/profile";
|
|
943
|
+
import { PressAwayOverlay as PressAwayOverlay2, usePopoverHost as usePopoverHost2, useWellColors, WELL_SHADOW } from "@tastic/hud/chrome";
|
|
944
|
+
import { getProfilePickerContentSize, ProfilePicker } from "@tastic/profile";
|
|
861
945
|
import { Redirect as Redirect2, router as router3, useIsFocused } from "expo-router";
|
|
862
|
-
import { useCallback as useCallback10, useEffect as useEffect6, useState as useState6 } from "react";
|
|
863
|
-
import { AppState,
|
|
864
|
-
import { Icon, Text } from "react-native-paper";
|
|
946
|
+
import { useCallback as useCallback10, useEffect as useEffect6, useMemo as useMemo6, useState as useState6 } from "react";
|
|
947
|
+
import { AppState, StyleSheet as StyleSheet4, View as View4 } from "react-native";
|
|
948
|
+
import { Icon, Portal, Text } from "react-native-paper";
|
|
865
949
|
|
|
866
950
|
// src/hooks/useFrozenWhileHidden.ts
|
|
867
951
|
import { useState as useState5 } from "react";
|
|
@@ -941,15 +1025,19 @@ function useHomeLayout() {
|
|
|
941
1025
|
}
|
|
942
1026
|
|
|
943
1027
|
// src/screens/HomeScreen.tsx
|
|
944
|
-
import { Fragment as
|
|
1028
|
+
import { Fragment as Fragment4, jsx as jsx14, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
945
1029
|
function HomeScreen() {
|
|
946
1030
|
const pack = usePack();
|
|
947
1031
|
const { consumeResumeGame } = useLastActiveGame();
|
|
1032
|
+
const { autoShowPack } = useGameGuide();
|
|
948
1033
|
const [resumeGame] = useState6(() => {
|
|
949
1034
|
const id = consumeResumeGame();
|
|
950
1035
|
return pack.games.find((g) => g.id === id && g.resumable) ?? null;
|
|
951
1036
|
});
|
|
952
|
-
|
|
1037
|
+
useEffect6(() => {
|
|
1038
|
+
if (!resumeGame) autoShowPack();
|
|
1039
|
+
}, [resumeGame, autoShowPack]);
|
|
1040
|
+
if (resumeGame) return /* @__PURE__ */ jsx14(Redirect2, { href: playHref(resumeGame.id) });
|
|
953
1041
|
return /* @__PURE__ */ jsx14(HomeMenu, {});
|
|
954
1042
|
}
|
|
955
1043
|
function HomeMenu() {
|
|
@@ -957,9 +1045,10 @@ function HomeMenu() {
|
|
|
957
1045
|
const rotation = useRotation4();
|
|
958
1046
|
const layout = useHomeLayout();
|
|
959
1047
|
const { insets } = layout;
|
|
960
|
-
const { dark } = useAutoPaperTheme4();
|
|
961
|
-
const { openFirstTime, openFor } = useGameGuide();
|
|
1048
|
+
const { dark, colors } = useAutoPaperTheme4();
|
|
1049
|
+
const { openFirstTime, openFor, openPack } = useGameGuide();
|
|
962
1050
|
const [settingsOpen, setSettingsOpen] = useState6(false);
|
|
1051
|
+
const [rulesGame, setRulesGame] = useState6(null);
|
|
963
1052
|
const isFocused = useIsFocused();
|
|
964
1053
|
const [appState, setAppState] = useState6(AppState.currentState);
|
|
965
1054
|
useEffect6(() => {
|
|
@@ -983,65 +1072,85 @@ function HomeMenu() {
|
|
|
983
1072
|
const closePicker = pickerHost.close;
|
|
984
1073
|
useEffect6(() => {
|
|
985
1074
|
closePicker();
|
|
986
|
-
}, [isWide, closePicker]);
|
|
987
|
-
const bg = dark ? "#000000" : "#FFFFFF";
|
|
988
|
-
const fg = dark ? "#FFFFFF" : "#000000";
|
|
989
|
-
const
|
|
990
|
-
const
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
1075
|
+
}, [isWide, rotation, layout.windowWidth, layout.windowHeight, closePicker]);
|
|
1076
|
+
const bg = pack.Backdrop ? "transparent" : dark ? "#000000" : "#FFFFFF";
|
|
1077
|
+
const fg = pack.foreground ?? (dark ? "#FFFFFF" : "#000000");
|
|
1078
|
+
const { previewColor } = pack;
|
|
1079
|
+
const p1Drawn = previewColor ? previewColor(p1Color, dark) : p1Color;
|
|
1080
|
+
const triggerStyle = useMemo6(() => pack.Backdrop ? [styles4.pill, { backgroundColor: colors.surface }] : void 0, [pack.Backdrop, colors.surface]);
|
|
1081
|
+
const openGame = useCallback10((game) => openFirstTime(game.id, () => router3.push(game.useLoadout ? loadoutHref(game.id) : playHref(game.id))), [openFirstTime]);
|
|
1082
|
+
const openRules = useCallback10((game) => game.Rules ? setRulesGame(game) : openFor(game.id), [openFor]);
|
|
1083
|
+
const colorSection = { value: p1Color, onChange: handleP1ColorChange, swatches: defaultColors2, caption: selectedProfile ? pack.colorCaption : void 0, previewValue: previewColor ? (hex) => previewColor(hex, dark) : void 0 };
|
|
1084
|
+
const usePickerAlign = pack.usePickerAlign ?? noPickerAlign;
|
|
1085
|
+
const pickerSize = getProfilePickerContentSize(profiles.length, true, colorSection);
|
|
1086
|
+
const pickerAlign = usePickerAlign(pickerHost.openId === `${SEAT_ONE_PICKER}-profile`, pickerSize.width, pickerSize.height);
|
|
1087
|
+
const picker = /* @__PURE__ */ jsx14(ProfilePicker, { idPrefix: SEAT_ONE_PICKER, host: pickerHost, profiles, selectedId: lastSelected[1], color: p1Drawn, dark, guestLabel: pack.guestLabel ?? "P1", nullLabel: pack.nullLabel, onSelect: (profile) => selectProfile(1, profile?.id ?? null), onManage: () => router3.push(PROFILES_HREF), colorSection, alignOverride: pickerAlign, triggerStyle });
|
|
1088
|
+
const pressAway = /* @__PURE__ */ jsx14(PressAwayOverlay2, { active: pickerHost.openId !== null, onPress: pickerHost.close, style: styles4.pressAway });
|
|
1089
|
+
const RulesSheet = rulesGame?.Rules;
|
|
1090
|
+
return /* @__PURE__ */ jsxs6(View4, { style: [styles4.container, { backgroundColor: bg }], children: [
|
|
1091
|
+
/* @__PURE__ */ jsx14(FakeLandscapeView2, { style: styles4.rotatable, children: /* @__PURE__ */ jsxs6(RulesHost, { enabled: pack.games.some((game) => game.Rules), children: [
|
|
1092
|
+
isWide ? /* @__PURE__ */ jsxs6(Fragment4, { children: [
|
|
1093
|
+
pressAway,
|
|
1094
|
+
/* @__PURE__ */ jsx14(FadingScrollView, { style: [styles4.wideRightPane, { left: layout.paddingLeft + layout.leftPaneWidth + HOME_WIDE_PANE_GAP }], contentContainerStyle: { paddingTop: 16 + insets.top, paddingBottom: 16 + insets.bottom, paddingRight: layout.paddingRight }, children: /* @__PURE__ */ jsx14(View4, { style: [styles4.grid, { width: layout.rightPaneWidth }], children: pack.games.map((game) => /* @__PURE__ */ jsx14(GameTile, { game, width: layout.tileWidth, previewWidth: layout.previewWidth, previewRevealed: previewsRevealed, dark, p1Color, p2Color, onPress: () => openGame(game), onRules: hasRules(game) ? () => openRules(game) : void 0 }, game.id)) }) }),
|
|
1095
|
+
/* @__PURE__ */ jsxs6(View4, { style: [styles4.wideLeftPane, { left: layout.paddingLeft, width: layout.leftPaneWidth, paddingTop: HOME_TOP_BAND + insets.top, paddingBottom: 16 + insets.bottom }, pickerHost.openId !== null && styles4.pickerOpen], children: [
|
|
997
1096
|
/* @__PURE__ */ jsx14(pack.HeroTitle, { words: pack.wordmark, color: fg, p1Color, p2Color, active: heroActive, fit: layout.heroFit }),
|
|
998
|
-
/* @__PURE__ */ jsx14(
|
|
1097
|
+
/* @__PURE__ */ jsx14(View4, { style: styles4.widePicker, children: picker })
|
|
999
1098
|
] })
|
|
1000
|
-
] }) : /* @__PURE__ */ jsxs6(
|
|
1099
|
+
] }) : /* @__PURE__ */ jsxs6(FadingScrollView, { style: styles4.scroll, contentContainerStyle: [styles4.content, { paddingTop: 56 + insets.top, paddingBottom: 24 + insets.bottom, paddingLeft: 16 + insets.left, paddingRight: 16 + insets.right }], children: [
|
|
1100
|
+
pressAway,
|
|
1001
1101
|
/* @__PURE__ */ jsx14(pack.HeroTitle, { words: pack.wordmark, color: fg, p1Color, p2Color, active: heroActive }),
|
|
1002
|
-
/* @__PURE__ */ jsx14(
|
|
1003
|
-
/* @__PURE__ */ jsx14(
|
|
1102
|
+
/* @__PURE__ */ jsx14(View4, { style: [styles4.picker, pickerHost.openId !== null && styles4.pickerOpen], children: picker }),
|
|
1103
|
+
/* @__PURE__ */ jsx14(View4, { style: styles4.list, children: pack.games.map((game) => /* @__PURE__ */ jsx14(GameRow, { game, dark, p1Color, p2Color, previewRevealed: previewsRevealed, onPress: () => openGame(game), onRules: hasRules(game) ? () => openRules(game) : void 0 }, game.id)) })
|
|
1004
1104
|
] }),
|
|
1005
|
-
/* @__PURE__ */ jsx14(IconButton, { icon: "trophy", iconColor: fg, size: 24, style: [styles4.corner, { top: 8 + insets.top, left: 8 + insets.left }], onPress: () => router3.push(
|
|
1006
|
-
/* @__PURE__ */ jsx14(IconButton, { icon: "cog", iconColor: fg, size: 24, style: [styles4.corner, { top: 8 + insets.top, right: 8 + insets.right }], onPress: () => setSettingsOpen(true), accessibilityLabel: "Settings" })
|
|
1007
|
-
|
|
1008
|
-
|
|
1105
|
+
/* @__PURE__ */ jsx14(IconButton, { icon: "trophy", iconColor: fg, size: 24, style: [styles4.corner, { top: 8 + insets.top, left: 8 + insets.left }], onPress: () => router3.push(STATS_HREF), accessibilityLabel: "Stats & Achievements" }),
|
|
1106
|
+
/* @__PURE__ */ jsx14(IconButton, { icon: "cog", iconColor: fg, size: 24, style: [styles4.corner, { top: 8 + insets.top, right: 8 + insets.right }], onPress: () => setSettingsOpen(true), accessibilityLabel: "Settings" }),
|
|
1107
|
+
RulesSheet && /* @__PURE__ */ jsx14(RulesSheet, { visible: true, onDismiss: () => setRulesGame(null) })
|
|
1108
|
+
] }) }),
|
|
1109
|
+
/* @__PURE__ */ jsx14(SettingsDialog, { visible: settingsOpen, onDismiss: () => setSettingsOpen(false), rotation, onShowHowToPlay: pack.guide ? openPack : void 0 })
|
|
1009
1110
|
] });
|
|
1010
1111
|
}
|
|
1112
|
+
var SEAT_ONE_PICKER = "home-p1";
|
|
1113
|
+
var noPickerAlign = () => void 0;
|
|
1114
|
+
function hasRules(game) {
|
|
1115
|
+
return !!(game.Rules || game.guide);
|
|
1116
|
+
}
|
|
1117
|
+
function RulesHost({ enabled, children }) {
|
|
1118
|
+
return enabled ? /* @__PURE__ */ jsx14(Portal.Host, { children }) : /* @__PURE__ */ jsx14(Fragment4, { children });
|
|
1119
|
+
}
|
|
1011
1120
|
function GameRow({ game, dark, p1Color, p2Color, previewRevealed, onPress, onRules }) {
|
|
1012
1121
|
const well = useWellColors();
|
|
1013
1122
|
const GameIcon = game.icon;
|
|
1014
1123
|
return (
|
|
1015
1124
|
// The rules button sits beside the row's own button, laid over its end, not inside it: a button
|
|
1016
1125
|
// inside a button is invalid on the web.
|
|
1017
|
-
/* @__PURE__ */ jsxs6(
|
|
1126
|
+
/* @__PURE__ */ jsxs6(View4, { children: [
|
|
1018
1127
|
/* @__PURE__ */ jsxs6(Pressable, { onPress, style: [styles4.row, { backgroundColor: well.bg }], accessibilityRole: "button", accessibilityLabel: game.title, children: [
|
|
1019
|
-
game.Preview ? /* @__PURE__ */ jsx14(PreviewBox, { game, width: ROW_PREVIEW_WIDTH, revealed: previewRevealed, dark, p1Color, p2Color }) : /* @__PURE__ */ jsx14(
|
|
1020
|
-
/* @__PURE__ */ jsxs6(
|
|
1128
|
+
game.Preview ? /* @__PURE__ */ jsx14(PreviewBox, { game, width: ROW_PREVIEW_WIDTH, revealed: previewRevealed, dark, p1Color, p2Color }) : /* @__PURE__ */ jsx14(View4, { style: [styles4.rowIcon, { backgroundColor: dark ? ICON_DARK_BG : ICON_LIGHT_BG }], children: GameIcon && /* @__PURE__ */ jsx14(GameIcon, { size: ROW_ICON_SIZE, p1Color, p2Color, dark }) }),
|
|
1129
|
+
/* @__PURE__ */ jsxs6(View4, { style: styles4.rowText, children: [
|
|
1021
1130
|
/* @__PURE__ */ jsx14(Text, { variant: "titleLarge", style: [styles4.rowTitle, { color: well.fg }], numberOfLines: 1, children: game.title }),
|
|
1022
1131
|
/* @__PURE__ */ jsx14(Text, { variant: "bodyMedium", style: { color: well.fgMuted }, numberOfLines: 2, children: game.blurb })
|
|
1023
1132
|
] })
|
|
1024
1133
|
] }),
|
|
1025
|
-
/* @__PURE__ */ jsx14(Pressable, { onPress: onRules, style: styles4.rulesButton, hitSlop: 12, accessibilityRole: "button", accessibilityLabel: `${game.title} rules`, children: /* @__PURE__ */ jsx14(Icon, { source: "information-outline", size: 22, color: well.fgMuted }) })
|
|
1134
|
+
onRules && /* @__PURE__ */ jsx14(Pressable, { onPress: onRules, style: styles4.rulesButton, hitSlop: 12, accessibilityRole: "button", accessibilityLabel: `${game.title} rules`, children: /* @__PURE__ */ jsx14(Icon, { source: "information-outline", size: 22, color: well.fgMuted }) })
|
|
1026
1135
|
] })
|
|
1027
1136
|
);
|
|
1028
1137
|
}
|
|
1029
1138
|
function GameTile({ game, width, previewWidth, dark, p1Color, p2Color, previewRevealed, onPress, onRules }) {
|
|
1030
1139
|
const well = useWellColors();
|
|
1031
|
-
return /* @__PURE__ */ jsxs6(
|
|
1140
|
+
return /* @__PURE__ */ jsxs6(View4, { style: { width }, children: [
|
|
1032
1141
|
/* @__PURE__ */ jsxs6(Pressable, { onPress, style: [styles4.tile, { backgroundColor: well.bg }], accessibilityRole: "button", accessibilityLabel: game.title, children: [
|
|
1033
1142
|
/* @__PURE__ */ jsx14(PreviewBox, { game, width: previewWidth, revealed: previewRevealed, dark, p1Color, p2Color }),
|
|
1034
1143
|
/* @__PURE__ */ jsx14(Text, { variant: "titleMedium", style: [styles4.rowTitle, { color: well.fg }], numberOfLines: 1, children: game.title }),
|
|
1035
1144
|
/* @__PURE__ */ jsx14(Text, { variant: "bodySmall", style: { color: well.fgMuted }, numberOfLines: 3, children: game.blurb })
|
|
1036
1145
|
] }),
|
|
1037
|
-
/* @__PURE__ */ jsx14(Pressable, { onPress: onRules, style: styles4.tileRulesButton, hitSlop: 12, accessibilityRole: "button", accessibilityLabel: `${game.title} rules`, children: /* @__PURE__ */ jsx14(Icon, { source: "information-outline", size: 20, color: "#FFFFFF" }) })
|
|
1146
|
+
onRules && /* @__PURE__ */ jsx14(Pressable, { onPress: onRules, style: styles4.tileRulesButton, hitSlop: 12, accessibilityRole: "button", accessibilityLabel: `${game.title} rules`, children: /* @__PURE__ */ jsx14(Icon, { source: "information-outline", size: 20, color: "#FFFFFF" }) })
|
|
1038
1147
|
] });
|
|
1039
1148
|
}
|
|
1040
1149
|
function PreviewBox({ game, width, revealed, dark, p1Color, p2Color }) {
|
|
1041
1150
|
const height = Math.round(width / PREVIEW_ASPECT);
|
|
1042
1151
|
const { Preview, icon: GameIcon } = game;
|
|
1043
1152
|
const iconSize = Math.min(height, ROW_ICON_SIZE);
|
|
1044
|
-
return /* @__PURE__ */ jsx14(
|
|
1153
|
+
return /* @__PURE__ */ jsx14(View4, { style: [styles4.preview, { width, height, backgroundColor: dark ? ICON_DARK_BG : ICON_LIGHT_BG }], children: Preview ? revealed && /* @__PURE__ */ jsx14(Preview, { width, height, p1Color, p2Color, dark }) : GameIcon && /* @__PURE__ */ jsx14(View4, { style: [styles4.previewIcon, { width: iconSize, height: iconSize, borderRadius: iconSize * 0.22 }], children: /* @__PURE__ */ jsx14(GameIcon, { size: iconSize, p1Color, p2Color, dark }) }) });
|
|
1045
1154
|
}
|
|
1046
1155
|
var ROW_ICON_SIZE = 56;
|
|
1047
1156
|
var ICON_LIGHT_BG = "#FAFAFA";
|
|
@@ -1076,6 +1185,15 @@ var styles4 = StyleSheet4.create({
|
|
|
1076
1185
|
pickerOpen: {
|
|
1077
1186
|
zIndex: 100
|
|
1078
1187
|
},
|
|
1188
|
+
// Seat 1's button on the theme's surface (over a backdrop), the same pill as the tiles' wells.
|
|
1189
|
+
pill: {
|
|
1190
|
+
borderRadius: 999
|
|
1191
|
+
},
|
|
1192
|
+
// Over the rows (and, wide, the grid), whose own stacking is unset, so a press on them reaches it on
|
|
1193
|
+
// the web too, and under the raised button and left pane, so the menu's rows stay pressable.
|
|
1194
|
+
pressAway: {
|
|
1195
|
+
zIndex: 5
|
|
1196
|
+
},
|
|
1079
1197
|
// The preview's own rounding, clipping whatever it draws to the box.
|
|
1080
1198
|
preview: {
|
|
1081
1199
|
alignItems: "center",
|
|
@@ -1166,66 +1284,109 @@ var styles4 = StyleSheet4.create({
|
|
|
1166
1284
|
|
|
1167
1285
|
// src/screens/ProfilesRoute.tsx
|
|
1168
1286
|
import { defaultColors as defaultColors3 } from "@rific/auto-paper";
|
|
1287
|
+
import { RotatedFrame as RotatedFrame2 } from "@tastic/hud/chrome";
|
|
1169
1288
|
import { ProfilesScreen } from "@tastic/profile";
|
|
1170
1289
|
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
1290
|
+
var ROSTER_MAX_WIDTH = 640;
|
|
1291
|
+
function useRosterAlign(open, contentWidth, contentHeight) {
|
|
1292
|
+
const { usePickerAlign } = usePack();
|
|
1293
|
+
return usePickerAlign(open, contentWidth, contentHeight, { maxContentWidth: ROSTER_MAX_WIDTH });
|
|
1294
|
+
}
|
|
1171
1295
|
function ProfilesRoute() {
|
|
1172
1296
|
const { profiles, createProfile, updateProfile, deleteProfile } = useProfiles();
|
|
1173
|
-
|
|
1297
|
+
const { Backdrop, foreground, newProfileColor, usePickerAlign } = usePack();
|
|
1298
|
+
return /* @__PURE__ */ jsx15(RotatedFrame2, { rotatedSafeArea: true, portalHost: true, maxContentWidth: ROSTER_MAX_WIDTH, children: /* @__PURE__ */ jsx15(ProfilesScreen, { profiles, defaultColor: newProfileColor ?? defaultColors3[0].value, onCreate: (patch) => createProfile(patch), onSave: (id, patch) => updateProfile(id, patch), onDelete: deleteProfile, onBack: safeBack, fg: foreground, bg: Backdrop ? "transparent" : void 0, useColorPickerAlign: usePickerAlign ? useRosterAlign : void 0 }) });
|
|
1174
1299
|
}
|
|
1175
1300
|
|
|
1176
1301
|
// src/screens/StatsScreen.tsx
|
|
1177
1302
|
import { useAutoPaperTheme as useAutoPaperTheme5 } from "@rific/auto-paper";
|
|
1178
|
-
import {
|
|
1179
|
-
import {
|
|
1180
|
-
import {
|
|
1181
|
-
import {
|
|
1182
|
-
import { StyleSheet as StyleSheet5, View as View4 } from "react-native";
|
|
1303
|
+
import { BaseStatsScreen, PressAwayOverlay as PressAwayOverlay3, RotatedFrame as RotatedFrame3, usePopoverHost as usePopoverHost3 } from "@tastic/hud/chrome";
|
|
1304
|
+
import { getOptionPickerContentSize as getOptionPickerContentSize2, OptionPicker as OptionPicker2 } from "@tastic/profile";
|
|
1305
|
+
import { useMemo as useMemo7, useState as useState7 } from "react";
|
|
1306
|
+
import { StyleSheet as StyleSheet5, View as View5 } from "react-native";
|
|
1183
1307
|
import { SegmentedButtons, Text as Text2 } from "react-native-paper";
|
|
1184
1308
|
import { useSafeAreaInsets as useSafeAreaInsets3 } from "react-native-safe-area-context";
|
|
1185
1309
|
import { useDispatch as useDispatch10 } from "react-redux";
|
|
1186
1310
|
import { jsx as jsx16, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1311
|
+
var STATS = "stats";
|
|
1312
|
+
var ACHIEVEMENTS = "achievements";
|
|
1187
1313
|
var TABS = [
|
|
1188
|
-
{ value:
|
|
1189
|
-
{ value:
|
|
1314
|
+
{ value: STATS, label: "Stats", icon: "chart-box-outline" },
|
|
1315
|
+
{ value: ACHIEVEMENTS, label: "Achievements", icon: "trophy-outline" }
|
|
1190
1316
|
];
|
|
1317
|
+
var LABEL_WIDE_TAB = { flexBasis: "auto", flexGrow: 1, flexShrink: 1 };
|
|
1318
|
+
var NO_PACK_TABS = [];
|
|
1191
1319
|
var EVERYONE = "__everyone__";
|
|
1192
1320
|
var ALL_GAMES = "__all__";
|
|
1193
1321
|
var PLAYER_PICKER_ID = "stats-player";
|
|
1194
1322
|
var GAME_PICKER_ID = "stats-game";
|
|
1323
|
+
var STATS_MAX_WIDTH = 640;
|
|
1195
1324
|
function StatsScreen() {
|
|
1325
|
+
const pickerHost = usePopoverHost3();
|
|
1326
|
+
return /* @__PURE__ */ jsx16(StatsPressAway, { host: pickerHost, children: /* @__PURE__ */ jsx16(RotatedFrame3, { rotatedSafeArea: true, portalHost: true, maxContentWidth: STATS_MAX_WIDTH, children: /* @__PURE__ */ jsx16(StatsContent, { pickerHost }) }) });
|
|
1327
|
+
}
|
|
1328
|
+
function StatsPressAway({ host, children }) {
|
|
1329
|
+
return /* @__PURE__ */ jsx16(View5, { testID: "stats-press-away", style: styles5.screen, onStartShouldSetResponder: () => host.openId !== null, onResponderRelease: host.close, children });
|
|
1330
|
+
}
|
|
1331
|
+
var useNoPackReset = () => null;
|
|
1332
|
+
var noPickerAlign2 = () => void 0;
|
|
1333
|
+
function StatsContent({ pickerHost }) {
|
|
1196
1334
|
const pack = usePack();
|
|
1197
1335
|
const { dark, colors } = useAutoPaperTheme5();
|
|
1198
|
-
const
|
|
1199
|
-
const
|
|
1200
|
-
const { profiles } = useProfiles();
|
|
1336
|
+
const insets = useSafeAreaInsets3();
|
|
1337
|
+
const { profiles, lastSelected } = useProfiles();
|
|
1201
1338
|
const dispatch = useDispatch10();
|
|
1202
|
-
const
|
|
1203
|
-
const [tab, setTab] = useState7(
|
|
1339
|
+
const fg = pack.foreground ?? colors.onBackground;
|
|
1340
|
+
const [tab, setTab] = useState7(STATS);
|
|
1204
1341
|
const [gameId, setGameId] = useState7(ALL_GAMES);
|
|
1205
|
-
const [selectedProfileId, setSelectedProfileId] = useState7(null);
|
|
1206
|
-
const profileId = selectedProfileId && profiles.some((p) => p.id === selectedProfileId) ? selectedProfileId : null;
|
|
1342
|
+
const [selectedProfileId, setSelectedProfileId] = useState7(() => pack.guestStats ? lastSelected[1] ?? GUEST_PLAYER_ID : null);
|
|
1343
|
+
const profileId = selectedProfileId && profiles.length > 0 && (pack.guestStats && selectedProfileId === GUEST_PLAYER_ID || profiles.some((p) => p.id === selectedProfileId)) ? selectedProfileId : null;
|
|
1207
1344
|
const games = gameId === ALL_GAMES ? pack.games : pack.games.filter((g) => g.id === gameId);
|
|
1208
|
-
const
|
|
1209
|
-
const
|
|
1210
|
-
const
|
|
1211
|
-
const
|
|
1212
|
-
|
|
1345
|
+
const packTabs = pack.statsTabs ?? NO_PACK_TABS;
|
|
1346
|
+
const packTab = packTabs.find((t) => t.value === tab);
|
|
1347
|
+
const showGamePicker = !(tab === ACHIEVEMENTS && pack.AchievementList);
|
|
1348
|
+
const playerOptions = useMemo7(() => [{ value: EVERYONE, label: "Everyone", icon: "account-group" }, ...pack.guestStats ? [{ value: GUEST_PLAYER_ID, label: "Guest", icon: "account-outline" }] : [], ...profiles.map((p) => ({ value: p.id, label: p.name, icon: "account", color: pack.previewColor ? pack.previewColor(p.color, dark) : p.color }))], [profiles, pack, dark]);
|
|
1349
|
+
const gameOptions = useMemo7(() => [{ value: ALL_GAMES, label: "All Games", icon: "view-grid-outline" }, ...pack.games.map((g) => ({ value: g.id, label: g.title }))], [pack.games]);
|
|
1350
|
+
const tabButtons = useMemo7(() => packTabs.length > 0 ? [{ value: STATS, label: "Stats" }, ...packTabs.map(({ value, label }) => ({ value, label })), { value: ACHIEVEMENTS, label: "Achievements" }].map((button) => ({ ...button, style: LABEL_WIDE_TAB })) : TABS, [packTabs]);
|
|
1351
|
+
const title = tab === STATS ? "Stats" : tab === ACHIEVEMENTS ? "Achievements" : packTab?.label ?? "";
|
|
1352
|
+
const usePackReset = pack.resetStats?.useReset ?? useNoPackReset;
|
|
1353
|
+
const packReset = usePackReset();
|
|
1354
|
+
const reset = resetFor(pack, packReset, profileId, profiles, games, () => games.forEach((g) => g.resetStats && dispatch(g.resetStats())));
|
|
1355
|
+
const usePickerAlign = pack.usePickerAlign ?? noPickerAlign2;
|
|
1356
|
+
const playerSize = getOptionPickerContentSize2(playerOptions.length);
|
|
1357
|
+
const gameSize = getOptionPickerContentSize2(gameOptions.length);
|
|
1358
|
+
const playerAlign = usePickerAlign(pickerHost.openId === PLAYER_PICKER_ID, playerSize.width, playerSize.height);
|
|
1359
|
+
const gameAlign = usePickerAlign(pickerHost.openId === GAME_PICKER_ID, gameSize.width, gameSize.height);
|
|
1360
|
+
const surface = pack.Backdrop ? { backgroundColor: colors.surface } : void 0;
|
|
1361
|
+
const triggerStyle = pack.Backdrop ? [styles5.pill, surface] : void 0;
|
|
1362
|
+
const PackTabContent = packTab?.Content;
|
|
1363
|
+
return /* @__PURE__ */ jsxs7(BaseStatsScreen, { title, onBack: safeBack, insets, onReset: reset.onReset, resetLabel: reset.label, resetConfirmTitle: reset.title, resetConfirmBody: reset.body, fg: pack.foreground, bg: pack.Backdrop ? "transparent" : void 0, rotation: 0, children: [
|
|
1213
1364
|
/* @__PURE__ */ jsx16(PressAwayOverlay3, { active: pickerHost.openId !== null, onPress: pickerHost.close, style: styles5.pressAway }),
|
|
1214
|
-
/* @__PURE__ */ jsx16(SegmentedButtons, { value: tab, onValueChange:
|
|
1215
|
-
/* @__PURE__ */ jsxs7(
|
|
1216
|
-
profiles.length > 0 && /* @__PURE__ */ jsx16(OptionPicker2, { id: PLAYER_PICKER_ID, host: pickerHost, options: playerOptions, value: profileId ?? EVERYONE, onChange: (value) => setSelectedProfileId(value === EVERYONE ? null : value), color: colors.primary, dark, icon: profileId ? "account" : "account-group" }),
|
|
1217
|
-
/* @__PURE__ */ jsx16(OptionPicker2, { id: GAME_PICKER_ID, host: pickerHost, options: gameOptions, value: gameId, onChange: setGameId, color: colors.primary, dark, icon: "view-grid-outline" })
|
|
1365
|
+
/* @__PURE__ */ jsx16(SegmentedButtons, { value: tab, onValueChange: setTab, buttons: tabButtons, style: [styles5.tabs, surface] }),
|
|
1366
|
+
/* @__PURE__ */ jsxs7(View5, { style: [styles5.filters, pickerHost.openId !== null && styles5.filtersOpen], onStartShouldSetResponder: () => pickerHost.openId !== null, children: [
|
|
1367
|
+
profiles.length > 0 && /* @__PURE__ */ jsx16(OptionPicker2, { id: PLAYER_PICKER_ID, host: pickerHost, options: playerOptions, value: profileId ?? EVERYONE, onChange: (value) => setSelectedProfileId(value === EVERYONE ? null : value), color: colors.primary, dark, icon: profileId ? "account" : "account-group", alignOverride: playerAlign, triggerStyle }),
|
|
1368
|
+
showGamePicker && /* @__PURE__ */ jsx16(OptionPicker2, { id: GAME_PICKER_ID, host: pickerHost, options: gameOptions, value: gameId, onChange: setGameId, color: colors.primary, dark, icon: "view-grid-outline", alignOverride: gameAlign, triggerStyle })
|
|
1218
1369
|
] }),
|
|
1219
|
-
tab ===
|
|
1220
|
-
|
|
1221
|
-
|
|
1370
|
+
tab === STATS && pack.StatsSections && /* @__PURE__ */ jsx16(pack.StatsSections, { profileId, gameId: gameId === ALL_GAMES ? null : gameId }),
|
|
1371
|
+
PackTabContent && /* @__PURE__ */ jsx16(PackTabContent, { profileId, gameId: gameId === ALL_GAMES ? null : gameId }),
|
|
1372
|
+
tab === ACHIEVEMENTS && pack.AchievementList ? /* @__PURE__ */ jsx16(pack.AchievementList, { profileId }) : (tab === STATS || tab === ACHIEVEMENTS) && games.map((game) => /* @__PURE__ */ jsx16(GameStats, { game, tab, profileId, showTitle: games.length > 1 && pack.statsGameTitles !== false, titleColor: fg }, game.id))
|
|
1373
|
+
] });
|
|
1222
1374
|
}
|
|
1223
|
-
function
|
|
1224
|
-
|
|
1375
|
+
function resetFor(pack, packReset, profileId, profiles, games, resetGames) {
|
|
1376
|
+
if (pack.resetStats && packReset) {
|
|
1377
|
+
const name = profileId === GUEST_PLAYER_ID ? "Guest" : profileId ? profiles.find((p) => p.id === profileId)?.name ?? null : null;
|
|
1378
|
+
return { onReset: () => packReset(profileId), ...pack.resetStats.labels(name) };
|
|
1379
|
+
}
|
|
1380
|
+
const scope = games.length === 1 ? games[0].title : "every game";
|
|
1381
|
+
return { onReset: resetGames, label: games.length === 1 ? `Reset ${scope} Stats` : "Reset All Stats", title: "Reset Stats?", body: `Erases every player's stats and achievements in ${scope}. This cannot be undone.` };
|
|
1382
|
+
}
|
|
1383
|
+
function GameStats({ game, tab, profileId, showTitle, titleColor }) {
|
|
1225
1384
|
const { StatsSections, AchievementList } = game;
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1385
|
+
const Sections = tab === STATS ? StatsSections : AchievementList;
|
|
1386
|
+
if (!Sections) return null;
|
|
1387
|
+
return /* @__PURE__ */ jsxs7(View5, { style: styles5.game, children: [
|
|
1388
|
+
showTitle && /* @__PURE__ */ jsx16(Text2, { variant: "titleLarge", style: [styles5.gameTitle, { color: titleColor }], children: game.title }),
|
|
1389
|
+
/* @__PURE__ */ jsx16(Sections, { profileId })
|
|
1229
1390
|
] });
|
|
1230
1391
|
}
|
|
1231
1392
|
var styles5 = StyleSheet5.create({
|
|
@@ -1245,14 +1406,17 @@ var styles5 = StyleSheet5.create({
|
|
|
1245
1406
|
fontWeight: "bold",
|
|
1246
1407
|
marginTop: 8
|
|
1247
1408
|
},
|
|
1409
|
+
// A picker's button on the theme's surface (over a backdrop), the same pill as Home's.
|
|
1410
|
+
pill: {
|
|
1411
|
+
borderRadius: 999
|
|
1412
|
+
},
|
|
1248
1413
|
pressAway: {
|
|
1249
1414
|
zIndex: 50
|
|
1250
1415
|
},
|
|
1251
|
-
|
|
1252
|
-
rotatable: {
|
|
1416
|
+
screen: {
|
|
1253
1417
|
flex: 1
|
|
1254
1418
|
},
|
|
1255
|
-
// SegmentedButtons' own rounding.
|
|
1419
|
+
// SegmentedButtons' own rounding, so a surface behind it is the same pill.
|
|
1256
1420
|
tabs: {
|
|
1257
1421
|
borderRadius: 20
|
|
1258
1422
|
}
|
|
@@ -1288,7 +1452,7 @@ import { defaultSoundSettings, hapticReducer, soundReducer } from "@rific/feedba
|
|
|
1288
1452
|
import { profilesReducer } from "@tastic/profile";
|
|
1289
1453
|
var initialSoundSettings = { ...defaultSoundSettings, enabled: !__DEV__ };
|
|
1290
1454
|
var appSoundReducer = (state = initialSoundSettings, action) => soundReducer(state, action);
|
|
1291
|
-
function createShellReducers({ seatColors, appearance = "system" }) {
|
|
1455
|
+
function createShellReducers({ seatColors, appearance = "system", seatTheme }) {
|
|
1292
1456
|
return {
|
|
1293
1457
|
// Explicit primary/secondary triad (rather than a single seed expanded via harmony) so the pack's
|
|
1294
1458
|
// identity is pinned exactly instead of drifting with whatever hue math a harmony offset would
|
|
@@ -1297,7 +1461,9 @@ function createShellReducers({ seatColors, appearance = "system" }) {
|
|
|
1297
1461
|
// derived, not picked, via getThirdColor: the hue maximally distant from both (see
|
|
1298
1462
|
// @rific/auto-paper's README "Explicit triad" section). The appearance is the pack's ('system',
|
|
1299
1463
|
// createThemeReducer's own default, when it sets none).
|
|
1300
|
-
|
|
1464
|
+
// A pack with its own seatTheme starts from what seat 1's default color makes of it instead (dark
|
|
1465
|
+
// only when the pack's appearance is; hooks/useSeatColors.ts re-applies it with the live mode).
|
|
1466
|
+
theme: createThemeReducer(seatTheme ? { appearance, ...seatTheme(seatColors, appearance === "dark") } : { appearance, color: { primary: seatColors[1], secondary: seatColors[2], tertiary: getThirdColor2(seatColors[1], seatColors[2]) } }),
|
|
1301
1467
|
haptic: hapticReducer,
|
|
1302
1468
|
sound: appSoundReducer,
|
|
1303
1469
|
// @rific/core's Keep Awake setting (see Providers.tsx's KeepAwakeBridge), under the namespace its
|
|
@@ -1325,7 +1491,7 @@ var errorMiddleware = () => (next) => (action) => {
|
|
|
1325
1491
|
if (!hasError(action)) return next(action);
|
|
1326
1492
|
return action;
|
|
1327
1493
|
};
|
|
1328
|
-
function createPackStore(pack, { keyPrefix, migrate }) {
|
|
1494
|
+
function createPackStore(pack, { keyPrefix, migrate, blacklist = [], serializableIgnoredPaths = [], middleware = [] }) {
|
|
1329
1495
|
const gameReducers = Object.assign({}, ...pack.games.map((game) => game.reducers));
|
|
1330
1496
|
const resumeReducers = pack.games.some((game) => game.resumable) ? { lastActiveGame: lastActiveGameSlice_default } : {};
|
|
1331
1497
|
const rootReducer = combineReducers({ ...createShellReducers(pack), ...resumeReducers, ...pack.reducers, ...gameReducers });
|
|
@@ -1343,7 +1509,7 @@ function createPackStore(pack, { keyPrefix, migrate }) {
|
|
|
1343
1509
|
// failing to ever resolve at all isn't a failure mode worth a 5s failsafe for.
|
|
1344
1510
|
timeout: 0,
|
|
1345
1511
|
// liveplay is per-round state that must never survive a relaunch (see liveplaySlice.ts).
|
|
1346
|
-
blacklist: ["liveplay"],
|
|
1512
|
+
blacklist: ["liveplay", ...blacklist],
|
|
1347
1513
|
migrate
|
|
1348
1514
|
},
|
|
1349
1515
|
rootReducer
|
|
@@ -1353,10 +1519,11 @@ function createPackStore(pack, { keyPrefix, migrate }) {
|
|
|
1353
1519
|
const defaultMiddleware = getDefaultMiddleware({
|
|
1354
1520
|
immutableCheck: false,
|
|
1355
1521
|
serializableCheck: {
|
|
1356
|
-
ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER]
|
|
1522
|
+
ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
|
|
1523
|
+
ignoredPaths: [...serializableIgnoredPaths]
|
|
1357
1524
|
}
|
|
1358
1525
|
});
|
|
1359
|
-
return defaultMiddleware.concat(errorMiddleware);
|
|
1526
|
+
return defaultMiddleware.concat(errorMiddleware, ...middleware);
|
|
1360
1527
|
},
|
|
1361
1528
|
reducer: persistedReducer
|
|
1362
1529
|
});
|
|
@@ -1407,7 +1574,7 @@ function forgetProfileAchievements(dropStats) {
|
|
|
1407
1574
|
|
|
1408
1575
|
// src/gameStats.tsx
|
|
1409
1576
|
import { createAchievementsSlice, mapUnlocksBySeat, useAchievements } from "@tastic/achievements";
|
|
1410
|
-
import { createContext as createContext5, useCallback as useCallback11, useContext as useContext5, useMemo as
|
|
1577
|
+
import { createContext as createContext5, useCallback as useCallback11, useContext as useContext5, useMemo as useMemo8 } from "react";
|
|
1411
1578
|
import { jsx as jsx18 } from "react/jsx-runtime";
|
|
1412
1579
|
var SEATS = [1, 2];
|
|
1413
1580
|
function createGameStats({ key, defaultStats, isValidStats, catalog, getProfileStatsView }) {
|
|
@@ -1440,7 +1607,7 @@ function createGameStats({ key, defaultStats, isValidStats, catalog, getProfileS
|
|
|
1440
1607
|
},
|
|
1441
1608
|
[recordOutcome]
|
|
1442
1609
|
);
|
|
1443
|
-
const value =
|
|
1610
|
+
const value = useMemo8(() => ({ stats, unlockedAchievements, record, resetAll }), [stats, unlockedAchievements, record, resetAll]);
|
|
1444
1611
|
return /* @__PURE__ */ jsx18(Context.Provider, { value, children });
|
|
1445
1612
|
}
|
|
1446
1613
|
function useStats() {
|
|
@@ -1452,7 +1619,7 @@ function createGameStats({ key, defaultStats, isValidStats, catalog, getProfileS
|
|
|
1452
1619
|
}
|
|
1453
1620
|
|
|
1454
1621
|
// src/playerSettings.ts
|
|
1455
|
-
import { useCallback as useCallback12, useMemo as
|
|
1622
|
+
import { useCallback as useCallback12, useMemo as useMemo9 } from "react";
|
|
1456
1623
|
import { useDispatch as useDispatch11, useSelector as useSelector9 } from "react-redux";
|
|
1457
1624
|
var SEATS2 = [1, 2];
|
|
1458
1625
|
function seatPlayerKey(selection, seat) {
|
|
@@ -1496,7 +1663,7 @@ function createPlayerSettings({ id, defaults, distinctControls }) {
|
|
|
1496
1663
|
const saved = useSelector9((state) => state[settingsKey]);
|
|
1497
1664
|
const controls = useSelector9((state) => state[controlsKey]);
|
|
1498
1665
|
const dispatch = useDispatch11();
|
|
1499
|
-
const settings =
|
|
1666
|
+
const settings = useMemo9(() => compose(saved, controls, selection, seatTwoIsPerson), [saved, controls, selection, seatTwoIsPerson]);
|
|
1500
1667
|
const setSettings = useCallback12(
|
|
1501
1668
|
(next) => {
|
|
1502
1669
|
const { controlScheme, ...changes } = next;
|
|
@@ -1516,6 +1683,7 @@ function createPlayerSettings({ id, defaults, distinctControls }) {
|
|
|
1516
1683
|
export {
|
|
1517
1684
|
DEFAULT_APP_SETTINGS,
|
|
1518
1685
|
DEFAULT_OPPONENT,
|
|
1686
|
+
GUEST_PLAYER_ID,
|
|
1519
1687
|
HomeScreen,
|
|
1520
1688
|
LoadoutRoute,
|
|
1521
1689
|
PREVIEW_ASPECT,
|