Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

imlemented emoji picker for chat #1143

Merged
merged 2 commits into from
Sep 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
"private": true,
"dependencies": {
"@date-io/date-fns": "^2.16.0",
"@emoji-mart/data": "^1.1.2",
"@emoji-mart/react": "^1.1.1",
"@emotion/react": "^11.7.0",
"@emotion/styled": "^11.6.0",
"@mui/icons-material": "^5.6.2",
Expand All @@ -16,6 +18,7 @@
"allotment": "^1.19.2",
"axios": "^0.24.0",
"date-fns": "^2.29.3",
"emoji-mart": "^5.5.2",
"i18next": "^21.5.4",
"nuka-carousel": "^6.0.3",
"react": "^17.0.2",
Expand Down
1 change: 0 additions & 1 deletion src/constants/translations/en/breadcrumbs.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
"myCooperations": "My cooperations",
"myResources": "Мy resources",
"newLesson": "New lesson",
"chat": "Chat",
"myOffers": "My offers",
"editLesson": "Edit lesson",
"lessonDetails": "Lesson details",
Expand Down
1 change: 0 additions & 1 deletion src/constants/translations/ua/breadcrumbs.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
"myCooperations": "Мої співпраці",
"myResources": "Мої ресурси",
"newLesson": "Новий урок",
"chat": "Чат",
"myOffers": "Мої пропозиції",
"editLesson": "Редагування уроку",
"lessonDetails": "Деталі уроку",
Expand Down
25 changes: 19 additions & 6 deletions src/containers/chat/chat-text-area/ChatTextArea.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,39 @@ export const styles = {
columnGap: '16px',
px: '16px'
},
testAreaWithPicker: { flex: 1, position: 'relative' },
emojiPicker: {
position: 'absolute',
bottom: '90px',
right: 0,
zIndex: 2,
'& > div *': {
maxHeight: '300px',
height: '100%'
}
},
textAreaWrapper: {
flex: 1,
backgroundColor: 'basic.white',
borderRadius: '6px',
p: { xs: '16px 10px', sm: '16px 32px' },
'& .MuiInputBase-root': { mt: 0 }
p: { xs: '16px 10px', sm: '16px 32px' }
},
textArea: {
userSelect: 'none',
'& .MuiInputBase-root': { p: '4px 0px 5px', mt: 0 },
'& :hover': {
'&::-webkit-scrollbar-track, &::-webkit-scrollbar-thumb': {
visibility: 'hidden'
}
}
},
textAreaLabel: (value: string) => ({
visibility: value ? VisibilityEnum.Hidden : VisibilityEnum.Visible,
color: palette.primary[300],
top: '-6px',
left: '14px'
shrink: false,
style: {
visibility: value ? VisibilityEnum.Hidden : VisibilityEnum.Visible,
color: palette.primary[300],
top: '-6px'
}
}),
icon: { width: '32px', height: '32px', color: 'primary.800' }
}
116 changes: 95 additions & 21 deletions src/containers/chat/chat-text-area/ChatTextArea.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
import { FC, ChangeEvent } from 'react'
import {
FC,
ChangeEvent,
useState,
useRef,
Dispatch,
SetStateAction
} from 'react'
import Picker from '@emoji-mart/react'
import data from '@emoji-mart/data'
import { SxProps } from '@mui/material'
import { TextFieldProps } from '@mui/material/TextField'
import Box from '@mui/material/Box'
import IconButton from '@mui/material/IconButton'
import SendIcon from '@mui/icons-material/Send'
import { SxProps } from '@mui/material'
import MoodIcon from '@mui/icons-material/Mood'
import AttachFileIcon from '@mui/icons-material/AttachFile'

import useBreakpoints from '~/hooks/use-breakpoints'
import AppTextArea from '~/components/app-text-area/AppTextArea'

import { styles } from '~/containers/chat/chat-text-area/ChatTextArea.styles'
Expand All @@ -13,43 +25,105 @@ import { spliceSx } from '~/utils/helper-functions'

interface ChatTextAreaProps extends Omit<TextFieldProps, 'onChange' | 'sx'> {
value: string
onChange: (e: ChangeEvent<HTMLInputElement>) => void
setValue: Dispatch<SetStateAction<string>>
onClick: () => void
sx?: {
textAreaWrapper?: SxProps
container?: SxProps
icon?: SxProps
}
emojiPickerProps?: { perLine: number }
}

