radio styling changes (add/fix mobile responsivity); WIP, player still does not function on iOS
This commit is contained in:
		@@ -16,7 +16,12 @@ const STATIONS = {
 | 
			
		||||
  pop: { label: "Pop", streamPath: "/pop.ogg" },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Global interval tracking (required for Astro + client:only)
 | 
			
		||||
// Detect iOS user agent (can refine if needed)
 | 
			
		||||
const isIOS = (() => {
 | 
			
		||||
  if (typeof window === "undefined") return false;
 | 
			
		||||
  return /iP(ad|hone|od)/.test(navigator.userAgent);
 | 
			
		||||
})();
 | 
			
		||||
 | 
			
		||||
let activeInterval = null;
 | 
			
		||||
let currentStationForInterval = null;
 | 
			
		||||
 | 
			
		||||
@@ -50,13 +55,19 @@ export function Player() {
 | 
			
		||||
 | 
			
		||||
  const progress = duration > 0 ? (elapsed / duration) * 100 : 0;
 | 
			
		||||
 | 
			
		||||
  // Create Howl instance on activeStation change
 | 
			
		||||
  // UseEffect: create Howl on station change (as before)
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (soundRef.current) {
 | 
			
		||||
      soundRef.current.unload();
 | 
			
		||||
      soundRef.current = null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // On iOS we defer creation until user plays, so skip creating Howl here
 | 
			
		||||
    if (isIOS) {
 | 
			
		||||
      setIsPlaying(false);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const streamUrl = "https://stream.codey.lol" + STATIONS[activeStation].streamPath;
 | 
			
		||||
 | 
			
		||||
    const howl = new Howl({
 | 
			
		||||
@@ -92,47 +103,80 @@ export function Player() {
 | 
			
		||||
    };
 | 
			
		||||
  }, [activeStation]);
 | 
			
		||||
 | 
			
		||||
const togglePlayback = () => {
 | 
			
		||||
  if (isPlaying) {
 | 
			
		||||
    if (soundRef.current) {
 | 
			
		||||
      soundRef.current.pause();
 | 
			
		||||
    }
 | 
			
		||||
    setIsPlaying(false);
 | 
			
		||||
  } else {
 | 
			
		||||
    // If a previous Howl exists, unload it
 | 
			
		||||
    if (soundRef.current) {
 | 
			
		||||
      soundRef.current.stop();
 | 
			
		||||
      soundRef.current.unload();
 | 
			
		||||
  // Toggle playback handler
 | 
			
		||||
  const togglePlayback = () => {
 | 
			
		||||
    if (isPlaying) {
 | 
			
		||||
      if (soundRef.current) {
 | 
			
		||||
        soundRef.current.pause();
 | 
			
		||||
      }
 | 
			
		||||
      setIsPlaying(false);
 | 
			
		||||
    } else {
 | 
			
		||||
      if (isIOS) {
 | 
			
		||||
        // On iOS, defer creation until user explicitly plays
 | 
			
		||||
        if (soundRef.current) {
 | 
			
		||||
          soundRef.current.stop();
 | 
			
		||||
          soundRef.current.unload();
 | 
			
		||||
          soundRef.current = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const streamUrl =
 | 
			
		||||
          "https://stream.codey.lol" +
 | 
			
		||||
          STATIONS[activeStation].streamPath +
 | 
			
		||||
          `?t=${Date.now()}`; // Cache-busting param
 | 
			
		||||
 | 
			
		||||
        const newHowl = new Howl({
 | 
			
		||||
          src: [streamUrl],
 | 
			
		||||
          html5: true,
 | 
			
		||||
          onend: () => newHowl.play(),
 | 
			
		||||
          onplay: () => {
 | 
			
		||||
            setIsPlaying(true);
 | 
			
		||||
          },
 | 
			
		||||
          onpause: () => setIsPlaying(false),
 | 
			
		||||
          onstop: () => setIsPlaying(false),
 | 
			
		||||
          onloaderror: (_, err) => console.error("Load error", err),
 | 
			
		||||
          onplayerror: (_, err) => {
 | 
			
		||||
            console.error("Play error", err);
 | 
			
		||||
            setTimeout(() => newHowl.play(), 1000);
 | 
			
		||||
          },
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        soundRef.current = newHowl;
 | 
			
		||||
        newHowl.play();
 | 
			
		||||
      } else {
 | 
			
		||||
        // Desktop browsers: current logic
 | 
			
		||||
        if (soundRef.current) {
 | 
			
		||||
          soundRef.current.stop();
 | 
			
		||||
          soundRef.current.unload();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const streamUrl =
 | 
			
		||||
          "https://stream.codey.lol" +
 | 
			
		||||
          STATIONS[activeStation].streamPath +
 | 
			
		||||
          `?t=${Date.now()}`; // Cache-busting param
 | 
			
		||||
 | 
			
		||||
        const newHowl = new Howl({
 | 
			
		||||
          src: [streamUrl],
 | 
			
		||||
          html5: true,
 | 
			
		||||
          onend: () => newHowl.play(),
 | 
			
		||||
          onplay: () => {
 | 
			
		||||
            setIsPlaying(true);
 | 
			
		||||
          },
 | 
			
		||||
          onpause: () => setIsPlaying(false),
 | 
			
		||||
          onstop: () => setIsPlaying(false),
 | 
			
		||||
          onloaderror: (_, err) => console.error("Load error", err),
 | 
			
		||||
          onplayerror: (_, err) => {
 | 
			
		||||
            console.error("Play error", err);
 | 
			
		||||
            setTimeout(() => newHowl.play(), 1000);
 | 
			
		||||
          },
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        soundRef.current = newHowl;
 | 
			
		||||
        newHowl.play();
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
    const streamUrl =
 | 
			
		||||
      "https://stream.codey.lol" +
 | 
			
		||||
      STATIONS[activeStation].streamPath +
 | 
			
		||||
      `?t=${Date.now()}`; // Cache-busting param
 | 
			
		||||
 | 
			
		||||
    const newHowl = new Howl({
 | 
			
		||||
      src: [streamUrl],
 | 
			
		||||
      html5: true,
 | 
			
		||||
      onend: () => newHowl.play(),
 | 
			
		||||
      onplay: () => {
 | 
			
		||||
        setIsPlaying(true);
 | 
			
		||||
      },
 | 
			
		||||
      onpause: () => setIsPlaying(false),
 | 
			
		||||
      onstop: () => setIsPlaying(false),
 | 
			
		||||
      onloaderror: (_, err) => console.error("Load error", err),
 | 
			
		||||
      onplayerror: (_, err) => {
 | 
			
		||||
        console.error("Play error", err);
 | 
			
		||||
        setTimeout(() => newHowl.play(), 1000);
 | 
			
		||||
      },
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    soundRef.current = newHowl;
 | 
			
		||||
    newHowl.play();
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  // Metadata fetcher: global-safe
 | 
			
		||||
  // Metadata fetcher (unchanged)
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    clearGlobalMetadataInterval();
 | 
			
		||||
 | 
			
		||||
@@ -145,7 +189,6 @@ const togglePlayback = () => {
 | 
			
		||||
        });
 | 
			
		||||
        const data = await response.json();
 | 
			
		||||
 | 
			
		||||
        // Ignore stale interval calls
 | 
			
		||||
        if (currentStationForInterval !== activeStation) return;
 | 
			
		||||
 | 
			
		||||
        if (data.artist === "N/A" && data.song === "N/A") {
 | 
			
		||||
@@ -227,12 +270,8 @@ const togglePlayback = () => {
 | 
			
		||||
              <div id="length" style={{ width: `${progress}%` }}></div>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div className="music-control">
 | 
			
		||||
              <div className="music-control__play" id="play">
 | 
			
		||||
                {!isPlaying ? (
 | 
			
		||||
                  <Play onClick={togglePlayback} />
 | 
			
		||||
                ) : (
 | 
			
		||||
                  <Pause onClick={togglePlayback} />
 | 
			
		||||
                )}
 | 
			
		||||
              <div className="music-control__play" id="play" onClick={togglePlayback} role="button" tabIndex={0} aria-pressed={isPlaying}>
 | 
			
		||||
                {!isPlaying ? <Play /> : <Pause />}
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
          </section>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user