From 2084907fbe8cef69d51be49d8c88ba59a7e108fd Mon Sep 17 00:00:00 2001 From: sillyangel Date: Wed, 25 Jun 2025 18:45:50 -0500 Subject: [PATCH] feat: enhance theme management and add login form component for user authentication --- app/components/ThemeProvider.tsx | 2 +- app/components/start-screen.tsx | 279 +++++++++++++++++++++++++++++++ app/page.tsx | 32 ++-- 3 files changed, 298 insertions(+), 15 deletions(-) create mode 100644 app/components/start-screen.tsx diff --git a/app/components/ThemeProvider.tsx b/app/components/ThemeProvider.tsx index 61b70fd..77f685c 100644 --- a/app/components/ThemeProvider.tsx +++ b/app/components/ThemeProvider.tsx @@ -46,7 +46,7 @@ export const ThemeProvider: React.FC = ({ children }) => { const root = document.documentElement; // Remove existing theme classes - root.classList.remove('theme-blue', 'theme-violet', 'dark'); + root.classList.remove('theme-blue', 'theme-violet', 'theme-red', 'theme-rose', 'theme-orange', 'theme-green', 'theme-yellow', 'dark'); // Add new theme class root.classList.add(`theme-${theme}`); diff --git a/app/components/start-screen.tsx b/app/components/start-screen.tsx new file mode 100644 index 0000000..ad76e8e --- /dev/null +++ b/app/components/start-screen.tsx @@ -0,0 +1,279 @@ +'use client'; + +import React, { useState } from 'react'; +import { cn } from "@/lib/utils" +import { Button } from "@/components/ui/button" +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card" +import { Input } from "@/components/ui/input" +import { Label } from "@/components/ui/label" +import { Select, SelectTrigger, SelectContent, SelectItem, SelectValue } from '@/components/ui/select'; +import { useNavidromeConfig } from '@/app/components/NavidromeConfigContext'; +import { useTheme } from '@/app/components/ThemeProvider'; +import { useToast } from '@/hooks/use-toast'; +import { FaServer, FaUser, FaLock, FaCheck, FaTimes, FaPalette, FaLastfm } from 'react-icons/fa'; + +export function LoginForm({ + className, + ...props +}: React.ComponentProps<"div">) { + const [step, setStep] = useState<'login' | 'settings'>('login'); + const { config, updateConfig, testConnection } = useNavidromeConfig(); + const { theme, setTheme } = useTheme(); + const { toast } = useToast(); + + const [formData, setFormData] = useState({ + serverUrl: config.serverUrl || '', + username: config.username || '', + password: config.password || '' + }); + + const [isTesting, setIsTesting] = useState(false); + + // Settings for step 2 + const [scrobblingEnabled, setScrobblingEnabled] = useState(() => { + if (typeof window !== 'undefined') { + return localStorage.getItem('lastfm-scrobbling-enabled') === 'true'; + } + return true; + }); + + const handleInputChange = (field: string, value: string) => { + setFormData(prev => ({ ...prev, [field]: value })); + }; + + const handleTestAndNext = async (e: React.FormEvent) => { + e.preventDefault(); + + if (!formData.serverUrl || !formData.username || !formData.password) { + toast({ + title: "Missing Information", + description: "Please fill in all fields before proceeding.", + variant: "destructive" + }); + return; + } + + setIsTesting(true); + try { + // Strip trailing slash from server URL before testing + const cleanServerUrl = formData.serverUrl.replace(/\/+$/, ''); + + const success = await testConnection({ + serverUrl: cleanServerUrl, + username: formData.username, + password: formData.password + }); + + if (success) { + // Save the config + updateConfig({ + serverUrl: cleanServerUrl, + username: formData.username, + password: formData.password + }); + + toast({ + title: "Connection Successful", + description: "Connected to Navidrome! Let's configure your preferences.", + }); + + // Move to settings step + setStep('settings'); + } else { + toast({ + title: "Connection Failed", + description: "Could not connect to the server. Please check your settings.", + variant: "destructive" + }); + } + } catch (error) { + toast({ + title: "Connection Error", + description: "An error occurred while testing the connection.", + variant: "destructive" + }); + } finally { + setIsTesting(false); + } + }; + + const handleFinishSetup = () => { + // Save scrobbling preference + localStorage.setItem('lastfm-scrobbling-enabled', scrobblingEnabled.toString()); + + toast({ + title: "Setup Complete", + description: "Welcome to mice! Your music streaming experience is ready.", + }); + + // Reload the page to start the main app + window.location.reload(); + }; + + const handleScrobblingToggle = (enabled: boolean) => { + setScrobblingEnabled(enabled); + }; + + if (step === 'settings') { + return ( +
+ + + + + Customize Your Experience + + + Configure your preferences to get started + + + +
+ {/* Theme Selection */} +
+ + +
+ + {/* Last.fm Scrobbling */} +
+ + +

+ {scrobblingEnabled + ? "Tracks will be scrobbled to Last.fm via Navidrome" + : "Last.fm scrobbling will be disabled"} +

+
+ +
+ + +
+
+
+
+
+ ); + } + + return ( +
+ + + + + Connect to Navidrome + + + Enter your Navidrome server details to get started + + + +
+
+
+ + handleInputChange('serverUrl', e.target.value)} + required + /> +
+ +
+ + handleInputChange('username', e.target.value)} + required + /> +
+ +
+ + handleInputChange('password', e.target.value)} + required + /> +
+ +
+ +
+
+
+
+
+
+ ) +} diff --git a/app/page.tsx b/app/page.tsx index 8766ca8..726a043 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -7,6 +7,7 @@ import { AlbumArtwork } from './components/album-artwork'; import { useNavidrome } from './components/NavidromeContext'; import { useEffect, useState } from 'react'; import { Album } from '@/lib/navidrome'; +import { LoginForm } from '@/app/components/start-screen'; export default function MusicPage() { const { albums, isLoading, error } = useNavidrome(); @@ -25,20 +26,23 @@ export default function MusicPage() { if (error) { return ( -
-
-

Connection Error

-

{error}

-

- If you need to change your settings, please go to the{' '} - - Settings - -

-
+ //
+ //
+ //

Connection Error

+ //

{error}

+ //

+ // If you need to change your settings, please go to the{' '} + // + // Settings + // + //

+ //
+ //
+
+
); }