Commit Graph

18 Commits

Author SHA1 Message Date
simoleo89 d73656bbfe chore: update react player and frontend deps 2026-06-04 22:15:12 +02:00
simoleo89 c7e258e3d1 feat(hooks): permission-driven gating via useHasPermission
Replace the rank-level family (useHasRankLevel + STAFF_LEVELS
constants + useIsRank) with a permission-driven family that reads
straight from the deployment's `permission_definitions` table — no
more hardcoded SecurityLevel/rank-id thresholds on the client. A new
rank in permission_ranks or a re-shuffling of permission_definitions
rank columns now propagates through the UI automatically.

Renderer-side wire shipped in companion commit
feat/react19-event-bus@159c5eb (UserPermissionsMapParser + Event,
SessionDataManager.getPermissionsSnapshot + USER_PERMISSIONS_UPDATED).

New public API in `useSessionSnapshots.ts`:
- useUserPermissions(): ReadonlyMap<string, number>  — full map
- useHasPermission(key): boolean                       — > 0 ⇒ true
- usePermissionValue(key): number                      — raw 1/2 or 0
- useIsAmbassador() now aliases useHasPermission('acc_ambassador')
- useUserRank() kept for PRESENTATIONAL use only (badge, prefix,
  prefix color) — documented as such in JSDoc; gating must use
  useHasPermission.

Dropped:
- src/api/nitro/session/RankLevels.ts (STAFF_LEVELS constants)
- useHasRankLevel / useIsRank exports (rank-based gating)

11 consumer migrations, each mapped to the right
`permission_definitions.permission_key`:

  - ToolbarView (mod-only chat-input button)        → acc_supporttool
  - ChooserWidgetView (room-object id column)       → acc_supporttool
  - CatalogClassicView (admin toggles)              → acc_catalogfurni
  - CatalogModernView (admin toggles)               → acc_catalogfurni
  - FurniEditorView (panel access)                  → acc_catalogfurni
  - CalendarView (force-open day)                   → acc_calendar_force
  - InfoStandWidgetFurniView (mod buildtools btn)   → acc_anyroomowner
  - AvatarInfoWidgetPetView (canPickUp)             → acc_anyroomowner
  - FurnitureMannequinView (controller mode)        → acc_anyroomowner
  - YouTubePlayerView (isMyRoom)                    → acc_anyroomowner
  - NavigatorRoomInfoView 'settings'                → acc_anyroomowner
  - NavigatorRoomInfoView 'staff_pick'              → acc_staff_pick

Test refresh:
- useUserRank still tested for the presentational shape.
- useHasPermission: true for non-zero, false for absent/zero.
- usePermissionValue: raw 1 / 2 / 0 (default).
- useUserPermissions: full map exposure.
- Runtime promote test: mutate the permissions map + dispatch
  USER_PERMISSIONS_UPDATED, assert useHasPermission flips false→true.
  Locks in the new reactive contract.

Mock unchanged (the test sets getPermissionsSnapshot via vi.mocked).

Verification: yarn typecheck clean, yarn lint:hooks clean, yarn test
214/214 (213 prior + 1 net new for useUserPermissions). Backward
compatible: older Arcturus deployments don't ship the map → empty
snapshot → every gate is false → mod UI hidden (safe default).
2026-05-19 19:00:10 +02:00
simoleo89 8aa02249e1 feat(hooks): rank-based API tied to permission_ranks DB table
Drop the SecurityLevel-named family (useIsModerator / useIsAdmin /
useIsCommunity / useIsPlayerSupport / useHasSecurityLevel /
useUserSecurityLevel) in favour of a rank-based family tied to the
operator's actual `permission_ranks` rows in the Arcturus DB:

- `useUserRank()` returns `{ id, name, level, badge, prefix,
  prefixColor }` derived from the snapshot. Powered by the renderer's
  extended IUserDataSnapshot (companion commit 87e67d5 on
  feat/react19-event-bus).
- `useHasRankLevel(min)` replaces useHasSecurityLevel; consumers
  pass a `permission_ranks.level` threshold from the deployment.
- `useIsRank(name)` matches `permission_ranks.rank_name` exactly.

To avoid bare integers in widget bodies, added a deployment-scoped
constants file at `src/api/nitro/session/RankLevels.ts`:

  export const STAFF_LEVELS = {
      MEMBER: 1, SUPPORT: 4, MOD: 5, SUPER_MOD: 6, ADMIN: 7
  };

A deployment that re-numbers `permission_ranks` only edits this file.

