From 64198100a188c9f7dbe66baaaa57fb423751e98b Mon Sep 17 00:00:00 2001 From: Bill Date: Sat, 26 Jun 2021 13:32:32 -0400 Subject: [PATCH] Context menu updates --- src/api/nitro/session/GetCanUseExpression.ts | 2 +- .../avatar-info/AvatarInfoWidgetView.tsx | 8 +- .../avatar/AvatarInfoWidgetAvatarView.tsx | 339 +++++++++++++++++- .../views/name/AvatarInfoWidgetNameView.tsx | 7 +- .../AvatarInfoWidgetOwnAvatarView.tsx | 198 +++++----- .../AvatarInfoWidgetRentableBotView.tsx | 190 +++++++++- .../AvatarInfoWidgetRentableBotView.types.ts | 7 + .../widgets/context-menu/ContextMenu.scss | 65 +++- .../widgets/context-menu/ContextMenuView.tsx | 19 +- .../context-menu/ContextMenuView.types.ts | 1 + .../list-item/ContextMenuListItemView.tsx | 17 +- .../ContextMenuListItemView.types.ts | 2 + .../views/list/ContextMenuListView.tsx | 2 +- .../currency-icon/CurrencyIcon.types.ts | 2 +- 14 files changed, 724 insertions(+), 135 deletions(-) diff --git a/src/api/nitro/session/GetCanUseExpression.ts b/src/api/nitro/session/GetCanUseExpression.ts index 933f64c8..8ea83509 100644 --- a/src/api/nitro/session/GetCanUseExpression.ts +++ b/src/api/nitro/session/GetCanUseExpression.ts @@ -10,5 +10,5 @@ export function GetCanUseExpression(): boolean const model = roomObject.model; const effectId = model.getValue(RoomObjectVariable.FIGURE_EFFECT); - return ((effectId === 29) || (effectId === 30) || (effectId === 185)); + return !((effectId === 29) || (effectId === 30) || (effectId === 185)); } diff --git a/src/views/room/widgets/avatar-info/AvatarInfoWidgetView.tsx b/src/views/room/widgets/avatar-info/AvatarInfoWidgetView.tsx index 9cfdadc1..9467aa4f 100644 --- a/src/views/room/widgets/avatar-info/AvatarInfoWidgetView.tsx +++ b/src/views/room/widgets/avatar-info/AvatarInfoWidgetView.tsx @@ -102,6 +102,8 @@ export const AvatarInfoWidgetView: FC = props => { if(!infoStandEvent) return; + console.log('tru') + setInfoStandEvent(null); }, [ infoStandEvent ]); @@ -119,7 +121,9 @@ export const AvatarInfoWidgetView: FC = 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 = props => } return null; - }, [ isGameMode, decorateView, name, isDancing, infoStandEvent, clearInfoStandEvent ]); + }, [ isGameMode, decorateView, name, isDancing, infoStandEvent, clearName, clearInfoStandEvent ]); return ( <> diff --git a/src/views/room/widgets/avatar-info/views/avatar/AvatarInfoWidgetAvatarView.tsx b/src/views/room/widgets/avatar-info/views/avatar/AvatarInfoWidgetAvatarView.tsx index 078d0d0e..d2f3bff6 100644 --- a/src/views/room/widgets/avatar-info/views/avatar/AvatarInfoWidgetAvatarView.tsx +++ b/src/views/room/widgets/avatar-info/views/avatar/AvatarInfoWidgetAvatarView.tsx @@ -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 = 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(RoomObjectVariable.FIGURE_CARRY_OBJECT); + + if((carryId > 0) && (carryId < 999999)) flag = true; + } + + return flag; + }, []); + + return ( + + + { userData.name } + + { (mode === MODE_NORMAL) && + <> + { userData.canBeAskedAsFriend && + processAction('friend') }> + { LocalizeText('infostand.button.friend') } + } + processAction('trade') }> + { LocalizeText('infostand.button.trade') } + + processAction('whisper') }> + { LocalizeText('infostand.button.whisper') } + + { (respectsLeft > 0) && + processAction('respect') }> + { LocalizeText('infostand.button.respect', [ 'count' ], [ respectsLeft.toString() ]) } + } + { !userData.isIgnored && + processAction('ignore') }> + { LocalizeText('infostand.button.ignore') } + } + { userData.isIgnored && + processAction('unignore') }> + { LocalizeText('infostand.button.unignore') } + } + processAction('report') }> + { LocalizeText('infostand.button.report') } + + { moderateMenuHasContent && + processAction('moderate') }> + + { LocalizeText('infostand.link.moderate') } + } + { userData.isAmbassador && + processAction('ambassador') }> + + { LocalizeText('infostand.link.ambassador') } + } + { canGiveHandItem && processAction('pass_hand_item') }> + { LocalizeText('avatar.widget.pass_hand_item') } + } + } + { (mode === MODE_MODERATE) && + <> + processAction('kick') }> + { LocalizeText('infostand.button.kick') } + + processAction('mute') }> + + { LocalizeText('infostand.button.mute') } + + processAction('ban') }> + + { LocalizeText('infostand.button.ban') } + + { isShowGiveRights && + processAction('give_rights') }> + { LocalizeText('infostand.button.giverights') } + } + { isShowRemoveRights && + processAction('remove_rights') }> + { LocalizeText('infostand.button.removerights') } + } + processAction('back') }> + + { LocalizeText('generic.back') } + + } + { (mode === MODE_MODERATE_BAN) && + <> + processAction('ban_hour') }> + { LocalizeText('infostand.button.ban_hour') } + + processAction('ban_day') }> + { LocalizeText('infostand.button.ban_day') } + + processAction('ban_perm') }> + { LocalizeText('infostand.button.perm_ban') } + + processAction('back_moderate') }> + + { LocalizeText('generic.back') } + + } + { (mode === MODE_MODERATE_MUTE) && + <> + processAction('mute_2min') }> + { LocalizeText('infostand.button.mute_2min') } + + processAction('mute_5min') }> + { LocalizeText('infostand.button.mute_5min') } + + processAction('mute_10min') }> + { LocalizeText('infostand.button.mute_10min') } + + processAction('back_moderate') }> + + { LocalizeText('generic.back') } + + } + { (mode === MODE_AMBASSADOR) && + <> + processAction('ambassador_alert') }> + { LocalizeText('infostand.button.alert') } + + processAction('ambassador_kick') }> + { LocalizeText('infostand.button.kick') } + + processAction('ambassador_mute') }> + + { LocalizeText('infostand.button.mute') } + + processAction('back') }> + + { LocalizeText('generic.back') } + + } + { (mode === MODE_AMBASSADOR_MUTE) && + <> + processAction('ambassador_mute_2min') }> + { LocalizeText('infostand.button.mute_2min') } + + processAction('ambassador_mute_10min') }> + { LocalizeText('infostand.button.mute_10min') } + + processAction('ambassador_mute_60min') }> + { LocalizeText('infostand.button.mute_60min') } + + processAction('ambassador_mute_18hr') }> + { LocalizeText('infostand.button.mute_18hour') } + + processAction('back_ambassador') }> + + { LocalizeText('generic.back') } + + } + + ); } diff --git a/src/views/room/widgets/avatar-info/views/name/AvatarInfoWidgetNameView.tsx b/src/views/room/widgets/avatar-info/views/name/AvatarInfoWidgetNameView.tsx index 27a9a574..786741dd 100644 --- a/src/views/room/widgets/avatar-info/views/name/AvatarInfoWidgetNameView.tsx +++ b/src/views/room/widgets/avatar-info/views/name/AvatarInfoWidgetNameView.tsx @@ -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 = props => @@ -14,10 +13,10 @@ export const AvatarInfoWidgetNameView: FC = props }, [ nameData ]); return ( - - + +
{ nameData.name } - +
); } diff --git a/src/views/room/widgets/avatar-info/views/own-avatar/AvatarInfoWidgetOwnAvatarView.tsx b/src/views/room/widgets/avatar-info/views/own-avatar/AvatarInfoWidgetOwnAvatarView.tsx index b722a725..ebbf725e 100644 --- a/src/views/room/widgets/avatar-info/views/own-avatar/AvatarInfoWidgetOwnAvatarView.tsx +++ b/src/views/room/widgets/avatar-info/views/own-avatar/AvatarInfoWidgetOwnAvatarView.tsx @@ -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 = props => { @@ -110,91 +109,98 @@ export const AvatarInfoWidgetOwnAvatarView: FC { userData.name }
- - { (mode === MODE_NORMAL) && - <> - processAction('decorate') }> - { LocalizeText('widget.avatar.decorate') } - - processAction('change_looks') }> - { LocalizeText('widget.memenu.myclothes') } - - { (HasHabboClub() && !isRidingHorse) && - processAction('dance_menu') }> - { LocalizeText('widget.memenu.dance') } - } - { (!isDancing && !HasHabboClub() && !isRidingHorse) && - processAction('dance') }> - { LocalizeText('widget.memenu.dance') } - } - { (isDancing && !HasHabboClub() && !isRidingHorse) && - processAction('dance_stop') }> - { LocalizeText('widget.memenu.dance.stop') } - } - processAction('expressions') }> - { LocalizeText('infostand.link.expressions') } - - processAction('signs') }> - { LocalizeText('infostand.show.signs') } - - { (userData.carryItem > 0) && - processAction('drop_carry_item') }> - { LocalizeText('avatar.widget.drop_hand_item') } - } - } - { (mode === MODE_CLUB_DANCES) && - <> - { isDancing && - processAction('dance_stop') }> - { LocalizeText('widget.memenu.dance.stop') } - } - processAction('dance_1') }> - { LocalizeText('widget.memenu.dance1') } - - processAction('dance_2') }> - { LocalizeText('widget.memenu.dance2') } - - processAction('dance_3') }> - { LocalizeText('widget.memenu.dance3') } - - processAction('dance_4') }> - { LocalizeText('widget.memenu.dance4') } - - processAction('back') }> - { LocalizeText('generic.back') } - - } - { (mode === MODE_EXPRESSIONS) && - <> - { (GetOwnPosture() === AvatarAction.POSTURE_STAND) && - processAction('sit') }> - { LocalizeText('widget.memenu.sit') } - } - { GetCanStandUp() && - processAction('stand') }> - { LocalizeText('widget.memenu.stand') } - } - { GetCanUseExpression() && - processAction('wave') }> - { LocalizeText('widget.memenu.wave') } - } - { (GetCanUseExpression() && HasHabboVip()) && - processAction('laugh') }> - { LocalizeText('widget.memenu.laugh') } - } - { (GetCanUseExpression() && HasHabboVip()) && - processAction('blow') }> - { LocalizeText('widget.memenu.blow') } - } - processAction('idle') }> - { LocalizeText('widget.memenu.idle') } - - processAction('back') }> - { LocalizeText('generic.back') } - - } - { (mode === MODE_SIGNS) && - <> + { (mode === MODE_NORMAL) && + <> + processAction('decorate') }> + { LocalizeText('widget.avatar.decorate') } + + processAction('change_looks') }> + { LocalizeText('widget.memenu.myclothes') } + + { (HasHabboClub() && !isRidingHorse) && + processAction('dance_menu') }> + + { LocalizeText('widget.memenu.dance') } + } + { (!isDancing && !HasHabboClub() && !isRidingHorse) && + processAction('dance') }> + { LocalizeText('widget.memenu.dance') } + } + { (isDancing && !HasHabboClub() && !isRidingHorse) && + processAction('dance_stop') }> + { LocalizeText('widget.memenu.dance.stop') } + } + processAction('expressions') }> + + { LocalizeText('infostand.link.expressions') } + + processAction('signs') }> + + { LocalizeText('infostand.show.signs') } + + { (userData.carryItem > 0) && + processAction('drop_carry_item') }> + { LocalizeText('avatar.widget.drop_hand_item') } + } + } + { (mode === MODE_CLUB_DANCES) && + <> + { isDancing && + processAction('dance_stop') }> + { LocalizeText('widget.memenu.dance.stop') } + } + processAction('dance_1') }> + { LocalizeText('widget.memenu.dance1') } + + processAction('dance_2') }> + { LocalizeText('widget.memenu.dance2') } + + processAction('dance_3') }> + { LocalizeText('widget.memenu.dance3') } + + processAction('dance_4') }> + { LocalizeText('widget.memenu.dance4') } + + processAction('back') }> + + { LocalizeText('generic.back') } + + } + { (mode === MODE_EXPRESSIONS) && + <> + { (GetOwnPosture() === AvatarAction.POSTURE_STAND) && + processAction('sit') }> + { LocalizeText('widget.memenu.sit') } + } + { GetCanStandUp() && + processAction('stand') }> + { LocalizeText('widget.memenu.stand') } + } + { GetCanUseExpression() && + processAction('wave') }> + { LocalizeText('widget.memenu.wave') } + } + { GetCanUseExpression() && + processAction('laugh') }> + + { LocalizeText('widget.memenu.laugh') } + } + { GetCanUseExpression() && + processAction('blow') }> + + { LocalizeText('widget.memenu.blow') } + } + processAction('idle') }> + { LocalizeText('widget.memenu.idle') } + + processAction('back') }> + + { LocalizeText('generic.back') } + + } + { (mode === MODE_SIGNS) && + <> +
processAction('sign_1') }> 1 @@ -204,6 +210,8 @@ export const AvatarInfoWidgetOwnAvatarView: FC processAction('sign_3') }> 3 +
+
processAction('sign_4') }> 4 @@ -213,6 +221,8 @@ export const AvatarInfoWidgetOwnAvatarView: FC processAction('sign_6') }> 6 +
+
processAction('sign_7') }> 7 @@ -222,6 +232,8 @@ export const AvatarInfoWidgetOwnAvatarView: FC processAction('sign_9') }> 9 +
+
processAction('sign_10') }> 10 @@ -231,6 +243,8 @@ export const AvatarInfoWidgetOwnAvatarView: FC processAction('sign_15') }> +
+
processAction('sign_12') }> @@ -240,6 +254,8 @@ export const AvatarInfoWidgetOwnAvatarView: FC processAction('sign_17') }> +
+
processAction('sign_16') }> @@ -249,8 +265,12 @@ export const AvatarInfoWidgetOwnAvatarView: FC processAction('sign_11') }> - } - +
+ processAction('back') }> + + { LocalizeText('generic.back') } + + }
); } diff --git a/src/views/room/widgets/avatar-info/views/rentable-bot/AvatarInfoWidgetRentableBotView.tsx b/src/views/room/widgets/avatar-info/views/rentable-bot/AvatarInfoWidgetRentableBotView.tsx index 8fcfd5e1..77792e92 100644 --- a/src/views/room/widgets/avatar-info/views/rentable-bot/AvatarInfoWidgetRentableBotView.tsx +++ b/src/views/room/widgets/avatar-info/views/rentable-bot/AvatarInfoWidgetRentableBotView.tsx @@ -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 = 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({ 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 ( + + + { rentableBotData.name } + + { (mode === MODE_NORMAL) && + <> + { ((rentableBotData.botSkills.indexOf(BotSkillsEnum.DONATE_TO_ALL) >= 0) && canControl) && + processAction('donate_to_all') }> + { LocalizeText('avatar.widget.donate_to_all') } + } + { ((rentableBotData.botSkills.indexOf(BotSkillsEnum.DONATE_TO_USER) >= 0) && canControl) && + processAction('donate_to_user') }> + { LocalizeText('avatar.widget.donate_to_user') } + } + { ((rentableBotData.botSkills.indexOf(BotSkillsEnum.CHANGE_BOT_NAME) >= 0) && canControl) && + processAction('change_bot_name') }> + { LocalizeText('avatar.widget.change_bot_name') } + } + { ((rentableBotData.botSkills.indexOf(BotSkillsEnum.CHANGE_BOT_MOTTO) >= 0) && canControl) && + processAction('change_bot_motto') }> + { LocalizeText('avatar.widget.change_bot_motto') } + } + { ((rentableBotData.botSkills.indexOf(BotSkillsEnum.DRESS_UP) >= 0) && canControl) && + processAction('dress_up') }> + { LocalizeText('avatar.widget.dress_up') } + } + { ((rentableBotData.botSkills.indexOf(BotSkillsEnum.RANDOM_WALK) >= 0) && canControl) && + processAction('random_walk') }> + { LocalizeText('avatar.widget.random_walk') } + } + { ((rentableBotData.botSkills.indexOf(BotSkillsEnum.SETUP_CHAT) >= 0) && canControl) && + processAction('setup_chat') }> + { LocalizeText('avatar.widget.setup_chat') } + } + { ((rentableBotData.botSkills.indexOf(BotSkillsEnum.DANCE) >= 0) && canControl) && + processAction('dance') }> + { LocalizeText('avatar.widget.dance') } + } + { ((rentableBotData.botSkills.indexOf(BotSkillsEnum.NO_PICK_UP) === -1) && canControl) && + processAction('pick') }> + { LocalizeText('avatar.widget.pick_up') } + } + } + { (mode === MODE_CHANGE_NAME) && + +

{ LocalizeText('bot.skill.name.configuration.new.name') }

+ setNewName(event.target.value) } /> +
+ + +
+
} +
+ ); } diff --git a/src/views/room/widgets/avatar-info/views/rentable-bot/AvatarInfoWidgetRentableBotView.types.ts b/src/views/room/widgets/avatar-info/views/rentable-bot/AvatarInfoWidgetRentableBotView.types.ts index d7b8912d..7bfba45b 100644 --- a/src/views/room/widgets/avatar-info/views/rentable-bot/AvatarInfoWidgetRentableBotView.types.ts +++ b/src/views/room/widgets/avatar-info/views/rentable-bot/AvatarInfoWidgetRentableBotView.types.ts @@ -5,3 +5,10 @@ export interface AvatarInfoWidgetRentableBotViewProps rentableBotData: RoomWidgetUpdateInfostandRentableBotEvent; close: () => void; } + +export interface BotChatOptions +{ + automaticChat: boolean; + chatDelay: number; + mixSentences: boolean; +} diff --git a/src/views/room/widgets/context-menu/ContextMenu.scss b/src/views/room/widgets/context-menu/ContextMenu.scss index 8f1fded7..e31271cb 100644 --- a/src/views/room/widgets/context-menu/ContextMenu.scss +++ b/src/views/room/widgets/context-menu/ContextMenu.scss @@ -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; + } + } } diff --git a/src/views/room/widgets/context-menu/ContextMenuView.tsx b/src/views/room/widgets/context-menu/ContextMenuView.tsx index 7dd3463e..328e425c 100644 --- a/src/views/room/widgets/context-menu/ContextMenuView.tsx +++ b/src/views/room/widgets/context-menu/ContextMenuView.tsx @@ -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 = 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(null); const [ opacity, setOpacity ] = useState(1); const [ isFading, setIsFading ] = useState(false); const [ fadeTime, setFadeTime ] = useState(0); @@ -16,19 +18,19 @@ export const ContextMenuView: FC = 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 = props => }); }, [ objectId, category, isFading, close ]); + useEffect(() => + { + setStack(new FixedSizeStack(25)); + }, []); + useEffect(() => { GetTicker().add(update); @@ -73,7 +80,7 @@ export const ContextMenuView: FC = props => }, [ fades ]); return ( -
+
{ children }
); diff --git a/src/views/room/widgets/context-menu/ContextMenuView.types.ts b/src/views/room/widgets/context-menu/ContextMenuView.types.ts index d9ba5843..e3650194 100644 --- a/src/views/room/widgets/context-menu/ContextMenuView.types.ts +++ b/src/views/room/widgets/context-menu/ContextMenuView.types.ts @@ -3,5 +3,6 @@ export interface ContextMenuViewProps objectId: number; category: number; fades?: boolean; + className?: string; close: () => void; } diff --git a/src/views/room/widgets/context-menu/views/list-item/ContextMenuListItemView.tsx b/src/views/room/widgets/context-menu/views/list-item/ContextMenuListItemView.tsx index 6220e916..0b4f3bcb 100644 --- a/src/views/room/widgets/context-menu/views/list-item/ContextMenuListItemView.tsx +++ b/src/views/room/widgets/context-menu/views/list-item/ContextMenuListItemView.tsx @@ -1,15 +1,20 @@ -import { FC } from 'react'; +import { FC, MouseEvent, useCallback } from 'react'; import { ContextMenuListItemViewProps } from './ContextMenuListItemView.types'; export const ContextMenuListItemView: FC = props => { - const { onClick = null, children = null } = props; + const { className = '', canSelect = true, onClick = null, children = null } = props; + + const handleClick = useCallback((event: MouseEvent) => + { + if(!canSelect) return; + + onClick(event); + }, [ canSelect, onClick ]); return ( -
-
- { children } -
+
+ { children }
) } diff --git a/src/views/room/widgets/context-menu/views/list-item/ContextMenuListItemView.types.ts b/src/views/room/widgets/context-menu/views/list-item/ContextMenuListItemView.types.ts index 21878f3a..32b49235 100644 --- a/src/views/room/widgets/context-menu/views/list-item/ContextMenuListItemView.types.ts +++ b/src/views/room/widgets/context-menu/views/list-item/ContextMenuListItemView.types.ts @@ -2,5 +2,7 @@ import { MouseEvent } from 'react'; export interface ContextMenuListItemViewProps { + className?: string; + canSelect?: boolean; onClick: (event: MouseEvent) => void; } diff --git a/src/views/room/widgets/context-menu/views/list/ContextMenuListView.tsx b/src/views/room/widgets/context-menu/views/list/ContextMenuListView.tsx index d90c8f6d..b9256ba8 100644 --- a/src/views/room/widgets/context-menu/views/list/ContextMenuListView.tsx +++ b/src/views/room/widgets/context-menu/views/list/ContextMenuListView.tsx @@ -6,7 +6,7 @@ export const ContextMenuListView: FC = props => const { columns = 1, children = null } = props; return ( -
+
{ children }
); diff --git a/src/views/shared/currency-icon/CurrencyIcon.types.ts b/src/views/shared/currency-icon/CurrencyIcon.types.ts index bb4bd334..b1cd5a72 100644 --- a/src/views/shared/currency-icon/CurrencyIcon.types.ts +++ b/src/views/shared/currency-icon/CurrencyIcon.types.ts @@ -1,4 +1,4 @@ export interface CurrencyIconProps { - type: number; + type: number | string; }