Update Saqel Platform: 2026-09-02 08:59:54

This commit is contained in:
Hamza-Ayed
2026-09-02 08:59:54 +03:00
parent 5ecfaafa48
commit 8c62d81f04
@@ -31,6 +31,7 @@ class _SocraticVideoPlayerScreenState extends State<SocraticVideoPlayerScreen> w
late AnimationController _watermarkController; late AnimationController _watermarkController;
VideoPlayerController? _videoController; VideoPlayerController? _videoController;
bool _isVideoInitialized = false; bool _isVideoInitialized = false;
int _lastSyncedPosition = -1;
@override @override
void initState() { void initState() {
@@ -43,14 +44,21 @@ class _SocraticVideoPlayerScreenState extends State<SocraticVideoPlayerScreen> w
duration: const Duration(seconds: 18), duration: const Duration(seconds: 18),
)..repeat(reverse: true); )..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) { _playbackTicker = Timer.periodic(const Duration(seconds: 1), (timer) {
final cubit = context.read<VideoPlaybackCubit>(); final cubit = context.read<VideoPlaybackCubit>();
final state = cubit.state; final state = cubit.state;
if (state is VideoPlaybackReady && _videoController?.value.isInitialized == true) { if (state is VideoPlaybackReady && _videoController?.value.isInitialized == true) {
final position = _videoController!.value.position.inSeconds; final position = _videoController!.value.position.inSeconds;
if (state.activeCheckpoint == null) cubit.updatePosition(position); 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<SocraticVideoPlayerScreen> w
Icon(CupertinoIcons.shield_lefthalf_fill, color: AppColors.saqelCyan, size: 14), Icon(CupertinoIcons.shield_lefthalf_fill, color: AppColors.saqelCyan, size: 14),
SizedBox(width: 6), SizedBox(width: 6),
Text( Text(
'حماية جنائية DRM', 'محتوى محمي',
style: TextStyle(color: AppColors.saqelCyan, fontSize: 11, fontWeight: FontWeight.w700), style: TextStyle(color: AppColors.saqelCyan, fontSize: 11, fontWeight: FontWeight.w700),
), ),
], ],
@@ -150,7 +158,7 @@ class _SocraticVideoPlayerScreenState extends State<SocraticVideoPlayerScreen> w
onOptionSelected: (opt) { onOptionSelected: (opt) {
final isCorrect = context.read<VideoPlaybackCubit>().submitCheckpointAnswer(opt); final isCorrect = context.read<VideoPlaybackCubit>().submitCheckpointAnswer(opt);
if (isCorrect) { if (isCorrect) {
SaqelToast.showSuccess(context, 'إجابة نموذجية! تم تعزيز الجاهزية (+0.5%)', title: 'فحص الفهم السقراطي ✨'); SaqelToast.showSuccess(context, 'إجابة صحيحة، تم تسجيل تقدمك.', title: 'نقاط الفهم ✨');
} else { } else {
SaqelToast.showError(context, 'تم إرجاع الفيديو لإعادة الاستماع للنقطة الجوهرية 🔄', title: 'سد الثغرة العلاجية'); SaqelToast.showError(context, 'تم إرجاع الفيديو لإعادة الاستماع للنقطة الجوهرية 🔄', title: 'سد الثغرة العلاجية');
} }
@@ -321,7 +329,7 @@ class _SocraticVideoPlayerScreenState extends State<SocraticVideoPlayerScreen> w
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
Text( Text(
state.isPlaying ? 'جاري البث والشرح التفاعلي ⚡' : 'الشرح متوقف مؤقتاً', state.isPlaying ? 'جاري الشرح التفاعلي ⚡' : 'الفيديو متوقف مؤقتاً',
style: const TextStyle( style: const TextStyle(
color: AppColors.textSecondaryDark, color: AppColors.textSecondaryDark,
fontSize: 12, fontSize: 12,
@@ -350,7 +358,7 @@ class _SocraticVideoPlayerScreenState extends State<SocraticVideoPlayerScreen> w
Icon(CupertinoIcons.sparkles, color: AppColors.saqelCyan, size: 12), Icon(CupertinoIcons.sparkles, color: AppColors.saqelCyan, size: 12),
SizedBox(width: 6), SizedBox(width: 6),
Text( Text(
'الشرح السقراطي المعزز 🤖', 'نقاط الفهم',
style: TextStyle(color: AppColors.saqelCyan, fontSize: 11, fontWeight: FontWeight.w700), style: TextStyle(color: AppColors.saqelCyan, fontSize: 11, fontWeight: FontWeight.w700),
), ),
], ],
@@ -404,7 +412,7 @@ class _SocraticVideoPlayerScreenState extends State<SocraticVideoPlayerScreen> w
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
// Socratic Timeline Progress Bar with Checkpoint Markers // Understanding checkpoints on the timeline
Stack( Stack(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
children: [ children: [