@jeremyckahn/farmhand 1.23.2 → 1.23.4

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.
Files changed (169) hide show
  1. package/README.md +5 -0
  2. package/dist/assets/index-4ynbl-GC.js +392 -0
  3. package/dist/assets/index-4ynbl-GC.js.map +1 -0
  4. package/dist/assets/index-CaYdR-lC.css +1 -0
  5. package/dist/assets/jersey-10-latin-400-normal-DrL73VYW.woff2 +0 -0
  6. package/dist/assets/jersey-10-latin-ext-400-normal-uIKqQSaG.woff2 +0 -0
  7. package/dist/assets/jersey-20-latin-400-normal-bNi78tgO.woff2 +0 -0
  8. package/dist/assets/jersey-20-latin-ext-400-normal-BO3lmWef.woff2 +0 -0
  9. package/dist/assets/jersey-25-latin-400-normal-BuYJnnt7.woff2 +0 -0
  10. package/dist/assets/jersey-25-latin-ext-400-normal-CP5Jl3WN.woff2 +0 -0
  11. package/dist/index.html +2 -2
  12. package/dist/service-worker.js +1 -1
  13. package/dist/service-worker.js.map +1 -1
  14. package/package.json +7 -12
  15. package/src/common/services/randomNumber.test.ts +61 -1
  16. package/src/common/services/randomNumber.ts +36 -1
  17. package/src/common/utils.ts +22 -13
  18. package/src/components/AccountingView/AccountingView.tsx +7 -13
  19. package/src/components/Achievement/Achievement.tsx +2 -15
  20. package/src/components/AchievementsView/AchievementsView.tsx +2 -7
  21. package/src/components/AnimatedNumber/AnimatedNumber.tsx +0 -6
  22. package/src/components/AppBar/AppBar.tsx +21 -17
  23. package/src/components/Cellar/Cellar.tsx +0 -2
  24. package/src/components/Cellar/CellarInventoryTabPanel.tsx +0 -6
  25. package/src/components/Cellar/FermentationTabPanel.tsx +0 -6
  26. package/src/components/Cellar/Keg.tsx +0 -5
  27. package/src/components/Cellar/TabPanel/index.tsx +0 -7
  28. package/src/components/Cellar/WinemakingTabPanel.tsx +0 -6
  29. package/src/components/ContextPane/ContextPane.tsx +0 -6
  30. package/src/components/CowCard/Bloodline/Bloodline.tsx +0 -6
  31. package/src/components/CowCard/CowCard.tsx +7 -39
  32. package/src/components/CowCard/Subheader/Subheader.tsx +21 -44
  33. package/src/components/CowPen/Cow.tsx +5 -11
  34. package/src/components/CowPen/CowPen.test.tsx +4 -4
  35. package/src/components/CowPen/CowPen.tsx +5 -16
  36. package/src/components/CowPenContextMenu/CowPenContextMenu.tsx +2 -18
  37. package/src/components/CowPenContextMenu/TabPanel/index.tsx +0 -7
  38. package/src/components/Farmhand/Farmhand.context.tsx +4 -3
  39. package/src/components/Farmhand/Farmhand.tsx +11 -15
  40. package/src/components/Farmhand/FarmhandReducers.tsx +1 -2
  41. package/src/components/Farmhand/FarmhandService.test.ts +6 -9
  42. package/src/components/Farmhand/hooks/useFarmhandDayAdvancement.ts +2 -6
  43. package/src/components/Farmhand/hooks/useFarmhandNetwork.test.ts +2 -2
  44. package/src/components/Farmhand/hooks/useFarmhandNetwork.ts +15 -31
  45. package/src/components/Farmhand/useFarmhand.test.ts +7 -5
  46. package/src/components/Farmhand/useFarmhand.ts +24 -15
  47. package/src/components/FermentationRecipeList/FermentationRecipe.tsx +0 -5
  48. package/src/components/FermentationRecipeList/FermentationRecipeList.test.tsx +10 -11
  49. package/src/components/FermentationRecipeList/FermentationRecipeList.tsx +2 -3
  50. package/src/components/Field/Field.tsx +15 -50
  51. package/src/components/Forest/Forest.tsx +10 -7
  52. package/src/components/ForestPlot/ForestPlot.tsx +5 -5
  53. package/src/components/ForestQuickSelect/ForestQuickSelect.tsx +0 -12
  54. package/src/components/Home/Home.tsx +2 -7
  55. package/src/components/Home/SnowBackground.tsx +1 -1
  56. package/src/components/IngredientsList/IngredientsList.tsx +0 -7
  57. package/src/components/Inventory/Inventory.test.tsx +4 -4
  58. package/src/components/Inventory/Inventory.tsx +5 -12
  59. package/src/components/Item/Item.tsx +11 -29
  60. package/src/components/ItemList/ItemList.tsx +4 -11
  61. package/src/components/KeybindingsView/KeybindingsView.tsx +0 -2
  62. package/src/components/LogView/LogView.tsx +6 -28
  63. package/src/components/Navigation/DayAndProgressContainer.tsx +0 -8
  64. package/src/components/Navigation/Navigation.tsx +12 -33
  65. package/src/components/NotificationSystem/NotificationSystem.test.tsx +45 -3
  66. package/src/components/NotificationSystem/NotificationSystem.tsx +45 -32
  67. package/src/components/OnlinePeersView/OnlinePeer/OnlinePeer.test.tsx +2 -2
  68. package/src/components/OnlinePeersView/OnlinePeer/OnlinePeer.tsx +0 -11
  69. package/src/components/OnlinePeersView/OnlinePeersView.tsx +0 -10
  70. package/src/components/PixelIcon/createPixelIcon.test.tsx +59 -0
  71. package/src/components/PixelIcon/createPixelIcon.tsx +97 -0
  72. package/src/components/PixelIcon/icons.ts +445 -0
  73. package/src/components/PixelIcon/index.ts +3 -0
  74. package/src/components/Plot/Plot.tsx +3 -3
  75. package/src/components/PriceEventView/PriceEventView.tsx +0 -6
  76. package/src/components/ProgressBar/ProgressBar.tsx +9 -8
  77. package/src/components/QuantityInput/QuantityInput.tsx +6 -11
  78. package/src/components/QuickSelect/QuickSelect.tsx +0 -10
  79. package/src/components/Recipe/Recipe.tsx +0 -9
  80. package/src/components/RecipeList/RecipeList.tsx +0 -6
  81. package/src/components/SearchBar/SearchBar.tsx +4 -10
  82. package/src/components/SettingsView/RandomSeedInput.test.tsx +2 -4
  83. package/src/components/SettingsView/RandomSeedInput.tsx +5 -8
  84. package/src/components/SettingsView/SettingsView.test.tsx +21 -0
  85. package/src/components/SettingsView/SettingsView.tsx +24 -18
  86. package/src/components/Shop/Shop.tsx +0 -20
  87. package/src/components/Shop/TabPanel/index.tsx +0 -7
  88. package/src/components/Stage/Stage.tsx +11 -20
  89. package/src/components/StatsView/StatsView.tsx +0 -18
  90. package/src/components/TierPurchase/TierPurchase.tsx +0 -12
  91. package/src/components/Toolbelt/Toolbelt.tsx +5 -10
  92. package/src/components/UpdateNotifier/UpdateNotifier.tsx +33 -5
  93. package/src/components/UpgradePurchase/UpgradePurchase.tsx +0 -10
  94. package/src/components/WineRecipeList/WineRecipe.test.tsx +2 -2
  95. package/src/components/WineRecipeList/WineRecipe.tsx +0 -5
  96. package/src/components/WineRecipeList/WineRecipeList.tsx +2 -3
  97. package/src/components/Workshop/ForgeTabPanel.tsx +0 -8
  98. package/src/components/Workshop/KitchenTabPanel.tsx +0 -7
  99. package/src/components/Workshop/RecyclingTabPanel.tsx +0 -7
  100. package/src/components/Workshop/TabPanel/index.tsx +0 -7
  101. package/src/components/Workshop/WoodChipperTabPanel.tsx +0 -7
  102. package/src/components/Workshop/Workshop.tsx +0 -9
  103. package/src/components/Workshop/getUpgradesAvailable.ts +1 -1
  104. package/src/constants.ts +18 -13
  105. package/src/data/crops/grape.ts +13 -15
  106. package/src/data/maps.test.ts +3 -4
  107. package/src/data/maps.ts +27 -29
  108. package/src/enums.ts +47 -19
  109. package/src/factories/OreFactory.ts +2 -1
  110. package/src/farmhand.d.ts +6 -3
  111. package/src/game-logic/reducers/applyPrecipitation.ts +4 -4
  112. package/src/game-logic/reducers/generatePriceEvents.ts +6 -3
  113. package/src/game-logic/reducers/harvestForestPlot.ts +1 -2
  114. package/src/game-logic/reducers/makeFermentationRecipe.ts +2 -3
  115. package/src/game-logic/reducers/minePlot.test.ts +2 -2
  116. package/src/game-logic/reducers/minePlot.ts +1 -1
  117. package/src/game-logic/reducers/processCowFertilizerProduction.test.ts +16 -14
  118. package/src/game-logic/reducers/processFeedingCows.test.ts +2 -5
  119. package/src/game-logic/reducers/purchaseCow.ts +2 -7
  120. package/src/game-logic/reducers/showNotification.test.ts +20 -0
  121. package/src/game-logic/reducers/showNotification.ts +4 -2
  122. package/src/game-logic/reducers/updateAchievements.ts +1 -1
  123. package/src/game-logic/reducers/updateFinancialRecords.test.ts +21 -25
  124. package/src/game-logic/reducers/updateFinancialRecords.ts +2 -5
  125. package/src/game-logic/reducers/updateInventoryRecordsForNextDay.test.ts +12 -14
  126. package/src/handlers/ui-events.test.ts +44 -42
  127. package/src/handlers/ui-events.tsx +18 -3
  128. package/src/hooks/usePrefersReducedMotion.ts +10 -6
  129. package/src/index.tsx +0 -2
  130. package/src/mui-theme.ts +258 -37
  131. package/src/scripts/generate-crop-table.ts +3 -2
  132. package/src/setupTests.ts +18 -12
  133. package/src/strings.ts +1 -1
  134. package/src/styles/fonts.ts +73 -0
  135. package/src/styles/pixel.ts +151 -0
  136. package/src/styles/sx.ts +46 -33
  137. package/src/styles/tokens.ts +11 -0
  138. package/src/test-utils/index.ts +1 -0
  139. package/src/utils/chooseRandom.ts +4 -1
  140. package/src/utils/chooseRandomIndex.ts +6 -2
  141. package/src/utils/getCowImage.ts +22 -27
  142. package/src/utils/getMaxYieldOfFermentationRecipe.ts +2 -2
  143. package/src/utils/getPeerMetadata.ts +1 -1
  144. package/src/utils/getPlotImage.test.ts +14 -14
  145. package/src/utils/getPlotImage.ts +10 -10
  146. package/src/utils/getRandomUnlockedCrop.ts +5 -3
  147. package/src/utils/getRandomizedLifespan.test.ts +2 -8
  148. package/src/utils/getValueAdjustmentStream.test.ts +9 -0
  149. package/src/utils/getValueAdjustmentStream.ts +5 -0
  150. package/src/utils/isItemAFarmProduct.ts +2 -2
  151. package/src/utils/shouldPrecipitateToday.ts +3 -1
  152. package/src/utils/shouldStormToday.ts +2 -1
  153. package/src/utils/transformStateDataForImport.ts +3 -5
  154. package/dist/assets/francois-one-latin-400-normal-DD1rFI7H.woff2 +0 -0
  155. package/dist/assets/francois-one-latin-400-normal-DNXs7uOx.woff +0 -0
  156. package/dist/assets/francois-one-latin-ext-400-normal-CwRdWRZD.woff2 +0 -0
  157. package/dist/assets/francois-one-latin-ext-400-normal-JvkpT0Av.woff +0 -0
  158. package/dist/assets/francois-one-vietnamese-400-normal-BLIKeG5Y.woff2 +0 -0
  159. package/dist/assets/francois-one-vietnamese-400-normal-CuWeHlXs.woff +0 -0
  160. package/dist/assets/index-CNcrTYN-.css +0 -1
  161. package/dist/assets/index-DNPchtK9.js +0 -844
  162. package/dist/assets/index-DNPchtK9.js.map +0 -1
  163. package/dist/assets/public-sans-latin-400-normal-8Rpg0ruU.woff2 +0 -0
  164. package/dist/assets/public-sans-latin-400-normal-SBbinRkI.woff +0 -0
  165. package/dist/assets/public-sans-latin-ext-400-normal-B02wwOdZ.woff +0 -0
  166. package/dist/assets/public-sans-latin-ext-400-normal-mk90oQqJ.woff2 +0 -0
  167. package/dist/assets/public-sans-vietnamese-400-normal-BO1meqcx.woff +0 -0
  168. package/dist/assets/public-sans-vietnamese-400-normal-TxZB8OSn.woff2 +0 -0
  169. package/src/fontsource.d.ts +0 -3
