diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml
new file mode 100644
index 0000000..f573c55
--- /dev/null
+++ b/.github/workflows/docker-publish.yml
@@ -0,0 +1,49 @@
+name: Build & Publish Docker Image
+
+on:
+ push:
+ branches: [main]
+
+env:
+ REGISTRY: ghcr.io
+ IMAGE_NAME: ${{ github.repository }}
+
+jobs:
+ build-and-push:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ packages: write
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Log in to GitHub Container Registry
+ uses: docker/login-action@v3
+ with:
+ registry: ${{ env.REGISTRY }}
+ username: ${{ github.actor }}
+ password: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Extract metadata (tags, labels)
+ id: meta
+ uses: docker/metadata-action@v5
+ with:
+ images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
+ tags: |
+ type=raw,value=latest
+ type=sha,prefix=sha-
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v3
+
+ - name: Build and push Docker image
+ uses: docker/build-push-action@v5
+ with:
+ context: .
+ push: true
+ tags: ${{ steps.meta.outputs.tags }}
+ labels: ${{ steps.meta.outputs.labels }}
+ cache-from: type=gha
+ cache-to: type=gha,mode=max
diff --git a/docker-compose.yml b/docker-compose.yml
index aede5f0..2aa5f52 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,5 +1,3 @@
-version: '3.8'
-
services:
mongodb:
image: mongo:8
@@ -31,9 +29,7 @@ services:
retries: 5
app:
- build:
- context: .
- dockerfile: Dockerfile
+ image: ghcr.io/r0m1k3/podcastic:latest
container_name: podcastic-app
restart: unless-stopped
ports:
diff --git a/frontend/src/components/EpisodeCard.tsx b/frontend/src/components/EpisodeCard.tsx
index 7dd3196..b805a2e 100644
--- a/frontend/src/components/EpisodeCard.tsx
+++ b/frontend/src/components/EpisodeCard.tsx
@@ -1,6 +1,6 @@
import React from 'react';
import { Episode } from '../services/episodeService';
-import { Play, Pause, Calendar, Activity, Check, CheckCircle2, Circle } from 'lucide-react';
+import { Play, Pause, Calendar, Activity, CheckCircle2, Circle } from 'lucide-react';
import { useAudio } from '../context/AudioContext';
interface EpisodeCardProps {
@@ -75,7 +75,6 @@ export default function EpisodeCard({ episode, onPlay, onDetails, onToggleRead }
{isCompleted && (
-
LU
)}
@@ -112,7 +111,7 @@ export default function EpisodeCard({ episode, onPlay, onDetails, onToggleRead }
- {podcast?.author || 'Artiste Premium'}
+ {podcast?.author || ''}
@@ -140,7 +139,7 @@ export default function EpisodeCard({ episode, onPlay, onDetails, onToggleRead }
?
:
}
-
{isCompleted ? 'Lu' : 'Lu ?'}
+
{isCompleted ? 'Lu' : 'Marquer'}
)}
diff --git a/frontend/src/components/Header.tsx b/frontend/src/components/Header.tsx
index 5997c64..54e058e 100644
--- a/frontend/src/components/Header.tsx
+++ b/frontend/src/components/Header.tsx
@@ -40,7 +40,7 @@ export default function Header({ title, subtitle, user, onLogout }: HeaderProps)
{user.username}
- Premium Member
+ Membre
diff --git a/frontend/src/components/Navigation.tsx b/frontend/src/components/Navigation.tsx
index aa5d385..69d3cd7 100644
--- a/frontend/src/components/Navigation.tsx
+++ b/frontend/src/components/Navigation.tsx
@@ -26,7 +26,7 @@ export default function Navigation() {
PODCASTIC
- Premium Audio
+ Your audio space
diff --git a/frontend/src/pages/Dashboard.tsx b/frontend/src/pages/Dashboard.tsx
index 23eb70d..e232be9 100644
--- a/frontend/src/pages/Dashboard.tsx
+++ b/frontend/src/pages/Dashboard.tsx
@@ -1,5 +1,5 @@
import { useEffect, useState } from 'react';
-import { useQuery } from '@tanstack/react-query';
+import { useQuery, useQueryClient } from '@tanstack/react-query';
import { Link } from 'react-router-dom';
import { episodeService, Episode } from '../services/episodeService';
import { authService } from '../services/authService';
@@ -13,6 +13,7 @@ import { Sparkles, Play, Clock, TrendingUp, Loader, RefreshCcw } from 'lucide-re
export default function Dashboard() {
const { playEpisode, currentEpisode, closePlayer, setUserId } = useAudio();
+ const queryClient = useQueryClient();
const [user, setUser] = useState
(null);
const [detailsEpisode, setDetailsEpisode] = useState(null);
const [isDetailsOpen, setIsDetailsOpen] = useState(false);
@@ -41,21 +42,29 @@ export default function Dashboard() {
};
const handleToggleRead = async (episode: Episode, completed: boolean) => {
+ const newProgress = { position: completed ? episode.duration : 0, isCompleted: completed };
+ // Optimistic update: local state + all cached query pages
+ setEpisodes(prev =>
+ prev.map(ep => ep._id === episode._id ? { ...ep, progress: newProgress } : ep)
+ );
+ queryClient.setQueriesData(
+ { queryKey: ['episodes', 'latest'], exact: false },
+ (old: any) => {
+ if (!old?.episodes) return old;
+ return {
+ ...old,
+ episodes: old.episodes.map((ep: Episode) =>
+ ep._id === episode._id ? { ...ep, progress: newProgress } : ep
+ ),
+ };
+ }
+ );
try {
- await episodeService.saveProgress(
- episode._id,
- completed ? episode.duration : 0,
- completed
- );
- setEpisodes(prev =>
- prev.map(ep =>
- ep._id === episode._id
- ? { ...ep, progress: { position: completed ? ep.duration : 0, isCompleted: completed } }
- : ep
- )
- );
+ await episodeService.saveProgress(episode._id, newProgress.position, completed);
} catch (error) {
console.error('Failed to toggle read state:', error);
+ // Revert on error
+ queryClient.invalidateQueries({ queryKey: ['episodes', 'latest'] });
}
};
@@ -163,7 +172,7 @@ export default function Dashboard() {
- Podcastic Premium
+ Podcastic
Découvrez les nouveautés de votre univers audio.
@@ -181,9 +190,9 @@ export default function Dashboard() {
Reprendre : {episodesInProgress[0].title.substring(0, 20)}...
) : (
-
+
- Découvrir
+ Découvrir des podcasts
)}
diff --git a/frontend/src/pages/Login.tsx b/frontend/src/pages/Login.tsx
index c3048c8..8b764d2 100644
--- a/frontend/src/pages/Login.tsx
+++ b/frontend/src/pages/Login.tsx
@@ -61,7 +61,7 @@ export default function Login() {
PODCASTIC
- Expérience Audio Premium
+ Votre espace audio