Article Lead and Description with simple editor (#189)
* Article Lead and Description
This commit is contained in:
parent
f0bb04b33d
commit
328bd89d8d
|
@ -577,3 +577,14 @@ a[data-toggle='tooltip'] {
|
|||
border-color: var(--black-500) transparent transparent transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.lead {
|
||||
@include font-size(1.8rem);
|
||||
|
||||
font-weight: 600;
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -233,6 +233,9 @@ export const FullArticle = (props: Props) => {
|
|||
</Show>
|
||||
</div>
|
||||
</Show>
|
||||
<Show when={props.article.lead}>
|
||||
<section class={styles.lead} innerHTML={props.article.lead} />
|
||||
</Show>
|
||||
<Show when={props.article.layout === 'audio'}>
|
||||
<AudioHeader
|
||||
title={props.article.title}
|
||||
|
|
|
@ -174,7 +174,7 @@ export const Editor = (props: Props) => {
|
|||
Image,
|
||||
Figcaption,
|
||||
Embed,
|
||||
CharacterCount,
|
||||
CharacterCount.configure(), // https://github.com/ueberdosis/tiptap/issues/2589#issuecomment-1093084689
|
||||
BubbleMenu.configure({
|
||||
pluginKey: 'textBubbleMenu',
|
||||
element: textBubbleMenuRef.current,
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
background: var(--black-50);
|
||||
border-radius: 16px;
|
||||
padding: 16px 16px 8px;
|
||||
position: relative;
|
||||
|
||||
.simplifiedEditorField {
|
||||
@include font-size(1.4rem);
|
||||
|
@ -92,4 +93,48 @@
|
|||
bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.minimal {
|
||||
background: unset;
|
||||
padding: 0;
|
||||
|
||||
& div[contenteditable] {
|
||||
font-size: 1.6rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
&.bordered {
|
||||
box-sizing: border-box;
|
||||
padding: 16px 12px 6px 12px;
|
||||
border-radius: 2px;
|
||||
border: 2px solid var(--black-100);
|
||||
background: var(--white-500);
|
||||
|
||||
& div[contenteditable] {
|
||||
font-size: 1.6rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
&.labelVisible {
|
||||
padding-top: 22px;
|
||||
}
|
||||
|
||||
.limit {
|
||||
position: absolute;
|
||||
right: 1rem;
|
||||
bottom: 0.5rem;
|
||||
font-weight: 500;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.label {
|
||||
@include font-size(1.2rem);
|
||||
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
left: 12px;
|
||||
color: var(--black-400);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { createEffect, onCleanup, onMount, Show } from 'solid-js'
|
||||
import { createEffect, createSignal, onCleanup, onMount, Show } from 'solid-js'
|
||||
import {
|
||||
createEditorTransaction,
|
||||
createTiptapEditor,
|
||||
|
@ -30,12 +30,19 @@ import { UploadedFile } from '../../pages/types'
|
|||
import { Figure } from './extensions/Figure'
|
||||
import { Image } from '@tiptap/extension-image'
|
||||
import { Figcaption } from './extensions/Figcaption'
|
||||
import { TextBubbleMenu } from './TextBubbleMenu'
|
||||
import { BubbleMenu } from '@tiptap/extension-bubble-menu'
|
||||
import { CharacterCount } from '@tiptap/extension-character-count'
|
||||
import { createStore } from 'solid-js/store'
|
||||
|
||||
type Props = {
|
||||
initialContent?: string
|
||||
label?: string
|
||||
onSubmit?: (text: string) => void
|
||||
onChange?: (text: string) => void
|
||||
placeholder: string
|
||||
variant?: 'minimal' | 'bordered'
|
||||
maxLength?: number
|
||||
submitButtonText?: string
|
||||
quoteEnabled?: boolean
|
||||
imageEnabled?: boolean
|
||||
|
@ -43,10 +50,13 @@ type Props = {
|
|||
smallHeight?: boolean
|
||||
submitByEnter?: boolean
|
||||
submitByShiftEnter?: boolean
|
||||
onlyBubbleControls?: boolean
|
||||
}
|
||||
|
||||
export const MAX_DESCRIPTION_LIMIT = 400
|
||||
const SimplifiedEditor = (props: Props) => {
|
||||
const { t } = useLocalize()
|
||||
const [counter, setCounter] = createSignal<number>()
|
||||
|
||||
const wrapperEditorElRef: {
|
||||
current: HTMLElement
|
||||
|
@ -60,6 +70,12 @@ const SimplifiedEditor = (props: Props) => {
|
|||
current: null
|
||||
}
|
||||
|
||||
const textBubbleMenuRef: {
|
||||
current: HTMLDivElement
|
||||
} = {
|
||||
current: null
|
||||
}
|
||||
|
||||
const {
|
||||
actions: { setEditor }
|
||||
} = useEditorContext()
|
||||
|
@ -69,6 +85,7 @@ const SimplifiedEditor = (props: Props) => {
|
|||
content: 'figcaption image'
|
||||
})
|
||||
|
||||
const content = props.initialContent
|
||||
const editor = createTiptapEditor(() => ({
|
||||
element: editorElRef.current,
|
||||
editorProps: {
|
||||
|
@ -85,11 +102,25 @@ const SimplifiedEditor = (props: Props) => {
|
|||
Link.configure({
|
||||
openOnClick: false
|
||||
}),
|
||||
|
||||
CharacterCount.configure({
|
||||
limit: MAX_DESCRIPTION_LIMIT
|
||||
}),
|
||||
Blockquote.configure({
|
||||
HTMLAttributes: {
|
||||
class: styles.blockQuote
|
||||
}
|
||||
}),
|
||||
BubbleMenu.configure({
|
||||
pluginKey: 'textBubbleMenu',
|
||||
element: textBubbleMenuRef.current,
|
||||
shouldShow: ({ view, state }) => {
|
||||
if (!props.onlyBubbleControls) return
|
||||
const { selection } = state
|
||||
const { empty } = selection
|
||||
return view.hasFocus() && !empty
|
||||
}
|
||||
}),
|
||||
ImageFigure,
|
||||
Image,
|
||||
Figcaption,
|
||||
|
@ -98,7 +129,7 @@ const SimplifiedEditor = (props: Props) => {
|
|||
placeholder: props.placeholder
|
||||
})
|
||||
],
|
||||
content: props.initialContent ?? null
|
||||
content: content ?? null
|
||||
}))
|
||||
|
||||
setEditor(editor)
|
||||
|
@ -193,94 +224,110 @@ const SimplifiedEditor = (props: Props) => {
|
|||
|
||||
const handleInsertLink = () => !editor().state.selection.empty && showModal('editorInsertLink')
|
||||
|
||||
createEffect(() => {
|
||||
if (html()) {
|
||||
setCounter(editor().storage.characterCount.characters())
|
||||
}
|
||||
})
|
||||
return (
|
||||
<div
|
||||
ref={(el) => (wrapperEditorElRef.current = el)}
|
||||
class={clsx(styles.SimplifiedEditor, {
|
||||
[styles.smallHeight]: props.smallHeight,
|
||||
[styles.isFocused]: isFocused() || !isEmpty()
|
||||
[styles.minimal]: props.variant === 'minimal',
|
||||
[styles.bordered]: props.variant === 'bordered',
|
||||
[styles.isFocused]: isFocused() || !isEmpty(),
|
||||
[styles.labelVisible]: props.label && counter() > 0
|
||||
})}
|
||||
>
|
||||
<Show when={props.maxLength && editor()}>
|
||||
<div class={styles.limit}>{MAX_DESCRIPTION_LIMIT - counter()}</div>
|
||||
</Show>
|
||||
<Show when={props.label && counter() > 0}>
|
||||
<div class={styles.label}>{props.label}</div>
|
||||
</Show>
|
||||
<div ref={(el) => (editorElRef.current = el)} />
|
||||
<div class={styles.controls}>
|
||||
<div class={styles.actions}>
|
||||
<Popover content={t('Bold')}>
|
||||
{(triggerRef: (el) => void) => (
|
||||
<button
|
||||
ref={triggerRef}
|
||||
type="button"
|
||||
class={clsx(styles.actionButton, { [styles.active]: isBold() })}
|
||||
onClick={() => editor().chain().focus().toggleBold().run()}
|
||||
>
|
||||
<Icon name="editor-bold" />
|
||||
</button>
|
||||
)}
|
||||
</Popover>
|
||||
<Popover content={t('Italic')}>
|
||||
{(triggerRef: (el) => void) => (
|
||||
<button
|
||||
ref={triggerRef}
|
||||
type="button"
|
||||
class={clsx(styles.actionButton, { [styles.active]: isItalic() })}
|
||||
onClick={() => editor().chain().focus().toggleItalic().run()}
|
||||
>
|
||||
<Icon name="editor-italic" />
|
||||
</button>
|
||||
)}
|
||||
</Popover>
|
||||
<Popover content={t('Add url')}>
|
||||
{(triggerRef: (el) => void) => (
|
||||
<button
|
||||
ref={triggerRef}
|
||||
type="button"
|
||||
onClick={handleInsertLink}
|
||||
class={clsx(styles.actionButton, { [styles.active]: isLink() })}
|
||||
>
|
||||
<Icon name="editor-link" />
|
||||
</button>
|
||||
)}
|
||||
</Popover>
|
||||
<Show when={props.quoteEnabled}>
|
||||
<Popover content={t('Add blockquote')}>
|
||||
<Show when={!props.onlyBubbleControls}>
|
||||
<div class={styles.controls}>
|
||||
<div class={styles.actions}>
|
||||
<Popover content={t('Bold')}>
|
||||
{(triggerRef: (el) => void) => (
|
||||
<button
|
||||
ref={triggerRef}
|
||||
type="button"
|
||||
onClick={() => editor().chain().focus().toggleBlockquote().run()}
|
||||
class={clsx(styles.actionButton, { [styles.active]: isBlockquote() })}
|
||||
class={clsx(styles.actionButton, { [styles.active]: isBold() })}
|
||||
onClick={() => editor().chain().focus().toggleBold().run()}
|
||||
>
|
||||
<Icon name="editor-quote" />
|
||||
<Icon name="editor-bold" />
|
||||
</button>
|
||||
)}
|
||||
</Popover>
|
||||
</Show>
|
||||
<Show when={props.imageEnabled}>
|
||||
<Popover content={t('Add image')}>
|
||||
<Popover content={t('Italic')}>
|
||||
{(triggerRef: (el) => void) => (
|
||||
<button
|
||||
ref={triggerRef}
|
||||
type="button"
|
||||
onClick={() => showModal('uploadImage')}
|
||||
class={clsx(styles.actionButton, { [styles.active]: isBlockquote() })}
|
||||
class={clsx(styles.actionButton, { [styles.active]: isItalic() })}
|
||||
onClick={() => editor().chain().focus().toggleItalic().run()}
|
||||
>
|
||||
<Icon name="editor-image-dd-full" />
|
||||
<Icon name="editor-italic" />
|
||||
</button>
|
||||
)}
|
||||
</Popover>
|
||||
<Popover content={t('Add url')}>
|
||||
{(triggerRef: (el) => void) => (
|
||||
<button
|
||||
ref={triggerRef}
|
||||
type="button"
|
||||
onClick={handleInsertLink}
|
||||
class={clsx(styles.actionButton, { [styles.active]: isLink() })}
|
||||
>
|
||||
<Icon name="editor-link" />
|
||||
</button>
|
||||
)}
|
||||
</Popover>
|
||||
<Show when={props.quoteEnabled}>
|
||||
<Popover content={t('Add blockquote')}>
|
||||
{(triggerRef: (el) => void) => (
|
||||
<button
|
||||
ref={triggerRef}
|
||||
type="button"
|
||||
onClick={() => editor().chain().focus().toggleBlockquote().run()}
|
||||
class={clsx(styles.actionButton, { [styles.active]: isBlockquote() })}
|
||||
>
|
||||
<Icon name="editor-quote" />
|
||||
</button>
|
||||
)}
|
||||
</Popover>
|
||||
</Show>
|
||||
<Show when={props.imageEnabled}>
|
||||
<Popover content={t('Add image')}>
|
||||
{(triggerRef: (el) => void) => (
|
||||
<button
|
||||
ref={triggerRef}
|
||||
type="button"
|
||||
onClick={() => showModal('uploadImage')}
|
||||
class={clsx(styles.actionButton, { [styles.active]: isBlockquote() })}
|
||||
>
|
||||
<Icon name="editor-image-dd-full" />
|
||||
</button>
|
||||
)}
|
||||
</Popover>
|
||||
</Show>
|
||||
</div>
|
||||
<Show when={!props.onChange}>
|
||||
<div class={styles.buttons}>
|
||||
<Button value={t('Cancel')} variant="secondary" disabled={isEmpty()} onClick={handleClear} />
|
||||
<Button
|
||||
value={props.submitButtonText ?? t('Send')}
|
||||
variant="primary"
|
||||
disabled={isEmpty()}
|
||||
onClick={() => props.onSubmit(html())}
|
||||
/>
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
<Show when={!props.onChange}>
|
||||
<div class={styles.buttons}>
|
||||
<Button value={t('Cancel')} variant="secondary" disabled={isEmpty()} onClick={handleClear} />
|
||||
<Button
|
||||
value={props.submitButtonText ?? t('Send')}
|
||||
variant="primary"
|
||||
disabled={isEmpty()}
|
||||
onClick={() => props.onSubmit(html())}
|
||||
/>
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
</Show>
|
||||
<Modal variant="narrow" name="editorInsertLink">
|
||||
<InsertLinkForm editor={editor()} onClose={() => hideModal()} />
|
||||
</Modal>
|
||||
|
@ -293,6 +340,13 @@ const SimplifiedEditor = (props: Props) => {
|
|||
/>
|
||||
</Modal>
|
||||
</Show>
|
||||
<Show when={props.onlyBubbleControls}>
|
||||
<TextBubbleMenu
|
||||
isCommonMarkup={true}
|
||||
editor={editor()}
|
||||
ref={(el) => (textBubbleMenuRef.current = el)}
|
||||
/>
|
||||
</Show>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -180,12 +180,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
.shoutCardLead {
|
||||
.shoutCardDescription {
|
||||
@include font-size(1.6rem);
|
||||
|
||||
color: var(--secondary-color);
|
||||
font-weight: 400;
|
||||
line-height: 1.3;
|
||||
color: var(--default-color);
|
||||
margin-bottom: 1.4rem;
|
||||
}
|
||||
|
||||
|
|
|
@ -164,10 +164,6 @@ export const ArticleCard = (props: ArticleCardProps) => {
|
|||
</div>
|
||||
</Show>
|
||||
</a>
|
||||
|
||||
<Show when={props.article.lead}>
|
||||
<div class={styles.shoutCardLead}>{props.article.lead}</div>
|
||||
</Show>
|
||||
</div>
|
||||
|
||||
<Show when={!props.settings?.noauthor || !props.settings?.nodate}>
|
||||
|
@ -196,7 +192,9 @@ export const ArticleCard = (props: ArticleCardProps) => {
|
|||
</Show>
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
<Show when={props.article.description}>
|
||||
<section class={styles.shoutCardDescription} innerHTML={props.article.description} />
|
||||
</Show>
|
||||
<Show when={props.settings?.isFeedMode}>
|
||||
<Show when={!props.settings?.noimage && props.article.cover}>
|
||||
<div class={styles.shoutCardCoverContainer}>
|
||||
|
|
|
@ -21,13 +21,13 @@ import deepEqual from 'fast-deep-equal'
|
|||
import { AutoSaveNotice } from '../Editor/AutoSaveNotice'
|
||||
import { PublishSettings } from './PublishSettings'
|
||||
import { createStore } from 'solid-js/store'
|
||||
import SimplifiedEditor from '../Editor/SimplifiedEditor'
|
||||
|
||||
type Props = {
|
||||
shout: Shout
|
||||
}
|
||||
|
||||
export const MAX_HEADER_LIMIT = 100
|
||||
export const MAX_LEAD_LIMIT = 400
|
||||
export const EMPTY_TOPIC: Topic = {
|
||||
id: -1,
|
||||
slug: ''
|
||||
|
@ -64,6 +64,8 @@ export const EditView = (props: Props) => {
|
|||
slug: props.shout.slug,
|
||||
shoutId: props.shout.id,
|
||||
title: props.shout.title,
|
||||
lead: props.shout.lead,
|
||||
description: props.shout.description,
|
||||
subtitle: props.shout.subtitle,
|
||||
selectedTopics: shoutTopics,
|
||||
mainTopic: shoutTopics.find((topic) => topic.slug === props.shout.mainTopic) || EMPTY_TOPIC,
|
||||
|
@ -75,7 +77,6 @@ export const EditView = (props: Props) => {
|
|||
}
|
||||
|
||||
const subtitleInput: { current: HTMLTextAreaElement } = { current: null }
|
||||
const leadInput: { current: HTMLTextAreaElement } = { current: null }
|
||||
|
||||
const [prevForm, setPrevForm] = createStore<ShoutForm>(clone(form))
|
||||
const [saving, setSaving] = createSignal(false)
|
||||
|
@ -226,7 +227,6 @@ export const EditView = (props: Props) => {
|
|||
}
|
||||
const showLeadInput = () => {
|
||||
setIsLeadVisible(true)
|
||||
leadInput.current.focus()
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -320,16 +320,13 @@ export const EditView = (props: Props) => {
|
|||
/>
|
||||
</Show>
|
||||
<Show when={isLeadVisible()}>
|
||||
<GrowingTextarea
|
||||
textAreaRef={(el) => {
|
||||
leadInput.current = el
|
||||
}}
|
||||
allowEnterKey={true}
|
||||
value={(value) => setForm('lead', value)}
|
||||
class={styles.leadInput}
|
||||
placeholder={t('Description')}
|
||||
initialValue={form.subtitle}
|
||||
maxLength={MAX_LEAD_LIMIT}
|
||||
<SimplifiedEditor
|
||||
variant="minimal"
|
||||
onlyBubbleControls={true}
|
||||
smallHeight={true}
|
||||
placeholder={t('A short introduction to keep the reader interested')}
|
||||
initialContent={form.lead}
|
||||
onChange={(value) => setForm('lead', value)}
|
||||
/>
|
||||
</Show>
|
||||
</Show>
|
||||
|
|
|
@ -10,7 +10,7 @@ import { useLocalize } from '../../../context/localize'
|
|||
import { Modal } from '../../Nav/Modal'
|
||||
import { Topic } from '../../../graphql/types.gen'
|
||||
import { apiClient } from '../../../utils/apiClient'
|
||||
import { EMPTY_TOPIC, MAX_LEAD_LIMIT } from '../Edit'
|
||||
import { EMPTY_TOPIC } from '../Edit'
|
||||
import { useSession } from '../../../context/session'
|
||||
import { Icon } from '../../_shared/Icon'
|
||||
import stylesBeside from '../../Feed/Beside.module.scss'
|
||||
|
@ -19,6 +19,7 @@ import { router } from '../../../stores/router'
|
|||
import { GrowingTextarea } from '../../_shared/GrowingTextarea'
|
||||
import { createStore } from 'solid-js/store'
|
||||
import { UploadedFile } from '../../../pages/types'
|
||||
import SimplifiedEditor, { MAX_DESCRIPTION_LIMIT } from '../../Editor/SimplifiedEditor'
|
||||
|
||||
type Props = {
|
||||
shoutId: number
|
||||
|
@ -35,12 +36,12 @@ export const PublishSettings = (props: Props) => {
|
|||
const { t } = useLocalize()
|
||||
const { user } = useSession()
|
||||
|
||||
const composeLead = () => {
|
||||
if (!props.form.lead) {
|
||||
const composeDescription = () => {
|
||||
if (!props.form.description) {
|
||||
const leadText = props.form.body.replaceAll(/<\/?[^>]+(>|$)/gi, ' ')
|
||||
return shorten(leadText, MAX_LEAD_LIMIT).trim()
|
||||
return shorten(leadText, MAX_DESCRIPTION_LIMIT).trim()
|
||||
}
|
||||
return props.form.lead
|
||||
return props.form.description
|
||||
}
|
||||
|
||||
const initialData: Partial<ShoutForm> = {
|
||||
|
@ -49,7 +50,7 @@ export const PublishSettings = (props: Props) => {
|
|||
slug: props.form.slug,
|
||||
title: props.form.title,
|
||||
subtitle: props.form.subtitle,
|
||||
lead: composeLead()
|
||||
description: composeDescription()
|
||||
}
|
||||
|
||||
const {
|
||||
|
@ -183,15 +184,15 @@ export const PublishSettings = (props: Props) => {
|
|||
allowEnterKey={false}
|
||||
maxLength={100}
|
||||
/>
|
||||
<GrowingTextarea
|
||||
class={styles.settingInput}
|
||||
<SimplifiedEditor
|
||||
variant="bordered"
|
||||
fieldName={t('Description')}
|
||||
onlyBubbleControls={true}
|
||||
smallHeight={true}
|
||||
placeholder={t('Write a short introduction')}
|
||||
initialValue={`${settingsForm.lead}`}
|
||||
value={(value) => setSettingsForm('lead', value)}
|
||||
allowEnterKey={false}
|
||||
maxLength={MAX_LEAD_LIMIT}
|
||||
label={t('Description')}
|
||||
initialContent={composeDescription()}
|
||||
onChange={(value) => setForm('description', value)}
|
||||
maxLength={MAX_DESCRIPTION_LIMIT}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
padding: 16px 12px;
|
||||
border-radius: 2px;
|
||||
border: 2px solid var(--black-100);
|
||||
background: var(--white-500, #fff);
|
||||
background: var(--white-500);
|
||||
}
|
||||
|
||||
&.hasFieldName {
|
||||
|
|
|
@ -21,12 +21,13 @@ export type ShoutForm = {
|
|||
slug: string
|
||||
title: string
|
||||
subtitle: string
|
||||
lead?: string
|
||||
description?: string
|
||||
selectedTopics: Topic[]
|
||||
mainTopic?: Topic
|
||||
body: string
|
||||
coverImageUrl: string
|
||||
media?: string
|
||||
lead?: string
|
||||
}
|
||||
|
||||
type EditorContextType = {
|
||||
|
@ -136,6 +137,8 @@ export const EditorProvider = (props: { children: JSX.Element }) => {
|
|||
slug: formToUpdate.slug,
|
||||
subtitle: formToUpdate.subtitle,
|
||||
title: formToUpdate.title,
|
||||
lead: formToUpdate.lead,
|
||||
description: formToUpdate.description,
|
||||
cover: formToUpdate.coverImageUrl,
|
||||
media: formToUpdate.media
|
||||
},
|
||||
|
|
|
@ -9,6 +9,8 @@ export default gql`
|
|||
slug
|
||||
title
|
||||
subtitle
|
||||
lead
|
||||
description
|
||||
body
|
||||
visibility
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ export default gql`
|
|||
loadShout(slug: $slug, shout_id: $shoutId) {
|
||||
id
|
||||
title
|
||||
lead
|
||||
description
|
||||
visibility
|
||||
subtitle
|
||||
slug
|
||||
|
|
|
@ -5,6 +5,8 @@ export default gql`
|
|||
loadShouts(options: $options) {
|
||||
id
|
||||
title
|
||||
lead
|
||||
description
|
||||
subtitle
|
||||
slug
|
||||
layout
|
||||
|
|
|
@ -554,6 +554,7 @@ export type Shout = {
|
|||
createdAt: Scalars['DateTime']
|
||||
deletedAt?: Maybe<Scalars['DateTime']>
|
||||
deletedBy?: Maybe<User>
|
||||
description?: Maybe<Scalars['String']>
|
||||
id: Scalars['Int']
|
||||
lang?: Maybe<Scalars['String']>
|
||||
layout?: Maybe<Scalars['String']>
|
||||
|
@ -577,7 +578,9 @@ export type ShoutInput = {
|
|||
body?: InputMaybe<Scalars['String']>
|
||||
community?: InputMaybe<Scalars['Int']>
|
||||
cover?: InputMaybe<Scalars['String']>
|
||||
description?: InputMaybe<Scalars['String']>
|
||||
layout?: InputMaybe<Scalars['String']>
|
||||
lead?: InputMaybe<Scalars['String']>
|
||||
mainTopic?: InputMaybe<TopicInput>
|
||||
media?: InputMaybe<Scalars['String']>
|
||||
slug?: InputMaybe<Scalars['String']>
|
||||
|
|
Loading…
Reference in New Issue
Block a user