From 3dc9c4b28caeab3fe7b42c77690f9304e95d3130 Mon Sep 17 00:00:00 2001 From: Ou Date: Thu, 17 Oct 2024 15:33:33 +0800 Subject: [PATCH] refactor: useSync --- server/api/oauth/github.ts | 1 - shared/sources.ts | 12 +-- shared/types.ts | 12 +-- src/atoms/primitiveMetadataAtom.ts | 27 +++--- src/components/column/index.tsx | 2 +- src/components/common/overlay-scrollbar.tsx | 6 +- src/hooks/useSticky.ts | 18 ---- src/hooks/useSync.ts | 101 +++++++++++--------- 8 files changed, 87 insertions(+), 92 deletions(-) delete mode 100644 src/hooks/useSticky.ts diff --git a/server/api/oauth/github.ts b/server/api/oauth/github.ts index ebb44c2..46fd9b0 100644 --- a/server/api/oauth/github.ts +++ b/server/api/oauth/github.ts @@ -43,7 +43,6 @@ export default defineEventHandler(async (event) => { }, }) - console.log(userInfo) const userID = String(userInfo.id) await userTable.addUser(userID, userInfo.notification_email || userInfo.email, "github") diff --git a/shared/sources.ts b/shared/sources.ts index 14f8db9..c93cbc8 100644 --- a/shared/sources.ts +++ b/shared/sources.ts @@ -68,12 +68,12 @@ export const originSources = { }, "hupu": { name: "虎扑", - active: false, + disable: true, home: "https://hupu.com", }, "tieba": { name: "百度贴吧", - active: false, + disable: true, home: "https://tieba.baidu.com", }, "toutiao": { @@ -91,13 +91,13 @@ export const originSources = { "thepaper": { name: "澎湃新闻", interval: Time.Common, - active: false, + disable: true, home: "https://www.thepaper.cn", }, "sputniknewscn": { name: "卫星通讯社", color: "orange", - active: false, + disable: true, home: "https://sputniknews.cn", }, "cankaoxiaoxi": { @@ -116,7 +116,7 @@ function genSources() { const parent = { name: source.name, type: source.type, - active: source.active ?? true, + disable: source.disable, color: source.color ?? "red", interval: source.interval ?? Time.Default, } @@ -139,5 +139,5 @@ function genSources() { } }) - return typeSafeObjectFromEntries(_.filter(([_, v]) => v.active)) + return typeSafeObjectFromEntries(_.filter(([_, v]) => !v.disable)) } diff --git a/shared/types.ts b/shared/types.ts index 334f8de..474f1fa 100644 --- a/shared/types.ts +++ b/shared/types.ts @@ -8,10 +8,10 @@ type ConstSources = typeof originSources type MainSourceID = keyof(ConstSources) export type SourceID = { - [Key in MainSourceID]: ConstSources[Key] extends { active?: false } ? never : + [Key in MainSourceID]: ConstSources[Key] extends { disable?: true } ? never : ConstSources[Key] extends { sub?: infer SubSource } ? { // @ts-expect-error >_< - [SubKey in keyof SubSource ]: SubSource[SubKey] extends { active?: false } ? never : `${Key}-${SubKey}` + [SubKey in keyof SubSource ]: SubSource[SubKey] extends { disable?: true } ? never : `${Key}-${SubKey}` }[keyof SubSource] | Key : Key; }[MainSourceID] @@ -33,15 +33,15 @@ export interface OriginSource { interval?: number type?: "hottest" | "realtime" /** - * @default true + * @default false */ - active?: boolean + disable?: boolean home: string color?: Color sub?: Record } @@ -51,7 +51,7 @@ export interface Source { title?: string type?: "hottest" | "realtime" color: Color - active: boolean + disable?: boolean interval: number redirect?: SourceID } diff --git a/src/atoms/primitiveMetadataAtom.ts b/src/atoms/primitiveMetadataAtom.ts index 827e9b1..24b44d3 100644 --- a/src/atoms/primitiveMetadataAtom.ts +++ b/src/atoms/primitiveMetadataAtom.ts @@ -4,6 +4,7 @@ import type { PrimitiveAtom } from "jotai" import { atom } from "jotai" import type { PrimitiveMetadata } from "@shared/types" import { verifyPrimitiveMetadata } from "@shared/verify" +import { sources } from "@shared/sources" import type { Update } from "./types" function createPrimitiveMetadataAtom( @@ -17,40 +18,42 @@ function createPrimitiveMetadataAtom( if (item) { const stored = JSON.parse(item) as PrimitiveMetadata verifyPrimitiveMetadata(stored) - return preprocess(stored) + return preprocess({ + ...stored, + action: "init", + }) } } catch { } return initialValue } const baseAtom = atom(getInitialValue()) - const derivedAtom = atom( - get => get(baseAtom), - (get, set, update: Update) => { - const nextValue = update instanceof Function ? update(get(baseAtom)) : update + const derivedAtom = atom(get => get(baseAtom), (get, set, update: Update) => { + const nextValue = update instanceof Function ? update(get(baseAtom)) : update + if (nextValue.updatedTime > get(baseAtom).updatedTime) { set(baseAtom, nextValue) localStorage.setItem(key, JSON.stringify(nextValue)) - }, - ) + } + }) return derivedAtom } const initialMetadata = typeSafeObjectFromEntries(typeSafeObjectEntries(metadata).map(([id, val]) => [id, val.sources])) -export function preprocessMetadata(target: PrimitiveMetadata, action: PrimitiveMetadata["action"] = "init") { +export function preprocessMetadata(target: PrimitiveMetadata) { return { data: { ...initialMetadata, ...typeSafeObjectFromEntries( typeSafeObjectEntries(target.data) .filter(([id]) => initialMetadata[id]) - .map(([id, sources]) => { - if (id === "focus") return [id, sources] - const oldS = sources.filter(k => initialMetadata[id].includes(k)) + .map(([id, s]) => { + if (id === "focus") return [id, s.filter(k => sources[k])] + const oldS = s.filter(k => initialMetadata[id].includes(k)) const newS = initialMetadata[id].filter(k => !oldS.includes(k)) return [id, [...oldS, ...newS]] }), ), }, - action, + action: target.action, updatedTime: target.updatedTime, } as PrimitiveMetadata } diff --git a/src/components/column/index.tsx b/src/components/column/index.tsx index 3679593..496c4a0 100644 --- a/src/components/column/index.tsx +++ b/src/components/column/index.tsx @@ -13,7 +13,7 @@ export function Column({ id }: { id: ColumnID }) { useEffect(() => { setCurrentColumnID(id) }, [id, setCurrentColumnID]) - useTitle(`NewsNow ${metadata[id].name}`) + useTitle(`NewsNow | ${metadata[id].name}`) return ( <>
diff --git a/src/components/common/overlay-scrollbar.tsx b/src/components/common/overlay-scrollbar.tsx index cc98940..3993318 100644 --- a/src/components/common/overlay-scrollbar.tsx +++ b/src/components/common/overlay-scrollbar.tsx @@ -46,13 +46,13 @@ export function OverlayScrollbar({ children, options, events, defer, ...props }: export function GlobalOverlayScrollbar({ children, ...props }: PropsWithChildren>) { const ref = useRef(null) - const lastScroll = useRef(0) + const lastTrigger = useRef(0) const timer = useRef() const setGoToTop = useSetAtom(goToTopAtom) const onScroll = useCallback((e: Event) => { const now = Date.now() - if (now - lastScroll.current > 50) { - lastScroll.current = now + if (now - lastTrigger.current > 50) { + lastTrigger.current = now clearTimeout(timer.current) timer.current = setTimeout( () => { diff --git a/src/hooks/useSticky.ts b/src/hooks/useSticky.ts deleted file mode 100644 index f6265b6..0000000 --- a/src/hooks/useSticky.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { useEffect, useRef, useState } from "react" - -export function useSticky() { - const ref = useRef(null) - - const [isSticky, setIsSticky] = useState(false) - - useEffect(() => { - const observer = new IntersectionObserver( - ([event]) => setIsSticky(event.intersectionRatio < 1), - { threshold: [1], rootMargin: "-1px 0px 0px 0px" }, - ) - observer.observe(ref.current!) - return () => observer.disconnect() - }, []) - - return { ref, isSticky } -} diff --git a/src/hooks/useSync.ts b/src/hooks/useSync.ts index 8c3dc22..d64d5f9 100644 --- a/src/hooks/useSync.ts +++ b/src/hooks/useSync.ts @@ -1,57 +1,68 @@ import type { PrimitiveMetadata } from "@shared/types" import { useAtom } from "jotai" import { ofetch } from "ofetch" -import { useCallback, useEffect } from "react" +import { useEffect } from "react" +import { useDebounce } from "react-use" import { preprocessMetadata, primitiveMetadataAtom } from "~/atoms" +export async function uploadMetadata(metadata: PrimitiveMetadata) { + if (!__ENABLE_LOGIN__) return + const jwt = localStorage.getItem("user_jwt") + if (!jwt) return + try { + await ofetch("/api/me/sync", { + method: "POST", + headers: { + Authorization: `Bearer ${jwt}`, + }, + body: { + data: metadata.data, + updatedTime: metadata.updatedTime, + }, + }) + } catch (e) { + console.error(e) + } +} + +export async function downloadMetadata(): Promise { + if (!__ENABLE_LOGIN__) return + const jwt = localStorage.getItem("user_jwt") + if (!jwt) return + try { + const { data, updatedTime } = await ofetch("/api/me/sync", { + headers: { + Authorization: `Bearer ${jwt}`, + }, + }) as PrimitiveMetadata + // 不用同步 action 字段 + if (data) { + return { + action: "sync", + data, + updatedTime, + } + } + } catch (e) { + console.error(e) + } +} + export function useSync() { const [primitiveMetadata, setPrimitiveMetadata] = useAtom(primitiveMetadataAtom) - const uploadMetadata = useCallback(async () => { - if (!__ENABLE_LOGIN__) return - const jwt = localStorage.getItem("user_jwt") - if (!jwt) return - if (primitiveMetadata.action !== "manual") return - try { - await ofetch("/api/me/sync", { - method: "POST", - headers: { - Authorization: `Bearer ${jwt}`, - }, - body: { - data: primitiveMetadata.data, - updatedTime: primitiveMetadata.updatedTime, - }, - }) - } catch (e) { - console.error(e) - } - }, [primitiveMetadata]) - const downloadMetadata = useCallback(async () => { - if (!__ENABLE_LOGIN__) return - const jwt = localStorage.getItem("user_jwt") - if (!jwt) return - try { - const { data, updatedTime } = await ofetch("/api/me/sync", { - headers: { - Authorization: `Bearer ${jwt}`, - }, - }) as PrimitiveMetadata - if (data && updatedTime > primitiveMetadata.updatedTime) { - // 不用同步 action 字段 - setPrimitiveMetadata(preprocessMetadata({ action: "sync", data, updatedTime }, "sync")) + useDebounce(async () => { + if (primitiveMetadata.action === "manual") { + uploadMetadata(primitiveMetadata) + } + }, 10000, [primitiveMetadata]) + useEffect(() => { + const fn = async () => { + const metadata = await downloadMetadata() + if (metadata) { + setPrimitiveMetadata(preprocessMetadata(metadata)) } - } catch (e) { - console.error(e) } - // 只需要在初始化时执行一次 - // eslint-disable-next-line react-hooks/exhaustive-deps + fn() }, [setPrimitiveMetadata]) - - useEffect(() => { - downloadMetadata() - }, [setPrimitiveMetadata, downloadMetadata]) - useEffect(() => { - uploadMetadata() - }, [primitiveMetadata, uploadMetadata]) }