Update widgets

This commit is contained in:
Bill 2022-04-17 20:46:39 -04:00
parent 61022c16ef
commit e564cac2fc
38 changed files with 702 additions and 1802 deletions

View File

@ -15,7 +15,7 @@
}
.scale-1-50 {
transform: scale(1.50) translateZ(0);
transform: scale(1.5) translateZ(0);
}
.scale-2 {
@ -35,7 +35,6 @@
}
ul {
&.columns-3 {
display: block;
columns: 3;
@ -92,7 +91,6 @@ ul {
}
.striped-children {
> :nth-child(1) {
background-color: $table-striped-bg;
}
@ -100,21 +98,25 @@ ul {
@keyframes bounceIn {
0% {
opacity: 0;
transform: scale(.3);
opacity: 0;
transform: scale(0.3);
}
50% {
opacity: 1;
transform: scale(1.1);
opacity: 1;
transform: scale(1.1);
}
70% {
transform: scale(.9);
transform: scale(0.9);
}
100% {
transform: scale(1);
transform: scale(1);
}
}
.btn {
pointer-events: all;
}
.no-resize {
resize: none !important;
}

View File

@ -6,6 +6,7 @@ export interface BaseProps<T = HTMLElement> extends DetailedHTMLProps<HTMLAttrib
innerRef?: MutableRefObject<T>;
display?: DisplayType;
fit?: boolean;
fitV?: boolean;
grow?: boolean;
shrink?: boolean;
fullWidth?: boolean;
@ -21,7 +22,7 @@ export interface BaseProps<T = HTMLElement> extends DetailedHTMLProps<HTMLAttrib
export const Base: FC<BaseProps<HTMLDivElement>> = props =>
{
const { ref = null, innerRef = null, display = null, fit = false, grow = false, shrink = false, fullWidth = false, fullHeight = false, overflow = null, position = null, float = null, pointer = false, visible = null, textColor = null, classNames = [], className = '', style = {}, ...rest } = props;
const { ref = null, innerRef = null, display = null, fit = false, fitV = false, grow = false, shrink = false, fullWidth = false, fullHeight = false, overflow = null, position = null, float = null, pointer = false, visible = null, textColor = null, classNames = [], className = '', style = {}, ...rest } = props;
const getClassNames = useMemo(() =>
{
@ -33,6 +34,8 @@ export const Base: FC<BaseProps<HTMLDivElement>> = props =>
if(fit || fullHeight) newClassNames.push('h-100');
if(fitV) newClassNames.push('vw-100', 'vh-100');
if(grow) newClassNames.push('flex-grow-1');
if(shrink) newClassNames.push('flex-shrink-0');
@ -52,7 +55,7 @@ export const Base: FC<BaseProps<HTMLDivElement>> = props =>
if(classNames.length) newClassNames.push(...classNames);
return newClassNames;
}, [ display, fit, grow, shrink, fullWidth, fullHeight, overflow, position, float, pointer, visible, textColor, classNames ]);
}, [ display, fit, fitV, grow, shrink, fullWidth, fullHeight, overflow, position, float, pointer, visible, textColor, classNames ]);
const getClassName = useMemo(() =>
{

View File

@ -7,7 +7,7 @@ export interface LayoutImageProps extends DetailedHTMLProps<HTMLAttributes<HTMLI
export const LayoutImage: FC<LayoutImageProps> = props =>
{
const { imageUrl = null, ...rest } = props;
const { imageUrl = null, className = '', ...rest } = props;
return <img src={ imageUrl } alt="" { ...rest } />;
return <img src={ imageUrl } className={ 'no-select ' + className } alt="" { ...rest } />;
}

View File

@ -1,150 +1,16 @@
import { EventDispatcher, IRoomSession, RoomEngineEvent, RoomGeometry, RoomId, RoomSessionEvent, RoomVariableEnum, Vector3d } from '@nitrots/nitro-renderer';
import { FC, useCallback, useEffect, useRef, useState } from 'react';
import { DispatchMouseEvent, DispatchTouchEvent, FurniChooserWidgetHandler, FurnitureContextMenuWidgetHandler, FurnitureCreditWidgetHandler, FurnitureCustomStackHeightWidgetHandler, FurnitureDimmerWidgetHandler, FurnitureExternalImageWidgetHandler, FurnitureInternalLinkHandler, FurnitureMannequinWidgetHandler, FurniturePresentWidgetHandler, FurnitureRoomLinkHandler, FurnitureYoutubeDisplayWidgetHandler, GetNitroInstance, GetRoomEngine, GetRoomSession, InitializeRoomInstanceRenderingCanvas, IRoomWidgetHandlerManager, PollWidgetHandler, RoomWidgetAvatarInfoHandler, RoomWidgetChatHandler, RoomWidgetChatInputHandler, RoomWidgetHandlerManager, RoomWidgetInfostandHandler, SetActiveRoomId, StartRoomSession, UserChooserWidgetHandler, WordQuizWidgetHandler } from '../../api';
import { FC, useEffect, useRef } from 'react';
import { DispatchMouseEvent, DispatchTouchEvent, GetNitroInstance } from '../../api';
import { Base } from '../../common';
import { UseRoomEngineEvent, UseRoomSessionManagerEvent } from '../../hooks';
import { useRoom } from '../../hooks';
import { RoomColorView } from './RoomColorView';
import { RoomContextProvider } from './RoomContext';
import { RoomWidgetsView } from './widgets/RoomWidgetsView';
export const RoomView: FC<{}> = props =>
{
const [ roomSession, setRoomSession ] = useState<IRoomSession>(null);
const [ widgetHandler, setWidgetHandler ] = useState<IRoomWidgetHandlerManager>(null);
const { roomSession = null, widgetHandler = null, resize = null } = useRoom();
const elementRef = useRef<HTMLDivElement>();
const onRoomEngineEvent = useCallback((event: RoomEngineEvent) =>
{
if(RoomId.isRoomPreviewerId(event.roomId)) return;
const session = GetRoomSession();
if(!session) return;
switch(event.type)
{
case RoomEngineEvent.INITIALIZED:
SetActiveRoomId(event.roomId);
setRoomSession(session);
return;
case RoomEngineEvent.DISPOSED:
setRoomSession(null);
return;
}
}, []);
UseRoomEngineEvent(RoomEngineEvent.INITIALIZED, onRoomEngineEvent);
UseRoomEngineEvent(RoomEngineEvent.DISPOSED, onRoomEngineEvent);
const onRoomSessionEvent = useCallback((event: RoomSessionEvent) =>
{
switch(event.type)
{
case RoomSessionEvent.CREATED:
StartRoomSession(event.session);
return;
case RoomSessionEvent.ENDED:
setRoomSession(null);
return;
}
}, []);
UseRoomSessionManagerEvent(RoomSessionEvent.CREATED, onRoomSessionEvent);
UseRoomSessionManagerEvent(RoomSessionEvent.ENDED, onRoomSessionEvent);
const resize = useCallback((event: UIEvent = null) =>
{
const canvas = GetNitroInstance().renderer.view;
if(!canvas) return;
canvas.style.width = `${ Math.floor(window.innerWidth) }px`;
canvas.style.height = `${ Math.floor(window.innerHeight) }px`;
const nitroInstance = GetNitroInstance();
nitroInstance.renderer.resolution = window.devicePixelRatio;
nitroInstance.renderer.resize(window.innerWidth, window.innerHeight);
InitializeRoomInstanceRenderingCanvas(window.innerWidth, window.innerHeight, 1);
nitroInstance.render();
}, []);
useEffect(() =>
{
if(!roomSession)
{
setWidgetHandler(null);
return;
}
const widgetHandlerManager = new RoomWidgetHandlerManager(roomSession, new EventDispatcher());
widgetHandlerManager.registerHandler(new RoomWidgetAvatarInfoHandler());
widgetHandlerManager.registerHandler(new RoomWidgetInfostandHandler());
widgetHandlerManager.registerHandler(new RoomWidgetChatInputHandler());
widgetHandlerManager.registerHandler(new RoomWidgetChatHandler());
widgetHandlerManager.registerHandler(new UserChooserWidgetHandler());
widgetHandlerManager.registerHandler(new WordQuizWidgetHandler());
widgetHandlerManager.registerHandler(new PollWidgetHandler());
widgetHandlerManager.registerHandler(new FurniChooserWidgetHandler());
widgetHandlerManager.registerHandler(new FurnitureContextMenuWidgetHandler());
widgetHandlerManager.registerHandler(new FurnitureCreditWidgetHandler());
widgetHandlerManager.registerHandler(new FurnitureCustomStackHeightWidgetHandler());
widgetHandlerManager.registerHandler(new FurnitureExternalImageWidgetHandler());
widgetHandlerManager.registerHandler(new FurniturePresentWidgetHandler());
widgetHandlerManager.registerHandler(new FurnitureDimmerWidgetHandler());
widgetHandlerManager.registerHandler(new FurnitureYoutubeDisplayWidgetHandler());
widgetHandlerManager.registerHandler(new FurnitureMannequinWidgetHandler());
widgetHandlerManager.registerHandler(new FurnitureInternalLinkHandler());
widgetHandlerManager.registerHandler(new FurnitureRoomLinkHandler());
setWidgetHandler(widgetHandlerManager);
const roomEngine = GetRoomEngine();
const roomId = roomSession.roomId;
const canvasId = 1;
resize();
const displayObject = roomEngine.getRoomInstanceDisplay(roomId, canvasId, window.innerWidth, window.innerHeight, RoomGeometry.SCALE_ZOOMED_IN);
if(!displayObject) return;
const geometry = (roomEngine.getRoomInstanceGeometry(roomId, canvasId) as RoomGeometry);
if(geometry)
{
const minX = (roomEngine.getRoomInstanceVariable<number>(roomId, RoomVariableEnum.ROOM_MIN_X) || 0);
const maxX = (roomEngine.getRoomInstanceVariable<number>(roomId, RoomVariableEnum.ROOM_MAX_X) || 0);
const minY = (roomEngine.getRoomInstanceVariable<number>(roomId, RoomVariableEnum.ROOM_MIN_Y) || 0);
const maxY = (roomEngine.getRoomInstanceVariable<number>(roomId, RoomVariableEnum.ROOM_MAX_Y) || 0);
let x = ((minX + maxX) / 2);
let y = ((minY + maxY) / 2);
const offset = 20;
x = (x + (offset - 1));
y = (y + (offset - 1));
const z = (Math.sqrt(((offset * offset) + (offset * offset))) * Math.tan(((30 / 180) * Math.PI)));
geometry.location = new Vector3d(x, y, z);
}
const stage = GetNitroInstance().stage;
if(!stage) return;
stage.addChild(displayObject);
SetActiveRoomId(roomSession.roomId);
}, [ roomSession, resize ]);
useEffect(() =>
{
const canvas = GetNitroInstance().renderer.view;

View File

@ -1,7 +1,7 @@
import { RoomEngineEvent, RoomEngineObjectEvent, RoomEngineRoomAdEvent, RoomEngineTriggerWidgetEvent, RoomEngineUseProductEvent, RoomId, RoomObjectCategory, RoomObjectOperationType, RoomObjectVariable, RoomSessionChatEvent, RoomSessionDanceEvent, RoomSessionDimmerPresetsEvent, RoomSessionDoorbellEvent, RoomSessionErrorMessageEvent, RoomSessionEvent, RoomSessionFavoriteGroupUpdateEvent, RoomSessionPetInfoUpdateEvent, RoomSessionPetStatusUpdateEvent, RoomSessionPollEvent, RoomSessionPresentEvent, RoomSessionUserBadgesEvent, RoomSessionUserFigureUpdateEvent, RoomSessionWordQuizEvent, RoomZoomEvent } from '@nitrots/nitro-renderer';
import { RoomEngineEvent, RoomEngineObjectEvent, RoomEngineRoomAdEvent, RoomEngineTriggerWidgetEvent, RoomEngineUseProductEvent, RoomId, RoomObjectVariable, RoomSessionChatEvent, RoomSessionDanceEvent, RoomSessionDimmerPresetsEvent, RoomSessionErrorMessageEvent, RoomSessionEvent, RoomSessionFavoriteGroupUpdateEvent, RoomSessionPetInfoUpdateEvent, RoomSessionPetStatusUpdateEvent, RoomSessionPollEvent, RoomSessionUserBadgesEvent, RoomSessionUserFigureUpdateEvent, RoomSessionWordQuizEvent, RoomZoomEvent } from '@nitrots/nitro-renderer';
import { FC, useCallback } from 'react';
import { CanManipulateFurniture, GetRoomEngine, GetSessionDataManager, IsFurnitureSelectionDisabled, LocalizeText, NotificationAlertType, NotificationUtilities, ProcessRoomObjectOperation, RoomWidgetFurniToWidgetMessage, RoomWidgetUpdateRoomEngineEvent, RoomWidgetUpdateRoomObjectEvent } from '../../../api';
import { UseRoomEngineEvent, UseRoomSessionManagerEvent } from '../../../hooks';
import { GetRoomEngine, GetSessionDataManager, LocalizeText, NotificationAlertType, NotificationUtilities, RoomWidgetFurniToWidgetMessage, RoomWidgetUpdateRoomEngineEvent, RoomWidgetUpdateRoomObjectEvent } from '../../../api';
import { DispatchUiEvent, UseRoomEngineEvent, UseRoomSessionManagerEvent } from '../../../hooks';
import { useRoomContext } from '../RoomContext';
import { AvatarInfoWidgetView } from './avatar-info/AvatarInfoWidgetView';
import { ChatInputView } from './chat-input/ChatInputView';
@ -67,73 +67,6 @@ export const RoomWidgetsView: FC<{}> = props =>
switch(event.type)
{
case RoomEngineObjectEvent.SELECTED:
if(!IsFurnitureSelectionDisabled(event)) updateEvent = new RoomWidgetUpdateRoomObjectEvent(RoomWidgetUpdateRoomObjectEvent.OBJECT_SELECTED, objectId, category, event.roomId);
break;
case RoomEngineObjectEvent.DESELECTED:
updateEvent = new RoomWidgetUpdateRoomObjectEvent(RoomWidgetUpdateRoomObjectEvent.OBJECT_DESELECTED, objectId, category, event.roomId);
break;
case RoomEngineObjectEvent.ADDED: {
let addedEventType: string = null;
switch(category)
{
case RoomObjectCategory.FLOOR:
case RoomObjectCategory.WALL:
addedEventType = RoomWidgetUpdateRoomObjectEvent.FURNI_ADDED;
break;
case RoomObjectCategory.UNIT:
addedEventType = RoomWidgetUpdateRoomObjectEvent.USER_ADDED;
break;
}
if(addedEventType) updateEvent = new RoomWidgetUpdateRoomObjectEvent(addedEventType, objectId, category, event.roomId);
break;
}
case RoomEngineObjectEvent.REMOVED: {
let removedEventType: string = null;
switch(category)
{
case RoomObjectCategory.FLOOR:
case RoomObjectCategory.WALL:
removedEventType = RoomWidgetUpdateRoomObjectEvent.FURNI_REMOVED;
break;
case RoomObjectCategory.UNIT:
removedEventType = RoomWidgetUpdateRoomObjectEvent.USER_REMOVED;
break;
}
if(removedEventType) updateEvent = new RoomWidgetUpdateRoomObjectEvent(removedEventType, objectId, category, event.roomId);
break;
}
case RoomEngineObjectEvent.REQUEST_MOVE:
if(CanManipulateFurniture(roomSession, objectId, category)) ProcessRoomObjectOperation(objectId, category, RoomObjectOperationType.OBJECT_MOVE);
break;
case RoomEngineObjectEvent.REQUEST_ROTATE:
if(CanManipulateFurniture(roomSession, objectId, category)) ProcessRoomObjectOperation(objectId, category, RoomObjectOperationType.OBJECT_ROTATE_POSITIVE);
break;
case RoomEngineObjectEvent.REQUEST_MANIPULATION:
if(CanManipulateFurniture(roomSession, objectId, category)) updateEvent = new RoomWidgetUpdateRoomObjectEvent(RoomWidgetUpdateRoomObjectEvent.OBJECT_REQUEST_MANIPULATION, objectId, category, event.roomId);
break;
case RoomEngineObjectEvent.MOUSE_ENTER:
updateEvent = new RoomWidgetUpdateRoomObjectEvent(RoomWidgetUpdateRoomObjectEvent.OBJECT_ROLL_OVER, objectId, category, event.roomId);
break;
case RoomEngineObjectEvent.MOUSE_LEAVE:
updateEvent = new RoomWidgetUpdateRoomObjectEvent(RoomWidgetUpdateRoomObjectEvent.OBJECT_ROLL_OUT, objectId, category, event.roomId);
break;
case RoomEngineTriggerWidgetEvent.REQUEST_CREDITFURNI:
widgetHandler.processWidgetMessage(new RoomWidgetFurniToWidgetMessage(RoomWidgetFurniToWidgetMessage.REQUEST_CREDITFURNI, objectId, category, event.roomId));
break;
case RoomEngineTriggerWidgetEvent.REQUEST_STICKIE:
widgetHandler.processWidgetMessage(new RoomWidgetFurniToWidgetMessage(RoomWidgetFurniToWidgetMessage.REQUEST_STICKIE, objectId, category, event.roomId));
break;
case RoomEngineTriggerWidgetEvent.REQUEST_PRESENT:
widgetHandler.processWidgetMessage(new RoomWidgetFurniToWidgetMessage(RoomWidgetFurniToWidgetMessage.REQUEST_PRESENT, objectId, category, event.roomId));
break;
case RoomEngineTriggerWidgetEvent.REQUEST_TROPHY:
widgetHandler.processWidgetMessage(new RoomWidgetFurniToWidgetMessage(RoomWidgetFurniToWidgetMessage.REQUEST_TROPHY, objectId, category, event.roomId));
break;
case RoomEngineTriggerWidgetEvent.REQUEST_TEASER:
widgetHandler.processWidgetMessage(new RoomWidgetFurniToWidgetMessage(RoomWidgetFurniToWidgetMessage.REQUEST_TEASER, objectId, category, event.roomId));
break;
@ -173,7 +106,6 @@ export const RoomWidgetsView: FC<{}> = props =>
case RoomEngineTriggerWidgetEvent.OPEN_FURNI_CONTEXT_MENU:
case RoomEngineTriggerWidgetEvent.CLOSE_FURNI_CONTEXT_MENU:
case RoomEngineTriggerWidgetEvent.REMOVE_DIMMER:
case RoomEngineTriggerWidgetEvent.REQUEST_MANNEQUIN:
case RoomEngineUseProductEvent.USE_PRODUCT_FROM_INVENTORY:
case RoomEngineUseProductEvent.USE_PRODUCT_FROM_ROOM:
case RoomEngineTriggerWidgetEvent.REQUEST_BACKGROUND_COLOR:
@ -200,24 +132,15 @@ export const RoomWidgetsView: FC<{}> = props =>
if(updateEvent instanceof RoomWidgetUpdateRoomObjectEvent) dispatchEvent = (!RoomId.isRoomPreviewerId(updateEvent.roomId));
if(dispatchEvent) widgetHandler.eventDispatcher.dispatchEvent(updateEvent);
if(dispatchEvent)
{
widgetHandler.eventDispatcher.dispatchEvent(updateEvent);
DispatchUiEvent(updateEvent);
}
}
}, [ roomSession, widgetHandler, handleRoomAdClick, handleRoomAdTooltip ]);
UseRoomEngineEvent(RoomEngineObjectEvent.SELECTED, onRoomEngineObjectEvent);
UseRoomEngineEvent(RoomEngineObjectEvent.DESELECTED, onRoomEngineObjectEvent);
UseRoomEngineEvent(RoomEngineObjectEvent.ADDED, onRoomEngineObjectEvent);
UseRoomEngineEvent(RoomEngineObjectEvent.REMOVED, onRoomEngineObjectEvent);
UseRoomEngineEvent(RoomEngineObjectEvent.PLACED, onRoomEngineObjectEvent);
UseRoomEngineEvent(RoomEngineObjectEvent.REQUEST_MOVE, onRoomEngineObjectEvent);
UseRoomEngineEvent(RoomEngineObjectEvent.REQUEST_ROTATE, onRoomEngineObjectEvent);
UseRoomEngineEvent(RoomEngineObjectEvent.REQUEST_MANIPULATION, onRoomEngineObjectEvent);
UseRoomEngineEvent(RoomEngineObjectEvent.MOUSE_ENTER, onRoomEngineObjectEvent);
UseRoomEngineEvent(RoomEngineObjectEvent.MOUSE_LEAVE, onRoomEngineObjectEvent);
UseRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_CREDITFURNI, onRoomEngineObjectEvent);
UseRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_STICKIE, onRoomEngineObjectEvent);
UseRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_PRESENT, onRoomEngineObjectEvent);
UseRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_TROPHY, onRoomEngineObjectEvent);
UseRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_TEASER, onRoomEngineObjectEvent);
UseRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_ECOTRONBOX, onRoomEngineObjectEvent);
UseRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_DIMMER, onRoomEngineObjectEvent);
@ -232,7 +155,6 @@ export const RoomWidgetsView: FC<{}> = props =>
UseRoomEngineEvent(RoomEngineTriggerWidgetEvent.OPEN_FURNI_CONTEXT_MENU, onRoomEngineObjectEvent);
UseRoomEngineEvent(RoomEngineTriggerWidgetEvent.CLOSE_FURNI_CONTEXT_MENU, onRoomEngineObjectEvent);
UseRoomEngineEvent(RoomEngineTriggerWidgetEvent.REMOVE_DIMMER, onRoomEngineObjectEvent);
UseRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_MANNEQUIN, onRoomEngineObjectEvent);
UseRoomEngineEvent(RoomEngineUseProductEvent.USE_PRODUCT_FROM_INVENTORY, onRoomEngineObjectEvent);
UseRoomEngineEvent(RoomEngineUseProductEvent.USE_PRODUCT_FROM_ROOM, onRoomEngineObjectEvent);
UseRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_BACKGROUND_COLOR, onRoomEngineObjectEvent);
@ -260,11 +182,7 @@ export const RoomWidgetsView: FC<{}> = props =>
UseRoomSessionManagerEvent(RoomSessionUserFigureUpdateEvent.USER_FIGURE, onRoomSessionEvent);
UseRoomSessionManagerEvent(RoomSessionFavoriteGroupUpdateEvent.FAVOURITE_GROUP_UPDATE, onRoomSessionEvent);
UseRoomSessionManagerEvent(RoomSessionPetStatusUpdateEvent.PET_STATUS_UPDATE, onRoomSessionEvent);
UseRoomSessionManagerEvent(RoomSessionDoorbellEvent.DOORBELL, onRoomSessionEvent);
UseRoomSessionManagerEvent(RoomSessionDoorbellEvent.RSDE_REJECTED, onRoomSessionEvent);
UseRoomSessionManagerEvent(RoomSessionDoorbellEvent.RSDE_ACCEPTED, onRoomSessionEvent);
UseRoomSessionManagerEvent(RoomSessionDimmerPresetsEvent.ROOM_DIMMER_PRESETS, onRoomSessionEvent);
UseRoomSessionManagerEvent(RoomSessionPresentEvent.RSPE_PRESENT_OPENED, onRoomSessionEvent);
UseRoomSessionManagerEvent(RoomSessionPetInfoUpdateEvent.PET_INFO, onRoomSessionEvent);
UseRoomSessionManagerEvent(RoomSessionWordQuizEvent.ANSWERED, onRoomSessionEvent);
UseRoomSessionManagerEvent(RoomSessionWordQuizEvent.FINISHED, onRoomSessionEvent);

