webapp/src/context/editor.tsx

129 lines
3.4 KiB
TypeScript
Raw Normal View History

import type { JSX } from 'solid-js'
import { Accessor, createContext, createSignal, useContext } from 'solid-js'
import { createStore, SetStoreFunction } from 'solid-js/store'
import { Topic } from '../graphql/types.gen'
2023-05-05 20:05:50 +00:00
import { apiClient } from '../utils/apiClient'
import { useLocalize } from './localize'
import { useSnackbar } from './snackbar'
type WordCounter = {
characters: number
words: number
}
type ShoutForm = {
shoutId: number
slug: string
title: string
subtitle: string
selectedTopics: Topic[]
mainTopic: string
body: string
coverImageUrl: string
}
type EditorContextType = {
isEditorPanelVisible: Accessor<boolean>
wordCounter: Accessor<WordCounter>
form: ShoutForm
2023-05-05 20:05:50 +00:00
formErrors: Partial<ShoutForm>
actions: {
2023-05-05 20:05:50 +00:00
saveShout: () => Promise<boolean>
publishShout: () => Promise<boolean>
toggleEditorPanel: () => void
countWords: (value: WordCounter) => void
setForm: SetStoreFunction<ShoutForm>
2023-05-05 20:05:50 +00:00
setFormErrors: SetStoreFunction<Partial<ShoutForm>>
}
}
const EditorContext = createContext<EditorContextType>()
export function useEditorContext() {
return useContext(EditorContext)
}
export const EditorProvider = (props: { children: JSX.Element }) => {
2023-05-05 20:05:50 +00:00
const { t } = useLocalize()
const {
actions: { showSnackbar }
} = useSnackbar()
2023-05-03 16:13:48 +00:00
const [isEditorPanelVisible, setIsEditorPanelVisible] = createSignal<boolean>(false)
const [form, setForm] = createStore<ShoutForm>(null)
2023-05-05 20:05:50 +00:00
const [formErrors, setFormErrors] = createStore<Partial<ShoutForm>>(null)
const [wordCounter, setWordCounter] = createSignal<WordCounter>({
characters: 0,
words: 0
})
2023-05-05 20:05:50 +00:00
2023-05-03 16:13:48 +00:00
const toggleEditorPanel = () => setIsEditorPanelVisible((value) => !value)
const countWords = (value) => setWordCounter(value)
2023-05-05 20:05:50 +00:00
const saveShout = async () => {
if (!form.title) {
setFormErrors('title', t('Required'))
return false
}
try {
await apiClient.updateArticle({
shoutId: form.shoutId,
shoutInput: {
2023-05-05 20:05:50 +00:00
body: form.body,
topics: form.selectedTopics.map((topic) => topic.slug),
// authors?: InputMaybe<Array<InputMaybe<Scalars['String']>>>
// community?: InputMaybe<Scalars['Int']>
mainTopic: form.selectedTopics[0]?.slug || 'society',
slug: form.slug,
subtitle: form.subtitle,
2023-05-07 19:33:20 +00:00
title: form.title,
cover: form.coverImageUrl
2023-05-05 20:05:50 +00:00
}
})
return true
} catch (error) {
console.error(error)
showSnackbar({ type: 'error', body: t('Error') })
return false
}
}
const publishShout = async () => {
try {
await apiClient.publishShout({
slug: form.slug,
shoutInput: {
body: form.body,
topics: form.selectedTopics.map((topic) => topic.slug),
// authors?: InputMaybe<Array<InputMaybe<Scalars['String']>>>
// community?: InputMaybe<Scalars['Int']>
mainTopic: form.selectedTopics[0]?.slug || '',
slug: form.slug,
subtitle: form.subtitle,
title: form.title
}
})
return true
} catch {
showSnackbar({ type: 'error', body: t('Error') })
return false
}
}
const actions = {
2023-05-05 20:05:50 +00:00
saveShout,
publishShout,
toggleEditorPanel,
countWords,
2023-05-05 20:05:50 +00:00
setForm,
setFormErrors
}
2023-05-05 20:05:50 +00:00
const value: EditorContextType = { actions, form, formErrors, isEditorPanelVisible, wordCounter }
return <EditorContext.Provider value={value}>{props.children}</EditorContext.Provider>
}