home-tops

This commit is contained in:
Untone 2024-06-28 10:47:38 +03:00
parent ae207a02f2
commit 305bd23f9f
9 changed files with 59 additions and 37 deletions

View File

@ -4,6 +4,7 @@ import sassDts from 'vite-plugin-sass-dts'
const isVercel = Boolean(process?.env.VERCEL)
const isBun = Boolean(process.env.BUN)
export default defineConfig({
ssr: true,
server: {

View File

@ -6,11 +6,13 @@
},
"vcs": {
"defaultBranch": "dev",
"useIgnoreFile": true
"useIgnoreFile": true,
"enabled": true,
"clientKind": "git"
},
"organizeImports": {
"enabled": true,
"ignore": ["./api", "./gen"]
"ignore": ["./gen"]
},
"formatter": {
"indentStyle": "space",

View File

@ -62,8 +62,8 @@ const getTitleAndSubtitle = (
title: string
subtitle: string
} => {
let title = article.title
let subtitle: string = article.subtitle || ''
let title = article?.title || ''
let subtitle: string = article?.subtitle || ''
if (!subtitle) {
let titleParts = article.title?.split('. ') || []
@ -220,7 +220,7 @@ export const ArticleCard = (props: ArticleCardProps) => {
[styles.shoutCardTitlesContainerFeedMode]: props.settings?.isFeedMode
})}
>
<A href={`/article${props.article.slug}`}>
<A href={`/article${props.article?.slug || ''}`}>
<div class={styles.shoutCardTitle}>
<span class={styles.shoutCardLinkWrapper}>
<span class={styles.shoutCardLinkContainer} innerHTML={title} />
@ -229,7 +229,7 @@ export const ArticleCard = (props: ArticleCardProps) => {
<Show when={!props.settings?.nosubtitle && subtitle}>
<div class={styles.shoutCardSubtitle}>
<span class={styles.shoutCardLinkContainer} innerHTML={subtitle} />
<span class={styles.shoutCardLinkContainer} innerHTML={subtitle || ''} />
</div>
</Show>
</A>

View File

@ -3,7 +3,7 @@ import { For, Show, createEffect, createMemo, createSignal, on } from 'solid-js'
import { useAuthors } from '~/context/authors'
import { useLocalize } from '../../context/localize'
import { useTopics } from '../../context/topics'
import { Shout, Topic } from '../../graphql/schema/core.gen'
import { Author, Shout, Topic } from '../../graphql/schema/core.gen'
import { capitalize } from '../../utils/capitalize'
import { splitToPages } from '../../utils/splitToPages'
import Banner from '../Discours/Banner'
@ -38,7 +38,7 @@ export interface HomeViewProps {
export const HomeView = (props: HomeViewProps) => {
const { t } = useLocalize()
const { topAuthors } = useAuthors()
const { topAuthors, addAuthors } = useAuthors()
const { topTopics, randomTopic } = useTopics()
const [randomTopicArticles, setRandomTopicArticles] = createSignal<Shout[]>([])
createEffect(
@ -53,6 +53,9 @@ export const HomeView = (props: HomeViewProps) => {
})
const shouts = await shoutsByTopicLoader()
setRandomTopicArticles(shouts || [])
shouts?.forEach((s: Shout) => addAuthors((s?.authors || []) as Author[]))
props.featuredShouts?.forEach((s: Shout) => addAuthors((s?.authors || []) as Author[]))
props.topRatedShouts?.forEach((s: Shout) => addAuthors((s?.authors || []) as Author[]))
}
},
{ defer: true }
@ -84,7 +87,7 @@ export const HomeView = (props: HomeViewProps) => {
<Beside
beside={props.featuredShouts[9]}
title={t('Top authors')}
values={topAuthors()}
values={topAuthors?.() || []}
wrapper={'author'}
nodate={true}
/>
@ -96,7 +99,7 @@ export const HomeView = (props: HomeViewProps) => {
<Row1 article={props.featuredShouts[16]} nodate={true} />
<Row3 articles={props.featuredShouts.slice(17, 20)} nodate={true} />
<Row3
articles={props.topCommentedShouts.slice(0, 3)}
articles={props.topCommentedShouts?.slice(0, 3) || []}
header={<h2>{t('Top commented')}</h2>}
nodate={true}
/>

View File

@ -23,7 +23,8 @@ export const ArticleCardSwiper = (props: Props) => {
let mainSwipeRef: SwiperRef | null
onMount(async () => {
if (props.slides.length > 1) {
if (props.slides?.length > 1) {
console.debug(props.slides)
const { register } = await import('swiper/element/bundle')
register()
SwiperCore.use([Pagination, Navigation, Manipulation])
@ -34,16 +35,16 @@ export const ArticleCardSwiper = (props: Props) => {
<ShowOnlyOnClient>
<div
class={clsx({
[styles.Swiper]: props.slides.length > 1,
[styles.Swiper]: props.slides?.length > 1,
[styles.articleMode]: true,
[styles.ArticleCardSwiper]: props.slides.length > 1
[styles.ArticleCardSwiper]: props.slides?.length > 1
})}
>
<Show when={props.title}>
<h2 class={styles.sliderTitle}>{props.title}</h2>
</Show>
<div class={styles.container}>
<Show when={props.slides.length > 0}>
<Show when={props.slides?.length > 0}>
<Show when={props.slides.length !== 1} fallback={<Row1 article={props.slides[0]} />}>
<Show when={props.slides.length !== 2} fallback={<Row2 articles={props.slides} />}>
<div class={styles.holder}>

View File

@ -1,7 +1,9 @@
import { type RouteDefinition, type RouteSectionProps, createAsync } from '@solidjs/router'
import { Show, Suspense, createEffect, createSignal, on, onMount } from 'solid-js'
import { LoadShoutsOptions } from '~/graphql/schema/core.gen'
import { loadShouts } from '~/lib/api'
import { restoreScrollPosition, saveScrollPosition } from '~/utils/scroll'
import { byStat } from '~/utils/sortby'
import { HomeView, HomeViewProps } from '../components/Views/Home'
import { Loading } from '../components/_shared/Loading'
import { PageLayout } from '../components/_shared/PageLayout'
@ -11,32 +13,33 @@ import { ReactionsProvider } from '../context/reactions'
export const SHOUTS_PER_PAGE = 20
const fetchHomeTopData = async () => {
const limit = 20
const topCommentedLoader = loadShouts({
filters: { featured: true },
limit
order_by: 'comments_stat',
limit: 10
})
const topMonthLoader = loadShouts({
filters: { featured: true },
limit
})
const topViewedLoader = loadShouts({
filters: { featured: true },
limit
})
const daysago = Date.now() - 30 * 24 * 60 * 60 * 1000
const after = Math.floor(daysago / 1000)
const options: LoadShoutsOptions = {
filters: {
featured: true,
after
},
order_by: 'likes_stat',
limit: 10
}
const topMonthLoader = loadShouts({ ...options })
const topRatedLoader = loadShouts({
filters: { featured: true },
limit
order_by: 'likes_stat',
limit: 10
})
return {
topCommentedShouts: await topCommentedLoader(),
topMonthShouts: await topMonthLoader(),
topRatedShouts: await topRatedLoader(),
topViewedShouts: await topViewedLoader()
topMonthShouts: await topMonthLoader(),
topCommentedShouts: await topCommentedLoader()
} as Partial<HomeViewProps>
}
@ -69,12 +72,19 @@ export default function HomePage(props: RouteSectionProps<HomeViewProps>) {
// async ssr-friendly router-level cached data source
const data = createAsync(async (prev?: HomeViewProps) => {
const featuredShoutsLoader = featuredLoader(featuredOffset())
const featuredShouts = await featuredShoutsLoader()
return {
const featuredShouts = [
...(prev?.featuredShouts || []),
...((await featuredShoutsLoader()) || props.data?.featuredShouts || [])
]
const sortFn = byStat('viewed')
const topViewedShouts = featuredShouts?.sort(sortFn) || []
const result = {
...prev,
...props.data,
featuredShouts: featuredShouts || prev?.featuredShouts || props.data?.featuredShouts
topViewedShouts,
featuredShouts
}
return result
})
const [canLoadMoreFeatured, setCanLoadMoreFeatured] = createSignal(false)

View File

@ -28,7 +28,7 @@ export const ArticlePage = (props: RouteSectionProps<{ article: Shout }>) => {
() => `${article()?.authors?.[0]?.name || t('Discours')}: ${article()?.title || ''}`
)
onMount(async () => {
if(gaIdentity) {
if (gaIdentity) {
try {
console.info('[routes.slug] mounted, connecting ga...')
await loadGAScript(gaIdentity)
@ -44,10 +44,11 @@ export const ArticlePage = (props: RouteSectionProps<{ article: Shout }>) => {
// wrapped by the returned tracking function is notified of a change`
createReaction(() => {
if (article()) {
console.debug('[routes.slug] article signal changed once')
window.gtag?.('event', 'page_view', {
page_title: article()?.title,
page_location: window.location.href,
page_path: window.location.pathname,
page_path: window.location.pathname
})
}
})

View File

@ -19,10 +19,12 @@ export const loadGAScript = (id: string) => {
export const initGA = (id: string) => {
const w = window as Window
if (w) {
// @ts-ignore
// biome-ignore lint/suspicious/noExplicitAny: ga-script
w.dataLayer = (w.dataLayer as any) || []
// biome-ignore lint/suspicious/noExplicitAny: ga-script
function gtag(...args: any[]) {
// @ts-ignore
w.dataLayer.push(args)
}
gtag('js', new Date())

View File

@ -1,4 +1,4 @@
import type { Author, Reaction, Shout, Topic, TopicStat } from '../graphql/schema/core.gen'
import type { Author, Maybe, Reaction, Shout, Topic, TopicStat } from '../graphql/schema/core.gen'
// biome-ignore lint/suspicious/noExplicitAny: sort by first char
export const byFirstChar = (a: { name?: any; title?: any }, b: { name?: any; title?: any }) =>
@ -26,8 +26,10 @@ export const byLength = (
return 0
}
export type SomeStat = { [x: string]: Maybe<number> } | undefined | null
export const byStat = (metric: string) => {
return (a: { stat: { [x: string]: number } }, b: { stat: { [x: string]: number } }) => {
return (a: { stat?: SomeStat }, b: { stat?: SomeStat }) => {
const aStat = a.stat?.[metric] ?? 0
const bStat = b.stat?.[metric] ?? 0
return aStat - bStat