Skip to content

Commit

Permalink
fix(Game): Global notification system
Browse files Browse the repository at this point in the history
This patch brings along a global notification system.
When opening items such as treasure chests, you'll now see the results cleaned up within the notification system that was previously utilized in the gathering page.
  • Loading branch information
SobieskiCodes committed Aug 9, 2024
1 parent aa2f4a9 commit 58a3459
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 40 deletions.
2 changes: 2 additions & 0 deletions src/app/store.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import playerReducer from '../features/player/playerSlice';
import expeditionReducer from '../features/expeditions/expeditionSlice';
import aquariumReducer from '../features/aquariumshop/aquariumSlice';
import gatheringReducer from '../features/gathering/gatheringSlice';
import notificationReducer from '../features/notifications/notificationSlice';
import { saveState, loadState } from '../features/player/saveState';

const preloadedState = loadState();
Expand All @@ -13,6 +14,7 @@ const rootReducer = {
expedition: expeditionReducer,
aquarium: aquariumReducer,
gathering: gatheringReducer,
notifications: notificationReducer,
};

const store = configureStore({
Expand Down
28 changes: 28 additions & 0 deletions src/components/Notification.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { clearNotificationMessage } from '../features/notifications/notificationSlice';
import '../styles/notification.css';

const Notification = () => {
const notificationMessage = useSelector((state) => state.notifications.message);
const dispatch = useDispatch();

useEffect(() => {
if (notificationMessage) {
const timer = setTimeout(() => {
dispatch(clearNotificationMessage());
}, 3000);
return () => clearTimeout(timer);
}
}, [notificationMessage, dispatch]);

if (!notificationMessage) return null;

return (
<div className="notification">
{notificationMessage}
</div>
);
};

export default Notification;
9 changes: 4 additions & 5 deletions src/features/gathering/gatheringSlice.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// src/features/gathering/gatheringSlice.js
import { createSlice } from '@reduxjs/toolkit';
import { updateSkillXp, addItemToInventory } from '../player/playerSlice';
import { setNotificationMessage } from '../notifications/notificationSlice'; // Import the new notification actions

const initialState = {
activeResource: null,
Expand All @@ -22,16 +23,13 @@ const gatheringSlice = createSlice({
state.activeResource = null;
state.gatheringStartTime = null;
},
setNotificationMessage: (state, action) => {
state.notificationMessage = action.payload;
},
clearNotificationMessage: (state) => {
state.notificationMessage = null;
},
},
});

export const { startGatheringResource, stopGatheringResource, setNotificationMessage, clearNotificationMessage } = gatheringSlice.actions;
export const { startGatheringResource, stopGatheringResource, clearNotificationMessage } = gatheringSlice.actions;

export const handleGathering = () => async (dispatch, getState) => {
console.log('handle gather called');
Expand All @@ -48,7 +46,8 @@ export const handleGathering = () => async (dispatch, getState) => {
console.log("Gathering completed, gained xp: ", xpGainedValue);
await dispatch(updateSkillXp({ skill: 'gathering', xp: xpGainedValue }));
await dispatch(addItemToInventory({ item: selectedItem }));
dispatch(setNotificationMessage(`Gathered ${selectedItem.name} and gained ${xpGainedValue} XP!`));
console.log(`Dispatching notification: Gathered ${selectedItem.name} and gained ${xpGainedValue} XP!`);
dispatch(setNotificationMessage(`Gathered ${selectedItem.name} and gained ${xpGainedValue} XP!`));
dispatch(handleGathering()); // Recurse to keep gathering
}
}, selectedItem.duration);
Expand Down
20 changes: 20 additions & 0 deletions src/features/notifications/notificationSlice.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { createSlice } from '@reduxjs/toolkit';

const notificationSlice = createSlice({
name: 'notifications',
initialState: {
message: null,
},
reducers: {
setNotificationMessage: (state, action) => {
state.message = action.payload;
},
clearNotificationMessage: (state) => {
state.message = null;
},
},
});

export const { setNotificationMessage, clearNotificationMessage } = notificationSlice.actions;

export default notificationSlice.reducer;
20 changes: 4 additions & 16 deletions src/pages/Gathering.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import React, { useState, useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import '../styles/gathering.css';
import items from '../data/items/items';
import { startGatheringResource, stopGatheringResource, handleGathering, setNotificationMessage, clearNotificationMessage } from '../features/gathering/gatheringSlice';
import { startGatheringResource, stopGatheringResource, handleGathering } from '../features/gathering/gatheringSlice';
import { clearActiveZone } from '../features/expeditions/expeditionSlice';
import { setNotificationMessage } from '../features/notifications/notificationSlice'; // Import the new notification actions
import { calculateLevelFromXP, getRequiredXPForLevel } from '../features/player/xpCalculator';
import Notification from '../components/Notification'; // Import the Notification component

const Gathering = () => {
const [activeTab, setActiveTab] = useState('minerals');
Expand All @@ -31,7 +33,6 @@ const Gathering = () => {
const gatheringSpeed = useSelector(state => state.player.gatheringSpeed);
const gatheringEfficiency = useSelector(state => state.player.gatheringEfficiency);
const activeResource = useSelector(state => state.gathering.activeResource);
const notificationMessage = useSelector(state => state.gathering.notificationMessage);

const currentXP = gatheringSkill.xp;
const currentLevel = calculateLevelFromXP(currentXP);
Expand Down Expand Up @@ -67,15 +68,6 @@ const Gathering = () => {
});
};

useEffect(() => {
if (notificationMessage) {
const timer = setTimeout(() => {
dispatch(clearNotificationMessage());
}, 3000);
return () => clearTimeout(timer);
}
}, [notificationMessage, dispatch]);

return (
<div className="gathering-container">
<div className="gathering-column">
Expand Down Expand Up @@ -177,11 +169,7 @@ const Gathering = () => {
)}
</div>
</div>
{notificationMessage && (
<div className="notification">
{notificationMessage}
</div>
)}
<Notification /> {/* Add Notification component */}
</div>
);
};
Expand Down
54 changes: 35 additions & 19 deletions src/pages/Inventory.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import '../styles/inventory.css';
import InventorySlot from '../components/InventorySlot';
import ContextMenu from '../components/ContextMenu';
import ConfirmationModal from '../components/ConfirmationModal';
import Notification from '../components/Notification';
import { setNotificationMessage } from '../features/notifications/notificationSlice';
import resource from '../data/items/items';

