Context menu updates

This commit is contained in:
Bill 2021-06-26 13:32:32 -04:00
parent d601507d38
commit 64198100a1
14 changed files with 724 additions and 135 deletions

View File

@ -10,5 +10,5 @@ export function GetCanUseExpression(): boolean
const model = roomObject.model;
const effectId = model.getValue<number>(RoomObjectVariable.FIGURE_EFFECT);
return ((effectId === 29) || (effectId === 30) || (effectId === 185));
return !((effectId === 29) || (effectId === 30) || (effectId === 185));
}

View File

@ -102,6 +102,8 @@ export const AvatarInfoWidgetView: FC<AvatarInfoWidgetViewProps> = props =>
{
if(!infoStandEvent) return;
console.log('tru')
setInfoStandEvent(null);
}, [ infoStandEvent ]);
@ -119,7 +121,9 @@ export const AvatarInfoWidgetView: FC<AvatarInfoWidgetViewProps> = props =>
const onRoomWidgetUpdateInfostandEvent = useCallback((event: RoomWidgetUpdateInfostandEvent) =>
{
if(name) setName(null);
setInfoStandEvent(event);
if(event.type === RoomWidgetUpdateInfostandFurniEvent.FURNI) setInfoStandEvent(null);
else setInfoStandEvent(event);
}, [ name ]);
CreateEventDispatcherHook(RoomWidgetUpdateInfostandFurniEvent.FURNI, eventDispatcher, onRoomWidgetUpdateInfostandEvent);
@ -228,7 +232,7 @@ export const AvatarInfoWidgetView: FC<AvatarInfoWidgetViewProps> = props =>
}
return null;
}, [ isGameMode, decorateView, name, isDancing, infoStandEvent, clearInfoStandEvent ]);
}, [ isGameMode, decorateView, name, isDancing, infoStandEvent, clearName, clearInfoStandEvent ]);
return (
<>

View File

@ -1,9 +1,340 @@
import { FC } from 'react';
import { RoomControllerLevel, RoomObjectCategory, RoomObjectVariable } from 'nitro-renderer';
import { FC, useCallback, useEffect, useMemo, useState } from 'react';
import { GetOwnRoomObject } from '../../../../../../api';
import { LocalizeText } from '../../../../../../utils/LocalizeText';
import { useRoomContext } from '../../../../context/RoomContext';
import { RoomWidgetMessage, RoomWidgetUserActionMessage } from '../../../../messages';
import { ContextMenuView } from '../../../context-menu/ContextMenuView';
import { ContextMenuHeaderView } from '../../../context-menu/views/header/ContextMenuHeaderView';
import { ContextMenuListItemView } from '../../../context-menu/views/list-item/ContextMenuListItemView';
import { AvatarInfoWidgetAvatarViewProps } from './AvatarInfoWidgetAvatarView.types';
const MODE_NORMAL = 0;
const MODE_MODERATE = 1;
const MODE_MODERATE_BAN = 2;
const MODE_MODERATE_MUTE = 3;
const MODE_AMBASSADOR = 4;
const MODE_AMBASSADOR_MUTE = 5;
export const AvatarInfoWidgetAvatarView: FC<AvatarInfoWidgetAvatarViewProps> = props =>
{
const { userData = null } = props;
return null;
const { userData = null, close = null } = props;
const [ mode, setMode ] = useState(MODE_NORMAL);
const [ respectsLeft, setRespectsLeft ] = useState(0);
const { widgetHandler = null } = useRoomContext();
useEffect(() =>
{
setMode(MODE_NORMAL);
setRespectsLeft(userData.respectLeft);
}, [ userData ])
const isShowGiveRights = useMemo(() =>
{
return (userData.amIOwner && (userData.targetRoomControllerLevel < RoomControllerLevel.GUEST) && !userData.isGuildRoom);
}, [ userData ]);
const isShowRemoveRights = useMemo(() =>
{
return (userData.amIOwner && (userData.targetRoomControllerLevel === RoomControllerLevel.GUEST) && !userData.isGuildRoom);
}, [ userData ]);
const moderateMenuHasContent = useMemo(() =>
{
return (userData.canBeKicked || userData.canBeBanned || userData.canBeMuted || isShowGiveRights || isShowRemoveRights);
}, [ isShowGiveRights, isShowRemoveRights, userData ]);
const processAction = useCallback((name: string) =>
{
let messageType: string = null;
let message: RoomWidgetMessage = null;
let hideMenu = true;
if(name)
{
switch(name)
{
case 'moderate':
hideMenu = false;
setMode(MODE_MODERATE);
break;
case 'ban':
hideMenu = false;
setMode(MODE_MODERATE_BAN);
break;
case 'mute':
hideMenu = false;
setMode(MODE_MODERATE_MUTE);
break;
case 'ambassador':
hideMenu = false;
setMode(MODE_AMBASSADOR);
break;
case 'ambassador_mute':
hideMenu = false;
setMode(MODE_AMBASSADOR_MUTE);
break;
case 'back_moderate':
hideMenu = false;
setMode(MODE_MODERATE);
break;
case 'back_ambassador':
hideMenu = false;
setMode(MODE_AMBASSADOR);
break;
case 'back':
hideMenu = false;
setMode(MODE_NORMAL);
break;
case 'whisper':
messageType = RoomWidgetUserActionMessage.WHISPER_USER;
break;
case 'friend':
//userData.canBeAskedAsFriend = false;
messageType = RoomWidgetUserActionMessage.SEND_FRIEND_REQUEST;
break;
case 'respect':
let newRespectsLeft = 0;
setRespectsLeft(prevValue =>
{
newRespectsLeft = (prevValue - 1);
return newRespectsLeft;
});
//userData.respectLeft--;
messageType = RoomWidgetUserActionMessage.RESPECT_USER;
if(newRespectsLeft > 0) hideMenu = false;
break;
case 'ignore':
messageType = RoomWidgetUserActionMessage.IGNORE_USER;
break;
case 'unignore':
messageType = RoomWidgetUserActionMessage.UNIGNORE_USER;
break;
case 'kick':
messageType = RoomWidgetUserActionMessage.KICK_USER;
break;
case 'ban_hour':
messageType = RoomWidgetUserActionMessage.BAN_USER_HOUR;
break;
case 'ban_day':
messageType = RoomWidgetUserActionMessage.BAN_USER_DAY;
break;
case 'perm_ban':
messageType = RoomWidgetUserActionMessage.BAN_USER_PERM;
break;
case 'mute_2min':
messageType = RoomWidgetUserActionMessage.MUTE_USER_2MIN;
break;
case 'mute_5min':
messageType = RoomWidgetUserActionMessage.MUTE_USER_5MIN;
break;
case 'mute_10min':
messageType = RoomWidgetUserActionMessage.MUTE_USER_10MIN;
break;
case 'give_rights':
messageType = RoomWidgetUserActionMessage.GIVE_RIGHTS;
break;
case 'remove_rights':
messageType = RoomWidgetUserActionMessage.TAKE_RIGHTS;
break;
case 'trade':
messageType = RoomWidgetUserActionMessage.START_TRADING;
break;
case 'report':
messageType = RoomWidgetUserActionMessage.REPORT_CFH_OTHER;
break;
case 'pass_hand_item':
messageType = RoomWidgetUserActionMessage.PASS_CARRY_ITEM;
break;
case 'ambassador_alert':
messageType = RoomWidgetUserActionMessage.AMBASSADOR_ALERT_USER;
break;
case 'ambassador_kick':
messageType = RoomWidgetUserActionMessage.AMBASSADOR_KICK_USER;
break;
case 'ambassador_mute_2min':
messageType = RoomWidgetUserActionMessage.MUTE_USER_2MIN;
break;
case 'ambassador_mute_10min':
messageType = RoomWidgetUserActionMessage.AMBASSADOR_MUTE_USER_10MIN;
break;
case 'ambassador_mute_60min':
messageType = RoomWidgetUserActionMessage.AMBASSADOR_MUTE_USER_60MIN;
break;
case 'ambassador_mute_18hour':
messageType = RoomWidgetUserActionMessage.AMBASSADOR_MUTE_USER_18HOUR;
break;
}
if(messageType) message = new RoomWidgetUserActionMessage(messageType, userData.webID);
if(message) widgetHandler.processWidgetMessage(message);
}
if(hideMenu) close();
}, [ widgetHandler, userData, close ]);
const canGiveHandItem = useMemo(() =>
{
let flag = false;
const roomObject = GetOwnRoomObject();
if(roomObject)
{
const carryId = roomObject.model.getValue<number>(RoomObjectVariable.FIGURE_CARRY_OBJECT);
if((carryId > 0) && (carryId < 999999)) flag = true;
}
return flag;
}, []);
return (
<ContextMenuView objectId={ userData.roomIndex } category={ RoomObjectCategory.UNIT } close={ close }>
<ContextMenuHeaderView>
{ userData.name }
</ContextMenuHeaderView>
{ (mode === MODE_NORMAL) &&
<>
{ userData.canBeAskedAsFriend &&
<ContextMenuListItemView onClick={ event => processAction('friend') }>
{ LocalizeText('infostand.button.friend') }
</ContextMenuListItemView> }
<ContextMenuListItemView onClick={ event => processAction('trade') }>
{ LocalizeText('infostand.button.trade') }
</ContextMenuListItemView>
<ContextMenuListItemView onClick={ event => processAction('whisper') }>
{ LocalizeText('infostand.button.whisper') }
</ContextMenuListItemView>
{ (respectsLeft > 0) &&
<ContextMenuListItemView onClick={ event => processAction('respect') }>
{ LocalizeText('infostand.button.respect', [ 'count' ], [ respectsLeft.toString() ]) }
</ContextMenuListItemView> }
{ !userData.isIgnored &&
<ContextMenuListItemView onClick={ event => processAction('ignore') }>
{ LocalizeText('infostand.button.ignore') }
</ContextMenuListItemView> }
{ userData.isIgnored &&
<ContextMenuListItemView onClick={ event => processAction('unignore') }>
{ LocalizeText('infostand.button.unignore') }
</ContextMenuListItemView> }
<ContextMenuListItemView onClick={ event => processAction('report') }>
{ LocalizeText('infostand.button.report') }
</ContextMenuListItemView>
{ moderateMenuHasContent &&
<ContextMenuListItemView onClick={ event => processAction('moderate') }>
<i className="fas fa-chevron-right right" />
{ LocalizeText('infostand.link.moderate') }
</ContextMenuListItemView> }
{ userData.isAmbassador &&
<ContextMenuListItemView onClick={ event => processAction('ambassador') }>
<i className="fas fa-chevron-right right" />
{ LocalizeText('infostand.link.ambassador') }
</ContextMenuListItemView> }
{ canGiveHandItem && <ContextMenuListItemView onClick={ event => processAction('pass_hand_item') }>
{ LocalizeText('avatar.widget.pass_hand_item') }
</ContextMenuListItemView> }
</> }
{ (mode === MODE_MODERATE) &&
<>
<ContextMenuListItemView onClick={ event => processAction('kick') }>
{ LocalizeText('infostand.button.kick') }
</ContextMenuListItemView>
<ContextMenuListItemView onClick={ event => processAction('mute') }>
<i className="fas fa-chevron-right right" />
{ LocalizeText('infostand.button.mute') }
</ContextMenuListItemView>
<ContextMenuListItemView onClick={ event => processAction('ban') }>
<i className="fas fa-chevron-right right" />
{ LocalizeText('infostand.button.ban') }
</ContextMenuListItemView>
{ isShowGiveRights &&
<ContextMenuListItemView onClick={ event => processAction('give_rights') }>
{ LocalizeText('infostand.button.giverights') }
</ContextMenuListItemView> }
{ isShowRemoveRights &&
<ContextMenuListItemView onClick={ event => processAction('remove_rights') }>
{ LocalizeText('infostand.button.removerights') }
</ContextMenuListItemView> }
<ContextMenuListItemView onClick={ event => processAction('back') }>
<i className="fas fa-chevron-left left" />
{ LocalizeText('generic.back') }
</ContextMenuListItemView>
</> }
{ (mode === MODE_MODERATE_BAN) &&
<>
<ContextMenuListItemView onClick={ event => processAction('ban_hour') }>
{ LocalizeText('infostand.button.ban_hour') }
</ContextMenuListItemView>
<ContextMenuListItemView onClick={ event => processAction('ban_day') }>
{ LocalizeText('infostand.button.ban_day') }
</ContextMenuListItemView>
<ContextMenuListItemView onClick={ event => processAction('ban_perm') }>
{ LocalizeText('infostand.button.perm_ban') }
</ContextMenuListItemView>
<ContextMenuListItemView onClick={ event => processAction('back_moderate') }>
<i className="fas fa-chevron-left left" />
{ LocalizeText('generic.back') }
</ContextMenuListItemView>
</> }
{ (mode === MODE_MODERATE_MUTE) &&
<>
<ContextMenuListItemView onClick={ event => processAction('mute_2min') }>
{ LocalizeText('infostand.button.mute_2min') }
</ContextMenuListItemView>
<ContextMenuListItemView onClick={ event => processAction('mute_5min') }>
{ LocalizeText('infostand.button.mute_5min') }
</ContextMenuListItemView>
<ContextMenuListItemView onClick={ event => processAction('mute_10min') }>
{ LocalizeText('infostand.button.mute_10min') }
</ContextMenuListItemView>
<ContextMenuListItemView onClick={ event => processAction('back_moderate') }>
<i className="fas fa-chevron-left left" />
{ LocalizeText('generic.back') }
</ContextMenuListItemView>
</> }
{ (mode === MODE_AMBASSADOR) &&
<>
<ContextMenuListItemView onClick={ event => processAction('ambassador_alert') }>
{ LocalizeText('infostand.button.alert') }
</ContextMenuListItemView>
<ContextMenuListItemView onClick={ event => processAction('ambassador_kick') }>
{ LocalizeText('infostand.button.kick') }
</ContextMenuListItemView>
<ContextMenuListItemView onClick={ event => processAction('ambassador_mute') }>
<i className="fas fa-chevron-right right" />
{ LocalizeText('infostand.button.mute') }
</ContextMenuListItemView>
<ContextMenuListItemView onClick={ event => processAction('back') }>
<i className="fas fa-chevron-left left" />
{ LocalizeText('generic.back') }
</ContextMenuListItemView>
</> }
{ (mode === MODE_AMBASSADOR_MUTE) &&
<>
<ContextMenuListItemView onClick={ event => processAction('ambassador_mute_2min') }>
{ LocalizeText('infostand.button.mute_2min') }
</ContextMenuListItemView>
<ContextMenuListItemView onClick={ event => processAction('ambassador_mute_10min') }>
{ LocalizeText('infostand.button.mute_10min') }
</ContextMenuListItemView>
<ContextMenuListItemView onClick={ event => processAction('ambassador_mute_60min') }>
{ LocalizeText('infostand.button.mute_60min') }
</ContextMenuListItemView>
<ContextMenuListItemView onClick={ event => processAction('ambassador_mute_18hr') }>
{ LocalizeText('infostand.button.mute_18hour') }
</ContextMenuListItemView>
<ContextMenuListItemView onClick={ event => processAction('back_ambassador') }>
<i className="fas fa-chevron-left left" />
{ LocalizeText('generic.back') }
</ContextMenuListItemView>
</> }
</ContextMenuView>
);
}

View File

@ -1,7 +1,6 @@
import { FC, useMemo } from 'react';
import { GetSessionDataManager } from '../../../../../../api';
import { ContextMenuView } from '../../../context-menu/ContextMenuView';
import { ContextMenuHeaderView } from '../../../context-menu/views/header/ContextMenuHeaderView';
import { AvatarInfoWidgetNameViewProps } from './AvatarInfoWidgetNameView.types';
export const AvatarInfoWidgetNameView: FC<AvatarInfoWidgetNameViewProps> = props =>
@ -14,10 +13,10 @@ export const AvatarInfoWidgetNameView: FC<AvatarInfoWidgetNameViewProps> = props
}, [ nameData ]);
return (
<ContextMenuView objectId={ nameData.roomIndex } category={ nameData.category } fades={ fades } close= { close }>
<ContextMenuHeaderView>
<ContextMenuView objectId={ nameData.roomIndex } category={ nameData.category } fades={ fades } className="name-only" close= { close }>
<div className="text-shadow">
{ nameData.name }
</ContextMenuHeaderView>
</div>
</ContextMenuView>
);
}

