This commit is contained in:
Layne 2022-03-22 22:45:02 -04:00
commit 6faf81daab
32 changed files with 232 additions and 148 deletions

View File

@ -14,7 +14,7 @@
"@fortawesome/fontawesome-svg-core": "^6.1.0", "@fortawesome/fontawesome-svg-core": "^6.1.0",
"@fortawesome/free-solid-svg-icons": "^6.1.0", "@fortawesome/free-solid-svg-icons": "^6.1.0",
"@fortawesome/react-fontawesome": "^0.1.17", "@fortawesome/react-fontawesome": "^0.1.17",
"@nitrots/nitro-renderer": "^1.1.14", "@nitrots/nitro-renderer": "^1.1.15",
"animate.css": "^4.1.1", "animate.css": "^4.1.1",
"classnames": "^2.3.1", "classnames": "^2.3.1",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",

View File

@ -13,6 +13,7 @@
"user.badges.max.slots": 5, "user.badges.max.slots": 5,
"camera.publish.disabled": false, "camera.publish.disabled": false,
"hc.disabled": false, "hc.disabled": false,
"badge.descriptions.enabled": true,
"hotelview": { "hotelview": {
"show.avatar": true, "show.avatar": true,
"widgets": { "widgets": {

View File

@ -1,11 +1,13 @@
import { ConfigurationEvent, HabboWebTools, LegacyExternalInterface, Nitro, NitroCommunicationDemoEvent, NitroEvent, NitroLocalizationEvent, NitroVersion, RoomEngineEvent, WebGL } from '@nitrots/nitro-renderer'; import { ConfigurationEvent, HabboWebTools, LegacyExternalInterface, Nitro, NitroCommunicationDemoEvent, NitroEvent, NitroLocalizationEvent, NitroVersion, RoomEngineEvent, WebGL } from '@nitrots/nitro-renderer';
import { FC, useCallback, useState } from 'react'; import { FC, useCallback, useState } from 'react';
import { GetCommunication, GetConfiguration, GetNitroInstance } from './api'; import { GetCommunication, GetConfiguration, GetNitroInstance, GetUIVersion } from './api';
import { Base, TransitionAnimation, TransitionAnimationTypes } from './common'; import { Base, TransitionAnimation, TransitionAnimationTypes } from './common';
import { LoadingView } from './components/loading/LoadingView'; import { LoadingView } from './components/loading/LoadingView';
import { MainView } from './components/main/MainView'; import { MainView } from './components/main/MainView';
import { DispatchUiEvent, UseConfigurationEvent, UseLocalizationEvent, UseMainEvent, UseRoomEngineEvent } from './hooks'; import { DispatchUiEvent, UseConfigurationEvent, UseLocalizationEvent, UseMainEvent, UseRoomEngineEvent } from './hooks';
NitroVersion.UI_VERSION = GetUIVersion();
export const App: FC<{}> = props => export const App: FC<{}> = props =>
{ {
const [ isReady, setIsReady ] = useState(false); const [ isReady, setIsReady ] = useState(false);
@ -16,11 +18,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(!GetNitroInstance()) if(!GetNitroInstance()) Nitro.bootstrap();
{
NitroVersion.UI_VERSION = '2.0.0';
Nitro.bootstrap();
}
const getPreloadAssetUrls = useCallback(() => const getPreloadAssetUrls = useCallback(() =>
{ {

View File

@ -0,0 +1,3 @@
import { NitroVersion } from '@nitrots/nitro-renderer';
export const GetRendererVersion = () => NitroVersion.RENDERER_VERSION;

1
src/api/GetUIVersion.ts Normal file
View File

@ -0,0 +1 @@
export const GetUIVersion = () => '2.0.0';

View File

@ -1,6 +1,8 @@
export * from './common'; export * from './common';
export * from './core'; export * from './core';
export * from './friends'; export * from './friends';
export * from './GetRendererVersion';
export * from './GetUIVersion';
export * from './groups'; export * from './groups';
export * from './navigator'; export * from './navigator';
export * from './nitro'; export * from './nitro';

View File

@ -138,11 +138,14 @@ export class RoomWidgetChatInputHandler extends RoomWidgetHandler
newWindow.document.write(image.outerHTML); newWindow.document.write(image.outerHTML);
return null; return null;
case ':pickall': case ':pickall':
if(this.container.roomSession.isRoomOwner || GetSessionDataManager().isModerator)
{
NotificationUtilities.confirm(LocalizeText('room.confirm.pick_all'), () => NotificationUtilities.confirm(LocalizeText('room.confirm.pick_all'), () =>
{ {
GetSessionDataManager().sendSpecialCommandMessage(':pickall'); GetSessionDataManager().sendSpecialCommandMessage(':pickall');
}, },
null, null, null, LocalizeText('generic.alert.title')); null, null, null, LocalizeText('generic.alert.title'));
}
return null; return null;
case ':furni': case ':furni':
@ -171,8 +174,7 @@ export class RoomWidgetChatInputHandler extends RoomWidgetHandler
case ':client': case ':client':
case ':nitro': case ':nitro':
case ':billsonnn': case ':billsonnn':
// this.container.notificationService.alertWithScrollableMessages([ NotificationUtilities.showNitroAlert();
// '<div class="d-flex flex-column justify-content-center align-items-center"><div class="nitro-info-box"></div><b>Version: ' + Nitro.RELEASE_VERSION + '</b><br />This client is powered by Nitro HTML5<br /><br /><div class="d-flex"><a class="btn btn-primary" href="https://discord.gg/66UR68FPgy" target="_blank">Discord</a><a class="btn btn-primary" href="https://git.krews.org/nitro" target="_blank">Git</a></div><br /></div>'], 'Nitro HTML5');
return null; return null;
case ':settings': case ':settings':
if(this.container.roomSession.isRoomOwner || GetSessionDataManager().isModerator) if(this.container.roomSession.isRoomOwner || GetSessionDataManager().isModerator)

View File

@ -1,4 +1,4 @@
import { IFurnitureData, NitroEvent, ObjectDataFactory, PetFigureData, PetRespectComposer, PetSupplementComposer, PetType, RoomControllerLevel, RoomModerationSettings, RoomObjectCategory, RoomObjectOperationType, RoomObjectType, RoomObjectVariable, RoomSessionPetInfoUpdateEvent, RoomSessionUserBadgesEvent, RoomSessionUserFigureUpdateEvent, RoomTradingLevelEnum, RoomUnitDropHandItemComposer, RoomUnitGiveHandItemComposer, RoomUnitGiveHandItemPetComposer, RoomUserData, RoomWidgetEnum, RoomWidgetEnumItemExtradataParameter, Vector3d } from '@nitrots/nitro-renderer'; import { IFurnitureData, NitroEvent, ObjectDataFactory, PetFigureData, PetRespectComposer, PetSupplementComposer, PetType, RoomControllerLevel, RoomModerationSettings, RoomObjectCategory, RoomObjectOperationType, RoomObjectType, RoomObjectVariable, RoomSessionFavoriteGroupUpdateEvent, RoomSessionPetInfoUpdateEvent, RoomSessionUserBadgesEvent, RoomSessionUserFigureUpdateEvent, RoomTradingLevelEnum, RoomUnitDropHandItemComposer, RoomUnitGiveHandItemComposer, RoomUnitGiveHandItemPetComposer, RoomUserData, RoomWidgetEnum, RoomWidgetEnumItemExtradataParameter, Vector3d } from '@nitrots/nitro-renderer';
import { SendMessageComposer } from '../../..'; import { SendMessageComposer } from '../../..';
import { GetNitroInstance, GetRoomEngine, GetSessionDataManager, IsOwnerOfFurniture } from '../../../..'; import { GetNitroInstance, GetRoomEngine, GetSessionDataManager, IsOwnerOfFurniture } from '../../../..';
import { FriendsHelper } from '../../../../../components/friends/common/FriendsHelper'; import { FriendsHelper } from '../../../../../components/friends/common/FriendsHelper';
@ -23,7 +23,10 @@ export class RoomWidgetInfostandHandler extends RoomWidgetHandler
this.container.eventDispatcher.dispatchEvent(event); this.container.eventDispatcher.dispatchEvent(event);
return; return;
case RoomSessionUserFigureUpdateEvent.USER_FIGURE: case RoomSessionUserFigureUpdateEvent.USER_FIGURE:
this.processRoomSessionUserFigureUpdateEvent((event as RoomSessionUserFigureUpdateEvent)); this.container.eventDispatcher.dispatchEvent(event);
return;
case RoomSessionFavoriteGroupUpdateEvent.FAVOURITE_GROUP_UPDATE:
this.container.eventDispatcher.dispatchEvent(event);
return; return;
} }
} }
@ -528,7 +531,7 @@ export class RoomWidgetInfostandHandler extends RoomWidgetHandler
// this._Str_16287(_local_12, _local_13); // this._Str_16287(_local_12, _local_13);
} }
event.groupId = parseInt(userData.guildId); event.groupId = userData.groupId;
event.groupBadgeId = GetSessionDataManager().getGroupBadge(event.groupId); event.groupBadgeId = GetSessionDataManager().getGroupBadge(event.groupId);
event.groupName = userData.groupName; event.groupName = userData.groupName;
event.badges = this.container.roomSession.userDataManager.getUserBadges(userData.webID); event.badges = this.container.roomSession.userDataManager.getUserBadges(userData.webID);
@ -662,17 +665,6 @@ export class RoomWidgetInfostandHandler extends RoomWidgetHandler
this.container.eventDispatcher.dispatchEvent(infostandEvent); this.container.eventDispatcher.dispatchEvent(infostandEvent);
} }
private processRoomSessionUserFigureUpdateEvent(event: RoomSessionUserFigureUpdateEvent): void
{
const userData = this.container.roomSession.userDataManager.getUserDataByIndex(event.userId);
if(!userData) return;
// update active infostand figure
// update motto
// update activity points
}
private checkGuildSetting(event: RoomWidgetUpdateInfostandUserEvent): boolean private checkGuildSetting(event: RoomWidgetUpdateInfostandUserEvent): boolean
{ {
if(event.isGuildRoom) return (event.roomControllerLevel >= RoomControllerLevel.GUILD_ADMIN); if(event.isGuildRoom) return (event.roomControllerLevel >= RoomControllerLevel.GUILD_ADMIN);
@ -740,33 +732,7 @@ export class RoomWidgetInfostandHandler extends RoomWidgetHandler
if(moderation) flag = checkSetting(event, moderation); if(moderation) flag = checkSetting(event, moderation);
return (flag && (event.roomControllerLevel < RoomControllerLevel.ROOM_OWNER)); return (flag && (event.targetRoomControllerLevel < RoomControllerLevel.ROOM_OWNER));
}
private getPetType(figure: string): number
{
return this.getPetFigurePart(figure, 0);
}
private getPetBreed(figure: string): number
{
return this.getPetFigurePart(figure, 1);
}
private getPetColor(figure: string): number
{
return this.getPetFigurePart(figure, 2);
}
private getPetFigurePart(figure: string, index: number): number
{
if(!figure || !figure.length) return -1;
const parts = figure.split(' ');
if(parts.length > 0) return parseInt(parts[index]);
return -1;
} }
public get type(): string public get type(): string
@ -779,7 +745,8 @@ export class RoomWidgetInfostandHandler extends RoomWidgetHandler
return [ return [
RoomSessionPetInfoUpdateEvent.PET_INFO, RoomSessionPetInfoUpdateEvent.PET_INFO,
RoomSessionUserBadgesEvent.RSUBE_BADGES, RoomSessionUserBadgesEvent.RSUBE_BADGES,
RoomSessionUserFigureUpdateEvent.USER_FIGURE RoomSessionUserFigureUpdateEvent.USER_FIGURE,
RoomSessionFavoriteGroupUpdateEvent.FAVOURITE_GROUP_UPDATE
]; ];
} }

