aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/Comment
diff options
context:
space:
mode:
authorArmand Philippot <git@armandphilippot.com>2021-12-17 17:58:03 +0100
committerArmand Philippot <git@armandphilippot.com>2021-12-17 17:58:03 +0100
commitabf6e96383035f99addab804e8c3dd1a74d36375 (patch)
tree066d672466495f948885379ac8fd7fb1228ad4cf /src/components/Comment
parent8a703cb39ff23ff3639b0da33f0d72f92f1cc55b (diff)
chore: display comments list on posts
Diffstat (limited to 'src/components/Comment')
-rw-r--r--src/components/Comment/Comment.module.scss87
-rw-r--r--src/components/Comment/Comment.tsx88
2 files changed, 175 insertions, 0 deletions
diff --git a/src/components/Comment/Comment.module.scss b/src/components/Comment/Comment.module.scss
new file mode 100644
index 0000000..8f17bfa
--- /dev/null
+++ b/src/components/Comment/Comment.module.scss
@@ -0,0 +1,87 @@
+@use "@styles/abstracts/functions" as fun;
+@use "@styles/abstracts/mixins" as mix;
+
+.item {
+ margin: var(--spacing-sm) 0;
+}
+
+.wrapper {
+ border: fun.convert-px(1) solid var(--color-border-light);
+ box-shadow: fun.convert-px(3) fun.convert-px(3) 0 0 var(--color-shadow-light),
+ fun.convert-px(4) fun.convert-px(4) fun.convert-px(3) fun.convert-px(-2)
+ var(--color-shadow-light);
+ padding: var(--spacing-md);
+ position: relative;
+
+ @include mix.media("screen") {
+ @include mix.dimensions("sm") {
+ display: grid;
+ grid-template-columns: minmax(#{fun.convert-px(150)}, 1fr) minmax(0, 85ch);
+ column-gap: var(--spacing-lg);
+ }
+ }
+}
+
+.header {
+ display: flex;
+ flex-flow: column wrap;
+ align-items: center;
+ row-gap: var(--spacing-sm);
+
+ @include mix.media("screen") {
+ @include mix.dimensions("sm") {
+ grid-row: 1 / 4;
+ }
+ }
+}
+
+.avatar {
+ width: fun.convert-px(85);
+ height: fun.convert-px(85);
+ border-radius: fun.convert-px(3);
+ box-shadow: 0 0 0 fun.convert-px(1) var(--color-shadow-light),
+ fun.convert-px(3) fun.convert-px(3) 0 0 var(--color-shadow-light);
+ position: relative;
+}
+
+.date {
+ color: var(--color-fg-secondary);
+ font-size: var(--font-size-sm);
+ display: grid;
+ grid-template-columns: repeat(2, max-content);
+ column-gap: var(--spacing-2xs);
+ justify-content: center;
+ margin: var(--spacing-sm) 0 var(--spacing-md);
+
+ @include mix.media("screen") {
+ @include mix.dimensions("sm") {
+ justify-content: left;
+ margin: 0 0 var(--spacing-md);
+ }
+ }
+}
+
+.body {
+ overflow-wrap: break-word;
+}
+
+.footer {
+ display: flex;
+ justify-content: flex-end;
+ align-items: center;
+ padding: var(--spacing-md) 0 0;
+
+ @include mix.media("screen") {
+ @include mix.dimensions("sm") {
+ padding: var(--spacing-sm) 0 0;
+ }
+ }
+
+ button {
+ margin: 0;
+ }
+}
+
+.list {
+ padding-left: var(--spacing-md);
+}
diff --git a/src/components/Comment/Comment.tsx b/src/components/Comment/Comment.tsx
new file mode 100644
index 0000000..ad6875c
--- /dev/null
+++ b/src/components/Comment/Comment.tsx
@@ -0,0 +1,88 @@
+import { Button } from '@components/Buttons';
+import { t } from '@lingui/macro';
+import { Comment as CommentData } from '@ts/types/comments';
+import Image from 'next/image';
+import Link from 'next/link';
+import { useRouter } from 'next/router';
+import styles from './Comment.module.scss';
+
+const Comment = ({ comment }: { comment: CommentData }) => {
+ const router = useRouter();
+
+ const getCommentAuthor = () => {
+ return comment.author.url ? (
+ <Link href={comment.author.url}>
+ <a>{comment.author.name}</a>
+ </Link>
+ ) : (
+ <span>{comment.author.name}</span>
+ );
+ };
+
+ const getLocaleDate = () => {
+ const commentDate = new Date(comment.date);
+ const date = commentDate.toLocaleDateString(router.locale, {
+ year: 'numeric',
+ month: 'long',
+ day: 'numeric',
+ });
+ const time = commentDate
+ .toLocaleTimeString(router.locale, {
+ hour: 'numeric',
+ minute: 'numeric',
+ })
+ .replace(':', 'h');
+ return t`${date} at ${time}`;
+ };
+
+ const getApprovedComment = () => {
+ return (
+ <>
+ <article className={styles.wrapper}>
+ <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>{t`Published on:`}</dt>
+ <dd>{getLocaleDate()}</dd>
+ </dl>
+ <div
+ className={styles.body}
+ dangerouslySetInnerHTML={{ __html: comment.content }}
+ ></div>
+ <footer className={styles.footer}>
+ <Button clickHandler={() => ''}>{t`Reply`}</Button>
+ </footer>
+ </article>
+ {comment.replies.length > 0 && (
+ <ol className={styles.list}>
+ {comment.replies.map((reply) => {
+ return <Comment key={reply.id} comment={reply} />;
+ })}
+ </ol>
+ )}
+ </>
+ );
+ };
+
+ const getCommentStatus = () => {
+ return <p>{t`This comment is awaiting moderation.`}</p>;
+ };
+
+ return (
+ <li className={styles.item}>
+ {comment.approved ? getApprovedComment() : getCommentStatus()}
+ </li>
+ );
+};
+
+export default Comment;