This commit is contained in:
dank074 2021-09-15 22:20:37 -05:00
commit 714e11fb5b
103 changed files with 2070 additions and 421 deletions

View File

@ -33,6 +33,7 @@
"system.packet.log": false,
"system.pong.manually": true,
"system.pong.interval.ms": 20000,
"room.color.skip.transition": true,
"avatar.mandatory.libraries": [
"bd:1",
"li:0"

View File

@ -5,24 +5,7 @@
"thumbnails.url": "https://nitro.nitrots.co/camera/thumbnail/%thumbnail%.png",
"url.prefix": "http://localhost:3000",
"chat.viewer.height.percentage": 0.40,
"navigator.slider.enabled": true,
"navigator.slider.content": [
{
"title": "Games Hub",
"image": "https://i.imgur.com/TFoivxi.png",
"roomId": 2240
},
{
"title": "Help Desk",
"image": "https://i.imgur.com/GO981GC.png",
"roomId": 2351
},
{
"title": "The Lido",
"image": "https://i.imgur.com/NVH38bV.png",
"roomId": 2346
}
],
"widget.dimmer.colorwheel": false,
"hotelview": {
"widgets": {
"slot.1.widget": "promoarticle",

View File

@ -5,6 +5,7 @@ import { useConfigurationEvent } from './hooks/events/core/configuration/configu
import { useLocalizationEvent } from './hooks/events/nitro/localization/localization-event';
import { dispatchMainEvent, useMainEvent } from './hooks/events/nitro/main-event';
import { useRoomEngineEvent } from './hooks/events/nitro/room/room-engine-event';
import { TransitionAnimation, TransitionAnimationTypes } from './layout';
import { LoadingView } from './views/loading/LoadingView';
import { MainView } from './views/main/MainView';
@ -126,7 +127,9 @@ export const App: FC<{}> = props =>
return (
<div className="nitro-app">
{ (!isReady || isError) && <LoadingView isError={ isError } message={ message } /> }
{ (isReady && !isError) && <MainView /> }
<TransitionAnimation type={ TransitionAnimationTypes.FADE_IN } inProp={ (isReady && !isError) } timeout={ 300 }>
<MainView />
</TransitionAnimation>
</div>
);
}

View File

@ -0,0 +1,35 @@
export class RoomDimmerPreset
{
private _id: number;
private _type: number;
private _color: number;
private _brightness: number;
constructor(id: number, type: number, color: number, brightness: number)
{
this._id = id;
this._type = type;
this._color = color;
this._brightness = brightness;
}
public get id(): number
{
return this._id;
}
public get type(): number
{
return this._type;
}
public get color(): number
{
return this._color;
}
public get brightness(): number
{
return this._brightness;
}
}

View File

@ -0,0 +1,34 @@
import { RoomWidgetUpdateEvent } from './RoomWidgetUpdateEvent';
export class RoomWidgetUpdateCreditFurniEvent extends RoomWidgetUpdateEvent
{
public static CREDIT_FURNI_UPDATE: string = 'RWUCFE_CREDIT_FURNI_UPDATE';
private _objectId: number;
private _value: number;
private _furniType: string;
constructor(type: string, objectId: number, value: number, furniType: string)
{
super(type);
this._objectId = objectId;
this._value = value;
this._furniType = furniType;
}
public get objectId(): number
{
return this._objectId;
}
public get value(): number
{
return this._value;
}
public get furniType(): string
{
return this._furniType;
}
}

View File

@ -0,0 +1,50 @@
import { RoomDimmerPreset } from './RoomDimmerPreset';
import { RoomWidgetUpdateEvent } from './RoomWidgetUpdateEvent';
export class RoomWidgetUpdateDimmerEvent extends RoomWidgetUpdateEvent
{
public static PRESETS: string = 'RWUDE_PRESETS';
public static HIDE: string = 'RWUDE_HIDE';
private _selectedPresetId: number = 0;
private _presets: RoomDimmerPreset[];
constructor(type: string)
{
super(type);
this._presets = [];
}
public get presetCount(): number
{
return this._presets.length;
}
public get presets(): RoomDimmerPreset[]
{
return this._presets;
}
public get selectedPresetId(): number
{
return this._selectedPresetId;
}
public set selectedPresetId(k: number)
{
this._selectedPresetId = k;
}
public setPresetValues(id: number, type: number, color: number, brightness: number):void
{
const preset = new RoomDimmerPreset(id, type, color, brightness);
this._presets[(id - 1)] = preset;
}
public getPresetNumber(id: number): RoomDimmerPreset
{
return this._presets[id];
}
}

View File

@ -0,0 +1,48 @@
import { RoomWidgetUpdateEvent } from './RoomWidgetUpdateEvent';
export class RoomWidgetUpdateDimmerStateEvent extends RoomWidgetUpdateEvent
{
public static DIMMER_STATE: string = 'RWUDSE_DIMMER_STATE';
private _state: number;
private _presetId: number;
private _effectId: number;
private _color: number;
private _brightness: number;
constructor(state: number, presetId: number, effectId: number, color: number, brightness: number)
{
super(RoomWidgetUpdateDimmerStateEvent.DIMMER_STATE);
this._state = state;
this._presetId = presetId;
this._effectId = effectId;
this._color = color;
this._brightness = brightness;
}
public get state(): number
{
return this._state;
}
public get presetId(): number
{
return this._presetId;
}
public get effectId(): number
{
return this._effectId;
}
public get color(): number
{
return this._color;
}
public get brightness(): number
{
return this._brightness;
}
}

View File

@ -0,0 +1,116 @@
import { RoomWidgetUpdateEvent } from './RoomWidgetUpdateEvent';
export class RoomWidgetUpdatePresentDataEvent extends RoomWidgetUpdateEvent
{
public static PACKAGEINFO: string = 'RWUPDE_PACKAGEINFO';
public static CONTENTS: string = 'RWUPDE_CONTENTS';
public static CONTENTS_CLUB: string = 'RWUPDE_CONTENTS_CLUB';
public static CONTENTS_FLOOR: string = 'RWUPDE_CONTENTS_FLOOR';
public static CONTENTS_LANDSCAPE: string = 'RWUPDE_CONTENTS_LANDSCAPE';
public static CONTENTS_WALLPAPER: string = 'RWUPDE_CONTENTS_WALLPAPER';
public static CONTENTS_IMAGE: string = 'RWUPDE_CONTENTS_IMAGE';
private _objectId: number = -1;
private _classId: number = 0;
private _itemType: string = '';
private _giftMessage: string = '';
private _imageUrl: string = null;
private _isController: boolean;
private _purchaserName: string;
private _purchaserFigure: string;
private _placedItemId: number = -1;
private _placedItemType: string = '';
private _placedInRoom: boolean;
constructor(type: string, objectId: number, giftMessage: string, isOwnerOfFurniture: boolean = false, imageUrl: string = null, purchaserName: string = null, purchaserFigure: string = null)
{
super(type);
this._objectId = objectId;
this._giftMessage = giftMessage;
this._imageUrl = imageUrl;
this._isController = isOwnerOfFurniture;
this._purchaserName = purchaserName;
this._purchaserFigure = purchaserFigure;
}
public get objectId(): number
{
return this._objectId;
}
public get classId(): number
{
return this._classId;
}
public set classId(classId: number)
{
this._classId = classId;
}
public get itemType(): string
{
return this._itemType;
}
public set itemType(type: string)
{
this._itemType = type;
}
public get giftMessage(): string
{
return this._giftMessage;
}
public get imageUrl(): string
{
return this._imageUrl;
}
public get isController(): boolean
{
return this._isController;
}
public get purchaserName(): string
{
return this._purchaserName;
}
public get purchaserFigure(): string
{
return this._purchaserFigure;
}
public get placedItemId(): number
{
return this._placedItemId;
}
public set placedItemId(itemId: number)
{
this._placedItemId = itemId;
}
public get placedInRoom(): boolean
{
return this._placedInRoom;
}
public set placedInRoom(flag: boolean)
{
this._placedInRoom = flag;
}
public get placedItemType(): string
{
return this._placedItemType;
}
public set placedItemType(type: string)
{
this._placedItemType = type;
}
}

View File

@ -1,4 +1,5 @@
export * from './IPhotoData';
export * from './RoomDimmerPreset';
export * from './RoomObjectItem';
export * from './RoomWidgetAvatarInfoEvent';
export * from './RoomWidgetChooserContentEvent';
@ -10,8 +11,11 @@ export * from './RoomWidgetRoomObjectUpdateEvent';
export * from './RoomWidgetUpdateBackgroundColorPreviewEvent';
export * from './RoomWidgetUpdateChatEvent';
export * from './RoomWidgetUpdateChatInputContentEvent';
export * from './RoomWidgetUpdateCreditFurniEvent';
export * from './RoomWidgetUpdateCustomStackHeightEvent';
export * from './RoomWidgetUpdateDanceStatusEvent';
export * from './RoomWidgetUpdateDimmerEvent';
export * from './RoomWidgetUpdateDimmerStateEvent';
export * from './RoomWidgetUpdateEvent';
export * from './RoomWidgetUpdateExternalImageEvent';
export * from './RoomWidgetUpdateInfostandEvent';
@ -19,6 +23,7 @@ export * from './RoomWidgetUpdateInfostandFurniEvent';
export * from './RoomWidgetUpdateInfostandPetEvent';
export * from './RoomWidgetUpdateInfostandRentableBotEvent';
export * from './RoomWidgetUpdateInfostandUserEvent';
export * from './RoomWidgetUpdatePresentDataEvent';
export * from './RoomWidgetUpdateRentableBotChatEvent';
export * from './RoomWidgetUpdateRoomViewEvent';
export * from './RoomWidgetUpdateSongEvent';

View File

@ -0,0 +1,59 @@
import { FurnitureExchangeComposer, NitroEvent, RoomObjectVariable, RoomWidgetEnum } from '@nitrots/nitro-renderer';
import { RoomWidgetCreditFurniRedeemMessage, RoomWidgetUpdateCreditFurniEvent, RoomWidgetUpdateEvent } from '..';
import { GetRoomEngine } from '../..';
import { IsOwnerOfFurniture } from '../../..';
import { RoomWidgetFurniToWidgetMessage, RoomWidgetMessage } from '../messages';
import { RoomWidgetHandler } from './RoomWidgetHandler';
export class FurnitureCreditWidgetHandler extends RoomWidgetHandler
{
public processEvent(event: NitroEvent): void
{
return;
}
public processWidgetMessage(message: RoomWidgetMessage): RoomWidgetUpdateEvent
{
switch(message.type)
{
case RoomWidgetFurniToWidgetMessage.REQUEST_CREDITFURNI: {
const creditMessage = (message as RoomWidgetFurniToWidgetMessage);
const roomObject = GetRoomEngine().getRoomObject(creditMessage.roomId, creditMessage.objectId, creditMessage.category);
if(!roomObject || !IsOwnerOfFurniture(roomObject)) return;
this.container.eventDispatcher.dispatchEvent(new RoomWidgetUpdateCreditFurniEvent(RoomWidgetUpdateCreditFurniEvent.CREDIT_FURNI_UPDATE, creditMessage.objectId, roomObject.model.getValue<number>(RoomObjectVariable.FURNITURE_CREDIT_VALUE), (roomObject.model.getValue<string>(RoomObjectVariable.FURNITURE_TYPE_ID) + '_' + creditMessage.type + '_' + creditMessage.objectId)));
break;
}
case RoomWidgetCreditFurniRedeemMessage.REDEEM: {
const redeemMessage = (message as RoomWidgetCreditFurniRedeemMessage);
this.container.roomSession.connection.send(new FurnitureExchangeComposer(redeemMessage.objectId));
break;
}
}
return null;
}
public get type(): string
{
return RoomWidgetEnum.FURNI_CREDIT_WIDGET;
}
public get eventTypes(): string[]
{
return [];
}
public get messageTypes(): string[]
{
return [
RoomWidgetFurniToWidgetMessage.REQUEST_CREDITFURNI,
RoomWidgetCreditFurniRedeemMessage.REDEEM
];
}
}

View File

@ -0,0 +1,114 @@
import { NitroEvent, RoomControllerLevel, RoomEngineDimmerStateEvent, RoomEngineTriggerWidgetEvent, RoomSessionDimmerPresetsEvent, RoomWidgetEnum } from '@nitrots/nitro-renderer';
import { GetRoomEngine } from '../..';
import { GetSessionDataManager } from '../../..';
import { RoomWidgetUpdateDimmerEvent, RoomWidgetUpdateEvent } from '../events';
import { RoomWidgetUpdateDimmerStateEvent } from '../events/RoomWidgetUpdateDimmerStateEvent';
import { RoomWidgetDimmerChangeStateMessage, RoomWidgetDimmerPreviewMessage, RoomWidgetFurniToWidgetMessage, RoomWidgetMessage } from '../messages';
import { RoomWidgetDimmerSavePresetMessage } from '../messages/RoomWidgetDimmerSavePresetMessage';
import { RoomWidgetHandler } from './RoomWidgetHandler';
export class FurnitureDimmerWidgetHandler extends RoomWidgetHandler
{
public processEvent(event: NitroEvent): void
{
switch(event.type)
{
case RoomSessionDimmerPresetsEvent.ROOM_DIMMER_PRESETS: {
const presetsEvent = (event as RoomSessionDimmerPresetsEvent);
const updateEvent = new RoomWidgetUpdateDimmerEvent(RoomWidgetUpdateDimmerEvent.PRESETS);
updateEvent.selectedPresetId = presetsEvent.selectedPresetId;
let i = 0;
while(i < presetsEvent.presetCount)
{
const preset = presetsEvent.getPreset(i);
if(preset) updateEvent.setPresetValues(preset.id, preset.type, preset.color, preset.brightness);
i++;
}
this.container.eventDispatcher.dispatchEvent(updateEvent);
return;
}
case RoomEngineDimmerStateEvent.ROOM_COLOR: {
const stateEvent = (event as RoomEngineDimmerStateEvent);
this.container.eventDispatcher.dispatchEvent(new RoomWidgetUpdateDimmerStateEvent(stateEvent.state, stateEvent.presetId, stateEvent.effectId, stateEvent.color, stateEvent.brightness));
return;
}
case RoomEngineTriggerWidgetEvent.REMOVE_DIMMER: {
this.container.eventDispatcher.dispatchEvent(new RoomWidgetUpdateDimmerEvent(RoomWidgetUpdateDimmerEvent.HIDE));
return;
}
}
}
public processWidgetMessage(message: RoomWidgetMessage): RoomWidgetUpdateEvent
{
switch(message.type)
{
case RoomWidgetFurniToWidgetMessage.REQUEST_DIMMER: {
if(this.canOpenWidget()) this.container.roomSession.requestMoodlightSettings();
break;
}
case RoomWidgetDimmerSavePresetMessage.SAVE_PRESET: {
if(this.canOpenWidget())
{
const savePresetMessage = (message as RoomWidgetDimmerSavePresetMessage);
this.container.roomSession.updateMoodlightData(savePresetMessage.presetNumber, savePresetMessage.effectTypeId, savePresetMessage.color, savePresetMessage.brightness, savePresetMessage.apply);
}
break;
}
case RoomWidgetDimmerChangeStateMessage.CHANGE_STATE: {
if(this.canOpenWidget()) this.container.roomSession.toggleMoodlightState();
break;
}
case RoomWidgetDimmerPreviewMessage.PREVIEW_DIMMER_PRESET: {
const roomId = this.container.roomSession.roomId;
const previewMessage = (message as RoomWidgetDimmerPreviewMessage);
GetRoomEngine().updateObjectRoomColor(roomId, previewMessage.color, previewMessage.brightness, previewMessage.bgOnly);
break;
}
}
return null;
}
private canOpenWidget(): boolean
{
return (this.container.roomSession.isRoomOwner || (this.container.roomSession.controllerLevel >= RoomControllerLevel.GUEST) || GetSessionDataManager().isModerator);
}
public get type(): string
{
return RoomWidgetEnum.ROOM_DIMMER;
}
public get eventTypes(): string[]
{
return [
RoomSessionDimmerPresetsEvent.ROOM_DIMMER_PRESETS,
RoomEngineDimmerStateEvent.ROOM_COLOR,
RoomEngineTriggerWidgetEvent.REMOVE_DIMMER
];
}
public get messageTypes(): string[]
{
return [
RoomWidgetFurniToWidgetMessage.REQUEST_DIMMER,
RoomWidgetDimmerSavePresetMessage.SAVE_PRESET,
RoomWidgetDimmerChangeStateMessage.CHANGE_STATE,
RoomWidgetDimmerPreviewMessage.PREVIEW_DIMMER_PRESET
];
}
}

View File

@ -0,0 +1,223 @@
import { IFurnitureData, IGetImageListener, NitroEvent, NitroRenderTexture, PetFigureData, RoomObjectCategory, RoomObjectVariable, RoomSessionPresentEvent, RoomWidgetEnum, TextureUtils, Vector3d } from '@nitrots/nitro-renderer';
import { GetSessionDataManager, IsOwnerOfFurniture } from '../../..';
import { GetRoomEngine, LocalizeText } from '../../../..';
import { ProductTypeEnum } from '../../../../../views/catalog/common/ProductTypeEnum';
import { RoomWidgetUpdateEvent, RoomWidgetUpdatePresentDataEvent } from '../events';
import { RoomWidgetFurniToWidgetMessage, RoomWidgetPresentOpenMessage } from '../messages';
import { RoomWidgetMessage } from '../messages/RoomWidgetMessage';
import { RoomWidgetHandler } from './RoomWidgetHandler';
export class FurniturePresentWidgetHandler extends RoomWidgetHandler implements IGetImageListener
{
private static FLOOR: string = 'floor';
private static WALLPAPER: string = 'wallpaper';
private static LANDSCAPE: string = 'landscape';
private static POSTER: string = 'poster';
private _lastFurniId: number = -1;
private _name: string = null;
public processEvent(event: NitroEvent): void
{
switch(event.type)
{
case RoomSessionPresentEvent.RSPE_PRESENT_OPENED: {
const presentEvent = (event as RoomSessionPresentEvent);
let furniData: IFurnitureData = null;
if(presentEvent.itemType === ProductTypeEnum.FLOOR)
{
furniData = GetSessionDataManager().getFloorItemData(presentEvent.classId);
}
else if(presentEvent.itemType === ProductTypeEnum.WALL)
{
furniData = GetSessionDataManager().getWallItemData(presentEvent.classId);
}
let isOwnerOfFurni = false;
if(presentEvent.placedInRoom)
{
const roomObject = GetRoomEngine().getRoomObject(this.container.roomSession.roomId, presentEvent.placedItemId, RoomObjectCategory.FLOOR);
if(roomObject) isOwnerOfFurni = IsOwnerOfFurniture(roomObject);
}
let giftImage: string = null;
let dataUpdateEvent: RoomWidgetUpdatePresentDataEvent = null;
switch(presentEvent.itemType)
{
case ProductTypeEnum.WALL: {
if(furniData)
{
switch(furniData.className)
{
case FurniturePresentWidgetHandler.FLOOR:
dataUpdateEvent = new RoomWidgetUpdatePresentDataEvent(RoomWidgetUpdatePresentDataEvent.CONTENTS_FLOOR, 0, LocalizeText('inventory.furni.item.floor.name'), isOwnerOfFurni);
break;
case FurniturePresentWidgetHandler.LANDSCAPE:
dataUpdateEvent = new RoomWidgetUpdatePresentDataEvent(RoomWidgetUpdatePresentDataEvent.CONTENTS_LANDSCAPE, 0, LocalizeText('inventory.furni.item.landscape.name'), isOwnerOfFurni);
break;
case FurniturePresentWidgetHandler.WALLPAPER:
dataUpdateEvent = new RoomWidgetUpdatePresentDataEvent(RoomWidgetUpdatePresentDataEvent.CONTENTS_WALLPAPER, 0, LocalizeText('inventory.furni.item.wallpaper.name'), isOwnerOfFurni);
break;
case FurniturePresentWidgetHandler.POSTER: {
const productCode = presentEvent.productCode;
let extras: string = null;
if(productCode.indexOf('poster') === 0) extras = productCode.replace('poster', '');
giftImage = GetRoomEngine().getFurnitureWallIconUrl(presentEvent.classId, extras);
const productData = GetSessionDataManager().getProductData(productCode);
if(productData) this._name = productData.name;
else if(furniData) this._name = furniData.name;
dataUpdateEvent = new RoomWidgetUpdatePresentDataEvent(RoomWidgetUpdatePresentDataEvent.CONTENTS, 0, this._name, isOwnerOfFurni, giftImage);
break;
}
default: {
giftImage = GetRoomEngine().getFurnitureWallIconUrl(presentEvent.classId);
if(furniData) this._name = furniData.name;
dataUpdateEvent = new RoomWidgetUpdatePresentDataEvent(RoomWidgetUpdatePresentDataEvent.CONTENTS, 0, this._name, isOwnerOfFurni, giftImage);
break;
}
}
}
break;
}
case ProductTypeEnum.HABBO_CLUB:
dataUpdateEvent = new RoomWidgetUpdatePresentDataEvent(RoomWidgetUpdatePresentDataEvent.CONTENTS_CLUB, 0, LocalizeText('widget.furni.present.hc'), false);
break;
default: {
if(presentEvent.placedItemType === ProductTypeEnum.PET)
{
const petfigureString = presentEvent.petFigureString;
if(petfigureString && petfigureString.length)
{
const petFigureData = new PetFigureData(petfigureString);
const petImage = GetRoomEngine().getRoomObjectPetImage(petFigureData.typeId, petFigureData.paletteId, petFigureData.color, new Vector3d(90), 64, this, true, 0, petFigureData.customParts);
if(petImage) giftImage = petImage.getImage().src;
}
}
if(!giftImage)
{
const furniImage = GetRoomEngine().getFurnitureFloorImage(presentEvent.classId, new Vector3d(90), 64, this);
if(furniImage) giftImage = furniImage.getImage().src;
}
const productData = GetSessionDataManager().getProductData(presentEvent.productCode);
if(productData) this._name = productData.name;
else this._name = furniData.name;
if(giftImage) dataUpdateEvent = new RoomWidgetUpdatePresentDataEvent(RoomWidgetUpdatePresentDataEvent.CONTENTS, 0, this._name, isOwnerOfFurni, giftImage);
break;
}
}
if(dataUpdateEvent)
{
dataUpdateEvent.classId = presentEvent.classId;
dataUpdateEvent.itemType = presentEvent.itemType;
dataUpdateEvent.placedItemId = presentEvent.placedItemId;
dataUpdateEvent.placedInRoom = presentEvent.placedInRoom;
dataUpdateEvent.placedItemType = presentEvent.placedItemType;
this.container.eventDispatcher.dispatchEvent(dataUpdateEvent);
}
return;
}
}
}
public processWidgetMessage(message: RoomWidgetMessage): RoomWidgetUpdateEvent
{
switch(message.type)
{
case RoomWidgetFurniToWidgetMessage.REQUEST_PRESENT: {
const widgetMessage = (message as RoomWidgetFurniToWidgetMessage);
const roomObject = GetRoomEngine().getRoomObject(widgetMessage.roomId, widgetMessage.objectId, widgetMessage.category);
if(!roomObject) return null;
this._lastFurniId = widgetMessage.objectId;
const giftMessage = (roomObject.model.getValue<string>(RoomObjectVariable.FURNITURE_DATA) || '');
const purchaserName = roomObject.model.getValue<string>(RoomObjectVariable.FURNITURE_PURCHASER_NAME);
const purchaserFigure = roomObject.model.getValue<string>(RoomObjectVariable.FURNITURE_PURCHASER_FIGURE);
const typeId = roomObject.model.getValue<number>(RoomObjectVariable.FURNITURE_TYPE_ID);
const extras = roomObject.model.getValue<string>(RoomObjectVariable.FURNITURE_EXTRAS);
const giftImage = GetRoomEngine().getFurnitureFloorImage(typeId, new Vector3d(180), 64, null, 0, extras);
this.container.eventDispatcher.dispatchEvent(new RoomWidgetUpdatePresentDataEvent(RoomWidgetUpdatePresentDataEvent.PACKAGEINFO, widgetMessage.objectId, giftMessage, IsOwnerOfFurniture(roomObject), giftImage.getImage().src, purchaserName, purchaserFigure));
break;
}
case RoomWidgetPresentOpenMessage.OPEN_PRESENT: {
const openMessage = (message as RoomWidgetPresentOpenMessage);
if(openMessage.objectId !== this._lastFurniId) return null;
this.container.roomSession.openGift(openMessage.objectId);
GetRoomEngine().changeObjectModelData(GetRoomEngine().activeRoomId, openMessage.objectId, RoomObjectCategory.FLOOR, RoomObjectVariable.FURNITURE_DISABLE_PICKING_ANIMATION, 1);
break;
}
}
return null;
}
public imageReady(id: number, texture: NitroRenderTexture, image: HTMLImageElement = null): void
{
let imageUrl: string = null;
if(image) imageUrl = image.src;
else if(texture) imageUrl = TextureUtils.generateImageUrl(texture);
this.container.eventDispatcher.dispatchEvent(new RoomWidgetUpdatePresentDataEvent(RoomWidgetUpdatePresentDataEvent.CONTENTS_IMAGE, 0, this._name, false, imageUrl));
}
public imageFailed(id: number): void
{
}
public get type(): string
{
return RoomWidgetEnum.FURNI_PRESENT_WIDGET;
}
public get eventTypes(): string[]
{
return [
RoomSessionPresentEvent.RSPE_PRESENT_OPENED
];
}
public get messageTypes(): string[]
{
return [
RoomWidgetFurniToWidgetMessage.REQUEST_PRESENT,
RoomWidgetPresentOpenMessage.OPEN_PRESENT
];
}
}

View File

@ -1,4 +1,5 @@
import { AvatarExpressionEnum, HabboClubLevelEnum, NitroEvent, RoomControllerLevel, RoomSessionChatEvent, RoomSettingsComposer, RoomWidgetEnum, RoomZoomEvent, TextureUtils } from '@nitrots/nitro-renderer';
import { GetConfiguration, GetNitroInstance } from '../../..';
import { GetRoomEngine, GetSessionDataManager } from '../../../..';
import { SendMessageHook } from '../../../../../hooks/messages';
import { RoomWidgetFloodControlEvent, RoomWidgetUpdateEvent } from '../events';
@ -146,6 +147,12 @@ export class RoomWidgetChatInputHandler extends RoomWidgetHandler
}
return null;
case ':togglefps': {
if(GetNitroInstance().ticker.maxFPS > 0) GetNitroInstance().ticker.maxFPS = 0;
else GetNitroInstance().ticker.maxFPS = GetConfiguration('system.animation.fps');
return null;
}
case ':client':
case ':nitro':
case ':billsonnn':

View File

@ -1,8 +1,11 @@
export * from './DoorbellWidgetHandler';
export * from './FurniChooserWidgetHandler';
export * from './FurnitureContextMenuWidgetHandler';
export * from './FurnitureCreditWidgetHandler';
export * from './FurnitureCustomStackHeightWidgetHandler';
export * from './FurnitureDimmerWidgetHandler';
export * from './FurnitureExternalImageWidgetHandler';
export * from './FurniturePresentWidgetHandler';
export * from './IRoomWidgetHandler';
export * from './IRoomWidgetHandlerManager';
export * from './RoomWidgetAvatarInfoHandler';

View File

@ -0,0 +1,20 @@
import { RoomWidgetMessage } from './RoomWidgetMessage';
export class RoomWidgetCreditFurniRedeemMessage extends RoomWidgetMessage
{
public static REDEEM: string = 'RWCFRM_REDEEM';
private _objectId: number;
constructor(type: string, objectId: number)
{
super(type);
this._objectId = objectId;
}
public get objectId(): number
{
return this._objectId;
}
}

View File

@ -0,0 +1,11 @@
import { RoomWidgetMessage } from './RoomWidgetMessage';
export class RoomWidgetDimmerChangeStateMessage extends RoomWidgetMessage
{
public static CHANGE_STATE: string = 'RWCDSM_CHANGE_STATE';
constructor()
{
super(RoomWidgetDimmerChangeStateMessage.CHANGE_STATE);
}
}

View File

@ -0,0 +1,34 @@
import { RoomWidgetMessage } from './RoomWidgetMessage';
export class RoomWidgetDimmerPreviewMessage extends RoomWidgetMessage
{
public static PREVIEW_DIMMER_PRESET: string = 'RWDPM_PREVIEW_DIMMER_PRESET';
private _color: number;
private _brightness: number;
private _bgOnly: boolean;
constructor(color: number, brightness: number, bgOnly: boolean)
{
super(RoomWidgetDimmerPreviewMessage.PREVIEW_DIMMER_PRESET);
this._color = color;
this._brightness = brightness;
this._bgOnly = bgOnly;
}
public get color(): number
{
return this._color;
}
public get brightness(): number
{
return this._brightness;
}
public get bgOnly(): boolean
{
return this._bgOnly;
}
}

View File

@ -0,0 +1,48 @@
import { RoomWidgetMessage } from './RoomWidgetMessage';
export class RoomWidgetDimmerSavePresetMessage extends RoomWidgetMessage
{
public static SAVE_PRESET: string = 'RWSDPM_SAVE_PRESET';
private _presetNumber: number;
private _effectTypeId: number;
private _color: number;
private _brightness: number;
private _apply: boolean;
constructor(presetNumber: number, effectTypeId: number, color: number, brightness: number, apply: boolean)
{
super(RoomWidgetDimmerSavePresetMessage.SAVE_PRESET);
this._presetNumber = presetNumber;
this._effectTypeId = effectTypeId;
this._color = color;
this._brightness = brightness;
this._apply = apply;
}
public get presetNumber(): number
{
return this._presetNumber;
}
public get effectTypeId(): number
{
return this._effectTypeId;
}
public get color(): number
{
return this._color;
}
public get brightness(): number
{
return this._brightness;
}
public get apply(): boolean
{
return this._apply;
}
}

View File

@ -0,0 +1,20 @@
import { RoomWidgetMessage } from './RoomWidgetMessage';
export class RoomWidgetPresentOpenMessage extends RoomWidgetMessage
{
public static OPEN_PRESENT: string = 'RWPOM_OPEN_PRESENT';
private _objectId: number;
constructor(type: string, objectId: number)
{
super(type);
this._objectId = objectId;
}
public get objectId(): number
{
return this._objectId;
}
}

View File

@ -4,11 +4,16 @@ export * from './RoomWidgetChangePostureMessage';
export * from './RoomWidgetChatMessage';
export * from './RoomWidgetChatSelectAvatarMessage';
export * from './RoomWidgetChatTypingMessage';
export * from './RoomWidgetCreditFurniRedeemMessage';
export * from './RoomWidgetDanceMessage';
export * from './RoomWidgetDimmerChangeStateMessage';
export * from './RoomWidgetDimmerPreviewMessage';
export * from './RoomWidgetDimmerSavePresetMessage';
export * from './RoomWidgetFurniActionMessage';
export * from './RoomWidgetFurniToWidgetMessage';
export * from './RoomWidgetLetUserInMessage';
export * from './RoomWidgetMessage';
export * from './RoomWidgetPresentOpenMessage';
export * from './RoomWidgetRequestWidgetMessage';
export * from './RoomWidgetRoomObjectMessage';
export * from './RoomWidgetUseProductMessage';

View File

@ -4,4 +4,14 @@ export class ColorUtils
{
return ('#' + color);
}
public static makeColorNumberHex(color: number): string
{
return ( '#' + color.toString(16));
}
public static convertFromHex(color: string): number
{
return parseInt(color.replace('#', ''), 16);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 795 B

View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -9,6 +9,8 @@ export class CatalogEvent extends NitroEvent
public static PURCHASE_FAILED: string = 'CE_PURCHASE_FAILED';
public static SOLD_OUT: string = 'CE_SOLD_OUT';
public static APPROVE_NAME_RESULT: string = 'CE_APPROVE_NAME_RESULT';
public static GIFT_RECEIVER_NOT_FOUND: string = 'CE_GIFT_RECEIVER_NOT_FOUND';
public static PURCHASE_APPROVED: string = 'CE_PURCHASE_APPROVED';
public static INIT_GIFT: string = 'CE_INIT_GIFT';
public static CATALOG_RESET: string = 'CE_RESET';
}

View File

@ -0,0 +1,9 @@
import { CatalogEvent } from './CatalogEvent';
export class CatalogGiftReceiverNotFoundEvent extends CatalogEvent
{
constructor()
{
super(CatalogEvent.GIFT_RECEIVER_NOT_FOUND);
}
}

View File

@ -0,0 +1,32 @@
import { CatalogEvent } from './CatalogEvent';
export class CatalogInitGiftEvent extends CatalogEvent
{
private _pageId: number;
private _offerId: number;
private _extraData: string;
constructor(pageId: number, offerId: number, extraData: string)
{
super(CatalogEvent.INIT_GIFT);
this._pageId = pageId;
this._offerId = offerId;
this._extraData = extraData;
}
public get pageId(): number
{
return this._pageId;
}
public get offerId(): number
{
return this._offerId;
}
public get extraData(): string
{
return this._extraData;
}
}

View File

@ -1,4 +1,4 @@
import { MessengerFriend } from '../../views/friend-list/common/MessengerFriend';
import { MessengerFriend } from '../../views/friends/common/MessengerFriend';
import { FriendListEvent } from './FriendListEvent';
export class FriendListContentEvent extends FriendListEvent

View File

@ -0,0 +1,6 @@
import { unstable_batchedUpdates } from 'react-dom';
export const BatchUpdates = (callback: () => any) =>
{
return unstable_batchedUpdates(callback);
}

View File

@ -1,3 +1,4 @@
export * from './BatchUpdates';
export * from './events';
export * from './events/core';
export * from './events/nitro';

View File

@ -29,3 +29,4 @@
@import './mini-camera/NitroLayoutMiniCameraView';
@import './notification-bubble/NotificationBubbleView';
@import './trophy/NitroLayoutTrophyView';
@import './gift-card/NitroLayoutGiftCardView';

View File

@ -1,12 +1,17 @@
import { FC, useState } from 'react';
import { FC, useEffect, useState } from 'react';
import { NitroCardAccordionItemViewProps } from './NitroCardAccordionItemView.types';
export const NitroCardAccordionItemView: FC<NitroCardAccordionItemViewProps> = props =>
{
const { className = '', headerClassName = '', contentClassName = '', headerText = '' } = props;
const { className = '', headerClassName = '', contentClassName = '', headerText = '', defaultState = false } = props;
const [ isExpanded, setIsExpanded ] = useState(false);
useEffect(() =>
{
setIsExpanded(defaultState);
}, [ defaultState ]);
return (
<div className={ 'nitro-card-accordion-item ' + className + (isExpanded ? ' active' : '') }>
<div className={ 'nitro-card-accordion-item-header px-2 py-1 d-flex ' + headerClassName } onClick={ () => setIsExpanded((value) => !value) }>

View File

@ -4,4 +4,5 @@ export interface NitroCardAccordionItemViewProps
headerClassName?: string;
contentClassName?: string;
headerText: string;
defaultState?: boolean;
}

View File

@ -0,0 +1,41 @@
.nitro-gift-card {
width: 306px;
height: 159px;
background: url(../../assets/images/gift/gift_tag.png) center no-repeat;
.gift-face {
width: 65px;
.gift-incognito {
width: 37px;
height: 48px;
background: url(../../assets/images/gift/incognito.png) center no-repeat;
}
.gift-avatar {
position: relative;
overflow: hidden;
width: 40px;
height: 50px;
.avatar-image {
position: absolute;
left: -25px;
top: -20px;
}
}
}
.gift-message {
width: 100%;
min-width: 100%;
max-width: 100%;
height: 90px;
min-height: 90px;
max-height: 90px;
border: none;
resize: none;
outline: none;
line-height: 17px;
}
}

View File

@ -0,0 +1,25 @@
import { FC } from 'react';
import { LocalizeText } from '../../api';
import { AvatarImageView } from '../../views/shared/avatar-image/AvatarImageView';
import { NitroLayoutGiftCardViewProps } from './NitroLayoutGiftCardView.types';
export const NitroLayoutGiftCardView: FC<NitroLayoutGiftCardViewProps> = props =>
{
const { figure = null, userName = null, message = null, editable = false, onChange = null } = props;
return (
<div className="nitro-gift-card d-flex text-black">
<div className="d-flex align-items-center justify-content-center gift-face flex-shrink-0">
{ !userName && <div className="gift-incognito"></div> }
{ figure && <div className="gift-avatar">
<AvatarImageView figure={ figure } direction={ 2 } headOnly={ true } />
</div> }
</div>
<div className="d-flex flex-column w-100 pt-4 pb-4 pe-4 ps-3">
{ !editable && <div className="gift-message">{ message }</div> }
{ editable && onChange && <textarea className="gift-message" maxLength={ 140 } value={ message } onChange={ (e) => onChange(e.target.value) } placeholder={ LocalizeText('catalog.gift_wrapping_new.message_hint') }></textarea> }
{ userName && <div className="mt-auto text-end fst-italic">{ LocalizeText('catalog.gift_wrapping_new.message_from', ['name'], [userName]) }</div> }
</div>
</div>
);
};

View File

@ -0,0 +1,8 @@
export interface NitroLayoutGiftCardViewProps
{
figure?:string;
userName?: string;
message?: string;
editable?: boolean;
onChange?: (value: string) => void;
}

View File

@ -0,0 +1,2 @@
export * from './NitroLayoutGiftCardView';
export * from './NitroLayoutGiftCardView.types';

View File

@ -1,5 +1,6 @@
export * from './card';
export * from './draggable-window';
export * from './gift-card';
export * from './loading-spinner';
export * from './mini-camera';
export * from './notification-bubble';

View File

@ -2,7 +2,7 @@
@import './avatar-editor/AvatarEditorView';
@import './camera/CameraWidgetView';
@import './catalog/CatalogView';
@import './friend-list/FriendListView';
@import './friends/FriendsView';
@import './groups/GroupView';
@import './hotel-view/HotelView';
@import './inventory/InventoryView';

View File

@ -1,7 +1,8 @@
import { ApproveNameMessageEvent, CatalogPageMessageEvent, CatalogPagesListEvent, CatalogPublishedMessageEvent, GiftWrappingConfigurationEvent, HabboClubOffersMessageEvent, LimitedEditionSoldOutEvent, ProductOfferEvent, PurchaseErrorMessageEvent, PurchaseNotAllowedMessageEvent, PurchaseOKMessageEvent, SellablePetPalettesMessageEvent, UserSubscriptionEvent } from '@nitrots/nitro-renderer';
import { ApproveNameMessageEvent, CatalogPageMessageEvent, CatalogPagesListEvent, CatalogPublishedMessageEvent, GiftReceiverNotFoundEvent, GiftWrappingConfigurationEvent, HabboClubOffersMessageEvent, LimitedEditionSoldOutEvent, ProductOfferEvent, PurchaseErrorMessageEvent, PurchaseNotAllowedMessageEvent, PurchaseOKMessageEvent, SellablePetPalettesMessageEvent, UserSubscriptionEvent } from '@nitrots/nitro-renderer';
import { GuildMembershipsMessageEvent } from '@nitrots/nitro-renderer/src/nitro/communication/messages/incoming/user/GuildMembershipsMessageEvent';
import { FC, useCallback } from 'react';
import { CatalogNameResultEvent, CatalogPurchaseFailureEvent } from '../../events';
import { CatalogGiftReceiverNotFoundEvent } from '../../events/catalog/CatalogGiftReceiverNotFoundEvent';
import { CatalogPurchasedEvent } from '../../events/catalog/CatalogPurchasedEvent';
import { CatalogPurchaseSoldOutEvent } from '../../events/catalog/CatalogPurchaseSoldOutEvent';
import { dispatchUiEvent } from '../../hooks/events/ui/ui-event';
@ -96,6 +97,11 @@ export const CatalogMessageHandler: FC<CatalogMessageHandlerProps> = props =>
dispatchUiEvent(new CatalogNameResultEvent(parser.result, parser.validationInfo));
}, []);
const onGiftReceiverNotFoundEvent = useCallback(() =>
{
dispatchUiEvent(new CatalogGiftReceiverNotFoundEvent());
}, []);
const onHabboClubOffersMessageEvent = useCallback((event: HabboClubOffersMessageEvent) =>
{
const parser = event.getParser();
@ -168,6 +174,7 @@ export const CatalogMessageHandler: FC<CatalogMessageHandlerProps> = props =>
CreateMessageHook(GuildMembershipsMessageEvent, onGuildMembershipsMessageEvent);
CreateMessageHook(SellablePetPalettesMessageEvent, onSellablePetPalettesMessageEvent);
CreateMessageHook(ApproveNameMessageEvent, onApproveNameMessageEvent);
CreateMessageHook(GiftReceiverNotFoundEvent, onGiftReceiverNotFoundEvent);
CreateMessageHook(HabboClubOffersMessageEvent, onHabboClubOffersMessageEvent);
CreateMessageHook(UserSubscriptionEvent, onUserSubscriptionEvent);
CreateMessageHook(CatalogPublishedMessageEvent, onCatalogPublishedMessageEvent);

View File

@ -10,6 +10,7 @@ import { CatalogMode, CatalogViewProps } from './CatalogView.types';
import { BuildCatalogPageTree } from './common/CatalogUtilities';
import { CatalogContextProvider } from './context/CatalogContext';
import { CatalogReducer, initialCatalog } from './reducers/CatalogReducer';
import { CatalogPageGiftView } from './views/gift/CatalogPageGiftView';
import { ACTIVE_PAGES, CatalogNavigationView } from './views/navigation/CatalogNavigationView';
import { CatalogPageView } from './views/page/CatalogPageView';
@ -213,6 +214,7 @@ export const CatalogView: FC<CatalogViewProps> = props =>
</div>
</NitroCardContentView>
</NitroCardView> }
<CatalogPageGiftView />
</CatalogContextProvider>
);
}