View File

@ -2,12 +2,12 @@ import { AvatarAction, AvatarExpressionEnum, RoomObjectCategory } from 'nitro-re
import { FC, useCallback, useState } from 'react';
import { GetCanStandUp, GetCanUseExpression, GetOwnPosture, HasHabboClub, HasHabboVip, IsRidingHorse } from '../../../../../../api';
import { LocalizeText } from '../../../../../../utils/LocalizeText';
import { CurrencyIcon } from '../../../../../shared/currency-icon/CurrencyIcon';
import { useRoomContext } from '../../../../context/RoomContext';
import { RoomWidgetAvatarExpressionMessage, RoomWidgetChangePostureMessage, RoomWidgetDanceMessage, RoomWidgetMessage, RoomWidgetUserActionMessage } from '../../../../messages';
import { ContextMenuView } from '../../../context-menu/ContextMenuView';
import { ContextMenuHeaderView } from '../../../context-menu/views/header/ContextMenuHeaderView';
import { ContextMenuListItemView } from '../../../context-menu/views/list-item/ContextMenuListItemView';
import { ContextMenuListView } from '../../../context-menu/views/list/ContextMenuListView';
import { AvatarInfoWidgetOwnAvatarViewProps } from './AvatarInfoWidgetOwnAvatarView.types';
const MODE_NORMAL = 0;
@ -15,7 +15,6 @@ const MODE_CLUB_DANCES = 1;
const MODE_NAME_CHANGE = 2;
const MODE_EXPRESSIONS = 3;
const MODE_SIGNS = 4;
const MODE_CHANGE_LOOKS = 5;
export const AvatarInfoWidgetOwnAvatarView: FC<AvatarInfoWidgetOwnAvatarViewProps> = props =>
{
@ -110,91 +109,98 @@ export const AvatarInfoWidgetOwnAvatarView: FC<AvatarInfoWidgetOwnAvatarViewProp
<ContextMenuHeaderView>
{ userData.name }
</ContextMenuHeaderView>
<ContextMenuListView columns={ (mode === MODE_SIGNS) ? 4 : 1 }>
{ (mode === MODE_NORMAL) &&
<>
<ContextMenuListItemView onClick={ event => processAction('decorate') }>
{ LocalizeText('widget.avatar.decorate') }
</ContextMenuListItemView>
<ContextMenuListItemView onClick={ event => processAction('change_looks') }>
{ LocalizeText('widget.memenu.myclothes') }
</ContextMenuListItemView>
{ (HasHabboClub() && !isRidingHorse) &&
<ContextMenuListItemView onClick={ event => processAction('dance_menu') }>
{ LocalizeText('widget.memenu.dance') }
</ContextMenuListItemView> }
{ (!isDancing && !HasHabboClub() && !isRidingHorse) &&
<ContextMenuListItemView onClick={ event => processAction('dance') }>
{ LocalizeText('widget.memenu.dance') }
</ContextMenuListItemView> }
{ (isDancing && !HasHabboClub() && !isRidingHorse) &&
<ContextMenuListItemView onClick={ event => processAction('dance_stop') }>
{ LocalizeText('widget.memenu.dance.stop') }
</ContextMenuListItemView> }
<ContextMenuListItemView onClick={ event => processAction('expressions') }>
{ LocalizeText('infostand.link.expressions') }
</ContextMenuListItemView>
<ContextMenuListItemView onClick={ event => processAction('signs') }>
{ LocalizeText('infostand.show.signs') }
</ContextMenuListItemView>
{ (userData.carryItem > 0) &&
<ContextMenuListItemView onClick={ event => processAction('drop_carry_item') }>
{ LocalizeText('avatar.widget.drop_hand_item') }
</ContextMenuListItemView> }
</> }
{ (mode === MODE_CLUB_DANCES) &&
<>
{ isDancing &&
<ContextMenuListItemView onClick={ event => processAction('dance_stop') }>
{ LocalizeText('widget.memenu.dance.stop') }
</ContextMenuListItemView> }
<ContextMenuListItemView onClick={ event => processAction('dance_1') }>
{ LocalizeText('widget.memenu.dance1') }
</ContextMenuListItemView>
<ContextMenuListItemView onClick={ event => processAction('dance_2') }>
{ LocalizeText('widget.memenu.dance2') }
</ContextMenuListItemView>
<ContextMenuListItemView onClick={ event => processAction('dance_3') }>
{ LocalizeText('widget.memenu.dance3') }
</ContextMenuListItemView>
<ContextMenuListItemView onClick={ event => processAction('dance_4') }>
{ LocalizeText('widget.memenu.dance4') }
</ContextMenuListItemView>
<ContextMenuListItemView onClick={ event => processAction('back') }>
{ LocalizeText('generic.back') }
</ContextMenuListItemView>
</> }
{ (mode === MODE_EXPRESSIONS) &&
<>
{ (GetOwnPosture() === AvatarAction.POSTURE_STAND) &&
<ContextMenuListItemView onClick={ event => processAction('sit') }>
{ LocalizeText('widget.memenu.sit') }
</ContextMenuListItemView> }
{ GetCanStandUp() &&
<ContextMenuListItemView onClick={ event => processAction('stand') }>
{ LocalizeText('widget.memenu.stand') }
</ContextMenuListItemView> }
{ GetCanUseExpression() &&
<ContextMenuListItemView onClick={ event => processAction('wave') }>
{ LocalizeText('widget.memenu.wave') }
</ContextMenuListItemView> }
{ (GetCanUseExpression() && HasHabboVip()) &&
<ContextMenuListItemView onClick={ event => processAction('laugh') }>
{ LocalizeText('widget.memenu.laugh') }
</ContextMenuListItemView> }
{ (GetCanUseExpression() && HasHabboVip()) &&
<ContextMenuListItemView onClick={ event => processAction('blow') }>
{ LocalizeText('widget.memenu.blow') }
</ContextMenuListItemView> }
<ContextMenuListItemView onClick={ event => processAction('idle') }>
{ LocalizeText('widget.memenu.idle') }
</ContextMenuListItemView>
<ContextMenuListItemView onClick={ event => processAction('back') }>
{ LocalizeText('generic.back') }
</ContextMenuListItemView>
</> }
{ (mode === MODE_SIGNS) &&
<>
{ (mode === MODE_NORMAL) &&
<>
<ContextMenuListItemView onClick={ event => processAction('decorate') }>
{ LocalizeText('widget.avatar.decorate') }
</ContextMenuListItemView>
<ContextMenuListItemView onClick={ event => processAction('change_looks') }>
{ LocalizeText('widget.memenu.myclothes') }
</ContextMenuListItemView>
{ (HasHabboClub() && !isRidingHorse) &&
<ContextMenuListItemView onClick={ event => processAction('dance_menu') }>
<i className="fas fa-chevron-right right" />
{ LocalizeText('widget.memenu.dance') }
</ContextMenuListItemView> }
{ (!isDancing && !HasHabboClub() && !isRidingHorse) &&
<ContextMenuListItemView onClick={ event => processAction('dance') }>
{ LocalizeText('widget.memenu.dance') }
</ContextMenuListItemView> }
{ (isDancing && !HasHabboClub() && !isRidingHorse) &&
<ContextMenuListItemView onClick={ event => processAction('dance_stop') }>
{ LocalizeText('widget.memenu.dance.stop') }
</ContextMenuListItemView> }
<ContextMenuListItemView onClick={ event => processAction('expressions') }>
<i className="fas fa-chevron-right right" />
{ LocalizeText('infostand.link.expressions') }
</ContextMenuListItemView>
<ContextMenuListItemView onClick={ event => processAction('signs') }>
<i className="fas fa-chevron-right right" />
{ LocalizeText('infostand.show.signs') }
</ContextMenuListItemView>
{ (userData.carryItem > 0) &&
<ContextMenuListItemView onClick={ event => processAction('drop_carry_item') }>
{ LocalizeText('avatar.widget.drop_hand_item') }
</ContextMenuListItemView> }
</> }
{ (mode === MODE_CLUB_DANCES) &&
<>
{ isDancing &&
<ContextMenuListItemView onClick={ event => processAction('dance_stop') }>
{ LocalizeText('widget.memenu.dance.stop') }
</ContextMenuListItemView> }
<ContextMenuListItemView onClick={ event => processAction('dance_1') }>
{ LocalizeText('widget.memenu.dance1') }
</ContextMenuListItemView>
<ContextMenuListItemView onClick={ event => processAction('dance_2') }>
{ LocalizeText('widget.memenu.dance2') }
</ContextMenuListItemView>
<ContextMenuListItemView onClick={ event => processAction('dance_3') }>
{ LocalizeText('widget.memenu.dance3') }
</ContextMenuListItemView>
<ContextMenuListItemView onClick={ event => processAction('dance_4') }>
{ LocalizeText('widget.memenu.dance4') }
</ContextMenuListItemView>
<ContextMenuListItemView onClick={ event => processAction('back') }>
<i className="fas fa-chevron-left left" />
{ LocalizeText('generic.back') }
</ContextMenuListItemView>
</> }
{ (mode === MODE_EXPRESSIONS) &&
<>
{ (GetOwnPosture() === AvatarAction.POSTURE_STAND) &&
<ContextMenuListItemView onClick={ event => processAction('sit') }>
{ LocalizeText('widget.memenu.sit') }
</ContextMenuListItemView> }
{ GetCanStandUp() &&
<ContextMenuListItemView onClick={ event => processAction('stand') }>
{ LocalizeText('widget.memenu.stand') }
</ContextMenuListItemView> }
{ GetCanUseExpression() &&
<ContextMenuListItemView onClick={ event => processAction('wave') }>
{ LocalizeText('widget.memenu.wave') }
</ContextMenuListItemView> }
{ GetCanUseExpression() &&
<ContextMenuListItemView canSelect={ HasHabboVip() } onClick={ event => processAction('laugh') }>
<CurrencyIcon type="hc" />
{ LocalizeText('widget.memenu.laugh') }
</ContextMenuListItemView> }
{ GetCanUseExpression() &&
<ContextMenuListItemView canSelect={ HasHabboVip() } onClick={ event => processAction('blow') }>
<CurrencyIcon type="hc" />
{ LocalizeText('widget.memenu.blow') }
</ContextMenuListItemView> }
<ContextMenuListItemView onClick={ event => processAction('idle') }>
{ LocalizeText('widget.memenu.idle') }
</ContextMenuListItemView>
<ContextMenuListItemView onClick={ event => processAction('back') }>
<i className="fas fa-chevron-left left" />
{ LocalizeText('generic.back') }
</ContextMenuListItemView>
</> }
{ (mode === MODE_SIGNS) &&
<>
<div className="d-flex menu-list-split-3">
<ContextMenuListItemView onClick={ event => processAction('sign_1') }>
1
</ContextMenuListItemView>
@ -204,6 +210,8 @@ export const AvatarInfoWidgetOwnAvatarView: FC<AvatarInfoWidgetOwnAvatarViewProp
<ContextMenuListItemView onClick={ event => processAction('sign_3') }>
3
</ContextMenuListItemView>
</div>
<div className="d-flex menu-list-split-3">
<ContextMenuListItemView onClick={ event => processAction('sign_4') }>
4
</ContextMenuListItemView>
@ -213,6 +221,8 @@ export const AvatarInfoWidgetOwnAvatarView: FC<AvatarInfoWidgetOwnAvatarViewProp
<ContextMenuListItemView onClick={ event => processAction('sign_6') }>
6
</ContextMenuListItemView>
</div>
<div className="d-flex menu-list-split-3">
<ContextMenuListItemView onClick={ event => processAction('sign_7') }>
7
</ContextMenuListItemView>
@ -222,6 +232,8 @@ export const AvatarInfoWidgetOwnAvatarView: FC<AvatarInfoWidgetOwnAvatarViewProp
<ContextMenuListItemView onClick={ event => processAction('sign_9') }>
9
</ContextMenuListItemView>
</div>
<div className="d-flex menu-list-split-3">
<ContextMenuListItemView onClick={ event => processAction('sign_10') }>
10
</ContextMenuListItemView>
@ -231,6 +243,8 @@ export const AvatarInfoWidgetOwnAvatarView: FC<AvatarInfoWidgetOwnAvatarViewProp
<ContextMenuListItemView onClick={ event => processAction('sign_15') }>
<i className="icon icon-sign-smile" />
</ContextMenuListItemView>
</div>
<div className="d-flex menu-list-split-3">
<ContextMenuListItemView onClick={ event => processAction('sign_12') }>
<i className="icon icon-sign-skull" />
</ContextMenuListItemView>
@ -240,6 +254,8 @@ export const AvatarInfoWidgetOwnAvatarView: FC<AvatarInfoWidgetOwnAvatarViewProp
<ContextMenuListItemView onClick={ event => processAction('sign_17') }>
<i className="icon icon-sign-yellow" />
</ContextMenuListItemView>
</div>
<div className="d-flex menu-list-split-3">
<ContextMenuListItemView onClick={ event => processAction('sign_16') }>
<i className="icon icon-sign-red" />
</ContextMenuListItemView>
@ -249,8 +265,12 @@ export const AvatarInfoWidgetOwnAvatarView: FC<AvatarInfoWidgetOwnAvatarViewProp
<ContextMenuListItemView onClick={ event => processAction('sign_11') }>
<i className="icon icon-sign-heart" />
</ContextMenuListItemView>
</> }
</ContextMenuListView>
</div>
<ContextMenuListItemView onClick={ event => processAction('back') }>
<i className="fas fa-chevron-left left" />
{ LocalizeText('generic.back') }
</ContextMenuListItemView>
</> }
</ContextMenuView>
);
}

