This commit is contained in:
parent
5e84e553a1
commit
16f3284110
@ -1,5 +1,3 @@
|
|||||||
"use client";
|
export default function HomePage() {
|
||||||
|
|
||||||
export default function Home() {
|
|
||||||
return <main>hi</main>;
|
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
|
* Populates the default settings
|
||||||
*/
|
*/
|
||||||
async populateDefaults() {
|
async populateDefaults() {
|
||||||
await this.settings.add({
|
await this.resetSettings();
|
||||||
id: SETTINGS_ID, // Fixed ID for the single settings object
|
|
||||||
backgroundImage: "/assets/background.jpg",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -70,6 +67,19 @@ export default class Database extends Dexie {
|
|||||||
async setSettings(settings: Settings) {
|
async setSettings(settings: Settings) {
|
||||||
return this.settings.update(SETTINGS_ID, 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();
|
export const db = new Database();
|
||||||
|
@ -16,9 +16,9 @@ export default class Settings extends Entity<Database> {
|
|||||||
playerId?: string;
|
playerId?: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The background image to use
|
* The background image or color to use
|
||||||
*/
|
*/
|
||||||
backgroundImage?: string;
|
backgroundCover?: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the players id
|
* Sets the players id
|
||||||
@ -36,7 +36,7 @@ export default class Settings extends Entity<Database> {
|
|||||||
* @param image the new background image
|
* @param image the new background image
|
||||||
*/
|
*/
|
||||||
public setBackgroundImage(image: string) {
|
public setBackgroundImage(image: string) {
|
||||||
this.backgroundImage = image;
|
this.backgroundCover = image;
|
||||||
this.db.setSettings(this);
|
this.db.setSettings(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,28 +9,28 @@ export default function BackgroundCover() {
|
|||||||
const database = useDatabase();
|
const database = useDatabase();
|
||||||
const settings = useLiveQuery(() => database.getSettings());
|
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
|
return null; // Don't render anything if the background image is not set
|
||||||
}
|
}
|
||||||
|
|
||||||
let backgroundImage = settings.backgroundImage;
|
let backgroundCover = settings.backgroundCover;
|
||||||
let prependWebsiteUrl = false;
|
let prependWebsiteUrl = false;
|
||||||
|
|
||||||
// Remove the prepending slash
|
// Remove the prepending slash
|
||||||
if (backgroundImage.startsWith("/")) {
|
if (backgroundCover.startsWith("/")) {
|
||||||
prependWebsiteUrl = true;
|
prependWebsiteUrl = true;
|
||||||
backgroundImage = backgroundImage.substring(1);
|
backgroundCover = backgroundCover.substring(1);
|
||||||
}
|
}
|
||||||
if (prependWebsiteUrl) {
|
if (prependWebsiteUrl) {
|
||||||
backgroundImage = config.siteUrl + "/" + backgroundImage;
|
backgroundCover = config.siteUrl + "/" + backgroundCover;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Static background color
|
// Static background color
|
||||||
if (backgroundImage.startsWith("#")) {
|
if (backgroundCover.startsWith("#")) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`fixed -z-50 object-cover w-screen h-screen pointer-events-none select-none`}
|
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 (
|
return (
|
||||||
// eslint-disable-next-line @next/next/no-img-element
|
// eslint-disable-next-line @next/next/no-img-element
|
||||||
<img
|
<img
|
||||||
src={getImageUrl(backgroundImage)}
|
src={getImageUrl(backgroundCover)}
|
||||||
alt="Background image"
|
alt="Background image"
|
||||||
fetchPriority="high"
|
fetchPriority="high"
|
||||||
className={`fixed -z-50 object-cover w-screen h-screen blur-sm brightness-[33%] pointer-events-none select-none`}
|
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 { MagnifyingGlassIcon } from "@radix-ui/react-icons";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
@ -8,6 +8,7 @@ import ProfileButton from "./profile-button";
|
|||||||
type NavbarItem = {
|
type NavbarItem = {
|
||||||
name: string;
|
name: string;
|
||||||
link: string;
|
link: string;
|
||||||
|
align: "left" | "right";
|
||||||
icon: React.ReactNode;
|
icon: React.ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -15,12 +16,20 @@ const items: NavbarItem[] = [
|
|||||||
{
|
{
|
||||||
name: "Home",
|
name: "Home",
|
||||||
link: "/",
|
link: "/",
|
||||||
icon: <HomeIcon className="h-5 w-5" />, // Add your home icon here
|
align: "left",
|
||||||
|
icon: <HomeIcon className="h-5 w-5" />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Search",
|
name: "Search",
|
||||||
link: "/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() {
|
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 (
|
return (
|
||||||
<div className="w-full sticky top-0 z-[999]">
|
<div className="w-full sticky top-0 z-[999]">
|
||||||
@ -42,17 +52,21 @@ export default function Navbar() {
|
|||||||
<div className="flex items-center h-full">
|
<div className="flex items-center h-full">
|
||||||
<ProfileButton />
|
<ProfileButton />
|
||||||
|
|
||||||
{items.slice(0, -1).map((item, index) => (
|
{leftItems.map((item, index) => (
|
||||||
<Link href={item.link} key={index} className="h-full">
|
<Link href={item.link} key={index} className="h-full">
|
||||||
<NavbarButton key={index}>{renderNavbarItem(item)}</NavbarButton>
|
<NavbarButton key={index}>{renderNavbarItem(item)}</NavbarButton>
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right-aligned item */}
|
{/* Right-aligned items */}
|
||||||
<Link href={rightItem.link} className="h-full">
|
<div className="flex items-center h-full">
|
||||||
<NavbarButton>{renderNavbarItem(rightItem)}</NavbarButton>
|
{rightItems.map((item, index) => (
|
||||||
|
<Link href={item.link} key={index} className="h-full">
|
||||||
|
<NavbarButton key={index}>{renderNavbarItem(item)}</NavbarButton>
|
||||||
</Link>
|
</Link>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</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: {
|
variant: {
|
||||||
default: "border bg-secondary text-foreground",
|
default: "border bg-secondary text-foreground",
|
||||||
destructive: "destructive group border-destructive bg-destructive text-destructive-foreground",
|
destructive: "destructive group border-destructive bg-destructive text-destructive-foreground",
|
||||||
|
success: "border bg-green-600 text-foreground",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
defaultVariants: {
|
defaultVariants: {
|
||||||
|
Reference in New Issue
Block a user