From ae3dbb4253ef1fa5a7b8ee8349aa50c6319630ab Mon Sep 17 00:00:00 2001 From: nyne Date: Tue, 15 Jul 2025 15:34:25 +0800 Subject: [PATCH] Add background --- frontend/package-lock.json | 46 ++- frontend/package.json | 1 + frontend/src/components/comment_tile.tsx | 7 +- frontend/src/components/navigator.tsx | 391 +++++++++++-------- frontend/src/components/resource_card.tsx | 2 +- frontend/src/index.css | 12 + frontend/src/pages/activities_page.tsx | 2 +- frontend/src/pages/resource_details_page.tsx | 23 +- frontend/src/pages/tags_page.tsx | 2 +- frontend/src/pages/user_page.tsx | 2 +- 10 files changed, 313 insertions(+), 175 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 96d9917..e93297c 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -11,6 +11,7 @@ "@marsidev/react-turnstile": "^1.1.0", "@tailwindcss/vite": "^4.1.5", "axios": "^1.9.0", + "framer-motion": "^12.23.5", "i18next": "^25.1.1", "i18next-browser-languagedetector": "^8.1.0", "masonic": "^4.1.0", @@ -3371,6 +3372,33 @@ "node": ">= 0.6" } }, + "node_modules/framer-motion": { + "version": "12.23.5", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.5.tgz", + "integrity": "sha512-t+6/f2TUowkr1gVuGwVwxR3ZQupCdCZj0mivG8M8CW2kwHPqtSePomECvmto15qoFCwost77O/XuEsq59MLDKw==", + "license": "MIT", + "dependencies": { + "motion-dom": "^12.23.5", + "motion-utils": "^12.23.2", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, "node_modules/fresh": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", @@ -4939,6 +4967,21 @@ "node": "*" } }, + "node_modules/motion-dom": { + "version": "12.23.5", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.5.tgz", + "integrity": "sha512-RrUS4X11w7kIU1COoSVptuYTx1QQ/sViDEI1Yl1zL0nem8UXn3HRcsMrFTCkZSSRLXeVpN540bFP1iS87SicPQ==", + "license": "MIT", + "dependencies": { + "motion-utils": "^12.23.2" + } + }, + "node_modules/motion-utils": { + "version": "12.23.2", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.2.tgz", + "integrity": "sha512-cIEXlBlXAOUyiAtR0S+QPQUM9L3Diz23Bo+zM420NvSd/oPQJwg6U+rT+WRTpp0rizMsBGQOsAwhWIfglUcZfA==", + "license": "MIT" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -6051,8 +6094,7 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD", - "optional": true + "license": "0BSD" }, "node_modules/turbo-stream": { "version": "2.4.0", diff --git a/frontend/package.json b/frontend/package.json index 7a5e87f..53c3855 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -18,6 +18,7 @@ "@marsidev/react-turnstile": "^1.1.0", "@tailwindcss/vite": "^4.1.5", "axios": "^1.9.0", + "framer-motion": "^12.23.5", "i18next": "^25.1.1", "i18next-browser-languagedetector": "^8.1.0", "masonic": "^4.1.0", diff --git a/frontend/src/components/comment_tile.tsx b/frontend/src/components/comment_tile.tsx index 70b8646..35e3c38 100644 --- a/frontend/src/components/comment_tile.tsx +++ b/frontend/src/components/comment_tile.tsx @@ -24,7 +24,7 @@ export function CommentTile({
- { e.stopPropagation(); @@ -49,7 +48,7 @@ export function CommentTile({ {comment.user.username} - +

diff --git a/frontend/src/components/navigator.tsx b/frontend/src/components/navigator.tsx index e514d4d..2543a5f 100644 --- a/frontend/src/components/navigator.tsx +++ b/frontend/src/components/navigator.tsx @@ -8,6 +8,7 @@ import UploadingSideBar from "./uploading_side_bar.tsx"; import { ThemeSwitcher } from "./theme_switcher.tsx"; import { IoLogoGithub } from "react-icons/io"; import { useAppContext } from "./AppContext.tsx"; +import { AnimatePresence, motion } from "framer-motion"; export default function Navigator() { const outlet = useOutlet(); @@ -16,204 +17,274 @@ export default function Navigator() { const [key, setKey] = useState(0); + const [background, setBackground] = useState(undefined); + const appContext = useAppContext(); const [naviContext, _] = useState({ refresh: () => { setKey(key + 1); }, + setBackground: (b: string) => { + if (b !== background) { + setBackground(b); + } + }, }); const { t } = useTranslation(); return ( - <> - +
+ {/* background */} + {background && ( +
+ + + +
+ )} + + {/* Background overlay */} + {background && ( +
+ )} + + {/* Content overlay with backdrop blur */}
-
-
-
- +
+
+
+
+
+
- - -
-
    -
  • { - const menu = document.getElementById( - "navi_menu", - ) as HTMLElement; - menu.blur(); - navigate("/"); - }} + + + +
+ +
+
+ +
+
+
    +
  • { + navigate("/"); + }} + > + {t("Home")} +
  • +
  • { + navigate("/tags"); + }} + > + {t("Tags")} +
  • +
  • { + navigate("/random"); + }} + > + {t("Random")} +
  • +
  • { navigate("/activity"); }} > - {t("Activity")} - -
  • -
  • { - const menu = document.getElementById( - "navi_menu", - ) as HTMLElement; - menu.blur(); - navigate("/random"); - }} + {t("Activity")} +
  • +
  • { + navigate("/about"); + }} + > + {t("About")} +
  • +
