mirror of
https://github.com/captbaritone/webamp.git
synced 2026-01-23 02:15:01 +00:00
New pages for scroll
This commit is contained in:
parent
8d4ff41f42
commit
1fb930cd63
4 changed files with 371 additions and 6 deletions
|
|
@ -57,7 +57,7 @@ export default function BottomMenuBar() {
|
|||
{isHamburgerOpen && (
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
position: "fixed",
|
||||
bottom: "4.5rem",
|
||||
left: "50%",
|
||||
transform: "translateX(-50%)",
|
||||
|
|
@ -65,13 +65,15 @@ export default function BottomMenuBar() {
|
|||
maxWidth: MOBILE_MAX_WIDTH,
|
||||
backgroundColor: "rgba(26, 26, 26, 0.98)",
|
||||
backdropFilter: "blur(10px)",
|
||||
borderTop: "1px solid rgba(255, 255, 255, 0.1)",
|
||||
border: "1px solid rgba(255, 255, 255, 0.2)",
|
||||
borderBottom: "none",
|
||||
boxShadow: "0 -4px 12px rgba(0, 0, 0, 0.3)",
|
||||
zIndex: 999,
|
||||
}}
|
||||
>
|
||||
<div ref={menuRef}>
|
||||
<HamburgerMenuItem
|
||||
href="/about"
|
||||
href="/scroll/about"
|
||||
icon={<Info size={20} />}
|
||||
label="About"
|
||||
onClick={() => {
|
||||
|
|
@ -87,13 +89,12 @@ export default function BottomMenuBar() {
|
|||
}}
|
||||
/>{" "}
|
||||
<HamburgerMenuItem
|
||||
href="https://github.com/captbaritone/webamp/issues"
|
||||
href="/scroll/feedback"
|
||||
icon={<MessageSquare size={20} />}
|
||||
label="Feedback"
|
||||
onClick={() => {
|
||||
setIsHamburgerOpen(false);
|
||||
}}
|
||||
external
|
||||
/>
|
||||
<HamburgerMenuItem
|
||||
href="https://github.com/captbaritone/webamp/"
|
||||
|
|
@ -111,7 +112,7 @@ export default function BottomMenuBar() {
|
|||
{/* Bottom Menu Bar */}
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
position: "sticky",
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
width: "100%",
|
||||
|
|
|
|||
180
packages/skin-database/app/(modern)/scroll/StaticPage.tsx
Normal file
180
packages/skin-database/app/(modern)/scroll/StaticPage.tsx
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
import { ReactNode, CSSProperties } from "react";
|
||||
import { MOBILE_MAX_WIDTH } from "../../../legacy-client/src/constants";
|
||||
|
||||
type StaticPageProps = {
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
export default function StaticPage({ children }: StaticPageProps) {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
minHeight: "100vh",
|
||||
backgroundColor: "#1a1a1a",
|
||||
paddingBottom: "5rem", // Space for bottom menu bar
|
||||
boxSizing: "border-box",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
maxWidth: MOBILE_MAX_WIDTH,
|
||||
margin: "0 auto",
|
||||
padding: "2rem 1.5rem",
|
||||
color: "#fff",
|
||||
lineHeight: "1.6",
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Styled heading components
|
||||
export function Heading({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<h1
|
||||
style={{
|
||||
fontSize: "2rem",
|
||||
marginBottom: "1.5rem",
|
||||
fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</h1>
|
||||
);
|
||||
}
|
||||
|
||||
export function Subheading({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<h2
|
||||
style={{
|
||||
fontSize: "1.5rem",
|
||||
marginTop: "2rem",
|
||||
marginBottom: "1rem",
|
||||
fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</h2>
|
||||
);
|
||||
}
|
||||
|
||||
// Styled link component
|
||||
export function Link({
|
||||
href,
|
||||
children,
|
||||
...props
|
||||
}: {
|
||||
href: string;
|
||||
children: ReactNode;
|
||||
target?: string;
|
||||
rel?: string;
|
||||
}) {
|
||||
return (
|
||||
<a
|
||||
href={href}
|
||||
style={{
|
||||
color: "#6b9eff",
|
||||
textDecoration: "underline",
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
// Styled paragraph component
|
||||
export function Paragraph({ children }: { children: ReactNode }) {
|
||||
return <p style={{ marginBottom: "1rem" }}>{children}</p>;
|
||||
}
|
||||
|
||||
// Styled form components
|
||||
export function Label({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<label
|
||||
style={{
|
||||
display: "block",
|
||||
marginBottom: "0.5rem",
|
||||
fontWeight: 500,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</label>
|
||||
);
|
||||
}
|
||||
|
||||
export function Input({
|
||||
style,
|
||||
...props
|
||||
}: React.InputHTMLAttributes<HTMLInputElement> & { style?: CSSProperties }) {
|
||||
return (
|
||||
<input
|
||||
style={{
|
||||
width: "100%",
|
||||
padding: "0.75rem",
|
||||
fontSize: "1rem",
|
||||
backgroundColor: "#2a2a2a",
|
||||
border: "1px solid rgba(255, 255, 255, 0.2)",
|
||||
borderRadius: "4px",
|
||||
color: "#fff",
|
||||
fontFamily: "inherit",
|
||||
...style,
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function Textarea({
|
||||
style,
|
||||
...props
|
||||
}: React.TextareaHTMLAttributes<HTMLTextAreaElement> & {
|
||||
style?: CSSProperties;
|
||||
}) {
|
||||
return (
|
||||
<textarea
|
||||
style={{
|
||||
width: "100%",
|
||||
padding: "0.75rem",
|
||||
fontSize: "1rem",
|
||||
backgroundColor: "#2a2a2a",
|
||||
border: "1px solid rgba(255, 255, 255, 0.2)",
|
||||
borderRadius: "4px",
|
||||
color: "#fff",
|
||||
fontFamily: "inherit",
|
||||
display: "block",
|
||||
resize: "vertical",
|
||||
...style,
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function Button({
|
||||
style,
|
||||
disabled,
|
||||
...props
|
||||
}: React.ButtonHTMLAttributes<HTMLButtonElement> & { style?: CSSProperties }) {
|
||||
return (
|
||||
<button
|
||||
style={{
|
||||
padding: "0.75rem 2rem",
|
||||
fontSize: "1rem",
|
||||
fontWeight: 500,
|
||||
backgroundColor: "#6b9eff",
|
||||
color: "#fff",
|
||||
border: "none",
|
||||
borderRadius: "4px",
|
||||
cursor: disabled ? "not-allowed" : "pointer",
|
||||
opacity: disabled ? 0.6 : 1,
|
||||
transition: "opacity 0.2s",
|
||||
...style,
|
||||
}}
|
||||
disabled={disabled}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
67
packages/skin-database/app/(modern)/scroll/about/page.tsx
Normal file
67
packages/skin-database/app/(modern)/scroll/about/page.tsx
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
import StaticPage, {
|
||||
Heading,
|
||||
Subheading,
|
||||
Link,
|
||||
Paragraph,
|
||||
} from "../StaticPage";
|
||||
|
||||
export default function AboutPage() {
|
||||
return (
|
||||
<StaticPage>
|
||||
<Heading>About</Heading>
|
||||
<Paragraph>
|
||||
The Winamp Skin Museum is an attempt to build a <i>fast</i>,{" "}
|
||||
<i>searchable</i>, and <i>shareable</i>, interface for the collection of
|
||||
Winamp Skins amassed on the{" "}
|
||||
<Link
|
||||
href="https://archive.org/details/winampskins"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Internet Archive
|
||||
</Link>
|
||||
.
|
||||
</Paragraph>
|
||||
<Subheading>Features:</Subheading>
|
||||
<ul style={{ marginBottom: "1.5rem", paddingLeft: "1.5rem" }}>
|
||||
<li style={{ marginBottom: "0.5rem" }}>
|
||||
<strong>Infinite scroll</strong> preview images
|
||||
</li>
|
||||
<li style={{ marginBottom: "0.5rem" }}>
|
||||
<strong>Experience</strong> skins with integrated{" "}
|
||||
<Link
|
||||
href="https://webamp.org"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Webamp
|
||||
</Link>
|
||||
</li>
|
||||
<li style={{ marginBottom: "0.5rem" }}>
|
||||
<strong>Fast search</strong> of indexed readme.txt texts
|
||||
</li>
|
||||
</ul>
|
||||
<Paragraph>
|
||||
Made by <Link href="https://jordaneldredge.com">Jordan Eldredge</Link>
|
||||
</Paragraph>
|
||||
<hr
|
||||
style={{
|
||||
border: "none",
|
||||
borderTop: "1px solid rgba(255, 255, 255, 0.2)",
|
||||
margin: "2rem 0",
|
||||
}}
|
||||
/>
|
||||
<Paragraph>
|
||||
Want Winamp on your Windows PC, but with supported updates & new
|
||||
features?{" "}
|
||||
<Link
|
||||
href="https://getwacup.com/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Try WACUP
|
||||
</Link>
|
||||
</Paragraph>
|
||||
</StaticPage>
|
||||
);
|
||||
}
|
||||
117
packages/skin-database/app/(modern)/scroll/feedback/page.tsx
Normal file
117
packages/skin-database/app/(modern)/scroll/feedback/page.tsx
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
"use client";
|
||||
|
||||
import { useState, useCallback } from "react";
|
||||
import { usePathname } from "next/navigation";
|
||||
import StaticPage, {
|
||||
Heading,
|
||||
Paragraph,
|
||||
Label,
|
||||
Input,
|
||||
Textarea,
|
||||
Button,
|
||||
} from "../StaticPage";
|
||||
|
||||
async function sendFeedback(variables: {
|
||||
message: string;
|
||||
email: string;
|
||||
url: string;
|
||||
}) {
|
||||
const mutation = `
|
||||
mutation GiveFeedback($message: String!, $email: String, $url: String) {
|
||||
send_feedback(message: $message, email: $email, url: $url)
|
||||
}
|
||||
`;
|
||||
|
||||
const response = await fetch("/api/graphql", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
query: mutation,
|
||||
variables,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to send feedback");
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
export default function FeedbackPage() {
|
||||
const pathname = usePathname();
|
||||
const [message, setMessage] = useState("");
|
||||
const [email, setEmail] = useState("");
|
||||
const [sending, setSending] = useState(false);
|
||||
const [sent, setSent] = useState(false);
|
||||
|
||||
const send = useCallback(async () => {
|
||||
if (message.trim().length === 0) {
|
||||
alert("Please add a message before sending.");
|
||||
return;
|
||||
}
|
||||
const body = {
|
||||
message,
|
||||
email,
|
||||
url: "https://skins.webamp.org" + pathname,
|
||||
};
|
||||
setSending(true);
|
||||
try {
|
||||
await sendFeedback(body);
|
||||
setSent(true);
|
||||
} catch (error) {
|
||||
alert("Failed to send feedback. Please try again.");
|
||||
setSending(false);
|
||||
}
|
||||
}, [message, email, pathname]);
|
||||
|
||||
if (sent) {
|
||||
return (
|
||||
<StaticPage>
|
||||
<Heading>Sent!</Heading>
|
||||
<Paragraph>
|
||||
Thanks for your feedback. I appreciate you taking the time to share
|
||||
your thoughts.
|
||||
</Paragraph>
|
||||
</StaticPage>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<StaticPage>
|
||||
<Heading>Feedback</Heading>
|
||||
<p style={{ marginBottom: "1.5rem" }}>
|
||||
Let me know what you think about the Winamp Skin Museum. Bug reports,
|
||||
feature suggestions, personal anecdotes, or criticism are all welcome.
|
||||
</p>
|
||||
<div style={{ marginBottom: "1.5rem" }}>
|
||||
<Label>Message</Label>
|
||||
<Textarea
|
||||
disabled={sending}
|
||||
value={message}
|
||||
onChange={(e) => setMessage(e.target.value)}
|
||||
style={{ minHeight: 150 }}
|
||||
placeholder="Your thoughts here..."
|
||||
/>
|
||||
</div>
|
||||
<div style={{ marginBottom: "1.5rem" }}>
|
||||
<Label>Email (optional)</Label>
|
||||
<Input
|
||||
disabled={sending}
|
||||
type="text"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
placeholder="user@example.com"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div style={{ textAlign: "right" }}>
|
||||
<Button onClick={send} disabled={sending}>
|
||||
{sending ? "Sending..." : "Send"}
|
||||
</Button>
|
||||
</div>
|
||||
</StaticPage>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue