From 8c62d81f046c2ab803f63b589b2d3a7ed1e60ac4 Mon Sep 17 00:00:00 2001 From: Hamza-Ayed Date: Wed, 2 Sep 2026 08:59:54 +0300 Subject: [PATCH] Update Saqel Platform: 2026-09-02 08:59:54 --- .../player/socratic_video_player_screen.dart | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/apps/student_app/lib/presentation/screens/player/socratic_video_player_screen.dart b/apps/student_app/lib/presentation/screens/player/socratic_video_player_screen.dart index 7d8f811..182026b 100644 --- a/apps/student_app/lib/presentation/screens/player/socratic_video_player_screen.dart +++ b/apps/student_app/lib/presentation/screens/player/socratic_video_player_screen.dart @@ -31,6 +31,7 @@ class _SocraticVideoPlayerScreenState extends State w late AnimationController _watermarkController; VideoPlayerController? _videoController; bool _isVideoInitialized = false; + int _lastSyncedPosition = -1; @override void initState() { @@ -43,14 +44,21 @@ class _SocraticVideoPlayerScreenState extends State w duration: const Duration(seconds: 18), )..repeat(reverse: true); - // Read the real player clock and periodically persist progress. + // Keep the UI clock responsive, but sync progress to the API every 15s. _playbackTicker = Timer.periodic(const Duration(seconds: 1), (timer) { final cubit = context.read(); final state = cubit.state; if (state is VideoPlaybackReady && _videoController?.value.isInitialized == true) { final position = _videoController!.value.position.inSeconds; if (state.activeCheckpoint == null) cubit.updatePosition(position); - cubit.saveProgress(positionSeconds: position, watchedSeconds: position); + final duration = _videoController!.value.duration.inSeconds; + final shouldSync = _lastSyncedPosition < 0 || + position - _lastSyncedPosition >= 15 || + (duration > 0 && position >= duration - 1); + if (shouldSync && position != _lastSyncedPosition) { + _lastSyncedPosition = position; + cubit.saveProgress(positionSeconds: position, watchedSeconds: position); + } } }); } @@ -104,7 +112,7 @@ class _SocraticVideoPlayerScreenState extends State w Icon(CupertinoIcons.shield_lefthalf_fill, color: AppColors.saqelCyan, size: 14), SizedBox(width: 6), Text( - 'حماية جنائية DRM', + 'محتوى محمي', style: TextStyle(color: AppColors.saqelCyan, fontSize: 11, fontWeight: FontWeight.w700), ), ], @@ -150,7 +158,7 @@ class _SocraticVideoPlayerScreenState extends State w onOptionSelected: (opt) { final isCorrect = context.read().submitCheckpointAnswer(opt); if (isCorrect) { - SaqelToast.showSuccess(context, 'إجابة نموذجية! تم تعزيز الجاهزية (+0.5%)', title: 'فحص الفهم السقراطي ✨'); + SaqelToast.showSuccess(context, 'إجابة صحيحة، تم تسجيل تقدمك.', title: 'نقاط الفهم ✨'); } else { SaqelToast.showError(context, 'تم إرجاع الفيديو لإعادة الاستماع للنقطة الجوهرية 🔄', title: 'سد الثغرة العلاجية'); } @@ -321,7 +329,7 @@ class _SocraticVideoPlayerScreenState extends State w ), const SizedBox(width: 8), Text( - state.isPlaying ? 'جاري البث والشرح التفاعلي ⚡' : 'الشرح متوقف مؤقتاً', + state.isPlaying ? 'جاري الشرح التفاعلي ⚡' : 'الفيديو متوقف مؤقتاً', style: const TextStyle( color: AppColors.textSecondaryDark, fontSize: 12, @@ -350,7 +358,7 @@ class _SocraticVideoPlayerScreenState extends State w Icon(CupertinoIcons.sparkles, color: AppColors.saqelCyan, size: 12), SizedBox(width: 6), Text( - 'الشرح السقراطي المعزز 🤖', + 'نقاط الفهم', style: TextStyle(color: AppColors.saqelCyan, fontSize: 11, fontWeight: FontWeight.w700), ), ], @@ -404,7 +412,7 @@ class _SocraticVideoPlayerScreenState extends State w child: Column( mainAxisSize: MainAxisSize.min, children: [ - // Socratic Timeline Progress Bar with Checkpoint Markers + // Understanding checkpoints on the timeline Stack( alignment: Alignment.centerLeft, children: [