home-tops
This commit is contained in:
parent
ae207a02f2
commit
305bd23f9f
|
@ -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: {
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
|
|
|
@ -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}>
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
})
|
||||
}
|
||||
})
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue
Block a user