+
+
+
+ + + + - {t("Random")} - -
  • { - const menu = document.getElementById( - "navi_menu", - ) as HTMLElement; - menu.blur(); - navigate("/about"); - }} - > - {t("About")} -
  • - -
    -
    - -
    -
    - -
    -
    -
    - - - - - - - {app.isLoggedIn() ? ( - - ) : ( - - )} + + + {app.isLoggedIn() ? ( + + ) : ( + + )} +
    + +
    {outlet}
    +
    - -
    {outlet}
    -
    - +
    ); } interface NavigatorContext { refresh: () => void; + setBackground: (background: string) => void; } const navigatorContext = createContext({ refresh: () => { // do nothing }, + setBackground: (_) => { + // do nothing + }, }); export function useNavigator() { diff --git a/frontend/src/components/resource_card.tsx b/frontend/src/components/resource_card.tsx index 8e680b8..09d6e7f 100644 --- a/frontend/src/components/resource_card.tsx +++ b/frontend/src/components/resource_card.tsx @@ -20,7 +20,7 @@ export default function ResourceCard({ resource }: { resource: Resource }) { navigate(`/resources/${resource.id}`); }} > -
    +
    {resource.image != null && (
    { if ( diff --git a/frontend/src/pages/resource_details_page.tsx b/frontend/src/pages/resource_details_page.tsx index c82c24f..08101b2 100644 --- a/frontend/src/pages/resource_details_page.tsx +++ b/frontend/src/pages/resource_details_page.tsx @@ -49,6 +49,7 @@ import { useAppContext } from "../components/AppContext.tsx"; import { BiLogoSteam } from "react-icons/bi"; import { CommentTile } from "../components/comment_tile.tsx"; import { CommentInput } from "../components/comment_input.tsx"; +import { useNavigator } from "../components/navigator.tsx"; export default function ResourcePage() { const params = useParams(); @@ -64,6 +65,8 @@ export default function ResourcePage() { const location = useLocation(); + const navigator = useNavigator(); + const reload = useCallback(async () => { if (!isNaN(id)) { setResource(null); @@ -97,7 +100,6 @@ export default function ResourcePage() { network.getResourceDetails(id).then((res) => { if (res.success) { setResource(res.data!); - document.title = res.data!.title; } else { showToast({ message: res.message, type: "error" }); } @@ -107,6 +109,15 @@ export default function ResourcePage() { } }, [id, location.state]); + useEffect(() => { + if (resource) { + document.title = resource.title; + if (resource.images.length > 0) { + navigator.setBackground(network.getImageUrl(resource.images[0].id)); + } + } + }, [resource]) + const navigate = useNavigate(); // 标签页与hash的映射 @@ -195,7 +206,7 @@ export default function ResourcePage() { {l.url.includes("steampowered.com") ? ( @@ -211,7 +222,9 @@ export default function ResourcePage() {

    )} -
    +