const Inventory = () => {
Expand All @@ -15,7 +17,6 @@ const Inventory = () => {
const getRandomInt = (max) => Math.floor(Math.random() * max);

const getItemFromResources = (resource, itemId) => {
// Flatten the nested resource objects
const flattenedResources = Object.values(resource).reduce((acc, curr) => {
if (typeof curr === 'object') {
Object.values(curr).forEach(item => {
Expand All @@ -24,22 +25,17 @@ const Inventory = () => {
}
return acc;
}, []);

// Find the item by its id
return flattenedResources.find(item => item.id === itemId);
};

const generateContents = (resource, items, currency) => {
const contents = [];

// Randomly select a currency amount
if (currency.length > 0 && Math.random() > 0.5) {
const selectedCurrency = currency[getRandomInt(currency.length)];
contents.push({ type: 'currency', amount: selectedCurrency });
} else if (items.length > 0) { // Ensure only one type is selected
} else if (items.length > 0) {
const itemId = items[getRandomInt(items.length)];
console.log("itemId-----------------", itemId);
console.log('resource obj', Object.values(resource));
const item = getItemFromResources(resource, itemId);
if (item) {
contents.push({ type: 'item', item });
Expand All @@ -62,20 +58,17 @@ const Inventory = () => {

const handleContextMenu = (e, item, index) => {
e.preventDefault();

const options = ['Sell'];
if (item.contents) {
options.push('Open (1)', 'Open (All)');
}

setContextMenu({ visible: true, x: e.clientX, y: e.clientY, item, index, options });
};

const handleOptionClick = (option) => {
if (option === 'Sell') {
const totalValue = contextMenu.item.sellValue * contextMenu.item.quantity;
const sellMessage = `Sell ${contextMenu.item.quantity} ${contextMenu.item.name}(s) for ${totalValue} currency?`;

setConfirmation({
visible: true,
item: contextMenu.item,
Expand All @@ -85,38 +78,61 @@ const Inventory = () => {
} else if (option.startsWith('Open')) {
const openAll = option === 'Open (All)';
const chestQuantity = openAll ? contextMenu.item.quantity : 1;


const allContents = [];

for (let i = 0; i < chestQuantity; i++) {
const chestContents = openTreasureChest(contextMenu.item);
allContents.push(...chestContents);

chestContents.forEach((content) => {
if (content.type === 'currency') {
dispatch({
type: 'player/addCurrency',
payload: content.amount,
});
console.log(`Opened a chest and received ${content.amount} currency`);
} else if (content.type === 'item' && content.item) {
dispatch({
type: 'player/addItemToInventory',
payload: { item: content.item },
});
console.log(`Opened a chest and received an item: ${content.item.name}`);
} else {
console.error('Invalid item content:', content);
}
});
}

// Update the quantity of the opened item

const itemCounts = allContents.reduce((acc, content) => {
if (content.type === 'item') {
acc[content.item.name] = (acc[content.item.name] || 0) + 1;
}
return acc;
}, {});

const receivedCurrency = allContents
.filter(content => content.type === 'currency')
.reduce((acc, content) => acc + content.amount, 0);

const itemMessages = Object.entries(itemCounts).map(([name, count]) => `${name} (x${count})`).join(', ');

let notificationMessage = '';
if (itemMessages) {
notificationMessage += `Received items: ${itemMessages}. `;
}
if (receivedCurrency > 0) {
notificationMessage += `Received currency: ${receivedCurrency}.`;
}

dispatch(setNotificationMessage(notificationMessage));

dispatch({
type: 'player/updateItemQuantity',
payload: { itemId: contextMenu.item.id, quantity: contextMenu.item.quantity - chestQuantity },
});
}

setContextMenu({ ...contextMenu, visible: false });
};


const handleClickOutside = () => {
if (contextMenu.visible) {
Expand All @@ -142,7 +158,6 @@ const Inventory = () => {
});
setConfirmation({ visible: false, item: null, index: null });
};


const handleSellCancel = () => {
setConfirmation({ visible: false, item: null, index: null });
Expand Down Expand Up @@ -175,11 +190,12 @@ const Inventory = () => {
)}
{confirmation.visible && (
<ConfirmationModal
message={`Are you sure you want to sell ${confirmation.item.quantity} ${confirmation.item.name}(s) for ${confirmation.item.sellValue * confirmation.item.quantity || 0} currency?`}
message={confirmation.message}
onConfirm={handleSellConfirm}
onCancel={handleSellCancel}
/>
)}
<Notification />
</div>
);
};
Expand Down
12 changes: 12 additions & 0 deletions src/styles/notification.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.notification {
position: fixed;
top: 20px;
right: 20px;
padding: 10px;
background-color: #323232;
color: #fff;
border-radius: 5px;
z-index: 1000;
transition: opacity 0.3s ease;
}

0 comments on commit 58a3459

Please sign in to comment.