leumas-private-shared
Version:
Private React JSX Package For Leumas Shared Components, Headers, Footers, Asides, Login Pages, API Key Manager and much more. Styles and everything reusable to avoid DRY code across all of our subdomains
192 lines (174 loc) • 7.08 kB
JSX
import React, { useState, useEffect, useRef } from 'react';
import { Container, Typography, Card, CardContent, Button, IconButton, Box } from '@mui/material';
import { Helmet } from 'react-helmet-async';
import { FaLinkedin, FaGithub, FaPlay, FaPause, FaForward, FaBackward } from 'react-icons/fa';
import { SiFiverr } from "react-icons/si";
import InfinityBackgroundComponent from '../Components/Backgrounds/Infinity';
import Gallery from '../Components/Gallery/Gallery';
const ServicePage = ({ service, galleryConfig }) => {
const [audioFiles, setAudioFiles] = useState([]);
const [currentAudio, setCurrentAudio] = useState(null);
const [audio, setAudio] = useState(null);
const [audioPlaying, setAudioPlaying] = useState(false);
const [frequencyData, setFrequencyData] = useState([]);
const [strobeMode, setStrobeMode] = useState(false); // State to toggle strobe mode
const audioContextRef = useRef(null);
const analyzerRef = useRef(null);
useEffect(() => {
const fetchAudioFiles = async () => {
try {
const response = await fetch('http://localhost:3002/cloudinary/audio/Audio', {
mode: 'cors'
});
const data = await response.json();
console.log(data);
setAudioFiles(data);
const defaultAudio = data.find(file => file.title === 'Skyfall_Lyrics_8D_Audio_z7ym2u');
if (defaultAudio) {
setCurrentAudio(defaultAudio);
const newAudio = new Audio(defaultAudio.src);
newAudio.crossOrigin = "anonymous"; // Add crossOrigin attribute
setAudio(newAudio);
}
} catch (error) {
console.error('Error fetching audio files:', error);
}
};
fetchAudioFiles();
}, []);
useEffect(() => {
if (audio && audioPlaying) {
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
const analyzer = audioContext.createAnalyser();
const source = audioContext.createMediaElementSource(audio);
source.connect(analyzer);
analyzer.connect(audioContext.destination);
analyzer.fftSize = 256;
const bufferLength = analyzer.frequencyBinCount;
const dataArray = new Uint8Array(bufferLength);
const updateFrequencyData = () => {
analyzer.getByteFrequencyData(dataArray);
setFrequencyData([...dataArray]);
requestAnimationFrame(updateFrequencyData);
};
updateFrequencyData();
audioContextRef.current = audioContext;
analyzerRef.current = analyzer;
audio.play().catch(error => console.error('Error playing audio:', error));
return () => {
audio.pause();
audioContext.close();
};
}
}, [audio, audioPlaying]);
const handleAudioChange = (newAudio) => {
if (audio) {
audio.pause();
}
const newAudioElement = new Audio(newAudio.src);
newAudioElement.crossOrigin = "anonymous"; // Add crossOrigin attribute
setCurrentAudio(newAudio);
setAudio(newAudioElement);
setAudioPlaying(true);
};
const handleAudioPlayPause = () => {
if (audio) {
if (audioPlaying) {
audio.pause();
} else {
audio.play().catch(error => console.error('Error playing audio:', error));
}
setAudioPlaying(!audioPlaying);
}
};
const handleAudioSkip = (direction) => {
if (audioFiles.length > 0) {
const currentIndex = audioFiles.findIndex(file => file.src === currentAudio.src);
let newIndex;
if (direction === 'next') {
newIndex = (currentIndex + 1) % audioFiles.length;
} else {
newIndex = (currentIndex - 1 + audioFiles.length) % audioFiles.length;
}
handleAudioChange(audioFiles[newIndex]);
}
};
const toggleStrobeMode = () => {
setStrobeMode(!strobeMode);
};
if (!service) {
return <Typography variant="h6" component="p">Service not found.</Typography>;
}
return (
<InfinityBackgroundComponent>
<Helmet>
<title>{service.seo.title}</title>
<meta name="description" content={service.seo.description} />
<meta name="keywords" content={service.seo.keywords} />
</Helmet>
<Container
sx={{
position: 'relative',
height: '100vh',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'start',
textAlign: 'center',
padding: '20px'
}}
>
<div style={{ position: 'absolute', top: 20, left: 20 }}>
<IconButton href="https://www.fiverr.com/bermudezw1008" target="_blank" color="primary">
<SiFiverr />
</IconButton>
<IconButton href="https://www.linkedin.com/in/william-bermudez-15845021a/" target="_blank" color="primary">
<FaLinkedin />
</IconButton>
<IconButton href="https://github.com/Leumas-Tech" target="_blank" color="primary">
<FaGithub />
</IconButton>
</div>
<div style={{ position: 'absolute', top: 20, right: 20 }}>
<Button
href={service.link}
target="_blank"
variant="contained"
color="primary"
>
Learn More
</Button>
</div>
<Typography variant="h2" component="h1" gutterBottom sx={{ fontWeight: 'bold', color: 'black', marginTop: '30px' }}>
{service.title}
</Typography>
<Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', marginBottom: '20px' }}>
<IconButton onClick={() => handleAudioSkip('previous')} color="primary">
<FaBackward />
</IconButton>
<IconButton onClick={handleAudioPlayPause} color="primary">
{audioPlaying ? <FaPause /> : <FaPlay />}
</IconButton>
<IconButton onClick={() => handleAudioSkip('next')} color="primary">
<FaForward />
</IconButton>
<Button onClick={toggleStrobeMode} variant="contained" color="secondary" sx={{ ml: 2 }}>
{strobeMode ? 'Disable Strobe' : 'Enable Strobe'}
</Button>
</Box>
<Typography variant="h6" component="p" sx={{ color: 'white', marginBottom: '10px' }}>
Now Playing: {currentAudio ? currentAudio.title : 'Loading...'}
</Typography>
<Card sx={{ width: '100%', maxWidth: '800px', boxShadow: 3 }}>
<CardContent>
<Typography variant="body1" color="text.secondary" gutterBottom>
<span dangerouslySetInnerHTML={{ __html: service.htmlContent }} />
</Typography>
</CardContent>
</Card>
<Gallery images={galleryConfig} frequencyData={frequencyData} audioPlaying={audioPlaying} strobeMode={strobeMode} />
</Container>
</InfinityBackgroundComponent>
);
};
export default ServicePage;