diff --git a/src/app/layout.tsx b/src/app/layout.tsx index f77d49771b..04657ae231 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -26,7 +26,7 @@ export const generateMetadata = defineMetadata(async (_, getData) => { return { metadataBase: new URL(url.webUrl), title: { - template: `%s | ${seo.title}`, + template: `%s - ${seo.title}`, default: `${seo.title} - ${seo.description}`, }, description: seo.description, diff --git a/src/app/projects/[id]/layout.tsx b/src/app/projects/[id]/layout.tsx new file mode 100644 index 0000000000..f4e8b80f63 --- /dev/null +++ b/src/app/projects/[id]/layout.tsx @@ -0,0 +1,8 @@ +import type { PropsWithChildren } from 'react' + +export const metadata = { + title: '项目详情', +} +export default function Page(props: PropsWithChildren) { + return props.children +} diff --git a/src/app/projects/[id]/page.tsx b/src/app/projects/[id]/page.tsx new file mode 100644 index 0000000000..763449f62b --- /dev/null +++ b/src/app/projects/[id]/page.tsx @@ -0,0 +1,38 @@ +'use client' + +import { useQuery } from '@tanstack/react-query' +import { useEffect } from 'react' +import { useParams, useRouter } from 'next/navigation' + +import { NotFound404 } from '~/components/common/404' +import { Loading } from '~/components/ui/loading' +import { apiClient } from '~/utils/request' + +export default function Page() { + const { id } = useParams() + + const { data, isLoading } = useQuery({ + queryKey: [id, 'project'], + queryFn: async ({ queryKey }) => { + const [id] = queryKey + return apiClient.project.getById(id) + }, + }) + const router = useRouter() + useEffect(() => { + if (data?.projectUrl) { + window.open(data.projectUrl) + router.back() + } + }, [data?.projectUrl]) + + if (isLoading) { + return + } + + if (!data) { + return + } + + return null +} diff --git a/src/app/projects/layout.tsx b/src/app/projects/layout.tsx new file mode 100644 index 0000000000..dc4288f695 --- /dev/null +++ b/src/app/projects/layout.tsx @@ -0,0 +1,11 @@ +import type { Metadata } from 'next' +import type { PropsWithChildren } from 'react' + +import { WiderContainer } from '~/components/layout/container/Wider' + +export const metadata: Metadata = { + title: '项目', +} +export default async function Layout(props: PropsWithChildren) { + return {props.children} +} diff --git a/src/app/projects/page.tsx b/src/app/projects/page.tsx new file mode 100644 index 0000000000..afbea7a730 --- /dev/null +++ b/src/app/projects/page.tsx @@ -0,0 +1,60 @@ +'use client' + +import { useQuery } from '@tanstack/react-query' + +import { CodiconGithubInverted } from '~/components/icons/menu-collection' +import { Loading } from '~/components/ui/loading' +import { BottomToUpTransitionView } from '~/components/ui/transition/BottomToUpTransitionView' +import { ProjectList } from '~/components/widgets/project/ProjectList' +import { NothingFound } from '~/components/widgets/shared/NothingFound' +import { noopArr } from '~/lib/noop' +import { useAggregationSelector } from '~/providers/root/aggregation-data-provider' +import { apiClient } from '~/utils/request' + +export default function Page() { + const { data, isLoading } = useQuery({ + queryKey: ['projects'], + queryFn: async () => { + const data = await apiClient.project.getAll() + return data.data + }, + }) + + const githubUsername = useAggregationSelector( + (state) => state.user?.socialIds?.github, + ) + + if (isLoading) { + return + } + + if (!data) return + + return ( +
+
+

项目们

+
+ +
+
+ 项目{' '} + {githubUsername && ( + + + + )} +
+ + + +
+
+ ) +} diff --git a/src/components/layout/header/internal/HeaderContent.tsx b/src/components/layout/header/internal/HeaderContent.tsx index 05f76f9584..ff67801cf6 100644 --- a/src/components/layout/header/internal/HeaderContent.tsx +++ b/src/components/layout/header/internal/HeaderContent.tsx @@ -2,7 +2,12 @@ import React, { memo } from 'react' import clsx from 'clsx' -import { AnimatePresence, m, useMotionValue } from 'framer-motion' +import { + AnimatePresence, + m, + useMotionTemplate, + useMotionValue, +} from 'framer-motion' import Link from 'next/link' import { usePathname } from 'next/navigation' import type { IHeaderMenu } from '../config' @@ -12,7 +17,11 @@ import { usePageScrollDirectionSelector } from '~/providers/root/page-scroll-inf import { clsxm } from '~/utils/helper' import { useHeaderConfig } from './HeaderDataConfigureProvider' -import { useHeaderBgOpacity, useMenuOpacity } from './hooks' +import { + useHeaderBgOpacity, + useHeaderHasMetaInfo, + useMenuOpacity, +} from './hooks' import { MenuPopover } from './MenuPopover' export const HeaderContent = () => { @@ -28,12 +37,13 @@ export const HeaderContent = () => { const AccessibleMenu: Component = () => { const headerOpacity = useHeaderBgOpacity() + const hasMetaInfo = useHeaderHasMetaInfo() const showShow = usePageScrollDirectionSelector( (d) => { - return d === 'up' && headerOpacity > 0.8 + return d === 'up' && headerOpacity > 0.8 && hasMetaInfo }, - [headerOpacity], + [headerOpacity, hasMetaInfo], ) return ( @@ -56,20 +66,25 @@ const AccessibleMenu: Component = () => { const AnimatedMenu: Component = ({ children }) => { const opacity = useMenuOpacity() + const hasMetaInfo = useHeaderHasMetaInfo() + const shouldHideNavBg = !hasMetaInfo && opacity === 0 return ( - {children} + {/* @ts-ignore */} + {React.cloneElement(children, { shouldHideNavBg })} ) } -const ForDesktop: Component = ({ className }) => { +const ForDesktop: Component<{ + shouldHideNavBg?: boolean +}> = ({ className, shouldHideNavBg }) => { const mouseX = useMotionValue(0) const mouseY = useMotionValue(0) const radius = useMotionValue(0) @@ -86,6 +101,8 @@ const ForDesktop: Component = ({ className }) => { const { config: headerMenuConfig } = useHeaderConfig() const pathname = usePathname() + const background = useMotionTemplate`radial-gradient(${radius}px circle at ${mouseX}px ${mouseY}px, var(--spotlight-color) 0%, transparent 65%)` + return ( { 'rounded-full bg-gradient-to-b from-zinc-50/70 to-white/90', 'shadow-lg shadow-zinc-800/5 ring-1 ring-zinc-900/5 backdrop-blur-md', 'dark:from-zinc-900/70 dark:to-zinc-800/90 dark:ring-zinc-100/10', - + 'group [--spotlight-color:hsl(var(--a)_/_0.05)]', + 'duration-200', + shouldHideNavBg && 'bg-none shadow-none ring-transparent', className, )} > + {/* Spotlight overlay */} +