diff --git a/src/components/layout/MobileNav.tsx b/src/components/layout/MobileNav.tsx
index d286e59..04f673e 100644
--- a/src/components/layout/MobileNav.tsx
+++ b/src/components/layout/MobileNav.tsx
@@ -1,5 +1,5 @@
import { NavLink } from 'react-router-dom'
-import { Home, Compass, Upload, Heart, User } from 'lucide-react'
+import { Home, Compass, Upload, TrendingUp, User, Sparkles } from 'lucide-react'
import { cn } from '@/lib/utils'
import { useAuthStore } from '@/stores/auth'
import { usePlayerStore } from '@/stores/player'
@@ -11,11 +11,11 @@ export function MobileNav() {
const links = [
{ to: '/', icon: Home, label: 'Accueil' },
{ to: '/explore', icon: Compass, label: 'Explorer' },
+ { to: '/trending', icon: TrendingUp, label: 'Tendances' },
...(user
? [
{ to: '/upload', icon: Upload, label: 'Publier' },
- { to: '/favorites', icon: Heart, label: 'Favoris' },
- { to: `/profile/${profile?.username || user.id}`, icon: User, label: 'Profil' },
+ { to: '/for-you', icon: Sparkles, label: 'Pour toi' },
]
: [{ to: '/auth', icon: User, label: 'Connexion' }]),
]
diff --git a/src/components/podcast/PodcastCard.tsx b/src/components/podcast/PodcastCard.tsx
index f9d7f25..035a594 100644
--- a/src/components/podcast/PodcastCard.tsx
+++ b/src/components/podcast/PodcastCard.tsx
@@ -1,4 +1,4 @@
-import { Play, Pause, Heart, MessageCircle, Clock, ListPlus, Download, Check, Loader2, BookmarkPlus } from 'lucide-react'
+import { Play, Pause, Heart, MessageCircle, Clock, ListPlus, Download, Check, Loader2, BookmarkPlus, Headphones } from 'lucide-react'
import { Link } from 'react-router-dom'
import type { Podcast } from '@/types'
import { formatDuration, timeAgo } from '@/lib/utils'
@@ -165,6 +165,10 @@ export function PodcastCard({ podcast, progressPercent: propProgress }: PodcastC
)}
+
+
+ {podcast.plays_count || 0}
+
{podcast.likes_count || 0}
diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx
index 29d5674..00f7551 100644
--- a/src/pages/Home.tsx
+++ b/src/pages/Home.tsx
@@ -8,6 +8,12 @@ import { PodcastCard } from '@/components/podcast/PodcastCard'
import { Button } from '@/components/ui/Button'
import { normalizePodcasts } from '@/lib/utils'
+function formatStat(n: number): string {
+ if (n >= 1000000) return `${(n / 1000000).toFixed(1).replace(/\.0$/, '')}M`
+ if (n >= 1000) return `${(n / 1000).toFixed(1).replace(/\.0$/, '')}k`
+ return n.toString()
+}
+
interface InProgressPodcast extends Podcast {
progressPercent: number
}
@@ -17,6 +23,7 @@ export function Home() {
const [recent, setRecent] = useState([])
const [inProgress, setInProgress] = useState([])
const [loading, setLoading] = useState(true)
+ const [stats, setStats] = useState({ plays: 0, creators: 0, podcasts: 0 })
const { user } = useAuthStore()
const navigate = useNavigate()
@@ -66,6 +73,17 @@ export function Home() {
setInProgress(items)
}
+ // Fetch real stats for hero
+ if (!user) {
+ const [{ count: podcastCount }, { data: playsData }, { count: creatorCount }] = await Promise.all([
+ supabase.from('podcasts').select('*', { count: 'exact', head: true }),
+ supabase.from('podcasts').select('plays_count'),
+ supabase.from('profiles').select('*', { count: 'exact', head: true }),
+ ])
+ const totalPlays = playsData?.reduce((sum: number, p: any) => sum + (p.plays_count || 0), 0) || 0
+ setStats({ plays: totalPlays, creators: creatorCount || 0, podcasts: podcastCount || 0 })
+ }
+
setLoading(false)
}
load()
@@ -73,55 +91,54 @@ export function Home() {
return (
- {/* Hero */}
-
- {/* Decorative blobs */}
-
-
-
+ {/* Hero — only for visitors */}
+ {!user && (
+
+
+
+
-
- {/* Floating mic icon */}
-
-
-
+
+
+
+
-
- Votre voix compte,{' '}
- partagez-la.
-
-
- La plateforme de podcast ou tout le monde peut creer, ecouter et decouvrir des voix authentiques.
-
+
+ Votre voix compte,{' '}
+ partagez-la.
+
+
+ La plateforme de podcast ou tout le monde peut creer, ecouter et decouvrir des voix authentiques.
+
-
-
-
-
+
+
+
+
- {/* Stats */}
-
- {[
- { icon: Headphones, label: 'Ecoutes', value: '2.4k' },
- { icon: Users, label: 'Createurs', value: '128' },
- { icon: Mic, label: 'Podcasts', value: '340' },
- ].map(({ icon: Icon, label, value }) => (
-
-
-
-
{value}
+
+ {[
+ { icon: Headphones, label: 'Ecoutes', value: formatStat(stats.plays) },
+ { icon: Users, label: 'Createurs', value: formatStat(stats.creators) },
+ { icon: Mic, label: 'Podcasts', value: formatStat(stats.podcasts) },
+ ].map(({ icon: Icon, label, value }) => (
+
+
+
+ {value}
+
+
{label}
-
{label}
-
- ))}
+ ))}
+
-
-
+
+ )}
{/* Continue listening */}
{inProgress.length > 0 && (
diff --git a/src/pages/Profile.tsx b/src/pages/Profile.tsx
index c82adb8..d7faaa4 100644
--- a/src/pages/Profile.tsx
+++ b/src/pages/Profile.tsx
@@ -1,6 +1,6 @@
import { useEffect, useState } from 'react'
-import { useParams } from 'react-router-dom'
-import { Users, Headphones } from 'lucide-react'
+import { useParams, useNavigate } from 'react-router-dom'
+import { Users, Headphones, Settings } from 'lucide-react'
import { supabase } from '@/lib/supabase'
import { useAuthStore } from '@/stores/auth'
import type { Profile as ProfileType, Podcast } from '@/types'
@@ -11,6 +11,7 @@ import { PodcastCard } from '@/components/podcast/PodcastCard'
export function Profile() {
const { username } = useParams<{ username: string }>()
const { user } = useAuthStore()
+ const navigate = useNavigate()
const [profile, setProfile] = useState
(null)
const [podcasts, setPodcasts] = useState([])
const [isFollowing, setIsFollowing] = useState(false)
@@ -116,8 +117,8 @@ export function Profile() {
{followingCount} abonnements
- {!isOwn && user && (
-
+
+ {!isOwn && user && (
-
- )}
+ )}
+ {isOwn && (
+
+ )}
+
diff --git a/src/stores/player.ts b/src/stores/player.ts
index 0fd3d85..661d6ad 100644
--- a/src/stores/player.ts
+++ b/src/stores/player.ts
@@ -215,6 +215,7 @@ export const usePlayerStore = create((set, get) => ({
// Resume same podcast (already loaded)
if (current?.id === podcast.id) {
+ // Don't count resume as a new play
if (external && embed?.platform === 'youtube' && ytPlayer) {
try { ytPlayer.playVideo() } catch { /* ignore */ }
startYtProgressTracking()
@@ -241,6 +242,17 @@ export const usePlayerStore = create((set, get) => ({
if (audio) { audio.pause(); audio.removeAttribute('src') }
destroyAll()
+ // Count as a play: increment plays_count via RPC (SECURITY DEFINER bypasses RLS)
+ supabase.rpc('increment_plays', { podcast_id: podcast.id })
+ .then(({ error }) => { if (error) console.error('[player] increment_plays RPC failed — did you run migration 010?', error) })
+ const userId = useAuthStore.getState().user?.id
+ if (userId) {
+ supabase.from('listen_history').upsert(
+ { user_id: userId, podcast_id: podcast.id, progress_seconds: 0, completed: false, listened_at: new Date().toISOString() },
+ { onConflict: 'user_id,podcast_id' }
+ ).then(() => {}, () => {})
+ }
+
// ── YouTube: use IFrame API for full control ──
if (external && embed?.platform === 'youtube') {
set({ audio: null, current: podcast, isPlaying: false, isExternal: true, progress: 0, duration: podcast.duration_seconds || 0 })
diff --git a/supabase/migrations/010_increment_plays.sql b/supabase/migrations/010_increment_plays.sql
new file mode 100644
index 0000000..7acf2a9
--- /dev/null
+++ b/supabase/migrations/010_increment_plays.sql
@@ -0,0 +1,9 @@
+-- RPC to atomically increment plays_count
+CREATE OR REPLACE FUNCTION increment_plays(podcast_id uuid)
+RETURNS void AS $$
+BEGIN
+ UPDATE podcasts
+ SET plays_count = plays_count + 1
+ WHERE id = podcast_id;
+END;
+$$ LANGUAGE plpgsql SECURITY DEFINER;