feat: blogs #118

Merged
enes merged 20 commits from feature/blogs into staging 2024-11-11 12:00:59 +00:00
2 changed files with 57 additions and 20 deletions
Showing only changes of commit 2f563e1bfb - Show all commits

View File

@ -167,7 +167,7 @@ type MenuBarProps = {
editor: Editor editor: Editor
} }
const MenuBar = ({ editor }: MenuBarProps) => { export const MenuBar = ({ editor }: MenuBarProps) => {
const setLink = () => { const setLink = () => {
// Prompt the user to enter a URL // Prompt the user to enter a URL
let url = prompt('URL') let url = prompt('URL')

View File

@ -1,28 +1,57 @@
import { useState } from 'react' import { useState } from 'react'
import { Form, useActionData, useNavigation } from 'react-router-dom' import {
Form,
useActionData,
useLoaderData,
useNavigation
} from 'react-router-dom'
import { import {
CheckboxFieldUncontrolled, CheckboxFieldUncontrolled,
InputField, InputError,
InputFieldUncontrolled InputFieldUncontrolled,
MenuBar
} from '../../components/Inputs' } from '../../components/Inputs'
import { ProfileSection } from '../../components/ProfileSection' import { ProfileSection } from '../../components/ProfileSection'
import { useAppSelector } from '../../hooks' import { useAppSelector } from '../../hooks'
import { BlogFormErrors } from 'types' import { BlogFormErrors, BlogPageLoaderResult } from 'types'
import '../../styles/innerPage.css' import '../../styles/innerPage.css'
import '../../styles/styles.css' import '../../styles/styles.css'
import '../../styles/write.css' import '../../styles/write.css'
import { LoadingSpinner } from 'components/LoadingSpinner' import { LoadingSpinner } from 'components/LoadingSpinner'
import { marked } from 'marked'
import DOMPurify from 'dompurify'
import { EditorContent, useEditor } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'
import Link from '@tiptap/extension-link'
import Image from '@tiptap/extension-image'
export const WritePage = () => { export const WritePage = () => {
const userState = useAppSelector((state) => state.user) const userState = useAppSelector((state) => state.user)
const data = useLoaderData() as BlogPageLoaderResult
const formErrors = useActionData() as BlogFormErrors const formErrors = useActionData() as BlogFormErrors
const navigation = useNavigation() const navigation = useNavigation()
const title = 'Submit a blog post'
const [content, setContent] = useState<string>('') const blog = data?.blog
const handleContentChange = (_: string, value: string) => { const title = data?.blog ? 'Edit blog post' : 'Submit a blog post'
setContent(value) const html = marked.parse(blog?.content || '', { async: false })
const sanitized = DOMPurify.sanitize(html)
const [content, setContent] = useState<string>(sanitized)
const editor = useEditor({
content: content,
extensions: [
StarterKit,
Link,
Image.configure({
inline: true,
HTMLAttributes: {
class: 'IBMSMSMBSSPostImg'
} }
})
],
onUpdate: ({ editor }) => {
setContent(editor.getHTML())
}
})
return ( return (
<div className='InnerBodyMain'> <div className='InnerBodyMain'>
@ -43,28 +72,34 @@ export const WritePage = () => {
<InputFieldUncontrolled <InputFieldUncontrolled
label='Title' label='Title'
name='title' name='title'
defaultValue={blog?.title}
error={formErrors?.title} error={formErrors?.title}
/> />
<InputField {editor && (
label='Content' <div className='inputLabelWrapperMain'>
name={'content'} <label className='form-label labelMain'>Content</label>
type='richtext' <div className='inputMain'>
placeholder='Blog content' <MenuBar editor={editor} />
value={content} <EditorContent editor={editor} />
error={formErrors?.content} </div>
onChange={handleContentChange} {typeof formErrors?.content !== 'undefined' && (
/> <InputError message={formErrors?.content} />
)}
<input name='content' hidden value={content} readOnly /> <input name='content' hidden value={content} readOnly />
</div>
)}
<InputFieldUncontrolled <InputFieldUncontrolled
label='Featured Image URL' label='Featured Image URL'
name='image' name='image'
inputMode='url' inputMode='url'
defaultValue={blog?.image}
error={formErrors?.image} error={formErrors?.image}
/> />
<InputFieldUncontrolled <InputFieldUncontrolled
label='Summary' label='Summary'
name='summary' name='summary'
type='textarea' type='textarea'
defaultValue={blog?.summary}
error={formErrors?.summary} error={formErrors?.summary}
/> />
<InputFieldUncontrolled <InputFieldUncontrolled
@ -72,11 +107,13 @@ export const WritePage = () => {
description='Separate each tag with a comma. (Example: tag1, tag2, tag3)' description='Separate each tag with a comma. (Example: tag1, tag2, tag3)'
placeholder='Tags' placeholder='Tags'
name='tags' name='tags'
defaultValue={blog?.tTags?.join(', ')}
error={formErrors?.tags} error={formErrors?.tags}
/> />
<CheckboxFieldUncontrolled <CheckboxFieldUncontrolled
label='This post is not safe for work (NSFW)' label='This post is not safe for work (NSFW)'
name='nsfw' name='nsfw'
defaultChecked={blog?.nsfw}
/> />
<div className='IBMSMSMBS_WriteAction'> <div className='IBMSMSMBS_WriteAction'>
<button <button