mirror of
https://github.com/duckietm/Nitro-V3.git
synced 2026-06-19 15:06:20 +00:00
81656e7b19
These are the bugs surfaced during the structural work that are simple
enough to fix in isolation. Larger ones (race conditions that need
session-token tracking, async-fetch ordering) are deferred and documented
in docs/ARCHITECTURE.md "Known logic bugs" — the repo has Issues
disabled, so the doc is the issue board.
== Fix: room history wiped on every tab close
src/components/room/widgets/room-tools/RoomToolsWidgetView.tsx had a
useEffect that registered a `beforeunload` handler calling
`window.localStorage.removeItem('nitro.room.history')`. The whole point
of localStorage is to persist across sessions; wiping it on tab close is
either a leftover debug call or a misunderstanding of the API.
Removed the handler. History now persists across browser sessions, which
matches user expectations. If "session-only" was the intent, the right
primitive is `sessionStorage` (not localStorage + cleanup) — left as a
note in the doc.
== Fix: AvatarInfoPetTrainingPanelView null-pointer on session change
src/components/room/widgets/avatar-info/AvatarInfoPetTrainingPanelView.tsx
read `roomSession.userDataManager.getPetData(parser.petId)` without
guarding for `roomSession` being null. The PetTrainingPanelMessageEvent
can arrive during a room transition when `roomSession` is briefly null,
crashing the widget. Added `?.` chain on both `roomSession` and
`userDataManager`.
== Doc: known logic bugs section
Two open issues documented for follow-up:
- MainView.tsx CREATED/ENDED race — needs session-token tracking, fits
cleanly into the future useNitroEventReducer companion to proposal #1.
- LayoutFurniImageView / LayoutAvatarImageView async fetch ordering —
needs request-id refs, or solves itself once React Query (proposal #2)
is enabled and the image fetch becomes a query keyed on props.
Plus a "recently fixed" subsection that records the four bugs already
addressed in this branch (doorbell close button, doorbell optimistic
remove, room history wipe, pet panel null-pointer) so the next reader
knows what changed and why.
== Verification
- yarn eslint on the two modified files: same error count before and
after (5 pre-existing set-state-in-effect on RoomToolsWidgetView,
none introduced).
- yarn tsc on the two modified files: clean.
https://claude.ai/code/session_01GrR87LAqnAEyKG2ZbmQt5Q
60 lines
2.8 KiB
TypeScript
60 lines
2.8 KiB
TypeScript
import { IRoomUserData, PetTrainingMessageParser, PetTrainingPanelMessageEvent } from '@nitrots/nitro-renderer';
|
|
import { FC, useState } from 'react';
|
|
import { LocalizeText } from '../../../../api';
|
|
import { Button, Column, Flex, Grid, LayoutPetImageView, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../../common';
|
|
import { useMessageEvent, useRoom, useSessionInfo } from '../../../../hooks';
|
|
|
|
export const AvatarInfoPetTrainingPanelView: FC<{}> = props =>
|
|
{
|
|
const [ petData, setPetData ] = useState<IRoomUserData>(null);
|
|
const [ petTrainInformation, setPetTrainInformation ] = useState<PetTrainingMessageParser>(null);
|
|
const { chatStyleId = 0 } = useSessionInfo();
|
|
const { roomSession = null } = useRoom();
|
|
|
|
useMessageEvent<PetTrainingPanelMessageEvent>(PetTrainingPanelMessageEvent, event =>
|
|
{
|
|
const parser = event.getParser();
|
|
|
|
if(!parser) return;
|
|
|
|
const roomPetData = roomSession?.userDataManager?.getPetData(parser.petId);
|
|
|
|
if(!roomPetData) return;
|
|
|
|
setPetData(roomPetData);
|
|
setPetTrainInformation(parser);
|
|
});
|
|
|
|
const processPetAction = (petName: string, commandName: string) =>
|
|
{
|
|
if(!petName || !commandName) return;
|
|
|
|
roomSession?.sendChatMessage(`${ petName } ${ commandName }`, chatStyleId);
|
|
};
|
|
|
|
if(!petData || !petTrainInformation) return null;
|
|
|
|
return (
|
|
<NitroCardView className="user-settings-window no-resize" theme="primary-slim" uniqueKey="user-settings">
|
|
<NitroCardHeaderView headerText={ LocalizeText('widgets.pet.commands.title') } onCloseClick={ () => setPetTrainInformation(null) } />
|
|
<NitroCardContentView className="text-black">
|
|
<Flex alignItems="center" gap={ 2 } justifyContent="center">
|
|
<Grid columnCount={ 2 }>
|
|
<Column fullWidth className="body-image pet p-1" overflow="hidden">
|
|
<LayoutPetImageView direction={ 2 } figure={ petData.figure } posture={ 'std' } />
|
|
</Column>
|
|
<Text small wrap variant="black">{ petData.name }</Text>
|
|
</Grid>
|
|
</Flex>
|
|
<Grid columnCount={ 2 }>
|
|
{
|
|
(petTrainInformation.commands && petTrainInformation.commands.length > 0) && petTrainInformation.commands.map((command, index) =>
|
|
<Button key={ index } disabled={ !petTrainInformation.enabledCommands.includes(command) } onClick={ () => processPetAction(petData.name, LocalizeText(`pet.command.${ command }`)) }>{ LocalizeText(`pet.command.${ command }`) }</Button>
|
|
)
|
|
}
|
|
</Grid>
|
|
</NitroCardContentView>
|
|
</NitroCardView>
|
|
);
|
|
};
|