mirror of
https://github.com/duckietm/Nitro-V3.git
synced 2026-06-19 15:06:20 +00:00
9cc9ef86c0
resetItems/removeUnseen/the UnseenItemsEvent handler each did `new Map(prevValue)` (a shallow copy) then spliced/pushed the per-category array returned by `.get(category)` — the SAME array reference still held by the previous Map. That mutates state outside React's data flow (breaks under StrictMode double-invoke and any updater replay). resetItems additionally did `splice(existing.indexOf(id), 1)` with no guard, so an id not present (indexOf === -1) spliced off the wrong LAST element. Replace each in-place splice/push with a cloned array set back on the new Map (filter for removals, spread+push for the merge).
133 lines
4.0 KiB
TypeScript
133 lines
4.0 KiB
TypeScript
import { UnseenItemsEvent, UnseenResetCategoryComposer, UnseenResetItemsComposer } from '@nitrots/nitro-renderer';
|
|
import { useCallback, useMemo, useState } from 'react';
|
|
import { useBetween } from 'use-between';
|
|
import { SendMessageComposer } from '../../api';
|
|
import { useMessageEvent } from '../events';
|
|
|
|
const sendResetCategoryMessage = (category: number) => SendMessageComposer(new UnseenResetCategoryComposer(category));
|
|
const sendResetItemsMessage = (category: number, itemIds: number[]) => SendMessageComposer(new UnseenResetItemsComposer(category, ...itemIds));
|
|
|
|
const useInventoryUnseenTrackerState = () =>
|
|
{
|
|
const [ unseenItems, setUnseenItems ] = useState<Map<number, number[]>>(new Map());
|
|
|
|
const getCount = useCallback((category: number) => (unseenItems.get(category)?.length || 0), [ unseenItems ]);
|
|
|
|
const getFullCount = useMemo(() =>
|
|
{
|
|
let count = 0;
|
|
|
|
for(const key of unseenItems.keys()) count += getCount(key);
|
|
|
|
return count;
|
|
}, [ unseenItems, getCount ]);
|
|
|
|
const resetCategory = useCallback((category: number) =>
|
|
{
|
|
let didReset = true;
|
|
|
|
setUnseenItems(prevValue =>
|
|
{
|
|
if(!prevValue.has(category))
|
|
{
|
|
didReset = false;
|
|
|
|
return prevValue;
|
|
}
|
|
|
|
const newValue = new Map(prevValue);
|
|
|
|
newValue.delete(category);
|
|
|
|
sendResetCategoryMessage(category);
|
|
|
|
return newValue;
|
|
});
|
|
|
|
return didReset;
|
|
}, []);
|
|
|
|
const resetItems = useCallback((category: number, itemIds: number[]) =>
|
|
{
|
|
let didReset = true;
|
|
|
|
setUnseenItems(prevValue =>
|
|
{
|
|
if(!prevValue.has(category))
|
|
{
|
|
didReset = false;
|
|
|
|
return prevValue;
|
|
}
|
|
|
|
const newValue = new Map(prevValue);
|
|
const existing = newValue.get(category);
|
|
|
|
// Replace the per-category array instead of splicing the one still
|
|
// referenced by the previous Map, and filter (an absent id used to
|
|
// splice(indexOf=-1) and drop the wrong last element).
|
|
if(existing) newValue.set(category, existing.filter(id => !itemIds.includes(id)));
|
|
|
|
sendResetItemsMessage(category, itemIds);
|
|
|
|
return newValue;
|
|
});
|
|
|
|
return didReset;
|
|
}, []);
|
|
|
|
const isUnseen = useCallback((category: number, itemId: number) =>
|
|
{
|
|
if(!unseenItems.has(category)) return false;
|
|
|
|
const items = unseenItems.get(category);
|
|
|
|
return (items.indexOf(itemId) >= 0);
|
|
}, [ unseenItems ]);
|
|
|
|
const removeUnseen = useCallback((category: number, itemId: number) =>
|
|
{
|
|
setUnseenItems(prevValue =>
|
|
{
|
|
if(!prevValue.has(category)) return prevValue;
|
|
|
|
const newValue = new Map(prevValue);
|
|
const items = newValue.get(category);
|
|
|
|
// Clone the array rather than splicing the one shared with prevValue.
|
|
if(items && items.indexOf(itemId) >= 0) newValue.set(category, items.filter(id => id !== itemId));
|
|
|
|
return newValue;
|
|
});
|
|
}, []);
|
|
|
|
useMessageEvent<UnseenItemsEvent>(UnseenItemsEvent, event =>
|
|
{
|
|
const parser = event.getParser();
|
|
|
|
setUnseenItems(prevValue =>
|
|
{
|
|
const newValue = new Map(prevValue);
|
|
|
|
for(const category of parser.categories)
|
|
{
|
|
// Clone the existing array so we never push into the one still
|
|
// referenced by the previous (shallow-copied) Map.
|
|
const merged = [ ...(newValue.get(category) ?? []) ];
|
|
|
|
const itemIds = parser.getItemsByCategory(category);
|
|
|
|
for(const itemId of itemIds) if(merged.indexOf(itemId) === -1) merged.push(itemId);
|
|
|
|
newValue.set(category, merged);
|
|
}
|
|
|
|
return newValue;
|
|
});
|
|
});
|
|
|
|
return { getCount, getFullCount, resetCategory, resetItems, isUnseen, removeUnseen };
|
|
};
|
|
|
|
export const useInventoryUnseenTracker = () => useBetween(useInventoryUnseenTrackerState);
|