Skip to content

Commit

Permalink
feat: DF-102 | imrpove comment input (#104)
Browse files Browse the repository at this point in the history
* feat: DF-102 | add multiline to post and comments

* feat: DF-102 | add sending comment on enter

* feat: DF-102 | fix comment send button on the bottom

* feat: DF-102 | add emoji picker to post and comment creation

* feat: DF-102 | fix lockfile conflict
  • Loading branch information
MangriMen committed Jun 18, 2023
1 parent 2495318 commit fc89fca
Show file tree
Hide file tree
Showing 10 changed files with 201 additions and 28 deletions.
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"preview": "vite preview"
},
"dependencies": {
"@emoji-mart/react": "^1.1.1",
"@emotion/react": "^11.10.6",
"@emotion/styled": "^11.10.6",
"@hookform/resolvers": "^2.9.10",
Expand All @@ -18,6 +19,7 @@
"@reduxjs/toolkit": "^1.9.2",
"axios": "^1.3.4",
"axios-case-converter": "^1.0.1",
"emoji-mart": "^5.5.2",
"i18next": "^22.4.10",
"mui-modal-provider": "^2.2.0",
"notistack": "^3.0.1",
Expand All @@ -33,6 +35,7 @@
},
"devDependencies": {
"@trivago/prettier-plugin-sort-imports": "^4.1.0",
"@types/emoji-mart": "^3.0.9",
"@types/node": "^18.14.0",
"@types/react": "^18.0.27",
"@types/react-dom": "^18.0.10",
Expand Down
35 changes: 34 additions & 1 deletion pnpm-lock.yaml

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

1 change: 1 addition & 0 deletions src/components/post/Comment/styles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export const CommentBody = styled(Span)`

export const CommentText = styled(Typography)`
float: left;
white-space: pre-wrap;
overflow-wrap: break-word;
font-size: ${props => props.theme.typography.body2.fontSize};
padding: 0 0.25rem;
Expand Down
31 changes: 29 additions & 2 deletions src/components/post/CreationDialog/CreatePostForm.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import '@mui/material';
import { Box, TextField, styled } from '@mui/material';
import { Box, InputAdornment, TextField, styled } from '@mui/material';
import { StyledButton } from 'components/common';
import { ImageUpload } from 'components/common/FileUpload/ImageUpload';
import { PostCardContent, PostCardStyled } from 'components/post/PostCard';
Expand All @@ -10,6 +10,7 @@ import {
} from 'consts';
import { useDataMutation } from 'ducks/data/api';
import { useCreatePostMutation } from 'ducks/post/api';
import { BaseEmoji } from 'emoji-mart/dist-es';
import { OptionsObject, useSnackbar } from 'notistack';
import { ChangeEvent, useEffect, useState } from 'react';
import {
Expand All @@ -20,6 +21,7 @@ import {
} from 'react-hook-form';
import { useTranslation } from 'react-i18next';

import { EmojiButton } from '../EmojiButton';
import { CreatePostFormProps, PostForm } from './interfaces';

const defaultValues = {
Expand Down Expand Up @@ -93,6 +95,13 @@ export const CreatePostForm = ({ onClose }: CreatePostFormProps) => {
}
}, [disable]);

const handleEmojiSelect = (emoji: BaseEmoji) => {
form.setValue(
'description',
`${form.getValues('description')}${emoji.native}`,
);
};

const onSubmitHandler: SubmitHandler<PostForm> = async data => {
setDisable(true);

Expand Down Expand Up @@ -146,7 +155,25 @@ export const CreatePostForm = ({ onClose }: CreatePostFormProps) => {
label={t('postDescription')}
multiline
maxRows={POST_DESCRIPTION_MAX_ROWS}
InputProps={{ disableUnderline: true }}
InputProps={{
disableUnderline: true,
endAdornment: (
<InputAdornment
style={{ alignSelf: 'flex-start' }}
position="end"
>
<EmojiButton
PopoverProps={{
transformOrigin: {
vertical: 'top',
horizontal: 'left',
},
}}
onEmojiSelect={handleEmojiSelect}
/>
</InputAdornment>
),
}}
inputProps={{
maxLength: SHAPE_CONSTRAINTS.DESCRIPTION_MAX,
}}
Expand Down
59 changes: 59 additions & 0 deletions src/components/post/EmojiButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import Picker from '@emoji-mart/react';
import SentimentSatisfiedOutlinedIcon from '@mui/icons-material/SentimentSatisfiedOutlined';
import {
IconButton,
IconButtonProps,
Popover,
PopoverProps,
} from '@mui/material';
import { BaseEmoji } from 'emoji-mart/dist-es';
import { useState } from 'react';
import { useTranslation } from 'react-i18next';

export const EmojiButton = ({
onEmojiSelect,
PopoverProps,
...props
}: {
onEmojiSelect: (emoji: BaseEmoji) => void;
PopoverProps?: Omit<PopoverProps, 'open'>;
} & IconButtonProps) => {
const { t, i18n } = useTranslation('translation', { keyPrefix: 'comment' });

const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);

const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget);
};

const handleClose = () => {
setAnchorEl(null);
};

const open = Boolean(anchorEl);

return (
<>
<IconButton title={t('emoji') ?? ''} {...props} onClick={handleClick}>
<SentimentSatisfiedOutlinedIcon />
</IconButton>
<Popover
open={open}
onClose={handleClose}
anchorEl={anchorEl}
anchorOrigin={{
vertical: 'top',
horizontal: 'center',
}}
transformOrigin={{
vertical: 'bottom',
horizontal: 'left',
}}
PaperProps={{ style: { background: 'transparent' } }}
{...PopoverProps}
>
<Picker locale={i18n.language} onEmojiSelect={onEmojiSelect} />
</Popover>
</>
);
};
91 changes: 66 additions & 25 deletions src/components/post/PostCard/PostCardInput.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import { yupResolver } from '@hookform/resolvers/yup';
import SendIcon from '@mui/icons-material/Send';
import { IconButton, InputBase, Paper, styled } from '@mui/material';
import { PostProps } from 'components/post';
import { POST_INPUT_MAX_ROWS } from 'consts';
import { useSendCommentMutation } from 'ducks/comment/api';
import { ChangeEvent, FormEvent, useCallback, useState } from 'react';
import { BaseEmoji } from 'emoji-mart/dist-es';
import { KeyboardEvent, useState } from 'react';
import { Controller, SubmitHandler, useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';

import { EmojiButton } from '../EmojiButton';
import { commentValidator } from './schemas';

const PaperStyled = styled(Paper)`
display: flex;
align-items: center;
Expand All @@ -17,48 +24,82 @@ const InputStyled = styled(InputBase)`
font-size: 16px;
`;

interface InputValues {
content: string;
}

const defaultValues = {
content: '',
};

export const PostCardInput = ({ post }: PostProps) => {
const { t } = useTranslation('translation', { keyPrefix: 'comment' });

const [isSendDisabled, setIsSendDisabled] = useState(false);
const { control, handleSubmit, reset, getValues, setValue } =
useForm<InputValues>({
defaultValues: defaultValues,
resolver: yupResolver(commentValidator),
});

const [commentText, setCommentText] = useState('');
const [isSendDisabled, setIsSendDisabled] = useState(false);

const [sendComment] = useSendCommentMutation();

const handleInputChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
setCommentText(e.target.value);
}, []);
const handleEmojiSelect = (emoji: BaseEmoji) => {
setValue('content', `${getValues('content')}${emoji.native}`);
};

const handleSendComment = useCallback(
async (e: FormEvent) => {
e.preventDefault();
const handleInputEnter = (event: KeyboardEvent<HTMLInputElement>) => {
if (event.key === 'Enter' && !event.shiftKey) {
event.preventDefault();
handleSubmit(handleSendComment)();
return false;
}
};

setIsSendDisabled(true);
const handleSendComment: SubmitHandler<InputValues> = async data => {
setIsSendDisabled(true);

await sendComment({
path: { post: post.id },
body: { content: commentText },
});
await sendComment({
path: { post: post.id },
body: data,
});

setCommentText('');
setIsSendDisabled(false);
},
[commentText, post.id, sendComment],
);
setIsSendDisabled(false);

reset();
};

return (
<PaperStyled component="form" onSubmit={handleSendComment} elevation={24}>
<InputStyled
placeholder={t('writeComment') ?? ''}
value={commentText}
onChange={handleInputChange}
<PaperStyled
component="form"
onSubmit={handleSubmit(handleSendComment)}
elevation={24}
>
<Controller
control={control}
name="content"
render={({ field }) => (
<InputStyled
multiline
maxRows={POST_INPUT_MAX_ROWS}
placeholder={t('writeComment') ?? ''}
{...field}
onKeyDown={handleInputEnter}
/>
)}
/>
<EmojiButton
disableRipple
style={{ alignSelf: 'flex-end' }}
onEmojiSelect={handleEmojiSelect}
/>
<IconButton
disableRipple
type="submit"
title={t('send') ?? ''}
disableRipple
disabled={isSendDisabled}
style={{ alignSelf: 'flex-end' }}
>
<SendIcon />
</IconButton>
Expand Down
5 changes: 5 additions & 0 deletions src/components/post/PostCard/schemas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import * as yup from 'yup';

export const commentValidator = yup.object().shape({
content: yup.string().required(),
});
1 change: 1 addition & 0 deletions src/components/post/PostCard/styles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ export const PostCardDescriptionCollapse = styled(Collapse)<
`;

export const PostCardDescriptionText = styled(Typography)`
white-space: pre-wrap;
overflow-wrap: break-word;
font-size: 14px;
padding: 0 4px;
Expand Down
2 changes: 2 additions & 0 deletions src/consts/post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ export const COMMENTS_FETCH_COUNT = {
export const POST_DESCRIPTION_MAX_ROWS = 18;
export const POST_DESCRIPTION_COLLAPSED_SIZE = '6.5625rem';

export const POST_INPUT_MAX_ROWS = 5;

export const POSTS_LOADING_OFFSET_HEIGHT = 320;

export const POST_WITHOUT_SCROLL_FETCH_DELAY = 100;
Expand Down
1 change: 1 addition & 0 deletions src/locales/ru/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"close": "Закрыть"
},
"comment": {
"emoji": "Эмодзи",
"writeComment": "Написать комментарий...",
"edit": "Редактировать",
"delete": "Удалить",
Expand Down

0 comments on commit fc89fca

Please sign in to comment.