Skip to content

Commit

Permalink
Created loading screen + dynamic track fetching in RadioContext
Browse files Browse the repository at this point in the history
  • Loading branch information
forreya committed Feb 16, 2025
1 parent fc4f4a3 commit fc5a0b5
Show file tree
Hide file tree
Showing 8 changed files with 259 additions and 15 deletions.
195 changes: 195 additions & 0 deletions client/src/components/LoadingScreen/LoadingScreen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
import React, { useEffect, useMemo, useState } from "react";

interface LoadingScreenProps {
onComplete: () => void;
}

const LoadingScreen: React.FC<LoadingScreenProps> = ({ onComplete }) => {
const [lines, setLines] = useState<string[]>([]);
const [step, setStep] = useState<number>(0);

const beowArt = String.raw`
* ,MMM8&&&. *
MMMM88&&&&& .
MMMM88&&&&&&&
* MMM88&&&&&&&& :::::::::::::::::::::::::::::::::::::::::
MMM88&&&&&&&&
'MMM88&&&&&&' ██████╗ ███████╗ ██████╗ ██╗ ██╗
'MMM8&&&' * ██╔══██╗ ██╔════╝ ██╔═══██╗ ██║ ██║
|\___/| ██████╔╝ ███████╗ ██║ ██║ ██║ ██╗██║
) ( . ' ██╔══██╗ ██╔════╝ ██║ ██║ ██║███╗██║
=\ /= ██║████║ ███████╗ ╚██████╔╝ ╚███╔███╔╝
)===( * ╚══════╝ ╚══════╝ ╚═════╝ ╚══╝╚══╝
/ \
| | :::::::::::::::::::::::::::::::::::::::::
/ \
\ /
_/\_/\_/\__ _/_/\_/\_/\_/\_/\_/\_/\_/\_/\_
| | | |( ( | | | | | | | | | |
| | | | ) ) | | | | | | | | | |
| | | |(_( | | | | | | | | | |
| | | | | | | | | | | | | | |
| | | | | | | | | | | | | | |
`;

const beowArt2 = String.raw`
* ,MMM8&&&. *
MMMM88&&&&& .
MMMM88&&&&&&&
* MMM88&&&&&&&& :::::::::::::::::::::::::::::::::::::::::
MMM88&&&&&&&&
'MMM88&&&&&&' ██████╗ ███████╗ ██████╗ ██╗ ██╗
'MMM8&&&' * ██╔══██╗ ██╔════╝ ██╔═══██╗ ██║ ██║
|\___/| ██████╔╝ ███████╗ ██║ ██║ ██║ ██╗██║
=) ^Y^ (= . ' ██╔══██╗ ██╔════╝ ██║ ██║ ██║███╗██║
\ ^ / ██║████║ ███████╗ ╚██████╔╝ ╚███╔███╔╝
)=*=( * ╚══════╝ ╚══════╝ ╚═════╝ ╚══╝╚══╝
/ \
| | :::::::::::::::::::::::::::::::::::::::::
/| | | |\
\| | |_|/\
_/\_/\_//_// ___/\_/\_/\_/\_/\_/\_/\_/\_/\_
| | | | \_) | | | | | | | | | |
| | | | | | | | | | | | | | |
| | | | | | | | | | | | | | |
| | | | | | | | | | | | | | |
| | | | | | | | | | | | | | |
`;

const messages = useMemo(
() => [
{
initialText: "Booting up _",
initialDelay: 500,
text: "Initializing default server protocols",
delay: 0,
},
{ text: "Scanning for available cats", delay: 250 },
{
text: "Feline signature detected (ID: 8 - Bloncs)",
delay: 750,
disableAnimation: true,
},
{ text: "Waking up Bloncs", delay: 750 },
{
text: "Bloncs online.",
delay: 750,
disableAnimation: true,
},
],
[]
);

const animateLine = (
message: string,
disableAnimation?: boolean,
replaceExisting: boolean = false
) => {
if (replaceExisting) {
setLines((prev) => {
const newLines = [...prev];
newLines[newLines.length - 1] = message;
return newLines;
});
} else {
setLines((prev) => [...prev, message]);
}

if (!disableAnimation) {
const intervalId = setInterval(() => {
setLines((prev) => {
const newLines = [...prev];
const lastLine = newLines[newLines.length - 1];
if (!lastLine.endsWith("...")) {
newLines[newLines.length - 1] = lastLine + ".";
} else {
clearInterval(intervalId);
newLines[newLines.length - 1] = lastLine + " done."
setStep((prev) => prev + 1);
}
return newLines;
});
}, 300);
} else {
setStep((prev) => prev + 1);
}
};

useEffect((): void | (() => void) => {
if (step < messages.length) {
const { initialText, initialDelay, text, delay, disableAnimation } =
messages[step];

const runMessage = (replaceExisting: boolean = false) => {
animateLine(text, disableAnimation, replaceExisting);
};

if (initialText) {
setLines((prev) => [...prev, initialText]);
setTimeout(() => {
setLines((prev) => [...prev.slice(0, -1), text]);
setTimeout(() => runMessage(true), delay);
}, initialDelay);
} else {
setTimeout(() => runMessage(), delay);
}
}
// Art rendering
else if (step === messages.length) {
setLines((prev) => [...prev, ""]);
let index = 0;
const artInterval = setInterval(() => {
index++;
setLines((prev) => {
const newLines = [...prev];
newLines[newLines.length - 1] = beowArt.substring(0, index);
return newLines;
});
if (index >= beowArt.length) {
setLines((prev) => [...prev.slice(0, -1), beowArt2]);
clearInterval(artInterval);
setStep((prev) => prev + 1);
}
}, 5);
return () => clearInterval(artInterval);
}
// Terminal countdown
else if (step === messages.length + 1) {
let count = 10;
setLines((prev) => [...prev, `Press anywhere to enter (${count})`]);
const countdownInterval = setInterval(() => {
count--;
if (count == 0) {
clearInterval(countdownInterval);
onComplete();
} else {
setLines((prev) => [
...prev.slice(0, -1),
`Press anywhere to enter (${count})`,
]);
}
}, 1000);
return () => clearInterval(countdownInterval);
}
}, [step, onComplete, messages, beowArt, beowArt2]);

return (
<>
{lines.map((line, index) => (
<pre
className=""
key={index}
style={{ margin: 0, whiteSpace: "pre-wrap" }}
>
{line}
</pre>
))}
</>
);
};

