Skip to content
Open
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
21 changes: 18 additions & 3 deletions src/app/article/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@ import Spinner from '../../components/spinner';
import ArticleCard from '../../components/article-card';
import LocaleSelect from '../../containers/locale-select';
import TopHead from '../../containers/top-head';
import { useDispatch, useSelector } from 'react-redux';
import { useDispatch, useSelector as useReduxSelector } from 'react-redux';
import shallowequal from 'shallowequal';
import articleActions from '../../store-redux/article/actions';
import commentsActions from '../../store-redux/comments/actions';
import HeadLayout from '../../components/head-layout';
import Comments from '../../containers/comments';
import useSelector from '../../hooks/use-selector';

function Article() {
const store = useStore();
Expand All @@ -24,18 +27,24 @@ function Article() {
const params = useParams();

useInit(() => {
//store.actions.article.load(params.id);
dispatch(articleActions.load(params.id));
dispatch(commentsActions.loadAll(params.id));
}, [params.id]);

const select = useSelector(
const select = useReduxSelector(
state => ({
article: state.article.data,
waiting: state.article.waiting,
comments: state.comments.data,
}),
shallowequal,
); // Нужно указать функцию для сравнения свойства объекта, так как хуком вернули объект

const profile = useSelector(state => ({
isAuth: state.session.exists,
userId: state.session.user._id,
}));

const { t } = useTranslate();

const callbacks = {
Expand All @@ -55,6 +64,12 @@ function Article() {
<Navigation />
<Spinner active={select.waiting}>
<ArticleCard article={select.article} onAdd={callbacks.addToBasket} t={t} />
<Comments
isAuth={profile.isAuth}
comments={select.comments}
userId={profile.userId}
articleId={params.id}
/>
</Spinner>
</PageLayout>
</>
Expand Down
30 changes: 30 additions & 0 deletions src/components/comment/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import CommentsForm from '../comments-form';
import './style.css';

export const Comment = ({
by,
text,
isMine,
date,
isFormOpen,
toggleForm,
onSubmit,
value,
onChange,
}) => {
return (
<div className="Comment">
<div className="Comment-subtitle-block">
<span className="Comment-userName" data-mine-comment={isMine ? 'isMine' : ''}>
{by}
</span>
<span className="Comment-date">{date}</span>
</div>
<div className="Comment-text" dangerouslySetInnerHTML={{ __html: text }} />
<button className="Comment-answer" onClick={toggleForm}>
<span>ответить</span>
</button>
{isFormOpen && <CommentsForm onSubmit={onSubmit} value={value} onChange={onChange} />}
</div>
);
};
36 changes: 36 additions & 0 deletions src/components/comment/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
.Comment {
font-size: 12px;
line-height: 18px;
font-weight: 400;
display: flex;
flex-direction: column;
gap: 6px;
align-items: flex-start;
}

.Comment-subtitle-block {
display: flex;
gap: 12px;
}

.Comment-userName {
font-weight: 700;

&[data-mine-comment='isMine'] {
color: #4b5563;
}
}

.Comment-date {
color: #666666;
}

.Comment-text {
font-size: 14px;
line-height: 20px;
}

.Comment-answer {
color: var(--primary);
padding: 0;
}
20 changes: 20 additions & 0 deletions src/components/comments-form/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { memo } from 'react';
import Button from '../button';
import Input from '../input';
import './style.css';

const CommentsForm = ({ onSubmit, value, onChange }) => {
const onSubmitHandler = () => {
onSubmit();
};

return (
<div className="Comments-form">
<span className="Comments-form-title">Новый комментарий</span>
<Input theme="full" name="text" type="text" value={value?.text ?? ''} onChange={onChange} />
<Button style="primary" title="Отправить" onClick={onSubmitHandler} />
</div>
);
};

export default memo(CommentsForm);
12 changes: 12 additions & 0 deletions src/components/comments-form/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.Comments-form {
display: flex;
flex-direction: column;
gap: 16px;
width: 100%;
}

.Comments-form-title {
font-weight: 700;
font-size: 16px;
line-height: 22px;
}
5 changes: 5 additions & 0 deletions src/components/input/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,8 @@
.Input_theme_small {
width: 233px;
}

.Input_theme_full {
width: 100%;
height: 88px;
}
66 changes: 66 additions & 0 deletions src/components/smart-comment/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { useDispatch, useSelector as useReduxSelector } from 'react-redux';
import shallowEqual from 'shallowequal';
import commentsActions from '../../store-redux/comments/actions';
import useInit from '../../hooks/use-init';
import { Comment } from '../comment';

export const SmartComment = ({
comment,
userId,
openFormForId,
handleToggleForm,
onSubmit,
value,
onChange,
}) => {
const dispatch = useDispatch();

useInit(() => {
dispatch(commentsActions.getAuthor(comment.author._id));
}, []);

const select = useReduxSelector(
state => ({
author: state.comments.authors[comment.author._id]?.profile.name,
}),
shallowEqual,
);

const formattedDate = new Intl.DateTimeFormat('ru-RU', {
dateStyle: 'long',
timeStyle: 'short',
}).format(new Date(comment.dateCreate));

return (
<div className="Smart-Comments">
{comment && (
<Comment
by={select.author}
text={comment.text}
isMine={userId === comment.author._id}
date={formattedDate}
isFormOpen={openFormForId === comment._id}
toggleForm={() => handleToggleForm(comment._id)}
onSubmit={onSubmit}
value={value}
onChange={onChange}
/>
)}
<div className="Smart-Comment">
{comment &&
comment.children?.map(comment => (
<SmartComment
key={comment._id}
comment={comment}
userId={userId}
openFormForId={openFormForId}
handleToggleForm={handleToggleForm}
onSubmit={onSubmit}
value={value}
onChange={onChange}
/>
))}
</div>
</div>
);
};
60 changes: 60 additions & 0 deletions src/containers/comments/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { memo, useEffect, useState } from 'react';
import { Link } from 'react-router-dom';
import './style.css';
import CommentsForm from '../../components/comments-form';
import commentsActions from '../../store-redux/comments/actions';
import useInit from '../../hooks/use-init';
import { useDispatch, useSelector as useReduxSelector } from 'react-redux';
import shallowEqual from 'shallowequal';
import { Comment } from '../../components/comment';
import { SmartComment } from '../../components/smart-comment';

const Comments = ({ isAuth, comments, userId, articleId }) => {
const [openFormForId, setOpenFormForId] = useState(null);
const [commentText, setCommentText] = useState('');
const dispatch = useDispatch();

const handleToggleForm = id => {
setOpenFormForId(prev => (prev === id ? null : id));
};

const handleSubmit = () => {
dispatch(commentsActions.create(commentText, openFormForId, articleId));
setCommentText('');
setOpenFormForId(null);
};

return (
<div className="Comments">
<span className="Comments-title">Комментарии ({comments.length})</span>
{!isAuth && (
<div>
<Link className="Comments-link-title" to="/login">
Войдите
</Link>
<span>, чтобы иметь возможность комментировать</span>
</div>
)}
<div className="Comments-line">
{comments.length > 0 &&
comments.map(comment => (
<SmartComment
key={comment._id}
comment={comment}
userId={userId}
openFormForId={openFormForId}
handleToggleForm={handleToggleForm}
onSubmit={handleSubmit}
value={commentText}
onChange={setCommentText}
/>
))}
</div>
{!isAuth || openFormForId ? null : (
<CommentsForm onSubmit={handleSubmit} value={commentText} onChange={setCommentText} />
)}
</div>
);
};

export default memo(Comments);
35 changes: 35 additions & 0 deletions src/containers/comments/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
.Comments {
display: flex;
flex-direction: column;
gap: 24px;
}

.Comments-title {
font-family: var(--second-font-family);
font-size: 24px;
font-weight: bold;
}

.Comments-link {
font-weight: 400;
font-size: 16px;
line-height: 22px;
}

.Comments-link-title {
color: var(--primary);
text-decoration: none;
&:hover {
text-decoration: underline;
}
}

.Smart-Comments {
display: flex;
flex-direction: column;
gap: 16px;
}

.Smart-Comment {
padding-left: 40px;
}
6 changes: 5 additions & 1 deletion src/store-redux/article/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,17 @@ export default {
dispatch({ type: 'article/load-start' });

try {
const params = new URLSearchParams({
fields: '*,madeIn(title,code),category(title)',
});
const res = await services.api.request({
url: `/api/v1/articles/${id}?fields=*,madeIn(title,code),category(title)`,
url: `/api/v1/articles/${id}?${params}`,
});
// Товар загружен успешно
dispatch({ type: 'article/load-success', payload: { data: res.data.result } });
} catch (e) {
//Ошибка загрузки
console.error(e);
dispatch({ type: 'article/load-error' });
}
};
Expand Down
Loading