add drafts section to settings

This commit is contained in:
Filip Krawczyk 2022-08-29 01:50:09 +02:00
parent 5f23176e6e
commit 89df32f4a9
5 changed files with 186 additions and 18 deletions

View File

@ -12,6 +12,7 @@ import 'app_config.dart';
import 'l10n/timeago/pl.dart';
import 'pages/log_console/log_console_page_store.dart';
import 'stores/accounts_store.dart';
import 'stores/comment_drafts_store.dart';
import 'stores/config_store.dart';
import 'util/mobx_provider.dart';
@ -21,6 +22,7 @@ Future<void> mainCommon(AppConfig appConfig) async {
final logConsoleStore = LogConsolePageStore();
final sharedPrefs = await SharedPreferences.getInstance();
await Hive.initFlutter();
await CommentDraftStore.open();
_setupLogger(appConfig, logConsoleStore);
_setupTimeago();

View File

@ -0,0 +1,140 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hive/hive.dart';
import '../../stores/comment_drafts_store.dart';
class DraftsPage extends HookWidget {
const DraftsPage._();
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: Scaffold(
appBar: AppBar(
leading: const BackButton(),
title: const Text('Drafts'),
bottom: const TabBar(
isScrollable: true,
tabs: [
Tab(child: Text('Comments')),
Tab(child: Text('Posts')),
],
),
),
body: const TabBarView(children: [
_CommentsTab(),
_PostsTab(),
])),
);
}
static Route route() => MaterialPageRoute(
builder: (context) => const DraftsPage._(),
);
}
class _CommentsTab extends HookWidget {
const _CommentsTab();
@override
Widget build(BuildContext context) {
return ValueListenableBuilder<LazyBox<String>>(
valueListenable: CommentDraftStore.allDraftsListenable(),
builder: (context, box, widget) {
if (box.isEmpty) {
return const Center(child: Text('no drafts yet'));
}
Future<void> removeAllDrafts() async {
final removeAll = await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: const Text(
'Do you want to remove ALL comment drafts?'),
actions: [
ElevatedButton(
onPressed: () {
Navigator.of(context).pop(true);
},
style: ElevatedButton.styleFrom(
primary: Colors.red,
),
child: const Text('Yes'),
),
OutlinedButton(
onPressed: () {
Navigator.of(context).pop(false);
},
child: const Text('No'),
),
],
)) ??
false;
if (removeAll) {
await CommentDraftStore.removeAllDrafts();
}
}
return ListView.builder(
itemCount: box.length + 1,
itemBuilder: (context, index) {
if (index == box.length) {
return Padding(
padding: const EdgeInsets.all(16),
child: ElevatedButton(
onPressed: removeAllDrafts,
style: ElevatedButton.styleFrom(
primary: Colors.red,
),
child: const Text('Remove all drafts'),
),
);
}
return _CommentDraftTile(CommentDraftStore.keyAt(index)!);
},
);
},
);
}
}
class _CommentDraftTile extends HookWidget {
final String databaseKey;
const _CommentDraftTile(this.databaseKey);
@override
Widget build(BuildContext context) {
final body = useState<String?>(null);
useEffect(() {
CommentDraftStore.loadDraft(databaseKey)
.then((value) => body.value = value);
return null;
});
return ListTile(
key: ValueKey(key),
title: body.value == null
? const CircularProgressIndicator.adaptive()
: Text(body.value!),
trailing: IconButton(
icon: const Icon(Icons.delete),
onPressed: () {
CommentDraftStore.removeDraft(databaseKey);
},
),
subtitle: Text(databaseKey),
);
}
}
class _PostsTab extends StatelessWidget {
const _PostsTab();
@override
Widget build(BuildContext context) {
return const Center(child: Text('TBD'));
}
}

View File

@ -16,6 +16,7 @@ import '../manage_account.dart';
import 'add_account_page.dart';
import 'add_instance_page.dart';
import 'blocks/blocks.dart';
import 'drafts_page.dart';
/// Page with a list of different settings sections
class SettingsPage extends HookWidget {
@ -60,6 +61,13 @@ class SettingsPage extends HookWidget {
goTo(context, (_) => const AppearanceConfigPage());
},
),
ListTile(
leading: const Icon(Icons.drive_file_rename_outline_outlined),
title: const Text('Drafts'),
onTap: () {
Navigator.of(context).push(DraftsPage.route());
},
),
const AboutTile()
],
),

View File

@ -1,24 +1,37 @@
import 'package:hive/hive.dart';
import 'package:flutter/foundation.dart';
import 'package:hive_flutter/adapters.dart';
class CommentDraftStore {
static const _boxKey = 'comment_drafts';
static late LazyBox<String> _box;
static Future<String?> loadDraft(String apId) async {
final box = await Hive.openBox<String>(_boxKey);
final text = box.get(apId);
await box.close();
return text;
static Future<void> open() async {
_box = await Hive.openLazyBox<String>(_boxKey);
}
static Future<void> saveDraft(String apId, String text) async {
final box = await Hive.openBox<String>(_boxKey);
await box.put(apId, text);
await box.close();
static Future<void> close() async {
await _box.compact();
await _box.close();
}
static Future<void> removeDraft(String apId) async {
final box = await Hive.openBox<String>(_boxKey);
await box.delete(apId);
await box.close();
static Future<void> compact() async {
await _box.compact();
}
static String? keyAt(int index) => _box.keyAt(index);
static ValueListenable<LazyBox<String>> allDraftsListenable() =>
_box.listenable();
static Future<String?> loadDraft(String apId) => _box.get(apId);
static Future<void> saveDraft(String apId, String text) =>
_box.put(apId, text);
static Future<void> removeDraft(String apId) => _box.delete(apId);
static Future<void> removeAllDrafts() async {
await _box.deleteFromDisk();
await open();
}
}

View File

@ -116,10 +116,15 @@ class WriteComment extends HookWidget {
leading: CloseButton(
onPressed: () async {
// save draft before closing
if (!_isEdit &&
editorController.textEditingController.text.trim().isNotEmpty) {
await CommentDraftStore.saveDraft(comment?.apId ?? post.apId,
editorController.textEditingController.text);
if (!_isEdit) {
if (editorController.textEditingController.text
.trim()
.isNotEmpty) {
await CommentDraftStore.saveDraft(comment?.apId ?? post.apId,
editorController.textEditingController.text);
} else {
await CommentDraftStore.removeDraft(comment?.apId ?? post.apId);
}
}
Navigator.of(context).pop();
},