import 'dart:convert'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:island/pods/config.dart'; part 'compose_storage.g.dart'; const kComposeDraftStoreKey = 'compose_drafts'; const kArticleDraftStoreKey = 'article_drafts'; class ComposeDraft { final String id; final String title; final String description; final String content; final List attachmentIds; final String visibility; final DateTime lastModified; ComposeDraft({ required this.id, required this.title, required this.description, required this.content, required this.attachmentIds, required this.visibility, required this.lastModified, }); Map toJson() => { 'id': id, 'title': title, 'description': description, 'content': content, 'attachmentIds': attachmentIds, 'visibility': visibility, 'lastModified': lastModified.toIso8601String(), }; factory ComposeDraft.fromJson(Map json) => ComposeDraft( id: json['id'] as String, title: json['title'] as String? ?? '', description: json['description'] as String? ?? '', content: json['content'] as String? ?? '', attachmentIds: List.from(json['attachmentIds'] as List? ?? []), visibility: json['visibility'] as String? ?? 'public', lastModified: DateTime.parse(json['lastModified'] as String), ); ComposeDraft copyWith({ String? id, String? title, String? description, String? content, List? attachmentIds, String? visibility, DateTime? lastModified, }) { return ComposeDraft( id: id ?? this.id, title: title ?? this.title, description: description ?? this.description, content: content ?? this.content, attachmentIds: attachmentIds ?? this.attachmentIds, visibility: visibility ?? this.visibility, lastModified: lastModified ?? this.lastModified, ); } bool get isEmpty => title.isEmpty && description.isEmpty && content.isEmpty && attachmentIds.isEmpty; } class ArticleDraft { final String id; final String title; final String description; final String content; final String visibility; final DateTime lastModified; ArticleDraft({ required this.id, required this.title, required this.description, required this.content, required this.visibility, required this.lastModified, }); Map toJson() => { 'id': id, 'title': title, 'description': description, 'content': content, 'visibility': visibility, 'lastModified': lastModified.toIso8601String(), }; factory ArticleDraft.fromJson(Map json) => ArticleDraft( id: json['id'] as String, title: json['title'] as String? ?? '', description: json['description'] as String? ?? '', content: json['content'] as String? ?? '', visibility: json['visibility'] as String? ?? 'public', lastModified: DateTime.parse(json['lastModified'] as String), ); ArticleDraft copyWith({ String? id, String? title, String? description, String? content, String? visibility, DateTime? lastModified, }) { return ArticleDraft( id: id ?? this.id, title: title ?? this.title, description: description ?? this.description, content: content ?? this.content, visibility: visibility ?? this.visibility, lastModified: lastModified ?? this.lastModified, ); } bool get isEmpty => title.isEmpty && description.isEmpty && content.isEmpty; } @riverpod class ComposeStorageNotifier extends _$ComposeStorageNotifier { @override Map build() { _loadDrafts(); return {}; } void _loadDrafts() { final prefs = ref.read(sharedPreferencesProvider); final draftsJson = prefs.getString(kComposeDraftStoreKey); if (draftsJson != null) { try { final Map draftsMap = jsonDecode(draftsJson); final drafts = {}; for (final entry in draftsMap.entries) { drafts[entry.key] = ComposeDraft.fromJson(entry.value); } state = drafts; } catch (e) { // If there's an error loading drafts, start with empty state state = {}; } } } Future _saveDrafts() async { final prefs = ref.read(sharedPreferencesProvider); final draftsMap = {}; for (final entry in state.entries) { draftsMap[entry.key] = entry.value.toJson(); } await prefs.setString(kComposeDraftStoreKey, jsonEncode(draftsMap)); } Future saveDraft(ComposeDraft draft) async { if (draft.isEmpty) { await deleteDraft(draft.id); return; } final updatedDraft = draft.copyWith(lastModified: DateTime.now()); state = {...state, updatedDraft.id: updatedDraft}; await _saveDrafts(); } Future deleteDraft(String id) async { final newState = Map.from(state); newState.remove(id); state = newState; await _saveDrafts(); } ComposeDraft? getDraft(String id) { return state[id]; } List getAllDrafts() { final drafts = state.values.toList(); drafts.sort((a, b) => b.lastModified.compareTo(a.lastModified)); return drafts; } Future clearAllDrafts() async { state = {}; final prefs = ref.read(sharedPreferencesProvider); await prefs.remove(kComposeDraftStoreKey); } } @riverpod class ArticleStorageNotifier extends _$ArticleStorageNotifier { @override Map build() { _loadDrafts(); return {}; } void _loadDrafts() { final prefs = ref.read(sharedPreferencesProvider); final draftsJson = prefs.getString(kArticleDraftStoreKey); if (draftsJson != null) { try { final Map draftsMap = jsonDecode(draftsJson); final drafts = {}; for (final entry in draftsMap.entries) { drafts[entry.key] = ArticleDraft.fromJson(entry.value); } state = drafts; } catch (e) { // If there's an error loading drafts, start with empty state state = {}; } } } Future _saveDrafts() async { final prefs = ref.read(sharedPreferencesProvider); final draftsMap = {}; for (final entry in state.entries) { draftsMap[entry.key] = entry.value.toJson(); } await prefs.setString(kArticleDraftStoreKey, jsonEncode(draftsMap)); } Future saveDraft(ArticleDraft draft) async { if (draft.isEmpty) { await deleteDraft(draft.id); return; } final updatedDraft = draft.copyWith(lastModified: DateTime.now()); state = {...state, updatedDraft.id: updatedDraft}; await _saveDrafts(); } Future deleteDraft(String id) async { final newState = Map.from(state); newState.remove(id); state = newState; await _saveDrafts(); } ArticleDraft? getDraft(String id) { return state[id]; } List getAllDrafts() { final drafts = state.values.toList(); drafts.sort((a, b) => b.lastModified.compareTo(a.lastModified)); return drafts; } Future clearAllDrafts() async { state = {}; final prefs = ref.read(sharedPreferencesProvider); await prefs.remove(kArticleDraftStoreKey); } }