From a09f55af080714d0b0022983421e942d14ceb562 Mon Sep 17 00:00:00 2001 From: Innei Date: Mon, 26 Aug 2024 13:49:40 +0800 Subject: [PATCH] fix: feed column resize perf Signed-off-by: Innei --- .../components/mark-all-button.tsx | 90 +++++----- .../src/modules/feed-column/index.tsx | 162 +++++++++--------- src/renderer/src/pages/(main)/layout.tsx | 3 +- 3 files changed, 128 insertions(+), 127 deletions(-) diff --git a/src/renderer/src/modules/entry-column/components/mark-all-button.tsx b/src/renderer/src/modules/entry-column/components/mark-all-button.tsx index dc84971ce..8d556a295 100644 --- a/src/renderer/src/modules/entry-column/components/mark-all-button.tsx +++ b/src/renderer/src/modules/entry-column/components/mark-all-button.tsx @@ -42,6 +42,52 @@ export const MarkAllReadWithOverlay = forwardRef< useOnClickOutside({ current: popoverRef }, () => { setShow(false) }) + const renderPopup = () => { + const $parent = containerRef.current! + const rect = $parent.getBoundingClientRect() + const paddingLeft = $parent.offsetLeft + return ( + + +
+ + + + Mark + + {which} + + as read? + + +
+ } + onClick={() => { + handleMarkAllAsRead() + setShow(false) + }} + > + Confirm + +
+
+
+
+ ) + } return ( - - {show && ( - - -
- - - - Mark - - {which} - - as read? - - -
- } - onClick={() => { - handleMarkAllAsRead() - setShow(false) - }} - > - Confirm - -
-
-
-
- )} -
+ {show && renderPopup()}
) }) diff --git a/src/renderer/src/modules/feed-column/index.tsx b/src/renderer/src/modules/feed-column/index.tsx index 220fa588c..356f309b8 100644 --- a/src/renderer/src/modules/feed-column/index.tsx +++ b/src/renderer/src/modules/feed-column/index.tsx @@ -1,5 +1,8 @@ +/* eslint-disable @eslint-react/hooks-extra/no-direct-set-state-in-use-layout-effect */ import { getReadonlyRoute } from "@renderer/atoms/route" -import { useUISettingKey } from "@renderer/atoms/settings/ui" +import { + useUISettingKey, +} from "@renderer/atoms/settings/ui" import { useSidebarActiveView } from "@renderer/atoms/sidebar" import { ActionButton } from "@renderer/components/ui/button" import { HotKeyScopeMap, views } from "@renderer/constants" @@ -8,21 +11,23 @@ import { useNavigateEntry } from "@renderer/hooks/biz/useNavigateEntry" import { useReduceMotion } from "@renderer/hooks/biz/useReduceMotion" import { getRouteParams } from "@renderer/hooks/biz/useRouteParams" import { useAuthQuery } from "@renderer/hooks/common" -import { nextFrame, stopPropagation } from "@renderer/lib/dom" +import { stopPropagation } from "@renderer/lib/dom" import { Routes } from "@renderer/lib/enum" -import { jotaiStore } from "@renderer/lib/jotai" import { clamp, cn } from "@renderer/lib/utils" import { Queries } from "@renderer/queries" import { useSubscriptionStore } from "@renderer/store/subscription" import { useFeedUnreadStore } from "@renderer/store/unread" import { useSubscribeElectronEvent } from "@shared/event" import { useWheel } from "@use-gesture/react" -import type { MotionValue } from "framer-motion" -import { m, useSpring } from "framer-motion" -import { atom, useAtomValue } from "jotai" +import { AnimatePresence, m } from "framer-motion" import { Lethargy } from "lethargy" -import type { PropsWithChildren } from "react" -import { useCallback, useLayoutEffect, useRef } from "react" +import type { FC, PropsWithChildren } from "react" +import { + useCallback, + useLayoutEffect, + useRef, + useState, +} from "react" import { isHotkeyPressed, useHotkeys } from "react-hotkeys-hook" import { WindowUnderBlur } from "../../components/ui/background" @@ -64,15 +69,11 @@ const useUnreadByView = () => { return totalUnread } -const carouselWidthAtom = atom(256) export function FeedColumn({ children }: PropsWithChildren) { const carouselRef = useRef(null) const [active, setActive_] = useSidebarActiveView() - const spring = useSpring(0, { - stiffness: 700, - damping: 40, - }) + const navigateBackHome = useBackHome(active) const setActive: typeof setActive_ = useCallback( (args) => { @@ -83,7 +84,7 @@ export function FeedColumn({ children }: PropsWithChildren) { navigateBackHome(nextActive) } }, - [active, navigateBackHome, spring], + [active, navigateBackHome], ) useLayoutEffect(() => { @@ -93,18 +94,6 @@ export function FeedColumn({ children }: PropsWithChildren) { } }, [setActive_]) - useLayoutEffect(() => { - const handler = () => { - spring.jump(-active * jotaiStore.get(carouselWidthAtom)) - } - const dispose = jotaiStore.sub(carouselWidthAtom, handler) - - spring.set(-active * jotaiStore.get(carouselWidthAtom)) - return () => { - dispose() - } - }, [active, spring]) - useHotkeys( shortcuts.feeds.switchBetweenViews.key, (e) => { @@ -147,22 +136,6 @@ export function FeedColumn({ children }: PropsWithChildren) { }, ) - useLayoutEffect(() => { - const $carousel = carouselRef.current - if (!$carousel) return - - const handler = () => { - const width = $carousel.clientWidth - - jotaiStore.set(carouselWidthAtom, width) - } - handler() - new ResizeObserver(handler).observe($carousel) - return () => { - new ResizeObserver(handler).disconnect() - } - }, []) - const unreadByView = useUnreadByView() const showSidebarUnreadCount = useUISettingKey("sidebarShowUnreadCount") @@ -212,14 +185,11 @@ export function FeedColumn({ children }: PropsWithChildren) { ))}
- + {views.map((item, index) => (
{active === index && ( -}> = ({ children, active, spring }) => { + children: React.JSX.Element[] +}> = ({ children, active }) => { const reduceMotion = useReduceMotion() - const carouselWidth = useAtomValue(carouselWidthAtom) + const feedColumnWidth = useUISettingKey("feedColWidth") const containerRef = useRef(null) + // useLayoutEffect(() => { + // const $container = containerRef.current; + // if (!$container) return; + + // const x = -active * feedColumnWidth; + // // NOTE: To fix the misalignment of the browser's layout, use display to re-render it. + // if (x !== $container.getBoundingClientRect().x) { + // $container.style.display = "none"; + + // nextFrame(() => { + // $container.style.display = ""; + // }); + // } + // }, []); + + const prevActiveIndexRef = useRef(-1) + const [isReady, setIsReady] = useState(false) + + const [direction, setDirection] = useState<"left" | "right">("right") + const [currentAnimtedActive, setCurrentAnimatedActive] = useState(active) + useLayoutEffect(() => { - const $container = containerRef.current - if (!$container) return - - const x = -active * carouselWidth - // NOTE: To fix the misalignment of the browser's layout, use display to re-render it. - if (x !== $container.getBoundingClientRect().x) { - $container.style.display = "none" - - nextFrame(() => { - $container.style.display = "" - }) + const prevActiveIndex = prevActiveIndexRef.current + if (prevActiveIndex !== active) { + if (prevActiveIndex < active) { + setDirection("right") + } else { + setDirection("left") + } } - }, []) + setCurrentAnimatedActive(active) + if (prevActiveIndexRef.current !== -1) { + setIsReady(true) + } + prevActiveIndexRef.current = active + }, [active]) if (reduceMotion) { - return ( -
- {children} -
- ) + return
{children}
} + return ( - - {children} - + + + {children[currentAnimtedActive]} + + ) } diff --git a/src/renderer/src/pages/(main)/layout.tsx b/src/renderer/src/pages/(main)/layout.tsx index 5cf7b42e6..c85f19bb3 100644 --- a/src/renderer/src/pages/(main)/layout.tsx +++ b/src/renderer/src/pages/(main)/layout.tsx @@ -194,13 +194,14 @@ const FeedResponsiveResizerContainer = ({