|
|
|
@@ -2,6 +2,7 @@ import { useEffect, useRef, useState } from 'react' |
|
|
|
import { Helmet } from 'react-helmet-async' |
|
|
|
import { useParams } from 'react-router-dom' |
|
|
|
|
|
|
|
import ErrorScreen from '@/components/ErrorScreen' |
|
|
|
import PostEmbed from '@/components/PostEmbed' |
|
|
|
import PrefetchLink from '@/components/PrefetchLink' |
|
|
|
import TagDetailSidebar from '@/components/TagDetailSidebar' |
|
|
|
@@ -46,6 +47,7 @@ export default (() => { |
|
|
|
const [content, setContent] = useState ('') |
|
|
|
const [loading, setLoading] = useState (false) |
|
|
|
const [sending, setSending] = useState (false) |
|
|
|
const [status, setStatus] = useState (200) |
|
|
|
const [theatre, setTheatre] = useState<Theatre | null> (null) |
|
|
|
const [theatreInfo, setTheatreInfo] = useState<TheatreInfo> (INITIAL_THEATRE_INFO) |
|
|
|
const [post, setPost] = useState<Post | null> (null) |
|
|
|
@@ -60,7 +62,7 @@ export default (() => { |
|
|
|
}, [videoLength]) |
|
|
|
|
|
|
|
useEffect (() => { |
|
|
|
lastCommentNoRef.current = comments.at (-1)?.no ?? 0 |
|
|
|
lastCommentNoRef.current = comments[0]?.no ?? 0 |
|
|
|
}, [comments]) |
|
|
|
|
|
|
|
useEffect (() => { |
|
|
|
@@ -85,7 +87,7 @@ export default (() => { |
|
|
|
} |
|
|
|
catch (error) |
|
|
|
{ |
|
|
|
console.error (error) |
|
|
|
setStatus ((error as any)?.response.status ?? 200) |
|
|
|
} |
|
|
|
}) () |
|
|
|
|
|
|
|
@@ -94,12 +96,6 @@ export default (() => { |
|
|
|
} |
|
|
|
}, [id]) |
|
|
|
|
|
|
|
useEffect (() => { |
|
|
|
commentsRef.current?.scrollTo ({ |
|
|
|
top: commentsRef.current.scrollHeight, |
|
|
|
behavior: 'smooth' }) |
|
|
|
}, [comments]) |
|
|
|
|
|
|
|
useEffect (() => { |
|
|
|
if (!(id)) |
|
|
|
return |
|
|
|
@@ -122,7 +118,7 @@ export default (() => { |
|
|
|
if (!(cancelled) && newComments.length > 0) |
|
|
|
{ |
|
|
|
lastCommentNoRef.current = newComments[newComments.length - 1].no |
|
|
|
setComments (prev => [...prev, ...newComments]) |
|
|
|
setComments (prev => [...newComments, ...prev]) |
|
|
|
} |
|
|
|
|
|
|
|
const currentInfo = theatreInfoRef.current |
|
|
|
@@ -232,6 +228,9 @@ export default (() => { |
|
|
|
embedRef.current?.seek (targetTime) |
|
|
|
} |
|
|
|
|
|
|
|
if (status >= 400) |
|
|
|
return <ErrorScreen status={status}/> |
|
|
|
|
|
|
|
return ( |
|
|
|
<div className="md:flex md:flex-1"> |
|
|
|
<Helmet> |
|
|
|
@@ -270,7 +269,7 @@ export default (() => { |
|
|
|
|
|
|
|
<SidebarComponent> |
|
|
|
<form |
|
|
|
className="w-full h-auto border border-black dark:border-white rounded" |
|
|
|
className="w-auto h-auto border border-black dark:border-white rounded mx-2" |
|
|
|
onSubmit={async e => { |
|
|
|
e.preventDefault () |
|
|
|
|
|
|
|
@@ -282,28 +281,20 @@ export default (() => { |
|
|
|
setSending (true) |
|
|
|
await apiPost (`/theatres/${ id }/comments`, { content }) |
|
|
|
setContent ('') |
|
|
|
commentsRef.current?.scrollTo ({ |
|
|
|
top: commentsRef.current.scrollHeight, |
|
|
|
behavior: 'smooth' }) |
|
|
|
commentsRef.current?.scrollTo ({ top: 0, behavior: 'smooth' }) |
|
|
|
} |
|
|
|
finally |
|
|
|
{ |
|
|
|
setSending (false) |
|
|
|
} |
|
|
|
}}> |
|
|
|
<div className="p-2"> |
|
|
|
現在の同接数:{theatreInfo.watchingUsers.length} |
|
|
|
</div> |
|
|
|
|
|
|
|
<div className="overflow-x-hidden overflow-y-scroll text-wrap w-full h-32 |
|
|
|
border rounded"> |
|
|
|
<ul className="list-inside list-disc"> |
|
|
|
{theatreInfo.watchingUsers.map (user => ( |
|
|
|
<li key={user.id} className="px-4 py-1 text-sm"> |
|
|
|
{user.name || `名もなきニジラー(#${ user.id })`} |
|
|
|
</li>))} |
|
|
|
</ul> |
|
|
|
</div> |
|
|
|
<input |
|
|
|
className="w-full p-2 border rounded" |
|
|
|
type="text" |
|
|
|
placeholder="ここにコメントを入力" |
|
|
|
value={content} |
|
|
|
onChange={e => setContent (e.target.value)} |
|
|
|
disabled={sending}/> |
|
|
|
|
|
|
|
<div |
|
|
|
ref={commentsRef} |
|
|
|
@@ -324,14 +315,23 @@ export default (() => { |
|
|
|
</div> |
|
|
|
</div>))} |
|
|
|
</div> |
|
|
|
|
|
|
|
<input |
|
|
|
className="w-full p-2 border rounded" |
|
|
|
type="text" |
|
|
|
value={content} |
|
|
|
onChange={e => setContent (e.target.value)} |
|
|
|
disabled={sending}/> |
|
|
|
</form> |
|
|
|
|
|
|
|
<div className="w-auto h-auto border border-black dark:border-white rounded mx-2 mt-4"> |
|
|
|
<div className="p-2"> |
|
|
|
現在の同接数:{theatreInfo.watchingUsers.length} |
|
|
|
</div> |
|
|
|
|
|
|
|
<div className="overflow-x-hidden overflow-y-scroll text-wrap w-full h-32 |
|
|
|
border rounded"> |
|
|
|
<ul className="list-inside list-disc"> |
|
|
|
{theatreInfo.watchingUsers.map (user => ( |
|
|
|
<li key={user.id} className="px-4 py-1 text-sm"> |
|
|
|
{user.name || `名もなきニジラー(#${ user.id })`} |
|
|
|
</li>))} |
|
|
|
</ul> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</SidebarComponent> |
|
|
|
|
|
|
|
<div className="md:hidden"> |
|
|
|
|