feat: blogs #118
@ -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')
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user