mirror of
https://github.com/duckietm/Nitro-Cool-UI.git
synced 2025-06-21 22:36:58 +00:00
New: Emoji added
This commit is contained in:
parent
27c3adbf06
commit
d2daaf6087
@ -0,0 +1,68 @@
|
||||
import { FC, MouseEvent, useEffect, useRef, useState } from 'react';
|
||||
import { Overlay, Popover } from 'react-bootstrap';
|
||||
import { Base, Flex, Grid, NitroCardContentView } from '../../../../common';
|
||||
import { emojiList } from './EmojiList';
|
||||
|
||||
interface ChatInputEmojiSelectorViewProps
|
||||
{
|
||||
addChatEmoji: (emoji: string) => void;
|
||||
}
|
||||
|
||||
export const ChatInputEmojiSelectorView: FC<ChatInputEmojiSelectorViewProps> = props =>
|
||||
{
|
||||
const { addChatEmoji = null } = props;
|
||||
const [ selectorVisible, setSelectorVisible ] = useState(false);
|
||||
const [ target, setTarget ] = useState<(EventTarget & HTMLElement)>(null);
|
||||
const iconRef = useRef<HTMLDivElement>(null);
|
||||
const componentRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
const className = 'emoji-icon';
|
||||
if (componentRef.current && !componentRef.current.contains(event.target as Node) && !(event.target as Element).classList.contains(className)) {
|
||||
setSelectorVisible(false);
|
||||
document.removeEventListener('mousedown', handleClickOutside as any);
|
||||
setTarget(null);
|
||||
}
|
||||
};
|
||||
|
||||
const selectEmoji = (emoji: string) =>
|
||||
{
|
||||
addChatEmoji(emoji);
|
||||
}
|
||||
|
||||
const toggleSelector = (event: MouseEvent<HTMLElement>) =>
|
||||
{
|
||||
setSelectorVisible(prevValue => !prevValue);
|
||||
}
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(selectorVisible) {
|
||||
document.addEventListener('mousedown', handleClickOutside as any);
|
||||
}
|
||||
else {
|
||||
setTarget(null);
|
||||
}
|
||||
}, [ componentRef, selectorVisible ]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Base pointer onClick={toggleSelector} innerRef={iconRef}>🙂</Base>
|
||||
<Overlay show={selectorVisible} target={iconRef} placement="top">
|
||||
<Popover className="nitro-chat-style-selector-container">
|
||||
<NitroCardContentView overflow="hidden" className="bg-transparent">
|
||||
<Grid columnCount={4} overflow="auto">
|
||||
{emojiList && emojiList.length > 0 && emojiList.map((emojiId) => {
|
||||
return (
|
||||
<Flex center pointer key={emojiId} onClick={(event) => selectEmoji(emojiId)}>
|
||||
<Base className="emoji" textColor="black" style={{ fontSize: '20px' }}>{emojiId}</Base>
|
||||
</Flex>
|
||||
);
|
||||
})}
|
||||
</Grid>
|
||||
</NitroCardContentView>
|
||||
</Popover>
|
||||
</Overlay>
|
||||
</>
|
||||
);
|
||||
}
|
@ -1,124 +0,0 @@
|
||||
import { FC, KeyboardEvent, MouseEvent, useEffect, useState } from 'react';
|
||||
import { Button, Overlay, Popover } from 'react-bootstrap';
|
||||
import { FaSearch } from 'react-icons/fa';
|
||||
import { GetSessionDataManager, LocalizeText } from '../../../../api';
|
||||
import { Base, Flex, Grid, NitroCardContentView } from '../../../../common';
|
||||
import { useChatInputWidget, useMessenger, useSessionInfo } from '../../../../hooks';
|
||||
|
||||
|
||||
export const ChatInputStickersSelectorConsolaView: FC<{}> = props =>
|
||||
{
|
||||
const [ target, setTarget ] = useState<(EventTarget & HTMLElement)>(null);
|
||||
const [ selectorVisible, setSelectorVisible ] = useState(false);
|
||||
const [ stickers, setStickers ] = useState(null)
|
||||
const [ stickersGiphy, setStickersGiphy ] = useState(null)
|
||||
const [ showNativeStickers, setShowNativeStickers ] = useState(true)
|
||||
const [ showGiphyStickers, setShowGiphyStickers ] = useState(false)
|
||||
const { sendChat = null } = useChatInputWidget();
|
||||
var evadeClosing = false;
|
||||
const { chatStyleId = 0 } = useSessionInfo();
|
||||
const { visibleThreads = [], activeThread = null, getMessageThread = null, sendMessage = null, setActiveThreadId = null, closeThread = null } = useMessenger();
|
||||
|
||||
function searchStickers(strSearch)
|
||||
{
|
||||
evadeClosing = true;
|
||||
if(strSearch.length == 0){
|
||||
setShowGiphyStickers(false)
|
||||
setShowNativeStickers(true)
|
||||
return;
|
||||
}
|
||||
|
||||
fetch('https://api.giphy.com/v1/gifs/search?api_key=4qwMrfzYMYVl1lhhl3wDaVxDNWAzAv6s&q=' + strSearch + '&limit=25&offset=0&rating=g&lang=en')
|
||||
.then((response) => response.json())
|
||||
.then((result) =>
|
||||
{
|
||||
setStickersGiphy(result);
|
||||
setShowNativeStickers(false)
|
||||
setShowGiphyStickers(true)
|
||||
})
|
||||
}
|
||||
const onKeyDown = (event: KeyboardEvent<HTMLInputElement>) =>
|
||||
{
|
||||
if(event.key !== 'Enter') return;
|
||||
searchStickers(inputText);
|
||||
}
|
||||
function sendSticker(sticker){
|
||||
const stickerArgs = sticker.split(".gif");
|
||||
sendMessage(activeThread, GetSessionDataManager().userId, stickerArgs[0]);
|
||||
}
|
||||
|
||||
function NativeStickers(){
|
||||
if(stickers == null) return null;
|
||||
else return(
|
||||
<div></div>
|
||||
);
|
||||
}
|
||||
|
||||
function GiphyStickers(){
|
||||
if(stickersGiphy == null) return null;
|
||||
else return(
|
||||
<Grid columnCount={ 4 } overflow="auto" id="giphyStickers">
|
||||
{ stickersGiphy.data.map(sticker => (
|
||||
// eslint-disable-next-line react/jsx-key
|
||||
<Base key={ sticker.images.downsized_medium.url}>
|
||||
<img src={ sticker.images.downsized_medium.url } width={ 50 } height={ 50 } className="sticker-img" onClick={ e => sendSticker(sticker.images.fixed_height_small.url) }/>
|
||||
</Base>
|
||||
)) }
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
const toggleSelector = (event: MouseEvent<HTMLElement>) =>
|
||||
{
|
||||
if(evadeClosing) return;
|
||||
let visible = true;
|
||||
|
||||
setSelectorVisible(prevValue =>
|
||||
{
|
||||
visible = !prevValue;
|
||||
|
||||
return visible;
|
||||
});
|
||||
|
||||
if(visible) setTarget((event.target as (EventTarget & HTMLElement)));
|
||||
evadeClosing = false;
|
||||
}
|
||||
|
||||
function handleClose(){
|
||||
evadeClosing = true;
|
||||
}
|
||||
|
||||
var inputText = "";
|
||||
function onChangeInput(value){
|
||||
inputText = value;
|
||||
}
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(selectorVisible) return;
|
||||
|
||||
setTarget(null);
|
||||
}, [ selectorVisible ]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Base pointer className="icon sticker-icon" onClick={ toggleSelector } style={{ display: "inline-block"}}>
|
||||
<Overlay show={ selectorVisible } target={ target } placement="top">
|
||||
<Popover className="nitro-chat-sticker-selector-container image-rendering-pixelated">
|
||||
<NitroCardContentView overflow="hidden" className="bg-transparent">
|
||||
<Flex gap={ 1 }>
|
||||
<input onClick={e => handleClose()} onChange={ e => onChangeInput(e.target.value) } onKeyDown={ onKeyDown } type="text" className="form-control form-control-sm" placeholder={ LocalizeText('generic.search') } />
|
||||
<Button variant="success" className="stickers-search-button" onClick={e => searchStickers(inputText)}>
|
||||
<FaSearch className="fa-icon" />
|
||||
</Button>
|
||||
</Flex>
|
||||
{ showNativeStickers ? <NativeStickers /> : null }
|
||||
{ showGiphyStickers ? <GiphyStickers /> : null }
|
||||
</NitroCardContentView>
|
||||
</Popover>
|
||||
</Overlay>
|
||||
</Base>
|
||||
</>
|
||||
);
|
||||
}
|
@ -1,127 +0,0 @@
|
||||
import { FC, KeyboardEvent, MouseEvent, useEffect, useState } from 'react';
|
||||
import { Button, Overlay, Popover } from 'react-bootstrap';
|
||||
import { FaSearch } from 'react-icons/fa';
|
||||
import { ChatMessageTypeEnum, LocalizeText } from '../../../../api';
|
||||
import { Base, Flex, Grid, NitroCardContentView } from '../../../../common';
|
||||
import { useChatInputWidget, useSessionInfo } from '../../../../hooks';
|
||||
|
||||
|
||||
export const ChatInputStickersSelectorView: FC<{}> = props =>
|
||||
{
|
||||
const [ target, setTarget ] = useState<(EventTarget & HTMLElement)>(null);
|
||||
const [ selectorVisible, setSelectorVisible ] = useState(false);
|
||||
const [ stickers, setStickers ] = useState(null)
|
||||
const [ stickersGiphy, setStickersGiphy ] = useState(null)
|
||||
const [ showNativeStickers, setShowNativeStickers ] = useState(true)
|
||||
const [ showGiphyStickers, setShowGiphyStickers ] = useState(false)
|
||||
const { sendChat = null } = useChatInputWidget();
|
||||
var evadeClosing = false;
|
||||
const { chatStyleId = 0 } = useSessionInfo();
|
||||
|
||||
function searchStickers(strSearch)
|
||||
{
|
||||
evadeClosing = true;
|
||||
if(strSearch.length == 0){
|
||||
setShowGiphyStickers(false)
|
||||
setShowNativeStickers(true)
|
||||
return;
|
||||
}
|
||||
|
||||
fetch('https://api.giphy.com/v1/gifs/search?api_key=4qwMrfzYMYVl1lhhl3wDaVxDNWAzAv6s&q=' + strSearch + '&limit=25&offset=0&rating=g&lang=en')
|
||||
.then((response) => response.json())
|
||||
.then((result) =>
|
||||
{
|
||||
setStickersGiphy(result);
|
||||
setShowNativeStickers(false)
|
||||
setShowGiphyStickers(true)
|
||||
})
|
||||
}
|
||||
|
||||
const onKeyDown = (event: KeyboardEvent<HTMLInputElement>) =>
|
||||
{
|
||||
if(event.key !== 'Enter') return;
|
||||
searchStickers(inputText);
|
||||
}
|
||||
|
||||
function sendSticker(sticker){
|
||||
const stickerArgs = sticker.split(".gif");
|
||||
sendChat(stickerArgs[0], ChatMessageTypeEnum.CHAT_DEFAULT , '', chatStyleId);
|
||||
|
||||
|
||||
}
|
||||
|
||||
function NativeStickers(){
|
||||
if(stickers == null) return null;
|
||||
else return(
|
||||
<div></div>
|
||||
);
|
||||
}
|
||||
|
||||
function GiphyStickers(){
|
||||
if(stickersGiphy == null) return null;
|
||||
else return(
|
||||
<Grid columnCount={ 4 } overflow="auto" id="giphyStickers">
|
||||
{ stickersGiphy.data.map(sticker => (
|
||||
// eslint-disable-next-line react/jsx-key
|
||||
<Base key={ sticker.images.downsized_medium.url}>
|
||||
<img src={ sticker.images.downsized_medium.url } width={ 50 } height={ 50 } className="sticker-img" onClick={ e => sendSticker(sticker.images.fixed_height_small.url) }/>
|
||||
</Base>
|
||||
)) }
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
const toggleSelector = (event: MouseEvent<HTMLElement>) =>
|
||||
{
|
||||
if(evadeClosing) return;
|
||||
let visible = true;
|
||||
|
||||
setSelectorVisible(prevValue =>
|
||||
{
|
||||
visible = !prevValue;
|
||||
|
||||
return visible;
|
||||
});
|
||||
|
||||
if(visible) setTarget((event.target as (EventTarget & HTMLElement)));
|
||||
evadeClosing = false;
|
||||
}
|
||||
|
||||
function handleClose(){
|
||||
evadeClosing = true;
|
||||
}
|
||||
|
||||
var inputText = "";
|
||||
function onChangeInput(value){
|
||||
inputText = value;
|
||||
}
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(selectorVisible) return;
|
||||
|
||||
setTarget(null);
|
||||
}, [ selectorVisible ]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Base pointer className="icon sticker-icon" onClick={ toggleSelector } style={{ display: "inline-block"}}>
|
||||
<Overlay show={ selectorVisible } target={ target } placement="top">
|
||||
<Popover className="nitro-chat-sticker-selector-container image-rendering-pixelated">
|
||||
<NitroCardContentView overflow="hidden" className="bg-transparent">
|
||||
<Flex gap={ 1 }>
|
||||
<input onClick={e => handleClose()} onKeyDown={ onKeyDown } onChange={ e => onChangeInput(e.target.value) } type="text" className="form-control form-control-sm" placeholder={ LocalizeText('generic.search') } />
|
||||
<Button variant="success" className="stickers-search-button" onClick={e => searchStickers(inputText)}>
|
||||
<FaSearch className="fa-icon" />
|
||||
</Button>
|
||||
</Flex>
|
||||
{ showNativeStickers ? <NativeStickers /> : null }
|
||||
{ showGiphyStickers ? <GiphyStickers /> : null }
|
||||
</NitroCardContentView>
|
||||
</Popover>
|
||||
</Overlay>
|
||||
</Base>
|
||||
</>
|
||||
);
|
||||
}
|
@ -70,14 +70,62 @@
|
||||
}
|
||||
}
|
||||
|
||||
.nitro-chat-input-container .input-sizer input{
|
||||
width: calc(100% - 80px)!important;
|
||||
}
|
||||
|
||||
.info-habbopages {
|
||||
cursor: pointer;
|
||||
background-image: url("@/assets/images/boxes/card/questionmark.png");
|
||||
width: 19px;
|
||||
height: 20px;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
background-image: url("@/assets/images/boxes/card/questionmark_hover.png");
|
||||
|
||||
&:active {
|
||||
background-image: url("@/assets/images/boxes/card/questionmark_click.png");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.nitro-chat-style-selector-container {
|
||||
width: $chat-input-style-selector-widget-width;
|
||||
width: auto;
|
||||
max-height: $chat-input-style-selector-widget-height;
|
||||
|
||||
.content-area {
|
||||
max-height: $chat-input-style-selector-widget-height !important;
|
||||
}
|
||||
|
||||
.emoji {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
|
||||
.emoji::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: lightblue;
|
||||
border-radius: 60%;
|
||||
z-index: -1;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
.emoji:hover::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.emoji:hover {
|
||||
transform: scale(1.5);
|
||||
}
|
||||
|
||||
.bubble-parent-container {
|
||||
height: 30px;
|
||||
|
||||
@ -87,31 +135,3 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.nitro-chat-input-container .input-sizer input{
|
||||
width: calc(100% - 80px)!important;
|
||||
}
|
||||
.sticker-img{
|
||||
object-fit: contain;
|
||||
}
|
||||
#submenuChat{
|
||||
padding: 5px 10px 1px 3px;
|
||||
position: absolute;
|
||||
background-color: rgb(21, 21, 21);
|
||||
top: 3px;
|
||||
right: 17px;
|
||||
display: none;
|
||||
z-index: 9999999;
|
||||
border-left: 2px solid rgb(0, 0, 0);
|
||||
border-radius: 5px;
|
||||
margin-right: -13px;
|
||||
}
|
||||
#submenuChatConsola {
|
||||
padding: 5px 10px 1px 3px;
|
||||
background-color: rgb(21, 21, 21);
|
||||
z-index: 99999;
|
||||
border-left: 2px solid rgb(0, 0, 0);
|
||||
border-radius: 5px;
|
||||
margin-right: 5px;
|
||||
width: 170px;
|
||||
display: none;
|
||||
}
|
||||
|
@ -1,10 +1,11 @@
|
||||
import { HabboClubLevelEnum, RoomControllerLevel } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { ChatMessageTypeEnum, GetClubMemberLevel, GetConfiguration, GetSessionDataManager, LocalizeText, RoomWidgetUpdateChatInputContentEvent } from '../../../../api';
|
||||
import { Text } from '../../../../common';
|
||||
import { ChatMessageTypeEnum, CreateLinkEvent, GetClubMemberLevel, GetConfiguration, GetSessionDataManager, LocalizeText, RoomWidgetUpdateChatInputContentEvent } from '../../../../api';
|
||||
import { Base, Flex, Text } from '../../../../common';
|
||||
import { useChatInputWidget, useRoom, useSessionInfo, useUiEvent } from '../../../../hooks';
|
||||
import { ChatInputStyleSelectorView } from './ChatInputStyleSelectorView';
|
||||
import { ChatInputEmojiSelectorView } from './ChatInputEmojiSelectorView';
|
||||
import { ChatInputColorSelectorView } from './ChatInputColorSelectorView';
|
||||
|
||||
|
||||
@ -13,6 +14,7 @@ export const ChatInputView: FC<{}> = props =>
|
||||
const [ chatValue, setChatValue ] = useState<string>('');
|
||||
const { chatStyleId = 0, updateChatStyleId = null, chatColour = '', updateChatColour = null } = useSessionInfo();
|
||||
const { selectedUsername = '', floodBlocked = false, floodBlockedSeconds = 0, setIsTyping = null, setIsIdle = null, sendChat = null } = useChatInputWidget();
|
||||
const [ showInfoHabboPages, setShowInfohabboPages ] = useState<boolean>(false);
|
||||
const { roomSession = null } = useRoom();
|
||||
|
||||
const inputRef = useRef<HTMLInputElement>();
|
||||
@ -104,7 +106,7 @@ export const ChatInputView: FC<{}> = props =>
|
||||
}
|
||||
|
||||
setChatValue(append);
|
||||
}, [ chatModeIdWhisper, chatModeIdShout, chatModeIdSpeak, maxChatLength, chatStyleId, setIsTyping, setIsIdle, chatColour ]);
|
||||
}, [ chatModeIdWhisper, chatModeIdShout, chatModeIdSpeak, maxChatLength, chatStyleId, setIsTyping, setIsIdle, sendChat, chatColour ]);
|
||||
|
||||
const updateChatInput = useCallback((value: string) =>
|
||||
{
|
||||
@ -216,6 +218,11 @@ export const ChatInputView: FC<{}> = props =>
|
||||
return styleIds;
|
||||
}, []);
|
||||
|
||||
const addEmojiToChat = (emoji: string) =>
|
||||
{
|
||||
setChatValue(chatValue + emoji);
|
||||
setIsTyping(true);
|
||||
}
|
||||
useEffect(() =>
|
||||
{
|
||||
document.body.addEventListener('keydown', onKeyDownEvent);
|
||||
@ -237,15 +244,19 @@ export const ChatInputView: FC<{}> = props =>
|
||||
|
||||
return (
|
||||
createPortal(
|
||||
<div className="nitro-chat-input-container">
|
||||
<div className="nitro-chat-input-container" onMouseEnter={ () => setShowInfohabboPages(true) } onMouseLeave={ () => setTimeout(() => setShowInfohabboPages(false), 100) }>
|
||||
<div className="input-sizer align-items-center">
|
||||
{ !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>
|
||||
<ChatInputEmojiSelectorView addChatEmoji={ addEmojiToChat } />
|
||||
<ChatInputColorSelectorView chatColour={ chatColour } selectColour={ updateChatColour } />
|
||||
<ChatInputStyleSelectorView chatStyleId={ chatStyleId } chatStyleIds={ chatStyleIds } selectChatStyleId={ updateChatStyleId } />
|
||||
</div>, document.getElementById('toolbar-chat-input-container'))
|
||||
{ (showInfoHabboPages) &&
|
||||
<Base className="info-habbopages" onClick={ () => CreateLinkEvent('habbopages/chat/chatting') }></Base>
|
||||
}
|
||||
</div>, document.getElementById('toolbar-chat-input-container'))
|
||||
);
|
||||
}
|
1
src/components/room/widgets/chat-input/EmojiList.ts
Normal file
1
src/components/room/widgets/chat-input/EmojiList.ts
Normal file
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user