import * as React from 'react' ; import { json , useLoaderData , Link , useCatch , useParams } from 'remix' ; import type { HeadersFunction } from 'remix' ; import type { CWKSeason , KCDHandle , KCDLoader } from '~/types' ; import { orderBy } from 'lodash' ; import { Grid } from '~/components/grid' ; import { getSeasonListItems } from '~/utils/simplecast.server' ; import { useChatsEpisodeUIState } from '~/utils/providers' ; import { formatTime , reuseUsefulLoaderHeaders } from '~/utils/misc' ; import { getCWKEpisodePath } from '~/utils/chats-with-kent' ; import { TriangleIcon } from '~/components/icons/triangle-icon' ; import { MissingSomething } from '~/components/kifs' ; import { H3 , Paragraph } from '~/components/typography' ; import { ServerError } from '~/components/errors' ; export const handle = { getSitemapEntries : async ( request ) => { const seasons = await getSeasonListItems ( { request } ) ; return seasons . map ( season => { return { route : `/chats/${ season . seasonNumber . toString ( ) . padStart ( 2 , '0' ) }` , priority : 0.4 , } ; } ) ; } , } ; type LoaderData = { season : CWKSeason ; } ; export const loader = async ( { params , request , } ) => { const seasons = await getSeasonListItems ( { request } ) ; const seasonNumber = Number ( params . season ) ; const season = seasons . find ( s => s . seasonNumber === seasonNumber ) ; if ( ! season ) { throw new Response ( `No season for ${ params . season }` , { status : 404 } ) ; } const data = { season } ; return json ( data , { headers : { 'Cache-Control' : 'public, max-age=600' , } , } ) ; } ; export const headers = reuseUsefulLoaderHeaders ; export default function ChatsSeason ( ) { const { season } = useLoaderData < LoaderData > ( ) ; const { sortOrder } = useChatsEpisodeUIState ( ) ; const episodes = orderBy ( season . episodes , 'episodeNumber' , sortOrder ) ; return episodes . map ( episode => ( < Link className = "group focus:outline-none" key = { episode . slug } to = { getCWKEpisodePath ( episode ) } > < Grid nested className = "relative py-10 border-b border-gray-200 dark:border-gray-600 lg:py-5" > < div className = "bg-secondary absolute -inset-px group-focus:block group-hover:block hidden -mx-6 rounded-lg" / > < div className = "relative flex-none col-span-1" > < div className = "absolute inset-0 flex items-center justify-center opacity-0 group-focus:opacity-100 group-hover:opacity-100 transform scale-0 group-hover:scale-100 transition" > < div className = "flex-none p-4 text-gray-800 bg-white rounded-full" > < TriangleIcon size = { 12 } / > < /div> < /div> < img className = "w-full h-16 rounded-lg object-cover" src = { episode . image } alt = { episode . title } / > < /div> < div className = "text-primary relative flex flex-col col-span-3 md:col-span-7 lg:flex-row lg:col-span-11 lg:items-center lg:justify-between" > < div className = "mb-3 text-xl font-medium lg:mb-0" > < span className = "inline-block w-10 lg:text-lg" > { `${ episode . episodeNumber . toString ( ) . padStart ( 2 , '0' ) }.` } < /span> { episode . title } < /div> < div className = "text-gray-400 text-lg font-medium" > { formatTime ( episode . duration ) } < /div> < /div> < /Grid> < /Link>)); } export function ErrorBoundary ( { error } ) { console . error ( error ) ; return < ServerError />; } export function CatchBoundary ( ) { const caught = useCatch ( ) ; const params = useParams ( ) ; console . error ( 'CatchBoundary' , caught ) ; if ( caught . status === 404 ) { return ( < Grid nested className = "mt-3" > < div className = "col-span-full md:col-span-5" > < H3 > { `Season not found` } < /H3> < Paragraph > { `Are you sure ${ params . season ? `season ${ params . season }` : 'this season' } exists?` } < /Paragraph> < /div> < div className = "md:col-span-start-6 col-span-full md:col-span-5" > < MissingSomething className = "rounded-lg" aspectRatio = "3:4" / > < /div> < /Grid>); } throw new Error ( `Unhandled error: ${ caught . status }` ) ; }