Expo random top articles (#331)
* WIP * done --------- Co-authored-by: Igor Lobanov <igor.lobanov@onetwotrip.com>
This commit is contained in:
parent
f9b9d129dd
commit
e5846deab7
|
@ -50,7 +50,6 @@ const pagesMap: Record<keyof typeof ROUTES, Component<PageProps>> = {
|
||||||
authorAbout: AuthorPage,
|
authorAbout: AuthorPage,
|
||||||
inbox: InboxPage,
|
inbox: InboxPage,
|
||||||
expo: ExpoPage,
|
expo: ExpoPage,
|
||||||
expoLayout: ExpoPage,
|
|
||||||
connect: ConnectPage,
|
connect: ConnectPage,
|
||||||
create: CreatePage,
|
create: CreatePage,
|
||||||
edit: EditPage,
|
edit: EditPage,
|
||||||
|
|
|
@ -3,49 +3,89 @@ import { clsx } from 'clsx'
|
||||||
import { createEffect, createMemo, createSignal, For, on, onCleanup, onMount, Show } from 'solid-js'
|
import { createEffect, createMemo, createSignal, For, on, onCleanup, onMount, Show } from 'solid-js'
|
||||||
|
|
||||||
import { useLocalize } from '../../../context/localize'
|
import { useLocalize } from '../../../context/localize'
|
||||||
import { LoadShoutsOptions, Shout } from '../../../graphql/types.gen'
|
import { LoadRandomTopShoutsParams, LoadShoutsOptions, Shout } from '../../../graphql/types.gen'
|
||||||
import { LayoutType } from '../../../pages/types'
|
import { LayoutType } from '../../../pages/types'
|
||||||
import { router, useRouter } from '../../../stores/router'
|
import { router } from '../../../stores/router'
|
||||||
import { loadShouts, resetSortedArticles, useArticlesStore } from '../../../stores/zine/articles'
|
import { loadShouts, resetSortedArticles, useArticlesStore } from '../../../stores/zine/articles'
|
||||||
|
import { apiClient } from '../../../utils/apiClient'
|
||||||
|
import { getServerDate } from '../../../utils/getServerDate'
|
||||||
import { restoreScrollPosition, saveScrollPosition } from '../../../utils/scroll'
|
import { restoreScrollPosition, saveScrollPosition } from '../../../utils/scroll'
|
||||||
import { splitToPages } from '../../../utils/splitToPages'
|
import { splitToPages } from '../../../utils/splitToPages'
|
||||||
import { Button } from '../../_shared/Button'
|
import { Button } from '../../_shared/Button'
|
||||||
import { ConditionalWrapper } from '../../_shared/ConditionalWrapper'
|
import { ConditionalWrapper } from '../../_shared/ConditionalWrapper'
|
||||||
import { Loading } from '../../_shared/Loading'
|
import { Loading } from '../../_shared/Loading'
|
||||||
|
import { ArticleCardSwiper } from '../../_shared/SolidSwiper/ArticleCardSwiper'
|
||||||
import { ArticleCard } from '../../Feed/ArticleCard'
|
import { ArticleCard } from '../../Feed/ArticleCard'
|
||||||
|
|
||||||
import styles from './Expo.module.scss'
|
import styles from './Expo.module.scss'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
shouts: Shout[]
|
shouts: Shout[]
|
||||||
|
layout: LayoutType
|
||||||
}
|
}
|
||||||
export const PRERENDERED_ARTICLES_COUNT = 28
|
|
||||||
|
export const PRERENDERED_ARTICLES_COUNT = 32
|
||||||
const LOAD_MORE_PAGE_SIZE = 16
|
const LOAD_MORE_PAGE_SIZE = 16
|
||||||
|
|
||||||
export const Expo = (props: Props) => {
|
export const Expo = (props: Props) => {
|
||||||
const [isLoaded, setIsLoaded] = createSignal<boolean>(Boolean(props.shouts))
|
const [isLoaded, setIsLoaded] = createSignal<boolean>(Boolean(props.shouts))
|
||||||
const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false)
|
const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false)
|
||||||
|
|
||||||
|
const [randomTopArticles, setRandomTopArticles] = createSignal<Shout[]>([])
|
||||||
|
const [randomTopMonthArticles, setRandomTopMonthArticles] = createSignal<Shout[]>([])
|
||||||
|
|
||||||
const { t } = useLocalize()
|
const { t } = useLocalize()
|
||||||
const { page: getPage } = useRouter()
|
|
||||||
const getLayout = createMemo<LayoutType>(() => getPage().params['layout'] as LayoutType)
|
|
||||||
const { sortedArticles } = useArticlesStore({
|
const { sortedArticles } = useArticlesStore({
|
||||||
shouts: isLoaded() ? props.shouts : [],
|
shouts: isLoaded() ? props.shouts : [],
|
||||||
})
|
})
|
||||||
|
|
||||||
const loadMore = async (count) => {
|
const loadMore = async (count: number) => {
|
||||||
saveScrollPosition()
|
saveScrollPosition()
|
||||||
const options: LoadShoutsOptions = {
|
const options: LoadShoutsOptions = {
|
||||||
limit: count,
|
limit: count,
|
||||||
offset: sortedArticles().length,
|
offset: sortedArticles().length,
|
||||||
}
|
}
|
||||||
|
|
||||||
options.filters = getLayout() ? { layout: getLayout() } : { excludeLayout: 'article' }
|
options.filters = props.layout ? { layout: props.layout } : { excludeLayout: 'article' }
|
||||||
|
|
||||||
const { hasMore } = await loadShouts(options)
|
const { hasMore } = await loadShouts(options)
|
||||||
setIsLoadMoreButtonVisible(hasMore)
|
setIsLoadMoreButtonVisible(hasMore)
|
||||||
restoreScrollPosition()
|
restoreScrollPosition()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const loadRandomTopArticles = async () => {
|
||||||
|
const params: LoadRandomTopShoutsParams = {
|
||||||
|
filters: {
|
||||||
|
visibility: 'public',
|
||||||
|
},
|
||||||
|
limit: 10,
|
||||||
|
fromRandomCount: 100,
|
||||||
|
}
|
||||||
|
params.filters = props.layout ? { layout: props.layout } : { excludeLayout: 'article' }
|
||||||
|
|
||||||
|
const result = await apiClient.getRandomTopShouts(params)
|
||||||
|
setRandomTopArticles(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadRandomTopMonthArticles = async () => {
|
||||||
|
const now = new Date()
|
||||||
|
const fromDate = getServerDate(new Date(now.setMonth(now.getMonth() - 1)))
|
||||||
|
|
||||||
|
const params: LoadRandomTopShoutsParams = {
|
||||||
|
filters: {
|
||||||
|
visibility: 'public',
|
||||||
|
fromDate,
|
||||||
|
},
|
||||||
|
limit: 10,
|
||||||
|
fromRandomCount: 10,
|
||||||
|
}
|
||||||
|
params.filters = props.layout ? { layout: props.layout } : { excludeLayout: 'article' }
|
||||||
|
|
||||||
|
const result = await apiClient.getRandomTopShouts(params)
|
||||||
|
setRandomTopMonthArticles(result)
|
||||||
|
}
|
||||||
|
|
||||||
const pages = createMemo<Shout[][]>(() =>
|
const pages = createMemo<Shout[][]>(() =>
|
||||||
splitToPages(sortedArticles(), PRERENDERED_ARTICLES_COUNT, LOAD_MORE_PAGE_SIZE),
|
splitToPages(sortedArticles(), PRERENDERED_ARTICLES_COUNT, LOAD_MORE_PAGE_SIZE),
|
||||||
)
|
)
|
||||||
|
@ -65,12 +105,21 @@ export const Expo = (props: Props) => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
loadRandomTopArticles()
|
||||||
|
loadRandomTopMonthArticles()
|
||||||
|
})
|
||||||
|
|
||||||
createEffect(
|
createEffect(
|
||||||
on(
|
on(
|
||||||
() => getLayout(),
|
() => props.layout,
|
||||||
() => {
|
() => {
|
||||||
resetSortedArticles()
|
resetSortedArticles()
|
||||||
|
setRandomTopArticles([])
|
||||||
|
setRandomTopMonthArticles([])
|
||||||
loadMore(PRERENDERED_ARTICLES_COUNT + LOAD_MORE_PAGE_SIZE)
|
loadMore(PRERENDERED_ARTICLES_COUNT + LOAD_MORE_PAGE_SIZE)
|
||||||
|
loadRandomTopArticles()
|
||||||
|
loadRandomTopMonthArticles()
|
||||||
},
|
},
|
||||||
{ defer: true },
|
{ defer: true },
|
||||||
),
|
),
|
||||||
|
@ -89,49 +138,49 @@ export const Expo = (props: Props) => {
|
||||||
<Show when={sortedArticles().length > 0} fallback={<Loading />}>
|
<Show when={sortedArticles().length > 0} fallback={<Loading />}>
|
||||||
<div class="wide-container">
|
<div class="wide-container">
|
||||||
<ul class={clsx('view-switcher', styles.navigation)}>
|
<ul class={clsx('view-switcher', styles.navigation)}>
|
||||||
<li class={clsx({ 'view-switcher__item--selected': !getLayout() })}>
|
<li class={clsx({ 'view-switcher__item--selected': !props.layout })}>
|
||||||
<ConditionalWrapper
|
<ConditionalWrapper
|
||||||
condition={Boolean(getLayout())}
|
condition={Boolean(props.layout)}
|
||||||
wrapper={(children) => <a href={getPagePath(router, 'expo')}>{children}</a>}
|
wrapper={(children) => <a href={getPagePath(router, 'expo', { layout: '' })}>{children}</a>}
|
||||||
>
|
>
|
||||||
<span class={clsx('linkReplacement')}>{t('All')}</span>
|
<span class={clsx('linkReplacement')}>{t('All')}</span>
|
||||||
</ConditionalWrapper>
|
</ConditionalWrapper>
|
||||||
</li>
|
</li>
|
||||||
<li class={clsx({ 'view-switcher__item--selected': getLayout() === 'literature' })}>
|
<li class={clsx({ 'view-switcher__item--selected': props.layout === 'literature' })}>
|
||||||
<ConditionalWrapper
|
<ConditionalWrapper
|
||||||
condition={getLayout() !== 'literature'}
|
condition={props.layout !== 'literature'}
|
||||||
wrapper={(children) => (
|
wrapper={(children) => (
|
||||||
<a href={getPagePath(router, 'expoLayout', { layout: 'literature' })}>{children}</a>
|
<a href={getPagePath(router, 'expo', { layout: 'literature' })}>{children}</a>
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<span class={clsx('linkReplacement')}>{t('Literature')}</span>
|
<span class={clsx('linkReplacement')}>{t('Literature')}</span>
|
||||||
</ConditionalWrapper>
|
</ConditionalWrapper>
|
||||||
</li>
|
</li>
|
||||||
<li class={clsx({ 'view-switcher__item--selected': getLayout() === 'music' })}>
|
<li class={clsx({ 'view-switcher__item--selected': props.layout === 'music' })}>
|
||||||
<ConditionalWrapper
|
<ConditionalWrapper
|
||||||
condition={getLayout() !== 'music'}
|
condition={props.layout !== 'music'}
|
||||||
wrapper={(children) => (
|
wrapper={(children) => (
|
||||||
<a href={getPagePath(router, 'expoLayout', { layout: 'music' })}>{children}</a>
|
<a href={getPagePath(router, 'expo', { layout: 'music' })}>{children}</a>
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<span class={clsx('linkReplacement')}>{t('Music')}</span>
|
<span class={clsx('linkReplacement')}>{t('Music')}</span>
|
||||||
</ConditionalWrapper>
|
</ConditionalWrapper>
|
||||||
</li>
|
</li>
|
||||||
<li class={clsx({ 'view-switcher__item--selected': getLayout() === 'image' })}>
|
<li class={clsx({ 'view-switcher__item--selected': props.layout === 'image' })}>
|
||||||
<ConditionalWrapper
|
<ConditionalWrapper
|
||||||
condition={getLayout() !== 'image'}
|
condition={props.layout !== 'image'}
|
||||||
wrapper={(children) => (
|
wrapper={(children) => (
|
||||||
<a href={getPagePath(router, 'expoLayout', { layout: 'image' })}>{children}</a>
|
<a href={getPagePath(router, 'expo', { layout: 'image' })}>{children}</a>
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<span class={clsx('linkReplacement')}>{t('Gallery')}</span>
|
<span class={clsx('linkReplacement')}>{t('Gallery')}</span>
|
||||||
</ConditionalWrapper>
|
</ConditionalWrapper>
|
||||||
</li>
|
</li>
|
||||||
<li class={clsx({ 'view-switcher__item--selected': getLayout() === 'video' })}>
|
<li class={clsx({ 'view-switcher__item--selected': props.layout === 'video' })}>
|
||||||
<ConditionalWrapper
|
<ConditionalWrapper
|
||||||
condition={getLayout() !== 'video'}
|
condition={props.layout !== 'video'}
|
||||||
wrapper={(children) => (
|
wrapper={(children) => (
|
||||||
<a href={getPagePath(router, 'expoLayout', { layout: 'video' })}>{children}</a>
|
<a href={getPagePath(router, 'expo', { layout: 'video' })}>{children}</a>
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<span class={clsx('cursorPointer linkReplacement')}>{t('Video')}</span>
|
<span class={clsx('cursorPointer linkReplacement')}>{t('Video')}</span>
|
||||||
|
@ -139,7 +188,7 @@ export const Expo = (props: Props) => {
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<For each={sortedArticles().slice(0, PRERENDERED_ARTICLES_COUNT)}>
|
<For each={sortedArticles().slice(0, PRERENDERED_ARTICLES_COUNT / 2)}>
|
||||||
{(shout) => (
|
{(shout) => (
|
||||||
<div class="col-md-6 mt-md-5 col-sm-8 mt-sm-3">
|
<div class="col-md-6 mt-md-5 col-sm-8 mt-sm-3">
|
||||||
<ArticleCard
|
<ArticleCard
|
||||||
|
@ -150,6 +199,23 @@ export const Expo = (props: Props) => {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</For>
|
</For>
|
||||||
|
<Show when={randomTopArticles().length > 0} keyed={true}>
|
||||||
|
<ArticleCardSwiper title={t('Favorite')} slides={randomTopArticles()} />
|
||||||
|
</Show>
|
||||||
|
<For each={sortedArticles().slice(PRERENDERED_ARTICLES_COUNT / 2, PRERENDERED_ARTICLES_COUNT)}>
|
||||||
|
{(shout) => (
|
||||||
|
<div class="col-md-6 mt-md-5 col-sm-8 mt-sm-3">
|
||||||
|
<ArticleCard
|
||||||
|
article={shout}
|
||||||
|
settings={{ nodate: true, nosubtitle: true, noAuthorLink: true }}
|
||||||
|
desktopCoverSize="XS"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
<Show when={randomTopMonthArticles().length > 0} keyed={true}>
|
||||||
|
<ArticleCardSwiper title={t('Top month articles')} slides={randomTopMonthArticles()} />
|
||||||
|
</Show>
|
||||||
<For each={pages()}>
|
<For each={pages()}>
|
||||||
{(page) => (
|
{(page) => (
|
||||||
<For each={page}>
|
<For each={page}>
|
||||||
|
|
|
@ -36,10 +36,7 @@ export const SearchView = (props: Props) => {
|
||||||
const loadMore = async () => {
|
const loadMore = async () => {
|
||||||
saveScrollPosition()
|
saveScrollPosition()
|
||||||
const { hasMore } = await loadShouts({
|
const { hasMore } = await loadShouts({
|
||||||
filters: {
|
filters: {},
|
||||||
title: query(),
|
|
||||||
body: query(),
|
|
||||||
},
|
|
||||||
offset: offset(),
|
offset: offset(),
|
||||||
limit: LOAD_MORE_PAGE_SIZE,
|
limit: LOAD_MORE_PAGE_SIZE,
|
||||||
})
|
})
|
||||||
|
|
46
src/graphql/query/articles-load-random-top.ts
Normal file
46
src/graphql/query/articles-load-random-top.ts
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
import { gql } from '@urql/core'
|
||||||
|
|
||||||
|
export default gql`
|
||||||
|
query LoadRandomTopShoutsQuery($params: LoadRandomTopShoutsParams) {
|
||||||
|
loadRandomTopShouts(params: $params) {
|
||||||
|
id
|
||||||
|
title
|
||||||
|
lead
|
||||||
|
description
|
||||||
|
subtitle
|
||||||
|
slug
|
||||||
|
layout
|
||||||
|
cover
|
||||||
|
lead
|
||||||
|
# community
|
||||||
|
mainTopic
|
||||||
|
topics {
|
||||||
|
id
|
||||||
|
title
|
||||||
|
body
|
||||||
|
slug
|
||||||
|
stat {
|
||||||
|
shouts
|
||||||
|
authors
|
||||||
|
followers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
authors {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
userpic
|
||||||
|
createdAt
|
||||||
|
bio
|
||||||
|
}
|
||||||
|
createdAt
|
||||||
|
publishedAt
|
||||||
|
stat {
|
||||||
|
viewed
|
||||||
|
reacted
|
||||||
|
rating
|
||||||
|
commented
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
|
@ -116,14 +116,19 @@ export enum FollowingEntity {
|
||||||
Topic = 'TOPIC',
|
Topic = 'TOPIC',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type LoadRandomTopShoutsParams = {
|
||||||
|
filters?: InputMaybe<LoadShoutsFilters>
|
||||||
|
fromRandomCount?: InputMaybe<Scalars['Int']['input']>
|
||||||
|
limit: Scalars['Int']['input']
|
||||||
|
}
|
||||||
|
|
||||||
export type LoadShoutsFilters = {
|
export type LoadShoutsFilters = {
|
||||||
author?: InputMaybe<Scalars['String']['input']>
|
author?: InputMaybe<Scalars['String']['input']>
|
||||||
body?: InputMaybe<Scalars['String']['input']>
|
|
||||||
days?: InputMaybe<Scalars['Int']['input']>
|
|
||||||
excludeLayout?: InputMaybe<Scalars['String']['input']>
|
excludeLayout?: InputMaybe<Scalars['String']['input']>
|
||||||
|
fromDate?: InputMaybe<Scalars['String']['input']>
|
||||||
layout?: InputMaybe<Scalars['String']['input']>
|
layout?: InputMaybe<Scalars['String']['input']>
|
||||||
reacted?: InputMaybe<Scalars['Boolean']['input']>
|
reacted?: InputMaybe<Scalars['Boolean']['input']>
|
||||||
title?: InputMaybe<Scalars['String']['input']>
|
toDate?: InputMaybe<Scalars['String']['input']>
|
||||||
topic?: InputMaybe<Scalars['String']['input']>
|
topic?: InputMaybe<Scalars['String']['input']>
|
||||||
visibility?: InputMaybe<Scalars['String']['input']>
|
visibility?: InputMaybe<Scalars['String']['input']>
|
||||||
}
|
}
|
||||||
|
@ -367,6 +372,7 @@ export type Query = {
|
||||||
loadMessagesBy: Result
|
loadMessagesBy: Result
|
||||||
loadMySubscriptions?: Maybe<MySubscriptionsQueryResult>
|
loadMySubscriptions?: Maybe<MySubscriptionsQueryResult>
|
||||||
loadNotifications: NotificationsQueryResult
|
loadNotifications: NotificationsQueryResult
|
||||||
|
loadRandomTopShouts: Array<Maybe<Shout>>
|
||||||
loadReactionsBy: Array<Maybe<Reaction>>
|
loadReactionsBy: Array<Maybe<Reaction>>
|
||||||
loadRecipients: Result
|
loadRecipients: Result
|
||||||
loadShout?: Maybe<Shout>
|
loadShout?: Maybe<Shout>
|
||||||
|
@ -419,6 +425,10 @@ export type QueryLoadNotificationsArgs = {
|
||||||
params: NotificationsQueryParams
|
params: NotificationsQueryParams
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type QueryLoadRandomTopShoutsArgs = {
|
||||||
|
params?: InputMaybe<LoadRandomTopShoutsParams>
|
||||||
|
}
|
||||||
|
|
||||||
export type QueryLoadReactionsByArgs = {
|
export type QueryLoadReactionsByArgs = {
|
||||||
by: ReactionBy
|
by: ReactionBy
|
||||||
limit?: InputMaybe<Scalars['Int']['input']>
|
limit?: InputMaybe<Scalars['Int']['input']>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { ROUTES } from '../../stores/router'
|
import { ROUTES } from '../../stores/router'
|
||||||
import { getServerRoute } from '../../utils/getServerRoute'
|
import { getServerRoute } from '../../utils/getServerRoute'
|
||||||
|
|
||||||
export default getServerRoute(ROUTES.expo)
|
// yes, it's a hack
|
||||||
|
export default getServerRoute(ROUTES.expo.replace(':layout?', '*'))
|
||||||
|
|
|
@ -4,12 +4,16 @@ import type { PageProps } from '../types'
|
||||||
import { PRERENDERED_ARTICLES_COUNT } from '../../components/Views/Expo/Expo'
|
import { PRERENDERED_ARTICLES_COUNT } from '../../components/Views/Expo/Expo'
|
||||||
import { apiClient } from '../../utils/apiClient'
|
import { apiClient } from '../../utils/apiClient'
|
||||||
|
|
||||||
export const onBeforeRender = async (_pageContext: PageContext) => {
|
export const onBeforeRender = async (pageContext: PageContext) => {
|
||||||
|
const { layout } = pageContext.routeParams
|
||||||
|
|
||||||
const expoShouts = await apiClient.getShouts({
|
const expoShouts = await apiClient.getShouts({
|
||||||
filters: { excludeLayout: 'article' },
|
filters: layout ? { layout } : { excludeLayout: 'article' },
|
||||||
limit: PRERENDERED_ARTICLES_COUNT,
|
limit: PRERENDERED_ARTICLES_COUNT,
|
||||||
})
|
})
|
||||||
|
|
||||||
const pageProps: PageProps = { expoShouts, seo: { title: '' } }
|
const pageProps: PageProps = { expoShouts, seo: { title: '' } }
|
||||||
|
|
||||||
return {
|
return {
|
||||||
pageContext: {
|
pageContext: {
|
||||||
pageProps,
|
pageProps,
|
||||||
|
|
|
@ -37,7 +37,7 @@ export const ExpoPage = (props: PageProps) => {
|
||||||
return (
|
return (
|
||||||
<PageLayout withPadding={true} zeroBottomPadding={true} title={title()}>
|
<PageLayout withPadding={true} zeroBottomPadding={true} title={title()}>
|
||||||
<Topics />
|
<Topics />
|
||||||
<Expo shouts={props.expoShouts} />
|
<Expo shouts={props.expoShouts} layout={getLayout()} />
|
||||||
</PageLayout>
|
</PageLayout>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
import { ROUTES } from '../../stores/router'
|
|
||||||
import { getServerRoute } from '../../utils/getServerRoute'
|
|
||||||
|
|
||||||
export default getServerRoute(ROUTES.expoLayout)
|
|
|
@ -1,21 +0,0 @@
|
||||||
import type { PageContext } from '../../renderer/types'
|
|
||||||
import type { PageProps } from '../types'
|
|
||||||
|
|
||||||
import { PRERENDERED_ARTICLES_COUNT } from '../../components/Views/Expo/Expo'
|
|
||||||
import { apiClient } from '../../utils/apiClient'
|
|
||||||
|
|
||||||
export const onBeforeRender = async (pageContext: PageContext) => {
|
|
||||||
const { layout } = pageContext.routeParams
|
|
||||||
const expoShouts = await apiClient.getShouts({
|
|
||||||
filters: { layout: layout },
|
|
||||||
limit: PRERENDERED_ARTICLES_COUNT,
|
|
||||||
})
|
|
||||||
|
|
||||||
const pageProps: PageProps = { expoShouts, seo: { title: '' } }
|
|
||||||
|
|
||||||
return {
|
|
||||||
pageContext: {
|
|
||||||
pageProps,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,7 +6,7 @@ import { apiClient } from '../utils/apiClient'
|
||||||
export const onBeforeRender = async (pageContext: PageContext) => {
|
export const onBeforeRender = async (pageContext: PageContext) => {
|
||||||
const { q } = pageContext.routeParams
|
const { q } = pageContext.routeParams
|
||||||
|
|
||||||
const searchResults = await apiClient.getShouts({ filters: { title: q, body: q }, limit: 50 })
|
const searchResults = await apiClient.getShouts({ filters: {}, limit: 50 })
|
||||||
|
|
||||||
const pageProps: PageProps = { searchResults, seo: { title: '' } }
|
const pageProps: PageProps = { searchResults, seo: { title: '' } }
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ export const SearchPage = (props: PageProps) => {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await loadShouts({ filters: { title: q(), body: q() }, limit: 50, offset: 0 })
|
await loadShouts({ filters: {}, limit: 50, offset: 0 })
|
||||||
setIsLoaded(true)
|
setIsLoaded(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -37,8 +37,7 @@ export const ROUTES = {
|
||||||
projects: '/about/projects',
|
projects: '/about/projects',
|
||||||
termsOfUse: '/about/terms-of-use',
|
termsOfUse: '/about/terms-of-use',
|
||||||
thanks: '/about/thanks',
|
thanks: '/about/thanks',
|
||||||
expo: '/expo',
|
expo: '/expo/:layout?',
|
||||||
expoLayout: '/expo/:layout',
|
|
||||||
profileSettings: '/profile/settings',
|
profileSettings: '/profile/settings',
|
||||||
profileSecurity: '/profile/security',
|
profileSecurity: '/profile/security',
|
||||||
profileSubscriptions: '/profile/subscriptions',
|
profileSubscriptions: '/profile/subscriptions',
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { createLazyMemo } from '@solid-primitives/memo'
|
||||||
import { createSignal } from 'solid-js'
|
import { createSignal } from 'solid-js'
|
||||||
|
|
||||||
import { apiClient } from '../../utils/apiClient'
|
import { apiClient } from '../../utils/apiClient'
|
||||||
|
import { getServerDate } from '../../utils/getServerDate'
|
||||||
import { byStat } from '../../utils/sortby'
|
import { byStat } from '../../utils/sortby'
|
||||||
|
|
||||||
import { addAuthorsByTopic } from './authors'
|
import { addAuthorsByTopic } from './authors'
|
||||||
|
@ -193,11 +194,13 @@ type InitialState = {
|
||||||
const TOP_MONTH_ARTICLES_COUNT = 10
|
const TOP_MONTH_ARTICLES_COUNT = 10
|
||||||
|
|
||||||
export const loadTopMonthArticles = async (): Promise<void> => {
|
export const loadTopMonthArticles = async (): Promise<void> => {
|
||||||
|
const now = new Date()
|
||||||
|
const fromDate = getServerDate(new Date(now.setMonth(now.getMonth() - 1)))
|
||||||
|
|
||||||
const articles = await apiClient.getShouts({
|
const articles = await apiClient.getShouts({
|
||||||
filters: {
|
filters: {
|
||||||
visibility: 'public',
|
visibility: 'public',
|
||||||
// TODO: replace with from, to
|
fromDate,
|
||||||
days: 30,
|
|
||||||
},
|
},
|
||||||
order_by: 'rating_stat',
|
order_by: 'rating_stat',
|
||||||
limit: TOP_MONTH_ARTICLES_COUNT,
|
limit: TOP_MONTH_ARTICLES_COUNT,
|
||||||
|
|
|
@ -19,6 +19,7 @@ import type {
|
||||||
NotificationsQueryParams,
|
NotificationsQueryParams,
|
||||||
NotificationsQueryResult,
|
NotificationsQueryResult,
|
||||||
MySubscriptionsQueryResult,
|
MySubscriptionsQueryResult,
|
||||||
|
LoadRandomTopShoutsParams,
|
||||||
} from '../graphql/types.gen'
|
} from '../graphql/types.gen'
|
||||||
|
|
||||||
import createArticle from '../graphql/mutation/article-create'
|
import createArticle from '../graphql/mutation/article-create'
|
||||||
|
@ -43,6 +44,8 @@ import { getToken, privateGraphQLClient } from '../graphql/privateGraphQLClient'
|
||||||
import { publicGraphQLClient } from '../graphql/publicGraphQLClient'
|
import { publicGraphQLClient } from '../graphql/publicGraphQLClient'
|
||||||
import shoutLoad from '../graphql/query/article-load'
|
import shoutLoad from '../graphql/query/article-load'
|
||||||
import shoutsLoadBy from '../graphql/query/articles-load-by'
|
import shoutsLoadBy from '../graphql/query/articles-load-by'
|
||||||
|
import shoutsLoadRandomTop from '../graphql/query/articles-load-random-top'
|
||||||
|
import articlesLoadRandomTop from '../graphql/query/articles-load-random-top'
|
||||||
import authCheckEmailQuery from '../graphql/query/auth-check-email'
|
import authCheckEmailQuery from '../graphql/query/auth-check-email'
|
||||||
import authLoginQuery from '../graphql/query/auth-login'
|
import authLoginQuery from '../graphql/query/auth-login'
|
||||||
import authorBySlug from '../graphql/query/author-by-slug'
|
import authorBySlug from '../graphql/query/author-by-slug'
|
||||||
|
@ -350,6 +353,15 @@ export const apiClient = {
|
||||||
return resp.data.loadShouts
|
return resp.data.loadShouts
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getRandomTopShouts: async (params: LoadRandomTopShoutsParams): Promise<Shout[]> => {
|
||||||
|
const resp = await publicGraphQLClient.query(articlesLoadRandomTop, { params }).toPromise()
|
||||||
|
if (resp.error) {
|
||||||
|
console.error(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp.data.loadRandomTopShouts
|
||||||
|
},
|
||||||
|
|
||||||
getMyFeed: async (options: LoadShoutsOptions) => {
|
getMyFeed: async (options: LoadShoutsOptions) => {
|
||||||
const resp = await privateGraphQLClient.query(myFeed, { options }).toPromise()
|
const resp = await privateGraphQLClient.query(myFeed, { options }).toPromise()
|
||||||
|
|
||||||
|
|
4
src/utils/getServerDate.ts
Normal file
4
src/utils/getServerDate.ts
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
export const getServerDate = (date: Date): string => {
|
||||||
|
// 2023-12-31
|
||||||
|
return date.toISOString().slice(0, 10)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user