diff --git a/src/components/Header.tsx b/src/components/Header.tsx deleted file mode 100644 index 68dbc0b..0000000 --- a/src/components/Header.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { Link } from "@tanstack/react-router" -import { useCallback } from "react" -import { useAtomValue, useSetAtom } from "jotai" -import logo from "~/assets/react.svg" -import { useDark } from "~/hooks/useDark" -import { currentSectionAtom, refetchSourcesAtom } from "~/atoms" - -function ThemeToggle() { - const { toggleDark } = useDark() - return ( - - ) -} - -function RefreshButton() { - const currentSection = useAtomValue(currentSectionAtom) - const setRefetchSource = useSetAtom(refetchSourcesAtom) - const refreshAll = useCallback(() => { - const obj = Object.fromEntries(currentSection.sourceList.map(id => [id, Date.now()])) - setRefetchSource(prev => ({ - ...prev, - ...obj, - })) - }, [currentSection, setRefetchSource]) - - return ( - - ) -} - -export function Header() { - return ( - - - - NewsNow - - - - - - - ) -} diff --git a/src/components/section/Card.tsx b/src/components/section/Card.tsx deleted file mode 100644 index 8594afc..0000000 --- a/src/components/section/Card.tsx +++ /dev/null @@ -1,199 +0,0 @@ -import type { NewsItem, SourceID, SourceInfo, SourceResponse } from "@shared/types" -import { OverlayScrollbarsComponent } from "overlayscrollbars-react" -import type { UseQueryResult } from "@tanstack/react-query" -import { useQuery } from "@tanstack/react-query" -import clsx from "clsx" -import { useInView } from "react-intersection-observer" -import { useAtom } from "jotai" -import { forwardRef, useCallback, useImperativeHandle, useRef } from "react" -import { sources } from "@shared/data" -import type { SyntheticListenerMap } from "@dnd-kit/core/dist/hooks/utilities" -import { focusSourcesAtom, refetchSourcesAtom } from "~/atoms" -import { useRelativeTime } from "~/hooks/useRelativeTime" - -export interface ItemsProps extends React.HTMLAttributes { - id: SourceID - /** - * 是否显示透明度,拖动时原卡片的样式 - */ - isDragged?: boolean - isOverlay?: boolean - handleListeners?: SyntheticListenerMap -} - -interface NewsCardProps { - id: SourceID - inView: boolean - isOverlay?: boolean - handleListeners?: SyntheticListenerMap -} - -interface Query { - query: UseQueryResult -} - -export const CardWrapper = forwardRef(({ id, isDragged, isOverlay, handleListeners, style, ...props }, dndRef) => { - const ref = useRef(null) - const { ref: inViewRef, inView } = useInView({ - threshold: 0, - }) - - useImperativeHandle(dndRef, () => ref.current!) - useImperativeHandle(inViewRef, () => ref.current!) - - return ( - - - - ) -}) - -export function NewsCard({ id, inView, isOverlay, handleListeners }: NewsCardProps) { - const [focusSources, setFocusSources] = useAtom(focusSourcesAtom) - const [refetchSource, setRefetchSource] = useAtom(refetchSourcesAtom) - const query = useQuery({ - queryKey: [id, refetchSource[id]], - queryFn: async ({ queryKey }) => { - const [_id, _refetchTime] = queryKey as [SourceID, number] - let url = `/api/${_id}` - if (Date.now() - _refetchTime < 1000) { - url = `/api/${_id}?latest` - } - const response: SourceResponse = await fetch(url).then(res => res.json()) - if (response.status === "error") { - throw new Error(response.message) - } else { - return response.data - } - }, - // refetch 时显示原有的数据 - placeholderData: prev => prev, - staleTime: 1000 * 60 * 5, - enabled: inView, - }) - - const addFocusList = useCallback(() => { - setFocusSources(focusSources.includes(id) ? focusSources.filter(i => i !== id) : [...focusSources, id]) - }, [setFocusSources, focusSources, id]) - const manualRefetch = useCallback(() => { - setRefetchSource(prev => ({ - ...prev, - [id]: Date.now(), - })) - }, [setRefetchSource, id]) - - return ( - <> - - - e.currentTarget.hidden = true} /> - - {sources[id].name} - - - {/* @ts-expect-error -_- */} - {sources[id]?.type} - - - - - - - - - - - - > - ) -} - -function UpdateTime({ query }: Query) { - const updatedTime = useRelativeTime(query.data?.updatedTime ?? "") - if (updatedTime) return {`${updatedTime}更新`} - if (query.isError) return 获取失败 - return -} - -function Num({ num }: { num: number }) { - const color = ["bg-red-900", "bg-red-500", "bg-red-400"] - return ( - - {num} - - ) -} - -function ExtraInfo({ item }: { item: NewsItem }) { - const relativeTime = useRelativeTime(item?.extra?.date) - if (relativeTime) { - return <>{relativeTime}> - } - - if (item?.extra?.icon) { - return ( - - ) - } -} - -function NewsList({ query }: Query) { - const items = query.data?.items - if (items?.length) { - return ( - <> - {items.slice(0, 20).map((item, i) => ( - - - - - {item.title} - - - - - - - ))} - > - ) - } - return ( - <> - {Array.from({ length: 20 }).map((_, i) => i).map(i => ( - - - - - ))} - > - ) -} diff --git a/src/components/section/Dnd.tsx b/src/components/section/Dnd.tsx deleted file mode 100644 index a4372a4..0000000 --- a/src/components/section/Dnd.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import { useCallback, useState } from "react" -import type { DragEndEvent, DragStartEvent } from "@dnd-kit/core" -import { - DndContext, - DragOverlay, - MouseSensor, - TouchSensor, - closestCenter, - useSensor, - useSensors, -} from "@dnd-kit/core" -import { SortableContext, arrayMove, rectSortingStrategy, useSortable } from "@dnd-kit/sortable" -import { useAtom } from "jotai" -import type { SourceID } from "@shared/types" -import { CSS } from "@dnd-kit/utilities" -import type { ItemsProps } from "./card" -import { CardWrapper } from "./card" -import { focusSourcesAtom } from "~/atoms" - -export function Dnd() { - const [items, setItems] = useAtom(focusSourcesAtom) - const [activeId, setActiveId] = useState(null) - const sensors = useSensors(useSensor(MouseSensor), useSensor(TouchSensor)) - - const handleDragStart = useCallback((event: DragStartEvent) => { - setActiveId(event.active.id as string) - }, []) - const handleDragEnd = useCallback((event: DragEndEvent) => { - const { active, over } = event - - if (active.id !== over?.id) { - setItems((items) => { - const oldIndex = items.indexOf(active.id as any) - const newIndex = items.indexOf(over!.id as any) - - return arrayMove(items, oldIndex, newIndex) - }) - } - - setActiveId(null) - }, [setItems]) - const handleDragCancel = useCallback(() => { - setActiveId(null) - }, []) - - return ( - - - {items.map(id => ( - - ))} - - - {!!activeId && } - - - ) -} - -function SortableCardWrapper({ id, ...props }: ItemsProps) { - const { - isDragging, - attributes, - listeners, - setNodeRef, - transform, - transition, - } = useSortable({ id }) - - const style = { - transform: CSS.Transform.toString(transform), - transition, - } - - return ( - - ) -}