Merge branch 'dev' into feature/mod-tools

This commit is contained in:
Bill 2021-10-16 02:27:53 -04:00
commit 20af2633dd
52 changed files with 878 additions and 484 deletions

View File

@ -35,6 +35,15 @@ $navigator-height: 420px;
$chat-input-style-selector-widget-width: 200px; $chat-input-style-selector-widget-width: 200px;
$chat-input-style-selector-widget-height: 200px; $chat-input-style-selector-widget-height: 200px;
$user-profile-width: 560px;
$user-profile-height: 500px;
$nitro-widget-custom-stack-height-width: 275px;
$nitro-widget-custom-stack-height-height: 220px;
$nitro-widget-exchange-credit-width: 375px;
$nitro-widget-exchange-credit-height: 150px;
.nitro-app { .nitro-app {
width: 100%; width: 100%;
height: 100%; height: 100%;

View File

@ -0,0 +1,41 @@
import { RoomWidgetUpdateEvent } from './RoomWidgetUpdateEvent';
export class RoomWidgetUpdateMannequinEvent extends RoomWidgetUpdateEvent
{
public static MANNEQUIN_UPDATE: string = 'RWUME_MANNEQUIN_UPDATE';
private _objectId: number;
private _figure: string;
private _gender: string;
private _name: string;
constructor(type: string, objectId: number, figure: string, gender: string, name: string)
{
super(type);
this._objectId = objectId;
this._figure = figure;
this._gender = gender;
this._name = name;
}
public get objectId(): number
{
return this._objectId;
}
public get figure(): string
{
return this._figure;
}
public get gender(): string
{
return this._gender;
}
public get name(): string
{
return this._name;
}
}

View File

@ -1,9 +1,9 @@
import { RoomWidgetUpdateEvent } from './RoomWidgetUpdateEvent'; import { RoomWidgetUpdateEvent } from './RoomWidgetUpdateEvent';
export class RoomWidgetRoomEngineUpdateEvent extends RoomWidgetUpdateEvent export class RoomWidgetUpdateRoomEngineEvent extends RoomWidgetUpdateEvent
{ {
public static GAME_MODE: string = 'RWREUE_GAME_MODE'; public static GAME_MODE: string = 'RWUREE_GAME_MODE';
public static NORMAL_MODE: string = 'RWREUE_NORMAL_MODE'; public static NORMAL_MODE: string = 'RWUREE_NORMAL_MODE';
private _roomId: number = 0; private _roomId: number = 0;

View File

@ -1,16 +1,16 @@
import { RoomWidgetUpdateEvent } from './RoomWidgetUpdateEvent'; import { RoomWidgetUpdateEvent } from './RoomWidgetUpdateEvent';
export class RoomWidgetRoomObjectUpdateEvent extends RoomWidgetUpdateEvent export class RoomWidgetUpdateRoomObjectEvent extends RoomWidgetUpdateEvent
{ {
public static OBJECT_SELECTED: string = 'RWROUE_OBJECT_SELECTED'; public static OBJECT_SELECTED: string = 'RWUROE_OBJECT_SELECTED';
public static OBJECT_DESELECTED: string = 'RWROUE_OBJECT_DESELECTED'; public static OBJECT_DESELECTED: string = 'RWUROE_OBJECT_DESELECTED';
public static USER_REMOVED: string = 'RWROUE_USER_REMOVED'; public static USER_REMOVED: string = 'RWUROE_USER_REMOVED';
public static FURNI_REMOVED: string = 'RWROUE_FURNI_REMOVED'; public static FURNI_REMOVED: string = 'RWUROE_FURNI_REMOVED';
public static FURNI_ADDED: string = 'RWROUE_FURNI_ADDED'; public static FURNI_ADDED: string = 'RWUROE_FURNI_ADDED';
public static USER_ADDED: string = 'RWROUE_USER_ADDED'; public static USER_ADDED: string = 'RWUROE_USER_ADDED';
public static OBJECT_ROLL_OVER: string = 'RWROUE_OBJECT_ROLL_OVER'; public static OBJECT_ROLL_OVER: string = 'RWUROE_OBJECT_ROLL_OVER';
public static OBJECT_ROLL_OUT: string = 'RWROUE_OBJECT_ROLL_OUT'; public static OBJECT_ROLL_OUT: string = 'RWUROE_OBJECT_ROLL_OUT';
public static OBJECT_REQUEST_MANIPULATION: string = 'RWROUE_OBJECT_REQUEST_MANIPULATION'; public static OBJECT_REQUEST_MANIPULATION: string = 'RWUROE_OBJECT_REQUEST_MANIPULATION';
private _id: number; private _id: number;
private _category: number; private _category: number;

View File

@ -0,0 +1,48 @@
import { RoomWidgetUpdateEvent } from './RoomWidgetUpdateEvent';
export class RoomWidgetUpdateTrophyEvent extends RoomWidgetUpdateEvent
{
public static TROPHY_UPDATE: string = 'RWUTE_TROPHY_UPDATE';
private _color: number;
private _name: string;
private _date: string;
private _message: string;
private _extra: number;
constructor(type: string, color: number, name: string, date: string, message: string, extra: number)
{
super(type);
this._color = color;
this._name = name;
this._date = date;
this._message = message;
this._extra = extra;
}
public get color(): number
{
return this._color;
}
public get name(): string
{
return this._name;
}
public get date(): string
{
return this._date;
}
public get message(): string
{
return this._message;
}
public get extra(): number
{
return this._extra;
}
}

View File

@ -6,8 +6,6 @@ export * from './RoomWidgetChooserContentEvent';
export * from './RoomWidgetDoorbellEvent'; export * from './RoomWidgetDoorbellEvent';
export * from './RoomWidgetFloodControlEvent'; export * from './RoomWidgetFloodControlEvent';
export * from './RoomWidgetObjectNameEvent'; export * from './RoomWidgetObjectNameEvent';
export * from './RoomWidgetRoomEngineUpdateEvent';
export * from './RoomWidgetRoomObjectUpdateEvent';
export * from './RoomWidgetUpdateBackgroundColorPreviewEvent'; export * from './RoomWidgetUpdateBackgroundColorPreviewEvent';
export * from './RoomWidgetUpdateChatEvent'; export * from './RoomWidgetUpdateChatEvent';
export * from './RoomWidgetUpdateChatInputContentEvent'; export * from './RoomWidgetUpdateChatInputContentEvent';
@ -24,10 +22,14 @@ export * from './RoomWidgetUpdateInfostandFurniEvent';
export * from './RoomWidgetUpdateInfostandPetEvent'; export * from './RoomWidgetUpdateInfostandPetEvent';
export * from './RoomWidgetUpdateInfostandRentableBotEvent'; export * from './RoomWidgetUpdateInfostandRentableBotEvent';
export * from './RoomWidgetUpdateInfostandUserEvent'; export * from './RoomWidgetUpdateInfostandUserEvent';
export * from './RoomWidgetUpdateMannequinEvent';
export * from './RoomWidgetUpdatePresentDataEvent'; export * from './RoomWidgetUpdatePresentDataEvent';
export * from './RoomWidgetUpdateRentableBotChatEvent'; export * from './RoomWidgetUpdateRentableBotChatEvent';
export * from './RoomWidgetUpdateRoomEngineEvent';
export * from './RoomWidgetUpdateRoomObjectEvent';
export * from './RoomWidgetUpdateRoomViewEvent'; export * from './RoomWidgetUpdateRoomViewEvent';
export * from './RoomWidgetUpdateSongEvent'; export * from './RoomWidgetUpdateSongEvent';
export * from './RoomWidgetUpdateTrophyEvent';
export * from './RoomWidgetUpdateUserDataEvent'; export * from './RoomWidgetUpdateUserDataEvent';
export * from './RoomWidgetUseProductBubbleEvent'; export * from './RoomWidgetUseProductBubbleEvent';
export * from './UseProductItem'; export * from './UseProductItem';

View File

@ -0,0 +1,54 @@
import { NitroEvent, RoomEngineTriggerWidgetEvent, RoomObjectVariable, RoomWidgetEnum } from '@nitrots/nitro-renderer';
import { RoomWidgetUpdateMannequinEvent } from '..';
import { GetRoomEngine } from '../../GetRoomEngine';
import { RoomWidgetUpdateEvent } from '../events/RoomWidgetUpdateEvent';
import { RoomWidgetMessage } from '../messages/RoomWidgetMessage';
import { RoomWidgetHandler } from './RoomWidgetHandler';
export class FurnitureMannequinWidgetHandler extends RoomWidgetHandler
{
public processEvent(event: NitroEvent): void
{
switch(event.type)
{
case RoomEngineTriggerWidgetEvent.REQUEST_MANNEQUIN: {
const widgetEvent = (event as RoomEngineTriggerWidgetEvent);
const roomObject = GetRoomEngine().getRoomObject(widgetEvent.roomId, widgetEvent.objectId, widgetEvent.category);
if(!roomObject) return;
const model = roomObject.model;
const figure = model.getValue<string>(RoomObjectVariable.FURNITURE_MANNEQUIN_FIGURE);
const gender = model.getValue<string>(RoomObjectVariable.FURNITURE_MANNEQUIN_GENDER);
const name = model.getValue<string>(RoomObjectVariable.FURNITURE_MANNEQUIN_NAME);
this.container.eventDispatcher.dispatchEvent(new RoomWidgetUpdateMannequinEvent(RoomWidgetUpdateMannequinEvent.MANNEQUIN_UPDATE, roomObject.id, figure, gender, name));
return;
}
}
}
public processWidgetMessage(message: RoomWidgetMessage): RoomWidgetUpdateEvent
{
switch(message.type)
{
}
return null;
}
public get type(): string
{
return RoomWidgetEnum.MANNEQUIN;
}
public get eventTypes(): string[]
{
return [ RoomEngineTriggerWidgetEvent.REQUEST_MANNEQUIN ];
}
public get messageTypes(): string[]
{
return [];
}
}

View File

@ -5,6 +5,7 @@ export * from './FurnitureCreditWidgetHandler';
export * from './FurnitureCustomStackHeightWidgetHandler'; export * from './FurnitureCustomStackHeightWidgetHandler';
export * from './FurnitureDimmerWidgetHandler'; export * from './FurnitureDimmerWidgetHandler';
export * from './FurnitureExternalImageWidgetHandler'; export * from './FurnitureExternalImageWidgetHandler';
export * from './FurnitureMannequinWidgetHandler';
export * from './FurniturePresentWidgetHandler'; export * from './FurniturePresentWidgetHandler';
export * from './IRoomWidgetHandler'; export * from './IRoomWidgetHandler';
export * from './IRoomWidgetHandlerManager'; export * from './IRoomWidgetHandlerManager';

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

View File

@ -13,6 +13,7 @@ $nitro-card-tabs-height: 33px;
width: 100%; width: 100%;
height: 100%; height: 100%;
pointer-events: none; pointer-events: none;
overflow: hidden;
.theme-primary { .theme-primary {
border: $border-width solid $border-color; border: $border-width solid $border-color;
@ -25,7 +26,7 @@ $nitro-card-tabs-height: 33px;
left: 0 !important; left: 0 !important;
width: 100%; width: 100%;
height: 100%; height: 100%;
transform: none !important; //transform: none !important;
overflow: hidden; overflow: hidden;
} }
@ -39,6 +40,29 @@ $nitro-card-tabs-height: 33px;
// } // }
} }
} }
@include media-breakpoint-down(sm) {
.draggable-window {
top: 0 !important;
left: 0 !important;
width: 100%;
height: 100%;
//transform: none !important;
overflow: hidden;
}
.nitro-card {
width: 100%;
max-width: 100%;
max-height: calc(100% - #{$toolbar-height});
margin: 0;
&.rounded {
border-radius: 0 !important;
}
}
}
} }
@import './accordion/NitroCardAccordionView'; @import './accordion/NitroCardAccordionView';

View File

@ -1 +1 @@
export type NitroLayoutSpacing = 1 | 2 | 3 | 4 | 5; export type NitroLayoutSpacing = 0 | 1 | 2 | 3 | 4 | 5;

View File

@ -1,5 +1,5 @@
import { MouseEventType } from '@nitrots/nitro-renderer'; import { MouseEventType, TouchEventType } from '@nitrots/nitro-renderer';
import { FC, Key, MouseEvent as ReactMouseEvent, useCallback, useEffect, useRef, useState } from 'react'; import { FC, Key, MouseEvent as ReactMouseEvent, TouchEvent as ReactTouchEvent, useCallback, useEffect, useRef, useState } from 'react';
import { DraggableWindowPosition, DraggableWindowProps } from './DraggableWindow.types'; import { DraggableWindowPosition, DraggableWindowProps } from './DraggableWindow.types';
const CURRENT_WINDOWS: HTMLElement[] = []; const CURRENT_WINDOWS: HTMLElement[] = [];
@ -29,7 +29,7 @@ export const DraggableWindow: FC<DraggableWindowProps> = props =>
} }
}, []); }, []);
const onMouseDown = useCallback((event: ReactMouseEvent) => const moveCurrentWindow = useCallback(() =>
{ {
const index = CURRENT_WINDOWS.indexOf(elementRef.current); const index = CURRENT_WINDOWS.indexOf(elementRef.current);
@ -50,18 +50,47 @@ export const DraggableWindow: FC<DraggableWindowProps> = props =>
bringToTop(); bringToTop();
}, [ bringToTop ]); }, [ bringToTop ]);
const onDragMouseDown = useCallback((event: MouseEvent) => const onMouseDown = useCallback((event: ReactMouseEvent<HTMLDivElement>) =>
{ {
setStart({ x: event.clientX, y: event.clientY }); moveCurrentWindow();
}, [ moveCurrentWindow ]);
const onTouchStart = useCallback((event: ReactTouchEvent<HTMLDivElement>) =>
{
moveCurrentWindow();
}, [ moveCurrentWindow ]);
const startDragging = useCallback((startX: number, startY: number) =>
{
setStart({ x: startX, y: startY });
setIsDragging(true); setIsDragging(true);
}, []); }, []);
const onDragMouseDown = useCallback((event: MouseEvent) =>
{
startDragging(event.clientX, event.clientY);
}, [ startDragging ]);
const onTouchDown = useCallback((event: TouchEvent) =>
{
const touch = event.touches[0];
startDragging(touch.clientX, touch.clientY);
}, [ startDragging ]);
const onDragMouseMove = useCallback((event: MouseEvent) => const onDragMouseMove = useCallback((event: MouseEvent) =>
{ {
setDelta({ x: (event.clientX - start.x), y: (event.clientY - start.y) }); setDelta({ x: (event.clientX - start.x), y: (event.clientY - start.y) });
}, [ start ]); }, [ start ]);
const onDragMouseUp = useCallback((event: MouseEvent) => const onDragTouchMove = useCallback((event: TouchEvent) =>
{
const touch = event.touches[0];
setDelta({ x: (touch.clientX - start.x), y: (touch.clientY - start.y) });
}, [ start ]);
const completeDrag = useCallback(() =>
{ {
if(!elementRef.current || !dragHandler) return; if(!elementRef.current || !dragHandler) return;
@ -98,6 +127,16 @@ export const DraggableWindow: FC<DraggableWindowProps> = props =>
if(uniqueKey !== null) POS_MEMORY.set(uniqueKey, { x: offsetX, y: offsetY }); if(uniqueKey !== null) POS_MEMORY.set(uniqueKey, { x: offsetX, y: offsetY });
}, [ dragHandler, delta, offset, uniqueKey ]); }, [ dragHandler, delta, offset, uniqueKey ]);
const onDragMouseUp = useCallback((event: MouseEvent) =>
{
completeDrag();
}, [ completeDrag ]);
const onDragTouchUp = useCallback((event: TouchEvent) =>
{
completeDrag();
}, [ completeDrag ]);
useEffect(() => useEffect(() =>
{ {
const element = (elementRef.current as HTMLElement); const element = (elementRef.current as HTMLElement);
@ -169,29 +208,35 @@ export const DraggableWindow: FC<DraggableWindowProps> = props =>
if(!dragHandler) return; if(!dragHandler) return;
dragHandler.addEventListener(MouseEventType.MOUSE_DOWN, onDragMouseDown); dragHandler.addEventListener(MouseEventType.MOUSE_DOWN, onDragMouseDown);
dragHandler.addEventListener(TouchEventType.TOUCH_START, onTouchDown);
return () => return () =>
{ {
dragHandler.removeEventListener(MouseEventType.MOUSE_DOWN, onDragMouseDown); dragHandler.removeEventListener(MouseEventType.MOUSE_DOWN, onDragMouseDown);
dragHandler.removeEventListener(TouchEventType.TOUCH_START, onTouchDown);
} }
}, [ dragHandler, onDragMouseDown ]); }, [ dragHandler, onDragMouseDown, onTouchDown ]);
useEffect(() => useEffect(() =>
{ {
if(!isDragging) return; if(!isDragging) return;
document.addEventListener(MouseEventType.MOUSE_UP, onDragMouseUp); document.addEventListener(MouseEventType.MOUSE_UP, onDragMouseUp);
document.addEventListener(TouchEventType.TOUCH_END, onDragTouchUp);
document.addEventListener(MouseEventType.MOUSE_MOVE, onDragMouseMove); document.addEventListener(MouseEventType.MOUSE_MOVE, onDragMouseMove);
document.addEventListener(TouchEventType.TOUCH_MOVE, onDragTouchMove);
return () => return () =>
{ {
document.removeEventListener(MouseEventType.MOUSE_UP, onDragMouseUp); document.removeEventListener(MouseEventType.MOUSE_UP, onDragMouseUp);
document.removeEventListener(TouchEventType.TOUCH_END, onDragTouchUp);
document.removeEventListener(MouseEventType.MOUSE_MOVE, onDragMouseMove); document.removeEventListener(MouseEventType.MOUSE_MOVE, onDragMouseMove);
document.removeEventListener(TouchEventType.TOUCH_MOVE, onDragTouchMove);
} }
}, [ isDragging, onDragMouseUp, onDragMouseMove ]); }, [ isDragging, onDragMouseUp, onDragMouseMove, onDragTouchUp, onDragTouchMove ]);
return ( return (
<div ref={ elementRef } className="position-absolute draggable-window" onMouseDownCapture={ onMouseDown }> <div ref={ elementRef } className="position-absolute draggable-window" onMouseDownCapture={ onMouseDown } onTouchStartCapture={ onTouchStart }>
{ children } { children }
</div> </div>
); );

