diff options
| -rw-r--r-- | src/components/CommentForm/CommentForm.tsx | 53 | ||||
| -rw-r--r-- | src/components/Notice/Notice.module.scss | 28 | ||||
| -rw-r--r-- | src/components/Notice/Notice.tsx | 20 | ||||
| -rw-r--r-- | src/pages/article/[slug].tsx | 2 | ||||
| -rw-r--r-- | src/services/graphql/comments.ts | 68 | ||||
| -rw-r--r-- | src/services/graphql/post.ts | 1 | ||||
| -rw-r--r-- | src/ts/types/comments.ts | 22 |
7 files changed, 191 insertions, 3 deletions
diff --git a/src/components/CommentForm/CommentForm.tsx b/src/components/CommentForm/CommentForm.tsx index be6f5a6..988468c 100644 --- a/src/components/CommentForm/CommentForm.tsx +++ b/src/components/CommentForm/CommentForm.tsx @@ -1,16 +1,62 @@ import { ButtonSubmit } from '@components/Buttons'; import { Form, FormItem, Input, TextArea } from '@components/Form'; +import Notice from '@components/Notice/Notice'; import { t } from '@lingui/macro'; +import { createComment } from '@services/graphql/comments'; import { useState } from 'react'; -const CommentForm = () => { +const CommentForm = ({ + articleId, + parentId = 0, +}: { + articleId: number; + parentId?: number; +}) => { const [name, setName] = useState(''); const [email, setEmail] = useState(''); const [website, setWebsite] = useState(''); const [message, setMessage] = useState(''); + const [isSuccess, setIsSuccess] = useState(false); + const [isApproved, setIsApproved] = useState(false); + + const resetForm = () => { + setName(''); + setEmail(''); + setWebsite(''); + setMessage(''); + }; + + const submitHandler = async (e: SubmitEvent) => { + e.preventDefault(); + + if (name && email && message && articleId) { + const createdComment = await createComment( + name, + email, + website, + message, + parentId, + articleId, + 'createComment' + ); + + if (createdComment.success) setIsSuccess(true); + if (isSuccess) { + resetForm(); + if (createdComment.comment?.approved) setIsApproved(true); + + setTimeout(() => { + setIsSuccess(false); + setIsApproved(false); + }, 8000); + } + } else { + setIsSuccess(false); + } + }; return ( - <Form> + <Form submitHandler={submitHandler}> <FormItem> <Input id="commenter-name" @@ -51,6 +97,9 @@ const CommentForm = () => { /> </FormItem> <ButtonSubmit>{t`Send`}</ButtonSubmit> + {isSuccess && !isApproved && ( + <Notice type="success">{t`Thanks for your comment! It is now awaiting moderation.`}</Notice> + )} </Form> ); }; diff --git a/src/components/Notice/Notice.module.scss b/src/components/Notice/Notice.module.scss new file mode 100644 index 0000000..deae4e4 --- /dev/null +++ b/src/components/Notice/Notice.module.scss @@ -0,0 +1,28 @@ +@use "@styles/abstracts/functions" as fun; + +.message { + border: fun.convert-px(2) solid; + font-weight: bold; + margin: var(--spacing-sm) auto; + padding: var(--spacing-2xs) var(--spacing-xs); + + &--error { + border-color: var(--color-error); + color: var(--color-error); + } + + &--info { + border-color: var(--color-info); + color: var(--color-info); + } + + &--success { + border-color: var(--color-success); + color: var(--color-success); + } + + &--warning { + border-color: var(--color-warning); + color: var(--color-warning); + } +} diff --git a/src/components/Notice/Notice.tsx b/src/components/Notice/Notice.tsx new file mode 100644 index 0000000..c941bf9 --- /dev/null +++ b/src/components/Notice/Notice.tsx @@ -0,0 +1,20 @@ +import { ReactNode } from 'react'; +import styles from './Notice.module.scss'; + +type NoticeType = 'error' | 'info' | 'success' | 'warning'; + +const Notice = ({ + children, + type, +}: { + children: ReactNode; + type: NoticeType; +}) => { + return ( + <div className={`${styles.message} ${styles[`message--${type}`]}`}> + {children} + </div> + ); +}; + +export default Notice; diff --git a/src/pages/article/[slug].tsx b/src/pages/article/[slug].tsx index df5ae51..55753c3 100644 --- a/src/pages/article/[slug].tsx +++ b/src/pages/article/[slug].tsx @@ -47,7 +47,7 @@ const SingleArticle: NextPageWithLayout<ArticleProps> = ({ post }) => { <h2>{t`Comments`}</h2> <CommentsList comments={comments} /> <h2>{t`Leave a comment`}</h2> - <CommentForm /> + <CommentForm articleId={post.databaseId} /> </section> </article> </> diff --git a/src/services/graphql/comments.ts b/src/services/graphql/comments.ts new file mode 100644 index 0000000..b7a9ed2 --- /dev/null +++ b/src/services/graphql/comments.ts @@ -0,0 +1,68 @@ +import { + CreatedCommentResponse, + CreatedCommentReturn, +} from '@ts/types/comments'; +import { gql } from 'graphql-request'; +import { getGraphQLClient } from './client'; + +export const createComment: CreatedCommentReturn = async ( + author: string, + authorEmail: string, + authorUrl: string, + content: string, + parent: number, + commentOn: number, + mutationId: string +) => { + const client = getGraphQLClient(); + const mutation = gql` + mutation CreateComment( + $author: String! + $authorEmail: String! + $authorUrl: String! + $content: String! + $parent: ID! + $commentOn: Int! + $mutationId: String! + ) { + createComment( + input: { + author: $author + authorEmail: $authorEmail + authorUrl: $authorUrl + content: $content + parent: $parent + commentOn: $commentOn + clientMutationId: $mutationId + } + ) { + clientMutationId + success + comment { + approved + } + } + } + `; + + const variables = { + author, + authorEmail, + authorUrl, + content, + parent, + commentOn, + mutationId, + }; + + try { + const response: CreatedCommentResponse = await client.request( + mutation, + variables + ); + return response.createComment; + } catch (error) { + console.error(error, undefined, 2); + throw new Error(`An uncaught exception has occurred: ${error}`); + } +}; diff --git a/src/services/graphql/post.ts b/src/services/graphql/post.ts index 245bf7a..08411bf 100644 --- a/src/services/graphql/post.ts +++ b/src/services/graphql/post.ts @@ -65,6 +65,7 @@ const fetchPostBySlug: FetchPostByReturn = async (slug: string) => { afterMore beforeMore } + databaseId date featuredImage { node { diff --git a/src/ts/types/comments.ts b/src/ts/types/comments.ts index 51852d0..a1bb120 100644 --- a/src/ts/types/comments.ts +++ b/src/ts/types/comments.ts @@ -26,3 +26,25 @@ export type RawComment = Omit<Comment, 'author'> & { export type CommentsResponse = { nodes: RawComment[]; }; + +export type CreatedComment = { + clientMutationId: string; + success: boolean; + comment: null | { + approved: boolean; + }; +}; + +export type CreatedCommentResponse = { + createComment: CreatedComment; +}; + +export type CreatedCommentReturn = ( + author: string, + authorEmail: string, + authorUrl: string, + content: string, + parent: number, + commentOn: number, + mutationId: string +) => Promise<CreatedComment>; |
