Refactor Root component to accept additional props and enhance LyricSearch functionality with URL hash handling and lyrics sanitization.

This commit is contained in:
2025-10-08 15:49:00 -04:00
parent ef4c80450a
commit 4227c21d1f
3 changed files with 38 additions and 7 deletions

View File

@@ -16,7 +16,7 @@ const MediaRequestForm = lazy(() => import('./TRip/MediaRequestForm.jsx'));
const RequestManagement = lazy(() => import('./TRip/RequestManagement.jsx')); const RequestManagement = lazy(() => import('./TRip/RequestManagement.jsx'));
const Player = lazy(() => import('./AudioPlayer.jsx')); const Player = lazy(() => import('./AudioPlayer.jsx'));
export default function Root({ child, user = undefined }) { export default function Root({ child, user = undefined, ...props }) {
window.toast = toast; window.toast = toast;
const theme = document.documentElement.getAttribute("data-theme") const theme = document.documentElement.getAttribute("data-theme")
usePrimeReactThemeSwitcher(theme); usePrimeReactThemeSwitcher(theme);
@@ -35,7 +35,7 @@ export default function Root({ child, user = undefined }) {
Work in progress... bugs are to be expected. Work in progress... bugs are to be expected.
</Alert> */} </Alert> */}
{child == "LoginPage" && (<LoginPage client:only="react" />)} {child == "LoginPage" && (<LoginPage client:only="react" />)}
{child == "LyricSearch" && (<LyricSearch client:only="react" />)} {child == "LyricSearch" && (<LyricSearch {...props} client:only="react" />)}
{child == "Player" && (<Player client:only="react" user={user} />)} {child == "Player" && (<Player client:only="react" user={user} />)}
{child == "Memes" && <Memes client:only="react" />} {child == "Memes" && <Memes client:only="react" />}
{child == "qs2.MediaRequestForm" && <MediaRequestForm client:only="react" />} {child == "qs2.MediaRequestForm" && <MediaRequestForm client:only="react" />}

View File

@@ -18,6 +18,7 @@ import { API_URL } from '../config';
export default function LyricSearch() { export default function LyricSearch() {
const [showLyrics, setShowLyrics] = useState(false); const [showLyrics, setShowLyrics] = useState(false);
return ( return (
<div className="lyric-search"> <div className="lyric-search">
<h2 className="title"> <h2 className="title">
@@ -52,6 +53,29 @@ export function LyricSearchInputField({ id, placeholder, setShowLyrics }) {
const autoCompleteRef = useRef(null); const autoCompleteRef = useRef(null);
const [theme, setTheme] = useState(document.documentElement.getAttribute("data-theme") || "light"); const [theme, setTheme] = useState(document.documentElement.getAttribute("data-theme") || "light");
// Handle URL hash changes and initial load
useEffect(() => {
const handleHashChange = () => {
const hash = window.location.hash.slice(1); // Remove the # symbol
if (hash) {
try {
const [artist, song] = decodeURIComponent(hash).split('/');
if (artist && song) {
setValue(`${artist} - ${song}`);
handleSearch(`${artist} - ${song}`);
}
} catch (e) {
console.error('Failed to parse URL hash:', e);
}
}
};
window.addEventListener('hashchange', handleHashChange);
handleHashChange(); // Handle initial load
return () => window.removeEventListener('hashchange', handleHashChange);
}, []);
useEffect(() => { useEffect(() => {
const handler = (e) => { const handler = (e) => {
const newTheme = e.detail; const newTheme = e.detail;
@@ -118,17 +142,17 @@ export function LyricSearchInputField({ id, placeholder, setShowLyrics }) {
}, 0); }, 0);
}; };
const handleSearch = async () => { const handleSearch = async (searchValue = value) => {
if (autoCompleteRef.current) { if (autoCompleteRef.current) {
autoCompleteRef.current.hide(); autoCompleteRef.current.hide();
} }
if (!value.includes(" - ")) { if (!searchValue.includes(" - ")) {
setAlertVisible(true); setAlertVisible(true);
return; return;
} }
const [artist, song] = value.split(" - ", 2).map((v) => v.trim()); const [artist, song] = searchValue.split(" - ", 2).map((v) => v.trim());
if (!artist || !song) { if (!artist || !song) {
setAlertVisible(true); setAlertVisible(true);
return; return;
@@ -169,6 +193,10 @@ export function LyricSearchInputField({ id, placeholder, setShowLyrics }) {
setLyricsResult({ artist: data.artist, song: data.song, lyrics: data.lyrics }); setLyricsResult({ artist: data.artist, song: data.song, lyrics: data.lyrics });
setShowLyrics(true); setShowLyrics(true);
// Update URL hash with search parameters
const hash = `#${encodeURIComponent(data.artist)}/${encodeURIComponent(data.song)}`;
window.history.pushState(null, '', hash);
toast.update(searchToastRef.current, { toast.update(searchToastRef.current, {
type: "success", type: "success",
render: `Found! (Took ${duration}s)`, render: `Found! (Took ${duration}s)`,

View File

@@ -6,8 +6,11 @@ import LyricSearch from '../components/LyricSearch.jsx';
<Base> <Base>
<section> <section>
<div class="prose prose-neutral dark:prose-invert"> <div class="prose prose-neutral dark:prose-invert">
<Root child="LyricSearch" client:only="react" /> <Root
child="LyricSearch"
client:only="react"
/>
</div> </div>
</section> </section>
</Base> </Base>