diff --git a/shared/data.ts b/shared/data.ts index 4d054d6..29d0fca 100644 --- a/shared/data.ts +++ b/shared/data.ts @@ -1,13 +1,13 @@ import type { Metadata } from "./types" -export const sectionIds = ["focus", "social", "china", "world", "tech", "code"] as const +export const sectionIds = ["focus", "realtime", "china", "world", "tech", "code"] as const export const metadata: Metadata = { focus: { name: "关注", sources: [], }, - social: { + realtime: { name: "实时", sources: ["weibo", "douyin", "zhihu", "toutiao", "wallstreetcn", "ithome", "36kr"], }, @@ -25,6 +25,6 @@ export const metadata: Metadata = { }, tech: { name: "科技", - sources: ["ithome", "coolapk"], + sources: ["ithome", "coolapk", "36kr-quick"], }, } diff --git a/src/atoms.ts b/src/atoms.ts index b6279b2..0d7e43f 100644 --- a/src/atoms.ts +++ b/src/atoms.ts @@ -2,10 +2,32 @@ import { atom } from "jotai" import type { SectionID, SourceID } from "@shared/types" import { metadata } from "@shared/data" import { sources } from "@shared/sources" +import { typeSafeObjectEntries, typeSafeObjectFromEntries } from "@shared/type.util" import { atomWithLocalStorage } from "./hooks/atomWithLocalStorage" -export const focusSourcesAtom = atomWithLocalStorage("focusSources", [], (stored) => { - return stored.filter(item => item in sources) +const initialSources = typeSafeObjectFromEntries(typeSafeObjectEntries(metadata).map(([id, val]) => [id, val.sources])) +export const localSourcesAtom = atomWithLocalStorage>("localsources", () => { + return initialSources +}, (stored) => { + return typeSafeObjectFromEntries(typeSafeObjectEntries({ + ...initialSources, + ...stored, + }).filter(([id]) => initialSources[id]).map(([id, val]) => { + if (id === "focus") return [id, val] + const oldS = val.filter(k => initialSources[id].includes(k)) + const newS = initialSources[id].filter(k => !oldS.includes(k)) + return [id, [...oldS, ...newS]] + })) +}) + +export const focusSourcesAtom = atom((get) => { + return get(localSourcesAtom).focus +}, (get, set, update: SourceID[] | ((prev: SourceID[]) => SourceID[])) => { + const _ = update instanceof Function ? update(get(focusSourcesAtom)) : update + set(localSourcesAtom, { + ...get(localSourcesAtom), + focus: _, + }) }) function initRefetchSources() { @@ -27,15 +49,11 @@ export const currentSectionIDAtom = atom("focus") export const currentSectionAtom = atom((get) => { const id = get(currentSectionIDAtom) - if (id === "focus") { - return { - id, - ...metadata[id], - sources: get(focusSourcesAtom), - } - } - return { - id, - ...metadata[id], - } + return get(localSourcesAtom)[id] +}, (get, set, update: SourceID[] | ((prev: SourceID[]) => SourceID[])) => { + const _ = update instanceof Function ? update(get(currentSectionAtom)) : update + set(localSourcesAtom, { + ...get(localSourcesAtom), + [get(currentSectionIDAtom)]: _, + }) }) diff --git a/src/components/header.tsx b/src/components/header.tsx index 418f62b..8c63534 100644 --- a/src/components/header.tsx +++ b/src/components/header.tsx @@ -24,7 +24,7 @@ function RefreshButton() { const currentSection = useAtomValue(currentSectionAtom) const setRefetchSource = useSetAtom(refetchSourcesAtom) const refreshAll = useCallback(() => { - const obj = Object.fromEntries(currentSection.sources.map(id => [id, Date.now()])) + const obj = Object.fromEntries(currentSection.map(id => [id, Date.now()])) setRefetchSource(prev => ({ ...prev, ...obj, @@ -33,7 +33,7 @@ function RefreshButton() { const isFetching = useIsFetching({ predicate: (query) => { - return currentSection.sources.includes(query.queryKey[0] as SourceID) + return currentSection.includes(query.queryKey[0] as SourceID) }, }) diff --git a/src/components/section/dnd.tsx b/src/components/section/dnd.tsx index a4372a4..523b936 100644 --- a/src/components/section/dnd.tsx +++ b/src/components/section/dnd.tsx @@ -15,10 +15,10 @@ 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" +import { currentSectionAtom } from "~/atoms" export function Dnd() { - const [items, setItems] = useAtom(focusSourcesAtom) + const [items, setItems] = useAtom(currentSectionAtom) const [activeId, setActiveId] = useState(null) const sensors = useSensors(useSensor(MouseSensor), useSensor(TouchSensor)) diff --git a/src/components/section/index.tsx b/src/components/section/index.tsx index bf65760..ecfe074 100644 --- a/src/components/section/index.tsx +++ b/src/components/section/index.tsx @@ -5,7 +5,6 @@ import clsx from "clsx" import { useSetAtom } from "jotai" import { useEffect } from "react" import { Dnd } from "./dnd" -import { CardWrapper } from "./card" import { currentSectionIDAtom } from "~/atoms" export function Section({ id }: { id: SectionID }) { @@ -38,17 +37,7 @@ export function Section({ id }: { id: SectionID }) { gridTemplateColumns: "repeat(auto-fill, minmax(350px, 1fr))", }} > - {id === "focus" - ? - : ( - <> - { - metadata[id].sources.map(source => ( - - )) - } - - )} + ) diff --git a/src/hooks/atomWithLocalStorage.ts b/src/hooks/atomWithLocalStorage.ts index f7c7148..a2cac0a 100644 --- a/src/hooks/atomWithLocalStorage.ts +++ b/src/hooks/atomWithLocalStorage.ts @@ -3,7 +3,7 @@ import { atom } from "jotai" export function atomWithLocalStorage( key: string, - initialValue: T, + initialValue: T | (() => T), initFn?: ((stored: T) => T), ): PrimitiveAtom { const getInitialValue = () => { @@ -17,7 +17,8 @@ export function atomWithLocalStorage( } catch { // } - return initialValue + if (initialValue instanceof Function) return initialValue() + else return initialValue } const baseAtom = atom(getInitialValue()) const derivedAtom = atom( diff --git a/src/routes/__root.tsx b/src/routes/__root.tsx index 6e8f0d7..26e1dce 100644 --- a/src/routes/__root.tsx +++ b/src/routes/__root.tsx @@ -16,11 +16,10 @@ export const Route = createRootRouteWithContext<{ }) function NotFoundComponent() { - // const nav = Route.useNavigate() - // nav({ - // to: "/", - // }) - // return
+ const nav = Route.useNavigate() + nav({ + to: "/", + }) } function RootComponent() { diff --git a/src/routes/index.tsx b/src/routes/index.tsx index 4538364..1adfb67 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -1,7 +1,7 @@ import { createFileRoute } from "@tanstack/react-router" import { useAtomValue } from "jotai" import { useMemo } from "react" -import { focusSourcesAtom } from "~/atoms" +import { localSourcesAtom } from "~/atoms" import { Section } from "~/components/section" export const Route = createFileRoute("/")({ @@ -9,9 +9,9 @@ export const Route = createFileRoute("/")({ }) function IndexComponent() { - const focusSources = useAtomValue(focusSourcesAtom) + const focusSources = useAtomValue(localSourcesAtom) const id = useMemo(() => { - return focusSources.length ? "focus" : "social" + return focusSources.focus.length ? "focus" : "realtime" // eslint-disable-next-line react-hooks/exhaustive-deps }, []) return