chore: rename to metadata

This commit is contained in:
Ou
2024-10-14 21:54:12 +08:00
parent 5e84629e23
commit 051c827ac1
11 changed files with 198 additions and 136 deletions
-2
View File
@@ -1,2 +0,0 @@
export default defineEventHandler(() => {
})
-34
View File
@@ -1,34 +0,0 @@
import type { Metadata } from "./types"
export const columnIds = ["focus", "realtime", "hottest", "china", "world", "tech", "finance"] as const
export const metadata: Metadata = {
focus: {
name: "关注",
sources: [],
},
realtime: {
name: "实时",
sources: ["weibo", "wallstreetcn", "ithome", "36kr", "zaobao"],
},
hottest: {
name: "最热",
sources: ["weibo", "douyin", "zhihu", "toutiao"],
},
china: {
name: "国内",
sources: ["weibo", "douyin", "toutiao", "zhihu"],
},
world: {
name: "国际",
sources: ["sputniknewscn", "zaobao", "cankaoxiaoxi"],
},
tech: {
name: "科技",
sources: ["ithome", "v2ex", "coolapk", "36kr-quick"],
},
finance: {
name: "财经",
sources: ["wallstreetcn", "36kr-quick"],
},
}
+58
View File
@@ -0,0 +1,58 @@
import { sources } from "./sources"
import { typeSafeObjectEntries, typeSafeObjectFromEntries } from "./type.util"
import type { Metadata } from "./types"
export const columnIds = ["focus", "realtime", "hottest", "china", "world", "tech", "finance"] as const
const originMetadata: Metadata = {
china: {
name: "国内",
sources: ["weibo", "douyin", "toutiao", "zhihu"],
},
world: {
name: "国际",
sources: ["zaobao", "cankaoxiaoxi"],
},
tech: {
name: "科技",
sources: ["ithome", "v2ex", "coolapk", "36kr-quick"],
},
finance: {
name: "财经",
sources: ["wallstreetcn", "36kr-quick"],
},
focus: {
name: "关注",
sources: [],
},
realtime: {
name: "实时",
sources: [],
},
hottest: {
name: "最热",
sources: [],
},
}
export const metadata = typeSafeObjectFromEntries(typeSafeObjectEntries(originMetadata).map(([k, v]) => {
switch (k) {
case "focus":
return [k, v]
case "hottest":
return [k, {
...v,
sources: typeSafeObjectEntries(sources).filter(([, v]) => v.type === "hottest" && !v.redirect).map(([k]) => k),
}]
case "realtime":
return [k, {
...v,
sources: typeSafeObjectEntries(sources).filter(([, v]) => v.type === "realtime" && !v.redirect).map(([k]) => k),
}]
default:
return [k, {
...v,
sources: v.sources.filter(s => sources[s]),
}]
}
}))
+53 -51
View File
@@ -17,6 +17,25 @@ export const originSources = {
color: "slate",
home: "https://v2ex.com/",
},
"zhihu": {
name: "知乎",
type: "hottest",
home: "https://www.zhihu.com",
},
"weibo": {
name: "微博",
title: "实时热搜",
type: "hottest",
color: "red",
interval: Time.Realtime,
home: "https://weibo.com",
},
"zaobao": {
name: "联合早报",
interval: Time.Common,
color: "red",
home: "https://www.zaobao.com",
},
"coolapk": {
name: "酷安",
type: "hottest",
@@ -27,22 +46,13 @@ export const originSources = {
"wallstreetcn": {
name: "华尔街见闻",
interval: Time.Fast,
type: "realtime",
home: "https://wallstreetcn.com/",
title: "快讯",
},
"sputniknewscn": {
name: "卫星通讯社",
color: "orange",
home: "https://sputniknews.cn",
},
"cankaoxiaoxi": {
name: "参考消息",
color: "red",
interval: Time.Common,
home: "https://china.cankaoxiaoxi.com",
},
"36kr": {
name: "36氪",
type: "realtime",
home: "https://36kr.com",
sub: {
quick: {
@@ -58,36 +68,14 @@ export const originSources = {
},
"hupu": {
name: "虎扑",
active: false,
home: "https://hupu.com",
},
"zhihu": {
name: "知乎",
type: "hottest",
home: "https://www.zhihu.com",
},
"weibo": {
name: "微博",
title: "实时热搜",
type: "hottest",
color: "red",
interval: Time.Realtime,
home: "https://weibo.com",
},
"tieba": {
name: "百度贴吧",
active: false,
home: "https://tieba.baidu.com",
},
"zaobao": {
name: "联合早报",
interval: Time.Common,
color: "red",
home: "https://www.zaobao.com",
},
"thepaper": {
name: "澎湃新闻",
interval: Time.Common,
home: "https://www.thepaper.cn",
},
"toutiao": {
name: "今日头条",
type: "hottest",
@@ -97,8 +85,27 @@ export const originSources = {
"ithome": {
name: "IT之家",
color: "red",
type: "realtime",
home: "https://www.ithome.com",
},
"thepaper": {
name: "澎湃新闻",
interval: Time.Common,
active: false,
home: "https://www.thepaper.cn",
},
"sputniknewscn": {
name: "卫星通讯社",
color: "orange",
active: false,
home: "https://sputniknews.cn",
},
"cankaoxiaoxi": {
name: "参考消息",
color: "red",
interval: Time.Common,
home: "https://china.cankaoxiaoxi.com",
},
} as const satisfies Record<string, OriginSource>
export const sources = genSources()
@@ -106,36 +113,31 @@ function genSources() {
const _: [SourceID, Source][] = []
Object.entries(originSources).forEach(([id, source]: [any, OriginSource]) => {
const parent = {
name: source.name,
type: source.type,
active: source.active ?? true,
color: source.color ?? "red",
interval: source.interval ?? Time.Default,
}
if (source.sub && Object.keys(source.sub).length) {
Object.entries(source.sub).forEach(([subId, subSource], i) => {
if (i === 0) {
_.push([id, {
redirect: `${id}-${subId}`,
name: source.name,
type: source.type,
color: source.color ?? "blue",
interval: source.interval ?? Time.Default,
...parent,
...subSource,
}] as [any, Source])
}
_.push([`${id}-${subId}`, {
name: source.name,
type: source.type,
color: source.color ?? "blue",
interval: source.interval ?? Time.Default,
...subSource,
}] as [any, Source])
_.push([`${id}-${subId}`, { ...parent, ...subSource }] as [any, Source])
})
} else {
_.push([id, {
name: source.name,
type: source.type,
color: source.color ?? "blue",
interval: source.interval ?? Time.Default,
title: source.title,
...parent,
}])
}
})
return typeSafeObjectFromEntries(_)
return typeSafeObjectFromEntries(_.filter(([_, v]) => v.active))
}
+4
View File
@@ -17,3 +17,7 @@ export function typeSafeObjectEntries<T extends Record<PropertyKey, unknown>>(ob
export function typeSafeObjectKeys<T extends Record<PropertyKey, unknown>>(obj: T): (keyof T)[] {
return Object.keys(obj) as (keyof T)[]
}
export function typeSafeObjectValues<T extends Record<PropertyKey, unknown>>(obj: T): T[keyof T][] {
return Object.values(obj) as T[keyof T][]
}
+12 -11
View File
@@ -1,5 +1,5 @@
import type { colors } from "unocss/preset-mini"
import type { columnIds } from "./data"
import type { columnIds } from "./metadata"
import type { originSources } from "./sources"
export type Color = Exclude<keyof typeof colors, "current" | "inherit" | "transparent" | "black" | "white">
@@ -8,10 +8,11 @@ type ConstSources = typeof originSources
type MainSourceID = keyof(ConstSources)
export type SourceID = {
[Key in MainSourceID]: ConstSources[Key] extends { sub?: infer SubType } ? keyof {
[Key in MainSourceID]: ConstSources[Key] extends { active?: false } ? never :
ConstSources[Key] extends { sub?: infer SubSource } ? {
// @ts-expect-error >_<
[K in keyof SubType as `${Key}-${K}` ]: never
} | Key : Key;
[SubKey in keyof SubSource ]: SubSource[SubKey] extends { active?: false } ? never : `${Key}-${SubKey}`
}[keyof SubSource] | Key : Key;
}[MainSourceID]
export type ColumnID = (typeof columnIds)[number]
@@ -24,18 +25,17 @@ export interface OriginSource {
* 刷新的间隔时间,复用缓存
*/
interval?: number
type?: "hottest" | "realtime"
/**
* @default latest
* @default true
*/
type?: "hottest" | "latest"
active?: boolean
home: string
color?: Color
sub?: Record<string, {
title: string
/**
* @default latest
*/
type?: "hottest" | "latest"
type?: "hottest" | "realtime"
active?: boolean
interval?: number
}>
}
@@ -43,8 +43,9 @@ export interface OriginSource {
export interface Source {
name: string
title?: string
type?: "hottest" | "latest"
type?: "hottest" | "realtime"
color: Color
active: boolean
interval: number
redirect?: SourceID
}
+67
View File
@@ -0,0 +1,67 @@
import { metadata } from "@shared/metadata"
import { typeSafeObjectEntries, typeSafeObjectFromEntries } from "@shared/type.util"
import type { ColumnID, SourceID } from "@shared/types"
import type { PrimitiveAtom } from "jotai"
import { atom } from "jotai"
import { ofetch } from "ofetch"
function sync(nextValue: any) {
if (__ENABLE_LOGIN__) {
const jwt = localStorage.getItem("user_jwt")
if (jwt) {
ofetch("/me/sync", {
method: "POST",
body: { data: nextValue },
headers: {
Authorization: `Bearer ${localStorage.getItem("user_jwt")}`,
},
}).then(console.log).catch(e => console.error(e))
}
}
}
export function atomWithLocalStorage<T>(
key: string,
initialValue: T | (() => T),
initFn?: ((stored: T) => T),
storage?: (next: T) => void,
): PrimitiveAtom<T> {
const getInitialValue = () => {
const item = localStorage.getItem(key)
try {
if (item !== null) {
const stored = JSON.parse(item)
if (initFn) return initFn(stored)
return stored
}
} catch {
//
}
if (initialValue instanceof Function) return initialValue()
else return initialValue
}
const baseAtom = atom(getInitialValue())
const derivedAtom = atom(
get => get(baseAtom),
(get, set, update: any) => {
const nextValue = typeof update === "function" ? update(get(baseAtom)) : update
set(baseAtom, nextValue)
localStorage.setItem(key, JSON.stringify(nextValue))
storage?.(nextValue)
},
)
return derivedAtom
}
const initialSources = typeSafeObjectFromEntries(typeSafeObjectEntries(metadata).map(([id, val]) => [id, val.sources]))
export const localSourcesAtom = atomWithLocalStorage<Record<ColumnID, SourceID[]>>("localsources", 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]]
}))
}, sync)
+2 -2
View File
@@ -1,9 +1,9 @@
import { atom } from "jotai"
import type { ColumnID, SourceID } from "@shared/types"
import { metadata } from "@shared/data"
import { metadata } from "@shared/metadata"
import { sources } from "@shared/sources"
import { typeSafeObjectEntries, typeSafeObjectFromEntries } from "@shared/type.util"
import { atomWithLocalStorage } from "./hooks/atomWithLocalStorage"
import { atomWithLocalStorage } from "./atomWithLocalStorage"
const initialSources = typeSafeObjectFromEntries(typeSafeObjectEntries(metadata).map(([id, val]) => [id, val.sources]))
export const localSourcesAtom = atomWithLocalStorage<Record<ColumnID, SourceID[]>>("localsources", () => {
+1 -1
View File
@@ -1,4 +1,4 @@
import { columnIds, metadata } from "@shared/data"
import { columnIds, metadata } from "@shared/metadata"
import type { ColumnID } from "@shared/types"
import { Link } from "@tanstack/react-router"
import clsx from "clsx"
-34
View File
@@ -1,34 +0,0 @@
import type { PrimitiveAtom } from "jotai"
import { atom } from "jotai"
export function atomWithLocalStorage<T>(
key: string,
initialValue: T | (() => T),
initFn?: ((stored: T) => T),
): PrimitiveAtom<T> {
const getInitialValue = () => {
const item = localStorage.getItem(key)
try {
if (item !== null) {
const stored = JSON.parse(item)
if (initFn) return initFn(stored)
return stored
}
} catch {
//
}
if (initialValue instanceof Function) return initialValue()
else return initialValue
}
const baseAtom = atom(getInitialValue())
const derivedAtom = atom(
get => get(baseAtom),
(get, set, update) => {
const nextValue
= typeof update === "function" ? update(get(baseAtom)) : update
set(baseAtom, nextValue)
localStorage.setItem(key, JSON.stringify(nextValue))
},
)
return derivedAtom
}
+1 -1
View File
@@ -1,5 +1,5 @@
import { createFileRoute, redirect } from "@tanstack/react-router"
import { columnIds } from "@shared/data"
import { columnIds } from "@shared/metadata"
import { Column } from "~/components/column"
export const Route = createFileRoute("/s/$column")({