mirror of
https://github.com/ourongxing/newsnow.git
synced 2025-01-19 03:09:14 +08:00
feat: support dnd in all sections
This commit is contained in:
parent
c3b6ae1f01
commit
731b31c8ce
@ -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"],
|
||||
},
|
||||
}
|
||||
|
44
src/atoms.ts
44
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<SourceID[]>("focusSources", [], (stored) => {
|
||||
return stored.filter(item => item in sources)
|
||||
const initialSources = typeSafeObjectFromEntries(typeSafeObjectEntries(metadata).map(([id, val]) => [id, val.sources]))
|
||||
export const localSourcesAtom = atomWithLocalStorage<Record<SectionID, SourceID[]>>("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<SectionID>("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)]: _,
|
||||
})
|
||||
})
|
||||
|
@ -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)
|
||||
},
|
||||
})
|
||||
|
||||
|
@ -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<string | null>(null)
|
||||
const sensors = useSensors(useSensor(MouseSensor), useSensor(TouchSensor))
|
||||
|
||||
|
@ -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"
|
||||
? <Dnd />
|
||||
: (
|
||||
<>
|
||||
{
|
||||
metadata[id].sources.map(source => (
|
||||
<CardWrapper key={source} id={source} />
|
||||
))
|
||||
}
|
||||
</>
|
||||
)}
|
||||
<Dnd />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
@ -3,7 +3,7 @@ import { atom } from "jotai"
|
||||
|
||||
export function atomWithLocalStorage<T>(
|
||||
key: string,
|
||||
initialValue: T,
|
||||
initialValue: T | (() => T),
|
||||
initFn?: ((stored: T) => T),
|
||||
): PrimitiveAtom<T> {
|
||||
const getInitialValue = () => {
|
||||
@ -17,7 +17,8 @@ export function atomWithLocalStorage<T>(
|
||||
} catch {
|
||||
//
|
||||
}
|
||||
return initialValue
|
||||
if (initialValue instanceof Function) return initialValue()
|
||||
else return initialValue
|
||||
}
|
||||
const baseAtom = atom(getInitialValue())
|
||||
const derivedAtom = atom(
|
||||
|
@ -16,11 +16,10 @@ export const Route = createRootRouteWithContext<{
|
||||
})
|
||||
|
||||
function NotFoundComponent() {
|
||||
// const nav = Route.useNavigate()
|
||||
// nav({
|
||||
// to: "/",
|
||||
// })
|
||||
// return <div></div>
|
||||
const nav = Route.useNavigate()
|
||||
nav({
|
||||
to: "/",
|
||||
})
|
||||
}
|
||||
|
||||
function RootComponent() {
|
||||
|
@ -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 <Section id={id} />
|
||||
|
Loading…
x
Reference in New Issue
Block a user