Merge pull request #27 from Discours/topics-authors
Client data fetching, topics, authors
This commit is contained in:
commit
a80f123308
|
@ -18,7 +18,6 @@ const getDevCssClassPrefix = (filename: string): string => {
|
||||||
.slice(filename.indexOf(PATH_PREFIX) + PATH_PREFIX.length)
|
.slice(filename.indexOf(PATH_PREFIX) + PATH_PREFIX.length)
|
||||||
.replace('.module.scss', '')
|
.replace('.module.scss', '')
|
||||||
.replace(/[/?\\]/g, '-')
|
.replace(/[/?\\]/g, '-')
|
||||||
.replace('?', '-')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const devGenerateScopedName = (name: string, filename: string, css: string) =>
|
const devGenerateScopedName = (name: string, filename: string, css: string) =>
|
||||||
|
|
|
@ -71,7 +71,7 @@ export const Header = (props: Props) => {
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
let scrollTop = window.scrollY
|
let scrollTop = window.scrollY
|
||||||
|
|
||||||
window.console.log(props.title)
|
// window.console.log(props.title)
|
||||||
|
|
||||||
const handleScroll = () => {
|
const handleScroll = () => {
|
||||||
setIsScrollingBottom(window.scrollY > scrollTop)
|
setIsScrollingBottom(window.scrollY > scrollTop)
|
||||||
|
|
|
@ -1,11 +1,27 @@
|
||||||
import { MainLayout } from '../Layouts/MainLayout'
|
import { MainLayout } from '../Layouts/MainLayout'
|
||||||
import { AllAuthorsView } from '../Views/AllAuthors'
|
import { AllAuthorsView } from '../Views/AllAuthors'
|
||||||
import type { PageProps } from '../types'
|
import type { PageProps } from '../types'
|
||||||
|
import { createSignal, onMount, Show } from 'solid-js'
|
||||||
|
import { loadAllAuthors } from '../../stores/zine/authors'
|
||||||
|
import { t } from '../../utils/intl'
|
||||||
|
|
||||||
export const AllAuthorsPage = (props: PageProps) => {
|
export const AllAuthorsPage = (props: PageProps) => {
|
||||||
|
const [isLoaded, setIsLoaded] = createSignal<boolean>(Boolean(props.allAuthors))
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
if (isLoaded()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await loadAllAuthors()
|
||||||
|
setIsLoaded(true)
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MainLayout>
|
<MainLayout>
|
||||||
<AllAuthorsView authors={props.authors} />
|
<Show when={isLoaded()} fallback={t('Loading')}>
|
||||||
|
<AllAuthorsView authors={props.allAuthors} />
|
||||||
|
</Show>
|
||||||
</MainLayout>
|
</MainLayout>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,27 @@
|
||||||
import { MainLayout } from '../Layouts/MainLayout'
|
import { MainLayout } from '../Layouts/MainLayout'
|
||||||
import { AllTopicsView } from '../Views/AllTopics'
|
import { AllTopicsView } from '../Views/AllTopics'
|
||||||
import type { PageProps } from '../types'
|
import type { PageProps } from '../types'
|
||||||
|
import { createSignal, onMount, Show } from 'solid-js'
|
||||||
|
import { t } from '../../utils/intl'
|
||||||
|
import { loadAllTopics } from '../../stores/zine/topics'
|
||||||
|
|
||||||
export const AllTopicsPage = (props: PageProps) => {
|
export const AllTopicsPage = (props: PageProps) => {
|
||||||
|
const [isLoaded, setIsLoaded] = createSignal<boolean>(Boolean(props.allTopics))
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
if (isLoaded()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await loadAllTopics()
|
||||||
|
setIsLoaded(true)
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MainLayout>
|
<MainLayout>
|
||||||
<AllTopicsView topics={props.topics} />
|
<Show when={isLoaded()} fallback={t('Loading')}>
|
||||||
|
<AllTopicsView topics={props.allTopics} />
|
||||||
|
</Show>
|
||||||
</MainLayout>
|
</MainLayout>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,26 +10,29 @@ import { useRouter } from '../../stores/router'
|
||||||
export const ArticlePage = (props: PageProps) => {
|
export const ArticlePage = (props: PageProps) => {
|
||||||
const sortedArticles = props.article ? [props.article] : []
|
const sortedArticles = props.article ? [props.article] : []
|
||||||
|
|
||||||
const { getPage } = useRouter()
|
const slug = createMemo(() => {
|
||||||
|
const { getPage } = useRouter()
|
||||||
|
|
||||||
const page = getPage()
|
const page = getPage()
|
||||||
|
|
||||||
if (page.route !== 'article') {
|
if (page.route !== 'article') {
|
||||||
throw new Error('ts guard')
|
throw new Error('ts guard')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return page.params.slug
|
||||||
|
})
|
||||||
|
|
||||||
const { articleEntities } = useArticlesStore({
|
const { articleEntities } = useArticlesStore({
|
||||||
sortedArticles
|
sortedArticles
|
||||||
})
|
})
|
||||||
|
|
||||||
const article = createMemo<Shout>(() => articleEntities()[page.params.slug])
|
const article = createMemo<Shout>(() => articleEntities()[slug()])
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
const slug = page.params.slug
|
const articleValue = articleEntities()[slug()]
|
||||||
const articleValue = articleEntities()[slug]
|
|
||||||
|
|
||||||
if (!articleValue || !articleValue.body) {
|
if (!articleValue || !articleValue.body) {
|
||||||
loadArticle({ slug })
|
loadArticle({ slug: slug() })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,45 @@
|
||||||
import { MainLayout } from '../Layouts/MainLayout'
|
import { MainLayout } from '../Layouts/MainLayout'
|
||||||
import { AuthorView } from '../Views/Author'
|
import { AuthorView } from '../Views/Author'
|
||||||
import type { PageProps } from '../types'
|
import type { PageProps } from '../types'
|
||||||
|
import { createMemo, createSignal, onCleanup, onMount, Show } from 'solid-js'
|
||||||
|
import { loadArticlesForAuthors, resetSortedArticles } from '../../stores/zine/articles'
|
||||||
|
import { useRouter } from '../../stores/router'
|
||||||
|
import { t } from '../../utils/intl'
|
||||||
|
import { loadAuthor } from '../../stores/zine/authors'
|
||||||
|
|
||||||
export const AuthorPage = (props: PageProps) => {
|
export const AuthorPage = (props: PageProps) => {
|
||||||
|
const [isLoaded, setIsLoaded] = createSignal(Boolean(props.authorArticles) && Boolean(props.author))
|
||||||
|
|
||||||
|
const slug = createMemo(() => {
|
||||||
|
const { getPage } = useRouter()
|
||||||
|
|
||||||
|
const page = getPage()
|
||||||
|
|
||||||
|
if (page.route !== 'author') {
|
||||||
|
throw new Error('ts guard')
|
||||||
|
}
|
||||||
|
|
||||||
|
return page.params.slug
|
||||||
|
})
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
if (isLoaded()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await loadArticlesForAuthors({ authorSlugs: [slug()] })
|
||||||
|
await loadAuthor({ slug: slug() })
|
||||||
|
|
||||||
|
setIsLoaded(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
onCleanup(() => resetSortedArticles())
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MainLayout>
|
<MainLayout>
|
||||||
<AuthorView author={props.author} authorArticles={props.articles} />
|
<Show when={isLoaded()} fallback={t('Loading')}>
|
||||||
|
<AuthorView author={props.author} authorArticles={props.authorArticles} authorSlug={slug()} />
|
||||||
|
</Show>
|
||||||
</MainLayout>
|
</MainLayout>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,30 @@
|
||||||
import { MainLayout } from '../Layouts/MainLayout'
|
import { MainLayout } from '../Layouts/MainLayout'
|
||||||
import { FeedView } from '../Views/Feed'
|
import { FeedView } from '../Views/Feed'
|
||||||
import type { PageProps } from '../types'
|
import type { PageProps } from '../types'
|
||||||
|
import { createSignal, onCleanup, onMount, Show } from 'solid-js'
|
||||||
|
import { loadRecentArticles, resetSortedArticles } from '../../stores/zine/articles'
|
||||||
|
import { t } from '../../utils/intl'
|
||||||
|
|
||||||
export const FeedPage = (props: PageProps) => {
|
export const FeedPage = (props: PageProps) => {
|
||||||
|
const [isLoaded, setIsLoaded] = createSignal(Boolean(props.feedArticles))
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
if (isLoaded()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await loadRecentArticles({ limit: 50, offset: 0 })
|
||||||
|
|
||||||
|
setIsLoaded(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
onCleanup(() => resetSortedArticles())
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MainLayout>
|
<MainLayout>
|
||||||
<FeedView articles={props.articles} />
|
<Show when={isLoaded()} fallback={t('Loading')}>
|
||||||
|
<FeedView articles={props.feedArticles} />
|
||||||
|
</Show>
|
||||||
</MainLayout>
|
</MainLayout>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,32 @@
|
||||||
import { HomeView } from '../Views/Home'
|
import { HomeView } from '../Views/Home'
|
||||||
import { MainLayout } from '../Layouts/MainLayout'
|
import { MainLayout } from '../Layouts/MainLayout'
|
||||||
import type { PageProps } from '../types'
|
import type { PageProps } from '../types'
|
||||||
|
import { createSignal, onCleanup, onMount, Show } from 'solid-js'
|
||||||
|
import { t } from '../../utils/intl'
|
||||||
|
import { loadPublishedArticles, resetSortedArticles } from '../../stores/zine/articles'
|
||||||
|
import { loadRandomTopics } from '../../stores/zine/topics'
|
||||||
|
|
||||||
export const HomePage = (props: PageProps) => {
|
export const HomePage = (props: PageProps) => {
|
||||||
|
const [isLoaded, setIsLoaded] = createSignal(Boolean(props.homeArticles) && Boolean(props.randomTopics))
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
if (isLoaded()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await loadPublishedArticles({ limit: 5, offset: 0 })
|
||||||
|
await loadRandomTopics()
|
||||||
|
|
||||||
|
setIsLoaded(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
onCleanup(() => resetSortedArticles())
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MainLayout>
|
<MainLayout>
|
||||||
<HomeView randomTopics={props.randomTopics} recentPublishedArticles={props.articles || []} />
|
<Show when={isLoaded()} fallback={t('Loading')}>
|
||||||
|
<HomeView randomTopics={props.randomTopics} recentPublishedArticles={props.homeArticles || []} />
|
||||||
|
</Show>
|
||||||
</MainLayout>
|
</MainLayout>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,42 @@
|
||||||
import { MainLayout } from '../Layouts/MainLayout'
|
import { MainLayout } from '../Layouts/MainLayout'
|
||||||
import { SearchView } from '../Views/Search'
|
import { SearchView } from '../Views/Search'
|
||||||
import type { PageProps } from '../types'
|
import type { PageProps } from '../types'
|
||||||
|
import { createMemo, createSignal, onCleanup, onMount, Show } from 'solid-js'
|
||||||
|
import { loadSearchResults, resetSortedArticles } from '../../stores/zine/articles'
|
||||||
|
import { t } from '../../utils/intl'
|
||||||
|
import { useRouter } from '../../stores/router'
|
||||||
|
|
||||||
export const SearchPage = (props: PageProps) => {
|
export const SearchPage = (props: PageProps) => {
|
||||||
|
const [isLoaded, setIsLoaded] = createSignal(Boolean(props.searchResults))
|
||||||
|
|
||||||
|
const q = createMemo(() => {
|
||||||
|
const { getPage } = useRouter()
|
||||||
|
|
||||||
|
const page = getPage()
|
||||||
|
|
||||||
|
if (page.route !== 'search') {
|
||||||
|
throw new Error('ts guard')
|
||||||
|
}
|
||||||
|
|
||||||
|
return page.params.q
|
||||||
|
})
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
if (isLoaded()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await loadSearchResults({ query: q(), limit: 50, offset: 0 })
|
||||||
|
setIsLoaded(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
onCleanup(() => resetSortedArticles())
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MainLayout>
|
<MainLayout>
|
||||||
<SearchView results={props.searchResults || []} query={props.searchQuery} />
|
<Show when={isLoaded()} fallback={t('Loading')}>
|
||||||
|
<SearchView results={props.searchResults || []} query={props.searchQuery} />
|
||||||
|
</Show>
|
||||||
</MainLayout>
|
</MainLayout>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,45 @@
|
||||||
import { MainLayout } from '../Layouts/MainLayout'
|
import { MainLayout } from '../Layouts/MainLayout'
|
||||||
import { TopicView } from '../Views/Topic'
|
import { TopicView } from '../Views/Topic'
|
||||||
import type { PageProps } from '../types'
|
import type { PageProps } from '../types'
|
||||||
|
import { createMemo, createSignal, onCleanup, onMount, Show } from 'solid-js'
|
||||||
|
import { loadArticlesForTopics, resetSortedArticles } from '../../stores/zine/articles'
|
||||||
|
import { useRouter } from '../../stores/router'
|
||||||
|
import { t } from '../../utils/intl'
|
||||||
|
import { loadTopic } from '../../stores/zine/topics'
|
||||||
|
|
||||||
export const TopicPage = (props: PageProps) => {
|
export const TopicPage = (props: PageProps) => {
|
||||||
|
const [isLoaded, setIsLoaded] = createSignal(Boolean(props.authorArticles) && Boolean(props.author))
|
||||||
|
|
||||||
|
const slug = createMemo(() => {
|
||||||
|
const { getPage } = useRouter()
|
||||||
|
|
||||||
|
const page = getPage()
|
||||||
|
|
||||||
|
if (page.route !== 'author') {
|
||||||
|
throw new Error('ts guard')
|
||||||
|
}
|
||||||
|
|
||||||
|
return page.params.slug
|
||||||
|
})
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
if (isLoaded()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await loadArticlesForTopics({ topicSlugs: [slug()] })
|
||||||
|
await loadTopic({ slug: slug() })
|
||||||
|
|
||||||
|
setIsLoaded(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
onCleanup(() => resetSortedArticles())
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MainLayout>
|
<MainLayout>
|
||||||
<TopicView topic={props.topic} topicArticles={props.articles} />
|
<Show when={isLoaded()} fallback={t('Loading')}>
|
||||||
|
<TopicView topic={props.topic} topicArticles={props.topicArticles} topicSlug={slug()} />
|
||||||
|
</Show>
|
||||||
</MainLayout>
|
</MainLayout>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
import { createEffect, createSignal, For, Show } from 'solid-js'
|
import { createEffect, createMemo, createSignal, For, Show } from 'solid-js'
|
||||||
import type { Author } from '../../graphql/types.gen'
|
import type { Author } from '../../graphql/types.gen'
|
||||||
import { AuthorCard } from '../Author/Card'
|
import { AuthorCard } from '../Author/Card'
|
||||||
import { byFirstChar, sortBy } from '../../utils/sortby'
|
|
||||||
import { groupByName } from '../../utils/groupby'
|
|
||||||
import { Icon } from '../Nav/Icon'
|
import { Icon } from '../Nav/Icon'
|
||||||
import { t } from '../../utils/intl'
|
import { t } from '../../utils/intl'
|
||||||
import { useAuthorsStore } from '../../stores/zine/authors'
|
import { useAuthorsStore, setSortAllBy as setSortAllAuthorsBy } from '../../stores/zine/authors'
|
||||||
import { handleClientRouteLinkClick, useRouter } from '../../stores/router'
|
import { handleClientRouteLinkClick, useRouter } from '../../stores/router'
|
||||||
import { useAuthStore } from '../../stores/auth'
|
import { useAuthStore } from '../../stores/auth'
|
||||||
|
import { getLogger } from '../../utils/logger'
|
||||||
import '../../styles/AllTopics.scss'
|
import '../../styles/AllTopics.scss'
|
||||||
|
import { Topic } from '../../graphql/types.gen'
|
||||||
|
|
||||||
|
const log = getLogger('AllAuthorsView')
|
||||||
|
|
||||||
type AllAuthorsPageSearchParams = {
|
type AllAuthorsPageSearchParams = {
|
||||||
by: '' | 'name' | 'shouts' | 'rating'
|
by: '' | 'name' | 'shouts' | 'rating'
|
||||||
|
@ -19,31 +21,38 @@ type Props = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AllAuthorsView = (props: Props) => {
|
export const AllAuthorsView = (props: Props) => {
|
||||||
const { sortedAuthors: authorList } = useAuthorsStore({ authors: props.authors })
|
const { sortedAuthors } = useAuthorsStore({ authors: props.authors })
|
||||||
const [sortedAuthors, setSortedAuthors] = createSignal<Author[]>([])
|
|
||||||
const [sortedKeys, setSortedKeys] = createSignal<string[]>([])
|
|
||||||
const [abc, setAbc] = createSignal([])
|
|
||||||
|
|
||||||
const { session } = useAuthStore()
|
const { session } = useAuthStore()
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
setSortAllAuthorsBy(getSearchParams().by || 'shouts')
|
||||||
|
})
|
||||||
|
|
||||||
const subscribed = (s) => Boolean(session()?.news?.authors && session()?.news?.authors?.includes(s || ''))
|
const subscribed = (s) => Boolean(session()?.news?.authors && session()?.news?.authors?.includes(s || ''))
|
||||||
|
|
||||||
const { getSearchParams } = useRouter<AllAuthorsPageSearchParams>()
|
const { getSearchParams } = useRouter<AllAuthorsPageSearchParams>()
|
||||||
|
|
||||||
createEffect(() => {
|
const byLetter = createMemo<{ [letter: string]: Author[] }>(() => {
|
||||||
if ((!getSearchParams().by || getSearchParams().by === 'name') && abc().length === 0) {
|
return sortedAuthors().reduce((acc, author) => {
|
||||||
console.log('[authors] default grouping by abc')
|
const letter = author.name[0]
|
||||||
const grouped = { ...groupByName(authorList()) }
|
if (!acc[letter]) {
|
||||||
grouped['A-Z'] = sortBy(grouped['A-Z'], byFirstChar)
|
acc[letter] = []
|
||||||
setAbc(grouped)
|
}
|
||||||
const keys = Object.keys(abc)
|
|
||||||
keys.sort()
|
acc[letter].push(author)
|
||||||
setSortedKeys(keys as string[])
|
|
||||||
} else {
|
return acc
|
||||||
console.log('[authors] sorting by ' + getSearchParams().by)
|
}, {} as { [letter: string]: Author[] })
|
||||||
setSortedAuthors(sortBy(authorList(), getSearchParams().by))
|
})
|
||||||
}
|
|
||||||
}, [authorList(), getSearchParams().by])
|
const sortedKeys = createMemo<string[]>(() => {
|
||||||
|
const keys = Object.keys(byLetter())
|
||||||
|
keys.sort()
|
||||||
|
return keys
|
||||||
|
})
|
||||||
|
|
||||||
|
// log.debug(getSearchParams())
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="all-topics-page">
|
<div class="all-topics-page">
|
||||||
|
@ -86,7 +95,7 @@ export const AllAuthorsView = (props: Props) => {
|
||||||
fallback={() => (
|
fallback={() => (
|
||||||
<div class="stats">
|
<div class="stats">
|
||||||
<For each={sortedAuthors()}>
|
<For each={sortedAuthors()}>
|
||||||
{(author: Author) => (
|
{(author) => (
|
||||||
<AuthorCard
|
<AuthorCard
|
||||||
author={author}
|
author={author}
|
||||||
compact={false}
|
compact={false}
|
||||||
|
@ -99,12 +108,12 @@ export const AllAuthorsView = (props: Props) => {
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<For each={sortedKeys()}>
|
<For each={sortedKeys()}>
|
||||||
{(letter: string) => (
|
{(letter) => (
|
||||||
<div class="group">
|
<div class="group">
|
||||||
<h2>{letter}</h2>
|
<h2>{letter}</h2>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<For each={abc()[letter]}>
|
<For each={byLetter()[letter]}>
|
||||||
{(author: Author) => (
|
{(author: Author) => (
|
||||||
<div class="topic col-sm-6 col-md-3">
|
<div class="topic col-sm-6 col-md-3">
|
||||||
<div class="topic-title">
|
<div class="topic-title">
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { createEffect, For, Show } from 'solid-js'
|
import { createEffect, createMemo, For, Show } from 'solid-js'
|
||||||
import type { Topic } from '../../graphql/types.gen'
|
import type { Topic } from '../../graphql/types.gen'
|
||||||
import { Icon } from '../Nav/Icon'
|
import { Icon } from '../Nav/Icon'
|
||||||
import { t } from '../../utils/intl'
|
import { t } from '../../utils/intl'
|
||||||
|
@ -33,6 +33,25 @@ export const AllTopicsView = (props: AllTopicsViewProps) => {
|
||||||
setSortAllTopicsBy(getSearchParams().by || 'shouts')
|
setSortAllTopicsBy(getSearchParams().by || 'shouts')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const byLetter = createMemo<{ [letter: string]: Topic[] }>(() => {
|
||||||
|
return sortedTopics().reduce((acc, topic) => {
|
||||||
|
const letter = topic.title[0]
|
||||||
|
if (!acc[letter]) {
|
||||||
|
acc[letter] = []
|
||||||
|
}
|
||||||
|
|
||||||
|
acc[letter].push(topic)
|
||||||
|
|
||||||
|
return acc
|
||||||
|
}, {} as { [letter: string]: Topic[] })
|
||||||
|
})
|
||||||
|
|
||||||
|
const sortedKeys = createMemo<string[]>(() => {
|
||||||
|
const keys = Object.keys(byLetter())
|
||||||
|
keys.sort()
|
||||||
|
return keys
|
||||||
|
})
|
||||||
|
|
||||||
const subscribed = (s) => Boolean(session()?.news?.topics && session()?.news?.topics?.includes(s || ''))
|
const subscribed = (s) => Boolean(session()?.news?.topics && session()?.news?.topics?.includes(s || ''))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -80,48 +99,39 @@ export const AllTopicsView = (props: AllTopicsViewProps) => {
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="stats">
|
<Show
|
||||||
<For each={sortedTopics()}>
|
when={getSearchParams().by === 'title'}
|
||||||
{(topic) => (
|
fallback={() => (
|
||||||
<TopicCard topic={topic} compact={false} subscribed={subscribed(topic.slug)} />
|
<div class="stats">
|
||||||
|
<For each={sortedTopics()}>
|
||||||
|
{(topic) => (
|
||||||
|
<TopicCard topic={topic} compact={false} subscribed={subscribed(topic.slug)} />
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<For each={sortedKeys()}>
|
||||||
|
{(letter) => (
|
||||||
|
<div class="group">
|
||||||
|
<h2>{letter}</h2>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<For each={byLetter()[letter]}>
|
||||||
|
{(topic) => (
|
||||||
|
<div class="topic col-sm-6 col-md-3">
|
||||||
|
<div class="topic-title">
|
||||||
|
<a href={`/topic/${topic.slug}`}>{topic.title}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</For>
|
</For>
|
||||||
</div>
|
</Show>
|
||||||
|
|
||||||
{/*FIXME*/}
|
|
||||||
{/*<Show*/}
|
|
||||||
{/* when={params()['by'] === 'abc'}*/}
|
|
||||||
{/* fallback={() => (*/}
|
|
||||||
{/* <div class="stats">*/}
|
|
||||||
{/* <For each={getSortedTopics()}>*/}
|
|
||||||
{/* {(topic: Topic) => (*/}
|
|
||||||
{/* <TopicCard topic={topic} compact={false} subscribed={subscribed(topic.slug)} />*/}
|
|
||||||
{/* )}*/}
|
|
||||||
{/* </For>*/}
|
|
||||||
{/* </div>*/}
|
|
||||||
{/* )}*/}
|
|
||||||
{/*>*/}
|
|
||||||
{/* <For each={sortedKeys() || []}>*/}
|
|
||||||
{/* {(letter: string) => (*/}
|
|
||||||
{/* <div class="group">*/}
|
|
||||||
{/* <h2>{letter}</h2>*/}
|
|
||||||
{/* <div class="container">*/}
|
|
||||||
{/* <div class="row">*/}
|
|
||||||
{/* <For each={abc()[letter]}>*/}
|
|
||||||
{/* {(topic: Partial<Topic>) => (*/}
|
|
||||||
{/* <div class="topic col-sm-6 col-md-3">*/}
|
|
||||||
{/* <div class="topic-title">*/}
|
|
||||||
{/* <a href={`/topic/${topic.slug}`}>{topic.title}</a>*/}
|
|
||||||
{/* </div>*/}
|
|
||||||
{/* </div>*/}
|
|
||||||
{/* )}*/}
|
|
||||||
{/* </For>*/}
|
|
||||||
{/* </div>*/}
|
|
||||||
{/* </div>*/}
|
|
||||||
{/* </div>*/}
|
|
||||||
{/* )}*/}
|
|
||||||
{/* </For>*/}
|
|
||||||
{/*</Show>*/}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -16,6 +16,7 @@ import { useRouter } from '../../stores/router'
|
||||||
type AuthorProps = {
|
type AuthorProps = {
|
||||||
authorArticles: Shout[]
|
authorArticles: Shout[]
|
||||||
author: Author
|
author: Author
|
||||||
|
authorSlug: string
|
||||||
// FIXME author topics fro server
|
// FIXME author topics fro server
|
||||||
// topics: Topic[]
|
// topics: Topic[]
|
||||||
}
|
}
|
||||||
|
@ -30,18 +31,9 @@ export const AuthorView = (props: AuthorProps) => {
|
||||||
})
|
})
|
||||||
const { authorEntities } = useAuthorsStore({ authors: [props.author] })
|
const { authorEntities } = useAuthorsStore({ authors: [props.author] })
|
||||||
|
|
||||||
const author = createMemo(() => authorEntities()[props.author.slug])
|
const author = createMemo(() => authorEntities()[props.authorSlug])
|
||||||
const { getSearchParams, changeSearchParam } = useRouter<AuthorPageSearchParams>()
|
const { getSearchParams, changeSearchParam } = useRouter<AuthorPageSearchParams>()
|
||||||
|
|
||||||
//const slug = createMemo(() => author().slug)
|
|
||||||
/*
|
|
||||||
const slug = createMemo<string>(() => {
|
|
||||||
let slug = props?.slug
|
|
||||||
if (props?.slug.startsWith('@')) slug = slug.replace('@', '')
|
|
||||||
return slug
|
|
||||||
})
|
|
||||||
*/
|
|
||||||
|
|
||||||
const title = createMemo(() => {
|
const title = createMemo(() => {
|
||||||
const m = getSearchParams().by
|
const m = getSearchParams().by
|
||||||
if (m === 'viewed') return t('Top viewed')
|
if (m === 'viewed') return t('Top viewed')
|
||||||
|
@ -101,8 +93,8 @@ export const AuthorView = (props: AuthorProps) => {
|
||||||
{/*/>*/}
|
{/*/>*/}
|
||||||
<Row3 articles={sortedArticles().slice(1, 4)} />
|
<Row3 articles={sortedArticles().slice(1, 4)} />
|
||||||
<Row2 articles={sortedArticles().slice(4, 6)} />
|
<Row2 articles={sortedArticles().slice(4, 6)} />
|
||||||
<Row3 articles={sortedArticles().slice(10, 13)} />
|
<Row3 articles={sortedArticles().slice(6, 9)} />
|
||||||
<Row3 articles={sortedArticles().slice(13, 16)} />
|
<Row3 articles={sortedArticles().slice(9, 12)} />
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { createMemo, For, onMount, Show } from 'solid-js'
|
import { createEffect, createMemo, For, onMount, Show } from 'solid-js'
|
||||||
import Banner from '../Discours/Banner'
|
import Banner from '../Discours/Banner'
|
||||||
import { NavTopics } from '../Nav/Topics'
|
import { NavTopics } from '../Nav/Topics'
|
||||||
import { Row5 } from '../Feed/Row5'
|
import { Row5 } from '../Feed/Row5'
|
||||||
|
@ -103,7 +103,7 @@ export const HomeView = (props: HomeProps) => {
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Show when={locale()}>
|
<Show when={locale() && sortedArticles().length > 0}>
|
||||||
<NavTopics topics={randomTopics()} />
|
<NavTopics topics={randomTopics()} />
|
||||||
|
|
||||||
<Row5 articles={sortedArticles().slice(0, 5)} />
|
<Row5 articles={sortedArticles().slice(0, 5)} />
|
||||||
|
|
|
@ -19,6 +19,7 @@ type TopicsPageSearchParams = {
|
||||||
interface TopicProps {
|
interface TopicProps {
|
||||||
topic: Topic
|
topic: Topic
|
||||||
topicArticles: Shout[]
|
topicArticles: Shout[]
|
||||||
|
topicSlug: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TopicView = (props: TopicProps) => {
|
export const TopicView = (props: TopicProps) => {
|
||||||
|
@ -29,7 +30,7 @@ export const TopicView = (props: TopicProps) => {
|
||||||
|
|
||||||
const { authorsByTopic } = useAuthorsStore()
|
const { authorsByTopic } = useAuthorsStore()
|
||||||
|
|
||||||
const topic = createMemo(() => topicEntities()[props.topic.slug])
|
const topic = createMemo(() => topicEntities()[props.topicSlug])
|
||||||
|
|
||||||
/*
|
/*
|
||||||
const slug = createMemo<string>(() => {
|
const slug = createMemo<string>(() => {
|
||||||
|
@ -40,11 +41,10 @@ export const TopicView = (props: TopicProps) => {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const title = createMemo(() => {
|
const title = createMemo(() => {
|
||||||
// FIXME
|
const m = getSearchParams().by
|
||||||
// const m = getSearchParams().by
|
if (m === 'viewed') return t('Top viewed')
|
||||||
// if (m === 'viewed') return t('Top viewed')
|
if (m === 'rating') return t('Top rated')
|
||||||
// if (m === 'rating') return t('Top rated')
|
if (m === 'commented') return t('Top discussed')
|
||||||
// if (m === 'commented') return t('Top discussed')
|
|
||||||
return t('Top recent')
|
return t('Top recent')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -5,11 +5,14 @@ import type { Author, Shout, Topic } from '../graphql/types.gen'
|
||||||
export type PageProps = {
|
export type PageProps = {
|
||||||
randomTopics?: Topic[]
|
randomTopics?: Topic[]
|
||||||
article?: Shout
|
article?: Shout
|
||||||
articles?: Shout[]
|
authorArticles?: Shout[]
|
||||||
|
topicArticles?: Shout[]
|
||||||
|
homeArticles?: Shout[]
|
||||||
|
feedArticles?: Shout[]
|
||||||
author?: Author
|
author?: Author
|
||||||
authors?: Author[]
|
allAuthors?: Author[]
|
||||||
topic?: Topic
|
topic?: Topic
|
||||||
topics?: Topic[]
|
allTopics?: Topic[]
|
||||||
searchQuery?: string
|
searchQuery?: string
|
||||||
// other types?
|
// other types?
|
||||||
searchResults?: Shout[]
|
searchResults?: Shout[]
|
||||||
|
|
|
@ -344,6 +344,7 @@ export type Query = {
|
||||||
getUsersBySlugs: Array<Maybe<User>>
|
getUsersBySlugs: Array<Maybe<User>>
|
||||||
isEmailUsed: Scalars['Boolean']
|
isEmailUsed: Scalars['Boolean']
|
||||||
loadChat: Array<Maybe<Message>>
|
loadChat: Array<Maybe<Message>>
|
||||||
|
markdownBody: Scalars['String']
|
||||||
myChats: Array<Maybe<ChatResult>>
|
myChats: Array<Maybe<ChatResult>>
|
||||||
reactionsByAuthor: Array<Maybe<Reaction>>
|
reactionsByAuthor: Array<Maybe<Reaction>>
|
||||||
reactionsByShout: Array<Maybe<Reaction>>
|
reactionsByShout: Array<Maybe<Reaction>>
|
||||||
|
@ -411,6 +412,10 @@ export type QueryLoadChatArgs = {
|
||||||
size: Scalars['Int']
|
size: Scalars['Int']
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type QueryMarkdownBodyArgs = {
|
||||||
|
body: Scalars['String']
|
||||||
|
}
|
||||||
|
|
||||||
export type QueryReactionsByAuthorArgs = {
|
export type QueryReactionsByAuthorArgs = {
|
||||||
limit: Scalars['Int']
|
limit: Scalars['Int']
|
||||||
offset: Scalars['Int']
|
offset: Scalars['Int']
|
||||||
|
|
|
@ -4,7 +4,7 @@ import '../styles/app.scss'
|
||||||
import { t } from '../utils/intl'
|
import { t } from '../utils/intl'
|
||||||
|
|
||||||
const lang = Astro.url.searchParams.get('lang') || 'ru'
|
const lang = Astro.url.searchParams.get('lang') || 'ru'
|
||||||
// console.log('[layout] server locale is', lang)
|
console.log('[layout] server locale is', lang)
|
||||||
setLocale(lang)
|
setLocale(lang)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
|
@ -15,5 +15,5 @@ Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate'
|
||||||
---
|
---
|
||||||
|
|
||||||
<Zine>
|
<Zine>
|
||||||
<Root articles={articles} author={author} client:load />
|
<Root authorArticles={articles} author={author} client:load />
|
||||||
</Zine>
|
</Zine>
|
||||||
|
|
|
@ -8,10 +8,8 @@ const authors = await apiClient.getAllAuthors()
|
||||||
|
|
||||||
const { pathname, search } = Astro.url
|
const { pathname, search } = Astro.url
|
||||||
initRouter(pathname, search)
|
initRouter(pathname, search)
|
||||||
|
|
||||||
Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate')
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<Zine>
|
<Zine>
|
||||||
<Root authors={authors} client:load />
|
<Root allAuthors={authors} client:load />
|
||||||
</Zine>
|
</Zine>
|
||||||
|
|
|
@ -12,5 +12,5 @@ const articles = await apiClient.getRecentArticles({ limit: 50 })
|
||||||
---
|
---
|
||||||
|
|
||||||
<Zine>
|
<Zine>
|
||||||
<Root articles={articles} client:load />
|
<Root feedArticles={articles} client:load />
|
||||||
</Zine>
|
</Zine>
|
||||||
|
|
|
@ -15,6 +15,6 @@ Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate'
|
||||||
---
|
---
|
||||||
|
|
||||||
<Zine>
|
<Zine>
|
||||||
<Root randomTopics={randomTopics} articles={articles} client:load />
|
<Root randomTopics={randomTopics} homeArticles={articles} client:load />
|
||||||
</Zine>
|
</Zine>
|
||||||
|
|
||||||
|
|
|
@ -16,5 +16,5 @@ Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate'
|
||||||
---
|
---
|
||||||
|
|
||||||
<Zine>
|
<Zine>
|
||||||
<Root articles={articles} topic={topic} client:load />
|
<Root topicArticles={articles} topic={topic} client:load />
|
||||||
</Zine>
|
</Zine>
|
||||||
|
|
|
@ -6,13 +6,11 @@ import { initRouter } from '../stores/router'
|
||||||
|
|
||||||
const topics = await apiClient.getAllTopics()
|
const topics = await apiClient.getAllTopics()
|
||||||
|
|
||||||
Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate')
|
|
||||||
|
|
||||||
const { pathname, search } = Astro.url
|
const { pathname, search } = Astro.url
|
||||||
initRouter(pathname, search)
|
initRouter(pathname, search)
|
||||||
---
|
---
|
||||||
|
|
||||||
<Zine>
|
<Zine>
|
||||||
<Root topics={topics} client:load />
|
<Root allTopics={topics} client:load />
|
||||||
</Zine>
|
</Zine>
|
||||||
|
|
||||||
|
|
|
@ -151,6 +151,22 @@ export const loadPublishedArticles = async ({
|
||||||
addSortedArticles(newArticles)
|
addSortedArticles(newArticles)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const loadArticlesForAuthors = async ({ authorSlugs }: { authorSlugs: string[] }): Promise<void> => {
|
||||||
|
const articles = await apiClient.getArticlesForAuthors({ authorSlugs, limit: 50 })
|
||||||
|
addArticles(articles)
|
||||||
|
setSortedArticles(articles)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const loadArticlesForTopics = async ({ topicSlugs }: { topicSlugs: string[] }): Promise<void> => {
|
||||||
|
const articles = await apiClient.getArticlesForTopics({ topicSlugs, limit: 50 })
|
||||||
|
addArticles(articles)
|
||||||
|
setSortedArticles(articles)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const resetSortedArticles = () => {
|
||||||
|
setSortedArticles([])
|
||||||
|
}
|
||||||
|
|
||||||
export const loadTopMonthArticles = async (): Promise<void> => {
|
export const loadTopMonthArticles = async (): Promise<void> => {
|
||||||
const articles = await apiClient.getTopMonthArticles()
|
const articles = await apiClient.getTopMonthArticles()
|
||||||
addArticles(articles)
|
addArticles(articles)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { apiClient } from '../../utils/apiClient'
|
import { apiClient } from '../../utils/apiClient'
|
||||||
import type { Author } from '../../graphql/types.gen'
|
import type { Author } from '../../graphql/types.gen'
|
||||||
import { byCreated } from '../../utils/sortby'
|
import { byCreated, byStat, byTopicStatDesc } from '../../utils/sortby'
|
||||||
|
|
||||||
import { getLogger } from '../../utils/logger'
|
import { getLogger } from '../../utils/logger'
|
||||||
import { createSignal } from 'solid-js'
|
import { createSignal } from 'solid-js'
|
||||||
|
@ -8,9 +8,11 @@ import { createLazyMemo } from '@solid-primitives/memo'
|
||||||
|
|
||||||
const log = getLogger('authors store')
|
const log = getLogger('authors store')
|
||||||
|
|
||||||
export type AuthorsSortBy = 'created' | 'name'
|
export type AuthorsSortBy = 'shouts' | 'name' | 'rating'
|
||||||
|
|
||||||
const [sortAllBy, setSortAllBy] = createSignal<AuthorsSortBy>('created')
|
const [sortAllBy, setSortAllBy] = createSignal<AuthorsSortBy>('shouts')
|
||||||
|
|
||||||
|
export { setSortAllBy }
|
||||||
|
|
||||||
const [authorEntities, setAuthorEntities] = createSignal<{ [authorSlug: string]: Author }>({})
|
const [authorEntities, setAuthorEntities] = createSignal<{ [authorSlug: string]: Author }>({})
|
||||||
const [authorsByTopic, setAuthorsByTopic] = createSignal<{ [topicSlug: string]: Author[] }>({})
|
const [authorsByTopic, setAuthorsByTopic] = createSignal<{ [topicSlug: string]: Author[] }>({})
|
||||||
|
@ -18,16 +20,21 @@ const [authorsByTopic, setAuthorsByTopic] = createSignal<{ [topicSlug: string]:
|
||||||
const sortedAuthors = createLazyMemo(() => {
|
const sortedAuthors = createLazyMemo(() => {
|
||||||
const authors = Object.values(authorEntities())
|
const authors = Object.values(authorEntities())
|
||||||
switch (sortAllBy()) {
|
switch (sortAllBy()) {
|
||||||
case 'created': {
|
// case 'created': {
|
||||||
// log.debug('sorted by created')
|
// log.debug('sorted by created')
|
||||||
authors.sort(byCreated)
|
// authors.sort(byCreated)
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
case 'rating':
|
||||||
|
// TODO:
|
||||||
break
|
break
|
||||||
}
|
case 'shouts':
|
||||||
case 'name': {
|
// TODO:
|
||||||
// log.debug('sorted by name')
|
break
|
||||||
|
case 'name':
|
||||||
|
log.debug('sorted by name')
|
||||||
authors.sort((a, b) => a.name.localeCompare(b.name))
|
authors.sort((a, b) => a.name.localeCompare(b.name))
|
||||||
break
|
break
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return authors
|
return authors
|
||||||
})
|
})
|
||||||
|
@ -46,6 +53,13 @@ const addAuthors = (authors: Author[]) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const loadAuthor = async ({ slug }: { slug: string }): Promise<void> => {
|
||||||
|
// TODO:
|
||||||
|
const articles = await apiClient.getArticlesForAuthors({ authorSlugs: [slug], limit: 1 })
|
||||||
|
const author = articles[0].authors.find((a) => a.slug === slug)
|
||||||
|
addAuthors([author])
|
||||||
|
}
|
||||||
|
|
||||||
export const addAuthorsByTopic = (newAuthorsByTopic: { [topicSlug: string]: Author[] }) => {
|
export const addAuthorsByTopic = (newAuthorsByTopic: { [topicSlug: string]: Author[] }) => {
|
||||||
const allAuthors = Object.values(newAuthorsByTopic).flat()
|
const allAuthors = Object.values(newAuthorsByTopic).flat()
|
||||||
addAuthors(allAuthors)
|
addAuthors(allAuthors)
|
||||||
|
|
|
@ -92,6 +92,18 @@ export const loadAllTopics = async (): Promise<void> => {
|
||||||
addTopics(topics)
|
addTopics(topics)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const loadRandomTopics = async (): Promise<void> => {
|
||||||
|
const topics = await apiClient.getRandomTopics({ amount: 12 })
|
||||||
|
setRandomTopics(topics)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const loadTopic = async ({ slug }: { slug: string }): Promise<void> => {
|
||||||
|
// TODO:
|
||||||
|
const articles = await apiClient.getArticlesForTopics({ topicSlugs: [slug], limit: 1 })
|
||||||
|
const topic = articles[0].topics.find(({ slug: topicSlug }) => topicSlug === slug)
|
||||||
|
addTopics([topic])
|
||||||
|
}
|
||||||
|
|
||||||
type InitialState = {
|
type InitialState = {
|
||||||
topics?: Topic[]
|
topics?: Topic[]
|
||||||
randomTopics?: Topic[]
|
randomTopics?: Topic[]
|
||||||
|
|
|
@ -176,6 +176,10 @@ export const apiClient = {
|
||||||
})
|
})
|
||||||
.toPromise()
|
.toPromise()
|
||||||
|
|
||||||
|
if (response.error) {
|
||||||
|
log.error('getArticlesForTopics', response.error)
|
||||||
|
}
|
||||||
|
|
||||||
return response.data.shoutsByTopics
|
return response.data.shoutsByTopics
|
||||||
},
|
},
|
||||||
getArticlesForAuthors: async ({
|
getArticlesForAuthors: async ({
|
||||||
|
@ -195,6 +199,10 @@ export const apiClient = {
|
||||||
})
|
})
|
||||||
.toPromise()
|
.toPromise()
|
||||||
|
|
||||||
|
if (response.error) {
|
||||||
|
log.error('getArticlesForAuthors', response.error)
|
||||||
|
}
|
||||||
|
|
||||||
return response.data.shoutsByAuthors
|
return response.data.shoutsByAuthors
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -217,6 +225,10 @@ export const apiClient = {
|
||||||
getPublishedArticles: async ({ limit = FEED_SIZE, offset }: { limit?: number; offset?: number }) => {
|
getPublishedArticles: async ({ limit = FEED_SIZE, offset }: { limit?: number; offset?: number }) => {
|
||||||
const response = await publicGraphQLClient.query(articlesRecentPublished, { limit, offset }).toPromise()
|
const response = await publicGraphQLClient.query(articlesRecentPublished, { limit, offset }).toPromise()
|
||||||
|
|
||||||
|
if (response.error) {
|
||||||
|
log.error('getPublishedArticles', response.error)
|
||||||
|
}
|
||||||
|
|
||||||
return response.data.recentPublished
|
return response.data.recentPublished
|
||||||
},
|
},
|
||||||
getAllTopics: async () => {
|
getAllTopics: async () => {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user