Update Saqel Platform: 2026-09-04 21:26:55
This commit is contained in:
+137
-43
@@ -1,23 +1,28 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import '../../../core/network/api_client.dart';
|
||||
import '../../../core/services/local_document_storage_service.dart';
|
||||
import '../../../core/theme/app_colors.dart';
|
||||
import '../../../core/utils/saqel_toast.dart';
|
||||
import '../../../data/datasources/curriculum_baked_data.dart';
|
||||
import '../../widgets/luxury_widgets.dart';
|
||||
import '../../widgets/english_tts_player_widget.dart';
|
||||
import 'physics_interactive_lab_view.dart';
|
||||
|
||||
/// ==============================================================================
|
||||
/// SAQEL ENTERPRISE (EDTECH 2.0) - DYNAMIC CURRICULUM DOCUMENT & TEXTBOOK VIEWER
|
||||
/// ==============================================================================
|
||||
///
|
||||
/// ملف: curriculum_document_viewer_screen.dart
|
||||
/// الهدف المعماري:
|
||||
/// استعراض الكتب المدرسية المقررة والمذكرات الوزارية وملخصات الدروس ديناميكياً:
|
||||
/// 1. جلب المحتوى الحي عبر API المنهاج مع إزالة أي بيانات وهمية ثابتة.
|
||||
/// 2. دعم الناطق الصوتي الذكي (English TTS) المدمج لنصوص ومفردات اللغة الإنجليزية.
|
||||
/// 3. زر انتقال مباشر إلى المختبر التفاعلي (Virtual Lab) لمفاهيم الفيزياء والعلوم.
|
||||
/// 4. تقسيم الماركداون إلى أقسام تفاعلية (الأهداف، القواعد، الأمثلة المحلولة، والتقييم الذاتي).
|
||||
/**
|
||||
* ==============================================================================
|
||||
* SAQEL ENTERPRISE (EDTECH 2.0) - CURRICULUM DOCUMENT & TEXTBOOK VIEWER
|
||||
* ==============================================================================
|
||||
*
|
||||
* ملف: curriculum_document_viewer_screen.dart
|
||||
* الهدف المعماري:
|
||||
* استعراض الكتب المدرسية المقررة والمذكرات الوزارية وأوراق العمل محلياً وسحابياً:
|
||||
* 1. دعم التخزين المحلي (Offline Storage): قراءة وتصفح دائم دون اتصال بعد الحفظ.
|
||||
* 2. محتوى وزاري مخبوز وأصيل (CurriculumBakedData) للمواد الثلاث (رياضيات 10، فيزياء 10، إنجليزي 10).
|
||||
* 3. دعم الناطق الصوتي الذكي (English TTS) المدمج لقراءة النصوص والمفردات.
|
||||
* 4. ربط فوري بالمختبر التفاعلي (Virtual Lab) لمفاهيم المتجهات والفيزياء.
|
||||
* 5. التحكم بحجم الخط وزر الحفظ في الجهاز.
|
||||
*/
|
||||
class CurriculumDocumentViewerScreen extends StatefulWidget {
|
||||
final String title;
|
||||
final String documentType; // 'worksheet', 'summary', 'textbook', 'lesson'
|
||||
@@ -46,6 +51,7 @@ class _CurriculumDocumentViewerScreenState extends State<CurriculumDocumentViewe
|
||||
final ApiClient _api = ApiClient();
|
||||
double _fontSize = 14.5;
|
||||
bool _isLoading = true;
|
||||
bool _isSavedLocally = false;
|
||||
String _documentContent = '';
|
||||
List<DocumentSection> _sections = [];
|
||||
|
||||
@@ -57,6 +63,9 @@ class _CurriculumDocumentViewerScreenState extends State<CurriculumDocumentViewe
|
||||
widget.subjectTitle.contains('فيزياء') ||
|
||||
(widget.subjectId ?? '').toLowerCase().contains('physic');
|
||||
|
||||
String get _effectiveSubjectId => widget.subjectId ?? (_isEnglish ? 'english_10' : (_isPhysics ? 'physics_10' : 'math_10'));
|
||||
String get _effectiveFilePath => widget.filePath ?? widget.title;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@@ -71,25 +80,83 @@ class _CurriculumDocumentViewerScreenState extends State<CurriculumDocumentViewe
|
||||
}
|
||||
|
||||
setState(() => _isLoading = true);
|
||||
|
||||
// 1. Check Offline Local Storage First (Zero Network Overhead)
|
||||
final savedLocally = await LocalDocumentStorageService.getSavedDocument(
|
||||
subjectId: _effectiveSubjectId,
|
||||
type: widget.documentType,
|
||||
filePath: _effectiveFilePath,
|
||||
);
|
||||
|
||||
if (savedLocally != null && savedLocally.isNotEmpty) {
|
||||
_isSavedLocally = true;
|
||||
_processContent(savedLocally);
|
||||
if (mounted) setState(() => _isLoading = false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if saved flag exists
|
||||
_isSavedLocally = await LocalDocumentStorageService.isDocumentSaved(
|
||||
subjectId: _effectiveSubjectId,
|
||||
type: widget.documentType,
|
||||
filePath: _effectiveFilePath,
|
||||
);
|
||||
|
||||
// 2. Attempt fetching from Live Server
|
||||
try {
|
||||
final res = await _api.get(
|
||||
'/api/curriculum/document',
|
||||
queryParams: {
|
||||
'subject': widget.subjectId ?? widget.subjectTitle,
|
||||
'file': widget.filePath ?? '',
|
||||
'subject': _effectiveSubjectId,
|
||||
'file': _effectiveFilePath,
|
||||
'type': widget.documentType,
|
||||
},
|
||||
);
|
||||
).timeout(const Duration(seconds: 3));
|
||||
|
||||
if (res is Map && res['content'] != null) {
|
||||
if (res is Map && res['content'] != null && res['content'].toString().trim().isNotEmpty) {
|
||||
_processContent(res['content'].toString());
|
||||
} else {
|
||||
_processContent(_getFallbackContent());
|
||||
if (mounted) setState(() => _isLoading = false);
|
||||
return;
|
||||
}
|
||||
} catch (_) {
|
||||
// Graceful fallback to rich baked curriculum data
|
||||
}
|
||||
|
||||
// 3. Fallback to authentic Jordanian curriculum baked content
|
||||
final baked = CurriculumBakedData.getDocument(
|
||||
subjectId: _effectiveSubjectId,
|
||||
type: widget.documentType,
|
||||
filePath: _effectiveFilePath,
|
||||
title: widget.title,
|
||||
);
|
||||
|
||||
_processContent(baked);
|
||||
if (mounted) setState(() => _isLoading = false);
|
||||
}
|
||||
|
||||
Future<void> _toggleSaveLocal() async {
|
||||
if (_isSavedLocally) {
|
||||
await LocalDocumentStorageService.removeSavedDocument(
|
||||
subjectId: _effectiveSubjectId,
|
||||
type: widget.documentType,
|
||||
filePath: _effectiveFilePath,
|
||||
);
|
||||
setState(() => _isSavedLocally = false);
|
||||
if (mounted) {
|
||||
SaqelToast.showInfo(context, 'تمت إزالة المستند من الذاكرة المحلية', title: 'إلغاء الحفظ');
|
||||
}
|
||||
} else {
|
||||
await LocalDocumentStorageService.saveDocumentLocally(
|
||||
subjectId: _effectiveSubjectId,
|
||||
type: widget.documentType,
|
||||
filePath: _effectiveFilePath,
|
||||
title: widget.title,
|
||||
content: _documentContent,
|
||||
);
|
||||
setState(() => _isSavedLocally = true);
|
||||
if (mounted) {
|
||||
SaqelToast.showSuccess(context, 'تم حفظ المستند في الجهاز — متاح الآن دون إنترنت 📖', title: 'حفظ محلي مكتمل');
|
||||
}
|
||||
} catch (e) {
|
||||
_processContent(_getFallbackContent());
|
||||
} finally {
|
||||
if (mounted) setState(() => _isLoading = false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,16 +195,6 @@ class _CurriculumDocumentViewerScreenState extends State<CurriculumDocumentViewe
|
||||
return list;
|
||||
}
|
||||
|
||||
String _getFallbackContent() {
|
||||
if (_isEnglish) {
|
||||
return "## Unit 01: Looking Good — Vocabulary & Reading\n\n### 1. Key Vocabulary\n- **Casual clothing:** Everyday informal garments (jeans, sneakers, hoodie).\n- **Traditional attire:** Cultural heritage wear (Jordanian Thobe and Keffiyeh).\n- **Subconscious influence:** The way external appearance affects cognitive confidence.\n\n### 2. Reading Text: The Power of First Impressions\nResearch shows that within the first seven seconds of meeting someone, people make subconscious judgments about character, competence, and reliability. In a famous experiment, doctors wearing clean white coats demonstrated higher diagnostic focus.\n\n### 3. Grammar Workshop: Articles (a, an, the, zero article)\n- Use a / an for non-specific singular countable nouns.\n- Use the when the listener knows which specific thing is meant.\n- Use zero article with plural or uncountable nouns spoken in general terms.";
|
||||
}
|
||||
if (_isPhysics) {
|
||||
return "## الوحدة الأولى: المتجهات والكميات الفيزيائية\n\n### 1. التمييز بين الكميات القياسية والمتجهة\n- **الكميات القياسية:** تُحدد بالمقدار ووحدة القياس فقط (الكتلة، الزمن، الطاقة، درجة الحرارة).\n- **الكميات المتجهة:** تُحدد بالمقدار والاتجاه ونقطة التأثير (القوة، السرعة المتجهة، التسارع، الإزاحة).\n\n### 2. جمع وتحليل المتجهات بيانياً وتحليلياً\n- **المركبة الأفقية:** A_x = A cos θ\n- **المركبة الرأسية:** A_y = A sin θ\n- **المحصلة:** R = √(R_x² + R_y²)\n- **زاوية الاتجاه:** tan θ = |R_y / R_x|";
|
||||
}
|
||||
return "## الوحدة الأولى: المنهاج الوزاري المعتمد\n\n### 1. النتاجات والمفاهيم الأساسية\n- استيعاب المفاهيم والمصطلحات المعتمدة في الإطار العام للمناهج.\n- الربط بين المعرفة النظرية والتطبيقات العملية والتمارين الوزارية.\n- التحقق الذاتي من الاستيعاب عبر بنك الأسئلة والمختبر الذكي.";
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
@@ -162,6 +219,17 @@ class _CurriculumDocumentViewerScreenState extends State<CurriculumDocumentViewe
|
||||
),
|
||||
centerTitle: true,
|
||||
actions: [
|
||||
// Offline Save / Download Button
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
_isSavedLocally ? CupertinoIcons.checkmark_seal_fill : CupertinoIcons.cloud_download,
|
||||
color: _isSavedLocally ? AppColors.emeraldGreen : Colors.white70,
|
||||
size: 20,
|
||||
),
|
||||
tooltip: _isSavedLocally ? 'محفوظ في الجهاز (دون إنترنت)' : 'حفظ في الجهاز للقراءة دون إنترنت',
|
||||
onPressed: _toggleSaveLocal,
|
||||
),
|
||||
// Font Resizing
|
||||
IconButton(
|
||||
icon: const Icon(CupertinoIcons.textformat_size, color: Colors.white70, size: 20),
|
||||
onPressed: () {
|
||||
@@ -171,6 +239,7 @@ class _CurriculumDocumentViewerScreenState extends State<CurriculumDocumentViewe
|
||||
SaqelToast.showInfo(context, 'تم ضبط حجم الخط (${_fontSize.toInt()}pt)', title: 'حجم الخط');
|
||||
},
|
||||
),
|
||||
// Share
|
||||
IconButton(
|
||||
icon: const Icon(CupertinoIcons.share, color: Colors.white70, size: 20),
|
||||
onPressed: () {
|
||||
@@ -258,36 +327,61 @@ class _CurriculumDocumentViewerScreenState extends State<CurriculumDocumentViewe
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
|
||||
// Document Header Status Banner
|
||||
// Document Header Status Banner with Offline Status
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12),
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
AppColors.saqelCyan.withAlpha(25),
|
||||
_isSavedLocally ? AppColors.emeraldGreen.withAlpha(30) : AppColors.saqelCyan.withAlpha(25),
|
||||
AppColors.appleBlue.withAlpha(20),
|
||||
],
|
||||
),
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
border: Border.all(color: AppColors.saqelCyan.withAlpha(40)),
|
||||
border: Border.all(
|
||||
color: _isSavedLocally ? AppColors.emeraldGreen.withAlpha(60) : AppColors.saqelCyan.withAlpha(40),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(CupertinoIcons.doc_checkmark_fill, color: AppColors.saqelCyan, size: 22),
|
||||
Icon(
|
||||
_isSavedLocally ? CupertinoIcons.check_mark_circled_solid : CupertinoIcons.doc_checkmark_fill,
|
||||
color: _isSavedLocally ? AppColors.emeraldGreen : AppColors.saqelCyan,
|
||||
size: 22,
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
widget.documentType == 'textbook'
|
||||
? 'الكتاب المدرسي المعتمد — وزارة التربية والتعليم'
|
||||
: 'مذكرة دراسية وملخص معتمد',
|
||||
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w700, fontSize: 13),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
widget.documentType == 'textbook'
|
||||
? 'الكتاب المدرسي المعتمد — وزارة التربية والتعليم'
|
||||
: 'ورقة عمل ومذكرة دراسية معتمدة',
|
||||
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w700, fontSize: 13),
|
||||
),
|
||||
const Spacer(),
|
||||
if (_isSavedLocally)
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.emeraldGreen.withAlpha(30),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
child: const Text(
|
||||
'محفوظ في الجهاز 💾',
|
||||
style: TextStyle(color: AppColors.emeraldGreen, fontSize: 10, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
const SizedBox(height: 3),
|
||||
Text(
|
||||
'محتوى حي ومحدث مطابق لنتاجات ${widget.subjectTitle}',
|
||||
_isSavedLocally
|
||||
? 'مخزن في ذاكرة الجهاز — متاح للقراءة في أي وقت بدون إنترنت'
|
||||
: 'محتوى حي ومحدث مطابق لنتاجات ${widget.subjectTitle}',
|
||||
style: const TextStyle(color: AppColors.textSecondaryDark, fontSize: 11),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -27,6 +27,8 @@ import 'islamic_interactive_lab_view.dart';
|
||||
import 'history_interactive_timeline_view.dart';
|
||||
import '../../../data/models/exam_model.dart';
|
||||
import '../../../data/repositories/app_repositories.dart';
|
||||
import '../../../core/services/local_document_storage_service.dart';
|
||||
import '../../../data/datasources/curriculum_baked_data.dart';
|
||||
|
||||
/// الشاشة المركزية للمادة الدراسية وبوابات الدروس والامتحانات والمصادر
|
||||
class SubjectHubScreen extends StatefulWidget {
|
||||
@@ -292,13 +294,35 @@ class _SubjectHubScreenState extends State<SubjectHubScreen> with SingleTickerPr
|
||||
|
||||
/// Tab 2: Worksheets & Summaries
|
||||
Widget _buildWorksheetsTab(BuildContext context) {
|
||||
final worksheets = widget.subject.worksheets.isNotEmpty
|
||||
? widget.subject.worksheets
|
||||
: [
|
||||
const ResourceItemModel(title: 'ورقة عمل 1: المفاهيم الأساسية والتطبيقات', filePath: 'ws1.pdf', type: 'worksheet'),
|
||||
const ResourceItemModel(title: 'ملخص شامل: القوانين والمعادلات الوزارية المقررة', filePath: 'summary.pdf', type: 'summary'),
|
||||
const ResourceItemModel(title: 'مراجعة ختامية ونماذج تدريبية شاملة', filePath: 'exam_prep.pdf', type: 'worksheet'),
|
||||
];
|
||||
final s = widget.subject.id.toLowerCase();
|
||||
final isMath = s.contains('math') || widget.subject.title.contains('رياضيات');
|
||||
final isPhys = s.contains('physic') || widget.subject.title.contains('فيزياء');
|
||||
final isEng = s.contains('english') || widget.subject.title.contains('إنجليز');
|
||||
|
||||
final defaultWorksheets = isMath
|
||||
? [
|
||||
const ResourceItemModel(title: 'ورقة عمل 1: الأسس والأنظمة والمعادلات الخاصة', filePath: 'grade_10/math_10/semester_1/resources/worksheet_1.md', type: 'worksheet'),
|
||||
const ResourceItemModel(title: 'ملخص شامل: قوانين المعادلات والتحليل إلى العوامل', filePath: 'math_summary.md', type: 'summary'),
|
||||
const ResourceItemModel(title: 'مراجعة تدريبية: حل أنظمة المعادلات بيانياً وجبرياً', filePath: 'math_exam_prep.md', type: 'worksheet'),
|
||||
]
|
||||
: (isPhys
|
||||
? [
|
||||
const ResourceItemModel(title: 'ورقة عمل وتطبيقات: تحليل المتجهات وقوانين نيوتن', filePath: 'physics_ws1.md', type: 'worksheet'),
|
||||
const ResourceItemModel(title: 'ملخص شامل: الكميات القياسية والمتجهة والضرب النقطي', filePath: 'physics_summary.md', type: 'summary'),
|
||||
const ResourceItemModel(title: 'دليل التجارب المخبرية: طاولة القوى والتسارع', filePath: 'physics_lab_guide.md', type: 'worksheet'),
|
||||
]
|
||||
: (isEng
|
||||
? [
|
||||
const ResourceItemModel(title: 'Action Pack 10 — Practice Worksheet: Unit 01 (Looking Good)', filePath: 'english_ws1.md', type: 'worksheet'),
|
||||
const ResourceItemModel(title: 'Grammar & Vocabulary Revision: Articles & First Impressions', filePath: 'english_summary.md', type: 'summary'),
|
||||
const ResourceItemModel(title: 'Unit 02 Reading & Grammar Worksheet (The Digital Mind)', filePath: 'english_ws2.md', type: 'worksheet'),
|
||||
]
|
||||
: [
|
||||
const ResourceItemModel(title: 'ورقة عمل 1: المفاهيم الأساسية والتطبيقات', filePath: 'ws1.md', type: 'worksheet'),
|
||||
const ResourceItemModel(title: 'ملخص شامل: القوانين والمعادلات الوزارية المقررة', filePath: 'summary.md', type: 'summary'),
|
||||
]));
|
||||
|
||||
final worksheets = widget.subject.worksheets.isNotEmpty ? widget.subject.worksheets : defaultWorksheets;
|
||||
|
||||
return ListView.builder(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 20),
|
||||
@@ -476,12 +500,32 @@ class _SubjectHubScreenState extends State<SubjectHubScreen> with SingleTickerPr
|
||||
|
||||
/// Tab 4: Official Ministry Textbooks
|
||||
Widget _buildTextbooksTab(BuildContext context) {
|
||||
final textbooks = widget.subject.textbooks.isNotEmpty
|
||||
? widget.subject.textbooks
|
||||
: [
|
||||
const ResourceItemModel(title: 'كتاب الطالب المقرّر — منهاج وزارة التربية والتعليم', filePath: 'book.pdf', type: 'textbook'),
|
||||
const ResourceItemModel(title: 'كتاب التجارب والأنشطة العلمية والعملية', filePath: 'workbook.pdf', type: 'textbook'),
|
||||
];
|
||||
final s = widget.subject.id.toLowerCase();
|
||||
final isMath = s.contains('math') || widget.subject.title.contains('رياضيات');
|
||||
final isPhys = s.contains('physic') || widget.subject.title.contains('فيزياء');
|
||||
final isEng = s.contains('english') || widget.subject.title.contains('إنجليز');
|
||||
|
||||
final defaultTextbooks = isMath
|
||||
? [
|
||||
const ResourceItemModel(title: 'كتاب الطالب المقرر — الرياضيات (أنظمة المعادلات والدائرة)', filePath: 'grade_10/math_10/semester_1/math_student_book.md', type: 'textbook'),
|
||||
const ResourceItemModel(title: 'كتاب التمارين والأنشطة الإضافية — الرياضيات 10', filePath: 'grade_10/math_10/semester_1/math_workbook.md', type: 'textbook'),
|
||||
]
|
||||
: (isPhys
|
||||
? [
|
||||
const ResourceItemModel(title: 'كتاب الفيزياء المقرر — الطالب (المتجهات والحركة)', filePath: 'grade_10/physics_10/semester_1/physics_student_book.md', type: 'textbook'),
|
||||
const ResourceItemModel(title: 'كتاب التجارب والأنشطة العلمية والعملية (طاولة القوى)', filePath: 'grade_10/physics_10/semester_1/physics_activities_book.md', type: 'textbook'),
|
||||
]
|
||||
: (isEng
|
||||
? [
|
||||
const ResourceItemModel(title: 'Action Pack 10 — Student\'s Book (Looking Good & The Digital Mind)', filePath: 'grade_10/english_10/semester_1/unit_01.md', type: 'textbook'),
|
||||
const ResourceItemModel(title: 'Action Pack 10 — Activity Book & Literature Spot', filePath: 'grade_10/english_10/semester_1/activity_book.md', type: 'textbook'),
|
||||
]
|
||||
: [
|
||||
const ResourceItemModel(title: 'كتاب الطالب المقرّر — منهاج وزارة التربية والتعليم', filePath: 'book.md', type: 'textbook'),
|
||||
const ResourceItemModel(title: 'كتاب التجارب والأنشطة العلمية والعملية', filePath: 'workbook.md', type: 'textbook'),
|
||||
]));
|
||||
|
||||
final textbooks = widget.subject.textbooks.isNotEmpty ? widget.subject.textbooks : defaultTextbooks;
|
||||
|
||||
return ListView.builder(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 20),
|
||||
@@ -513,7 +557,7 @@ class _SubjectHubScreenState extends State<SubjectHubScreen> with SingleTickerPr
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
const Text(
|
||||
'منهاج وزارة التربية والتعليم الأردنية المعتمد (PDF كامل)',
|
||||
'منهاج وزارة التربية والتعليم الأردنية المعتمد (جاهز للقراءة والتنزيل)',
|
||||
style: TextStyle(color: AppColors.textSecondaryDark, fontSize: 12),
|
||||
),
|
||||
],
|
||||
@@ -592,9 +636,29 @@ class _SubjectHubScreenState extends State<SubjectHubScreen> with SingleTickerPr
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
),
|
||||
onPressed: () {
|
||||
onPressed: () async {
|
||||
Navigator.of(ctx).pop();
|
||||
SaqelToast.showSuccess(context, 'تم تفعيل وضع القراءة دون إنترنت 📖', title: 'حفظ محلي');
|
||||
final docType = title.contains('كتاب') ? 'textbook' : 'worksheet';
|
||||
final content = CurriculumBakedData.getDocument(
|
||||
subjectId: widget.subject.id,
|
||||
type: docType,
|
||||
filePath: filePath,
|
||||
title: title,
|
||||
);
|
||||
await LocalDocumentStorageService.saveDocumentLocally(
|
||||
subjectId: widget.subject.id,
|
||||
type: docType,
|
||||
filePath: filePath ?? title,
|
||||
title: title,
|
||||
content: content,
|
||||
);
|
||||
if (context.mounted) {
|
||||
SaqelToast.showSuccess(
|
||||
context,
|
||||
'تم حفظ المستند في الجهاز — متاح الآن للقراءة دون إنترنت 📖',
|
||||
title: 'حفظ محلي مكتمل',
|
||||
);
|
||||
}
|
||||
},
|
||||
icon: const Icon(CupertinoIcons.cloud_download, size: 18),
|
||||
label: const Text('حفظ في الجهاز', style: TextStyle(fontWeight: FontWeight.w700)),
|
||||
|
||||
Reference in New Issue
Block a user