'use client'; import React, { useState, useEffect, useCallback } 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 { Badge } from '@/components/ui/badge'; 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 [canSkipNavidrome, setCanSkipNavidrome] = useState(false); 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; }); // New settings - removed sidebar and standalone lastfm options // Check if Navidrome is configured via environment variables const hasEnvConfig = React.useMemo(() => { return !!(process.env.NEXT_PUBLIC_NAVIDROME_URL && process.env.NEXT_PUBLIC_NAVIDROME_USERNAME && process.env.NEXT_PUBLIC_NAVIDROME_PASSWORD); }, []); // Check if Navidrome is already working on component mount const checkNavidromeConnection = useCallback(async () => { try { // First check if there's a working API instance const { getNavidromeAPI } = await import('@/lib/navidrome'); const api = getNavidromeAPI(); if (api) { // Test the existing API const success = await api.ping(); if (success) { setCanSkipNavidrome(true); // Get the current config to populate form if (config.serverUrl && config.username && config.password) { setFormData({ serverUrl: config.serverUrl, username: config.username, password: config.password }); } // If this is first-time setup and Navidrome is working, skip to settings const hasCompletedOnboarding = localStorage.getItem('onboarding-completed'); if (!hasCompletedOnboarding) { setStep('settings'); } return; } } // If no working API, check if we have config that just needs testing if (config.serverUrl && config.username && config.password) { const success = await testConnection(config); if (success) { setCanSkipNavidrome(true); setFormData({ serverUrl: config.serverUrl, username: config.username, password: config.password }); const hasCompletedOnboarding = localStorage.getItem('onboarding-completed'); if (!hasCompletedOnboarding) { setStep('settings'); } } } } catch (error) { console.log('Navidrome connection check failed, will show config step'); } }, [config, setStep, setFormData, setCanSkipNavidrome, testConnection]); useEffect(() => { checkNavidromeConnection(); }, [checkNavidromeConnection]); 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 all settings localStorage.setItem('lastfm-scrobbling-enabled', scrobblingEnabled.toString()); // Mark onboarding as complete localStorage.setItem('onboarding-completed', '1.1.0'); 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); }; const handleDemoSetup = async () => { const demoCredentials = { serverUrl: 'https://demo.navidrome.org', username: 'demo', password: 'demo' }; // Set form data setFormData(demoCredentials); setIsTesting(true); try { const success = await testConnection(demoCredentials); if (success) { // Save the config updateConfig(demoCredentials); toast({ title: "Demo Server Connected", description: "Successfully connected to the Navidrome demo server! Let's configure your preferences.", }); // Move to settings step setStep('settings'); } else { toast({ title: "Demo Server Unavailable", description: "The demo server is currently unavailable. Please try again later or enter your own server details.", variant: "destructive" }); } } catch (error) { toast({ title: "Connection Error", description: "Could not connect to the demo server. Please check your internet connection.", variant: "destructive" }); } finally { setIsTesting(false); } }; if (step === 'settings') { return (
{scrobblingEnabled ? "Tracks will be scrobbled to Last.fm via Navidrome" : "Last.fm scrobbling will be disabled"}