@@ -17,10 +17,12 @@ const defaultProps = {
17
17
  handleExportDataClick: noop,
18
18
  handleImportDataClick: noop,
19
19
  handleSaveButtonClick: noop,
20
+ handleNotificationDurationChange: noop,
20
21
  handleShowNotificationsChange: noop,
21
22
  handleUseAlternateEndDayButtonPositionChange: noop,
22
23
  handleShowHomeScreenChange: noop,
23
24
  showNotifications: true,
25
+ notificationDuration: 6000,
24
26
  useAlternateEndDayButtonPosition: false,
25
27
  showHomeScreen: false,
26
28
  }
@@ -144,6 +146,25 @@ test('handles switch toggling', async () => {
144
146
  expect(handleAllowCustomPeerCowNamesChange).toHaveBeenCalledTimes(1)
145
147
  })
146
148
 
149
+ test('handles notification duration changes', async () => {
150
+ const user = userEvent.setup()
151
+ const handleNotificationDurationChange = vitest.fn()
152
+
153
+ render(
154
+ <SettingsView
155
+ {...defaultProps}
156
+ handleNotificationDurationChange={handleNotificationDurationChange}
157
+ />
158
+ )
159
+
160
+ const slider = screen.getByRole('slider')
161
+
162
+ await user.click(slider)
163
+ await user.keyboard('{ArrowRight}')
164
+
165
+ expect(handleNotificationDurationChange).toHaveBeenCalled()
166
+ })
167
+
147
168
  test('opens delete confirmation dialog when delete button is clicked', async () => {
148
169
  const user = userEvent.setup()
149
170
 
@@ -1,5 +1,4 @@
1
1
  import React, { useState } from 'react'
2
- import { bool, func } from 'prop-types'
3
2
  import Button from '@mui/material/Button/index.js'
4
3
  import Dialog from '@mui/material/Dialog/index.js'
5
4
  import DialogActions from '@mui/material/DialogActions/index.js'
@@ -11,11 +10,16 @@ import FormControlLabel from '@mui/material/FormControlLabel/index.js'
11
10
  import FormGroup from '@mui/material/FormGroup/index.js'
12
11
  import FormLabel from '@mui/material/FormLabel/index.js'
13
12
  import Switch from '@mui/material/Switch/index.js'
13
+ import Slider from '@mui/material/Slider/index.js'
14
14
  import Tooltip from '@mui/material/Tooltip/index.js'
15
15
  import FileReaderInput from 'react-file-reader-input'
16
16
 
17
17
  import FarmhandContext from '../Farmhand/Farmhand.context.js'
18
18
  import { Div } from '../Elements/index.js'
19
+ import {
20
+ NOTIFICATION_DURATION_MAX,
21
+ NOTIFICATION_DURATION_MIN,
22
+ } from '../../constants.js'
19
23
 
20
24
  import { RandomSeedInput } from './RandomSeedInput.js'
21
25
 
@@ -26,10 +30,12 @@ const SettingsView = ({
26
30
  handleExportDataClick,
27
31
  handleImportDataClick,
28
32
  handleSaveButtonClick,
33
+ handleNotificationDurationChange,
29
34
  handleShowNotificationsChange,
30
35
  handleUseAlternateEndDayButtonPositionChange,
31
36
  handleShowHomeScreenChange,
32
37
  showNotifications,
38
+ notificationDuration,
33
39
  useAlternateEndDayButtonPosition,
34
40
  showHomeScreen,
35
41
  }: {
@@ -41,6 +47,10 @@ const SettingsView = ({
41
47
  handleExportDataClick: () => void
42
48
  handleImportDataClick: (results: any) => void
43
49
  handleSaveButtonClick: () => void
50
+ handleNotificationDurationChange: (
51
+ event: React.SyntheticEvent | Event,
52
+ value: number | number[]
53
+ ) => void
44
54
  handleShowNotificationsChange: (
45
55
  event: React.ChangeEvent<HTMLInputElement>
46
56
  ) => void
@@ -51,6 +61,7 @@ const SettingsView = ({
51
61
  event: React.ChangeEvent<HTMLInputElement>
52
62
  ) => void
53
63
  showNotifications: boolean
64
+ notificationDuration: number
54
65
  useAlternateEndDayButtonPosition: boolean
55
66
  showHomeScreen: boolean
56
67
  }) => {
@@ -110,6 +121,18 @@ const SettingsView = ({
110
121
  }
111
122
  label="Show new notifications"
112
123
  />
124
+ <FormLabel component="legend">Notification display time</FormLabel>
125
+ <Slider
126
+ aria-label="Notification display time"
127
+ marks
128
+ min={NOTIFICATION_DURATION_MIN / 1000}
129
+ max={NOTIFICATION_DURATION_MAX / 1000}
130
+ onChange={handleNotificationDurationChange}
131
+ sx={{ margin: '0 auto', width: '90%' }}
132
+ value={notificationDuration / 1000}
133
+ valueLabelDisplay="auto"
134
+ valueLabelFormat={value => `${value} seconds`}
135
+ />
113
136
  <FormControlLabel
114
137
  control={
115
138
  <Switch
@@ -139,7 +162,6 @@ const SettingsView = ({
139
162
  <div className="button-row">
140
163
  <Tooltip
141
164
  {...{
142
- arrow: true,
143
165
  placement: 'top',
144
166
  title: 'Save your game data as a file on your device',
145
167
  }}
@@ -164,7 +186,6 @@ const SettingsView = ({
164
186
  >
165
187
  <Tooltip
166
188
  {...{
167
- arrow: true,
168
189
  placement: 'top',
169
190
  title: 'Load game data that was previously saved',
170
191
  }}
@@ -230,21 +251,6 @@ const SettingsView = ({
230
251
  )
231
252
  }
232
253
 
233
- SettingsView.propTypes = {
234
- allowCustomPeerCowNames: bool.isRequired,
235
- handleAllowCustomPeerCowNamesChange: func.isRequired,
236
- handleClearPersistedDataClick: func.isRequired,
237
- handleExportDataClick: func.isRequired,
238
- handleImportDataClick: func.isRequired,
239
- handleSaveButtonClick: func.isRequired,
240
- handleShowHomeScreenChange: func.isRequired,
241
- handleShowNotificationsChange: func.isRequired,
242
- handleUseAlternateEndDayButtonPositionChange: func.isRequired,
243
- showHomeScreen: bool.isRequired,
244
- showNotifications: bool.isRequired,
245
- useAlternateEndDayButtonPosition: bool.isRequired,
246
- }
247
-
248
254
  export { SettingsView }
249
255
 
250
256
  export default function Consumer(
@@ -1,5 +1,4 @@
1
1
  import React from 'react'
2
- import { array, func, number, object } from 'prop-types'
3
2
  import Button from '@mui/material/Button/index.js'
4
3
  import Card from '@mui/material/Card/index.js'
5
4
  import CardActions from '@mui/material/CardActions/index.js'
@@ -395,25 +394,6 @@ export const Shop = ({
395
394
  )
396
395
  }
397
396
 
398
- Shop.propTypes = {
399
- handleCombinePurchase: func.isRequired,
400
- handleCowPenPurchase: func.isRequired,
401
- handleCellarPurchase: func.isRequired,
402
- handleFieldPurchase: func.isRequired,
403
- handleStorageExpansionPurchase: func.isRequired,
404
- inventoryLimit: number.isRequired,
405
- money: number.isRequired,
406
- purchasedCowPen: number.isRequired,
407
- purchasedCellar: number.isRequired,
408
- purchasedField: number.isRequired,
409
- purchasedSmelter: number.isRequired,
410
- purchasedWoodChipper: number.isRequired,
411
- purchasedCombine: number.isRequired,
412
- shopInventory: array.isRequired,
413
- toolLevels: object.isRequired,
414
- valueAdjustments: object.isRequired,
415
- }
416
-
417
397
  export default function Consumer(props: Partial<Parameters<typeof Shop>[0]>) {
418
398
  return (
419
399
  <FarmhandContext.Consumer>
@@ -1,5 +1,4 @@
1
1
  import React from 'react'
2
- import { node, number } from 'prop-types'
3
2
 
4
3
  export const TabPanel = (
5
4
  props: React.PropsWithChildren<
@@ -21,12 +20,6 @@ export const TabPanel = (
21
20
  )
22
21
  }
23
22
 
24
- TabPanel.propTypes = {
25
- children: node,
26
- index: number.isRequired,
27
- value: number.isRequired,
28
- }
29
-
30
23
  export const a11yProps = (index: number) => ({
31
24
  id: `shop-tab-${index}`,
32
25
  'aria-controls': `shop-tabpanel-${index}`,
@@ -1,6 +1,5 @@
1
1
  import React, { useEffect, useRef } from 'react'
2
2
  import classNames from 'classnames'
3
- import { array, arrayOf, bool, number, string } from 'prop-types'
4
3
  import { Theme } from '@mui/material/styles/index.js'
5
4
 
6
5
  import FarmhandContext from '../Farmhand/Farmhand.context.js'
@@ -45,9 +44,8 @@ export const Stage = ({
45
44
  useAlternateEndDayButtonPosition = false,
46
45
  viewTitle,
47
46
  }: StageProps) => {
48
- const ref = /** @type {React.MutableRefObject<HTMLDivElement | null>} */ useRef(
49
- null
50
- )
47
+ const ref =
48
+ /** @type {React.MutableRefObject<HTMLDivElement | null>} */ useRef(null)
51
49
 
52
50
  useEffect(() => {
53
51
  if (ref.current) {
@@ -67,8 +65,8 @@ export const Stage = ({
67
65
  [stageFocusType.HOME]: isOctober()
68
66
  ? jackOLanternBg
69
67
  : isDecember()
70
- ? winterBg
71
- : brownDotBg,
68
+ ? winterBg
69
+ : brownDotBg,
72
70
  [stageFocusType.SHOP]: yellowDotBg,
73
71
  [stageFocusType.WORKSHOP]: lavenderDotBg,
74
72
  [stageFocusType.CELLAR]: floorboardBg,
@@ -81,11 +79,13 @@ export const Stage = ({
81
79
  // filter (see seasonFilterMap) applied via the `.stage-background` layer
82
80
  // below - scoped to just the background, not the crop/cow/tree sprites
83
81
  // rendered on top of it.
84
- const hasSeasonalColorShift = ([
85
- stageFocusType.FIELD,
86
- stageFocusType.COW_PEN,
87
- stageFocusType.FOREST,
88
- ] as string[]).includes(stageFocus)
82
+ const hasSeasonalColorShift = (
83
+ [
84
+ stageFocusType.FIELD,
85
+ stageFocusType.COW_PEN,
86
+ stageFocusType.FOREST,
87
+ ] as string[]
88
+ ).includes(stageFocus)
89
89
 
90
90
  return (
91
91
  <Div
@@ -230,15 +230,6 @@ export const Stage = ({
230
230
  )
231
231
  }
232
232
 
233
- Stage.propTypes = {
234
- dayCount: number.isRequired,
235
- field: arrayOf(array).isRequired,
236
- isMenuOpen: bool,
237
- stageFocus: string.isRequired,
238
- useAlternateEndDayButtonPosition: bool,
239
- viewTitle: string.isRequired,
240
- }
241
-
242
233
  export default function Consumer(props: Partial<Parameters<typeof Stage>[0]>) {
243
234
  return (
244
235
  <FarmhandContext.Consumer>
@@ -1,5 +1,4 @@
1
1
  import React from 'react'
2
- import { array, object, number, string } from 'prop-types'
3
2
  import classNames from 'classnames'
4
3
  import Table from '@mui/material/Table/index.js'
5
4
  import TableBody from '@mui/material/TableBody/index.js'
@@ -83,7 +82,6 @@ export const StatsView = ({
83
82
  </TableRow>
84
83
  <Tooltip
85
84
  {...{
86
- arrow: true,
87
85
  placement: 'top',
88
86
  title: FARM_PRODUCTS_TOOLTIP_TEXT,
89
87
  }}
@@ -99,7 +97,6 @@ export const StatsView = ({
99
97
  </Tooltip>
100
98
  <Tooltip
101
99
  {...{
102
- arrow: true,
103
100
  placement: 'top',
104
101
  title: FARM_PRODUCTS_TOOLTIP_TEXT,
105
102
  }}
@@ -123,7 +120,6 @@ export const StatsView = ({
123
120
  </TableRow>
124
121
  <Tooltip
125
122
  {...{
126
- arrow: true,
127
123
  placement: 'top',
128
124
  title: COW_SOLD_TOOLTIP_TEXT,
129
125
  }}
@@ -262,20 +258,6 @@ export const StatsView = ({
262
258
  </Div>
263
259
  )
264
260
 
265
- StatsView.propTypes = {
266
- cowsTraded: number.isRequired,
267
- farmName: string.isRequired,
268
- historicalDailyLosses: array.isRequired,
269
- historicalDailyRevenue: array.isRequired,
270
- itemsSold: object.isRequired,
271
- profitabilityStreak: number.isRequired,
272
- record7dayProfitAverage: number.isRequired,
273
- recordProfitabilityStreak: number.isRequired,
274
- revenue: number.isRequired,
275
- todaysLosses: number.isRequired,
276
- todaysRevenue: number.isRequired,
277
- }
278
-
279
261
  export default function Consumer(props: Partial<StatsViewProps>) {
280
262
  return (
281
263
  <FarmhandContext.Consumer>
@@ -7,7 +7,6 @@ import CardContent from '@mui/material/CardContent/index.js'
7
7
  import CardHeader from '@mui/material/CardHeader/index.js'
8
8
  import CardActions from '@mui/material/CardActions/index.js'
9
9
  import Typography from '@mui/material/Typography/index.js'
10
- import { func, instanceOf, node, number, string } from 'prop-types'
11
10
 
12
11
  import FarmhandContext from '../Farmhand/Farmhand.context.js'
13
12
 
@@ -130,17 +129,6 @@ export function TierPurchase({
130
129
  )
131
130
  }
132
131
 
133
- TierPurchase.propTypes = {
134
- description: string,
135
- onBuyClick: func.isRequired,
136
- maxedOutPlaceholder: node,
137
- money: number.isRequired,
138
- purchasedTier: number.isRequired,
139
- renderTierLabel: func.isRequired,
140
- tiers: instanceOf(Map),
141
- title: string.isRequired,
142
- }
143
-
144
132
  export default function Consumer(
145
133
  props: Partial<Parameters<typeof TierPurchase>[0]>
146
134
  ) {
@@ -1,5 +1,4 @@
1
1
  import React from 'react'
2
- import PropTypes from 'prop-types'
3
2
  import classNames from 'classnames'
4
3
  import ReactMarkdown from 'react-markdown'
5
4
 
@@ -20,7 +19,7 @@ import { breakpoints } from '../../styles/tokens.js'
20
19
 
21
20
  const getTools = memoize(
22
21
  (toolLevels: Record<toolType, toolLevel>, stageFocus?: stageFocusType) => {
23
- const tools: typeof toolsData[keyof typeof toolsData][] = []
22
+ const tools: (typeof toolsData)[keyof typeof toolsData][] = []
24
23
 
25
24
  for (let tool of Object.values(toolsData)) {
26
25
  if (toolLevels[tool.type] === toolLevel.UNAVAILABLE) continue
@@ -66,9 +65,10 @@ export const Toolbelt = ({
66
65
  sx={{
67
66
  display: 'flex',
68
67
  flexFlow: 'row',
69
- [`@media (orientation: landscape) and (min-height: ${breakpoints.largePhone}px)`]: {
70
- flexFlow: 'column',
71
- },
68
+ [`@media (orientation: landscape) and (min-height: ${breakpoints.largePhone}px)`]:
69
+ {
70
+ flexFlow: 'column',
71
+ },
72
72
  '& button': { flexGrow: 1, margin: '0 0.5em' },
73
73
  }}
74
74
  >
@@ -146,11 +146,6 @@ export const Toolbelt = ({
146
146
  )
147
147
  }
148
148
 
149
- Toolbelt.propTypes = {
150
- fieldMode: PropTypes.string.isRequired,
151
- handleFieldModeSelect: PropTypes.func,
152
- }
153
-
154
149
  Toolbelt.defaultProps = {
155
150
  handleFieldModeSelect: noop,
156
151
  toolLevels: {},
@@ -1,11 +1,16 @@
1
- import { useContext, useEffect } from 'react'
1
+ import { useContext, useEffect, useRef } from 'react'
2
2
  // eslint-disable-next-line import/extensions
3
3
  import { useRegisterSW } from 'virtual:pwa-register/react'
4
4
 
5
5
  import FarmhandContext from '../Farmhand/Farmhand.context.js'
6
6
 
7
+ // How long to wait for the new service worker to take over and reload the
8
+ // page before reloading it directly.
9
+ const RELOAD_FALLBACK_DELAY = 1000
10
+
7
11
  const UpdateNotifier = () => {
8
12
  const {
13
+ gameState: { hasBooted },
9
14
  handlers: { handleGameUpdateAvailable },
10
15
  } = useContext(FarmhandContext)
11
16
 
@@ -14,18 +19,41 @@ const UpdateNotifier = () => {
14
19
  updateServiceWorker,
15
20
  } = useRegisterSW()
16
21
 
22
+ const hasNotifiedRef = useRef(false)
23
+
17
24
  useEffect(() => {
18
- if (!appNeedsUpdate) {
25
+ // Wait for the saved game to load: restoring it replaces
26
+ // todaysNotifications, which would drop this notification from the
27
+ // Farmer's Log.
28
+ if (!appNeedsUpdate || !hasBooted || hasNotifiedRef.current) {
19
29
  return
20
30
  }
21
31
 
22
- handleGameUpdateAvailable(updateServiceWorker)
32
+ // handleGameUpdateAvailable isn't referentially stable, so without this
33
+ // guard every re-render would show the notification again (and the
34
+ // resulting state update would re-render, looping forever).
35
+ hasNotifiedRef.current = true
36
+
37
+ handleGameUpdateAvailable(async () => {
38
+ await updateServiceWorker(true)
23
39
 
24
- // NOTE: This ensures the game is updated when the user next opensit.
40
+ // updateServiceWorker only reloads the page once a waiting service
41
+ // worker takes control, which might never happen (e.g. if it was
42
+ // already activated by another tab). Reload anyway so the button
43
+ // always does something.
44
+ setTimeout(() => window.location.reload(), RELOAD_FALLBACK_DELAY)
45
+ })
46
+
47
+ // NOTE: This ensures the game is updated when the user next opens it.
25
48
  window.addEventListener('beforeunload', () => {
26
49
  updateServiceWorker(true)
27
50
  })
28
- }, [appNeedsUpdate, handleGameUpdateAvailable, updateServiceWorker])
51
+ }, [
52
+ appNeedsUpdate,
53
+ hasBooted,
54
+ handleGameUpdateAvailable,
55
+ updateServiceWorker,
56
+ ])
29
57
 
30
58
  return null
31
59
  }
@@ -4,7 +4,6 @@ import Button from '@mui/material/Button/index.js'
4
4
  import Card from '@mui/material/Card/index.js'
5
5
  import CardHeader from '@mui/material/CardHeader/index.js'
6
6
  import CardActions from '@mui/material/CardActions/index.js'
7
- import { array, func, number, object } from 'prop-types'
8
7
 
9
8
  import IngredientsList from '../IngredientsList/index.js'
10
9
 
@@ -85,15 +84,6 @@ export function UpgradePurchase({
85
84
  )
86
85
  }
87
86
 
88
- UpgradePurchase.propTypes = {
89
- handleUpgradeTool: func.isRequired,
90
- inventory: array.isRequired,
91
- inventoryLimit: number.isRequired,
92
- playerInventoryQuantities: object.isRequired,
93
- toolLevels: object,
94
- upgrade: object,
95
- }
96
-
97
87
  export default function Consumer(
98
88
  props: Partial<Parameters<typeof UpgradePurchase>[0]>
99
89
  ) {
@@ -56,14 +56,14 @@ const WineRecipeStub = (
56
56
  return (
57
57
  <FarmhandContext.Provider
58
58
  value={
59
- ({
59
+ {
60
60
  gameState: { ...stubGameState, ...state },
61
61
  handlers: {
62
62
  ...stubHandlers,
63
63
  ...handlers,
64
64
  debounced: { ...stubHandlers, ...handlers },
65
65
  },
66
- } as unknown) as React.ContextType<typeof FarmhandContext>
66
+ } as unknown as React.ContextType<typeof FarmhandContext>
67
67
  }
68
68
  >
69
69
  <WineRecipe
@@ -1,5 +1,4 @@
1
1
  import React, { useContext, useEffect, useState } from 'react'
2
- import { oneOf } from 'prop-types'
3
2
  import Card from '@mui/material/Card/index.js'
4
3
  import CardHeader from '@mui/material/CardHeader/index.js'
5
4
  import CardActions from '@mui/material/CardActions/index.js'
@@ -142,7 +141,3 @@ export const WineRecipe = ({ wineVariety }: { wineVariety: grapeVariety }) => {
142
141
  </Card>
143
142
  )
144
143
  }
145
-
146
- WineRecipe.propTypes = {
147
- wineVariety: oneOf(Object.keys(grapeVariety)),
148
- }
@@ -15,9 +15,8 @@ export const WineRecipeList = () => {
15
15
  gameState: { itemsSold },
16
16
  } = useContext(FarmhandContext)
17
17
 
18
- const wineVarietiesAvailableToMake = getWineVarietiesAvailableToMake(
19
- itemsSold
20
- )
18
+ const wineVarietiesAvailableToMake =
19
+ getWineVarietiesAvailableToMake(itemsSold)
21
20
 
22
21
  const [searchQuery, setSearchQuery] = useState('')
23
22
 
@@ -1,5 +1,4 @@
1
1
  import React from 'react'
2
- import { number, array, object } from 'prop-types'
3
2
 
4
3
  import Card from '@mui/material/Card/index.js'
5
4
  import CardContent from '@mui/material/CardContent/index.js'
@@ -73,10 +72,3 @@ export function ForgeTabPanel({
73
72
  </TabPanel>
74
73
  )
75
74
  }
76
-
77
- ForgeTabPanel.propTypes = {
78
- currentTab: number.isRequired,
79
- index: number.isRequired,
80
- learnedForgeRecipes: array.isRequired,
81
- toolLevels: object.isRequired,
82
- }
@@ -1,5 +1,4 @@
1
1
  import React from 'react'
2
- import { number, array } from 'prop-types'
3
2
 
4
3
  import Card from '@mui/material/Card/index.js'
5
4
  import CardContent from '@mui/material/CardContent/index.js'
@@ -48,9 +47,3 @@ export function KitchenTabPanel({
48
47
  </TabPanel>
49
48
  )
50
49
  }
51
-
52
- KitchenTabPanel.propTypes = {
53
- currentTab: number.isRequired,
54
- index: number.isRequired,
55
- learnedKitchenRecipes: array.isRequired,
56
- }
@@ -1,5 +1,4 @@
1
1
  import React from 'react'
2
- import { number, array } from 'prop-types'
3
2
 
4
3
  import Card from '@mui/material/Card/index.js'
5
4
  import CardContent from '@mui/material/CardContent/index.js'
@@ -48,9 +47,3 @@ export function RecyclingTabPanel({
48
47
  </TabPanel>
49
48
  )
50
49
  }
51
-
52
- RecyclingTabPanel.propTypes = {
53
- currentTab: number.isRequired,
54
- index: number.isRequired,
55
- learnedRecipes: array.isRequired,
56
- }
@@ -1,5 +1,4 @@
1
1
  import React from 'react'
2
- import { node, number } from 'prop-types'
3
2
 
4
3
  interface TabPanelProps {
5
4
  children?: React.ReactNode
@@ -24,12 +23,6 @@ export const TabPanel = (props: TabPanelProps) => {
24
23
  )
25
24
  }
26
25
 
27
- TabPanel.propTypes = {
28
- children: node,
29
- index: number.isRequired,
30
- value: number.isRequired,
31
- }
32
-
33
26
  export const a11yProps = (index: number) => ({
34
27
  id: `workshop-tab-${index}`,
35
28
  'aria-controls': `workshop-tabpanel-${index}`,
@@ -1,5 +1,4 @@
1
1
  import React from 'react'
2
- import { number, array } from 'prop-types'
3
2
 
4
3
  import Card from '@mui/material/Card/index.js'
5
4
  import CardContent from '@mui/material/CardContent/index.js'
@@ -48,9 +47,3 @@ export function WoodChipperTabPanel({
48
47
  </TabPanel>
49
48
  )
50
49
  }
51
-
52
- WoodChipperTabPanel.propTypes = {
53
- currentTab: number.isRequired,
54
- index: number.isRequired,
55
- learnedRecipes: array.isRequired,
56
- }
@@ -1,5 +1,4 @@
1
1
  import React from 'react'
2
- import { number, object } from 'prop-types'
3
2
  import Tab from '@mui/material/Tab/index.js'
4
3
  import Tabs from '@mui/material/Tabs/index.js'
5
4
 
@@ -115,14 +114,6 @@ const Workshop = ({
115
114
  )
116
115
  }
117
116
 
118
- Workshop.propTypes = {
119
- learnedRecipes: object.isRequired,
120
- purchasedComposter: number,
121
- purchasedSmelter: number,
122
- purchasedWoodChipper: number,
123
- toolLevels: object.isRequired,
124
- }
125
-
126
117
  export default function Consumer(
127
118
  props: Partial<Parameters<typeof Workshop>[0]>
128
119
  ) {
@@ -17,7 +17,7 @@ export function getUpgradesAvailable({
17
17
  }: GetUpgradesAvailableArgs): farmhand.upgradesMetadatum[] {
18
18
  let upgradesAvailable: farmhand.upgradesMetadatum[] = []
19
19
 
20
- const typedToolUpgrades = (toolUpgrades as unknown) as Record<
20
+ const typedToolUpgrades = toolUpgrades as unknown as Record<
21
21
  string,
22
22
  Record<string, farmhand.upgradesMetadatum>
23
23
  >