View File

@ -1,28 +1,46 @@
import { FC, useEffect, useState } from 'react'; import { FC, useEffect, useMemo, useState } from 'react';
import { NitroLayoutBase } from '../base';
import { TransitionAnimation, TransitionAnimationTypes } from '../transitions';
import { NotificationBubbleViewProps } from './NotificationBubbleView.types'; import { NotificationBubbleViewProps } from './NotificationBubbleView.types';
export const NotificationBubbleView: FC<NotificationBubbleViewProps> = props => export const NotificationBubbleView: FC<NotificationBubbleViewProps> = props =>
{ {
const { fadesOut = false, close = null, className = '', children = null, ...rest } = props; const { fadesOut = true, timeoutMs = 8000, close = null, className = '', ...rest } = props;
const [ isFading, setIsFading ] = useState(false); const [ isVisible, setIsVisible ] = useState(false);
const getClassName = useMemo(() =>
{
let newClassName = 'nitro-notification-bubble rounded';
if(className && className.length) newClassName += ` ${ className }`;
return newClassName;
}, [ className ]);
useEffect(() =>
{
setIsVisible(true);
return () => setIsVisible(false);
}, []);
useEffect(() => useEffect(() =>
{ {
if(!fadesOut) return; if(!fadesOut) return;
const timeout = setTimeout(() => const timeout = setTimeout(() =>
{ {
setIsFading(true); setIsVisible(false);
setTimeout(() => close()); setTimeout(() => close(), 300);
}, 8000); }, timeoutMs);
return () => clearTimeout(timeout); return () => clearTimeout(timeout);
}, [ fadesOut, close ]); }, [ fadesOut, timeoutMs, close ]);
return ( return (
<div className={ ('nitro-notification-bubble rounded ' + (className || '')) } { ...rest } onClick={ close }> <TransitionAnimation type={ TransitionAnimationTypes.FADE_IN } inProp={ isVisible } timeout={ 300 }>
{ children } <NitroLayoutBase className={ getClassName } onClick={ close } { ...rest } />
</div> </TransitionAnimation>
) );
} }

View File

@ -1,7 +1,8 @@
import { DetailsHTMLAttributes } from 'react'; import { NitroLayoutBaseProps } from '../base';
export interface NotificationBubbleViewProps extends DetailsHTMLAttributes<HTMLDivElement> export interface NotificationBubbleViewProps extends NitroLayoutBaseProps
{ {
fadesOut?: boolean; fadesOut?: boolean;
timeoutMs?: number;
close: () => void; close: () => void;
} }

View File

@ -3,6 +3,7 @@
width: 340px; width: 340px;
height: 173px; height: 173px;
color: black; color: black;
pointer-events: all;
background-position: 0px 0px; background-position: 0px 0px;
background-image: url('../../assets/images/room-widgets/trophy-widget/trophy-spritesheet.png'); background-image: url('../../assets/images/room-widgets/trophy-widget/trophy-spritesheet.png');

View File