View File

@ -2,3 +2,4 @@
@import './navigation/CatalogNavigationView';
@import './page/CatalogPageView';
@import './search/CatalogSearchView';
@import './gift/CatalogPageGiftView';

View File

@ -0,0 +1,14 @@
.nitro-catalog-gift {
.gift-preview {
width: 80px;
height: 80px;
overflow: hidden;
}
.gift-color {
width: 15px;
height: 15px;
border-radius: $border-radius;
}
}

View File

@ -0,0 +1,235 @@
import { PurchaseFromCatalogAsGiftComposer } from '@nitrots/nitro-renderer';
import classNames from 'classnames';
import { FC, useCallback, useEffect, useMemo, useState } from 'react';
import { GetSessionDataManager, LocalizeText } from '../../../../api';
import { CatalogEvent } from '../../../../events';
import { CatalogInitGiftEvent } from '../../../../events/catalog/CatalogInitGiftEvent';
import { SendMessageHook, useUiEvent } from '../../../../hooks';
import { NitroCardContentView, NitroCardHeaderView, NitroCardView, NitroLayoutGiftCardView } from '../../../../layout';
import { CurrencyIcon } from '../../../shared/currency-icon/CurrencyIcon';
import { FurniImageView } from '../../../shared/furni-image/FurniImageView';
import { useCatalogContext } from '../../context/CatalogContext';
export const CatalogPageGiftView: FC<{}> = props =>
{
const { catalogState = null } = useCatalogContext();
const { giftConfiguration = null } = catalogState;
const [ isVisible, setIsVisible ] = useState<boolean>(false);
const [ pageId, setPageId ] = useState<number>(0);
const [ offerId, setOfferId ] = useState<number>(0);
const [ receiverName, setReceiverName ] = useState<string>('');
const [ showMyFace, setShowMyFace ] = useState<boolean>(true);
const [ message, setMessage ] = useState<string>('');
const [ colors, setColors ] = useState<{ id: number, color: string }[]>([]);
const [ selectedBoxIndex, setSelectedBoxIndex ] = useState<number>(0);
const [ selectedRibbonIndex, setSelectedRibbonIndex ] = useState<number>(0);
const [ selectedColorId, setSelectedColorId ] = useState<number>(0);
const [ maxBoxIndex, setMaxBoxIndex ] = useState<number>(0);
const [ maxRibbonIndex, setMaxRibbonIndex ] = useState<number>(0);
const [ receiverNotFound, setReceiverNotFound ] = useState<boolean>(false);
useEffect(() =>
{
setReceiverNotFound(false);
}, [ receiverName ]);
useEffect(() =>
{
if(!giftConfiguration) return;
setMaxBoxIndex(giftConfiguration.boxTypes.length - 1);
setMaxRibbonIndex(giftConfiguration.ribbonTypes.length - 1);
const newColors: { id: number, color: string }[] = [];
for(const colorId of giftConfiguration.stuffTypes)
{
const giftData = GetSessionDataManager().getFloorItemData(colorId);
if(!giftData) continue;
if(giftData.colors && giftData.colors.length > 0) newColors.push({ id: colorId, color: `#${giftData.colors[0].toString(16)}` });
}
setSelectedColorId(newColors[0].id);
setColors(newColors);
}, [ giftConfiguration ]);
const close = useCallback(() =>
{
setIsVisible(false);
setPageId(0);
setOfferId(0);
setReceiverName('');
setShowMyFace(true);
setMessage('');
setSelectedBoxIndex(0);
setSelectedRibbonIndex(0);
setSelectedColorId(colors[0].id);
}, [ colors ]);
const onCatalogEvent = useCallback((event: CatalogEvent) =>
{
switch(event.type)
{
case CatalogEvent.PURCHASE_SUCCESS:
close();
return;
case CatalogEvent.INIT_GIFT:
const castedEvent = (event as CatalogInitGiftEvent);
close();
setPageId(castedEvent.pageId);
setOfferId(castedEvent.offerId);
setIsVisible(true);
return;
case CatalogEvent.GIFT_RECEIVER_NOT_FOUND:
setReceiverNotFound(true);
return;
}
}, [ close ]);
useUiEvent(CatalogEvent.PURCHASE_SUCCESS, onCatalogEvent);
useUiEvent(CatalogEvent.INIT_GIFT, onCatalogEvent);
useUiEvent(CatalogEvent.GIFT_RECEIVER_NOT_FOUND, onCatalogEvent);
const isBoxDefault = useMemo(() =>
{
return giftConfiguration ? giftConfiguration.defaultStuffTypes.findIndex(s => s === giftConfiguration.boxTypes[selectedBoxIndex]) > -1 : true;
}, [ giftConfiguration, selectedBoxIndex ]);
const boxName = useMemo(() =>
{
return isBoxDefault ? 'catalog.gift_wrapping_new.box.default' : `catalog.gift_wrapping_new.box.${selectedBoxIndex}`;
}, [ isBoxDefault, selectedBoxIndex ]);
const ribbonName = useMemo(() =>
{
return `catalog.gift_wrapping_new.ribbon.${selectedRibbonIndex}`;
}, [ selectedRibbonIndex ]);
const priceText = useMemo(() =>
{
return isBoxDefault ? 'catalog.gift_wrapping_new.freeprice' : 'catalog.gift_wrapping_new.price';
}, [ isBoxDefault ]);
const extraData = useMemo(() =>
{
if(!giftConfiguration) return '';
return ((giftConfiguration.boxTypes[selectedBoxIndex] * 1000) + giftConfiguration.ribbonTypes[selectedRibbonIndex]).toString();
}, [ giftConfiguration, selectedBoxIndex, selectedRibbonIndex ]);
const isColorable = useMemo(() =>
{
if(!giftConfiguration) return false;
const boxType = giftConfiguration.boxTypes[selectedBoxIndex];
return (boxType === 8 || (boxType >= 3 && boxType <= 6)) ? false : true;
}, [ giftConfiguration, selectedBoxIndex ]);
const handleAction = useCallback((action: string) =>
{
switch(action)
{
case 'prev_box':
setSelectedBoxIndex(value =>
{
return (value === 0 ? maxBoxIndex : value - 1);
});
return;
case 'next_box':
setSelectedBoxIndex(value =>
{
return (value === maxBoxIndex ? 0 : value + 1);
});
return;
case 'prev_ribbon':
setSelectedRibbonIndex(value =>
{
return (value === 0 ? maxRibbonIndex : value - 1);
});
return;
case 'next_ribbon':
setSelectedRibbonIndex(value =>
{
return (value === maxRibbonIndex ? 0 : value + 1);
});
return;
case 'buy':
if(!receiverName || receiverName.length === 0)
{
setReceiverNotFound(true);
return;
}
SendMessageHook(new PurchaseFromCatalogAsGiftComposer(pageId, offerId, extraData, receiverName, message, selectedColorId, selectedBoxIndex, selectedRibbonIndex, showMyFace));
return;
}
}, [ extraData, maxBoxIndex, maxRibbonIndex, message, offerId, pageId, receiverName, selectedBoxIndex, selectedColorId, selectedRibbonIndex, showMyFace ]);
if(!giftConfiguration || !giftConfiguration.isEnabled || !isVisible) return null;
return (
<NitroCardView uniqueKey="catalog-gift" className="nitro-catalog-gift" simple={ true }>
<NitroCardHeaderView headerText={ LocalizeText('catalog.gift_wrapping.title') } onCloseClick={ close } />
<NitroCardContentView className="text-black">
<div className="form-group">
<label>{ LocalizeText('catalog.gift_wrapping.receiver') }</label>
<input type="text" className={ 'form-control form-control-sm' + classNames({ ' is-invalid': receiverNotFound }) } value={ receiverName } onChange={ (e) => setReceiverName(e.target.value) } />
{ receiverNotFound && <div className="invalid-feedback">{ LocalizeText('catalog.gift_wrapping.receiver_not_found.title') }</div> }
</div>
<div className="mt-2">
<NitroLayoutGiftCardView figure={ GetSessionDataManager().figure } userName={ GetSessionDataManager().userName } message={ message } editable={ true } onChange={ (value) => setMessage(value) } />
</div>
<div className="form-check mt-1">
<input className="form-check-input" type="checkbox" name="showMyFace" checked={ showMyFace } onChange={ (e) => setShowMyFace(value => !value) } />
<label className="form-check-label">{ LocalizeText('catalog.gift_wrapping.show_face.title') }</label>
</div>
<div className="d-flex gap-2 mt-1 align-items-center">
<div className="gift-preview">
{ selectedColorId && <FurniImageView spriteId={ selectedColorId } type="s" extras={ extraData } /> }
</div>
<div className="d-flex flex-column gap-2">
<div className="d-flex gap-2">
<div className="btn-group">
<button className="btn btn-primary" onClick={ () => handleAction('prev_box') }><i className="fas fa-chevron-left" /></button>
<button className="btn btn-primary" onClick={ () => handleAction('next_box') }><i className="fas fa-chevron-right" /></button>
</div>
<div>
<div className="fw-bold">{ LocalizeText(boxName) }</div>
<div className="d-flex align-items-center gap-1">{ LocalizeText(priceText, ['price'], [giftConfiguration.price.toString()]) }<CurrencyIcon type={ -1 } /></div>
</div>
</div>
<div className="d-flex gap-2 align-items-center">
<div className="btn-group">
<button className="btn btn-primary" onClick={ () => handleAction('prev_ribbon') }><i className="fas fa-chevron-left" /></button>
<button className="btn btn-primary" onClick={ () => handleAction('next_ribbon') }><i className="fas fa-chevron-right" /></button>
</div>
<div>
<div className="fw-bold">{ LocalizeText(ribbonName) }</div>
</div>
</div>
</div>
</div>
<div className="mt-1">
<div className="fw-bold">{ LocalizeText('catalog.gift_wrapping.pick_color') }</div>
<div className="btn-group w-100">
{ colors.map(color =>
{
return <button key={ color.id } className={ 'btn btn-dark btn-sm' + classNames({ ' active': color.id === selectedColorId }) } disabled={ !isColorable } style={{ backgroundColor: color.color }} onClick={ () => setSelectedColorId(color.id) }></button>
}) }
</div>
</div>
<div className="d-flex justify-content-between align-items-center mt-2">
<div className="text-decoration-underline cursor-pointer" onClick={ close }>{ LocalizeText('cancel') }</div>
<button className="btn btn-success" onClick={ () => handleAction('buy') }>{ LocalizeText('catalog.gift_wrapping.give_gift') }</button>
</div>
</NitroCardContentView>
</NitroCardView>
);
};

