feat: STEM Virtual Lab, English TTS companion, dynamic textbooks and zero-demo video fallback

This commit is contained in:
Hamza-Ayed
2026-09-03 17:37:15 +03:00
parent 445a1aa0e3
commit 58c3c48f69
19 changed files with 2162 additions and 258 deletions
@@ -1,4 +1,5 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../core/utils/app_logger.dart';
import '../../data/models/socratic_checkpoint_model.dart';
import '../../data/models/subject_model.dart';
@@ -81,11 +82,20 @@ class VideoPlaybackCubit extends Cubit<VideoPlaybackState> {
emit(VideoPlaybackLoading());
try {
final playback = await _repo.getLessonPlayback(lesson.markdownFilePath ?? lesson.id, subjectId: subject?.id);
// Load saved resume position
int resumePos = playback.lastPositionSeconds ?? 0;
try {
final prefs = await SharedPreferences.getInstance();
final localPos = prefs.getInt('saved_video_pos_${lesson.id}') ?? 0;
if (localPos > resumePos) resumePos = localPos;
} catch (_) {}
emit(VideoPlaybackReady(
playbackData: playback,
lessonItem: lesson,
subject: subject,
currentPositionSeconds: 0,
currentPositionSeconds: resumePos,
isPlaying: true,
));
} catch (e) {
@@ -155,13 +165,20 @@ class VideoPlaybackCubit extends Cubit<VideoPlaybackState> {
Future<void> saveProgress({required int positionSeconds, required int watchedSeconds}) async {
final currentState = state;
if (currentState is! VideoPlaybackReady || currentState.playbackData.lessonId <= 0) return;
if (currentState is! VideoPlaybackReady) return;
try {
await _repo.saveProgress(
lessonId: currentState.playbackData.lessonId,
positionSeconds: positionSeconds,
watchedSeconds: watchedSeconds,
);
final prefs = await SharedPreferences.getInstance();
if (currentState.lessonItem != null) {
await prefs.setInt('saved_video_pos_${currentState.lessonItem!.id}', positionSeconds);
}
if (currentState.playbackData.lessonId > 0) {
await prefs.setInt('saved_video_pos_${currentState.playbackData.lessonId}', positionSeconds);
await _repo.saveProgress(
lessonId: currentState.playbackData.lessonId,
positionSeconds: positionSeconds,
watchedSeconds: watchedSeconds,
);
}
} catch (e) {
AppLogger.log('Progress sync deferred: $e', tag: 'VIDEO_CUBIT');
}