2023-11-14 15:10:00 +00:00
import { redirectPage } from '@nanostores/router'
2023-08-12 07:48:43 +00:00
import { clsx } from 'clsx'
2024-01-24 11:32:36 +00:00
import { createEffect , createSignal , lazy , onMount , Show } from 'solid-js'
2023-11-14 15:10:00 +00:00
import { createStore } from 'solid-js/store'
2023-08-12 07:48:43 +00:00
import { ShoutForm , useEditorContext } from '../../../context/editor'
import { useLocalize } from '../../../context/localize'
import { useSession } from '../../../context/session'
2024-01-23 19:01:41 +00:00
import { Topic } from '../../../graphql/schema/core.gen'
2023-11-14 15:10:00 +00:00
import { UploadedFile } from '../../../pages/types'
2023-08-12 07:48:43 +00:00
import { router } from '../../../stores/router'
2023-11-14 15:10:00 +00:00
import { hideModal , showModal } from '../../../stores/ui'
2024-01-22 18:53:04 +00:00
import { loadAllTopics , useTopicsStore } from '../../../stores/zine/topics'
2023-11-14 15:10:00 +00:00
import { Button } from '../../_shared/Button'
import { Icon } from '../../_shared/Icon'
2023-10-27 18:50:13 +00:00
import { Image } from '../../_shared/Image'
2023-11-14 15:10:00 +00:00
import { TopicSelect , UploadModalContent } from '../../Editor'
import { Modal } from '../../Nav/Modal'
import { EMPTY_TOPIC } from '../Edit'
import styles from './PublishSettings.module.scss'
import stylesBeside from '../../Feed/Beside.module.scss'
2023-08-12 07:48:43 +00:00
2023-11-18 14:10:02 +00:00
const SimplifiedEditor = lazy ( ( ) = > import ( '../../Editor/SimplifiedEditor' ) )
const GrowingTextarea = lazy ( ( ) = > import ( '../../_shared/GrowingTextarea/GrowingTextarea' ) )
const DESCRIPTION_MAX_LENGTH = 400
2023-08-12 07:48:43 +00:00
type Props = {
shoutId : number
form : ShoutForm
}
const shorten = ( str : string , maxLen : number ) = > {
if ( str . length <= maxLen ) return str
2023-08-13 21:26:40 +00:00
const result = str . slice ( 0 , Math . max ( 0 , str . lastIndexOf ( ' ' , maxLen ) ) ) . trim ( )
return ` ${ result } ... `
2023-08-12 07:48:43 +00:00
}
2023-12-03 08:44:11 +00:00
export const PublishSettings = ( props : Props ) = > {
2023-08-12 07:48:43 +00:00
const { t } = useLocalize ( )
2023-11-28 18:04:51 +00:00
const { author } = useSession ( )
2023-12-03 08:44:11 +00:00
const { sortedTopics } = useTopicsStore ( )
2023-08-12 07:48:43 +00:00
2024-01-23 19:01:41 +00:00
const [ topics , setTopics ] = createSignal < Topic [ ] > ( sortedTopics ( ) )
2024-01-24 11:32:36 +00:00
onMount ( async ( ) = > {
await loadAllTopics ( )
} )
createEffect ( ( ) = > {
2024-01-23 19:01:41 +00:00
setTopics ( sortedTopics ( ) )
2024-01-22 18:53:04 +00:00
} )
2023-08-22 13:37:54 +00:00
const composeDescription = ( ) = > {
if ( ! props . form . description ) {
2023-08-28 11:48:54 +00:00
const cleanFootnotes = props . form . body . replaceAll ( /<footnote data-value=".*?">.*?<\/footnote>/g , '' )
const leadText = cleanFootnotes . replaceAll ( /<\/?[^>]+(>|$)/gi , ' ' )
2023-11-18 14:10:02 +00:00
return shorten ( leadText , DESCRIPTION_MAX_LENGTH ) . trim ( )
2023-08-12 07:48:43 +00:00
}
2023-08-22 13:37:54 +00:00
return props . form . description
2023-08-12 07:48:43 +00:00
}
const initialData : Partial < ShoutForm > = {
coverImageUrl : props.form.coverImageUrl ,
mainTopic : props.form.mainTopic || EMPTY_TOPIC ,
slug : props.form.slug ,
title : props.form.title ,
subtitle : props.form.subtitle ,
2023-11-14 15:10:00 +00:00
description : composeDescription ( ) ,
2024-01-22 17:37:27 +00:00
selectedTopics : [ ] ,
2023-08-12 07:48:43 +00:00
}
const {
formErrors ,
2023-11-14 15:10:00 +00:00
actions : { setForm , setFormErrors , saveShout , publishShout } ,
2023-08-12 07:48:43 +00:00
} = useEditorContext ( )
const [ settingsForm , setSettingsForm ] = createStore ( initialData )
2023-08-15 09:38:49 +00:00
const handleUploadModalContentCloseSetCover = ( image : UploadedFile ) = > {
2023-08-12 07:48:43 +00:00
hideModal ( )
2023-08-15 09:38:49 +00:00
setSettingsForm ( 'coverImageUrl' , image . url )
2023-08-12 07:48:43 +00:00
}
const handleDeleteCoverImage = ( ) = > {
setSettingsForm ( 'coverImageUrl' , '' )
}
const handleTopicSelectChange = ( newSelectedTopics ) = > {
if (
props . form . selectedTopics . length === 0 ||
newSelectedTopics . every ( ( topic ) = > topic . id !== props . form . mainTopic . id )
) {
setSettingsForm ( ( prev ) = > {
return {
. . . prev ,
2023-11-14 15:10:00 +00:00
mainTopic : newSelectedTopics [ 0 ] ,
2023-08-12 07:48:43 +00:00
}
} )
}
if ( newSelectedTopics . length > 0 ) {
setFormErrors ( 'selectedTopics' , '' )
}
setForm ( 'selectedTopics' , newSelectedTopics )
}
const handleBackClick = ( ) = > {
redirectPage ( router , 'edit' , {
2023-11-14 15:10:00 +00:00
shoutId : props.shoutId.toString ( ) ,
2023-08-12 07:48:43 +00:00
} )
}
const handleCancelClick = ( ) = > {
setSettingsForm ( initialData )
handleBackClick ( )
}
const handlePublishSubmit = ( ) = > {
publishShout ( { . . . props . form , . . . settingsForm } )
}
const handleSaveDraft = ( ) = > {
saveShout ( { . . . props . form , . . . settingsForm } )
}
return (
2023-08-15 10:24:08 +00:00
< form class = { clsx ( styles . PublishSettings , 'inputs-wrapper' ) } >
< div class = "wide-container" >
< div class = "row" >
< div class = "col-md-19 col-lg-18 col-xl-16 offset-md-5" >
< div >
< button type = "button" class = { styles . goBack } onClick = { handleBackClick } >
< Icon name = "arrow-left" class = { stylesBeside . icon } / >
{ t ( 'Back to editor' ) }
< / button >
2023-08-12 07:48:43 +00:00
< / div >
2023-08-15 10:24:08 +00:00
< h1 > { t ( 'Publish Settings' ) } < / h1 >
< h4 > { t ( 'Material card' ) } < / h4 >
< div class = { styles . articlePreview } >
< div class = { styles . actions } >
< Button
variant = "primary"
onClick = { ( ) = > showModal ( 'uploadCoverImage' ) }
value = { settingsForm . coverImageUrl ? t ( 'Add another image' ) : t ( 'Add image' ) }
/ >
< Show when = { settingsForm . coverImageUrl } >
< Button variant = "secondary" onClick = { handleDeleteCoverImage } value = { t ( 'Delete cover' ) } / >
< / Show >
< / div >
< div
class = { clsx ( styles . shoutCardCoverContainer , {
2023-11-14 15:10:00 +00:00
[ styles . hasImage ] : settingsForm . coverImageUrl ,
2023-08-15 10:24:08 +00:00
} ) }
>
< Show when = { settingsForm . coverImageUrl ? ? initialData . coverImageUrl } >
< div class = { styles . shoutCardCover } >
2023-11-18 14:10:02 +00:00
< Image src = { settingsForm . coverImageUrl } alt = { initialData . title } width = { 800 } / >
2023-08-15 10:24:08 +00:00
< / div >
< / Show >
< div class = { styles . text } >
< Show when = { settingsForm . mainTopic } >
< div class = { styles . mainTopic } > { settingsForm . mainTopic . title } < / div >
< / Show >
< div class = { styles . shoutCardTitle } > { settingsForm . title } < / div >
< div class = { styles . shoutCardSubtitle } > { settingsForm . subtitle } < / div >
2023-11-28 13:18:25 +00:00
< div class = { styles . shoutAuthor } > { author ( ) ? . name } < / div >
2023-08-15 10:24:08 +00:00
< / div >
< / div >
< / div >
< p class = "description" >
{ t (
2023-11-14 15:10:00 +00:00
'Choose a title image for the article. You can immediately see how the publication card will look like.' ,
2023-08-15 10:24:08 +00:00
) }
< / p >
2023-08-12 07:48:43 +00:00
2023-08-15 10:24:08 +00:00
< div class = { styles . commonSettings } >
< GrowingTextarea
class = { styles . settingInput }
variant = "bordered"
2023-08-16 13:11:58 +00:00
fieldName = { t ( 'Header' ) }
2023-08-15 10:24:08 +00:00
placeholder = { t ( 'Come up with a title for your story' ) }
initialValue = { settingsForm . title }
value = { ( value ) = > setSettingsForm ( 'title' , value ) }
allowEnterKey = { false }
maxLength = { 100 }
/ >
< GrowingTextarea
class = { styles . settingInput }
variant = "bordered"
2023-08-16 13:11:58 +00:00
fieldName = { t ( 'Subheader' ) }
2023-08-15 10:24:08 +00:00
placeholder = { t ( 'Come up with a subtitle for your story' ) }
initialValue = { settingsForm . subtitle }
value = { ( value ) = > setSettingsForm ( 'subtitle' , value ) }
allowEnterKey = { false }
maxLength = { 100 }
/ >
2023-08-22 13:37:54 +00:00
< SimplifiedEditor
2023-08-15 10:24:08 +00:00
variant = "bordered"
2023-08-22 13:37:54 +00:00
onlyBubbleControls = { true }
smallHeight = { true }
2023-08-15 10:24:08 +00:00
placeholder = { t ( 'Write a short introduction' ) }
2023-08-22 13:37:54 +00:00
label = { t ( 'Description' ) }
initialContent = { composeDescription ( ) }
onChange = { ( value ) = > setForm ( 'description' , value ) }
2023-11-18 14:10:02 +00:00
maxLength = { DESCRIPTION_MAX_LENGTH }
2023-08-15 10:24:08 +00:00
/ >
< / div >
2023-08-12 07:48:43 +00:00
2023-08-15 10:24:08 +00:00
< h4 > { t ( 'Slug' ) } < / h4 >
< div class = "pretty-form__item" >
< input type = "text" name = "slug" id = "slug" value = { settingsForm . slug } / >
< label for = "slug" > { t ( 'Slug' ) } < / label >
< / div >
< h4 > { t ( 'Topics' ) } < / h4 >
< p class = "description" >
{ t (
2023-11-14 15:10:00 +00:00
'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' ,
2023-08-15 10:24:08 +00:00
) }
< / p >
< div class = { styles . inputContainer } >
< div class = { clsx ( 'pretty-form__item' , styles . topicSelectContainer ) } >
2024-01-23 19:01:41 +00:00
< Show when = { topics ( ) . length > 0 } >
2023-08-15 10:24:08 +00:00
< TopicSelect
2024-01-23 19:01:41 +00:00
topics = { topics ( ) }
2023-08-15 10:24:08 +00:00
onChange = { handleTopicSelectChange }
selectedTopics = { props . form . selectedTopics }
onMainTopicChange = { ( mainTopic ) = > setForm ( 'mainTopic' , mainTopic ) }
mainTopic = { props . form . mainTopic }
/ >
< / Show >
< / div >
< Show when = { formErrors . selectedTopics } >
< div class = { styles . validationError } > { formErrors . selectedTopics } < / div >
< / Show >
< / div >
2023-12-20 16:06:42 +00:00
< h4 > { t ( 'Collaborators' ) } < / h4 >
< Button
variant = "primary"
onClick = { ( ) = > showModal ( 'inviteCoAuthors' ) }
value = { t ( 'Invite collaborators' ) }
/ >
2023-08-15 10:24:08 +00:00
< / div >
2023-08-12 07:48:43 +00:00
< / div >
< / div >
< div class = { styles . formActions } >
2023-08-15 10:24:08 +00:00
< div class = "wide-container" >
< div class = "row" >
< div class = "col-md-19 col-lg-18 col-xl-16 offset-md-5" >
< div class = { styles . content } >
< Button
variant = "light"
value = { t ( 'Cancel changes' ) }
class = { styles . cancel }
onClick = { handleCancelClick }
/ >
< Button variant = "secondary" onClick = { handleSaveDraft } value = { t ( 'Save draft' ) } / >
< Button onClick = { handlePublishSubmit } variant = "primary" value = { t ( 'Publish' ) } / >
< / div >
< / div >
< / div >
< / div >
2023-08-12 07:48:43 +00:00
< / div >
< Modal variant = "narrow" name = "uploadCoverImage" >
< UploadModalContent onClose = { ( value ) = > handleUploadModalContentCloseSetCover ( value ) } / >
< / Modal >
2023-08-15 10:24:08 +00:00
< / form >
2023-08-12 07:48:43 +00:00
)
}