aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/templates/page/page-comments.tsx
diff options
context:
space:
mode:
authorArmand Philippot <git@armandphilippot.com>2023-11-20 12:27:46 +0100
committerArmand Philippot <git@armandphilippot.com>2023-11-20 19:32:09 +0100
commit70b4f633a6fbedb58c8b9134ac64ede854d489de (patch)
treec757bb12ad9a588e23b25cdb8b46710ac14dbcb1 /src/components/templates/page/page-comments.tsx
parent9a481f066e1427d53a06cf7aeec525a745abf03f (diff)
refactor(components): replace PageLayout template with Page
* split pages in smaller components (it is both easier to maintain and more readable, we avoid the use of fragments in pages directory) * extract breadcrumbs from article tag (the navigation is not related to the page contents) * remove useReadingTime hook * remove layout options except `isHome`
Diffstat (limited to 'src/components/templates/page/page-comments.tsx')
-rw-r--r--src/components/templates/page/page-comments.tsx178
1 files changed, 178 insertions, 0 deletions
diff --git a/src/components/templates/page/page-comments.tsx b/src/components/templates/page/page-comments.tsx
new file mode 100644
index 0000000..bc715e8
--- /dev/null
+++ b/src/components/templates/page/page-comments.tsx
@@ -0,0 +1,178 @@
+import {
+ type ForwardRefRenderFunction,
+ type HTMLAttributes,
+ forwardRef,
+ type ReactNode,
+ useCallback,
+} from 'react';
+import { useIntl } from 'react-intl';
+import { sendComment } from '../../../services/graphql';
+import type { SendCommentInput } from '../../../types';
+import { Heading, Link } from '../../atoms';
+import { Card, CardBody } from '../../molecules';
+import {
+ type CommentData,
+ CommentsList,
+ type CommentsListProps,
+} from '../../organisms/comments-list';
+import { CommentForm, type CommentFormSubmit } from '../../organisms/forms';
+import styles from './page.module.scss';
+
+const link = (chunks: ReactNode) => (
+ // eslint-disable-next-line react/jsx-no-literals
+ <Link href="#comment-form-section">{chunks}</Link>
+);
+
+export type PageCommentsProps = Omit<
+ HTMLAttributes<HTMLDivElement>,
+ 'children' | 'onSubmit'
+> &
+ Pick<CommentsListProps, 'depth'> & {
+ /**
+ * Should the comments form be removed from the page?
+ *
+ * @default false
+ */
+ areCommentsClosed?: boolean;
+ /**
+ * The page comments.
+ */
+ comments: CommentData[];
+ /**
+ * The database page id.
+ */
+ pageId: number;
+ };
+
+const PageCommentsWithRef: ForwardRefRenderFunction<
+ HTMLDivElement,
+ PageCommentsProps
+> = (
+ {
+ areCommentsClosed = false,
+ className = '',
+ comments,
+ depth,
+ pageId,
+ ...props
+ },
+ ref
+) => {
+ const wrapperClass = `${styles.comments} ${className}`;
+ const commentsCount =
+ comments.length +
+ comments.reduce(
+ (accumulator, currentValue) =>
+ accumulator + (currentValue.replies?.length ?? 0),
+ 0
+ );
+ const intl = useIntl();
+ const commentsListTitle = intl.formatMessage(
+ {
+ defaultMessage:
+ '{commentsCount, plural, =0 {No comments} one {# comment} other {# comments}}',
+ description: 'PageComments: the section title of the comments list',
+ id: 'H4pKJP',
+ },
+ { commentsCount }
+ );
+ const commentFormSectionTitle = intl.formatMessage({
+ defaultMessage: 'Leave a comment',
+ description: 'PageComments: the section title of the comment form',
+ id: 'Y7XdNp',
+ });
+ const commentFormTitle = intl.formatMessage({
+ defaultMessage: 'Comment form',
+ description: 'PageComments: an accessible name for the comment form',
+ id: 'o+wCJz',
+ });
+ const noCommentsYet = intl.formatMessage<ReactNode>(
+ {
+ defaultMessage: 'No comments yet. <link>Be the first!</link>',
+ id: 'w+BpPg',
+ description: 'PageComments: no comments text',
+ },
+ {
+ link,
+ }
+ );
+
+ const saveComment: CommentFormSubmit = useCallback(
+ async (data) => {
+ const commentData: SendCommentInput = {
+ author: data.author,
+ authorEmail: data.email,
+ authorUrl: data.website ?? '',
+ clientMutationId: 'comment',
+ commentOn: pageId,
+ content: data.comment,
+ parent: data.parentId,
+ };
+ const { comment, success } = await sendComment(commentData);
+ const successPrefix = intl.formatMessage({
+ defaultMessage: 'Thanks, your comment was successfully sent.',
+ description: 'PageComments: comment form success message',
+ id: 'ZcFroC',
+ });
+ const successMessage = comment?.approved
+ ? intl.formatMessage({
+ defaultMessage: 'It has been approved.',
+ id: 'UgJwSU',
+ description: 'PageComments: comment approved.',
+ })
+ : intl.formatMessage({
+ defaultMessage: 'It is now awaiting moderation.',
+ id: '/EfcyW',
+ description: 'PageComments: comment awaiting moderation',
+ });
+
+ return {
+ messages: {
+ success: `${successPrefix} ${successMessage}`,
+ },
+ validator: () => success,
+ };
+ },
+ [intl, pageId]
+ );
+
+ return (
+ <div {...props} className={wrapperClass} ref={ref}>
+ <section className={styles.section}>
+ <Heading className={styles.heading} level={2}>
+ {commentsListTitle}
+ </Heading>
+ {comments.length ? (
+ <CommentsList
+ areRepliesForbidden={areCommentsClosed}
+ comments={comments}
+ depth={depth}
+ onSubmit={saveComment}
+ />
+ ) : (
+ <Card variant={2}>
+ <CardBody>{noCommentsYet}</CardBody>
+ </Card>
+ )}
+ </section>
+ {areCommentsClosed ? null : (
+ <section
+ className={styles.section}
+ // eslint-disable-next-line react/jsx-no-literals
+ id="comment-form-section"
+ >
+ <Heading className={styles.heading} level={2}>
+ {commentFormSectionTitle}
+ </Heading>
+ <CommentForm
+ aria-label={commentFormTitle}
+ className={styles.form}
+ onSubmit={saveComment}
+ />
+ </section>
+ )}
+ </div>
+ );
+};
+
+export const PageComments = forwardRef(PageCommentsWithRef);