Commit Graph

13 Commits

Author SHA1 Message Date
simoleo89 3d1f1103a3 feat(furni-editor): 'Import from Habbo' button to fetch official texts
useFurniEditor gains importText(id) + importResult (10049 round-trip); the
edit view shows an 'Import from Habbo' button that fills Display Name/
Description with the official text for review before Save (nonce-guarded,
classname-matched), with an inline result note.
2026-06-06 17:42:43 +02:00
simoleo89 d7e294b343 feat(furni-editor): editable furnidata name/desc section + read-only classname/public_name + diff-confirm + revert 2026-06-06 17:42:41 +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 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
Life c4d948cd3a feat: FurniEditor WebSocket — full UI with toolbar icon, infostand button, Edit/Search views, Tailwind styling 2026-03-28 08:52:59 +01:00
duckietm accb69d6c9 🆙 Reverting furnieditor 2026-03-23 12:00:31 +01:00
duckietm a0d0b5c4a4 Revert "Merge branch 'main' into furnisettingeditor-pr"
This reverts commit dfbfb1c2c1, reversing
changes made to 07702c44d0.
2026-03-23 11:49:05 +01:00
DuckieTM dfbfb1c2c1 Merge branch 'main' into furnisettingeditor-pr 2026-03-23 11:13:00 +01:00
Life 07702c44d0 feat(furni-editor): migrate to WebSocket communication and improve UI
Migrate the Furni Editor from REST API to WebSocket-based communication
using custom packet handlers (10040-10046). The editor now communicates
directly with the emulator for all CRUD operations on furniture items.

Key changes:
- Replace REST API calls with WebSocket composers/parsers for search,
  edit, create, and delete operations
- Read furnituredata.json path from renderer-config.json for asset
  management
- Improve search UI with larger fonts, better contrast, and click-to-copy
  ID functionality with toast notification
- Compact edit view layout with collapsible sections and visual dividers
- Remove unused Create tab (creation handled via edit workflow)
- Add isModerator guard for admin-only access
- Support search by ID, name, or sprite ID with type filtering
2026-03-22 17:57:27 +01:00
Life a5ea88010e feat: catalog style toggle (classic/new) with admin mode & favorites 2026-03-22 16:54:40 +01:00
simoleo89 a11987e1e0 Add FurniEditor tool with Next.js API integration
- FurniEditor component with Search/Edit tabs (NitroCard UI)
- useFurniEditor hook connecting to Next.js API routes via Vite proxy
- Edit Furni button in room infostand (godMode) with sprite ID lookup
- Toolbar: 3-column flex layout (icons | chat | friends)
- Heroicons SVG for ID/Sprite display in infostand and edit view
- Vite config: proxy /api to Next.js, aliases for renderer3 packages
2026-03-15 00:27:09 +01:00