Update Saqel Platform: 2026-09-02 08:59:54
This commit is contained in:
@@ -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: [
|
||||||
|
|||||||
Reference in New Issue
Block a user