mirror of
https://github.com/billsonnn/nitro-react.git
synced 2025-01-18 13:26:27 +01:00
Add flood block
This commit is contained in:
parent
c02f088045
commit
98b16b66d7
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -13,5 +13,6 @@
|
|||||||
"files.eol": "\n",
|
"files.eol": "\n",
|
||||||
"files.insertFinalNewline": true,
|
"files.insertFinalNewline": true,
|
||||||
"files.trimFinalNewlines": true,
|
"files.trimFinalNewlines": true,
|
||||||
"editor.wordWrap": "on"
|
"editor.wordWrap": "on",
|
||||||
|
"emmet.showExpandedAbbreviation": "never"
|
||||||
}
|
}
|
||||||
|
@ -31,13 +31,13 @@
|
|||||||
"slot.7.conf": ""
|
"slot.7.conf": ""
|
||||||
},
|
},
|
||||||
"images": {
|
"images": {
|
||||||
"background": "${asset.url}images/reception/stretch_blue.png",
|
"background": "${asset.url}/images/reception/stretch_blue.png",
|
||||||
"background.colour": "#6eadc8",
|
"background.colour": "#6eadc8",
|
||||||
"sun": "${asset.url}images/reception/sun.png",
|
"sun": "${asset.url}/images/reception/sun.png",
|
||||||
"drape": "${asset.url}images/reception/drape.png",
|
"drape": "${asset.url}/images/reception/drape.png",
|
||||||
"left": "${asset.url}images/reception/ts.png",
|
"left": "${asset.url}/images/reception/ts.png",
|
||||||
"right": "${asset.url}images/reception/US_right.png",
|
"right": "${asset.url}/images/reception/US_right.png",
|
||||||
"right.repeat": "${asset.url}images/reception/US_top_right.png"
|
"right.repeat": "${asset.url}/images/reception/US_top_right.png"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"achievements.unseen.ignored": [
|
"achievements.unseen.ignored": [
|
||||||
|
@ -0,0 +1,56 @@
|
|||||||
|
import { RoomDataParser } from '@nitrots/nitro-renderer';
|
||||||
|
import { FC, MouseEvent, useEffect, useState } from 'react';
|
||||||
|
import { Overlay, Popover } from 'react-bootstrap';
|
||||||
|
import { Base, NitroCardContentView } from '../../../../common';
|
||||||
|
import { BatchUpdates } from '../../../../hooks';
|
||||||
|
|
||||||
|
interface NavigatorSearchResultItemInfoViewProps
|
||||||
|
{
|
||||||
|
roomData: RoomDataParser;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const NavigatorSearchResultItemInfoView: FC<NavigatorSearchResultItemInfoViewProps> = props =>
|
||||||
|
{
|
||||||
|
const { roomData = null } = props;
|
||||||
|
const [ target, setTarget ] = useState<(EventTarget & HTMLElement)>(null);
|
||||||
|
const [ isVisible, setIsVisible ] = useState(false);
|
||||||
|
|
||||||
|
const toggle = (event: MouseEvent<HTMLElement>) =>
|
||||||
|
{
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
BatchUpdates(() =>
|
||||||
|
{
|
||||||
|
let visible = false;
|
||||||
|
|
||||||
|
setIsVisible(prevValue =>
|
||||||
|
{
|
||||||
|
visible = !prevValue;
|
||||||
|
|
||||||
|
return visible;
|
||||||
|
});
|
||||||
|
|
||||||
|
if(visible) setTarget((event.target as (EventTarget & HTMLElement)));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
if(isVisible) return;
|
||||||
|
|
||||||
|
setTarget(null);
|
||||||
|
}, [ isVisible ]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Base pointer className="icon icon-navigator-info" onClick={ toggle } />
|
||||||
|
<Overlay show={ isVisible } target={ target } placement="right">
|
||||||
|
<Popover>
|
||||||
|
<NitroCardContentView overflow="hidden" className="bg-transparent">
|
||||||
|
do it
|
||||||
|
</NitroCardContentView>
|
||||||
|
</Popover>
|
||||||
|
</Overlay>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
@ -5,6 +5,7 @@ import { CreateRoomSession, GetSessionDataManager, TryVisitRoom } from '../../..
|
|||||||
import { Flex, LayoutGridItemProps, Text } from '../../../../common';
|
import { Flex, LayoutGridItemProps, Text } from '../../../../common';
|
||||||
import { UpdateDoorStateEvent } from '../../../../events';
|
import { UpdateDoorStateEvent } from '../../../../events';
|
||||||
import { DispatchUiEvent } from '../../../../hooks';
|
import { DispatchUiEvent } from '../../../../hooks';
|
||||||
|
import { NavigatorSearchResultItemInfoView } from './NavigatorSearchResultItemInfoView';
|
||||||
|
|
||||||
export interface NavigatorSearchResultItemViewProps extends LayoutGridItemProps
|
export interface NavigatorSearchResultItemViewProps extends LayoutGridItemProps
|
||||||
{
|
{
|
||||||
@ -76,7 +77,7 @@ export const NavigatorSearchResultItemView: FC<NavigatorSearchResultItemViewProp
|
|||||||
</Flex>
|
</Flex>
|
||||||
<Text truncate className="flex-grow-1">{ roomData.roomName }</Text>
|
<Text truncate className="flex-grow-1">{ roomData.roomName }</Text>
|
||||||
<Flex reverse alignItems="center" gap={ 1 }>
|
<Flex reverse alignItems="center" gap={ 1 }>
|
||||||
<i className="icon icon-navigator-info" onClick={ openInfo } />
|
<NavigatorSearchResultItemInfoView roomData={ roomData } />
|
||||||
{ roomData.habboGroupId > 0 && <i className="icon icon-navigator-room-group" /> }
|
{ roomData.habboGroupId > 0 && <i className="icon icon-navigator-room-group" /> }
|
||||||
{ (roomData.doorMode !== RoomDataParser.OPEN_STATE) &&
|
{ (roomData.doorMode !== RoomDataParser.OPEN_STATE) &&
|
||||||
<i className={ ('icon icon-navigator-room-' + ((roomData.doorMode === RoomDataParser.DOORBELL_STATE) ? 'locked' : (roomData.doorMode === RoomDataParser.PASSWORD_STATE) ? 'password' : (roomData.doorMode === RoomDataParser.INVISIBLE_STATE) ? 'invisible' : '')) } /> }
|
<i className={ ('icon icon-navigator-room-' + ((roomData.doorMode === RoomDataParser.DOORBELL_STATE) ? 'locked' : (roomData.doorMode === RoomDataParser.PASSWORD_STATE) ? 'password' : (roomData.doorMode === RoomDataParser.INVISIBLE_STATE) ? 'invisible' : '')) } /> }
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { HabboClubLevelEnum, RoomControllerLevel } from '@nitrots/nitro-renderer';
|
import { HabboClubLevelEnum, RoomControllerLevel } from '@nitrots/nitro-renderer';
|
||||||
import { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { createPortal } from 'react-dom';
|
import { createPortal } from 'react-dom';
|
||||||
import { GetClubMemberLevel, GetConfiguration, GetSessionDataManager, LocalizeText, RoomWidgetChatMessage, RoomWidgetChatTypingMessage, RoomWidgetUpdateChatInputContentEvent, RoomWidgetUpdateInfostandUserEvent, RoomWidgetUpdateRoomObjectEvent } from '../../../../api';
|
import { GetClubMemberLevel, GetConfiguration, GetSessionDataManager, LocalizeText, RoomWidgetChatMessage, RoomWidgetChatTypingMessage, RoomWidgetFloodControlEvent, RoomWidgetUpdateChatInputContentEvent, RoomWidgetUpdateInfostandUserEvent, RoomWidgetUpdateRoomObjectEvent } from '../../../../api';
|
||||||
import { UseEventDispatcherHook } from '../../../../hooks';
|
import { Text } from '../../../../common';
|
||||||
|
import { BatchUpdates, UseEventDispatcherHook } from '../../../../hooks';
|
||||||
import { useRoomContext } from '../../RoomContext';
|
import { useRoomContext } from '../../RoomContext';
|
||||||
import { ChatInputStyleSelectorView } from './ChatInputStyleSelectorView';
|
import { ChatInputStyleSelectorView } from './ChatInputStyleSelectorView';
|
||||||
|
|
||||||
@ -15,6 +16,8 @@ export const ChatInputView: FC<{}> = props =>
|
|||||||
const [ isIdle, setIsIdle ] = useState(false);
|
const [ isIdle, setIsIdle ] = useState(false);
|
||||||
const [ chatStyleId, setChatStyleId ] = useState(GetSessionDataManager().chatStyle);
|
const [ chatStyleId, setChatStyleId ] = useState(GetSessionDataManager().chatStyle);
|
||||||
const [ needsChatStyleUpdate, setNeedsChatStyleUpdate ] = useState(false);
|
const [ needsChatStyleUpdate, setNeedsChatStyleUpdate ] = useState(false);
|
||||||
|
const [ floodBlocked, setFloodBlocked ] = useState(false);
|
||||||
|
const [ floodBlockedSeconds, setFloodBlockedSeconds ] = useState(0);
|
||||||
const { eventDispatcher = null, widgetHandler = null } = useRoomContext();
|
const { eventDispatcher = null, widgetHandler = null } = useRoomContext();
|
||||||
const inputRef = useRef<HTMLInputElement>();
|
const inputRef = useRef<HTMLInputElement>();
|
||||||
|
|
||||||
@ -146,7 +149,7 @@ export const ChatInputView: FC<{}> = props =>
|
|||||||
|
|
||||||
const onKeyDownEvent = useCallback((event: KeyboardEvent) =>
|
const onKeyDownEvent = useCallback((event: KeyboardEvent) =>
|
||||||
{
|
{
|
||||||
if(!inputRef.current || anotherInputHasFocus()) return;
|
if(floodBlocked || !inputRef.current || anotherInputHasFocus()) return;
|
||||||
|
|
||||||
if(document.activeElement !== inputRef.current) setInputFocus();
|
if(document.activeElement !== inputRef.current) setInputFocus();
|
||||||
|
|
||||||
@ -174,7 +177,7 @@ export const ChatInputView: FC<{}> = props =>
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
}, [ inputRef, chatModeIdWhisper, anotherInputHasFocus, setInputFocus, checkSpecialKeywordForInput, sendChatValue ]);
|
}, [ floodBlocked, inputRef, chatModeIdWhisper, anotherInputHasFocus, setInputFocus, checkSpecialKeywordForInput, sendChatValue ]);
|
||||||
|
|
||||||
const onRoomWidgetRoomObjectUpdateEvent = useCallback((event: RoomWidgetUpdateRoomObjectEvent) =>
|
const onRoomWidgetRoomObjectUpdateEvent = useCallback((event: RoomWidgetUpdateRoomObjectEvent) =>
|
||||||
{
|
{
|
||||||
@ -205,6 +208,17 @@ export const ChatInputView: FC<{}> = props =>
|
|||||||
|
|
||||||
UseEventDispatcherHook(RoomWidgetUpdateChatInputContentEvent.CHAT_INPUT_CONTENT, eventDispatcher, onRoomWidgetChatInputContentUpdateEvent);
|
UseEventDispatcherHook(RoomWidgetUpdateChatInputContentEvent.CHAT_INPUT_CONTENT, eventDispatcher, onRoomWidgetChatInputContentUpdateEvent);
|
||||||
|
|
||||||
|
const onRoomWidgetFloodControlEvent = useCallback((event: RoomWidgetFloodControlEvent) =>
|
||||||
|
{
|
||||||
|
BatchUpdates(() =>
|
||||||
|
{
|
||||||
|
setFloodBlocked(true);
|
||||||
|
setFloodBlockedSeconds(event.seconds);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
UseEventDispatcherHook(RoomWidgetFloodControlEvent.FLOOD_CONTROL, eventDispatcher, onRoomWidgetFloodControlEvent);
|
||||||
|
|
||||||
const selectChatStyleId = useCallback((styleId: number) =>
|
const selectChatStyleId = useCallback((styleId: number) =>
|
||||||
{
|
{
|
||||||
setChatStyleId(styleId);
|
setChatStyleId(styleId);
|
||||||
@ -300,6 +314,32 @@ export const ChatInputView: FC<{}> = props =>
|
|||||||
return () => clearTimeout(timeout);
|
return () => clearTimeout(timeout);
|
||||||
}, [ isIdle ]);
|
}, [ isIdle ]);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
if(!floodBlocked) return;
|
||||||
|
|
||||||
|
let seconds = 0;
|
||||||
|
|
||||||
|
const interval = setInterval(() =>
|
||||||
|
{
|
||||||
|
setFloodBlockedSeconds(prevValue =>
|
||||||
|
{
|
||||||
|
seconds = ((prevValue || 0) - 1);
|
||||||
|
|
||||||
|
return seconds;
|
||||||
|
});
|
||||||
|
|
||||||
|
if(seconds < 0)
|
||||||
|
{
|
||||||
|
clearInterval(interval);
|
||||||
|
|
||||||
|
setFloodBlocked(false);
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
return () => clearInterval(interval);
|
||||||
|
}, [ floodBlocked ])
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
document.body.addEventListener('keydown', onKeyDownEvent);
|
document.body.addEventListener('keydown', onKeyDownEvent);
|
||||||
@ -320,8 +360,11 @@ export const ChatInputView: FC<{}> = props =>
|
|||||||
return (
|
return (
|
||||||
createPortal(
|
createPortal(
|
||||||
<div className="nitro-chat-input-container">
|
<div className="nitro-chat-input-container">
|
||||||
<div className="input-sizer">
|
<div className="input-sizer align-items-center">
|
||||||
<input ref={ inputRef } type="text" className="chat-input" placeholder={ LocalizeText('widgets.chatinput.default') } value={ chatValue } maxLength={ maxChatLength } onChange={ event => updateChatInput(event.target.value) } onMouseDown={ event => setInputFocus() } />
|
{ !floodBlocked &&
|
||||||
|
<input ref={ inputRef } type="text" className="chat-input" placeholder={ LocalizeText('widgets.chatinput.default') } value={ chatValue } maxLength={ maxChatLength } onChange={ event => updateChatInput(event.target.value) } onMouseDown={ event => setInputFocus() } /> }
|
||||||
|
{ floodBlocked &&
|
||||||
|
<Text variant="danger">{ LocalizeText('chat.input.alert.flood', [ 'time' ], [ floodBlockedSeconds.toString() ]) } </Text>}
|
||||||
</div>
|
</div>
|
||||||
<ChatInputStyleSelectorView chatStyleId={ chatStyleId } chatStyleIds={ chatStyleIds } selectChatStyleId={ selectChatStyleId } />
|
<ChatInputStyleSelectorView chatStyleId={ chatStyleId } chatStyleIds={ chatStyleIds } selectChatStyleId={ selectChatStyleId } />
|
||||||
</div>, document.getElementById('toolbar-chat-input-container'))
|
</div>, document.getElementById('toolbar-chat-input-container'))
|
||||||
|
Loading…
Reference in New Issue
Block a user