Browse Source

#8 とりあへずは完了

#23
みてるぞ 1 month ago
parent
commit
fa547bc62f
2 changed files with 124 additions and 11 deletions
  1. +114
    -0
      frontend/src/components/NicoViewer.tsx
  2. +10
    -11
      frontend/src/pages/PostDetailPage.tsx

+ 114
- 0
frontend/src/components/NicoViewer.tsx View File

@@ -0,0 +1,114 @@
import React, { useRef, useLayoutEffect, useEffect, useState, CSSProperties } from 'react'
import axios from 'axios'
import { Link, useNavigate, useLocation } from 'react-router-dom'
import { API_BASE_URL } from '../config'

type Props = { id: string,
width: number,
height: number,
style?: CSSProperties }


const NicoViewer: React.FC = (props: Props) => {
const { id, width, height, style = { } } = props

const iframeRef = useRef<HTMLIFrameElement> (null)
const [screenWidth, setScreenWidth] = useState<CSSProperties['width']> ()
const [screenHeight, setScreenHeight] = useState<CSSProperties['height']> ()
const [isLandscape, setIsLandScape] = useState<boolean> (false)
const [isFullScreen, setIsFullScreen] = useState<boolean> (false)

const src = `https://embed.nicovideo.jp/watch/${id}?persistence=1&oldScript=1&referer=&from=0&allowProgrammaticFullScreen=1`;

const styleFullScreen: CSSProperties = isFullScreen ? {
top: 0,
left: isLandscape ? 0 : '100%',
position: 'fixed',
width: screenWidth,
height: screenHeight,
zIndex: 2147483647,
maxWidth: 'none',
transformOrigin: '0% 0%',
transform: isLandscape ? 'none' : 'rotate(90deg)',
WebkitTransformOrigin: '0% 0%',
WebkitTransform: isLandscape ? 'none' : 'rotate(90deg)',
} : {};

const margedStyle = {
border: 'none',
maxWidth: '100%',
...style,
...styleFullScreen,
};

useEffect(() => {
const onMessage = (event: MessageEvent<any>) => {
if (!iframeRef.current || event.source !== iframeRef.current.contentWindow) return;
if (event.data.eventName === 'enterProgrammaticFullScreen') {
setIsFullScreen(true);
} else if (event.data.eventName === 'exitProgrammaticFullScreen') {
setIsFullScreen(false);
}
};

window.addEventListener('message', onMessage);

return () => {
window.removeEventListener('message', onMessage);
};
}, []);

useLayoutEffect(() => {
if (!isFullScreen) return;

const initialScrollX = window.scrollX;
const initialScrollY = window.scrollY;
let timer: NodeJS.Timeout;
let ended = false;

const pollingResize = () => {
if (ended) return;

const isLandscape = window.innerWidth >= window.innerHeight;
const windowWidth = `${isLandscape ? window.innerWidth : window.innerHeight}px`;
const windowHeight = `${isLandscape ? window.innerHeight : window.innerWidth}px`;

setIsLandScape(isLandscape);
setScreenWidth(windowWidth);
setScreenHeight(windowHeight);
timer = setTimeout(startPollingResize, 200);
}

const startPollingResize = () => {
if (window.requestAnimationFrame) {
window.requestAnimationFrame(pollingResize);
} else {
pollingResize();
}
}

startPollingResize();

return () => {
clearTimeout(timer);
ended = true;
window.scrollTo(initialScrollX, initialScrollY);
};
}, [isFullScreen]);

useEffect(() => {
if (!isFullScreen) return;
window.scrollTo(0, 0);
}, [screenWidth, screenHeight, isFullScreen]);

return <iframe ref={iframeRef}
src={src}
width={width}
height={height}
style={margedStyle}
allowFullScreen
allow="autoplay" />
}


export default NicoViewer

+ 10
- 11
frontend/src/pages/PostDetailPage.tsx View File

@@ -2,6 +2,7 @@ import React, { useEffect, useState } from 'react'
import { Link, useLocation, useParams } from 'react-router-dom' import { Link, useLocation, useParams } from 'react-router-dom'
import axios from 'axios' import axios from 'axios'
import { API_BASE_URL, SITE_TITLE } from '../config' import { API_BASE_URL, SITE_TITLE } from '../config'
import NicoViewer from '../components/NicoViewer'


type Post = { id: number type Post = { id: number
url: string url: string
@@ -28,19 +29,17 @@ const PostDetailPage = () => {


document.title = `${ post.url } | ${ SITE_TITLE }` document.title = `${ post.url } | ${ SITE_TITLE }`


const url = new URL (post.url)

return ( return (
<div className="p-4"> <div className="p-4">
<h1 className="text-2xl font-bold mb-2">{post.url}</h1>
<img src={post.thumbnail} alt={post.url} className="mb-4 max-w-lg" />
<p className="mb-2">{post.description}</p>
<div className="text-sm text-gray-600">
タグ:
{post.tags.map(tag => (
<span key={tag.name} className="ml-2 px-2 py-1 bg-gray-200 rounded">
#{tag.name}
</span>
))}
</div>
{url.hostname.split ('.').slice (-2).join ('.') === 'nicovideo.jp' ? (
<NicoViewer id={url.pathname.match (/(?<=\/watch\/)[a-zA-Z0-9]+?(?=\/|$)/)[0]}
width="640"
height="360" />
) : (
<img src={post.thumbnail} alt={post.url} className="mb-4 w-full" />
)}
</div> </div>
) )
} }


Loading…
Cancel
Save