From 698253e4aefb7b02fdcdc2b671f74d9bebc7a617 Mon Sep 17 00:00:00 2001 From: Ou Date: Sat, 12 Oct 2024 12:49:20 +0800 Subject: [PATCH] pref: colorful card --- server/routes/[id].ts | 8 ++++++-- server/sources/weibo.ts | 2 +- shared/colors.ts | 26 ++++++++++++++++++++++++++ shared/sources.ts | 18 +++++++++++++++--- shared/types.ts | 5 ++++- src/components/section/card.tsx | 24 +++++++++++++----------- src/hooks/useSticky.ts | 18 ++++++++++++++++++ src/routes/__root.tsx | 15 ++++++++++++++- src/styles/globals.css | 9 ++++----- uno.config.ts | 26 ++++++++------------------ 10 files changed, 109 insertions(+), 42 deletions(-) create mode 100644 shared/colors.ts create mode 100644 src/hooks/useSticky.ts diff --git a/server/routes/[id].ts b/server/routes/[id].ts index 1818b38..3776004 100644 --- a/server/routes/[id].ts +++ b/server/routes/[id].ts @@ -1,6 +1,7 @@ -import { Interval, TTL } from "@shared/consts" +import { TTL } from "@shared/consts" import type { SourceID, SourceResponse } from "@shared/types" import { sources } from "@shared/sources" +import { delay } from "@shared/utils" import { sourcesFn } from "#/sources" import { Cache } from "#/cache" @@ -26,8 +27,11 @@ export default defineEventHandler(async (event): Promise => { if (cache) { // interval 刷新间隔,对于缓存失效也要执行的。本质上表示本来内容更新就很慢,这个间隔内可能内容压根不会更新。 // 默认 10 分钟,是低于 TTL 的,但部分 Source 的间隔会超过 TTL,甚至有的一天刷新一次。 - const interval = sources[id]?.interval ?? Interval + const interval = sources[id].interval if (now - cache.updated < interval) { + if (id === "cankaoxiaoxi") { + await delay(2000) + } return { status: "success", data: { diff --git a/server/sources/weibo.ts b/server/sources/weibo.ts index 909896d..d1bc23b 100644 --- a/server/sources/weibo.ts +++ b/server/sources/weibo.ts @@ -29,7 +29,7 @@ export default defineSource(async () => { const res: Res = await $fetch(url) if (!res.ok || res.data.realtime.length === 0) throw new Error("Cannot fetch data") return res.data.realtime - .filter(k => !k.icon_desc || k.icon_desc !== "荐") + .filter(k => !k.icon_desc || !/[荐促商宣]/.test(k.icon_desc)) .slice(0, 20) .map((k) => { const keyword = k.word_scheme ? k.word_scheme : `#${k.word}#` diff --git a/shared/colors.ts b/shared/colors.ts new file mode 100644 index 0000000..dc952aa --- /dev/null +++ b/shared/colors.ts @@ -0,0 +1,26 @@ +export const colors = [ + "blue", // #0000FF + "indigo", // #4B0082 + "violet", // #EE82EE + "purple", // #800080 + "fuchsia", // #FF00FF + "pink", // #FFC0CB + "rose", // #FF007F + "amber", // #FFBF00 + "yellow", // #FFFF00 + "lime", // #00FF00 + "green", // #008000 + "emerald", // #50C878 + "teal", // #008080 + "cyan", // #00FFFF + "sky", // #87CEEB + "slate", // #708090 + "gray", // #808080 + "zinc", // #A0A0A0 + "neutral", // #828282 + "stone", // #D2B48C + "red", // #FF0000 + "orange", // #FFA500 +] as const + +export type Color = typeof colors[number] diff --git a/shared/sources.ts b/shared/sources.ts index 12c4bea..8675b27 100644 --- a/shared/sources.ts +++ b/shared/sources.ts @@ -14,11 +14,13 @@ const Time = { export const originSources = { "v2ex": { name: "V2EX", + color: "slate", home: "https://v2ex.com/", }, "coolapk": { name: "酷安", type: "hottest", + color: "green", home: "https://coolapk.com", }, "wallstreetcn": { @@ -29,10 +31,12 @@ export const originSources = { }, "sputniknewscn": { name: "俄罗斯卫星通讯社", + color: "yellow", home: "https://sputniknews.cn", }, "cankaoxiaoxi": { name: "参考消息", + color: "red", interval: Time.Common, home: "https://china.cankaoxiaoxi.com", }, @@ -48,6 +52,7 @@ export const originSources = { "douyin": { name: "抖音", type: "hottest", + color: "gray", home: "https://www.douyin.com", }, "hupu": { @@ -63,6 +68,7 @@ export const originSources = { name: "微博", title: "实时热搜", type: "hottest", + color: "red", interval: Time.Realtime, home: "https://weibo.com", }, @@ -73,6 +79,7 @@ export const originSources = { "zaobao": { name: "联合早报", interval: Time.Common, + color: "red", home: "https://www.zaobao.com", }, "thepaper": { @@ -83,10 +90,12 @@ export const originSources = { "toutiao": { name: "今日头条", type: "hottest", + color: "red", home: "https://www.toutiao.com", }, "ithome": { name: "IT之家", + color: "red", home: "https://www.ithome.com", }, } as const satisfies Record @@ -103,14 +112,16 @@ function genSources() { redirect: `${id}-${subId}`, name: source.name, type: source.type, - interval: source.interval, + color: source.color ?? "blue", + interval: source.interval ?? Time.Default, ...subSource, }] as [any, Source]) } _.push([`${id}-${subId}`, { name: source.name, type: source.type, - interval: source.interval, + color: source.color ?? "blue", + interval: source.interval ?? Time.Default, ...subSource, }] as [any, Source]) }) @@ -118,7 +129,8 @@ function genSources() { _.push([id, { name: source.name, type: source.type, - interval: source.interval, + color: source.color ?? "blue", + interval: source.interval ?? Time.Default, title: source.title, }]) } diff --git a/shared/types.ts b/shared/types.ts index 272a142..c5394da 100644 --- a/shared/types.ts +++ b/shared/types.ts @@ -1,3 +1,4 @@ +import type { Color } from "./colors" import type { sectionIds } from "./data" import type { originSources } from "./sources" @@ -26,6 +27,7 @@ export interface OriginSource { */ type?: "hottest" | "latest" home: string + color?: Color sub?: Record(({ id, isDragg className={clsx( "flex flex-col h-500px rounded-2xl bg-blue bg-op-50 p-4 backdrop-blur-5", isDragged && "op-50", + `bg-${sources[id].color}`, )} style={{ transformOrigin: "50% 50%", @@ -59,17 +60,18 @@ export const CardWrapper = forwardRef(({ id, isDragg export function CardOverlay({ id }: { id: SourceID }) { return (
- {id} e.currentTarget.src = "/icons/default.png"} +
@@ -131,11 +133,11 @@ function NewsCard({ id, inView, handleListeners }: NewsCardProps) { <>
- {id} e.currentTarget.src = "/icons/default.png"} +
diff --git a/src/hooks/useSticky.ts b/src/hooks/useSticky.ts new file mode 100644 index 0000000..f6265b6 --- /dev/null +++ b/src/hooks/useSticky.ts @@ -0,0 +1,18 @@ +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/routes/__root.tsx b/src/routes/__root.tsx index 6248eb7..af26b14 100644 --- a/src/routes/__root.tsx +++ b/src/routes/__root.tsx @@ -5,9 +5,12 @@ import "~/styles/globals.css" import "virtual:uno.css" import type { QueryClient } from "@tanstack/react-query" import { Author, Homepage } from "@shared/consts" +import clsx from "clsx" +import { useEffect } from "react" import { Header } from "~/components/header" import { useOnReload } from "~/hooks/useOnReload" import { OverlayScrollbar } from "~/components/common/overlay-scrollbar" +import { useSticky } from "~/hooks/useSticky" export const Route = createRootRouteWithContext<{ queryClient: QueryClient @@ -25,10 +28,20 @@ function NotFoundComponent() { function RootComponent() { useOnReload() + const { ref, isSticky } = useSticky() + useEffect(() => { + console.log(isSticky) + }, [isSticky]) return ( <> +
-
+
diff --git a/src/styles/globals.css b/src/styles/globals.css index 4f1d22e..557a4b8 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -37,10 +37,9 @@ button:disabled { --os-handle-bg-active: rgba(255, 255, 255, 0.66); } -.card { -box-shadow: 0px 2px 3px -1px rgba(0,0,0,0.1), 0px 1px 0px 0px rgba(25,28,33,0.02), 0px 0px 0px 1px rgba(25,28,33,0.08); -} -.dark .card { - box-shadow: rgba(50, 50, 105, 0.15) 0px 2px 5px 0px, rgba(0, 0, 0, 0.05) 0px 1px 1px 0px; +.bg { + background-image: radial-gradient(ellipse 80% 80% at 50% -30%, + rgba(254, 94, 83, 0.3), + rgba(255, 255, 255, 0)); } \ No newline at end of file diff --git a/uno.config.ts b/uno.config.ts index 2a51512..dabc3f2 100644 --- a/uno.config.ts +++ b/uno.config.ts @@ -1,4 +1,5 @@ import { defineConfig, presetIcons, presetUno, transformerDirectives, transformerVariantGroup } from "unocss" +import { colors } from "./shared/colors" export default defineConfig({ mergeSelectors: false, @@ -26,26 +27,15 @@ export default defineConfig({ "btn-pure": "op50 hover:op75", "btn-action": "border border-base rounded flex gap-2 items-center px2 py1 op75 hover:op100 hover:bg-hover", - "btn-action-sm": "btn-action text-sm", "btn-action-active": "color-active border-active! bg-active op100!", "skeleton": "bg-gray-400/10 rounded-md h-5 w-full animate-pulse", }, - theme: { - colors: { - primary: { - DEFAULT: "#FDB022", - 50: "#FFFCF5", - 100: "#FFFAEB", - 200: "#FEF0C7", - 300: "#FEDF89", - 400: "#FEC84B", - 500: "#FDB022", - 600: "#F79009", - 700: "#DC6803", - 800: "#B54708", - 900: "#93370D", - 950: "#7A2E0E", - }, - }, + safelist: [ + ...colors.map(color => `bg-${color}`), + ], + extendTheme: (theme) => { + // @ts-expect-error >_< + theme.colors.primary = theme.colors.red + return theme }, })