feat: enhance news detail page with subscription prompt and content restriction for basic users

This commit is contained in:
Ardeman 2025-03-15 16:58:02 +08:00
parent c7195b7428
commit cc5331284b
2 changed files with 44 additions and 22 deletions

View File

@ -1,16 +1,20 @@
import htmlParse from 'html-react-parser' import htmlParse from 'html-react-parser'
import { useReadingTime } from 'react-hook-reading-time' import { useReadingTime } from 'react-hook-reading-time'
import { useRouteLoaderData } from 'react-router' import { useRouteLoaderData } from 'react-router'
import { twMerge } from 'tailwind-merge'
import { Button } from '~/components/ui/button'
import { Card } from '~/components/ui/card' import { Card } from '~/components/ui/card'
import { CarouselSection } from '~/components/ui/carousel-section' import { CarouselSection } from '~/components/ui/carousel-section'
import { NewsAuthor } from '~/components/ui/news-author' import { NewsAuthor } from '~/components/ui/news-author'
import { SocialShareButtons } from '~/components/ui/social-share' import { SocialShareButtons } from '~/components/ui/social-share'
import { Tags } from '~/components/ui/tags' import { Tags } from '~/components/ui/tags'
import { useNewsContext } from '~/contexts/news'
import type { loader } from '~/routes/_news.detail.$slug' import type { loader } from '~/routes/_news.detail.$slug'
import type { TNews } from '~/types/news' import type { TNews } from '~/types/news'
export const NewsDetailPage = () => { export const NewsDetailPage = () => {
const { setIsSuccessOpen } = useNewsContext()
const loaderData = useRouteLoaderData<typeof loader>( const loaderData = useRouteLoaderData<typeof loader>(
'routes/_news.detail.$slug', 'routes/_news.detail.$slug',
) )
@ -22,6 +26,7 @@ export const NewsDetailPage = () => {
const currentUrl = globalThis.location const currentUrl = globalThis.location
const { title, content, featured_image, author, live_at, tags } = const { title, content, featured_image, author, live_at, tags } =
loaderData?.newsDetailData || {} loaderData?.newsDetailData || {}
const { shouldSubscribe } = loaderData || {}
const { text } = useReadingTime(content || '') const { text } = useReadingTime(content || '')
@ -51,10 +56,23 @@ export const NewsDetailPage = () => {
/> />
</div> </div>
<div className="mt-8 flex items-center justify-center"> <div className="mt-8 flex flex-col items-center justify-center gap-y-4">
<article className="prose prose-headings:my-0.5 prose-p:my-0.5"> <article
className={twMerge(
'prose prose-headings:my-0.5 prose-p:my-0.5',
shouldSubscribe ? 'line-clamp-5' : '',
)}
>
{content && htmlParse(content)} {content && htmlParse(content)}
</article> </article>
{shouldSubscribe && (
<Button
onClick={() => setIsSuccessOpen('warning')}
className="w-full"
>
Read More
</Button>
)}
</div> </div>
<div className="items-end justify-between border-b-gray-300 py-4 sm:flex"> <div className="items-end justify-between border-b-gray-300 py-4 sm:flex">
<div className="flex flex-col max-sm:mb-3"> <div className="flex flex-col max-sm:mb-3">

View File

@ -1,4 +1,5 @@
import { isRouteErrorResponse, redirect } from 'react-router' import { isRouteErrorResponse } from 'react-router'
import { stripHtml } from 'string-strip-html'
import { getCategories } from '~/apis/common/get-categories' import { getCategories } from '~/apis/common/get-categories'
import { getNews } from '~/apis/common/get-news' import { getNews } from '~/apis/common/get-news'
@ -12,17 +13,21 @@ import type { Route } from './+types/_news.detail.$slug'
export const loader = async ({ request, params }: Route.LoaderArgs) => { export const loader = async ({ request, params }: Route.LoaderArgs) => {
const { userToken: accessToken } = await handleCookie(request) const { userToken: accessToken } = await handleCookie(request)
if (!accessToken) { let userData
return redirect('/') if (accessToken) {
const { data } = await getUser({ accessToken })
userData = data
} }
const { data: userData } = await getUser({ accessToken })
const { slug } = params const { slug } = params
const { data: newsDetailData } = await getNewsBySlug({ slug, accessToken }) let { data: newsDetailData } = await getNewsBySlug({ slug, accessToken })
if ( const shouldSubscribe =
userData.subscribe.subscribe_plan.code === 'basic' && (!accessToken || userData?.subscribe?.subscribe_plan?.code === 'basic') &&
newsDetailData.is_premium newsDetailData?.is_premium
) { newsDetailData = {
return redirect('/') ...newsDetailData,
content: shouldSubscribe
? stripHtml(newsDetailData.content).result.slice(0, 600)
: newsDetailData.content,
} }
const { data: categoriesData } = await getCategories() const { data: categoriesData } = await getCategories()
const beritaCode = 'berita' const beritaCode = 'berita'
@ -36,12 +41,12 @@ export const loader = async ({ request, params }: Route.LoaderArgs) => {
newsDetailData, newsDetailData,
beritaCategory, beritaCategory,
beritaNews, beritaNews,
shouldSubscribe,
} }
} }
export const meta = ({ data }: Route.MetaArgs) => { export const meta = ({ data }: Route.MetaArgs) => {
if (data) { const { newsDetailData } = data || {}
const { newsDetailData } = data
const metaTitle = APP.title const metaTitle = APP.title
const title = `${newsDetailData?.title} - ${metaTitle}` const title = `${newsDetailData?.title} - ${metaTitle}`
@ -51,7 +56,6 @@ export const meta = ({ data }: Route.MetaArgs) => {
}, },
] ]
} }
}
export const ErrorBoundary = ({ error }: Route.ErrorBoundaryProps) => { export const ErrorBoundary = ({ error }: Route.ErrorBoundaryProps) => {
let message = 'Oops!' let message = 'Oops!'