mirror of
https://github.com/duckietm/Nitro-V3.git
synced 2026-06-19 15:06:20 +00:00
cc225bdc5d
Three top-level files brought in sync with the work landed on feat/react19-modernization: - CHANGELOG.md gets a 'React 19 Modernization Phase 2 (2026-05-12)' section spanning all four pattern groups (event-state companions, TanStack queries on the catalog layer, god-hook splits in the doorbell + singleton-filter styles, Pixi v8 / TS 5.7+ alignment), the Vitest growth 65 -> 113, and the in-scope logic bug fixes. - ARCHITECTURE.md bumps the test ledger 99 -> 113 (adds the avatar-info reducer suite), documents the new pure-module test convention (concrete file paths + 'import type' for renderer event types), and lists the two new singleton-filter splits (notification, friends). - CLAUDE.md mirrors the same updates plus a 'Singleton-filter split' recipe alongside the doorbell-style one; useNitroEventInvalidator is documented next to useNitroQuery; the 'What's wired up' table enumerates all 10 split hooks. Test count bumped 99 -> 113 in both the 'Vitest' row and the green-bar house rule.
195 lines
11 KiB
Markdown
195 lines
11 KiB
Markdown
# Changelog
|
|
|
|
## React 19 Modernization Phase 2 (2026-05-12)
|
|
|
|
Long-running work on the `feat/react19-modernization` branch — see
|
|
[`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) for the design rationale.
|
|
Companion changes shipped on `feat/react19-event-bus` in
|
|
[`Nitro_Render_V3`](../Nitro_Render_V3) — see that repo's CLAUDE.md
|
|
for the renderer-side notes.
|
|
|
|
### Pattern #1: `useNitroEventState` + companions
|
|
- New `useNitroEventReducer` / `useMessageEventReducer` for the case
|
|
where multiple event types collapse into one owned state slice.
|
|
- New `useExternalSnapshot` — typed wrapper of
|
|
`useSyncExternalStore` pairing the renderer's
|
|
`EventDispatcher.subscribe()` with `getXxxSnapshot()` getters.
|
|
- Pilot adoption: `useAvatarInfoWidget` now owns the figure / badges /
|
|
group merge (three event listeners moved out of
|
|
`InfoStandWidgetUserView`, three `CloneObject` calls dropped).
|
|
Reducers extracted to `src/hooks/rooms/widgets/avatarInfo.reducers.ts`
|
|
with 14 Vitest cases.
|
|
- `useInventoryFurni` refactored to call three pure reducers
|
|
(`useInventoryFurni.reducers.ts`) instead of inlining ~250 LOC of
|
|
merge logic in the event handlers. Module-level
|
|
`furniMsgFragments` becomes a `useRef` — eliminates a latent bug
|
|
where two simultaneous client instances would have trampled each
|
|
other's fragment buffers. Empty `FurniturePostItPlacedEvent` listener
|
|
dropped.
|
|
|
|
### Pattern #2: `useNitroQuery` adoption
|
|
- New `useNitroEventInvalidator(eventType, queryKey, accept?)` companion
|
|
in `src/api/nitro-query/` — invalidates a query slot every time the
|
|
renderer pushes the matching parser event. Required when the server
|
|
refreshes data outside the request cycle (e.g. ClubGiftInfoEvent
|
|
after a gift claim).
|
|
- Seven catalog fetches lifted out of `useCatalog` into dedicated
|
|
TanStack queries:
|
|
- `useGiftConfiguration` (GiftWrappingConfigurationEvent)
|
|
- `useUserGroups` — consolidates 5 sites that each dispatched
|
|
`CatalogGroupsComposer` independently
|
|
- `useClubOffers(windowId)` — per-windowId, with `accept` filter
|
|
- `useSellablePetPalette(breed)` — per-breed, with `accept` filter
|
|
- `useMarketplaceConfiguration` — lifted out of a self-fetch in
|
|
`MarketplacePostOfferView`
|
|
- `useClubGifts` — paired with `useNitroEventInvalidator` for the
|
|
server-push-after-SelectClubGift case
|
|
- `ICatalogOptions` (the "catalogOptions" bag that the various views
|
|
stuffed their fetched data into) is now **empty and deleted**.
|
|
|
|
### Pattern #4: god-hook splits
|
|
Five new splits in this round, two patterns. The doorbell-style
|
|
(state + actions + shim, no shared singleton) for hooks whose actions
|
|
are pure-dispatch:
|
|
|
|
- **chat-input** (334 LOC → 3 files) — `useChatInputState` owns the
|
|
5 state slices + 3 event listeners + 3 lifecycle effects;
|
|
`useChatInputActions` owns `sendChat` with the full slash-command
|
|
repertoire and the outgoing-translation pipeline. Single consumer
|
|
(`ChatInputView`) keeps the original tuple via the shim.
|
|
|
|
The `useBetween` singleton-filter style for hooks where actions
|
|
mutate shared state:
|
|
|
|
- **wired-tools** (618 LOC) — 20 consumers; `useWiredToolsStore`
|
|
internal singleton, public `useWiredToolsState` /
|
|
`useWiredToolsActions` filter views, `useWiredTools` shim.
|
|
- **translation** (600 LOC) — 6 consumers; `useTranslationStore`
|
|
inline + filter views.
|
|
- **notification** (493 LOC) — ~44 consumers, most of which use a
|
|
single action (`simpleAlert` or `showConfirm`); the read-only state
|
|
slice exposes the three queue arrays for the renderer view layer.
|
|
- **friends** (258 LOC) — 16 consumers; state slice covers the friend
|
|
list / settings / derived online-offline split, actions slice covers
|
|
`requestFriend` / `requestResponse` / `followFriend` /
|
|
`updateRelationship`.
|
|
|
|
Documented skip-motivated splits: `useChatWidget`,
|
|
`useChatCommandSelector`, `useFurniturePresentWidget`,
|
|
`useAvatarInfoWidget`, `useNavigator`, `useMessenger`,
|
|
`usePetPackageWidget`, `useWordQuizWidget`. Reasons logged in commit
|
|
messages.
|
|
|
|
### Typecheck / Pixi v8 / Arcturus alignment
|
|
- Repository-wide `tsgo` (TS 7 preview) error count: **134 → 0** client,
|
|
**24 → 0** renderer. Notable clusters: framer-motion `Variants`
|
|
typing on Toolbar + FriendsBar (-33), `useFurniChooserState`
|
|
retyped as `IRoomObject` + dead `getUserData` guard dropped (-10),
|
|
React 19 `useRef<T>()` → `useRef<T>(null)` sweep on 15 sites (-15),
|
|
`IGetImageListener` single-arg signature migration on 3 sites,
|
|
`ColorVariantType` extended with the 5 `outline-*` bootstrap
|
|
variants.
|
|
- Renderer-side aligned with Pixi v8 (Filter[] narrowing,
|
|
WebGLRenderer narrowing, ImageLike cast) and TS 5.7+ ArrayBuffer
|
|
drift (BinaryReader / BinaryWriter / WsSessionCrypto / NitroBundle).
|
|
- Cross-repo additions on `Nitro_Render_V3`:
|
|
`RoomEnterComposer` now accepts optional `spawnX`/`spawnY` matching
|
|
Arcturus' `RequestRoomLoadEvent` optional tail; `RoomSettingsData`
|
|
surfaces the `allowUnderpass` field that Arcturus already emits.
|
|
Dead `sendWhisperGroupMessage` / `ChatWhisperGroupComposer`
|
|
reference removed.
|
|
|
|
### Vitest coverage
|
|
Bumped from 65 → 113 cases across 8 test files. New coverage:
|
|
- `dedupeBadges.test.ts` (6) — slot-preserving badge dedup.
|
|
- `catalog-favorites.helpers.test.ts` (16) — v2→v3 localStorage
|
|
migration + per-catalog-type storage-key routing.
|
|
- `avatar-info-reducers.test.ts` (14) — three reducer bail-out
|
|
branches + apply paths.
|
|
- `friendly-time.test.ts` (12) — `FriendlyTime` with a deterministic
|
|
`LocalizeText` mock.
|
|
|
|
### Logic bug fixes (in scope)
|
|
- `useInventoryFurni`'s module-level `furniMsgFragments` buffer
|
|
scoped to `useRef`.
|
|
- `RoomChatHandler.dispatchEvent(RoomSessionChatEvent)` arg order
|
|
fix in renderer — `chatColours` and `links` slots were swapped.
|
|
- `PetBreedingMessageParser.bytesAvailable < 12` was a boolean-vs-number
|
|
bug; replaced with the standard guard pattern.
|
|
- `useOnClickChat` was passing an extra 8th arg to `showConfirm`
|
|
(signature only takes 7).
|
|
- `UserContainerView` was passing `userProfile.friendsCount` (number)
|
|
to a `LocalizeText` placeholder array (expects string).
|
|
|
|
## Badge System Rework (2026-04-04)
|
|
|
|
### Bug Fixes
|
|
- **Slot 0 drag bug**: Dragging from slot 0 no longer causes badges to disappear. The root cause was `'0'` being falsy in JavaScript, which made the drop handler take the wrong code path and overwrite the target badge.
|
|
- **Badge duplication**: Fixed badges appearing in multiple slots when dragging in the InfoStand. The issue was a stale props fallback — after a drag operation, the hook updated correctly but the component fell back to old server props for empty slots, showing ghost copies.
|
|
- **Race condition**: Replaced single boolean `localChangeRef` with a counter (`pendingUpdatesRef`) to correctly handle rapid sequential drag operations without the server overwriting local state.
|
|
- **Badge deduplication**: `toFixedSlots()` now deduplicates badges, preventing the same badge from appearing in multiple slots even if the server returns duplicates.
|
|
- **Server badge dedup in InfoStand**: `RoomSessionUserBadgesEvent` handler now deduplicates badges from the server before updating the avatar info.
|
|
|
|
### Drag & Drop Visual Feedback
|
|
- **Custom drag preview**: Badge image is used as the drag ghost instead of the browser default (via `setDragImage`).
|
|
- **Source opacity**: The dragged item becomes semi-transparent (`opacity-40`) during drag.
|
|
- **Pulsing glow on drop targets**: Valid drop targets pulse with a blue glow animation (`animate-pulse-glow`).
|
|
- **Drop settle animation**: A brief scale-down animation (`animate-drop-settle`, 300ms) plays when a badge lands in a slot.
|
|
- **Remove indicator**: Dragging an active badge over the inventory area shows a red pulsing background with a trash icon overlay.
|
|
- **Grab cursor**: All draggable badge elements now show `cursor-grab` / `cursor-grabbing`.
|
|
|
|
### Sparse Slot Support
|
|
- `activeBadgeCodes` changed from compact `string[]` to fixed-size `(string | null)[]` array. Empty slots are `null` instead of being collapsed, allowing gaps between badges.
|
|
- All operations (`setBadgeAtSlot`, `removeBadge`, `reorderBadges`, `swapBadges`, `toggleBadge`) work on the fixed-size array without compaction.
|
|
|
|
### New Badge Glow (Feature)
|
|
- Unseen (newly received) badges in the inventory now pulse with a **gold glow** (`animate-pulse-glow-gold`) instead of the previous flat green background.
|
|
- The glow disappears when the badge is selected (unseen status cleared).
|
|
|
|
### Badge Received Toast Notification (Feature)
|
|
- When a new badge is received, a bubble notification appears with:
|
|
- Badge image and localized name
|
|
- **"Indossa" / "Wear"** button that directly equips the badge via `toggleBadge` and closes the notification
|
|
- **"Non ora" / "Later"** link to dismiss
|
|
- Auto-fades after 8 seconds (standard bubble behavior).
|
|
- Uses the existing `NotificationBubbleType.BADGE_RECEIVED` (was defined but unused).
|
|
- New component: `NotificationBadgeReceivedBubbleView`.
|
|
|
|
### Dynamic Badge Slot Count
|
|
- Badge slot count is now fully driven by `user.badges.max.slots` config (default: 5).
|
|
- **5 slots**: 5 badge slots + group badge in InfoStand (6 boxes total)
|
|
- **6 slots**: 6 badge slots, group badge is replaced by the 6th slot
|
|
- Both the inventory grid and InfoStand layout adapt automatically.
|
|
- Removed all hardcoded `maxSlots = 5` references.
|
|
|
|
### InfoStand Double-Click to Remove
|
|
- Double-clicking a badge in the InfoStand removes it from active badges (own user only).
|
|
|
|
### Localization
|
|
- Added `notification.badge.received` key:
|
|
- IT: "Nuovo Distintivo!"
|
|
- EN: "New Badge!"
|
|
- Located in `public/nitro-assets/config/UITexts.json` and `UITexts_en.json`.
|
|
|
|
### Files Modified
|
|
| File | Changes |
|
|
|------|---------|
|
|
| `src/hooks/inventory/useInventoryBadges.ts` | Sparse slots, dedup, race condition fix, toFixedSlots |
|
|
| `src/hooks/notification/useNotification.ts` | BadgeReceivedEvent listener |
|
|
| `src/components/inventory/views/badge/InventoryBadgeView.tsx` | Visual feedback, dynamic maxSlots, fix '0' falsy |
|
|
| `src/components/inventory/views/badge/InventoryBadgeItemView.tsx` | Drag preview, opacity, cursor |
|
|
| `src/components/room/widgets/avatar-info/infostand/InfoStandBadgeSlotView.tsx` | Visual feedback, double-click remove, no stale props |
|
|
| `src/components/room/widgets/avatar-info/infostand/InfoStandWidgetUserView.tsx` | Dynamic layout, server badge dedup |
|
|
| `src/components/notification-center/views/bubble-layouts/GetBubbleLayout.tsx` | BADGE_RECEIVED routing |
|
|
| `src/components/notification-center/views/bubble-layouts/NotificationBadgeReceivedBubbleView.tsx` | New component |
|
|
| `src/layout/InfiniteGrid.tsx` | Gold glow for unseen items |
|
|
| `tailwind.config.js` | Custom keyframes and animations |
|
|
|
|
### Configuration
|
|
```json
|
|
{
|
|
"user.badges.max.slots": 5
|
|
}
|
|
```
|
|
Set to `6` to replace the group badge slot with a 6th badge slot.
|