View File

@ -1,9 +1,189 @@
import { FC } from 'react';
import { AvatarInfoWidgetRentableBotViewProps } from './AvatarInfoWidgetRentableBotView.types';
import { BotCommandConfigurationEvent, BotRemoveComposer, BotSkillSaveComposer, Nitro, RequestBotCommandConfigurationComposer, RoomObjectCategory } from 'nitro-renderer';
import { FC, useCallback, useState } from 'react';
import { GetConnection } from '../../../../../../api';
import { CreateMessageHook } from '../../../../../../hooks/messages';
import { LocalizeText } from '../../../../../../utils/LocalizeText';
import { useRoomContext } from '../../../../context/RoomContext';
import { ContextMenuView } from '../../../context-menu/ContextMenuView';
import { ContextMenuHeaderView } from '../../../context-menu/views/header/ContextMenuHeaderView';
import { ContextMenuListItemView } from '../../../context-menu/views/list-item/ContextMenuListItemView';
import { BotSkillsEnum } from '../../utils/BotSkillsEnum';
import { AvatarInfoWidgetRentableBotViewProps, BotChatOptions } from './AvatarInfoWidgetRentableBotView.types';
const MODE_NORMAL = 0;
const MODE_CHANGE_NAME = 1;
const MODE_CHANGE_MOTTO = 2;
const MODE_CHANGE_SPEECH = 3;
export const AvatarInfoWidgetRentableBotView: FC<AvatarInfoWidgetRentableBotViewProps> = props =>
{
const { rentableBotData = null } = props;
return null;
const { rentableBotData = null, close = null } = props;
const [ mode, setMode ] = useState(MODE_NORMAL);
const [ newName, setNewName ] = useState('');
const [ newMotto, setNewMotto ] = useState('');
const [ newChat, setNewChat ] = useState('');
const [ chatOptions, setChatOptions ] = useState<BotChatOptions>({ automaticChat: false, chatDelay: 0, mixSentences: false });
const { widgetHandler = null } = useRoomContext();
const onBotCommandConfigurationEvent = useCallback((event: BotCommandConfigurationEvent) =>
{
const parser = event.getParser();
if(!parser) return;
if(parser.botId !== rentableBotData.webID) return;
switch(parser.commandId)
{
case BotSkillsEnum.CHANGE_BOT_NAME:
setNewName(parser.data);
setMode(MODE_CHANGE_NAME);
return;
case BotSkillsEnum.CHANGE_BOT_MOTTO:
setNewMotto(parser.data);
setMode(MODE_CHANGE_MOTTO);
return;
case BotSkillsEnum.SETUP_CHAT: {
const data = parser.data;
const pieces = data.split(((data.indexOf(';#;') === -1) ? ';' : ';#;'));
if((pieces.length === 3) || (pieces.length === 4))
{
setNewChat(pieces[0]);
setChatOptions({
automaticChat: ((pieces[1].toLowerCase() === 'true') || (pieces[1] === '1')),
chatDelay: parseInt(pieces[2]),
mixSentences: ((pieces[3]) ? ((pieces[3].toLowerCase() === 'true') || (pieces[3] === '1')) : false)
});
}
setMode(MODE_CHANGE_SPEECH);
return;
}
}
}, [ rentableBotData ]);
CreateMessageHook(BotCommandConfigurationEvent, onBotCommandConfigurationEvent);
const requestBotCommandConfiguration = useCallback((skillType: number) =>
{
GetConnection().send(new RequestBotCommandConfigurationComposer(rentableBotData.webID, skillType));
}, [ rentableBotData ]);
const processAction = useCallback((name: string) =>
{
let hideMenu = true;
if(name)
{
switch(name)
{
case 'donate_to_all':
requestBotCommandConfiguration(BotSkillsEnum.DONATE_TO_ALL);
GetConnection().send(new BotSkillSaveComposer(rentableBotData.webID, BotSkillsEnum.DONATE_TO_ALL, ''));
break;
case 'donate_to_user':
requestBotCommandConfiguration(BotSkillsEnum.DONATE_TO_USER);
GetConnection().send(new BotSkillSaveComposer(rentableBotData.webID, BotSkillsEnum.DONATE_TO_USER, ''));
break;
case 'change_bot_name':
requestBotCommandConfiguration(BotSkillsEnum.CHANGE_BOT_NAME);
hideMenu = false;
break;
case 'save_bot_name':
GetConnection().send(new BotSkillSaveComposer(rentableBotData.webID, BotSkillsEnum.CHANGE_BOT_NAME, newName));
break;
case 'change_bot_motto':
requestBotCommandConfiguration(BotSkillsEnum.CHANGE_BOT_MOTTO);
hideMenu = false;
break;
case 'save_bot_motto':
GetConnection().send(new BotSkillSaveComposer(rentableBotData.webID, BotSkillsEnum.CHANGE_BOT_MOTTO, newMotto));
break;
case 'dress_up':
GetConnection().send(new BotSkillSaveComposer(rentableBotData.webID, BotSkillsEnum.DRESS_UP, ''));
break;
case 'random_walk':
GetConnection().send(new BotSkillSaveComposer(rentableBotData.webID, BotSkillsEnum.RANDOM_WALK, ''));
break;
case 'setup_chat':
requestBotCommandConfiguration(BotSkillsEnum.SETUP_CHAT);
hideMenu = false;
break;
case 'dance':
GetConnection().send(new BotSkillSaveComposer(rentableBotData.webID, BotSkillsEnum.DANCE, ''));
break;
case 'nux_take_tour':
Nitro.instance.createLinkEvent('help/tour');
GetConnection().send(new BotSkillSaveComposer(rentableBotData.webID, BotSkillsEnum.NUX_TAKE_TOUR, ''));
break;
case 'pick':
GetConnection().send(new BotRemoveComposer(rentableBotData.webID));
break;
default:
break;
}
}
if(hideMenu) close();
}, [ rentableBotData, newName, newMotto, requestBotCommandConfiguration, close ]);
const canControl = (rentableBotData.amIOwner || rentableBotData.amIAnyRoomController);
return (
<ContextMenuView objectId={ rentableBotData.roomIndex } category={ RoomObjectCategory.UNIT } close={ close }>
<ContextMenuHeaderView>
{ rentableBotData.name }
</ContextMenuHeaderView>
{ (mode === MODE_NORMAL) &&
<>
{ ((rentableBotData.botSkills.indexOf(BotSkillsEnum.DONATE_TO_ALL) >= 0) && canControl) &&
<ContextMenuListItemView onClick={ event => processAction('donate_to_all') }>
{ LocalizeText('avatar.widget.donate_to_all') }
</ContextMenuListItemView> }
{ ((rentableBotData.botSkills.indexOf(BotSkillsEnum.DONATE_TO_USER) >= 0) && canControl) &&
<ContextMenuListItemView onClick={ event => processAction('donate_to_user') }>
{ LocalizeText('avatar.widget.donate_to_user') }
</ContextMenuListItemView> }
{ ((rentableBotData.botSkills.indexOf(BotSkillsEnum.CHANGE_BOT_NAME) >= 0) && canControl) &&
<ContextMenuListItemView onClick={ event => processAction('change_bot_name') }>
{ LocalizeText('avatar.widget.change_bot_name') }
</ContextMenuListItemView> }
{ ((rentableBotData.botSkills.indexOf(BotSkillsEnum.CHANGE_BOT_MOTTO) >= 0) && canControl) &&
<ContextMenuListItemView onClick={ event => processAction('change_bot_motto') }>
{ LocalizeText('avatar.widget.change_bot_motto') }
</ContextMenuListItemView> }
{ ((rentableBotData.botSkills.indexOf(BotSkillsEnum.DRESS_UP) >= 0) && canControl) &&
<ContextMenuListItemView onClick={ event => processAction('dress_up') }>
{ LocalizeText('avatar.widget.dress_up') }
</ContextMenuListItemView> }
{ ((rentableBotData.botSkills.indexOf(BotSkillsEnum.RANDOM_WALK) >= 0) && canControl) &&
<ContextMenuListItemView onClick={ event => processAction('random_walk') }>
{ LocalizeText('avatar.widget.random_walk') }
</ContextMenuListItemView> }
{ ((rentableBotData.botSkills.indexOf(BotSkillsEnum.SETUP_CHAT) >= 0) && canControl) &&
<ContextMenuListItemView onClick={ event => processAction('setup_chat') }>
{ LocalizeText('avatar.widget.setup_chat') }
</ContextMenuListItemView> }
{ ((rentableBotData.botSkills.indexOf(BotSkillsEnum.DANCE) >= 0) && canControl) &&
<ContextMenuListItemView onClick={ event => processAction('dance') }>
{ LocalizeText('avatar.widget.dance') }
</ContextMenuListItemView> }
{ ((rentableBotData.botSkills.indexOf(BotSkillsEnum.NO_PICK_UP) === -1) && canControl) &&
<ContextMenuListItemView onClick={ event => processAction('pick') }>
{ LocalizeText('avatar.widget.pick_up') }
</ContextMenuListItemView> }
</> }
{ (mode === MODE_CHANGE_NAME) &&
<ContextMenuListItemView className="flex-column" onClick={ null }>
<p className="mb-1">{ LocalizeText('bot.skill.name.configuration.new.name') }</p>
<input type="text" className="form-control form-control-sm mb-2" value={ newName } onChange={ event => setNewName(event.target.value) } />
<div className="d-flex justify-content-between align-items-center">
<button type="button" className="btn btn-secondary btn-sm" onClick={ event => processAction(null) }>{ LocalizeText('cancel') }</button>
<button type="button" className="btn btn-success btn-sm" onClick={ event => processAction('save_bot_name') }>{ LocalizeText('save') }</button>
</div>
</ContextMenuListItemView> }
</ContextMenuView>
);
}

