refactor(subscription-column): enhance swipe functionality and animation handling

- Introduced custom ordering for timeline views to improve swipe direction calculations.
- Updated state management to reflect the active view index accurately.
- Replaced animation transition properties with predefined spring presets for smoother animations.

These changes enhance the user experience by providing more intuitive swipe interactions and improved animation performance.

Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
Innei 2025-09-06 00:08:12 +08:00
parent 82dc3992c9
commit 4e0dfdfdde
No known key found for this signature in database
GPG Key ID: 0F62D33977F021F7
1 changed files with 27 additions and 16 deletions

View File

@ -1,4 +1,5 @@
import { useGlobalFocusableScopeSelector } from "@follow/components/common/Focusable/hooks.js"
import { Spring } from "@follow/components/constants/spring.js"
import { ActionButton } from "@follow/components/ui/button/index.js"
import { RootPortal } from "@follow/components/ui/portal/index.js"
import { FeedViewType } from "@follow/constants"
@ -12,7 +13,7 @@ import { useWheel } from "@use-gesture/react"
import { Lethargy } from "lethargy"
import { AnimatePresence, m } from "motion/react"
import type { FC, PropsWithChildren } from "react"
import { memo, useCallback, useEffect, useLayoutEffect, useRef, useState } from "react"
import { memo, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react"
import { useRootContainerElement } from "~/atoms/dom"
import { useUISettingKey } from "~/atoms/settings/ui"
@ -182,35 +183,45 @@ const SwipeWrapper: FC<{ active: string; children: React.JSX.Element[] }> = memo
({ children, active }) => {
const reduceMotion = useReduceMotion()
const timelineList = useTimelineList()
const index = timelineList.indexOf(active)
const viewIndex = timelineList.indexOf(active)
const feedColumnWidth = useUISettingKey("feedColWidth")
const timelineTabs = useUISettingKey("timelineTabs")
const containerRef = useRef<HTMLDivElement>(null)
const prevActiveIndexRef = useRef(-1)
// Use custom ordering for direction calculation
const orderedForDirection = useMemo(() => {
if (timelineList.length === 0) return [] as string[]
const first = timelineList[0]
const rest = timelineList.slice(1)
const savedVisible = (timelineTabs?.visible ?? []).filter((id) => rest.includes(id))
const ordered = [first, ...savedVisible, ...rest.filter((id) => !savedVisible.includes(id))]
return ordered
}, [timelineList, timelineTabs])
const orderIndex = orderedForDirection.indexOf(active)
const prevOrderIndexRef = useRef(-1)
const [isReady, setIsReady] = useState(false)
const [direction, setDirection] = useState<"left" | "right">("right")
const [currentAnimtedActive, setCurrentAnimatedActive] = useState(index)
const [currentAnimtedActive, setCurrentAnimatedActive] = useState(viewIndex)
useLayoutEffect(() => {
const prevActiveIndex = prevActiveIndexRef.current
if (prevActiveIndex !== index) {
if (prevActiveIndex < index) {
setDirection("right")
} else {
setDirection("left")
}
const prevOrderIndex = prevOrderIndexRef.current
if (prevOrderIndex !== orderIndex) {
if (prevOrderIndex < orderIndex) setDirection("right")
else setDirection("left")
}
// eslint-disable-next-line @eslint-react/web-api/no-leaked-timeout
setTimeout(() => {
setCurrentAnimatedActive(index)
setCurrentAnimatedActive(viewIndex)
}, 0)
if (prevActiveIndexRef.current !== -1) {
if (prevOrderIndexRef.current !== -1) {
setIsReady(true)
}
prevActiveIndexRef.current = index
}, [index])
prevOrderIndexRef.current = orderIndex
}, [orderIndex, viewIndex])
if (reduceMotion) {
return <div ref={containerRef}>{children[currentAnimtedActive]}</div>
@ -226,7 +237,7 @@ const SwipeWrapper: FC<{ active: string; children: React.JSX.Element[] }> = memo
}
animate={{ x: 0 }}
exit={{ x: direction === "right" ? -feedColumnWidth : feedColumnWidth }}
transition={{ x: { type: "spring", stiffness: 700, damping: 40 } }}
transition={Spring.presets.snappy}
ref={containerRef}
>
{children[currentAnimtedActive]}