@tastic/shell 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -557,6 +557,7 @@ var styles = import_react_native2.StyleSheet.create({
557
557
  // src/screens/GameRoute.tsx
558
558
  var import_expo_router5 = require("expo-router");
559
559
  var import_react14 = require("react");
560
+ var import_react_native5 = require("react-native");
560
561
 
561
562
  // src/routes.ts
562
563
  var playHref = (game) => ({ pathname: "/play/[game]", params: { game } });
@@ -1006,7 +1007,9 @@ function useResumePoint(gameId) {
1006
1007
  (0, import_react14.useEffect)(() => {
1007
1008
  if (!gameId) return;
1008
1009
  setLastActiveGame(gameId);
1009
- return clearLastActiveGame;
1010
+ return () => {
1011
+ if (import_react_native5.AppState.currentState === "active") clearLastActiveGame();
1012
+ };
1010
1013
  }, [gameId, setLastActiveGame, clearLastActiveGame]);
1011
1014
  }
1012
1015
 
@@ -1018,7 +1021,7 @@ var import_chrome5 = require("@tastic/hud/chrome");
1018
1021
  var import_profile7 = require("@tastic/profile");
1019
1022
  var import_expo_router6 = require("expo-router");
1020
1023
  var import_react16 = require("react");
1021
- var import_react_native5 = require("react-native");
1024
+ var import_react_native6 = require("react-native");
1022
1025
  var import_react_native_paper = require("react-native-paper");
1023
1026
 
1024
1027
  // src/hooks/useFrozenWhileHidden.ts
@@ -1124,9 +1127,9 @@ function HomeMenu() {
1124
1127
  const [settingsOpen, setSettingsOpen] = (0, import_react16.useState)(false);
1125
1128
  const [rulesGame, setRulesGame] = (0, import_react16.useState)(null);
1126
1129
  const isFocused = (0, import_expo_router6.useIsFocused)();
1127
- const [appState, setAppState] = (0, import_react16.useState)(import_react_native5.AppState.currentState);
1130
+ const [appState, setAppState] = (0, import_react16.useState)(import_react_native6.AppState.currentState);
1128
1131
  (0, import_react16.useEffect)(() => {
1129
- const subscription = import_react_native5.AppState.addEventListener("change", setAppState);
1132
+ const subscription = import_react_native6.AppState.addEventListener("change", setAppState);
1130
1133
  return () => subscription.remove();
1131
1134
  }, []);
1132
1135
  const heroActive = isFocused && appState === "active";
@@ -1161,20 +1164,20 @@ function HomeMenu() {
1161
1164
  const picker = /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_profile7.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: () => import_expo_router6.router.push(PROFILES_HREF), colorSection, alignOverride: pickerAlign, triggerStyle });
1162
1165
  const pressAway = /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_chrome5.PressAwayOverlay, { active: pickerHost.openId !== null, onPress: pickerHost.close, style: styles4.pressAway });
1163
1166
  const RulesSheet = rulesGame?.Rules;
1164
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native5.View, { style: [styles4.container, { backgroundColor: bg }], children: [
1167
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native6.View, { style: [styles4.container, { backgroundColor: bg }], children: [
1165
1168
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_core12.FakeLandscapeView, { style: styles4.rotatable, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(RulesHost, { enabled: pack.games.some((game) => game.Rules), children: [
1166
1169
  isWide ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
1167
1170
  pressAway,
1168
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_auto_paper5.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__ */ (0, import_jsx_runtime14.jsx)(import_react_native5.View, { style: [styles4.grid, { width: layout.rightPaneWidth }], children: pack.games.map((game) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(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)) }) }),
1169
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native5.View, { 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: [
1171
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_auto_paper5.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__ */ (0, import_jsx_runtime14.jsx)(import_react_native6.View, { style: [styles4.grid, { width: layout.rightPaneWidth }], children: pack.games.map((game) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(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)) }) }),
1172
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native6.View, { 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: [
1170
1173
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(pack.HeroTitle, { words: pack.wordmark, color: fg, p1Color, p2Color, active: heroActive, fit: layout.heroFit }),
1171
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native5.View, { style: styles4.widePicker, children: picker })
1174
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native6.View, { style: styles4.widePicker, children: picker })
1172
1175
  ] })
1173
1176
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_auto_paper5.FadingScrollView, { style: styles4.scroll, contentContainerStyle: [styles4.content, { paddingTop: 56 + insets.top, paddingBottom: 24 + insets.bottom, paddingLeft: 16 + insets.left, paddingRight: 16 + insets.right }], children: [
1174
1177
  pressAway,
1175
1178
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(pack.HeroTitle, { words: pack.wordmark, color: fg, p1Color, p2Color, active: heroActive }),
1176
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native5.View, { style: [styles4.picker, pickerHost.openId !== null && styles4.pickerOpen], children: picker }),
1177
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native5.View, { style: styles4.list, children: pack.games.map((game) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(GameRow, { game, dark, p1Color, p2Color, previewRevealed: previewsRevealed, onPress: () => openGame(game), onRules: hasRules(game) ? () => openRules(game) : void 0 }, game.id)) })
1179
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native6.View, { style: [styles4.picker, pickerHost.openId !== null && styles4.pickerOpen], children: picker }),
1180
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native6.View, { style: styles4.list, children: pack.games.map((game) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(GameRow, { game, dark, p1Color, p2Color, previewRevealed: previewsRevealed, onPress: () => openGame(game), onRules: hasRules(game) ? () => openRules(game) : void 0 }, game.id)) })
1178
1181
  ] }),