View File

@ -5,3 +5,10 @@ export interface AvatarInfoWidgetRentableBotViewProps
rentableBotData: RoomWidgetUpdateInfostandRentableBotEvent;
close: () => void;
}
export interface BotChatOptions
{
automaticChat: boolean;
chatDelay: number;
mixSentences: boolean;
}

View File

@ -7,10 +7,20 @@
border-radius: $border-radius;
font-size: $font-size-sm;
&.name-only {
background-color: rgba($black, 0.7);
padding: 1px 8px;
min-width: 60px;
width: unset;
max-width: unset;
text-align: center;
font-size: 18px;
}
&:after {
content: "";
position: absolute;
bottom: -8px;
bottom: -7px;
left: 0;
right: 0;
margin: auto;
@ -19,7 +29,7 @@
transform: rotate(45deg);
border-color: transparent rgba($white, 0.5) rgba($white, 0.5) transparent;
border-style: solid;
border-width: 6px;
border-width: 5px;
}
.menu-header {
@ -27,25 +37,48 @@
color: $white;
height: 25px;
max-height: 25px;
font-size: 16px;
}
.menu-list {
margin: 0;
.menu-list-split-3 {
.menu-list-item-container {
margin-top: 2px;
.menu-list-item {
margin-right: 2px;
.menu-list-item {
height: 24px;
max-height: 24px;
padding: 3px;
background: $bg-mirage-split-background;
cursor: pointer;
&:hover {
background: $bg-cello-split-background;
}
&:nth-child(3n+3) {
margin-right: unset;
}
}
}
.menu-list-item {
position: relative;
height: 24px;
max-height: 24px;
margin-top: 2px;
padding: 3px;
background: $bg-mirage-split-background;
cursor: pointer;
overflow: hidden;
&.disabled {
filter: brightness(0.7);
cursor: not-allowed;
}
.fas,
.nitro-currency-icon {
position: absolute;
right: 8px;
&.left {
left: 8px;
right: unset;
}
}
&:not(.disabled):hover {
background: $bg-cello-split-background;
}
}
}

View File

@ -1,3 +1,4 @@
import { FixedSizeStack } from 'nitro-renderer';
import { FC, useCallback, useEffect, useRef, useState } from 'react';
import { GetRoomObjectBounds, GetRoomSession, GetTicker } from '../../../../api';
import { ContextMenuViewProps } from './ContextMenuView.types';
@ -7,8 +8,9 @@ const fadeLength = 75;
export const ContextMenuView: FC<ContextMenuViewProps> = props =>
{
const { objectId = -1, category = -1, fades = false, close = null, children = null } = props;
const { objectId = -1, category = -1, fades = false, className = '', close = null, children = null } = props;
const [ pos, setPos ] = useState<{ x: number, y: number }>({ x: null, y: null });
const [ stack, setStack ] = useState<FixedSizeStack>(null);
const [ opacity, setOpacity ] = useState(1);
const [ isFading, setIsFading ] = useState(false);
const [ fadeTime, setFadeTime ] = useState(0);
@ -16,19 +18,19 @@ export const ContextMenuView: FC<ContextMenuViewProps> = props =>
const update = useCallback((time: number) =>
{
let fadeTime = time;
let newFadeTime = time;
let newOpacity = 1;
if(isFading)
{
setFadeTime(prevValue =>
{
fadeTime += prevValue;
newFadeTime += prevValue;
return fadeTime;
return newFadeTime;
});
newOpacity = ((1 - (fadeTime / fadeLength)) * 1);
newOpacity = ((1 - (newFadeTime / fadeLength)) * 1);
if(newOpacity <= 0)
{
@ -50,6 +52,11 @@ export const ContextMenuView: FC<ContextMenuViewProps> = props =>
});
}, [ objectId, category, isFading, close ]);
useEffect(() =>
{
setStack(new FixedSizeStack(25));
}, []);
useEffect(() =>
{
GetTicker().add(update);
@ -73,7 +80,7 @@ export const ContextMenuView: FC<ContextMenuViewProps> = props =>
}, [ fades ]);
return (
<div ref={ elementRef } className={ 'position-absolute nitro-context-menu ' + (pos.x !== null ? 'visible' : 'invisible') } style={ { left: (pos.x || 0), top: (pos.y || 0), opacity: opacity } }>
<div ref={ elementRef } className={ `position-absolute nitro-context-menu ${ className }${ (pos.x !== null ? ' visible' : ' invisible') }` } style={ { left: (pos.x || 0), top: (pos.y || 0), opacity: opacity } }>
{ children }
</div>
);

View File

@ -3,5 +3,6 @@ export interface ContextMenuViewProps
objectId: number;
category: number;
fades?: boolean;
className?: string;
close: () => void;
}

View File

@ -1,15 +1,20 @@
import { FC } from 'react';
import { FC, MouseEvent, useCallback } from 'react';
import { ContextMenuListItemViewProps } from './ContextMenuListItemView.types';
export const ContextMenuListItemView: FC<ContextMenuListItemViewProps> = props =>
{
const { onClick = null, children = null } = props;
const { className = '', canSelect = true, onClick = null, children = null } = props;
const handleClick = useCallback((event: MouseEvent<HTMLDivElement>) =>
{
if(!canSelect) return;
onClick(event);
}, [ canSelect, onClick ]);
return (
<div className="col menu-list-item-container" onClick={ onClick }>
<div className="d-flex justify-content-center align-items-center menu-list-item">
{ children }
</div>
<div className={ `d-flex justify-content-center align-items-center w-100 menu-list-item ${ !canSelect ? 'disabled ' : '' }${ className }` } onClick={ handleClick }>
{ children }
</div>
)
}

View File

@ -2,5 +2,7 @@ import { MouseEvent } from 'react';
export interface ContextMenuListItemViewProps
{
className?: string;
canSelect?: boolean;
onClick: (event: MouseEvent) => void;
}

View File

@ -6,7 +6,7 @@ export const ContextMenuListView: FC<ContextMenuListViewProps> = props =>
const { columns = 1, children = null } = props;
return (
<div className={ `row row-cols-${ columns } menu-list g-1` }>
<div className={ `d-flex flex-column menu-list` }>
{ children }
</div>
);

View File

@ -1,4 +1,4 @@
export interface CurrencyIconProps
{
type: number;
type: number | string;
}