const ChatTextArea: FC<ChatTextAreaProps> = ({
value,
onChange,
setValue,
onClick,
maxRows = 6,
minRows = 1,
sx = {},
emojiPickerProps,
...props
}) => {
const { isMobile } = useBreakpoints()
const [isEmojiPickerOpen, setIsEmojiPickerOpen] = useState<boolean>(false)
const inputRef = useRef<HTMLInputElement | null>(null)

const onChange = (e: ChangeEvent<HTMLInputElement>) =>
setValue(e.target.value)

const onEmojiSelect = ({ native: emoji }: { native: string }) => {
Tolik170 marked this conversation as resolved.
Show resolved Hide resolved
if (inputRef.current) {
const input = inputRef.current
const start = input.selectionStart ?? 0

setValue((prev) => {
const updatedValue = prev.split('')
updatedValue.splice(start, 0, emoji)
return updatedValue.join('')
})

const newCursorPosition = start + emoji.length

input.setSelectionRange(newCursorPosition, newCursorPosition)
input.focus()
}
}

const onOpenPicker = () => setIsEmojiPickerOpen(true)
const onClosePicker = () => setIsEmojiPickerOpen(false)

const endAdornment = (
<>
<IconButton onMouseEnter={onOpenPicker}>
<MoodIcon />
</IconButton>
<IconButton disabled>
<AttachFileIcon />
</IconButton>
</>
)

return (
<Box sx={spliceSx(styles.container, sx.container)}>
<AppTextArea
InputLabelProps={{
style: styles.textAreaLabel(value),
shrink: false
}}
InputProps={{ disableUnderline: true }}
fullWidth
maxRows={maxRows}
minRows={minRows}
onChange={onChange}
sx={spliceSx(styles.textAreaWrapper, sx.textAreaWrapper)}
textFieldStyles={styles.textArea}
value={value}
variant={TextFieldVariantEnum.Standard}
withHelperText={false}
{...props}
/>
<Box sx={styles.testAreaWithPicker}>
{isEmojiPickerOpen && (
<Box data-testid='emoji-picker' sx={styles.emojiPicker}>
<Picker
data={data}
emojiSize={20}
maxFrequentRows={2}
onClickOutside={onClosePicker}
onEmojiSelect={onEmojiSelect}
perLine={isMobile ? 6 : 8}
previewPosition='none'
searchPosition='none'
skinTonePosition='none'
{...emojiPickerProps}
/>
</Box>
)}

<AppTextArea
InputLabelProps={styles.textAreaLabel(value)}
InputProps={{
disableUnderline: true,
endAdornment: endAdornment
}}
fullWidth
inputRef={inputRef}
maxRows={maxRows}
minRows={minRows}
onChange={onChange}
sx={spliceSx(styles.textAreaWrapper, sx.textAreaWrapper)}
textFieldStyles={styles.textArea}
value={value}
variant={TextFieldVariantEnum.Standard}
withHelperText={false}
{...props}
/>
</Box>

<IconButton data-testid='send-btn' onClick={onClick}>
<SendIcon sx={spliceSx(styles.icon, sx.icon)} />
</IconButton>
Expand Down
16 changes: 3 additions & 13 deletions src/containers/offer-page/chat-dialog-window/ChatDialogWindow.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,4 @@
import {
useCallback,
useEffect,
ChangeEvent,
useState,
useRef,
FC
} from 'react'
import { useCallback, useEffect, useState, useRef, FC } from 'react'
import { useTranslation } from 'react-i18next'
import SimpleBar from 'simplebar-react'
import Box from '@mui/material/Box'
Expand Down Expand Up @@ -39,10 +32,6 @@ const ChatDialogWindow: FC<ChatDialogWindow> = ({ chatInfo }) => {
const scrollRef = useRef<HTMLDivElement | null>(null)
const { setChatInfo } = useChatContext()

const onTextAreaChange = (e: ChangeEvent<HTMLInputElement>) => {
setTextAreaValue(e.target.value)
}

const getMessages = useCallback(
() => messageService.getMessages({ chatId: chatInfo.chatId }),
[chatInfo.chatId]
Expand Down Expand Up @@ -137,10 +126,11 @@ const ChatDialogWindow: FC<ChatDialogWindow> = ({ chatInfo }) => {
</Box>
)}
<ChatTextArea
emojiPickerProps={{ perLine: 6 }}
label={t('chatPage.chat.inputLabel')}
maxRows={3}
onChange={onTextAreaChange}
onClick={() => null}
setValue={setTextAreaValue}
sx={styles.textArea}
value={textAreaValue}
/>
Expand Down
2 changes: 1 addition & 1 deletion src/pages/chat/Chat.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { TypographyVariantEnum } from '~/types'
export const styles = {
root: {
position: 'relative',
mb: '20px',
mb: 0,
'& .sash-module_sash__K-9lB': {
'&:before': {
backgroundColor: 'transparent',
Expand Down
17 changes: 3 additions & 14 deletions src/pages/chat/Chat.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,4 @@
import {
useState,
useCallback,
useEffect,
useRef,
ChangeEvent,
MouseEvent
} from 'react'
import { useState, useCallback, useEffect, useRef, MouseEvent } from 'react'
import { useTranslation } from 'react-i18next'
import { Allotment } from 'allotment'
import SimpleBar from 'simplebar-react'
Expand Down Expand Up @@ -61,10 +54,6 @@ const Chat = () => {
setIsSidebarOpen(event)
}

const onTextAreaChange = (e: ChangeEvent<HTMLInputElement>) => {
setTextAreaValue(e.target.value)
}

const onMessagesResponse = useCallback(
(response: MessageInterface[]) => setMessages(response),
[setMessages]
Expand Down Expand Up @@ -165,7 +154,7 @@ const Chat = () => {
)

return (
<PageWrapper sx={styles.root}>
<PageWrapper disableGutters maxWidth={false} sx={styles.root}>
{isMobile && (
<AppDrawer
anchor={PositionEnum.Left}
Expand Down Expand Up @@ -209,8 +198,8 @@ const Chat = () => {
</SimpleBar>
<ChatTextArea
label={t('chatPage.chat.inputLabel')}
onChange={onTextAreaChange}
onClick={() => void onMessageSend()}
setValue={setTextAreaValue}
value={textAreaValue}
/>
</>
Expand Down
5 changes: 0 additions & 5 deletions src/router/constants/crumbs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,6 @@ export const userProfile = ({ data }: { data: UserResponse }) => ({
name: `${data.firstName} ${data.lastName}`
})

export const chat = {
name: t('breadCrumbs.chat'),
path: authRoutes.chat.route
}

export const newQuestion = {
name: t('breadCrumbs.newQuestion'),
path: authRoutes.myResources.newQuestion.route
Expand Down
Loading