From 0c46cf28db7b371ba3e5948659b30d42a59e385c Mon Sep 17 00:00:00 2001 From: miteruzo Date: Sun, 13 Jul 2025 02:46:13 +0900 Subject: [PATCH] #49 --- frontend/package-lock.json | 48 +++++- frontend/package.json | 2 + frontend/src/App.tsx | 2 +- frontend/src/components/NicoViewer.tsx | 127 ++++++++------- frontend/src/components/PostEditForm.tsx | 7 +- frontend/src/components/TagDetailSidebar.tsx | 18 +-- frontend/src/components/TagSearch.tsx | 40 ++--- frontend/src/components/TagSearchBox.tsx | 16 +- frontend/src/components/TagSidebar.tsx | 5 +- frontend/src/components/TopNav.tsx | 150 +++++++++--------- frontend/src/components/common/TabGroup.tsx | 4 +- .../src/components/users/InheritDialogue.tsx | 10 +- .../src/components/users/UserCodeDialogue.tsx | 14 +- frontend/src/config.sample.ts | 2 +- frontend/src/consts.ts | 18 +-- frontend/src/pages/posts/PostDetailPage.tsx | 137 ++++++++-------- frontend/src/pages/posts/PostListPage.tsx | 23 +-- frontend/src/pages/posts/PostNewPage.tsx | 22 +-- frontend/src/pages/tags/NicoTagListPage.tsx | 5 +- frontend/src/pages/users/SettingPage.tsx | 9 +- frontend/src/pages/wiki/WikiDetailPage.tsx | 74 +++++---- frontend/src/pages/wiki/WikiDiffPage.tsx | 8 +- frontend/src/pages/wiki/WikiEditPage.tsx | 100 ++++++------ frontend/src/pages/wiki/WikiHistoryPage.tsx | 9 +- frontend/src/pages/wiki/WikiNewPage.tsx | 91 +++++------ frontend/src/pages/wiki/WikiSearchPage.tsx | 13 +- frontend/src/router.tsx | 10 +- frontend/src/types.ts | 5 +- frontend/tsconfig.app.json | 4 +- 29 files changed, 513 insertions(+), 460 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index c230e92..79ed7ca 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -30,6 +30,8 @@ "devDependencies": { "@eslint/js": "^9.25.0", "@types/axios": "^0.9.36", + "@types/markdown-it": "^14.1.2", + "@types/node": "^24.0.13", "@types/react": "^19.1.2", "@types/react-dom": "^19.1.2", "@types/react-router-dom": "^5.3.3", @@ -1993,6 +1995,24 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/markdown-it": { + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, "node_modules/@types/mdast": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", @@ -2002,16 +2022,34 @@ "@types/unist": "*" } }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/ms": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", "license": "MIT" }, + "node_modules/@types/node": { + "version": "24.0.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.13.tgz", + "integrity": "sha512-Qm9OYVOFHFYg3wJoTSrz80hoec5Lia/dPp84do3X7dZvLikQvM1YpmvTBEdIr/e+U8HTkFjLHLnl78K/qjf+jQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.8.0" + } + }, "node_modules/@types/react": { "version": "19.1.4", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.4.tgz", "integrity": "sha512-EB1yiiYdvySuIITtD5lhW4yPyJ31RkJkkDw794LaQYrxCSaQV/47y5o1FMC4zF9ZyjUjzJMZwbovEnT5yHTW6g==", + "dev": true, "license": "MIT", "dependencies": { "csstype": "^3.0.2" @@ -2021,7 +2059,7 @@ "version": "19.1.5", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.5.tgz", "integrity": "sha512-CMCjrWucUBZvohgZxkjd6S9h0nZxXjzus6yDfUb+xLxYM7VvjKNH1tQrE9GWLql1XoOP4/Ds3bwFqShHUYraGg==", - "devOptional": true, + "dev": true, "license": "MIT", "peerDependencies": { "@types/react": "^19.0.0" @@ -2882,6 +2920,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true, "license": "MIT" }, "node_modules/debug": { @@ -6289,6 +6328,13 @@ "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", "license": "MIT" }, + "node_modules/undici-types": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", + "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", + "dev": true, + "license": "MIT" + }, "node_modules/unified": { "version": "11.0.5", "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", diff --git a/frontend/package.json b/frontend/package.json index f808241..02d1c79 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -31,6 +31,8 @@ "devDependencies": { "@eslint/js": "^9.25.0", "@types/axios": "^0.9.36", + "@types/markdown-it": "^14.1.2", + "@types/node": "^24.0.13", "@types/react": "^19.1.2", "@types/react-dom": "^19.1.2", "@types/react-router-dom": "^5.3.3", diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index b195c44..691fc67 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -56,7 +56,7 @@ export default () => { <>
- +
} /> diff --git a/frontend/src/components/NicoViewer.tsx b/frontend/src/components/NicoViewer.tsx index ac4caec..71314f5 100644 --- a/frontend/src/components/NicoViewer.tsx +++ b/frontend/src/components/NicoViewer.tsx @@ -1,15 +1,17 @@ -import React, { useRef, useLayoutEffect, useEffect, useState, CSSProperties } from 'react' -import { Link, useNavigate, useLocation } from 'react-router-dom' +import { useRef, useLayoutEffect, useEffect, useState } from 'react' type Props = { id: string, - width: number, - height: number, - style?: CSSProperties } + width: number, + height: number, + style?: CSSProperties } +import type { CSSProperties } from 'react' -const NicoViewer: React.FC = (props: Props) => { + +export default (props: Props) => { const { id, width, height, style = { } } = props const iframeRef = useRef (null) + const [screenWidth, setScreenWidth] = useState () const [screenHeight, setScreenHeight] = useState () const [landscape, setLandscape] = useState (false) @@ -18,97 +20,92 @@ const NicoViewer: React.FC = (props: Props) => { const src = `https://embed.nicovideo.jp/watch/${id}?persistence=1&oldScript=1&referer=&from=0&allowProgrammaticFullScreen=1`; const styleFullScreen: CSSProperties = fullScreen ? { - top: 0, - left: landscape ? 0 : '100%', - position: 'fixed', - width: screenWidth, - height: screenHeight, - zIndex: 2147483647, - maxWidth: 'none', - transformOrigin: '0% 0%', - transform: landscape ? 'none' : 'rotate(90deg)', - WebkitTransformOrigin: '0% 0%', - WebkitTransform: landscape ? 'none' : 'rotate(90deg)', - } : {}; + top: 0, + left: landscape ? 0 : '100%', + position: 'fixed', + width: screenWidth, + height: screenHeight, + zIndex: 2147483647, + maxWidth: 'none', + transformOrigin: '0% 0%', + transform: landscape ? 'none' : 'rotate(90deg)', + WebkitTransformOrigin: '0% 0%', + WebkitTransform: landscape ? 'none' : 'rotate(90deg)' } : {}; const margedStyle = { - border: 'none', - maxWidth: '100%', - ...style, - ...styleFullScreen, - }; + border: 'none', + maxWidth: '100%', + ...style, + ...styleFullScreen } - useEffect(() => { + useEffect (() => { const onMessage = (event: MessageEvent) => { - if (!iframeRef.current || event.source !== iframeRef.current.contentWindow) return; - if (event.data.eventName === 'enterProgrammaticFullScreen') { - setFullScreen(true); - } else if (event.data.eventName === 'exitProgrammaticFullScreen') { - setFullScreen(false); - } - }; + if (!(iframeRef.current) + || (event.source !== iframeRef.current.contentWindow)) + return + + if (event.data.eventName === 'enterProgrammaticFullScreen') + setFullScreen (true) + else if (event.data.eventName === 'exitProgrammaticFullScreen') + setFullScreen (false) + } - addEventListener('message', onMessage); + addEventListener ('message', onMessage) - return () => { - removeEventListener('message', onMessage); - }; - }, []); + return () => removeEventListener ('message', onMessage) + }, []) useLayoutEffect(() => { if (!(fullScreen)) return - const initialScrollX = window.scrollX - const initialScrollY = window.scrollY + const initialScrollX = scrollX + const initialScrollY = scrollY let timer: NodeJS.Timeout let ended = false const pollingResize = () => { if (ended) - return + return - const landscape = window.innerWidth >= window.innerHeight - const windowWidth = `${landscape ? window.innerWidth : window.innerHeight}px` - const windowHeight = `${landscape ? window.innerHeight : window.innerWidth}px` + const landscape = innerWidth >= innerHeight + const windowWidth = `${landscape ? innerWidth : innerHeight}px` + const windowHeight = `${landscape ? innerHeight : innerWidth}px` - setLandScape (Landscape) + setLandscape (landscape) setScreenWidth (windowWidth) setScreenHeight (windowHeight) timer = setTimeout (startPollingResize, 200) } const startPollingResize = () => { - if (window.requestAnimationFrame) { - window.requestAnimationFrame(pollingResize); - } else { - pollingResize(); - } + if (requestAnimationFrame) + requestAnimationFrame (pollingResize) + else + pollingResize () } - startPollingResize(); + startPollingResize () return () => { - clearTimeout(timer); - ended = true; - window.scrollTo(initialScrollX, initialScrollY); - }; - }, [fullScreen]); + clearTimeout (timer) + ended = true + scrollTo (initialScrollX, initialScrollY) + } + }, [fullScreen]) - useEffect(() => { + useEffect (() => { if (!(fullScreen)) return scrollTo (0, 0) }, [screenWidth, screenHeight, fullScreen]) - return