emoji-mart#Picker TypeScript Examples
The following examples show how to use
emoji-mart#Picker.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: Chat.tsx From watchparty with MIT License | 6 votes |
render() {
return (
<div style={{ position: 'absolute', bottom: '60px' }}>
<Picker
set="google"
sheetSize={64}
theme="dark"
showPreview={false}
showSkinTones={false}
onSelect={this.props.addEmoji}
/>
</div>
);
}
Example #2
Source File: EmojiPicker.tsx From SocialDev-Firebase with MIT License | 6 votes |
EmojiPicker: React.FC<Props> = ({ top, right, handleAddEmoji }) => {
const [pageWidth, setPageWidth] = useState(window.innerWidth);
const updateDimensions = () => {
setPageWidth(window.innerWidth);
};
useEffect(() => {
window.addEventListener('resize', updateDimensions);
return () => {
window.removeEventListener('resize', updateDimensions);
};
}, [pageWidth]);
return (
<>
{pageWidth >= 700 ? (
<Picker
set="messenger"
style={{ position: 'absolute', top: `${top}`, right: `${right}`, zIndex: 10 }}
darkMode={false}
onSelect={handleAddEmoji}
showSkinTones={false}
showPreview={false}
color="#1ca0f2"
/>
) : null}
</>
);
}
Example #3
Source File: SpfxEmojipicker.tsx From SPFx with Mozilla Public License 2.0 | 6 votes |
public render(): React.ReactElement<ISpfxEmojipickerProps> {
return (
<div className={styles.spfxEmojipicker}>
<Picker onSelect={(emoji) => { console.log(emoji) }} />
<Emoji emoji={{ id: 'santa', skin: 3 }} size={32} />
<Emoji emoji=':santa::skin-tone-3:' size={32} />
<Emoji emoji='santa' set='apple' size={32} />
</div>
);
}
Example #4
Source File: TextArea.tsx From core with GNU Affero General Public License v3.0 | 5 votes |
TextArea: React.FC<TextAreaProps> = ({ name, placeholder, theme='auto', max, setValue, value }) => {
const ref = useRef()
const [ emojiPickerHidden, setEmojiPickerHidden ] = useState(true)
useOutsideClick(ref, () => {
setEmojiPickerHidden(true)
})
return <div className='border border-grey-light dark:border-transparent h-96 text-black dark:bg-very-black dark:text-white rounded px-4 py-3 inline-block relative w-full'>
<Field as='textarea' name={name} className='dark:border-transparent text-black dark:bg-very-black dark:text-white w-full relative h-full resize-none outline-none' placeholder={placeholder} />
<div ref={ref}>
<div className='absolute bottom-12 left-10 z-30'>
{
!emojiPickerHidden && <Picker title='선택해주세요' emoji='sunglasses' set='twitter' enableFrequentEmojiSort theme={theme} showSkinTones={false} onSelect={(e) => {
setEmojiPickerHidden(true)
setValue(value + ' ' + ((e as { native: string }).native || e.colons))
}} i18n={{
search: '검색',
notfound: '검색 결과가 없습니다.',
categories: {
search: '검색 결과',
recent: '최근 사용',
people: '사람',
nature: '자연',
foods: '음식',
activity: '활동',
places: '장소',
objects: '사물',
symbols: '기호',
flags: '국기',
custom: '커스텀'
}
}} custom={KoreanbotsEmoji}/>
}
</div>
<div className='absolute bottom-2 left-4 hidden sm:block'>
<div className='emoji-selector-button outline-none' onClick={() => setEmojiPickerHidden(false)} onKeyPress={() => setEmojiPickerHidden(false)} role='button' tabIndex={0} />
</div>
{
max && <span className={`absolute bottom-2 right-4 ${max < value.length ? ' text-red-400' : ''}`}>
{max-value.length}
</span>
}
</div>
</div>
}
Example #5
Source File: CommentCreator.tsx From taskcafe with MIT License | 4 votes |
CommentCreator: React.FC<CommentCreatorProps> = ({
me,
disabled = false,
message,
onMemberProfile,
onCreateComment,
onCancelEdit,
autoFocus = false,
}) => {
const $commentWrapper = useRef<HTMLDivElement>(null);
const $comment = useRef<HTMLTextAreaElement>(null);
const $emoji = useRef<HTMLDivElement>(null);
const $emojiCart = useRef<HTMLDivElement>(null);
const [comment, setComment] = useState(message ?? '');
const [showCommentActions, setShowCommentActions] = useState(autoFocus);
const { showPopup, hidePopup } = usePopup();
useEffect(() => {
if (autoFocus && $comment && $comment.current) {
$comment.current.select();
}
}, []);
useOnOutsideClick(
[$commentWrapper, $emojiCart],
showCommentActions,
() => {
if (onCancelEdit) {
onCancelEdit();
}
setShowCommentActions(false);
},
null,
);
return (
<CommentInnerWrapper ref={$commentWrapper}>
{me && onMemberProfile && (
<CommentProfile
member={me}
size={32}
onMemberProfile={$target => {
onMemberProfile($target, me.id);
}}
/>
)}
<CommentEditorContainer>
<CommentTextArea
$showCommentActions={showCommentActions}
placeholder="Write a comment..."
ref={$comment}
disabled={disabled}
value={comment}
onChange={e => setComment(e.currentTarget.value)}
onFocus={() => {
setShowCommentActions(true);
}}
/>
<CommentEditorActions visible={showCommentActions}>
<CommentEditorActionIcon>
<Paperclip width={12} height={12} />
</CommentEditorActionIcon>
<CommentEditorActionIcon>
<At width={12} height={12} />
</CommentEditorActionIcon>
<CommentEditorActionIcon
ref={$emoji}
onClick={() => {
showPopup(
$emoji,
<div ref={$emojiCart}>
<Picker
onClick={emoji => {
if ($comment && $comment.current) {
const textToInsert = `${emoji.colons} `;
const cursorPosition = $comment.current.selectionStart;
const textBeforeCursorPosition = $comment.current.value.substring(0, cursorPosition);
const textAfterCursorPosition = $comment.current.value.substring(
cursorPosition,
$comment.current.value.length,
);
setComment(textBeforeCursorPosition + textToInsert + textAfterCursorPosition);
}
hidePopup();
}}
set="google"
/>
</div>,
);
}}
>
<Smile width={12} height={12} />
</CommentEditorActionIcon>
<CommentEditorActionIcon>
<Task width={12} height={12} />
</CommentEditorActionIcon>
<CommentEditorSaveButton
onClick={() => {
setShowCommentActions(false);
onCreateComment(comment);
setComment('');
}}
>
Save
</CommentEditorSaveButton>
</CommentEditorActions>
</CommentEditorContainer>
</CommentInnerWrapper>
);
}
Example #6
Source File: MessageInput.tsx From convoychat with GNU General Public License v3.0 | 4 votes |
MessageInput: MessageInputType = ({
value,
innerRef,
onCancel,
handleSubmit,
handleChange,
onEmojiClick,
mentionSuggestions,
setValue,
...props
}) => {
const isMobile = !mql.matches;
const formRef = useRef<HTMLFormElement>();
const textareaRef = useRef<HTMLTextAreaElement>();
const suggestionsData = useRef<ISuggestionsData>();
const {
open,
getRootProps,
isDragActive,
getInputProps,
uploadImageInProgress,
} = useImageUpload({
value,
setValue,
});
const imparativeSubmit = (event: any) => {
event.preventDefault();
formRef?.current.dispatchEvent(new Event("submit", { cancelable: true }));
};
const handleKeydown = (event: React.KeyboardEvent<HTMLTextAreaElement>) => {
if (event.key === "Escape") {
onCancel && onCancel();
}
if (isMobile) return;
if (event.key === "Enter" && !event.shiftKey) {
imparativeSubmit(event);
}
};
const getRef: any = (e: any) => {
textareaRef.current = e;
if (innerRef) {
innerRef.current = e;
}
};
useEffect(() => {
textareaAutoResize(textareaRef?.current);
}, [value]);
useEffect(() => {
suggestionsData.current = mentionSuggestions?.map(curr => {
return {
display: curr.username,
id: curr.id,
};
});
}, [mentionSuggestions]);
return (
<MessageInputWrapper className="message__input">
<Flex gap="large" align="center" justify="space-between" nowrap>
<IconButton
onClick={open}
icon={<FaImage />}
data-testid="upload-button"
isLoading={uploadImageInProgress}
/>
<form
ref={formRef}
onSubmit={handleSubmit}
className={isDragActive ? "active-animation" : ""}
>
<div {...getRootProps({ className: "dropzone" })}>
<MentionsInput
data-testid="messageInput"
name={"message"}
inputRef={getRef}
autoComplete={"off"}
placeholder="Write something"
value={value}
onChange={handleChange}
onKeyDown={handleKeydown}
style={defaultMentionStyles}
allowSuggestionsAboveCursor={true}
{...props}
>
<Mention
trigger="@"
data={suggestionsData?.current || []}
displayTransform={id =>
`@${suggestionsData.current.find(i => i.id === id).display} `
}
/>
</MentionsInput>
<input {...getInputProps()} data-testid="dropzone" />
</div>
</form>
{isMobile && (
<SendButton
icon={FaPaperPlane}
onClick={imparativeSubmit}
className="input__send-button"
/>
)}
{!isMobile && (
<Dropdown>
<Dropdown.Toggle>
<IconButton icon={<FaSmile />} />
</Dropdown.Toggle>
<Dropdown.Content style={{ position: "absolute", bottom: 0 }}>
<Picker
set="apple"
theme="dark"
onSelect={(emoji: any) => onEmojiClick && onEmojiClick(emoji)}
/>
</Dropdown.Content>
</Dropdown>
)}
</Flex>
</MessageInputWrapper>
);
}
Example #7
Source File: EmojiInput.tsx From glific-frontend with GNU Affero General Public License v3.0 | 4 votes |
EmojiInput: React.FC<EmojiInputProps> = ({
field: { value, name, onBlur },
handleChange,
getEditorValue,
handleBlur,
...props
}: EmojiInputProps) => {
const [showEmojiPicker, setShowEmojiPicker] = useState(false);
const { t } = useTranslation();
const updateValue = (input: any, isEmoji = false) => {
const editorContentState = value.getCurrentContent();
const editorSelectionState: any = value.getSelection();
const ModifiedContent = Modifier.replaceText(
editorContentState,
editorSelectionState,
isEmoji ? input.native : input
);
let updatedEditorState = EditorState.push(value, ModifiedContent, 'insert-characters');
if (!isEmoji) {
const editorSelectionStateMod = updatedEditorState.getSelection();
const updatedSelection = editorSelectionStateMod.merge({
anchorOffset: editorSelectionStateMod.getAnchorOffset() - 1,
focusOffset: editorSelectionStateMod.getFocusOffset() - 1,
});
updatedEditorState = EditorState.forceSelection(updatedEditorState, updatedSelection);
}
props.form.setFieldValue(name, updatedEditorState);
};
const handleKeyCommand = (command: any, editorState: any) => {
if (command === 'underline') {
return 'handled';
}
if (command === 'bold') {
updateValue('**');
} else if (command === 'italic') {
updateValue('__');
} else {
const newState = RichUtils.handleKeyCommand(editorState, command);
if (newState) {
props.form.setFieldValue(name, newState);
return 'handled';
}
}
return 'not-handled';
};
const draftJsChange = (editorState: any) => {
if (handleChange) {
handleChange(getPlainTextFromEditor(props.form.values.example));
}
if (getEditorValue) {
getEditorValue(editorState);
} else {
props.form.setFieldValue(name, editorState);
}
};
const mentions = props.inputProp?.suggestions || [];
const [open, setOpen] = useState(false);
const [suggestions, setSuggestions] = useState(mentions);
const onOpenChange = (_open: boolean) => {
setOpen(_open);
};
const getSuggestions = useCallback(customSuggestionsFilter, []);
const onSearchChange = ({ value: searchValue }: { value: string }) => {
setSuggestions(getSuggestions(searchValue, mentions));
};
const inputProps = {
component: Editor,
editorState: value,
open,
readOnly: props.disabled,
suggestions,
onOpenChange,
onSearchChange,
handlePastedText: (text: string, html: string, editorState: EditorState) => {
const pastedBlocks = ContentState.createFromText(text).getBlockMap();
const newState = Modifier.replaceWithFragment(
editorState.getCurrentContent(),
editorState.getSelection(),
pastedBlocks
);
const newEditorState = EditorState.push(editorState, newState, 'insert-fragment');
draftJsChange(newEditorState);
return 'handled';
},
handleKeyCommand,
onBlur: handleBlur,
onChange: draftJsChange,
};
const editor = { inputComponent: DraftField, inputProps };
const emojiPicker = showEmojiPicker ? (
<Picker
data-testid="emoji-container"
title={t('Pick your emoji…')}
emoji="point_up"
style={{ position: 'absolute', top: '10px', right: '0px', zIndex: 2 }}
onSelect={(emojiValue) => updateValue(emojiValue, true)}
/>
) : (
''
);
const handleClickAway = () => {
setShowEmojiPicker(false);
};
const picker = (
<ClickAwayListener onClickAway={handleClickAway}>
<InputAdornment position="end" className={Styles.EmojiPosition}>
<IconButton
color="primary"
data-testid="emoji-picker"
aria-label="pick emoji"
component="span"
className={Styles.Emoji}
onClick={() => setShowEmojiPicker(!showEmojiPicker)}
>
<span role="img" aria-label="pick emoji">
?
</span>
</IconButton>
{emojiPicker}
</InputAdornment>
</ClickAwayListener>
);
const input = (
<Input field={{ name, value, onBlur }} {...props} editor={editor} emojiPicker={picker} />
);
return input;
}
Example #8
Source File: WhatsAppEditor.tsx From glific-frontend with GNU Affero General Public License v3.0 | 4 votes |
WhatsAppEditor: React.SFC<WhatsAppEditorProps> = (props) => {
const { setEditorState, sendMessage, editorState, handleHeightChange, readOnly = false } = props;
const [showEmojiPicker, setShowEmojiPicker] = useState(false);
const { t } = useTranslation();
const handleChange = (editorStateChange: any) => {
setEditorState(editorStateChange);
};
const updateValue = (input: any, isEmoji: boolean = false) => {
const editorContentState = editorState.getCurrentContent();
const editorSelectionState: any = editorState.getSelection();
const ModifiedContent = Modifier.replaceText(
editorContentState,
editorSelectionState,
isEmoji ? input.native : input
);
let updatedEditorState = EditorState.push(editorState, ModifiedContent, 'insert-characters');
if (!isEmoji) {
const editorSelectionStateMod = updatedEditorState.getSelection();
const updatedSelection = editorSelectionStateMod.merge({
anchorOffset: editorSelectionStateMod.getAnchorOffset() - 1,
focusOffset: editorSelectionStateMod.getFocusOffset() - 1,
});
updatedEditorState = EditorState.forceSelection(updatedEditorState, updatedSelection);
}
setEditorState(updatedEditorState);
};
const handleKeyCommand = (command: string, editorStateChange: any) => {
// On enter, submit. Otherwise, deal with commands like normal.
if (command === 'enter') {
// Convert Draft.js to WhatsApp
sendMessage(getPlainTextFromEditor(editorStateChange));
return 'handled';
}
if (command === 'underline') {
return 'handled';
}
if (command === 'bold') {
updateValue('**');
} else if (command === 'italic') {
updateValue('__');
} else {
const newState = RichUtils.handleKeyCommand(editorStateChange, command);
if (newState) {
setEditorState(newState);
return 'handled';
}
}
return 'not-handled';
};
const keyBindingFn = (e: any) => {
// Shift-enter is by default supported. Only 'enter' needs to be changed.
if (e.keyCode === 13 && !e.nativeEvent.shiftKey) {
return 'enter';
}
return getDefaultKeyBinding(e);
};
const handleClickAway = () => {
setShowEmojiPicker(false);
};
const emojiStyles: any = {
position: 'absolute',
bottom: '60px',
right: '-150px',
zIndex: 100,
};
if (window.innerWidth <= 768) {
emojiStyles.right = '5%';
}
return (
<>
<ReactResizeDetector
data-testid="resizer"
handleHeight
onResize={(width: any, height: any) => handleHeightChange(height - 40)} // 40 is the initial height
>
<div className={styles.Editor}>
<Editor
data-testid="editor"
editorState={editorState}
onChange={handleChange}
handleKeyCommand={handleKeyCommand}
keyBindingFn={keyBindingFn}
placeholder={t('Type a message...')}
readOnly={readOnly}
/>
</div>
</ReactResizeDetector>
<ClickAwayListener onClickAway={handleClickAway}>
<div>
<div className={styles.EmojiButton}>
<IconButton
data-testid="emoji-picker"
color="primary"
aria-label="pick emoji"
component="span"
onClick={() => setShowEmojiPicker(!showEmojiPicker)}
>
<span role="img" aria-label="pick emoji">
?
</span>
</IconButton>
</div>
{showEmojiPicker ? (
<Picker
data-testid="emoji-popup"
title={t('Pick your emoji…')}
emoji="point_up"
style={emojiStyles}
onSelect={(emoji) => updateValue(emoji, true)}
/>
) : null}
</div>
</ClickAwayListener>
</>
);
}
Example #9
Source File: Chat.tsx From SkyOffice with MIT License | 4 votes |
export default function Chat() {
const [inputValue, setInputValue] = useState('')
const [showEmojiPicker, setShowEmojiPicker] = useState(false)
const messagesEndRef = useRef<HTMLDivElement>(null)
const inputRef = useRef<HTMLInputElement>(null)
const chatMessages = useAppSelector((state) => state.chat.chatMessages)
const focused = useAppSelector((state) => state.chat.focused)
const showChat = useAppSelector((state) => state.chat.showChat)
const dispatch = useAppDispatch()
const game = phaserGame.scene.keys.game as Game
const handleChange = (event: React.FormEvent<HTMLInputElement>) => {
setInputValue(event.currentTarget.value)
}
const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
if (event.key === 'Escape') {
// move focus back to the game
inputRef.current?.blur()
dispatch(setShowChat(false))
}
}
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault()
// move focus back to the game
inputRef.current?.blur()
const val = inputValue.trim()
setInputValue('')
if (val) {
game.network.addChatMessage(val)
game.myPlayer.updateDialogBubble(val)
}
}
const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' })
}
useEffect(() => {
if (focused) {
inputRef.current?.focus()
}
}, [focused])
useEffect(() => {
scrollToBottom()
}, [chatMessages, showChat])
return (
<Backdrop>
<Wrapper>
{showChat ? (
<>
<ChatHeader>
<h3>Chat</h3>
<IconButton
aria-label="close dialog"
className="close"
onClick={() => dispatch(setShowChat(false))}
size="small"
>
<CloseIcon />
</IconButton>
</ChatHeader>
<ChatBox>
{chatMessages.map(({ messageType, chatMessage }, index) => (
<Message chatMessage={chatMessage} messageType={messageType} key={index} />
))}
<div ref={messagesEndRef} />
{showEmojiPicker && (
<EmojiPickerWrapper>
<Picker
theme="dark"
showSkinTones={false}
showPreview={false}
onSelect={(emoji) => {
setInputValue(inputValue + emoji.native)
setShowEmojiPicker(!showEmojiPicker)
dispatch(setFocused(true))
}}
exclude={['recent', 'flags']}
/>
</EmojiPickerWrapper>
)}
</ChatBox>
<InputWrapper onSubmit={handleSubmit}>
<InputTextField
inputRef={inputRef}
autoFocus={focused}
fullWidth
placeholder="Press Enter to chat"
value={inputValue}
onKeyDown={handleKeyDown}
onChange={handleChange}
onFocus={() => {
if (!focused) dispatch(setFocused(true))
}}
onBlur={() => dispatch(setFocused(false))}
/>
<IconButton aria-label="emoji" onClick={() => setShowEmojiPicker(!showEmojiPicker)}>
<InsertEmoticonIcon />
</IconButton>
</InputWrapper>
</>
) : (
<FabWrapper>
<Fab
color="secondary"
aria-label="showChat"
onClick={() => {
dispatch(setShowChat(true))
dispatch(setFocused(true))
}}
>
<ChatBubbleOutlineIcon />
</Fab>
</FabWrapper>
)}
</Wrapper>
</Backdrop>
)
}
Example #10
Source File: foot.tsx From sphinx-win-linux-desktop with MIT License | 4 votes |
export default function Foot({
height,
messagePrice,
tribeBots,
msgPrice,
setMsgPrice,
}) {
const { ui, msg, meme, details, user } = useStores();
const [recording, setRecording] = useState(false);
const [record, setRecord] = useState(false);
const [uploading, setUploading] = useState(false);
return useObserver(() => {
const myid = user.myid;
const chat = ui.selectedChat;
let text = (chat ? ui.tribeText[chat.id] : "") || "";
useEffect(() => {
if (recording) {
setRecord(true);
}
}, [recording]);
const msgInputRef = useRef();
// if height change, maybe clicked reply
const oldHeight = useRef(height);
useEffect(() => {
if (oldHeight.current < height) {
if (msgInputRef && msgInputRef.current) {
(msgInputRef.current as any).focus();
}
}
}, [height]);
async function sendGif(amount: number) {
const params = ui.imgViewerParams;
const gifJSON = JSON.stringify({
id: params.id,
url: params.data,
aspect_ratio: params.aspect_ratio,
text: text,
});
const b64 = btoa(gifJSON);
let contact_id = chat.contact_ids.find((cid) => cid !== myid);
await msg.sendMessage({
contact_id,
chat_id: chat.id,
text: "giphy::" + b64,
reply_uuid: "",
amount: amount || 0,
});
ui.setImgViewerParams(null);
ui.setTribeText(chat.id, "");
}
async function sendPaidMsg() {
setUploading(true);
const server = meme.getDefaultServer();
const file = new File([text], "message.txt", {
type: "text/plain;charset=utf-8",
});
const r = await uploadFile(
file,
"sphinx/text",
server.host,
server.token,
"message.txt"
);
await msg.sendAttachment({
contact_id: null,
chat_id: chat.id,
muid: r.muid,
media_key: r.media_key,
media_type: "sphinx/text",
text: "",
price: parseInt(msgPrice) || 0,
amount: messagePrice || 0,
});
ui.setTribeText(chat.id, "");
setMsgPrice("");
setUploading(false);
}
function sendMessage() {
if (!text) return;
if (msgPrice) {
return sendPaidMsg();
}
let contact_id = chat.contact_ids.find((cid) => cid !== myid);
let { price, failureMessage } = calcBotPrice(tribeBots, text);
if (failureMessage) {
return alert(failureMessage);
}
if (price > details.balance) {
return alert("Not enough balance");
}
if (ui.imgViewerParams && ui.imgViewerParams.type === "image/gif") {
return sendGif(messagePrice + price);
}
let txt = text;
if (ui.extraTextContent) {
const { type, ...rest } = ui.extraTextContent;
txt = type + "::" + JSON.stringify({ ...rest, text });
}
msg.sendMessage({
contact_id,
text: txt,
chat_id: chat.id || null,
amount: messagePrice + price || 0, // 5, // CHANGE THIS
reply_uuid: ui.replyUUID || "",
});
ui.setTribeText(chat.id, "");
if (ui.replyUUID) ui.setReplyUUID("");
if (ui.extraTextContent) ui.setExtraTextContent(null);
}
let [count, setCount] = useState(0);
useInterval(
() => {
// Your custom logic here
setCount(count + 1);
},
recording ? 1000 : null
);
function duration(seconds) {
var start = moment(0);
var end = moment(seconds * 1000);
let diff = end.diff(start);
return moment.utc(diff).format("m:ss");
}
async function onStop(res) {
const blob = res.blob;
const file = new File([blob], "Audio.wav", { type: blob.type });
const server = meme.getDefaultServer();
setUploading(true);
const r = await uploadFile(
file,
blob.type,
server.host,
server.token,
"Audio.wav"
);
await msg.sendAttachment({
contact_id: null,
chat_id: chat.id,
muid: r.muid,
media_key: r.media_key,
media_type: blob.type,
text: "",
price: 0,
amount: 0,
});
setUploading(false);
setRecording(false);
setCount(0);
}
const [anchorEl, setAnchorEl] = React.useState(null);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const open = Boolean(anchorEl);
const id = open ? "simple-popover" : undefined;
const msgs = chat && msg.messages[chat.id];
const { replyMessageSenderAlias, replyMessageContent, replyColor } =
useReplyContent(msgs, ui.replyUUID, ui.extraTextContent);
const [showGiphy, setShowGiphy] = useState(false);
function handleGiphy() {
setShowGiphy(true);
}
function handlePriceChange(e) {
let numRegex = /^\d+$/;
if (numRegex.test(e.target.value) || e.target.value === "") {
setMsgPrice(e.target.value);
}
}
if (ui.showBots) {
return <></>;
}
if (recording) {
return (
<MicWrap style={{ background: theme.bg, height }}>
<Blinker>
<BlinkingButton
style={{
height: 10,
padding: 7,
backgroundColor: "#ea7574",
marginTop: -1,
}}
/>
</Blinker>
<WaveWrap>
<ReactMic
className="sound-wave"
record={record}
backgroundColor={theme.bg}
onStop={onStop}
// onStart={onStart}
strokeColor="#ffffff"
/>
</WaveWrap>
<div
style={{
color: "white",
height: 25,
marginTop: 8,
marginRight: 10,
}}
>
{duration(count)}
</div>
<IconButton
style={{
width: 39,
height: 39,
marginRight: 17,
backgroundColor: "#ea7574",
opacity: uploading ? 0.8 : 1,
}}
onClick={() => {
setRecord(false), setRecording(false), setCount(0);
}}
disabled={uploading}
>
<Close
style={{ color: "white", fontSize: 30, borderRadius: "50%" }}
/>
</IconButton>
<IconButton
style={{
width: 39,
height: 39,
marginRight: 17,
backgroundColor: "#47ca97",
opacity: uploading ? 0.8 : 1,
}}
onClick={() => setRecord(false)}
disabled={uploading}
>
<Check
style={{ color: "white", fontSize: 30, borderRadius: "50%" }}
/>
</IconButton>
</MicWrap>
);
}
return (
<Wrap style={{ background: theme.bg, height }}>
{replyMessageContent && replyMessageSenderAlias && (
<ReplyMsg color={replyColor || "grey"}>
<ReplyMsgText>
<span style={{ color: "white" }}>{replyMessageSenderAlias}</span>
<span style={{ color: "#809ab7", marginTop: 5 }}>
{replyMessageContent}
</span>
</ReplyMsgText>
<CloseButton
style={{ cursor: "pointer" }}
onClick={() => {
ui.setReplyUUID(null);
ui.setExtraTextContent(null);
}}
/>
</ReplyMsg>
)}
{showGiphy && (
<GiphyWrap>
<ReactGiphySearchbox
style={{ position: "absolute" }}
apiKey="cnc84wQZqQn2vsWeg4sYK3RQJSrYPAl7"
onSelect={(item) => {
const data = item.images.original.url;
const height = parseInt(item.images.original.height) || 200;
const width = parseInt(item.images.original.width) || 200;
ui.setImgViewerParams({
data,
aspect_ratio: width / height,
id: item.id,
type: "image/gif",
});
setShowGiphy(false);
}}
/>
<CloseWrap onClick={() => setShowGiphy(false)}>
CLOSE <CloseButton />
</CloseWrap>
</GiphyWrap>
)}
<InnerWrap>
<IconButton
style={{
pointerEvents:
chat && chat.type === constants.chat_types.conversation
? "auto"
: "none",
cursor: "pointer",
height: 30,
width: 30,
marginLeft: 10,
backgroundColor: "#618af8",
}}
onClick={() => ui.setSendRequestModal(chat)}
>
<AddIcon
style={{ color: chat ? "#ffffff" : "#b0c4ff", fontSize: 22 }}
/>
</IconButton>
<img
src={giphyIcon}
onClick={chat && handleGiphy}
style={{
cursor: chat ? "pointer" : "auto",
marginLeft: "15px",
filter: chat ? "grayscale(0%)" : "grayscale(75%)",
}}
/>
<InsertEmoticonButton
style={{
pointerEvents: chat ? "auto" : "none",
cursor: "pointer",
marginLeft: 10,
color: chat ? "#8f9ca9" : "#2a3540",
fontSize: 30,
}}
aria-describedby={id}
onClick={handleClick}
/>
<Popover
id={id}
open={open}
anchorEl={anchorEl}
onClose={handleClose}
anchorOrigin={{
vertical: "top",
horizontal: "right",
}}
transformOrigin={{
vertical: "bottom",
horizontal: "left",
}}
>
<Picker
showPreview={false}
showSkinTones={false}
onSelect={(emoji) =>
ui.setTribeText(chat.id, text + emoji.native)
}
/>
</Popover>
<Input
value={text}
onChange={(e) => ui.setTribeText(chat.id, e.target.value)}
placeholder="Message"
ref={msgInputRef}
style={{
background: theme.extraDeep,
fontSize: 18,
textAlign: "left",
}}
disabled={!chat || chat.status === constants.chat_statuses.pending}
onKeyPress={(e) => {
if (e.key === "Enter") {
e.preventDefault(), sendMessage();
}
}}
></Input>
<PriceInput theme={theme}>
<div>Price:</div>
<PriceAmount
onChange={handlePriceChange}
value={msgPrice}
theme={theme}
disabled={!chat}
/>
</PriceInput>
<IconButton
style={{
width: 39,
height: 39,
marginRight: 10,
marginLeft: 10,
backgroundColor: "#618af8",
}}
disabled={!chat || !text || uploading}
onClick={sendMessage}
>
<SendIcon
style={{ color: chat ? "#ffffff" : "#b0c4ff", fontSize: 22 }}
/>
</IconButton>
<IconButton
style={{
width: 39,
height: 39,
marginRight: 10,
backgroundColor: "transparent",
}}
disabled={!chat}
onClick={() => setRecording(true)}
>
<MicIcon
style={{ color: chat ? "#8f9ca9" : "#2a3540", fontSize: 30 }}
/>
</IconButton>
</InnerWrap>
</Wrap>
);
});
}
Example #11
Source File: Chat.tsx From sync-party with GNU General Public License v3.0 | 4 votes |
export default function Chat({
isActive,
socket,
setPlayerFocused,
freezeUiVisible
}: Props): ReactElement {
const party = useSelector((state: RootAppState) => state.globalState.party);
const user = useSelector((state: RootAppState) => state.globalState.user);
const chat = useSelector((state: RootAppState) => state.globalState.chat);
const uiVisible = useSelector(
(state: RootAppState) => state.globalState.uiVisible
);
const uiFocused = useSelector(
(state: RootAppState) => state.globalState.uiFocused
);
const { t } = useTranslation();
const dispatch = useDispatch();
const [textInput, setTextInput] = useState('');
const [showEmojiPicker, setShowEmojiPicker] = useState(false);
const [historyStaysVisible, setHistoryStaysVisible] = useState(false);
const chatHistoryRef = useRef<HTMLDivElement | null>(null);
const textInputRef = useRef<HTMLTextAreaElement | null>(null);
const scrollHistoryToBottom = (): void => {
if (chatHistoryRef && chatHistoryRef.current) {
chatHistoryRef.current.scrollTop =
chatHistoryRef.current.scrollHeight;
}
};
const sendMessage = (message: string): void => {
if (socket && user && party && message !== '') {
const chatMessage = {
userId: user.id,
partyId: party.id,
userName: user.username,
message: message
};
socket.emit('chatMessage', chatMessage);
setTextInput('');
}
};
const focusTextInput = useCallback((): void => {
if (textInputRef.current) {
textInputRef.current.focus();
freezeUiVisible(true);
}
}, [freezeUiVisible]);
const blurTextInput = (): void => {
if (textInputRef.current) {
textInputRef.current.blur();
}
};
const handleInputFieldKeyDown = (event: React.KeyboardEvent): void => {
if (event.key === 'Enter') {
event.preventDefault();
sendMessage(textInput);
freezeUiVisible(false);
setShowEmojiPicker(false);
} else if (event.key === 'Escape') {
if (showEmojiPicker) {
setShowEmojiPicker(false);
focusTextInput();
} else {
setPlayerFocused(true);
freezeUiVisible(false);
blurTextInput();
}
}
};
const handleEmojiPickerKeydown = (event: React.KeyboardEvent): void => {
if (event.key === 'Escape') {
setShowEmojiPicker(false);
focusTextInput();
}
};
const handleEmojiPickerIconClick = (): void => {
if (!showEmojiPicker) {
freezeUiVisible(true);
}
setShowEmojiPicker(!showEmojiPicker);
setPlayerFocused(!showEmojiPicker);
focusTextInput();
};
const addEmoji = (emoji: BaseEmoji): void => {
if (textInputRef.current) {
textInputRef.current.focus();
setTimeout(() => {
if (textInputRef.current) {
const newCursorPosition =
textInputRef.current.selectionStart +
emoji.native.length;
const textBeforeCursorPosition =
textInputRef.current.value.substring(
0,
textInputRef.current.selectionStart
);
const textAfterCursorPosition =
textInputRef.current.value.substring(
textInputRef.current.selectionEnd,
textInputRef.current.value.length
);
setTextInput(
textBeforeCursorPosition +
emoji.native +
textAfterCursorPosition
);
textInputRef.current.selectionStart =
textInputRef.current.selectionEnd = newCursorPosition;
}
}, 10);
}
};
// At mounting
useEffect(() => {
scrollHistoryToBottom();
}, [isActive]);
// If isActive changes
useEffect(() => {
if (isActive !== uiFocused.chat) {
if (isActive) {
setTimeout(() => {
scrollHistoryToBottom();
focusTextInput();
}, 50);
} else {
setHistoryStaysVisible(false);
}
dispatch(
setGlobalState({
uiFocused: {
...uiFocused,
chat: isActive
}
})
);
}
}, [isActive, dispatch, focusTextInput, uiFocused]);
// If ui visibility or history timeout changes
useEffect(() => {
scrollHistoryToBottom();
}, [uiVisible, historyStaysVisible]);
// If there is a new message
useEffect(() => {
scrollHistoryToBottom();
setHistoryStaysVisible(true);
const historyTimeoutId = setTimeout(() => {
setHistoryStaysVisible(false);
}, 12000);
return (): void => {
clearTimeout(historyTimeoutId);
};
}, [chat, freezeUiVisible]);
return (
<div
className={
'absolute bottom-0 left-0 ml-3' +
(uiVisible ? ' mb-24' : ' mb-10')
}
>
<div className="flex flex-row">
<div className="flex flex-col mt-auto z-50">
{!(uiVisible && !isActive && !historyStaysVisible) &&
(uiVisible || historyStaysVisible) &&
party &&
user &&
chat[party.id] && (
<ChatHistory
chatHistoryRef={chatHistoryRef}
chat={chat}
party={party}
userId={user.id}
isActive={isActive}
uiVisible={uiVisible}
t={t}
></ChatHistory>
)}
{isActive && uiVisible && (
<div className="mt-auto">
<ChatInput
textInputRef={textInputRef}
textInput={textInput}
setPlayerFocused={setPlayerFocused}
freezeUiVisible={freezeUiVisible}
handleInputFieldKeyDown={
handleInputFieldKeyDown
}
setTextInput={setTextInput}
t={t}
></ChatInput>
</div>
)}
</div>
{isActive && (
<div className="mt-auto">
{showEmojiPicker && uiVisible && (
<div
className="ml-2 mb-1"
onKeyDown={handleEmojiPickerKeydown}
>
<Picker
native={true}
sheetSize={16}
showPreview={false}
useButton={false}
onSelect={(emoji: BaseEmoji): void => {
addEmoji(emoji);
}}
></Picker>
</div>
)}
{!showEmojiPicker && uiVisible && (
<FontAwesomeIcon
icon={faSmile}
className="ml-2 cursor-pointer text-2xl mb-1"
onClick={handleEmojiPickerIconClick}
></FontAwesomeIcon>
)}
</div>
)}
</div>
</div>
);
}