Update Saqel Platform: 2026-09-04 21:26:55

This commit is contained in:
Hamza-Ayed
2026-09-04 21:26:55 +03:00
parent 24f6b5a589
commit ae00aa943e
36 changed files with 5621 additions and 1617 deletions
@@ -0,0 +1,126 @@
import 'package:shared_preferences/shared_preferences.dart';
/**
* ==============================================================================
* SAQEL EDTECH 2.0 - LOCAL DOCUMENT & TEXTBOOK STORAGE SERVICE
* ==============================================================================
*
* خدمة التخزين المحلي للكتب والمذكرات وأوراق العمل:
* - حفظ محتوى الكتاب/المذكرة كاملاً في ذاكرة الجهاز (Offline Local Storage).
* - تمكين الطالب من تصفح الكتب وقراءتها في أي وقت دون اتصال بالإنترنت (Zero Network Dependency).
* - إدارة قائمة المستندات المحفوظة وحجمها وإمكانية حذفها أو تحديثها.
*/
class LocalDocumentStorageService {
static const String _prefix = 'saqel_doc_cache_';
static const String _savedListKey = 'saqel_saved_documents_list';
static String _buildKey(String subjectId, String type, String filePath) {
final cleanPath = filePath.replaceAll(RegExp(r'[^a-zA-Z0-9_]'), '_');
return '$_prefix${subjectId}_${type}_$cleanPath';
}
/// Check if a document is already saved on the device
static Future<bool> isDocumentSaved({
required String subjectId,
required String type,
required String filePath,
}) async {
try {
final prefs = await SharedPreferences.getInstance();
final key = _buildKey(subjectId, type, filePath);
return prefs.containsKey(key);
} catch (_) {
return false;
}
}
/// Retrieve locally stored document markdown content
static Future<String?> getSavedDocument({
required String subjectId,
required String type,
required String filePath,
}) async {
try {
final prefs = await SharedPreferences.getInstance();
final key = _buildKey(subjectId, type, filePath);
return prefs.getString(key);
} catch (_) {
return null;
}
}
/// Save document markdown content locally on the device
static Future<bool> saveDocumentLocally({
required String subjectId,
required String type,
required String filePath,
required String title,
required String content,
}) async {
try {
final prefs = await SharedPreferences.getInstance();
final key = _buildKey(subjectId, type, filePath);
// Save document body
await prefs.setString(key, content);
// Register in saved documents catalog
final savedList = prefs.getStringList(_savedListKey) ?? [];
final record = '$subjectId|$type|$filePath|$title|${DateTime.now().toIso8601String()}';
savedList.removeWhere((item) => item.startsWith('$subjectId|$type|$filePath|'));
savedList.add(record);
await prefs.setStringList(_savedListKey, savedList);
return true;
} catch (_) {
return false;
}
}
/// Delete locally saved document to free up space
static Future<bool> removeSavedDocument({
required String subjectId,
required String type,
required String filePath,
}) async {
try {
final prefs = await SharedPreferences.getInstance();
final key = _buildKey(subjectId, type, filePath);
await prefs.remove(key);
final savedList = prefs.getStringList(_savedListKey) ?? [];
savedList.removeWhere((item) => item.startsWith('$subjectId|$type|$filePath|'));
await prefs.setStringList(_savedListKey, savedList);
return true;
} catch (_) {
return false;
}
}
/// Get list of all locally saved offline documents on this device
static Future<List<Map<String, String>>> getAllSavedDocuments() async {
try {
final prefs = await SharedPreferences.getInstance();
final savedList = prefs.getStringList(_savedListKey) ?? [];
final List<Map<String, String>> docs = [];
for (var record in savedList) {
final parts = record.split('|');
if (parts.length >= 4) {
docs.add({
'subject_id': parts[0],
'type': parts[1],
'file_path': parts[2],
'title': parts[3],
'saved_at': parts.length > 4 ? parts[4] : '',
});
}
}
return docs;
} catch (_) {
return [];
}
}
}