View File

@ -6,5 +6,11 @@
height: 80px !important;
max-height: 80px !important;
}
.hc-banner {
width: 68px;
height: 40px;
background: url(../../../../../../assets/images/catalog/hc_big.png) center no-repeat;
}
}
}

View File

@ -1,22 +1,43 @@
import { ClubOfferData, GetClubOffersMessageComposer, PurchaseFromCatalogComposer } from '@nitrots/nitro-renderer';
import { FC, useCallback, useEffect, useMemo, useState } from 'react';
import { Button } from 'react-bootstrap';
import { LocalizeText } from '../../../../../../api';
import { CatalogEvent } from '../../../../../../events/catalog/CatalogEvent';
import { useUiEvent } from '../../../../../../hooks';
import { SendMessageHook } from '../../../../../../hooks/messages/message-event';
import { NitroCardGridItemView } from '../../../../../../layout/card/grid/item/NitroCardGridItemView';
import { NitroCardGridView } from '../../../../../../layout/card/grid/NitroCardGridView';
import { LoadingSpinnerView } from '../../../../../../layout/loading-spinner/LoadingSpinnerView';
import { GetCurrencyAmount } from '../../../../../purse/common/CurrencyHelper';
import { GLOBAL_PURSE } from '../../../../../purse/PurseView';
import { CurrencyIcon } from '../../../../../shared/currency-icon/CurrencyIcon';
import { GetCatalogPageImage } from '../../../../common/CatalogUtilities';
import { useCatalogContext } from '../../../../context/CatalogContext';
import { CatalogPurchaseState } from '../../purchase/purchase-button/CatalogPurchaseButtonView.types';
import { CatalogLayoutVipBuyViewProps } from './CatalogLayoutVipBuyView.types';
export const CatalogLayoutVipBuyView: FC<CatalogLayoutVipBuyViewProps> = props =>
{
const { catalogState = null } = useCatalogContext();
const { pageParser = null, clubOffers = null, subscriptionInfo = null } = catalogState;
const [ pendingOffer, setPendingOffer ] = useState<ClubOfferData>(null);
const [ purchaseState, setPurchaseState ] = useState(CatalogPurchaseState.NONE);
const onCatalogEvent = useCallback((event: CatalogEvent) =>
{
switch(event.type)
{
case CatalogEvent.PURCHASE_SUCCESS:
setPurchaseState(CatalogPurchaseState.NONE);
return;
case CatalogEvent.PURCHASE_FAILED:
setPurchaseState(CatalogPurchaseState.FAILED);
return;
}
}, []);
useUiEvent(CatalogEvent.PURCHASE_SUCCESS, onCatalogEvent);
useUiEvent(CatalogEvent.PURCHASE_FAILED, onCatalogEvent);
const getOfferText = useCallback((offer: ClubOfferData) =>
{
@ -77,130 +98,105 @@ export const CatalogLayoutVipBuyView: FC<CatalogLayoutVipBuyViewProps> = props =
{
if(!pendingOffer) return;
setPurchaseState(CatalogPurchaseState.PURCHASE);
SendMessageHook(new PurchaseFromCatalogComposer(pageParser.pageId, pendingOffer.offerId, null, 1));
}, [ pendingOffer, pageParser ]);
useEffect(() =>
{
if(clubOffers === null)
{
SendMessageHook(new GetClubOffersMessageComposer(1));
return;
}
if(clubOffers === null) SendMessageHook(new GetClubOffersMessageComposer(1));
}, [ clubOffers ]);
const getPurchaseButton = (offer: ClubOfferData) =>
const setOffer = useCallback((offer: ClubOfferData) =>
{
if(!offer) return null;
setPurchaseState(CatalogPurchaseState.NONE);
setPendingOffer(offer);
}, []);
if(offer.priceCredits > GetCurrencyAmount(-1))
const getPurchaseButton = useCallback(() =>
{
if(!pendingOffer) return null;
if(pendingOffer.priceCredits > GetCurrencyAmount(-1))
{
return <Button variant="danger" size="sm">{ LocalizeText('catalog.alert.notenough.title') }</Button>;
return <button className="btn btn-danger btn-sm w-100">{ LocalizeText('catalog.alert.notenough.title') }</button>;
}
if(offer.priceActivityPoints > GetCurrencyAmount(offer.priceActivityPointsType))
if(pendingOffer.priceActivityPoints > GetCurrencyAmount(pendingOffer.priceActivityPointsType))
{
return <Button variant="danger" size="sm">{ LocalizeText('catalog.alert.notenough.activitypoints.title.' + offer.priceActivityPointsType) }</Button>;
return <button className="btn btn-danger btn-sm w-100">{ LocalizeText('catalog.alert.notenough.activitypoints.title.' + pendingOffer.priceActivityPointsType) }</button>;
}
return <Button variant="primary" size="sm" onClick={ event => setPendingOffer(offer) }>{ LocalizeText('buy') }</Button>;
}
console.log(pendingOffer)
switch(purchaseState)
{
case CatalogPurchaseState.CONFIRM:
return <button type="button" className="btn btn-warning w-100" onClick={ purchaseSubscription }>{ LocalizeText('catalog.marketplace.confirm_title') }</button>;
case CatalogPurchaseState.PURCHASE:
return <button type="button" className="btn btn-primary w-100" disabled><LoadingSpinnerView /></button>;
case CatalogPurchaseState.FAILED:
return <button type="button" className="btn btn-danger w-100" disabled>{ LocalizeText('generic.failed') }</button>;
case CatalogPurchaseState.NONE:
default:
return <button type="button" className="btn btn-success w-100" onClick={ () => setPurchaseState(CatalogPurchaseState.CONFIRM) }>{ LocalizeText('buy') }</button>;
}
}, [ pendingOffer, purchaseState, purchaseSubscription ]);
return (
<>
<div className="row h-100 nitro-catalog-layout-vip-buy">
<div className="col-7 h-100">
<div className="col-6 h-100">
<NitroCardGridView columns={ 1 } className="vip-buy-grid">
{ clubOffers && (clubOffers.length > 0) && clubOffers.map((offer, index) =>
{
return (
<NitroCardGridItemView key={ index } className="justify-content-between p-1">
{ (pendingOffer === offer) &&
<>
<div className="d-flex flex-column text-black text-small m-1">
<div className="d-flex align-items-center">
<i className="icon icon-catalogue-hc_small me-1"></i>
{ getPurchaseHeader() }
</div>
<div className="text-black text-small">{ getPurchaseValidUntil() }</div>
<div className="d-flex">
{ (offer.priceCredits > 0) &&
<div className="d-flex align-items-center justify-content-end">
<span className="text-black">{ offer.priceCredits }</span>
<CurrencyIcon type={ -1 } />
</div> }
{ (offer.priceActivityPoints > 0) &&
<div className="d-flex align-items-center justify-content-end">
<span className="text-black">{ offer.priceActivityPoints }</span>
<CurrencyIcon type={ offer.priceActivityPointsType } />
</div> }
</div>
<NitroCardGridItemView key={ index } className="justify-content-between py-1 px-2 text-black" itemActive={ pendingOffer === offer } onClick={ () => setOffer(offer) }>
<div className="hc-banner"></div>
<div className="fw-bold">
<div className="text-end">{ getOfferText(offer) }</div>
<div className="d-flex gap-2 justify-content-end">
{ (offer.priceCredits > 0) &&
<div className="d-flex align-items-center justify-content-end gap-1">
<span className="text-black">{ offer.priceCredits }</span>
<CurrencyIcon type={ -1 } />
</div> }
{ (offer.priceActivityPoints > 0) &&
<div className="d-flex align-items-center justify-content-end gap-1">
<span className="text-black">{ offer.priceActivityPoints }</span>
<CurrencyIcon type={ offer.priceActivityPointsType } />
</div> }
</div>
<Button variant="primary" size="sm" onClick={ purchaseSubscription }>{ LocalizeText('catalog.marketplace.confirm_title') }</Button>
</> }
{ (pendingOffer !== offer) &&
<>
<div className="d-flex flex-column text-black text-small m-1">
<div className="d-flex align-items-center">
<i className="icon icon-catalogue-hc_small me-1"></i>
{ getOfferText(offer) }
</div>
<div className="d-flex">
{ (offer.priceCredits > 0) &&
<div className="d-flex align-items-center justify-content-end">
<span className="text-black">{ offer.priceCredits }</span>
<CurrencyIcon type={ -1 } />
</div> }
{ (offer.priceActivityPoints > 0) &&
<div className="d-flex align-items-center justify-content-end">
<span className="text-black">{ offer.priceActivityPoints }</span>
<CurrencyIcon type={ offer.priceActivityPointsType } />
</div> }
</div>
</div>
{ getPurchaseButton(offer) }
</> }
</div>
</NitroCardGridItemView>
);
}) }
</NitroCardGridView>
{/* <div className="row row-cols-1 align-content-start g-0 mb-n1 w-100 catalog-offers-container h-100 overflow-auto">
{ clubOffers && (clubOffers.length > 0) && clubOffers.map((offer, index) =>
{
return (
<div key={ index } className="col pe-1 pb-1 catalog-offer-item-container">
<div className="position-relative border border-2 rounded catalog-offer-item">
<div className="d-flex align-items-center text-black text-small m-1">
<i className="icon icon-catalogue-hc_small me-1"></i>
{ getOfferText(offer) }
</div>
<div className="d-flex">
{ (offer.priceCredits > 0) &&
<div className="d-flex align-items-center justify-content-end">
<span className="text-black ms-1">{ offer.priceCredits }</span>
<CurrencyIcon type={ -1 } />
</div> }
{ (offer.priceActivityPoints > 0) &&
<div className="d-flex align-items-center justify-content-end">
<span className="text-black ms-1">{ offer.priceActivityPoints }</span>
<CurrencyIcon type={ offer.priceActivityPointsType } />
</div> }
</div>
<Button variant="primary" size="sm" onClick={ event => setPendingOffer(offer) } />
</div>
</div>
);
}) }
</div> */}
</div>
<div className="position-relative d-flex flex-column col-5 justify-content-center align-items-center">
<div className="position-relative d-flex flex-column col-6 justify-content-center align-items-center">
<div className="d-block mb-2">
<img alt="" src={ GetCatalogPageImage(pageParser, 1) } />
</div>
<div className="fs-6 text-center text-black lh-sm overflow-hidden" dangerouslySetInnerHTML={ { __html: getSubscriptionDetails } }></div>
<div className="text-center text-black small bg-muted rounded p-1" dangerouslySetInnerHTML={ { __html: getSubscriptionDetails } }></div>
{ pendingOffer && <div className="mt-auto w-100 text-black">
<div className="d-flex gap-2 mb-2 align-items-center">
<div className="w-100">
<div className="fw-bold">{ getPurchaseHeader() }</div>
<div className="small">{ getPurchaseValidUntil() }</div>
</div>
<div>
{ (pendingOffer.priceCredits > 0) &&
<div className="d-flex align-items-center justify-content-end gap-1">
<span className="text-black">{ pendingOffer.priceCredits }</span>
<CurrencyIcon type={ -1 } />
</div> }
{ (pendingOffer.priceActivityPoints > 0) &&
<div className="d-flex align-items-center justify-content-end gap-1">
<span className="text-black">{ pendingOffer.priceActivityPoints }</span>
<CurrencyIcon type={ pendingOffer.priceActivityPointsType } />
</div> }
</div>
</div>
{ getPurchaseButton() }
</div>}
</div>
</div>
</>

View File

@ -71,7 +71,7 @@ export const CatalogPurchaseView: FC<CatalogPurchaseViewProps> = props =>
</div>
<div className="d-flex flex-column mt-1">
<CatalogPurchaseButtonView className="btn-sm w-100" offer={ offer } pageId={ pageId } extra={ extraData } quantity={ quantity } disabled={ disabled } />
{ offer.giftable && <CatalogPurchaseGiftButtonView className="btn-sm w-100 mt-1" offer={ offer } pageId={ pageId } extra={ extraData } quantity={ quantity } /> }
{ offer.giftable && <CatalogPurchaseGiftButtonView className="btn-sm w-100 mt-1" offer={ offer } pageId={ pageId } extra={ extraData } disabled={ disabled } /> }
</div>
</div>
);

View File

@ -36,7 +36,6 @@ export const CatalogPurchaseButtonView: FC<CatalogPurchaseButtonViewProps> = pro
const purchase = useCallback(() =>
{
console.log(pageId, offer.offerId, extra, quantity);
SendMessageHook(new PurchaseFromCatalogComposer(pageId, offer.offerId, extra, quantity));
}, [ pageId, offer, extra, quantity ]);

View File

@ -1,12 +1,19 @@
import { FC } from 'react';
import { FC, useCallback } from 'react';
import { LocalizeText } from '../../../../../../api';
import { CatalogInitGiftEvent } from '../../../../../../events/catalog/CatalogInitGiftEvent';
import { dispatchUiEvent } from '../../../../../../hooks';
import { CatalogPurchaseGiftButtonViewProps } from './CatalogPurchaseGiftButtonView.types';
export const CatalogPurchaseGiftButtonView: FC<CatalogPurchaseGiftButtonViewProps> = props =>
{
const { className = '', offer = null, pageId = -1, extra = null, quantity = 1, isPurchaseAllowed = true, beforePurchase = null } = props;
const { className = '', offer = null, pageId = -1, extra = null, disabled = false } = props;
const initGift = useCallback(() =>
{
dispatchUiEvent(new CatalogInitGiftEvent(pageId, offer.offerId, extra));
}, [ extra, offer, pageId ]);
return (
<button type="button" className={ 'btn btn-secondary ' + className }>{ LocalizeText('catalog.purchase_confirmation.gift') }</button>
<button type="button" className={ 'btn btn-secondary ' + className } onClick={ initGift } disabled={ disabled }>{ LocalizeText('catalog.purchase_confirmation.gift') }</button>
);
}

View File

@ -6,7 +6,5 @@ export interface CatalogPurchaseGiftButtonViewProps
offer: CatalogPageMessageOfferData;
pageId: number;
extra?: string;
quantity?: number;
isPurchaseAllowed?: boolean;
beforePurchase?: () => void;
disabled?: boolean;
}

