summaryrefslogtreecommitdiffstats
path: root/src/components/Comment/Comment.tsx
blob: 03712887c6ad201228fb9d2735d245a5c28dc906 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import { Button } from '@components/Buttons';
import Spinner from '@components/Spinner/Spinner';
import { Comment as CommentData } from '@ts/types/comments';
import { settings } from '@utils/config';
import { getFormattedDate } from '@utils/helpers/format';
import dynamic from 'next/dynamic';
import Image from 'next/image';
import Link from 'next/link';
import { useRouter } from 'next/router';
import Script from 'next/script';
import { useState } from 'react';
import { useIntl } from 'react-intl';
import { Comment as CommentSchema, WithContext } from 'schema-dts';
import styles from './Comment.module.scss';

const DynamicCommentForm = dynamic(
  () => import('@components/CommentForm/CommentForm'),
  {
    loading: () => <Spinner />,
  }
);

const Comment = ({
  articleId,
  comment,
  isNested = false,
}: {
  articleId: number;
  comment: CommentData;
  isNested?: boolean;
}) => {
  const intl = useIntl();
  const router = useRouter();
  const locale = router.locale ? router.locale : settings.locales.defaultLocale;
  const [shouldOpenForm, setShouldOpenForm] = useState<boolean>(false);

  const getCommentAuthor = () => {
    return comment.author.url ? (
      <Link href={comment.author.url}>
        <a className={styles.author}>{comment.author.name}</a>
      </Link>
    ) : (
      <span className={styles.author}>{comment.author.name}</span>
    );
  };

  const getLocaleDate = () => {
    const date = getFormattedDate(comment.date, locale);
    const time = new Date(comment.date)
      .toLocaleTimeString(locale, {
        hour: 'numeric',
        minute: 'numeric',
      })
      .replace(':', 'h');
    return intl.formatMessage(
      {
        defaultMessage: '{date} at {time}',
        description: 'Comment: publication date',
      },
      {
        date,
        time,
      }
    );
  };

  const getApprovedComment = () => {
    return (
      <>
        <article className={styles.wrapper} id={`comment-${comment.commentId}`}>
          <header className={styles.header}>
            {comment.author.gravatarUrl && (
              <div className={styles.avatar}>
                <Image
                  src={comment.author.gravatarUrl}
                  alt={comment.author.name}
                  layout="fill"
                />
              </div>
            )}
            {getCommentAuthor()}
          </header>
          <dl className={styles.date}>
            <dt>
              {intl.formatMessage({
                defaultMessage: 'Published on:',
                description: 'Comment: publication date label',
              })}
            </dt>
            <dd>
              <time dateTime={comment.date}>
                <Link href={`#comment-${comment.commentId}`}>
                  <a>{getLocaleDate()}</a>
                </Link>
              </time>
            </dd>
          </dl>
          <div
            className={styles.body}
            dangerouslySetInnerHTML={{ __html: comment.content }}
          ></div>
          {!isNested && (
            <footer className={styles.footer}>
              <Button clickHandler={() => setShouldOpenForm((prev) => !prev)}>
                {shouldOpenForm
                  ? intl.formatMessage({
                      defaultMessage: 'Cancel reply',
                      description: 'Comment: reply button',
                    })
                  : intl.formatMessage({
                      defaultMessage: 'Reply',
                      description: 'Comment: reply button',
                    })}
              </Button>
            </footer>
          )}
        </article>
        {shouldOpenForm && (
          <DynamicCommentForm
            articleId={articleId}
            parentId={comment.commentId}
          />
        )}
        {comment.replies.length > 0 && (
          <ol className={styles.list}>
            {comment.replies.map((reply) => {
              return (
                <Comment
                  articleId={articleId}
                  key={reply.commentId}
                  comment={reply}
                  isNested={true}
                />
              );
            })}
          </ol>
        )}
      </>
    );
  };

  const getCommentStatus = () => {
    return (
      <p>
        {intl.formatMessage({
          defaultMessage: 'This comment is awaiting moderation.',
          description: 'Comment: awaiting moderation message',
        })}
      </p>
    );
  };

  const schemaJsonLd: WithContext<CommentSchema> = {
    '@context': 'https://schema.org',
    '@id': `${settings.url}/#comment-${comment.commentId}`,
    '@type': 'Comment',
    parentItem: isNested
      ? { '@id': `${settings.url}/#comment-${comment.parentDatabaseId}` }
      : undefined,
    about: { '@type': 'Article', '@id': `${settings.url}/#article` },
    author: {
      '@type': 'Person',
      name: comment.author.name,
      image: comment.author.gravatarUrl,
      url: comment.author.url,
    },
    creator: {
      '@type': 'Person',
      name: comment.author.name,
      image: comment.author.gravatarUrl,
      url: comment.author.url,
    },
    dateCreated: comment.date,
    datePublished: comment.date,
    text: comment.content,
  };

  return (
    <>
      <Script
        id="schema-comments"
        type="application/ld+json"
        dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }}
      />
      <li className={styles.item}>
        {comment.approved ? getApprovedComment() : getCommentStatus()}
      </li>
    </>
  );
};

export default Comment;