View File

@ -1,57 +1,52 @@
import { FC, useCallback, useMemo, useState } from 'react';
import { FC, useEffect, useMemo, useState } from 'react';
import { AutoSizer, List, ListRowProps, ListRowRenderer } from 'react-virtualized';
import { RoomObjectItem, RoomWidgetRoomObjectMessage } from '../../../../api';
import { GetSessionDataManager, RoomObjectItem } from '../../../../api';
import { LocalizeText } from '../../../../api/utils';
import { Column, Flex, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../../common';
import { useRoomContext } from '../../RoomContext';
interface ChooserWidgetViewProps
{
title: string;
items: RoomObjectItem[];
displayItemId: boolean;
onCloseClick: () => void;
selectItem: (item: RoomObjectItem) => void;
close: () => void;
}
export const ChooserWidgetView: FC<ChooserWidgetViewProps> = props =>
{
const { title = null, items = null, displayItemId = false, onCloseClick = null } = props;
const { title = null, items = [], selectItem = null, close = null } = props;
const [ selectedItem, setSelectedItem ] = useState<RoomObjectItem>(null);
const [ searchValue, setSearchValue ] = useState('');
const { widgetHandler = null } = useRoomContext();
const canSeeId = GetSessionDataManager().isModerator;
const filteredItems = useMemo(() =>
{
if(!items) return [];
if(!searchValue || !searchValue.length) return items;
const value = searchValue.toLocaleLowerCase();
return items.filter(item => item.name.toLocaleLowerCase().includes(value));
}, [ items, searchValue ]);
const onItemClick = useCallback((item: RoomObjectItem) =>
{
setSelectedItem(item);
widgetHandler.processWidgetMessage(new RoomWidgetRoomObjectMessage(RoomWidgetRoomObjectMessage.SELECT_OBJECT, item.id, item.category));
}, [ widgetHandler, setSelectedItem ]);
const rowRenderer: ListRowRenderer = (props: ListRowProps) =>
{
const item = filteredItems[props.index];
return (
<Flex key={ props.key } alignItems="center" position="absolute" className={ 'rounded px-1' + ((selectedItem === item) ? ' bg-muted' : '') } pointer style={ props.style } onClick={ event => onItemClick(item) }>
<Text truncate>{ item.name } { displayItemId && (' - ' + item.id) }</Text>
<Flex key={ props.key } alignItems="center" position="absolute" className={ 'rounded px-1' + ((selectedItem === item) ? ' bg-muted' : '') } pointer style={ props.style } onClick={ event => setSelectedItem(item) }>
<Text truncate>{ item.name } { canSeeId && (' - ' + item.id) }</Text>
</Flex>
);
}
useEffect(() =>
{
if(!selectedItem) return;
selectItem(selectedItem);
}, [ selectedItem, selectItem ]);
return (
<NitroCardView className="nitro-chooser-widget" theme="primary-slim">
<NitroCardHeaderView headerText={ title } onCloseClick={ onCloseClick } />
<NitroCardHeaderView headerText={ title } onCloseClick={ close } />
<NitroCardContentView overflow="hidden">
<input type="text" className="form-control form-control-sm" placeholder={ LocalizeText('generic.search') } value={ searchValue } onChange={ event => setSearchValue(event.target.value) } />
<Column fullHeight overflow="auto">

View File

@ -1,60 +1,31 @@
import { SecurityLevel } from '@nitrots/nitro-renderer';
import { FC, useCallback, useState } from 'react';
import { GetSessionDataManager, LocalizeText, RoomObjectItem, RoomWidgetChooserContentEvent, RoomWidgetRequestWidgetMessage, RoomWidgetUpdateRoomObjectEvent } from '../../../../api';
import { UseEventDispatcherHook } from '../../../../hooks';
import { useRoomContext } from '../../RoomContext';
import { ILinkEventTracker } from '@nitrots/nitro-renderer';
import { FC, useEffect } from 'react';
import { AddEventLinkTracker, LocalizeText, RemoveLinkEventTracker } from '../../../../api';
import { useFurniChooserWidget } from '../../../../hooks';
import { ChooserWidgetView } from './ChooserWidgetView';
export const FurniChooserWidgetView: FC<{}> = props =>
{
const [ isVisible, setIsVisible ] = useState(false);
const [ items, setItems ] = useState<RoomObjectItem[]>(null);
const [ refreshTimeout, setRefreshTimeout ] = useState<ReturnType<typeof setTimeout>>(null);
const { eventDispatcher = null, widgetHandler = null } = useRoomContext();
const { items = null, close = null, selectItem = null, populateChooser = null } = useFurniChooserWidget();
const refreshChooser = useCallback(() =>
useEffect(() =>
{
if(!isVisible) return;
const linkTracker: ILinkEventTracker = {
linkReceived: (url: string) =>
{
const parts = url.split('/');
setRefreshTimeout(prevValue =>
{
if(prevValue) clearTimeout(prevValue);
populateChooser();
},
eventUrlPrefix: 'furni-chooser/'
};
return setTimeout(() => widgetHandler.processWidgetMessage(new RoomWidgetRequestWidgetMessage(RoomWidgetRequestWidgetMessage.FURNI_CHOOSER)), 100);
});
}, [ isVisible, widgetHandler ]);
AddEventLinkTracker(linkTracker);
const onRoomWidgetChooserContentEvent = useCallback((event: RoomWidgetChooserContentEvent) =>
{
setItems(event.items);
setIsVisible(true);
}, []);
UseEventDispatcherHook(RoomWidgetChooserContentEvent.FURNI_CHOOSER_CONTENT, eventDispatcher, onRoomWidgetChooserContentEvent);
const onRoomWidgetRoomObjectUpdateEvent = useCallback((event: RoomWidgetUpdateRoomObjectEvent) =>
{
if(!isVisible) return;
switch(event.type)
{
case RoomWidgetUpdateRoomObjectEvent.FURNI_ADDED:
case RoomWidgetUpdateRoomObjectEvent.FURNI_REMOVED:
refreshChooser();
return;
}
}, [ isVisible, refreshChooser ]);
UseEventDispatcherHook(RoomWidgetUpdateRoomObjectEvent.FURNI_ADDED, eventDispatcher, onRoomWidgetRoomObjectUpdateEvent);
UseEventDispatcherHook(RoomWidgetUpdateRoomObjectEvent.FURNI_REMOVED, eventDispatcher, onRoomWidgetRoomObjectUpdateEvent);
const close = useCallback(() =>
{
setIsVisible(false);
setItems(null);
}, []);
return () => RemoveLinkEventTracker(linkTracker);
}, [ populateChooser ]);
if(!items) return null;
return <ChooserWidgetView title={ LocalizeText('widget.chooser.furni.title') } displayItemId={ GetSessionDataManager().hasSecurity(SecurityLevel.MODERATOR) } items={ items } onCloseClick={ close } />;
return <ChooserWidgetView title={ LocalizeText('widget.chooser.furni.title') } items={ items } selectItem={ selectItem } close={ close } />;
}

View File

@ -1,59 +1,31 @@
import { FC, useCallback, useState } from 'react';
import { LocalizeText, RoomObjectItem, RoomWidgetChooserContentEvent, RoomWidgetRequestWidgetMessage, RoomWidgetUpdateRoomObjectEvent } from '../../../../api';
import { UseEventDispatcherHook } from '../../../../hooks';
import { useRoomContext } from '../../RoomContext';
import { ILinkEventTracker } from '@nitrots/nitro-renderer';
import { FC, useEffect } from 'react';
import { AddEventLinkTracker, LocalizeText, RemoveLinkEventTracker } from '../../../../api';
import { useUserChooserWidget } from '../../../../hooks';
import { ChooserWidgetView } from './ChooserWidgetView';
export const UserChooserWidgetView: FC<{}> = props =>
{
const [ isVisible, setIsVisible ] = useState(false);
const [ items, setItems ] = useState<RoomObjectItem[]>(null);
const [ refreshTimeout, setRefreshTimeout ] = useState<ReturnType<typeof setTimeout>>(null);
const { eventDispatcher = null, widgetHandler = null } = useRoomContext();
const { items = null, close = null, selectItem = null, populateChooser = null } = useUserChooserWidget();
const refreshChooser = useCallback(() =>
useEffect(() =>
{
if(!isVisible) return;
const linkTracker: ILinkEventTracker = {
linkReceived: (url: string) =>
{
const parts = url.split('/');
setRefreshTimeout(prevValue =>
{
if(prevValue) clearTimeout(prevValue);
populateChooser();
},
eventUrlPrefix: 'user-chooser/'
};
return setTimeout(() => widgetHandler.processWidgetMessage(new RoomWidgetRequestWidgetMessage(RoomWidgetRequestWidgetMessage.USER_CHOOSER)), 100);
})
}, [ isVisible, widgetHandler ]);
AddEventLinkTracker(linkTracker);
const onRoomWidgetChooserContentEvent = useCallback((event: RoomWidgetChooserContentEvent) =>
{
setItems(event.items);
setIsVisible(true);
}, []);
return () => RemoveLinkEventTracker(linkTracker);
}, [ populateChooser ]);
UseEventDispatcherHook(RoomWidgetChooserContentEvent.USER_CHOOSER_CONTENT, eventDispatcher, onRoomWidgetChooserContentEvent);
if(!items) return null;
const onRoomWidgetRoomObjectUpdateEvent = useCallback((event: RoomWidgetUpdateRoomObjectEvent) =>
{
if(!isVisible) return;
switch(event.type)
{
case RoomWidgetUpdateRoomObjectEvent.USER_ADDED:
case RoomWidgetUpdateRoomObjectEvent.USER_REMOVED:
refreshChooser();
return;
}
}, [ isVisible, refreshChooser ]);
UseEventDispatcherHook(RoomWidgetUpdateRoomObjectEvent.USER_ADDED, eventDispatcher, onRoomWidgetRoomObjectUpdateEvent);
UseEventDispatcherHook(RoomWidgetUpdateRoomObjectEvent.USER_REMOVED, eventDispatcher, onRoomWidgetRoomObjectUpdateEvent);
const close = useCallback(() =>
{
setIsVisible(false);
setItems(null);
}, []);
if(!isVisible) return null;
return <ChooserWidgetView title={ LocalizeText('widget.chooser.user.title') } displayItemId={ false } items={ items } onCloseClick={ close } />;
return <ChooserWidgetView title={ LocalizeText('widget.chooser.user.title') } items={ items } selectItem={ selectItem } close={ close } />;
}

View File

@ -3,13 +3,11 @@ import { RoomObjectCategory } from '@nitrots/nitro-renderer';
import { FC } from 'react';
import { LocalizeText, MessengerRequest } from '../../../../api';
import { Base, Button, Column, Flex, Text } from '../../../../common';
import { useFriends } from '../../../../hooks';
import { ObjectLocationView } from '../object-location/ObjectLocationView';
export const FriendRequestDialogView: FC<{ roomIndex: number, request: MessengerRequest, hideFriendRequest: (userId: number) => void }> = props =>
export const FriendRequestDialogView: FC<{ roomIndex: number, request: MessengerRequest, hideFriendRequest: (userId: number) => void, requestResponse: (requestId: number, flag: boolean) => void }> = props =>
{
const { roomIndex = -1, request = null, hideFriendRequest = null } = props;
const { requestResponse = null } = useFriends();
const { roomIndex = -1, request = null, hideFriendRequest = null, requestResponse = null } = props;
return (
<ObjectLocationView objectId={ roomIndex } category={ RoomObjectCategory.UNIT }>

View File

@ -1,100 +1,17 @@
import { RoomObjectCategory, RoomObjectUserType } from '@nitrots/nitro-renderer';
import { FC, useCallback, useEffect, useState } from 'react';
import { MessengerRequest, RoomWidgetUpdateRoomObjectEvent } from '../../../../api';
import { UseEventDispatcherHook, useFriends } from '../../../../hooks';
import { useRoomContext } from '../../RoomContext';
import { FC } from 'react';
import { useFriendRequestWidget, useFriends } from '../../../../hooks';
import { FriendRequestDialogView } from './FriendRequestDialogView';
export const FriendRequestWidgetView: FC<{}> = props =>
{
const [ displayedRequests, setDisplayedRequests ] = useState<{ roomIndex: number, request: MessengerRequest }[]>([]);
const [ dismissedRequestIds, setDismissedRequestIds ] = useState<number[]>([]);
const { roomSession = null, eventDispatcher = null } = useRoomContext();
const { requests = [] } = useFriends();
const { displayedRequests = [], hideFriendRequest = null } = useFriendRequestWidget();
const { requestResponse = null } = useFriends();
const hideFriendRequest = (userId: number) =>
{
setDismissedRequestIds(prevValue =>
{
if(prevValue.indexOf(userId) >= 0) return prevValue;
const newValue = [ ...prevValue ];
newValue.push(userId);
return newValue;
});
}
const onRoomWidgetUpdateRoomObjectEvent = useCallback((event: RoomWidgetUpdateRoomObjectEvent) =>
{
if(event.category !== RoomObjectCategory.UNIT) return;
const userData = roomSession.userDataManager.getUserDataByIndex(event.id);
if(userData && (userData.type === RoomObjectUserType.getTypeNumber(RoomObjectUserType.USER)))
{
if(event.type === RoomWidgetUpdateRoomObjectEvent.USER_ADDED)
{
const request = requests.find(request => (request.requesterUserId === userData.webID));
if(!request || displayedRequests.find(request => (request.request.requesterUserId === userData.webID))) return;
const newValue = [ ...displayedRequests ];
newValue.push({ roomIndex: userData.roomIndex, request });
setDisplayedRequests(newValue);
}
return;
}
if(event.type === RoomWidgetUpdateRoomObjectEvent.USER_REMOVED)
{
const index = displayedRequests.findIndex(request => (request.roomIndex === event.id));
if(index === -1) return;
const newValue = [ ...displayedRequests ];
newValue.splice(index, 1);
setDisplayedRequests(newValue);
}
}, [ roomSession, requests, displayedRequests ]);
UseEventDispatcherHook(RoomWidgetUpdateRoomObjectEvent.USER_ADDED, eventDispatcher, onRoomWidgetUpdateRoomObjectEvent);
UseEventDispatcherHook(RoomWidgetUpdateRoomObjectEvent.USER_REMOVED, eventDispatcher, onRoomWidgetUpdateRoomObjectEvent);
useEffect(() =>
{
if(!requests || !requests.length) return;
const newDisplayedRequests: { roomIndex: number, request: MessengerRequest }[] = [];
for(const request of requests)
{
const userData = roomSession.userDataManager.getUserData(request.requesterUserId);
if(!userData) continue;
newDisplayedRequests.push({ roomIndex: userData.roomIndex, request });
}
setDisplayedRequests(newDisplayedRequests);
}, [ roomSession, requests ]);
if(!requests.length) return null;
if(!displayedRequests.length) return null;
return (
<>
{ displayedRequests.map((request, index) =>
{
if(dismissedRequestIds.indexOf(request.request.requesterUserId) >= 0) return null;
return <FriendRequestDialogView key={ index } roomIndex={ request.roomIndex } request={ request.request } hideFriendRequest={ hideFriendRequest } />;
}) }
{ displayedRequests.map((request, index) => <FriendRequestDialogView key={ index } roomIndex={ request.roomIndex } request={ request.request } hideFriendRequest={ hideFriendRequest } requestResponse={ requestResponse } />) }
</>
);
}

View File

@ -0,0 +1,63 @@
import { FC } from 'react';
import ReactSlider from 'react-slider';
import { LocalizeText } from '../../../../api';
import { Button, Column, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../../common';
import { useFurnitureBackgroundColorWidget } from '../../../../hooks';
export const FurnitureBackgroundColorView: FC<{}> = props =>
{
const { objectId = -1, hue = 0, setHue = null, saturation = 0, setSaturation = null, lightness = 0, setLightness = null, applyToner = null, toggleToner = null, close = null } = useFurnitureBackgroundColorWidget();
if(objectId === -1) return null;
return (
<NitroCardView theme="primary-slim" className="nitro-room-widget-toner">
<NitroCardHeaderView headerText={ LocalizeText('widget.backgroundcolor.title') } onCloseClick={ close } />
<NitroCardContentView overflow="hidden" justifyContent="between">
<Column overflow="auto" gap={ 1 }>
<Column>
<Text bold>{ LocalizeText('widget.backgroundcolor.hue') }</Text>
<ReactSlider
className={ 'nitro-slider' }
min={ 0 }
max={ 360 }
value={ hue }
onChange={ event => setHue(event) }
thumbClassName={ 'thumb degree' }
renderThumb={ (props, state) => <div { ...props }>{ state.valueNow }</div> } />
</Column>
<Column>
<Text bold>{ LocalizeText('widget.backgroundcolor.saturation') }</Text>
<ReactSlider
className={ 'nitro-slider' }
min={ 0 }
max={ 100 }
value={ saturation }
onChange={ event => setSaturation(event) }
thumbClassName={ 'thumb percent' }
renderThumb={ (props, state) => <div { ...props }>{ state.valueNow }</div> } />
</Column>
<Column>
<Text bold>{ LocalizeText('widget.backgroundcolor.lightness') }</Text>
<ReactSlider
className={ 'nitro-slider' }
min={ 0 }
max={ 100 }
value={ lightness }
onChange={ event => setLightness(event) }
thumbClassName={ 'thumb percent' }
renderThumb={ (props, state) => <div { ...props }>{ state.valueNow }</div> } />
</Column>
</Column>
<Column gap={ 1 }>
<Button fullWidth variant="primary" onClick={ toggleToner }>
{ LocalizeText('widget.backgroundcolor.button.on') }
</Button>
<Button fullWidth variant="primary" onClick={ applyToner }>
{ LocalizeText('widget.backgroundcolor.button.apply') }
</Button>
</Column>
</NitroCardContentView>
</NitroCardView>
);
}

View File

@ -0,0 +1,12 @@
import { FC } from 'react';
import { LayoutTrophyView } from '../../../../common';
import { useFurnitureBadgeDisplayWidget } from '../../../../hooks';
export const FurnitureBadgeDisplayView: FC<{}> = props =>
{
const { objectId = -1, color = '1', badgeName = '', badgeDesc = '', date = '', senderName = '', close = null } = useFurnitureBadgeDisplayWidget();
if(objectId === -1) return null;
return <LayoutTrophyView color={ color } message={ badgeDesc } date={ date } senderName={ senderName } customTitle={ badgeName } onCloseClick={ close } />;
}

View File

@ -0,0 +1,40 @@
import { FC } from 'react';
import ReactSlider from 'react-slider';
import { LocalizeText } from '../../../../api';
import { Button, Column, Flex, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../../common';
import { useFurnitureStackHeightWidget } from '../../../../hooks';
export const FurnitureCustomStackHeightView: FC<{}> = props =>
{
const { objectId = -1, height = 0, maxHeight = 40, close = null, updateHeight = null, sendUpdate = null } = useFurnitureStackHeightWidget();
if(objectId === -1) return null;
return (
<NitroCardView className="nitro-widget-custom-stack-height" theme="primary-slim">
<NitroCardHeaderView headerText={ LocalizeText('widget.custom.stack.height.title') } onCloseClick={ close } />
<NitroCardContentView justifyContent="between">
<Text>{ LocalizeText('widget.custom.stack.height.text') }</Text>
<Flex gap={ 2 }>
<ReactSlider
className="nitro-slider"
min={ 0 }
max={ maxHeight }
step={ 0.01 }
value={ height }
onChange={ event => updateHeight(event) }
renderThumb={ (props, state) => <div { ...props }>{ state.valueNow }</div> } />
<input type="number" min={ 0 } max={ maxHeight } value={ height } onChange={ event => updateHeight(parseFloat(event.target.value)) } />
</Flex>
<Column gap={ 1 }>
<Button onClick={ event => sendUpdate(-100) }>
{ LocalizeText('furniture.above.stack') }
</Button>
<Button onClick={ event => sendUpdate(0) }>
{ LocalizeText('furniture.floor.level') }
</Button>
</Column>
</NitroCardContentView>
</NitroCardView>
);
}

View File

@ -1,35 +1,11 @@
import { FC, useCallback, useState } from 'react';
import { LocalizeText, RoomWidgetCreditFurniRedeemMessage, RoomWidgetUpdateCreditFurniEvent } from '../../../../../api';
import { Base, Button, Column, Flex, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../../../common';
import { UseEventDispatcherHook } from '../../../../../hooks';
import { useRoomContext } from '../../../RoomContext';
import { FC } from 'react';
import { LocalizeText } from '../../../../api';
import { Base, Button, Column, Flex, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../../common';
import { useFurnitureExchangeWidget } from '../../../../hooks';
export const FurnitureExchangeCreditView: FC<{}> = props =>
{
const [ objectId, setObjectId ] = useState(-1);
const [ value, setValue ] = useState(0);
const { eventDispatcher = null, widgetHandler = null } = useRoomContext();
const onRoomWidgetUpdateCreditFurniEvent = useCallback((event: RoomWidgetUpdateCreditFurniEvent) =>
{
setObjectId(event.objectId);
setValue(event.value);
}, []);
UseEventDispatcherHook(RoomWidgetUpdateCreditFurniEvent.CREDIT_FURNI_UPDATE, eventDispatcher, onRoomWidgetUpdateCreditFurniEvent);
const close = () =>
{
setObjectId(-1);
setValue(0);
}
const redeem = () =>
{
widgetHandler.processWidgetMessage(new RoomWidgetCreditFurniRedeemMessage(RoomWidgetCreditFurniRedeemMessage.REDEEM, objectId));
close();
}
const { objectId = -1, value = 0, close = null, redeem = null } = useFurnitureExchangeWidget();
if(objectId === -1) return null;

View File

@ -0,0 +1,29 @@
import { FC } from 'react';
import { LocalizeText } from '../../../../api';
import { Flex, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../../common';
import { useFurnitureExternalImageWidget } from '../../../../hooks';
export const FurnitureExternalImageView: FC<{}> = props =>
{
const { objectId = -1, photoData = null, close = null } = useFurnitureExternalImageWidget();
if((objectId === -1) || !photoData) return null;
return (
<NitroCardView className="nitro-external-image-widget" theme="primary-slim">
<NitroCardHeaderView headerText="" onCloseClick={ close } />
<NitroCardContentView>
<Flex center className="picture-preview border border-black" style={ photoData.w ? { backgroundImage: 'url(' + photoData.w + ')' } : {} }>
{ !photoData.w &&
<Text bold>{ LocalizeText('camera.loading') }</Text> }
</Flex>
{ photoData.m && photoData.m.length &&
<Text center>{ photoData.m }</Text> }
<Flex alignItems="center" justifyContent="between">
<Text>{ (photoData.n || '') }</Text>
<Text>{ new Date(photoData.t * 1000).toLocaleDateString() }</Text>
</Flex>
</NitroCardContentView>
</NitroCardView>
);
}

View File

@ -0,0 +1,62 @@
import { FC } from 'react';
import { CreateLinkEvent, LocalizeText } from '../../../../api';
import { Button, Column, Flex, LayoutGiftTagView, LayoutImage, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../../common';
import { useFurniturePresentWidget } from '../../../../hooks';
export const FurnitureGiftOpeningView: FC<{}> = props =>
{
const { objectId = -1, classId = -1, itemType = null, text = null, isOwnerOfFurniture = false, senderName = null, senderFigure = null, placedItemId = -1, placedItemType = null, placedInRoom = false, imageUrl = null, openPresent = null, close = null } = useFurniturePresentWidget();
if(objectId === -1) return null;
return (
<NitroCardView className="nitro-gift-opening" theme="primary-slim">
<NitroCardHeaderView headerText={ LocalizeText(senderName ? 'widget.furni.present.window.title_from' : 'widget.furni.present.window.title', [ 'name' ], [ senderName ]) } onCloseClick={ close } />
<NitroCardContentView>
{ (placedItemId === -1) &&
<Column overflow="hidden">
<Flex center overflow="auto">
<LayoutGiftTagView userName={ senderName } figure={ senderFigure } message={ text } />
</Flex>
{ isOwnerOfFurniture &&
<Flex gap={ 1 }>
{ senderName &&
<Button fullWidth onClick={ event => CreateLinkEvent('catalog/open') }>
{ LocalizeText('widget.furni.present.give_gift', [ 'name' ], [ senderName ]) }
</Button> }
<Button fullWidth variant="success" onClick={ openPresent }>
{ LocalizeText('widget.furni.present.open_gift') }
</Button>
</Flex> }
</Column> }
{ (placedItemId > -1) &&
<Flex gap={ 2 } overflow="hidden">
<Column center className="p-2">
<LayoutImage imageUrl={ imageUrl } />
</Column>
<Column grow>
<Column center gap={ 1 }>
<Text wrap small>{ LocalizeText('widget.furni.present.message_opened') }</Text>
<Text bold fontSize={ 5 }>{ text }</Text>
</Column>
<Column grow gap={ 1 }>
<Flex gap={ 1 }>
{ placedInRoom &&
<Button fullWidth onClick={ null }>
{ LocalizeText('widget.furni.present.put_in_inventory') }
</Button> }
<Button fullWidth variant="success" onClick={ null }>
{ LocalizeText(placedInRoom ? 'widget.furni.present.keep_in_room' : 'widget.furni.present.place_in_room') }
</Button>
</Flex>
{ (senderName && senderName.length) &&
<Button fullWidth onClick={ event => CreateLinkEvent('catalog/open') }>
{ LocalizeText('widget.furni.present.give_gift', [ 'name' ], [ senderName ]) }
</Button> }
</Column>
</Column>
</Flex> }
</NitroCardContentView>
</NitroCardView>
);
}

View File

@ -0,0 +1,54 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { RoomObjectOperationType } from '@nitrots/nitro-renderer';
import { FC } from 'react';
import { ProcessRoomObjectOperation } from '../../../../api';
import { Button, ButtonGroup } from '../../../../common';
import { useFurnitureManipulationWidget } from '../../../../hooks';
import { ObjectLocationView } from '../object-location/ObjectLocationView';
export const FurnitureManipulationMenuView: FC<{}> = props =>
{
const { manipulatingId = -1, manipulatingCategory = -1 } = useFurnitureManipulationWidget();
// const onRoomWidgetUpdateDecorateModeEvent = useCallback((event: RoomWidgetUpdateDecorateModeEvent) =>
// {
// if(event.isDecorating) return;
// moveFurniture();
// setIsVisible(false);
// setObjectId(-1);
// setCategory(-1);
// }, [ moveFurniture ]);
// UseEventDispatcherHook(RoomWidgetUpdateDecorateModeEvent.UPDATE_DECORATE, eventDispatcher, onRoomWidgetUpdateDecorateModeEvent);
// useEffect(() =>
// {
// if(!isVisible)
// {
// eventDispatcher.dispatchEvent(new RoomWidgetUpdateDecorateModeEvent(false));
// return;
// }
// eventDispatcher.dispatchEvent(new RoomWidgetUpdateDecorateModeEvent(true));
// moveFurniture();
// }, [ eventDispatcher, isVisible, moveFurniture ]);
if((manipulatingId === -1) || (manipulatingCategory === -1)) return null;
return (
<ObjectLocationView objectId={ manipulatingId } category={ manipulatingCategory }>
<ButtonGroup>
<Button onClick={ event => ProcessRoomObjectOperation(manipulatingId, manipulatingCategory, RoomObjectOperationType.OBJECT_PICKUP) }>
<FontAwesomeIcon icon="times" />
</Button>
<Button onClick={ event => ProcessRoomObjectOperation(manipulatingId, manipulatingCategory, RoomObjectOperationType.OBJECT_ROTATE_POSITIVE) }>
<FontAwesomeIcon icon="undo" />
</Button>
</ButtonGroup>
</ObjectLocationView>
);
}

View File

@ -0,0 +1,144 @@
import { HabboClubLevelEnum, RoomControllerLevel } from '@nitrots/nitro-renderer';
import { FC, useEffect, useState } from 'react';
import { GetAvatarRenderManager, GetClubMemberLevel, GetMergedMannequinFigureContainer, GetRoomSession, GetSessionDataManager, LocalizeText, TransformAsMannequinFigure } from '../../../../api';
import { Base, Button, Column, Flex, LayoutAvatarImageView, LayoutCurrencyIcon, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../../common';
import { useFurnitureMannequinWidget } from '../../../../hooks';
const MODE_NONE: number = -1;
const MODE_CONTROLLER: number = 0;
const MODE_UPDATE: number = 1;
const MODE_PEER: number = 2;
const MODE_NO_CLUB: number = 3;
const MODE_WRONG_GENDER: number = 4;
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, close = null } = useFurnitureMannequinWidget();
useEffect(() =>
{
if(objectId === -1) return;
const roomSession = GetRoomSession();
if(roomSession.isRoomOwner || (roomSession.controllerLevel >= RoomControllerLevel.GUEST) || GetSessionDataManager().isModerator)
{
setMode(MODE_CONTROLLER);
return;
}
if(GetSessionDataManager().gender.toLowerCase() !== gender.toLowerCase())
{
setMode(MODE_WRONG_GENDER);
return;
}
if(GetClubMemberLevel() < clubLevel)
{
setMode(MODE_NO_CLUB);
return;
}
setMode(MODE_PEER);
}, [ objectId, gender, clubLevel ]);
useEffect(() =>
{
switch(mode)
{
case MODE_CONTROLLER:
case MODE_WRONG_GENDER: {
const figureContainer = GetAvatarRenderManager().createFigureContainer(figure);
TransformAsMannequinFigure(figureContainer);
setRenderedFigure(figureContainer.getFigureString());
break;
}
case MODE_UPDATE: {
const figureContainer = GetAvatarRenderManager().createFigureContainer(GetSessionDataManager().figure);
TransformAsMannequinFigure(figureContainer);
setRenderedFigure(figureContainer.getFigureString());
break;
}
case MODE_PEER:
case MODE_NO_CLUB: {
const figureContainer = GetMergedMannequinFigureContainer(GetSessionDataManager().figure, figure);
setRenderedFigure(figureContainer.getFigureString());
break;
}
}
}, [ mode, figure, clubLevel ]);
if(objectId === -1) return null;
return (
<NitroCardView className="nitro-mannequin no-resize" theme="primary-slim">
<NitroCardHeaderView headerText={ LocalizeText('mannequin.widget.title') } onCloseClick={ close } />
<NitroCardContentView center>
<Flex fullWidth gap={ 2 } overflow="hidden">
<Column>
<Base position="relative" className="mannequin-preview">
<LayoutAvatarImageView position="absolute" figure={ renderedFigure } direction={ 2 } />
{ (clubLevel > 0) &&
<LayoutCurrencyIcon className="position-absolute end-2 bottom-2" type="hc" /> }
</Base>
</Column>
<Column grow justifyContent="between" overflow="auto">
{ (mode === MODE_CONTROLLER) &&
<>
<input type="text" className="form-control form-control-sm" value={ name } onChange={ event => setName(event.target.value) } onBlur={ saveName } />
<Column gap={ 1 }>
<Button variant="success" onClick={ event => setMode(MODE_UPDATE) }>
{ LocalizeText('mannequin.widget.style') }
</Button>
<Button variant="success" onClick={ wearFigure }>
{ LocalizeText('mannequin.widget.wear') }
</Button>
</Column>
</> }
{ (mode === MODE_UPDATE) &&
<>
<Column gap={ 1 }>
<Text bold>{ name }</Text>
<Text wrap>{ LocalizeText('mannequin.widget.savetext') }</Text>
</Column>
<Flex alignItems="center" justifyContent="between">
<Text underline pointer onClick={ event => setMode(MODE_CONTROLLER) }>
{ LocalizeText('mannequin.widget.back') }
</Text>
<Button variant="success" onClick={ saveFigure }>
{ LocalizeText('mannequin.widget.save') }
</Button>
</Flex>
</> }
{ (mode === MODE_PEER) &&
<>
<Column gap={ 1 }>
<Text bold>{ name }</Text>
<Text>{ LocalizeText('mannequin.widget.weartext') }</Text>
</Column>
<Button variant="success" onClick={ wearFigure }>
{ LocalizeText('mannequin.widget.wear') }
</Button>
</> }
{ (mode === MODE_NO_CLUB) &&
<Flex center grow>
<Text>{ LocalizeText('mannequin.widget.clubnotification') }</Text>
</Flex> }
{ (mode === MODE_WRONG_GENDER) &&
<Text>{ LocalizeText('mannequin.widget.wronggender') }</Text> }
</Column>
</Flex>
</NitroCardContentView>
</NitroCardView>
);
}

View File

@ -0,0 +1,52 @@
import { FC, useEffect, useState } from 'react';
import { ColorUtils } from '../../../../api';
import { DraggableWindow, DraggableWindowPosition } from '../../../../common';
import { useFurnitureStickieWidget } from '../../../../hooks';
const STICKIE_COLORS = [ '9CCEFF','FF9CFF', '9CFF9C','FFFF33' ];
const STICKIE_COLOR_NAMES = [ 'blue', 'pink', 'green', 'yellow' ];
const getStickieColorName = (color: string) =>
{
let index = STICKIE_COLORS.indexOf(color);
if(index === -1) index = 0;
return STICKIE_COLOR_NAMES[index];
}
export const FurnitureStickieView: FC<{}> = props =>
{
const { objectId = -1, color = '0', text = '', canModify = false, updateColor = null, updateText = null, trash = null, close = null } = useFurnitureStickieWidget();
const [ isEditing, setIsEditing ] = useState(false);
useEffect(() =>
{
setIsEditing(false);
}, [ objectId, color, text ]);
if(objectId === -1) return null;
return (
<DraggableWindow handleSelector=".drag-handler" windowPosition={ DraggableWindowPosition.NOTHING }>
<div className={ 'nitro-stickie nitro-stickie-image stickie-' + getStickieColorName(color) }>
<div className="d-flex align-items-center stickie-header drag-handler">
<div className="d-flex align-items-center flex-grow-1 h-100">
{ canModify &&
<>
<div className="nitro-stickie-image stickie-trash header-trash" onClick={ trash }></div>
{ STICKIE_COLORS.map(color =>
{
return <div key={ color } className="stickie-color ms-1" onClick={ event => updateColor(color) } style={ { backgroundColor: ColorUtils.makeColorHex(color) } } />
}) }
</> }
</div>
<div className="d-flex align-items-center nitro-stickie-image stickie-close header-close" onClick={ close }></div>
</div>
<div className="stickie-context">
{ !isEditing ? <div className="context-text" onClick={ event => setIsEditing(true) }>{ text }</div> : <textarea className="context-text" defaultValue={ text } tabIndex={ 0 } onBlur={ event => updateText(event.target.value) } autoFocus></textarea> }
</div>
</div>
</DraggableWindow>
);
}

View File

@ -0,0 +1,12 @@
import { FC } from 'react';
import { LayoutTrophyView } from '../../../../common';
import { useFurnitureTrophyWidget } from '../../../../hooks';
export const FurnitureTrophyView: FC<{}> = props =>
{
const { objectId = -1, color = '1', senderName = '', date = '', message = '' } = useFurnitureTrophyWidget();
if(objectId === -1) return null;
return <LayoutTrophyView color={ color } message={ message } date={ date } senderName={ senderName } onCloseClick={ close } />;
}

View File

@ -17,13 +17,14 @@
.dimmer-banner {
width: 56px;
height: 79px;
background: url('../../../../assets/images/room-widgets/dimmer-widget/dimmer_banner.png') center no-repeat;
background: url("../../../../assets/images/room-widgets/dimmer-widget/dimmer_banner.png")
center no-repeat;
}
.color-swatch {
height: 30px;
border: 2px solid $white;
box-shadow: inset 3px 3px rgba(0, 0, 0, .2);
box-shadow: inset 3px 3px rgba(0, 0, 0, 0.2);
&.active {
box-shadow: none;
@ -36,14 +37,13 @@
height: $nitro-widget-exchange-credit-height;
.exchange-image {
background-image: url('../../../../assets/images/room-widgets/exchange-credit/exchange-credit-image.png');
background-image: url("../../../../assets/images/room-widgets/exchange-credit/exchange-credit-image.png");
width: 103px;
height: 103px;
}
}
.nitro-external-image-widget {
.picture-preview {
width: 320px;
height: 320px;
@ -51,24 +51,129 @@
}
.nitro-gift-opening {
width: 340px;
resize: none;
}
.nitro-mannequin {
width: 300px;
.mannequin-preview {
display: flex;
justify-content: center;
align-items: center;
width: 83px;
height: 130px;
background-image: url("../../../../assets/images/room-widgets/mannequin-widget/mannequin-spritesheet.png");
overflow: hidden;
.avatar-image {
background-position: unset;
top: -8px;
}
}
}
.mannequin-preview {
display: flex;
justify-content: center;
align-items: center;
width: 83px;
height: 130px;
background-image: url('../../../../assets/images/room-widgets/mannequin-widget/mannequin-spritesheet.png');
overflow: hidden;
.nitro-stickie {
position: relative;
width: 185px;
height: 178px;
top: 25px;
left: 25px;
padding: 1px;
pointer-events: all;
.stickie-header {
width: 183px;
height: 18px;
padding: 0 7px;
.header-trash,
.header-close {
cursor: pointer;
}
.stickie-color {
width: 10px;
height: 10px;
cursor: pointer;
}
}
.stickie-context {
width: 183px;
height: 145px;
padding: 2px 7px;
font-size: 12px;
color: $black;
.context-text {
width: 100%;
height: 100%;
padding: 0;
overflow-wrap: break-word;
white-space: break-spaces;
overflow-y: auto;
}
textarea {
background: transparent;
border: 0;
outline: none;
box-shadow: none;
resize: none;
font-style: italic;
&:active {
border: 0;
outline: none;
box-shadow: none;
}
}
}
}
@import './friend-furni/FurnitureFriendFurniView';
@import './manipulation-menu/FurnitureManipulationMenuView';
@import './stickie/FurnitureStickieView';
@import './high-score/FurnitureHighScoreView';
@import './youtube-tv/FurnitureYoutubeDisplayView';
.nitro-stickie-image {
background-image: url("../../../../assets/images/room-widgets/stickie-widget/stickie-spritesheet.png");
&.stickie-blue,
&.stickie-yellow,
&.stickie-green,
&.stickie-pink {
width: 185px;
height: 178px;
}
&.stickie-blue {
background-position: -2px -2px;
}
&.stickie-yellow {
background-image: url("../../../../assets/images/room-widgets/stickie-widget/stickie-yellow.png");
//background-position: -191px -184px;
}
&.stickie-green {
background-position: -191px -2px;
}
&.stickie-pink {
background-position: -2px -184px;
}
&.stickie-close {
width: 10px;
height: 10px;
background-position: -2px -366px;
}
&.stickie-trash {
width: 9px;
height: 10px;
background-position: -16px -366px;
}
}
@import "./friend-furni/FurnitureFriendFurniView";
@import "./high-score/FurnitureHighScoreView";
@import "./youtube-tv/FurnitureYoutubeDisplayView";

View File

@ -1,24 +1,25 @@
import { FC } from 'react';
import { FurnitureBackgroundColorView } from './background-color/FurnitureBackgroundColorView';
import { FurnitureBadgeDisplayView } from './badge-display/FurnitureBadgeDisplayView';
import { Base } from '../../../../common';
import { FurnitureContextMenuView } from './context-menu/FurnitureContextMenuView';
import { FurnitureCustomStackHeightView } from './custom-stack-height/FurnitureCustomStackHeightView';
import { FurnitureDimmerView } from './dimmer/FurnitureDimmerView';
import { FurnitureExchangeCreditView } from './exchange-credit/FurnitureExchangeCreditView';
import { FurnitureExternalImageView } from './external-image/FurnitureExternalImageView';
import { FurnitureFriendFurniView } from './friend-furni/FurnitureFriendFurniView';
import { FurnitureGiftOpeningView } from './gift-opening/FurnitureGiftOpeningView';
import { FurnitureBackgroundColorView } from './FurnitureBackgroundColorView';
import { FurnitureBadgeDisplayView } from './FurnitureBadgeDisplayView';
import { FurnitureCustomStackHeightView } from './FurnitureCustomStackHeightView';
import { FurnitureExchangeCreditView } from './FurnitureExchangeCreditView';
import { FurnitureExternalImageView } from './FurnitureExternalImageView';
import { FurnitureGiftOpeningView } from './FurnitureGiftOpeningView';
import { FurnitureManipulationMenuView } from './FurnitureManipulationMenuView';
import { FurnitureMannequinView } from './FurnitureMannequinView';
import { FurnitureStickieView } from './FurnitureStickieView';
import { FurnitureTrophyView } from './FurnitureTrophyView';
import { FurnitureHighScoreView } from './high-score/FurnitureHighScoreView';
import { FurnitureManipulationMenuView } from './manipulation-menu/FurnitureManipulationMenuView';
import { FurnitureMannequinView } from './mannequin/FurnitureMannequinView';
import { FurnitureStickieView } from './stickie/FurnitureStickieView';
import { FurnitureTrophyView } from './trophy/FurnitureTrophyView';
import { FurnitureYoutubeDisplayView } from './youtube-tv/FurnitureYoutubeDisplayView';
export const FurnitureWidgetsView: FC<{}> = props =>
{
return (
<div className="position-absolute nitro-room-widgets top-0 start-0 w-100 h-100">
<Base fit position="absolute" className="nitro-room-widgets top-0 start-0">
<FurnitureBackgroundColorView />
<FurnitureContextMenuView />
<FurnitureCustomStackHeightView />
@ -34,6 +35,6 @@ export const FurnitureWidgetsView: FC<{}> = props =>
<FurnitureBadgeDisplayView />
<FurnitureExternalImageView />
<FurnitureYoutubeDisplayView />
</div>
</Base>
);
}

View File

@ -1,130 +0,0 @@
import { ApplyTonerComposer, RoomControllerLevel, RoomEngineObjectEvent, RoomEngineTriggerWidgetEvent, RoomObjectVariable } from '@nitrots/nitro-renderer';
import { FC, useCallback, useEffect, useState } from 'react';
import ReactSlider from 'react-slider';
import { GetRoomEngine, GetSessionDataManager, LocalizeText, RoomWidgetUpdateBackgroundColorPreviewEvent, RoomWidgetUpdateRoomObjectEvent, SendMessageComposer } from '../../../../../api';
import { Button, Column, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../../../common';
import { UseEventDispatcherHook, UseRoomEngineEvent } from '../../../../../hooks';
import { useRoomContext } from '../../../RoomContext';
export const FurnitureBackgroundColorView: FC<{}> = props =>
{
const [ objectId, setObjectId ] = useState(-1);
const [ hue, setHue ] = useState(0);
const [ saturation, setSaturation ] = useState(0);
const [ lightness, setLightness ] = useState(0);
const { roomSession = null, eventDispatcher = null } = useRoomContext();
const close = useCallback(() =>
{
eventDispatcher.dispatchEvent(new RoomWidgetUpdateBackgroundColorPreviewEvent(RoomWidgetUpdateBackgroundColorPreviewEvent.CLEAR_PREVIEW));
setObjectId(-1);
}, [ eventDispatcher ]);
const canOpenBackgroundToner = useCallback(() =>
{
return (roomSession.isRoomOwner || (roomSession.controllerLevel >= RoomControllerLevel.GUEST) || GetSessionDataManager().isModerator);
}, [ roomSession ]);
const onRoomEngineObjectEvent = useCallback((event: RoomEngineObjectEvent) =>
{
switch(event.type)
{
case RoomEngineTriggerWidgetEvent.REQUEST_BACKGROUND_COLOR: {
if(!canOpenBackgroundToner()) return;
const roomObject = GetRoomEngine().getRoomObject(event.roomId, event.objectId, event.category);
const model = roomObject.model;
setObjectId(roomObject.id);
setHue(parseInt(model.getValue<string>(RoomObjectVariable.FURNITURE_ROOM_BACKGROUND_COLOR_HUE)));
setSaturation(parseInt(model.getValue<string>(RoomObjectVariable.FURNITURE_ROOM_BACKGROUND_COLOR_SATURATION)));
setLightness(parseInt(model.getValue<string>(RoomObjectVariable.FURNITURE_ROOM_BACKGROUND_COLOR_LIGHTNESS)));
return;
}
case RoomWidgetUpdateRoomObjectEvent.FURNI_REMOVED: {
if(objectId !== event.objectId) return;
close();
return;
}
}
}, [ objectId, canOpenBackgroundToner, close ]);
UseRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_BACKGROUND_COLOR, onRoomEngineObjectEvent);
UseEventDispatcherHook(RoomWidgetUpdateRoomObjectEvent.FURNI_REMOVED, eventDispatcher, onRoomEngineObjectEvent);
const processAction = useCallback((name: string) =>
{
switch(name)
{
case 'apply':
SendMessageComposer(new ApplyTonerComposer(objectId, hue, saturation, lightness));
break;
case 'toggle':
roomSession.useMultistateItem(objectId);
break;
}
}, [ roomSession, objectId, hue, saturation, lightness ]);
useEffect(() =>
{
if(objectId === -1) return;
eventDispatcher.dispatchEvent(new RoomWidgetUpdateBackgroundColorPreviewEvent(RoomWidgetUpdateBackgroundColorPreviewEvent.PREVIEW, hue, saturation, lightness));
}, [ eventDispatcher, objectId, hue, saturation, lightness ]);
if(objectId === -1) return null;
return (
<NitroCardView theme="primary-slim" className="nitro-room-widget-toner">
<NitroCardHeaderView headerText={ LocalizeText('widget.backgroundcolor.title') } onCloseClick={ close } />
<NitroCardContentView overflow="hidden" justifyContent="between">
<Column overflow="auto" gap={ 1 }>
<Column>
<Text bold>{ LocalizeText('widget.backgroundcolor.hue') }</Text>
<ReactSlider
className={ 'nitro-slider' }
min={ 0 }
max={ 360 }
value={ hue }
onChange={ event => setHue(event) }
thumbClassName={ 'thumb degree' }
renderThumb={ (props, state) => <div { ...props }>{ state.valueNow }</div> } />
</Column>
<Column>
<Text bold>{ LocalizeText('widget.backgroundcolor.saturation') }</Text>
<ReactSlider
className={ 'nitro-slider' }
min={ 0 }
max={ 100 }
value={ saturation }
onChange={ event => setSaturation(event) }
thumbClassName={ 'thumb percent' }
renderThumb={ (props, state) => <div { ...props }>{ state.valueNow }</div> } />
</Column>
<Column>
<Text bold>{ LocalizeText('widget.backgroundcolor.lightness') }</Text>
<ReactSlider
className={ 'nitro-slider' }
min={ 0 }
max={ 100 }
value={ lightness }
onChange={ event => setLightness(event) }
thumbClassName={ 'thumb percent' }
renderThumb={ (props, state) => <div { ...props }>{ state.valueNow }</div> } />
</Column>
</Column>
<Column gap={ 1 }>
<Button fullWidth variant="primary" onClick={ event => processAction('toggle') }>
{ LocalizeText('widget.backgroundcolor.button.on') }
</Button>
<Button fullWidth variant="primary" onClick={ event => processAction('apply') }>
{ LocalizeText('widget.backgroundcolor.button.apply') }
</Button>
</Column>
</NitroCardContentView>
</NitroCardView>
);
}

View File

@ -1,69 +0,0 @@
import { NitroEvent, RoomEngineTriggerWidgetEvent, StringDataType } from '@nitrots/nitro-renderer';
import { FC, useCallback, useState } from 'react';
import { GetRoomEngine, LocalizeBadgeDescription, LocalizeBadgeName, RoomWidgetUpdateRoomObjectEvent } from '../../../../../api';
import { LayoutTrophyView } from '../../../../../common';
import { UseEventDispatcherHook, UseRoomEngineEvent } from '../../../../../hooks';
import { useRoomContext } from '../../../RoomContext';
import { FurnitureTrophyData } from '../trophy/FurnitureTrophyData';
export const FurnitureBadgeDisplayView: FC<{}> = props =>
{
const [ trophyData, setTrophyData ] = useState<FurnitureTrophyData>(null);
const { widgetHandler = null } = useRoomContext();
const onNitroEvent = useCallback((event: NitroEvent) =>
{
switch(event.type)
{
case RoomEngineTriggerWidgetEvent.REQUEST_ACHIEVEMENT_RESOLUTION_ENGRAVING:
case RoomEngineTriggerWidgetEvent.REQUEST_BADGE_DISPLAY_ENGRAVING: {
const widgetEvent = (event as RoomEngineTriggerWidgetEvent);
const roomObject = GetRoomEngine().getRoomObject(widgetEvent.roomId, widgetEvent.objectId, widgetEvent.category);
if(!roomObject) return;
const stringStuff = new StringDataType();
stringStuff.initializeFromRoomObjectModel(roomObject.model);
const badgeName = LocalizeBadgeName(stringStuff.getValue(1));
const badgeDesc = LocalizeBadgeDescription(stringStuff.getValue(1));
const date = stringStuff.getValue(2);
const senderName = stringStuff.getValue(3);
setTrophyData(new FurnitureTrophyData(widgetEvent.objectId, widgetEvent.category, '1', senderName, date, badgeDesc, badgeName));
return;
}
case RoomWidgetUpdateRoomObjectEvent.FURNI_REMOVED: {
const widgetEvent = (event as RoomWidgetUpdateRoomObjectEvent);
setTrophyData(prevState =>
{
if(!prevState || (widgetEvent.id !== prevState.objectId) || (widgetEvent.category !== prevState.category)) return prevState;
return null;
});
return;
}
}
}, []);
UseRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_BADGE_DISPLAY_ENGRAVING, onNitroEvent);
UseRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_ACHIEVEMENT_RESOLUTION_ENGRAVING, onNitroEvent);
UseEventDispatcherHook(RoomWidgetUpdateRoomObjectEvent.FURNI_REMOVED, widgetHandler.eventDispatcher, onNitroEvent);
const processAction = useCallback((type: string, value: string = null) =>
{
switch(type)
{
case 'close':
setTrophyData(null);
return;
}
}, []);
if(!trophyData) return null;
return <LayoutTrophyView color={ trophyData.color } message={ trophyData.message } date={ trophyData.date } senderName={ trophyData.ownerName } customTitle={ trophyData.customTitle } onCloseClick={ () => processAction('close') } />;
}

View File

@ -1,104 +0,0 @@
import { FurnitureStackHeightComposer, FurnitureStackHeightEvent } from '@nitrots/nitro-renderer';
import { FC, useCallback, useEffect, useState } from 'react';
import ReactSlider from 'react-slider';
import { LocalizeText, RoomWidgetUpdateCustomStackHeightEvent, SendMessageComposer } from '../../../../../api';
import { Button, Column, Flex, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../../../common';
import { UseEventDispatcherHook, UseMessageEventHook } from '../../../../../hooks';
import { useRoomContext } from '../../../RoomContext';
const MAX_HEIGHT: number = 40;
export const FurnitureCustomStackHeightView: FC<{}> = props =>
{
const [ objectId, setObjectId ] = useState(-1);
const [ height, setHeight ] = useState(0);
const [ pendingHeight, setPendingHeight ] = useState(-1);
const { eventDispatcher = null } = useRoomContext();
const close = () =>
{
setObjectId(-1);
setHeight(0);
}
const updateHeight = useCallback((height: number, fromServer: boolean = false) =>
{
if(!height) height = 0;
height = Math.abs(height);
if(!fromServer) ((height > MAX_HEIGHT) && (height = MAX_HEIGHT));
setHeight(parseFloat(height.toFixed(2)));
if(!fromServer) setPendingHeight(height * 100);
}, []);
const onRoomWidgetUpdateCustomStackHeightEvent = useCallback((event: RoomWidgetUpdateCustomStackHeightEvent) =>
{
switch(event.type)
{
case RoomWidgetUpdateCustomStackHeightEvent.UPDATE_CUSTOM_STACK_HEIGHT: {
setObjectId(event.objectId);
updateHeight(event.height, true);
}
}
}, [ updateHeight ]);
UseEventDispatcherHook(RoomWidgetUpdateCustomStackHeightEvent.UPDATE_CUSTOM_STACK_HEIGHT, eventDispatcher, onRoomWidgetUpdateCustomStackHeightEvent);
const onFurnitureStackHeightEvent = useCallback((event: FurnitureStackHeightEvent) =>
{
const parser = event.getParser();
if(objectId !== parser.furniId) return;
updateHeight(parser.height, true);
}, [ objectId, updateHeight ]);
UseMessageEventHook(FurnitureStackHeightEvent, onFurnitureStackHeightEvent);
const sendUpdate = useCallback((height: number) =>
{
SendMessageComposer(new FurnitureStackHeightComposer(objectId, ~~(height)));
}, [ objectId ]);
useEffect(() =>
{
if((objectId === -1) || (pendingHeight === -1)) return;
const timeout = setTimeout(() => sendUpdate(~~(pendingHeight)), 10);
return () => clearTimeout(timeout);
}, [ objectId, pendingHeight, sendUpdate ]);
if(objectId === -1) return null;
return (
<NitroCardView className="nitro-widget-custom-stack-height" theme="primary-slim">
<NitroCardHeaderView headerText={ LocalizeText('widget.custom.stack.height.title') } onCloseClick={ close } />
<NitroCardContentView justifyContent="between">
<Text>{ LocalizeText('widget.custom.stack.height.text') }</Text>
<Flex gap={ 2 }>
<ReactSlider
className="nitro-slider"
min={ 0 }
max={ MAX_HEIGHT }
step={ 0.01 }
value={ height }
onChange={ event => updateHeight(event) }
renderThumb={ (props, state) => <div { ...props }>{ state.valueNow }</div> } />
<input type="number" min={ 0 } max={ MAX_HEIGHT } value={ height } onChange={ event => updateHeight(parseFloat(event.target.value)) } />
</Flex>
<Column gap={ 1 }>
<Button onClick={ event => sendUpdate(-100) }>
{ LocalizeText('furniture.above.stack') }
</Button>
<Button onClick={ event => sendUpdate(0) }>
{ LocalizeText('furniture.floor.level') }
</Button>
</Column>
</NitroCardContentView>
</NitroCardView>
);
}

View File

@ -1,51 +0,0 @@
import { FC, useCallback, useState } from 'react';
import { IPhotoData, LocalizeText, RoomWidgetUpdateExternalImageEvent } from '../../../../../api';
import { Flex, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../../../common';
import { UseEventDispatcherHook } from '../../../../../hooks';
import { useRoomContext } from '../../../RoomContext';
export const FurnitureExternalImageView: FC<{}> = props =>
{
const [ objectId, setObjectId ] = useState(-1);
const [ photoData, setPhotoData ] = useState<IPhotoData>(null);
const { eventDispatcher = null } = useRoomContext();
const close = () =>
{
setObjectId(-1);
setPhotoData(null)
}
const onRoomWidgetUpdateExternalImageEvent = useCallback((event: RoomWidgetUpdateExternalImageEvent) =>
{
switch(event.type)
{
case RoomWidgetUpdateExternalImageEvent.UPDATE_EXTERNAL_IMAGE:
setObjectId(event.objectId);
setPhotoData(event.photoData);
return;
}
}, []);
UseEventDispatcherHook(RoomWidgetUpdateExternalImageEvent.UPDATE_EXTERNAL_IMAGE, eventDispatcher, onRoomWidgetUpdateExternalImageEvent);
if((objectId === -1) || !photoData) return null;
return (
<NitroCardView className="nitro-external-image-widget" theme="primary-slim">
<NitroCardHeaderView headerText={ '' } onCloseClick={ close } />
<NitroCardContentView>
<Flex center className="picture-preview border border-black" style={ photoData.w ? { backgroundImage: 'url(' + photoData.w + ')' } : {} }>
{ !photoData.w &&
<Text bold>{ LocalizeText('camera.loading') }</Text> }
</Flex>
{ photoData.m && photoData.m.length &&
<Text center>{ photoData.m }</Text> }
<Flex alignItems="center" justifyContent="between">
<Text>{ (photoData.n || '') }</Text>
<Text>{ new Date(photoData.t * 1000).toLocaleDateString() }</Text>
</Flex>
</NitroCardContentView>
</NitroCardView>
);
}

View File

@ -1,241 +0,0 @@
import { RoomObjectCategory, RoomObjectOperationType } from '@nitrots/nitro-renderer';
import { FC, useCallback, useMemo, useState } from 'react';
import { CreateLinkEvent, GetRoomEngine, GetSessionDataManager, LocalizeText, ProductTypeEnum, RoomWidgetPresentOpenMessage, RoomWidgetUpdatePresentDataEvent, RoomWidgetUpdateRoomObjectEvent } from '../../../../../api';
import { Button, Column, Flex, LayoutGiftTagView, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../../../common';
import { UseEventDispatcherHook } from '../../../../../hooks/events/UseEventDispatcherHook';
import { useRoomContext } from '../../../RoomContext';
const FLOOR: string = 'floor';
const WALLPAPER: string = 'wallpaper';
const LANDSCAPE: string = 'landscape';
const ACTION_GIVE_GIFT = 0;
const ACTION_OPEN = 1;
const ACTION_PLACE = 2;
const ACTION_INVENTORY = 3;
export const FurnitureGiftOpeningView: FC<{}> = props =>
{
const [ objectId, setObjectId ] = useState(-1);
const [ classId, setClassId ] = useState(-1);
const [ itemType, setItemType ] = useState<string>(null);
const [ text, setText ] = useState<string>(null);
const [ isOwnerOfFurniture, setIsOwnerOfFurniture ] = useState(false);
const [ senderName, setSenderName ] = useState<string>(null);
const [ senderFigure, setSenderFigure ] = useState<string>(null);
const [ placedItemId, setPlacedItemId ] = useState(-1);
const [ placedItemType, setPlacedItemType ] = useState<string>(null);
const [ placedInRoom, setPlacedInRoom ] = useState(false);
const [ imageUrl, setImageUrl ] = useState<string>(null);
const [ openRequested, setOpenRequested ] = useState(false);
const { roomSession = null, eventDispatcher = null, widgetHandler = null } = useRoomContext();
const clearGift = useCallback(() =>
{
if(!openRequested) setObjectId(-1);
setText(null);
setIsOwnerOfFurniture(false);
}, [ openRequested ]);
const getGiftImageUrl = useCallback((name: string) =>
{
return '';
}, []);
const onRoomWidgetUpdatePresentDataEvent = useCallback((event: RoomWidgetUpdatePresentDataEvent) =>
{
switch(event.type)
{
case RoomWidgetUpdatePresentDataEvent.PACKAGEINFO: {
setOpenRequested(false);
setObjectId(event.objectId);
setText(event.giftMessage);
setIsOwnerOfFurniture(event.isController);
setSenderName(event.purchaserName);
setSenderFigure(event.purchaserFigure);
setImageUrl(event.imageUrl);
return;
}
case RoomWidgetUpdatePresentDataEvent.CONTENTS_FLOOR:
case RoomWidgetUpdatePresentDataEvent.CONTENTS_LANDSCAPE:
case RoomWidgetUpdatePresentDataEvent.CONTENTS_WALLPAPER: {
let imageType: string = null;
if(event.type === RoomWidgetUpdatePresentDataEvent.CONTENTS_FLOOR) imageType = 'packagecard_icon_floor';
else if(event.type === RoomWidgetUpdatePresentDataEvent.CONTENTS_LANDSCAPE) imageType = 'packagecard_icon_landscape';
else if(event.type === RoomWidgetUpdatePresentDataEvent.CONTENTS_WALLPAPER) imageType = 'packagecard_icon_wallpaper';
setObjectId(event.objectId);
setClassId(event.classId);
setItemType(event.itemType);
setText(event.giftMessage);
setIsOwnerOfFurniture(event.isController);
setPlacedItemId(event.placedItemId);
setPlacedItemType(event.placedItemType);
setPlacedInRoom(event.placedInRoom);
setImageUrl(getGiftImageUrl(imageType));
return;
}
case RoomWidgetUpdatePresentDataEvent.CONTENTS_CLUB: {
setObjectId(event.objectId);
setClassId(event.classId);
setItemType(event.itemType);
setText(event.giftMessage);
setIsOwnerOfFurniture(event.isController);
setImageUrl(getGiftImageUrl('packagecard_icon_hc'));
return;
}
case RoomWidgetUpdatePresentDataEvent.CONTENTS: {
if(!openRequested) return;
setObjectId(event.objectId);
setClassId(event.classId);
setItemType(event.itemType);
setText(event.giftMessage);
setIsOwnerOfFurniture(event.isController);
setPlacedItemId(event.placedItemId);
setPlacedItemType(event.placedItemType);
setPlacedInRoom(event.placedInRoom);
setImageUrl(event.imageUrl);
return;
}
case RoomWidgetUpdatePresentDataEvent.CONTENTS_IMAGE: {
if(!openRequested) return;
setImageUrl(event.imageUrl);
}
}
}, [ openRequested, getGiftImageUrl ]);
UseEventDispatcherHook(RoomWidgetUpdatePresentDataEvent.PACKAGEINFO, eventDispatcher, onRoomWidgetUpdatePresentDataEvent);
UseEventDispatcherHook(RoomWidgetUpdatePresentDataEvent.CONTENTS, eventDispatcher, onRoomWidgetUpdatePresentDataEvent);
UseEventDispatcherHook(RoomWidgetUpdatePresentDataEvent.CONTENTS_FLOOR, eventDispatcher, onRoomWidgetUpdatePresentDataEvent);
UseEventDispatcherHook(RoomWidgetUpdatePresentDataEvent.CONTENTS_LANDSCAPE, eventDispatcher, onRoomWidgetUpdatePresentDataEvent);
UseEventDispatcherHook(RoomWidgetUpdatePresentDataEvent.CONTENTS_WALLPAPER, eventDispatcher, onRoomWidgetUpdatePresentDataEvent);
UseEventDispatcherHook(RoomWidgetUpdatePresentDataEvent.CONTENTS_CLUB, eventDispatcher, onRoomWidgetUpdatePresentDataEvent);
UseEventDispatcherHook(RoomWidgetUpdatePresentDataEvent.CONTENTS_IMAGE, eventDispatcher, onRoomWidgetUpdatePresentDataEvent);
const onRoomWidgetRoomObjectUpdateEvent = useCallback((event: RoomWidgetUpdateRoomObjectEvent) =>
{
if(event.id === objectId) clearGift();
if(event.id === placedItemId)
{
if(placedInRoom) setPlacedInRoom(false);
}
}, [ objectId, placedItemId, placedInRoom, clearGift ]);
UseEventDispatcherHook(RoomWidgetUpdateRoomObjectEvent.FURNI_REMOVED, eventDispatcher, onRoomWidgetRoomObjectUpdateEvent);
const close = useCallback(() =>
{
setObjectId(-1);
setOpenRequested(false);
setPlacedItemId(-1);
setPlacedInRoom(false);
setText(null);
setIsOwnerOfFurniture(false);
}, []);
const isSpaces = useMemo(() =>
{
if(itemType !== ProductTypeEnum.WALL) return false;
const furniData = GetSessionDataManager().getWallItemData(classId);
if(!furniData) return false;
const className = furniData.className;
return (className === FLOOR || className === LANDSCAPE || className === WALLPAPER);
}, [ classId, itemType ]);
const productName = useMemo(() =>
{
if(objectId === -1) return '';
if(isSpaces) return 'widget.furni.present.spaces.message_opened';
return 'widget.furni.present.message_opened';
}, [ objectId, isSpaces ]);
const handleAction = useCallback((action: number) =>
{
switch(action)
{
case ACTION_GIVE_GIFT:
CreateLinkEvent('catalog/open');
return;
case ACTION_OPEN:
setOpenRequested(true);
widgetHandler.processWidgetMessage(new RoomWidgetPresentOpenMessage(RoomWidgetPresentOpenMessage.OPEN_PRESENT, objectId));
return;
case ACTION_PLACE:
return;
case ACTION_INVENTORY:
if((placedItemId > 0) && placedInRoom)
{
if(placedItemType === ProductTypeEnum.PET)
{
roomSession.pickupPet(placedItemId);
}
else
{
const roomObject = GetRoomEngine().getRoomObject(roomSession.roomId, placedItemId, RoomObjectCategory.FLOOR);
if(roomObject) GetRoomEngine().processRoomObjectOperation(roomObject.id, RoomObjectCategory.FLOOR, RoomObjectOperationType.OBJECT_PICKUP);
}
}
close();
return;
}
}, [ roomSession, widgetHandler, objectId, placedInRoom, placedItemId, placedItemType, close ]);
if(objectId === -1) return null;
return (
<NitroCardView className="nitro-gift-opening" theme="primary-slim">
<NitroCardHeaderView headerText={ LocalizeText(senderName ? 'widget.furni.present.window.title_from' : 'widget.furni.present.window.title', [ 'name' ], [ senderName ]) } onCloseClick={ close } />
<NitroCardContentView center>
{ (placedItemId === -1) &&
<Column overflow="hidden">
<Flex center overflow="auto">
<LayoutGiftTagView userName={ senderName } figure={ senderFigure } message={ text } />
</Flex>
<Flex gap={ 1 }>
{ senderName &&
<Button fullWidth onClick={ event => handleAction(ACTION_GIVE_GIFT) }>
{ LocalizeText('widget.furni.present.give_gift', [ 'name' ], [ senderName ]) }
</Button> }
<Button fullWidth variant="success" onClick={ event => handleAction(ACTION_OPEN) }>
{ LocalizeText('widget.furni.present.open_gift') }
</Button>
</Flex>
</Column> }
{ (placedItemId > -1) &&
<Column overflow="hidden">
<Flex center overflow="auto" gap={ 2 }>
<img src={ imageUrl } className="no-select" alt="" />
<Text wrap>{ LocalizeText(productName, [ 'product' ], [ text ]) }</Text>
</Flex>
<Column grow gap={ 1 }>
<Flex gap={ 1 }>
<Button fullWidth onClick={ event => handleAction(ACTION_INVENTORY) }>
{ LocalizeText('widget.furni.present.put_in_inventory') }
</Button>
<Button fullWidth variant="success" onClick={ event => handleAction(ACTION_PLACE) }>
{ LocalizeText(placedInRoom ? 'widget.furni.present.keep_in_room' : 'widget.furni.present.place_in_room') }
</Button>
</Flex>
{ (senderName && senderName.length) &&
<Button fullWidth onClick={ event => handleAction(ACTION_GIVE_GIFT) }>
{ LocalizeText('widget.furni.present.give_gift', [ 'name' ], [ senderName ]) }
</Button> }
</Column>
</Column> }
</NitroCardContentView>
</NitroCardView>
);
}

View File

@ -1,103 +0,0 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { RoomObjectOperationType } from '@nitrots/nitro-renderer';
import { FC, useCallback, useEffect, useState } from 'react';
import { ProcessRoomObjectOperation, RoomWidgetUpdateDecorateModeEvent, RoomWidgetUpdateRoomObjectEvent } from '../../../../../api';
import { UseEventDispatcherHook } from '../../../../../hooks';
import { useRoomContext } from '../../../RoomContext';
import { ObjectLocationView } from '../../object-location/ObjectLocationView';
export const FurnitureManipulationMenuView: FC<{}> = props =>
{
const [ isVisible, setIsVisible ] = useState(false);
const [ objectId, setObjectId ] = useState(-1);
const [ objectType, setObjectType ] = useState(-1);
const { roomSession = null, eventDispatcher = null, widgetHandler = null } = useRoomContext();
const rotateFurniture = useCallback(() =>
{
ProcessRoomObjectOperation(objectId, objectType, RoomObjectOperationType.OBJECT_ROTATE_POSITIVE);
}, [ objectId, objectType ]);
const moveFurniture = useCallback(() =>
{
ProcessRoomObjectOperation(objectId, objectType, RoomObjectOperationType.OBJECT_MOVE);
}, [ objectId, objectType ]);
const pickupFurniture = useCallback(() =>
{
ProcessRoomObjectOperation(objectId, objectType, RoomObjectOperationType.OBJECT_PICKUP);
}, [ objectId, objectType ]);
const onRoomWidgetRoomObjectUpdateEvent = useCallback((event: RoomWidgetUpdateRoomObjectEvent) =>
{
switch(event.type)
{
case RoomWidgetUpdateRoomObjectEvent.OBJECT_REQUEST_MANIPULATION: {
setIsVisible(true);
setObjectId(event.id);
setObjectType(event.category);
return;
}
case RoomWidgetUpdateRoomObjectEvent.FURNI_REMOVED: {
if(event.id === objectId)
{
setIsVisible(false);
setObjectId(-1);
setObjectType(-1);
}
return;
}
case RoomWidgetUpdateRoomObjectEvent.OBJECT_DESELECTED: {
setIsVisible(false);
setObjectId(-1);
setObjectType(-1);
return;
}
}
}, [ objectId ]);
UseEventDispatcherHook(RoomWidgetUpdateRoomObjectEvent.OBJECT_REQUEST_MANIPULATION, eventDispatcher, onRoomWidgetRoomObjectUpdateEvent);
UseEventDispatcherHook(RoomWidgetUpdateRoomObjectEvent.OBJECT_DESELECTED, eventDispatcher, onRoomWidgetRoomObjectUpdateEvent);
const onRoomWidgetUpdateDecorateModeEvent = useCallback((event: RoomWidgetUpdateDecorateModeEvent) =>
{
if(event.isDecorating) return;
moveFurniture();
setIsVisible(false);
setObjectId(-1);
setObjectType(-1);
}, [ moveFurniture ]);
UseEventDispatcherHook(RoomWidgetUpdateDecorateModeEvent.UPDATE_DECORATE, eventDispatcher, onRoomWidgetUpdateDecorateModeEvent);
useEffect(() =>
{
if(!isVisible)
{
eventDispatcher.dispatchEvent(new RoomWidgetUpdateDecorateModeEvent(false));
return;
}
eventDispatcher.dispatchEvent(new RoomWidgetUpdateDecorateModeEvent(true));
moveFurniture();
}, [ eventDispatcher, isVisible, moveFurniture ]);
if(!isVisible) return null;
return (
<ObjectLocationView objectId={ objectId } category={ objectType }>
<div className="btn-group">
<button type="button" className="btn btn-primary btn-sm" onClick={ pickupFurniture }>
<FontAwesomeIcon icon="times" />
</button>
<button type="button" className="btn btn-primary btn-sm" onClick={ rotateFurniture }>
<FontAwesomeIcon icon="undo" />
</button>
</div>
</ObjectLocationView>
);
}

View File

@ -1,12 +0,0 @@
export class FurnitureMannequinData
{
constructor(
public objectId: number,
public category: number,
public name: string,
public figure: string,
public gender: string,
public clubLevel: number,
public renderedFigure: string = null)
{}
}

View File

@ -1,21 +0,0 @@
import { FC } from 'react';
import { Base, LayoutAvatarImageView, LayoutCurrencyIcon } from '../../../../../common';
interface FurnitureMannequinPreviewViewProps
{
figure: string;
clubLevel: number;
}
export const FurnitureMannequinPreviewView: FC<FurnitureMannequinPreviewViewProps> = props =>
{
const { figure = null, clubLevel = 0 } = props;
return (
<Base position="relative" className="mannequin-preview">
<LayoutAvatarImageView figure={ figure } direction={ 2 } />
{ (clubLevel > 0) &&
<LayoutCurrencyIcon className="position-absolute end-2 bottom-2" type="hc" /> }
</Base>
);
}

View File

@ -1,221 +0,0 @@
import { AvatarFigurePartType, FurnitureMannequinSaveLookComposer, FurnitureMannequinSaveNameComposer, FurnitureMultiStateComposer, HabboClubLevelEnum, IAvatarFigureContainer, RoomControllerLevel } from '@nitrots/nitro-renderer';
import { FC, KeyboardEvent, useCallback, useEffect, useState } from 'react';
import { GetAvatarRenderManager, GetClubMemberLevel, GetSessionDataManager, LocalizeText, RoomWidgetUpdateMannequinEvent, SendMessageComposer } from '../../../../../api';
import { Base, Button, Column, Flex, LayoutAvatarImageView, LayoutCurrencyIcon, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../../../common';
import { UseEventDispatcherHook } from '../../../../../hooks';
import { useRoomContext } from '../../../RoomContext';
const MODE_NONE: number = -1;
const MODE_CONTROLLER: number = 0;
const MODE_UPDATE: number = 1;
const MODE_PEER: number = 2;
const MODE_NO_CLUB: number = 3;
const MODE_WRONG_GENDER: number = 4;
const ACTION_SET_NAME: number = 1;
const ACTION_WEAR: number = 2;
const ACTION_SAVE: number = 3;
const MANNEQUIN_FIGURE = [ 'hd', 99999, [ 99998 ] ];
const MANNEQUIN_CLOTHING_PART_TYPES = [
AvatarFigurePartType.CHEST_ACCESSORY,
AvatarFigurePartType.COAT_CHEST,
AvatarFigurePartType.CHEST,
AvatarFigurePartType.LEGS,
AvatarFigurePartType.SHOES,
AvatarFigurePartType.WAIST_ACCESSORY
];
export const FurnitureMannequinView: FC<{}> = props =>
{
const [ objectId, setObjectId ] = useState(-1);
const [ figure, setFigure ] = useState<string>(null);
const [ gender, setGender ] = useState<string>(null);
const [ name, setName ] = useState<string>(null);
const [ clubLevel, setClubLevel ] = useState(HabboClubLevelEnum.NO_CLUB);
const [ renderedFigure, setRenderedFigure ] = useState<string>(null);
const [ renderedClubLevel, setRenderedClubLevel ] = useState(HabboClubLevelEnum.NO_CLUB);
const [ mode, setMode ] = useState(MODE_NONE);
const { roomSession = null, eventDispatcher = null } = useRoomContext();
const onRoomWidgetUpdateMannequinEvent = useCallback((event: RoomWidgetUpdateMannequinEvent) =>
{
const figureContainer = GetAvatarRenderManager().createFigureContainer(event.figure);
const figureClubLevel = GetAvatarRenderManager().getFigureClubLevel(figureContainer, event.gender, MANNEQUIN_CLOTHING_PART_TYPES);
setObjectId(event.objectId);
setFigure(event.figure);
setGender(event.gender);
setName(event.name);
setClubLevel(figureClubLevel);
if(roomSession.isRoomOwner || (roomSession.controllerLevel >= RoomControllerLevel.GUEST) || GetSessionDataManager().isModerator)
{
setMode(MODE_CONTROLLER);
}
else if(GetSessionDataManager().gender.toLowerCase() !== event.gender.toLowerCase())
{
setMode(MODE_WRONG_GENDER);
}
else if(GetClubMemberLevel() < figureClubLevel)
{
setMode(MODE_NO_CLUB);
}
else
{
setMode(MODE_PEER);
}
}, [ roomSession ]);
UseEventDispatcherHook(RoomWidgetUpdateMannequinEvent.MANNEQUIN_UPDATE, eventDispatcher, onRoomWidgetUpdateMannequinEvent);
const getMergedFigureContainer = (figure: string, targetFigure: string) =>
{
const figureContainer = GetAvatarRenderManager().createFigureContainer(figure);
const targetFigureContainer = GetAvatarRenderManager().createFigureContainer(targetFigure);
for(const part of MANNEQUIN_CLOTHING_PART_TYPES) figureContainer.removePart(part);
for(const part of targetFigureContainer.getPartTypeIds())
{
figureContainer.updatePart(part, targetFigureContainer.getPartSetId(part), targetFigureContainer.getPartColorIds(part));
}
return figureContainer;
}
const transformAsMannequinFigure = (figureContainer: IAvatarFigureContainer) =>
{
for(const part of figureContainer.getPartTypeIds())
{
if(MANNEQUIN_CLOTHING_PART_TYPES.indexOf(part) >= 0) continue;
figureContainer.removePart(part);
}
figureContainer.updatePart((MANNEQUIN_FIGURE[0] as string), (MANNEQUIN_FIGURE[1] as number), (MANNEQUIN_FIGURE[2] as number[]));
};
const processAction = useCallback((action: number, value: string = null) =>
{
switch(action)
{
case ACTION_SAVE:
SendMessageComposer(new FurnitureMannequinSaveLookComposer(objectId));
break;
case ACTION_WEAR:
SendMessageComposer(new FurnitureMultiStateComposer(objectId));
break;
case ACTION_SET_NAME:
SendMessageComposer(new FurnitureMannequinSaveNameComposer(objectId, name));
return;
}
setMode(MODE_NONE);
}, [ objectId, name ]);
const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) =>
{
if(event.key !== 'Enter') return;
processAction(ACTION_SET_NAME);
};
useEffect(() =>
{
switch(mode)
{
case MODE_CONTROLLER:
case MODE_WRONG_GENDER: {
const figureContainer = GetAvatarRenderManager().createFigureContainer(figure);
transformAsMannequinFigure(figureContainer);
setRenderedFigure(figureContainer.getFigureString());
setRenderedClubLevel(clubLevel);
break;
}
case MODE_UPDATE: {
const figureContainer = GetAvatarRenderManager().createFigureContainer(GetSessionDataManager().figure);
transformAsMannequinFigure(figureContainer);
setRenderedFigure(figureContainer.getFigureString());
setRenderedClubLevel(GetAvatarRenderManager().getFigureClubLevel(figureContainer, GetSessionDataManager().gender, MANNEQUIN_CLOTHING_PART_TYPES));
break;
}
case MODE_PEER:
case MODE_NO_CLUB: {
const figureContainer = getMergedFigureContainer(GetSessionDataManager().figure, figure);
setRenderedFigure(figureContainer.getFigureString());
setRenderedClubLevel(clubLevel);
break;
}
}
}, [ mode, figure, clubLevel ]);
if(mode === MODE_NONE) return null;
return (
<NitroCardView className="nitro-mannequin" theme="primary-slim">
<NitroCardHeaderView headerText={ LocalizeText('mannequin.widget.title') } onCloseClick={ event => setMode(MODE_NONE) } />
<NitroCardContentView center>
<Flex gap={ 2 } overflow="hidden">
<Column>
<Base position="relative" className="mannequin-preview">
<LayoutAvatarImageView figure={ renderedFigure } direction={ 2 } />
{ (clubLevel > 0) &&
<LayoutCurrencyIcon className="position-absolute end-2 bottom-2" type="hc" /> }
</Base>
</Column>
<Column justifyContent="between" overflow="auto">
{ (mode === MODE_CONTROLLER) &&
<>
<input type="text" className="form-control" value={ name } onChange={ event => setName(event.target.value) } onKeyDown={ event => handleKeyDown(event) } />
<Column gap={ 1 }>
<Button variant="success" onClick={ event => setMode(MODE_UPDATE) }>
{ LocalizeText('mannequin.widget.style') }
</Button>
<Button variant="success" onClick={ event => processAction(ACTION_WEAR) }>
{ LocalizeText('mannequin.widget.wear') }
</Button>
</Column>
</> }
{ (mode === MODE_UPDATE) &&
<>
<Column gap={ 1 }>
<Text bold>{ name }</Text>
<Text wrap>{ LocalizeText('mannequin.widget.savetext') }</Text>
</Column>
<Flex alignItems="center" justifyContent="between">
<Text underline pointer onClick={ event => setMode(MODE_CONTROLLER) }>
{ LocalizeText('mannequin.widget.back') }
</Text>
<Button variant="success" onClick={ event => processAction(ACTION_SAVE) }>
{ LocalizeText('mannequin.widget.save') }
</Button>
</Flex>
</> }
{ (mode === MODE_PEER) &&
<>
<Column gap={ 1 }>
<Text bold>{ name }</Text>
<Text>{ LocalizeText('mannequin.widget.weartext') }</Text>
</Column>
<Button variant="success" onClick={ event => processAction(ACTION_WEAR) }>
{ LocalizeText('mannequin.widget.wear') }
</Button>
</> }
{ (mode === MODE_NO_CLUB) &&
<Text>{ LocalizeText('mannequin.widget.clubnotification') }</Text> }
{ (mode === MODE_WRONG_GENDER) &&
<Text>{ LocalizeText('mannequin.widget.wronggender') }</Text> }
</Column>
</Flex>
</NitroCardContentView>
</NitroCardView>
);
}

View File

@ -1,11 +0,0 @@
export class FurnitureStickieData
{
constructor(
public objectId: number,
public category: number,
public color: string,
public text: string,
public canModify: boolean = false,
public isEditing: boolean = false)
{}
}

View File

@ -1,11 +0,0 @@
export const STICKIE_COLORS = [ '9CCEFF','FF9CFF', '9CFF9C','FFFF33' ];
export const STICKIE_COLOR_NAMES = [ 'blue', 'pink', 'green', 'yellow' ];
export function getStickieColorName(color: string): string
{
let index = STICKIE_COLORS.indexOf(color);
if(index === -1) index = 0;
return STICKIE_COLOR_NAMES[index];
}

View File

@ -1,99 +0,0 @@
.nitro-stickie {
position: relative;
width: 185px;
height: 178px;
top: 25px;
left: 25px;
padding: 1px;
pointer-events: all;
.stickie-header {
width: 183px;
height: 18px;
padding: 0 7px;
.header-trash,
.header-close {
cursor: pointer;
}
.stickie-color {
width: 10px;
height: 10px;
cursor: pointer;
}
}
.stickie-context {
width: 183px;
height: 145px;
padding: 2px 7px;
font-size: 12px;
color: $black;
.context-text {
width: 100%;
height: 100%;
padding: 0;
overflow-wrap: break-word;
white-space: break-spaces;
overflow-y: auto;
}
textarea {
background: transparent;
border: 0;
outline: none;
box-shadow: none;
resize: none;
font-style: italic;
&:active {
border: 0;
outline: none;
box-shadow: none;
}
}
}
}
.nitro-stickie-image {
background-image: url('../../../../../assets/images/room-widgets/stickie-widget/stickie-spritesheet.png');
&.stickie-blue,
&.stickie-yellow,
&.stickie-green,
&.stickie-pink {
width: 185px;
height: 178px;
}
&.stickie-blue {
background-position: -2px -2px;
}
&.stickie-yellow {
background-image: url('../../../../../assets/images/room-widgets/stickie-widget/stickie-yellow.png');
//background-position: -191px -184px;
}
&.stickie-green {
background-position: -191px -2px;
}
&.stickie-pink {
background-position: -2px -184px;
}
&.stickie-close {
width: 10px;
height: 10px;
background-position: -2px -366px;
}
&.stickie-trash {
width: 9px;
height: 10px;
background-position: -16px -366px;
}
}

View File

@ -1,135 +0,0 @@
import { NitroEvent, RoomEngineTriggerWidgetEvent, RoomObjectVariable } from '@nitrots/nitro-renderer';
import { FC, useCallback, useState } from 'react';
import { ColorUtils, GetRoomEngine, GetRoomSession, GetSessionDataManager, RoomWidgetUpdateRoomObjectEvent } from '../../../../../api';
import { DraggableWindow, DraggableWindowPosition } from '../../../../../common';
import { UseEventDispatcherHook, UseRoomEngineEvent } from '../../../../../hooks';
import { useRoomContext } from '../../../RoomContext';
import { FurnitureStickieData } from './FurnitureStickieData';
import { getStickieColorName, STICKIE_COLORS } from './FurnitureStickieUtils';
export const FurnitureStickieView: FC<{}> = props =>
{
const { eventDispatcher = null, widgetHandler = null } = useRoomContext();
const [ stickieData, setStickieData ] = useState<FurnitureStickieData>(null);
const onNitroEvent = useCallback((event: NitroEvent) =>
{
switch(event.type)
{
case RoomEngineTriggerWidgetEvent.REQUEST_STICKIE: {
const widgetEvent = (event as RoomEngineTriggerWidgetEvent);
const roomObject = GetRoomEngine().getRoomObject(widgetEvent.roomId, widgetEvent.objectId, widgetEvent.category);
if(!roomObject) return;
const data = roomObject.model.getValue<string>(RoomObjectVariable.FURNITURE_ITEMDATA);
if(data.length < 6) return;
let color: string = null;
let text: string = null;
if(data.indexOf(' ') > 0)
{
color = data.slice(0, data.indexOf(' '));
text = data.slice((data.indexOf(' ') + 1), data.length);
}
else
{
color = data;
}
setStickieData(new FurnitureStickieData(widgetEvent.objectId, widgetEvent.category, color, text, (GetRoomSession().isRoomOwner || GetSessionDataManager().isModerator), false));
return;
}
case RoomWidgetUpdateRoomObjectEvent.FURNI_REMOVED: {
const widgetEvent = (event as RoomWidgetUpdateRoomObjectEvent);
setStickieData(prevState =>
{
if(!prevState || (widgetEvent.id !== prevState.objectId) || (widgetEvent.category !== prevState.category)) return prevState;
return null;
});
return;
}
}
}, []);
UseRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_STICKIE, onNitroEvent);
UseEventDispatcherHook(RoomWidgetUpdateRoomObjectEvent.FURNI_REMOVED, eventDispatcher, onNitroEvent);
const processAction = useCallback((type: string, value: string = null) =>
{
switch(type)
{
case 'close':
setStickieData(null);
return;
case 'trash':
setStickieData(prevState =>
{
if(!prevState) return null;
GetRoomEngine().deleteRoomObject(prevState.objectId, prevState.category);
return null;
});
return;
case 'changeColor':
setStickieData(prevState =>
{
const newStickieData = new FurnitureStickieData(prevState.objectId, prevState.category, value, prevState.text, prevState.canModify);
GetRoomEngine().modifyRoomObjectData(newStickieData.objectId, newStickieData.category, newStickieData.color, newStickieData.text);
return newStickieData;
});
return;
case 'changeText':
setStickieData(prevState =>
{
const newStickieData = new FurnitureStickieData(prevState.objectId, prevState.category, prevState.color, value, prevState.canModify);
GetRoomEngine().modifyRoomObjectData(newStickieData.objectId, newStickieData.category, newStickieData.color, newStickieData.text);
return newStickieData;
});
return;
case 'editMode':
setStickieData(prevValue =>
{
if(!prevValue.canModify) return prevValue;
return new FurnitureStickieData(prevValue.objectId, prevValue.category, prevValue.color, prevValue.text, prevValue.canModify, true);
});
return;
}
}, []);
if(!stickieData) return null;
return (
<DraggableWindow handleSelector=".drag-handler" windowPosition={ DraggableWindowPosition.NOTHING }>
<div className={ 'nitro-stickie nitro-stickie-image stickie-' + getStickieColorName(stickieData.color) }>
<div className="d-flex align-items-center stickie-header drag-handler">
<div className="d-flex align-items-center flex-grow-1 h-100">
{ stickieData.canModify &&
<>
<div className="nitro-stickie-image stickie-trash header-trash" onClick={ event => processAction('trash') }></div>
{ STICKIE_COLORS.map(color =>
{
return <div key={ color } className="stickie-color ms-1" onClick={ event => processAction('changeColor', color) } style={ { backgroundColor: ColorUtils.makeColorHex(color) } } />
}) }
</> }
</div>
<div className="d-flex align-items-center nitro-stickie-image stickie-close header-close" onClick={ event => processAction('close') }></div>
</div>
<div className="stickie-context">
{ !stickieData.isEditing ? <div className="context-text" onClick={ event => processAction('editMode') }>{ stickieData.text }</div> : <textarea className="context-text" defaultValue={ stickieData.text || '' } tabIndex={ 0 } onBlur={ event => processAction('changeText', event.target.value) } autoFocus></textarea> }
</div>
</div>
</DraggableWindow>
);
}

View File

@ -1,12 +0,0 @@
export class FurnitureTrophyData
{
constructor(
public objectId: number,
public category: number,
public color: string,
public ownerName: string,
public date: string,
public message: string,
public customTitle?: string)
{}
}

View File

@ -1,72 +0,0 @@
import { NitroEvent, RoomEngineTriggerWidgetEvent, RoomObjectVariable } from '@nitrots/nitro-renderer';
import { FC, useCallback, useState } from 'react';
import { GetRoomEngine, RoomWidgetUpdateRoomObjectEvent } from '../../../../../api';
import { LayoutTrophyView } from '../../../../../common';
import { UseEventDispatcherHook, UseRoomEngineEvent } from '../../../../../hooks';
import { useRoomContext } from '../../../RoomContext';
import { FurnitureTrophyData } from './FurnitureTrophyData';
export const FurnitureTrophyView: FC<{}> = props =>
{
const { eventDispatcher = null, widgetHandler = null } = useRoomContext();
const [ trophyData, setTrophyData ] = useState<FurnitureTrophyData>(null);
const onNitroEvent = useCallback((event: NitroEvent) =>
{
switch(event.type)
{
case RoomEngineTriggerWidgetEvent.REQUEST_TROPHY: {
const widgetEvent = (event as RoomEngineTriggerWidgetEvent);
const roomObject = GetRoomEngine().getRoomObject(widgetEvent.roomId, widgetEvent.objectId, widgetEvent.category);
if(!roomObject) return;
let data = roomObject.model.getValue<string>(RoomObjectVariable.FURNITURE_DATA);
let extra = roomObject.model.getValue<string>(RoomObjectVariable.FURNITURE_EXTRAS);
if(!extra) extra = '0';
const color = roomObject.model.getValue<string>(RoomObjectVariable.FURNITURE_COLOR);
const ownerName = data.substring(0, data.indexOf('\t'));
data = data.substring((ownerName.length + 1), data.length);
const trophyDate = data.substring(0, data.indexOf('\t'));
const trophyText = data.substr((trophyDate.length + 1), data.length);
setTrophyData(new FurnitureTrophyData(widgetEvent.objectId, widgetEvent.category, color, ownerName, trophyDate, trophyText));
return;
}
case RoomWidgetUpdateRoomObjectEvent.FURNI_REMOVED: {
const widgetEvent = (event as RoomWidgetUpdateRoomObjectEvent);
setTrophyData(prevState =>
{
if(!prevState || (widgetEvent.id !== prevState.objectId) || (widgetEvent.category !== prevState.category)) return prevState;
return null;
});
return;
}
}
}, []);
UseRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_TROPHY, onNitroEvent);
UseEventDispatcherHook(RoomWidgetUpdateRoomObjectEvent.FURNI_REMOVED, widgetHandler.eventDispatcher, onNitroEvent);
const processAction = useCallback((type: string, value: string = null) =>
{
switch(type)
{
case 'close':
setTrophyData(null);
return;
}
}, []);
if(!trophyData) return null;
return <LayoutTrophyView color={ trophyData.color } message={ trophyData.message } date={ trophyData.date } senderName={ trophyData.ownerName } onCloseClick={ () => processAction('close') } />;
}