Migrated all 11 consumer reads (same set as the earlier session's
useIsModerator migration plus the audit catch): ToolbarView,
CatalogClassicView, CatalogModernView, ChooserWidgetView,
CalendarView, YouTubePlayerView, FurniEditorView,
InfoStandWidgetFurniView, AvatarInfoWidgetPetView,
FurnitureMannequinView, NavigatorRoomInfoView. The
NavigatorRoomInfoView `staff_pick` permission was previously
`securityLevel >= COMMUNITY (7)` via the renderer-enum wrapper —
ported to `useHasRankLevel(STAFF_LEVELS.ADMIN)` because in the
default seed level 7 is Administrator, which is the actual rank that
gets the `acc_anyroomowner`-style permissions for staff-picking.

Tests refreshed under `useSessionSnapshots.test.tsx`:
- useUserRank surfaces the full metadata block;
- useHasRankLevel does `>=` against the threshold;
- useIsRank exact-matches against rank_name;
- a runtime promote (snapshot mutation + SESSION_DATA_UPDATED
  dispatch) flips the result, locking in the reactive contract.

Mock extended only minimally — kept the SecurityLevel enum class for
any consumer outside the dropped family that still imports it.

Verification: yarn typecheck clean, yarn lint:hooks clean, yarn test
213/213. The Arcturus-side composer change (UserPermissionsComposer
appending the 5 extra fields) is staged but UNCOMMITTED on Arcturus
main (which has unrelated WIP); the wire is backward-compatible so
the React client works against both pre- and post-extension
emulators.
2026-05-19 18:38:31 +02:00
simoleo89 c11a6c4699 feat(hooks): generalise security-level family + audit catch + reactivity test
Build on the useIsModerator landing (532cb28c) along three axes:

1. Family. Extract `useHasSecurityLevel(min)` as the primitive,
   backed by a fresh `useUserSecurityLevel()` raw-level reader. The
   six SecurityLevel constants (1..9) deserve named wrappers so the
   "show this only to X-and-up" pattern doesn't get re-derived ad-hoc
   each time: shipped `useIsModerator` / `useIsPlayerSupport` /
   `useIsCommunity` / `useIsAdmin` as one-line shims. Also added
   `useIsAmbassador()` as a sibling — not derived from security level,
   reads the boolean field on the snapshot directly.

2. Audit. The 532cb28c migration covered 6 React-render reads but
   missed 5 more discovered by a follow-up grep:
   - FurniEditorView (top-level `const isMod`)
   - InfoStandWidgetFurniView (inline JSX, mod-only build-tools button)
   - NavigatorRoomInfoView (3 reads in hasPermission(): isModerator
     and securityLevel >= COMMUNITY for the staff-pick gate. The
     userId read stays imperative — userId doesn't flip at runtime in
     practice, no reactivity gain.)
   - AvatarInfoWidgetPetView (inside useMemo with [roomSession] deps;
     migrated and isModerator added to the deps so a runtime
     promote/demote re-derives canPickUp without remount)
   - FurnitureMannequinView (inside useEffect; same treatment — added
     isModerator to the deps so the mode re-resolves on flip)

   The remaining ~17 reads (CanManipulateFurniture,
   AvatarInfoUtilities.populate*, useChatInputActions,
   useFurnitureDimmerWidget / useFurniturePlaylistEditorWidget /
   useFurnitureStickieWidget canModify checks, useCatalog admin
   filter, useNavigator door-mode guard) are click-time / event-time
   imperative — they read at the moment a user action fires, so a
   reactive value would be cached at hook execution and stale by the
   time the action runs. Leaving them on the synchronous manager read
   is correct.

3. Test. Added four cases pinning the contract:
   - useUserSecurityLevel returns the raw level.
   - useHasSecurityLevel does `>=` against the threshold.
   - Named wrappers map to the right constants (MODERATOR=5,
     COMMUNITY=7, ADMINISTRATOR=8).
   - **Reactive flip** — mutate the snapshot, dispatch the
     SESSION_DATA_UPDATED event on the mock dispatcher, assert the
     hook re-derives. Locks in the whole point of the snapshot
     pattern (a static read would pass cases 1-3 but fail case 4).

Mock changes:
- Added SecurityLevel class (mirrors the renderer enum 0..9) so the
  family wrappers resolve to actual numbers in jsdom — without it
  `useIsModerator()` would call `useHasSecurityLevel(undefined)` and
  the test would silently pass false-positives.

Verification: yarn typecheck clean, yarn lint:hooks clean, yarn test
213/213 (209 baseline + 4 new family/reactivity cases).
2026-05-19 18:18:20 +02:00
simoleo89 ab93113ce7 widgets: wrap each room + furniture widget in its own WidgetErrorBoundary
The umbrella boundary on RoomWidgetsView caught any widget crash but
unmounted every sibling along with the failing widget — a single bad
parser in ChatWidget would dark out the avatar info, chat input,
doorbell and all furniture overlays until the next remount.

