import { useCallback, useEffect, useRef, useState } from 'react' import { useNavigate } from 'react-router-dom' import { Card, DotLoading, Empty } from 'antd-mobile' import { api } from '../../api/client.js' import useRefreshOnVisible from '../../hooks/useRefreshOnVisible.js' import { fmtDate } from '../../utils/format.js' // 新闻 (V0.2 从「首页」并入「宏观」Tab) // - 数据: mx_finance_search_news 每日 3 点拉取国内外经济政治新闻, 按时间倒序 // - 无限滚动: 触底自动加载下一页 // - 详情: /macro/news/:id (挂在宏观 Tab 下, 保持底部 Tab 高亮) export default function NewsView() { const [items, setItems] = useState([]) const [loading, setLoading] = useState(false) const [done, setDone] = useState(false) const cursorRef = useRef(null) const sentinelRef = useRef(null) const inFlightRef = useRef(false) const navigate = useNavigate() const fetchPage = useCallback(async () => { if (inFlightRef.current || done) return inFlightRef.current = true setLoading(true) try { const url = '/news?limit=10' + (cursorRef.current ? '&cursor=' + encodeURIComponent(cursorRef.current) : '') const data = await api(url) setItems((prev) => prev.concat(data.items || [])) cursorRef.current = data.nextCursor if (!data.nextCursor) setDone(true) } catch (e) { console.error('news load error', e) setDone(true) } finally { inFlightRef.current = false setLoading(false) } }, [done]) useEffect(() => { fetchPage() }, []) // 后台回前台: 重置游标重拉第一页, 新数据到了再更新视图 const reload = useCallback(async () => { if (inFlightRef.current) return inFlightRef.current = true setLoading(true) try { const data = await api('/news?limit=10') setItems(data.items || []) cursorRef.current = data.nextCursor setDone(!data.nextCursor) } catch (e) { console.error('news reload error', e) setDone(true) } finally { inFlightRef.current = false setLoading(false) } }, []) useRefreshOnVisible(reload) useEffect(() => { if (done || items.length === 0) return const el = sentinelRef.current if (!el) return const io = new IntersectionObserver((entries) => { if (entries[0].isIntersecting) fetchPage() }, { rootMargin: '200px' }) io.observe(el) return () => io.disconnect() }, [items.length, done, fetchPage]) if (items.length === 0 && !loading) { return (
) } return (
{items.map((n) => ( navigate('/macro/news/' + n.id)}>
{n.title}
{n.summary &&
{n.summary}
}
{n.source || '未知来源'} {fmtDate(n.publishedAt)} #{n.impactRank}
))}
{loading ? : (done ? '— 已加载全部 —' : '')}
) }