Merge branch 'feature/hotelview' of https://github.com/billsonnn/nitro-react into feature/hotelview

This commit is contained in:
Dank074 2021-07-25 02:15:08 -05:00
commit 9ce3f103ad
106 changed files with 1294 additions and 491 deletions

View File

@ -91,8 +91,8 @@
"widgets": { "widgets": {
"slot.1.widget": "promoarticle", "slot.1.widget": "promoarticle",
"slot.1.conf": "", "slot.1.conf": "",
"slot.2.widget": "", "slot.2.widget": "widgetcontainer",
"slot.2.conf": "", "slot.2.conf": "image:${image.library.url}web_promo_small/spromo_Canal_Bundle.png,btn:HelloHELLO,btnLink:https://google.com/",
"slot.3.widget": "", "slot.3.widget": "",
"slot.3.conf": "", "slot.3.conf": "",
"slot.4.widget": "", "slot.4.widget": "",

View File

@ -1,6 +1,6 @@
import { ConfigurationEvent, LegacyExternalInterface, Nitro, NitroCommunicationDemoEvent, NitroEvent, NitroLocalizationEvent, RoomEngineEvent, WebGL } from 'nitro-renderer'; import { ConfigurationEvent, LegacyExternalInterface, Nitro, NitroCommunicationDemoEvent, NitroEvent, NitroLocalizationEvent, RoomEngineEvent, WebGL } from 'nitro-renderer';
import { FC, useCallback, useState } from 'react'; import { FC, useCallback, useState } from 'react';
import { GetConfiguration } from './api'; import { GetCommunication, GetConfiguration, GetNitroInstance } from './api';
import { useConfigurationEvent } from './hooks/events/core/configuration/configuration-event'; import { useConfigurationEvent } from './hooks/events/core/configuration/configuration-event';
import { useLocalizationEvent } from './hooks/events/nitro/localization/localization-event'; import { useLocalizationEvent } from './hooks/events/nitro/localization/localization-event';
import { dispatchMainEvent, useMainEvent } from './hooks/events/nitro/main-event'; import { dispatchMainEvent, useMainEvent } from './hooks/events/nitro/main-event';
@ -19,7 +19,7 @@ export const App: FC<{}> = props =>
//@ts-ignore //@ts-ignore
if(!NitroConfig) throw new Error('NitroConfig is not defined!'); if(!NitroConfig) throw new Error('NitroConfig is not defined!');
if(!Nitro.instance) Nitro.bootstrap(); if(!GetNitroInstance()) Nitro.bootstrap();
const getPreloadAssetUrls = useCallback(() => const getPreloadAssetUrls = useCallback(() =>
{ {
@ -28,7 +28,7 @@ export const App: FC<{}> = props =>
if(assetUrls && assetUrls.length) if(assetUrls && assetUrls.length)
{ {
for(const url of assetUrls) urls.push(Nitro.instance.core.configuration.interpolate(url)); for(const url of assetUrls) urls.push(GetNitroInstance().core.configuration.interpolate(url));
} }
return urls; return urls;
@ -39,7 +39,7 @@ export const App: FC<{}> = props =>
switch(event.type) switch(event.type)
{ {
case ConfigurationEvent.LOADED: case ConfigurationEvent.LOADED:
Nitro.instance.localization.init(); GetNitroInstance().localization.init();
return; return;
case ConfigurationEvent.FAILED: case ConfigurationEvent.FAILED:
setIsError(true); setIsError(true);
@ -73,14 +73,14 @@ export const App: FC<{}> = props =>
case NitroCommunicationDemoEvent.CONNECTION_AUTHENTICATED: case NitroCommunicationDemoEvent.CONNECTION_AUTHENTICATED:
setMessage('Finishing Up'); setMessage('Finishing Up');
Nitro.instance.init(); GetNitroInstance().init();
return; return;
case NitroCommunicationDemoEvent.CONNECTION_ERROR: case NitroCommunicationDemoEvent.CONNECTION_ERROR:
setIsError(true); setIsError(true);
setMessage('Connection Error'); setMessage('Connection Error');
return; return;
case NitroCommunicationDemoEvent.CONNECTION_CLOSED: case NitroCommunicationDemoEvent.CONNECTION_CLOSED:
if(Nitro.instance.roomEngine) Nitro.instance.roomEngine.dispose(); if(GetNitroInstance().roomEngine) GetNitroInstance().roomEngine.dispose();
setIsError(true); setIsError(true);
setMessage('Connection Error'); setMessage('Connection Error');
@ -91,13 +91,13 @@ export const App: FC<{}> = props =>
setIsReady(true); setIsReady(true);
return; return;
case NitroLocalizationEvent.LOADED: case NitroLocalizationEvent.LOADED:
Nitro.instance.core.asset.downloadAssets(getPreloadAssetUrls(), (status: boolean) => GetNitroInstance().core.asset.downloadAssets(getPreloadAssetUrls(), (status: boolean) =>
{ {
if(status) if(status)
{ {
setMessage('Connecting'); setMessage('Connecting');
Nitro.instance.communication.init(); GetCommunication().init();
} }
else else
{ {
@ -127,7 +127,7 @@ export const App: FC<{}> = props =>
} }
else else
{ {
Nitro.instance.core.configuration.init(); GetNitroInstance().core.configuration.init();
} }
return ( return (

View File

@ -1,6 +0,0 @@
import { CatalogPageComposer } from 'nitro-renderer';
export function GetCatalogPageComposer(...args: ConstructorParameters<typeof CatalogPageComposer>): CatalogPageComposer
{
return new CatalogPageComposer(...args);
}

View File

@ -0,0 +1,7 @@
import { IConfigurationManager } from 'nitro-renderer';
import { GetNitroCore } from './GetNitroCore';
export function GetConfigurationManager(): IConfigurationManager
{
return GetNitroCore().configuration;
}

View File

@ -0,0 +1,7 @@
import { INitroCore } from 'nitro-renderer';
import { GetNitroInstance } from '../nitro';
export function GetNitroCore(): INitroCore
{
return GetNitroInstance().core;
}

2
src/api/core/index.ts Normal file
View File

@ -0,0 +1,2 @@
export * from './GetConfigurationManager';
export * from './GetNitroCore';

View File

@ -1 +1,2 @@
export * from './core';
export * from './nitro'; export * from './nitro';

View File

@ -0,0 +1,7 @@
import { ILinkEventTracker } from 'nitro-renderer';
import { GetNitroInstance } from './GetNitroInstance';
export function AddEventLinkTracker(tracker: ILinkEventTracker): void
{
GetNitroInstance().addLinkEventTracker(tracker);
}

View File

@ -0,0 +1,7 @@
import { INitroCommunicationManager } from 'nitro-renderer';
import { GetNitroInstance } from './GetNitroInstance';
export function GetCommunication(): INitroCommunicationManager
{
return GetNitroInstance().communication;
}

View File

@ -1,6 +1,6 @@
import { Nitro } from 'nitro-renderer'; import { GetNitroInstance } from './GetNitroInstance';
export function GetConfiguration<T>(key: string, value: T = null): T export function GetConfiguration<T>(key: string, value: T = null): T
{ {
return Nitro.instance.getConfiguration(key, value); return GetNitroInstance().getConfiguration(key, value);
} }

View File

@ -1,6 +1,7 @@
import { IConnection, Nitro } from 'nitro-renderer'; import { IConnection } from 'nitro-renderer';
import { GetCommunication } from './GetCommunication';
export function GetConnection(): IConnection export function GetConnection(): IConnection
{ {
return Nitro.instance.communication.connection; return GetCommunication().connection;
} }

View File

@ -0,0 +1,6 @@
import { INitro, Nitro } from 'nitro-renderer';
export function GetNitroInstance(): INitro
{
return Nitro.instance;
}

View File

@ -1,6 +1,6 @@
import { Nitro } from 'nitro-renderer'; import { GetNitroInstance } from './GetNitroInstance';
export function GetTicker() export function GetTicker()
{ {
return Nitro.instance.ticker; return GetNitroInstance().ticker;
} }

View File

@ -0,0 +1,7 @@
import { ILinkEventTracker } from 'nitro-renderer';
import { GetNitroInstance } from './GetNitroInstance';
export function RemoveLinkEventTracker(tracker: ILinkEventTracker): void
{
GetNitroInstance().removeLinkEventTracker(tracker);
}

View File

@ -1,6 +1,7 @@
import { IAvatarRenderManager, Nitro } from 'nitro-renderer'; import { IAvatarRenderManager } from 'nitro-renderer';
import { GetNitroInstance } from '../GetNitroInstance';
export function GetAvatarRenderManager(): IAvatarRenderManager export function GetAvatarRenderManager(): IAvatarRenderManager
{ {
return Nitro.instance.avatar; return GetNitroInstance().avatar;
} }

View File

@ -1,7 +1,7 @@
import { Nitro } from 'nitro-renderer';
import { IRoomCameraWidgetManager } from 'nitro-renderer/src/nitro/camera/IRoomCameraWidgetManager'; import { IRoomCameraWidgetManager } from 'nitro-renderer/src/nitro/camera/IRoomCameraWidgetManager';
import { GetNitroInstance } from '../GetNitroInstance';
export function GetRoomCameraWidgetManager(): IRoomCameraWidgetManager export function GetRoomCameraWidgetManager(): IRoomCameraWidgetManager
{ {
return Nitro.instance.cameraManager; return GetNitroInstance().cameraManager;
} }

View File

@ -1,7 +1,11 @@
export * from './AddLinkEventTracker';
export * from './avatar'; export * from './avatar';
export * from './camera'; export * from './camera';
export * from './GetCommunication';
export * from './GetConfiguration'; export * from './GetConfiguration';
export * from './GetConnection'; export * from './GetConnection';
export * from './GetNitroInstance';
export * from './GetTicker'; export * from './GetTicker';
export * from './RemoveLinkEventTracker';
export * from './room'; export * from './room';
export * from './session'; export * from './session';

View File

@ -1,6 +1,7 @@
import { IRoomEngine, Nitro } from 'nitro-renderer'; import { IRoomEngine } from 'nitro-renderer';
import { GetNitroInstance } from '../GetNitroInstance';
export function GetRoomEngine(): IRoomEngine export function GetRoomEngine(): IRoomEngine
{ {
return Nitro.instance.roomEngine; return GetNitroInstance().roomEngine;
} }

View File

@ -1,6 +1,7 @@
import { IRoomSessionManager, Nitro } from 'nitro-renderer'; import { IRoomSessionManager } from 'nitro-renderer';
import { GetNitroInstance } from '../GetNitroInstance';
export function GetRoomSessionManager(): IRoomSessionManager export function GetRoomSessionManager(): IRoomSessionManager
{ {
return Nitro.instance.roomSessionManager; return GetNitroInstance().roomSessionManager;
} }

View File

@ -1,6 +1,7 @@
import { ISessionDataManager, Nitro } from 'nitro-renderer'; import { ISessionDataManager } from 'nitro-renderer';
import { GetNitroInstance } from '../GetNitroInstance';
export function GetSessionDataManager(): ISessionDataManager export function GetSessionDataManager(): ISessionDataManager
{ {
return Nitro.instance.sessionDataManager; return GetNitroInstance().sessionDataManager;
} }

View File

@ -80,7 +80,9 @@ $cello-light: #21516e !default;
$cello-dark: #1e465e !default; $cello-dark: #1e465e !default;
$pale-sky: #677181 !default; $pale-sky: #677181 !default;
$oslo-gray: #8F9297 !default; $oslo-gray: #8F9297 !default;
$gainsboro: #d9d9d9 !default;
$ghost: #c8cad0 !default;
$gray-chateau: #a3a7b1 !default;
$success: $green !default; $success: $green !default;
$info: $cyan !default; $info: $cyan !default;
$warning: $yellow !default; $warning: $yellow !default;
@ -122,7 +124,8 @@ $theme-colors: (
"white": $white, "white": $white,
"black": $black, "black": $black,
"muted": $muted, "muted": $muted,
"purple": $purple "purple": $purple,
"gainsboro": $gainsboro
) !default; ) !default;
// scss-docs-end theme-colors-map // scss-docs-end theme-colors-map

View File

@ -0,0 +1,6 @@
import { NitroEvent } from 'nitro-renderer';
export class InventoryBadgesRequestEvent extends NitroEvent
{
public static REQUEST_BADGES: string = 'IBRE_REQUEST_BADGES';
}

View File

@ -0,0 +1,20 @@
import { NitroEvent } from 'nitro-renderer';
export class InventoryBadgesUpdatedEvent extends NitroEvent
{
public static BADGES_UPDATED: string = 'IBUE_BADGES_UPDATED';
private _badges: string[] = [];
constructor(type: string, badges: string[] = [])
{
super(type);
this._badges = badges;
}
public get badges(): string[]
{
return this._badges;
}
}

View File

@ -1,3 +1,4 @@
export * from './InventoryBadgesUpdatedEvent';
export * from './InventoryEvent'; export * from './InventoryEvent';
export * from './InventoryTradeRequestEvent'; export * from './InventoryTradeRequestEvent';
export * from './InventoryTradeStartEvent'; export * from './InventoryTradeStartEvent';

View File

@ -1,8 +1,8 @@
import { NitroEvent } from 'nitro-renderer'; import { NitroEvent } from 'nitro-renderer';
import { Nitro } from 'nitro-renderer/src/nitro/Nitro'; import { GetCommunication } from '../../../../api';
import { CreateEventDispatcherHook } from '../../event-dispatcher.base'; import { CreateEventDispatcherHook } from '../../event-dispatcher.base';
export function useCommunicationEvent(type: string, handler: (event: NitroEvent) => void): void export function useCommunicationEvent(type: string, handler: (event: NitroEvent) => void): void
{ {
CreateEventDispatcherHook(type, Nitro.instance.communication.events, handler); CreateEventDispatcherHook(type, GetCommunication().events, handler);
} }

View File

@ -1,8 +1,8 @@
import { NitroEvent } from 'nitro-renderer'; import { NitroEvent } from 'nitro-renderer';
import { Nitro } from 'nitro-renderer/src/nitro/Nitro'; import { GetConfigurationManager } from '../../../../api';
import { CreateEventDispatcherHook } from '../../event-dispatcher.base'; import { CreateEventDispatcherHook } from '../../event-dispatcher.base';
export function useConfigurationEvent(type: string, handler: (event: NitroEvent) => void): void export function useConfigurationEvent(type: string, handler: (event: NitroEvent) => void): void
{ {
CreateEventDispatcherHook(type, Nitro.instance.core.configuration.events, handler); CreateEventDispatcherHook(type, GetConfigurationManager().events, handler);
} }

View File

@ -1 +1,2 @@
export * from './communication';
export * from './configuration'; export * from './configuration';

View File

@ -1,8 +1,8 @@
import { NitroEvent } from 'nitro-renderer'; import { NitroEvent } from 'nitro-renderer';
import { Nitro } from 'nitro-renderer/src/nitro/Nitro'; import { GetAvatarRenderManager } from '../../../../api';
import { CreateEventDispatcherHook } from '../../event-dispatcher.base'; import { CreateEventDispatcherHook } from '../../event-dispatcher.base';
export function useAvatarEvent(type: string, handler: (event: NitroEvent) => void): void export function useAvatarEvent(type: string, handler: (event: NitroEvent) => void): void
{ {
CreateEventDispatcherHook(type, Nitro.instance.avatar.events, handler); CreateEventDispatcherHook(type, GetAvatarRenderManager().events, handler);
} }

View File

@ -1,8 +1,8 @@
import { NitroEvent } from 'nitro-renderer'; import { NitroEvent } from 'nitro-renderer';
import { Nitro } from 'nitro-renderer/src/nitro/Nitro'; import { GetNitroInstance } from '../../../../api';
import { CreateEventDispatcherHook } from '../../event-dispatcher.base'; import { CreateEventDispatcherHook } from '../../event-dispatcher.base';
export function useCameraEvent(type: string, handler: (event: NitroEvent) => void): void export function useCameraEvent(type: string, handler: (event: NitroEvent) => void): void
{ {
CreateEventDispatcherHook(type, Nitro.instance.cameraManager.events, handler); CreateEventDispatcherHook(type, GetNitroInstance().cameraManager.events, handler);
} }

View File

@ -1,6 +1,5 @@
export * from './avatar'; export * from './avatar';
export * from './camera'; export * from './camera';
export * from './communication';
export * from './localization'; export * from './localization';
export * from './main-event'; export * from './main-event';
export * from './room'; export * from './room';

View File

@ -1,8 +1,8 @@
import { NitroEvent } from 'nitro-renderer'; import { NitroEvent } from 'nitro-renderer';
import { Nitro } from 'nitro-renderer/src/nitro/Nitro'; import { GetNitroInstance } from '../../../../api';
import { CreateEventDispatcherHook } from '../../event-dispatcher.base'; import { CreateEventDispatcherHook } from '../../event-dispatcher.base';
export function useLocalizationEvent(type: string, handler: (event: NitroEvent) => void): void export function useLocalizationEvent(type: string, handler: (event: NitroEvent) => void): void
{ {
CreateEventDispatcherHook(type, Nitro.instance.localization.events, handler); CreateEventDispatcherHook(type, GetNitroInstance().localization.events, handler);
} }

View File

@ -1,12 +1,13 @@
import { Nitro, NitroEvent } from 'nitro-renderer'; import { NitroEvent } from 'nitro-renderer';
import { GetNitroInstance } from '../../../api';
import { CreateEventDispatcherHook, DispatchEventHook } from '../event-dispatcher.base'; import { CreateEventDispatcherHook, DispatchEventHook } from '../event-dispatcher.base';
export function useMainEvent(type: string, handler: (event: NitroEvent) => void): void export function useMainEvent(type: string, handler: (event: NitroEvent) => void): void
{ {
CreateEventDispatcherHook(type, Nitro.instance.events, handler); CreateEventDispatcherHook(type, GetNitroInstance().events, handler);
} }
export function dispatchMainEvent(event: NitroEvent): void export function dispatchMainEvent(event: NitroEvent): void
{ {
DispatchEventHook(Nitro.instance.events, event); DispatchEventHook(GetNitroInstance().events, event);
} }

View File

@ -1,8 +1,8 @@
import { NitroEvent } from 'nitro-renderer'; import { NitroEvent } from 'nitro-renderer';
import { Nitro } from 'nitro-renderer/src/nitro/Nitro'; import { GetRoomEngine } from '../../../../api';
import { CreateEventDispatcherHook } from '../../event-dispatcher.base'; import { CreateEventDispatcherHook } from '../../event-dispatcher.base';
export function useRoomEngineEvent(type: string, handler: (event: NitroEvent) => void): void export function useRoomEngineEvent(type: string, handler: (event: NitroEvent) => void): void
{ {
CreateEventDispatcherHook(type, Nitro.instance.roomEngine.events, handler); CreateEventDispatcherHook(type, GetRoomEngine().events, handler);
} }

View File

@ -1,7 +1,8 @@
import { Nitro, NitroEvent } from 'nitro-renderer'; import { NitroEvent } from 'nitro-renderer';
import { GetRoomSessionManager } from '../../../../api';
import { CreateEventDispatcherHook } from '../../event-dispatcher.base'; import { CreateEventDispatcherHook } from '../../event-dispatcher.base';
export function useRoomSessionManagerEvent(type: string, handler: (event: NitroEvent) => void): void export function useRoomSessionManagerEvent(type: string, handler: (event: NitroEvent) => void): void
{ {
CreateEventDispatcherHook(type, Nitro.instance.roomSessionManager.events, handler); CreateEventDispatcherHook(type, GetRoomSessionManager().events, handler);
} }

View File

@ -1,13 +1,6 @@
export * from './events'; export * from './events';
export * from './events/core'; export * from './events/core';
export * from './events/core/configuration';
export * from './events/nitro'; export * from './events/nitro';
export * from './events/nitro/avatar';
export * from './events/nitro/camera';
export * from './events/nitro/communication';
export * from './events/nitro/localization';
export * from './events/nitro/room';
export * from './events/nitro/session';
export * from './events/ui'; export * from './events/ui';
export * from './messages'; export * from './messages';
export * from './UseMountEffect'; export * from './UseMountEffect';

View File

@ -1,6 +1,6 @@
import { IMessageComposer, IMessageEvent, MessageEvent } from 'nitro-renderer'; import { IMessageComposer, IMessageEvent, MessageEvent } from 'nitro-renderer';
import { Nitro } from 'nitro-renderer/src/nitro/Nitro';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { GetCommunication, GetConnection } from '../../api';
export function CreateMessageHook(eventType: typeof MessageEvent, handler: (event: IMessageEvent) => void): void export function CreateMessageHook(eventType: typeof MessageEvent, handler: (event: IMessageEvent) => void): void
{ {
@ -9,13 +9,13 @@ export function CreateMessageHook(eventType: typeof MessageEvent, handler: (even
//@ts-ignore //@ts-ignore
const event = new eventType(handler); const event = new eventType(handler);
Nitro.instance.communication.registerMessageEvent(event); GetCommunication().registerMessageEvent(event);
return () => Nitro.instance.communication.removeMessageEvent(event); return () => GetCommunication().removeMessageEvent(event);
}, [ eventType, handler ]); }, [ eventType, handler ]);
} }
export function SendMessageHook(event: IMessageComposer<unknown[]>): void export function SendMessageHook(event: IMessageComposer<unknown[]>): void
{ {
Nitro.instance.communication.connection.send(event); GetConnection().send(event);
} }

View File

@ -64,11 +64,9 @@
} }
.avatar-image { .avatar-image {
width: 100% !important;
height: 100% !important;
background-position: center; background-position: center;
background-repeat: no-repeat; background-repeat: no-repeat;
background-position-y: -32px !important; background-position-y: 12px !important;
} }
.trade-button { .trade-button {

View File

@ -1,6 +1,6 @@
import { Nitro } from 'nitro-renderer'; import { GetNitroInstance } from '../api';
export function LocalizeBadgeName(key: string): string export function LocalizeBadgeName(key: string): string
{ {
return Nitro.instance.localization.getBadgeName(key); return GetNitroInstance().localization.getBadgeName(key);
} }

View File

@ -1,6 +1,6 @@
import { Nitro } from 'nitro-renderer'; import { GetNitroInstance } from '../api';
export function LocalizeText(key: string, parameters: string[] = null, replacements: string[] = null): string export function LocalizeText(key: string, parameters: string[] = null, replacements: string[] = null): string
{ {
return Nitro.instance.getLocalizationWithParameters(key, parameters, replacements); return GetNitroInstance().getLocalizationWithParameters(key, parameters, replacements);
} }

View File

@ -83,8 +83,36 @@
.wardrobe-grid { .wardrobe-grid {
.grid-item-container { .grid-item-container {
height: 100% !important; height: 142px !important;
max-height: 100% !important; max-height: 142px !important;
.grid-item {
background-color: $ghost;
.avatar-image {
position: absolute;
bottom: 0;
background-position-y: -23px !important;
z-index: 3;
}
.figure-button-container {
background-color: $gray-chateau;
z-index: 2;
}
&:after {
position: absolute;
content: '';
height: 50%;
bottom: 0;
left: 0;
right: 0;
background-color: $gray-chateau;
box-shadow: 0 0 8px 2px rgba($white,.6);
z-index: 1;
}
}
} }
} }
} }

View File

@ -79,12 +79,20 @@ export const AvatarEditorView: FC<AvatarEditorViewProps> = props =>
const onUserWardrobePageEvent = useCallback((event: UserWardrobePageEvent) => const onUserWardrobePageEvent = useCallback((event: UserWardrobePageEvent) =>
{ {
const parser = event.getParser(); const parser = event.getParser();
const savedFigures: [ string, string ][] = [];
const savedFigures: [ string, string ][] = new Array(MAX_SAVED_FIGURES); let i = 0;
for(const value of parser.looks.values()) while(i < MAX_SAVED_FIGURES)
{ {
console.log(value); savedFigures.push([ null, null ]);
i++;
}
for(let [ index, value ] of parser.looks.entries())
{
savedFigures[(index - 1)] = [ value[0], value[1] ];
} }
setSavedFigures(savedFigures) setSavedFigures(savedFigures)
@ -280,7 +288,7 @@ export const AvatarEditorView: FC<AvatarEditorViewProps> = props =>
<div className="row h-100"> <div className="row h-100">
<div className="col-9 d-flex flex-column h-100"> <div className="col-9 d-flex flex-column h-100">
{ (activeCategory && !isWardrobeVisible) && <AvatarEditorModelView model={ activeCategory } gender={ figureData.gender } setGender={ setGender } /> } { (activeCategory && !isWardrobeVisible) && <AvatarEditorModelView model={ activeCategory } gender={ figureData.gender } setGender={ setGender } /> }
{ isWardrobeVisible && <AvatarEditorWardrobeView figures={ savedFigures } /> } { isWardrobeVisible && <AvatarEditorWardrobeView figureData={ figureData } savedFigures={ savedFigures } setSavedFigures={ setSavedFigures } loadAvatarInEditor={ loadAvatarInEditor } /> }
</div> </div>
<div className="col-3 d-flex flex-column h-100"> <div className="col-3 d-flex flex-column h-100">
<div className="figure-preview-container"> <div className="figure-preview-container">

View File

@ -162,7 +162,7 @@ export class AvatarEditorUtilities
partItems.sort(this.clubItemsFirst ? this.clubSorter : this.noobSorter); partItems.sort(this.clubItemsFirst ? this.clubSorter : this.noobSorter);
// if(this._forceSellableClothingVisibility || Nitro.instance.getConfiguration<boolean>("avatareditor.support.sellablefurni", false)) // if(this._forceSellableClothingVisibility || GetNitroInstance().getConfiguration<boolean>("avatareditor.support.sellablefurni", false))
// { // {
// _local_31 = (this._manager.windowManager.assets.getAssetByName("camera_zoom_in") as BitmapDataAsset); // _local_31 = (this._manager.windowManager.assets.getAssetByName("camera_zoom_in") as BitmapDataAsset);
// _local_32 = (_local_31.content as BitmapData).clone(); // _local_32 = (_local_31.content as BitmapData).clone();

View File

@ -206,7 +206,7 @@ export class FigureData
{ {
const colors = this._colors.get(setType); const colors = this._colors.get(setType);
if(colors === undefined) continue; if(!colors) continue;
let setId = this._data.get(setType); let setId = this._data.get(setType);

View File

@ -1,4 +1,7 @@
import { FC, useMemo } from 'react'; import { UserWardrobeSaveComposer } from 'nitro-renderer';
import { FC, useCallback, useMemo } from 'react';
import { Button } from 'react-bootstrap';
import { SendMessageHook } from '../../../../hooks';
import { NitroCardGridItemView } from '../../../../layout/card/grid/item/NitroCardGridItemView'; import { NitroCardGridItemView } from '../../../../layout/card/grid/item/NitroCardGridItemView';
import { NitroCardGridView } from '../../../../layout/card/grid/NitroCardGridView'; import { NitroCardGridView } from '../../../../layout/card/grid/NitroCardGridView';
import { NitroCardGridThemes } from '../../../../layout/card/grid/NitroCardGridView.types'; import { NitroCardGridThemes } from '../../../../layout/card/grid/NitroCardGridView.types';
@ -7,20 +10,40 @@ import { AvatarEditorWardrobeViewProps } from './AvatarEditorWardrobeView.types'
export const AvatarEditorWardrobeView: FC<AvatarEditorWardrobeViewProps> = props => export const AvatarEditorWardrobeView: FC<AvatarEditorWardrobeViewProps> = props =>
{ {
const { figures = [] } = props; const { figureData = null, savedFigures = [], setSavedFigures = null, loadAvatarInEditor = null } = props;
const savedFigures = useMemo(() => const wearFigureAtIndex = useCallback((index: number) =>
{ {
if(!figures) return []; if((index >= savedFigures.length) || (index < 0)) return;
let i = 0; const [ figure, gender ] = savedFigures[index];
loadAvatarInEditor(figure, gender);
}, [ savedFigures, loadAvatarInEditor ]);
const saveFigureAtWardrobeIndex = useCallback((index: number) =>
{
if(!figureData || (index >= savedFigures.length) || (index < 0)) return;
const newFigures = [ ...savedFigures ];
const figure = figureData.getFigureString();
const gender = figureData.gender;
newFigures[index] = [ figure, gender ];
setSavedFigures(newFigures);
SendMessageHook(new UserWardrobeSaveComposer((index + 1), figure, gender));
}, [ figureData, savedFigures, setSavedFigures ]);
const figures = useMemo(() =>
{
if(!savedFigures) return [];
const items: JSX.Element[] = []; const items: JSX.Element[] = [];
while(i < figures.length) savedFigures.forEach((figure, index) =>
{ {
const figure = figures[i];
let figureString = null; let figureString = null;
let gender = null; let gender = null;
@ -31,24 +54,24 @@ export const AvatarEditorWardrobeView: FC<AvatarEditorWardrobeViewProps> = props
} }
items.push( items.push(
<NitroCardGridItemView key={ i } columns={ 2 }> <NitroCardGridItemView key={ index } className="flex-column justify-content-end">
<AvatarImageView figure={ figureString } gender={ gender } /> { figureString && <AvatarImageView figure={ figureString } gender={ gender } direction={ 2 } /> }
<div className="d-flex w-100 figure-button-container p-1">
<Button variant="link" size="sm" className="w-100" onClick={ event => saveFigureAtWardrobeIndex(index) }>Save</Button>
{ figureString && <Button variant="link" size="sm" className="w-100" onClick={ event => wearFigureAtIndex(index) }>Use</Button> }
</div>
</NitroCardGridItemView> </NitroCardGridItemView>
); );
});
i++
}
return items; return items;
}, [ figures ]); }, [ savedFigures, saveFigureAtWardrobeIndex, wearFigureAtIndex ]);
console.log(figures.length);
return ( return (
<div className="row h-100"> <div className="row h-100">
<div className="col-12 d-flex h-100"> <div className="col-12 d-flex h-100">
<NitroCardGridView className="wardrobe-grid" columns={ 12 } theme={ NitroCardGridThemes.THEME_DEFAULT }> <NitroCardGridView className="wardrobe-grid" columns={ 5 } theme={ NitroCardGridThemes.THEME_DEFAULT }>
{ savedFigures } { figures }
</NitroCardGridView> </NitroCardGridView>
</div> </div>
</div> </div>

View File

@ -1,4 +1,10 @@
import { Dispatch, SetStateAction } from 'react';
import { FigureData } from '../../common/FigureData';
export interface AvatarEditorWardrobeViewProps export interface AvatarEditorWardrobeViewProps
{ {
figures: [ string, string ][]; figureData: FigureData;
savedFigures: [ string, string ][];
setSavedFigures: Dispatch<SetStateAction<[ string, string][]>>;
loadAvatarInEditor: (figure: string, gender: string, reset?: boolean) => void;
} }

View File

@ -127,11 +127,11 @@ export const CatalogMessageHandler: FC<CatalogMessageHandlerProps> = props =>
type: CatalogActions.SET_SUBSCRIPTION_INFO, type: CatalogActions.SET_SUBSCRIPTION_INFO,
payload: { payload: {
subscriptionInfo: new SubscriptionInfo( subscriptionInfo: new SubscriptionInfo(
Math.max(0, parser.days), Math.max(0, parser.daysToPeriodEnd),
Math.max(0, parser.months), Math.max(0, parser.periodsSubscribedAhead),
parser.isVip, parser.isVip,
parser.pastClubDays, parser.pastClubDays,
parser.pastVIPDays parser.pastVipDays
) )
} }
}); });
@ -149,8 +149,6 @@ export const CatalogMessageHandler: FC<CatalogMessageHandlerProps> = props =>
{ {
const parser = event.getParser(); const parser = event.getParser();
console.log(parser);
dispatchCatalogState({ dispatchCatalogState({
type: CatalogActions.SET_GIFT_CONFIGURATION, type: CatalogActions.SET_GIFT_CONFIGURATION,
payload: { payload: {

View File

@ -1,7 +1,6 @@
import { CatalogModeComposer, CatalogRequestGiftConfigurationComposer, ICatalogPageData, RoomPreviewer } from 'nitro-renderer'; import { CatalogModeComposer, CatalogPageComposer, CatalogRequestGiftConfigurationComposer, ICatalogPageData, ILinkEventTracker, RoomPreviewer } from 'nitro-renderer';
import { FC, useCallback, useEffect, useReducer, useState } from 'react'; import { FC, useCallback, useEffect, useReducer, useState } from 'react';
import { GetRoomEngine } from '../../api'; import { AddEventLinkTracker, GetRoomEngine, RemoveLinkEventTracker } from '../../api';
import { GetCatalogPageComposer } from '../../api/catalog/GetCatalogPageComposer';
import { CatalogEvent } from '../../events'; import { CatalogEvent } from '../../events';
import { useUiEvent } from '../../hooks/events/ui/ui-event'; import { useUiEvent } from '../../hooks/events/ui/ui-event';
import { SendMessageHook } from '../../hooks/messages/message-event'; import { SendMessageHook } from '../../hooks/messages/message-event';
@ -9,6 +8,7 @@ import { NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, Nitro
import { LocalizeText } from '../../utils/LocalizeText'; import { LocalizeText } from '../../utils/LocalizeText';
import { CatalogMessageHandler } from './CatalogMessageHandler'; import { CatalogMessageHandler } from './CatalogMessageHandler';
import { CatalogMode, CatalogViewProps } from './CatalogView.types'; import { CatalogMode, CatalogViewProps } from './CatalogView.types';
import { GetCatalogPageTree } from './common/CatalogUtilities';
import { CatalogContextProvider } from './context/CatalogContext'; import { CatalogContextProvider } from './context/CatalogContext';
import { CatalogActions, CatalogReducer, initialCatalog } from './reducers/CatalogReducer'; import { CatalogActions, CatalogReducer, initialCatalog } from './reducers/CatalogReducer';
import { CatalogNavigationView } from './views/navigation/CatalogNavigationView'; import { CatalogNavigationView } from './views/navigation/CatalogNavigationView';
@ -18,8 +18,10 @@ export const CatalogView: FC<CatalogViewProps> = props =>
{ {
const [ isVisible, setIsVisible ] = useState(false); const [ isVisible, setIsVisible ] = useState(false);
const [ roomPreviewer, setRoomPreviewer ] = useState<RoomPreviewer>(null); const [ roomPreviewer, setRoomPreviewer ] = useState<RoomPreviewer>(null);
const [ pendingPageId, setPendingPageId ] = useState(-1);
const [ pendingTree, setPendingTree ] = useState<ICatalogPageData[]>(null);
const [ catalogState, dispatchCatalogState ] = useReducer(CatalogReducer, initialCatalog); const [ catalogState, dispatchCatalogState ] = useReducer(CatalogReducer, initialCatalog);
const { root = null, currentTab = null, pageParser = null, activeOffer = null, searchResult = null } = catalogState; const { root = null, currentTab = null, pageParser = null, activeOffer = null, searchResult = null} = catalogState;
const onCatalogEvent = useCallback((event: CatalogEvent) => const onCatalogEvent = useCallback((event: CatalogEvent) =>
{ {
@ -42,22 +44,99 @@ export const CatalogView: FC<CatalogViewProps> = props =>
useUiEvent(CatalogEvent.TOGGLE_CATALOG, onCatalogEvent); useUiEvent(CatalogEvent.TOGGLE_CATALOG, onCatalogEvent);
useUiEvent(CatalogEvent.CATALOG_RESET, onCatalogEvent); useUiEvent(CatalogEvent.CATALOG_RESET, onCatalogEvent);
const setCurrentTab = useCallback((page: ICatalogPageData) =>
{
dispatchCatalogState({
type: CatalogActions.SET_CATALOG_CURRENT_TAB,
payload: {
currentTab: page
}
});
}, [ dispatchCatalogState ]);
const buildCatalogPageTree = useCallback((page: ICatalogPageData, targetPageId: number) =>
{
const pageTree: ICatalogPageData[] = [];
GetCatalogPageTree(page, targetPageId, pageTree);
if(pageTree.length) pageTree.reverse();
return pageTree;
}, []);
const linkReceived = useCallback((url: string) =>
{
const parts = url.split('/');
if(parts.length < 2) return;
switch(parts[1])
{
case 'open':
if(parts.length > 2)
{
dispatchCatalogState({
type: CatalogActions.SET_PENDING_PAGE_ID,
payload: {
pendingPageId: parseInt(parts[2])
}
});
}
else
{
setIsVisible(true);
}
return;
}
}, [ dispatchCatalogState ]);
useEffect(() => useEffect(() =>
{ {
if(!isVisible) return; const linkTracker: ILinkEventTracker = {
linkReceived,
eventUrlPrefix: 'catalog/'
};
if(!catalogState.root) AddEventLinkTracker(linkTracker);
return () => RemoveLinkEventTracker(linkTracker);
}, [ linkReceived]);
useEffect(() =>
{
const loadCatalog = (((pendingPageId > -1) && !catalogState.root) || (isVisible && !catalogState.root));
if(loadCatalog)
{ {
SendMessageHook(new CatalogModeComposer(CatalogMode.MODE_NORMAL)); SendMessageHook(new CatalogModeComposer(CatalogMode.MODE_NORMAL));
SendMessageHook(new CatalogRequestGiftConfigurationComposer()); SendMessageHook(new CatalogRequestGiftConfigurationComposer());
} }
}, [ isVisible, catalogState.root ]);
if(catalogState.root)
{
if(!isVisible && (pendingPageId > -1))
{
setIsVisible(true);
return;
}
if(pendingPageId > -1)
{
const tree = buildCatalogPageTree(catalogState.root, pendingPageId);
setCurrentTab(tree.shift());
setPendingTree(tree);
}
}
}, [ isVisible, pendingPageId, catalogState.root, buildCatalogPageTree, setCurrentTab ]);
useEffect(() => useEffect(() =>
{ {
if(!currentTab) return; if(!currentTab) return;
SendMessageHook(GetCatalogPageComposer(currentTab.pageId, -1, CatalogMode.MODE_NORMAL)); SendMessageHook(new CatalogPageComposer(currentTab.pageId, -1, CatalogMode.MODE_NORMAL));
}, [ currentTab ]); }, [ currentTab ]);
useEffect(() => useEffect(() =>
@ -75,16 +154,6 @@ export const CatalogView: FC<CatalogViewProps> = props =>
} }
}, []); }, []);
const setCurrentTab = useCallback((page: ICatalogPageData) =>
{
dispatchCatalogState({
type: CatalogActions.SET_CATALOG_CURRENT_TAB,
payload: {
currentTab: page
}
});
}, [ dispatchCatalogState ]);
const currentNavigationPage = ((searchResult && searchResult.page) || currentTab); const currentNavigationPage = ((searchResult && searchResult.page) || currentTab);
return ( return (
@ -105,7 +174,7 @@ export const CatalogView: FC<CatalogViewProps> = props =>
</NitroCardTabsView> </NitroCardTabsView>
<NitroCardContentView> <NitroCardContentView>
<div className="row h-100"> <div className="row h-100">
{ pageParser && !pageParser.frontPageItems.length && { (!pageParser || (pageParser && !pageParser.frontPageItems.length)) &&
<div className="col-3 d-flex flex-column h-100"> <div className="col-3 d-flex flex-column h-100">
<CatalogNavigationView page={ currentNavigationPage } /> <CatalogNavigationView page={ currentNavigationPage } />
</div> } </div> }

View File

@ -149,3 +149,20 @@ export function GetPetAvailableColors(petIndex: number, palettes: SellablePetPal
} }
} }
} }
export function GetCatalogPageTree(page: ICatalogPageData, targetPageId: number, tree: ICatalogPageData[])
{
if(page.pageId === targetPageId) return page;
for(const pageData of page.children)
{
const foundPageData = GetCatalogPageTree(pageData, targetPageId, tree);
if(foundPageData)
{
tree.push(pageData);
return pageData;
}
}
}

View File

@ -1,4 +1,4 @@
import { Nitro } from 'nitro-renderer'; import { GetNitroInstance } from '../../../api';
import { IPurse } from './IPurse'; import { IPurse } from './IPurse';
export class Purse implements IPurse export class Purse implements IPurse
@ -22,7 +22,7 @@ export class Purse implements IPurse
public set credits(k: number) public set credits(k: number)
{ {
this._lastUpdated = Nitro.instance.time; this._lastUpdated = GetNitroInstance().time;
this._credits = k; this._credits = k;
} }
@ -33,7 +33,7 @@ export class Purse implements IPurse
public set clubDays(k: number) public set clubDays(k: number)
{ {
this._lastUpdated = Nitro.instance.time; this._lastUpdated = GetNitroInstance().time;
this._clubDays = k; this._clubDays = k;
} }
@ -44,7 +44,7 @@ export class Purse implements IPurse
public set clubPeriods(k: number) public set clubPeriods(k: number)
{ {
this._lastUpdated = Nitro.instance.time; this._lastUpdated = GetNitroInstance().time;
this._clubPeriods = k; this._clubPeriods = k;
} }
@ -80,7 +80,7 @@ export class Purse implements IPurse
public set _Str_6288(k: number) public set _Str_6288(k: number)
{ {
this._lastUpdated = Nitro.instance.time; this._lastUpdated = GetNitroInstance().time;
this._pastClubDays = k; this._pastClubDays = k;
} }
@ -91,7 +91,7 @@ export class Purse implements IPurse
public set _Str_4605(k: number) public set _Str_4605(k: number)
{ {
this._lastUpdated = Nitro.instance.time; this._lastUpdated = GetNitroInstance().time;
this._pastVipDays = k; this._pastVipDays = k;
} }
@ -102,7 +102,7 @@ export class Purse implements IPurse
public set _Str_18527(k: Map<number, number>) public set _Str_18527(k: Map<number, number>)
{ {
this._lastUpdated = Nitro.instance.time; this._lastUpdated = GetNitroInstance().time;
this._activityPoints = k; this._activityPoints = k;
} }
@ -113,14 +113,14 @@ export class Purse implements IPurse
public set _Str_4458(k: number) public set _Str_4458(k: number)
{ {
this._lastUpdated = Nitro.instance.time; this._lastUpdated = GetNitroInstance().time;
this._minutesUntilExpiration = k; this._minutesUntilExpiration = k;
} }
public get _Str_4458(): number public get _Str_4458(): number
{ {
const k = ((Nitro.instance.time - this._lastUpdated) / (1000 * 60)); const k = ((GetNitroInstance().time - this._lastUpdated) / (1000 * 60));
const _local_2 = (this._minutesUntilExpiration - k); const _local_2 = (this._minutesUntilExpiration - k);
return (_local_2 > 0) ? _local_2 : 0; return (_local_2 > 0) ? _local_2 : 0;
@ -128,7 +128,7 @@ export class Purse implements IPurse
public set _Str_6312(k: number) public set _Str_6312(k: number)
{ {
this._lastUpdated = Nitro.instance.time; this._lastUpdated = GetNitroInstance().time;
this._minutesSinceLastModified = k; this._minutesSinceLastModified = k;
} }

View File

@ -19,6 +19,7 @@ export interface ICatalogState
clubOffers: CatalogClubOfferData[]; clubOffers: CatalogClubOfferData[];
subscriptionInfo: SubscriptionInfo; subscriptionInfo: SubscriptionInfo;
giftConfiguration: GiftWrappingConfiguration; giftConfiguration: GiftWrappingConfiguration;
pendingPageId: number;
} }
export interface ICatalogAction export interface ICatalogAction
@ -37,6 +38,7 @@ export interface ICatalogAction
clubOffers?: CatalogClubOfferData[]; clubOffers?: CatalogClubOfferData[];
subscriptionInfo?: SubscriptionInfo; subscriptionInfo?: SubscriptionInfo;
giftConfiguration?: CatalogGiftConfigurationParser; giftConfiguration?: CatalogGiftConfigurationParser;
pendingPageId?: number;
} }
} }
@ -54,6 +56,7 @@ export class CatalogActions
public static SET_SEARCH_RESULT: string = 'CA_SET_SEARCH_RESULT'; public static SET_SEARCH_RESULT: string = 'CA_SET_SEARCH_RESULT';
public static SET_SUBSCRIPTION_INFO: string = 'CA_SET_SUBSCRIPTION_INFO'; public static SET_SUBSCRIPTION_INFO: string = 'CA_SET_SUBSCRIPTION_INFO';
public static SET_GIFT_CONFIGURATION: string = 'CA_SET_GIFT_CONFIGURATION'; public static SET_GIFT_CONFIGURATION: string = 'CA_SET_GIFT_CONFIGURATION';
public static SET_PENDING_PAGE_ID: string = 'CA_SET_PENDING_PAGE_ID';
} }
export const initialCatalog: ICatalogState = { export const initialCatalog: ICatalogState = {
@ -68,7 +71,8 @@ export const initialCatalog: ICatalogState = {
petPalettes: [], petPalettes: [],
clubOffers: null, clubOffers: null,
subscriptionInfo: new SubscriptionInfo(), subscriptionInfo: new SubscriptionInfo(),
giftConfiguration: null giftConfiguration: null,
pendingPageId: -1
} }
export const CatalogReducer: Reducer<ICatalogState, ICatalogAction> = (state, action) => export const CatalogReducer: Reducer<ICatalogState, ICatalogAction> = (state, action) =>
@ -168,6 +172,11 @@ export const CatalogReducer: Reducer<ICatalogState, ICatalogAction> = (state, ac
return { ...state, giftConfiguration }; return { ...state, giftConfiguration };
} }
case CatalogActions.SET_PENDING_PAGE_ID: {
const pendingPageId = (action.payload.pendingPageId || -1);
return { ...state, pendingPageId };
}
default: default:
return state; return state;
} }

View File

@ -1,7 +1,8 @@
import { CatalogPageComposer, ICatalogPageData } from 'nitro-renderer';
import { FC, useCallback, useEffect, useState } from 'react'; import { FC, useCallback, useEffect, useState } from 'react';
import { GetCatalogPageComposer } from '../../../../../api/catalog/GetCatalogPageComposer';
import { SendMessageHook } from '../../../../../hooks/messages/message-event'; import { SendMessageHook } from '../../../../../hooks/messages/message-event';
import { CatalogMode } from '../../../CatalogView.types'; import { CatalogMode } from '../../../CatalogView.types';
import { useCatalogContext } from '../../../context/CatalogContext';
import { CatalogIconView } from '../../catalog-icon/CatalogIconView'; import { CatalogIconView } from '../../catalog-icon/CatalogIconView';
import { CatalogNavigationSetView } from '../set/CatalogNavigationSetView'; import { CatalogNavigationSetView } from '../set/CatalogNavigationSetView';
import { CatalogNavigationItemViewProps } from './CatalogNavigationItemView.types'; import { CatalogNavigationItemViewProps } from './CatalogNavigationItemView.types';
@ -10,6 +11,31 @@ export const CatalogNavigationItemView: FC<CatalogNavigationItemViewProps> = pro
{ {
const { page = null, isActive = false, setActiveChild = null } = props; const { page = null, isActive = false, setActiveChild = null } = props;
const [ isExpanded, setIsExpanded ] = useState(false); const [ isExpanded, setIsExpanded ] = useState(false);
const [ myTree, setMyTree ] = useState<ICatalogPageData[]>(null);
const { dispatchCatalogState = null } = useCatalogContext();
const select = useCallback((selectPage: ICatalogPageData) =>
{
if(!selectPage) return;
setActiveChild(prevValue =>
{
if(prevValue === selectPage)
{
SendMessageHook(new CatalogPageComposer(selectPage.pageId, -1, CatalogMode.MODE_NORMAL));
}
return selectPage;
});
if(selectPage.children && selectPage.children.length)
{
setIsExpanded(prevValue =>
{
return !prevValue;
});
}
}, [ setActiveChild ]);
useEffect(() => useEffect(() =>
{ {
@ -17,35 +43,14 @@ export const CatalogNavigationItemView: FC<CatalogNavigationItemViewProps> = pro
setIsExpanded(true); setIsExpanded(true);
SendMessageHook(GetCatalogPageComposer(page.pageId, -1, CatalogMode.MODE_NORMAL)); SendMessageHook(new CatalogPageComposer(page.pageId, -1, CatalogMode.MODE_NORMAL));
}, [ isActive, page ]); }, [ isActive, page, select, dispatchCatalogState ]);
const select = useCallback(() => if(!page.visible) return null;
{
if(!page) return;
setActiveChild(prevValue =>
{
if(prevValue === page)
{
SendMessageHook(GetCatalogPageComposer(page.pageId, -1, CatalogMode.MODE_NORMAL));
}
return page;
});
if(page.children && page.children.length)
{
setIsExpanded(prevValue =>
{
return !prevValue;
});
}
}, [ page, setActiveChild ]);
return ( return (
<div className="col pb-1 catalog-navigation-item-container"> <div className="col pb-1 catalog-navigation-item-container">
<div className={ 'd-flex align-items-center cursor-pointer catalog-navigation-item ' + (isActive ? 'active ': '') } onClick={ select }> <div className={ 'd-flex align-items-center cursor-pointer catalog-navigation-item ' + (isActive ? 'active ': '') } onClick={ event => select(page) }>
<CatalogIconView icon={ page.icon } /> <CatalogIconView icon={ page.icon } />
<div className="flex-grow-1 text-black text-truncate px-1">{ page.localization }</div> <div className="flex-grow-1 text-black text-truncate px-1">{ page.localization }</div>
{ (page.children.length > 0) && <i className={ 'fas fa-caret-' + (isExpanded ? 'up' : 'down') } /> } { (page.children.length > 0) && <i className={ 'fas fa-caret-' + (isExpanded ? 'up' : 'down') } /> }

View File

@ -1,3 +1,4 @@
@import './badge-display/CatalogLayoutBadgeDisplayView';
@import './default/CatalogLayoutDefaultView'; @import './default/CatalogLayoutDefaultView';
@import './frontpage4/CatalogLayoutFrontpage4View'; @import './frontpage4/CatalogLayoutFrontpage4View';
@import './pets/CatalogLayoutPetView'; @import './pets/CatalogLayoutPetView';

View File

@ -1,4 +1,5 @@
import { ICatalogPageParser, RoomPreviewer } from 'nitro-renderer'; import { ICatalogPageParser, RoomPreviewer } from 'nitro-renderer';
import { CatalogLayoutBadgeDisplayView } from './badge-display/CatalogLayoutBadgeDisplayView';
import { CatalogLayoutDefaultView } from './default/CatalogLayoutDefaultView'; import { CatalogLayoutDefaultView } from './default/CatalogLayoutDefaultView';
import { CatalogLayoutFrontpage4View } from './frontpage4/CatalogLayoutFrontpage4View'; import { CatalogLayoutFrontpage4View } from './frontpage4/CatalogLayoutFrontpage4View';
import { CatalogLayouGuildCustomFurniView } from './guild-custom-furni/CatalogLayoutGuildCustomFurniView'; import { CatalogLayouGuildCustomFurniView } from './guild-custom-furni/CatalogLayoutGuildCustomFurniView';
@ -47,9 +48,11 @@ export const GetCatalogLayout = (pageParser: ICatalogPageParser, roomPreviewer:
case 'spaces_new': case 'spaces_new':
return <CatalogLayoutSpacesView roomPreviewer={ roomPreviewer } pageParser={ pageParser } />; return <CatalogLayoutSpacesView roomPreviewer={ roomPreviewer } pageParser={ pageParser } />;
case 'trophies': case 'trophies':
return <CatalogLayoutTrophiesView roomPreviewer={roomPreviewer} pageParser={pageParser} />; return <CatalogLayoutTrophiesView roomPreviewer={roomPreviewer} pageParser={ pageParser } />;
case 'info_loyalty': case 'info_loyalty':
return <CatalogLayoutInfoLoyaltyView roomPreviewer={ roomPreviewer } pageParser={ pageParser } />; return <CatalogLayoutInfoLoyaltyView roomPreviewer={ roomPreviewer } pageParser={ pageParser } />;
case 'badge_display':
return <CatalogLayoutBadgeDisplayView roomPreviewer={roomPreviewer} pageParser={ pageParser } />;
case 'bots': case 'bots':
case 'default_3x3': case 'default_3x3':
default: default:

View File

@ -0,0 +1,7 @@
.nitro-catalog-layout-badge-display {
.inventory-badge-grid {
height: 200px;
max-height: 200px;
}
}

View File

@ -0,0 +1,80 @@
import { StringDataType } from 'nitro-renderer';
import { FC, useCallback, useEffect, useMemo, useState } from 'react';
import { InventoryBadgesUpdatedEvent } from '../../../../../../events';
import { InventoryBadgesRequestEvent } from '../../../../../../events/inventory/InventoryBadgesRequestEvent';
import { dispatchUiEvent, useUiEvent } from '../../../../../../hooks';
import { NitroCardGridItemView } from '../../../../../../layout/card/grid/item/NitroCardGridItemView';
import { NitroCardGridView } from '../../../../../../layout/card/grid/NitroCardGridView';
import { BadgeImageView } from '../../../../../shared/badge-image/BadgeImageView';
import { GetOfferName } from '../../../../common/CatalogUtilities';
import { useCatalogContext } from '../../../../context/CatalogContext';
import { CatalogRoomPreviewerView } from '../../../catalog-room-previewer/CatalogRoomPreviewerView';
import { CatalogPageOffersView } from '../../offers/CatalogPageOffersView';
import { CatalogPurchaseView } from '../../purchase/CatalogPurchaseView';
import { CatalogLayoutBadgeDisplayViewProps } from './CatalogLayoutBadgeDisplayView.types';
export const CatalogLayoutBadgeDisplayView: FC<CatalogLayoutBadgeDisplayViewProps> = props =>
{
const { roomPreviewer = null, pageParser = null } = props;
const { catalogState = null, dispatchCatalogState = null } = useCatalogContext();
const { activeOffer = null } = catalogState;
const [ currentBadge, setCurrentBadge ] = useState<string>(null);
const [ badges, setBadges ] = useState<string[]>([]);
const product = ((activeOffer && activeOffer.products[0]) || null);
const onInventoryBadgesUpdatedEvent = useCallback((event: InventoryBadgesUpdatedEvent) =>
{
console.log(event);
setBadges(event.badges);
}, []);
useUiEvent(InventoryBadgesUpdatedEvent.BADGES_UPDATED, onInventoryBadgesUpdatedEvent);
const badgeElements = useMemo(() =>
{
return badges.map(code =>
{
return (
<NitroCardGridItemView key={ code } itemActive={ (currentBadge === code) } onMouseDown={ event => setCurrentBadge(code) }>
<BadgeImageView badgeCode={ code } />
</NitroCardGridItemView>
);
});
}, [ badges, currentBadge ]);
useEffect(() =>
{
dispatchUiEvent(new InventoryBadgesRequestEvent(InventoryBadgesRequestEvent.REQUEST_BADGES));
}, []);
useEffect(() =>
{
if(!currentBadge || !activeOffer) return;
const stuffData = new StringDataType();
stuffData.setValue([ null, currentBadge ]);
roomPreviewer.updateObjectStuffData(stuffData);
}, [ currentBadge, activeOffer, roomPreviewer ]);
return (
<div className="row h-100 nitro-catalog-layout-badge-display">
<div className="d-flex flex-column col-7 h-100">
<CatalogPageOffersView offers={ pageParser.offers } />
<div className="d-flex mt-2">
<NitroCardGridView className="inventory-badge-grid">
{ badgeElements }
</NitroCardGridView>
</div>
</div>
{ product &&
<div className="position-relative d-flex flex-column col">
<CatalogRoomPreviewerView roomPreviewer={ roomPreviewer } height={ 140 } />
<div className="fs-6 text-black mt-1 overflow-hidden">{ GetOfferName(activeOffer) }</div>
<CatalogPurchaseView offer={ activeOffer } pageId={ pageParser.pageId } extra={ 'Bill 22-7-2021 ADM' } />
</div> }
</div>
);
}

View File

@ -0,0 +1,6 @@
import { CatalogLayoutProps } from '../CatalogLayout.types';
export interface CatalogLayoutBadgeDisplayViewProps extends CatalogLayoutProps
{
}

View File

@ -1,7 +1,10 @@
.nitro-catalog-layout-vip-buy { .nitro-catalog-layout-vip-buy {
.catalog-offer-item-container { .vip-buy-grid {
height: 60px !important;
max-height: 60px !important; .grid-item-container {
height: 70px !important;
max-height: 70px !important;
}
} }
} }

View File

@ -1,7 +1,11 @@
import { CatalogClubOfferData, CatalogRequestVipOffersComposer } from 'nitro-renderer'; import { CatalogClubOfferData, CatalogPurchaseComposer, CatalogRequestVipOffersComposer } from 'nitro-renderer';
import { FC, useCallback, useEffect, useMemo } from 'react'; import { FC, useCallback, useEffect, useMemo, useState } from 'react';
import { Button } from 'react-bootstrap';
import { SendMessageHook } from '../../../../../../hooks/messages/message-event'; import { SendMessageHook } from '../../../../../../hooks/messages/message-event';
import { NitroCardGridItemView } from '../../../../../../layout/card/grid/item/NitroCardGridItemView';
import { NitroCardGridView } from '../../../../../../layout/card/grid/NitroCardGridView';
import { LocalizeText } from '../../../../../../utils/LocalizeText'; import { LocalizeText } from '../../../../../../utils/LocalizeText';
import { GLOBAL_PURSE } from '../../../../../purse/PurseView';
import { CurrencyIcon } from '../../../../../shared/currency-icon/CurrencyIcon'; import { CurrencyIcon } from '../../../../../shared/currency-icon/CurrencyIcon';
import { GetCatalogPageImage } from '../../../../common/CatalogUtilities'; import { GetCatalogPageImage } from '../../../../common/CatalogUtilities';
import { useCatalogContext } from '../../../../context/CatalogContext'; import { useCatalogContext } from '../../../../context/CatalogContext';
@ -11,16 +15,7 @@ export const CatalogLayoutVipBuyView: FC<CatalogLayoutVipBuyViewProps> = props =
{ {
const { catalogState = null } = useCatalogContext(); const { catalogState = null } = useCatalogContext();
const { pageParser = null, clubOffers = null, subscriptionInfo = null } = catalogState; const { pageParser = null, clubOffers = null, subscriptionInfo = null } = catalogState;
const [ pendingOffer, setPendingOffer ] = useState<CatalogClubOfferData>(null);
useEffect(() =>
{
if(clubOffers === null)
{
SendMessageHook(new CatalogRequestVipOffersComposer(1));
return;
}
}, [ clubOffers ]);
const getOfferText = useCallback((offer: CatalogClubOfferData) => const getOfferText = useCallback((offer: CatalogClubOfferData) =>
{ {
@ -41,6 +36,31 @@ export const CatalogLayoutVipBuyView: FC<CatalogLayoutVipBuyViewProps> = props =
return offerText; return offerText;
}, []); }, []);
const getPurchaseHeader = useCallback(() =>
{
const purse = GLOBAL_PURSE;
if(!purse) return '';
const extensionOrSubscription = (purse.clubDays > 0 || purse.clubPeriods > 0) ? 'extension.' : 'subscription.';
const daysOrMonths = ((pendingOffer.months === 0) ? 'days' : 'months');
const daysOrMonthsText = ((pendingOffer.months === 0) ? pendingOffer.extraDays : pendingOffer.months);
const locale = LocalizeText('catalog.vip.buy.confirm.' + extensionOrSubscription + daysOrMonths);
return locale.replace('%NUM_' + daysOrMonths.toUpperCase() + '%', daysOrMonthsText.toString());
}, [ pendingOffer ]);
const getPurchaseValidUntil = useCallback(() =>
{
let locale = LocalizeText('catalog.vip.buy.confirm.end_date');
locale = locale.replace('%month%', pendingOffer.month.toString());
locale = locale.replace('%day%', pendingOffer.day.toString());
locale = locale.replace('%year%', pendingOffer.year.toString());
return locale;
}, [ pendingOffer ]);
const getSubscriptionDetails = useMemo(() => const getSubscriptionDetails = useMemo(() =>
{ {
if(!subscriptionInfo) return ''; if(!subscriptionInfo) return '';
@ -52,13 +72,69 @@ export const CatalogLayoutVipBuyView: FC<CatalogLayoutVipBuyViewProps> = props =
return LocalizeText('catalog.vip.extend.info', [ 'days' ], [ totalDays.toString() ]); return LocalizeText('catalog.vip.extend.info', [ 'days' ], [ totalDays.toString() ]);
}, [ subscriptionInfo ]); }, [ subscriptionInfo ]);
const purchaseSubscription = useCallback(() =>
{
if(!pendingOffer) return;
SendMessageHook(new CatalogPurchaseComposer(pageParser.pageId, pendingOffer.offerId, null, 1));
}, [ pendingOffer, pageParser ])
useEffect(() =>
{
if(clubOffers === null)
{
SendMessageHook(new CatalogRequestVipOffersComposer(1));
return;
}
}, [ clubOffers ]);
return ( return (
<>
<div className="row h-100 nitro-catalog-layout-vip-buy"> <div className="row h-100 nitro-catalog-layout-vip-buy">
<div className="col-7 h-100"> <div className="col-7 h-100">
<div className="row row-cols-1 align-content-start g-0 mb-n1 w-100 catalog-offers-container h-100 overflow-auto"> <NitroCardGridView columns={ 1 } className="vip-buy-grid">
{ clubOffers && (clubOffers.length > 0) && clubOffers.map((offer, index) => { clubOffers && (clubOffers.length > 0) && clubOffers.map((offer, index) =>
{ {
return <div key={ index } className="col pe-1 pb-1 catalog-offer-item-container"> return (
<NitroCardGridItemView key={ index } className="justify-content-between p-1">
{ (pendingOffer === offer) &&
<div className="d-flex flex-column justify-content-center align-items-center w-100">
<div className="text-black text-small">{ getPurchaseHeader() }</div>
<div className="text-black text-small">{ getPurchaseValidUntil() }</div>
<Button variant="primary" size="sm" onClick={ purchaseSubscription }>{ LocalizeText('buy') }</Button>
</div> }
{ (pendingOffer !== offer) &&
<>
<div className="d-flex flex-column text-black text-small m-1">
<div>
<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>
</div>
<Button variant="primary" size="sm" onClick={ event => setPendingOffer(offer) }>{ LocalizeText('buy') }</Button>
</> }
</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="position-relative border border-2 rounded catalog-offer-item">
<div className="d-flex align-items-center text-black text-small m-1"> <div className="d-flex align-items-center text-black text-small m-1">
<i className="icon icon-catalogue-hc_small me-1"></i> <i className="icon icon-catalogue-hc_small me-1"></i>
@ -76,10 +152,12 @@ export const CatalogLayoutVipBuyView: FC<CatalogLayoutVipBuyViewProps> = props =
<CurrencyIcon type={ offer.priceActivityPointsType } /> <CurrencyIcon type={ offer.priceActivityPointsType } />
</div> } </div> }
</div> </div>
<Button variant="primary" size="sm" onClick={ event => setPendingOffer(offer) } />
</div> </div>
</div>;
})}
</div> </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-5 justify-content-center align-items-center">
<div className="d-block mb-2"> <div className="d-block mb-2">
@ -88,5 +166,6 @@ export const CatalogLayoutVipBuyView: FC<CatalogLayoutVipBuyViewProps> = props =
<div className="fs-6 text-center text-black lh-sm overflow-hidden" dangerouslySetInnerHTML={ {__html: getSubscriptionDetails } }></div> <div className="fs-6 text-center text-black lh-sm overflow-hidden" dangerouslySetInnerHTML={ {__html: getSubscriptionDetails } }></div>
</div> </div>
</div> </div>
</>
); );
} }

View File

@ -96,3 +96,4 @@
@import './views/widgets/promo-article/PromoArticleWidgetView.scss'; @import './views/widgets/promo-article/PromoArticleWidgetView.scss';
@import './views/widgets/bonus-rare/BonusRareWidgetView.scss'; @import './views/widgets/bonus-rare/BonusRareWidgetView.scss';
@import './views/widgets/hall-of-fame/HallOfFameWidgetView.scss'; @import './views/widgets/hall-of-fame/HallOfFameWidgetView.scss';
@import './views/widgets/widgetcontainer/WidgetContainerView.scss'

View File

@ -1,6 +1,6 @@
import { Nitro, RoomSessionEvent } from 'nitro-renderer'; import { RoomSessionEvent } from 'nitro-renderer';
import { FC, useCallback, useState } from 'react'; import { FC, useCallback, useState } from 'react';
import { GetConfiguration } from '../../api'; import { GetConfiguration, GetConfigurationManager } from '../../api';
import { useRoomSessionManagerEvent } from '../../hooks/events/nitro/session/room-session-manager-event'; import { useRoomSessionManagerEvent } from '../../hooks/events/nitro/session/room-session-manager-event';
import { HotelViewProps } from './HotelView.types'; import { HotelViewProps } from './HotelView.types';
import { WidgetSlotView } from './views/widget-slot/WidgetSlotView'; import { WidgetSlotView } from './views/widget-slot/WidgetSlotView';
@ -29,12 +29,12 @@ export const HotelView: FC<HotelViewProps> = props =>
if (!isVisible) return null; if (!isVisible) return null;
const backgroundColor = GetConfiguration('hotelview')['images']['background.colour']; const backgroundColor = GetConfiguration('hotelview')['images']['background.colour'];
const background = Nitro.instance.core.configuration.interpolate(GetConfiguration('hotelview')['images']['background']); const background = GetConfigurationManager().interpolate(GetConfiguration('hotelview')['images']['background']);
const sun = Nitro.instance.core.configuration.interpolate(GetConfiguration('hotelview')['images']['sun']); const sun = GetConfigurationManager().interpolate(GetConfiguration('hotelview')['images']['sun']);
const drape = Nitro.instance.core.configuration.interpolate(GetConfiguration('hotelview')['images']['drape']); const drape = GetConfigurationManager().interpolate(GetConfiguration('hotelview')['images']['drape']);
const left = Nitro.instance.core.configuration.interpolate(GetConfiguration('hotelview')['images']['left']); const left = GetConfigurationManager().interpolate(GetConfiguration('hotelview')['images']['left']);
const rightRepeat = Nitro.instance.core.configuration.interpolate(GetConfiguration('hotelview')['images']['right.repeat']); const rightRepeat = GetConfigurationManager().interpolate(GetConfiguration('hotelview')['images']['right.repeat']);
const right = Nitro.instance.core.configuration.interpolate(GetConfiguration('hotelview')['images']['right']); const right = GetConfigurationManager().interpolate(GetConfiguration('hotelview')['images']['right']);
return ( return (
<div className="nitro-hotel-view" style={(backgroundColor && backgroundColor) ? { background: backgroundColor } : {}}> <div className="nitro-hotel-view" style={(backgroundColor && backgroundColor) ? { background: backgroundColor } : {}}>
@ -44,9 +44,10 @@ export const HotelView: FC<HotelViewProps> = props =>
<WidgetSlotView <WidgetSlotView
widgetSlot={ 1 } widgetSlot={ 1 }
widgetType={GetConfiguration('hotelview')['widgets']['slot.' + 1 + '.widget']} widgetType={GetConfiguration('hotelview')['widgets']['slot.' + 1 + '.widget']}
widgetConf={GetConfiguration('hotelview')['widgets']['slot.' + 1 +'.conf']} widgetConf={GetConfiguration('hotelview')['widgets']['slot.' + 1 + '.conf']}
className="col-6"
/> />
<div className="row mx-0"> <div className="col-12 row mx-0">
<WidgetSlotView <WidgetSlotView
widgetSlot={ 2 } widgetSlot={ 2 }
widgetType={GetConfiguration('hotelview')['widgets']['slot.' + 2 + '.widget']} widgetType={GetConfiguration('hotelview')['widgets']['slot.' + 2 + '.widget']}

View File

@ -3,6 +3,7 @@ import { BonusRareWidgetView } from './bonus-rare/BonusRareWidgetView';
import { GetWidgetLayoutProps } from './GetWidgetLayout.types'; import { GetWidgetLayoutProps } from './GetWidgetLayout.types';
import { HallOfFameWidgetView } from './hall-of-fame/HallOfFameWidgetView'; import { HallOfFameWidgetView } from './hall-of-fame/HallOfFameWidgetView';
import { PromoArticleWidgetView } from './promo-article/PromoArticleWidgetView'; import { PromoArticleWidgetView } from './promo-article/PromoArticleWidgetView';
import { WidgetContainerView } from './widgetcontainer/WIdgetContainerView';
export const GetWidgetLayout: FC<GetWidgetLayoutProps> = props => export const GetWidgetLayout: FC<GetWidgetLayoutProps> = props =>
{ {
@ -14,6 +15,8 @@ export const GetWidgetLayout: FC<GetWidgetLayoutProps> = props =>
return <HallOfFameWidgetView slot={props.slot} conf={props.widgetConf} />; return <HallOfFameWidgetView slot={props.slot} conf={props.widgetConf} />;
case "bonusrare": case "bonusrare":
return <BonusRareWidgetView />; return <BonusRareWidgetView />;
case "widgetcontainer":
return <WidgetContainerView conf={props.widgetConf} />
default: default:
return null; return null;
} }

View File

@ -37,15 +37,16 @@ export const PromoArticleWidgetView: FC<PromoArticleWidgetViewProps> = props =>
</div> </div>
<div className="d-flex flex-row mb-1"> <div className="d-flex flex-row mb-1">
{articles && (articles.length > 0) && articles.map((article, ind) => {articles && (articles.length > 0) && articles.map((article, ind) =>
<div className={`promo-articles-bullet ` + (article === articles[index] ? 'promo-articles-bullet-active' : '')} key={article.id} onClick={event => handleSelect(ind)} /> <div className={`promo-articles-bullet cursor-pointer ` + (article === articles[index] ? 'promo-articles-bullet-active' : '')} key={article.id} onClick={event => handleSelect(ind)} />
)} )}
</div> </div>
{articles && articles[index] && {articles && articles[index] &&
<div className="promo-article d-flex flex-row row mx-0"> <div className="promo-article d-flex flex-row row mx-0">
<div className="promo-article-image" style={ {backgroundImage: `url(${articles[index].imageUrl})`} }/> <div className="promo-article-image" style={ {backgroundImage: `url(${articles[index].imageUrl})`} }/>
<div className="col-3"> <div className="col-3 d-flex flex-column h-100">
<h3 className="my-0">{articles[index].title}</h3> <h3 className="my-0">{articles[index].title}</h3>
<b>{ articles[index].bodyText }</b> <b>{ articles[index].bodyText }</b>
<button className="btn btn-sm mt-auto btn-gainsboro">{ articles[index].buttonText }</button>
</div> </div>
</div> </div>
} }

View File

@ -0,0 +1,47 @@
import { FC, useCallback, useMemo } from 'react';
import { GetConfigurationManager } from '../../../../../api/core/';
import { WidgetContainerViewProps } from './WidgetContainerView.types';
export const WidgetContainerView: FC<WidgetContainerViewProps> = props =>
{
const { conf = null } = props;
const config = useMemo(() =>
{
const config = {};
if(!conf || !conf.length) return config;
let options = conf.split(",");
options.forEach(option =>
{
let [ key, value ] = option.split(':');
if(key && value) config[key] = value;
});
return config;
}, [ conf ]);
const getOption = useCallback((key: string) =>
{
const option = config[key];
if(!option) return null;
switch(key)
{
case 'image':
return GetConfigurationManager().interpolate(option);
}
return option;
}, [ config ]);
return (
<div className="widgetcontainer widget">
{ getOption('image') }
</div>
);
}

View File

@ -0,0 +1,4 @@
export interface WidgetContainerViewProps
{
conf: string;
}

View File

@ -1,7 +1,10 @@
import { AdvancedMap, BadgesEvent, BotAddedToInventoryEvent, BotInventoryMessageEvent, BotRemovedFromInventoryEvent, FurnitureListAddOrUpdateEvent, FurnitureListEvent, FurnitureListInvalidateEvent, FurnitureListItemParser, FurnitureListRemovedEvent, FurniturePostItPlacedEvent, PetAddedToInventoryEvent, PetData, PetInventoryEvent, PetRemovedFromInventory, TradingAcceptEvent, TradingCloseEvent, TradingCompletedEvent, TradingConfirmationEvent, TradingListItemEvent, TradingNotOpenEvent, TradingOpenEvent, TradingOpenFailedEvent, TradingOtherNotAllowedEvent, TradingYouAreNotAllowedEvent, UnseenItemsEvent } from 'nitro-renderer'; import { AdvancedMap, BadgeReceivedEvent, BadgesEvent, BotAddedToInventoryEvent, BotInventoryMessageEvent, BotRemovedFromInventoryEvent, FurnitureListAddOrUpdateEvent, FurnitureListEvent, FurnitureListInvalidateEvent, FurnitureListItemParser, FurnitureListRemovedEvent, FurniturePostItPlacedEvent, PetAddedToInventoryEvent, PetData, PetInventoryEvent, PetRemovedFromInventory, RequestBadgesComposer, TradingAcceptEvent, TradingCloseEvent, TradingCompletedEvent, TradingConfirmationEvent, TradingListItemEvent, TradingNotOpenEvent, TradingOpenEvent, TradingOpenFailedEvent, TradingOtherNotAllowedEvent, TradingYouAreNotAllowedEvent, UnseenItemsEvent } from 'nitro-renderer';
import { FC, useCallback } from 'react'; import { FC, useCallback } from 'react';
import { GetRoomSession, GetSessionDataManager } from '../../api'; import { GetRoomSession, GetSessionDataManager } from '../../api';
import { CreateMessageHook } from '../../hooks/messages/message-event'; import { InventoryBadgesUpdatedEvent } from '../../events';
import { InventoryBadgesRequestEvent } from '../../events/inventory/InventoryBadgesRequestEvent';
import { dispatchUiEvent, useUiEvent } from '../../hooks';
import { CreateMessageHook, SendMessageHook } from '../../hooks/messages/message-event';
import { mergeFurniFragments } from './common/FurnitureUtilities'; import { mergeFurniFragments } from './common/FurnitureUtilities';
import { mergePetFragments } from './common/PetUtilities'; import { mergePetFragments } from './common/PetUtilities';
import { TradeState } from './common/TradeState'; import { TradeState } from './common/TradeState';
@ -17,7 +20,7 @@ let petMsgFragments: Map<number, PetData>[] = null;
export const InventoryMessageHandler: FC<InventoryMessageHandlerProps> = props => export const InventoryMessageHandler: FC<InventoryMessageHandlerProps> = props =>
{ {
const { dispatchFurnitureState = null, dispatchBotState = null, dispatchPetState = null, dispatchBadgeState = null, unseenTracker = null } = useInventoryContext(); const { dispatchFurnitureState = null, dispatchBotState = null, dispatchPetState = null, badgeState = null, dispatchBadgeState = null, unseenTracker = null } = useInventoryContext();
const onFurnitureListAddOrUpdateEvent = useCallback((event: FurnitureListAddOrUpdateEvent) => const onFurnitureListAddOrUpdateEvent = useCallback((event: FurnitureListAddOrUpdateEvent) =>
{ {
@ -163,6 +166,18 @@ export const InventoryMessageHandler: FC<InventoryMessageHandlerProps> = props =
}); });
}, [ dispatchBadgeState ]); }, [ dispatchBadgeState ]);
const onBadgeReceivedEvent = useCallback((event: BadgeReceivedEvent) =>
{
const parser = event.getParser();
dispatchBadgeState({
type: InventoryBadgeActions.ADD_BADGE,
payload: {
badgeCode: parser.badgeCode
}
});
}, [ dispatchBadgeState ]);
const onTradingAcceptEvent = useCallback((event: TradingAcceptEvent) => const onTradingAcceptEvent = useCallback((event: TradingAcceptEvent) =>
{ {
const parser = event.getParser(); const parser = event.getParser();
@ -318,6 +333,7 @@ export const InventoryMessageHandler: FC<InventoryMessageHandlerProps> = props =
CreateMessageHook(PetRemovedFromInventory, onPetRemovedFromInventory); CreateMessageHook(PetRemovedFromInventory, onPetRemovedFromInventory);
CreateMessageHook(PetAddedToInventoryEvent, onPetAddedToInventoryEvent); CreateMessageHook(PetAddedToInventoryEvent, onPetAddedToInventoryEvent);
CreateMessageHook(BadgesEvent, onBadgesEvent); CreateMessageHook(BadgesEvent, onBadgesEvent);
CreateMessageHook(BadgeReceivedEvent, onBadgeReceivedEvent);
CreateMessageHook(TradingAcceptEvent, onTradingAcceptEvent); CreateMessageHook(TradingAcceptEvent, onTradingAcceptEvent);
CreateMessageHook(TradingCloseEvent, onTradingCloseEvent); CreateMessageHook(TradingCloseEvent, onTradingCloseEvent);
CreateMessageHook(TradingCompletedEvent, onTradingCompletedEvent); CreateMessageHook(TradingCompletedEvent, onTradingCompletedEvent);
@ -330,5 +346,19 @@ export const InventoryMessageHandler: FC<InventoryMessageHandlerProps> = props =
CreateMessageHook(TradingYouAreNotAllowedEvent, onTradingYouAreNotAllowedEvent); CreateMessageHook(TradingYouAreNotAllowedEvent, onTradingYouAreNotAllowedEvent);
CreateMessageHook(UnseenItemsEvent, onUnseenItemsEvent); CreateMessageHook(UnseenItemsEvent, onUnseenItemsEvent);
const onInventoryBadgesRequestEvent = useCallback((event: InventoryBadgesRequestEvent) =>
{
if(badgeState.needsBadgeUpdate)
{
SendMessageHook(new RequestBadgesComposer());
return;
}
dispatchUiEvent(new InventoryBadgesUpdatedEvent(InventoryBadgesUpdatedEvent.BADGES_UPDATED, badgeState.badges));
}, [ badgeState ])
useUiEvent(InventoryBadgesRequestEvent.REQUEST_BADGES, onInventoryBadgesRequestEvent);
return null; return null;
} }

View File

@ -1,10 +1,10 @@
import { IRoomSession, RoomEngineObjectEvent, RoomEngineObjectPlacedEvent, RoomPreviewer, RoomSessionEvent, TradingCancelComposer, TradingCloseComposer, TradingOpenComposer } from 'nitro-renderer'; import { IRoomSession, RoomEngineObjectEvent, RoomEngineObjectPlacedEvent, RoomPreviewer, RoomSessionEvent, TradingCancelComposer, TradingCloseComposer, TradingOpenComposer } from 'nitro-renderer';
import { FC, useCallback, useEffect, useReducer, useState } from 'react'; import { FC, useCallback, useEffect, useReducer, useState } from 'react';
import { GetRoomEngine } from '../../api'; import { GetRoomEngine } from '../../api';
import { InventoryEvent, InventoryTradeRequestEvent } from '../../events'; import { InventoryBadgesUpdatedEvent, InventoryEvent, InventoryTradeRequestEvent } from '../../events';
import { useRoomEngineEvent } from '../../hooks/events/nitro/room/room-engine-event'; import { useRoomEngineEvent } from '../../hooks/events/nitro/room/room-engine-event';
import { useRoomSessionManagerEvent } from '../../hooks/events/nitro/session/room-session-manager-event'; import { useRoomSessionManagerEvent } from '../../hooks/events/nitro/session/room-session-manager-event';
import { useUiEvent } from '../../hooks/events/ui/ui-event'; import { dispatchUiEvent, useUiEvent } from '../../hooks/events/ui/ui-event';
import { SendMessageHook } from '../../hooks/messages'; import { SendMessageHook } from '../../hooks/messages';
import { NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../layout'; import { NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../layout';
import { LocalizeText } from '../../utils/LocalizeText'; import { LocalizeText } from '../../utils/LocalizeText';
@ -186,6 +186,13 @@ export const InventoryView: FC<InventoryViewProps> = props =>
} }
}, [ furnitureState.tradeData, isVisible ]); }, [ furnitureState.tradeData, isVisible ]);
useEffect(() =>
{
if(!badgeState.badges) return;
dispatchUiEvent(new InventoryBadgesUpdatedEvent(InventoryBadgesUpdatedEvent.BADGES_UPDATED, badgeState.badges));
}, [ badgeState.badges ]);
return ( return (
<InventoryContextProvider value={ { furnitureState, dispatchFurnitureState, botState, dispatchBotState, petState, dispatchPetState, badgeState, dispatchBadgeState, unseenTracker } }> <InventoryContextProvider value={ { furnitureState, dispatchFurnitureState, botState, dispatchBotState, petState, dispatchPetState, badgeState, dispatchBadgeState, unseenTracker } }>
<InventoryMessageHandler /> <InventoryMessageHandler />

View File

@ -1,4 +1,5 @@
import { IFurnitureItemData, IObjectData, Nitro } from 'nitro-renderer'; import { IFurnitureItemData, IObjectData } from 'nitro-renderer';
import { GetNitroInstance } from '../../../api';
import { IFurnitureItem } from './IFurnitureItem'; import { IFurnitureItem } from './IFurnitureItem';
export class FurnitureItem implements IFurnitureItem export class FurnitureItem implements IFurnitureItem
@ -122,7 +123,7 @@ export class FurnitureItem implements IFurnitureItem
if(this._hasRentPeriodStarted) if(this._hasRentPeriodStarted)
{ {
time = (this._secondsToExpiration - ((Nitro.instance.time - this._expirationTimeStamp) / 1000)); time = (this._secondsToExpiration - ((GetNitroInstance().time - this._expirationTimeStamp) / 1000));
if(time < 0) time = 0; if(time < 0) time = 0;
} }

View File

@ -23,11 +23,12 @@ export interface IInventoryBadgeAction
export class InventoryBadgeActions export class InventoryBadgeActions
{ {
public static SET_NEEDS_UPDATE: string = 'IBDA_SET_NEEDS_UPDATE'; public static SET_NEEDS_UPDATE: string = 'IBA_SET_NEEDS_UPDATE';
public static SET_BADGE: string = 'IBDA_SET_BADGE'; public static SET_BADGE: string = 'IBA_SET_BADGE';
public static SET_BADGES: string = 'IBDA_SET_BADGES'; public static SET_BADGES: string = 'IBA_SET_BADGES';
public static ADD_ACTIVE_BADGE: string = 'IBDA_ADD_ACTIVE_BADGE'; public static ADD_BADGE: string = 'IBA_ADD_BADGE';
public static REMOVE_ACTIVE_BADGE: string = 'IBDA_REMOVE_ACTIVE_BADGE'; public static ADD_ACTIVE_BADGE: string = 'IBA_ADD_ACTIVE_BADGE';
public static REMOVE_ACTIVE_BADGE: string = 'IBA_REMOVE_ACTIVE_BADGE';
} }
export const initialInventoryBadge: IInventoryBadgeState = { export const initialInventoryBadge: IInventoryBadgeState = {
@ -77,6 +78,14 @@ export const InventoryBadgeReducer: Reducer<IInventoryBadgeState, IInventoryBadg
return { ...state, badges, activeBadges }; return { ...state, badges, activeBadges };
} }
case InventoryBadgeActions.ADD_BADGE: {
const badges = [ ...state.badges ];
const badge = (action.payload.badgeCode);
if(badges.indexOf(badge) === -1) badges.push(badge);
return { ...state, badges };
}
case InventoryBadgeActions.ADD_ACTIVE_BADGE: { case InventoryBadgeActions.ADD_ACTIVE_BADGE: {
const badgeCode = action.payload.badgeCode; const badgeCode = action.payload.badgeCode;

View File

@ -19,6 +19,7 @@ export const InventoryBadgeView: FC<InventoryBadgeViewProps> = props =>
{ {
if(needsBadgeUpdate) if(needsBadgeUpdate)
{ {
console.log('yee')
dispatchBadgeState({ dispatchBadgeState({
type: InventoryBadgeActions.SET_NEEDS_UPDATE, type: InventoryBadgeActions.SET_NEEDS_UPDATE,
payload: { payload: {

View File

@ -1,5 +1,6 @@
import { Nitro, RoomSessionEvent } from 'nitro-renderer'; import { RoomSessionEvent } from 'nitro-renderer';
import { FC, useCallback, useEffect, useState } from 'react'; import { FC, useCallback, useEffect, useState } from 'react';
import { GetCommunication } from '../../api';
import { useRoomSessionManagerEvent } from '../../hooks/events/nitro/session/room-session-manager-event'; import { useRoomSessionManagerEvent } from '../../hooks/events/nitro/session/room-session-manager-event';
import { AchievementsView } from '../achievements/AchievementsView'; import { AchievementsView } from '../achievements/AchievementsView';
import { AvatarEditorView } from '../avatar-editor/AvatarEditorView'; import { AvatarEditorView } from '../avatar-editor/AvatarEditorView';
@ -41,7 +42,7 @@ export const MainView: FC<MainViewProps> = props =>
{ {
setIsReady(true); setIsReady(true);
Nitro.instance.communication.connection.onReady(); GetCommunication().connection.onReady();
}, []); }, []);
return ( return (

View File

@ -1,5 +1,7 @@
import { NavigatorInitComposer, NavigatorSearchComposer, RoomSessionEvent } from 'nitro-renderer'; import { ILinkEventTracker, NavigatorInitComposer, NavigatorSearchComposer, RoomSessionEvent } from 'nitro-renderer';
import { FC, useCallback, useEffect, useReducer, useState } from 'react'; import { FC, useCallback, useEffect, useReducer, useState } from 'react';
import { AddEventLinkTracker, RemoveLinkEventTracker } from '../../api';
import { TryVisitRoom } from '../../api/navigator/TryVisitRoom';
import { NavigatorEvent } from '../../events'; import { NavigatorEvent } from '../../events';
import { useRoomSessionManagerEvent } from '../../hooks/events/nitro/session/room-session-manager-event'; import { useRoomSessionManagerEvent } from '../../hooks/events/nitro/session/room-session-manager-event';
import { useUiEvent } from '../../hooks/events/ui/ui-event'; import { useUiEvent } from '../../hooks/events/ui/ui-event';
@ -73,6 +75,45 @@ export const NavigatorView: FC<NavigatorViewProps> = props =>
SendMessageHook(new NavigatorSearchComposer(contextCode, searchValue)); SendMessageHook(new NavigatorSearchComposer(contextCode, searchValue));
}, []); }, []);
const linkReceived = useCallback((url: string) =>
{
const parts = url.split('/');
if(parts.length < 2) return;
switch(parts[1])
{
case 'goto':
if(parts.length > 2)
{
switch(parts[2])
{
case 'home':
//goToHomeRoom();
break;
default: {
const roomId = parseInt(parts[2]);
TryVisitRoom(roomId);
}
}
}
return;
}
}, []);
useEffect(() =>
{
const linkTracker: ILinkEventTracker = {
linkReceived,
eventUrlPrefix: 'navigator/'
};
AddEventLinkTracker(linkTracker);
return () => RemoveLinkEventTracker(linkTracker);
}, [ linkReceived]);
useEffect(() => useEffect(() =>
{ {
if(!isVisible || !needsNavigatorUpdate) return; if(!isVisible || !needsNavigatorUpdate) return;

View File

@ -1,6 +1,5 @@
import { Nitro } from 'nitro-renderer';
import { FC, useCallback, useEffect, useRef, useState } from 'react'; import { FC, useCallback, useEffect, useRef, useState } from 'react';
import { GetConfiguration } from '../../../../api'; import { GetConfiguration, GetNitroInstance } from '../../../../api';
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout'; import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout';
import { LocalizeText } from '../../../../utils/LocalizeText'; import { LocalizeText } from '../../../../utils/LocalizeText';
import { useNavigatorContext } from '../../context/NavigatorContext'; import { useNavigatorContext } from '../../context/NavigatorContext';
@ -26,7 +25,7 @@ export const NavigatorRoomLinkView: FC<NavigatorRoomLinkViewProps> = props =>
setRoomThumbnail(GetConfiguration<string>('image.library.url') + roomInfoData.enteredGuestRoom.officialRoomPicRef); setRoomThumbnail(GetConfiguration<string>('image.library.url') + roomInfoData.enteredGuestRoom.officialRoomPicRef);
} }
const roomLinkRaw = Nitro.instance.core.configuration.interpolate(LocalizeText('navigator.embed.src', ['roomId'], [roomInfoData.enteredGuestRoom.roomId.toString()])); const roomLinkRaw = GetNitroInstance().core.configuration.interpolate(LocalizeText('navigator.embed.src', ['roomId'], [roomInfoData.enteredGuestRoom.roomId.toString()]));
setRoomLink(roomLinkRaw); setRoomLink(roomLinkRaw);
}, [ roomInfoData ]); }, [ roomInfoData ]);

View File

@ -3,6 +3,7 @@
.content-area { .content-area {
min-height: 125px; min-height: 125px;
max-height: 300px;
} }
} }

View File

@ -1,69 +1,59 @@
import { UserCreditsEvent, UserCurrencyEvent, UserCurrencyUpdateEvent, UserSubscriptionEvent } from 'nitro-renderer'; import { UserCreditsEvent, UserCurrencyEvent, UserCurrencyUpdateEvent, UserSubscriptionEvent, UserSubscriptionParser } from 'nitro-renderer';
import { FC, useCallback } from 'react'; import { FC, useCallback } from 'react';
import { CreateMessageHook } from '../../hooks/messages/message-event'; import { CreateMessageHook } from '../../hooks/messages/message-event';
import { Currency } from './common/Currency';
import { usePurseContext } from './context/PurseContext'; import { usePurseContext } from './context/PurseContext';
import { PurseMessageHandlerProps } from './PurseMessageHandler.types'; import { PurseMessageHandlerProps } from './PurseMessageHandler.types';
import { PurseActions } from './reducers/PurseReducer';
export const PurseMessageHandler: FC<PurseMessageHandlerProps> = props => export const PurseMessageHandler: FC<PurseMessageHandlerProps> = props =>
{ {
const { dispatchPurseState = null } = usePurseContext(); const { purse = null } = usePurseContext();
const onUserCreditsEvent = useCallback((event: UserCreditsEvent) => const onUserCreditsEvent = useCallback((event: UserCreditsEvent) =>
{ {
const parser = event.getParser(); const parser = event.getParser();
dispatchPurseState({ purse.credits = parseFloat(parser.credits);
type: PurseActions.SET_CURRENCY,
payload: { purse.notify();
currency: { type: -1, amount: parseFloat(parser.credits) } }, [ purse ]);
}
});
}, [ dispatchPurseState ]);
const onUserCurrencyEvent = useCallback((event: UserCurrencyEvent) => const onUserCurrencyEvent = useCallback((event: UserCurrencyEvent) =>
{ {
const parser = event.getParser(); const parser = event.getParser();
const currencies: Currency[] = []; purse.activityPoints = parser.currencies;
for(const [ key, value ] of parser.currencies.entries()) currencies.push({ type: key, amount: value }); purse.notify();
}, [ purse ]);
dispatchPurseState({
type: PurseActions.SET_CURRENCIES,
payload: { currencies }
});
}, [ dispatchPurseState ]);
const onUserCurrencyUpdateEvent = useCallback((event: UserCurrencyUpdateEvent) => const onUserCurrencyUpdateEvent = useCallback((event: UserCurrencyUpdateEvent) =>
{ {
const parser = event.getParser(); const parser = event.getParser();
dispatchPurseState({ purse.activityPoints.set(parser.type, parser.amount);
type: PurseActions.SET_CURRENCY,
payload: { purse.notify();
currency: { type: parser.type, amount: parser.amount } }, [ purse ]);
}
});
}, [ dispatchPurseState ]);
const onUserSubscriptionEvent = useCallback((event: UserSubscriptionEvent) => const onUserSubscriptionEvent = useCallback((event: UserSubscriptionEvent) =>
{ {
const parser = event.getParser(); const parser = event.getParser();
switch(parser.name) const productName = parser.productName;
{
case 'habbo_club': if((productName !== 'club_habbo') && (productName !== 'habbo_club')) return;
dispatchPurseState({
type: PurseActions.SET_CLUB_SUBSCRIPTION, purse.clubDays = Math.max(0, parser.daysToPeriodEnd);
payload: { purse.clubPeriods = Math.max(0, parser.periodsSubscribedAhead);
clubSubscription: parser purse.isVip = parser.isVip;
} purse.pastClubDays = parser.pastClubDays;
}); purse.pastVipDays = parser.pastVipDays;
return; purse.isExpiring = ((parser.responseType === UserSubscriptionParser.RESPONSE_TYPE_DISCOUNT_AVAILABLE) ? true : false);
} purse.minutesUntilExpiration = parser.minutesUntilExpiration;
}, [ dispatchPurseState ]); purse.minutesSinceLastModified = parser.minutesSinceLastModified;
purse.notify();
}, [ purse ]);
CreateMessageHook(UserCreditsEvent, onUserCreditsEvent); CreateMessageHook(UserCreditsEvent, onUserCreditsEvent);
CreateMessageHook(UserCurrencyEvent, onUserCurrencyEvent); CreateMessageHook(UserCurrencyEvent, onUserCurrencyEvent);

View File

@ -1,15 +1,37 @@
.nitro-purse { .nitro-purse {
background: rgba($dark,.95); padding: 2px;
border: 1px solid lighten($dark,8.3); background-color: #1c323f;
box-shadow: inset 0px 3px lighten(rgba($dark,.6),2.5), inset 0 -2px darken(rgba($dark,.6),4); border: 2px solid rgba($white, 0.5);
font-weight: bolder; border-top: 0;
font-size: $font-size-sm;
pointer-events: all;
margin-bottom:5px;
.notification-button { .notification-button {
color:lighten($dark,20); color:lighten($dark,20);
cursor: pointer; cursor: pointer;
font-size: 0.9rem; font-size: 0.9rem;
pointer-events: all; pointer-events: all;
display: none
}
.nitro-purse-hc {
background-color: #3d5f6e;
margin:0 2px;
}
.nitro-purse-button {
background: $bg-mirage-split-background;
&:not(:first-child) {
margin-top:2px;
}
&:hover {
background: $bg-cello-split-background;
}
} }
} }
@import './currency/CurrencyView'; @import './views';

View File

@ -1,52 +1,144 @@
import { UserCurrencyComposer } from 'nitro-renderer'; import { FriendlyTime, HabboClubLevelEnum, UserCurrencyComposer, UserSubscriptionComposer } from 'nitro-renderer';
import { FC, useCallback, useEffect, useMemo, useReducer } from 'react'; import { FC, useCallback, useEffect, useMemo, useState } from 'react';
import { GetConfiguration } from '../../api'; import { GetConfiguration } from '../../api';
import { NotificationCenterEvent } from '../../events';
import { dispatchUiEvent } from '../../hooks/events';
import { SendMessageHook } from '../../hooks/messages/message-event'; import { SendMessageHook } from '../../hooks/messages/message-event';
import { SetLastCurrencies } from './common/CurrencyHelper'; import { LocalizeText } from '../../utils/LocalizeText';
import { CurrencyIcon } from '../shared/currency-icon/CurrencyIcon';
import { IPurse } from './common/IPurse';
import { Purse } from './common/Purse';
import { PurseContextProvider } from './context/PurseContext'; import { PurseContextProvider } from './context/PurseContext';
import { CurrencyView } from './currency/CurrencyView';
import { PurseMessageHandler } from './PurseMessageHandler'; import { PurseMessageHandler } from './PurseMessageHandler';
import { PurseViewProps } from './PurseView.types'; import { CurrencyView } from './views/currency/CurrencyView';
import { initialPurse, PurseReducer } from './reducers/PurseReducer'; import { SeasonalView } from './views/seasonal/SeasonalView';
export const PurseView: FC<PurseViewProps> = props => export let GLOBAL_PURSE: IPurse = null;
export const PurseView: FC<{}> = props =>
{ {
const [ purseState, dispatchPurseState ] = useReducer(PurseReducer, initialPurse); const [ purse, setPurse ] = useState<IPurse>(new Purse());
const { currencies = [] } = purseState; const [ updateId, setUpdateId ] = useState(-1);
const displayedCurrencies = useMemo(() => const displayedCurrencies = useMemo(() =>
{ {
return GetConfiguration<number[]>('system.currency.types', []); return GetConfiguration<number[]>('system.currency.types', []);
}, []); }, []);
const getCurrencyElements = useCallback((offset: number, limit: number = -1, seasonal: boolean = false) =>
{
if(!purse.activityPoints.size) return null;
const types = Array.from(purse.activityPoints.keys()).filter(type => (displayedCurrencies.indexOf(type) >= 0));
let count = 0;
while(count < offset)
{
types.shift();
count++;
}
count = 0;
const elements: JSX.Element[] = [];
for(const type of types)
{
if((limit > -1) && (count === limit)) break;
if(seasonal) elements.push(<SeasonalView key={ type } type={ type } amount={ purse.activityPoints.get(type) } />);
else elements.push(<CurrencyView key={ type } type={ type } amount={ purse.activityPoints.get(type) } />);
count++;
}
return elements;
}, [ purse, displayedCurrencies ]);
const getClubText = useCallback(() =>
{
const totalDays = ((purse.clubPeriods * 31) + purse.clubDays);
const minutesUntilExpiration = purse.minutesUntilExpiration;
if(purse.clubLevel === HabboClubLevelEnum.NO_CLUB)
{
return LocalizeText('purse.clubdays.zero.amount.text');
}
else if((minutesUntilExpiration > -1) && (minutesUntilExpiration < (60 * 24)))
{
return FriendlyTime.shortFormat(minutesUntilExpiration * 60);
}
else
{
return FriendlyTime.shortFormat(totalDays * 86400);
}
}, [ purse ]);
useEffect(() => useEffect(() =>
{ {
SendMessageHook(new UserCurrencyComposer()); const purse = new Purse();
GLOBAL_PURSE = purse;
purse.notifier = () => setUpdateId(prevValue => (prevValue + 1));
setPurse(purse);
return () => (purse.notifier = null);
}, []); }, []);
SetLastCurrencies(currencies); useEffect(() =>
const toggleNotificationCenter = useCallback(() =>
{ {
dispatchUiEvent(new NotificationCenterEvent(NotificationCenterEvent.TOGGLE_NOTIFICATION_CENTER)); if(!purse) return;
}, []);
SendMessageHook(new UserCurrencyComposer());
}, [ purse ]);
useEffect(() =>
{
SendMessageHook(new UserSubscriptionComposer('habbo_club'));
const interval = setInterval(() =>
{
SendMessageHook(new UserSubscriptionComposer('habbo_club'));
}, 50000);
return () => clearInterval(interval);
}, [ purse ]);
if(!purse) return null;
return ( return (
<PurseContextProvider value={ { purseState, dispatchPurseState }}> <PurseContextProvider value={ { purse } }>
<PurseMessageHandler /> <PurseMessageHandler />
<div className="nitro-purse rounded d-flex flex-row py-1 justify-content-between"> <div className="nitro-purse rounded-bottom d-flex flex-row justify-content-between">
{ currencies && currencies.map(currency => <div className="row mx-0 w-100">
{ <div className="col-6 px-0">
if(displayedCurrencies.indexOf(currency.type) === -1) return null; <div className="d-flex flex-column nitro-currencies">
<CurrencyView type={ -1 } amount={ purse.credits } />
return <CurrencyView key={ currency.type } currency={ currency } />; { getCurrencyElements(0, 2) }
}) } </div>
<div className="notification-button px-2" onClick={ toggleNotificationCenter }> </div>
<div className="col-4 px-0">
<div className="nitro-purse-hc p-1 d-flex flex-column justify-content-center align-items-center h-100">
<CurrencyIcon className="flex-shrink-0" type="hc" />
<span>{ getClubText() }</span>
</div>
</div>
<div className="col-2 px-0">
<div className="d-flex flex-column nitro-purse-buttons h-100 justify-content-center">
<div className="nitro-purse-button text-white h-100 text-center d-flex align-items-center justify-content-center"><i className="fas fa-life-ring"/></div>
<div className="nitro-purse-button text-white h-100 text-center d-flex align-items-center justify-content-center"><i className="fas fa-cogs"/></div>
</div>
</div>
</div>
{/*<div className="notification-button px-2" onClick={toggleNotificationCenter}>
<i className="fas fa-bars" /> <i className="fas fa-bars" />
</div>*/}
</div> </div>
</div> { getCurrencyElements(2, -1, true) }
</PurseContextProvider> </PurseContextProvider>
); );
} }

View File

@ -1,4 +0,0 @@
export interface PurseViewProps
{
}

View File

@ -1,19 +1,16 @@
import { Currency } from './Currency'; import { GLOBAL_PURSE } from '../PurseView';
let lastCurrencies: Currency[] = [];
export function SetLastCurrencies(currencies: Currency[]): void
{
lastCurrencies = currencies;
}
export function GetCurrencyAmount(type: number): number export function GetCurrencyAmount(type: number): number
{ {
for(const currency of lastCurrencies) const purse = GLOBAL_PURSE;
{
if(currency.type !== type) continue;
return currency.amount; if(type === -1) return purse.credits;
for(const [ key, value ] of purse.activityPoints.entries())
{
if(key !== type) continue;
return value;
} }
return 0; return 0;

View File

@ -0,0 +1,17 @@
export interface IPurse
{
credits: number;
activityPoints: Map<number, number>;
clubDays: number;
clubPeriods: number;
_Str_13571: boolean;
isVip: boolean;
pastClubDays: number;
pastVipDays: number;
isExpiring: boolean;
minutesUntilExpiration: number;
minutesSinceLastModified: number;
clubLevel: number;
notifier: () => void
notify(): void;
}

View File

@ -0,0 +1,163 @@
import { HabboClubLevelEnum } from 'nitro-renderer';
import { GetNitroInstance } from '../../../api';
import { IPurse } from './IPurse';
export class Purse implements IPurse
{
private _credits: number = 0;
private _activityPoints: Map<number, number> = new Map();
private _clubDays: number = 0;
private _clubPeriods: number = 0;
private _isVIP: boolean = false;
private _pastClubDays: number = 0;
private _pastVipDays: number = 0;
private _isExpiring: boolean = false;
private _minutesUntilExpiration: number = 0;
private _minutesSinceLastModified: number;
private _lastUpdated: number;
private _notifier: () => void;
public get credits(): number
{
return this._credits;
}
public set credits(credits: number)
{
this._lastUpdated = GetNitroInstance().time;
this._credits = credits;
}
public get activityPoints(): Map<number, number>
{
return this._activityPoints;
}
public set activityPoints(k: Map<number, number>)
{
this._lastUpdated = GetNitroInstance().time;
this._activityPoints = k;
}
public get clubDays(): number
{
return this._clubDays;
}
public set clubDays(k: number)
{
this._lastUpdated = GetNitroInstance().time;
this._clubDays = k;
}
public get clubPeriods(): number
{
return this._clubPeriods;
}
public set clubPeriods(k: number)
{
this._lastUpdated = GetNitroInstance().time;
this._clubPeriods = k;
}
public get _Str_13571(): boolean
{
return (this._clubDays > 0) || (this._clubPeriods > 0);
}
public get isVip(): boolean
{
return this._isVIP;
}
public set isVip(k: boolean)
{
this._isVIP = k;
}
public get pastClubDays(): number
{
return this._pastClubDays;
}
public set pastClubDays(k: number)
{
this._lastUpdated = GetNitroInstance().time;
this._pastClubDays = k;
}
public get pastVipDays(): number
{
return this._pastVipDays;
}
public set pastVipDays(k: number)
{
this._lastUpdated = GetNitroInstance().time;
this._pastVipDays = k;
}
public get isExpiring(): boolean
{
return this._isExpiring;
}
public set isExpiring(k: boolean)
{
this._isExpiring = k;
}
public get minutesUntilExpiration(): number
{
var k: number = ((GetNitroInstance().time - this._lastUpdated) / (1000 * 60));
var _local_2: number = (this._minutesUntilExpiration - k);
return (_local_2 > 0) ? _local_2 : 0;
}
public set minutesUntilExpiration(k: number)
{
this._lastUpdated = GetNitroInstance().time;
this._minutesUntilExpiration = k;
}
public get minutesSinceLastModified(): number
{
return this._minutesSinceLastModified;
}
public set minutesSinceLastModified(k: number)
{
this._lastUpdated = GetNitroInstance().time;
this._minutesSinceLastModified = k;
}
public get lastUpdated(): number
{
return this._lastUpdated;
}
public get notifier(): () => void
{
return this._notifier;
}
public set notifier(notifier: () => void)
{
this._notifier = notifier;
}
public get clubLevel(): number
{
if(((this.clubDays === 0) && (this.clubPeriods === 0))) return HabboClubLevelEnum.NO_CLUB;
if (this.isVip) return HabboClubLevelEnum.VIP;
return HabboClubLevelEnum.CLUB;
}
public notify(): void
{
if(this._notifier) this._notifier();
}
}

View File

@ -2,8 +2,7 @@ import { createContext, FC, useContext } from 'react';
import { IPurseContext, PurseContextProps } from './PurseContext.types'; import { IPurseContext, PurseContextProps } from './PurseContext.types';
const PurseContext = createContext<IPurseContext>({ const PurseContext = createContext<IPurseContext>({
purseState: null, purse: null
dispatchPurseState: null
}); });
export const PurseContextProvider: FC<PurseContextProps> = props => export const PurseContextProvider: FC<PurseContextProps> = props =>

View File

@ -1,10 +1,9 @@
import { Dispatch, ProviderProps } from 'react'; import { ProviderProps } from 'react';
import { IPurseAction, IPurseState } from '../reducers/PurseReducer'; import { IPurse } from '../common/IPurse';
export interface IPurseContext export interface IPurseContext
{ {
purseState: IPurseState; purse: IPurse;
dispatchPurseState: Dispatch<IPurseAction>;
} }
export interface PurseContextProps extends ProviderProps<IPurseContext> export interface PurseContextProps extends ProviderProps<IPurseContext>

View File

@ -1,10 +0,0 @@
.nitro-currency {
pointer-events: all;
.nitro-currency-text {
max-width: 60px;
}
&:not(:last-child) {
border-right:1px solid #000;
box-shadow: 1px 0 lighten($dark,8.3)
}
}

View File

@ -1,27 +0,0 @@
import { FC } from 'react';
import { OverlayTrigger, Tooltip } from 'react-bootstrap';
import { LocalizeShortNumber } from '../../../utils/LocalizeShortNumber';
import { CurrencyIcon } from '../../shared/currency-icon/CurrencyIcon';
import { CurrencyViewProps } from './CurrencyView.types';
export const CurrencyView: FC<CurrencyViewProps> = props =>
{
const { currency = null } = props;
return (
<OverlayTrigger
placement="left"
overlay={
<Tooltip id={`tooltip-${currency.type}`}>
{ currency.amount }
</Tooltip>
}>
<div className="nitro-currency px-1 d-flex">
<div className="px-1 text-end text-truncate nitro-currency-text">{LocalizeShortNumber(currency.amount)}</div>
<div className="icon">
<CurrencyIcon type={ currency.type } />
</div>
</div>
</OverlayTrigger>
);
}

View File

@ -1,6 +0,0 @@
import { Currency } from '../common/Currency';
export interface CurrencyViewProps
{
currency: Currency;
}

View File

@ -1,77 +0,0 @@
import { UserSubscriptionParser } from 'nitro-renderer';
import { Reducer } from 'react';
import { Currency } from '../common/Currency';
export interface IPurseState
{
currencies: Currency[];
clubSubscription: UserSubscriptionParser;
}
export interface IPurseAction
{
type: string;
payload: {
currency?: Currency;
currencies?: Currency[];
clubSubscription?: UserSubscriptionParser;
}
}
export class PurseActions
{
public static SET_CURRENCY: string = 'PA_SET_CURRENCY';
public static SET_CURRENCIES: string = 'PA_SET_CURRENCIES';
public static SET_CLUB_SUBSCRIPTION: string = 'PA_SET_CLUB_SUBSCRIPTION';
}
export const initialPurse: IPurseState = {
currencies: [],
clubSubscription: null
}
export const PurseReducer: Reducer<IPurseState, IPurseAction> = (state, action) =>
{
switch(action.type)
{
case PurseActions.SET_CURRENCY: {
const updated = action.payload.currency;
let didSet = false;
const currencies = state.currencies.map(existing =>
{
if(existing.type !== updated.type) return existing;
didSet = true;
return { ...updated };
});
if(!didSet) currencies.push({ ...updated });
return { ...state, currencies };
}
case PurseActions.SET_CURRENCIES: {
const updated = action.payload.currencies;
const currencies = state.currencies.filter(existing =>
{
if(existing.type !== -1) return null;
return existing;
});
if(updated && updated.length) currencies.push(...updated);
return { ...state, currencies };
}
case PurseActions.SET_CLUB_SUBSCRIPTION: {
const clubSubscription = action.payload.clubSubscription;
return { ...state, clubSubscription };
}
default:
return state;
}
}

View File

@ -0,0 +1,9 @@
.nitro-currency {
pointer-events: all;
background: $bg-mirage-split-background;
position: relative;
&:not(:first-of-type) {
margin-top:2px;
}
}

View File

@ -0,0 +1,25 @@
import { FC } from 'react';
import { OverlayTrigger, Tooltip } from 'react-bootstrap';
import { LocalizeShortNumber } from '../../../../utils/LocalizeShortNumber';
import { CurrencyIcon } from '../../../shared/currency-icon/CurrencyIcon';
import { CurrencyViewProps } from './CurrencyView.types';
export const CurrencyView: FC<CurrencyViewProps> = props =>
{
const { type = -1, amount = -1 } = props;
return (
<OverlayTrigger
placement="left"
overlay={
<Tooltip id={`tooltip-${ type }`}>
{ amount }
</Tooltip>
}>
<div className="nitro-currency d-flex justify-content-end nitro-purse-button">
<div className="px-1 text-end text-truncate nitro-currency-text align-self-center">{LocalizeShortNumber(amount)}</div>
<CurrencyIcon className="flex-shrink-0" type={ type } />
</div>
</OverlayTrigger>
);
}

View File

@ -1,4 +1,4 @@
export interface Currency export interface CurrencyViewProps
{ {
type: number; type: number;
amount: number; amount: number;

View File

@ -0,0 +1,2 @@
@import './currency/CurrencyView';
@import './seasonal/SeasonalView';

View File

@ -0,0 +1,16 @@
.nitro-seasonal-currency {
pointer-events: all;
padding: 2px;
background-color: #1c323f;
border: 2px solid rgba($white, 0.5);
font-size: $font-size-sm;
margin-bottom: 5px;
.nitro-currency-text {
background: $bg-mirage-split-background;
}
.nitro-seasonal-icon {
background-color: #3d5f6e
}
}

View File

@ -0,0 +1,22 @@
import { FC } from 'react';
import { LocalizeShortNumber } from '../../../../utils/LocalizeShortNumber';
import { LocalizeText } from '../../../../utils/LocalizeText';
import { CurrencyIcon } from '../../../shared/currency-icon/CurrencyIcon';
import { SeasonalViewProps } from './SeasonalView.types';
export const SeasonalView: FC<SeasonalViewProps> = props =>
{
const { type = -1, amount = -1 } = props;
return (
<div className="nitro-seasonal-currency rounded d-flex justify-content-end">
<div className="nitro-currency-text w-100 px-1 d-flex justify-content-between">
<span>{ LocalizeText(`purse.seasonal.currency.${ type }`) }</span>
<span>{ LocalizeShortNumber(amount) }</span>
</div>
<div className="nitro-seasonal-icon">
<CurrencyIcon type={ type } />
</div>
</div>
);
}

View File

@ -0,0 +1,6 @@
export interface SeasonalViewProps
{
type: number;
amount: number;
}

View File

@ -1,6 +1,6 @@
.nitro-right-side { .nitro-right-side {
position: absolute; position: absolute;
top: 10px; top: 0px;
right: 10px; right: 10px;
min-width: 200px; min-width: 200px;
max-width: 400px; max-width: 400px;

View File

@ -1,6 +1,6 @@
import { ColorConverter, Nitro, NitroAdjustmentFilter, NitroContainer, NitroSprite, NitroTexture, RoomBackgroundColorEvent, RoomEngineEvent, RoomId, RoomObjectHSLColorEnabledEvent } from 'nitro-renderer'; import { ColorConverter, NitroAdjustmentFilter, NitroContainer, NitroSprite, NitroTexture, RoomBackgroundColorEvent, RoomEngineEvent, RoomId, RoomObjectHSLColorEnabledEvent } from 'nitro-renderer';
import { FC, useCallback, useState } from 'react'; import { FC, useCallback, useState } from 'react';
import { GetRoomEngine } from '../../api'; import { GetNitroInstance, GetRoomEngine } from '../../api';
import { UseMountEffect } from '../../hooks'; import { UseMountEffect } from '../../hooks';
import { CreateEventDispatcherHook, useRoomEngineEvent } from '../../hooks/events'; import { CreateEventDispatcherHook, useRoomEngineEvent } from '../../hooks/events';
import { useRoomContext } from './context/RoomContext'; import { useRoomContext } from './context/RoomContext';
@ -48,8 +48,8 @@ export const RoomColorView: FC<{}> = props =>
if(color === undefined) color = 0x000000; if(color === undefined) color = 0x000000;
background.tint = color; background.tint = color;
background.width = Nitro.instance.width; background.width = GetNitroInstance().width;
background.height = Nitro.instance.height; background.height = GetNitroInstance().height;
}, [ getRoomBackground ]); }, [ getRoomBackground ]);
const updateRoomBackgroundColor = useCallback((hue: number, saturation: number, lightness: number, original: boolean = false) => const updateRoomBackgroundColor = useCallback((hue: number, saturation: number, lightness: number, original: boolean = false) =>

View File

@ -1,7 +1,7 @@
import { EventDispatcher, Nitro, NitroRectangle, RoomGeometry, RoomVariableEnum, Vector3d } from 'nitro-renderer'; import { EventDispatcher, NitroRectangle, RoomGeometry, RoomVariableEnum, Vector3d } from 'nitro-renderer';
import { FC, useEffect, useState } from 'react'; import { FC, useEffect, useState } from 'react';
import { createPortal } from 'react-dom'; import { createPortal } from 'react-dom';
import { InitializeRoomInstanceRenderingCanvas } from '../../api'; import { GetNitroInstance, InitializeRoomInstanceRenderingCanvas } from '../../api';
import { DispatchMouseEvent } from '../../api/nitro/room/DispatchMouseEvent'; import { DispatchMouseEvent } from '../../api/nitro/room/DispatchMouseEvent';
import { DispatchTouchEvent } from '../../api/nitro/room/DispatchTouchEvent'; import { DispatchTouchEvent } from '../../api/nitro/room/DispatchTouchEvent';
import { GetRoomEngine } from '../../api/nitro/room/GetRoomEngine'; import { GetRoomEngine } from '../../api/nitro/room/GetRoomEngine';
@ -47,11 +47,11 @@ export const RoomView: FC<RoomViewProps> = props =>
setWidgetHandler(widgetHandlerManager); setWidgetHandler(widgetHandlerManager);
Nitro.instance.renderer.resize(window.innerWidth, window.innerHeight); GetNitroInstance().renderer.resize(window.innerWidth, window.innerHeight);
const canvasId = 1; const canvasId = 1;
const displayObject = GetRoomEngine().getRoomInstanceDisplay(roomSession.roomId, canvasId, Nitro.instance.width, Nitro.instance.height, RoomGeometry.SCALE_ZOOMED_IN); const displayObject = GetRoomEngine().getRoomInstanceDisplay(roomSession.roomId, canvasId, GetNitroInstance().width, GetNitroInstance().height, RoomGeometry.SCALE_ZOOMED_IN);
if(!displayObject) return; if(!displayObject) return;
@ -77,13 +77,13 @@ export const RoomView: FC<RoomViewProps> = props =>
geometry.location = new Vector3d(x, y, z); geometry.location = new Vector3d(x, y, z);
} }
const stage = Nitro.instance.stage; const stage = GetNitroInstance().stage;
if(!stage) return; if(!stage) return;
stage.addChild(displayObject); stage.addChild(displayObject);
const canvas = Nitro.instance.renderer.view; const canvas = GetNitroInstance().renderer.view;
if(!canvas) return; if(!canvas) return;
@ -99,16 +99,16 @@ export const RoomView: FC<RoomViewProps> = props =>
window.onresize = () => window.onresize = () =>
{ {
Nitro.instance.renderer.resize(window.innerWidth, window.innerHeight); GetNitroInstance().renderer.resize(window.innerWidth, window.innerHeight);
InitializeRoomInstanceRenderingCanvas(roomSession.roomId, canvasId, Nitro.instance.width, Nitro.instance.height); InitializeRoomInstanceRenderingCanvas(roomSession.roomId, canvasId, GetNitroInstance().width, GetNitroInstance().height);
const bounds = canvas.getBoundingClientRect(); const bounds = canvas.getBoundingClientRect();
const rectangle = new NitroRectangle((bounds.x || 0), (bounds.y || 0), (bounds.width || 0), (bounds.height || 0)); const rectangle = new NitroRectangle((bounds.x || 0), (bounds.y || 0), (bounds.width || 0), (bounds.height || 0));
widgetHandlerManager.eventDispatcher.dispatchEvent(new RoomWidgetUpdateRoomViewEvent(RoomWidgetUpdateRoomViewEvent.SIZE_CHANGED, rectangle)); widgetHandlerManager.eventDispatcher.dispatchEvent(new RoomWidgetUpdateRoomViewEvent(RoomWidgetUpdateRoomViewEvent.SIZE_CHANGED, rectangle));
Nitro.instance.render(); GetNitroInstance().render();
} }
setRoomCanvas(canvas); setRoomCanvas(canvas);

View File

@ -30,4 +30,6 @@ export class RoomWidgetUpdateInfostandFurniEvent extends RoomWidgetUpdateInfosta
public purchaseCouldBeUsedForBuyout: boolean = false; public purchaseCouldBeUsedForBuyout: boolean = false;
public rentCouldBeUsedForBuyout: boolean = false; public rentCouldBeUsedForBuyout: boolean = false;
public availableForBuildersClub: boolean = false; public availableForBuildersClub: boolean = false;
public tileSizeX: number = 1;
public tileSizeY: number = 1;
} }

View File

@ -1,5 +1,5 @@
import { IFurnitureData, Nitro, NitroEvent, ObjectDataFactory, PetFigureData, PetRespectComposer, PetSupplementComposer, PetType, RoomAdsUpdateComposer, RoomControllerLevel, RoomModerationSettings, RoomObjectCategory, RoomObjectOperationType, RoomObjectType, RoomObjectVariable, RoomSessionPetInfoUpdateEvent, RoomSessionUserBadgesEvent, RoomTradingLevelEnum, RoomUnitDropHandItemComposer, RoomUnitGiveHandItemComposer, RoomUnitGiveHandItemPetComposer, RoomUserData, RoomWidgetEnum, RoomWidgetEnumItemExtradataParameter, SecurityLevel, Vector3d } from 'nitro-renderer'; import { IFurnitureData, NitroEvent, ObjectDataFactory, PetFigureData, PetRespectComposer, PetSupplementComposer, PetType, RoomAdsUpdateComposer, RoomControllerLevel, RoomModerationSettings, RoomObjectCategory, RoomObjectOperationType, RoomObjectType, RoomObjectVariable, RoomSessionPetInfoUpdateEvent, RoomSessionUserBadgesEvent, RoomTradingLevelEnum, RoomUnitDropHandItemComposer, RoomUnitGiveHandItemComposer, RoomUnitGiveHandItemPetComposer, RoomUserData, RoomWidgetEnum, RoomWidgetEnumItemExtradataParameter, SecurityLevel, Vector3d } from 'nitro-renderer';
import { GetRoomEngine, GetSessionDataManager, IsOwnerOfFurniture } from '../../../api'; import { GetNitroInstance, GetRoomEngine, GetSessionDataManager, IsOwnerOfFurniture } from '../../../api';
import { InventoryTradeRequestEvent, WiredSelectObjectEvent } from '../../../events'; import { InventoryTradeRequestEvent, WiredSelectObjectEvent } from '../../../events';
import { FriendListSendFriendRequestEvent } from '../../../events/friend-list/FriendListSendFriendRequestEvent'; import { FriendListSendFriendRequestEvent } from '../../../events/friend-list/FriendListSendFriendRequestEvent';
import { dispatchUiEvent } from '../../../hooks/events'; import { dispatchUiEvent } from '../../../hooks/events';
@ -390,6 +390,8 @@ export class RoomWidgetInfostandHandler extends RoomWidgetHandler
event.rentOfferId = furnitureData.rentOfferId; event.rentOfferId = furnitureData.rentOfferId;
event.rentCouldBeUsedForBuyout = furnitureData.rentCouldBeUsedForBuyout; event.rentCouldBeUsedForBuyout = furnitureData.rentCouldBeUsedForBuyout;
event.availableForBuildersClub = furnitureData.availableForBuildersClub; event.availableForBuildersClub = furnitureData.availableForBuildersClub;
event.tileSizeX = furnitureData.tileSizeX;
event.tileSizeY = furnitureData.tileSizeY;
dispatchUiEvent(new WiredSelectObjectEvent(event.id, event.category)); dispatchUiEvent(new WiredSelectObjectEvent(event.id, event.category));
} }
@ -400,7 +402,7 @@ export class RoomWidgetInfostandHandler extends RoomWidgetHandler
const expiryTime = model.getValue<number>(RoomObjectVariable.FURNITURE_EXPIRY_TIME); const expiryTime = model.getValue<number>(RoomObjectVariable.FURNITURE_EXPIRY_TIME);
const expiryTimestamp = model.getValue<number>(RoomObjectVariable.FURNITURE_EXPIRTY_TIMESTAMP); const expiryTimestamp = model.getValue<number>(RoomObjectVariable.FURNITURE_EXPIRTY_TIMESTAMP);
event.expiration = ((expiryTime < 0) ? expiryTime : Math.max(0, (expiryTime - ((Nitro.instance.time - expiryTimestamp) / 1000)))); event.expiration = ((expiryTime < 0) ? expiryTime : Math.max(0, (expiryTime - ((GetNitroInstance().time - expiryTimestamp) / 1000))));
let roomObjectImage = GetRoomEngine().getRoomObjectImage(roomId, message.id, message.category, new Vector3d(180), 64, null); let roomObjectImage = GetRoomEngine().getRoomObjectImage(roomId, message.id, message.category, new Vector3d(180), 64, null);

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