1179
1182
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_feedback_press3.IconButton, { icon: "trophy", iconColor: fg, size: 24, style: [styles4.corner, { top: 8 + insets.top, left: 8 + insets.left }], onPress: () => import_expo_router6.router.push(STATS_HREF), accessibilityLabel: "Stats & Achievements" }),
1180
1183
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_feedback_press3.IconButton, { icon: "cog", iconColor: fg, size: 24, style: [styles4.corner, { top: 8 + insets.top, right: 8 + insets.right }], onPress: () => setSettingsOpen(true), accessibilityLabel: "Settings" }),
@@ -1197,10 +1200,10 @@ function GameRow({ game, dark, p1Color, p2Color, previewRevealed, onPress, onRul
1197
1200
  return (
1198
1201
  // The rules button sits beside the row's own button, laid over its end, not inside it: a button
1199
1202
  // inside a button is invalid on the web.
1200
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native5.View, { children: [
1203
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native6.View, { children: [
1201
1204
  /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_feedback_press3.Pressable, { onPress, style: [styles4.row, { backgroundColor: well.bg }], accessibilityRole: "button", accessibilityLabel: game.title, children: [
1202
- game.Preview ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(PreviewBox, { game, width: ROW_PREVIEW_WIDTH, revealed: previewRevealed, dark, p1Color, p2Color }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native5.View, { style: [styles4.rowIcon, { backgroundColor: dark ? ICON_DARK_BG : ICON_LIGHT_BG }], children: GameIcon && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(GameIcon, { size: ROW_ICON_SIZE, p1Color, p2Color, dark }) }),
1203
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native5.View, { style: styles4.rowText, children: [
1205
+ game.Preview ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(PreviewBox, { game, width: ROW_PREVIEW_WIDTH, revealed: previewRevealed, dark, p1Color, p2Color }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native6.View, { style: [styles4.rowIcon, { backgroundColor: dark ? ICON_DARK_BG : ICON_LIGHT_BG }], children: GameIcon && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(GameIcon, { size: ROW_ICON_SIZE, p1Color, p2Color, dark }) }),
1206
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native6.View, { style: styles4.rowText, children: [
1204
1207
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper.Text, { variant: "titleLarge", style: [styles4.rowTitle, { color: well.fg }], numberOfLines: 1, children: game.title }),
1205
1208
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper.Text, { variant: "bodyMedium", style: { color: well.fgMuted }, numberOfLines: 2, children: game.blurb })
1206
1209
  ] })
@@ -1211,7 +1214,7 @@ function GameRow({ game, dark, p1Color, p2Color, previewRevealed, onPress, onRul
1211
1214
  }
1212
1215
  function GameTile({ game, width, previewWidth, dark, p1Color, p2Color, previewRevealed, onPress, onRules }) {
1213
1216
  const well = (0, import_chrome5.useWellColors)();
1214
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native5.View, { style: { width }, children: [
1217
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native6.View, { style: { width }, children: [
1215
1218
  /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_feedback_press3.Pressable, { onPress, style: [styles4.tile, { backgroundColor: well.bg }], accessibilityRole: "button", accessibilityLabel: game.title, children: [
1216
1219
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(PreviewBox, { game, width: previewWidth, revealed: previewRevealed, dark, p1Color, p2Color }),
1217
1220
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper.Text, { variant: "titleMedium", style: [styles4.rowTitle, { color: well.fg }], numberOfLines: 1, children: game.title }),
@@ -1224,13 +1227,13 @@ function PreviewBox({ game, width, revealed, dark, p1Color, p2Color }) {
1224
1227
  const height = Math.round(width / PREVIEW_ASPECT);
1225
1228
  const { Preview, icon: GameIcon } = game;
1226
1229
  const iconSize = Math.min(height, ROW_ICON_SIZE);
1227
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native5.View, { style: [styles4.preview, { width, height, backgroundColor: dark ? ICON_DARK_BG : ICON_LIGHT_BG }], children: Preview ? revealed && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Preview, { width, height, p1Color, p2Color, dark }) : GameIcon && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native5.View, { style: [styles4.previewIcon, { width: iconSize, height: iconSize, borderRadius: iconSize * 0.22 }], children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(GameIcon, { size: iconSize, p1Color, p2Color, dark }) }) });
1230
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native6.View, { style: [styles4.preview, { width, height, backgroundColor: dark ? ICON_DARK_BG : ICON_LIGHT_BG }], children: Preview ? revealed && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Preview, { width, height, p1Color, p2Color, dark }) : GameIcon && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native6.View, { style: [styles4.previewIcon, { width: iconSize, height: iconSize, borderRadius: iconSize * 0.22 }], children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(GameIcon, { size: iconSize, p1Color, p2Color, dark }) }) });
1228
1231
  }
1229
1232
  var ROW_ICON_SIZE = 56;
1230
1233
  var ICON_LIGHT_BG = "#FAFAFA";
1231
1234
  var ICON_DARK_BG = "#050505";
1232
1235
  var RULES_BUTTON_SPACE = 44;
1233
- var styles4 = import_react_native5.StyleSheet.create({
1236
+ var styles4 = import_react_native6.StyleSheet.create({
1234
1237
  container: {
1235
1238
  flex: 1
1236
1239
  },
@@ -1377,7 +1380,7 @@ var import_auto_paper7 = require("@rific/auto-paper");
1377
1380
  var import_chrome7 = require("@tastic/hud/chrome");
1378
1381
  var import_profile9 = require("@tastic/profile");
1379
1382
  var import_react17 = require("react");
1380
- var import_react_native6 = require("react-native");
1383
+ var import_react_native7 = require("react-native");
1381
1384
  var import_react_native_paper2 = require("react-native-paper");
1382
1385
  var import_react_native_safe_area_context4 = require("react-native-safe-area-context");
1383
1386
  var import_react_redux10 = require("react-redux");
@@ -1400,7 +1403,7 @@ function StatsScreen() {
1400
1403
  return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(StatsPressAway, { host: pickerHost, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_chrome7.RotatedFrame, { rotatedSafeArea: true, portalHost: true, maxContentWidth: STATS_MAX_WIDTH, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(StatsContent, { pickerHost }) }) });
1401
1404
  }
1402
1405
  function StatsPressAway({ host, children }) {
1403
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_native6.View, { testID: "stats-press-away", style: styles5.screen, onStartShouldSetResponder: () => host.openId !== null, onResponderRelease: host.close, children });
1406
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_native7.View, { testID: "stats-press-away", style: styles5.screen, onStartShouldSetResponder: () => host.openId !== null, onResponderRelease: host.close, children });
1404
1407
  }
1405
1408
  var useNoPackReset = () => null;
1406
1409
  var noPickerAlign2 = () => void 0;
@@ -1437,7 +1440,7 @@ function StatsContent({ pickerHost }) {
1437
1440
  return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_chrome7.BaseStatsScreen, { title, onBack: import_core8.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: [
1438
1441
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_chrome7.PressAwayOverlay, { active: pickerHost.openId !== null, onPress: pickerHost.close, style: styles5.pressAway }),
1439
1442
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_native_paper2.SegmentedButtons, { value: tab, onValueChange: setTab, buttons: tabButtons, style: [styles5.tabs, surface] }),
1440
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_react_native6.View, { style: [styles5.filters, pickerHost.openId !== null && styles5.filtersOpen], onStartShouldSetResponder: () => pickerHost.openId !== null, children: [
1443
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_react_native7.View, { style: [styles5.filters, pickerHost.openId !== null && styles5.filtersOpen], onStartShouldSetResponder: () => pickerHost.openId !== null, children: [
1441
1444
  profiles.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_profile9.OptionPicker, { 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 }),
1442
1445
  showGamePicker && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_profile9.OptionPicker, { id: GAME_PICKER_ID, host: pickerHost, options: gameOptions, value: gameId, onChange: setGameId, color: colors.primary, dark, icon: "view-grid-outline", alignOverride: gameAlign, triggerStyle })
1443
1446
  ] }),
@@ -1458,12 +1461,12 @@ function GameStats({ game, tab, profileId, showTitle, titleColor }) {
1458
1461
  const { StatsSections, AchievementList } = game;
1459
1462
  const Sections = tab === STATS ? StatsSections : AchievementList;
1460
1463
  if (!Sections) return null;
1461
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_react_native6.View, { style: styles5.game, children: [
1464
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_react_native7.View, { style: styles5.game, children: [
1462
1465
  showTitle && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_native_paper2.Text, { variant: "titleLarge", style: [styles5.gameTitle, { color: titleColor }], children: game.title }),
1463
1466
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Sections, { profileId })
1464
1467
  ] });
1465
1468
  }
1466
- var styles5 = import_react_native6.StyleSheet.create({
1469
+ var styles5 = import_react_native7.StyleSheet.create({
1467
1470
  filters: {
1468
1471
  flexDirection: "row",
1469
1472
  flexWrap: "wrap",
@@ -1497,11 +1500,11 @@ var styles5 = import_react_native6.StyleSheet.create({
1497
1500
  });
1498
1501
 
1499
1502
  // src/components/imageGameIcon.tsx
1500
- var import_react_native7 = require("react-native");
1503
+ var import_react_native8 = require("react-native");
1501
1504
  var import_jsx_runtime17 = require("react/jsx-runtime");
1502
1505
  function imageGameIcon(images) {
1503
1506
  return function ImageGameIcon({ size, dark }) {
1504
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_react_native7.Image, { source: dark ? images.dark : images.light, style: { height: size, width: size }, accessibilityIgnoresInvertColors: true });
1507
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_react_native8.Image, { source: dark ? images.dark : images.light, style: { height: size, width: size }, accessibilityIgnoresInvertColors: true });
1505
1508
  };
1506
1509
  }
1507
1510
 
package/dist/index.mjs CHANGED
@@ -483,6 +483,7 @@ var styles = StyleSheet.create({
483
483
  // src/screens/GameRoute.tsx
484
484
  import { Redirect, useLocalSearchParams } from "expo-router";
485
485
  import { useEffect as useEffect5 } from "react";
486
+ import { AppState } from "react-native";
486
487
 
487
488
  // src/routes.ts
488
489
  var playHref = (game) => ({ pathname: "/play/[game]", params: { game } });
@@ -932,7 +933,9 @@ function useResumePoint(gameId) {
932
933
  useEffect5(() => {
933
934
  if (!gameId) return;
934
935
  setLastActiveGame(gameId);
935
- return clearLastActiveGame;
936
+ return () => {
937
+ if (AppState.currentState === "active") clearLastActiveGame();
938
+ };
936
939
  }, [gameId, setLastActiveGame, clearLastActiveGame]);
937
940
  }
938
941
 
@@ -944,7 +947,7 @@ import { PressAwayOverlay as PressAwayOverlay2, usePopoverHost as usePopoverHost
944
947
  import { getProfilePickerContentSize, ProfilePicker } from "@tastic/profile";
945
948
  import { Redirect as Redirect2, router as router3, useIsFocused } from "expo-router";
946
949
  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";
950
+ import { AppState as AppState2, StyleSheet as StyleSheet4, View as View4 } from "react-native";
948
951
  import { Icon, Portal, Text } from "react-native-paper";
949
952
 
950
953
  // src/hooks/useFrozenWhileHidden.ts
@@ -1050,9 +1053,9 @@ function HomeMenu() {
1050
1053
  const [settingsOpen, setSettingsOpen] = useState6(false);
1051
1054
  const [rulesGame, setRulesGame] = useState6(null);
1052
1055
  const isFocused = useIsFocused();
1053
- const [appState, setAppState] = useState6(AppState.currentState);
1056
+ const [appState, setAppState] = useState6(AppState2.currentState);
1054
1057
  useEffect6(() => {
1055
- const subscription = AppState.addEventListener("change", setAppState);
1058
+ const subscription = AppState2.addEventListener("change", setAppState);
1056
1059
  return () => subscription.remove();
1057
1060
  }, []);
1058
1061
  const heroActive = isFocused && appState === "active";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tastic/shell",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "The app around a pack of React Native games, two-seat or for one player: Home, the loadout, the stats screen, Profiles and Settings, the pack's Redux store and its root providers, driven by one PackDefinition",
5
5
  "keywords": [
6
6
  "react-native",
@@ -1,5 +1,6 @@
1
1
  import { Redirect, useLocalSearchParams } from 'expo-router'
2
2
  import { useEffect } from 'react'
3
+ import { AppState } from 'react-native'
3
4
 
4
5
  import { useLastActiveGame } from '../hooks/useLastActiveGame'
5
6
  import { usePackGame } from '../PackContext'
@@ -36,14 +37,20 @@ export function PlayRoute() {
36
37
  }
37
38
 
38
39
  // A resumable game's play screen (GameDefinition.resumable) is where a cold launch comes back to:
39
- // remembered as it mounts, and forgotten when it unmounts, which is the player backing out of it
40
- // (its own back, hardware back or a browser back). Backgrounding the app leaves the screen mounted,
41
- // so it stays remembered. Runs once per play screen: the context's functions never change.
40
+ // remembered as it mounts, and forgotten when the player backs out of it (its own back, hardware back
41
+ // or a browser back), which unmounts it while the app is in use. Backgrounding the app leaves the
42
+ // screen mounted, so it stays remembered. An unmount with the app NOT active keeps it too: when iOS
43
+ // terminates an app it hasn't suspended yet (swiped away from the app switcher soon after leaving
44
+ // it), React Native's renderer stops every surface (RCTSurfacePresenter's will-terminate handler),
45
+ // which unmounts the whole tree, and clearing then sent every such relaunch to Home. Runs once per
46
+ // play screen: the context's functions never change.
42
47
  function useResumePoint(gameId: string | null) {
43
48
  const { setLastActiveGame, clearLastActiveGame } = useLastActiveGame()
44
49
  useEffect(() => {
45
50
  if (!gameId) return
46
51
  setLastActiveGame(gameId)
47
- return clearLastActiveGame
52
+ return () => {
53
+ if (AppState.currentState === 'active') clearLastActiveGame()
54
+ }
48
55
  }, [gameId, setLastActiveGame, clearLastActiveGame])
49
56
  }