feat(ui): show diff position when refresh

This commit is contained in:
Ou 2024-10-28 00:42:08 +08:00
parent 47767a6d62
commit b14cb9ea1f
3 changed files with 51 additions and 5 deletions

View File

@ -93,6 +93,7 @@ export interface NewsItem {
hover?: string hover?: string
date?: number | string date?: number | string
info?: false | string info?: false | string
diff?: number
icon?: false | string | { icon?: false | string | {
url: string url: string
scale: number scale: number

View File

@ -1,9 +1,9 @@
import type { NewsItem, SourceID, SourceResponse } from "@shared/types" import type { NewsItem, SourceID, SourceResponse } from "@shared/types"
import { useQuery } from "@tanstack/react-query" import { useQuery } from "@tanstack/react-query"
import clsx from "clsx" import clsx from "clsx"
import { useInView } from "framer-motion" import { AnimatePresence, motion, useInView } from "framer-motion"
import { useAtom } from "jotai" import { useAtom } from "jotai"
import { forwardRef, useCallback, useImperativeHandle, useMemo, useRef } from "react" import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react"
import { sources } from "@shared/sources" import { sources } from "@shared/sources"
import type { SyntheticListenerMap } from "@dnd-kit/core/dist/hooks/utilities" import type { SyntheticListenerMap } from "@dnd-kit/core/dist/hooks/utilities"
import { ofetch } from "ofetch" import { ofetch } from "ofetch"
@ -54,6 +54,7 @@ export const CardWrapper = forwardRef<HTMLDivElement, ItemsProps>(({ id, isDragg
) )
}) })
const prevSourceItems: Partial<Record<SourceID, NewsItem[]>> = {}
function NewsCard({ id, inView, handleListeners }: NewsCardProps) { function NewsCard({ id, inView, handleListeners }: NewsCardProps) {
const [focusSources, setFocusSources] = useAtom(focusSourcesAtom) const [focusSources, setFocusSources] = useAtom(focusSourcesAtom)
const [refetchSource, setRefetchSource] = useAtom(refetchSourcesAtom) const [refetchSource, setRefetchSource] = useAtom(refetchSourcesAtom)
@ -72,10 +73,28 @@ function NewsCard({ id, inView, handleListeners }: NewsCardProps) {
timeout: 10000, timeout: 10000,
headers, headers,
}) })
try {
if (response.items && sources[_id].type === "hottest" && prevSourceItems[_id]) {
response.items.forEach((item, i) => {
const o = prevSourceItems[_id]!.findIndex(k => k.id === item.id)
item.extra = {
...item?.extra,
diff: o === -1 ? undefined : o - i,
}
})
}
} catch (e) {
console.log(e)
}
return response return response
}, },
// refetch 时显示原有的数据 // refetch 时显示原有的数据
placeholderData: prev => prev, placeholderData: (prev) => {
if (prev?.items && sources[id].type === "hottest") prevSourceItems[id] = prev.items
return prev
},
staleTime: 1000 * 60 * 5, staleTime: 1000 * 60 * 5,
enabled: inView, enabled: inView,
}) })
@ -162,6 +181,31 @@ function UpdatedTime({ isError, updatedTime }: { updatedTime: any, isError: bool
return "加载中..." return "加载中..."
} }
function DiffNumber({ diff }: { diff: number }) {
const [shown, setShown] = useState(true)
useEffect(() => {
setShown(true)
const timer = setTimeout(() => {
setShown(false)
}, 5000)
return () => clearTimeout(timer)
}, [setShown, diff])
return (
<AnimatePresence>
{ shown && (
<motion.span
initial={{ opacity: 0, y: -15 }}
animate={{ opacity: 0.5, y: -7 }}
exit={{ opacity: 0, y: -15 }}
className={clsx("absolute left-0 text-xs", diff < 0 ? "text-green" : "text-red")}
>
{diff > 0 ? `+${diff}` : diff}
</motion.span>
)}
</AnimatePresence>
)
}
function ExtraInfo({ item }: { item: NewsItem }) { function ExtraInfo({ item }: { item: NewsItem }) {
if (item?.extra?.info) { if (item?.extra?.info) {
return <>{item.extra.info}</> return <>{item.extra.info}</>
@ -196,13 +240,14 @@ function NewsListHot({ items }: { items: NewsItem[] }) {
key={item.id} key={item.id}
title={item.extra?.hover} title={item.extra?.hover}
className={clsx( className={clsx(
"flex gap-2 items-center mb-2 items-stretch", "flex gap-2 items-center mb-2 items-stretch relative",
"hover:bg-neutral-400/10 rounded-md pr-1 visited:(text-neutral-400)", "hover:bg-neutral-400/10 rounded-md pr-1 visited:(text-neutral-400)",
)} )}
> >
<span className={clsx("bg-neutral-400/10 min-w-6 flex justify-center items-center rounded-md text-sm")}> <span className={clsx("bg-neutral-400/10 min-w-6 flex justify-center items-center rounded-md text-sm")}>
{i + 1} {i + 1}
</span> </span>
{!!item.extra?.diff && <DiffNumber diff={item.extra.diff} />}
<span className="self-start line-height-none"> <span className="self-start line-height-none">
<span className="mr-2 text-base"> <span className="mr-2 text-base">
{item.title} {item.title}