diff --git a/src/App.tsx b/src/App.tsx
index c174442..e41f4ac 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,243 +1,82 @@
-import React, { useState, useEffect } from "react";
-import { Flashlight, FlashlightOff, Twitter, Github } from "lucide-react";
-import CordycepsImage from "./assets/cordyceps.png";
+import { Twitter, Github } from "lucide-react";
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";
-export default function MouseFlashlight() {
- const [pointerPosition, setPointerPosition] = useState({ x: 500, y: 500 });
- const [isActive, setIsActive] = useState(false);
- const [isTouchDevice, setIsTouchDevice] = useState(false);
- const [isFlashlightEnabled, setIsFlashlightEnabled] = useState(false);
-
- // Detect touch device on component mount
- useEffect(() => {
- const isTouchCapable =
- "ontouchstart" in window || navigator.maxTouchPoints > 0;
-
- setIsTouchDevice(isTouchCapable);
- }, []);
-
- // Handle mouse events for desktop
- const handleMouseMove = (e: React.MouseEvent) => {
- setPointerPosition({ x: e.clientX, y: e.clientY });
- setIsActive(true);
- };
-
- const handleMouseLeave = () => {
- setIsActive(false);
- };
-
- // Handle touch events for mobile
- const handleTouchMove = (e: React.TouchEvent) => {
- if (e.touches && e.touches[0]) {
- e.preventDefault(); // Prevent scrolling while touching
- const touch = e.touches[0];
- setPointerPosition({ x: touch.clientX, y: touch.clientY });
- setIsActive(true);
- }
- };
-
- const handleTouchStart = (e: React.TouchEvent) => {
- if (e.touches && e.touches[0]) {
- const touch = e.touches[0];
- setPointerPosition({ x: touch.clientX, y: touch.clientY });
- setIsActive(true);
- }
- };
-
- const handleTouchEnd = () => {
- // On mobile, we'll keep the last position visible for a moment
- setTimeout(() => {
- setIsActive(false);
- }, 1000);
- };
-
- useEffect(() => {
- // Define window event handlers with correct types
- const windowMouseMove = (e: MouseEvent) => {
- setPointerPosition({ x: e.clientX, y: e.clientY });
- setIsActive(true);
- };
-
- const windowMouseLeave = () => {
- setIsActive(false);
- };
-
- const windowTouchStart = (e: TouchEvent) => {
- if (e.touches && e.touches[0]) {
- const touch = e.touches[0];
- setPointerPosition({ x: touch.clientX, y: touch.clientY });
- setIsActive(true);
- }
- };
-
- const windowTouchMove = (e: TouchEvent) => {
- if (e.touches && e.touches[0]) {
- e.preventDefault?.(); // Optional chaining in case preventDefault isn't available
- const touch = e.touches[0];
- setPointerPosition({ x: touch.clientX, y: touch.clientY });
- setIsActive(true);
- }
- };
-
- const windowTouchEnd = () => {
- // On mobile, we'll keep the last position visible for a moment
- setTimeout(() => {
- setIsActive(false);
- }, 1000);
- };
-
- if (isFlashlightEnabled) {
- // Add desktop event listeners
- window.addEventListener("mousemove", windowMouseMove);
- window.addEventListener("mouseleave", windowMouseLeave);
-
- // Add mobile event listeners
- window.addEventListener("touchstart", windowTouchStart, {
- passive: false,
- });
- window.addEventListener("touchmove", windowTouchMove, { passive: false });
- window.addEventListener("touchend", windowTouchEnd);
- } else {
- // Remove desktop event listeners
- window.removeEventListener("mousemove", windowMouseMove);
- window.removeEventListener("mouseleave", windowMouseLeave);
-
- // Remove mobile event listeners
- window.removeEventListener("touchstart", windowTouchStart);
- window.removeEventListener("touchmove", windowTouchMove);
- window.removeEventListener("touchend", windowTouchEnd);
- }
-
- // Cleanup when component unmounts
- return () => {
- window.removeEventListener("mousemove", windowMouseMove);
- window.removeEventListener("mouseleave", windowMouseLeave);
- window.removeEventListener("touchstart", windowTouchStart);
- window.removeEventListener("touchmove", windowTouchMove);
- window.removeEventListener("touchend", windowTouchEnd);
- };
- }, [isFlashlightEnabled]);
-
+export default function App() {
return (
-
-
-
-
-
setIsFlashlightEnabled(!isFlashlightEnabled)}
- >
- {isFlashlightEnabled ?
:
}
+
+
+
+
+
+
+ alex, 18
+
+
+

+
+
-
welcome !
-
+
+
what i do
+
+ qa @{" "}
+
+ medal.tv
+
+ , game, code
+
-
-
-
-
-
- alex, 18
-
-

+
obsessed with
+
tlou, twd, rdr, dexter
-
-
-
-
-
what i do
-
- qa @{" "}
-
- medal.tv
-
- , game, code
-
+
status
+
+
-
obsessed with
-
tlou, twd, rdr
-
-
watching
-
-
-
socials
-
+
socials
+
-
- {isFlashlightEnabled && (
- <>
-
-
-

-
-
- >
- )}
-
+
);
}
diff --git a/src/components/GameComponent.tsx b/src/components/GameComponent.tsx
new file mode 100644
index 0000000..6a762f7
--- /dev/null
+++ b/src/components/GameComponent.tsx
@@ -0,0 +1,62 @@
+import { useState, useEffect } from "react";
+
+type GameObject = {
+ name: string;
+ location: string;
+ startedOn: number;
+ finished: boolean;
+};
+
+export default function GameComponent() {
+ const [game, setGame] = useState
(null);
+ const [failedFetch, setFailedFetch] = useState(false);
+
+ useEffect(() => {
+ async function fetchGame() {
+ const response = await fetch(
+ "https://api.alexav.gg/v4/tracker/latestGame"
+ );
+ if (response.ok) {
+ const data = await response.json();
+ if (data.data && data.data.length <= 0) {
+ setFailedFetch(true);
+ } else setGame(data.data as GameObject);
+ } else setFailedFetch(true);
+ }
+
+ setTimeout(() => {
+ fetchGame();
+ }, 60000);
+ fetchGame();
+ }, []);
+
+ if (game) {
+ return (
+
+ );
+ } else {
+ if (failedFetch) {
+ return (
+
+
failed to get game :(
+
+ );
+ } else {
+ return (
+
+ );
+ }
+ }
+}
diff --git a/src/components/Layout.tsx b/src/components/Layout.tsx
new file mode 100644
index 0000000..ec75a59
--- /dev/null
+++ b/src/components/Layout.tsx
@@ -0,0 +1,175 @@
+import { useState, useEffect, type ReactNode } from "react";
+import { Flashlight, FlashlightOff } from "lucide-react";
+import CordycepsImage from "../assets/cordyceps.png";
+
+export default function Layout({ children }: { children: ReactNode }) {
+ const [pointerPosition, setPointerPosition] = useState({ x: 500, y: 500 });
+ const [isActive, setIsActive] = useState(false);
+ const [isTouchDevice, setIsTouchDevice] = useState(false);
+ const [isFlashlightEnabled, setIsFlashlightEnabled] = useState(false);
+
+ // Detect touch device on component mount
+ useEffect(() => {
+ const isTouchCapable =
+ "ontouchstart" in window || navigator.maxTouchPoints > 0;
+
+ setIsTouchDevice(isTouchCapable);
+ }, []);
+
+ // Handle mouse events for desktop
+ const handleMouseMove = (e: React.MouseEvent) => {
+ setPointerPosition({ x: e.clientX, y: e.clientY });
+ setIsActive(true);
+ };
+
+ const handleMouseLeave = () => {
+ setIsActive(false);
+ };
+
+ // Handle touch events for mobile
+ const handleTouchMove = (e: React.TouchEvent) => {
+ if (e.touches && e.touches[0]) {
+ e.preventDefault(); // Prevent scrolling while touching
+ const touch = e.touches[0];
+ setPointerPosition({ x: touch.clientX, y: touch.clientY });
+ setIsActive(true);
+ }
+ };
+
+ const handleTouchStart = (e: React.TouchEvent) => {
+ if (e.touches && e.touches[0]) {
+ const touch = e.touches[0];
+ setPointerPosition({ x: touch.clientX, y: touch.clientY });
+ setIsActive(true);
+ }
+ };
+
+ const handleTouchEnd = () => {
+ // On mobile, we'll keep the last position visible for a moment
+ setTimeout(() => {
+ setIsActive(false);
+ }, 1000);
+ };
+
+ useEffect(() => {
+ // Define window event handlers with correct types
+ const windowMouseMove = (e: MouseEvent) => {
+ setPointerPosition({ x: e.clientX, y: e.clientY });
+ setIsActive(true);
+ };
+
+ const windowMouseLeave = () => {
+ setIsActive(false);
+ };
+
+ const windowTouchStart = (e: TouchEvent) => {
+ if (e.touches && e.touches[0]) {
+ const touch = e.touches[0];
+ setPointerPosition({ x: touch.clientX, y: touch.clientY });
+ setIsActive(true);
+ }
+ };
+
+ const windowTouchMove = (e: TouchEvent) => {
+ if (e.touches && e.touches[0]) {
+ e.preventDefault?.(); // Optional chaining in case preventDefault isn't available
+ const touch = e.touches[0];
+ setPointerPosition({ x: touch.clientX, y: touch.clientY });
+ setIsActive(true);
+ }
+ };
+
+ const windowTouchEnd = () => {
+ // On mobile, we'll keep the last position visible for a moment
+ setTimeout(() => {
+ setIsActive(false);
+ }, 1000);
+ };
+
+ if (isFlashlightEnabled) {
+ // Add desktop event listeners
+ window.addEventListener("mousemove", windowMouseMove);
+ window.addEventListener("mouseleave", windowMouseLeave);
+
+ // Add mobile event listeners
+ window.addEventListener("touchstart", windowTouchStart, {
+ passive: false,
+ });
+ window.addEventListener("touchmove", windowTouchMove, { passive: false });
+ window.addEventListener("touchend", windowTouchEnd);
+ } else {
+ // Remove desktop event listeners
+ window.removeEventListener("mousemove", windowMouseMove);
+ window.removeEventListener("mouseleave", windowMouseLeave);
+
+ // Remove mobile event listeners
+ window.removeEventListener("touchstart", windowTouchStart);
+ window.removeEventListener("touchmove", windowTouchMove);
+ window.removeEventListener("touchend", windowTouchEnd);
+ }
+
+ // Cleanup when component unmounts
+ return () => {
+ window.removeEventListener("mousemove", windowMouseMove);
+ window.removeEventListener("mouseleave", windowMouseLeave);
+ window.removeEventListener("touchstart", windowTouchStart);
+ window.removeEventListener("touchmove", windowTouchMove);
+ window.removeEventListener("touchend", windowTouchEnd);
+ };
+ }, [isFlashlightEnabled]);
+
+ return (
+
+
+
+
+
setIsFlashlightEnabled(!isFlashlightEnabled)}
+ >
+ {isFlashlightEnabled ? : }
+
+
welcome !
+
+
+ {children}
+
+
+ {isFlashlightEnabled && (
+ <>
+
+
+

+
+
+ >
+ )}
+
+ );
+}
diff --git a/src/components/RandomLyric.tsx b/src/components/RandomLyric.tsx
new file mode 100644
index 0000000..dc2bb0e
--- /dev/null
+++ b/src/components/RandomLyric.tsx
@@ -0,0 +1,113 @@
+import { useEffect, useRef, useState, FC } from "react";
+
+interface AutoScrollingTextProps {
+ speed?: number;
+}
+const lyrics: LyricItem[] = [
+ {
+ string: "and we're laying on the roof of my car",
+ url: "https://open.spotify.com/track/4Pt2R2EJ3rZBRn2JpH7WnJ?si=7a167c7b22354d24",
+ },
+ {
+ string: "your funeral was beautiful",
+ url: "https://open.spotify.com/track/4ZJ4vzLQekI0WntDbanNC7?si=914245f4e48a4c00",
+ },
+ {
+ string: "she was always the one",
+ url: "https://open.spotify.com/track/21n769QrJtrmN0pXuBkYOm?si=b4a5e7f627b54fd9",
+ },
+ {
+ string: "how lucky are we?",
+ url: "https://open.spotify.com/track/5iJKGpnFfvbjZJeAtwXfCj?si=a0b160c9cead4863",
+ },
+ {
+ string: "i'm yours to take darlin'",
+ url: "https://open.spotify.com/track/2nATTe1i7aMzIZ8klEpgQ7?si=1bcd8e6aa9324b81",
+ },
+ {
+ string: "wet, hot, american nights",
+ url: "https://open.spotify.com/track/3c9EsIo34kil8Oj1reaozB?si=8c0aaf0fdb7a4e5f",
+ },
+ {
+ string: "it ain't the whiskey that gets me",
+ url: "https://open.spotify.com/track/0AsrOS8HktHe9TaB1i9W3v?si=c186a29f4e804d05",
+ },
+];
+
+type LyricItem = {
+ string: string;
+ url: string;
+};
+
+const RandomLyric: FC = ({ speed = 0.5 }) => {
+ const scrollContainerRef = useRef(null);
+ const contentRef = useRef(null);
+ const [lyric, setLyric] = useState();
+
+ const getRandomLyric = () => {
+ const randomNumber = Math.floor(Math.random() * lyrics.length);
+ console.log(randomNumber);
+ setLyric(lyrics[randomNumber]);
+ };
+ useEffect(() => {
+ getRandomLyric();
+ }, []);
+
+ useEffect(() => {
+ const scrollContainer = scrollContainerRef.current;
+ const contentElement = contentRef.current;
+
+ if (!scrollContainer || !contentElement) return;
+
+ // Create a clone of the content element
+ const clone = contentElement.cloneNode(true) as HTMLDivElement;
+ scrollContainer.appendChild(clone);
+
+ let animationId: number;
+ let scrollPosition = 0;
+
+ const animate = () => {
+ if (!scrollContainer || !contentElement) return;
+
+ // Increment scroll position based on speed
+ scrollPosition += speed;
+
+ // Reset position when first element is scrolled out of view
+ if (scrollPosition >= contentElement.offsetWidth) {
+ scrollPosition = 0;
+ }
+
+ // Apply scroll position
+ scrollContainer.scrollLeft = scrollPosition;
+
+ animationId = requestAnimationFrame(animate);
+ };
+
+ animationId = requestAnimationFrame(animate);
+
+ return () => {
+ cancelAnimationFrame(animationId);
+ };
+ }, [speed, lyric]);
+
+ if (lyric) {
+ return (
+
+
+
+
+
+ {lyric?.string}
+
+
+
+
+ );
+ }
+};
+
+export default RandomLyric;
diff --git a/src/main.tsx b/src/main.tsx
index d2eccc8..1ffc8ff 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -2,6 +2,4 @@ import { createRoot } from "react-dom/client";
import "./assets/main.css";
import App from "./App.tsx";
-createRoot(document.getElementById("root")!).render(
- ,
-);
+createRoot(document.getElementById("root")!).render();