mirror of
https://github.com/duckietm/Nitro-V3.git
synced 2026-06-19 23:16:21 +00:00
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).
This commit is contained in:
@@ -2,9 +2,9 @@ import { CrackableDataType, CreateLinkEvent, FurnitureFloorUpdateEvent, GetRoomE
|
||||
import { FC, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { FaCrosshairs, FaTimes } from 'react-icons/fa';
|
||||
import { GrFormNextLink, GrRotateLeft, GrRotateRight } from 'react-icons/gr';
|
||||
import { AvatarInfoFurni, GetGroupInformation, LocalizeText, SendMessageComposer, STAFF_LEVELS } from '../../../../../api';
|
||||
import { AvatarInfoFurni, GetGroupInformation, LocalizeText, SendMessageComposer } from '../../../../../api';
|
||||
import { Button, Column, Flex, LayoutBadgeImageView, LayoutLimitedEditionCompactPlateView, LayoutRarityLevelView, LayoutRoomObjectImageView, Text, UserProfileIconView } from '../../../../../common';
|
||||
import { useHasRankLevel, useMessageEvent, useNitroEvent, useRoom, useWiredTools } from '../../../../../hooks';
|
||||
import { useHasPermission, useMessageEvent, useNitroEvent, useRoom, useWiredTools } from '../../../../../hooks';
|
||||
import { NitroInput } from '../../../../../layout';
|
||||
|
||||
interface InfoStandWidgetFurniViewProps
|
||||
@@ -22,7 +22,7 @@ export const InfoStandWidgetFurniView: FC<InfoStandWidgetFurniViewProps> = props
|
||||
const { avatarInfo = null, onClose = null } = props;
|
||||
const { roomSession = null } = useRoom();
|
||||
const { openInspectionForFurni, showInspectButton } = useWiredTools();
|
||||
const isModerator = useHasRankLevel(STAFF_LEVELS.MOD);
|
||||
const isModerator = useHasPermission('acc_anyroomowner');
|
||||
|
||||
const [ pickupMode, setPickupMode ] = useState(0);
|
||||
const [ canMove, setCanMove ] = useState(false);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { PetRespectComposer, PetType, RoomControllerLevel, RoomObjectCategory, RoomObjectType, RoomObjectVariable, RoomUnitGiveHandItemPetComposer } from '@nitrots/nitro-renderer';
|
||||
import { FC, useEffect, useMemo, useState } from 'react';
|
||||
import { AvatarInfoPet, GetOwnRoomObject, LocalizeText, SendMessageComposer, STAFF_LEVELS } from '../../../../../api';
|
||||
import { useHasRankLevel, useRoom, useSessionInfo } from '../../../../../hooks';
|
||||
import { AvatarInfoPet, GetOwnRoomObject, LocalizeText, SendMessageComposer } from '../../../../../api';
|
||||
import { useHasPermission, useRoom, useSessionInfo } from '../../../../../hooks';
|
||||
import { ContextMenuHeaderView } from '../../context-menu/ContextMenuHeaderView';
|
||||
import { ContextMenuListItemView } from '../../context-menu/ContextMenuListItemView';
|
||||
import { ContextMenuView } from '../../context-menu/ContextMenuView';
|
||||
@@ -23,12 +23,12 @@ export const AvatarInfoWidgetPetView: FC<AvatarInfoWidgetPetViewProps> = props =
|
||||
const [ mode, setMode ] = useState(MODE_NORMAL);
|
||||
const { roomSession = null, isHandItemBlocked = false } = useRoom();
|
||||
const { petRespectRemaining = 0, respectPet = null } = useSessionInfo();
|
||||
const isModerator = useHasRankLevel(STAFF_LEVELS.MOD);
|
||||
const canManageAnyRoom = useHasPermission('acc_anyroomowner');
|
||||
|
||||
const canPickUp = useMemo(() =>
|
||||
{
|
||||
return (roomSession.isRoomOwner || (roomSession.controllerLevel >= RoomControllerLevel.GUEST) || isModerator);
|
||||
}, [ roomSession, isModerator ]);
|
||||
return (roomSession.isRoomOwner || (roomSession.controllerLevel >= RoomControllerLevel.GUEST) || canManageAnyRoom);
|
||||
}, [ roomSession, canManageAnyRoom ]);
|
||||
|
||||
const canGiveHandItem = useMemo(() =>
|
||||
{
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { FurniturePickupAllComposer } from '@nitrots/nitro-renderer';
|
||||
import { FC, useEffect, useEffectEvent, useMemo, useState } from 'react';
|
||||
import { LocalizeText, RoomObjectItem, SendMessageComposer, STAFF_LEVELS, chooserSelectionVisualizer } from '../../../../api';
|
||||
import { LocalizeText, RoomObjectItem, SendMessageComposer, chooserSelectionVisualizer } from '../../../../api';
|
||||
import { Button, Flex, InfiniteScroll, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../../common';
|
||||
import { useHasRankLevel } from '../../../../hooks';
|
||||
import { useHasPermission } from '../../../../hooks';
|
||||
import { NitroInput, classNames } from '../../../../layout';
|
||||
|
||||
const LIMIT_FURNI_PICKALL = 100;
|
||||
@@ -24,7 +24,7 @@ export const ChooserWidgetView: FC<ChooserWidgetViewProps> = props =>
|
||||
const [ searchValue, setSearchValue ] = useState('');
|
||||
const [ checkAll, setCheckAll ] = useState(false);
|
||||
const [ checkedIds, setCheckedIds ] = useState<number[]>([]);
|
||||
const canSeeId = useHasRankLevel(STAFF_LEVELS.MOD);
|
||||
const canSeeId = useHasPermission('acc_supporttool');
|
||||
|
||||
const ownerNames = useMemo(() =>
|
||||
{
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { GetAvatarRenderManager, GetSessionDataManager, HabboClubLevelEnum, RoomControllerLevel } from '@nitrots/nitro-renderer';
|
||||
import { FC, useEffect, useState } from 'react';
|
||||
import { GetClubMemberLevel, GetRoomSession, LocalizeText, MannequinUtilities, STAFF_LEVELS } from '../../../../api';
|
||||
import { GetClubMemberLevel, GetRoomSession, LocalizeText, MannequinUtilities } from '../../../../api';
|
||||
import { Button, Column, LayoutAvatarImageView, LayoutCurrencyIcon, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../../common';
|
||||
import { useFurnitureMannequinWidget, useHasRankLevel } from '../../../../hooks';
|
||||
import { useFurnitureMannequinWidget, useHasPermission } from '../../../../hooks';
|
||||
import { NitroInput } from '../../../../layout';
|
||||
|
||||
const MODE_NONE: number = -1;
|
||||
@@ -17,7 +17,7 @@ export const FurnitureMannequinView: FC<{}> = props =>
|
||||
const [ renderedFigure, setRenderedFigure ] = useState<string>(null);
|
||||
const [ mode, setMode ] = useState(MODE_NONE);
|
||||
const { objectId = -1, figure = null, gender = null, clubLevel = HabboClubLevelEnum.NO_CLUB, name = null, setName = null, saveFigure = null, wearFigure = null, saveName = null, onClose = null } = useFurnitureMannequinWidget();
|
||||
const isModerator = useHasRankLevel(STAFF_LEVELS.MOD);
|
||||
const canManageAnyRoom = useHasPermission('acc_anyroomowner');
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
@@ -25,7 +25,7 @@ export const FurnitureMannequinView: FC<{}> = props =>
|
||||
|
||||
const roomSession = GetRoomSession();
|
||||
|
||||
if(roomSession.isRoomOwner || (roomSession.controllerLevel >= RoomControllerLevel.GUEST) || isModerator)
|
||||
if(roomSession.isRoomOwner || (roomSession.controllerLevel >= RoomControllerLevel.GUEST) || canManageAnyRoom)
|
||||
{
|
||||
setMode(MODE_CONTROLLER);
|
||||
|
||||
@@ -47,7 +47,7 @@ export const FurnitureMannequinView: FC<{}> = props =>
|
||||
}
|
||||
|
||||
setMode(MODE_PEER);
|
||||
}, [ objectId, gender, clubLevel, isModerator ]);
|
||||
}, [ objectId, gender, clubLevel, canManageAnyRoom ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user