Clean up: move docs to Obsidian vault, remove outdated md files
This commit is contained in:
@@ -0,0 +1,228 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import '../core/gitea_sync_service.dart';
|
||||
import '../core/offline_mindmap_repository.dart';
|
||||
import '../core/markdown_mindmap_parser.dart';
|
||||
import '../models/mind_map_model.dart';
|
||||
|
||||
/// Provider для GiteaSyncService
|
||||
final giteaSyncServiceProvider = Provider<GiteaSyncService>((ref) {
|
||||
return GiteaSyncService();
|
||||
});
|
||||
|
||||
/// Provider для OfflineMindmapRepository
|
||||
final offlineMindmapRepoProvider = Provider<OfflineMindmapRepository>((ref) {
|
||||
return OfflineMindmapRepository();
|
||||
});
|
||||
|
||||
/// State для mindmap синхронизации
|
||||
class MindmapSyncState {
|
||||
final List<GiteaFile> files;
|
||||
final List<CachedMindmap> cachedMindmaps;
|
||||
final bool isLoading;
|
||||
final bool isSyncing;
|
||||
final String? error;
|
||||
final bool isConfigured;
|
||||
|
||||
MindmapSyncState({
|
||||
this.files = const [],
|
||||
this.cachedMindmaps = const [],
|
||||
this.isLoading = false,
|
||||
this.isSyncing = false,
|
||||
this.error,
|
||||
this.isConfigured = false,
|
||||
});
|
||||
|
||||
MindmapSyncState copyWith({
|
||||
List<GiteaFile>? files,
|
||||
List<CachedMindmap>? cachedMindmaps,
|
||||
bool? isLoading,
|
||||
bool? isSyncing,
|
||||
String? error,
|
||||
bool? isConfigured,
|
||||
}) {
|
||||
return MindmapSyncState(
|
||||
files: files ?? this.files,
|
||||
cachedMindmaps: cachedMindmaps ?? this.cachedMindmaps,
|
||||
isLoading: isLoading ?? this.isLoading,
|
||||
isSyncing: isSyncing ?? this.isSyncing,
|
||||
error: error ?? this.error,
|
||||
isConfigured: isConfigured ?? this.isConfigured,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Notifier для управления синхронизацией mindmaps
|
||||
class MindmapSyncNotifier extends StateNotifier<MindmapSyncState> {
|
||||
final GiteaSyncService _gitea;
|
||||
final OfflineMindmapRepository _repo;
|
||||
|
||||
MindmapSyncNotifier(this._gitea, this._repo) : super(MindmapSyncState());
|
||||
|
||||
/// Инициализация сервиса
|
||||
Future<void> initialize() async {
|
||||
await _repo.initialize();
|
||||
await _gitea.initialize();
|
||||
state = state.copyWith(isConfigured: _gitea.isConfigured);
|
||||
|
||||
if (state.isConfigured) {
|
||||
await loadCachedMindmaps();
|
||||
}
|
||||
}
|
||||
|
||||
/// Настройка Gitea
|
||||
Future<void> configure({
|
||||
required String baseUrl,
|
||||
required String token,
|
||||
required String owner,
|
||||
required String repo,
|
||||
}) async {
|
||||
state = state.copyWith(isLoading: true, error: null);
|
||||
try {
|
||||
await _gitea.configure(
|
||||
baseUrl: baseUrl,
|
||||
token: token,
|
||||
owner: owner,
|
||||
repo: repo,
|
||||
);
|
||||
final connected = await _gitea.testConnection();
|
||||
if (!connected) {
|
||||
throw Exception('Failed to connect to Gitea. Check token and URL.');
|
||||
}
|
||||
state = state.copyWith(isConfigured: true, isLoading: false);
|
||||
await syncFromGitea();
|
||||
} catch (e) {
|
||||
state = state.copyWith(isLoading: false, error: e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/// Загрузить mindmaps из кэша
|
||||
Future<void> loadCachedMindmaps() async {
|
||||
final cached = _repo.getAllMindmaps();
|
||||
state = state.copyWith(cachedMindmaps: cached);
|
||||
}
|
||||
|
||||
/// Полная синхронизация с Gitea (download)
|
||||
Future<void> syncFromGitea({String branch = 'main'}) async {
|
||||
if (!state.isConfigured) return;
|
||||
|
||||
state = state.copyWith(isSyncing: true, error: null);
|
||||
try {
|
||||
// Получаем дерево файлов
|
||||
final files = await _gitea.getTree(branch: branch);
|
||||
final mindmapFiles = files.where((f) =>
|
||||
f.path.endsWith('.md') || f.path.endsWith('.canvas')).toList();
|
||||
|
||||
// Загружаем каждый файл
|
||||
for (final file in mindmapFiles) {
|
||||
try {
|
||||
final giteaFile = await _gitea.getFile(path: file.path, branch: branch);
|
||||
final content = giteaFile.decodedContent;
|
||||
if (content != null) {
|
||||
await _repo.cacheMindmap(CachedMindmap(
|
||||
path: file.path,
|
||||
content: content,
|
||||
sha: giteaFile.sha,
|
||||
lastSync: DateTime.now(),
|
||||
lastModified: DateTime.now(),
|
||||
branch: branch,
|
||||
));
|
||||
}
|
||||
} catch (e) {
|
||||
// Логируем ошибку но продолжаем
|
||||
print('Failed to sync ${file.path}: $e');
|
||||
}
|
||||
}
|
||||
|
||||
await loadCachedMindmaps();
|
||||
state = state.copyWith(isSyncing: false);
|
||||
} catch (e) {
|
||||
state = state.copyWith(isSyncing: false, error: e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/// Парсить markdown-кэш в MindMapNode
|
||||
List<MindMapNode>? parseMindmap(String path) {
|
||||
final cached = _repo.getMindmap(path);
|
||||
if (cached == null) return null;
|
||||
|
||||
return MarkdownMindmapParser.parse(
|
||||
cached.content,
|
||||
path.replaceAll('/', '_'),
|
||||
);
|
||||
}
|
||||
|
||||
/// Сохранить изменения mindmap и запушить в Gitea
|
||||
Future<void> saveAndPush({
|
||||
required String path,
|
||||
required List<MindMapNode> nodes,
|
||||
String? title,
|
||||
String branch = 'main',
|
||||
}) async {
|
||||
if (!state.isConfigured) {
|
||||
throw Exception('Gitea not configured');
|
||||
}
|
||||
|
||||
final markdown = MarkdownMindmapParser.toMarkdown(nodes, title: title);
|
||||
|
||||
// Сохраняем локально
|
||||
await _repo.cacheMindmap(CachedMindmap(
|
||||
path: path,
|
||||
content: markdown,
|
||||
lastSync: DateTime.now(),
|
||||
lastModified: DateTime.now(),
|
||||
branch: branch,
|
||||
));
|
||||
|
||||
// Пушим в Gitea
|
||||
try {
|
||||
await _gitea.updateFile(
|
||||
path: path,
|
||||
content: markdown,
|
||||
commitMessage: 'mobile: update $path',
|
||||
branch: branch,
|
||||
authorName: 'NANAXE Mobile',
|
||||
authorEmail: 'mobile@nanaxe.local',
|
||||
);
|
||||
} catch (e) {
|
||||
// Если нет сети — кладём в очередь
|
||||
await _repo.queueChange(
|
||||
path: path,
|
||||
content: markdown,
|
||||
operation: 'update',
|
||||
commitMessage: 'mobile: update $path',
|
||||
);
|
||||
}
|
||||
|
||||
await loadCachedMindmaps();
|
||||
}
|
||||
|
||||
/// Отправить накопленные offline-изменения
|
||||
Future<void> pushPendingChanges({String branch = 'main'}) async {
|
||||
if (!state.isConfigured) return;
|
||||
|
||||
final pending = _repo.getPendingChanges();
|
||||
for (final change in pending) {
|
||||
try {
|
||||
await _gitea.updateFile(
|
||||
path: change['path'],
|
||||
content: change['content'],
|
||||
commitMessage: change['commitMessage'] ?? 'Mobile sync',
|
||||
branch: branch,
|
||||
);
|
||||
// Удаляем из очереди по timestamp_path
|
||||
final key = '${change['timestamp']}_${change['path']}';
|
||||
await _repo.removeQueuedChange(key);
|
||||
} catch (e) {
|
||||
print('Failed to push queued change: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// StateNotifierProvider для mindmap синхронизации
|
||||
final mindmapSyncProvider =
|
||||
StateNotifierProvider<MindmapSyncNotifier, MindmapSyncState>((ref) {
|
||||
final gitea = ref.watch(giteaSyncServiceProvider);
|
||||
final repo = ref.watch(offlineMindmapRepoProvider);
|
||||
return MindmapSyncNotifier(gitea, repo);
|
||||
});
|
||||
Reference in New Issue
Block a user