diff --git a/package-lock.json b/package-lock.json index a383ef9ea..0cbfe08bc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@jeremyckahn/farmhand", - "version": "1.17.1", + "version": "1.17.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@jeremyckahn/farmhand", - "version": "1.17.1", + "version": "1.17.2", "license": "GPL-2.0-or-later", "dependencies": { "@fortawesome/fontawesome-svg-core": "^1.2.27", diff --git a/package.json b/package.json index 1ec585a9d..168f76529 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@jeremyckahn/farmhand", - "version": "1.17.1", + "version": "1.17.2", "publishConfig": { "access": "public" }, diff --git a/src/components/Farmhand/Farmhand.js b/src/components/Farmhand/Farmhand.js index da33df018..916f71be8 100644 --- a/src/components/Farmhand/Farmhand.js +++ b/src/components/Farmhand/Farmhand.js @@ -1,6 +1,9 @@ -/** @typedef {import("../../index").farmhand.item} farmhand.item */ -/** @typedef {import("../../index").farmhand.cow} farmhand.cow */ -/** @typedef {import("../../index").farmhand.keg} farmhand.keg */ +/** + * @typedef {import("../../index").farmhand.item} farmhand.item + * @typedef {import("../../index").farmhand.cow} farmhand.cow + * @typedef {import("../../index").farmhand.keg} farmhand.keg + * @typedef {import("../../index").farmhand.notification} notification + */ import React, { Component } from 'react' import window from 'global/window' import { Redirect } from 'react-router-dom' @@ -74,7 +77,6 @@ import { COW_TRADE_TIMEOUT, DEFAULT_ROOM, INITIAL_STORAGE_LIMIT, - PURCHASEABLE_COW_PENS, STAGE_TITLE_MAP, STANDARD_LOAN_AMOUNT, } from '../../constants' @@ -84,7 +86,6 @@ import { } from '../../common/constants' import { CONNECTED_TO_ROOM, - COW_PEN_PURCHASED, LOAN_INCREASED, POSITIONS_POSTED_NOTIFICATION, RECIPE_LEARNED, @@ -230,9 +231,9 @@ const applyPriceEvents = (valueAdjustments, priceCrashes, priceSurges) => { * @property {number} loanBalance * @property {number} loansTakenOut * @property {number} money - * @property {farmhand.notification} latestNotification - * @property {Array.} newDayNotifications - * @property {Array.} notificationLog + * @property {notification} latestNotification + * @property {Array.} newDayNotifications + * @property {Array.} notificationLog * @property {Object} peers Keys are (Trystero) peer ids, values are their respective metadata or null. * @property {Object?} peerRoom See https://github.com/dmotz/trystero * @property {Array.} pendingPeerMessages An array of @@ -264,7 +265,7 @@ const applyPriceEvents = (valueAdjustments, priceCrashes, priceSurges) => { * @property {boolean} showHomeScreen Option to show the Home Screen * @property {boolean} showNotifications * @property {farmhand.module:enums.stageFocusType} stageFocus - * @property {Array.} todaysNotifications + * @property {Array.} todaysNotifications * @property {number} todaysLosses Should always be a negative number. * @property {Object} todaysPurchases Keys are item names, values are their * respective quantities. @@ -635,7 +636,7 @@ export default class Farmhand extends Component { this.setState({ hasBooted: true }) } - componentDidUpdate(prevProps, prevState) { + componentDidUpdate(_prevProps, prevState) { const { hasBooted, heartbeatTimeoutId, @@ -766,7 +767,6 @@ export default class Farmhand extends Component { } ;[ - 'showCowPenPurchasedNotifications', 'showInventoryFullNotifications', 'showRecipeLearnedNotifications', ].forEach(fn => this[fn](prevState)) @@ -813,9 +813,7 @@ export default class Farmhand extends Component { const { ownerId } = peerPlayerCow const [peerId] = - Object.entries(peers).find( - ([peerId, peer]) => peer?.id === ownerId - ) ?? [] + Object.entries(peers).find(([, peer]) => peer?.id === ownerId) ?? [] if (!peerId) { console.error( @@ -1035,21 +1033,6 @@ export default class Farmhand extends Component { })) } - /*! - * @param {farmhand.state} prevState - */ - showCowPenPurchasedNotifications(prevState) { - const { - state: { purchasedCowPen }, - } = this - - if (purchasedCowPen !== prevState.purchasedCowPen) { - const { cows } = PURCHASEABLE_COW_PENS.get(purchasedCowPen) - - this.showNotification(COW_PEN_PURCHASED`${cows}`) - } - } - /*! * @param {farmhand.state} prevState */ diff --git a/src/game-logic/reducers/purchaseCellar.js b/src/game-logic/reducers/purchaseCellar.js index 0fd038d4a..93d8da288 100644 --- a/src/game-logic/reducers/purchaseCellar.js +++ b/src/game-logic/reducers/purchaseCellar.js @@ -1,6 +1,10 @@ import { moneyTotal } from '../../utils' import { PURCHASEABLE_CELLARS } from '../../constants' +import { CELLAR_PURCHASED } from '../../templates' + +import { showNotification } from './showNotification' + /** * @param {farmhand.state} state * @param {number} cellarId @@ -13,8 +17,13 @@ export const purchaseCellar = (state, cellarId) => { return state } + const { price, space } = PURCHASEABLE_CELLARS.get(cellarId) + + state = showNotification(state, CELLAR_PURCHASED`${space}`, 'success') + return { + ...state, purchasedCellar: cellarId, - money: moneyTotal(money, -PURCHASEABLE_CELLARS.get(cellarId).price), + money: moneyTotal(money, -price), } } diff --git a/src/game-logic/reducers/purchaseCellar.test.js b/src/game-logic/reducers/purchaseCellar.test.js index 1cf282c88..39a412019 100644 --- a/src/game-logic/reducers/purchaseCellar.test.js +++ b/src/game-logic/reducers/purchaseCellar.test.js @@ -4,17 +4,33 @@ import { purchaseCellar } from './purchaseCellar' describe('purchaseCellar', () => { test('updates purchasedCellar', () => { - const { purchasedCellar } = purchaseCellar({}, 1) + const { purchasedCellar } = purchaseCellar({ todaysNotifications: [] }, 1) expect(purchasedCellar).toEqual(1) }) test('prevents repurchasing options', () => { - const { purchasedCellar } = purchaseCellar({ purchasedCellar: 2 }, 1) + const { purchasedCellar } = purchaseCellar( + { todaysNotifications: [], purchasedCellar: 2 }, + 1 + ) expect(purchasedCellar).toEqual(2) }) test('deducts money', () => { - const { money } = purchaseCellar({ money: 1_000_000 }, 1) + const { money } = purchaseCellar( + { todaysNotifications: [], money: 1_000_000 }, + 1 + ) expect(money).toEqual(1_000_000 - PURCHASEABLE_CELLARS.get(1).price) }) + + test('shows notification of purchase', () => { + const { todaysNotifications } = purchaseCellar( + { todaysNotifications: [] }, + 1 + ) + expect(todaysNotifications[0].message).toEqual( + 'Purchased a cellar with capacity for 10 kegs! View your keg inventory by going to the "Cellar" page.' + ) + }) }) diff --git a/src/game-logic/reducers/purchaseCowPen.js b/src/game-logic/reducers/purchaseCowPen.js index 44983f1d8..1fa5ef74d 100644 --- a/src/game-logic/reducers/purchaseCowPen.js +++ b/src/game-logic/reducers/purchaseCowPen.js @@ -1,10 +1,16 @@ +/** + * @typedef {import("../../components/Farmhand/Farmhand").farmhand.state} state + */ import { moneyTotal } from '../../utils' import { PURCHASEABLE_COW_PENS } from '../../constants' +import { COW_PEN_PURCHASED } from '../../templates' + +import { showNotification } from './showNotification' /** - * @param {farmhand.state} state + * @param {state} state * @param {number} cowPenId - * @returns {farmhand.state} + * @returns {state} */ export const purchaseCowPen = (state, cowPenId) => { const { money, purchasedCowPen } = state @@ -13,8 +19,13 @@ export const purchaseCowPen = (state, cowPenId) => { return state } + const { cows, price } = PURCHASEABLE_COW_PENS.get(cowPenId) + + state = showNotification(state, COW_PEN_PURCHASED`${cows}`, 'success') + return { + ...state, purchasedCowPen: cowPenId, - money: moneyTotal(money, -PURCHASEABLE_COW_PENS.get(cowPenId).price), + money: moneyTotal(money, -price), } } diff --git a/src/game-logic/reducers/purchaseCowPen.test.js b/src/game-logic/reducers/purchaseCowPen.test.js index 29705e067..cf05e2e5e 100644 --- a/src/game-logic/reducers/purchaseCowPen.test.js +++ b/src/game-logic/reducers/purchaseCowPen.test.js @@ -4,17 +4,33 @@ import { purchaseCowPen } from './purchaseCowPen' describe('purchaseCowPen', () => { test('updates purchasedCowPen', () => { - const { purchasedCowPen } = purchaseCowPen({}, 1) + const { purchasedCowPen } = purchaseCowPen({ todaysNotifications: [] }, 1) expect(purchasedCowPen).toEqual(1) }) test('prevents repurchasing options', () => { - const { purchasedCowPen } = purchaseCowPen({ purchasedCowPen: 2 }, 1) + const { purchasedCowPen } = purchaseCowPen( + { todaysNotifications: [], purchasedCowPen: 2 }, + 1 + ) expect(purchasedCowPen).toEqual(2) }) test('deducts money', () => { - const { money } = purchaseCowPen({ money: 1500 }, 1) + const { money } = purchaseCowPen( + { todaysNotifications: [], money: 1500 }, + 1 + ) expect(money).toEqual(1500 - PURCHASEABLE_COW_PENS.get(1).price) }) + + test('shows notification of purchase', () => { + const { todaysNotifications } = purchaseCowPen( + { todaysNotifications: [] }, + 1 + ) + expect(todaysNotifications[0].message).toEqual( + 'Purchased a cow pen with capacity for 10 cows! You can visit your cow pen by going to the "Cows" page.' + ) + }) }) diff --git a/src/game-logic/reducers/showNotification.js b/src/game-logic/reducers/showNotification.js index bca919982..8c02be4f9 100644 --- a/src/game-logic/reducers/showNotification.js +++ b/src/game-logic/reducers/showNotification.js @@ -1,11 +1,16 @@ +/** + * @typedef {import("../../components/Farmhand/Farmhand").farmhand.state} state + * @typedef {import("@material-ui/lab/Alert").Color} alertSeverity + */ + // TODO: Change showNotification to accept a configuration object instead of so // many formal parameters. /** - * @param {farmhand.state} state + * @param {state} state * @param {string} message - * @param {string} [severity] Corresponds to the `severity` prop here: + * @param {alertSeverity} [severity] Corresponds to the `severity` prop here: * https://material-ui.com/api/alert/ - * @returns {farmhand.state} + * @returns {state} * @see https://material-ui.com/api/alert/ */ export const showNotification = ( diff --git a/src/shell/notifications.test.js b/src/shell/notifications.test.js index c9e4a7932..539aac0be 100644 --- a/src/shell/notifications.test.js +++ b/src/shell/notifications.test.js @@ -2,7 +2,7 @@ import { screen } from '@testing-library/react' import { within } from '@testing-library/dom' import userEvent from '@testing-library/user-event' -import { getItemByName, nextView } from '../test-utils/ui' +import { getItemByName } from '../test-utils/ui' import { farmhandStub } from '../test-utils/stubs/farmhandStub' import { saveDataStubFactory } from '../test-utils/stubs/saveDataStubFactory' @@ -29,42 +29,6 @@ describe('notifications', () => { expect(within(notification).getByText('Carrot Soup')).toBeInTheDocument() }) - test('notification is shown when cow pen is purchased', async () => { - const loadedState = saveDataStubFactory({ - money: 1500, - }) - - await farmhandStub({ - localforage: { - getItem: () => Promise.resolve(loadedState), - setItem: (_key, data) => Promise.resolve(data), - }, - }) - - await nextView() - const upgradesTab = screen.getByText('Upgrades') - userEvent.click(upgradesTab) - - const cowPenContainer = screen - .getByText('Buy cow pen') - .closest('.TierPurchase') - - // Open the list of cow pen options - userEvent.click(within(cowPenContainer).getAllByRole('button')[1]) - - userEvent.click(screen.getByRole('option', { name: '$1,500: 10 cow pen' })) - - // Make the purchase - userEvent.click(within(cowPenContainer).getAllByRole('button')[0]) - - const notification = await screen.findByRole('alert') - expect( - within(notification).getByText( - 'Purchased a cow pen with capacity for 10 cows! You can visit your cow pen by going to the "Cows" page.' - ) - ).toBeInTheDocument() - }) - test('multiple notifications are shown when multiple recipes are learned', async () => { const loadedState = saveDataStubFactory({ inventory: [ diff --git a/src/templates.js b/src/templates.js index 733c75205..02fbefed1 100644 --- a/src/templates.js +++ b/src/templates.js @@ -33,12 +33,21 @@ export const CROWS_DESTROYED = (_, numCropsDestroyed) => }!` /** + * @param {string} _ * @param {number} cows * @returns {string} */ export const COW_PEN_PURCHASED = (_, cows) => `Purchased a cow pen with capacity for ${cows} cows! You can visit your cow pen by going to the "Cows" page.` +/** + * @param {string} _ + * @param {number} kegCapacity + * @returns {string} + */ +export const CELLAR_PURCHASED = (_, kegCapacity) => + `Purchased a cellar with capacity for ${kegCapacity} kegs! View your keg inventory by going to the "Cellar" page.` + /** * @param {Object.} milksProduced * @returns {string}