diff --git a/app/layout.tsx b/app/layout.tsx index 2cc502f..a8e8931 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -2,6 +2,7 @@ import type { Metadata } from "next"; import Sidebar from "../components/Sidebar"; import styles from "../styles/Layout.module.css"; import "./globals.css"; +import AppContainer from "@/components/Container"; export const metadata: Metadata = { title: "Hex: Unlocked", @@ -16,12 +17,14 @@ export default function RootLayout({ return ( -
- -
- {children} -
-
+ +
+ +
+ {children} +
+
+
); diff --git a/components/Container.tsx b/components/Container.tsx new file mode 100644 index 0000000..d8f9c37 --- /dev/null +++ b/components/Container.tsx @@ -0,0 +1,44 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { useWebsocketStore } from "../store/useWebsocketStore"; +import styles from "../styles/AppContainer.module.css"; + +export default function AppContainer({ children }: { children: React.ReactNode }) { + const { connect, isConnected } = useWebsocketStore(); + const [showHelp, setShowHelp] = useState(false); + + useEffect(() => { + connect(); + + const timer = setTimeout(() => { + setShowHelp(true); + }, 3000); + + return () => clearTimeout(timer); + }, [connect]); + + if (!isConnected) { + return ( +
+
+

Hex: Unlocked

+

Awaiting unlocker connection

+ +
+ + {showHelp && ( +
+

Make sure the client is running.

+

+ Don't have it? Download it here +

+
+ )} +
+
+ ); + } + + return <>{children}; +} \ No newline at end of file diff --git a/styles/AppContainer.module.css b/styles/AppContainer.module.css new file mode 100644 index 0000000..3e2f370 --- /dev/null +++ b/styles/AppContainer.module.css @@ -0,0 +1,83 @@ +.overlay { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100vh; + width: 100vw; + background: #000000; + color: #ffffff; + font-family: 'Roboto Condensed', sans-serif; + text-align: center; +} + +.card { + background: #050505; + border: 1px solid #1a1a1a; + border-top: 3px solid #a30000; + padding: 3rem 4rem; + display: flex; + flex-direction: column; + align-items: center; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.6); + min-width: 400px; +} + +.title { + font-size: 2.25rem; + text-transform: uppercase; + color: #ffffff; + letter-spacing: 0.05em; + margin: 0 0 0.5rem 0; + text-shadow: 2px 2px 5px rgba(0, 0, 0, 0.8); + font-family: 'Oswald', sans-serif; +} + +.subtitle { + font-size: 0.9rem; + color: #777777; + text-transform: uppercase; + letter-spacing: 0.1em; + margin: 0 0 2rem 0; +} + +.spinner { + width: 36px; + height: 36px; + border: 3px solid #1a1a1a; + border-top: 3px solid #a30000; + border-radius: 50%; + animation: spin 1s linear infinite; + margin-bottom: 2rem; +} + +.helpText { + color: #555555; + font-size: 0.85rem; + margin-top: 1rem; + letter-spacing: 0.05em; + text-transform: uppercase; + animation: fadeIn 1s ease-in; +} + +.link { + color: #a30000; + text-decoration: none; + font-weight: bold; + transition: all 0.2s ease; +} + +.link:hover { + color: #ff0000; + text-shadow: 0 0 10px rgba(163, 0, 0, 0.4); +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } +}