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
}
const MenuBar = ({ editor }: MenuBarProps) => {
export const MenuBar = ({ editor }: MenuBarProps) => {
const setLink = () => {
// Prompt the user to enter a URL
let url = prompt('URL')

View File

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