diff --git a/client/app/components/rewind/Rewind.tsx b/client/app/components/rewind/Rewind.tsx
index 8e1908c..2553b35 100644
--- a/client/app/components/rewind/Rewind.tsx
+++ b/client/app/components/rewind/Rewind.tsx
@@ -8,9 +8,16 @@ interface Props {
}
export default function Rewind(props: Props) {
- const artistimg = props.stats.top_artists[0].image;
- const albumimg = props.stats.top_albums[0].image;
- const trackimg = props.stats.top_tracks[0].image;
+ const artistimg = props.stats.top_artists[0]?.image;
+ const albumimg = props.stats.top_albums[0]?.image;
+ const trackimg = props.stats.top_tracks[0]?.image;
+ if (
+ !props.stats.top_artists[0] ||
+ !props.stats.top_albums[0] ||
+ !props.stats.top_tracks[0]
+ ) {
+ return
{props.stats.title}
diff --git a/client/app/routes/RewindPage.tsx b/client/app/routes/RewindPage.tsx
index b14e5fc..2d60697 100644
--- a/client/app/routes/RewindPage.tsx
+++ b/client/app/routes/RewindPage.tsx
@@ -1,52 +1,199 @@
import Rewind from "~/components/rewind/Rewind";
import type { Route } from "./+types/Home";
-import { type RewindStats } from "api/api";
-import { useState } from "react";
+import { imageUrl, type RewindStats } from "api/api";
+import { useEffect, useState } from "react";
import type { LoaderFunctionArgs } from "react-router";
import { useLoaderData } from "react-router";
import { getRewindYear } from "~/utils/utils";
+import { useNavigate } from "react-router";
+import { average } from "color.js";
+import { ChevronLeft, ChevronRight } from "lucide-react";
+
+// TODO: Bind year and month selectors to what data actually exists
+
+const months = [
+ "Full Year",
+ "January",
+ "February",
+ "March",
+ "April",
+ "May",
+ "June",
+ "July",
+ "August",
+ "September",
+ "October",
+ "November",
+ "December",
+];
export async function clientLoader({ request }: LoaderFunctionArgs) {
const url = new URL(request.url);
- const year = url.searchParams.get("year") || getRewindYear();
+ const year = parseInt(url.searchParams.get("year") || "0") || getRewindYear();
+ const month = parseInt(url.searchParams.get("month") || "0");
- const res = await fetch(`/apis/web/v1/summary?year=${year}`);
+ const res = await fetch(`/apis/web/v1/summary?year=${year}&month=${month}`);
if (!res.ok) {
throw new Response("Failed to load summary", { status: 500 });
}
const stats: RewindStats = await res.json();
- stats.title = `Your ${year} Rewind`;
+ stats.title = `Your ${month === 0 ? "" : months[month]} ${year} Rewind`;
return { stats };
}
-export function meta({}: Route.MetaArgs) {
- return [
- { title: `Rewind - Koito` },
- { name: "description", content: "Rewind - Koito" },
- ];
-}
-
export default function RewindPage() {
+ const currentParams = new URLSearchParams(location.search);
+ let year = parseInt(currentParams.get("year") || "0");
+ let month = parseInt(currentParams.get("month") || "0");
+ const navigate = useNavigate();
const [showTime, setShowTime] = useState(false);
const { stats: stats } = useLoaderData<{ stats: RewindStats }>();
+
+ const [bgColor, setBgColor] = useState
("(--color-bg)");
+
+ useEffect(() => {
+ if (!stats.top_artists[0]) return;
+
+ const img = (stats.top_artists[0] as any)?.image;
+ if (!img) return;
+
+ average(imageUrl(img, "small"), { amount: 1 }).then((color) => {
+ setBgColor(`rgba(${color[0]},${color[1]},${color[2]},0.4)`);
+ });
+ }, [stats]);
+
+ const updateParams = (params: Record) => {
+ const nextParams = new URLSearchParams(location.search);
+
+ for (const key in params) {
+ const val = params[key];
+
+ if (val !== null && val !== "0") {
+ nextParams.set(key, val);
+ } else {
+ nextParams.delete(key);
+ }
+ }
+
+ const url = `/rewind?${nextParams.toString()}`;
+
+ navigate(url, { replace: false });
+ };
+
+ const navigateMonth = (direction: "prev" | "next") => {
+ if (direction === "next") {
+ if (month === 12) {
+ month = 0;
+ } else {
+ month += 1;
+ }
+ } else {
+ if (month === 0) {
+ month = 12;
+ } else {
+ month -= 1;
+ }
+ }
+
+ updateParams({
+ year: year.toString(),
+ month: month.toString(),
+ });
+ };
+ const navigateYear = (direction: "prev" | "next") => {
+ if (direction === "next") {
+ year += 1;
+ } else {
+ year -= 1;
+ }
+
+ updateParams({
+ year: year.toString(),
+ month: month.toString(),
+ });
+ };
+
+ const pgTitle = `${stats.title} - Koito`;
+
return (
-
- {stats.title} - Koito
-
-
-
-
-
-
setShowTime(!showTime)}
- >
+
+
+
{pgTitle}
+
+
+
+ {stats !== undefined && (
+
+ )}
+
+
+
+
+ {months[month]}
+
+
+
+
+
+
{year}
+
+
+
+
+
+ setShowTime(!showTime)}
+ >
+
- {stats !== undefined &&
}
-
+
);
}