@tastic/shell 0.3.1 → 0.5.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 +27 -15
- package/dist/index.d.mts +10 -14
- package/dist/index.d.ts +10 -14
- package/dist/index.js +203 -167
- package/dist/index.mjs +161 -125
- package/package.json +16 -18
- package/src/RootLayout.tsx +27 -66
- package/src/components/GuideHost.tsx +4 -6
- package/src/components/SettingsDialog.tsx +4 -10
- package/src/components/Theme.tsx +3 -2
- package/src/components/UpdateDialog.tsx +1 -3
- package/src/hooks/useFrozenWhileHidden.ts +2 -2
- package/src/index.ts +1 -1
- package/src/redux/appSettingsSlice.ts +0 -11
- package/src/screens/HomeScreen.tsx +98 -30
- package/src/screens/LoadoutScreen.tsx +35 -53
- package/src/screens/ProfilesRoute.tsx +5 -5
- package/src/screens/StatsScreen.tsx +4 -5
- package/src/screens/homeLayout.ts +15 -19
- package/src/types.ts +24 -21
package/dist/index.mjs
CHANGED
|
@@ -25,13 +25,12 @@ function usePackGame(id) {
|
|
|
25
25
|
import { useAutoPaperTheme } from "@rific/auto-paper";
|
|
26
26
|
import { useHapticSettings } from "@rific/feedback-press";
|
|
27
27
|
import { Toaster, ToastProvider } from "@rific/toaster";
|
|
28
|
-
import {
|
|
28
|
+
import { useThemedRootBackground } from "@tastic/core";
|
|
29
29
|
import { RotatedFrame } from "@tastic/hud/chrome";
|
|
30
30
|
import { ProfileColorProvider } from "@tastic/profile";
|
|
31
31
|
import * as Haptics from "expo-haptics";
|
|
32
32
|
import { DarkTheme, DefaultTheme, Stack, ThemeProvider as NavigationThemeProvider } from "expo-router";
|
|
33
|
-
import {
|
|
34
|
-
import { useCallback as useCallback6, useMemo as useMemo3 } from "react";
|
|
33
|
+
import { useCallback as useCallback5, useMemo as useMemo3 } from "react";
|
|
35
34
|
import { Platform as Platform2, StyleSheet, View } from "react-native";
|
|
36
35
|
import { GestureHandlerRootView } from "react-native-gesture-handler";
|
|
37
36
|
import * as RNPaper2 from "react-native-paper";
|
|
@@ -60,7 +59,7 @@ function Fonts({ children }) {
|
|
|
60
59
|
}
|
|
61
60
|
|
|
62
61
|
// src/components/GuideHost.tsx
|
|
63
|
-
import { useIsTouchPrimaryDevice
|
|
62
|
+
import { useIsTouchPrimaryDevice } from "@tastic/core";
|
|
64
63
|
import { GuideProvider, useGuide } from "@tastic/hud/guide";
|
|
65
64
|
import { createContext as createContext2, useCallback, useContext as useContext2, useEffect, useMemo, useRef, useState } from "react";
|
|
66
65
|
import { shallowEqual, useDispatch, useSelector, useStore } from "react-redux";
|
|
@@ -89,7 +88,6 @@ function GuideHost({ children }) {
|
|
|
89
88
|
const packGuide = subject === PACK_GUIDE ? pack.guide : void 0;
|
|
90
89
|
const game = subject === PACK_GUIDE ? void 0 : pack.games.find((g) => g.id === subject);
|
|
91
90
|
const gameGuide = game?.guide;
|
|
92
|
-
const rotation = useRotation();
|
|
93
91
|
const touch = useIsTouchPrimaryDevice();
|
|
94
92
|
const seatTwo = useSelector((state) => state.opponent.seatTwo);
|
|
95
93
|
const input = useSelector((state) => gameGuide ? gameGuide.selectInput(state) : NO_INPUT, shallowEqual);
|
|
@@ -104,7 +102,7 @@ function GuideHost({ children }) {
|
|
|
104
102
|
},
|
|
105
103
|
[dispatch, packGuide, gameGuide]
|
|
106
104
|
);
|
|
107
|
-
return /* @__PURE__ */ jsx3(GuideProvider, { steps, currentVersion: (packGuide ?? gameGuide)?.version ?? 0, seenVersion, onSeen,
|
|
105
|
+
return /* @__PURE__ */ jsx3(GuideProvider, { steps, currentVersion: (packGuide ?? gameGuide)?.version ?? 0, seenVersion, onSeen, children: /* @__PURE__ */ jsx3(GameGuideBridge, { onPick: setSubject, children }) });
|
|
108
106
|
}
|
|
109
107
|
function GameGuideBridge({ onPick, children }) {
|
|
110
108
|
const pack = usePack();
|
|
@@ -227,62 +225,29 @@ function Theme({ children }) {
|
|
|
227
225
|
|
|
228
226
|
// src/components/UpdateDialog.tsx
|
|
229
227
|
import { useUpdateErrorToast } from "@rific/toaster";
|
|
230
|
-
import { useRotation as useRotation2 } from "@tastic/core";
|
|
231
228
|
import { UpdateDialog as SharedUpdateDialog } from "@tastic/hud/chrome";
|
|
232
229
|
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
233
230
|
function UpdateDialog() {
|
|
234
231
|
const onError = useUpdateErrorToast();
|
|
235
|
-
|
|
236
|
-
return /* @__PURE__ */ jsx6(SharedUpdateDialog, { onError, rotation });
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// src/hooks/useAppSettings.ts
|
|
240
|
-
import { useCallback as useCallback4 } from "react";
|
|
241
|
-
import { useDispatch as useDispatch4, useSelector as useSelector4 } from "react-redux";
|
|
242
|
-
|
|
243
|
-
// src/redux/appSettingsSlice.ts
|
|
244
|
-
import { createSettingsSlice } from "@rific/core";
|
|
245
|
-
var DEFAULT_APP_SETTINGS = {
|
|
246
|
-
lockOrientation: false,
|
|
247
|
-
deferBottomEdgeGestures: false
|
|
248
|
-
};
|
|
249
|
-
var { actions, reducer } = createSettingsSlice("appSettings", {
|
|
250
|
-
initialState: DEFAULT_APP_SETTINGS,
|
|
251
|
-
initializeMode: "merge",
|
|
252
|
-
fieldSetters: []
|
|
253
|
-
});
|
|
254
|
-
var appSettingsActions = actions;
|
|
255
|
-
var appSettingsSlice_default = reducer;
|
|
256
|
-
|
|
257
|
-
// src/hooks/useAppSettings.ts
|
|
258
|
-
function useAppSettings() {
|
|
259
|
-
const settings = useSelector4((state) => state.appSettings);
|
|
260
|
-
const dispatch = useDispatch4();
|
|
261
|
-
const setSettings = useCallback4(
|
|
262
|
-
(patch) => {
|
|
263
|
-
dispatch(appSettingsActions.initialize(patch));
|
|
264
|
-
},
|
|
265
|
-
[dispatch]
|
|
266
|
-
);
|
|
267
|
-
return { settings, setSettings };
|
|
232
|
+
return /* @__PURE__ */ jsx6(SharedUpdateDialog, { onError });
|
|
268
233
|
}
|
|
269
234
|
|
|
270
235
|
// src/hooks/useLastActiveGame.tsx
|
|
271
236
|
import { createContext as createContext3, useContext as useContext3, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
|
|
272
|
-
import { useDispatch as
|
|
237
|
+
import { useDispatch as useDispatch4, useStore as useStore2 } from "react-redux";
|
|
273
238
|
|
|
274
239
|
// src/redux/lastActiveGameSlice.ts
|
|
275
|
-
import { createSettingsSlice
|
|
276
|
-
var { actions
|
|
277
|
-
var lastActiveGameActions =
|
|
278
|
-
var lastActiveGameSlice_default =
|
|
240
|
+
import { createSettingsSlice } from "@rific/core";
|
|
241
|
+
var { actions, reducer } = createSettingsSlice("lastActiveGame", { initialState: { gameId: null } });
|
|
242
|
+
var lastActiveGameActions = actions;
|
|
243
|
+
var lastActiveGameSlice_default = reducer;
|
|
279
244
|
|
|
280
245
|
// src/hooks/useLastActiveGame.tsx
|
|
281
246
|
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
282
247
|
var LastActiveGameContext = createContext3(null);
|
|
283
248
|
function LastActiveGameProvider({ children }) {
|
|
284
249
|
const store = useStore2();
|
|
285
|
-
const dispatch =
|
|
250
|
+
const dispatch = useDispatch4();
|
|
286
251
|
const [initial] = useState2(() => store.getState().lastActiveGame?.gameId ?? null);
|
|
287
252
|
const pendingResume = useRef2(initial);
|
|
288
253
|
const persisted = useRef2(initial);
|
|
@@ -321,24 +286,24 @@ function useLastActiveGame() {
|
|
|
321
286
|
|
|
322
287
|
// src/hooks/useProfiles.tsx
|
|
323
288
|
import { createProfileRecord, profilesActions, resolveInitialProfiles, useSharedProfilesSync } from "@tastic/profile";
|
|
324
|
-
import { createContext as createContext4, useCallback as
|
|
325
|
-
import { useDispatch as
|
|
289
|
+
import { createContext as createContext4, useCallback as useCallback4, useContext as useContext4, useEffect as useEffect2, useState as useState3 } from "react";
|
|
290
|
+
import { useDispatch as useDispatch5, useSelector as useSelector4 } from "react-redux";
|
|
326
291
|
|
|
327
292
|
// src/redux/profileSelectionSlice.ts
|
|
328
293
|
import { createProfileSelectionSlice } from "@tastic/profile";
|
|
329
294
|
var defaultProfileSelectionState = { 1: null, 2: null };
|
|
330
|
-
var { actions:
|
|
331
|
-
var profileSelectionActions =
|
|
332
|
-
var profileSelectionSlice_default =
|
|
295
|
+
var { actions: actions2, reducer: reducer2 } = createProfileSelectionSlice("pack", defaultProfileSelectionState);
|
|
296
|
+
var profileSelectionActions = actions2;
|
|
297
|
+
var profileSelectionSlice_default = reducer2;
|
|
333
298
|
|
|
334
299
|
// src/hooks/useProfiles.tsx
|
|
335
300
|
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
336
301
|
var SHARED_GROUP_ID = "group.com.infinitetoken.tastic";
|
|
337
302
|
var ProfilesContext = createContext4(null);
|
|
338
303
|
function ProfilesProvider({ children }) {
|
|
339
|
-
const profiles =
|
|
340
|
-
const lastSelected =
|
|
341
|
-
const dispatch =
|
|
304
|
+
const profiles = useSelector4((state) => state.profiles);
|
|
305
|
+
const lastSelected = useSelector4((state) => state.profileSelection);
|
|
306
|
+
const dispatch = useDispatch5();
|
|
342
307
|
const [loaded, setLoaded] = useState3(false);
|
|
343
308
|
useEffect2(() => {
|
|
344
309
|
let cancelled = false;
|
|
@@ -354,7 +319,7 @@ function ProfilesProvider({ children }) {
|
|
|
354
319
|
}, []);
|
|
355
320
|
const { syncToShared } = useSharedProfilesSync({
|
|
356
321
|
groupId: SHARED_GROUP_ID,
|
|
357
|
-
onRemoteChange:
|
|
322
|
+
onRemoteChange: useCallback4(
|
|
358
323
|
(remote) => {
|
|
359
324
|
dispatch(profilesActions.setAll(remote));
|
|
360
325
|
dispatch(profileSelectionActions.pruneToRoster(remote.map((p) => p.id)));
|
|
@@ -362,7 +327,7 @@ function ProfilesProvider({ children }) {
|
|
|
362
327
|
[dispatch]
|
|
363
328
|
)
|
|
364
329
|
});
|
|
365
|
-
const createProfile =
|
|
330
|
+
const createProfile = useCallback4(
|
|
366
331
|
(input) => {
|
|
367
332
|
const record = createProfileRecord(input);
|
|
368
333
|
dispatch(profilesActions.add(record));
|
|
@@ -371,14 +336,14 @@ function ProfilesProvider({ children }) {
|
|
|
371
336
|
},
|
|
372
337
|
[profiles, dispatch, syncToShared]
|
|
373
338
|
);
|
|
374
|
-
const updateProfile =
|
|
339
|
+
const updateProfile = useCallback4(
|
|
375
340
|
(id, patch) => {
|
|
376
341
|
dispatch(profilesActions.update({ id, patch }));
|
|
377
342
|
syncToShared(profiles.map((p) => p.id === id ? { ...p, ...patch, updatedAt: Date.now() } : p));
|
|
378
343
|
},
|
|
379
344
|
[profiles, dispatch, syncToShared]
|
|
380
345
|
);
|
|
381
|
-
const deleteProfile =
|
|
346
|
+
const deleteProfile = useCallback4(
|
|
382
347
|
(id) => {
|
|
383
348
|
dispatch(profilesActions.remove(id));
|
|
384
349
|
syncToShared(profiles.filter((p) => p.id !== id));
|
|
@@ -386,7 +351,7 @@ function ProfilesProvider({ children }) {
|
|
|
386
351
|
},
|
|
387
352
|
[profiles, dispatch, syncToShared]
|
|
388
353
|
);
|
|
389
|
-
const selectProfile =
|
|
354
|
+
const selectProfile = useCallback4(
|
|
390
355
|
(seat, profileId) => {
|
|
391
356
|
dispatch(profileSelectionActions.select({ profileId, seat }));
|
|
392
357
|
},
|
|
@@ -412,10 +377,10 @@ function AppStack() {
|
|
|
412
377
|
const { dark } = useAutoPaperTheme();
|
|
413
378
|
const darkRoot = Backdrop ? true : dark;
|
|
414
379
|
const rootBackground = useThemedRootBackground(darkRoot, false);
|
|
415
|
-
const
|
|
380
|
+
const statusBarStyle = darkRoot ? "light" : "dark";
|
|
416
381
|
const packOptions = (pack.useScreenOptions ?? noScreenOptions)();
|
|
417
382
|
const backdropOptions = Backdrop ? { contentStyle: TRANSPARENT_CONTENT, animation: Platform2.select({ android: "fade" }) } : void 0;
|
|
418
|
-
const playOptions =
|
|
383
|
+
const playOptions = useCallback5(
|
|
419
384
|
({ route }) => {
|
|
420
385
|
const id = route.params?.game;
|
|
421
386
|
return pack.games.some((g) => g.id === id && g.keepStatusBar) ? KEEP_STATUS_BAR_OPTIONS : PLAY_OPTIONS;
|
|
@@ -427,7 +392,7 @@ function AppStack() {
|
|
|
427
392
|
const base = darkRoot ? DarkTheme : DefaultTheme;
|
|
428
393
|
return { ...base, colors: { ...base.colors, background: navigationBackground } };
|
|
429
394
|
}, [darkRoot, navigationBackground]);
|
|
430
|
-
const stack = /* @__PURE__ */ jsx9(NavigationThemeProvider, { value: navigationTheme, children: /* @__PURE__ */ jsxs2(Stack, { screenOptions: { ...rootBackground,
|
|
395
|
+
const stack = /* @__PURE__ */ jsx9(NavigationThemeProvider, { value: navigationTheme, children: /* @__PURE__ */ jsxs2(Stack, { screenOptions: { ...rootBackground, statusBarStyle, ...backdropOptions, ...packOptions }, children: [
|
|
431
396
|
/* @__PURE__ */ jsx9(Stack.Screen, { name: "index" }),
|
|
432
397
|
/* @__PURE__ */ jsx9(Stack.Screen, { name: "play/[game]", options: playOptions })
|
|
433
398
|
] }) });
|
|
@@ -437,11 +402,6 @@ function AppStack() {
|
|
|
437
402
|
stack
|
|
438
403
|
] });
|
|
439
404
|
}
|
|
440
|
-
function OrientationBridge({ children }) {
|
|
441
|
-
const { settings, setSettings } = useAppSettings();
|
|
442
|
-
const { portraitWhileKeyboard } = usePack();
|
|
443
|
-
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 });
|
|
444
|
-
}
|
|
445
405
|
function GatedApp({ children }) {
|
|
446
406
|
const { loaded } = useProfiles();
|
|
447
407
|
return /* @__PURE__ */ jsx9(SplashGate, { gate: "profiles", ready: loaded, children });
|
|
@@ -452,7 +412,7 @@ function HapticGatedToastProvider({ children }) {
|
|
|
452
412
|
return /* @__PURE__ */ jsx9(ToastProvider, { haptics: settings.vibrate ? Haptics : void 0, paper: PackToasterComponent ? void 0 : RNPaper2, children });
|
|
453
413
|
}
|
|
454
414
|
function RotatingToaster() {
|
|
455
|
-
const renderFrame =
|
|
415
|
+
const renderFrame = useCallback5(
|
|
456
416
|
(toasts) => /* @__PURE__ */ jsx9(RotatedFrame, { rotatedSafeArea: true, passThrough: true, children: toasts }),
|
|
457
417
|
[]
|
|
458
418
|
);
|
|
@@ -466,7 +426,7 @@ function GameProviders({ games, children }) {
|
|
|
466
426
|
}
|
|
467
427
|
function PreviewColors({ previewColor, children }) {
|
|
468
428
|
const { dark } = useAutoPaperTheme();
|
|
469
|
-
const preview =
|
|
429
|
+
const preview = useCallback5((hex) => previewColor(hex, dark), [previewColor, dark]);
|
|
470
430
|
return /* @__PURE__ */ jsx9(ProfileColorProvider, { preview, children });
|
|
471
431
|
}
|
|
472
432
|
function PackRoot({ pack, children }) {
|
|
@@ -475,11 +435,11 @@ function PackRoot({ pack, children }) {
|
|
|
475
435
|
return previewColor ? /* @__PURE__ */ jsx9(PreviewColors, { previewColor, children: inner }) : /* @__PURE__ */ jsx9(Fragment3, { children: inner });
|
|
476
436
|
}
|
|
477
437
|
function RootLayout({ pack, store, persistor }) {
|
|
478
|
-
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__ */
|
|
438
|
+
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__ */ jsxs2(HapticGatedToastProvider, { children: [
|
|
479
439
|
/* @__PURE__ */ jsx9(GuideHost, { children: /* @__PURE__ */ jsx9(AppStack, {}) }),
|
|
480
440
|
pack.Toaster ? /* @__PURE__ */ jsx9(PackToaster, { Toaster: pack.Toaster }) : /* @__PURE__ */ jsx9(RotatingToaster, {}),
|
|
481
441
|
/* @__PURE__ */ jsx9(UpdateDialog, {})
|
|
482
|
-
] }) }) }) }) }) }) }) }) }) }) }) })
|
|
442
|
+
] }) }) }) }) }) }) }) }) }) }) }) });
|
|
483
443
|
}
|
|
484
444
|
var styles = StyleSheet.create({
|
|
485
445
|
root: { flex: 1 }
|
|
@@ -498,13 +458,13 @@ var PROFILES_HREF = "/profiles";
|
|
|
498
458
|
|
|
499
459
|
// src/screens/LoadoutScreen.tsx
|
|
500
460
|
import { defaultColors, getRgb, useAutoPaperTheme as useAutoPaperTheme3 } from "@rific/auto-paper";
|
|
501
|
-
import {
|
|
461
|
+
import { useOrientationState } from "@tastic/core";
|
|
502
462
|
import { CornerActionButtons, PressAwayOverlay, usePopoverHost } from "@tastic/hud/chrome";
|
|
503
463
|
import { PlayerSetupPanel, ReadyButton as ReadyButton2, SharedActionBand } from "@tastic/hud/lobby";
|
|
504
464
|
import { DualZoneLayout, needsSharedNeutralZone, useDualZoneLayout } from "@tastic/split-screen";
|
|
505
465
|
import { router as router2, useFocusEffect as useFocusEffect2 } from "expo-router";
|
|
506
466
|
import { useCallback as useCallback9, useEffect as useEffect4, useMemo as useMemo5, useRef as useRef4, useState as useState4 } from "react";
|
|
507
|
-
import {
|
|
467
|
+
import { StyleSheet as StyleSheet3, View as View3 } from "react-native";
|
|
508
468
|
import Animated from "react-native-reanimated";
|
|
509
469
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
510
470
|
import { useDispatch as useDispatch9 } from "react-redux";
|
|
@@ -590,13 +550,45 @@ var styles2 = StyleSheet2.create({
|
|
|
590
550
|
// src/components/SettingsDialog.tsx
|
|
591
551
|
import { useUpdateErrorToast as useUpdateErrorToast2 } from "@rific/toaster";
|
|
592
552
|
import { BaseSettingsDialog } from "@tastic/hud/chrome";
|
|
553
|
+
|
|
554
|
+
// src/hooks/useAppSettings.ts
|
|
555
|
+
import { useCallback as useCallback6 } from "react";
|
|
556
|
+
import { useDispatch as useDispatch6, useSelector as useSelector5 } from "react-redux";
|
|
557
|
+
|
|
558
|
+
// src/redux/appSettingsSlice.ts
|
|
559
|
+
import { createSettingsSlice as createSettingsSlice2 } from "@rific/core";
|
|
560
|
+
var DEFAULT_APP_SETTINGS = {
|
|
561
|
+
deferBottomEdgeGestures: false
|
|
562
|
+
};
|
|
563
|
+
var { actions: actions3, reducer: reducer3 } = createSettingsSlice2("appSettings", {
|
|
564
|
+
initialState: DEFAULT_APP_SETTINGS,
|
|
565
|
+
initializeMode: "merge",
|
|
566
|
+
fieldSetters: []
|
|
567
|
+
});
|
|
568
|
+
var appSettingsActions = actions3;
|
|
569
|
+
var appSettingsSlice_default = reducer3;
|
|
570
|
+
|
|
571
|
+
// src/hooks/useAppSettings.ts
|
|
572
|
+
function useAppSettings() {
|
|
573
|
+
const settings = useSelector5((state) => state.appSettings);
|
|
574
|
+
const dispatch = useDispatch6();
|
|
575
|
+
const setSettings = useCallback6(
|
|
576
|
+
(patch) => {
|
|
577
|
+
dispatch(appSettingsActions.initialize(patch));
|
|
578
|
+
},
|
|
579
|
+
[dispatch]
|
|
580
|
+
);
|
|
581
|
+
return { settings, setSettings };
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// src/components/SettingsDialog.tsx
|
|
593
585
|
import { jsx as jsx11, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
594
|
-
function SettingsDialog({ visible, onDismiss,
|
|
586
|
+
function SettingsDialog({ visible, onDismiss, onShowHowToPlay, children }) {
|
|
595
587
|
const onUpdateError = useUpdateErrorToast2();
|
|
596
588
|
const { settings, setSettings } = useAppSettings();
|
|
597
589
|
const { otaVersion, SettingsSections, edgeGuard = true } = usePack();
|
|
598
590
|
const edgeGuardProps = edgeGuard ? { deferBottomEdgeGestures: settings.deferBottomEdgeGestures, onDeferBottomEdgeGestures: (value) => setSettings({ deferBottomEdgeGestures: value }) } : {};
|
|
599
|
-
return /* @__PURE__ */ jsxs4(BaseSettingsDialog, { visible, onDismiss,
|
|
591
|
+
return /* @__PURE__ */ jsxs4(BaseSettingsDialog, { visible, onDismiss, version: otaVersion, ...edgeGuardProps, onUpdateError, onShowHowToPlay, children: [
|
|
600
592
|
children,
|
|
601
593
|
SettingsSections && /* @__PURE__ */ jsx11(SettingsSections, {})
|
|
602
594
|
] });
|
|
@@ -768,14 +760,12 @@ function LoadoutScreen({ game }) {
|
|
|
768
760
|
const loadout = game.useLoadout(mode);
|
|
769
761
|
const { openFor } = useGameGuide();
|
|
770
762
|
const { profiles, lastSelected, selectProfile } = useProfiles();
|
|
771
|
-
const { orientationMode
|
|
772
|
-
const
|
|
773
|
-
const
|
|
774
|
-
const { panelLayout, panelFadeStyle } = useDualZoneLayout(fixedSeats ? "faceToFace" : orientationMode, fixedSeats ? true : p1OnRight, fixedSeats || p1OnRightResolved, fixedSeats ? fixedUpsideDown : upsideDown, PANEL_SWAP_FADE_MS);
|
|
763
|
+
const { orientationMode } = useOrientationState();
|
|
764
|
+
const panelMode = p2IsHuman && !game.sideBySideWhenWide ? "faceToFace" : orientationMode;
|
|
765
|
+
const { panelLayout, panelFadeStyle } = useDualZoneLayout(panelMode, false, true, false, PANEL_SWAP_FADE_MS);
|
|
775
766
|
const playersRowFade = panelFadeStyle;
|
|
776
767
|
const [settingsOpen, setSettingsOpen] = useState4(false);
|
|
777
|
-
const
|
|
778
|
-
const insets = rotateInsets(useSafeAreaInsets(), rotation);
|
|
768
|
+
const insets = useSafeAreaInsets();
|
|
779
769
|
const { colors: themeColors, dark } = useAutoPaperTheme3();
|
|
780
770
|
const { p1Color, p2Color, handleP1ColorChange, handleP2ColorChange } = useSeatColors(p2IsHuman);
|
|
781
771
|
const dispatch = useDispatch9();
|
|
@@ -813,7 +803,7 @@ function LoadoutScreen({ game }) {
|
|
|
813
803
|
const triggerStyle = useMemo5(() => pack.Backdrop ? [styles3.pill, { backgroundColor: themeColors.surface }] : void 0, [pack.Backdrop, themeColors.surface]);
|
|
814
804
|
const isPortrait = panelLayout.orientationMode === "faceToFace";
|
|
815
805
|
const showMetaInSharedBand = needsSharedNeutralZone(panelLayout.orientationMode, humanSeats.length);
|
|
816
|
-
const p2ZoneStyle = p2IsHuman ? isPortrait ? styles3.p2ZoneTop :
|
|
806
|
+
const p2ZoneStyle = p2IsHuman ? isPortrait ? styles3.p2ZoneTop : styles3.p2ZoneRight : null;
|
|
817
807
|
const showReadyButton = p2IsHuman;
|
|
818
808
|
const seatPickerAlign = p2IsHuman ? void 0 : pack.usePickerAlign;
|
|
819
809
|
const layoutKey = p2IsHuman ? "zones" : "row";
|
|
@@ -823,7 +813,6 @@ function LoadoutScreen({ game }) {
|
|
|
823
813
|
const anyPlayerPickerOpen = !!(controlsHost.openId?.startsWith("p1-") || controlsHost.openId?.startsWith("p2-"));
|
|
824
814
|
const optionsRow = loadout.options({
|
|
825
815
|
host: controlsHost,
|
|
826
|
-
rotation,
|
|
827
816
|
accentColor: themeColors.tertiary,
|
|
828
817
|
mutedColor: fgMuted,
|
|
829
818
|
onAccentColor: themeColors.onTertiary,
|
|
@@ -834,13 +823,13 @@ function LoadoutScreen({ game }) {
|
|
|
834
823
|
});
|
|
835
824
|
const optionsPopoverOpen = controlsHost.openId !== null && loadout.optionIds.includes(controlsHost.openId);
|
|
836
825
|
const sharedControls = showMetaInSharedBand ? /* @__PURE__ */ jsx12(SharedActionBand, { onBack: safeBack, onSettings: () => setSettingsOpen(true), onRandomize: loadout.randomize, onReset: loadout.reset, fg, popoverOpen: optionsPopoverOpen, children: optionsRow }) : optionsRow;
|
|
837
|
-
const controlLook = { accentColor: themeColors.tertiary, mutedColor: fgMuted, dark
|
|
826
|
+
const controlLook = { accentColor: themeColors.tertiary, mutedColor: fgMuted, dark };
|
|
838
827
|
const p1Control = loadout.seatControl(1, controlsHost, { ...controlLook, seatColor: p1Drawn });
|
|
839
828
|
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: pack.guestLabel ?? "P1", nullLabel: pack.nullLabel, onProfileSelect: handleP1ProfileSelect, onManageProfiles: () => router2.push(PROFILES_HREF), previewValue: seatPreview, triggerStyle, useSeatPickerAlign: seatPickerAlign }, `p1-${layoutKey}`);
|
|
840
829
|
const p2Control = p2IsHuman ? loadout.seatControl(2, p2Host, { ...controlLook, seatColor: p2Drawn }) : void 0;
|
|
841
830
|
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, previewValue: seatPreview, triggerStyle, usePickerAlign: seatPickerAlign }, `p2-${layoutKey}`);
|
|
842
831
|
return /* @__PURE__ */ jsxs5(View3, { style: [styles3.container, { backgroundColor: bg }], children: [
|
|
843
|
-
/* @__PURE__ */ jsxs5(
|
|
832
|
+
/* @__PURE__ */ jsxs5(View3, { style: styles3.frame, children: [
|
|
844
833
|
/* @__PURE__ */ jsx12(PressAwayOverlay, { active: controlsHost.openId !== null, onPress: controlsHost.close }),
|
|
845
834
|
p2ZoneStyle && /* @__PURE__ */ jsx12(PressAwayOverlay, { active: controlsHost.openId !== null || p2Host.openId !== null, onPress: p2Host.close, style: p2ZoneStyle }),
|
|
846
835
|
!showMetaInSharedBand && /* @__PURE__ */ jsx12(CornerActionButtons, { onBack: safeBack, onSettings: () => setSettingsOpen(true), fg, insets }),
|
|
@@ -859,13 +848,13 @@ function LoadoutScreen({ game }) {
|
|
|
859
848
|
) : /* @__PURE__ */ jsxs5(View3, { style: styles3.stackedZone, children: [
|
|
860
849
|
sharedControls,
|
|
861
850
|
/* @__PURE__ */ jsxs5(Animated.View, { style: [styles3.playersRow, anyPlayerPickerOpen && styles3.playersRowOpen, playersRowFade], children: [
|
|
862
|
-
|
|
863
|
-
|
|
851
|
+
p1Panel,
|
|
852
|
+
p2Panel
|
|
864
853
|
] }),
|
|
865
854
|
/* @__PURE__ */ jsx12(ReadyButton2, { color: p1Drawn, ready: ready[1], onToggleReady: () => setReady((r) => ({ ...r, 1: !r[1] })), style: anyPlayerPickerOpen && styles3.readyButtonHidden })
|
|
866
855
|
] })
|
|
867
856
|
] }),
|
|
868
|
-
/* @__PURE__ */ jsx12(SettingsDialog, { visible: settingsOpen, onDismiss: () => setSettingsOpen(false),
|
|
857
|
+
/* @__PURE__ */ jsx12(SettingsDialog, { visible: settingsOpen, onDismiss: () => setSettingsOpen(false), onShowHowToPlay: () => openFor(game.id) })
|
|
869
858
|
] });
|
|
870
859
|
}
|
|
871
860
|
function halfOf(color) {
|
|
@@ -876,12 +865,13 @@ var styles3 = StyleSheet3.create({
|
|
|
876
865
|
container: {
|
|
877
866
|
flex: 1
|
|
878
867
|
},
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
868
|
+
// A real full-bleed box to size the absolutely-positioned children against.
|
|
869
|
+
frame: {
|
|
870
|
+
alignItems: "center",
|
|
871
|
+
flex: 1,
|
|
872
|
+
gap: 32,
|
|
873
|
+
justifyContent: "center",
|
|
874
|
+
paddingHorizontal: 16
|
|
885
875
|
},
|
|
886
876
|
p2ZoneRight: {
|
|
887
877
|
bottom: 0,
|
|
@@ -916,14 +906,6 @@ var styles3 = StyleSheet3.create({
|
|
|
916
906
|
opacity: 0,
|
|
917
907
|
pointerEvents: "none"
|
|
918
908
|
},
|
|
919
|
-
// A real full-bleed box for FakeLandscapeView to size its absolutely-positioned children against.
|
|
920
|
-
rotatable: {
|
|
921
|
-
alignItems: "center",
|
|
922
|
-
flex: 1,
|
|
923
|
-
gap: 32,
|
|
924
|
-
justifyContent: "center",
|
|
925
|
-
paddingHorizontal: 16
|
|
926
|
-
},
|
|
927
909
|
stackedZone: {
|
|
928
910
|
alignItems: "center",
|
|
929
911
|
gap: 28
|
|
@@ -964,13 +946,12 @@ function useResumePoint(gameId) {
|
|
|
964
946
|
// src/screens/HomeScreen.tsx
|
|
965
947
|
import { defaultColors as defaultColors2, FadingScrollView, useAutoPaperTheme as useAutoPaperTheme4 } from "@rific/auto-paper";
|
|
966
948
|
import { IconButton, Pressable } from "@rific/feedback-press";
|
|
967
|
-
import { FakeLandscapeView as FakeLandscapeView2, useRotation as useRotation4 } from "@tastic/core";
|
|
968
949
|
import { PressAwayOverlay as PressAwayOverlay2, usePopoverHost as usePopoverHost2, useWellColors, WELL_SHADOW } from "@tastic/hud/chrome";
|
|
969
950
|
import { getProfilePickerContentSize, ProfilePicker } from "@tastic/profile";
|
|
970
951
|
import { Redirect as Redirect2, router as router3, useIsFocused } from "expo-router";
|
|
971
|
-
import { useCallback as useCallback10, useEffect as useEffect6, useMemo as useMemo6, useState as useState6 } from "react";
|
|
952
|
+
import { useCallback as useCallback10, useEffect as useEffect6, useMemo as useMemo6, useRef as useRef5, useState as useState6 } from "react";
|
|
972
953
|
import { AppState as AppState2, StyleSheet as StyleSheet4, View as View4 } from "react-native";
|
|
973
|
-
import { Icon, Portal, Text } from "react-native-paper";
|
|
954
|
+
import { Icon, Portal, SegmentedButtons, Text } from "react-native-paper";
|
|
974
955
|
|
|
975
956
|
// src/hooks/useFrozenWhileHidden.ts
|
|
976
957
|
import { useState as useState5 } from "react";
|
|
@@ -989,7 +970,8 @@ function useFrozenWhileHidden(value, hidden) {
|
|
|
989
970
|
}
|
|
990
971
|
|
|
991
972
|
// src/screens/homeLayout.ts
|
|
992
|
-
import { clamp
|
|
973
|
+
import { clamp } from "@tastic/core";
|
|
974
|
+
import { useWindowDimensions } from "react-native";
|
|
993
975
|
import { useSafeAreaInsets as useSafeAreaInsets2 } from "react-native-safe-area-context";
|
|
994
976
|
var PREVIEW_ASPECT = 360 / 200;
|
|
995
977
|
var ROW_PREVIEW_WIDTH = 92;
|
|
@@ -1043,9 +1025,8 @@ function computeHomeLayout(windowWidth, windowHeight, insets) {
|
|
|
1043
1025
|
};
|
|
1044
1026
|
}
|
|
1045
1027
|
function useHomeLayout() {
|
|
1046
|
-
const { width, height } =
|
|
1047
|
-
const
|
|
1048
|
-
const insets = rotateInsets2(useSafeAreaInsets2(), rotation);
|
|
1028
|
+
const { width, height } = useWindowDimensions();
|
|
1029
|
+
const insets = useSafeAreaInsets2();
|
|
1049
1030
|
return computeHomeLayout(width, height, insets);
|
|
1050
1031
|
}
|
|
1051
1032
|
|
|
@@ -1067,7 +1048,6 @@ function HomeScreen() {
|
|
|
1067
1048
|
}
|
|
1068
1049
|
function HomeMenu() {
|
|
1069
1050
|
const pack = usePack();
|
|
1070
|
-
const rotation = useRotation4();
|
|
1071
1051
|
const layout = useHomeLayout();
|
|
1072
1052
|
const { insets } = layout;
|
|
1073
1053
|
const { dark, colors } = useAutoPaperTheme4();
|
|
@@ -1097,12 +1077,19 @@ function HomeMenu() {
|
|
|
1097
1077
|
const closePicker = pickerHost.close;
|
|
1098
1078
|
useEffect6(() => {
|
|
1099
1079
|
closePicker();
|
|
1100
|
-
}, [isWide,
|
|
1080
|
+
}, [isWide, layout.windowWidth, layout.windowHeight, closePicker]);
|
|
1101
1081
|
const bg = pack.Backdrop ? "transparent" : dark ? "#000000" : "#FFFFFF";
|
|
1102
1082
|
const fg = pack.foreground ?? (dark ? "#FFFFFF" : "#000000");
|
|
1103
1083
|
const { previewColor } = pack;
|
|
1104
1084
|
const p1Drawn = previewColor ? previewColor(p1Color, dark) : p1Color;
|
|
1105
1085
|
const triggerStyle = useMemo6(() => pack.Backdrop ? [styles4.pill, { backgroundColor: colors.surface }] : void 0, [pack.Backdrop, colors.surface]);
|
|
1086
|
+
const tabs = pack.homeTabs && pack.homeTabs.length > 1 ? pack.homeTabs : null;
|
|
1087
|
+
const [tabLabel, setTabLabel] = useState6(() => tabs?.[0].label ?? null);
|
|
1088
|
+
const tab = tabs ? tabs.find((t) => t.label === tabLabel) ?? tabs[0] : null;
|
|
1089
|
+
const tabButtons = useMemo6(() => tabs?.map(({ label }) => ({ value: label, label })) ?? [], [tabs]);
|
|
1090
|
+
const tabBar = tab && /* @__PURE__ */ jsx14(SegmentedButtons, { value: tab.label, onValueChange: setTabLabel, buttons: tabButtons, style: [styles4.tabs, pack.Backdrop && { backgroundColor: colors.surface }] });
|
|
1091
|
+
const columnClamp = useClampOnShrink();
|
|
1092
|
+
const gridClamp = useClampOnShrink();
|
|
1106
1093
|
const openGame = useCallback10((game) => openFirstTime(game.id, () => router3.push(game.useLoadout ? loadoutHref(game.id) : playHref(game.id))), [openFirstTime]);
|
|
1107
1094
|
const openRules = useCallback10((game) => game.Rules ? setRulesGame(game) : openFor(game.id), [openFor]);
|
|
1108
1095
|
const colorSection = { value: p1Color, onChange: handleP1ColorChange, swatches: defaultColors2, caption: selectedProfile ? pack.colorCaption : void 0, previewValue: previewColor ? (hex) => previewColor(hex, dark) : void 0 };
|
|
@@ -1113,42 +1100,74 @@ function HomeMenu() {
|
|
|
1113
1100
|
const pressAway = /* @__PURE__ */ jsx14(PressAwayOverlay2, { active: pickerHost.openId !== null, onPress: pickerHost.close, style: styles4.pressAway });
|
|
1114
1101
|
const RulesSheet = rulesGame?.Rules;
|
|
1115
1102
|
return /* @__PURE__ */ jsxs6(View4, { style: [styles4.container, { backgroundColor: bg }], children: [
|
|
1116
|
-
/* @__PURE__ */ jsx14(
|
|
1103
|
+
/* @__PURE__ */ jsx14(View4, { style: styles4.frame, children: /* @__PURE__ */ jsxs6(RulesHost, { enabled: pack.games.some((game) => game.Rules), children: [
|
|
1117
1104
|
isWide ? /* @__PURE__ */ jsxs6(Fragment4, { children: [
|
|
1118
1105
|
pressAway,
|
|
1119
|
-
/* @__PURE__ */
|
|
1106
|
+
/* @__PURE__ */ jsxs6(FadingScrollView, { ...tab && gridClamp, 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: [
|
|
1107
|
+
tabBar && /* @__PURE__ */ jsx14(View4, { style: [styles4.wideTabs, { width: layout.rightPaneWidth }], children: tabBar }),
|
|
1108
|
+
/* @__PURE__ */ jsx14(View4, { style: [styles4.grid, { width: layout.rightPaneWidth }], children: pack.games.map((game) => /* @__PURE__ */ jsx14(GameTile, { game, hidden: isHidden(tab, game), width: layout.tileWidth, previewWidth: layout.previewWidth, previewRevealed: previewsRevealed, dark, p1Color, p2Color, onPress: () => openGame(game), onRules: hasRules(game) ? () => openRules(game) : void 0 }, game.id)) })
|
|
1109
|
+
] }),
|
|
1120
1110
|
/* @__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: [
|
|
1121
1111
|
/* @__PURE__ */ jsx14(pack.HeroTitle, { words: pack.wordmark, color: fg, p1Color, p2Color, active: heroActive, fit: layout.heroFit }),
|
|
1122
1112
|
/* @__PURE__ */ jsx14(View4, { style: styles4.widePicker, children: picker })
|
|
1123
1113
|
] })
|
|
1124
|
-
] }) : /* @__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: [
|
|
1114
|
+
] }) : /* @__PURE__ */ jsxs6(FadingScrollView, { ...tab && columnClamp, style: styles4.scroll, contentContainerStyle: [styles4.content, { paddingTop: 56 + insets.top, paddingBottom: 24 + insets.bottom, paddingLeft: 16 + insets.left, paddingRight: 16 + insets.right }], children: [
|
|
1125
1115
|
pressAway,
|
|
1126
1116
|
/* @__PURE__ */ jsx14(pack.HeroTitle, { words: pack.wordmark, color: fg, p1Color, p2Color, active: heroActive }),
|
|
1127
1117
|
/* @__PURE__ */ jsx14(View4, { style: [styles4.picker, pickerHost.openId !== null && styles4.pickerOpen], children: picker }),
|
|
1128
|
-
/* @__PURE__ */ jsx14(View4, { style: styles4.
|
|
1118
|
+
tabBar && /* @__PURE__ */ jsx14(View4, { style: styles4.listWidth, children: tabBar }),
|
|
1119
|
+
/* @__PURE__ */ jsx14(View4, { style: styles4.list, children: pack.games.map((game) => /* @__PURE__ */ jsx14(GameRow, { game, hidden: isHidden(tab, game), dark, p1Color, p2Color, previewRevealed: previewsRevealed, onPress: () => openGame(game), onRules: hasRules(game) ? () => openRules(game) : void 0 }, game.id)) })
|
|
1129
1120
|
] }),
|
|
1130
1121
|
/* @__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" }),
|
|
1131
1122
|
/* @__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" }),
|
|
1132
1123
|
RulesSheet && /* @__PURE__ */ jsx14(RulesSheet, { visible: true, onDismiss: () => setRulesGame(null) })
|
|
1133
1124
|
] }) }),
|
|
1134
|
-
/* @__PURE__ */ jsx14(SettingsDialog, { visible: settingsOpen, onDismiss: () => setSettingsOpen(false),
|
|
1125
|
+
/* @__PURE__ */ jsx14(SettingsDialog, { visible: settingsOpen, onDismiss: () => setSettingsOpen(false), onShowHowToPlay: pack.guide ? openPack : void 0 })
|
|
1135
1126
|
] });
|
|
1136
1127
|
}
|
|
1137
1128
|
var SEAT_ONE_PICKER = "home-p1";
|
|
1138
1129
|
var noPickerAlign = () => void 0;
|
|
1130
|
+
function isHidden(tab, game) {
|
|
1131
|
+
return !!tab && !tab.includes(game);
|
|
1132
|
+
}
|
|
1133
|
+
function useClampOnShrink() {
|
|
1134
|
+
const view = useRef5(null);
|
|
1135
|
+
const sizes = useRef5({ viewport: 0, offset: 0 });
|
|
1136
|
+
return useMemo6(
|
|
1137
|
+
() => ({
|
|
1138
|
+
ref: (instance) => {
|
|
1139
|
+
view.current = instance;
|
|
1140
|
+
sizes.current.offset = 0;
|
|
1141
|
+
},
|
|
1142
|
+
onLayout: (e) => {
|
|
1143
|
+
sizes.current.viewport = e.nativeEvent.layout.height;
|
|
1144
|
+
},
|
|
1145
|
+
onScroll: (e) => {
|
|
1146
|
+
sizes.current.offset = e.nativeEvent.contentOffset.y;
|
|
1147
|
+
},
|
|
1148
|
+
onContentSizeChange: (_width, height) => {
|
|
1149
|
+
const end = Math.max(0, height - sizes.current.viewport);
|
|
1150
|
+
if (sizes.current.offset <= end) return;
|
|
1151
|
+
sizes.current.offset = end;
|
|
1152
|
+
view.current?.scrollTo({ y: end, animated: false });
|
|
1153
|
+
}
|
|
1154
|
+
}),
|
|
1155
|
+
[]
|
|
1156
|
+
);
|
|
1157
|
+
}
|
|
1139
1158
|
function hasRules(game) {
|
|
1140
1159
|
return !!(game.Rules || game.guide);
|
|
1141
1160
|
}
|
|
1142
1161
|
function RulesHost({ enabled, children }) {
|
|
1143
1162
|
return enabled ? /* @__PURE__ */ jsx14(Portal.Host, { children }) : /* @__PURE__ */ jsx14(Fragment4, { children });
|
|
1144
1163
|
}
|
|
1145
|
-
function GameRow({ game, dark, p1Color, p2Color, previewRevealed, onPress, onRules }) {
|
|
1164
|
+
function GameRow({ game, hidden, dark, p1Color, p2Color, previewRevealed, onPress, onRules }) {
|
|
1146
1165
|
const well = useWellColors();
|
|
1147
1166
|
const GameIcon = game.icon;
|
|
1148
1167
|
return (
|
|
1149
1168
|
// The rules button sits beside the row's own button, laid over its end, not inside it: a button
|
|
1150
1169
|
// inside a button is invalid on the web.
|
|
1151
|
-
/* @__PURE__ */ jsxs6(View4, { children: [
|
|
1170
|
+
/* @__PURE__ */ jsxs6(View4, { style: hidden ? styles4.hidden : void 0, children: [
|
|
1152
1171
|
/* @__PURE__ */ jsxs6(Pressable, { onPress, style: [styles4.row, { backgroundColor: well.bg }], accessibilityRole: "button", accessibilityLabel: game.title, children: [
|
|
1153
1172
|
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 }) }),
|
|
1154
1173
|
/* @__PURE__ */ jsxs6(View4, { style: styles4.rowText, children: [
|
|
@@ -1160,9 +1179,9 @@ function GameRow({ game, dark, p1Color, p2Color, previewRevealed, onPress, onRul
|
|
|
1160
1179
|
] })
|
|
1161
1180
|
);
|
|
1162
1181
|
}
|
|
1163
|
-
function GameTile({ game, width, previewWidth, dark, p1Color, p2Color, previewRevealed, onPress, onRules }) {
|
|
1182
|
+
function GameTile({ game, hidden, width, previewWidth, dark, p1Color, p2Color, previewRevealed, onPress, onRules }) {
|
|
1164
1183
|
const well = useWellColors();
|
|
1165
|
-
return /* @__PURE__ */ jsxs6(View4, { style: { width }, children: [
|
|
1184
|
+
return /* @__PURE__ */ jsxs6(View4, { style: [{ width }, hidden && styles4.hidden], children: [
|
|
1166
1185
|
/* @__PURE__ */ jsxs6(Pressable, { onPress, style: [styles4.tile, { backgroundColor: well.bg }], accessibilityRole: "button", accessibilityLabel: game.title, children: [
|
|
1167
1186
|
/* @__PURE__ */ jsx14(PreviewBox, { game, width: previewWidth, revealed: previewRevealed, dark, p1Color, p2Color }),
|
|
1168
1187
|
/* @__PURE__ */ jsx14(Text, { variant: "titleMedium", style: [styles4.rowTitle, { color: well.fg }], numberOfLines: 1, children: game.title }),
|
|
@@ -1192,18 +1211,30 @@ var styles4 = StyleSheet4.create({
|
|
|
1192
1211
|
corner: {
|
|
1193
1212
|
position: "absolute"
|
|
1194
1213
|
},
|
|
1214
|
+
frame: {
|
|
1215
|
+
flex: 1
|
|
1216
|
+
},
|
|
1195
1217
|
// The wide layout's tiles: wrapped rows of tiles the layout sizes to fill the pane's width.
|
|
1196
1218
|
grid: {
|
|
1197
1219
|
flexDirection: "row",
|
|
1198
1220
|
flexWrap: "wrap",
|
|
1199
1221
|
gap: HOME_GRID_GAP
|
|
1200
1222
|
},
|
|
1223
|
+
// Out of the layout (a grid closes up around it) and hidden from screen readers.
|
|
1224
|
+
hidden: {
|
|
1225
|
+
display: "none"
|
|
1226
|
+
},
|
|
1201
1227
|
// Solitaire's list: one column, never wider than a phone's, centred on a wide screen.
|
|
1202
1228
|
list: {
|
|
1203
1229
|
gap: 14,
|
|
1204
1230
|
maxWidth: 480,
|
|
1205
1231
|
width: "100%"
|
|
1206
1232
|
},
|
|
1233
|
+
// The list's width, for the tabs over it.
|
|
1234
|
+
listWidth: {
|
|
1235
|
+
maxWidth: 480,
|
|
1236
|
+
width: "100%"
|
|
1237
|
+
},
|
|
1207
1238
|
picker: {
|
|
1208
1239
|
alignItems: "center"
|
|
1209
1240
|
},
|
|
@@ -1229,9 +1260,6 @@ var styles4 = StyleSheet4.create({
|
|
|
1229
1260
|
previewIcon: {
|
|
1230
1261
|
overflow: "hidden"
|
|
1231
1262
|
},
|
|
1232
|
-
rotatable: {
|
|
1233
|
-
flex: 1
|
|
1234
|
-
},
|
|
1235
1263
|
// A well (@tastic/hud's wellSurface): the theme's surface on MD3's level-1 shadow, the same surface
|
|
1236
1264
|
// as Solitaire's game rows, the stats screen's sections and every dialog.
|
|
1237
1265
|
row: {
|
|
@@ -1266,6 +1294,10 @@ var styles4 = StyleSheet4.create({
|
|
|
1266
1294
|
scroll: {
|
|
1267
1295
|
flex: 1
|
|
1268
1296
|
},
|
|
1297
|
+
// SegmentedButtons' own rounding, so a surface behind it is the same pill (the stats screen's tabs).
|
|
1298
|
+
tabs: {
|
|
1299
|
+
borderRadius: 20
|
|
1300
|
+
},
|
|
1269
1301
|
// The same well as a row, turned on its side.
|
|
1270
1302
|
tile: {
|
|
1271
1303
|
borderRadius: 12,
|
|
@@ -1304,6 +1336,10 @@ var styles4 = StyleSheet4.create({
|
|
|
1304
1336
|
position: "absolute",
|
|
1305
1337
|
right: 0,
|
|
1306
1338
|
top: 0
|
|
1339
|
+
},
|
|
1340
|
+
// The tabs over the grid, a little further from it than the tiles are from each other.
|
|
1341
|
+
wideTabs: {
|
|
1342
|
+
marginBottom: 16
|
|
1307
1343
|
}
|
|
1308
1344
|
});
|
|
1309
1345
|
|
|
@@ -1329,7 +1365,7 @@ import { BaseStatsScreen, PressAwayOverlay as PressAwayOverlay3, RotatedFrame as
|
|
|
1329
1365
|
import { getOptionPickerContentSize as getOptionPickerContentSize2, OptionPicker as OptionPicker2 } from "@tastic/profile";
|
|
1330
1366
|
import { useMemo as useMemo7, useState as useState7 } from "react";
|
|
1331
1367
|
import { StyleSheet as StyleSheet5, View as View5 } from "react-native";
|
|
1332
|
-
import { SegmentedButtons, Text as Text2 } from "react-native-paper";
|
|
1368
|
+
import { SegmentedButtons as SegmentedButtons2, Text as Text2 } from "react-native-paper";
|
|
1333
1369
|
import { useSafeAreaInsets as useSafeAreaInsets3 } from "react-native-safe-area-context";
|
|
1334
1370
|
import { useDispatch as useDispatch10 } from "react-redux";
|
|
1335
1371
|
import { jsx as jsx16, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
@@ -1387,7 +1423,7 @@ function StatsContent({ pickerHost }) {
|
|
|
1387
1423
|
const PackTabContent = packTab?.Content;
|
|
1388
1424
|
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: [
|
|
1389
1425
|
/* @__PURE__ */ jsx16(PressAwayOverlay3, { active: pickerHost.openId !== null, onPress: pickerHost.close, style: styles5.pressAway }),
|
|
1390
|
-
/* @__PURE__ */ jsx16(
|
|
1426
|
+
/* @__PURE__ */ jsx16(SegmentedButtons2, { value: tab, onValueChange: setTab, buttons: tabButtons, style: [styles5.tabs, surface] }),
|
|
1391
1427
|
/* @__PURE__ */ jsxs7(View5, { style: [styles5.filters, pickerHost.openId !== null && styles5.filtersOpen], onStartShouldSetResponder: () => pickerHost.openId !== null, children: [
|
|
1392
1428
|
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 }),
|
|
1393
1429
|
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 })
|