diff --git a/package-lock.json b/package-lock.json index 1384e17..c5ae16d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "lucide-react": "^0.488.0", "react": "^19.0.0", "react-dom": "^19.0.0", + "react-router-dom": "^7.6.2", "tailwindcss": "^4.1.4" }, "devDependencies": { @@ -2086,6 +2087,15 @@ "dev": true, "license": "MIT" }, + "node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -3342,6 +3352,44 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.6.2.tgz", + "integrity": "sha512-U7Nv3y+bMimgWjhlT5CRdzHPu2/KVmqPwKUCChW8en5P3znxUqwlYFlbmyj8Rgp1SF6zs5X4+77kBVknkg6a0w==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.6.2.tgz", + "integrity": "sha512-Q8zb6VlTbdYKK5JJBLQEN06oTUa/RAbG/oQS1auK1I0TbJOXktqm+QENEVJU6QvWynlXPRBXI3fiOQcSEA78rA==", + "license": "MIT", + "dependencies": { + "react-router": "7.6.2" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -3442,6 +3490,12 @@ "semver": "bin/semver.js" } }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", diff --git a/package.json b/package.json index 8a98e95..42d768c 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "lucide-react": "^0.488.0", "react": "^19.0.0", "react-dom": "^19.0.0", + "react-router-dom": "^7.6.2", "tailwindcss": "^4.1.4" }, "devDependencies": { diff --git a/src/assets/main.css b/src/assets/main.css index cc2b822..46554a3 100644 --- a/src/assets/main.css +++ b/src/assets/main.css @@ -7,4 +7,9 @@ body { font-family: 'Pathway Gothic One', sans-serif; overflow: hidden; +} + +@keyframes scroll { + 0% { transform: translateX(100%); } + 100% { transform: translateX(-100%); } } \ No newline at end of file diff --git a/src/components/MusicComponent.tsx b/src/components/MusicComponent.tsx index 6a6c584..0f5f397 100644 --- a/src/components/MusicComponent.tsx +++ b/src/components/MusicComponent.tsx @@ -1,21 +1,8 @@ import { useState, useEffect } from "react"; - -type MusicObject = { - artist: { - mbid: string; - "#text": string; - }; - name: string; - image: ImageObject[]; - url: string; -}; -type ImageObject = { - size: "small" | "medium" | "large" | "extralarge"; - "#text": string; -}; +import type { LastFMMusicObject } from "../utils/types"; export default function MusicComponent() { - const [music, setMusic] = useState(null); + const [music, setMusic] = useState(null); const [failedFetch, setFailedFetch] = useState(false); useEffect(() => { @@ -23,7 +10,7 @@ export default function MusicComponent() { const response = await fetch("https://api.alexav.gg/v4/social/music"); if (response.ok) { const data = await response.json(); - setMusic(data.data.tracks[0] as MusicObject); + setMusic(data.data.tracks[0] as LastFMMusicObject); } else setFailedFetch(true); } diff --git a/src/main.tsx b/src/main.tsx index 1ffc8ff..07d3c54 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,5 +1,20 @@ import { createRoot } from "react-dom/client"; +import { createBrowserRouter, RouterProvider } from "react-router-dom"; import "./assets/main.css"; -import App from "./App.tsx"; +import App from "./pages/App.tsx"; +import Music from "./pages/Music.tsx"; -createRoot(document.getElementById("root")!).render(); +const router = createBrowserRouter([ + { + path: "/", + element: , + }, + { + path: "/music", + element: , + }, +]); + +createRoot(document.getElementById("root")!).render( + +); diff --git a/src/App.tsx b/src/pages/App.tsx similarity index 86% rename from src/App.tsx rename to src/pages/App.tsx index 08f9f51..8b94bcf 100644 --- a/src/App.tsx +++ b/src/pages/App.tsx @@ -1,8 +1,8 @@ -import MusicComponent from "./components/MusicComponent"; -import TVComponent from "./components/TVComponent"; -import Layout from "./components/Layout"; -import GameComponent from "./components/GameComponent"; -import RandomLyric from "./components/RandomLyric"; +import Layout from "../components/Layout"; +import MusicComponent from "../components/MusicComponent"; +import TVComponent from "../components/TVComponent"; +import GameComponent from "../components/GameComponent"; +import RandomLyric from "../components/RandomLyric"; export default function App() { return ( diff --git a/src/pages/Music.tsx b/src/pages/Music.tsx new file mode 100644 index 0000000..ac2492b --- /dev/null +++ b/src/pages/Music.tsx @@ -0,0 +1,44 @@ +import { useState, useEffect } from "react"; +import { LastFMMusicObject } from "../utils/types"; + +export default function Music() { + const [musicState, setMusicState] = useState(null); + + useEffect(() => { + const fetchMusic = async () => { + const res = await fetch("https://api.alexav.gg/v4/social/music"); + if (res.ok) { + const data = await res.json(); + setMusicState(data.data.tracks[0]); + } else { + console.error("failed to load music!"); + } + }; + + fetchMusic(); + setInterval(fetchMusic, 60000); + }, []); + + return ( +
+
+ v.size === "large")[0]["#text"]} + /> + +
+
+
+ + {musicState?.name} + +
+
+

{musicState?.artist?.["#text"]}

+
+
+
+ ); +} diff --git a/src/utils/types.ts b/src/utils/types.ts new file mode 100644 index 0000000..22f1d82 --- /dev/null +++ b/src/utils/types.ts @@ -0,0 +1,15 @@ +type LastFMMusicObject = { + artist: { + mbid: string; + "#text": string; + }; + name: string; + image: LastFMImageObject[]; + url: string; +}; +type LastFMImageObject = { + size: "small" | "medium" | "large" | "extralarge"; + "#text": string; +}; + +export type { LastFMMusicObject, LastFMImageObject };