View File

@ -1,5 +0,0 @@
.nitro-friend-list {
width: 250px;
}
@import './views//friend-bar/FriendBarView';

View File

@ -1,17 +0,0 @@
import { FC } from 'react';
import { FriendListFriendsItemView } from '../friends-item/FriendListFriendsItemView';
import { FriendListFriendsViewProps } from './FriendListFriendsView.types';
export const FriendListFriendsView: FC<FriendListFriendsViewProps> = props =>
{
const { list = null } = props;
if(!list) return null;
return (<>
{ list.map((friend, index) =>
{
return <FriendListFriendsItemView key={ index } friend={ friend } />
}) }
</>);
}

View File

@ -1,5 +0,0 @@
import { MessengerFriend } from './../../common/MessengerFriend';
export interface FriendListFriendsViewProps
{
list: MessengerFriend[];
}

View File

@ -1,6 +0,0 @@
import { MessengerRequest } from './../../common/MessengerRequest';
export interface FriendListRequestsItemViewProps
{
request: MessengerRequest;
}

View File

@ -1,17 +0,0 @@
import { FC } from 'react';
import { FriendListRequestsItemView } from '../requests-item/FriendListRequestsItemView';
import { FriendListRequestsViewProps } from './FriendListRequestsView.types';
export const FriendListRequestsView: FC<FriendListRequestsViewProps> = props =>
{
const { list = null } = props;
if(!list) return null;
return (<>
{ list.map((request, index) =>
{
return <FriendListRequestsItemView key={ index } request={ request } />
}) }
</>);
};