export default LoadingScreen;
5 changes: 4 additions & 1 deletion client/src/components/Radio/Radio.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export const Radio = () => {
const {currentTrack, audioElementRef, currentTime, duration} = useRadioContext();

return (
currentTrack ?
<div>
<AudioPlayer
audioElementRef={audioElementRef}
Expand All @@ -14,10 +15,12 @@ export const Radio = () => {
<RadioModal
title={currentTrack.title}
artist={currentTrack.artist}
featuring_artists={currentTrack.featuring_artists}
src={currentTrack.src}
duration={duration}
currentTime={currentTime}
/>
</div>
</div> :
<h1>Loading...</h1>
)
}
3 changes: 2 additions & 1 deletion client/src/components/Radio/RadioModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ interface RadioModalProps extends Track {
const RadioModal: React.FC<RadioModalProps> = ({
title,
artist,
featuring_artists,
duration,
currentTime,
}: RadioModalProps) => {
Expand Down Expand Up @@ -45,7 +46,7 @@ const RadioModal: React.FC<RadioModalProps> = ({
duration={formatPlaybackTime(duration)}
/>
<div className='text-lg'>{title}</div>
<div>{artist}</div>
<div>{artist} - {featuring_artists}</div>
<div className="flex justify-between items-center mt-4">
<button></button>
<TogglePlayButton />
Expand Down
34 changes: 29 additions & 5 deletions client/src/context/RadioContext.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { createContext, Dispatch, ReactNode, useContext, useRef, useState } from "react";
import { sampleTracks } from "../assets/test/sampleAudioData.ts"
import { createContext, Dispatch, ReactNode, useContext, useEffect, useRef, useState } from "react";
import { Track } from "../types/RadioTypes.ts";

interface RadioContextType {
currentTrack: Track;
setCurrentTrack: Dispatch<React.SetStateAction<Track>>;
currentTrack: Track | null;
setCurrentTrack: Dispatch<React.SetStateAction<Track | null>>;
setTrackIndex: Dispatch<React.SetStateAction<number>>;
audioElementRef: React.RefObject<HTMLAudioElement>;
currentTime: number;
Expand All @@ -17,13 +16,38 @@ const RadioContext = createContext<RadioContextType | undefined>(undefined);

export const RadioProvider = ({children} : {children: ReactNode;}) => {
const [trackIndex, setTrackIndex] = useState<number>(0);
const [currentTrack, setCurrentTrack] = useState<Track>(sampleTracks[trackIndex]);
const [currentTrack, setCurrentTrack] = useState<Track | null>(null);
const [currentTime, setCurrentTime] = useState<number>(0);
const [duration, setDuration] = useState<number>(0);
const audioElementRef = useRef<HTMLAudioElement>(null);

useEffect(() => {
const fetchRandomTrack = async () => {
try {
const response = await fetch(`${import.meta.env.VITE_BACKEND_URL}music/tracks/`);
if (!response.ok) {
throw new Error("Network response was not ok");
}
const data = await response.json();
const trackData: Track = {
title: data.title,
artist: data.artist,
featuring_artists: data.featuring_artists,
src: data.s3_url,
};
setCurrentTrack(trackData);
} catch (error) {
console.error("Error fetching track:", error);
}
}

fetchRandomTrack()
}, []);

const contextValue = {
currentTrack,
setCurrentTrack,
trackIndex,
setTrackIndex,
audioElementRef,
currentTime,
Expand Down
24 changes: 18 additions & 6 deletions client/src/pages/HomePage.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
import { useState } from "react";
import LoadingScreen from "../components/LoadingScreen/LoadingScreen";
import { Radio } from "../components/Radio/Radio";
import { RadioProvider } from "../context/RadioContext";

const HomePage = () => {
const [isLoading, setIsLoading] = useState(true);

return (
<div>
<h1 className="text-6xl font-oldComputer">HEADER</h1>
<RadioProvider>
<Radio />
</RadioProvider>
</div>
<>
{isLoading ? (
<div className="w-screen h-screen bg-lightBeige" onClick={() => setIsLoading(false)}>
<LoadingScreen onComplete={() => setIsLoading(false)}/>
</div>
) : (
<div className="main-content bg-lightBeige">
<h1 className="text-6xl">HEADER</h1>
<RadioProvider>
<Radio />
</RadioProvider>
</div>
)}
</>
)
}

Expand Down
1 change: 1 addition & 0 deletions client/src/types/RadioTypes.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export interface Track {
title: string;
src: string;
featuring_artists: string;
artist: string;
}
3 changes: 2 additions & 1 deletion server/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ sqlparse==0.5.3
psycopg2-binary==2.9.10
djangorestframework==3.15.2
boto3==1.36.2
python-dotenv==1.0.1
python-dotenv==1.0.1
django-cors-headers==4.7.0
9 changes: 8 additions & 1 deletion server/server/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
SECRET_KEY = os.getenv('SECRET_KEY')

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = os.getenv('ENV_VAR', 'False').lower() in ('true', '1')
DEBUG = os.getenv('DEBUG', 'False').lower() in ('true', '1')

ALLOWED_HOSTS = ['0.0.0.0', 'localhost']

Expand All @@ -38,6 +38,7 @@
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'corsheaders',
'users',
'music',
]
Expand All @@ -50,6 +51,12 @@
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
]

CORS_ALLOWED_ORIGINS = [
os.getenv('FRONTEND_URL'),
]

ROOT_URLCONF = 'server.urls'
Expand Down

0 comments on commit fc5a0b5

Please sign in to comment.