From e1ec1d942bdeb1103891d2bf7a70b98343d8e0a9 Mon Sep 17 00:00:00 2001 From: Chris-1010 Date: Tue, 23 Sep 2025 01:24:20 +0100 Subject: [PATCH] Add Back Button to `StatView` page Return to the `Home` page by pressing a back button (lucide-react icon) on the `StatView` page This commit also includes some general formatting --- app/package-lock.json | 10 ++ app/package.json | 1 + app/src/pages/StatView.tsx | 210 +++++++++++++++++++------------------ 3 files changed, 118 insertions(+), 103 deletions(-) diff --git a/app/package-lock.json b/app/package-lock.json index 7bb1a2b..7c198e3 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -12,6 +12,7 @@ "idb": "^8.0.3", "lodash": "^4.17.21", "lodash.debounce": "^4.0.8", + "lucide-react": "^0.544.0", "react": "^19.1.1", "react-dom": "^19.1.1", "react-range-slider-input": "^3.2.1", @@ -3177,6 +3178,15 @@ "yallist": "^3.0.2" } }, + "node_modules/lucide-react": { + "version": "0.544.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.544.0.tgz", + "integrity": "sha512-t5tS44bqd825zAW45UQxpG2CvcC4urOwn2TrwSH8u+MjeE+1NnWl6QqeQ/6NdjMqdOygyiT9p3Ev0p1NJykxjw==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/magic-string": { "version": "0.30.17", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", diff --git a/app/package.json b/app/package.json index 403a892..7eec6a5 100644 --- a/app/package.json +++ b/app/package.json @@ -14,6 +14,7 @@ "idb": "^8.0.3", "lodash": "^4.17.21", "lodash.debounce": "^4.0.8", + "lucide-react": "^0.544.0", "react": "^19.1.1", "react-dom": "^19.1.1", "react-range-slider-input": "^3.2.1", diff --git a/app/src/pages/StatView.tsx b/app/src/pages/StatView.tsx index 87a526e..593c08e 100644 --- a/app/src/pages/StatView.tsx +++ b/app/src/pages/StatView.tsx @@ -1,139 +1,143 @@ -import { useEffect, useState } from 'react'; -import { getAllStreams } from '../util/db'; -import { formatSeconds, tsToDate, getDaysBetween, getDaysAfter, dateToTs } from '../util/time'; -import RangeSlider from 'react-range-slider-input'; -import 'react-range-slider-input/dist/style.css'; -import type { stream } from '../model/types'; -import { getFirstStreamDate, getLastStreamDate, getListenedTracks, getListenedArtists } from '../model/parser'; +import { useEffect, useState } from "react"; +import { getAllStreams } from "../util/db"; +import { formatSeconds, tsToDate, getDaysBetween, getDaysAfter, dateToTs } from "../util/time"; +import RangeSlider from "react-range-slider-input"; +import "react-range-slider-input/dist/style.css"; +import type { stream } from "../model/types"; +import { getFirstStreamDate, getLastStreamDate, getListenedTracks, getListenedArtists } from "../model/parser"; +import { useNavigate } from "react-router-dom"; +import { ArrowLeft as Back } from "lucide-react"; const StatView = () => { - const [streams, setStreams] = useState([]); - const [mostListenedSongs, setMostListenedSongs] = useState([]); - const [mostListenedArtists, setMostListenedArtists] = useState([]); + const [streams, setStreams] = useState([]); + const [mostListenedSongs, setMostListenedSongs] = useState([]); + const [mostListenedArtists, setMostListenedArtists] = useState([]); - const [firstStreamDate, setFirstStreamDate] = useState(); - const [lastStreamDate, setLastStreamDate] = useState(); + const [firstStreamDate, setFirstStreamDate] = useState(); + const [lastStreamDate, setLastStreamDate] = useState(); - const [dateRange, setDateRange] = useState<[Date, Date]>([new Date(), new Date()]); + const [dateRange, setDateRange] = useState<[Date, Date]>([new Date(), new Date()]); + const navigate = useNavigate(); - useEffect(() => { - getAllStreams().then((streams) => { - setStreams(streams); - if (streams.length > 0) { - const firstDate = tsToDate(getFirstStreamDate(streams)); - const lastDate = tsToDate(getLastStreamDate(streams)); - setFirstStreamDate(firstDate); - setLastStreamDate(lastDate); - setDateRange([firstDate, lastDate]); - } - }).catch((error) => { - console.error('Error fetching streams:', error); - }); - }, []); + useEffect(() => { + getAllStreams() + .then((streams) => { + setStreams(streams); + if (streams.length > 0) { + const firstDate = tsToDate(getFirstStreamDate(streams)); + const lastDate = tsToDate(getLastStreamDate(streams)); + setFirstStreamDate(firstDate); + setLastStreamDate(lastDate); + setDateRange([firstDate, lastDate]); + } + }) + .catch((error) => { + console.error("Error fetching streams:", error); + }); + }, []); - useEffect(() => { - setMostListenedSongs(getListenedTracks(streams, dateToTs(dateRange[0]), dateToTs(dateRange[1]), 100)); - setMostListenedArtists(getListenedArtists(streams, dateToTs(dateRange[0]), dateToTs(dateRange[1]), 100)); - }, [dateRange]); + useEffect(() => { + setMostListenedSongs(getListenedTracks(streams, dateToTs(dateRange[0]), dateToTs(dateRange[1]), 100)); + setMostListenedArtists(getListenedArtists(streams, dateToTs(dateRange[0]), dateToTs(dateRange[1]), 100)); + }, [dateRange]); - if (streams.length === 0 || !firstStreamDate || !lastStreamDate) { - return
Loading...
; - } + if (streams.length === 0 || !firstStreamDate || !lastStreamDate) { + return
Loading...
; + } - return ( -
+ return ( +
+ navigate(-1)} />
-

Track Statistics

+

Track Statistics

    - {mostListenedSongs.map((track, index) => ( + {mostListenedSongs.map((track, index) => (
  • {index + 1}. {track.master_metadata_track_name} -
    +
{formatSeconds(track.ms_played / 1000)}
- - ))} - -
+ + ))} + +
-

Artist Statistics

+

Artist Statistics

    - {mostListenedArtists.map((artist, index) => ( + {mostListenedArtists.map((artist, index) => (
  • {index + 1}. {artist.master_metadata_album_artist_name} -
    +
{formatSeconds(artist.ms_played / 1000)}
- - ))} - -
+ + ))} + + + - - - { - const startDate = getDaysAfter(firstStreamDate, value[0]); - const endDate = getDaysAfter(firstStreamDate, value[1]); - setDateRange([startDate, endDate]); - }} - className="w-full" - /> + onInput={(value) => { + const startDate = getDaysAfter(firstStreamDate, value[0]); + const endDate = getDaysAfter(firstStreamDate, value[1]); + setDateRange([startDate, endDate]); + }} + className="w-full" + /> -
- { - if (new Date(e.target.value) > dateRange[1]) { - alert('Start date cannot be after end date'); - return; - } +
+ { + if (new Date(e.target.value) > dateRange[1]) { + alert("Start date cannot be after end date"); + return; + } + // check if valid date + if (isNaN(new Date(e.target.value).getTime())) { + return; + } - // check if valid date - if (isNaN(new Date(e.target.value).getTime())) { - return; - } - - setDateRange([new Date(e.target.value), dateRange[1]]) - }} - className="mr-2" - /> - { - if (new Date(e.target.value) < dateRange[0]) { - alert('End date cannot be before start date'); - return; - } + setDateRange([new Date(e.target.value), dateRange[1]]); + }} + className="mr-2" + /> + { + if (new Date(e.target.value) < dateRange[0]) { + alert("End date cannot be before start date"); + return; + } - // check if valid date - if (isNaN(new Date(e.target.value).getTime())) { - return; - } + // check if valid date + if (isNaN(new Date(e.target.value).getTime())) { + return; + } - setDateRange([dateRange[0], new Date(e.target.value)]); - }} - className="ml-2" - /> + setDateRange([dateRange[0], new Date(e.target.value)]); + }} + className="ml-2" + /> -

({getDaysBetween(dateRange[0], dateRange[1])} days)

-
-
- ) -} +

({getDaysBetween(dateRange[0], dateRange[1])} days)

+ + + ); +}; -export default StatView; \ No newline at end of file +export default StatView;