Merge remote-tracking branch 'gitlab/dev' into editor_settings/upload_cover_image
This commit is contained in:
commit
480d14ed6f
|
@ -277,5 +277,6 @@
|
||||||
"user already exist": "user already exists",
|
"user already exist": "user already exists",
|
||||||
"view": "view",
|
"view": "view",
|
||||||
"zine": "zine",
|
"zine": "zine",
|
||||||
|
"Unnamed draft": "Unnamed draft",
|
||||||
"Publish Settings": "Publish Settings"
|
"Publish Settings": "Publish Settings"
|
||||||
}
|
}
|
||||||
|
|
|
@ -298,5 +298,6 @@
|
||||||
"user already exist": "пользователь уже существует",
|
"user already exist": "пользователь уже существует",
|
||||||
"view": "просмотр",
|
"view": "просмотр",
|
||||||
"zine": "журнал",
|
"zine": "журнал",
|
||||||
"Publish Settings": "Настройки публикации"
|
"Publish Settings": "Настройки публикации",
|
||||||
|
"Unnamed draft": "Unnamed draft"
|
||||||
}
|
}
|
||||||
|
|
|
@ -368,6 +368,10 @@ img {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[data-float] {
|
||||||
|
max-width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
[data-float='left'] {
|
[data-float='left'] {
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,14 +124,16 @@ export const FullArticle = (props: ArticleProps) => {
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<article class="col-md-16 col-lg-14 col-xl-12 offset-md-5">
|
<article class="col-md-16 col-lg-14 col-xl-12 offset-md-5">
|
||||||
<div class={styles.shoutHeader}>
|
<div class={styles.shoutHeader}>
|
||||||
<div class={styles.shoutTopic}>
|
<Show when={mainTopic()}>
|
||||||
<a
|
<div class={styles.shoutTopic}>
|
||||||
href={getPagePath(router, 'topic', { slug: props.article.mainTopic })}
|
<a
|
||||||
class={styles.mainTopicLink}
|
href={getPagePath(router, 'topic', { slug: props.article.mainTopic })}
|
||||||
>
|
class={styles.mainTopicLink}
|
||||||
{mainTopic().title}
|
>
|
||||||
</a>
|
{mainTopic().title}
|
||||||
</div>
|
</a>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
|
||||||
<h1>{props.article.title}</h1>
|
<h1>{props.article.title}</h1>
|
||||||
<Show when={props.article.subtitle}>
|
<Show when={props.article.subtitle}>
|
||||||
|
@ -237,7 +239,7 @@ export const FullArticle = (props: ArticleProps) => {
|
||||||
<Show when={canEdit()}>
|
<Show when={canEdit()}>
|
||||||
<div class={styles.shoutStatsItem}>
|
<div class={styles.shoutStatsItem}>
|
||||||
<a
|
<a
|
||||||
href={getPagePath(router, 'edit', { shoutSlug: props.article.slug })}
|
href={getPagePath(router, 'edit', { shoutId: props.article.id.toString() })}
|
||||||
class={styles.shoutStatsItemInner}
|
class={styles.shoutStatsItemInner}
|
||||||
>
|
>
|
||||||
<Icon name="edit" class={clsx(styles.icon, styles.iconEdit)} />
|
<Icon name="edit" class={clsx(styles.icon, styles.iconEdit)} />
|
||||||
|
|
44
src/components/Draft/Draft.module.scss
Normal file
44
src/components/Draft/Draft.module.scss
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
.created {
|
||||||
|
@include font-size(1.2rem);
|
||||||
|
|
||||||
|
line-height: 1.5rem;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
height: 1.2rem;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.titleContainer {
|
||||||
|
@include font-size(2.6rem);
|
||||||
|
|
||||||
|
line-height: 3.2rem;
|
||||||
|
margin-bottom: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
@include font-size(1.2rem);
|
||||||
|
|
||||||
|
a {
|
||||||
|
border: 0;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
a + a {
|
||||||
|
margin-left: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deleteLink {
|
||||||
|
color: #d00820;
|
||||||
|
}
|
||||||
|
|
||||||
|
.publishLink {
|
||||||
|
color: #2bb452;
|
||||||
|
}
|
||||||
|
}
|
52
src/components/Draft/Draft.tsx
Normal file
52
src/components/Draft/Draft.tsx
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import { clsx } from 'clsx'
|
||||||
|
import styles from './Draft.module.scss'
|
||||||
|
import type { Shout } from '../../graphql/types.gen'
|
||||||
|
import { Icon } from '../_shared/Icon'
|
||||||
|
import { formatDate } from '../../utils'
|
||||||
|
import formatDateTime from '../../utils/formatDateTime'
|
||||||
|
import { useLocalize } from '../../context/localize'
|
||||||
|
import { getPagePath, openPage } from '@nanostores/router'
|
||||||
|
import { router } from '../../stores/router'
|
||||||
|
import { useEditorContext } from '../../context/editor'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
class?: string
|
||||||
|
shout: Shout
|
||||||
|
onPublish: (shout: Shout) => void
|
||||||
|
onDelete: (shout: Shout) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Draft = (props: Props) => {
|
||||||
|
const { t } = useLocalize()
|
||||||
|
|
||||||
|
const handlePublishLinkClick = (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
props.onPublish(props.shout)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDeleteLinkClick = (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
props.onDelete(props.shout)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class={clsx(props.class)}>
|
||||||
|
<div class={styles.created}>
|
||||||
|
<Icon name="pencil-outline" class={styles.icon} /> {formatDate(new Date(props.shout.createdAt))}
|
||||||
|
{formatDateTime(props.shout.createdAt)()}
|
||||||
|
</div>
|
||||||
|
<div class={styles.titleContainer}>
|
||||||
|
<span class={styles.title}>{props.shout.title || t('Unnamed draft')}</span> {props.shout.subtitle}
|
||||||
|
</div>
|
||||||
|
<div class={styles.actions}>
|
||||||
|
<a href={getPagePath(router, 'edit', { shoutId: props.shout.id.toString() })}>{t('Edit')}</a>
|
||||||
|
<a href="#" onClick={handlePublishLinkClick} class={styles.publishLink}>
|
||||||
|
{t('Publish')}
|
||||||
|
</a>
|
||||||
|
<a href="#" onClick={handleDeleteLinkClick} class={styles.deleteLink}>
|
||||||
|
{t('Delete')}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
1
src/components/Draft/index.ts
Normal file
1
src/components/Draft/index.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export { Draft } from './Draft'
|
|
@ -42,6 +42,7 @@ import { useEditorContext } from '../../context/editor'
|
||||||
import { isTextSelection } from '@tiptap/core'
|
import { isTextSelection } from '@tiptap/core'
|
||||||
import type { Doc } from 'yjs/dist/src/utils/Doc'
|
import type { Doc } from 'yjs/dist/src/utils/Doc'
|
||||||
import './Prosemirror.scss'
|
import './Prosemirror.scss'
|
||||||
|
import { TrailingNode } from './extensions/TrailingNode'
|
||||||
|
|
||||||
type EditorProps = {
|
type EditorProps = {
|
||||||
shoutId: number
|
shoutId: number
|
||||||
|
@ -176,7 +177,8 @@ export const Editor = (props: EditorProps) => {
|
||||||
placement: 'left'
|
placement: 'left'
|
||||||
},
|
},
|
||||||
element: floatingMenuRef.current
|
element: floatingMenuRef.current
|
||||||
})
|
}),
|
||||||
|
TrailingNode
|
||||||
]
|
]
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
left: 0;
|
left: 0;
|
||||||
position: relative;
|
position: relative;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
padding-top: 5px;
|
||||||
|
|
||||||
button {
|
button {
|
||||||
opacity: 0.3;
|
opacity: 0.3;
|
||||||
vertical-align: text-bottom;
|
|
||||||
transition: opacity 0.3s ease-in-out;
|
transition: opacity 0.3s ease-in-out;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
|
@ -21,3 +21,7 @@
|
||||||
min-width: 64vw;
|
min-width: 64vw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:global(.tippy-box) {
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { getPagePath } from '@nanostores/router'
|
||||||
import { router } from '../../../stores/router'
|
import { router } from '../../../stores/router'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
shoutSlug: string
|
shoutId: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Panel = (props: Props) => {
|
export const Panel = (props: Props) => {
|
||||||
|
@ -18,7 +18,7 @@ export const Panel = (props: Props) => {
|
||||||
const {
|
const {
|
||||||
isEditorPanelVisible,
|
isEditorPanelVisible,
|
||||||
wordCounter,
|
wordCounter,
|
||||||
actions: { toggleEditorPanel }
|
actions: { toggleEditorPanel, saveShout, publishShout }
|
||||||
} = useEditorContext()
|
} = useEditorContext()
|
||||||
|
|
||||||
const containerRef: { current: HTMLElement } = {
|
const containerRef: { current: HTMLElement } = {
|
||||||
|
@ -37,6 +37,16 @@ export const Panel = (props: Props) => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const handleSaveLinkClick = (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
saveShout()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlePublishLinkClick = (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
publishShout()
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<aside
|
<aside
|
||||||
ref={(el) => (containerRef.current = el)}
|
ref={(el) => (containerRef.current = el)}
|
||||||
|
@ -53,10 +63,14 @@ export const Panel = (props: Props) => {
|
||||||
<div class={clsx(styles.actionsHolder, styles.scrolled)}>
|
<div class={clsx(styles.actionsHolder, styles.scrolled)}>
|
||||||
<section>
|
<section>
|
||||||
<p>
|
<p>
|
||||||
<a>{t('Publish')}</a>
|
<a href="#" onClick={handlePublishLinkClick}>
|
||||||
|
{t('Publish')}
|
||||||
|
</a>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<a>{t('Save draft')}</a>
|
<a href="#" onClick={handleSaveLinkClick}>
|
||||||
|
{t('Save draft')}
|
||||||
|
</a>
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
@ -70,7 +84,7 @@ export const Panel = (props: Props) => {
|
||||||
<p>
|
<p>
|
||||||
<a
|
<a
|
||||||
class={styles.linkWithIcon}
|
class={styles.linkWithIcon}
|
||||||
href={getPagePath(router, 'edit', { shoutSlug: props.shoutSlug })}
|
href={getPagePath(router, 'edit', { shoutId: props.shoutId.toString() })}
|
||||||
>
|
>
|
||||||
<Icon name="pencil-outline" class={styles.icon} />
|
<Icon name="pencil-outline" class={styles.icon} />
|
||||||
{t('Editing')}
|
{t('Editing')}
|
||||||
|
@ -89,7 +103,7 @@ export const Panel = (props: Props) => {
|
||||||
<a>{t('Invite co-authors')}</a>
|
<a>{t('Invite co-authors')}</a>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<a href={getPagePath(router, 'editSettings', { shoutSlug: props.shoutSlug })}>
|
<a href={getPagePath(router, 'editSettings', { shoutId: props.shoutId.toString() })}>
|
||||||
{t('Publication settings')}
|
{t('Publication settings')}
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
70
src/components/Editor/extensions/TrailingNode.ts
Normal file
70
src/components/Editor/extensions/TrailingNode.ts
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
import { Extension } from '@tiptap/core'
|
||||||
|
import { Plugin, PluginKey } from '@tiptap/pm/state'
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
function nodeEqualsType({ types, node }) {
|
||||||
|
return (Array.isArray(types) && types.includes(node.type)) || node.type === types
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extension based on:
|
||||||
|
* - https://github.com/ueberdosis/tiptap/blob/v1/packages/tiptap-extensions/src/extensions/TrailingNode.js
|
||||||
|
* - https://github.com/remirror/remirror/blob/e0f1bec4a1e8073ce8f5500d62193e52321155b9/packages/prosemirror-trailing-node/src/trailing-node-plugin.ts
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface TrailingNodeOptions {
|
||||||
|
node: string
|
||||||
|
notAfter: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TrailingNode = Extension.create<TrailingNodeOptions>({
|
||||||
|
name: 'trailingNode',
|
||||||
|
|
||||||
|
addOptions() {
|
||||||
|
return {
|
||||||
|
node: 'paragraph',
|
||||||
|
notAfter: ['paragraph']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
addProseMirrorPlugins() {
|
||||||
|
const plugin = new PluginKey(this.name)
|
||||||
|
const disabledNodes = Object.entries(this.editor.schema.nodes)
|
||||||
|
.map(([, value]) => value)
|
||||||
|
.filter((node) => this.options.notAfter.includes(node.name))
|
||||||
|
|
||||||
|
return [
|
||||||
|
new Plugin({
|
||||||
|
key: plugin,
|
||||||
|
appendTransaction: (_, __, state) => {
|
||||||
|
const { doc, tr, schema } = state
|
||||||
|
const shouldInsertNodeAtEnd = plugin.getState(state)
|
||||||
|
const endPosition = doc.content.size
|
||||||
|
const type = schema.nodes[this.options.node]
|
||||||
|
|
||||||
|
if (!shouldInsertNodeAtEnd) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return tr.insert(endPosition, type.create())
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
init: (_, state) => {
|
||||||
|
const lastNode = state.tr.doc.lastChild
|
||||||
|
|
||||||
|
return !nodeEqualsType({ node: lastNode, types: disabledNodes })
|
||||||
|
},
|
||||||
|
apply: (tr, value) => {
|
||||||
|
if (!tr.docChanged) {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
const lastNode = tr.doc.lastChild
|
||||||
|
|
||||||
|
return !nodeEqualsType({ node: lastNode, types: disabledNodes })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
|
@ -12,7 +12,7 @@ import stylesHeader from '../Nav/Header.module.scss'
|
||||||
import { getDescription } from '../../utils/meta'
|
import { getDescription } from '../../utils/meta'
|
||||||
import { FeedArticlePopup } from './FeedArticlePopup'
|
import { FeedArticlePopup } from './FeedArticlePopup'
|
||||||
import { useLocalize } from '../../context/localize'
|
import { useLocalize } from '../../context/localize'
|
||||||
import { openPage } from '@nanostores/router'
|
import { getPagePath, openPage } from '@nanostores/router'
|
||||||
import { router, useRouter } from '../../stores/router'
|
import { router, useRouter } from '../../stores/router'
|
||||||
|
|
||||||
interface ArticleCardProps {
|
interface ArticleCardProps {
|
||||||
|
@ -75,7 +75,7 @@ export const ArticleCard = (props: ArticleCardProps) => {
|
||||||
|
|
||||||
const { title, subtitle } = getTitleAndSubtitle(props.article)
|
const { title, subtitle } = getTitleAndSubtitle(props.article)
|
||||||
|
|
||||||
const { cover, layout, slug, authors, stat, body } = props.article
|
const { id, cover, layout, slug, authors, stat, body } = props.article
|
||||||
|
|
||||||
const { changeSearchParam } = useRouter()
|
const { changeSearchParam } = useRouter()
|
||||||
const scrollToComments = (event) => {
|
const scrollToComments = (event) => {
|
||||||
|
@ -190,9 +190,9 @@ export const ArticleCard = (props: ArticleCardProps) => {
|
||||||
|
|
||||||
<div class={styles.shoutCardDetailsContent}>
|
<div class={styles.shoutCardDetailsContent}>
|
||||||
<div class={styles.shoutCardDetailsItem}>
|
<div class={styles.shoutCardDetailsItem}>
|
||||||
<button>
|
<a href={getPagePath(router, 'edit', { shoutId: id.toString() })}>
|
||||||
<Icon name="pencil-outline" class={clsx(styles.icon, styles.feedControlIcon)} />
|
<Icon name="pencil-outline" class={clsx(styles.icon, styles.feedControlIcon)} />
|
||||||
</button>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class={styles.shoutCardDetailsItem}>
|
<div class={styles.shoutCardDetailsItem}>
|
||||||
|
|
|
@ -14,7 +14,6 @@ import { useLocalize } from '../../context/localize'
|
||||||
import { getPagePath, openPage } from '@nanostores/router'
|
import { getPagePath, openPage } from '@nanostores/router'
|
||||||
import { Button } from '../_shared/Button'
|
import { Button } from '../_shared/Button'
|
||||||
import { useEditorContext } from '../../context/editor'
|
import { useEditorContext } from '../../context/editor'
|
||||||
import { apiClient } from '../../utils/apiClient'
|
|
||||||
|
|
||||||
type HeaderAuthProps = {
|
type HeaderAuthProps = {
|
||||||
setIsProfilePopupVisible: (value: boolean) => void
|
setIsProfilePopupVisible: (value: boolean) => void
|
||||||
|
@ -57,11 +56,12 @@ export const HeaderAuth = (props: HeaderAuthProps) => {
|
||||||
toggleEditorPanel()
|
toggleEditorPanel()
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSaveButtonClick = async () => {
|
const handleSaveButtonClick = () => {
|
||||||
const result = await saveShout()
|
saveShout()
|
||||||
if (result) {
|
}
|
||||||
openPage(router, 'drafts')
|
|
||||||
}
|
const handlePublishButtonClick = () => {
|
||||||
|
publishShout()
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -111,6 +111,7 @@ export const HeaderAuth = (props: HeaderAuthProps) => {
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
variant={'outline'}
|
variant={'outline'}
|
||||||
|
onClick={handlePublishButtonClick}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
7
src/components/Views/DraftsView/DraftsView.module.scss
Normal file
7
src/components/Views/DraftsView/DraftsView.module.scss
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
.DraftsView {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.draft {
|
||||||
|
margin-bottom: 56px;
|
||||||
|
}
|
68
src/components/Views/DraftsView/DraftsView.tsx
Normal file
68
src/components/Views/DraftsView/DraftsView.tsx
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
import { clsx } from 'clsx'
|
||||||
|
import styles from './DraftsView.module.scss'
|
||||||
|
import { createSignal, For, onMount, Show } from 'solid-js'
|
||||||
|
import { Draft } from '../../Draft'
|
||||||
|
import { useSession } from '../../../context/session'
|
||||||
|
import { Shout } from '../../../graphql/types.gen'
|
||||||
|
import { apiClient } from '../../../utils/apiClient'
|
||||||
|
import { useEditorContext } from '../../../context/editor'
|
||||||
|
import { openPage } from '@nanostores/router'
|
||||||
|
import { router } from '../../../stores/router'
|
||||||
|
|
||||||
|
export const DraftsView = () => {
|
||||||
|
const { isAuthenticated, isSessionLoaded, user } = useSession()
|
||||||
|
|
||||||
|
const [drafts, setDrafts] = createSignal<Shout[]>([])
|
||||||
|
|
||||||
|
const loadDrafts = async () => {
|
||||||
|
const loadedDrafts = await apiClient.getDrafts()
|
||||||
|
setDrafts(loadedDrafts)
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
loadDrafts()
|
||||||
|
})
|
||||||
|
|
||||||
|
const {
|
||||||
|
actions: { publishShoutById, deleteShout }
|
||||||
|
} = useEditorContext()
|
||||||
|
|
||||||
|
const handleDraftDelete = (shout: Shout) => {
|
||||||
|
const result = deleteShout(shout.id)
|
||||||
|
if (result) {
|
||||||
|
loadDrafts()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDraftPublish = (shout: Shout) => {
|
||||||
|
const result = publishShoutById(shout.id)
|
||||||
|
if (result) {
|
||||||
|
openPage(router, 'feed')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class={clsx(styles.DraftsView)}>
|
||||||
|
<Show when={isSessionLoaded()}>
|
||||||
|
<div class="wide-container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-19 col-lg-18 col-xl-16 offset-md-5">
|
||||||
|
<Show when={isAuthenticated()} fallback="Давайте авторизуемся">
|
||||||
|
<For each={drafts()}>
|
||||||
|
{(draft) => (
|
||||||
|
<Draft
|
||||||
|
class={styles.draft}
|
||||||
|
shout={draft}
|
||||||
|
onDelete={handleDraftDelete}
|
||||||
|
onPublish={handleDraftPublish}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
1
src/components/Views/DraftsView/index.ts
Normal file
1
src/components/Views/DraftsView/index.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export { DraftsView } from './DraftsView'
|
|
@ -156,6 +156,7 @@
|
||||||
|
|
||||||
.scrollTopButton {
|
.scrollTopButton {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
user-select: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
left: 2rem;
|
left: 2rem;
|
||||||
position: sticky;
|
position: sticky;
|
||||||
|
|
|
@ -74,11 +74,6 @@ export const EditView = (props: EditViewProps) => {
|
||||||
if (title) {
|
if (title) {
|
||||||
setFormErrors('title', '')
|
setFormErrors('title', '')
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (!isSlugChanged()) {
|
|
||||||
// const slug = translit(title).replaceAll(' ', '-')
|
|
||||||
// setForm('slug', slug)
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSlugInputChange = (e) => {
|
const handleSlugInputChange = (e) => {
|
||||||
|
@ -254,7 +249,7 @@ export const EditView = (props: EditViewProps) => {
|
||||||
<Modal variant="narrow" name="uploadImage">
|
<Modal variant="narrow" name="uploadImage">
|
||||||
<UploadModalContent onClose={(value) => handleUploadModalContentCloseSetCover(value)} />
|
<UploadModalContent onClose={(value) => handleUploadModalContentCloseSetCover(value)} />
|
||||||
</Modal>
|
</Modal>
|
||||||
<Panel shoutSlug={props.shout.slug} />
|
<Panel shoutId={props.shout.id} />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,9 @@ import { Topic } from '../graphql/types.gen'
|
||||||
import { apiClient } from '../utils/apiClient'
|
import { apiClient } from '../utils/apiClient'
|
||||||
import { useLocalize } from './localize'
|
import { useLocalize } from './localize'
|
||||||
import { useSnackbar } from './snackbar'
|
import { useSnackbar } from './snackbar'
|
||||||
|
import { translit } from '../utils/ru2en'
|
||||||
|
import { openPage } from '@nanostores/router'
|
||||||
|
import { router, useRouter } from '../stores/router'
|
||||||
|
|
||||||
type WordCounter = {
|
type WordCounter = {
|
||||||
characters: number
|
characters: number
|
||||||
|
@ -28,8 +31,10 @@ type EditorContextType = {
|
||||||
form: ShoutForm
|
form: ShoutForm
|
||||||
formErrors: Partial<ShoutForm>
|
formErrors: Partial<ShoutForm>
|
||||||
actions: {
|
actions: {
|
||||||
saveShout: () => Promise<boolean>
|
saveShout: () => Promise<void>
|
||||||
publishShout: () => Promise<boolean>
|
publishShout: () => Promise<void>
|
||||||
|
publishShoutById: (shoutId: number) => Promise<void>
|
||||||
|
deleteShout: (shoutId: number) => Promise<boolean>
|
||||||
toggleEditorPanel: () => void
|
toggleEditorPanel: () => void
|
||||||
countWords: (value: WordCounter) => void
|
countWords: (value: WordCounter) => void
|
||||||
setForm: SetStoreFunction<ShoutForm>
|
setForm: SetStoreFunction<ShoutForm>
|
||||||
|
@ -45,6 +50,9 @@ export function useEditorContext() {
|
||||||
|
|
||||||
export const EditorProvider = (props: { children: JSX.Element }) => {
|
export const EditorProvider = (props: { children: JSX.Element }) => {
|
||||||
const { t } = useLocalize()
|
const { t } = useLocalize()
|
||||||
|
|
||||||
|
const { page } = useRouter()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
actions: { showSnackbar }
|
actions: { showSnackbar }
|
||||||
} = useSnackbar()
|
} = useSnackbar()
|
||||||
|
@ -62,12 +70,59 @@ export const EditorProvider = (props: { children: JSX.Element }) => {
|
||||||
const toggleEditorPanel = () => setIsEditorPanelVisible((value) => !value)
|
const toggleEditorPanel = () => setIsEditorPanelVisible((value) => !value)
|
||||||
const countWords = (value) => setWordCounter(value)
|
const countWords = (value) => setWordCounter(value)
|
||||||
|
|
||||||
const saveShout = async () => {
|
const validate = () => {
|
||||||
if (!form.title) {
|
if (!form.title) {
|
||||||
setFormErrors('title', t('Required'))
|
setFormErrors('title', t('Required'))
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
const saveShout = async () => {
|
||||||
|
if (!validate()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const shout = await apiClient.updateArticle({
|
||||||
|
shoutId: form.shoutId,
|
||||||
|
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 || 'society',
|
||||||
|
slug: form.slug,
|
||||||
|
subtitle: form.subtitle,
|
||||||
|
title: form.title,
|
||||||
|
cover: form.coverImageUrl
|
||||||
|
},
|
||||||
|
publish: false
|
||||||
|
})
|
||||||
|
|
||||||
|
if (shout.visibility === 'owner') {
|
||||||
|
openPage(router, 'drafts')
|
||||||
|
} else {
|
||||||
|
openPage(router, 'article', { slug: shout.slug })
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[saveShout]', error)
|
||||||
|
showSnackbar({ type: 'error', body: t('Error') })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const publishShout = async () => {
|
||||||
|
if (!validate()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (page().route === 'edit') {
|
||||||
|
const slug = translit(form.title.toLowerCase()).replaceAll(' ', '-')
|
||||||
|
setForm('slug', slug)
|
||||||
|
openPage(router, 'editSettings', { shoutId: form.shoutId.toString() })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await apiClient.updateArticle({
|
await apiClient.updateArticle({
|
||||||
shoutId: form.shoutId,
|
shoutId: form.shoutId,
|
||||||
|
@ -81,30 +136,34 @@ export const EditorProvider = (props: { children: JSX.Element }) => {
|
||||||
subtitle: form.subtitle,
|
subtitle: form.subtitle,
|
||||||
title: form.title,
|
title: form.title,
|
||||||
cover: form.coverImageUrl
|
cover: form.coverImageUrl
|
||||||
}
|
},
|
||||||
|
publish: true
|
||||||
})
|
})
|
||||||
return true
|
openPage(router, 'feed')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error('[publishShout]', error)
|
||||||
showSnackbar({ type: 'error', body: t('Error') })
|
showSnackbar({ type: 'error', body: t('Error') })
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const publishShout = async () => {
|
const publishShoutById = async (shoutId: number) => {
|
||||||
try {
|
try {
|
||||||
await apiClient.publishShout({
|
await apiClient.updateArticle({
|
||||||
slug: form.slug,
|
shoutId,
|
||||||
shoutInput: {
|
publish: true
|
||||||
body: form.body,
|
})
|
||||||
topics: form.selectedTopics.map((topic) => topic.slug),
|
|
||||||
// authors?: InputMaybe<Array<InputMaybe<Scalars['String']>>>
|
openPage(router, 'feed')
|
||||||
// community?: InputMaybe<Scalars['Int']>
|
} catch (error) {
|
||||||
mainTopic: form.selectedTopics[0]?.slug || '',
|
console.error('[publishShoutById]', error)
|
||||||
slug: form.slug,
|
showSnackbar({ type: 'error', body: t('Error') })
|
||||||
subtitle: form.subtitle,
|
}
|
||||||
title: form.title
|
}
|
||||||
}
|
|
||||||
|
const deleteShout = async (shoutId: number) => {
|
||||||
|
try {
|
||||||
|
await apiClient.deleteShout({
|
||||||
|
shoutId
|
||||||
})
|
})
|
||||||
return true
|
return true
|
||||||
} catch {
|
} catch {
|
||||||
|
@ -116,6 +175,8 @@ export const EditorProvider = (props: { children: JSX.Element }) => {
|
||||||
const actions = {
|
const actions = {
|
||||||
saveShout,
|
saveShout,
|
||||||
publishShout,
|
publishShout,
|
||||||
|
publishShoutById,
|
||||||
|
deleteShout,
|
||||||
toggleEditorPanel,
|
toggleEditorPanel,
|
||||||
countWords,
|
countWords,
|
||||||
setForm,
|
setForm,
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { gql } from '@urql/core'
|
import { gql } from '@urql/core'
|
||||||
|
|
||||||
export default gql`
|
export default gql`
|
||||||
mutation UpdateShoutMutation($shoutId: Int!, $shoutInput: ShoutInput!) {
|
mutation UpdateShoutMutation($shoutId: Int!, $shoutInput: ShoutInput, $publish: Boolean) {
|
||||||
updateShout(shout_id: $shoutId, shout_input: $shoutInput) {
|
updateShout(shout_id: $shoutId, shout_input: $shoutInput, publish: $publish) {
|
||||||
error
|
error
|
||||||
shout {
|
shout {
|
||||||
id
|
id
|
||||||
|
@ -10,6 +10,7 @@ export default gql`
|
||||||
title
|
title
|
||||||
subtitle
|
subtitle
|
||||||
body
|
body
|
||||||
|
visibility
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
import { gql } from '@urql/core'
|
|
||||||
|
|
||||||
export default gql`
|
|
||||||
mutation PublishShoutMutation($slug: String!, $shout: ShoutInput!) {
|
|
||||||
publishShout(slug: $slug, inp: $shout) {
|
|
||||||
error
|
|
||||||
shout {
|
|
||||||
id
|
|
||||||
slug
|
|
||||||
title
|
|
||||||
subtitle
|
|
||||||
body
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { gql } from '@urql/core'
|
import { gql } from '@urql/core'
|
||||||
|
|
||||||
export default gql`
|
export default gql`
|
||||||
query LoadShoutQuery($slug: String!) {
|
query LoadShoutQuery($slug: String, $shoutId: Int) {
|
||||||
loadShout(slug: $slug) {
|
loadShout(slug: $slug, shout_id: $shoutId) {
|
||||||
id
|
id
|
||||||
title
|
title
|
||||||
subtitle
|
subtitle
|
||||||
|
|
|
@ -173,7 +173,6 @@ export type Mutation = {
|
||||||
follow: Result
|
follow: Result
|
||||||
getSession: AuthResult
|
getSession: AuthResult
|
||||||
markAsRead: Result
|
markAsRead: Result
|
||||||
publishShout: Result
|
|
||||||
rateUser: Result
|
rateUser: Result
|
||||||
registerUser: AuthResult
|
registerUser: AuthResult
|
||||||
sendLink: Result
|
sendLink: Result
|
||||||
|
@ -245,11 +244,6 @@ export type MutationMarkAsReadArgs = {
|
||||||
ids: Array<InputMaybe<Scalars['Int']>>
|
ids: Array<InputMaybe<Scalars['Int']>>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MutationPublishShoutArgs = {
|
|
||||||
shout_id: Scalars['Int']
|
|
||||||
shout_input?: InputMaybe<ShoutInput>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type MutationRateUserArgs = {
|
export type MutationRateUserArgs = {
|
||||||
slug: Scalars['String']
|
slug: Scalars['String']
|
||||||
value: Scalars['Int']
|
value: Scalars['Int']
|
||||||
|
@ -292,8 +286,9 @@ export type MutationUpdateReactionArgs = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MutationUpdateShoutArgs = {
|
export type MutationUpdateShoutArgs = {
|
||||||
|
publish?: InputMaybe<Scalars['Boolean']>
|
||||||
shout_id: Scalars['Int']
|
shout_id: Scalars['Int']
|
||||||
shout_input: ShoutInput
|
shout_input?: InputMaybe<ShoutInput>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MutationUpdateTopicArgs = {
|
export type MutationUpdateTopicArgs = {
|
||||||
|
@ -394,7 +389,8 @@ export type QueryLoadRecipientsArgs = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export type QueryLoadShoutArgs = {
|
export type QueryLoadShoutArgs = {
|
||||||
slug: Scalars['String']
|
shout_id?: InputMaybe<Scalars['Int']>
|
||||||
|
slug?: InputMaybe<Scalars['String']>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type QueryLoadShoutsArgs = {
|
export type QueryLoadShoutsArgs = {
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { apiClient } from '../utils/apiClient'
|
||||||
|
|
||||||
export const onBeforeRender = async (pageContext: PageContext) => {
|
export const onBeforeRender = async (pageContext: PageContext) => {
|
||||||
const { slug } = pageContext.routeParams
|
const { slug } = pageContext.routeParams
|
||||||
const article = await apiClient.getShout(slug)
|
const article = await apiClient.getShoutBySlug(slug)
|
||||||
|
|
||||||
const pageProps: PageProps = { article }
|
const pageProps: PageProps = { article }
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { redirectPage } from '@nanostores/router'
|
||||||
export const CreatePage = () => {
|
export const CreatePage = () => {
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
const shout = await apiClient.createArticle({ article: {} })
|
const shout = await apiClient.createArticle({ article: {} })
|
||||||
redirectPage(router, 'edit', { shoutSlug: shout.slug })
|
redirectPage(router, 'edit', { shoutId: shout.id.toString() })
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,40 +1,15 @@
|
||||||
import { createSignal, For, onMount, Show } from 'solid-js'
|
|
||||||
import { PageLayout } from '../components/_shared/PageLayout'
|
import { PageLayout } from '../components/_shared/PageLayout'
|
||||||
import { useSession } from '../context/session'
|
import { Title } from '@solidjs/meta'
|
||||||
import { Shout } from '../graphql/types.gen'
|
import { useLocalize } from '../context/localize'
|
||||||
import { apiClient } from '../utils/apiClient'
|
import { DraftsView } from '../components/Views/DraftsView'
|
||||||
import { getPagePath } from '@nanostores/router'
|
|
||||||
import { router } from '../stores/router'
|
|
||||||
|
|
||||||
export const DraftsPage = () => {
|
export const DraftsPage = () => {
|
||||||
const { isAuthenticated, isSessionLoaded, user } = useSession()
|
const { t } = useLocalize()
|
||||||
|
|
||||||
const [drafts, setDrafts] = createSignal<Shout[]>([])
|
|
||||||
|
|
||||||
onMount(async () => {
|
|
||||||
const loadedDrafts = await apiClient.getDrafts()
|
|
||||||
setDrafts(loadedDrafts)
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageLayout>
|
<PageLayout>
|
||||||
<Show when={isSessionLoaded()}>
|
<Title>{t('Drafts')}</Title>
|
||||||
<div class="wide-container">
|
<DraftsView />
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-19 col-lg-18 col-xl-16 offset-md-5">
|
|
||||||
<Show when={isAuthenticated()} fallback="Давайте авторизуемся">
|
|
||||||
<For each={drafts()}>
|
|
||||||
{(draft) => (
|
|
||||||
<div>
|
|
||||||
<a href={getPagePath(router, 'edit', { shoutSlug: draft.slug })}>{draft.id}</a>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</For>
|
|
||||||
</Show>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Show>
|
|
||||||
</PageLayout>
|
</PageLayout>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,12 +13,12 @@ export const EditPage = () => {
|
||||||
|
|
||||||
const { page } = useRouter()
|
const { page } = useRouter()
|
||||||
|
|
||||||
const shoutSlug = createMemo(() => (page().params as Record<'shoutSlug', string>).shoutSlug)
|
const shoutId = createMemo(() => Number((page().params as Record<'shoutId', string>).shoutId))
|
||||||
|
|
||||||
const [shout, setShout] = createSignal<Shout>(null)
|
const [shout, setShout] = createSignal<Shout>(null)
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
const loadedShout = await apiClient.getShout(shoutSlug())
|
const loadedShout = await apiClient.getShoutById(shoutId())
|
||||||
setShout(loadedShout)
|
setShout(loadedShout)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,8 @@ export const ROUTES = {
|
||||||
inbox: '/inbox',
|
inbox: '/inbox',
|
||||||
connect: '/connect',
|
connect: '/connect',
|
||||||
create: '/create',
|
create: '/create',
|
||||||
edit: '/edit/:shoutSlug',
|
edit: '/edit/:shoutId',
|
||||||
editSettings: '/edit/:shoutSlug/settings',
|
editSettings: '/edit/:shoutId/settings',
|
||||||
drafts: '/drafts',
|
drafts: '/drafts',
|
||||||
topics: '/topics',
|
topics: '/topics',
|
||||||
topic: '/topic/:slug',
|
topic: '/topic/:slug',
|
||||||
|
|
|
@ -124,7 +124,7 @@ const addSortedArticles = (articles: Shout[]) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const loadShout = async (slug: string): Promise<void> => {
|
export const loadShout = async (slug: string): Promise<void> => {
|
||||||
const newArticle = await apiClient.getShout(slug)
|
const newArticle = await apiClient.getShoutBySlug(slug)
|
||||||
addArticles([newArticle])
|
addArticles([newArticle])
|
||||||
const newArticleIndex = sortedArticles().findIndex((s) => s.id === newArticle.id)
|
const newArticleIndex = sortedArticles().findIndex((s) => s.id === newArticle.id)
|
||||||
if (newArticleIndex >= 0) {
|
if (newArticleIndex >= 0) {
|
||||||
|
|
|
@ -52,7 +52,7 @@ import loadRecipients from '../graphql/query/chat-recipients'
|
||||||
import createMessage from '../graphql/mutation/create-chat-message'
|
import createMessage from '../graphql/mutation/create-chat-message'
|
||||||
import updateProfile from '../graphql/mutation/update-profile'
|
import updateProfile from '../graphql/mutation/update-profile'
|
||||||
import updateArticle from '../graphql/mutation/article-update'
|
import updateArticle from '../graphql/mutation/article-update'
|
||||||
import publishShout from '../graphql/mutation/shout-publish'
|
import deleteShout from '../graphql/mutation/article-delete'
|
||||||
|
|
||||||
type ApiErrorCode =
|
type ApiErrorCode =
|
||||||
| 'unknown'
|
| 'unknown'
|
||||||
|
@ -250,21 +250,22 @@ export const apiClient = {
|
||||||
},
|
},
|
||||||
updateArticle: async ({
|
updateArticle: async ({
|
||||||
shoutId,
|
shoutId,
|
||||||
shoutInput
|
shoutInput,
|
||||||
|
publish
|
||||||
}: {
|
}: {
|
||||||
shoutId: number
|
shoutId: number
|
||||||
shoutInput: ShoutInput
|
shoutInput?: ShoutInput
|
||||||
|
publish: boolean
|
||||||
}): Promise<Shout> => {
|
}): Promise<Shout> => {
|
||||||
const response = await privateGraphQLClient.mutation(updateArticle, { shoutId, shoutInput }).toPromise()
|
const response = await privateGraphQLClient
|
||||||
|
.mutation(updateArticle, { shoutId, shoutInput, publish })
|
||||||
|
.toPromise()
|
||||||
console.debug('[updateArticle]:', response.data)
|
console.debug('[updateArticle]:', response.data)
|
||||||
return response.data.updateShout.shout
|
return response.data.updateShout.shout
|
||||||
},
|
},
|
||||||
publishShout: async ({ slug, shoutInput }: { slug: string; shoutInput: ShoutInput }): Promise<Shout> => {
|
deleteShout: async ({ shoutId }: { shoutId: number }): Promise<void> => {
|
||||||
const response = await privateGraphQLClient
|
const response = await privateGraphQLClient.mutation(deleteShout, { shoutId }).toPromise()
|
||||||
.mutation(publishShout, { slug, shout: shoutInput })
|
console.debug('[deleteShout]:', response)
|
||||||
.toPromise()
|
|
||||||
console.debug('[publishShout]:', response)
|
|
||||||
return response.data.publishShout.shout
|
|
||||||
},
|
},
|
||||||
getDrafts: async (): Promise<Shout[]> => {
|
getDrafts: async (): Promise<Shout[]> => {
|
||||||
const response = await privateGraphQLClient.query(draftsLoad, {}).toPromise()
|
const response = await privateGraphQLClient.query(draftsLoad, {}).toPromise()
|
||||||
|
@ -292,15 +293,33 @@ export const apiClient = {
|
||||||
const resp = await publicGraphQLClient.query(authorsLoadBy, options).toPromise()
|
const resp = await publicGraphQLClient.query(authorsLoadBy, options).toPromise()
|
||||||
return resp.data.loadAuthorsBy
|
return resp.data.loadAuthorsBy
|
||||||
},
|
},
|
||||||
getShout: async (slug: string) => {
|
getShoutBySlug: async (slug: string) => {
|
||||||
const resp = await publicGraphQLClient
|
const resp = await publicGraphQLClient
|
||||||
.query(shoutLoad, {
|
.query(shoutLoad, {
|
||||||
slug
|
slug
|
||||||
})
|
})
|
||||||
.toPromise()
|
.toPromise()
|
||||||
if (resp.error) console.debug(resp)
|
|
||||||
|
if (resp.error) {
|
||||||
|
console.error(resp)
|
||||||
|
}
|
||||||
|
|
||||||
return resp.data.loadShout
|
return resp.data.loadShout
|
||||||
},
|
},
|
||||||
|
getShoutById: async (shoutId: number) => {
|
||||||
|
const resp = await publicGraphQLClient
|
||||||
|
.query(shoutLoad, {
|
||||||
|
shoutId
|
||||||
|
})
|
||||||
|
.toPromise()
|
||||||
|
|
||||||
|
if (resp.error) {
|
||||||
|
console.error(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp.data.loadShout
|
||||||
|
},
|
||||||
|
|
||||||
getShouts: async (options: LoadShoutsOptions) => {
|
getShouts: async (options: LoadShoutsOptions) => {
|
||||||
const resp = await publicGraphQLClient.query(shoutsLoadBy, { options }).toPromise()
|
const resp = await publicGraphQLClient.query(shoutsLoadBy, { options }).toPromise()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user