This commit is contained in:
parent
5e84e553a1
commit
16f3284110
@ -1,5 +1,3 @@
|
||||
"use client";
|
||||
|
||||
export default function Home() {
|
||||
export default function HomePage() {
|
||||
return <main>hi</main>;
|
||||
}
|
||||
|
16
src/app/(pages)/settings/page.tsx
Normal file
16
src/app/(pages)/settings/page.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
import Card from "@/components/card";
|
||||
import Settings from "@/components/settings/settings";
|
||||
|
||||
export default function SettingsPage() {
|
||||
return (
|
||||
<main className="w-full">
|
||||
<Card className="w-full gap-4">
|
||||
<div>
|
||||
<p className="font-semibold">Settings</p>
|
||||
<p>Configure settings your ScoreSaber Reloaded settings!</p>
|
||||
</div>
|
||||
<Settings />
|
||||
</Card>
|
||||
</main>
|
||||
);
|
||||
}
|
@ -46,10 +46,7 @@ export default class Database extends Dexie {
|
||||
* Populates the default settings
|
||||
*/
|
||||
async populateDefaults() {
|
||||
await this.settings.add({
|
||||
id: SETTINGS_ID, // Fixed ID for the single settings object
|
||||
backgroundImage: "/assets/background.jpg",
|
||||
});
|
||||
await this.resetSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -70,6 +67,19 @@ export default class Database extends Dexie {
|
||||
async setSettings(settings: Settings) {
|
||||
return this.settings.update(SETTINGS_ID, settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the settings in the database
|
||||
*/
|
||||
async resetSettings() {
|
||||
this.settings.delete(SETTINGS_ID);
|
||||
this.settings.add({
|
||||
id: SETTINGS_ID, // Fixed ID for the single settings object
|
||||
backgroundCover: "/assets/background.jpg",
|
||||
});
|
||||
|
||||
return this.getSettings();
|
||||
}
|
||||
}
|
||||
|
||||
export const db = new Database();
|
||||
|
@ -16,9 +16,9 @@ export default class Settings extends Entity<Database> {
|
||||
playerId?: string;
|
||||
|
||||
/**
|
||||
* The background image to use
|
||||
* The background image or color to use
|
||||
*/
|
||||
backgroundImage?: string;
|
||||
backgroundCover?: string;
|
||||
|
||||
/**
|
||||
* Sets the players id
|
||||
@ -36,7 +36,7 @@ export default class Settings extends Entity<Database> {
|
||||
* @param image the new background image
|
||||
*/
|
||||
public setBackgroundImage(image: string) {
|
||||
this.backgroundImage = image;
|
||||
this.backgroundCover = image;
|
||||
this.db.setSettings(this);
|
||||
}
|
||||
}
|
||||
|
@ -9,28 +9,28 @@ export default function BackgroundCover() {
|
||||
const database = useDatabase();
|
||||
const settings = useLiveQuery(() => database.getSettings());
|
||||
|
||||
if (settings == undefined || settings?.backgroundImage == undefined || settings?.backgroundImage == "") {
|
||||
if (settings == undefined || settings?.backgroundCover == undefined || settings?.backgroundCover == "") {
|
||||
return null; // Don't render anything if the background image is not set
|
||||
}
|
||||
|
||||
let backgroundImage = settings.backgroundImage;
|
||||
let backgroundCover = settings.backgroundCover;
|
||||
let prependWebsiteUrl = false;
|
||||
|
||||
// Remove the prepending slash
|
||||
if (backgroundImage.startsWith("/")) {
|
||||
if (backgroundCover.startsWith("/")) {
|
||||
prependWebsiteUrl = true;
|
||||
backgroundImage = backgroundImage.substring(1);
|
||||
backgroundCover = backgroundCover.substring(1);
|
||||
}
|
||||
if (prependWebsiteUrl) {
|
||||
backgroundImage = config.siteUrl + "/" + backgroundImage;
|
||||
backgroundCover = config.siteUrl + "/" + backgroundCover;
|
||||
}
|
||||
|
||||
// Static background color
|
||||
if (backgroundImage.startsWith("#")) {
|
||||
if (backgroundCover.startsWith("#")) {
|
||||
return (
|
||||
<div
|
||||
className={`fixed -z-50 object-cover w-screen h-screen pointer-events-none select-none`}
|
||||
style={{ backgroundColor: backgroundImage }}
|
||||
style={{ backgroundColor: backgroundCover }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -38,7 +38,7 @@ export default function BackgroundCover() {
|
||||
return (
|
||||
// eslint-disable-next-line @next/next/no-img-element
|
||||
<img
|
||||
src={getImageUrl(backgroundImage)}
|
||||
src={getImageUrl(backgroundCover)}
|
||||
alt="Background image"
|
||||
fetchPriority="high"
|
||||
className={`fixed -z-50 object-cover w-screen h-screen blur-sm brightness-[33%] pointer-events-none select-none`}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { HomeIcon } from "@heroicons/react/24/solid";
|
||||
import { CogIcon, HomeIcon } from "@heroicons/react/24/solid";
|
||||
import { MagnifyingGlassIcon } from "@radix-ui/react-icons";
|
||||
import Link from "next/link";
|
||||
import React from "react";
|
||||
@ -8,6 +8,7 @@ import ProfileButton from "./profile-button";
|
||||
type NavbarItem = {
|
||||
name: string;
|
||||
link: string;
|
||||
align: "left" | "right";
|
||||
icon: React.ReactNode;
|
||||
};
|
||||
|
||||
@ -15,12 +16,20 @@ const items: NavbarItem[] = [
|
||||
{
|
||||
name: "Home",
|
||||
link: "/",
|
||||
icon: <HomeIcon className="h-5 w-5" />, // Add your home icon here
|
||||
align: "left",
|
||||
icon: <HomeIcon className="h-5 w-5" />,
|
||||
},
|
||||
{
|
||||
name: "Search",
|
||||
link: "/search",
|
||||
icon: <MagnifyingGlassIcon className="h-5 w-5" />, // Add your search icon here
|
||||
align: "right",
|
||||
icon: <MagnifyingGlassIcon className="h-5 w-5" />,
|
||||
},
|
||||
{
|
||||
name: "Settings",
|
||||
link: "/settings",
|
||||
align: "right",
|
||||
icon: <CogIcon className="h-5 w-5" />,
|
||||
},
|
||||
];
|
||||
|
||||
@ -33,7 +42,8 @@ const renderNavbarItem = (item: NavbarItem) => (
|
||||
);
|
||||
|
||||
export default function Navbar() {
|
||||
const rightItem = items[items.length - 1];
|
||||
const rightItems = items.filter(item => item.align === "right");
|
||||
const leftItems = items.filter(item => item.align === "left");
|
||||
|
||||
return (
|
||||
<div className="w-full sticky top-0 z-[999]">
|
||||
@ -42,17 +52,21 @@ export default function Navbar() {
|
||||
<div className="flex items-center h-full">
|
||||
<ProfileButton />
|
||||
|
||||
{items.slice(0, -1).map((item, index) => (
|
||||
{leftItems.map((item, index) => (
|
||||
<Link href={item.link} key={index} className="h-full">
|
||||
<NavbarButton key={index}>{renderNavbarItem(item)}</NavbarButton>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Right-aligned item */}
|
||||
<Link href={rightItem.link} className="h-full">
|
||||
<NavbarButton>{renderNavbarItem(rightItem)}</NavbarButton>
|
||||
{/* Right-aligned items */}
|
||||
<div className="flex items-center h-full">
|
||||
{rightItems.map((item, index) => (
|
||||
<Link href={item.link} key={index} className="h-full">
|
||||
<NavbarButton key={index}>{renderNavbarItem(item)}</NavbarButton>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
84
src/components/settings/settings.tsx
Normal file
84
src/components/settings/settings.tsx
Normal file
@ -0,0 +1,84 @@
|
||||
"use client";
|
||||
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { z } from "zod";
|
||||
import { Button } from "../ui/button";
|
||||
import { Form, FormControl, FormField, FormItem, FormLabel } from "../ui/form";
|
||||
import { Input } from "../ui/input";
|
||||
import useDatabase from "@/hooks/use-database";
|
||||
import { useLiveQuery } from "dexie-react-hooks";
|
||||
import { useEffect } from "react";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
|
||||
const formSchema = z.object({
|
||||
backgroundCover: z.string().min(4).max(128),
|
||||
});
|
||||
|
||||
export default function Settings() {
|
||||
const database = useDatabase();
|
||||
const settings = useLiveQuery(() => database.getSettings());
|
||||
const { toast } = useToast();
|
||||
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
// They will get overwritten later...
|
||||
backgroundCover: "",
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Handles the form submission
|
||||
*
|
||||
* @param backgroundCover the new background cover
|
||||
*/
|
||||
async function onSubmit({ backgroundCover }: z.infer<typeof formSchema>) {
|
||||
if (!settings) {
|
||||
return;
|
||||
}
|
||||
|
||||
settings.backgroundCover = backgroundCover;
|
||||
await database.setSettings(settings);
|
||||
|
||||
toast({
|
||||
title: "Settings saved",
|
||||
description: "Your settings have been saved.",
|
||||
variant: "success",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle setting the default form values.
|
||||
*/
|
||||
useEffect(() => {
|
||||
form.setValue("backgroundCover", settings?.backgroundCover || "");
|
||||
}, [settings]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-3">
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className="flex flex-col gap-2">
|
||||
{/* Background Cover */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="backgroundCover"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Background Cover</FormLabel>
|
||||
<FormControl>
|
||||
<Input className="w-full sm:w-72 text-sm" placeholder="Hex or URL..." {...field} />
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* Saving Settings */}
|
||||
<Button type="submit" className="w-fit">
|
||||
Save
|
||||
</Button>
|
||||
</form>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -31,6 +31,7 @@ const toastVariants = cva(
|
||||
variant: {
|
||||
default: "border bg-secondary text-foreground",
|
||||
destructive: "destructive group border-destructive bg-destructive text-destructive-foreground",
|
||||
success: "border bg-green-600 text-foreground",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
|
Reference in New Issue
Block a user