This commit is contained in:
Ou 2024-10-19 13:15:29 +08:00
parent 9eeee30e8c
commit 179358c919
11 changed files with 196 additions and 123 deletions

View File

@ -36,9 +36,13 @@ export function Dnd() {
initial="hidden"
animate="visible"
variants={{
hidden: {
opacity: 0,
},
visible: {
opacity: 1,
transition: {
delayChildren: 0.2,
delayChildren: 0.1,
staggerChildren: 0.1,
},
},

View File

@ -1,8 +1,10 @@
import type { ColumnID } from "@shared/types"
import { useAtom } from "jotai"
import { useEffect } from "react"
import { useTitle } from "react-use"
import { metadata } from "@shared/metadata"
import { NavBar } from "../navbar"
import { Dnd } from "./dnd"
import { NavBar } from "./navbar"
import { currentColumnIDAtom } from "~/atoms"
export function Column({ id }: { id: ColumnID }) {
@ -10,10 +12,18 @@ export function Column({ id }: { id: ColumnID }) {
useEffect(() => {
setCurrentColumnID(id)
}, [id, setCurrentColumnID])
return (
<>
<NavBar id={id} />
{ currentColumnID === id && <Dnd />}
</>
)
useTitle(`NewsNow | ${metadata[id].name}`)
if (id === currentColumnID) {
return (
<>
<div className="flex justify-center md:hidden">
<NavBar />
</div>
<div className="mt-10">
<Dnd />
</div>
</>
)
}
}

View File

@ -1,33 +0,0 @@
import { columnIds, metadata } from "@shared/metadata"
import type { ColumnID } from "@shared/types"
import { Link } from "@tanstack/react-router"
import clsx from "clsx"
import { useTitle } from "react-use"
export function NavBar({ id }: { id: ColumnID }) {
useTitle(`NewsNow | ${metadata[id].name}`)
return (
<div className="w-full flex justify-center">
<span className={clsx([
"flex mb-4 p-3 rounded-2xl bg-primary/1",
"shadow shadow-primary/20 hover:shadow-primary/50 transition-shadow-500",
"md:(z-100 mb-6)",
])}
>
{columnIds.map(columnId => (
<Link
key={columnId}
to="/c/$column"
params={{ column: columnId }}
className={clsx(
"text-sm px-2",
id === columnId ? "color-primary font-bold" : "op-70 dark:op-90 hover:(bg-primary/15 rounded-md)",
)}
>
{metadata[columnId].name}
</Link>
))}
</span>
</div>
)
}

View File

@ -0,0 +1,35 @@
import clsx from "clsx"
import { Toaster } from "sonner"
export function Toast() {
return (
<Toaster
toastOptions={{
duration: 10000000,
unstyled: true,
classNames: {
toast: clsx(
"flex gap-1 p-1 rounded-xl backdrop-blur-5 items-center bg-op-40! w-full",
"bg-blue",
"data-[type=error]:(bg-red)",
"data-[type=success]:(bg-green)",
"data-[type=info]:(bg-blue)",
"data-[type=warning]:(bg-yellow)",
),
icon: "text-white ml-1 dark:text-dark-600 text-op-80!",
content: "bg-base bg-op-70! p-2 rounded-md color-base w-full backdrop-blur-md",
title: "font-normal text-base",
description: "color-base text-op-80! text-sm",
actionButton: "bg-base bg-op-70! rounded-md py-2 w-4em backdrop-blur-md hover:(bg-base bg-op-60!)",
closeButton: "bg-base bg-op-50! border-0 hover:(bg-base bg-op-70!)",
},
}}
closeButton
expand
style={{
top: 10,
}}
position="top-center"
/>
)
}

View File

@ -5,76 +5,23 @@ import { useIsFetching } from "@tanstack/react-query"
import clsx from "clsx"
import type { SourceID } from "@shared/types"
import { Homepage, Version } from "@shared/consts"
import { useLocalStorage } from "react-use"
import { useDark } from "~/hooks/useDark"
import { NavBar } from "../navbar"
import { Menu } from "./menu"
import { currentSourcesAtom, goToTopAtom, refetchSourcesAtom } from "~/atoms"
function ThemeToggle() {
const { toggleDark } = useDark()
return (
<button
type="button"
title="Toggle Dark Mode"
className="i-ph-sun-dim-duotone dark:i-ph-moon-stars-duotone btn"
onClick={toggleDark}
/>
)
}
function LoginIn() {
// useLocalStorage 默认会自动序列化
const [info] = useLocalStorage<{ name: string, avatar: string }>("user_info")
const [jwt, _setJwt] = useLocalStorage<string>("user_jwt", undefined, {
raw: true,
})
if (jwt) {
return (
<button
type="button"
className="btn"
title={info?.name ?? ""}
onClick={() => {
// setJwt("")
}}
>
<div
className="h-6 w-6 rounded-full bg-cover border p-1 border-primary-600 dark:border-primary"
style={
{
backgroundImage: `url(${info?.avatar})`,
}
}
/>
</button>
)
}
return (
<a
title="Login in with GitHub"
className="i-ph:sign-in-duotone btn"
href={`https://github.com/login/oauth/authorize?client_id=${__G_CLIENT_ID__}`}
/>
)
}
function GoTop() {
const { ok, fn: goToTop } = useAtomValue(goToTopAtom)
return (
ok && (
<button
type="button"
title="Go To Top"
className="i-ph:arrow-fat-up-duotone btn"
onClick={goToTop}
/>
)
<button
type="button"
title="Go To Top"
className={clsx("i-ph:arrow-fat-up-duotone", ok ? "op-50 btn" : "op-0")}
onClick={goToTop}
/>
)
}
export function GithubIcon() {
return <a className="i-ph-github-logo-duotone inline-block btn" href={Homepage} title="Project Homepage" />
}
function RefreshButton() {
function Refresh() {
const currentSources = useAtomValue(currentSourcesAtom)
const setRefetchSource = useSetAtom(refetchSourcesAtom)
const refreshAll = useCallback(() => {
@ -115,16 +62,17 @@ export function Header() {
</p>
</span>
</Link>
<a target="_blank" className="btn text-sm ml-1 font-mono">
<a target="_blank" href={`${Homepage}/release/tag/${Version}`} className="btn text-sm ml-1 font-mono">
{`v${Version}`}
</a>
</span>
<span className="hidden md:inline-block">
<NavBar />
</span>
<span className="flex gap-2 items-center text-xl text-primary-600 dark:text-primary">
<GoTop />
<RefreshButton />
<ThemeToggle />
<GithubIcon />
{ __ENABLE_LOGIN__ && <LoginIn />}
<Refresh />
<Menu />
</span>
</>
)

View File

@ -0,0 +1,85 @@
import { Homepage } from "@shared/consts"
import clsx from "clsx"
import { motion } from "framer-motion"
import { useRef, useState } from "react"
import { useClickAway } from "react-use"
import { useDark } from "~/hooks/useDark"
import { useLogin } from "~/hooks/useLogin"
function ThemeToggle() {
const { isDark, toggleDark } = useDark()
return (
<li onClick={toggleDark}>
<span className={clsx("inline-block", isDark ? "i-ph-moon-stars-duotone" : "i-ph-sun-dim-duotone")} />
<span>
{isDark ? "黑暗模式" : "白天模式"}
</span>
</li>
)
}
export function Menu() {
const { loggedIn, login, logout, enabledLogin, userInfo } = useLogin()
const [shown, show] = useState(false)
const ref = useRef<HTMLDivElement>(null)
useClickAway(ref, () => {
show(false)
})
return (
<span ref={ref} className="relative">
<span className="flex items-center scale-90" onClick={() => show(!shown)}>
{
enabledLogin && loggedIn && userInfo.avatar
? (
<button
type="button"
className="h-6 w-6 rounded-full bg-cover"
style={
{
backgroundImage: `url(${userInfo.avatar})`,
}
}
>
</button>
)
: <button type="button" className="btn i-si:more-muted-horiz-circle-duotone" />
}
</span>
{shown && (
<motion.div
id="dropdown-menu"
className={clsx([
"absolute top-2rem right-0 z-99 w-200px",
"bg-primary p-1 backdrop-blur-5 bg-op-40! rounded-xl",
])}
initial={{
scale: 0.9,
}}
animate={{
scale: 1,
}}
>
<ol className="bg-base bg-op-70! backdrop-blur-md p-2 rounded-md color-base text-base">
{enabledLogin && !loggedIn && (
<li onClick={login}>
<span className="i-ph:sign-in-duotone inline-block" />
<span>Github </span>
</li>
)}
{enabledLogin && loggedIn && (
<li onClick={logout}>
<span className="i-ph:sign-out-duotone inline-block" />
<span>退</span>
</li>
)}
<ThemeToggle />
<li onClick={() => window.open(Homepage)}>
<span className="i-ph:github-logo-duotone inline-block" />
<span>Star on Github </span>
</li>
</ol>
</motion.div>
)}
</span>
)
}

30
src/components/navbar.tsx Normal file
View File

@ -0,0 +1,30 @@
import { columnIds, metadata } from "@shared/metadata"
import { Link } from "@tanstack/react-router"
import clsx from "clsx"
import { useAtomValue } from "jotai"
import { currentColumnIDAtom } from "~/atoms"
export function NavBar() {
const currentId = useAtomValue(currentColumnIDAtom)
return (
<span className={clsx([
"flex p-3 rounded-2xl bg-primary/1",
"shadow shadow-primary/20 hover:shadow-primary/50 transition-shadow-500",
])}
>
{columnIds.map(columnId => (
<Link
key={columnId}
to="/c/$column"
params={{ column: columnId }}
className={clsx(
"text-sm px-2 hover:(bg-primary/10 rounded-md)",
currentId === columnId ? "color-primary font-bold" : "op-70 dark:op-90",
)}
>
{metadata[columnId].name}
</Link>
))}
</span>
)
}

View File

@ -4,9 +4,7 @@ import { useLocalStorage, useMedia } from "react-use"
export declare type ColorScheme = "dark" | "light" | "auto"
export function useDark(key = "color-scheme", defaultColorScheme: ColorScheme = "auto") {
const [colorScheme, setColorScheme] = useLocalStorage(key, defaultColorScheme, {
raw: true,
})
const [colorScheme, setColorScheme] = useLocalStorage(key, defaultColorScheme)
const prefersDarkMode = useMedia("(prefers-color-scheme: dark)")
const isDark = useMemo(() => colorScheme === "auto" ? prefersDarkMode : colorScheme === "dark", [colorScheme, prefersDarkMode])

View File

@ -1,5 +1,4 @@
import { useEffect } from "react"
import { useBeforeUnload } from "react-use"
import { useBeforeUnload, useMount } from "react-use"
export function useOnReload(fn?: () => Promise<void> | void, fallback?: () => Promise<void> | void) {
useBeforeUnload(() => {
@ -7,7 +6,7 @@ export function useOnReload(fn?: () => Promise<void> | void, fallback?: () => Pr
return false
})
useEffect(() => {
useMount(() => {
const _ = localStorage.getItem("quitTime")
const quitTime = _ ? Number(_) : 0
if (!Number.isNaN(quitTime) && Date.now() - quitTime < 1000) {
@ -15,6 +14,5 @@ export function useOnReload(fn?: () => Promise<void> | void, fallback?: () => Pr
} else {
fallback?.()
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
})
}

View File

@ -1,9 +1,7 @@
import { createFileRoute } from "@tanstack/react-router"
import { useAtomValue } from "jotai"
import { useMemo } from "react"
import { focusSourcesAtom } from "~/atoms"
import { Column } from "~/components/column"
import { } from "cookie-es"
export const Route = createFileRoute("/")({
component: IndexComponent,
@ -11,9 +9,5 @@ export const Route = createFileRoute("/")({
function IndexComponent() {
const focusSources = useAtomValue(focusSourcesAtom)
const id = useMemo(() => {
return focusSources.length ? "focus" : "realtime"
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
return <Column id={id} />
return <Column id={focusSources.length ? "focus" : "realtime"} />
}

View File

@ -46,4 +46,8 @@ button:disabled {
*, a, button {
cursor: default;
user-select: none;
}
#dropdown-menu li {
--at-apply: hover:bg-neutral-400/10 rounded-md flex items-center p-1 gap-1;
}