isolate-utils-authors-with-@
This commit is contained in:
parent
645e65751b
commit
ef1408327f
|
@ -4,7 +4,7 @@ import { Icon } from '~/components/_shared/Icon'
|
|||
import { Popover } from '~/components/_shared/Popover'
|
||||
import { useLocalize } from '~/context/localize'
|
||||
import { MediaItem } from '~/types/mediaitem'
|
||||
import { getArticleDescription } from '~/utils/meta'
|
||||
import { descFromBody } from '~/utils/meta'
|
||||
import { SharePopup, getShareUrl } from '../SharePopup'
|
||||
|
||||
import styles from './AudioPlayer.module.scss'
|
||||
|
@ -137,7 +137,7 @@ export const PlayerPlaylist = (props: Props) => {
|
|||
>
|
||||
<SharePopup
|
||||
title={mi.title}
|
||||
description={getArticleDescription(props.body || '')}
|
||||
description={descFromBody(props.body || '')}
|
||||
imageUrl={mi.pic || ''}
|
||||
shareUrl={getShareUrl({ pathname: `/${props.articleSlug}` })}
|
||||
trigger={
|
||||
|
|
|
@ -8,7 +8,7 @@ import { useLocalize } from '~/context/localize'
|
|||
import { useSession } from '~/context/session'
|
||||
import type { Author, Maybe, Shout, Topic } from '~/graphql/schema/core.gen'
|
||||
import { capitalize } from '~/utils/capitalize'
|
||||
import { getArticleDescription } from '~/utils/meta'
|
||||
import { descFromBody } from '~/utils/meta'
|
||||
import { CoverImage } from '../../Article/CoverImage'
|
||||
import { SharePopup, getShareUrl } from '../../Article/SharePopup'
|
||||
import { ShoutRatingControl } from '../../Article/ShoutRatingControl'
|
||||
|
@ -109,7 +109,7 @@ export const ArticleCard = (props: ArticleCardProps) => {
|
|||
const [isActionPopupActive, setIsActionPopupActive] = createSignal(false)
|
||||
const [isCoverImageLoadError, setIsCoverImageLoadError] = createSignal(false)
|
||||
const [isCoverImageLoading, setIsCoverImageLoading] = createSignal(true)
|
||||
const description = getArticleDescription(props.article?.body)
|
||||
const description = descFromBody(props.article?.body)
|
||||
const aspectRatio: Accessor<string> = () => LAYOUT_ASPECT[props.article?.layout as string]
|
||||
const [mainTopicTitle, mainTopicSlug] = getMainTopicTitle(props.article, lang())
|
||||
const { title, subtitle } = getTitleAndSubtitle(props.article)
|
||||
|
|
|
@ -4,7 +4,7 @@ import { useLocalize } from '~/context/localize'
|
|||
import { useTopics } from '~/context/topics'
|
||||
import { loadShouts } from '~/graphql/api/public'
|
||||
import { Author, Shout, Topic } from '~/graphql/schema/core.gen'
|
||||
import { SHOUTS_PER_PAGE } from '~/routes/(home)'
|
||||
import { SHOUTS_PER_PAGE } from '~/routes/(main)'
|
||||
import { capitalize } from '~/utils/capitalize'
|
||||
import { splitToPages } from '~/utils/splitToPages'
|
||||
import Banner from '../Discours/Banner'
|
||||
|
|
|
@ -7,7 +7,7 @@ import { useLocalize } from '~/context/localize'
|
|||
import { useTopics } from '~/context/topics'
|
||||
import { loadAuthors, loadFollowersByTopic, loadShouts } from '~/graphql/api/public'
|
||||
import { Author, AuthorsBy, LoadShoutsOptions, Shout, Topic } from '~/graphql/schema/core.gen'
|
||||
import { SHOUTS_PER_PAGE } from '~/routes/(home)'
|
||||
import { SHOUTS_PER_PAGE } from '~/routes/(main)'
|
||||
import { getUnixtime } from '~/utils/getServerDate'
|
||||
import { restoreScrollPosition, saveScrollPosition } from '~/utils/scroll'
|
||||
import { splitToPages } from '~/utils/splitToPages'
|
||||
|
|
|
@ -8,7 +8,7 @@ import { Shout } from '~/graphql/schema/core.gen'
|
|||
import enKeywords from '~/intl/locales/en/keywords.json'
|
||||
import ruKeywords from '~/intl/locales/ru/keywords.json'
|
||||
import { getImageUrl, getOpenGraphImageUrl } from '~/lib/getImageUrl'
|
||||
import { getArticleKeywords } from '~/utils/meta'
|
||||
import { descFromBody } from '~/utils/meta'
|
||||
import { FooterView } from '../Discours/Footer'
|
||||
import { Header } from '../Nav/Header'
|
||||
import styles from './PageLayout.module.scss'
|
||||
|
@ -50,7 +50,7 @@ export const PageLayout = (props: PageLayoutProps) => {
|
|||
const keypath = createMemo(() => (props.key || loc?.pathname.split('/')[0]) as keyof typeof ruKeywords)
|
||||
const keywords = createMemo(
|
||||
() =>
|
||||
(props.article && getArticleKeywords(props.article as Shout)) ||
|
||||
(props.article && descFromBody(props.article.body)) ||
|
||||
(lang() === 'ru' ? ruKeywords[keypath()] : enKeywords[keypath()])
|
||||
)
|
||||
const [scrollToComments, setScrollToComments] = createSignal<boolean>(false)
|
||||
|
|
|
@ -1,104 +0,0 @@
|
|||
import { RouteDefinition, RouteSectionProps, createAsync, useLocation, useParams } from '@solidjs/router'
|
||||
import { HttpStatusCode } from '@solidjs/start'
|
||||
import {
|
||||
ErrorBoundary,
|
||||
Show,
|
||||
Suspense,
|
||||
createEffect,
|
||||
createMemo,
|
||||
createSignal,
|
||||
on,
|
||||
onMount
|
||||
} from 'solid-js'
|
||||
import { FourOuFourView } from '~/components/Views/FourOuFour'
|
||||
import { Loading } from '~/components/_shared/Loading'
|
||||
import { gaIdentity } from '~/config'
|
||||
import { useLocalize } from '~/context/localize'
|
||||
import { getShout } from '~/graphql/api/public'
|
||||
import type { Reaction, Shout } from '~/graphql/schema/core.gen'
|
||||
import { initGA, loadGAScript } from '~/utils/ga'
|
||||
import { getArticleKeywords } from '~/utils/meta'
|
||||
import { FullArticle } from '../components/Article/FullArticle'
|
||||
import { PageLayout } from '../components/_shared/PageLayout'
|
||||
import { ReactionsProvider } from '../context/reactions'
|
||||
|
||||
const fetchShout = async (slug: string): Promise<Shout | undefined> => {
|
||||
const shoutLoader = getShout({ slug })
|
||||
const result = await shoutLoader()
|
||||
return result
|
||||
}
|
||||
|
||||
export const route: RouteDefinition = {
|
||||
load: async ({ params }) => ({
|
||||
article: await fetchShout(params.slug)
|
||||
})
|
||||
}
|
||||
|
||||
export default (
|
||||
props: RouteSectionProps<{ article?: Shout; comments?: Reaction[]; votes?: Reaction[] }>
|
||||
) => {
|
||||
const params = useParams()
|
||||
const loc = useLocation()
|
||||
const { t } = useLocalize()
|
||||
const [scrollToComments, setScrollToComments] = createSignal<boolean>(false)
|
||||
const article = createAsync(async () => props.data.article || (await fetchShout(params.slug)))
|
||||
|
||||
const title = createMemo(
|
||||
() => `${article()?.authors?.[0]?.name || t('Discours')} :: ${article()?.title || ''}`
|
||||
)
|
||||
|
||||
onMount(async () => {
|
||||
if (gaIdentity && article()?.id) {
|
||||
try {
|
||||
await loadGAScript(gaIdentity)
|
||||
initGA(gaIdentity)
|
||||
} catch (error) {
|
||||
console.warn('Failed to connect Google Analytics:', error)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
createEffect(
|
||||
on(
|
||||
article,
|
||||
(a?: Shout) => {
|
||||
if (!a?.id) return
|
||||
window?.gtag?.('event', 'page_view', {
|
||||
page_title: a.title,
|
||||
page_location: window?.location.href || '',
|
||||
page_path: loc.pathname
|
||||
})
|
||||
},
|
||||
{ defer: true }
|
||||
)
|
||||
)
|
||||
|
||||
return (
|
||||
<ErrorBoundary fallback={() => <HttpStatusCode code={500} />}>
|
||||
<Suspense fallback={<Loading />}>
|
||||
<Show
|
||||
when={article()?.id}
|
||||
fallback={
|
||||
<PageLayout isHeaderFixed={false} hideFooter={true} title={t('Nothing is here')}>
|
||||
<FourOuFourView />
|
||||
<HttpStatusCode code={404} />
|
||||
</PageLayout>
|
||||
}
|
||||
>
|
||||
<PageLayout
|
||||
title={title()}
|
||||
desc={getArticleKeywords(article() as Shout)}
|
||||
headerTitle={article()?.title || ''}
|
||||
slug={article()?.slug}
|
||||
cover={article()?.cover || ''}
|
||||
scrollToComments={(value) => setScrollToComments(value)}
|
||||
>
|
||||
<ReactionsProvider>
|
||||
<FullArticle article={article() as Shout} scrollToComments={scrollToComments()} />
|
||||
</ReactionsProvider>
|
||||
</PageLayout>
|
||||
</Show>
|
||||
</Suspense>
|
||||
</ErrorBoundary>
|
||||
)
|
||||
}
|
134
src/routes/[slug]/(author-or-post).tsx
Normal file
134
src/routes/[slug]/(author-or-post).tsx
Normal file
|
@ -0,0 +1,134 @@
|
|||
import { RouteDefinition, RouteSectionProps, createAsync, useLocation, useParams } from '@solidjs/router'
|
||||
import { HttpStatusCode } from '@solidjs/start'
|
||||
import {
|
||||
ErrorBoundary,
|
||||
Show,
|
||||
Suspense,
|
||||
createEffect,
|
||||
createMemo,
|
||||
createSignal,
|
||||
on,
|
||||
onMount
|
||||
} from 'solid-js'
|
||||
import { FourOuFourView } from '~/components/Views/FourOuFour'
|
||||
import { Loading } from '~/components/_shared/Loading'
|
||||
import { gaIdentity } from '~/config'
|
||||
import { useLocalize } from '~/context/localize'
|
||||
import { getAuthor, getShout } from '~/graphql/api/public'
|
||||
import type { Author, Reaction, Shout } from '~/graphql/schema/core.gen'
|
||||
import { initGA, loadGAScript } from '~/utils/ga'
|
||||
import { descFromBody, keywordsFromTopics } from '~/utils/meta'
|
||||
import { FullArticle } from '../../components/Article/FullArticle'
|
||||
import { PageLayout } from '../../components/_shared/PageLayout'
|
||||
import { ReactionsProvider } from '../../context/reactions'
|
||||
import AuthorPage, { AuthorPageProps } from '../author/[slug]/[...tab]'
|
||||
|
||||
const fetchShout = async (slug: string): Promise<Shout | undefined> => {
|
||||
const shoutLoader = getShout({ slug })
|
||||
const result = await shoutLoader()
|
||||
return result
|
||||
}
|
||||
|
||||
const fetchAuthor = async (slug: string): Promise<Author | undefined> => {
|
||||
const authorLoader = getAuthor({ slug })
|
||||
const result = await authorLoader()
|
||||
return result
|
||||
}
|
||||
|
||||
export const route: RouteDefinition = {
|
||||
load: async ({ params }) => ({
|
||||
article: await fetchShout(params.slug)
|
||||
})
|
||||
}
|
||||
|
||||
type SlugPageProps = { article?: Shout; comments?: Reaction[]; votes?: Reaction[]; author?: Author }
|
||||
|
||||
export default (props: RouteSectionProps<SlugPageProps>) => {
|
||||
const params = useParams()
|
||||
if (params.slug.startsWith('@')) return AuthorPage(props as RouteSectionProps<AuthorPageProps>)
|
||||
|
||||
const loc = useLocation()
|
||||
const { t } = useLocalize()
|
||||
const [scrollToComments, setScrollToComments] = createSignal<boolean>(false)
|
||||
const article = createAsync(async () => props.data.article || (await fetchShout(params.slug)))
|
||||
const author = createAsync(async () =>
|
||||
params.slug.startsWith('@')
|
||||
? props.data.author || (await fetchAuthor(params.slug))
|
||||
: article()?.authors?.[0]
|
||||
)
|
||||
const titleSuffix = createMemo(
|
||||
() => (article()?.title || author()?.name) ?? ` :: ${article()?.title || author()?.name || ''}`
|
||||
)
|
||||
|
||||
onMount(async () => {
|
||||
if (gaIdentity && article()?.id) {
|
||||
try {
|
||||
await loadGAScript(gaIdentity)
|
||||
initGA(gaIdentity)
|
||||
} catch (error) {
|
||||
console.warn('Failed to connect Google Analytics:', error)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
createEffect(
|
||||
on(
|
||||
article,
|
||||
(a?: Shout) => {
|
||||
if (!a?.id) return
|
||||
window?.gtag?.('event', 'page_view', {
|
||||
page_title: a.title,
|
||||
page_location: window?.location.href || '',
|
||||
page_path: loc.pathname
|
||||
})
|
||||
},
|
||||
{ defer: true }
|
||||
)
|
||||
)
|
||||
|
||||
return (
|
||||
<ErrorBoundary fallback={() => <HttpStatusCode code={500} />}>
|
||||
<Suspense fallback={<Loading />}>
|
||||
<Show
|
||||
when={article()?.id}
|
||||
fallback={
|
||||
<PageLayout isHeaderFixed={false} hideFooter={true} title={t('Nothing is here')}>
|
||||
<FourOuFourView />
|
||||
<HttpStatusCode code={404} />
|
||||
</PageLayout>
|
||||
}
|
||||
>
|
||||
<Show
|
||||
when={params.slug.startsWith('@')}
|
||||
fallback={
|
||||
<PageLayout
|
||||
title={`${t('Discours')}${titleSuffix() || ''}`}
|
||||
desc={keywordsFromTopics(article()?.topics as { title: string }[])}
|
||||
headerTitle={article()?.title || ''}
|
||||
slug={article()?.slug}
|
||||
cover={article()?.cover || ''}
|
||||
scrollToComments={(value) => setScrollToComments(value)}
|
||||
>
|
||||
<ReactionsProvider>
|
||||
<FullArticle article={article() as Shout} scrollToComments={scrollToComments()} />
|
||||
</ReactionsProvider>
|
||||
</PageLayout>
|
||||
}
|
||||
>
|
||||
<PageLayout
|
||||
title={`${t('Discours')}${titleSuffix() || ''}`}
|
||||
desc={descFromBody(author()?.about || author()?.bio || '')}
|
||||
headerTitle={author()?.name || ''}
|
||||
slug={author()?.slug}
|
||||
cover={author()?.pic || ''}
|
||||
>
|
||||
<ReactionsProvider>
|
||||
<FullArticle article={article() as Shout} scrollToComments={scrollToComments()} />
|
||||
</ReactionsProvider>
|
||||
</PageLayout>
|
||||
</Show>
|
||||
</Show>
|
||||
</Suspense>
|
||||
</ErrorBoundary>
|
||||
)
|
||||
}
|
|
@ -16,7 +16,7 @@ import {
|
|||
Topic
|
||||
} from '~/graphql/schema/core.gen'
|
||||
import { getImageUrl } from '~/lib/getImageUrl'
|
||||
import { SHOUTS_PER_PAGE } from '../../(home)'
|
||||
import { SHOUTS_PER_PAGE } from '../../(main)'
|
||||
|
||||
const fetchAuthorShouts = async (slug: string, offset?: number) => {
|
||||
const opts: LoadShoutsOptions = { filters: { author: slug }, limit: SHOUTS_PER_PAGE, offset }
|
||||
|
@ -47,7 +47,9 @@ export const route = {
|
|||
}
|
||||
}
|
||||
|
||||
export default (props: RouteSectionProps<{ articles: Shout[]; author: Author; topics: Topic[] }>) => {
|
||||
export type AuthorPageProps = { articles?: Shout[]; author?: Author; topics?: Topic[] }
|
||||
|
||||
export const AuthorPage = (props: RouteSectionProps<AuthorPageProps>) => {
|
||||
const params = useParams()
|
||||
const { addAuthor } = useAuthors()
|
||||
const articles = createAsync(
|
||||
|
@ -55,7 +57,7 @@ export default (props: RouteSectionProps<{ articles: Shout[]; author: Author; to
|
|||
)
|
||||
const author = createAsync(async () => {
|
||||
const a = props.data.author || (await fetchAuthor(params.slug))
|
||||
addAuthor(a)
|
||||
a && addAuthor(a)
|
||||
return a
|
||||
})
|
||||
const topics = createAsync(async () => props.data.topics || (await fetchAllTopics()))
|
||||
|
@ -104,3 +106,5 @@ export default (props: RouteSectionProps<{ articles: Shout[]; author: Author; to
|
|||
</ErrorBoundary>
|
||||
)
|
||||
}
|
||||
|
||||
export default AuthorPage
|
||||
|
|
|
@ -7,7 +7,7 @@ import { useLocalize } from '~/context/localize'
|
|||
import { loadShouts } from '~/graphql/api/public'
|
||||
import { LoadShoutsOptions, Shout } from '~/graphql/schema/core.gen'
|
||||
import { LayoutType } from '~/types/common'
|
||||
import { SHOUTS_PER_PAGE } from '../(home)'
|
||||
import { SHOUTS_PER_PAGE } from '../(main)'
|
||||
|
||||
const fetchExpoShouts = async (layouts: string[]) => {
|
||||
const result = await loadShouts({
|
||||
|
|
|
@ -7,7 +7,7 @@ import { useLocalize } from '~/context/localize'
|
|||
import { ReactionsProvider } from '~/context/reactions'
|
||||
import { loadShouts } from '~/graphql/api/public'
|
||||
import { LoadShoutsOptions, Shout } from '~/graphql/schema/core.gen'
|
||||
import { SHOUTS_PER_PAGE } from '../(home)'
|
||||
import { SHOUTS_PER_PAGE } from '../(main)'
|
||||
|
||||
export type FeedPeriod = 'week' | 'month' | 'year'
|
||||
|
||||
|
|
|
@ -10,8 +10,8 @@ import { useTopics } from '~/context/topics'
|
|||
import { loadShouts, loadTopics } from '~/graphql/api/public'
|
||||
import { LoadShoutsOptions, Shout, Topic } from '~/graphql/schema/core.gen'
|
||||
import { getImageUrl } from '~/lib/getImageUrl'
|
||||
import { getArticleDescription } from '~/utils/meta'
|
||||
import { SHOUTS_PER_PAGE } from '../(home)'
|
||||
import { descFromBody } from '~/utils/meta'
|
||||
import { SHOUTS_PER_PAGE } from '../(main)'
|
||||
|
||||
const fetchTopicShouts = async (slug: string, offset?: number) => {
|
||||
const opts: LoadShoutsOptions = { filters: { topic: slug }, limit: SHOUTS_PER_PAGE, offset }
|
||||
|
@ -72,7 +72,7 @@ export default (props: RouteSectionProps<{ articles: Shout[]; topics: Topic[] }>
|
|||
|
||||
const desc = createMemo(() =>
|
||||
topic()?.body
|
||||
? getArticleDescription(topic()?.body || '')
|
||||
? descFromBody(topic()?.body || '')
|
||||
: t('The most interesting publications on the topic', { topicName: title() })
|
||||
)
|
||||
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
import { Shout } from '~/graphql/schema/core.gen'
|
||||
|
||||
const MAX_DESCRIPTION_LENGTH = 150
|
||||
|
||||
export const getArticleDescription = (body: string): string => {
|
||||
export const descFromBody = (body: string): string => {
|
||||
if (!body) {
|
||||
return ''
|
||||
}
|
||||
const descriptionWordsArray = body
|
||||
.replaceAll(/<[^>]*>/g, ' ')
|
||||
.replaceAll(/\s+/g, ' ')
|
||||
.replace(/<[^>]*>/g, ' ') // Remove HTML tags
|
||||
.replace(/\s+/g, ' ') // Normalize whitespace
|
||||
.split(' ')
|
||||
// ¯\_(ツ)_/¯ maybe need to remove the punctuation
|
||||
|
||||
let description = ''
|
||||
let i = 0
|
||||
while (i < descriptionWordsArray.length && description.length < MAX_DESCRIPTION_LENGTH) {
|
||||
|
@ -20,6 +18,6 @@ export const getArticleDescription = (body: string): string => {
|
|||
return description.trim()
|
||||
}
|
||||
|
||||
export const getArticleKeywords = (shout: Shout): string => {
|
||||
return (shout.topics || [])?.map((topic) => topic?.title).join(', ')
|
||||
export const keywordsFromTopics = (topics: { title: string }[]): string => {
|
||||
return topics.map((topic: { title: string }) => topic.title).join(', ')
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user