From e0aa14f0b52b677a1da6ef3c967e958a280d2fcb Mon Sep 17 00:00:00 2001 From: miteruzo Date: Sat, 4 Jul 2026 14:09:18 +0900 Subject: [PATCH 1/4] #385 --- frontend/src/components/MaterialSidebar.tsx | 2 +- frontend/src/components/TagDetailSidebar.tsx | 2 +- frontend/src/components/TagSidebar.tsx | 2 +- .../components/layout/SidebarComponent.tsx | 342 +++++++++++++++++- .../src/pages/theatres/TheatreDetailPage.tsx | 92 +++-- 5 files changed, 396 insertions(+), 44 deletions(-) diff --git a/frontend/src/components/MaterialSidebar.tsx b/frontend/src/components/MaterialSidebar.tsx index 8428c09..e686a61 100644 --- a/frontend/src/components/MaterialSidebar.tsx +++ b/frontend/src/components/MaterialSidebar.tsx @@ -489,7 +489,7 @@ const MaterialSidebar: FC = () => {
- +
= ({ post, sp }) => { }, [baseTags]) return ( - + = ({ posts, onClick }) => { ) return ( - +
diff --git a/frontend/src/components/layout/SidebarComponent.tsx b/frontend/src/components/layout/SidebarComponent.tsx index 779755e..922b787 100644 --- a/frontend/src/components/layout/SidebarComponent.tsx +++ b/frontend/src/components/layout/SidebarComponent.tsx @@ -1,19 +1,307 @@ import { motion } from 'framer-motion' import { Helmet } from 'react-helmet-async' -import type { FC, ReactNode } from 'react' +import { cn } from '@/lib/utils' -type Props = { children: ReactNode } +import type { CSSProperties, FC, MouseEvent, PointerEvent, ReactNode } from 'react' +import { useCallback, useEffect, useRef, useState } from 'react' + +type SidebarSide = 'left' | 'right' + +type Props = { + children: ReactNode + className?: string + maxWidth?: number + onWidthChange?: (width: number) => void + sidebarKey: string + side?: SidebarSide } + +type ResizeState = { + pointerId: number + startWidth: number + startX: number + side: SidebarSide } + +type PendingPressState = ResizeState & { startY: number } + +const DEFAULT_SIDEBAR_WIDTH = 256 +const MIN_SIDEBAR_WIDTH = 192 +const MIN_MAIN_WIDTH = 360 +const MAX_VIEWPORT_WIDTH_RATIO = .8 +const LONG_PRESS_DELAY_MS = 350 +const TOUCH_MOVE_THRESHOLD_PX = 8 -const SidebarComponent: FC = ({ children }) => ( - - - - + + - {children} - ) +
+ {children} +
-export default SidebarComponent \ No newline at end of file +
+ ) +} + +export default SidebarComponent diff --git a/frontend/src/pages/theatres/TheatreDetailPage.tsx b/frontend/src/pages/theatres/TheatreDetailPage.tsx index e4db3d2..6efb2cc 100644 --- a/frontend/src/pages/theatres/TheatreDetailPage.tsx +++ b/frontend/src/pages/theatres/TheatreDetailPage.tsx @@ -10,6 +10,7 @@ import PrefetchLink from '@/components/PrefetchLink' import TagLink from '@/components/TagLink' import FieldError from '@/components/common/FieldError' import { useDialogue } from '@/components/dialogues/DialogueProvider' +import SidebarComponent from '@/components/layout/SidebarComponent' import { Button } from '@/components/ui/button' import { SITE_TITLE } from '@/config' import { CATEGORIES, CATEGORY_NAMES } from '@/consts' @@ -52,6 +53,8 @@ const INITIAL_WEIGHTS: TheatrePostSelectionWeights = const LAYOUT_STORAGE_KEY = 'theatre-layout-mode' const TAG_FLOW_STORAGE_KEY = 'theatre-tag-flow' +const MIN_MAIN_WIDTH = 360 +const SIDEBAR_GAP_WIDTH = 16 const LAYOUT_LABELS: Record = { threeColumns: '3 列', @@ -211,11 +214,16 @@ const TheatreDetailPage: FC = ({ user }: Props) => { const commentsRef = useRef (null) const embedRef = useRef (null) const loadingRef = useRef (false) + const sidebarContainerRef = useRef (null) const theatreInfoRef = useRef (INITIAL_THEATRE_INFO) const theatreInfoReceivedAtRef = useRef (performance.now ()) const videoLengthRef = useRef (0) const lastCommentNoRef = useRef (0) + const [leftSidebarWidth, setLeftSidebarWidth] = useState (256) + const [rightSidebarWidth, setRightSidebarWidth] = useState (256) + const [sidebarContainerWidth, setSidebarContainerWidth] = useState ( + () => typeof window === 'undefined' ? 0 : window.innerWidth) const [comments, setComments] = useState ([]) const [content, setContent] = useState ('') const [editingPost, setEditingPost] = useState (null) @@ -324,6 +332,17 @@ const TheatreDetailPage: FC = ({ user }: Props) => { lastCommentNoRef.current = comments[0]?.no ?? 0 }, [comments]) + useEffect (() => { + const updateSidebarContainerWidth = () => { + const nextWidth = sidebarContainerRef.current?.clientWidth ?? window.innerWidth + setSidebarContainerWidth (nextWidth) + } + + updateSidebarContainerWidth () + window.addEventListener ('resize', updateSidebarContainerWidth) + return () => window.removeEventListener ('resize', updateSidebarContainerWidth) + }, []) + useEffect (() => { if (!(id)) return @@ -639,6 +658,26 @@ const TheatreDetailPage: FC = ({ user }: Props) => { if (status >= 400) return + const singleSidebarMaxWidth = Math.max ( + 192, + sidebarContainerWidth - MIN_MAIN_WIDTH - SIDEBAR_GAP_WIDTH) + const leftSidebarMaxWidth = layoutMode === 'threeColumns' + ? Math.max ( + 192, + sidebarContainerWidth + - rightSidebarWidth + - MIN_MAIN_WIDTH + - SIDEBAR_GAP_WIDTH * 2) + : singleSidebarMaxWidth + const rightSidebarMaxWidth = layoutMode === 'threeColumns' + ? Math.max ( + 192, + sidebarContainerWidth + - leftSidebarWidth + - MIN_MAIN_WIDTH + - SIDEBAR_GAP_WIDTH * 2) + : singleSidebarMaxWidth + const tagPanel = (
@@ -806,30 +845,26 @@ const TheatreDetailPage: FC = ({ user }: Props) => { {theatre && {`${ theatreTitle } | ${ SITE_TITLE }`}} -
+
{layoutMode !== 'tagsBottom' && ( - -
- {tagPanel} -
-
)} + + {tagPanel} + )} -
+ className="order-1 min-w-0 flex-1 space-y-4 md:order-none + md:min-w-[360px] md:overflow-y-auto"> +
- {commentsPanel} - {participantsPanel} - )} + +
+ {commentsPanel} + {participantsPanel} +
+
)}
) } -- 2.34.1 From 7e0b6910f6c22c75e17e3824fa0d852f9c856177 Mon Sep 17 00:00:00 2001 From: miteruzo Date: Sat, 4 Jul 2026 14:34:16 +0900 Subject: [PATCH 2/4] #385 --- frontend/src/components/layout/SidebarComponent.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/layout/SidebarComponent.tsx b/frontend/src/components/layout/SidebarComponent.tsx index 922b787..2037066 100644 --- a/frontend/src/components/layout/SidebarComponent.tsx +++ b/frontend/src/components/layout/SidebarComponent.tsx @@ -86,11 +86,13 @@ const SidebarComponent: FC = ({ const getClampedWidth = useCallback (( nextWidth: number, containerWidth: number): number => { - const dynamicMaxWidth = getSidebarMaxWidth (containerWidth) + const viewportWidth = + typeof window === 'undefined' ? containerWidth : window.innerWidth + const viewportMaxWidth = Math.floor (viewportWidth * MAX_VIEWPORT_WIDTH_RATIO) const maxAllowedWidth = maxWidth == null - ? dynamicMaxWidth - : Math.max (MIN_SIDEBAR_WIDTH, Math.min (dynamicMaxWidth, maxWidth)) + ? getSidebarMaxWidth (containerWidth) + : Math.max (MIN_SIDEBAR_WIDTH, Math.min (viewportMaxWidth, maxWidth)) return Math.min (maxAllowedWidth, Math.max (MIN_SIDEBAR_WIDTH, nextWidth)) }, [maxWidth]) -- 2.34.1 From 88b0195e0311b5a799137867163894dcd755eb38 Mon Sep 17 00:00:00 2001 From: miteruzo Date: Sat, 4 Jul 2026 15:41:37 +0900 Subject: [PATCH 3/4] #385 --- .../layout/SidebarComponent.test.tsx | 99 +++++++++++++++++++ .../components/layout/SidebarComponent.tsx | 15 +-- .../pages/theatres/TheatreDetailPage.test.tsx | 68 +++++++------ .../src/pages/theatres/TheatreDetailPage.tsx | 9 +- 4 files changed, 149 insertions(+), 42 deletions(-) create mode 100644 frontend/src/components/layout/SidebarComponent.test.tsx diff --git a/frontend/src/components/layout/SidebarComponent.test.tsx b/frontend/src/components/layout/SidebarComponent.test.tsx new file mode 100644 index 0000000..7b55142 --- /dev/null +++ b/frontend/src/components/layout/SidebarComponent.test.tsx @@ -0,0 +1,99 @@ +import { fireEvent, render, screen, waitFor } from '@testing-library/react' +import { HelmetProvider } from 'react-helmet-async' +import { beforeEach, describe, expect, it, vi } from 'vitest' + +import SidebarComponent from '@/components/layout/SidebarComponent' + + +const sidebarWidth = (element: Element | null): string => + (element as HTMLElement).style.getPropertyValue ('--sidebar-width') + + +const sidebarRoot = (container: HTMLElement): Element => + container.querySelector ('[style*="--sidebar-width"]')! + + +const renderSidebar = ( + props: Partial[0]> = {}, + containerWidth = 800, +) => { + Object.defineProperty (HTMLElement.prototype, 'clientWidth', { + configurable: true, + get () { + return this.hasAttribute ('data-sidebar-container') ? containerWidth : 0 + }, + }) + + const rendered = render ( + +
+ + Sidebar body + +
+
) + + return rendered +} + + +describe ('SidebarComponent', () => { + beforeEach (() => { + Object.defineProperty (window, 'innerWidth', { + configurable: true, + value: 1_000, + }) + + Object.defineProperty (HTMLElement.prototype, 'setPointerCapture', { + configurable: true, + value: vi.fn (), + }) + Object.defineProperty (HTMLElement.prototype, 'releasePointerCapture', { + configurable: true, + value: vi.fn (), + }) + Object.defineProperty (HTMLElement.prototype, 'hasPointerCapture', { + configurable: true, + value: vi.fn (() => true), + }) + + vi.spyOn (HTMLElement.prototype, 'setPointerCapture').mockImplementation (() => { + ; + }) + vi.spyOn (HTMLElement.prototype, 'releasePointerCapture').mockImplementation (() => { + ; + }) + vi.spyOn (HTMLElement.prototype, 'hasPointerCapture').mockReturnValue (true) + }) + + it ('uses maxWidth without applying the container main-width reserve twice', async () => { + localStorage.setItem ('sidebar:test-sidebar:left:width', '700') + const onWidthChange = vi.fn () + + const { container } = renderSidebar ({ maxWidth: 700, onWidthChange }, 800) + + await waitFor (() => { + expect (sidebarWidth (sidebarRoot (container))).toBe ('700px') + }) + expect (onWidthChange).toHaveBeenLastCalledWith (700) + }) + + it ('resets width and removes only its storage key on double click', async () => { + localStorage.setItem ('sidebar:test-sidebar:right:width', '420') + localStorage.setItem ('sidebar:other-sidebar:right:width', '480') + + const { container } = renderSidebar ({ side: 'right' }, 900) + + await waitFor (() => { + expect (sidebarWidth (sidebarRoot (container))).toBe ('420px') + }) + + fireEvent.doubleClick (screen.getByRole ('separator', { name: 'サイドバー幅' })) + + await waitFor (() => { + expect (sidebarWidth (sidebarRoot (container))).toBe ('256px') + }) + expect (localStorage.getItem ('sidebar:test-sidebar:right:width')).toBeNull () + expect (localStorage.getItem ('sidebar:other-sidebar:right:width')).toBe ('480') + }) +}) diff --git a/frontend/src/components/layout/SidebarComponent.tsx b/frontend/src/components/layout/SidebarComponent.tsx index 2037066..a96b7a2 100644 --- a/frontend/src/components/layout/SidebarComponent.tsx +++ b/frontend/src/components/layout/SidebarComponent.tsx @@ -47,23 +47,16 @@ const getSidebarMaxWidth = (containerWidth: number): number => { } -const clampSidebarWidth = (width: number, containerWidth: number): number => - Math.min (getSidebarMaxWidth (containerWidth), Math.max (MIN_SIDEBAR_WIDTH, width)) - - const readStoredSidebarWidth = ( sidebarKey: string, - side: SidebarSide, - containerWidth: number): number => { + side: SidebarSide): number => { if (typeof window === 'undefined') return DEFAULT_SIDEBAR_WIDTH const value = window.localStorage.getItem (storageKeyForSidebar (sidebarKey, side)) const width = value == null ? Number.NaN : Number (value) - return Number.isFinite (width) - ? clampSidebarWidth (width, containerWidth) - : clampSidebarWidth (DEFAULT_SIDEBAR_WIDTH, containerWidth) + return Number.isFinite (width) ? width : DEFAULT_SIDEBAR_WIDTH } @@ -101,7 +94,7 @@ const SidebarComponent: FC = ({ typeof window === 'undefined' ? DEFAULT_SIDEBAR_WIDTH : getClampedWidth ( - readStoredSidebarWidth (sidebarKey, side, window.innerWidth), + readStoredSidebarWidth (sidebarKey, side), window.innerWidth))) const [resizing, setResizing] = useState (false) const widthRef = useRef (width) @@ -256,7 +249,7 @@ const SidebarComponent: FC = ({ useEffect (() => { const containerWidth = getContainerWidth () const nextWidth = getClampedWidth ( - readStoredSidebarWidth (sidebarKey, side, containerWidth), + readStoredSidebarWidth (sidebarKey, side), containerWidth) widthRef.current = nextWidth setWidth (nextWidth) diff --git a/frontend/src/pages/theatres/TheatreDetailPage.test.tsx b/frontend/src/pages/theatres/TheatreDetailPage.test.tsx index 2c87aba..745ea4e 100644 --- a/frontend/src/pages/theatres/TheatreDetailPage.test.tsx +++ b/frontend/src/pages/theatres/TheatreDetailPage.test.tsx @@ -105,6 +105,20 @@ const tagSection = (): HTMLElement => screen.getAllByRole ('heading', { name: 'タグ' })[0].closest ('section')! const mockDefaultApi = () => { + let commentsFetched = false + let currentTheatreInfo = buildTheatreInfo ({ + postId: currentPost.id, + postStartedAt: '2026-01-02T03:04:05.000Z', + postElapsedMs: 1_000, + watchingUsers, + skipVote: { + votesCount: 0, + requiredCount: 2, + watchingUsersCount: watchingUsers.length, + voted: false, + }, + }) + api.apiGet.mockImplementation ((path: string) => { switch (path) { @@ -112,13 +126,17 @@ const mockDefaultApi = () => { return Promise.resolve (theatre) case '/theatres/7/comments': + if (commentsFetched) + return Promise.resolve ([]) + + commentsFetched = true return Promise.resolve ([ - buildTheatreComment ({ - theatreId: 7, - no: 2, - user: { id: 1, name: 'tester' }, - content: '視聴コメント', - }), + buildTheatreComment ({ + theatreId: 7, + no: 2, + user: { id: 1, name: 'tester' }, + content: '視聴コメント', + }), ]) case '/theatres/7/programmes': @@ -136,32 +154,22 @@ const mockDefaultApi = () => { switch (path) { case '/theatres/7/watching': - return Promise.resolve (buildTheatreInfo ({ - postId: currentPost.id, - postStartedAt: '2026-01-02T03:04:05.000Z', - postElapsedMs: 1_000, - watchingUsers, - skipVote: { - votesCount: 0, - requiredCount: 2, - watchingUsersCount: watchingUsers.length, - voted: false, - }, - })) + return Promise.resolve (currentTheatreInfo) case '/theatres/7/skip_vote': - return Promise.resolve (buildTheatreInfo ({ - postId: currentPost.id, - postStartedAt: '2026-01-02T03:04:05.000Z', - postElapsedMs: 2_000, - watchingUsers, - skipVote: { - votesCount: 1, - requiredCount: 2, - watchingUsersCount: watchingUsers.length, - voted: true, - }, - })) + currentTheatreInfo = buildTheatreInfo ({ + postId: currentPost.id, + postStartedAt: '2026-01-02T03:04:05.000Z', + postElapsedMs: 2_000, + watchingUsers, + skipVote: { + votesCount: 1, + requiredCount: 2, + watchingUsersCount: watchingUsers.length, + voted: true, + }, + }) + return Promise.resolve (currentTheatreInfo) default: return Promise.reject (new Error (`Unexpected PUT ${ path }`)) diff --git a/frontend/src/pages/theatres/TheatreDetailPage.tsx b/frontend/src/pages/theatres/TheatreDetailPage.tsx index 6efb2cc..c414048 100644 --- a/frontend/src/pages/theatres/TheatreDetailPage.tsx +++ b/frontend/src/pages/theatres/TheatreDetailPage.tsx @@ -266,6 +266,7 @@ const TheatreDetailPage: FC = ({ user }: Props) => { const applyTheatreInfo = useCallback ((nextInfo: TheatreInfo) => { theatreInfoReceivedAtRef.current = performance.now () + theatreInfoRef.current = nextInfo setTheatreInfo (nextInfo) }, []) @@ -406,6 +407,9 @@ const TheatreDetailPage: FC = ({ user }: Props) => { setComments (prev => [...newComments, ...prev]) } + if (loadingRef.current) + return + const currentInfo = theatreInfoRef.current const ended = currentInfo.hostFlg @@ -427,8 +431,9 @@ const TheatreDetailPage: FC = ({ user }: Props) => { return } + const watchingRequestedAt = performance.now () const nextInfo = await apiPut (`/theatres/${ id }/watching`) - if (!(cancelled)) + if (!(cancelled) && watchingRequestedAt >= theatreInfoReceivedAtRef.current) applyTheatreInfo (nextInfo) } catch (error) @@ -557,6 +562,7 @@ const TheatreDetailPage: FC = ({ user }: Props) => { if (!(id) || !(post)) return + loadingRef.current = true setLoading (true) try { @@ -585,6 +591,7 @@ const TheatreDetailPage: FC = ({ user }: Props) => { } finally { + loadingRef.current = false setLoading (false) } } -- 2.34.1 From 3f2cddcd074aaa5aeb6c460be17c14a8f22bfb15 Mon Sep 17 00:00:00 2001 From: miteruzo Date: Sat, 4 Jul 2026 17:40:55 +0900 Subject: [PATCH 4/4] #385 --- frontend/src/components/MaterialSidebar.tsx | 36 ++++++------- frontend/src/components/TagDetailSidebar.tsx | 9 ++-- .../layout/SidebarComponent.test.tsx | 41 +++++++++++--- .../components/layout/SidebarComponent.tsx | 53 ++++++++++++------- .../src/pages/materials/MaterialBasePage.tsx | 6 ++- frontend/src/pages/posts/PostDetailPage.tsx | 12 ++--- frontend/src/pages/posts/PostListPage.tsx | 1 + 7 files changed, 101 insertions(+), 57 deletions(-) diff --git a/frontend/src/components/MaterialSidebar.tsx b/frontend/src/components/MaterialSidebar.tsx index e686a61..3fef9da 100644 --- a/frontend/src/components/MaterialSidebar.tsx +++ b/frontend/src/components/MaterialSidebar.tsx @@ -488,25 +488,23 @@ const MaterialSidebar: FC = () => {
-
- -
- - {isLoading && ( -

読込中……

)} - {isError && ( -

- タグ一覧の取得に失敗しました. -

)} - {(!isLoading && !isError) && ( -
    - {renderDesktopTree (visibleRootTags)} -
)} -
-
-
+ +
+ + {isLoading && ( +

読込中……

)} + {isError && ( +

+ タグ一覧の取得に失敗しました. +

)} + {(!isLoading && !isError) && ( +
    + {renderDesktopTree (visibleRootTags)} +
)} +
+
) } diff --git a/frontend/src/components/TagDetailSidebar.tsx b/frontend/src/components/TagDetailSidebar.tsx index debc040..10b7eb6 100644 --- a/frontend/src/components/TagDetailSidebar.tsx +++ b/frontend/src/components/TagDetailSidebar.tsx @@ -146,10 +146,13 @@ const DropSlot = ({ cat }: { cat: Category }) => { } -type Props = { post: Post; sp?: boolean } +type Props = { + className?: string + post: Post + sp?: boolean } -const TagDetailSidebar: FC = ({ post, sp }) => { +const TagDetailSidebar: FC = ({ className, post, sp }) => { sp = Boolean (sp) const qc = useQueryClient () @@ -278,7 +281,7 @@ const TagDetailSidebar: FC = ({ post, sp }) => { }, [baseTags]) return ( - + container.querySelector ('[style*="--sidebar-width"]')! +const sidebarTree = (props: Partial[0]>) => ( + +
+ + Sidebar body + +
+
) + + const renderSidebar = ( props: Partial[0]> = {}, containerWidth = 800, @@ -24,14 +34,7 @@ const renderSidebar = ( }, }) - const rendered = render ( - -
- - Sidebar body - -
-
) + const rendered = render (sidebarTree (props)) return rendered } @@ -96,4 +99,26 @@ describe ('SidebarComponent', () => { expect (localStorage.getItem ('sidebar:test-sidebar:right:width')).toBeNull () expect (localStorage.getItem ('sidebar:other-sidebar:right:width')).toBe ('480') }) + + it ('clamps current width on maxWidth changes without re-reading storage', async () => { + localStorage.setItem ('sidebar:test-sidebar:left:width', '420') + + const { container, rerender } = renderSidebar ({ maxWidth: 420 }, 900) + + await waitFor (() => { + expect (sidebarWidth (sidebarRoot (container))).toBe ('420px') + }) + + rerender (sidebarTree ({ maxWidth: 300 })) + + await waitFor (() => { + expect (sidebarWidth (sidebarRoot (container))).toBe ('300px') + }) + + rerender (sidebarTree ({ maxWidth: 500 })) + + await waitFor (() => { + expect (sidebarWidth (sidebarRoot (container))).toBe ('300px') + }) + }) }) diff --git a/frontend/src/components/layout/SidebarComponent.tsx b/frontend/src/components/layout/SidebarComponent.tsx index a96b7a2..ea4b015 100644 --- a/frontend/src/components/layout/SidebarComponent.tsx +++ b/frontend/src/components/layout/SidebarComponent.tsx @@ -69,6 +69,7 @@ const SidebarComponent: FC = ({ side = 'left', }) => { const rootRef = useRef (null) + const maxWidthRef = useRef (maxWidth) const getContainerWidth = useCallback (() => { const containerWidth = rootRef.current ?.closest ('[data-sidebar-container]') @@ -83,12 +84,14 @@ const SidebarComponent: FC = ({ typeof window === 'undefined' ? containerWidth : window.innerWidth const viewportMaxWidth = Math.floor (viewportWidth * MAX_VIEWPORT_WIDTH_RATIO) const maxAllowedWidth = - maxWidth == null + maxWidthRef.current == null ? getSidebarMaxWidth (containerWidth) - : Math.max (MIN_SIDEBAR_WIDTH, Math.min (viewportMaxWidth, maxWidth)) + : Math.max ( + MIN_SIDEBAR_WIDTH, + Math.min (viewportMaxWidth, maxWidthRef.current)) return Math.min (maxAllowedWidth, Math.max (MIN_SIDEBAR_WIDTH, nextWidth)) - }, [maxWidth]) + }, []) const [width, setWidth] = useState (() => ( typeof window === 'undefined' @@ -153,6 +156,21 @@ const SidebarComponent: FC = ({ restoreBodyStyle () }, [clearLongPressTimer, restoreBodyStyle, sidebarKey]) + const applyWidth = useCallback ((nextWidth: number) => { + if (nextWidth === widthRef.current) + return + + widthRef.current = nextWidth + setWidth (nextWidth) + }, []) + + useEffect (() => { + maxWidthRef.current = maxWidth + const containerWidth = getContainerWidth () + const nextWidth = getClampedWidth (widthRef.current, containerWidth) + applyWidth (nextWidth) + }, [applyWidth, getClampedWidth, getContainerWidth, maxWidth]) + const updateWidth = useCallback ((clientX: number) => { const state = resizeRef.current if (state == null) @@ -163,9 +181,8 @@ const SidebarComponent: FC = ({ ? clientX - state.startX : state.startX - clientX const nextWidth = getClampedWidth (state.startWidth + delta, containerWidth) - widthRef.current = nextWidth - setWidth (nextWidth) - }, [getClampedWidth, getContainerWidth]) + applyWidth (nextWidth) + }, [applyWidth, getClampedWidth, getContainerWidth]) const handlePointerDown = (ev: PointerEvent) => { if (ev.pointerType !== 'mouse' && ev.pointerType !== 'touch') @@ -235,10 +252,9 @@ const SidebarComponent: FC = ({ const resetWidth = useCallback (() => { const containerWidth = getContainerWidth () const nextWidth = getClampedWidth (DEFAULT_SIDEBAR_WIDTH, containerWidth) - widthRef.current = nextWidth - setWidth (nextWidth) + applyWidth (nextWidth) window.localStorage.removeItem (storageKeyForSidebar (sidebarKey, side)) - }, [getClampedWidth, getContainerWidth, sidebarKey, side]) + }, [applyWidth, getClampedWidth, getContainerWidth, sidebarKey, side]) const handleDoubleClick = (ev: MouseEvent) => { ev.preventDefault () @@ -251,9 +267,14 @@ const SidebarComponent: FC = ({ const nextWidth = getClampedWidth ( readStoredSidebarWidth (sidebarKey, side), containerWidth) - widthRef.current = nextWidth - setWidth (nextWidth) - }, [getClampedWidth, getContainerWidth, sidebarKey, side]) + applyWidth (nextWidth) + }, [applyWidth, getClampedWidth, getContainerWidth, sidebarKey, side]) + + useEffect (() => { + const containerWidth = getContainerWidth () + const nextWidth = getClampedWidth (widthRef.current, containerWidth) + applyWidth (nextWidth) + }, [applyWidth, getClampedWidth, getContainerWidth]) useEffect (() => { if (typeof window === 'undefined') @@ -262,16 +283,12 @@ const SidebarComponent: FC = ({ const handleResize = () => { const containerWidth = getContainerWidth () const nextWidth = getClampedWidth (widthRef.current, containerWidth) - if (nextWidth === widthRef.current) - return - - widthRef.current = nextWidth - setWidth (nextWidth) + applyWidth (nextWidth) } window.addEventListener ('resize', handleResize) return () => window.removeEventListener ('resize', handleResize) - }, [getClampedWidth, getContainerWidth]) + }, [applyWidth, getClampedWidth, getContainerWidth]) useEffect (() => { onWidthChange?.(width) diff --git a/frontend/src/pages/materials/MaterialBasePage.tsx b/frontend/src/pages/materials/MaterialBasePage.tsx index c7b6292..c444b79 100644 --- a/frontend/src/pages/materials/MaterialBasePage.tsx +++ b/frontend/src/pages/materials/MaterialBasePage.tsx @@ -6,9 +6,11 @@ import type { FC } from 'react' const MaterialBasePage: FC = () => ( -
+
) -export default MaterialBasePage \ No newline at end of file +export default MaterialBasePage diff --git a/frontend/src/pages/posts/PostDetailPage.tsx b/frontend/src/pages/posts/PostDetailPage.tsx index e5e823d..015c16c 100644 --- a/frontend/src/pages/posts/PostDetailPage.tsx +++ b/frontend/src/pages/posts/PostDetailPage.tsx @@ -92,16 +92,16 @@ const PostDetailPage: FC = ({ user }) => { : 'bg-gray-500 hover:bg-gray-600') return ( -
+
{(post?.thumbnail || post?.thumbnailBase) && ( )} {post && {`${ post.title || post.url } | ${ SITE_TITLE }`}} -
- {post && } -
+ {post && } {post @@ -179,9 +179,7 @@ const PostDetailPage: FC = ({ user }) => { : 'Loading...'} -
- {post && } -
+ {post && }
) } diff --git a/frontend/src/pages/posts/PostListPage.tsx b/frontend/src/pages/posts/PostListPage.tsx index 9fe7d25..6b116c8 100644 --- a/frontend/src/pages/posts/PostListPage.tsx +++ b/frontend/src/pages/posts/PostListPage.tsx @@ -70,6 +70,7 @@ const PostListPage: FC = () => { return (
-- 2.34.1