feat: persist learner progress and comments
This commit is contained in:
@@ -19,7 +19,7 @@ import {
|
||||
} from 'lucide-react';
|
||||
import { Course, Comment, Lesson, Attachment } from '../types';
|
||||
import { useAuth } from '../context/AuthContext';
|
||||
import { getComments, saveComment, saveProgress, incrementViews } from '../services/db';
|
||||
import { getComments, getCompletedLessonIds, saveComment, saveLessonCompletion, incrementViews } from '../services/db';
|
||||
import { LoginModal } from './LoginModal';
|
||||
import { MaterialCard } from './MaterialCard';
|
||||
|
||||
@@ -32,7 +32,6 @@ export const CoursePlayerModal: React.FC<CoursePlayerModalProps> = ({ course, on
|
||||
const { user } = useAuth();
|
||||
const [comments, setComments] = useState<Comment[]>([]);
|
||||
const [newComment, setNewComment] = useState('');
|
||||
const [currentProgress, setCurrentProgress] = useState(0);
|
||||
const [currentLesson, setCurrentLesson] = useState<Lesson | null>(null);
|
||||
const [completedLessonIds, setCompletedLessonIds] = useState<string[]>([]);
|
||||
const [showLoginPrompt, setShowLoginPrompt] = useState(false);
|
||||
@@ -41,28 +40,18 @@ export const CoursePlayerModal: React.FC<CoursePlayerModalProps> = ({ course, on
|
||||
|
||||
const videoRef = useRef<HTMLVideoElement>(null);
|
||||
|
||||
// Load course data & completed lessons from storage
|
||||
// Load course data and this learner's saved state from PostgreSQL.
|
||||
useEffect(() => {
|
||||
if (course) {
|
||||
incrementViews(course.id);
|
||||
getComments(course.id).then(setComments);
|
||||
setCurrentProgress(course.progress || 0);
|
||||
|
||||
// Load completed lessons from localStorage
|
||||
const storageKey = `course_${course.id}_completed_lessons`;
|
||||
const savedCompleted = localStorage.getItem(storageKey);
|
||||
if (savedCompleted) {
|
||||
try {
|
||||
setCompletedLessonIds(JSON.parse(savedCompleted));
|
||||
} catch (e) {
|
||||
setCompletedLessonIds([]);
|
||||
}
|
||||
} else if (course.progress && course.progress > 0 && course.lessons && course.lessons.length > 0) {
|
||||
// Initial demo completion based on progress
|
||||
const count = Math.ceil((course.progress / 100) * course.lessons.length);
|
||||
const initialCompleted = course.lessons.slice(0, count).map(l => l.id);
|
||||
setCompletedLessonIds(initialCompleted);
|
||||
localStorage.setItem(storageKey, JSON.stringify(initialCompleted));
|
||||
setCompletedLessonIds([]);
|
||||
if (user) {
|
||||
getComments(course.id).then(setComments).catch(() => setComments([]));
|
||||
getCompletedLessonIds(course.id).then((completed) => {
|
||||
setCompletedLessonIds(completed);
|
||||
}).catch(() => setCompletedLessonIds([]));
|
||||
} else {
|
||||
setComments([]);
|
||||
}
|
||||
|
||||
// Select first lesson by default
|
||||
@@ -70,28 +59,28 @@ export const CoursePlayerModal: React.FC<CoursePlayerModalProps> = ({ course, on
|
||||
setCurrentLesson(course.lessons[0]);
|
||||
}
|
||||
}
|
||||
}, [course]);
|
||||
}, [course, user]);
|
||||
|
||||
// Persist completed lessons and recalculate progress
|
||||
const updateCompletedLessons = (newCompleted: string[]) => {
|
||||
setCompletedLessonIds(newCompleted);
|
||||
if (course) {
|
||||
localStorage.setItem(`course_${course.id}_completed_lessons`, JSON.stringify(newCompleted));
|
||||
if (course.lessons && course.lessons.length > 0) {
|
||||
const percent = Math.round((newCompleted.length / course.lessons.length) * 100);
|
||||
setCurrentProgress(percent);
|
||||
saveProgress(course.id, percent);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const toggleLessonCompleted = (lessonId: string) => {
|
||||
if (!user) {
|
||||
setShowLoginPrompt(true);
|
||||
return;
|
||||
}
|
||||
if (completedLessonIds.includes(lessonId)) {
|
||||
const updated = completedLessonIds.filter(id => id !== lessonId);
|
||||
updateCompletedLessons(updated);
|
||||
saveLessonCompletion(lessonId, false).catch(() => updateCompletedLessons(completedLessonIds));
|
||||
} else {
|
||||
const updated = [...completedLessonIds, lessonId];
|
||||
updateCompletedLessons(updated);
|
||||
saveLessonCompletion(lessonId, true).catch(() => updateCompletedLessons(completedLessonIds));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -135,18 +124,19 @@ export const CoursePlayerModal: React.FC<CoursePlayerModalProps> = ({ course, on
|
||||
e.preventDefault();
|
||||
if (!newComment.trim() || !course) return;
|
||||
|
||||
const comment: Comment = {
|
||||
id: Date.now().toString(),
|
||||
const comment = {
|
||||
courseId: course.id,
|
||||
lessonId: currentLesson?.id,
|
||||
user: user?.name || 'Aluno Convidado',
|
||||
text: newComment,
|
||||
date: new Date().toLocaleDateString('pt-BR')
|
||||
};
|
||||
|
||||
await saveComment(comment);
|
||||
setComments([comment, ...comments]);
|
||||
setNewComment('');
|
||||
try {
|
||||
const savedComment = await saveComment(comment);
|
||||
setComments([savedComment, ...comments]);
|
||||
setNewComment('');
|
||||
} catch {
|
||||
// The server remains the source of truth; leave the text intact for a retry.
|
||||
}
|
||||
};
|
||||
|
||||
const changeLesson = (lesson: Lesson) => {
|
||||
|
||||
Reference in New Issue
Block a user