import { useNavigate } from '@solidjs/router' import { clsx } from 'clsx' import { Show, createEffect, createSignal, lazy, onMount } from 'solid-js' import { createStore } from 'solid-js/store' import { UploadModalContent } from '~/components/Upload/UploadModalContent/UploadModalContent' import { Button } from '~/components/_shared/Button' import { Icon } from '~/components/_shared/Icon' import { Image } from '~/components/_shared/Image' import { ShoutForm, useEditorContext } from '~/context/editor' import { useLocalize } from '~/context/localize' import { useSession } from '~/context/session' import { useTopics } from '~/context/topics' import { useSnackbar, useUI } from '~/context/ui' import { Topic } from '~/graphql/schema/core.gen' import { UploadedFile } from '~/types/upload' import { Modal } from '../_shared/Modal' import { TopicSelect } from '../_shared/TopicSelect' import stylesBeside from '../Feed/Beside.module.scss' // TODO: should not be here, implement more components import styles from './PublishSettings.module.scss' const MicroEditor = lazy(() => import('../Editor/MicroEditor')) const GrowingTextarea = lazy(() => import('~/components/_shared/GrowingTextarea/GrowingTextarea')) const DESCRIPTION_MAX_LENGTH = 400 type Props = { shoutId: number form: ShoutForm } const shorten = (str: string, maxLen: number) => { if (str.length <= maxLen) return str const result = str.slice(0, Math.max(0, str.lastIndexOf(' ', maxLen))).trim() return `${result}...` } const EMPTY_TOPIC: Topic = { id: -1, slug: '' } interface FormConfig { coverImageUrl?: string mainTopic?: Topic slug?: string title?: string subtitle?: string description?: string selectedTopics?: Topic[] } const emptyConfig: FormConfig = { coverImageUrl: '', mainTopic: EMPTY_TOPIC, slug: '', title: '', subtitle: '', description: '', selectedTopics: [] } export const PublishSettings = (props: Props) => { const { t } = useLocalize() const { showModal, hideModal } = useUI() const navigate = useNavigate() const { session } = useSession() const { sortedTopics } = useTopics() const { showSnackbar } = useSnackbar() const [topics, setTopics] = createSignal(sortedTopics()) const composeDescription = () => { if (!props.form.description) { const cleanFootnotes = props.form.body.replaceAll(/(.*?)<\/footnote>/g, '') const leadText = cleanFootnotes.replaceAll(/<\/?[^>]+(>|$)/gi, ' ') return shorten(leadText, DESCRIPTION_MAX_LENGTH).trim() } return props.form.description } const initialData = () => { return { coverImageUrl: props.form?.coverImageUrl, mainTopic: props.form?.mainTopic || EMPTY_TOPIC, slug: props.form?.slug || '', title: props.form?.title || '', subtitle: props.form?.subtitle || '', description: composeDescription() || '', selectedTopics: [] } } const [settingsForm, setSettingsForm] = createStore(emptyConfig) onMount(() => { setSettingsForm(initialData()) }) createEffect(() => setTopics(sortedTopics())) const { formErrors, setForm, setFormErrors, saveShout, publishShout } = useEditorContext() const handleUploadModalContentCloseSetCover = (image: UploadedFile | undefined) => { hideModal() setSettingsForm('coverImageUrl', image?.url) } const handleDeleteCoverImage = () => { setSettingsForm('coverImageUrl', '') } const handleTopicSelectChange = (newSelectedTopics: Topic[]) => { if ( props.form.selectedTopics.length === 0 || newSelectedTopics.every((topic: Topic) => topic.id !== props.form.mainTopic?.id) ) { setSettingsForm((prev) => { return { ...prev, mainTopic: newSelectedTopics[0] } }) } if (newSelectedTopics.length > 0) { setFormErrors('selectedTopics', '') } setForm('selectedTopics', newSelectedTopics) } const handleBackClick = () => { navigate(`/edit/${props.shoutId}`) } const handleCancelClick = () => { setSettingsForm(initialData()) handleBackClick() } const handlePublishSubmit = () => { const shoutData = { ...props.form, ...settingsForm } if (shoutData?.mainTopic) { publishShout(shoutData) } else { showSnackbar({ body: t('Please, set the main topic first') }) } } const handleSaveDraft = () => { saveShout({ ...props.form, ...settingsForm }) } const removeSpecial = (ev: InputEvent) => { const input = ev.target as HTMLInputElement const value = input.value const newValue = value.startsWith('@') || value.startsWith('!') ? value.substring(1) : value input.value = newValue } return (

{t('Publish Settings')}

{t('Material card')}

{initialData().title}
{settingsForm.mainTopic?.title || ''}
{settingsForm.title}
{settingsForm.subtitle || ''}
{session()?.user?.app_data?.profile?.name || t('Anonymous')}

{t( 'Choose a title image for the article. You can immediately see how the publication card will look like.' )}

value={(value: any) => setSettingsForm('title', value)} allowEnterKey={false} maxLength={100} /> value={(value: any) => setSettingsForm('subtitle', value)} allowEnterKey={false} maxLength={100} /> value && setForm('description', value)} />

{t('Slug')}

{t('Topics')}

{t( 'Add a few topics so that the reader knows what your content is about and can find it on pages of topics that interest them. Topics can be swapped, the first topic becomes the title' )}

0}> setForm('mainTopic', mainTopic)} mainTopic={props.form.mainTopic} />
{formErrors.selectedTopics}

{t('Collaborators')}

handleUploadModalContentCloseSetCover(value as UploadedFile) } />
) }