Clean up: move docs to Obsidian vault, remove outdated md files
This commit is contained in:
@@ -0,0 +1,180 @@
|
||||
import 'dart:convert';
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
/// Hive модель для кэшированного mindmap-файла
|
||||
class CachedMindmap {
|
||||
final String path;
|
||||
final String content;
|
||||
final String? sha;
|
||||
final DateTime lastSync;
|
||||
final DateTime lastModified;
|
||||
final String branch;
|
||||
|
||||
CachedMindmap({
|
||||
required this.path,
|
||||
required this.content,
|
||||
this.sha,
|
||||
required this.lastSync,
|
||||
required this.lastModified,
|
||||
this.branch = 'main',
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'path': path,
|
||||
'content': content,
|
||||
'sha': sha,
|
||||
'lastSync': lastSync.toIso8601String(),
|
||||
'lastModified': lastModified.toIso8601String(),
|
||||
'branch': branch,
|
||||
};
|
||||
|
||||
factory CachedMindmap.fromJson(Map<String, dynamic> json) {
|
||||
return CachedMindmap(
|
||||
path: json['path'] ?? '',
|
||||
content: json['content'] ?? '',
|
||||
sha: json['sha'],
|
||||
lastSync:
|
||||
DateTime.parse(json['lastSync'] ?? DateTime.now().toIso8601String()),
|
||||
lastModified: DateTime.parse(
|
||||
json['lastModified'] ?? DateTime.now().toIso8601String()),
|
||||
branch: json['branch'] ?? 'main',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Оффлайн репозиторий для mindmaps
|
||||
/// Хранит кэш файлов из Gitea и очередь изменений для синхронизации
|
||||
class OfflineMindmapRepository {
|
||||
static const String _boxName = 'mindmaps_cache';
|
||||
static const String _queueBoxName = 'mindmaps_queue';
|
||||
static const String _metaKey = '_meta_sync_state';
|
||||
|
||||
Box<String>? _box;
|
||||
Box<String>? _queueBox;
|
||||
|
||||
Future<void> initialize() async {
|
||||
_box = await Hive.openBox<String>(_boxName);
|
||||
_queueBox = await Hive.openBox<String>(_queueBoxName);
|
||||
}
|
||||
|
||||
bool get isInitialized => _box != null && _box!.isOpen;
|
||||
|
||||
// ==================== CACHE ====================
|
||||
|
||||
/// Сохранить файл в кэш
|
||||
Future<void> cacheMindmap(CachedMindmap mindmap) async {
|
||||
if (!isInitialized) throw Exception('Repository not initialized');
|
||||
await _box!.put(mindmap.path, jsonEncode(mindmap.toJson()));
|
||||
}
|
||||
|
||||
/// Получить файл из кэша
|
||||
CachedMindmap? getMindmap(String path) {
|
||||
if (!isInitialized) return null;
|
||||
final raw = _box!.get(path);
|
||||
if (raw == null) return null;
|
||||
try {
|
||||
return CachedMindmap.fromJson(jsonDecode(raw));
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Получить все кэшированные mindmaps
|
||||
List<CachedMindmap> getAllMindmaps() {
|
||||
if (!isInitialized) return [];
|
||||
return _box!.values
|
||||
.map((raw) {
|
||||
try {
|
||||
return CachedMindmap.fromJson(jsonDecode(raw));
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.whereType<CachedMindmap>()
|
||||
.toList();
|
||||
}
|
||||
|
||||
/// Удалить файл из кэша
|
||||
Future<void> deleteMindmap(String path) async {
|
||||
if (!isInitialized) return;
|
||||
await _box!.delete(path);
|
||||
}
|
||||
|
||||
/// Очистить весь кэш
|
||||
Future<void> clearCache() async {
|
||||
if (!isInitialized) return;
|
||||
await _box!.clear();
|
||||
}
|
||||
|
||||
// ==================== SYNC QUEUE ====================
|
||||
|
||||
/// Добавить изменение в очередь (для offline → online sync)
|
||||
Future<void> queueChange({
|
||||
required String path,
|
||||
required String content,
|
||||
required String operation, // 'create' | 'update' | 'delete'
|
||||
String? commitMessage,
|
||||
}) async {
|
||||
if (!isInitialized) throw Exception('Repository not initialized');
|
||||
final entry = {
|
||||
'path': path,
|
||||
'content': content,
|
||||
'operation': operation,
|
||||
'commitMessage': commitMessage ?? 'Mobile sync: $path',
|
||||
'timestamp': DateTime.now().toIso8601String(),
|
||||
};
|
||||
await _queueBox!.put(
|
||||
'${DateTime.now().millisecondsSinceEpoch}_$path', jsonEncode(entry));
|
||||
}
|
||||
|
||||
/// Получить все изменения из очереди
|
||||
List<Map<String, dynamic>> getPendingChanges() {
|
||||
if (!isInitialized) return [];
|
||||
return _queueBox!.values
|
||||
.map((raw) {
|
||||
try {
|
||||
return jsonDecode(raw) as Map<String, dynamic>;
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.whereType<Map<String, dynamic>>()
|
||||
.toList()
|
||||
..sort((a, b) => (a['timestamp'] ?? '').compareTo(b['timestamp'] ?? ''));
|
||||
}
|
||||
|
||||
/// Удалить выполненное изменение из очереди
|
||||
Future<void> removeQueuedChange(String key) async {
|
||||
if (!isInitialized) return;
|
||||
await _queueBox!.delete(key);
|
||||
}
|
||||
|
||||
/// Очистить очередь
|
||||
Future<void> clearQueue() async {
|
||||
if (!isInitialized) return;
|
||||
await _queueBox!.clear();
|
||||
}
|
||||
|
||||
bool get hasPendingChanges => (_queueBox?.length ?? 0) > 0;
|
||||
|
||||
// ==================== SYNC STATE ====================
|
||||
|
||||
/// Сохранить время последней успешной синхронизации
|
||||
Future<void> setLastSync(DateTime time) async {
|
||||
if (!isInitialized) return;
|
||||
await _box!.put(_metaKey, jsonEncode({'lastSync': time.toIso8601String()}));
|
||||
}
|
||||
|
||||
/// Получить время последней синхронизации
|
||||
DateTime? getLastSync() {
|
||||
if (!isInitialized) return null;
|
||||
final raw = _box!.get(_metaKey);
|
||||
if (raw == null) return null;
|
||||
try {
|
||||
final data = jsonDecode(raw) as Map<String, dynamic>;
|
||||
return DateTime.tryParse(data['lastSync'] ?? '');
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user