View File

@ -1,6 +0,0 @@
import { MessengerRequest } from './../../common/MessengerRequest';
export interface FriendListRequestsViewProps
{
list: MessengerRequest[];
}

View File

@ -0,0 +1,5 @@
.nitro-friend-list {
width: 250px;
}
@import './views/friend-bar/FriendBarView';

View File

@ -11,15 +11,13 @@ import { SendMessageHook } from '../../hooks/messages/message-event';
import { NitroCardAccordionItemView, NitroCardAccordionView, NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../layout';
import { FriendListContextProvider } from './context/FriendListContext';
import { FriendListMessageHandler } from './FriendListMessageHandler';
import { FriendListViewProps } from './FriendListView.types';
import { FriendListReducer, initialFriendList } from './reducers/FriendListReducer';
import { FriendBarView } from './views/friend-bar/FriendBarView';
import { FriendListFriendsView } from './views/friends/FriendListFriendsView';
import { FriendListRequestsView } from './views/requests/FriendListRequestsView';
import { FriendsListView } from './views/list/FriendsListView';
const TABS: string[] = ['friendlist.friends', 'generic.search'];
export const FriendListView: FC<FriendListViewProps> = props =>
export const FriendsView: FC<{}> = props =>
{
const [ friendListState, dispatchFriendListState ] = useReducer(FriendListReducer, initialFriendList);
const { friends = null, requests = null, settings = null } = friendListState;
@ -120,14 +118,14 @@ export const FriendListView: FC<FriendListViewProps> = props =>
</NitroCardTabsView>
<div className="text-black">
{ currentTab === 0 && <NitroCardAccordionView>
<NitroCardAccordionItemView headerText={ LocalizeText('friendlist.friends') + ` (${onlineFriends.length})` }>
<FriendListFriendsView list={ onlineFriends } />
<NitroCardAccordionItemView headerText={ LocalizeText('friendlist.friends') + ` (${onlineFriends.length})` } defaultState={ true }>
<FriendsListView list={ onlineFriends } />
</NitroCardAccordionItemView>
<NitroCardAccordionItemView headerText={ LocalizeText('friendlist.friends.offlinecaption') + ` (${offlineFriends.length})` }>
<FriendListFriendsView list={ offlineFriends } />
<FriendsListView list={ offlineFriends } />
</NitroCardAccordionItemView>
{ requests.length > 0 && <NitroCardAccordionItemView headerText={ LocalizeText('friendlist.tab.friendrequests') + ` (${requests.length})` }>
<FriendListRequestsView list={ requests } />
<FriendsListView list={ requests } />
</NitroCardAccordionItemView> }
</NitroCardAccordionView> }
</div>

View File

@ -1,6 +1,3 @@
export interface FriendListViewProps
{}
export class FriendListTabs
{
public static readonly FRIENDS: string = 'friendlist.friends';

View File

@ -4,9 +4,9 @@ import { LocalizeText } from '../../../../api';
import { SendMessageHook } from '../../../../hooks';
import { UserProfileIconView } from '../../../shared/user-profile-icon/UserProfileIconView';
import { MessengerFriend } from '../../common/MessengerFriend';
import { FriendListFriendsItemViewProps } from './FriendListFriendsItemView.types';
import { FriendsListItemViewProps } from './FriendsListItemView.types';
export const FriendListFriendsItemView: FC<FriendListFriendsItemViewProps> = props =>
export const FriendsListItemView: FC<FriendsListItemViewProps> = props =>
{
const { friend = null } = props;

View File

@ -1,6 +1,6 @@
import { MessengerFriend } from '../../common/MessengerFriend';
export interface FriendListFriendsItemViewProps
export interface FriendsListItemViewProps
{
friend: MessengerFriend;
}

View File

@ -0,0 +1,25 @@
import React, { FC } from 'react';
import { MessengerFriend } from '../../common/MessengerFriend';
import { MessengerRequest } from '../../common/MessengerRequest';
import { FriendsListItemView } from '../friend-item/FriendsListItemView';
import { FriendsRequestItemView } from '../request-item/FriendsRequestItemView';
import { FriendsListViewProps } from './FriendsListView.types';
export const FriendsListView: FC<FriendsListViewProps> = props =>
{
const { list = null } = props;
if(!list) return null;
return (<>
{ list.map((item, index) =>
{
if(item instanceof MessengerFriend)
return <FriendsListItemView key={ index } friend={ item } />
else if(item instanceof MessengerRequest)
return <FriendsRequestItemView key={ index } request={ item } />
else
return null;
}) }
</>);
}

View File

@ -0,0 +1,7 @@
import { MessengerFriend } from '../../common/MessengerFriend';
import { MessengerRequest } from '../../common/MessengerRequest';
export interface FriendsListViewProps
{
list: MessengerFriend[] | MessengerRequest[];
}

View File

@ -2,9 +2,9 @@ import { AcceptFriendMessageComposer, DeclineFriendMessageComposer } from '@nitr
import { FC, useCallback } from 'react';
import { SendMessageHook } from '../../../../hooks/messages/message-event';
import { UserProfileIconView } from '../../../shared/user-profile-icon/UserProfileIconView';
import { FriendListRequestsItemViewProps } from './FriendListRequestsItemView.types';
import { FriendsRequestItemViewProps } from './FriendsRequestItemView.types';
export const FriendListRequestsItemView: FC<FriendListRequestsItemViewProps> = props =>
export const FriendsRequestItemView: FC<FriendsRequestItemViewProps> = props =>
{
const { request = null } = props;

View File

@ -0,0 +1,6 @@
import { MessengerRequest } from '../../common/MessengerRequest';
export interface FriendsRequestItemViewProps
{
request: MessengerRequest;
}

View File

@ -115,7 +115,7 @@ export const GroupMembersView: FC<GroupMembersViewProps> = props =>
}
searchMembers(pageData.pageIndex);
}, [ pageData ]);
}, [ pageData, searchMembers ]);
const acceptMembership = useCallback((member) =>
{
@ -124,7 +124,7 @@ export const GroupMembersView: FC<GroupMembersViewProps> = props =>
SendMessageHook(new GroupMembershipAcceptComposer(pageData.groupId, member.id));
searchMembers(pageData.pageIndex);
}
}, [ pageData ]);
}, [ pageData, searchMembers ]);
const removeMemberOrDeclineMembership = useCallback((member) =>
{
@ -138,66 +138,68 @@ export const GroupMembersView: FC<GroupMembersViewProps> = props =>
setRemovingMemberName(member.name);
SendMessageHook(new GroupConfirmRemoveMemberComposer(pageData.groupId, member.id));
}
}, [ pageData ]);
}, [ pageData, searchMembers ]);
if(!pageData) return null;
if(!groupId) return null;
return (
<NitroCardView className="nitro-group-members" simple={ true }>
<NitroCardHeaderView headerText={ LocalizeText('group.members.title', ['groupName'], [ pageData.groupTitle ]) } onCloseClick={ onClose } />
<NitroCardContentView className="pb-2">
<div className="d-flex gap-2 align-items-center mb-2">
<div className="group-badge">
<BadgeImageView badgeCode={ pageData.badge } isGroup={ true } />
<NitroCardHeaderView headerText={ LocalizeText('group.members.title', ['groupName'], [ pageData ? pageData.groupTitle : '' ]) } onCloseClick={ onClose } />
{ pageData && <>
<NitroCardContentView className="pb-2">
<div className="d-flex gap-2 align-items-center mb-2">
<div className="group-badge">
<BadgeImageView badgeCode={ pageData.badge } isGroup={ true } />
</div>
<div className="w-100">
<input type="text" className="form-control form-control-sm w-100 mb-1" placeholder={ LocalizeText('group.members.searchinfo') } value={ searchQuery } onChange={ (e) => setSearchQuery(e.target.value) } onBlur={ () => searchMembers(pageData.pageIndex) } onKeyDown={ onKeyDown } />
<select className="form-select form-select-sm w-100" value={ searchLevelId } onChange={ (e) => selectSearchLevelId(Number(e.target.value)) }>
<option value="0">{ LocalizeText('group.members.search.all') }</option>
<option value="1">{ LocalizeText('group.members.search.admins') }</option>
<option value="2">{ LocalizeText('group.members.search.pending') }</option>
</select>
</div>
</div>
<div className="w-100">
<input type="text" className="form-control form-control-sm w-100 mb-1" placeholder={ LocalizeText('group.members.searchinfo') } value={ searchQuery } onChange={ (e) => setSearchQuery(e.target.value) } onBlur={ () => searchMembers(pageData.pageIndex) } onKeyDown={ onKeyDown } />
<select className="form-select form-select-sm w-100" value={ searchLevelId } onChange={ (e) => selectSearchLevelId(Number(e.target.value)) }>
<option value="0">{ LocalizeText('group.members.search.all') }</option>
<option value="1">{ LocalizeText('group.members.search.admins') }</option>
<option value="2">{ LocalizeText('group.members.search.pending') }</option>
</select>
</div>
</div>
<div className="row row-cols-2 align-content-start g-0 w-100 members-list overflow-auto">
{ pageData.result.map((member, index) =>
{
return (
<div key={ index } className={ 'col pb-1' + classNames({ ' pe-1': index % 2 === 0 }) }>
<div className="list-member bg-white rounded d-flex text-black">
<div className="avatar-head flex-shrink-0 cursor-pointer" onClick={ () => { GetUserProfile(member.id) } }>
<AvatarImageView figure={ member.figure } headOnly={ true } direction={ 2 } />
</div>
<div className="p-1 w-100 d-flex flex-column justify-content-center">
<div className="fw-bold small cursor-pointer" onClick={ () => { GetUserProfile(member.id) } }>{ member.name }</div>
{ member.rank !== GroupRank.REQUESTED && <div className="text-muted fst-italic small">{ LocalizeText('group.members.since', ['date'], [member.joinedAt]) }</div> }
</div>
<div className="d-flex flex-column pe-2 align-items-center justify-content-center">
<div className="d-flex align-items-center">
<i className={ 'icon icon-group-small-' + classNames({ 'owner': member.rank === GroupRank.OWNER, 'admin': member.rank === GroupRank.ADMIN, 'not-admin': member.rank === GroupRank.MEMBER, 'cursor-pointer': pageData.admin }) } title={ LocalizeText(getRankDescription(member)) } onClick={ () => toggleAdmin(member) } />
<div className="row row-cols-2 align-content-start g-0 w-100 members-list overflow-auto">
{ pageData.result.map((member, index) =>
{
return (
<div key={ index } className={ 'col pb-1' + classNames({ ' pe-1': index % 2 === 0 }) }>
<div className="list-member bg-white rounded d-flex text-black">
<div className="avatar-head flex-shrink-0 cursor-pointer" onClick={ () => { GetUserProfile(member.id) } }>
<AvatarImageView figure={ member.figure } headOnly={ true } direction={ 2 } />
</div>
<div className="p-1 w-100 d-flex flex-column justify-content-center">
<div className="fw-bold small cursor-pointer" onClick={ () => { GetUserProfile(member.id) } }>{ member.name }</div>
{ member.rank !== GroupRank.REQUESTED && <div className="text-muted fst-italic small">{ LocalizeText('group.members.since', ['date'], [member.joinedAt]) }</div> }
</div>
<div className="d-flex flex-column pe-2 align-items-center justify-content-center">
<div className="d-flex align-items-center">
<i className={ 'icon icon-group-small-' + classNames({ 'owner': member.rank === GroupRank.OWNER, 'admin': member.rank === GroupRank.ADMIN, 'not-admin': member.rank === GroupRank.MEMBER, 'cursor-pointer': pageData.admin }) } title={ LocalizeText(getRankDescription(member)) } onClick={ () => toggleAdmin(member) } />
</div>
{ member.rank === GroupRank.REQUESTED && <div className="d-flex align-items-center">
<i className="icon cursor-pointer icon-accept" title={ LocalizeText('group.members.accept') } onClick={ () => acceptMembership(member) } />
</div> }
{ member.rank !== GroupRank.OWNER && pageData.admin && member.id !== GetSessionDataManager().userId &&<div className="d-flex align-items-center mt-1">
<i className="icon cursor-pointer icon-deny" title={ LocalizeText(member.rank === GroupRank.REQUESTED ? 'group.members.reject' : 'group.members.kick') } onClick={ () => removeMemberOrDeclineMembership(member) } />
</div> }
</div>
{ member.rank === GroupRank.REQUESTED && <div className="d-flex align-items-center">
<i className="icon cursor-pointer icon-accept" title={ LocalizeText('group.members.accept') } onClick={ () => acceptMembership(member) } />
</div> }
{ member.rank !== GroupRank.OWNER && pageData.admin && member.id !== GetSessionDataManager().userId &&<div className="d-flex align-items-center mt-1">
<i className="icon cursor-pointer icon-deny" title={ LocalizeText(member.rank === GroupRank.REQUESTED ? 'group.members.reject' : 'group.members.kick') } onClick={ () => removeMemberOrDeclineMembership(member) } />
</div> }
</div>
</div>
</div>
);
}) }
</div>
<div className="d-flex w-100 align-items-center">
<div>
<button disabled={ pageData.pageIndex === 0 } className="btn btn-primary" onClick={ previousPage }><i className="fas fa-chevron-left" /></button>
);
}) }
</div>
<div className="text-center text-black w-100">{ LocalizeText('group.members.pageinfo', ['amount', 'page', 'totalPages'], [pageData.totalMembersCount.toString(), (pageData.pageIndex + 1).toString(), totalPages.toString()]) }</div>
<div>
<button disabled={ pageData.pageIndex === totalPages - 1 } className="btn btn-primary" onClick={ nextPage }><i className="fas fa-chevron-right" /></button>
<div className="d-flex w-100 align-items-center">
<div>
<button disabled={ pageData.pageIndex === 0 } className="btn btn-primary" onClick={ previousPage }><i className="fas fa-chevron-left" /></button>
</div>
<div className="text-center text-black w-100">{ LocalizeText('group.members.pageinfo', ['amount', 'page', 'totalPages'], [pageData.totalMembersCount.toString(), (pageData.pageIndex + 1).toString(), totalPages.toString()]) }</div>
<div>
<button disabled={ pageData.pageIndex === totalPages - 1 } className="btn btn-primary" onClick={ nextPage }><i className="fas fa-chevron-right" /></button>
</div>
</div>
</div>
</NitroCardContentView>
</NitroCardContentView>
</> }
</NitroCardView>
);
};

View File

