fix: use git commit SHA for versioning, fix audio playback resume, remove all streak localStorage code
This commit is contained in:
@@ -115,8 +115,9 @@ export const AudioPlayerProvider: React.FC<{ children: React.ReactNode }> = ({ c
|
||||
if (savedCurrentTrack) {
|
||||
try {
|
||||
const track = JSON.parse(savedCurrentTrack);
|
||||
// Clear autoPlay flag when loading from localStorage to prevent auto-play on refresh
|
||||
track.autoPlay = false;
|
||||
// Check if there's a saved playback position - if so, user was likely playing
|
||||
const savedTime = localStorage.getItem('navidrome-current-track-time');
|
||||
track.autoPlay = savedTime !== null && parseFloat(savedTime) > 0;
|
||||
setCurrentTrack(track);
|
||||
} catch (error) {
|
||||
console.error('Failed to parse saved current track:', error);
|
||||
@@ -230,40 +231,6 @@ export const AudioPlayerProvider: React.FC<{ children: React.ReactNode }> = ({ c
|
||||
|
||||
if (currentTrack) {
|
||||
setPlayedTracks((prev) => [...prev, currentTrack]);
|
||||
|
||||
// Record the play for listening streak
|
||||
// This will store timestamp with the track play
|
||||
try {
|
||||
const today = new Date().toISOString().split('T')[0];
|
||||
const streakData = localStorage.getItem('navidrome-streak-data');
|
||||
|
||||
if (streakData) {
|
||||
const parsedData = JSON.parse(streakData);
|
||||
const todayData = parsedData[today] || {
|
||||
date: today,
|
||||
tracks: 0,
|
||||
uniqueArtists: [],
|
||||
uniqueAlbums: [],
|
||||
totalListeningTime: 0
|
||||
};
|
||||
|
||||
// Update today's listening data
|
||||
todayData.tracks += 1;
|
||||
if (!todayData.uniqueArtists.includes(currentTrack.artistId)) {
|
||||
todayData.uniqueArtists.push(currentTrack.artistId);
|
||||
}
|
||||
if (!todayData.uniqueAlbums.includes(currentTrack.albumId)) {
|
||||
todayData.uniqueAlbums.push(currentTrack.albumId);
|
||||
}
|
||||
todayData.totalListeningTime += currentTrack.duration;
|
||||
|
||||
// Save updated data
|
||||
parsedData[today] = todayData;
|
||||
localStorage.setItem('navidrome-streak-data', JSON.stringify(parsedData));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to update listening streak data:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Set autoPlay flag on the track
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useListeningStreak } from '@/hooks/use-listening-streak';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { Flame } from 'lucide-react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
|
||||
export default function CompactListeningStreak() {
|
||||
const { stats, hasListenedToday, getStreakEmoji } = useListeningStreak();
|
||||
const [animate, setAnimate] = useState(false);
|
||||
|
||||
// Trigger animation when streak increases
|
||||
useEffect(() => {
|
||||
if (stats.currentStreak > 0) {
|
||||
setAnimate(true);
|
||||
const timer = setTimeout(() => setAnimate(false), 1000);
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
}, [stats.currentStreak]);
|
||||
|
||||
const hasCompletedToday = hasListenedToday();
|
||||
const streakEmoji = getStreakEmoji();
|
||||
|
||||
// Only show if the streak is 3 days or more
|
||||
if (stats.currentStreak < 3) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Card className="mb-4">
|
||||
<CardContent className="p-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<Flame className={cn(
|
||||
"w-5 h-5",
|
||||
hasCompletedToday ? "text-amber-500" : "text-muted-foreground"
|
||||
)} />
|
||||
<AnimatePresence>
|
||||
<motion.div
|
||||
key={stats.currentStreak}
|
||||
initial={{ scale: animate ? 0.8 : 1 }}
|
||||
animate={{ scale: 1 }}
|
||||
className="flex items-center"
|
||||
>
|
||||
<span className="text-xl font-bold">
|
||||
{stats.currentStreak}
|
||||
</span>
|
||||
<span className="ml-1 text-sm text-muted-foreground">
|
||||
day streak
|
||||
</span>
|
||||
{streakEmoji && (
|
||||
<motion.span
|
||||
className="ml-1 text-xl"
|
||||
animate={{ rotate: animate ? [0, 15, -15, 0] : 0 }}
|
||||
>
|
||||
{streakEmoji}
|
||||
</motion.span>
|
||||
)}
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
<div className="text-sm text-muted-foreground">
|
||||
{hasCompletedToday ? "Today's goal complete!" : "Keep listening!"}
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useListeningStreak } from '@/hooks/use-listening-streak';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Progress } from '@/components/ui/progress';
|
||||
import { Flame, Calendar, Clock, Music, Disc, User2 } from 'lucide-react';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
export default function ListeningStreakCard() {
|
||||
const { stats, hasListenedToday, getStreakEmoji, getTodaySummary, streakThresholds } = useListeningStreak();
|
||||
const [animate, setAnimate] = useState(false);
|
||||
|
||||
// Trigger animation when streak increases
|
||||
useEffect(() => {
|
||||
if (stats.currentStreak > 0) {
|
||||
setAnimate(true);
|
||||
const timer = setTimeout(() => setAnimate(false), 1000);
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
}, [stats.currentStreak]);
|
||||
|
||||
const todaySummary = getTodaySummary();
|
||||
const hasCompletedToday = hasListenedToday();
|
||||
|
||||
// Calculate progress towards today's goal
|
||||
const trackProgress = Math.min(100, (todaySummary.tracks / streakThresholds.tracks) * 100);
|
||||
const timeInMinutes = parseInt(todaySummary.time.replace('m', ''), 10) || 0;
|
||||
const timeThresholdMinutes = Math.floor(streakThresholds.time / 60);
|
||||
const timeProgress = Math.min(100, (timeInMinutes / timeThresholdMinutes) * 100);
|
||||
|
||||
// Overall progress (highest of the two metrics)
|
||||
const overallProgress = Math.max(trackProgress, timeProgress);
|
||||
|
||||
return (
|
||||
<Card className="mb-6 break-inside-avoid py-5">
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<Flame className={cn(
|
||||
"w-5 h-5 transition-all",
|
||||
hasCompletedToday ? "text-amber-500" : "text-muted-foreground"
|
||||
)} />
|
||||
<span>Listening Streak</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Calendar className="w-4 h-4 text-muted-foreground" />
|
||||
<span className="text-sm font-normal text-muted-foreground">
|
||||
{stats.totalDaysListened} days
|
||||
</span>
|
||||
</div>
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="flex flex-col items-center py-2">
|
||||
<AnimatePresence>
|
||||
<motion.div
|
||||
key={stats.currentStreak}
|
||||
initial={{ scale: animate ? 0.5 : 1 }}
|
||||
animate={{ scale: 1 }}
|
||||
exit={{ scale: 0.5 }}
|
||||
className="relative mb-2"
|
||||
>
|
||||
<div className="text-5xl font-bold text-center">
|
||||
{stats.currentStreak}
|
||||
</div>
|
||||
<div className="text-sm text-center text-muted-foreground">
|
||||
day{stats.currentStreak !== 1 ? 's' : ''} streak
|
||||
</div>
|
||||
{getStreakEmoji() && (
|
||||
<motion.div
|
||||
className="absolute -top-2 -right-4 text-2xl"
|
||||
animate={{ rotate: animate ? [0, 15, -15, 0] : 0 }}
|
||||
transition={{ duration: 0.5 }}
|
||||
>
|
||||
{getStreakEmoji()}
|
||||
</motion.div>
|
||||
)}
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
|
||||
<div className="w-full mt-4">
|
||||
<div className="flex justify-between items-center text-sm mb-1">
|
||||
<span className="text-muted-foreground">Today's Progress</span>
|
||||
<span className={cn(
|
||||
hasCompletedToday ? "text-green-500 font-medium" : "text-muted-foreground"
|
||||
)}>
|
||||
{hasCompletedToday ? "Complete!" : "In progress..."}
|
||||
</span>
|
||||
</div>
|
||||
<Progress
|
||||
value={overallProgress}
|
||||
className={cn(
|
||||
"h-2",
|
||||
hasCompletedToday ? "bg-green-500/20" : "",
|
||||
hasCompletedToday ? "[&>div]:bg-green-500" : ""
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4 w-full mt-6">
|
||||
<div className="flex flex-col items-center p-3 rounded-md bg-accent/30">
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<Music className="w-4 h-4 text-muted-foreground" />
|
||||
<span className="text-sm text-muted-foreground">Tracks</span>
|
||||
</div>
|
||||
<span className="text-xl font-semibold">{todaySummary.tracks}</span>
|
||||
<span className="text-xs text-muted-foreground">
|
||||
Goal: {streakThresholds.tracks}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-center p-3 rounded-md bg-accent/30">
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<Clock className="w-4 h-4 text-muted-foreground" />
|
||||
<span className="text-sm text-muted-foreground">Time</span>
|
||||
</div>
|
||||
<span className="text-xl font-semibold">{todaySummary.time}</span>
|
||||
<span className="text-xs text-muted-foreground">
|
||||
Goal: {timeThresholdMinutes}m
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4 w-full mt-4">
|
||||
<div className="flex flex-col items-center p-3 rounded-md bg-accent/20">
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<User2 className="w-4 h-4 text-muted-foreground" />
|
||||
<span className="text-sm text-muted-foreground">Artists</span>
|
||||
</div>
|
||||
<span className="text-xl font-semibold">{todaySummary.artists}</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-center p-3 rounded-md bg-accent/20">
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<Disc className="w-4 h-4 text-muted-foreground" />
|
||||
<span className="text-sm text-muted-foreground">Albums</span>
|
||||
</div>
|
||||
<span className="text-xl font-semibold">{todaySummary.albums}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-4 text-xs text-center text-muted-foreground">
|
||||
{hasCompletedToday ? (
|
||||
<span>You've met your daily listening goal! 🎵</span>
|
||||
) : (
|
||||
<span>Listen to {streakThresholds.tracks} tracks or {timeThresholdMinutes} minutes to continue your streak!</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user