aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPinapelz <yukais@pinapelz.com>2026-06-04 18:34:18 -0700
committerPinapelz <yukais@pinapelz.com>2026-06-04 18:34:18 -0700
commit95602704d53bb72d96d4947869bce63830888d52 (patch)
tree89f158d98beb5667ececf371258545aef5c64732 /src
parent66a7890647d225957d81c8b361a46d0f797025da (diff)
overhaul how daily is updated from server to client
Diffstat (limited to 'src')
-rw-r--r--src/components/Game/index.tsx25
-rw-r--r--src/components/Player/index.tsx14
-rw-r--r--src/helpers/fetchSolution.ts21
-rw-r--r--src/pages/DailyPage.tsx10
-rw-r--r--src/react-app-env.d.ts9
5 files changed, 51 insertions, 28 deletions
diff --git a/src/components/Game/index.tsx b/src/components/Game/index.tsx
index cde682d..59f9289 100644
--- a/src/components/Game/index.tsx
+++ b/src/components/Game/index.tsx
@@ -11,6 +11,7 @@ import * as Styled from "./index.styled";
interface Props {
guesses: GuessType[];
todaysSolution: Song;
+ dailyDate?: string;
currentTry: number;
didGuess: boolean;
setSelectedSong: React.Dispatch<React.SetStateAction<Song | undefined>>;
@@ -24,17 +25,10 @@ function getUtcDate() {
return new Date().toISOString().split("T")[0];
}
-function checkDailyIsGenerated(): boolean {
- const CDN_URL = import.meta.env.VITE_CDN_URL;
- if (!CDN_URL) return false;
-
- const date = getUtcDate();
- return !!localStorage.getItem(`${CDN_URL}/${date}.mp3`);
-}
-
export function Game({
guesses,
todaysSolution,
+ dailyDate,
currentTry,
didGuess,
setSelectedSong,
@@ -43,22 +37,23 @@ export function Game({
mode = "daily",
onPlayAgain,
}: Props) {
- const [sessionDate] = React.useState(() => getUtcDate());
const recentFinishedPlay = localStorage.getItem("recentFinishedPlay");
const hasFinishedCurrentRound = didGuess || currentTry >= guesses.length;
- const isGameOver = hasFinishedCurrentRound;
+ const hasFinishedResponseDaily =
+ mode === "daily" && !!dailyDate && recentFinishedPlay === dailyDate;
+ const isGameOver = hasFinishedCurrentRound || hasFinishedResponseDaily;
const isBlocked =
mode === "daily" &&
- !!recentFinishedPlay &&
- new Date(sessionDate) > new Date(recentFinishedPlay) &&
- !checkDailyIsGenerated();
+ !!dailyDate &&
+ !hasFinishedResponseDaily &&
+ new Date(getUtcDate()) > new Date(dailyDate);
React.useEffect(() => {
if (mode !== "daily") return;
if (!hasFinishedCurrentRound) return;
- localStorage.setItem("recentFinishedPlay", sessionDate);
- }, [mode, hasFinishedCurrentRound, sessionDate]);
+ localStorage.setItem("recentFinishedPlay", dailyDate ?? getUtcDate());
+ }, [mode, hasFinishedCurrentRound, dailyDate]);
if (isBlocked) {
return <h1>Daily MIXX is not available yet. Check back soon!</h1>;
diff --git a/src/components/Player/index.tsx b/src/components/Player/index.tsx
index 841d256..bbf35b0 100644
--- a/src/components/Player/index.tsx
+++ b/src/components/Player/index.tsx
@@ -33,6 +33,7 @@ export function Player({ currentTry }: Props) {
const [play, setPlay] = React.useState(false);
const [currentTime, setCurrentTime] = React.useState(0);
const [isReady, setIsReady] = React.useState(false);
+ const [isUnavailable, setIsUnavailable] = React.useState(false);
const [volume, setVolume] = React.useState(loadVolume);
const CDN_URL =
@@ -65,12 +66,21 @@ export function Player({ currentTry }: Props) {
);
React.useEffect(() => {
+ setIsReady(false);
+ setIsUnavailable(false);
+
const audio = new Audio(`${CDN_URL}/${dateString}.mp3`);
audio.volume = loadVolume();
audioRef.current = audio;
audio.addEventListener("loadeddata", () => {
setIsReady(true);
+ setIsUnavailable(false);
+ });
+
+ audio.addEventListener("error", () => {
+ setIsReady(false);
+ setIsUnavailable(true);
});
audio.addEventListener("timeupdate", () => {
@@ -138,7 +148,9 @@ export function Player({ currentTry }: Props) {
return (
<>
- {isReady ? (
+ {isUnavailable ? (
+ <p>We are still generating the audio! Come back later.</p>
+ ) : isReady ? (
<>
<Styled.ProgressBackground>
{currentTime !== 0 && (
diff --git a/src/helpers/fetchSolution.ts b/src/helpers/fetchSolution.ts
index 10c4fa1..e3010fe 100644
--- a/src/helpers/fetchSolution.ts
+++ b/src/helpers/fetchSolution.ts
@@ -20,25 +20,32 @@ function xor(data: Uint8Array, key: Uint8Array): Uint8Array {
return output;
}
-function getObfuscationKey(): Uint8Array {
- const date = new Date().toISOString().split('T')[0];
+function getObfuscationKey(date = new Date().toISOString().split('T')[0]): Uint8Array {
return new TextEncoder().encode(SALT + date);
}
-function decryptResponse(data: string): Song {
- const obfuscationKey = getObfuscationKey();
+function decryptResponse(data: string, date?: string): Song {
+ const obfuscationKey = getObfuscationKey(date);
const obfuscatedBytes = hexToBytes(data);
const decrypted = xor(obfuscatedBytes, obfuscationKey);
return JSON.parse(new TextDecoder().decode(decrypted)) as Song;
}
-export async function getDailySolution(): Promise<Song> {
+export interface DailySolution {
+ date: string;
+ song: Song;
+}
+
+export async function getDailySolution(): Promise<DailySolution> {
const solutionData = await fetch(`${API_URL}/today`);
if (!solutionData.ok) {
throw new Error(`Failed to fetch solution: ${solutionData.statusText}`);
}
- const { data } = await solutionData.json();
- return decryptResponse(data);
+ const { data, date } = await solutionData.json();
+ return {
+ date,
+ song: decryptResponse(data, date),
+ };
}
export async function getSelectSolution(): Promise<Song> {
diff --git a/src/pages/DailyPage.tsx b/src/pages/DailyPage.tsx
index 5033366..24ac20c 100644
--- a/src/pages/DailyPage.tsx
+++ b/src/pages/DailyPage.tsx
@@ -1,8 +1,7 @@
import React from "react";
-import { Song } from "../types/song";
import { GuessType } from "../types/guess";
-import { getDailySolution } from "../helpers/fetchSolution";
+import { DailySolution, getDailySolution } from "../helpers/fetchSolution";
import { useGameState } from "../hooks/useGameState";
import { Header, InfoPopUp, Game, Footer } from "../components";
@@ -10,7 +9,7 @@ import { Header, InfoPopUp, Game, Footer } from "../components";
import * as Styled from "../app.styled";
export function DailyPage() {
- const [todaysSolution, setTodaysSolution] = React.useState<Song | null>(null);
+ const [todaysSolution, setTodaysSolution] = React.useState<DailySolution | null>(null);
const firstRun = localStorage.getItem("firstRun") === null;
@@ -70,7 +69,7 @@ export function DailyPage() {
didGuess,
skip,
guess,
- } = useGameState({ solution: todaysSolution, persist: true });
+ } = useGameState({ solution: todaysSolution?.song ?? null, persist: true });
const [isInfoPopUpOpen, setIsInfoPopUpOpen] =
React.useState<boolean>(firstRun);
@@ -98,7 +97,8 @@ export function DailyPage() {
<Game
guesses={guesses}
didGuess={didGuess}
- todaysSolution={todaysSolution}
+ todaysSolution={todaysSolution.song}
+ dailyDate={todaysSolution.date}
currentTry={currentTry}
setSelectedSong={setSelectedSong}
skip={skip}
diff --git a/src/react-app-env.d.ts b/src/react-app-env.d.ts
index 6431bc5..f6733c9 100644
--- a/src/react-app-env.d.ts
+++ b/src/react-app-env.d.ts
@@ -1 +1,10 @@
/// <reference types="react-scripts" />
+declare global {
+ interface ImportMetaEnv {
+ readonly VITE_CDN_URL?: string;
+ }
+
+ interface ImportMeta {
+ readonly env: ImportMetaEnv;
+ }
+}
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage