|
- import { useEffect, useRef, useState } from 'react'
- import { Helmet } from 'react-helmet-async'
- import { useParams } from 'react-router-dom'
-
- import PostEmbed from '@/components/PostEmbed'
- import MainArea from '@/components/layout/MainArea'
- import { SITE_TITLE } from '@/config'
- import { apiGet, apiPatch, apiPut } from '@/lib/api'
- import { fetchPost } from '@/lib/posts'
-
- import type { FC } from 'react'
-
- import type { NiconicoMetadata, NiconicoViewerHandle, Post, Theatre } from '@/types'
-
- type TheatreInfo = {
- hostFlg: boolean
- postId: number | null
- postStartedAt: string | null }
-
-
- export default (() => {
- const { id } = useParams ()
-
- const embedRef = useRef<NiconicoViewerHandle> (null)
-
- const [loading, setLoading] = useState (false)
- const [theatre, setTheatre] = useState<Theatre | null> (null)
- const [theatreInfo, setTheatreInfo] =
- useState<TheatreInfo> ({ hostFlg: false, postId: null, postStartedAt: null })
- const [post, setPost] = useState<Post | null> (null)
- const [videoLength, setVideoLength] = useState (9_999_999_999)
-
- useEffect (() => {
- if (!(id))
- return
-
- void (async () => {
- setTheatre (await apiGet<Theatre> (`/theatres/${ id }`))
- }) ()
-
- const interval = setInterval (async () => {
- if (theatreInfo.hostFlg
- && theatreInfo.postStartedAt
- && ((new Date).getTime () - (new Date (theatreInfo.postStartedAt)).getTime ()
- > videoLength))
- setTheatreInfo ({ hostFlg: true, postId: null, postStartedAt: null })
- else
- setTheatreInfo (await apiPut<TheatreInfo> (`/theatres/${ id }/watching`))
- }, 1_000)
-
- return () => clearInterval (interval)
- }, [id, theatreInfo.hostFlg, theatreInfo.postStartedAt, videoLength])
-
- useEffect (() => {
- if (!(theatreInfo.hostFlg) || loading)
- return
-
- if (theatreInfo.postId == null)
- {
- void (async () => {
- setLoading (true)
- await apiPatch<void> (`/theatres/${ id }/next_post`)
- setLoading (false)
- }) ()
- return
- }
- }, [id, loading, theatreInfo.hostFlg, theatreInfo.postId])
-
- useEffect (() => {
- if (theatreInfo.postId == null)
- return
-
- void (async () => {
- setPost (await fetchPost (String (theatreInfo.postId)))
- }) ()
- }, [theatreInfo.postId, theatreInfo.postStartedAt])
-
- const syncPlayback = (meta: NiconicoMetadata) => {
- if (!(theatreInfo.postStartedAt))
- return
-
- const targetTime =
- ((new Date).getTime () - (new Date (theatreInfo.postStartedAt)).getTime ())
-
- const drift = Math.abs (meta.currentTime - targetTime)
-
- if (drift > 5_000)
- embedRef.current?.seek (targetTime)
- }
-
- return (
- <MainArea>
- <Helmet>
- {theatre && (
- <title>
- {'上映会場'
- + (theatre.name ? `『${ theatre.name }』` : ` #${ theatre.id }`)
- + ` | ${ SITE_TITLE }`}
- </title>)}
- </Helmet>
-
- {post && (
- <PostEmbed
- ref={embedRef}
- post={post}
- onLoadComplete={info => {
- embedRef.current?.play ()
- setVideoLength (info.lengthInSeconds * 1_000)
- }}
- onMetadataChange={meta => {
- syncPlayback (meta)
- }}/>)}
- </MainArea>)
- }) satisfies FC
|