diff --git a/frontend/src/pages/Analyze.tsx b/frontend/src/pages/Analyze.tsx index 0c51322..4ae3fdb 100644 --- a/frontend/src/pages/Analyze.tsx +++ b/frontend/src/pages/Analyze.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react' +import { useState, useEffect } from 'react' import { Lightbulb, Loader2, Sparkles } from 'lucide-react' import { analyzeSong, type AnalyzeResponse } from '../lib/api' import RecommendationCard from '../components/RecommendationCard' @@ -9,10 +9,21 @@ export default function Analyze() { const [title, setTitle] = useState('') const [loading, setLoading] = useState(false) const [error, setError] = useState('') - const [result, setResult] = useState(null) + const [result, setResult] = useState(() => { + try { + const saved = sessionStorage.getItem('vynl_analyze_results') + return saved ? JSON.parse(saved) : null + } catch { return null } + }) const [savingIds, setSavingIds] = useState>(new Set()) const [dislikingIds, setDislikingIds] = useState>(new Set()) + useEffect(() => { + if (result) { + sessionStorage.setItem('vynl_analyze_results', JSON.stringify(result)) + } + }, [result]) + const handleAnalyze = async () => { if (!artist.trim() || !title.trim()) return setLoading(true) diff --git a/frontend/src/pages/ArtistDive.tsx b/frontend/src/pages/ArtistDive.tsx index 70af49a..684b1bf 100644 --- a/frontend/src/pages/ArtistDive.tsx +++ b/frontend/src/pages/ArtistDive.tsx @@ -8,9 +8,20 @@ export default function ArtistDive() { const navigate = useNavigate() const [artist, setArtist] = useState(searchParams.get('artist') || '') const [loading, setLoading] = useState(false) - const [result, setResult] = useState(null) + const [result, setResult] = useState(() => { + try { + const saved = sessionStorage.getItem('vynl_artist_dive_results') + return saved ? JSON.parse(saved) : null + } catch { return null } + }) const [error, setError] = useState('') + useEffect(() => { + if (result) { + sessionStorage.setItem('vynl_artist_dive_results', JSON.stringify(result)) + } + }, [result]) + const handleDive = async (artistName?: string) => { const name = artistName || artist if (!name.trim()) return diff --git a/frontend/src/pages/BandcampDiscover.tsx b/frontend/src/pages/BandcampDiscover.tsx index 72d23ec..065ed51 100644 --- a/frontend/src/pages/BandcampDiscover.tsx +++ b/frontend/src/pages/BandcampDiscover.tsx @@ -10,9 +10,19 @@ const SORT_OPTIONS = [ export default function BandcampDiscover() { const [tags, setTags] = useState([]) - const [selectedTags, setSelectedTags] = useState([]) + const [selectedTags, setSelectedTags] = useState(() => { + try { + const saved = sessionStorage.getItem('vynl_bandcamp_tags') + return saved ? JSON.parse(saved) : [] + } catch { return [] } + }) const [sort, setSort] = useState('new') - const [releases, setReleases] = useState([]) + const [releases, setReleases] = useState(() => { + try { + const saved = sessionStorage.getItem('vynl_bandcamp_results') + return saved ? JSON.parse(saved) : [] + } catch { return [] } + }) const [page, setPage] = useState(1) const [loading, setLoading] = useState(false) const [loadingMore, setLoadingMore] = useState(false) @@ -25,6 +35,18 @@ export default function BandcampDiscover() { .finally(() => setTagsLoading(false)) }, []) + useEffect(() => { + if (selectedTags.length > 0) { + sessionStorage.setItem('vynl_bandcamp_tags', JSON.stringify(selectedTags)) + } + }, [selectedTags]) + + useEffect(() => { + if (releases.length > 0) { + sessionStorage.setItem('vynl_bandcamp_results', JSON.stringify(releases)) + } + }, [releases]) + const fetchReleases = async (newPage: number, append: boolean = false) => { if (selectedTags.length === 0) return append ? setLoadingMore(true) : setLoading(true) diff --git a/frontend/src/pages/Compatibility.tsx b/frontend/src/pages/Compatibility.tsx index d09c057..6bbf11f 100644 --- a/frontend/src/pages/Compatibility.tsx +++ b/frontend/src/pages/Compatibility.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react' +import { useState, useEffect } from 'react' import { Users, Loader2, Music, Sparkles, Heart } from 'lucide-react' import { checkCompatibility, type CompatibilityResponse } from '../lib/api' @@ -69,7 +69,18 @@ export default function Compatibility() { const [email, setEmail] = useState('') const [loading, setLoading] = useState(false) const [error, setError] = useState(null) - const [result, setResult] = useState(null) + const [result, setResult] = useState(() => { + try { + const saved = sessionStorage.getItem('vynl_compatibility_results') + return saved ? JSON.parse(saved) : null + } catch { return null } + }) + + useEffect(() => { + if (result) { + sessionStorage.setItem('vynl_compatibility_results', JSON.stringify(result)) + } + }, [result]) const handleCompare = async (e: React.FormEvent) => { e.preventDefault() diff --git a/frontend/src/pages/CrateDigger.tsx b/frontend/src/pages/CrateDigger.tsx index 76913b6..3309bea 100644 --- a/frontend/src/pages/CrateDigger.tsx +++ b/frontend/src/pages/CrateDigger.tsx @@ -1,19 +1,33 @@ -import { useState, useCallback } from 'react' +import { useState, useCallback, useEffect } from 'react' import { Disc3, X, Heart, ExternalLink, Loader2, RotateCcw } from 'lucide-react' import { fillCrate, crateSave, type CrateItem } from '../lib/api' type CardState = 'visible' | 'saving' | 'passing' export default function CrateDigger() { - const [crate, setCrate] = useState([]) - const [currentIndex, setCurrentIndex] = useState(0) + const [crate, setCrate] = useState(() => { + try { + const saved = sessionStorage.getItem('vynl_crate') + return saved ? JSON.parse(saved) : [] + } catch { return [] } + }) + const [currentIndex, setCurrentIndex] = useState(() => { + try { return Number(sessionStorage.getItem('vynl_crate_index') || '0') } catch { return 0 } + }) const [loading, setLoading] = useState(false) const [error, setError] = useState('') const [cardState, setCardState] = useState('visible') - const [savedCount, setSavedCount] = useState(0) - const [crateSize, setCrateSize] = useState(0) + const [savedCount, setSavedCount] = useState(() => { + try { return Number(sessionStorage.getItem('vynl_crate_saved') || '0') } catch { return 0 } + }) + const [crateSize, setCrateSize] = useState(() => { + try { return Number(sessionStorage.getItem('vynl_crate_size') || '0') } catch { return 0 } + }) const [finished, setFinished] = useState(false) + useEffect(() => { sessionStorage.setItem('vynl_crate_index', String(currentIndex)) }, [currentIndex]) + useEffect(() => { sessionStorage.setItem('vynl_crate_saved', String(savedCount)) }, [savedCount]) + const loadCrate = useCallback(async () => { setLoading(true) setError('') @@ -25,6 +39,10 @@ export default function CrateDigger() { const items = await fillCrate(20) setCrate(items) setCrateSize(items.length) + sessionStorage.setItem('vynl_crate', JSON.stringify(items)) + sessionStorage.setItem('vynl_crate_size', String(items.length)) + sessionStorage.setItem('vynl_crate_index', '0') + sessionStorage.setItem('vynl_crate_saved', '0') } catch { setError('Failed to fill the crate. Try again.') } finally { diff --git a/frontend/src/pages/Discover.tsx b/frontend/src/pages/Discover.tsx index 9168cfd..01c6003 100644 --- a/frontend/src/pages/Discover.tsx +++ b/frontend/src/pages/Discover.tsx @@ -27,7 +27,12 @@ export default function Discover() { const [playlists, setPlaylists] = useState([]) const [selectedPlaylist, setSelectedPlaylist] = useState('') const [query, setQuery] = useState('') - const [results, setResults] = useState([]) + const [results, setResults] = useState(() => { + try { + const saved = sessionStorage.getItem('vynl_discover_results') + return saved ? JSON.parse(saved) : [] + } catch { return [] } + }) const [remaining, setRemaining] = useState(null) const [discovering, setDiscovering] = useState(false) const [loading, setLoading] = useState(true) @@ -50,6 +55,13 @@ export default function Discover() { if (preQuery) setQuery(preQuery) }, [searchParams]) + // Persist results to sessionStorage + useEffect(() => { + if (results.length > 0) { + sessionStorage.setItem('vynl_discover_results', JSON.stringify(results)) + } + }, [results]) + useEffect(() => { const load = async () => { try { diff --git a/frontend/src/pages/PlaylistGenerator.tsx b/frontend/src/pages/PlaylistGenerator.tsx index 51cec1e..1f5fc0a 100644 --- a/frontend/src/pages/PlaylistGenerator.tsx +++ b/frontend/src/pages/PlaylistGenerator.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react' +import { useState, useEffect } from 'react' import { ListMusic, Loader2, Save, Copy, Check, ExternalLink } from 'lucide-react' import { generatePlaylist, type GeneratedPlaylistResponse } from '../lib/api' @@ -9,11 +9,22 @@ export default function PlaylistGenerator() { const [count, setCount] = useState(25) const [loading, setLoading] = useState(false) const [error, setError] = useState('') - const [result, setResult] = useState(null) + const [result, setResult] = useState(() => { + try { + const saved = sessionStorage.getItem('vynl_playlist_gen_results') + return saved ? JSON.parse(saved) : null + } catch { return null } + }) const [saving, setSaving] = useState(false) const [saved, setSaved] = useState(false) const [copied, setCopied] = useState(false) + useEffect(() => { + if (result) { + sessionStorage.setItem('vynl_playlist_gen_results', JSON.stringify(result)) + } + }, [result]) + const handleGenerate = async () => { if (!theme.trim()) return setLoading(true) diff --git a/frontend/src/pages/RabbitHole.tsx b/frontend/src/pages/RabbitHole.tsx index 27d1d9e..26882bf 100644 --- a/frontend/src/pages/RabbitHole.tsx +++ b/frontend/src/pages/RabbitHole.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react' +import { useState, useEffect } from 'react' import { ArrowDownCircle, ExternalLink, Link2, Play, Music } from 'lucide-react' import type { RabbitHoleStep } from '../lib/api' import { generateRabbitHole } from '../lib/api' @@ -10,10 +10,26 @@ export default function RabbitHole() { const [seedTitle, setSeedTitle] = useState('') const [stepCount, setStepCount] = useState(8) const [loading, setLoading] = useState(false) - const [theme, setTheme] = useState('') - const [steps, setSteps] = useState([]) + const [theme, setTheme] = useState(() => { + try { + const saved = sessionStorage.getItem('vynl_rabbithole_results') + return saved ? JSON.parse(saved).theme : '' + } catch { return '' } + }) + const [steps, setSteps] = useState(() => { + try { + const saved = sessionStorage.getItem('vynl_rabbithole_results') + return saved ? JSON.parse(saved).steps : [] + } catch { return [] } + }) const [error, setError] = useState('') + useEffect(() => { + if (theme && steps.length > 0) { + sessionStorage.setItem('vynl_rabbithole_results', JSON.stringify({ theme, steps })) + } + }, [theme, steps]) + const handleGenerate = async () => { setLoading(true) setError('')