@ -12,13 +12,13 @@ export const GroupRoomInformationView: FC<{}> = props =>
const [ groupId, setGroupId ] = useState<number>(null);
const [ groupInformation, setGroupInformation ] = useState<GroupInformationParser>(null);
const [ isExpended, setIsExpended ] = useState<boolean>(true);
const onRoomInfoEvent = useCallback((event: RoomInfoEvent) =>
{
const parser = event.getParser();
setGroupInformation(null);
if(parser.data.habboGroupId)
{
setGroupId(parser.data.habboGroupId);
@ -33,7 +33,7 @@ export const GroupRoomInformationView: FC<{}> = props =>
const parser = event.getParser();
if(parser.flag || groupId !== parser.id) return;
console.log(parser);
setGroupInformation(null);
setGroupInformation(parser);
}, [ groupId ]);
@ -110,7 +110,7 @@ export const GroupRoomInformationView: FC<{}> = props =>
<i className={ 'fas fa-chevron-' + (isExpended ? 'up' : 'down') } />
</div>
{ isExpended && <>
<div className="d-flex cursor-pointer" onClick={ () => GetGroupInformation(groupInformation.id) }>
<div className="d-flex cursor-pointer" onClick={ () => GetGroupInformation(groupId) }>
<div className="group-badge flex-shrink-0 me-1">
<BadgeImageView badgeCode={ groupInformation.badge } isGroup={ true } />
</div>

View File

@ -2,11 +2,12 @@ import { RoomSessionEvent } from '@nitrots/nitro-renderer';
import { FC, useCallback, useEffect, useState } from 'react';
import { GetCommunication } from '../../api';
import { useRoomSessionManagerEvent } from '../../hooks/events/nitro/session/room-session-manager-event';
import { TransitionAnimation, TransitionAnimationTypes } from '../../layout';
import { AchievementsView } from '../achievements/AchievementsView';
import { AvatarEditorView } from '../avatar-editor/AvatarEditorView';
import { CameraWidgetView } from '../camera/CameraWidgetView';
import { CatalogView } from '../catalog/CatalogView';
import { FriendListView } from '../friend-list/FriendListView';
import { FriendsView } from '../friends/FriendsView';
import { GroupsView } from '../groups/GroupsView';
import { HotelView } from '../hotel-view/HotelView';
import { InventoryView } from '../inventory/InventoryView';
@ -50,7 +51,9 @@ export const MainView: FC<MainViewProps> = props =>
return (
<div className="nitro-main">
{ landingViewVisible && <HotelView /> }
<TransitionAnimation type={ TransitionAnimationTypes.FADE_IN } inProp={ landingViewVisible } timeout={ 300 }>
<HotelView />
</TransitionAnimation>
<ToolbarView isInRoom={ !landingViewVisible } />
<ModToolsView />
<RoomHostView />
@ -60,7 +63,7 @@ export const MainView: FC<MainViewProps> = props =>
<NavigatorView />
<InventoryView />
<CatalogView />
<FriendListView />
<FriendsView />
<RightSideView />
<UserSettingsView />
<UserProfileView />

View File

@ -3,6 +3,7 @@ import { FC, useCallback, useState } from 'react';
import { GetRoomSession, SetActiveRoomId, StartRoomSession } from '../../api';
import { useRoomEngineEvent } from '../../hooks/events/nitro/room/room-engine-event';
import { useRoomSessionManagerEvent } from '../../hooks/events/nitro/session/room-session-manager-event';
import { TransitionAnimation, TransitionAnimationTypes } from '../../layout';
import { RoomView } from '../room/RoomView';
export const RoomHostView: FC<{}> = props =>
@ -49,8 +50,10 @@ export const RoomHostView: FC<{}> = props =>
useRoomSessionManagerEvent(RoomSessionEvent.ENDED, onRoomSessionEvent);
return (
<div className="nitro-room-host w-100 h-100">
<RoomView roomSession={ roomSession } />
</div>
<TransitionAnimation type={ TransitionAnimationTypes.FADE_IN } inProp={ !!roomSession } timeout={ 300 }>
<div className="nitro-room-host w-100 h-100">
<RoomView roomSession={ roomSession } />
</div>
</TransitionAnimation>
);
}

View File

@ -1,4 +1,4 @@
import { ColorConverter, NitroAdjustmentFilter, NitroContainer, NitroSprite, NitroTexture, RoomBackgroundColorEvent, RoomEngineEvent, RoomId, RoomObjectHSLColorEnabledEvent } from '@nitrots/nitro-renderer';
import { ColorConverter, NitroAdjustmentFilter, NitroContainer, NitroSprite, NitroTexture, RoomBackgroundColorEvent, RoomEngineDimmerStateEvent, RoomEngineEvent, RoomId, RoomObjectHSLColorEnabledEvent } from '@nitrots/nitro-renderer';
import { FC, useCallback, useState } from 'react';
import { GetNitroInstance, GetRoomEngine, RoomWidgetUpdateBackgroundColorPreviewEvent, RoomWidgetUpdateRoomViewEvent } from '../../api';
import { UseMountEffect } from '../../hooks';
@ -11,8 +11,7 @@ export const RoomColorView: FC<{}> = props =>
const [ roomBackgroundColor, setRoomBackgroundColor ] = useState(0);
const [ originalRoomBackgroundColor, setOriginalRoomBackgroundColor ] = useState(0);
const [ roomFilter, setRoomFilter ] = useState<NitroAdjustmentFilter>(null);
const [ roomFilterColor, setRoomFilterColor ] = useState(-1);
const { roomSession = null, canvasId = -1, eventDispatcher = null } = useRoomContext();
const { roomSession = null, canvasId = -1, widgetHandler = null, eventDispatcher = null } = useRoomContext();
const getRenderingCanvas = useCallback(() =>
{
@ -113,7 +112,6 @@ export const RoomColorView: FC<{}> = props =>
{
const newColor = ColorConverter.hslToRGB(((ColorConverter.rgbToHSL(color) & 0xFFFF00) + brightness));
setRoomFilterColor(newColor);
updateRoomFilter(newColor);
}, [ updateRoomFilter ]);
@ -139,11 +137,15 @@ export const RoomColorView: FC<{}> = props =>
return;
}
case RoomEngineDimmerStateEvent.ROOM_COLOR: {
widgetHandler.processEvent(event);
}
}
}, [ updateRoomBackgroundColor, updateRoomFilterColor ]);
}, [ widgetHandler, updateRoomBackgroundColor, updateRoomFilterColor ]);
useRoomEngineEvent(RoomObjectHSLColorEnabledEvent.ROOM_BACKGROUND_COLOR, onRoomEngineEvent);
useRoomEngineEvent(RoomBackgroundColorEvent.ROOM_COLOR, onRoomEngineEvent);
useRoomEngineEvent(RoomEngineDimmerStateEvent.ROOM_COLOR, onRoomEngineEvent);
const onRoomWidgetUpdateRoomViewEvent = useCallback((event: RoomWidgetUpdateRoomViewEvent) =>
{

View File

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

View File

@ -1,6 +1,6 @@
import { RoomEngineDimmerStateEvent, RoomEngineEvent, RoomEngineObjectEvent, RoomEngineTriggerWidgetEvent, RoomEngineUseProductEvent, RoomId, RoomObjectCategory, RoomObjectOperationType, 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 { CanManipulateFurniture, GetRoomEngine, IsFurnitureSelectionDisabled, LocalizeText, ProcessRoomObjectOperation, RoomWidgetRoomEngineUpdateEvent, RoomWidgetRoomObjectUpdateEvent } from '../../../api';
import { CanManipulateFurniture, GetRoomEngine, GetSessionDataManager, IsFurnitureSelectionDisabled, LocalizeText, ProcessRoomObjectOperation, RoomWidgetFurniToWidgetMessage, RoomWidgetRoomEngineUpdateEvent, RoomWidgetRoomObjectUpdateEvent } from '../../../api';
import { useRoomEngineEvent, useRoomSessionManagerEvent } from '../../../hooks/events';
import { useRoomContext } from '../context/RoomContext';
import { AvatarInfoWidgetView } from './avatar-info/AvatarInfoWidgetView';
@ -41,16 +41,22 @@ export const RoomWidgetsView: FC<RoomWidgetViewProps> = props =>
return;
}
case RoomEngineDimmerStateEvent.ROOM_COLOR: {
return;
}
}
}, [ eventDispatcher ]);
useRoomEngineEvent(RoomEngineEvent.NORMAL_MODE, onRoomEngineEvent);
useRoomEngineEvent(RoomEngineEvent.GAME_MODE, onRoomEngineEvent);
useRoomEngineEvent(RoomZoomEvent.ROOM_ZOOM, onRoomEngineEvent);
useRoomEngineEvent(RoomEngineDimmerStateEvent.ROOM_COLOR, onRoomEngineEvent);
const handleRoomAdClick = useCallback((event: RoomEngineRoomAdEvent) =>
{
}, []);
const handleRoomAdTooltip = useCallback((event: RoomEngineRoomAdEvent) =>
{
}, []);
const onRoomEngineObjectEvent = useCallback((event: RoomEngineObjectEvent) =>
{
@ -118,12 +124,76 @@ export const RoomWidgetsView: FC<RoomWidgetViewProps> = props =>
case RoomEngineObjectEvent.MOUSE_LEAVE:
updateEvent = new RoomWidgetRoomObjectUpdateEvent(RoomWidgetRoomObjectUpdateEvent.OBJECT_ROLL_OUT, objectId, category, event.roomId);
break;
case RoomEngineUseProductEvent.USE_PRODUCT_FROM_INVENTORY:
case RoomEngineUseProductEvent.USE_PRODUCT_FROM_ROOM:
case RoomEngineTriggerWidgetEvent.REQUEST_CREDITFURNI:
widgetHandler.processWidgetMessage(new RoomWidgetFurniToWidgetMessage(RoomWidgetFurniToWidgetMessage.REQUEST_CREDITFURNI, objectId, category, event.roomId));
break;
case RoomEngineTriggerWidgetEvent.REQUEST_STICKIE:
widgetHandler.processWidgetMessage(new RoomWidgetFurniToWidgetMessage(RoomWidgetFurniToWidgetMessage.REQUEST_STICKIE, objectId, category, event.roomId));
break;
case RoomEngineTriggerWidgetEvent.REQUEST_PRESENT:
widgetHandler.processWidgetMessage(new RoomWidgetFurniToWidgetMessage(RoomWidgetFurniToWidgetMessage.REQUEST_PRESENT, objectId, category, event.roomId));
break;
case RoomEngineTriggerWidgetEvent.REQUEST_TROPHY:
widgetHandler.processWidgetMessage(new RoomWidgetFurniToWidgetMessage(RoomWidgetFurniToWidgetMessage.REQUEST_TROPHY, objectId, category, event.roomId));
break;
case RoomEngineTriggerWidgetEvent.REQUEST_TEASER:
widgetHandler.processWidgetMessage(new RoomWidgetFurniToWidgetMessage(RoomWidgetFurniToWidgetMessage.REQUEST_TEASER, objectId, category, event.roomId));
break;
case RoomEngineTriggerWidgetEvent.REQUEST_ECOTRONBOX:
widgetHandler.processWidgetMessage(new RoomWidgetFurniToWidgetMessage(RoomWidgetFurniToWidgetMessage.REQUEST_ECOTRONBOX, objectId, category, event.roomId));
break;
case RoomEngineTriggerWidgetEvent.REQUEST_DIMMER:
widgetHandler.processWidgetMessage(new RoomWidgetFurniToWidgetMessage(RoomWidgetFurniToWidgetMessage.REQUEST_DIMMER, objectId, category, event.roomId));
break;
case RoomEngineTriggerWidgetEvent.REQUEST_PLACEHOLDER:
widgetHandler.processWidgetMessage(new RoomWidgetFurniToWidgetMessage(RoomWidgetFurniToWidgetMessage.REQUEST_PLACEHOLDER, objectId, category, event.roomId));
break;
case RoomEngineTriggerWidgetEvent.REQUEST_CLOTHING_CHANGE:
widgetHandler.processWidgetMessage(new RoomWidgetFurniToWidgetMessage(RoomWidgetFurniToWidgetMessage.REQUEST_CLOTHING_CHANGE, objectId, category, event.roomId));
break;
case RoomEngineTriggerWidgetEvent.REQUEST_PLAYLIST_EDITOR:
widgetHandler.processWidgetMessage(new RoomWidgetFurniToWidgetMessage(RoomWidgetFurniToWidgetMessage.REQUEST_PLAYLIST_EDITOR, objectId, category, event.roomId));
break;
case RoomEngineTriggerWidgetEvent.REQUEST_ACHIEVEMENT_RESOLUTION_ENGRAVING:
widgetHandler.processWidgetMessage(new RoomWidgetFurniToWidgetMessage(RoomWidgetFurniToWidgetMessage.REQUEST_ACHIEVEMENT_RESOLUTION_ENGRAVING, objectId, category, event.roomId));
break;
case RoomEngineTriggerWidgetEvent.REQUEST_BADGE_DISPLAY_ENGRAVING:
widgetHandler.processWidgetMessage(new RoomWidgetFurniToWidgetMessage(RoomWidgetFurniToWidgetMessage.REQUEST_BADGE_DISPLAY_ENGRAVING, objectId, category, event.roomId));
break;
case RoomEngineTriggerWidgetEvent.REQUEST_ACHIEVEMENT_RESOLUTION_FAILED: {
const roomObject = GetRoomEngine().getRoomObject(event.roomId, objectId, category);
const ownerId = roomObject.model.getValue<number>(RoomObjectVariable.FURNITURE_OWNER_ID);
if(ownerId === GetSessionDataManager().userId)
{
widgetHandler.processWidgetMessage(new RoomWidgetFurniToWidgetMessage(RoomWidgetFurniToWidgetMessage.REQUEST_ACHIEVEMENT_RESOLUTION_FAILED, objectId, category, event.roomId));
}
break;
}
case RoomEngineTriggerWidgetEvent.OPEN_WIDGET:
case RoomEngineTriggerWidgetEvent.CLOSE_WIDGET:
case RoomEngineTriggerWidgetEvent.OPEN_FURNI_CONTEXT_MENU:
case RoomEngineTriggerWidgetEvent.CLOSE_FURNI_CONTEXT_MENU:
case RoomEngineTriggerWidgetEvent.REMOVE_DIMMER:
case RoomEngineTriggerWidgetEvent.REQUEST_MANNEQUIN:
case RoomEngineUseProductEvent.USE_PRODUCT_FROM_INVENTORY:
case RoomEngineUseProductEvent.USE_PRODUCT_FROM_ROOM:
case RoomEngineTriggerWidgetEvent.REQUEST_BACKGROUND_COLOR:
case RoomEngineTriggerWidgetEvent.REQUEST_FRIEND_FURNITURE_ENGRAVING:
case RoomEngineTriggerWidgetEvent.REQUEST_HIGH_SCORE_DISPLAY:
case RoomEngineTriggerWidgetEvent.REQUEST_HIDE_HIGH_SCORE_DISPLAY:
case RoomEngineTriggerWidgetEvent.REQUEST_INTERNAL_LINK:
case RoomEngineTriggerWidgetEvent.REQUEST_ROOM_LINK:
widgetHandler.processEvent(event);
break;
case RoomEngineRoomAdEvent.FURNI_CLICK:
case RoomEngineRoomAdEvent.FURNI_DOUBLE_CLICK:
handleRoomAdClick(event);
break;
case RoomEngineRoomAdEvent.TOOLTIP_SHOW:
case RoomEngineRoomAdEvent.TOOLTIP_HIDE:
handleRoomAdTooltip(event);
break;
}
if(updateEvent)
@ -134,7 +204,7 @@ export const RoomWidgetsView: FC<RoomWidgetViewProps> = props =>
if(dispatchEvent) widgetHandler.eventDispatcher.dispatchEvent(updateEvent);
}
}, [ roomSession, widgetHandler ]);
}, [ roomSession, widgetHandler, handleRoomAdClick, handleRoomAdTooltip ]);
useRoomEngineEvent(RoomEngineObjectEvent.SELECTED, onRoomEngineObjectEvent);
useRoomEngineEvent(RoomEngineObjectEvent.DESELECTED, onRoomEngineObjectEvent);
@ -146,10 +216,37 @@ export const RoomWidgetsView: FC<RoomWidgetViewProps> = props =>
useRoomEngineEvent(RoomEngineObjectEvent.REQUEST_MANIPULATION, onRoomEngineObjectEvent);
useRoomEngineEvent(RoomEngineObjectEvent.MOUSE_ENTER, onRoomEngineObjectEvent);
useRoomEngineEvent(RoomEngineObjectEvent.MOUSE_LEAVE, onRoomEngineObjectEvent);
useRoomEngineEvent(RoomEngineUseProductEvent.USE_PRODUCT_FROM_INVENTORY, onRoomEngineObjectEvent);
useRoomEngineEvent(RoomEngineUseProductEvent.USE_PRODUCT_FROM_ROOM, onRoomEngineObjectEvent);
useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_CREDITFURNI, onRoomEngineObjectEvent);
useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_STICKIE, onRoomEngineObjectEvent);
useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_PRESENT, onRoomEngineObjectEvent);
useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_TROPHY, onRoomEngineObjectEvent);
useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_TEASER, onRoomEngineObjectEvent);
useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_ECOTRONBOX, onRoomEngineObjectEvent);
useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_DIMMER, onRoomEngineObjectEvent);
useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_PLACEHOLDER, onRoomEngineObjectEvent);
useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_CLOTHING_CHANGE, onRoomEngineObjectEvent);
useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_PLAYLIST_EDITOR, onRoomEngineObjectEvent);
useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_ACHIEVEMENT_RESOLUTION_ENGRAVING, onRoomEngineObjectEvent);
useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_BADGE_DISPLAY_ENGRAVING, onRoomEngineObjectEvent);
useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_ACHIEVEMENT_RESOLUTION_FAILED, onRoomEngineObjectEvent);
useRoomEngineEvent(RoomEngineTriggerWidgetEvent.OPEN_WIDGET, onRoomEngineObjectEvent);
useRoomEngineEvent(RoomEngineTriggerWidgetEvent.CLOSE_WIDGET, onRoomEngineObjectEvent);
useRoomEngineEvent(RoomEngineTriggerWidgetEvent.OPEN_FURNI_CONTEXT_MENU, onRoomEngineObjectEvent);
useRoomEngineEvent(RoomEngineTriggerWidgetEvent.CLOSE_FURNI_CONTEXT_MENU, onRoomEngineObjectEvent);
useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REMOVE_DIMMER, onRoomEngineObjectEvent);
useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_MANNEQUIN, onRoomEngineObjectEvent);
useRoomEngineEvent(RoomEngineUseProductEvent.USE_PRODUCT_FROM_INVENTORY, onRoomEngineObjectEvent);
useRoomEngineEvent(RoomEngineUseProductEvent.USE_PRODUCT_FROM_ROOM, onRoomEngineObjectEvent);
useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_BACKGROUND_COLOR, onRoomEngineObjectEvent);
useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_FRIEND_FURNITURE_ENGRAVING, onRoomEngineObjectEvent);
useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_HIGH_SCORE_DISPLAY, onRoomEngineObjectEvent);
useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_HIDE_HIGH_SCORE_DISPLAY, onRoomEngineObjectEvent);
useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_INTERNAL_LINK, onRoomEngineObjectEvent);
useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_ROOM_LINK, onRoomEngineObjectEvent);
useRoomEngineEvent(RoomEngineRoomAdEvent.FURNI_CLICK, onRoomEngineObjectEvent);
useRoomEngineEvent(RoomEngineRoomAdEvent.FURNI_DOUBLE_CLICK, onRoomEngineObjectEvent);
useRoomEngineEvent(RoomEngineRoomAdEvent.TOOLTIP_SHOW, onRoomEngineObjectEvent);
useRoomEngineEvent(RoomEngineRoomAdEvent.TOOLTIP_HIDE, onRoomEngineObjectEvent);
const onRoomSessionEvent = useCallback((event: RoomSessionEvent) =>
{

View File

@ -24,7 +24,7 @@ export const AvatarInfoWidgetView: FC<{}> = props =>
const [ infoStandEvent, setInfoStandEvent ] = useState<RoomWidgetUpdateInfostandEvent>(null);
const [ isGameMode, setGameMode ] = useState(false);
const [ isDancing, setIsDancing ] = useState(false);
const [ isDecorating, setIsDecorating ] = useState(GetRoomSession().isDecorating);
const [ isDecorating, setIsDecorating ] = useState(false);
const [ rentableBotChatEvent, setRentableBotChatEvent ] = useState<RoomWidgetUpdateRentableBotChatEvent>(null);
const removeNameBubble = useCallback((index: number) =>

View File

@ -6,3 +6,4 @@
@import './mannequin/FurnitureMannequinView';
@import './stickie/FurnitureStickieView';
@import './high-score/FurnitureHighScoreView';
@import './gift-opening/FurnitureGiftOpeningView';

View File

@ -7,10 +7,10 @@ import { FurnitureDimmerView } from './dimmer/FurnitureDimmerView';
import { FurnitureExchangeCreditView } from './exchange-credit/FurnitureExchangeCreditView';
import { FurnitureExternalImageView } from './external-image/FurnitureExternalImageView';
import { FurnitureFriendFurniView } from './friend-furni/FurnitureFriendFurniView';
import { FurnitureGiftOpeningView } from './gift-opening/FurnitureGiftOpeningView';
import { FurnitureHighScoreView } from './high-score/FurnitureHighScoreView';
import { FurnitureManipulationMenuView } from './manipulation-menu/FurnitureManipulationMenuView';
import { FurnitureMannequinView } from './mannequin/FurnitureMannequinView';
import { FurniturePresentView } from './present/FurniturePresentView';
import { FurnitureStickieView } from './stickie/FurnitureStickieView';
import { FurnitureTrophyView } from './trophy/FurnitureTrophyView';
@ -23,11 +23,11 @@ export const FurnitureWidgetsView: FC<{}> = props =>
<FurnitureCustomStackHeightView />
<FurnitureDimmerView />
<FurnitureFriendFurniView />
<FurnitureGiftOpeningView />
<FurnitureExchangeCreditView />
<FurnitureHighScoreView />
<FurnitureManipulationMenuView />
<FurnitureMannequinView />
<FurniturePresentView />
<FurnitureStickieView />
<FurnitureTrophyView />
<FurnitureBadgeDisplayView />

View File

@ -0,0 +1,8 @@
export class DimmerFurnitureWidgetPresetItem
{
constructor(
public id: number = 0,
public type: number = 0,
public color: number = 0,
public light: number = 0) {}
}

View File

@ -2,6 +2,5 @@ export class FurnitureDimmerData
{
constructor(
public objectId: number,
public category: number,
public active: boolean) {}
public category: number) {}
}

View File

@ -1,3 +1,13 @@
.nitro-dimmer {
width: 300px;
.dimmer-banner {
width: 56px;
height: 79px;
background: url(../../../../../assets/images/room-widgets/dimmer-widget/dimmer_banner.png) center no-repeat;
}
.color-swatch {
height: 30px;
}
}

View File

@ -1,80 +1,205 @@
import { FC, useCallback, useState } from 'react';
import { LocalizeText } from '../../../../../api';
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../../layout';
import { NitroEvent } from '@nitrots/nitro-renderer';
import { FC, useCallback, useEffect, useMemo, useState } from 'react';
import ReactSlider from 'react-slider';
import { ColorUtils, GetConfiguration, LocalizeText, RoomWidgetDimmerChangeStateMessage, RoomWidgetDimmerPreviewMessage, RoomWidgetDimmerSavePresetMessage, RoomWidgetUpdateDimmerEvent, RoomWidgetUpdateDimmerStateEvent } from '../../../../../api';
import { BatchUpdates, CreateEventDispatcherHook } from '../../../../../hooks';
import { NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../../../../layout';
import { useRoomContext } from '../../../context/RoomContext';
import { FurnitureDimmerData } from './FurnitureDimmerData';
import { DimmerFurnitureWidgetPresetItem } from './DimmerFurnitureWidgetPresetItem';
const AVAILABLE_COLORS: number[] = [7665141, 21495, 15161822, 15353138, 15923281, 8581961, 0];
const HTML_COLORS: string[] = ['#74F5F5', '#0053F7', '#E759DE', '#EA4532', '#F2F851', '#82F349', '#000000'];
const MIN_BRIGHTNESS: number = 75;
const MAX_BRIGHTNESS: number = 255;
export const FurnitureDimmerView: FC<{}> = props =>
{
const [ isVisible, setIsVisible ] = useState(false);
const [ presets, setPresets ] = useState<DimmerFurnitureWidgetPresetItem[]>([]);
const [ selectedPresetId, setSelectedPresetId ] = useState(0);
const [ dimmerState, setDimmerState ] = useState(0);
const [ lastDimmerState, setLastDimmerState ] = useState(0);
const [ effectId, setEffectId ] = useState(0);
const [ color, setColor ] = useState(0xFFFFFF);
const [ brightness, setBrightness ] = useState(0xFF);
const [ selectedEffectId, setSelectedEffectId ] = useState(0);
const [ selectedColor, setSelectedColor ] = useState(0);
const [ selectedBrightness, setSelectedBrightness ] = useState(0);
const { eventDispatcher = null, widgetHandler = null } = useRoomContext();
const [ dimmerData, setDimmerData ] = useState<FurnitureDimmerData>(null);
// const onNitroEvent = useCallback((event: NitroEvent) =>
// {
// switch(event.type)
// {
// case RoomEngineTriggerWidgetEvent.REQUEST_DIMMER: {
// const widgetEvent = (event as RoomEngineTriggerWidgetEvent);
// const roomObject = GetRoomEngine().getRoomObject(widgetEvent.roomId, widgetEvent.objectId, widgetEvent.category);
// if(!roomObject) return;
// const data = roomObject.model.getValue<string[]>(RoomObjectVariable.FURNITURE_DATA);
// console.log('data', data);
// setDimmerData(new FurnitureDimmerData(widgetEvent.objectId, widgetEvent.category, false));
// return;
// }
// case RoomWidgetRoomObjectUpdateEvent.FURNI_REMOVED: {
// const widgetEvent = (event as RoomWidgetRoomObjectUpdateEvent);
// setDimmerData(prevState =>
// {
// if(!prevState || (widgetEvent.id !== prevState.objectId) || (widgetEvent.category !== prevState.category)) return prevState;
// return null;
// });
// return;
// }
// case RoomWidgetDimmerUpdateEvent.RWDUE_PRESETS: {
// const widgetEvent = (event as RoomWidgetDimmerUpdateEvent);
// console.log(widgetEvent);
// return;
// }
// case RoomWidgetDimmerStateUpdateEvent.RWDSUE_DIMMER_STATE: {
// const widgetEvent = (event as RoomWidgetDimmerStateUpdateEvent);
// console.log(widgetEvent);
// return;
// }
// }
// }, []);
// useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_DIMMER, onNitroEvent);
// CreateEventDispatcherHook(RoomWidgetRoomObjectUpdateEvent.FURNI_REMOVED, props.events, onNitroEvent);
// CreateEventDispatcherHook(RoomWidgetDimmerUpdateEvent.RWDUE_PRESETS, props.events, onNitroEvent);
// CreateEventDispatcherHook(RoomWidgetDimmerStateUpdateEvent.RWDSUE_DIMMER_STATE, props.events, onNitroEvent);
const processAction = useCallback((type: string, value: string = null) =>
const onNitroEvent = useCallback((event: NitroEvent) =>
{
switch(type)
switch(event.type)
{
case 'close':
setDimmerData(null);
case RoomWidgetUpdateDimmerEvent.PRESETS: {
const widgetEvent = (event as RoomWidgetUpdateDimmerEvent);
const presets: DimmerFurnitureWidgetPresetItem[] = [];
for(const preset of widgetEvent.presets) presets.push(new DimmerFurnitureWidgetPresetItem(preset.id, preset.type, preset.color, preset.brightness));
setPresets(presets);
setSelectedPresetId(widgetEvent.selectedPresetId);
setIsVisible(true);
return;
}
case RoomWidgetUpdateDimmerEvent.HIDE: {
setIsVisible(false);
return;
}
case RoomWidgetUpdateDimmerStateEvent.DIMMER_STATE: {
const widgetEvent = (event as RoomWidgetUpdateDimmerStateEvent);
BatchUpdates(() =>
{
let prevDimmerState = 0;
setDimmerState(prevValue =>
{
setLastDimmerState(prevValue);
return widgetEvent.state;
});
setLastDimmerState(prevDimmerState);
setSelectedPresetId(widgetEvent.presetId);
setEffectId(widgetEvent.effectId);
setSelectedEffectId(widgetEvent.effectId);
setColor(widgetEvent.color);
setSelectedColor(widgetEvent.color);
setBrightness(widgetEvent.brightness);
setSelectedBrightness(widgetEvent.brightness);
});
return;
}
}
}, []);
if(!dimmerData) return null;
CreateEventDispatcherHook(RoomWidgetUpdateDimmerEvent.PRESETS, eventDispatcher, onNitroEvent);
CreateEventDispatcherHook(RoomWidgetUpdateDimmerEvent.HIDE, eventDispatcher, onNitroEvent);
CreateEventDispatcherHook(RoomWidgetUpdateDimmerStateEvent.DIMMER_STATE, eventDispatcher, onNitroEvent);
const selectPresetId = useCallback((id: number) =>
{
const preset = presets[(id - 1)];
if(!preset) return;
setSelectedPresetId(preset.id);
setSelectedEffectId(preset.type);
setSelectedColor(preset.color);
setSelectedBrightness(preset.light);
}, [ presets ]);
const close = useCallback(() =>
{
widgetHandler.processWidgetMessage(new RoomWidgetDimmerPreviewMessage(color, brightness, (effectId === 2)));
setIsVisible(false);
}, [ widgetHandler, color, brightness, effectId ]);
const toggleState = useCallback(() =>
{
widgetHandler.processWidgetMessage(new RoomWidgetDimmerChangeStateMessage());
}, [ widgetHandler ]);
const applyChanges = useCallback(() =>
{
if(dimmerState === 0) return;
const selectedPresetIndex = (selectedPresetId - 1);
if((selectedPresetId < 1) || (selectedPresetId > presets.length)) return;
const preset = presets[selectedPresetIndex];
if(!preset || ((selectedEffectId === preset.type) && (selectedColor === preset.color) && (selectedBrightness === preset.light))) return;
setPresets(prevValue =>
{
const newValue = [ ...prevValue ];
newValue[selectedPresetIndex] = new DimmerFurnitureWidgetPresetItem(preset.id, selectedEffectId, selectedColor, selectedBrightness);
return newValue;
});
widgetHandler.processWidgetMessage(new RoomWidgetDimmerSavePresetMessage(preset.id, selectedEffectId, selectedColor, selectedBrightness, true));
}, [ widgetHandler, dimmerState, selectedPresetId, presets, selectedEffectId, selectedColor, selectedBrightness ]);
const scaledBrightness = useCallback((value: number) =>
{
return ~~((((value - MIN_BRIGHTNESS) * (100 - 0)) / (MAX_BRIGHTNESS - MIN_BRIGHTNESS)) + 0);
}, []);
const isFreeColorMode = useMemo(() =>
{
return GetConfiguration<boolean>('widget.dimmer.colorwheel', false);
}, []);
useEffect(() =>
{
if((dimmerState === 0) && (lastDimmerState === 0)) return;
widgetHandler.processWidgetMessage(new RoomWidgetDimmerPreviewMessage(selectedColor, selectedBrightness, (selectedEffectId === 2)));
}, [ widgetHandler, dimmerState, lastDimmerState, selectedColor, selectedBrightness, selectedEffectId ]);
if(!isVisible) return null;
return (
<NitroCardView className="nitro-dimmer">
<NitroCardHeaderView headerText={ LocalizeText('widget.dimmer.title') } onCloseClick={ event => processAction('close') } />
<NitroCardContentView>
<NitroCardHeaderView headerText={ LocalizeText('widget.dimmer.title') } onCloseClick={ close } />
<NitroCardContentView className="p-0">
{ (dimmerState === 0) &&
<div className="d-flex flex-column gap-2 align-items-center p-2">
<div className="dimmer-banner"></div>
<div className="bg-muted rounded p-1 text-center text-black">{ LocalizeText('widget.dimmer.info.off') }</div>
<button className="btn-success btn w-100" onClick={ toggleState }>{ LocalizeText('widget.dimmer.button.on') }</button>
</div> }
{ (dimmerState === 1) &&
<>
<NitroCardTabsView>
{ presets.map(preset =>
{
return <NitroCardTabsItemView key={ preset.id } isActive={ (selectedPresetId === preset.id) } onClick={ event => selectPresetId(preset.id) }>{ LocalizeText(`widget.dimmer.tab.${preset.id}`) }</NitroCardTabsItemView>
}) }
</NitroCardTabsView>
<div className="p-2">
<div className="form-group mb-2">
<label className="fw-bold text-black">{ LocalizeText('widget.backgroundcolor.hue') }</label>
{ isFreeColorMode &&
<input type="color" className="form-control" value={ ColorUtils.makeColorNumberHex(selectedColor) } onChange={ event => setSelectedColor(ColorUtils.convertFromHex(event.target.value)) } /> }
{ !isFreeColorMode &&
<div className="d-flex gap-2">
{ AVAILABLE_COLORS.map((color, index) =>
{
return <div key={ index } className="rounded w-100 color-swatch cursor-pointer" onClick={ () => setSelectedColor(color) } style={{ backgroundColor: HTML_COLORS[index] }}></div>;
}) }
</div> }
</div>
<div className="form-group mb-2">
<label className="fw-bold text-black">{ LocalizeText('widget.backgroundcolor.lightness') }</label>
<ReactSlider
className={ 'nitro-slider' }
min={ MIN_BRIGHTNESS }
max={ MAX_BRIGHTNESS }
value={ selectedBrightness }
onChange={ value => setSelectedBrightness(value) }
thumbClassName={ 'thumb percent' }
renderThumb={ (props, state) => <div {...props}>{ scaledBrightness(state.valueNow) }</div> } />
</div>
<div className="form-check mb-2">
<input className="form-check-input" type="checkbox" checked={ (selectedEffectId === 2) } onChange={ event => setSelectedEffectId(event.target.checked ? 2 : 1) } />
<label className="form-check-label text-black">{ LocalizeText('widget.dimmer.type.checkbox') }</label>
</div>
<div className="d-flex gap-2">
<button className="btn btn-danger w-100" onClick={ toggleState }>{ LocalizeText('widget.dimmer.button.off') }</button>
<button className="btn btn-success w-100" onClick={ applyChanges }>{ LocalizeText('widget.dimmer.button.apply') }</button>
</div>
</div>
</> }
</NitroCardContentView>
</NitroCardView>
);

View File

@ -1,7 +0,0 @@
export class FurnitureExchangeCreditData
{
constructor(
public objectId: number,
public category: number,
public value: number) {}
}

View File

@ -1,73 +1,51 @@
import { FurnitureExchangeComposer, NitroEvent, RoomEngineTriggerWidgetEvent, RoomObjectVariable } from '@nitrots/nitro-renderer';
import { FC, useCallback, useState } from 'react';
import { GetRoomEngine, GetRoomSession, IsOwnerOfFurniture, LocalizeText, RoomWidgetRoomObjectUpdateEvent } from '../../../../../api';
import { LocalizeText, RoomWidgetCreditFurniRedeemMessage, RoomWidgetUpdateCreditFurniEvent } from '../../../../../api';
import { BatchUpdates } from '../../../../../hooks';
import { CreateEventDispatcherHook } from '../../../../../hooks/events/event-dispatcher.base';
import { useRoomEngineEvent } from '../../../../../hooks/events/nitro/room/room-engine-event';
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../../layout';
import { useRoomContext } from '../../../context/RoomContext';
import { FurnitureExchangeCreditData } from './FurnitureExchangeCreditData';
export const FurnitureExchangeCreditView: FC<{}> = props =>
{
const [ objectId, setObjectId ] = useState(-1);
const [ value, setValue ] = useState(0);
const { eventDispatcher = null, widgetHandler = null } = useRoomContext();
const [ exchangeCreditData, setExchangeCreditData ] = useState<FurnitureExchangeCreditData>(null);
const onNitroEvent = useCallback((event: NitroEvent) =>
const onRoomWidgetUpdateCreditFurniEvent = useCallback((event: RoomWidgetUpdateCreditFurniEvent) =>
{
switch(event.type)
{
case RoomEngineTriggerWidgetEvent.REQUEST_CREDITFURNI: {
const widgetEvent = (event as RoomEngineTriggerWidgetEvent);
const roomObject = GetRoomEngine().getRoomObject(widgetEvent.roomId, widgetEvent.objectId, widgetEvent.category);
if(!roomObject || !IsOwnerOfFurniture(roomObject)) return;
const value = roomObject.model.getValue<number>(RoomObjectVariable.FURNITURE_CREDIT_VALUE);
setExchangeCreditData(new FurnitureExchangeCreditData(widgetEvent.objectId, widgetEvent.category, value));
return;
}
case RoomWidgetRoomObjectUpdateEvent.FURNI_REMOVED: {
const widgetEvent = (event as RoomWidgetRoomObjectUpdateEvent);
setExchangeCreditData(prevState =>
{
if(!prevState || (widgetEvent.id !== prevState.objectId) || (widgetEvent.category !== prevState.category)) return prevState;
return null;
});
return;
}
}
setObjectId(event.objectId);
setValue(event.value);
}, []);
useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_CREDITFURNI, onNitroEvent);
CreateEventDispatcherHook(RoomWidgetRoomObjectUpdateEvent.FURNI_REMOVED, eventDispatcher, onNitroEvent);
CreateEventDispatcherHook(RoomWidgetUpdateCreditFurniEvent.CREDIT_FURNI_UPDATE, eventDispatcher, onRoomWidgetUpdateCreditFurniEvent);
const processAction = useCallback((type: string, value: string = null) =>
{
switch(type)
{
case 'close':
setExchangeCreditData(null);
BatchUpdates(() =>
{
setObjectId(-1);
setValue(0);
});
return;
case 'redeem':
if(!exchangeCreditData) return null;
GetRoomSession().connection.send(new FurnitureExchangeComposer(exchangeCreditData.objectId));
widgetHandler.processWidgetMessage(new RoomWidgetCreditFurniRedeemMessage(RoomWidgetCreditFurniRedeemMessage.REDEEM, objectId));
processAction('close');
return;
}
}, [exchangeCreditData]);
}, [ widgetHandler, objectId ]);
if(!exchangeCreditData) return null;
if(objectId === -1) return null;
return (
<NitroCardView className="nitro-exchange-credit" simple={ true }>
<NitroCardHeaderView headerText={ LocalizeText('catalog.redeem.dialog.title') } onCloseClick={ event => processAction('close') } />
<NitroCardContentView>
<div className="text-black mb-2">
{ LocalizeText('widgets.furniture.credit.redeem.value', [ 'value' ], [ exchangeCreditData.value.toString() ]) }
{ LocalizeText('widgets.furniture.credit.redeem.value', [ 'value' ], [ value.toString() ]) }
</div>
<button className="btn btn-success w-100" onClick={ event => processAction('redeem') }>{ LocalizeText('catalog.redeem.dialog.button.exchange') }</button>
</NitroCardContentView>

View File

@ -0,0 +1,3 @@
.nitro-gift-opening {
min-width: 340px;
}

View File

@ -0,0 +1,227 @@
import { RoomObjectCategory, RoomObjectOperationType } from '@nitrots/nitro-renderer';
import { FC, useCallback, useMemo, useState } from 'react';
import { CreateLinkEvent, GetRoomEngine, GetSessionDataManager, LocalizeText, RoomWidgetPresentOpenMessage, RoomWidgetRoomObjectUpdateEvent, RoomWidgetUpdatePresentDataEvent } from '../../../../../api';
import { CreateEventDispatcherHook } from '../../../../../hooks/events/event-dispatcher.base';
import { NitroCardContentView, NitroCardHeaderView, NitroCardView, NitroLayoutGiftCardView } from '../../../../../layout';
import { ProductTypeEnum } from '../../../../catalog/common/ProductTypeEnum';
import { useRoomContext } from '../../../context/RoomContext';
const FLOOR: string = 'floor';
const WALLPAPER: string = 'wallpaper';
const LANDSCAPE: string = 'landscape';
export const FurnitureGiftOpeningView: FC<{}> = props =>
{
const [ objectId, setObjectId ] = useState(-1);
const [ classId, setClassId ] = useState(-1);
const [ itemType, setItemType ] = useState<string>(null);
const [ text, setText ] = useState<string>(null);
const [ isOwnerOfFurniture, setIsOwnerOfFurniture ] = useState(false);
const [ senderName, setSenderName ] = useState<string>(null);
const [ senderFigure, setSenderFigure ] = useState<string>(null);
const [ placedItemId, setPlacedItemId ] = useState(-1);
const [ placedItemType, setPlacedItemType ] = useState<string>(null);
const [ placedInRoom, setPlacedInRoom ] = useState(false);
const [ imageUrl, setImageUrl ] = useState<string>(null);
const [ openRequested, setOpenRequested ] = useState(false);
const { roomSession = null, eventDispatcher = null, widgetHandler = null } = useRoomContext();
const clearGift = useCallback(() =>
{
if(!openRequested) setObjectId(-1);
setText(null);
setIsOwnerOfFurniture(false);
}, [ openRequested ]);
const getGiftImageUrl = useCallback((name: string) =>
{
return '';
}, []);
const onRoomWidgetUpdatePresentDataEvent = useCallback((event: RoomWidgetUpdatePresentDataEvent) =>
{
switch(event.type)
{
case RoomWidgetUpdatePresentDataEvent.PACKAGEINFO: {
setOpenRequested(false);
setObjectId(event.objectId);
setText(event.giftMessage);
setIsOwnerOfFurniture(event.isController);
setSenderName(event.purchaserName);
setSenderFigure(event.purchaserFigure);
setImageUrl(event.imageUrl);
return;
}
case RoomWidgetUpdatePresentDataEvent.CONTENTS_FLOOR:
case RoomWidgetUpdatePresentDataEvent.CONTENTS_LANDSCAPE:
case RoomWidgetUpdatePresentDataEvent.CONTENTS_WALLPAPER: {
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;
if(event.type === RoomWidgetUpdatePresentDataEvent.CONTENTS_FLOOR) imageType = 'packagecard_icon_floor';
else if(event.type === RoomWidgetUpdatePresentDataEvent.CONTENTS_LANDSCAPE) imageType = 'packagecard_icon_landscape';
else if(event.type === RoomWidgetUpdatePresentDataEvent.CONTENTS_WALLPAPER) imageType = 'packagecard_icon_wallpaper';
setImageUrl(getGiftImageUrl(imageType));
return;
}
case RoomWidgetUpdatePresentDataEvent.CONTENTS_CLUB: {
setObjectId(event.objectId);
setClassId(event.classId);
setItemType(event.itemType);
setText(event.giftMessage);
setIsOwnerOfFurniture(event.isController);
setImageUrl(getGiftImageUrl('packagecard_icon_hc'));
return;
}
case RoomWidgetUpdatePresentDataEvent.CONTENTS: {
if(!openRequested) return;
setObjectId(event.objectId);
setClassId(event.classId);
setItemType(event.itemType);
setText(event.giftMessage);
setIsOwnerOfFurniture(event.isController);
setPlacedItemId(event.placedItemId);
setPlacedItemType(event.placedItemType);
setPlacedInRoom(event.placedInRoom);
return;
}
case RoomWidgetUpdatePresentDataEvent.CONTENTS_IMAGE: {
if(!openRequested) return;
setImageUrl(event.imageUrl);
}
}
}, [ openRequested, getGiftImageUrl ]);
CreateEventDispatcherHook(RoomWidgetUpdatePresentDataEvent.PACKAGEINFO, eventDispatcher, onRoomWidgetUpdatePresentDataEvent);
CreateEventDispatcherHook(RoomWidgetUpdatePresentDataEvent.CONTENTS, eventDispatcher, onRoomWidgetUpdatePresentDataEvent);
CreateEventDispatcherHook(RoomWidgetUpdatePresentDataEvent.CONTENTS_FLOOR, eventDispatcher, onRoomWidgetUpdatePresentDataEvent);
CreateEventDispatcherHook(RoomWidgetUpdatePresentDataEvent.CONTENTS_LANDSCAPE, eventDispatcher, onRoomWidgetUpdatePresentDataEvent);
CreateEventDispatcherHook(RoomWidgetUpdatePresentDataEvent.CONTENTS_WALLPAPER, eventDispatcher, onRoomWidgetUpdatePresentDataEvent);
CreateEventDispatcherHook(RoomWidgetUpdatePresentDataEvent.CONTENTS_CLUB, eventDispatcher, onRoomWidgetUpdatePresentDataEvent);
CreateEventDispatcherHook(RoomWidgetUpdatePresentDataEvent.CONTENTS_IMAGE, eventDispatcher, onRoomWidgetUpdatePresentDataEvent);
const onRoomWidgetRoomObjectUpdateEvent = useCallback((event: RoomWidgetRoomObjectUpdateEvent) =>
{
if(event.id === objectId) clearGift();
if(event.id === placedItemId)
{
if(placedInRoom) setPlacedInRoom(false);
}
}, [ objectId, placedItemId, placedInRoom, clearGift ]);
CreateEventDispatcherHook(RoomWidgetRoomObjectUpdateEvent.FURNI_REMOVED, eventDispatcher, onRoomWidgetRoomObjectUpdateEvent);
const close = useCallback(() =>
{
setObjectId(-1);
setOpenRequested(false);
setPlacedItemId(-1);
setPlacedInRoom(false);
setText(null);
setIsOwnerOfFurniture(false);
}, [ clearGift ]);
const isSpaces = useMemo(() =>
{
if(itemType !== ProductTypeEnum.WALL) return false;
const furniData = GetSessionDataManager().getWallItemData(classId);
if(!furniData) return false;
const className = furniData.className;
return (className === FLOOR || className === LANDSCAPE || className === WALLPAPER);
}, [ classId, itemType ]);
const productName = useMemo(() =>
{
if(objectId === -1) return '';
if(isSpaces)
return 'widget.furni.present.spaces.message_opened';
return 'widget.furni.present.message_opened';
}, [ objectId, isSpaces ]);
const handleAction = useCallback((action: string) =>
{
switch(action)
{
case 'give_gift':
CreateLinkEvent('catalog/open');
return;
case 'open':
setOpenRequested(true);
widgetHandler.processWidgetMessage(new RoomWidgetPresentOpenMessage(RoomWidgetPresentOpenMessage.OPEN_PRESENT, objectId));
return;
case 'room':
return;
case 'inventory':
if((placedItemId > 0) && placedInRoom)
{
if(placedItemType === ProductTypeEnum.PET)
{
roomSession.pickupPet(placedItemId);
}
else
{
const roomObject = GetRoomEngine().getRoomObject(roomSession.roomId, placedItemId, RoomObjectCategory.FLOOR);
if(roomObject) GetRoomEngine().processRoomObjectOperation(roomObject.id, RoomObjectCategory.FLOOR, RoomObjectOperationType.OBJECT_PICKUP);
}
}
close();
return;
}
}, [ roomSession, widgetHandler, objectId, placedInRoom, placedItemId, placedItemType, close ]);
if(objectId === -1) return null;
return (
<NitroCardView className="nitro-gift-opening" simple={ true }>
<NitroCardHeaderView headerText={ LocalizeText(senderName ? 'widget.furni.present.window.title_from' : 'widget.furni.present.window.title', ['name'], [senderName]) } onCloseClick={ close } />
<NitroCardContentView>
{ placedItemId === -1 && <>
<NitroLayoutGiftCardView userName={ senderName } figure={ senderFigure } message={ text } />
{ isOwnerOfFurniture && <div className="d-flex gap-2 mt-2">
{ senderName && <button className="btn btn-primary w-100 text-nowrap" onClick={ () => handleAction('give_gift') }>{ LocalizeText('widget.furni.present.give_gift', ['name'], [senderName]) }</button> }
<button className="btn btn-success w-100 text-nowrap" onClick={ () => handleAction('open') }>{ LocalizeText('widget.furni.present.open_gift') }</button>
</div> }
</> }
{ placedItemId !== -1 && <>
<div className="d-flex gap-2 align-items-center">
<div>
<img src={ imageUrl } alt="" />
</div>
<div className="bg-muted rounded p-2 text-center text-black">
{ LocalizeText(productName, ['product'], [text]) }
</div>
</div>
<div className="d-flex gap-2 mt-3">
<button className="btn btn-primary w-100 text-nowrap" onClick={ () => handleAction('inventory') }>{ LocalizeText('widget.furni.present.put_in_inventory') }</button>
<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>
</div>
{ senderName && <>
<button className="btn btn-primary w-100 text-nowrap mt-2" onClick={ () => handleAction('give_gift') }>{ LocalizeText('widget.furni.present.give_gift', ['name'], [senderName]) }</button>
</> }
</> }
</NitroCardContentView>
</NitroCardView>
);
}

View File

@ -1,18 +0,0 @@
import { NitroEvent, RoomEngineTriggerWidgetEvent } from '@nitrots/nitro-renderer';
import { FC } from 'react';
import { useRoomEngineEvent } from '../../../../../hooks/events/nitro/room/room-engine-event';
import { useRoomContext } from '../../../context/RoomContext';
export const FurniturePresentView: FC<{}> = props =>
{
const { eventDispatcher = null, widgetHandler = null } = useRoomContext();
const onNitroEvent = (event: NitroEvent) =>
{
console.log(event);
};
useRoomEngineEvent(RoomEngineTriggerWidgetEvent.REQUEST_PRESENT, onNitroEvent);
return null;
}

Some files were not shown because too many files have changed in this diff Show More