@ -158,6 +158,11 @@ export const AchievementsView: FC<AchievementsViewProps> = props =>
return progress; return progress;
}, [ achievementCategories ]); }, [ achievementCategories ]);
const scaledProgressPercent = useMemo(() =>
{
return ~~((((getProgress - 0) * (100 - 0)) / (getMaxProgress - 0)) + 0);
}, [ getProgress, getMaxProgress ]);
const getSelectedCategory = useMemo(() => const getSelectedCategory = useMemo(() =>
{ {
if(!achievementCategories || !achievementCategories.length) return null; if(!achievementCategories || !achievementCategories.length) return null;
@ -220,8 +225,10 @@ export const AchievementsView: FC<AchievementsViewProps> = props =>
<> <>
<AchievementsCategoryListView categories={ achievementCategories } selectedCategoryCode={ selectedCategoryCode } setSelectedCategoryCode={ setSelectedCategoryCode } /> <AchievementsCategoryListView categories={ achievementCategories } selectedCategoryCode={ selectedCategoryCode } setSelectedCategoryCode={ setSelectedCategoryCode } />
<NitroLayoutFlexColumn className="flex-grow-1 justify-content-end" gap={ 2 }> <NitroLayoutFlexColumn className="flex-grow-1 justify-content-end" gap={ 2 }>
<NitroLayoutBase className="bg-muted text-black text-center rounded"> <NitroLayoutBase className="progress">
{ LocalizeText('achievements.categories.totalprogress', [ 'progress', 'limit' ], [ getProgress.toString(), getMaxProgress.toString() ]) } <NitroLayoutBase className="progress-bar" style={ { width: (scaledProgressPercent + '%') }}>
{ LocalizeText('achievements.categories.totalprogress', [ 'progress', 'limit' ], [ getProgress.toString(), getMaxProgress.toString() ]) }
</NitroLayoutBase>
</NitroLayoutBase> </NitroLayoutBase>
<NitroLayoutBase className="bg-muted text-black text-center rounded"> <NitroLayoutBase className="bg-muted text-black text-center rounded">
{ LocalizeText('achievements.categories.score', [ 'score' ], [ achievementScore.toString() ]) } { LocalizeText('achievements.categories.score', [ 'score' ], [ achievementScore.toString() ]) }

View File

@ -0,0 +1,8 @@
import { AchievementData } from '@nitrots/nitro-renderer';
export const GetAchievementLevel = (achievement: AchievementData) =>
{
if(achievement.finalLevel) return achievement.level;
return (achievement.level - 1);
}

View File

@ -0,0 +1,8 @@
import { AchievementData } from '@nitrots/nitro-renderer';
export const GetScaledProgressPercent = (achievement: AchievementData) =>
{
if(!achievement) return 0;
return ~~(((((achievement.currentPoints + achievement.scoreAtStartOfLevel) - 0) * (100 - 0)) / ((achievement.scoreLimit + achievement.scoreAtStartOfLevel) - 0)) + 0);
}

View File

@ -1,9 +1,11 @@
import { FC, useMemo } from 'react'; import { FC } from 'react';
import { LocalizeBadgeDescription, LocalizeBadgeName, LocalizeText } from '../../../../api'; import { LocalizeBadgeDescription, LocalizeBadgeName, LocalizeText } from '../../../../api';
import { NitroLayoutFlex, NitroLayoutFlexColumn } from '../../../../layout'; import { NitroLayoutFlex, NitroLayoutFlexColumn } from '../../../../layout';
import { NitroLayoutBase } from '../../../../layout/base'; import { NitroLayoutBase } from '../../../../layout/base';
import { CurrencyIcon } from '../../../shared/currency-icon/CurrencyIcon'; import { CurrencyIcon } from '../../../shared/currency-icon/CurrencyIcon';
import { AchievementUtilities } from '../../common/AchievementUtilities'; import { AchievementUtilities } from '../../common/AchievementUtilities';
import { GetAchievementLevel } from '../../common/GetAchievementLevel';
import { GetScaledProgressPercent } from '../../common/GetScaledProgressPercent';
import { AchievementBadgeView } from '../achievement-badge/AchievementBadgeView'; import { AchievementBadgeView } from '../achievement-badge/AchievementBadgeView';
import { AchievementDetailsViewProps } from './AchievementDetailsView.types'; import { AchievementDetailsViewProps } from './AchievementDetailsView.types';
@ -11,25 +13,17 @@ export const AchievementDetailsView: FC<AchievementDetailsViewProps> = props =>
{ {
const { achievement = null } = props; const { achievement = null } = props;
const getAchievementLevel = useMemo(() =>
{
if(achievement.finalLevel) return achievement.level;
return (achievement.level - 1);
}, [ achievement ]);
if(!achievement) return null; if(!achievement) return null;
const currentAmount = achievement.currentPoints; const achievementLevel = GetAchievementLevel(achievement);
const maxAmount = achievement.scoreLimit; const scaledProgressPercent = GetScaledProgressPercent(achievement);
const scoreAtStartOfLevel = achievement.scoreAtStartOfLevel;
return ( return (
<NitroLayoutFlex className="bg-muted rounded p-2 text-black flex-shrink-0" gap={ 2 } overflow="hidden"> <NitroLayoutFlex className="bg-muted rounded p-2 text-black flex-shrink-0" gap={ 2 } overflow="hidden">
<NitroLayoutFlexColumn className="justify-content-center align-items-center"> <NitroLayoutFlexColumn className="justify-content-center align-items-center">
<AchievementBadgeView className="nitro-achievements-badge-image" achievement={ achievement } scale={ 2 } /> <AchievementBadgeView className="nitro-achievements-badge-image" achievement={ achievement } scale={ 2 } />
<NitroLayoutBase className="fw-bold"> <NitroLayoutBase className="fw-bold">
{ LocalizeText('achievements.details.level', [ 'level', 'limit' ], [ getAchievementLevel.toString(), achievement.levelCount.toString() ]) } { LocalizeText('achievements.details.level', [ 'level', 'limit' ], [ achievementLevel.toString(), achievement.levelCount.toString() ]) }
</NitroLayoutBase> </NitroLayoutBase>
</NitroLayoutFlexColumn> </NitroLayoutFlexColumn>
<NitroLayoutFlexColumn className="justify-content-center w-100" overflow="hidden" gap={ 2 }> <NitroLayoutFlexColumn className="justify-content-center w-100" overflow="hidden" gap={ 2 }>
@ -41,20 +35,24 @@ export const AchievementDetailsView: FC<AchievementDetailsViewProps> = props =>
{ LocalizeBadgeDescription(AchievementUtilities.getBadgeCode(achievement)) } { LocalizeBadgeDescription(AchievementUtilities.getBadgeCode(achievement)) }
</NitroLayoutBase> </NitroLayoutBase>
</NitroLayoutFlexColumn> </NitroLayoutFlexColumn>
{ ((achievement.levelRewardPoints > 0) || (maxAmount > 0)) && { ((achievement.levelRewardPoints > 0) || (achievement.scoreLimit > 0)) &&
<NitroLayoutFlexColumn gap={ 1 }> <NitroLayoutFlexColumn gap={ 1 }>
{ (achievement.levelRewardPoints > 0) && { (achievement.levelRewardPoints > 0) &&
<NitroLayoutFlex gap={ 1 }> <NitroLayoutFlex gap={ 1 }>
<NitroLayoutBase className="text-truncate"> <NitroLayoutBase className="text-truncate small">
{ LocalizeText('achievements.details.reward') } { LocalizeText('achievements.details.reward') }
</NitroLayoutBase> </NitroLayoutBase>
<NitroLayoutFlex className="fw-bold align-items-center justify-content-center" gap={ 1 }> <NitroLayoutFlex className="fw-bold align-items-center justify-content-center small" gap={ 1 }>
{ achievement.levelRewardPoints } { achievement.levelRewardPoints }
<CurrencyIcon type={ achievement.levelRewardPointType } /> <CurrencyIcon type={ achievement.levelRewardPointType } />
</NitroLayoutFlex> </NitroLayoutFlex>
</NitroLayoutFlex> } </NitroLayoutFlex> }
{ (maxAmount > 0) && { (achievement.scoreLimit > 0) &&
LocalizeText('achievements.details.progress', [ 'progress', 'limit' ], [ (currentAmount + scoreAtStartOfLevel).toString(), (maxAmount + scoreAtStartOfLevel).toString() ]) } <NitroLayoutBase className="progress">
<NitroLayoutBase className="progress-bar" style={ { width: (scaledProgressPercent + '%') }}>
{ LocalizeText('achievements.details.progress', [ 'progress', 'limit' ], [ (achievement.currentPoints + achievement.scoreAtStartOfLevel).toString(), (achievement.scoreLimit + achievement.scoreAtStartOfLevel).toString() ]) }
</NitroLayoutBase>
</NitroLayoutBase> }
</NitroLayoutFlexColumn> } </NitroLayoutFlexColumn> }
</NitroLayoutFlexColumn> </NitroLayoutFlexColumn>
</NitroLayoutFlex> </NitroLayoutFlex>

View File

@ -9,9 +9,9 @@ export const AchievementListView: FC<AchievementListViewProps> = props =>
return ( return (
<NitroCardGridView { ...rest }> <NitroCardGridView { ...rest }>
{ achievements && (achievements.length > 0) && achievements.map(achievement => { achievements && (achievements.length > 0) && achievements.map((achievement, index) =>
{ {
return <AchievementListItemView key={ achievement.achievementId } achievement={ achievement } itemActive={ (selectedAchievementId === achievement.achievementId) } onClick={ event => setSelectedAchievementId(achievement.achievementId) } />; return <AchievementListItemView key={ index } achievement={ achievement } itemActive={ (selectedAchievementId === achievement.achievementId) } onClick={ event => setSelectedAchievementId(achievement.achievementId) } />;
}) } }) }
</NitroCardGridView> </NitroCardGridView>
); );

View File

@ -54,8 +54,11 @@ export const CatalogGiftView: FC<{}> = props =>
if(giftData.colors && giftData.colors.length > 0) newColors.push({ id: colorId, color: `#${giftData.colors[0].toString(16)}` }); if(giftData.colors && giftData.colors.length > 0) newColors.push({ id: colorId, color: `#${giftData.colors[0].toString(16)}` });
} }
setSelectedColorId(newColors[0].id); if(newColors.length)
setColors(newColors); {
setSelectedColorId(newColors[0].id);
setColors(newColors);
}
}, [ giftConfiguration ]); }, [ giftConfiguration ]);
const close = useCallback(() => const close = useCallback(() =>
@ -68,7 +71,8 @@ export const CatalogGiftView: FC<{}> = props =>
setMessage(''); setMessage('');
setSelectedBoxIndex(0); setSelectedBoxIndex(0);
setSelectedRibbonIndex(0); setSelectedRibbonIndex(0);
setSelectedColorId(colors[0].id);
if(colors.length) setSelectedColorId(colors[0].id);
}, [ colors ]); }, [ colors ]);
const onCatalogEvent = useCallback((event: CatalogEvent) => const onCatalogEvent = useCallback((event: CatalogEvent) =>

View File

@ -85,7 +85,7 @@ export const InventoryBadgeView: FC<InventoryBadgeViewProps> = props =>
</NitroLayoutGridColumn> </NitroLayoutGridColumn>
<NitroLayoutGridColumn className="justify-content-between" size={ 5 } overflow="auto"> <NitroLayoutGridColumn className="justify-content-between" size={ 5 } overflow="auto">
<NitroLayoutFlexColumn overflow="hidden" gap={ 2 }> <NitroLayoutFlexColumn overflow="hidden" gap={ 2 }>
<NitroLayoutBase className="text-black text-truncate">{ LocalizeText('inventory.badges.activebadges') }</NitroLayoutBase> <NitroLayoutBase className="text-black text-truncate flex-shrink-0">{ LocalizeText('inventory.badges.activebadges') }</NitroLayoutBase>
<InventoryActiveBadgeResultsView badges={ activeBadges } /> <InventoryActiveBadgeResultsView badges={ activeBadges } />
</NitroLayoutFlexColumn> </NitroLayoutFlexColumn>
{ badge && (badge.length > 0) && { badge && (badge.length > 0) &&

View File

@ -10,7 +10,7 @@ export const NotificationClubGiftBubbleView: FC<NotificationBubbleLayoutViewProp
const { item = null, close = null, ...rest } = props; const { item = null, close = null, ...rest } = props;
return ( return (
<NotificationBubbleView className="flex-column club-gift" close={ close } { ...rest }> <NotificationBubbleView fadesOut={ false } className="flex-column club-gift" close={ close } { ...rest }>
<div className="d-flex align-items-center gap-2 mb-2"> <div className="d-flex align-items-center gap-2 mb-2">
<CurrencyIcon type="hc" className="flex-shrink-0" /> <CurrencyIcon type="hc" className="flex-shrink-0" />
<span className="ms-1">{ LocalizeText('notifications.text.club_gift') }</span> <span className="ms-1">{ LocalizeText('notifications.text.club_gift') }</span>

View File

@ -7,7 +7,7 @@ export const NotificationDefaultBubbleView: FC<NotificationDefaultBubbleViewProp
const { item = null, close = null, ...rest } = props; const { item = null, close = null, ...rest } = props;
return ( return (
<NotificationBubbleView className="d-flex" fadesOut={ false } close={ close } { ...rest }> <NotificationBubbleView className="d-flex" close={ close } { ...rest }>
{ (item.iconUrl && item.iconUrl.length) && <img className="bubble-image no-select" src={ item.iconUrl } alt="" /> } { (item.iconUrl && item.iconUrl.length) && <img className="bubble-image no-select" src={ item.iconUrl } alt="" /> }
<span>{ item.message }</span> <span>{ item.message }</span>
</NotificationBubbleView> </NotificationBubbleView>

View File

@ -1,6 +1,6 @@
import { EventDispatcher, NitroRectangle, RoomGeometry, RoomVariableEnum, Vector3d } from '@nitrots/nitro-renderer'; import { EventDispatcher, NitroRectangle, RoomGeometry, RoomVariableEnum, Vector3d } from '@nitrots/nitro-renderer';
import { FC, useEffect, useRef, useState } from 'react'; import { FC, useEffect, useRef, useState } from 'react';
import { DispatchMouseEvent, DispatchTouchEvent, DoorbellWidgetHandler, FurniChooserWidgetHandler, FurnitureContextMenuWidgetHandler, FurnitureCreditWidgetHandler, FurnitureCustomStackHeightWidgetHandler, FurnitureDimmerWidgetHandler, FurnitureExternalImageWidgetHandler, FurniturePresentWidgetHandler, GetNitroInstance, GetRoomEngine, InitializeRoomInstanceRenderingCanvas, IRoomWidgetHandlerManager, RoomWidgetAvatarInfoHandler, RoomWidgetChatHandler, RoomWidgetChatInputHandler, RoomWidgetHandlerManager, RoomWidgetInfostandHandler, RoomWidgetRoomToolsHandler, RoomWidgetUpdateRoomViewEvent, UserChooserWidgetHandler } from '../../api'; import { DispatchMouseEvent, DispatchTouchEvent, DoorbellWidgetHandler, FurniChooserWidgetHandler, FurnitureContextMenuWidgetHandler, FurnitureCreditWidgetHandler, FurnitureCustomStackHeightWidgetHandler, FurnitureDimmerWidgetHandler, FurnitureExternalImageWidgetHandler, FurnitureMannequinWidgetHandler, FurniturePresentWidgetHandler, GetNitroInstance, GetRoomEngine, InitializeRoomInstanceRenderingCanvas, IRoomWidgetHandlerManager, RoomWidgetAvatarInfoHandler, RoomWidgetChatHandler, RoomWidgetChatInputHandler, RoomWidgetHandlerManager, RoomWidgetInfostandHandler, RoomWidgetRoomToolsHandler, RoomWidgetUpdateRoomViewEvent, UserChooserWidgetHandler } from '../../api';
import { RoomContextProvider } from './context/RoomContext'; import { RoomContextProvider } from './context/RoomContext';
import { RoomColorView } from './RoomColorView'; import { RoomColorView } from './RoomColorView';
import { RoomViewProps } from './RoomView.types'; import { RoomViewProps } from './RoomView.types';
@ -44,6 +44,7 @@ export const RoomView: FC<RoomViewProps> = props =>
widgetHandlerManager.registerHandler(new FurnitureExternalImageWidgetHandler()); widgetHandlerManager.registerHandler(new FurnitureExternalImageWidgetHandler());
widgetHandlerManager.registerHandler(new FurniturePresentWidgetHandler()); widgetHandlerManager.registerHandler(new FurniturePresentWidgetHandler());
widgetHandlerManager.registerHandler(new FurnitureDimmerWidgetHandler()); widgetHandlerManager.registerHandler(new FurnitureDimmerWidgetHandler());
widgetHandlerManager.registerHandler(new FurnitureMannequinWidgetHandler());
setWidgetHandler(widgetHandlerManager); setWidgetHandler(widgetHandlerManager);

View File

@ -1,6 +1,6 @@
import { RoomEngineEvent, RoomEngineObjectEvent, RoomEngineRoomAdEvent, RoomEngineTriggerWidgetEvent, RoomEngineUseProductEvent, RoomId, RoomObjectCategory, RoomObjectOperationType, RoomObjectVariable, RoomSessionChatEvent, RoomSessionDanceEvent, RoomSessionDimmerPresetsEvent, RoomSessionDoorbellEvent, RoomSessionErrorMessageEvent, RoomSessionEvent, RoomSessionFriendRequestEvent, RoomSessionPetInfoUpdateEvent, RoomSessionPresentEvent, RoomSessionUserBadgesEvent, RoomZoomEvent } from '@nitrots/nitro-renderer'; import { RoomEngineEvent, RoomEngineObjectEvent, RoomEngineRoomAdEvent, RoomEngineTriggerWidgetEvent, RoomEngineUseProductEvent, RoomId, RoomObjectCategory, RoomObjectOperationType, RoomObjectVariable, RoomSessionChatEvent, RoomSessionDanceEvent, RoomSessionDimmerPresetsEvent, RoomSessionDoorbellEvent, RoomSessionErrorMessageEvent, RoomSessionEvent, RoomSessionFriendRequestEvent, RoomSessionPetInfoUpdateEvent, RoomSessionPresentEvent, RoomSessionUserBadgesEvent, RoomZoomEvent } from '@nitrots/nitro-renderer';
import { FC, useCallback } from 'react'; import { FC, useCallback } from 'react';
import { CanManipulateFurniture, GetRoomEngine, GetSessionDataManager, IsFurnitureSelectionDisabled, LocalizeText, ProcessRoomObjectOperation, RoomWidgetFurniToWidgetMessage, RoomWidgetRoomEngineUpdateEvent, RoomWidgetRoomObjectUpdateEvent } from '../../../api'; import { CanManipulateFurniture, GetRoomEngine, GetSessionDataManager, IsFurnitureSelectionDisabled, LocalizeText, ProcessRoomObjectOperation, RoomWidgetFurniToWidgetMessage, RoomWidgetUpdateRoomEngineEvent, RoomWidgetUpdateRoomObjectEvent } from '../../../api';
import { useRoomEngineEvent, useRoomSessionManagerEvent } from '../../../hooks/events'; import { useRoomEngineEvent, useRoomSessionManagerEvent } from '../../../hooks/events';
import { NotificationAlertType } from '../../notification-center/common/NotificationAlertType'; import { NotificationAlertType } from '../../notification-center/common/NotificationAlertType';
import { NotificationUtilities } from '../../notification-center/common/NotificationUtilities'; import { NotificationUtilities } from '../../notification-center/common/NotificationUtilities';
@ -27,10 +27,10 @@ export const RoomWidgetsView: FC<RoomWidgetViewProps> = props =>
switch(event.type) switch(event.type)
{ {
case RoomEngineEvent.NORMAL_MODE: case RoomEngineEvent.NORMAL_MODE:
eventDispatcher.dispatchEvent(new RoomWidgetRoomEngineUpdateEvent(RoomWidgetRoomEngineUpdateEvent.NORMAL_MODE, event.roomId)); eventDispatcher.dispatchEvent(new RoomWidgetUpdateRoomEngineEvent(RoomWidgetUpdateRoomEngineEvent.NORMAL_MODE, event.roomId));
return; return;
case RoomEngineEvent.GAME_MODE: case RoomEngineEvent.GAME_MODE:
eventDispatcher.dispatchEvent(new RoomWidgetRoomEngineUpdateEvent(RoomWidgetRoomEngineUpdateEvent.GAME_MODE, event.roomId)); eventDispatcher.dispatchEvent(new RoomWidgetUpdateRoomEngineEvent(RoomWidgetUpdateRoomEngineEvent.GAME_MODE, event.roomId));
return; return;
case RoomZoomEvent.ROOM_ZOOM: { case RoomZoomEvent.ROOM_ZOOM: {
const zoomEvent = (event as RoomZoomEvent); const zoomEvent = (event as RoomZoomEvent);
@ -67,15 +67,15 @@ export const RoomWidgetsView: FC<RoomWidgetViewProps> = props =>
const objectId = event.objectId; const objectId = event.objectId;
const category = event.category; const category = event.category;
let updateEvent: RoomWidgetRoomObjectUpdateEvent = null; let updateEvent: RoomWidgetUpdateRoomObjectEvent = null;
switch(event.type) switch(event.type)
{ {
case RoomEngineObjectEvent.SELECTED: case RoomEngineObjectEvent.SELECTED:
if(!IsFurnitureSelectionDisabled(event)) updateEvent = new RoomWidgetRoomObjectUpdateEvent(RoomWidgetRoomObjectUpdateEvent.OBJECT_SELECTED, objectId, category, event.roomId); if(!IsFurnitureSelectionDisabled(event)) updateEvent = new RoomWidgetUpdateRoomObjectEvent(RoomWidgetUpdateRoomObjectEvent.OBJECT_SELECTED, objectId, category, event.roomId);
break; break;
case RoomEngineObjectEvent.DESELECTED: case RoomEngineObjectEvent.DESELECTED:
updateEvent = new RoomWidgetRoomObjectUpdateEvent(RoomWidgetRoomObjectUpdateEvent.OBJECT_DESELECTED, objectId, category, event.roomId); updateEvent = new RoomWidgetUpdateRoomObjectEvent(RoomWidgetUpdateRoomObjectEvent.OBJECT_DESELECTED, objectId, category, event.roomId);
break; break;
case RoomEngineObjectEvent.ADDED: { case RoomEngineObjectEvent.ADDED: {
let addedEventType: string = null; let addedEventType: string = null;
@ -84,14 +84,14 @@ export const RoomWidgetsView: FC<RoomWidgetViewProps> = props =>
{ {
case RoomObjectCategory.FLOOR: case RoomObjectCategory.FLOOR:
case RoomObjectCategory.WALL: case RoomObjectCategory.WALL:
addedEventType = RoomWidgetRoomObjectUpdateEvent.FURNI_ADDED; addedEventType = RoomWidgetUpdateRoomObjectEvent.FURNI_ADDED;
break; break;
case RoomObjectCategory.UNIT: case RoomObjectCategory.UNIT:
addedEventType = RoomWidgetRoomObjectUpdateEvent.USER_ADDED; addedEventType = RoomWidgetUpdateRoomObjectEvent.USER_ADDED;
break; break;
} }
if(addedEventType) updateEvent = new RoomWidgetRoomObjectUpdateEvent(addedEventType, objectId, category, event.roomId); if(addedEventType) updateEvent = new RoomWidgetUpdateRoomObjectEvent(addedEventType, objectId, category, event.roomId);
break; break;
} }
case RoomEngineObjectEvent.REMOVED: { case RoomEngineObjectEvent.REMOVED: {
@ -101,14 +101,14 @@ export const RoomWidgetsView: FC<RoomWidgetViewProps> = props =>
{ {
case RoomObjectCategory.FLOOR: case RoomObjectCategory.FLOOR:
case RoomObjectCategory.WALL: case RoomObjectCategory.WALL:
removedEventType = RoomWidgetRoomObjectUpdateEvent.FURNI_REMOVED; removedEventType = RoomWidgetUpdateRoomObjectEvent.FURNI_REMOVED;
break; break;
case RoomObjectCategory.UNIT: case RoomObjectCategory.UNIT:
removedEventType = RoomWidgetRoomObjectUpdateEvent.USER_REMOVED; removedEventType = RoomWidgetUpdateRoomObjectEvent.USER_REMOVED;
break; break;
} }
if(removedEventType) updateEvent = new RoomWidgetRoomObjectUpdateEvent(removedEventType, objectId, category, event.roomId); if(removedEventType) updateEvent = new RoomWidgetUpdateRoomObjectEvent(removedEventType, objectId, category, event.roomId);
break; break;
} }
case RoomEngineObjectEvent.REQUEST_MOVE: case RoomEngineObjectEvent.REQUEST_MOVE:
@ -118,13 +118,13 @@ export const RoomWidgetsView: FC<RoomWidgetViewProps> = props =>
if(CanManipulateFurniture(roomSession, objectId, category)) ProcessRoomObjectOperation(objectId, category, RoomObjectOperationType.OBJECT_ROTATE_POSITIVE); if(CanManipulateFurniture(roomSession, objectId, category)) ProcessRoomObjectOperation(objectId, category, RoomObjectOperationType.OBJECT_ROTATE_POSITIVE);
break; break;
case RoomEngineObjectEvent.REQUEST_MANIPULATION: case RoomEngineObjectEvent.REQUEST_MANIPULATION:
if(CanManipulateFurniture(roomSession, objectId, category)) updateEvent = new RoomWidgetRoomObjectUpdateEvent(RoomWidgetRoomObjectUpdateEvent.OBJECT_REQUEST_MANIPULATION, objectId, category, event.roomId); if(CanManipulateFurniture(roomSession, objectId, category)) updateEvent = new RoomWidgetUpdateRoomObjectEvent(RoomWidgetUpdateRoomObjectEvent.OBJECT_REQUEST_MANIPULATION, objectId, category, event.roomId);
break; break;
case RoomEngineObjectEvent.MOUSE_ENTER: case RoomEngineObjectEvent.MOUSE_ENTER:
updateEvent = new RoomWidgetRoomObjectUpdateEvent(RoomWidgetRoomObjectUpdateEvent.OBJECT_ROLL_OVER, objectId, category, event.roomId); updateEvent = new RoomWidgetUpdateRoomObjectEvent(RoomWidgetUpdateRoomObjectEvent.OBJECT_ROLL_OVER, objectId, category, event.roomId);
break; break;
case RoomEngineObjectEvent.MOUSE_LEAVE: case RoomEngineObjectEvent.MOUSE_LEAVE:
updateEvent = new RoomWidgetRoomObjectUpdateEvent(RoomWidgetRoomObjectUpdateEvent.OBJECT_ROLL_OUT, objectId, category, event.roomId); updateEvent = new RoomWidgetUpdateRoomObjectEvent(RoomWidgetUpdateRoomObjectEvent.OBJECT_ROLL_OUT, objectId, category, event.roomId);
break; break;
case RoomEngineTriggerWidgetEvent.REQUEST_CREDITFURNI: case RoomEngineTriggerWidgetEvent.REQUEST_CREDITFURNI:
widgetHandler.processWidgetMessage(new RoomWidgetFurniToWidgetMessage(RoomWidgetFurniToWidgetMessage.REQUEST_CREDITFURNI, objectId, category, event.roomId)); widgetHandler.processWidgetMessage(new RoomWidgetFurniToWidgetMessage(RoomWidgetFurniToWidgetMessage.REQUEST_CREDITFURNI, objectId, category, event.roomId));
@ -202,7 +202,7 @@ export const RoomWidgetsView: FC<RoomWidgetViewProps> = props =>
{ {
let dispatchEvent = true; let dispatchEvent = true;
if(updateEvent instanceof RoomWidgetRoomObjectUpdateEvent) dispatchEvent = (!RoomId.isRoomPreviewerId(updateEvent.roomId)); if(updateEvent instanceof RoomWidgetUpdateRoomObjectEvent) dispatchEvent = (!RoomId.isRoomPreviewerId(updateEvent.roomId));
if(dispatchEvent) widgetHandler.eventDispatcher.dispatchEvent(updateEvent); if(dispatchEvent) widgetHandler.eventDispatcher.dispatchEvent(updateEvent);
} }

View File

@ -1,6 +1,6 @@
import { RoomEnterEffect, RoomObjectCategory } from '@nitrots/nitro-renderer'; import { RoomEnterEffect, RoomObjectCategory } from '@nitrots/nitro-renderer';
import { FC, useCallback, useMemo, useState } from 'react'; import { FC, useCallback, useMemo, useState } from 'react';
import { GetRoomSession, GetSessionDataManager, RoomWidgetObjectNameEvent, RoomWidgetRoomEngineUpdateEvent, RoomWidgetRoomObjectMessage, RoomWidgetRoomObjectUpdateEvent, RoomWidgetUpdateDanceStatusEvent, RoomWidgetUpdateDecorateModeEvent, RoomWidgetUpdateInfostandEvent, RoomWidgetUpdateInfostandFurniEvent, RoomWidgetUpdateInfostandPetEvent, RoomWidgetUpdateInfostandRentableBotEvent, RoomWidgetUpdateInfostandUserEvent, RoomWidgetUpdateRentableBotChatEvent, RoomWidgetUseProductBubbleEvent, UseProductItem } from '../../../../api'; import { GetRoomSession, GetSessionDataManager, RoomWidgetObjectNameEvent, RoomWidgetRoomObjectMessage, RoomWidgetUpdateDanceStatusEvent, RoomWidgetUpdateDecorateModeEvent, RoomWidgetUpdateInfostandEvent, RoomWidgetUpdateInfostandFurniEvent, RoomWidgetUpdateInfostandPetEvent, RoomWidgetUpdateInfostandRentableBotEvent, RoomWidgetUpdateInfostandUserEvent, RoomWidgetUpdateRentableBotChatEvent, RoomWidgetUpdateRoomEngineEvent, RoomWidgetUpdateRoomObjectEvent, RoomWidgetUseProductBubbleEvent, UseProductItem } from '../../../../api';
import { CreateEventDispatcherHook } from '../../../../hooks/events/event-dispatcher.base'; import { CreateEventDispatcherHook } from '../../../../hooks/events/event-dispatcher.base';
import { useRoomContext } from '../../context/RoomContext'; import { useRoomContext } from '../../context/RoomContext';
import { AvatarInfoWidgetAvatarView } from './views/avatar/AvatarInfoWidgetAvatarView'; import { AvatarInfoWidgetAvatarView } from './views/avatar/AvatarInfoWidgetAvatarView';
@ -73,25 +73,25 @@ export const AvatarInfoWidgetView: FC<{}> = props =>
setProductBubbles([]); setProductBubbles([]);
}, []); }, []);
const onRoomWidgetRoomEngineUpdateEvent = useCallback((event: RoomWidgetRoomEngineUpdateEvent) => const onRoomWidgetRoomEngineUpdateEvent = useCallback((event: RoomWidgetUpdateRoomEngineEvent) =>
{ {
switch(event.type) switch(event.type)
{ {
case RoomWidgetRoomEngineUpdateEvent.NORMAL_MODE: { case RoomWidgetUpdateRoomEngineEvent.NORMAL_MODE: {
if(isGameMode) setGameMode(false); if(isGameMode) setGameMode(false);
return; return;
} }
case RoomWidgetRoomEngineUpdateEvent.GAME_MODE: { case RoomWidgetUpdateRoomEngineEvent.GAME_MODE: {
if(!isGameMode) setGameMode(true); if(!isGameMode) setGameMode(true);
return; return;
} }
} }
}, [ isGameMode ]); }, [ isGameMode ]);
CreateEventDispatcherHook(RoomWidgetRoomEngineUpdateEvent.NORMAL_MODE, eventDispatcher, onRoomWidgetRoomEngineUpdateEvent); CreateEventDispatcherHook(RoomWidgetUpdateRoomEngineEvent.NORMAL_MODE, eventDispatcher, onRoomWidgetRoomEngineUpdateEvent);
CreateEventDispatcherHook(RoomWidgetRoomEngineUpdateEvent.GAME_MODE, eventDispatcher, onRoomWidgetRoomEngineUpdateEvent); CreateEventDispatcherHook(RoomWidgetUpdateRoomEngineEvent.GAME_MODE, eventDispatcher, onRoomWidgetRoomEngineUpdateEvent);
const onRoomObjectRemoved = useCallback((event: RoomWidgetRoomObjectUpdateEvent) => const onRoomObjectRemoved = useCallback((event: RoomWidgetUpdateRoomObjectEvent) =>
{ {
if(name) if(name)
{ {
@ -152,15 +152,15 @@ export const AvatarInfoWidgetView: FC<{}> = props =>
} }
}, [ name, infoStandEvent, nameBubbles, productBubbles, removeNameBubble, clearInfoStandEvent ]); }, [ name, infoStandEvent, nameBubbles, productBubbles, removeNameBubble, clearInfoStandEvent ]);
CreateEventDispatcherHook(RoomWidgetRoomObjectUpdateEvent.USER_REMOVED, eventDispatcher, onRoomObjectRemoved); CreateEventDispatcherHook(RoomWidgetUpdateRoomObjectEvent.USER_REMOVED, eventDispatcher, onRoomObjectRemoved);
CreateEventDispatcherHook(RoomWidgetRoomObjectUpdateEvent.FURNI_REMOVED, eventDispatcher, onRoomObjectRemoved); CreateEventDispatcherHook(RoomWidgetUpdateRoomObjectEvent.FURNI_REMOVED, eventDispatcher, onRoomObjectRemoved);
const onObjectRolled = useCallback((event: RoomWidgetRoomObjectUpdateEvent) => const onObjectRolled = useCallback((event: RoomWidgetUpdateRoomObjectEvent) =>
{ {
switch(event.type) switch(event.type)
{ {
case RoomWidgetRoomObjectUpdateEvent.OBJECT_ROLL_OVER: { case RoomWidgetUpdateRoomObjectEvent.OBJECT_ROLL_OVER: {
const roomObjectEvent = (event as RoomWidgetRoomObjectUpdateEvent); const roomObjectEvent = (event as RoomWidgetUpdateRoomObjectEvent);
if(infoStandEvent) return; if(infoStandEvent) return;
@ -168,8 +168,8 @@ export const AvatarInfoWidgetView: FC<{}> = props =>
return; return;
} }
case RoomWidgetRoomObjectUpdateEvent.OBJECT_ROLL_OUT: { case RoomWidgetUpdateRoomObjectEvent.OBJECT_ROLL_OUT: {
const roomObjectEvent = (event as RoomWidgetRoomObjectUpdateEvent); const roomObjectEvent = (event as RoomWidgetUpdateRoomObjectEvent);
if(!name || (name.roomIndex !== roomObjectEvent.id)) return; if(!name || (name.roomIndex !== roomObjectEvent.id)) return;
@ -180,16 +180,16 @@ export const AvatarInfoWidgetView: FC<{}> = props =>
} }
}, [ infoStandEvent, name, widgetHandler ]); }, [ infoStandEvent, name, widgetHandler ]);
CreateEventDispatcherHook(RoomWidgetRoomObjectUpdateEvent.OBJECT_ROLL_OVER, eventDispatcher, onObjectRolled); CreateEventDispatcherHook(RoomWidgetUpdateRoomObjectEvent.OBJECT_ROLL_OVER, eventDispatcher, onObjectRolled);
CreateEventDispatcherHook(RoomWidgetRoomObjectUpdateEvent.OBJECT_ROLL_OUT, eventDispatcher, onObjectRolled); CreateEventDispatcherHook(RoomWidgetUpdateRoomObjectEvent.OBJECT_ROLL_OUT, eventDispatcher, onObjectRolled);
const onObjectDeselected = useCallback((event: RoomWidgetRoomObjectUpdateEvent) => const onObjectDeselected = useCallback((event: RoomWidgetUpdateRoomObjectEvent) =>
{ {
if(infoStandEvent) clearInfoStandEvent(); if(infoStandEvent) clearInfoStandEvent();
if(productBubbles.length) setProductBubbles([]); if(productBubbles.length) setProductBubbles([]);
}, [ infoStandEvent, productBubbles, clearInfoStandEvent ]); }, [ infoStandEvent, productBubbles, clearInfoStandEvent ]);
CreateEventDispatcherHook(RoomWidgetRoomObjectUpdateEvent.OBJECT_DESELECTED, eventDispatcher, onObjectDeselected); CreateEventDispatcherHook(RoomWidgetUpdateRoomObjectEvent.OBJECT_DESELECTED, eventDispatcher, onObjectDeselected);
const onRoomWidgetObjectNameEvent = useCallback((event: RoomWidgetObjectNameEvent) => const onRoomWidgetObjectNameEvent = useCallback((event: RoomWidgetObjectNameEvent) =>
{ {

View File

@ -71,7 +71,7 @@
max-height: $chat-input-style-selector-widget-height; max-height: $chat-input-style-selector-widget-height;
.content-area { .content-area {
max-height: $chat-input-style-selector-widget-height; max-height: $chat-input-style-selector-widget-height !important;
} }
.grid-item { .grid-item {

View File

@ -1,7 +1,7 @@
import { HabboClubLevelEnum, RoomControllerLevel } from '@nitrots/nitro-renderer'; import { HabboClubLevelEnum, RoomControllerLevel } from '@nitrots/nitro-renderer';
import { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { createPortal } from 'react-dom'; import { createPortal } from 'react-dom';
import { GetConfiguration, GetSessionDataManager, LocalizeText, RoomWidgetChatMessage, RoomWidgetChatTypingMessage, RoomWidgetRoomObjectUpdateEvent, RoomWidgetUpdateChatInputContentEvent, RoomWidgetUpdateInfostandUserEvent } from '../../../../api'; import { GetConfiguration, GetSessionDataManager, LocalizeText, RoomWidgetChatMessage, RoomWidgetChatTypingMessage, RoomWidgetUpdateChatInputContentEvent, RoomWidgetUpdateInfostandUserEvent, RoomWidgetUpdateRoomObjectEvent } from '../../../../api';
import { CreateEventDispatcherHook } from '../../../../hooks/events'; import { CreateEventDispatcherHook } from '../../../../hooks/events';
import { useRoomContext } from '../../context/RoomContext'; import { useRoomContext } from '../../context/RoomContext';
import { ChatInputStyleSelectorView } from './style-selector/ChatInputStyleSelectorView'; import { ChatInputStyleSelectorView } from './style-selector/ChatInputStyleSelectorView';
@ -177,12 +177,12 @@ export const ChatInputView: FC<{}> = props =>
}, [ inputRef, chatModeIdWhisper, anotherInputHasFocus, setInputFocus, checkSpecialKeywordForInput, sendChatValue ]); }, [ inputRef, chatModeIdWhisper, anotherInputHasFocus, setInputFocus, checkSpecialKeywordForInput, sendChatValue ]);
const onRoomWidgetRoomObjectUpdateEvent = useCallback((event: RoomWidgetRoomObjectUpdateEvent) => const onRoomWidgetRoomObjectUpdateEvent = useCallback((event: RoomWidgetUpdateRoomObjectEvent) =>
{ {
setSelectedUsername(''); setSelectedUsername('');
}, []); }, []);
CreateEventDispatcherHook(RoomWidgetRoomObjectUpdateEvent.OBJECT_DESELECTED, eventDispatcher, onRoomWidgetRoomObjectUpdateEvent); CreateEventDispatcherHook(RoomWidgetUpdateRoomObjectEvent.OBJECT_DESELECTED, eventDispatcher, onRoomWidgetRoomObjectUpdateEvent);
const onRoomWidgetUpdateInfostandUserEvent = useCallback((event: RoomWidgetUpdateInfostandUserEvent) => const onRoomWidgetUpdateInfostandUserEvent = useCallback((event: RoomWidgetUpdateInfostandUserEvent) =>
{ {

View File

@ -1,5 +1,5 @@
import { FC, useCallback, useState } from 'react'; import { FC, useCallback, useState } from 'react';
import { LocalizeText, RoomObjectItem, RoomWidgetChooserContentEvent, RoomWidgetRequestWidgetMessage, RoomWidgetRoomObjectUpdateEvent } from '../../../../api'; import { LocalizeText, RoomObjectItem, RoomWidgetChooserContentEvent, RoomWidgetRequestWidgetMessage, RoomWidgetUpdateRoomObjectEvent } from '../../../../api';
import { CreateEventDispatcherHook } from '../../../../hooks'; import { CreateEventDispatcherHook } from '../../../../hooks';
import { useRoomContext } from '../../context/RoomContext'; import { useRoomContext } from '../../context/RoomContext';
import { ChooserWidgetView } from './ChooserWidgetView'; import { ChooserWidgetView } from './ChooserWidgetView';
@ -31,21 +31,21 @@ export const FurniChooserWidgetView: FC<{}> = props =>
CreateEventDispatcherHook(RoomWidgetChooserContentEvent.FURNI_CHOOSER_CONTENT, eventDispatcher, onRoomWidgetChooserContentEvent); CreateEventDispatcherHook(RoomWidgetChooserContentEvent.FURNI_CHOOSER_CONTENT, eventDispatcher, onRoomWidgetChooserContentEvent);
const onRoomWidgetRoomObjectUpdateEvent = useCallback((event: RoomWidgetRoomObjectUpdateEvent) => const onRoomWidgetRoomObjectUpdateEvent = useCallback((event: RoomWidgetUpdateRoomObjectEvent) =>
{ {
if(!isVisible) return; if(!isVisible) return;
switch(event.type) switch(event.type)
{ {
case RoomWidgetRoomObjectUpdateEvent.FURNI_ADDED: case RoomWidgetUpdateRoomObjectEvent.FURNI_ADDED:
case RoomWidgetRoomObjectUpdateEvent.FURNI_REMOVED: case RoomWidgetUpdateRoomObjectEvent.FURNI_REMOVED:
refreshChooser(); refreshChooser();
return; return;
} }
}, [ isVisible, refreshChooser ]); }, [ isVisible, refreshChooser ]);
CreateEventDispatcherHook(RoomWidgetRoomObjectUpdateEvent.FURNI_ADDED, eventDispatcher, onRoomWidgetRoomObjectUpdateEvent); CreateEventDispatcherHook(RoomWidgetUpdateRoomObjectEvent.FURNI_ADDED, eventDispatcher, onRoomWidgetRoomObjectUpdateEvent);
CreateEventDispatcherHook(RoomWidgetRoomObjectUpdateEvent.FURNI_REMOVED, eventDispatcher, onRoomWidgetRoomObjectUpdateEvent); CreateEventDispatcherHook(RoomWidgetUpdateRoomObjectEvent.FURNI_REMOVED, eventDispatcher, onRoomWidgetRoomObjectUpdateEvent);
const close = useCallback(() => const close = useCallback(() =>
{ {

View File

@ -1,5 +1,5 @@
import { FC, useCallback, useState } from 'react'; import { FC, useCallback, useState } from 'react';
import { LocalizeText, RoomObjectItem, RoomWidgetChooserContentEvent, RoomWidgetRequestWidgetMessage, RoomWidgetRoomObjectUpdateEvent } from '../../../../api'; import { LocalizeText, RoomObjectItem, RoomWidgetChooserContentEvent, RoomWidgetRequestWidgetMessage, RoomWidgetUpdateRoomObjectEvent } from '../../../../api';
import { CreateEventDispatcherHook } from '../../../../hooks'; import { CreateEventDispatcherHook } from '../../../../hooks';
import { useRoomContext } from '../../context/RoomContext'; import { useRoomContext } from '../../context/RoomContext';
import { ChooserWidgetView } from './ChooserWidgetView'; import { ChooserWidgetView } from './ChooserWidgetView';
@ -31,21 +31,21 @@ export const UserChooserWidgetView: FC<{}> = props =>
CreateEventDispatcherHook(RoomWidgetChooserContentEvent.USER_CHOOSER_CONTENT, eventDispatcher, onRoomWidgetChooserContentEvent); CreateEventDispatcherHook(RoomWidgetChooserContentEvent.USER_CHOOSER_CONTENT, eventDispatcher, onRoomWidgetChooserContentEvent);
const onRoomWidgetRoomObjectUpdateEvent = useCallback((event: RoomWidgetRoomObjectUpdateEvent) => const onRoomWidgetRoomObjectUpdateEvent = useCallback((event: RoomWidgetUpdateRoomObjectEvent) =>
{ {
if(!isVisible) return; if(!isVisible) return;
switch(event.type) switch(event.type)
{ {
case RoomWidgetRoomObjectUpdateEvent.USER_ADDED: case RoomWidgetUpdateRoomObjectEvent.USER_ADDED:
case RoomWidgetRoomObjectUpdateEvent.USER_REMOVED: case RoomWidgetUpdateRoomObjectEvent.USER_REMOVED:
refreshChooser(); refreshChooser();
return; return;
} }
}, [ isVisible, refreshChooser ]); }, [ isVisible, refreshChooser ]);
CreateEventDispatcherHook(RoomWidgetRoomObjectUpdateEvent.USER_ADDED, eventDispatcher, onRoomWidgetRoomObjectUpdateEvent); CreateEventDispatcherHook(RoomWidgetUpdateRoomObjectEvent.USER_ADDED, eventDispatcher, onRoomWidgetRoomObjectUpdateEvent);
CreateEventDispatcherHook(RoomWidgetRoomObjectUpdateEvent.USER_REMOVED, eventDispatcher, onRoomWidgetRoomObjectUpdateEvent); CreateEventDispatcherHook(RoomWidgetUpdateRoomObjectEvent.USER_REMOVED, eventDispatcher, onRoomWidgetRoomObjectUpdateEvent);
const close = useCallback(() => const close = useCallback(() =>
{ {

View File

@ -7,6 +7,7 @@
border-radius: $border-radius; border-radius: $border-radius;
font-size: $font-size-sm; font-size: $font-size-sm;
z-index: $context-menu-zindex; z-index: $context-menu-zindex;
pointer-events: all;
&.name-only { &.name-only {
background-color: rgba($black, 0.5); background-color: rgba($black, 0.5);
@ -34,8 +35,7 @@
.menu-header { .menu-header {
background-color: $william; background-color: $william;
color: $white; color: $white;
width: 117px; min-width: 117px;
max-width: 117px;
height: 25px; height: 25px;
max-height: 25px; max-height: 25px;
font-size: 16px; font-size: 16px;

View File

@ -1,3 +1,9 @@
.nitro-room-widgets {
pointer-events: none;
}
@import './custom-stack-height/FurnitureCustomStackHeightView';
@import './dimmer/FurnitureDimmerView'; @import './dimmer/FurnitureDimmerView';
@import './exchange-credit/FurnitureExchangeCreditView'; @import './exchange-credit/FurnitureExchangeCreditView';
@import './external-image/FurnitureExternalImageView'; @import './external-image/FurnitureExternalImageView';

View File

@ -17,7 +17,7 @@ import { FurnitureTrophyView } from './trophy/FurnitureTrophyView';
export const FurnitureWidgetsView: FC<{}> = props => export const FurnitureWidgetsView: FC<{}> = props =>
{ {
return ( return (
<div className="position-absolute nitro-room-widgets top-0 start-0"> <div className="position-absolute nitro-room-widgets top-0 start-0 w-100 h-100">
<FurnitureBackgroundColorView /> <FurnitureBackgroundColorView />
<FurnitureContextMenuView /> <FurnitureContextMenuView />
<FurnitureCustomStackHeightView /> <FurnitureCustomStackHeightView />

View File

@ -1,7 +1,7 @@
import { ApplyTonerComposer, RoomControllerLevel, RoomEngineObjectEvent, RoomEngineTriggerWidgetEvent, RoomObjectVariable } from '@nitrots/nitro-renderer'; import { ApplyTonerComposer, RoomControllerLevel, RoomEngineObjectEvent, RoomEngineTriggerWidgetEvent, RoomObjectVariable } from '@nitrots/nitro-renderer';
import { FC, useCallback, useEffect, useState } from 'react'; import { FC, useCallback, useEffect, useState } from 'react';
import ReactSlider from 'react-slider'; import ReactSlider from 'react-slider';
import { GetRoomEngine, GetSessionDataManager, LocalizeText, RoomWidgetRoomObjectUpdateEvent, RoomWidgetUpdateBackgroundColorPreviewEvent } from '../../../../../api'; import { GetRoomEngine, GetSessionDataManager, LocalizeText, RoomWidgetUpdateBackgroundColorPreviewEvent, RoomWidgetUpdateRoomObjectEvent } from '../../../../../api';
import { SendMessageHook } from '../../../../../hooks'; import { SendMessageHook } from '../../../../../hooks';
import { CreateEventDispatcherHook, useRoomEngineEvent } from '../../../../../hooks/events'; import { CreateEventDispatcherHook, useRoomEngineEvent } from '../../../../../hooks/events';
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../../layout'; import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../../layout';
@ -48,7 +48,7 @@ export const FurnitureBackgroundColorView: FC<{}> = props =>
return; return;
} }
case RoomWidgetRoomObjectUpdateEvent.FURNI_REMOVED: { case RoomWidgetUpdateRoomObjectEvent.FURNI_REMOVED: {
if(objectId !== event.objectId) return; if(objectId !== event.objectId) return;
close(); close();
@ -58,7 +58,7 @@ export const FurnitureBackgroundColorView: FC<{}> = props =>
}, [ objectId, canOpenBackgroundToner, close ]); }, [ objectId, canOpenBackgroundToner, close ]);
useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_BACKGROUND_COLOR, onRoomEngineObjectEvent); useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_BACKGROUND_COLOR, onRoomEngineObjectEvent);
CreateEventDispatcherHook(RoomWidgetRoomObjectUpdateEvent.FURNI_REMOVED, eventDispatcher, onRoomEngineObjectEvent); CreateEventDispatcherHook(RoomWidgetUpdateRoomObjectEvent.FURNI_REMOVED, eventDispatcher, onRoomEngineObjectEvent);
const processAction = useCallback((name: string) => const processAction = useCallback((name: string) =>
{ {

View File

@ -1,6 +1,6 @@
import { NitroEvent, RoomEngineTriggerWidgetEvent, StringDataType } from '@nitrots/nitro-renderer'; import { NitroEvent, RoomEngineTriggerWidgetEvent, StringDataType } from '@nitrots/nitro-renderer';
import { FC, useCallback, useState } from 'react'; import { FC, useCallback, useState } from 'react';
import { GetRoomEngine, LocalizeBadgeDescription, LocalizeBadgeName, RoomWidgetRoomObjectUpdateEvent } from '../../../../../api'; import { GetRoomEngine, LocalizeBadgeDescription, LocalizeBadgeName, RoomWidgetUpdateRoomObjectEvent } from '../../../../../api';
import { CreateEventDispatcherHook } from '../../../../../hooks'; import { CreateEventDispatcherHook } from '../../../../../hooks';
import { useRoomEngineEvent } from '../../../../../hooks/events/nitro/room/room-engine-event'; import { useRoomEngineEvent } from '../../../../../hooks/events/nitro/room/room-engine-event';
import { NitroLayoutTrophyView } from '../../../../../layout'; import { NitroLayoutTrophyView } from '../../../../../layout';
@ -36,8 +36,8 @@ export const FurnitureBadgeDisplayView: FC<{}> = props =>
setTrophyData(new FurnitureTrophyData(widgetEvent.objectId, widgetEvent.category, '1', senderName, date, badgeDesc, badgeName)); setTrophyData(new FurnitureTrophyData(widgetEvent.objectId, widgetEvent.category, '1', senderName, date, badgeDesc, badgeName));
return; return;
} }
case RoomWidgetRoomObjectUpdateEvent.FURNI_REMOVED: { case RoomWidgetUpdateRoomObjectEvent.FURNI_REMOVED: {
const widgetEvent = (event as RoomWidgetRoomObjectUpdateEvent); const widgetEvent = (event as RoomWidgetUpdateRoomObjectEvent);
setTrophyData(prevState => setTrophyData(prevState =>
{ {
@ -52,7 +52,7 @@ export const FurnitureBadgeDisplayView: FC<{}> = props =>
useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_BADGE_DISPLAY_ENGRAVING, onNitroEvent); useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_BADGE_DISPLAY_ENGRAVING, onNitroEvent);
useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_ACHIEVEMENT_RESOLUTION_ENGRAVING, onNitroEvent); useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_ACHIEVEMENT_RESOLUTION_ENGRAVING, onNitroEvent);
CreateEventDispatcherHook(RoomWidgetRoomObjectUpdateEvent.FURNI_REMOVED, widgetHandler.eventDispatcher, onNitroEvent); CreateEventDispatcherHook(RoomWidgetUpdateRoomObjectEvent.FURNI_REMOVED, widgetHandler.eventDispatcher, onNitroEvent);
const processAction = useCallback((type: string, value: string = null) => const processAction = useCallback((type: string, value: string = null) =>
{ {

View File

@ -0,0 +1,4 @@
.nitro-widget-custom-stack-height {
width: $nitro-widget-custom-stack-height-width;
height: $nitro-widget-custom-stack-height-height;
}

View File

@ -2,9 +2,10 @@ import { FurnitureStackHeightComposer, FurnitureStackHeightEvent } from '@nitrot
import { FC, useCallback, useEffect, useState } from 'react'; import { FC, useCallback, useEffect, useState } from 'react';
import ReactSlider from 'react-slider'; import ReactSlider from 'react-slider';
import { LocalizeText, RoomWidgetUpdateCustomStackHeightEvent } from '../../../../../api'; import { LocalizeText, RoomWidgetUpdateCustomStackHeightEvent } from '../../../../../api';
import { CreateMessageHook, SendMessageHook } from '../../../../../hooks'; import { BatchUpdates, CreateMessageHook, SendMessageHook } from '../../../../../hooks';
import { CreateEventDispatcherHook } from '../../../../../hooks/events'; import { CreateEventDispatcherHook } from '../../../../../hooks/events';
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../../layout'; import { NitroCardContentView, NitroCardHeaderView, NitroCardView, NitroLayoutButton, NitroLayoutFlex, NitroLayoutFlexColumn, NitroLayoutGrid, NitroLayoutGridColumn } from '../../../../../layout';
import { NitroLayoutBase } from '../../../../../layout/base';
import { useRoomContext } from '../../../context/RoomContext'; import { useRoomContext } from '../../../context/RoomContext';
const MAX_HEIGHT: number = 40; const MAX_HEIGHT: number = 40;
@ -14,13 +15,15 @@ export const FurnitureCustomStackHeightView: FC<{}> = props =>
const [ objectId, setObjectId ] = useState(-1); const [ objectId, setObjectId ] = useState(-1);
const [ height, setHeight ] = useState(0); const [ height, setHeight ] = useState(0);
const [ pendingHeight, setPendingHeight ] = useState(-1); const [ pendingHeight, setPendingHeight ] = useState(-1);
const { eventDispatcher = null } = useRoomContext();
const { roomSession = null, eventDispatcher = null } = useRoomContext();
const close = useCallback(() => const close = useCallback(() =>
{ {
setObjectId(-1); BatchUpdates(() =>
setHeight(0); {
setObjectId(-1);
setHeight(0);
});
}, []); }, []);
const updateHeight = useCallback((height: number, fromServer: boolean = false) => const updateHeight = useCallback((height: number, fromServer: boolean = false) =>
@ -31,9 +34,12 @@ export const FurnitureCustomStackHeightView: FC<{}> = props =>
if(!fromServer) ((height > MAX_HEIGHT) && (height = MAX_HEIGHT)); if(!fromServer) ((height > MAX_HEIGHT) && (height = MAX_HEIGHT));
setHeight(parseFloat(height.toFixed(2))); BatchUpdates(() =>
{
setHeight(parseFloat(height.toFixed(2)));
if(!fromServer) setPendingHeight(height * 100); if(!fromServer) setPendingHeight(height * 100);
});
}, []); }, []);
const onRoomWidgetUpdateCustomStackHeightEvent = useCallback((event: RoomWidgetUpdateCustomStackHeightEvent) => const onRoomWidgetUpdateCustomStackHeightEvent = useCallback((event: RoomWidgetUpdateCustomStackHeightEvent) =>
@ -65,16 +71,6 @@ export const FurnitureCustomStackHeightView: FC<{}> = props =>
SendMessageHook(new FurnitureStackHeightComposer(objectId, ~~(height))); SendMessageHook(new FurnitureStackHeightComposer(objectId, ~~(height)));
}, [ objectId ]); }, [ objectId ]);
const placeAboveStack = useCallback(() =>
{
sendUpdate(-100);
}, [ sendUpdate ]);
const placeAtFloor = useCallback(() =>
{
sendUpdate(0);
}, [ sendUpdate ]);
useEffect(() => useEffect(() =>
{ {
if((objectId === -1) || (pendingHeight === -1)) return; if((objectId === -1) || (pendingHeight === -1)) return;
@ -87,27 +83,35 @@ export const FurnitureCustomStackHeightView: FC<{}> = props =>
if(objectId === -1) return null; if(objectId === -1) return null;
return ( return (
<NitroCardView> <NitroCardView className="nitro-widget-custom-stack-height" simple={ true }>
<NitroCardHeaderView headerText={ LocalizeText('widget.custom.stack.height.title') } onCloseClick={ close } /> <NitroCardHeaderView headerText={ LocalizeText('widget.custom.stack.height.title') } onCloseClick={ close } />
<NitroCardContentView> <NitroCardContentView>
<div className="form-group"> <NitroLayoutGrid>
<label className="fw-bold text-black">{ LocalizeText('widget.custom.stack.height.text') }</label> <NitroLayoutGridColumn className="justify-content-between" size={ 12 }>
<ReactSlider <NitroLayoutBase className="text-black" overflow="auto">
className={ 'nitro-slider' } { LocalizeText('widget.custom.stack.height.text') }
min={ 0 } </NitroLayoutBase>
max={ MAX_HEIGHT } <NitroLayoutFlexColumn gap={ 2 }>
step={ 0.01 } <NitroLayoutFlex gap={ 2 }>
value={ height } <ReactSlider
onChange={ event => updateHeight(event) } className="nitro-slider"
renderThumb={ (props, state) => <div { ...props }>{ state.valueNow }</div> } /> min={ 0 }
</div> max={ MAX_HEIGHT }
<div className="form-group"> step={ 0.01 }
<input type="number" min={ 0 } max={ MAX_HEIGHT } value={ height } onChange={ event => updateHeight(parseFloat(event.target.value)) } /> value={ height }
</div> onChange={ event => updateHeight(event) }
<div className="form-group"> renderThumb={ (props, state) => <div { ...props }>{ state.valueNow }</div> } />
<button type="button" className="btn btn-primary" onClick={ placeAboveStack }>{ LocalizeText('furniture.above.stack') }</button> <input type="number" min={ 0 } max={ MAX_HEIGHT } value={ height } onChange={ event => updateHeight(parseFloat(event.target.value)) } />
<button type="button" className="btn btn-primary" onClick={ placeAtFloor }>{ LocalizeText('furniture.floor.level') }</button> </NitroLayoutFlex>
</div> <NitroLayoutButton variant="primary" onClick={ event => sendUpdate(-100) }>
{ LocalizeText('furniture.above.stack') }
</NitroLayoutButton>
<NitroLayoutButton variant="primary" onClick={ event => sendUpdate(0) }>
{ LocalizeText('furniture.floor.level') }
</NitroLayoutButton>
</NitroLayoutFlexColumn>
</NitroLayoutGridColumn>
</NitroLayoutGrid>
</NitroCardContentView> </NitroCardContentView>
</NitroCardView> </NitroCardView>
); );

View File

@ -1,3 +1,10 @@
.nitro-exchange-credit { .nitro-widget-exchange-credit {
width: 290px; width: $nitro-widget-exchange-credit-width;
height: $nitro-widget-exchange-credit-height;
.exchange-image {
background-image: url('../../../../../assets/images/room-widgets/exchange-credit/exchange-credit-image.png');
width: 103px;
height: 103px;
}
} }

View File

@ -2,7 +2,8 @@ import { FC, useCallback, useState } from 'react';
import { LocalizeText, RoomWidgetCreditFurniRedeemMessage, RoomWidgetUpdateCreditFurniEvent } from '../../../../../api'; import { LocalizeText, RoomWidgetCreditFurniRedeemMessage, RoomWidgetUpdateCreditFurniEvent } from '../../../../../api';
import { BatchUpdates } from '../../../../../hooks'; import { BatchUpdates } from '../../../../../hooks';
import { CreateEventDispatcherHook } from '../../../../../hooks/events/event-dispatcher.base'; import { CreateEventDispatcherHook } from '../../../../../hooks/events/event-dispatcher.base';
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../../layout'; import { NitroCardContentView, NitroCardHeaderView, NitroCardView, NitroLayoutButton, NitroLayoutFlexColumn, NitroLayoutGrid, NitroLayoutGridColumn } from '../../../../../layout';
import { NitroLayoutBase } from '../../../../../layout/base';
import { useRoomContext } from '../../../context/RoomContext'; import { useRoomContext } from '../../../context/RoomContext';
export const FurnitureExchangeCreditView: FC<{}> = props => export const FurnitureExchangeCreditView: FC<{}> = props =>
@ -19,35 +20,46 @@ export const FurnitureExchangeCreditView: FC<{}> = props =>
CreateEventDispatcherHook(RoomWidgetUpdateCreditFurniEvent.CREDIT_FURNI_UPDATE, eventDispatcher, onRoomWidgetUpdateCreditFurniEvent); CreateEventDispatcherHook(RoomWidgetUpdateCreditFurniEvent.CREDIT_FURNI_UPDATE, eventDispatcher, onRoomWidgetUpdateCreditFurniEvent);
const processAction = useCallback((type: string, value: string = null) => const close = useCallback(() =>
{ {
switch(type) BatchUpdates(() =>
{ {
case 'close': setObjectId(-1);
BatchUpdates(() => setValue(0);
{ });
setObjectId(-1); }, []);
setValue(0);
});
return;
case 'redeem':
widgetHandler.processWidgetMessage(new RoomWidgetCreditFurniRedeemMessage(RoomWidgetCreditFurniRedeemMessage.REDEEM, objectId));
processAction('close'); const redeem = useCallback(() =>
return; {
} widgetHandler.processWidgetMessage(new RoomWidgetCreditFurniRedeemMessage(RoomWidgetCreditFurniRedeemMessage.REDEEM, objectId));
}, [ widgetHandler, objectId ]);
close();
}, [ widgetHandler, objectId, close ]);
if(objectId === -1) return null; if(objectId === -1) return null;
return ( return (
<NitroCardView className="nitro-exchange-credit" simple={ true }> <NitroCardView className="nitro-widget-exchange-credit" simple={ true }>
<NitroCardHeaderView headerText={ LocalizeText('catalog.redeem.dialog.title') } onCloseClick={ event => processAction('close') } /> <NitroCardHeaderView headerText={ LocalizeText('catalog.redeem.dialog.title') } onCloseClick={ close } />
<NitroCardContentView> <NitroCardContentView>
<div className="text-black mb-2"> <NitroLayoutGrid>
{ LocalizeText('widgets.furniture.credit.redeem.value', [ 'value' ], [ value.toString() ]) } <NitroLayoutGridColumn className="justify-content-center align-items-center" overflow="hidden" size={ 4 }>
</div> <NitroLayoutBase className="exchange-image" />
<button className="btn btn-success w-100" onClick={ event => processAction('redeem') }>{ LocalizeText('catalog.redeem.dialog.button.exchange') }</button> </NitroLayoutGridColumn>
<NitroLayoutGridColumn className="justify-content-between" overflow="hidden" size={ 8 }>
<NitroLayoutFlexColumn gap={ 1 } overflow="auto">
<NitroLayoutBase className="text-black fw-bold">
{ LocalizeText('creditfurni.description', [ 'credits' ], [ value.toString() ]) }
</NitroLayoutBase>
<NitroLayoutBase className="text-black">
{ LocalizeText('creditfurni.prompt') }
</NitroLayoutBase>
</NitroLayoutFlexColumn>
<NitroLayoutButton variant="success" onClick={ redeem }>
{ LocalizeText('catalog.redeem.dialog.button.exchange') }
</NitroLayoutButton>
</NitroLayoutGridColumn>
</NitroLayoutGrid>
</NitroCardContentView> </NitroCardContentView>
</NitroCardView> </NitroCardView>
); );

View File

@ -1,6 +1,6 @@
import { FriendFurniConfirmLockMessageComposer, LoveLockFurniFinishedEvent, LoveLockFurniFriendConfirmedEvent, LoveLockFurniStartEvent, NitroEvent, RoomEngineTriggerWidgetEvent, RoomObjectVariable } from '@nitrots/nitro-renderer'; import { FriendFurniConfirmLockMessageComposer, LoveLockFurniFinishedEvent, LoveLockFurniFriendConfirmedEvent, LoveLockFurniStartEvent, NitroEvent, RoomEngineTriggerWidgetEvent, RoomObjectVariable } from '@nitrots/nitro-renderer';
import { FC, useCallback, useState } from 'react'; import { FC, useCallback, useState } from 'react';
import { GetRoomEngine, GetRoomSession, LocalizeText, RoomWidgetRoomObjectUpdateEvent } from '../../../../../api'; import { GetRoomEngine, GetRoomSession, LocalizeText, RoomWidgetUpdateRoomObjectEvent } from '../../../../../api';
import { CreateEventDispatcherHook } from '../../../../../hooks/events/event-dispatcher.base'; import { CreateEventDispatcherHook } from '../../../../../hooks/events/event-dispatcher.base';
import { useRoomEngineEvent } from '../../../../../hooks/events/nitro/room/room-engine-event'; import { useRoomEngineEvent } from '../../../../../hooks/events/nitro/room/room-engine-event';
import { CreateMessageHook } from '../../../../../hooks/messages/message-event'; import { CreateMessageHook } from '../../../../../hooks/messages/message-event';
@ -37,8 +37,8 @@ export const FurnitureFriendFurniView: FC<{}> = props =>
} }
return; return;
} }
case RoomWidgetRoomObjectUpdateEvent.FURNI_REMOVED: { case RoomWidgetUpdateRoomObjectEvent.FURNI_REMOVED: {
const widgetEvent = (event as RoomWidgetRoomObjectUpdateEvent); const widgetEvent = (event as RoomWidgetUpdateRoomObjectEvent);
setEngravingLockData(prevState => setEngravingLockData(prevState =>
{ {
@ -52,7 +52,7 @@ export const FurnitureFriendFurniView: FC<{}> = props =>
}; };
useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_FRIEND_FURNITURE_ENGRAVING, onNitroEvent); useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_FRIEND_FURNITURE_ENGRAVING, onNitroEvent);
CreateEventDispatcherHook(RoomWidgetRoomObjectUpdateEvent.FURNI_REMOVED, eventDispatcher, onNitroEvent); CreateEventDispatcherHook(RoomWidgetUpdateRoomObjectEvent.FURNI_REMOVED, eventDispatcher, onNitroEvent);
const onLoveLockFurniStartEvent = useCallback((event: LoveLockFurniStartEvent) => const onLoveLockFurniStartEvent = useCallback((event: LoveLockFurniStartEvent) =>
{ {

View File

@ -1,8 +1,10 @@
import { RoomObjectCategory, RoomObjectOperationType } from '@nitrots/nitro-renderer'; import { RoomObjectCategory, RoomObjectOperationType } from '@nitrots/nitro-renderer';
import { FC, useCallback, useMemo, useState } from 'react'; import { FC, useCallback, useMemo, useState } from 'react';
import { CreateLinkEvent, GetRoomEngine, GetSessionDataManager, LocalizeText, RoomWidgetPresentOpenMessage, RoomWidgetRoomObjectUpdateEvent, RoomWidgetUpdatePresentDataEvent } from '../../../../../api'; import { CreateLinkEvent, GetRoomEngine, GetSessionDataManager, LocalizeText, RoomWidgetPresentOpenMessage, RoomWidgetUpdatePresentDataEvent, RoomWidgetUpdateRoomObjectEvent } from '../../../../../api';
import { BatchUpdates } from '../../../../../hooks';
import { CreateEventDispatcherHook } from '../../../../../hooks/events/event-dispatcher.base'; import { CreateEventDispatcherHook } from '../../../../../hooks/events/event-dispatcher.base';
import { NitroCardContentView, NitroCardHeaderView, NitroCardView, NitroLayoutGiftCardView } from '../../../../../layout'; import { NitroCardContentView, NitroCardHeaderView, NitroCardView, NitroLayoutButton, NitroLayoutFlex, NitroLayoutFlexColumn, NitroLayoutGiftCardView, NitroLayoutGrid, NitroLayoutGridColumn } from '../../../../../layout';
import { NitroLayoutBase } from '../../../../../layout/base';
import { ProductTypeEnum } from '../../../../catalog/common/ProductTypeEnum'; import { ProductTypeEnum } from '../../../../catalog/common/ProductTypeEnum';
import { useRoomContext } from '../../../context/RoomContext'; import { useRoomContext } from '../../../context/RoomContext';
@ -10,6 +12,11 @@ const FLOOR: string = 'floor';
const WALLPAPER: string = 'wallpaper'; const WALLPAPER: string = 'wallpaper';
const LANDSCAPE: string = 'landscape'; 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 => export const FurnitureGiftOpeningView: FC<{}> = props =>
{ {
const [ objectId, setObjectId ] = useState(-1); const [ objectId, setObjectId ] = useState(-1);
@ -44,57 +51,67 @@ export const FurnitureGiftOpeningView: FC<{}> = props =>
switch(event.type) switch(event.type)
{ {
case RoomWidgetUpdatePresentDataEvent.PACKAGEINFO: { case RoomWidgetUpdatePresentDataEvent.PACKAGEINFO: {
setOpenRequested(false); BatchUpdates(() =>
setObjectId(event.objectId); {
setText(event.giftMessage); setOpenRequested(false);
setIsOwnerOfFurniture(event.isController); setObjectId(event.objectId);
setSenderName(event.purchaserName); setText(event.giftMessage);
setSenderFigure(event.purchaserFigure); setIsOwnerOfFurniture(event.isController);
setImageUrl(event.imageUrl); setSenderName(event.purchaserName);
setSenderFigure(event.purchaserFigure);
setImageUrl(event.imageUrl);
});
return; return;
} }
case RoomWidgetUpdatePresentDataEvent.CONTENTS_FLOOR: case RoomWidgetUpdatePresentDataEvent.CONTENTS_FLOOR:
case RoomWidgetUpdatePresentDataEvent.CONTENTS_LANDSCAPE: case RoomWidgetUpdatePresentDataEvent.CONTENTS_LANDSCAPE:
case RoomWidgetUpdatePresentDataEvent.CONTENTS_WALLPAPER: { case RoomWidgetUpdatePresentDataEvent.CONTENTS_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);
let imageType: string = null; let imageType: string = null;
if(event.type === RoomWidgetUpdatePresentDataEvent.CONTENTS_FLOOR) imageType = 'packagecard_icon_floor'; 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_LANDSCAPE) imageType = 'packagecard_icon_landscape';
else if(event.type === RoomWidgetUpdatePresentDataEvent.CONTENTS_WALLPAPER) imageType = 'packagecard_icon_wallpaper'; else if(event.type === RoomWidgetUpdatePresentDataEvent.CONTENTS_WALLPAPER) imageType = 'packagecard_icon_wallpaper';
setImageUrl(getGiftImageUrl(imageType)); BatchUpdates(() =>
{
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; return;
} }
case RoomWidgetUpdatePresentDataEvent.CONTENTS_CLUB: { case RoomWidgetUpdatePresentDataEvent.CONTENTS_CLUB: {
setObjectId(event.objectId); BatchUpdates(() =>
setClassId(event.classId); {
setItemType(event.itemType); setObjectId(event.objectId);
setText(event.giftMessage); setClassId(event.classId);
setIsOwnerOfFurniture(event.isController); setItemType(event.itemType);
setImageUrl(getGiftImageUrl('packagecard_icon_hc')); setText(event.giftMessage);
setIsOwnerOfFurniture(event.isController);
setImageUrl(getGiftImageUrl('packagecard_icon_hc'));
});
return; return;
} }
case RoomWidgetUpdatePresentDataEvent.CONTENTS: { case RoomWidgetUpdatePresentDataEvent.CONTENTS: {
if(!openRequested) return; if(!openRequested) return;
setObjectId(event.objectId); BatchUpdates(() =>
setClassId(event.classId); {
setItemType(event.itemType); setObjectId(event.objectId);
setText(event.giftMessage); setClassId(event.classId);
setIsOwnerOfFurniture(event.isController); setItemType(event.itemType);
setPlacedItemId(event.placedItemId); setText(event.giftMessage);
setPlacedItemType(event.placedItemType); setIsOwnerOfFurniture(event.isController);
setPlacedInRoom(event.placedInRoom); setPlacedItemId(event.placedItemId);
setPlacedItemType(event.placedItemType);
setPlacedInRoom(event.placedInRoom);
});
return; return;
} }
case RoomWidgetUpdatePresentDataEvent.CONTENTS_IMAGE: { case RoomWidgetUpdatePresentDataEvent.CONTENTS_IMAGE: {
@ -113,7 +130,7 @@ export const FurnitureGiftOpeningView: FC<{}> = props =>
CreateEventDispatcherHook(RoomWidgetUpdatePresentDataEvent.CONTENTS_CLUB, eventDispatcher, onRoomWidgetUpdatePresentDataEvent); CreateEventDispatcherHook(RoomWidgetUpdatePresentDataEvent.CONTENTS_CLUB, eventDispatcher, onRoomWidgetUpdatePresentDataEvent);
CreateEventDispatcherHook(RoomWidgetUpdatePresentDataEvent.CONTENTS_IMAGE, eventDispatcher, onRoomWidgetUpdatePresentDataEvent); CreateEventDispatcherHook(RoomWidgetUpdatePresentDataEvent.CONTENTS_IMAGE, eventDispatcher, onRoomWidgetUpdatePresentDataEvent);
const onRoomWidgetRoomObjectUpdateEvent = useCallback((event: RoomWidgetRoomObjectUpdateEvent) => const onRoomWidgetRoomObjectUpdateEvent = useCallback((event: RoomWidgetUpdateRoomObjectEvent) =>
{ {
if(event.id === objectId) clearGift(); if(event.id === objectId) clearGift();
@ -123,17 +140,20 @@ export const FurnitureGiftOpeningView: FC<{}> = props =>
} }
}, [ objectId, placedItemId, placedInRoom, clearGift ]); }, [ objectId, placedItemId, placedInRoom, clearGift ]);
CreateEventDispatcherHook(RoomWidgetRoomObjectUpdateEvent.FURNI_REMOVED, eventDispatcher, onRoomWidgetRoomObjectUpdateEvent); CreateEventDispatcherHook(RoomWidgetUpdateRoomObjectEvent.FURNI_REMOVED, eventDispatcher, onRoomWidgetRoomObjectUpdateEvent);
const close = useCallback(() => const close = useCallback(() =>
{ {
setObjectId(-1); BatchUpdates(() =>
setOpenRequested(false); {
setPlacedItemId(-1); setObjectId(-1);
setPlacedInRoom(false); setOpenRequested(false);
setText(null); setPlacedItemId(-1);
setIsOwnerOfFurniture(false); setPlacedInRoom(false);
}, [ clearGift ]); setText(null);
setIsOwnerOfFurniture(false);
});
}, []);
const isSpaces = useMemo(() => const isSpaces = useMemo(() =>
{ {
@ -152,26 +172,25 @@ export const FurnitureGiftOpeningView: FC<{}> = props =>
{ {
if(objectId === -1) return ''; if(objectId === -1) return '';
if(isSpaces) if(isSpaces) return 'widget.furni.present.spaces.message_opened';
return 'widget.furni.present.spaces.message_opened';
return 'widget.furni.present.message_opened'; return 'widget.furni.present.message_opened';
}, [ objectId, isSpaces ]); }, [ objectId, isSpaces ]);
const handleAction = useCallback((action: string) => const handleAction = useCallback((action: number) =>
{ {
switch(action) switch(action)
{ {
case 'give_gift': case ACTION_GIVE_GIFT:
CreateLinkEvent('catalog/open'); CreateLinkEvent('catalog/open');
return; return;
case 'open': case ACTION_OPEN:
setOpenRequested(true); setOpenRequested(true);
widgetHandler.processWidgetMessage(new RoomWidgetPresentOpenMessage(RoomWidgetPresentOpenMessage.OPEN_PRESENT, objectId)); widgetHandler.processWidgetMessage(new RoomWidgetPresentOpenMessage(RoomWidgetPresentOpenMessage.OPEN_PRESENT, objectId));
return; return;
case 'room': case ACTION_PLACE:
return; return;
case 'inventory': case ACTION_INVENTORY:
if((placedItemId > 0) && placedInRoom) if((placedItemId > 0) && placedInRoom)
{ {
if(placedItemType === ProductTypeEnum.PET) if(placedItemType === ProductTypeEnum.PET)
@ -195,32 +214,48 @@ export const FurnitureGiftOpeningView: FC<{}> = props =>
return ( return (
<NitroCardView className="nitro-gift-opening" simple={ true }> <NitroCardView className="nitro-gift-opening" simple={ true }>
<NitroCardHeaderView headerText={ LocalizeText(senderName ? 'widget.furni.present.window.title_from' : 'widget.furni.present.window.title', ['name'], [senderName]) } onCloseClick={ close } /> <NitroCardHeaderView headerText={ LocalizeText(senderName ? 'widget.furni.present.window.title_from' : 'widget.furni.present.window.title', [ 'name' ], [ senderName ]) } onCloseClick={ close } />
<NitroCardContentView> <NitroCardContentView>
{ placedItemId === -1 && <> <NitroLayoutGrid>
<NitroLayoutGiftCardView userName={ senderName } figure={ senderFigure } message={ text } /> { (placedItemId === -1) &&
{ isOwnerOfFurniture && <div className="d-flex gap-2 mt-2"> <NitroLayoutGridColumn size={ 12 }>
{ senderName && <button className="btn btn-primary w-100 text-nowrap" onClick={ () => handleAction('give_gift') }>{ LocalizeText('widget.furni.present.give_gift', ['name'], [senderName]) }</button> } <NitroLayoutFlex className="justify-content-center align-items-center" overflow="auto">
<button className="btn btn-success w-100 text-nowrap" onClick={ () => handleAction('open') }>{ LocalizeText('widget.furni.present.open_gift') }</button> <NitroLayoutGiftCardView userName={ senderName } figure={ senderFigure } message={ text } />
</div> } </NitroLayoutFlex>
</> } <NitroLayoutFlex gap={ 2 }>
{ placedItemId !== -1 && <> { senderName &&
<div className="d-flex gap-2 align-items-center"> <NitroLayoutButton className="text-nowrap w-100" variant="primary" onClick={ event => handleAction(ACTION_GIVE_GIFT) }>
<div> { LocalizeText('widget.furni.present.give_gift', [ 'name' ], [ senderName ]) }
<img src={ imageUrl } alt="" /> </NitroLayoutButton> }
</div> <NitroLayoutButton className="text-nowrap w-100" variant="success" onClick={ event => handleAction(ACTION_OPEN) }>
<div className="bg-muted rounded p-2 text-center text-black"> { LocalizeText('widget.furni.present.open_gift') }
{ LocalizeText(productName, ['product'], [text]) } </NitroLayoutButton>
</div> </NitroLayoutFlex>
</div> </NitroLayoutGridColumn> }
<div className="d-flex gap-2 mt-3"> { (placedItemId > -1) &&
<button className="btn btn-primary w-100 text-nowrap" onClick={ () => handleAction('inventory') }>{ LocalizeText('widget.furni.present.put_in_inventory') }</button> <NitroLayoutGridColumn size={ 12 }>
<button className="btn btn-success w-100 text-nowrap" onClick={ () => handleAction('room') }>{ LocalizeText(placedInRoom ? 'widget.furni.present.keep_in_room' : 'widget.furni.present.place_in_room') }</button> <NitroLayoutFlex className="justify-content-center align-items-center" overflow="auto" gap={ 2 }>
</div> <img src={ imageUrl } alt="" />
{ senderName && <> <NitroLayoutBase className="text-black">
<button className="btn btn-primary w-100 text-nowrap mt-2" onClick={ () => handleAction('give_gift') }>{ LocalizeText('widget.furni.present.give_gift', ['name'], [senderName]) }</button> { LocalizeText(productName, [ 'product' ], [ text ]) }
</> } </NitroLayoutBase>
</> } </NitroLayoutFlex>
<NitroLayoutFlexColumn gap={ 2 }>
<NitroLayoutFlex gap={ 2 }>
<NitroLayoutButton className="w-100" variant="primary" onClick={ event => handleAction(ACTION_INVENTORY) }>
{ LocalizeText('widget.furni.present.put_in_inventory') }
</NitroLayoutButton>
<NitroLayoutButton className="w-100" variant="success" onClick={ event => handleAction(ACTION_PLACE) }>
{ LocalizeText(placedInRoom ? 'widget.furni.present.keep_in_room' : 'widget.furni.present.place_in_room') }
</NitroLayoutButton>
</NitroLayoutFlex>
{ (senderName && senderName.length) &&
<NitroLayoutButton className="w-100" variant="primary" onClick={ event => handleAction(ACTION_GIVE_GIFT) }>
{ LocalizeText('widget.furni.present.give_gift', [ 'name' ], [ senderName ]) }
</NitroLayoutButton> }
</NitroLayoutFlexColumn>
</NitroLayoutGridColumn> }
</NitroLayoutGrid>
</NitroCardContentView> </NitroCardContentView>
</NitroCardView> </NitroCardView>
); );

View File

@ -1,5 +1,6 @@
.highscore-widget .nitro-widget-high-score
{ {
width: 150px; width: 250px;
max-width: 150px; max-width: 250px;
height: 200px;
} }

View File

@ -2,13 +2,15 @@ import { HighScoreDataType, ObjectDataFactory, RoomEngineTriggerWidgetEvent, Roo
import { FC, useCallback, useState } from 'react'; import { FC, useCallback, useState } from 'react';
import { GetRoomEngine, LocalizeText } from '../../../../../api'; import { GetRoomEngine, LocalizeText } from '../../../../../api';
import { useRoomEngineEvent } from '../../../../../hooks'; import { useRoomEngineEvent } from '../../../../../hooks';
import { NitroLayoutGrid, NitroLayoutGridColumn } from '../../../../../layout';
import { NitroLayoutBase } from '../../../../../layout/base';
import { useRoomContext } from '../../../context/RoomContext'; import { useRoomContext } from '../../../context/RoomContext';
import { ContextMenuView } from '../../context-menu/ContextMenuView'; import { ContextMenuView } from '../../context-menu/ContextMenuView';
import { ContextMenuHeaderView } from '../../context-menu/views/header/ContextMenuHeaderView'; import { ContextMenuHeaderView } from '../../context-menu/views/header/ContextMenuHeaderView';
import { ContextMenuListView } from '../../context-menu/views/list/ContextMenuListView'; import { ContextMenuListView } from '../../context-menu/views/list/ContextMenuListView';
const SCORE_TYPES = ['perteam', 'mostwins', 'classic']; const SCORE_TYPES = [ 'perteam', 'mostwins', 'classic' ];
const CLEAR_TYPES = ['alltime', 'daily', 'weekly', 'monthly']; const CLEAR_TYPES = [ 'alltime', 'daily', 'weekly', 'monthly' ];
export const FurnitureHighScoreView: FC<{}> = props => export const FurnitureHighScoreView: FC<{}> = props =>
{ {
@ -55,33 +57,48 @@ export const FurnitureHighScoreView: FC<{}> = props =>
if((objectId === -1) || !stuffData) return null; if((objectId === -1) || !stuffData) return null;
return ( return (
<ContextMenuView objectId={ objectId } category={ RoomObjectCategory.FLOOR } close={ close } fades={ false } className="highscore-widget"> <ContextMenuView objectId={ objectId } category={ RoomObjectCategory.FLOOR } close={ close } fades={ false } className="nitro-widget-high-score">
<ContextMenuHeaderView> <ContextMenuHeaderView>
{ LocalizeText('high.score.display.caption', [ 'scoretype', 'cleartype' ], [LocalizeText(`high.score.display.scoretype.${ SCORE_TYPES[stuffData.scoreType] }`), LocalizeText(`high.score.display.cleartype.${ CLEAR_TYPES[stuffData.clearType] }`) ]) } { LocalizeText('high.score.display.caption', [ 'scoretype', 'cleartype' ], [LocalizeText(`high.score.display.scoretype.${ SCORE_TYPES[stuffData.scoreType] }`), LocalizeText(`high.score.display.cleartype.${ CLEAR_TYPES[stuffData.clearType] }`) ]) }
</ContextMenuHeaderView> </ContextMenuHeaderView>
<ContextMenuListView> <ContextMenuListView>
<div className="row"> <NitroLayoutGrid>
<div className="col-6">{ LocalizeText('high.score.display.users.header') }</div> <NitroLayoutGridColumn size={ 6 }>
<div className="col-6">{ LocalizeText('high.score.display.score.header') }</div> <NitroLayoutBase className="text-center fw-bold">
</div> { LocalizeText('high.score.display.users.header') }
<div className="row"> </NitroLayoutBase>
<div className="col-6"> </NitroLayoutGridColumn>
<div className="container h-100"> <NitroLayoutGridColumn size={ 6 }>
{ stuffData.entries.map((entry, index) => <NitroLayoutBase className="text-center fw-bold">
{ { LocalizeText('high.score.display.score.header') }
return <div key={ index }>{entry.users.join()}</div> </NitroLayoutBase>
}) </NitroLayoutGridColumn>
} </NitroLayoutGrid>
</div> <hr className="m-0 my-1" />
</div> <NitroLayoutGrid overflow="hidden">
<div className="col-6"> <NitroLayoutGridColumn size={ 6 }>
{ stuffData.entries.map((entry, index) => { stuffData.entries.map((entry, index) =>
{ {
return <div key={ index }>{entry.score}</div> return (
<NitroLayoutBase key={ index } className="text-center">
{ entry.users.join(', ') }
</NitroLayoutBase>
);
}) })
} }
</div> </NitroLayoutGridColumn>
</div> <NitroLayoutGridColumn size={ 6 }>
{ stuffData.entries.map((entry, index) =>
{
return (
<NitroLayoutBase key={ index } className="text-center">
{ entry.score }
</NitroLayoutBase>
);
})
}
</NitroLayoutGridColumn>
</NitroLayoutGrid>
</ContextMenuListView> </ContextMenuListView>
</ContextMenuView> </ContextMenuView>
); );

View File

@ -1,6 +1,6 @@
import { RoomObjectOperationType } from '@nitrots/nitro-renderer'; import { RoomObjectOperationType } from '@nitrots/nitro-renderer';
import { FC, useCallback, useEffect, useState } from 'react'; import { FC, useCallback, useEffect, useState } from 'react';
import { ProcessRoomObjectOperation, RoomWidgetRoomObjectUpdateEvent, RoomWidgetUpdateDecorateModeEvent } from '../../../../../api'; import { ProcessRoomObjectOperation, RoomWidgetUpdateDecorateModeEvent, RoomWidgetUpdateRoomObjectEvent } from '../../../../../api';
import { BatchUpdates } from '../../../../../hooks'; import { BatchUpdates } from '../../../../../hooks';
import { CreateEventDispatcherHook } from '../../../../../hooks/events/event-dispatcher.base'; import { CreateEventDispatcherHook } from '../../../../../hooks/events/event-dispatcher.base';
import { useRoomContext } from '../../../context/RoomContext'; import { useRoomContext } from '../../../context/RoomContext';
@ -28,11 +28,11 @@ export const FurnitureManipulationMenuView: FC<{}> = props =>
ProcessRoomObjectOperation(objectId, objectType, RoomObjectOperationType.OBJECT_PICKUP); ProcessRoomObjectOperation(objectId, objectType, RoomObjectOperationType.OBJECT_PICKUP);
}, [ objectId, objectType ]); }, [ objectId, objectType ]);
const onRoomWidgetRoomObjectUpdateEvent = useCallback((event: RoomWidgetRoomObjectUpdateEvent) => const onRoomWidgetRoomObjectUpdateEvent = useCallback((event: RoomWidgetUpdateRoomObjectEvent) =>
{ {
switch(event.type) switch(event.type)
{ {
case RoomWidgetRoomObjectUpdateEvent.OBJECT_REQUEST_MANIPULATION: { case RoomWidgetUpdateRoomObjectEvent.OBJECT_REQUEST_MANIPULATION: {
BatchUpdates(() => BatchUpdates(() =>
{ {
setIsVisible(true); setIsVisible(true);
@ -41,7 +41,7 @@ export const FurnitureManipulationMenuView: FC<{}> = props =>
}); });
return; return;
} }
case RoomWidgetRoomObjectUpdateEvent.FURNI_REMOVED: { case RoomWidgetUpdateRoomObjectEvent.FURNI_REMOVED: {
if(event.id === objectId) if(event.id === objectId)
{ {
BatchUpdates(() => BatchUpdates(() =>
@ -53,7 +53,7 @@ export const FurnitureManipulationMenuView: FC<{}> = props =>
} }
return; return;
} }
case RoomWidgetRoomObjectUpdateEvent.OBJECT_DESELECTED: { case RoomWidgetUpdateRoomObjectEvent.OBJECT_DESELECTED: {
BatchUpdates(() => BatchUpdates(() =>
{ {
setIsVisible(false); setIsVisible(false);
@ -65,8 +65,8 @@ export const FurnitureManipulationMenuView: FC<{}> = props =>
} }
}, [ objectId ]); }, [ objectId ]);
CreateEventDispatcherHook(RoomWidgetRoomObjectUpdateEvent.OBJECT_REQUEST_MANIPULATION, eventDispatcher, onRoomWidgetRoomObjectUpdateEvent); CreateEventDispatcherHook(RoomWidgetUpdateRoomObjectEvent.OBJECT_REQUEST_MANIPULATION, eventDispatcher, onRoomWidgetRoomObjectUpdateEvent);
CreateEventDispatcherHook(RoomWidgetRoomObjectUpdateEvent.OBJECT_DESELECTED, eventDispatcher, onRoomWidgetRoomObjectUpdateEvent); CreateEventDispatcherHook(RoomWidgetUpdateRoomObjectEvent.OBJECT_DESELECTED, eventDispatcher, onRoomWidgetRoomObjectUpdateEvent);
const onRoomWidgetUpdateDecorateModeEvent = useCallback((event: RoomWidgetUpdateDecorateModeEvent) => const onRoomWidgetUpdateDecorateModeEvent = useCallback((event: RoomWidgetUpdateDecorateModeEvent) =>
{ {

View File

@ -1,15 +1,26 @@
import { AvatarFigurePartType, FurnitureMannequinSaveLookComposer, FurnitureMannequinSaveNameComposer, FurnitureMultiStateComposer, IAvatarFigureContainer, NitroEvent, RoomEngineTriggerWidgetEvent, RoomObjectVariable } from '@nitrots/nitro-renderer'; import { AvatarFigurePartType, FurnitureMannequinSaveLookComposer, FurnitureMannequinSaveNameComposer, FurnitureMultiStateComposer, HabboClubLevelEnum, IAvatarFigureContainer, RoomControllerLevel } from '@nitrots/nitro-renderer';
import { FC, KeyboardEvent, useCallback, useEffect, useState } from 'react'; import { FC, KeyboardEvent, useCallback, useEffect, useState } from 'react';
import { GetAvatarRenderManager, GetNitroInstance, GetRoomEngine, GetRoomSession, GetSessionDataManager, LocalizeText, RoomWidgetRoomObjectUpdateEvent } from '../../../../../api'; import { GetAvatarRenderManager, GetSessionDataManager, LocalizeText, RoomWidgetUpdateMannequinEvent } from '../../../../../api';
import { BatchUpdates, SendMessageHook } from '../../../../../hooks';
import { CreateEventDispatcherHook } from '../../../../../hooks/events/event-dispatcher.base'; import { CreateEventDispatcherHook } from '../../../../../hooks/events/event-dispatcher.base';
import { useRoomEngineEvent } from '../../../../../hooks/events/nitro/room/room-engine-event'; import { NitroCardContentView, NitroCardHeaderView, NitroCardView, NitroLayoutButton, NitroLayoutFlex, NitroLayoutFlexColumn, NitroLayoutGrid, NitroLayoutGridColumn } from '../../../../../layout';
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../../layout'; import { NitroLayoutBase } from '../../../../../layout/base';
import { AvatarImageView } from '../../../../shared/avatar-image/AvatarImageView';
import { useRoomContext } from '../../../context/RoomContext'; import { useRoomContext } from '../../../context/RoomContext';
import { MannequinViewMode } from './common/MannequinViewMode'; import { FurnitureMannequinPreviewView } from './views/preview/FurnitureMannequinPreviewView';
import { FurnitureMannequinData } from './FurnitureMannequinData';
const parts = [ 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.CHEST_ACCESSORY,
AvatarFigurePartType.COAT_CHEST, AvatarFigurePartType.COAT_CHEST,
AvatarFigurePartType.CHEST, AvatarFigurePartType.CHEST,
@ -17,197 +28,209 @@ const parts = [
AvatarFigurePartType.SHOES, AvatarFigurePartType.SHOES,
AvatarFigurePartType.WAIST_ACCESSORY AvatarFigurePartType.WAIST_ACCESSORY
]; ];
const baseAvatar = ['hd', 99999, 99998];
export const FurnitureMannequinView: FC<{}> = props => export const FurnitureMannequinView: FC<{}> = props =>
{ {
const { eventDispatcher = null } = useRoomContext(); 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 [ mannequinData, setMannequinData ] = useState<FurnitureMannequinData>(null); const onRoomWidgetUpdateMannequinEvent = useCallback((event: RoomWidgetUpdateMannequinEvent) =>
const [ viewMode, setViewMode ] = useState('');
const loadMannequinFigure = useCallback((figureContainer: IAvatarFigureContainer) =>
{ {
for(const item of figureContainer.getPartTypeIds()) const figureContainer = GetAvatarRenderManager().createFigureContainer(event.figure);
const figureClubLevel = GetAvatarRenderManager().getFigureClubLevel(figureContainer, event.gender, MANNEQUIN_CLOTHING_PART_TYPES);
BatchUpdates(() =>
{ {
if(parts.indexOf(item) === -1) setObjectId(event.objectId);
setFigure(event.figure);
setGender(event.gender);
setName(event.name);
setClubLevel(figureClubLevel);
if(roomSession.isRoomOwner || (roomSession.controllerLevel >= RoomControllerLevel.GUEST) || GetSessionDataManager().isModerator)
{ {
figureContainer.removePart(item); setMode(MODE_CONTROLLER);
} }
}
figureContainer.updatePart(baseAvatar[0].toString(), Number(baseAvatar[1]), [ Number(baseAvatar[2]) ]); else if(GetSessionDataManager().gender.toLowerCase() !== event.gender.toLowerCase())
setMannequinData(mannequinData => new FurnitureMannequinData(mannequinData.objectId, mannequinData.category, mannequinData.name, mannequinData.figure, mannequinData.gender, mannequinData.clubLevel, figureContainer.getFigureString()));
}, []);
useEffect(() =>
{
if(mannequinData && !mannequinData.renderedFigure)
{
const figureContainer = GetAvatarRenderManager().createFigureContainer(mannequinData.figure);
loadMannequinFigure(figureContainer);
}
}, [loadMannequinFigure, mannequinData]);
const loadViewMode = useCallback((mannequinData: FurnitureMannequinData) =>
{
if(!mannequinData) return;
const userCanEdit = (GetRoomSession().isRoomOwner || GetSessionDataManager().isModerator);
const userGender = GetNitroInstance().sessionDataManager.gender;
const userClubLevel = GetNitroInstance().sessionDataManager.clubLevel;
if(userCanEdit)
{
setViewMode(MannequinViewMode.EDIT);
}
else
{
if(!mannequinData.figure || mannequinData.figure.length <= 1) return;
if(userGender.toUpperCase() !== mannequinData.gender.toUpperCase())
{ {
setViewMode(MannequinViewMode.INCOMPATIBLE_GENDER); setMode(MODE_WRONG_GENDER);
} }
else if(userClubLevel < mannequinData.clubLevel)
else if(GetSessionDataManager().clubLevel < figureClubLevel)
{ {
setViewMode(MannequinViewMode.CLUB); setMode(MODE_NO_CLUB);
} }
else else
{ {
setViewMode(MannequinViewMode.DEFAULT); setMode(MODE_PEER);
} }
} });
}, []); }, [ roomSession ]);
const onNitroEvent = useCallback((event: NitroEvent) => CreateEventDispatcherHook(RoomWidgetUpdateMannequinEvent.MANNEQUIN_UPDATE, eventDispatcher, onRoomWidgetUpdateMannequinEvent);
const getMergedFigureContainer = (figure: string, targetFigure: string) =>
{ {
switch(event.type) 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())
{ {
case RoomEngineTriggerWidgetEvent.REQUEST_MANNEQUIN: { figureContainer.updatePart(part, targetFigureContainer.getPartSetId(part), targetFigureContainer.getPartColorIds(part));
const widgetEvent = (event as RoomEngineTriggerWidgetEvent);
const roomObject = GetRoomEngine().getRoomObject(widgetEvent.roomId, widgetEvent.objectId, widgetEvent.category);
if(!roomObject) return;
const figure = roomObject.model.getValue<string>(RoomObjectVariable.FURNITURE_MANNEQUIN_FIGURE);
const gender = roomObject.model.getValue<string>(RoomObjectVariable.FURNITURE_MANNEQUIN_GENDER);
const name = roomObject.model.getValue<string>(RoomObjectVariable.FURNITURE_MANNEQUIN_NAME);
const figureContainer = GetAvatarRenderManager().createFigureContainer(figure);
const clubLevel = GetAvatarRenderManager().getFigureClubLevel(figureContainer, gender, parts);
const mannequinData = new FurnitureMannequinData(widgetEvent.objectId, widgetEvent.category, name, figure, gender, clubLevel);
setMannequinData(mannequinData);
loadViewMode(mannequinData);
return;
}
case RoomWidgetRoomObjectUpdateEvent.FURNI_REMOVED: {
const widgetEvent = (event as RoomWidgetRoomObjectUpdateEvent);
setMannequinData(prevState =>
{
if(!prevState || (widgetEvent.id !== prevState.objectId) || (widgetEvent.category !== prevState.category)) return prevState;
return null;
});
return;
}
} }
}, [loadViewMode]);
useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_MANNEQUIN, onNitroEvent); return figureContainer;
CreateEventDispatcherHook(RoomWidgetRoomObjectUpdateEvent.FURNI_REMOVED, eventDispatcher, onNitroEvent); }
const processAction = useCallback((type: string, value: string = null) => const transformAsMannequinFigure = (figureContainer: IAvatarFigureContainer) =>
{ {
switch(type) for(const part of figureContainer.getPartTypeIds())
{ {
case 'close': if(MANNEQUIN_CLOTHING_PART_TYPES.indexOf(part) >= 0) continue;
setMannequinData(null);
return; figureContainer.removePart(part);
case 'set_name': }
setMannequinData(mannequinData => new FurnitureMannequinData(mannequinData.objectId, mannequinData.category, value, mannequinData.figure, mannequinData.gender, mannequinData.clubLevel, mannequinData.renderedFigure));
return; figureContainer.updatePart((MANNEQUIN_FIGURE[0] as string), (MANNEQUIN_FIGURE[1] as number), (MANNEQUIN_FIGURE[2] as number[]));
case 'load_figure': };
loadMannequinFigure(GetAvatarRenderManager().createFigureContainer(GetNitroInstance().sessionDataManager.figure));
setViewMode(MannequinViewMode.SAVE); const processAction = useCallback((action: number, value: string = null) =>
return; {
case 'back': switch(action)
loadMannequinFigure(GetAvatarRenderManager().createFigureContainer(mannequinData.figure)); {
setViewMode(MannequinViewMode.EDIT); case ACTION_SAVE:
return; SendMessageHook(new FurnitureMannequinSaveLookComposer(objectId));
case 'save_name': break;
GetRoomSession().connection.send(new FurnitureMannequinSaveNameComposer(mannequinData.objectId, mannequinData.name)); case ACTION_WEAR:
return; SendMessageHook(new FurnitureMultiStateComposer(objectId));
case 'save_figure': break;
GetRoomSession().connection.send(new FurnitureMannequinSaveLookComposer(mannequinData.objectId)); case ACTION_SET_NAME:
processAction('save_name'); SendMessageHook(new FurnitureMannequinSaveNameComposer(objectId, name));
processAction('close');
return;
case 'wear':
GetRoomSession().connection.send(new FurnitureMultiStateComposer(mannequinData.objectId));
processAction('close');
return; return;
} }
}, [ loadMannequinFigure, mannequinData ]);
setMode(MODE_NONE);
}, [ objectId, name ]);
const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) =>
{ {
if(event.key !== 'Enter') return; if(event.key !== 'Enter') return;
processAction('save_name'); processAction(ACTION_SET_NAME);
}; };
if(!mannequinData) return null; 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 ( return (
<NitroCardView className="nitro-mannequin" simple={ true }> <NitroCardView className="nitro-mannequin" simple={ true }>
<NitroCardHeaderView headerText={ LocalizeText('mannequin.widget.title') } onCloseClick={ event => processAction('close') } /> <NitroCardHeaderView headerText={ LocalizeText('mannequin.widget.title') } onCloseClick={ event => setMode(MODE_NONE) } />
<NitroCardContentView> <NitroCardContentView>
<div className="row"> <NitroLayoutGrid>
<div className="col-4"> <NitroLayoutGridColumn className="justify-content-center align-items-center" overflow="hidden" size={ 4 }>
<div className="mannequin-preview"> <FurnitureMannequinPreviewView figure={ renderedFigure } clubLevel={ renderedClubLevel } />
<AvatarImageView figure={ mannequinData.renderedFigure } direction={ 2 } /> </NitroLayoutGridColumn>
</div> <NitroLayoutGridColumn className="justify-content-between" overflow="hidden" size={ 8 }>
</div> { (mode === MODE_CONTROLLER) &&
<div className="d-flex flex-column justify-content-between col">
{ viewMode === MannequinViewMode.DEFAULT &&
<> <>
<div className="h-100"> <NitroLayoutFlexColumn gap={ 1 } overflow="auto">
<div className="mb-1 text-black fw-bold">{ mannequinData.name }</div> <input type="text" className="form-control" value={ name } onChange={ event => setName(event.target.value) } onKeyDown={ event => handleKeyDown(event) } />
<div className="text-black">{ LocalizeText('mannequin.widget.weartext') }</div> </NitroLayoutFlexColumn>
</div> <NitroLayoutFlexColumn gap={ 1 }>
<div className="btn btn-success float-end" onClick={ event => processAction('wear') }>{ LocalizeText('mannequin.widget.wear') }</div> <NitroLayoutButton variant="success" onClick={ event => setMode(MODE_UPDATE) }>
</> } { LocalizeText('mannequin.widget.style') }
{ viewMode === MannequinViewMode.EDIT && </NitroLayoutButton>
<> <NitroLayoutButton variant="success" onClick={ event => processAction(ACTION_WEAR) }>
<input type="text" className="form-control mb-2" value={ mannequinData.name } onChange={ event => processAction('set_name', event.target.value) } onKeyDown={ event => handleKeyDown(event) } /> { LocalizeText('mannequin.widget.wear') }
<div className="d-flex flex-column w-100"> </NitroLayoutButton>
<div className="btn btn-success mb-2 w-100" onClick={ event => processAction('load_figure') }>{ LocalizeText('mannequin.widget.style') }</div> </NitroLayoutFlexColumn>
<div className="btn btn-success w-100" onClick={ event => processAction('wear') }>{ LocalizeText('mannequin.widget.wear') }</div>
</div>
</> } </> }
{ viewMode === MannequinViewMode.SAVE && { (mode === MODE_UPDATE) &&
<> <>
<div className="h-100"> <NitroLayoutFlexColumn gap={ 1 } overflow="auto">
<div className="mb-1 text-black fw-bold">{ mannequinData.name }</div> <NitroLayoutBase className="text-black fw-bold">
<div className="text-black">{ LocalizeText('mannequin.widget.savetext') }</div> { name }
</div> </NitroLayoutBase>
<div className="d-flex justify-content-between align-items-center"> <NitroLayoutBase className="text-black">
<div className="text-black text-decoration-underline cursor-pointer" onClick={ event => processAction('back') }>{ LocalizeText('mannequin.widget.back') }</div> { LocalizeText('mannequin.widget.savetext') }
<div className="btn btn-success" onClick={ event => processAction('save_figure') }>{ LocalizeText('mannequin.widget.save') }</div> </NitroLayoutBase>
</div> </NitroLayoutFlexColumn>
<NitroLayoutFlex className="justify-content-between align-items-center">
<NitroLayoutBase className="text-black text-decoration-underline cursor-pointer" onClick={ event => setMode(MODE_CONTROLLER) }>
{ LocalizeText('mannequin.widget.back') }
</NitroLayoutBase>
<NitroLayoutButton variant="success" onClick={ event => processAction(ACTION_SAVE) }>
{ LocalizeText('mannequin.widget.save') }
</NitroLayoutButton>
</NitroLayoutFlex>
</> } </> }
{ viewMode === MannequinViewMode.CLUB && { (mode === MODE_PEER) &&
<div className="text-black">{ LocalizeText('mannequin.widget.clubnotification') }</div> } <>
{ viewMode === MannequinViewMode.INCOMPATIBLE_GENDER && <NitroLayoutFlexColumn gap={ 1 } overflow="auto">
<div className="text-black">{ LocalizeText('mannequin.widget.wronggender') }</div> } <NitroLayoutBase className="text-black fw-bold">
</div> { name }
</div> </NitroLayoutBase>
<NitroLayoutBase className="text-black">
{ LocalizeText('mannequin.widget.weartext') }
</NitroLayoutBase>
</NitroLayoutFlexColumn>
<NitroLayoutButton variant="success" onClick={ event => processAction(ACTION_WEAR) }>
{ LocalizeText('mannequin.widget.wear') }
</NitroLayoutButton>
</> }
{ (mode === MODE_NO_CLUB) &&
<NitroLayoutBase className="text-black">
{ LocalizeText('mannequin.widget.clubnotification') }
</NitroLayoutBase> }
{ (mode === MODE_WRONG_GENDER) &&
<NitroLayoutBase className="text-black">
{ LocalizeText('mannequin.widget.wronggender') }
</NitroLayoutBase> }
</NitroLayoutGridColumn>
</NitroLayoutGrid>
</NitroCardContentView> </NitroCardContentView>
</NitroCardView> </NitroCardView>
); );

View File

@ -1,8 +0,0 @@
export class MannequinViewMode
{
public static readonly EDIT: string = 'edit';
public static readonly SAVE: string = 'save';
public static readonly CLUB: string = 'club';
public static readonly DEFAULT: string = 'default';
public static readonly INCOMPATIBLE_GENDER: string = 'incompatible_gender';
}

View File

@ -0,0 +1,17 @@
import { FC } from 'react';
import { NitroLayoutBase } from '../../../../../../../layout/base';
import { AvatarImageView } from '../../../../../../shared/avatar-image/AvatarImageView';
import { CurrencyIcon } from '../../../../../../shared/currency-icon/CurrencyIcon';
import { FurnitureMannequinPreviewViewProps } from './FurnitureMannequinPreviewView.types';
export const FurnitureMannequinPreviewView: FC<FurnitureMannequinPreviewViewProps> = props =>
{
const { figure = null, clubLevel = 0 } = props;
return (
<NitroLayoutBase className="mannequin-preview" position="relative">
<AvatarImageView figure={ figure } direction={ 2 } />
{ (clubLevel > 0) && <CurrencyIcon className="position-absolute end-2 bottom-2" type="hc" /> }
</NitroLayoutBase>
);
}

View File

@ -0,0 +1,5 @@
export interface FurnitureMannequinPreviewViewProps
{
figure: string;
clubLevel: number;
}

View File

@ -5,6 +5,7 @@
top: 25px; top: 25px;
left: 25px; left: 25px;
padding: 1px; padding: 1px;
pointer-events: all;
.stickie-header { .stickie-header {
width: 183px; width: 183px;

View File

@ -1,6 +1,6 @@
import { NitroEvent, RoomEngineTriggerWidgetEvent, RoomObjectVariable } from '@nitrots/nitro-renderer'; import { NitroEvent, RoomEngineTriggerWidgetEvent, RoomObjectVariable } from '@nitrots/nitro-renderer';
import { FC, useCallback, useState } from 'react'; import { FC, useCallback, useState } from 'react';
import { ColorUtils, GetRoomEngine, GetRoomSession, GetSessionDataManager, RoomWidgetRoomObjectUpdateEvent } from '../../../../../api'; import { ColorUtils, GetRoomEngine, GetRoomSession, GetSessionDataManager, RoomWidgetUpdateRoomObjectEvent } from '../../../../../api';
import { CreateEventDispatcherHook } from '../../../../../hooks/events/event-dispatcher.base'; import { CreateEventDispatcherHook } from '../../../../../hooks/events/event-dispatcher.base';
import { useRoomEngineEvent } from '../../../../../hooks/events/nitro/room/room-engine-event'; import { useRoomEngineEvent } from '../../../../../hooks/events/nitro/room/room-engine-event';
import { DraggableWindowPosition } from '../../../../../layout'; import { DraggableWindowPosition } from '../../../../../layout';
@ -45,8 +45,8 @@ export const FurnitureStickieView: FC<{}> = props =>
setStickieData(new FurnitureStickieData(widgetEvent.objectId, widgetEvent.category, color, text, (GetRoomSession().isRoomOwner || GetSessionDataManager().isModerator), false)); setStickieData(new FurnitureStickieData(widgetEvent.objectId, widgetEvent.category, color, text, (GetRoomSession().isRoomOwner || GetSessionDataManager().isModerator), false));
return; return;
} }
case RoomWidgetRoomObjectUpdateEvent.FURNI_REMOVED: { case RoomWidgetUpdateRoomObjectEvent.FURNI_REMOVED: {
const widgetEvent = (event as RoomWidgetRoomObjectUpdateEvent); const widgetEvent = (event as RoomWidgetUpdateRoomObjectEvent);
setStickieData(prevState => setStickieData(prevState =>
{ {
@ -60,7 +60,7 @@ export const FurnitureStickieView: FC<{}> = props =>
}, []); }, []);
useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_STICKIE, onNitroEvent); useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_STICKIE, onNitroEvent);
CreateEventDispatcherHook(RoomWidgetRoomObjectUpdateEvent.FURNI_REMOVED, eventDispatcher, onNitroEvent); CreateEventDispatcherHook(RoomWidgetUpdateRoomObjectEvent.FURNI_REMOVED, eventDispatcher, onNitroEvent);
const processAction = useCallback((type: string, value: string = null) => const processAction = useCallback((type: string, value: string = null) =>
{ {

View File

@ -1,5 +1,5 @@
import { FC, useCallback, useState } from 'react'; import { FC, useCallback, useState } from 'react';
import { RoomWidgetRoomObjectMessage, RoomWidgetRoomObjectUpdateEvent, RoomWidgetUpdateEvent, RoomWidgetUpdateInfostandEvent, RoomWidgetUpdateInfostandFurniEvent, RoomWidgetUpdateInfostandPetEvent, RoomWidgetUpdateInfostandRentableBotEvent, RoomWidgetUpdateInfostandUserEvent } from '../../../../api'; import { RoomWidgetRoomObjectMessage, RoomWidgetUpdateEvent, RoomWidgetUpdateInfostandEvent, RoomWidgetUpdateInfostandFurniEvent, RoomWidgetUpdateInfostandPetEvent, RoomWidgetUpdateInfostandRentableBotEvent, RoomWidgetUpdateInfostandUserEvent, RoomWidgetUpdateRoomObjectEvent } from '../../../../api';
import { CreateEventDispatcherHook } from '../../../../hooks/events/event-dispatcher.base'; import { CreateEventDispatcherHook } from '../../../../hooks/events/event-dispatcher.base';
import { useRoomContext } from '../../context/RoomContext'; import { useRoomContext } from '../../context/RoomContext';
import { InfoStandWidgetBotView } from './views/bot/InfoStandWidgetBotView'; import { InfoStandWidgetBotView } from './views/bot/InfoStandWidgetBotView';
@ -22,33 +22,33 @@ export const InfoStandWidgetView: FC<{}> = props =>
{ {
switch(event.type) switch(event.type)
{ {
case RoomWidgetRoomObjectUpdateEvent.OBJECT_SELECTED: { case RoomWidgetUpdateRoomObjectEvent.OBJECT_SELECTED: {
const roomObjectEvent = (event as RoomWidgetRoomObjectUpdateEvent); const roomObjectEvent = (event as RoomWidgetUpdateRoomObjectEvent);
widgetHandler.processWidgetMessage(new RoomWidgetRoomObjectMessage(RoomWidgetRoomObjectMessage.GET_OBJECT_INFO, roomObjectEvent.id, roomObjectEvent.category)); widgetHandler.processWidgetMessage(new RoomWidgetRoomObjectMessage(RoomWidgetRoomObjectMessage.GET_OBJECT_INFO, roomObjectEvent.id, roomObjectEvent.category));
return; return;
} }
case RoomWidgetRoomObjectUpdateEvent.OBJECT_DESELECTED: { case RoomWidgetUpdateRoomObjectEvent.OBJECT_DESELECTED: {
const roomObjectEvent = (event as RoomWidgetRoomObjectUpdateEvent); const roomObjectEvent = (event as RoomWidgetUpdateRoomObjectEvent);
closeInfostand(); closeInfostand();
return; return;
} }
case RoomWidgetRoomObjectUpdateEvent.FURNI_REMOVED: case RoomWidgetUpdateRoomObjectEvent.FURNI_REMOVED:
case RoomWidgetRoomObjectUpdateEvent.USER_REMOVED: { case RoomWidgetUpdateRoomObjectEvent.USER_REMOVED: {
const roomObjectEvent = (event as RoomWidgetRoomObjectUpdateEvent); const roomObjectEvent = (event as RoomWidgetUpdateRoomObjectEvent);
setInfoStandEvent(prevValue => setInfoStandEvent(prevValue =>
{ {
switch(event.type) switch(event.type)
{ {
case RoomWidgetRoomObjectUpdateEvent.FURNI_REMOVED: case RoomWidgetUpdateRoomObjectEvent.FURNI_REMOVED:
if(prevValue instanceof RoomWidgetUpdateInfostandFurniEvent) if(prevValue instanceof RoomWidgetUpdateInfostandFurniEvent)
{ {
if(prevValue.id === roomObjectEvent.id) return null; if(prevValue.id === roomObjectEvent.id) return null;
} }
break; break;
case RoomWidgetRoomObjectUpdateEvent.USER_REMOVED: case RoomWidgetUpdateRoomObjectEvent.USER_REMOVED:
if(prevValue instanceof RoomWidgetUpdateInfostandUserEvent || prevValue instanceof RoomWidgetUpdateInfostandRentableBotEvent) if(prevValue instanceof RoomWidgetUpdateInfostandUserEvent || prevValue instanceof RoomWidgetUpdateInfostandRentableBotEvent)
{ {
if(prevValue.roomIndex === roomObjectEvent.id) return null; if(prevValue.roomIndex === roomObjectEvent.id) return null;
@ -80,10 +80,10 @@ export const InfoStandWidgetView: FC<{}> = props =>
} }
}, [ widgetHandler, closeInfostand ]); }, [ widgetHandler, closeInfostand ]);
CreateEventDispatcherHook(RoomWidgetRoomObjectUpdateEvent.OBJECT_SELECTED, eventDispatcher, onRoomWidgetUpdateEvent); CreateEventDispatcherHook(RoomWidgetUpdateRoomObjectEvent.OBJECT_SELECTED, eventDispatcher, onRoomWidgetUpdateEvent);
CreateEventDispatcherHook(RoomWidgetRoomObjectUpdateEvent.OBJECT_DESELECTED, eventDispatcher, onRoomWidgetUpdateEvent); CreateEventDispatcherHook(RoomWidgetUpdateRoomObjectEvent.OBJECT_DESELECTED, eventDispatcher, onRoomWidgetUpdateEvent);
CreateEventDispatcherHook(RoomWidgetRoomObjectUpdateEvent.USER_REMOVED, eventDispatcher, onRoomWidgetUpdateEvent); CreateEventDispatcherHook(RoomWidgetUpdateRoomObjectEvent.USER_REMOVED, eventDispatcher, onRoomWidgetUpdateEvent);
CreateEventDispatcherHook(RoomWidgetRoomObjectUpdateEvent.FURNI_REMOVED, eventDispatcher, onRoomWidgetUpdateEvent); CreateEventDispatcherHook(RoomWidgetUpdateRoomObjectEvent.FURNI_REMOVED, eventDispatcher, onRoomWidgetUpdateEvent);
CreateEventDispatcherHook(RoomWidgetUpdateInfostandFurniEvent.FURNI, eventDispatcher, onRoomWidgetUpdateEvent); CreateEventDispatcherHook(RoomWidgetUpdateInfostandFurniEvent.FURNI, eventDispatcher, onRoomWidgetUpdateEvent);
CreateEventDispatcherHook(RoomWidgetUpdateInfostandUserEvent.OWN_USER, eventDispatcher, onRoomWidgetUpdateEvent); CreateEventDispatcherHook(RoomWidgetUpdateInfostandUserEvent.OWN_USER, eventDispatcher, onRoomWidgetUpdateEvent);
CreateEventDispatcherHook(RoomWidgetUpdateInfostandUserEvent.PEER, eventDispatcher, onRoomWidgetUpdateEvent); CreateEventDispatcherHook(RoomWidgetUpdateInfostandUserEvent.PEER, eventDispatcher, onRoomWidgetUpdateEvent);