Wraps each of the 13 direct children of RoomWidgetsView (AvatarInfo,
Chat, ChatInput, Doorbell, RoomTools, RoomFilterWords, RoomThumbnail,
FurniChooser, PetPackage, UserChooser, WordQuiz, FriendRequest, plus
the FurnitureWidgets umbrella) and each of the 20 sub-widgets inside
FurnitureWidgetsView in its own named WidgetErrorBoundary. A crash
now silently logs through NitroLogger with the widget name and
renders null for that one widget; every sibling keeps rendering.

The outer umbrella stays as defense-in-depth for the wrapper div and
the listener setup in RoomWidgetsView itself.

Closes the "Per-widget WidgetErrorBoundary wrapping" roadmap item;
updates CLAUDE.md and docs/ARCHITECTURE.md accordingly.
2026-05-14 20:18:38 +02:00
simoleo89 019295226d Sweep targeted typecheck errors: 11 fixes across 9 files
- ProductImageUtility: 'CatalogPageMessageProductData.I' was clearly a
  placeholder/typo in the WALL branch — getProductCategory's first
  param is FurnitureType, so use the enclosing productType.
- YouTubePlayerView: IRoomUserData has webID, not userId. Two
  spectator/watcher-list sites used the wrong field.
- AvatarInfoWidgetView REQUEST_MANIPULATION handler: avatarInfo is
  IAvatarInfo (union); .category / .id only exist on AvatarInfoFurni.
  Type-guard before reading.
- InfoStandWidgetPetView: deleted the duplicate local 'interface
  AvatarInfoPet' — was shadowing the imported one. Drop AvatarInfoPet
  from the import (local interface stands alone).
- FurnitureExternalImageView: missing GetSessionDataManager import (the
  reportedUserId field reads it inline). Added.
- GroupCreatorView setGroupData call: null values for groupName /
  groupDescription / groupColors / groupBadgeParts where IGroupData
  expects string / number[] / GroupBadgePart[]. Empty defaults. Also
  added the previously-omitted groupHasForum field.
- ContextMenuView + WiredCreatorToolsView: 'return () =>
  ticker.remove(updateOverlays)' — Pixi Ticker.remove() returns the
  ticker, leaking the value to React's EffectCallback cleanup which
  expects 'void | (() => void)'. Wrap in block body.
- Deleted src/components/room/widgets/chat/ChatWidgetWindowView_old.tsx
  — dead code (zero references in the codebase), tripping the
  NitroCardHeaderView onCloseClick prop change.

Net tsgo error count: -11.
2026-05-11 21:34:34 +02:00
simoleo89 535fa71020 ESLint --fix: auto-fix brace-style, indent, semi, no-trailing-spaces
Run eslint --fix across src/ to clear ~1900 mechanical lint errors
surfaced by the @typescript-eslint v8 + react-hooks v7 + react-compiler
upgrade in the React 19 modernization PR.

Issues fixed automatically:
- brace-style (Allman): try/catch one-liners reformatted to multi-line
- indent: tab-vs-space and depth corrections
- semi: missing trailing semicolons
- no-trailing-spaces

No semantic changes. Remaining 701 errors are real-code issues
(set-state-in-effect, rules-of-hooks, no-unsafe-* type checks) that
need manual per-file review.

https://claude.ai/code/session_01GrR87LAqnAEyKG2ZbmQt5Q
2026-05-11 16:31:50 +00:00
DuckieTM 92e9bb19cd 🆙 Updates thanks to Life
react-slider → @radix-ui/react-slider
react-tiny-popover → @radix-ui/react-popover
react-youtube → react-player (more formats, better maintained)
2026-05-03 17:54:10 +02:00
duckietm 9dbe360448 🆙 Fixed Buddy Pets showing in catalogue 2026-04-15 13:09:27 +02:00
Lorenzune f12b43ca92 Merge remote-tracking branch 'upstream/main' into merge/duckietm-main-20260407 2026-04-07 15:28:04 +02:00
Lorenzune c4e1318fd5 fix: polish furniture widgets and area hide toggle 2026-04-03 13:00:05 +02:00
duckietm 203a754ce1 ↔️ Lets also make the highscore moveable 2026-04-03 12:44:36 +02:00
duckietm d5a5c1683f 🆙 Fix highscore wired view 2026-04-03 12:36:57 +02:00
Lorenzune e4b1f14fa2 feat: update room control widgets and menus 2026-04-03 12:09:16 +02:00
duckietm 57f6960735 🆙 Fix the black screen in some cases 2026-03-31 14:55:29 +02:00
duckietm faadd0cf31 🆙 Better way to load the slider 2026-02-25 11:51:03 +01:00
duckietm f2446d232b 🆙 Upgrade to tailwind css 4.2.0 2026-02-20 08:17:17 +01:00
DuckieTM 7feb10ab15 🆙 Init V3 2026-01-31 09:10:52 +01:00