View File

@ -1,4 +1,4 @@
import { NotificationBubbleType } from './NotificationBubbleType'; import { NotificationAlertType } from './NotificationAlertType';
export class NotificationAlertItem export class NotificationAlertItem
{ {
@ -12,7 +12,7 @@ export class NotificationAlertItem
private _title: string; private _title: string;
private _imageUrl: string; private _imageUrl: string;
constructor(messages: string[], alertType: string = NotificationBubbleType.INFO, clickUrl: string = null, clickUrlText: string = null, title: string = null, imageUrl: string = null) constructor(messages: string[], alertType: string = NotificationAlertType.DEFAULT, clickUrl: string = null, clickUrlText: string = null, title: string = null, imageUrl: string = null)
{ {
NotificationAlertItem.ITEM_ID += 1; NotificationAlertItem.ITEM_ID += 1;

View File

@ -4,4 +4,5 @@ export class NotificationAlertType
public static MOTD: string = 'motd'; public static MOTD: string = 'motd';
public static MODERATION: string = 'moderation'; public static MODERATION: string = 'moderation';
public static EVENT: string = 'event'; public static EVENT: string = 'event';
public static NITRO: string = 'nitro';
} }

View File

@ -130,6 +130,11 @@ export class NotificationUtilities
DispatchUiEvent(new NotificationAlertEvent([ this.cleanText(message) ], type, clickUrl, clickUrlText, title, imageUrl)); DispatchUiEvent(new NotificationAlertEvent([ this.cleanText(message) ], type, clickUrl, clickUrlText, title, imageUrl));
} }
public static showNitroAlert(): void
{
DispatchUiEvent(new NotificationAlertEvent(null, NotificationAlertType.NITRO));
}
public static showModeratorMessage(message: string, url: string = null, showHabboWay: boolean = true): void public static showModeratorMessage(message: string, url: string = null, showHabboWay: boolean = true): void
{ {
this.simpleAlert(message, NotificationAlertType.MODERATION, url, LocalizeText('mod.alert.link'), LocalizeText('mod.alert.title')); this.simpleAlert(message, NotificationAlertType.MODERATION, url, LocalizeText('mod.alert.link'), LocalizeText('mod.alert.title'));

View File

@ -0,0 +1,14 @@
export const CloneObject = <T>(object: T): T =>
{
if((object == null) || ('object' != typeof object)) return object;
// @ts-ignore
const copy = new object.constructor();
for(const attr in object)
{
if(object.hasOwnProperty(attr)) copy[attr] = object[attr];
}
return copy;
}

View File

@ -1,3 +1,4 @@
export * from './CloneObject';
export * from './ColorUtils'; export * from './ColorUtils';
export * from './LocalizeBadgeDescription'; export * from './LocalizeBadgeDescription';
export * from './LocalizeBageName'; export * from './LocalizeBageName';

View File

@ -1,9 +1,9 @@
import { CSSProperties, DetailedHTMLProps, FC, HTMLAttributes, LegacyRef, useMemo } from 'react'; import { CSSProperties, DetailedHTMLProps, FC, HTMLAttributes, MutableRefObject, useMemo } from 'react';
import { ColorVariantType, DisplayType, FloatType, OverflowType, PositionType } from './types'; import { ColorVariantType, DisplayType, FloatType, OverflowType, PositionType } from './types';
export interface BaseProps<T = HTMLElement> extends DetailedHTMLProps<HTMLAttributes<T>, T> export interface BaseProps<T = HTMLElement> extends DetailedHTMLProps<HTMLAttributes<T>, T>
{ {
innerRef?: LegacyRef<T>; innerRef?: MutableRefObject<T>;
display?: DisplayType; display?: DisplayType;
fit?: boolean; fit?: boolean;
grow?: boolean; grow?: boolean;

View File

@ -1,6 +1,6 @@
import { BadgeImageReadyEvent, NitroSprite, TextureUtils } from '@nitrots/nitro-renderer'; import { BadgeImageReadyEvent, NitroSprite, TextureUtils } from '@nitrots/nitro-renderer';
import { CSSProperties, FC, useEffect, useMemo, useState } from 'react'; import { CSSProperties, FC, useEffect, useMemo, useState } from 'react';
import { GetSessionDataManager, LocalizeBadgeDescription, LocalizeBadgeName, LocalizeText } from '../../api'; import { GetConfiguration, GetSessionDataManager, LocalizeBadgeDescription, LocalizeBadgeName, LocalizeText } from '../../api';
import { Base, BaseProps } from '../Base'; import { Base, BaseProps } from '../Base';
export interface LayoutBadgeImageViewProps extends BaseProps<HTMLDivElement> export interface LayoutBadgeImageViewProps extends BaseProps<HTMLDivElement>
@ -89,6 +89,8 @@ export const LayoutBadgeImageView: FC<LayoutBadgeImageViewProps> = props =>
{ {
const { title = null, description = null } = props; const { title = null, description = null } = props;
if(!GetConfiguration('badge.descriptions.enabled', true)) return null;
return ( return (
<Base className="badge-information text-black py-1 px-2 small"> <Base className="badge-information text-black py-1 px-2 small">
<div className="fw-bold mb-1">{ title }</div> <div className="fw-bold mb-1">{ title }</div>

View File

@ -1,6 +1,6 @@
import { SetActivatedBadgesComposer } from '@nitrots/nitro-renderer'; import { SetActivatedBadgesComposer } from '@nitrots/nitro-renderer';
import { Reducer } from 'react'; import { Reducer } from 'react';
import { SendMessageComposer } from '../../../api'; import { GetConfiguration, SendMessageComposer } from '../../../api';
export interface IInventoryBadgeState export interface IInventoryBadgeState
{ {
@ -97,10 +97,7 @@ export const InventoryBadgeReducer: Reducer<IInventoryBadgeState, IInventoryBadg
const composer = new SetActivatedBadgesComposer(); const composer = new SetActivatedBadgesComposer();
for(const badgeCode of activeBadges) for(let i = 0; i < GetConfiguration<number>('user.badges.max.slots', 5); i++) composer.addActivatedBadge(activeBadges[i] || null);
{
composer.addActivatedBadge(badgeCode);
}
SendMessageComposer(composer); SendMessageComposer(composer);
@ -119,10 +116,7 @@ export const InventoryBadgeReducer: Reducer<IInventoryBadgeState, IInventoryBadg
const composer = new SetActivatedBadgesComposer(); const composer = new SetActivatedBadgesComposer();
for(const badgeCode of activeBadges) for(let i = 0; i < GetConfiguration<number>('user.badges.max.slots', 5); i++) composer.addActivatedBadge(activeBadges[i] || null);
{
composer.addActivatedBadge(badgeCode);
}
SendMessageComposer(composer); SendMessageComposer(composer);

View File

@ -72,7 +72,7 @@ export const InventoryBadgeView: FC<InventoryBadgeViewProps> = props =>
return ( return (
<Grid> <Grid>
<Column size={ 7 } overflow="hidden"> <Column size={ 7 } overflow="hidden">
<AutoGrid columnCount={ 5 }> <AutoGrid columnCount={ 4 }>
{ badges && (badges.length > 0) && badges.map((code, index) => { badges && (badges.length > 0) && badges.map((code, index) =>
{ {
if(activeBadges.indexOf(code) >= 0) return null; if(activeBadges.indexOf(code) >= 0) return null;

View File

@ -1,3 +1,4 @@
import { NitroLogger } from '@nitrots/nitro-renderer';
import { FC, useCallback, useEffect, useRef, useState } from 'react'; import { FC, useCallback, useEffect, useRef, useState } from 'react';
import { AddEventLinkTracker, GetConfiguration, NotificationUtilities, RemoveLinkEventTracker } from '../../api'; import { AddEventLinkTracker, GetConfiguration, NotificationUtilities, RemoveLinkEventTracker } from '../../api';
import { Base, NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../common'; import { Base, NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../common';
@ -12,6 +13,8 @@ export const NitropediaView: FC<{}> = props =>
const elementRef = useRef<HTMLDivElement>(null); const elementRef = useRef<HTMLDivElement>(null);
const openPage = useCallback(async (link: string) => const openPage = useCallback(async (link: string) =>
{
try
{ {
const response = await fetch(link); const response = await fetch(link);
@ -26,6 +29,12 @@ export const NitropediaView: FC<{}> = props =>
setHeader(splitData.shift()); setHeader(splitData.shift());
setContent(splitData.join('')); setContent(splitData.join(''));
}); });
}
catch (error)
{
NitroLogger.error(`Failed to fetch ${ link }`);
}
}, []); }, []);
const onLinkReceived = useCallback((link: string) => const onLinkReceived = useCallback((link: string) =>

View File

@ -1,4 +1,5 @@
import { NotificationAlertItem } from '../../../../api'; import { NotificationAlertItem, NotificationAlertType } from '../../../../api';
import { NitroSystemAlertView } from './NitroSystemAlertView';
import { NotificationDefaultAlertView } from './NotificationDefaultAlertView'; import { NotificationDefaultAlertView } from './NotificationDefaultAlertView';
export const GetAlertLayout = (item: NotificationAlertItem, close: () => void) => export const GetAlertLayout = (item: NotificationAlertItem, close: () => void) =>
@ -7,5 +8,11 @@ export const GetAlertLayout = (item: NotificationAlertItem, close: () => void) =
const props = { key: item.id, item, close }; const props = { key: item.id, item, close };
switch(item.alertType)
{
case NotificationAlertType.NITRO:
return <NitroSystemAlertView { ...props } />
default:
return <NotificationDefaultAlertView { ...props } /> return <NotificationDefaultAlertView { ...props } />
} }
}

View File

@ -0,0 +1,39 @@
import { FC } from 'react';
import { GetRendererVersion, GetUIVersion, NotificationAlertItem } from '../../../../api';
import { Button, Column, Flex, Grid, LayoutNotificationAlertView, LayoutNotificationAlertViewProps, Text } from '../../../../common';
interface NotificationDefaultAlertViewProps extends LayoutNotificationAlertViewProps
{
item: NotificationAlertItem;
}
export const NitroSystemAlertView: FC<NotificationDefaultAlertViewProps> = props =>
{
const { title = 'Nitro', close = null, ...rest } = props;
return (
<LayoutNotificationAlertView title={ title } close={ close } { ...rest }>
<Grid>
<Column center size={ 5 }>
<object data="https://assets.nitrodev.co/logos/nitro-n-dark.svg" width="100" height="100">&nbsp;</object>
</Column>
<Column size={ 7 }>
<Column alignItems="center" gap={ 0 }>
<Text bold fontSize={ 4 }>Nitro React</Text>
<Text>v{ GetUIVersion() }</Text>
</Column>
<Column alignItems="center">
<Text><b>Renderer:</b> v{ GetRendererVersion() }</Text>
<Column fullWidth gap={ 1 }>
<Button fullWidth variant="success" onClick={ event => window.open('https://nitrots.co/discord') }>Discord</Button>
<Flex gap={ 1 }>
<Button fullWidth onClick={ event => window.open('https://git.krews.org/nitro/nitro-react') }>Git</Button>
<Button fullWidth onClick={ event => window.open('https://git.krews.org/nitro/nitro-react/-/issues') }>Bug Report</Button>
</Flex>
</Column>
</Column>
</Column>
</Grid>
</LayoutNotificationAlertView>
);
}

View File

@ -1,5 +1,5 @@
import { FC } from 'react'; import { FC } from 'react';
import { NotificationBubbleItem } from '../../../../api'; import { NotificationBubbleItem, NotificationUtilities } from '../../../../api';
import { Flex, LayoutNotificationBubbleView, LayoutNotificationBubbleViewProps, Text } from '../../../../common'; import { Flex, LayoutNotificationBubbleView, LayoutNotificationBubbleViewProps, Text } from '../../../../common';
export interface NotificationDefaultBubbleViewProps extends LayoutNotificationBubbleViewProps export interface NotificationDefaultBubbleViewProps extends LayoutNotificationBubbleViewProps
@ -14,7 +14,7 @@ export const NotificationDefaultBubbleView: FC<NotificationDefaultBubbleViewProp
const htmlText = item.message.replace(/\r\n|\r|\n/g, '<br />'); const htmlText = item.message.replace(/\r\n|\r|\n/g, '<br />');
return ( return (
<LayoutNotificationBubbleView close={ close } gap={ 2 } alignItems="center" { ...rest }> <LayoutNotificationBubbleView close={ close } gap={ 2 } alignItems="center" onClick={ event => (item.linkUrl && item.linkUrl.length && NotificationUtilities.openUrl(item.linkUrl)) } { ...rest }>
<Flex center className="bubble-image-container"> <Flex center className="bubble-image-container">
{ (item.iconUrl && item.iconUrl.length) && { (item.iconUrl && item.iconUrl.length) &&
<img className="no-select" src={ item.iconUrl } alt="" /> } <img className="no-select" src={ item.iconUrl } alt="" /> }

View File

@ -121,7 +121,7 @@ export const RoomView: FC<RoomViewProps> = props =>
GetNitroInstance().render(); GetNitroInstance().render();
} }
if(elementRef && elementRef.current) elementRef.current.appendChild(canvas); if(elementRef && elementRef.current) elementRef.current.replaceChildren(canvas);
setCanvasId(canvasId); setCanvasId(canvasId);
}, [ roomSession ]); }, [ roomSession ]);

View File

@ -1,4 +1,4 @@
import { RoomEngineEvent, RoomEngineObjectEvent, RoomEngineRoomAdEvent, RoomEngineTriggerWidgetEvent, RoomEngineUseProductEvent, RoomId, RoomObjectCategory, RoomObjectOperationType, RoomObjectVariable, RoomSessionChatEvent, RoomSessionDanceEvent, RoomSessionDimmerPresetsEvent, RoomSessionDoorbellEvent, RoomSessionErrorMessageEvent, RoomSessionEvent, RoomSessionFriendRequestEvent, RoomSessionPetInfoUpdateEvent, RoomSessionPetStatusUpdateEvent, RoomSessionPollEvent, RoomSessionPresentEvent, RoomSessionUserBadgesEvent, RoomSessionUserFigureUpdateEvent, RoomSessionWordQuizEvent, RoomZoomEvent } from '@nitrots/nitro-renderer'; import { RoomEngineEvent, RoomEngineObjectEvent, RoomEngineRoomAdEvent, RoomEngineTriggerWidgetEvent, RoomEngineUseProductEvent, RoomId, RoomObjectCategory, RoomObjectOperationType, RoomObjectVariable, RoomSessionChatEvent, RoomSessionDanceEvent, RoomSessionDimmerPresetsEvent, RoomSessionDoorbellEvent, RoomSessionErrorMessageEvent, RoomSessionEvent, RoomSessionFavoriteGroupUpdateEvent, RoomSessionFriendRequestEvent, RoomSessionPetInfoUpdateEvent, RoomSessionPetStatusUpdateEvent, RoomSessionPollEvent, RoomSessionPresentEvent, RoomSessionUserBadgesEvent, RoomSessionUserFigureUpdateEvent, RoomSessionWordQuizEvent, RoomZoomEvent } from '@nitrots/nitro-renderer';
import { FC, useCallback } from 'react'; import { FC, useCallback } from 'react';
import { CanManipulateFurniture, GetRoomEngine, GetSessionDataManager, IsFurnitureSelectionDisabled, LocalizeText, NotificationAlertType, NotificationUtilities, ProcessRoomObjectOperation, RoomWidgetFurniToWidgetMessage, RoomWidgetUpdateRoomEngineEvent, RoomWidgetUpdateRoomObjectEvent } from '../../../api'; import { CanManipulateFurniture, GetRoomEngine, GetSessionDataManager, IsFurnitureSelectionDisabled, LocalizeText, NotificationAlertType, NotificationUtilities, ProcessRoomObjectOperation, RoomWidgetFurniToWidgetMessage, RoomWidgetUpdateRoomEngineEvent, RoomWidgetUpdateRoomObjectEvent } from '../../../api';
import { FriendRequestEvent } from '../../../events'; import { FriendRequestEvent } from '../../../events';
@ -263,6 +263,7 @@ export const RoomWidgetsView: FC<{}> = props =>
UseRoomSessionManagerEvent(RoomSessionDanceEvent.RSDE_DANCE, onRoomSessionEvent); UseRoomSessionManagerEvent(RoomSessionDanceEvent.RSDE_DANCE, onRoomSessionEvent);
UseRoomSessionManagerEvent(RoomSessionUserBadgesEvent.RSUBE_BADGES, onRoomSessionEvent); UseRoomSessionManagerEvent(RoomSessionUserBadgesEvent.RSUBE_BADGES, onRoomSessionEvent);
UseRoomSessionManagerEvent(RoomSessionUserFigureUpdateEvent.USER_FIGURE, onRoomSessionEvent); UseRoomSessionManagerEvent(RoomSessionUserFigureUpdateEvent.USER_FIGURE, onRoomSessionEvent);
UseRoomSessionManagerEvent(RoomSessionFavoriteGroupUpdateEvent.FAVOURITE_GROUP_UPDATE, onRoomSessionEvent);
UseRoomSessionManagerEvent(RoomSessionPetStatusUpdateEvent.PET_STATUS_UPDATE, onRoomSessionEvent); UseRoomSessionManagerEvent(RoomSessionPetStatusUpdateEvent.PET_STATUS_UPDATE, onRoomSessionEvent);
UseRoomSessionManagerEvent(RoomSessionDoorbellEvent.DOORBELL, onRoomSessionEvent); UseRoomSessionManagerEvent(RoomSessionDoorbellEvent.DOORBELL, onRoomSessionEvent);
UseRoomSessionManagerEvent(RoomSessionDoorbellEvent.RSDE_REJECTED, onRoomSessionEvent); UseRoomSessionManagerEvent(RoomSessionDoorbellEvent.RSDE_REJECTED, onRoomSessionEvent);

View File

@ -199,7 +199,6 @@ export const AvatarInfoWidgetAvatarView: FC<AvatarInfoWidgetAvatarViewProps> = p
break; break;
case 'rship_none': case 'rship_none':
messageType = RoomWidgetUserActionMessage.RELATIONSHIP_NONE; messageType = RoomWidgetUserActionMessage.RELATIONSHIP_NONE;
console.log('here')
break; break;
} }

View File

@ -1,6 +1,4 @@
.nitro-context-menu { .nitro-context-menu {
width: 125px;
max-width: 125px;
padding: 2px; padding: 2px;
background-color: $gable-green; background-color: $gable-green;
border: 2px solid rgba($white, 0.5); border: 2px solid rgba($white, 0.5);
@ -22,7 +20,10 @@
font-size: 18px; font-size: 18px;
} }
&:not(.name-only):after { &:not(.name-only) {
min-width: 125px;
&:after {
content: ""; content: "";
position: absolute; position: absolute;
bottom: -7px; bottom: -7px;
@ -36,6 +37,7 @@
border-style: solid; border-style: solid;
border-width: 5px; border-width: 5px;
} }
}
.menu-header { .menu-header {
background-color: $william; background-color: $william;

View File

@ -23,11 +23,7 @@ export const FurnitureBackgroundColorView: FC<{}> = props =>
const canOpenBackgroundToner = useCallback(() => const canOpenBackgroundToner = useCallback(() =>
{ {
const isRoomOwner = roomSession.isRoomOwner; return (roomSession.isRoomOwner || (roomSession.controllerLevel >= RoomControllerLevel.GUEST) || GetSessionDataManager().isModerator);
const hasLevel = (roomSession.controllerLevel >= RoomControllerLevel.GUEST);
const isGodMode = GetSessionDataManager().isGodMode;
return (isRoomOwner || hasLevel || isGodMode);
}, [ roomSession ]); }, [ roomSession ]);
const onRoomEngineObjectEvent = useCallback((event: RoomEngineObjectEvent) => const onRoomEngineObjectEvent = useCallback((event: RoomEngineObjectEvent) =>

View File

@ -30,7 +30,7 @@ export const InfoStandWidgetUserRelationshipsView: FC<InfoStandWidgetUserRelatio
<i className={`nitro-friends-spritesheet icon-${relationshipName}`} /> <i className={`nitro-friends-spritesheet icon-${relationshipName}`} />
<Flex alignItems="center" gap={ 0 }> <Flex alignItems="center" gap={ 0 }>
<Text small variant="white" onClick={ event => GetUserProfile(relationshipInfo.randomFriendId) }> <Text small variant="white" onClick={ event => GetUserProfile(relationshipInfo.randomFriendId) }>
{ relationshipInfo.randomFriendName } <u>{ relationshipInfo.randomFriendName }</u>
{ (relationshipInfo.friendCount > 1) && (' ' + LocalizeText(`extendedprofile.relstatus.others.${ relationshipName }`, [ 'count' ], [ (relationshipInfo.friendCount - 1).toString() ])) } { (relationshipInfo.friendCount > 1) && (' ' + LocalizeText(`extendedprofile.relstatus.others.${ relationshipName }`, [ 'count' ], [ (relationshipInfo.friendCount - 1).toString() ])) }
</Text> </Text>
</Flex> </Flex>

View File

@ -1,7 +1,7 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { RelationshipStatusInfoEvent, RelationshipStatusInfoMessageParser, RoomSessionUserBadgesEvent, UserRelationshipsComposer } from '@nitrots/nitro-renderer'; import { RelationshipStatusInfoEvent, RelationshipStatusInfoMessageParser, RoomSessionFavoriteGroupUpdateEvent, RoomSessionUserBadgesEvent, RoomSessionUserFigureUpdateEvent, UserRelationshipsComposer } from '@nitrots/nitro-renderer';
import { FC, FocusEvent, KeyboardEvent, useCallback, useEffect, useState } from 'react'; import { Dispatch, FC, FocusEvent, KeyboardEvent, SetStateAction, useCallback, useEffect, useState } from 'react';
import { GetConfiguration, GetGroupInformation, LocalizeText, RoomWidgetChangeMottoMessage, RoomWidgetUpdateInfostandUserEvent, SendMessageComposer } from '../../../../api'; import { CloneObject, GetConfiguration, GetGroupInformation, GetSessionDataManager, LocalizeText, RoomWidgetChangeMottoMessage, RoomWidgetUpdateInfostandUserEvent, SendMessageComposer } from '../../../../api';
import { Base, Column, Flex, LayoutAvatarImageView, LayoutBadgeImageView, Text, UserProfileIconView } from '../../../../common'; import { Base, Column, Flex, LayoutAvatarImageView, LayoutBadgeImageView, Text, UserProfileIconView } from '../../../../common';
import { BatchUpdates, UseEventDispatcherHook, UseMessageEventHook } from '../../../../hooks'; import { BatchUpdates, UseEventDispatcherHook, UseMessageEventHook } from '../../../../hooks';
import { useRoomContext } from '../../RoomContext'; import { useRoomContext } from '../../RoomContext';
@ -10,23 +10,22 @@ import { InfoStandWidgetUserRelationshipsView } from './InfoStandWidgetUserRelat
interface InfoStandWidgetUserViewProps interface InfoStandWidgetUserViewProps
{ {
userData: RoomWidgetUpdateInfostandUserEvent; userData: RoomWidgetUpdateInfostandUserEvent;
setUserData: Dispatch<SetStateAction<RoomWidgetUpdateInfostandUserEvent>>;
close: () => void; close: () => void;
} }
export const InfoStandWidgetUserView: FC<InfoStandWidgetUserViewProps> = props => export const InfoStandWidgetUserView: FC<InfoStandWidgetUserViewProps> = props =>
{ {
const { userData = null, close = null } = props; const { userData = null, setUserData = null, close = null } = props;
const { eventDispatcher = null, widgetHandler = null } = useRoomContext(); const [ motto, setMotto ] = useState<string>(null);
const [ badges, setBadges ] = useState<string[]>([]);
const [ motto, setMotto ] = useState(null);
const [ isEditingMotto, setIsEditingMotto ] = useState(false); const [ isEditingMotto, setIsEditingMotto ] = useState(false);
const [ userRelationships, setUserRelationships ] = useState<RelationshipStatusInfoMessageParser>(null); const [ relationships, setRelationships ] = useState<RelationshipStatusInfoMessageParser>(null);
const { eventDispatcher = null, widgetHandler = null } = useRoomContext();
const maxBadgeCount = GetConfiguration<number>('user.badges.max.slots', 5); const maxBadgeCount = GetConfiguration<number>('user.badges.max.slots', 5);
const saveMotto = (motto: string) => const saveMotto = (motto: string) =>
{ {
if(motto.length > 38) return; if(!isEditingMotto || (motto.length > 38)) return;
widgetHandler.processWidgetMessage(new RoomWidgetChangeMottoMessage(motto)); widgetHandler.processWidgetMessage(new RoomWidgetChangeMottoMessage(motto));
@ -51,18 +50,63 @@ export const InfoStandWidgetUserView: FC<InfoStandWidgetUserViewProps> = props =
{ {
if(!userData || (userData.webID !== event.userId)) return; if(!userData || (userData.webID !== event.userId)) return;
setBadges(event.badges); setUserData(prevValue =>
}, [ userData ]); {
const newValue = CloneObject(prevValue);
newValue.badges = event.badges;
return newValue;
});
}, [ userData, setUserData ]);
UseEventDispatcherHook(RoomSessionUserBadgesEvent.RSUBE_BADGES, eventDispatcher, onRoomSessionUserBadgesEvent); UseEventDispatcherHook(RoomSessionUserBadgesEvent.RSUBE_BADGES, eventDispatcher, onRoomSessionUserBadgesEvent);
const onRoomSessionUserFigureUpdateEvent = useCallback((event: RoomSessionUserFigureUpdateEvent) =>
{
if(!userData || (userData.roomIndex !== event.roomIndex)) return;
setUserData(prevValue =>
{
const newValue = CloneObject(prevValue);
newValue.figure = event.figure;
newValue.motto = event.customInfo;
newValue.achievementScore = event.activityPoints;
return newValue;
});
}, [ userData, setUserData ]);
UseEventDispatcherHook(RoomSessionUserFigureUpdateEvent.USER_FIGURE, eventDispatcher, onRoomSessionUserFigureUpdateEvent);
const onRoomSessionFavoriteGroupUpdateEvent = useCallback((event: RoomSessionFavoriteGroupUpdateEvent) =>
{
if(!userData || (userData.roomIndex !== event.roomIndex)) return;
setUserData(prevValue =>
{
const newValue = CloneObject(prevValue);
const clearGroup = ((event.status === -1) || (event.habboGroupId <= 0));
newValue.groupId = clearGroup ? -1 : event.habboGroupId;
newValue.groupName = clearGroup ? null : event.habboGroupName
newValue.groupBadgeId = clearGroup ? null : GetSessionDataManager().getGroupBadge(event.habboGroupId);
return newValue;
});
}, [ userData, setUserData ]);
UseEventDispatcherHook(RoomSessionFavoriteGroupUpdateEvent.FAVOURITE_GROUP_UPDATE, eventDispatcher, onRoomSessionFavoriteGroupUpdateEvent);
const onUserRelationshipsEvent = useCallback((event: RelationshipStatusInfoEvent) => const onUserRelationshipsEvent = useCallback((event: RelationshipStatusInfoEvent) =>
{ {
const parser = event.getParser(); const parser = event.getParser();
if(!userData || (userData.webID !== parser.userId)) return; if(!userData || (userData.webID !== parser.userId)) return;
setUserRelationships(parser); setRelationships(parser);
}, [ userData ]); }, [ userData ]);
UseMessageEventHook(RelationshipStatusInfoEvent, onUserRelationshipsEvent); UseMessageEventHook(RelationshipStatusInfoEvent, onUserRelationshipsEvent);
@ -71,7 +115,6 @@ export const InfoStandWidgetUserView: FC<InfoStandWidgetUserViewProps> = props =
{ {
BatchUpdates(() => BatchUpdates(() =>
{ {
setBadges(userData.badges);
setIsEditingMotto(false); setIsEditingMotto(false);
setMotto(userData.motto); setMotto(userData.motto);
}); });
@ -80,8 +123,12 @@ export const InfoStandWidgetUserView: FC<InfoStandWidgetUserViewProps> = props =
return () => return () =>
{ {
setBadges([]); BatchUpdates(() =>
setUserRelationships(null); {
setIsEditingMotto(false);
setMotto(null);
setRelationships(null);
});
} }
}, [ userData ]); }, [ userData ]);
@ -108,7 +155,7 @@ export const InfoStandWidgetUserView: FC<InfoStandWidgetUserViewProps> = props =
<Column grow gap={ 0 }> <Column grow gap={ 0 }>
<Flex gap={ 1 }> <Flex gap={ 1 }>
<Base className="badge-image"> <Base className="badge-image">
{ badges[0] && <LayoutBadgeImageView badgeCode={ badges[0] } showInfo={ true } /> } { userData.badges[0] && <LayoutBadgeImageView badgeCode={ userData.badges[0] } showInfo={ true } /> }
</Base> </Base>
<Base pointer={ ( userData.groupId > 0) } className="badge-image" onClick={ event => GetGroupInformation(userData.groupId) }> <Base pointer={ ( userData.groupId > 0) } className="badge-image" onClick={ event => GetGroupInformation(userData.groupId) }>
{ userData.groupId > 0 && { userData.groupId > 0 &&
@ -117,18 +164,18 @@ export const InfoStandWidgetUserView: FC<InfoStandWidgetUserViewProps> = props =
</Flex> </Flex>
<Flex gap={ 1 }> <Flex gap={ 1 }>
<Base className="badge-image"> <Base className="badge-image">
{ badges[1] && <LayoutBadgeImageView badgeCode={ badges[1] } showInfo={ true } /> } { userData.badges[1] && <LayoutBadgeImageView badgeCode={ userData.badges[1] } showInfo={ true } /> }
</Base> </Base>
<Base className="badge-image"> <Base className="badge-image">
{ badges[2] && <LayoutBadgeImageView badgeCode={ badges[2] } showInfo={ true } /> } { userData.badges[2] && <LayoutBadgeImageView badgeCode={ userData.badges[2] } showInfo={ true } /> }
</Base> </Base>
</Flex> </Flex>
<Flex gap={ 1 }> <Flex gap={ 1 }>
<Base className="badge-image"> <Base className="badge-image">
{ badges[3] && <LayoutBadgeImageView badgeCode={ badges[3] } showInfo={ true } /> } { userData.badges[3] && <LayoutBadgeImageView badgeCode={ userData.badges[3] } showInfo={ true } /> }
</Base> </Base>
<Base className="badge-image"> <Base className="badge-image">
{ badges[4] && <LayoutBadgeImageView badgeCode={ badges[4] } showInfo={ true } /> } { userData.badges[4] && <LayoutBadgeImageView badgeCode={ userData.badges[4] } showInfo={ true } /> }
</Base> </Base>
</Flex> </Flex>
</Column> </Column>
@ -167,7 +214,7 @@ export const InfoStandWidgetUserView: FC<InfoStandWidgetUserViewProps> = props =
</> } </> }
</Column> </Column>
<Column gap={ 1 }> <Column gap={ 1 }>
<InfoStandWidgetUserRelationshipsView relationships={ userRelationships } /> <InfoStandWidgetUserRelationshipsView relationships={ relationships } />
</Column> </Column>
</Column> </Column>
</Column> </Column>

View File

@ -91,6 +91,7 @@ export const InfoStandWidgetView: FC<{}> = props =>
UseEventDispatcherHook(RoomWidgetUpdateInfostandUserEvent.BOT, eventDispatcher, onRoomWidgetUpdateEvent); UseEventDispatcherHook(RoomWidgetUpdateInfostandUserEvent.BOT, eventDispatcher, onRoomWidgetUpdateEvent);
UseEventDispatcherHook(RoomWidgetUpdateInfostandRentableBotEvent.RENTABLE_BOT, eventDispatcher, onRoomWidgetUpdateEvent); UseEventDispatcherHook(RoomWidgetUpdateInfostandRentableBotEvent.RENTABLE_BOT, eventDispatcher, onRoomWidgetUpdateEvent);
UseEventDispatcherHook(RoomWidgetUpdateInfostandPetEvent.PET_INFO, eventDispatcher, onRoomWidgetUpdateEvent); UseEventDispatcherHook(RoomWidgetUpdateInfostandPetEvent.PET_INFO, eventDispatcher, onRoomWidgetUpdateEvent);
UseEventDispatcherHook(RoomWidgetUpdateInfostandPetEvent.PET_INFO, eventDispatcher, onRoomWidgetUpdateEvent);
const getInfostandView = useCallback(() => const getInfostandView = useCallback(() =>
{ {
@ -102,7 +103,7 @@ export const InfoStandWidgetView: FC<{}> = props =>
return <InfoStandWidgetFurniView furniData={ (infoStandEvent as RoomWidgetUpdateInfostandFurniEvent) } close={ closeInfostand } />; return <InfoStandWidgetFurniView furniData={ (infoStandEvent as RoomWidgetUpdateInfostandFurniEvent) } close={ closeInfostand } />;
case RoomWidgetUpdateInfostandUserEvent.OWN_USER: case RoomWidgetUpdateInfostandUserEvent.OWN_USER:
case RoomWidgetUpdateInfostandUserEvent.PEER: case RoomWidgetUpdateInfostandUserEvent.PEER:
return <InfoStandWidgetUserView userData={ (infoStandEvent as RoomWidgetUpdateInfostandUserEvent) } close={ closeInfostand } />; return <InfoStandWidgetUserView userData={ (infoStandEvent as RoomWidgetUpdateInfostandUserEvent) } setUserData={ setInfoStandEvent} close={ closeInfostand } />;
case RoomWidgetUpdateInfostandUserEvent.BOT: case RoomWidgetUpdateInfostandUserEvent.BOT:
return <InfoStandWidgetBotView botData={ (infoStandEvent as RoomWidgetUpdateInfostandUserEvent) } close={ closeInfostand } />; return <InfoStandWidgetBotView botData={ (infoStandEvent as RoomWidgetUpdateInfostandUserEvent) } close={ closeInfostand } />;
case RoomWidgetUpdateInfostandRentableBotEvent.RENTABLE_BOT: case RoomWidgetUpdateInfostandRentableBotEvent.RENTABLE_BOT:

View File

@ -66,11 +66,6 @@ export const RoomToolsWidgetView: FC<{}> = props =>
return () => clearTimeout(timeout); return () => clearTimeout(timeout);
}, [ roomName, roomOwner, roomTags ]); }, [ roomName, roomOwner, roomTags ]);
useEffect(() =>
{
console.log(navigatorData);
}, [ navigatorData ]);
return ( return (
<Flex className="nitro-room-tools-container" gap={ 2 }> <Flex className="nitro-room-tools-container" gap={ 2 }>
<Column center className="nitro-room-tools p-2"> <Column center className="nitro-room-tools p-2">

View File

@ -8,7 +8,4 @@ import './index.scss';
//@ts-ignore //@ts-ignore
library.add(fas); library.add(fas);
ReactDOM.render( ReactDOM.render(<App />, document.getElementById('root'));
<App />,
document.getElementById('root')
);

View File

@ -1426,10 +1426,10 @@
"@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/resolve-uri" "^3.0.3"
"@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/sourcemap-codec" "^1.4.10"
"@nitrots/nitro-renderer@^1.1.14": "@nitrots/nitro-renderer@^1.1.15":
version "1.1.14" version "1.1.15"
resolved "https://registry.yarnpkg.com/@nitrots/nitro-renderer/-/nitro-renderer-1.1.14.tgz#30c792572f4df26e85efab0342894bfeb10c3cfa" resolved "https://registry.yarnpkg.com/@nitrots/nitro-renderer/-/nitro-renderer-1.1.15.tgz#cdb670680685a24ae8ca28a424ed3d00988cdc67"
integrity sha512-P4ruZ7eJ169q/pzsTN/AYf5pqr4ZoTCefvsAGHON4VSFqUO1vIivqqNmTgWJlBHMukj2VqrbLZukYrbt0+cUCQ== integrity sha512-fXFWyaioHVTSbm6zsxKq8btVou8CXQ5gIPIxQbyJZVnBqZpkp7nFn3UPoTWnD6d/YPHbH1keyIsMEfTo6zztvg==
dependencies: dependencies:
"@pixi/canvas-renderer" "^6.2.2" "@pixi/canvas-renderer" "^6.2.2"
"@pixi/extract" "^6.2.2" "@pixi/extract" "^6.2.2"