improve export/import

Signed-off-by: dom <githubaccount56556@proton.me>
This commit is contained in:
dom
2026-03-14 16:13:54 +08:00
parent 0788a4de2d
commit b9e543f26b
5 changed files with 443 additions and 243 deletions

View File

@@ -1,7 +1,9 @@
import 'package:PiliPlus/common/widgets/dialog/dialog.dart';
import 'package:PiliPlus/common/widgets/dialog/export_import.dart';
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
import 'package:PiliPlus/common/widgets/view_sliver_safe_area.dart';
import 'package:PiliPlus/grpc/bilibili/main/community/reply/v1.pb.dart';
import 'package:PiliPlus/grpc/bilibili/main/community/reply/v1.pb.dart'
show ReplyInfo;
import 'package:PiliPlus/pages/video/reply/widgets/reply_item_grpc.dart';
import 'package:PiliPlus/utils/app_scheme.dart';
import 'package:PiliPlus/utils/id_utils.dart';
@@ -9,9 +11,13 @@ import 'package:PiliPlus/utils/page_utils.dart';
import 'package:PiliPlus/utils/reply_utils.dart';
import 'package:PiliPlus/utils/storage.dart';
import 'package:PiliPlus/utils/storage_pref.dart';
import 'package:PiliPlus/utils/utils.dart';
import 'package:PiliPlus/utils/waterfall.dart';
import 'package:flutter/foundation.dart' show kDebugMode;
import 'package:flutter/material.dart';
import 'package:get/get_core/src/get_main.dart';
import 'package:get/get_navigation/src/extension_navigation.dart';
import 'package:get/get_rx/get_rx.dart';
import 'package:waterfall_flow/waterfall_flow.dart';
class MyReply extends StatefulWidget {
@@ -22,13 +28,19 @@ class MyReply extends StatefulWidget {
}
class _MyReplyState extends State<MyReply> with DynMixin {
late final List<ReplyInfo> _replies;
final List<ReplyInfo> _replies = <ReplyInfo>[];
@override
void initState() {
super.initState();
_replies = GStorage.reply!.values.map(ReplyInfo.fromBuffer).toList()
..sort((a, b) => b.ctime.compareTo(a.ctime)); // rpid not aligned
_initReply();
}
void _initReply() {
_replies.assignAll(
GStorage.reply!.values.map(ReplyInfo.fromBuffer).toList()
..sort((a, b) => b.ctime.compareTo(a.ctime)), // rpid not aligned
);
}
@override
@@ -36,24 +48,33 @@ class _MyReplyState extends State<MyReply> with DynMixin {
return Scaffold(
appBar: AppBar(
title: const Text('我的评论'),
actions: kDebugMode
? [
IconButton(
tooltip: 'Clear',
onPressed: () => showConfirmDialog(
context: context,
title: 'Clear Local Storage?',
onConfirm: () {
GStorage.reply!.clear();
_replies.clear();
setState(() {});
},
),
icon: const Icon(Icons.clear_all),
),
const SizedBox(width: 6),
]
: null,
actions: [
if (kDebugMode)
IconButton(
tooltip: 'Clear',
onPressed: () => showConfirmDialog(
context: context,
title: 'Clear Local Storage?',
onConfirm: () {
GStorage.reply!.clear();
_replies.clear();
setState(() {});
},
),
icon: const Icon(Icons.clear_all),
),
IconButton(
tooltip: '导出',
onPressed: _showExportDialog,
icon: const Icon(Icons.file_upload_outlined),
),
IconButton(
tooltip: '导入',
onPressed: _showImportDialog,
icon: const Icon(Icons.file_download_outlined),
),
const SizedBox(width: 6),
],
),
body: CustomScrollView(
physics: const AlwaysScrollableScrollPhysics(),
@@ -121,4 +142,89 @@ class _MyReplyState extends State<MyReply> with DynMixin {
isManual: true,
);
}
String _onExport() {
return Utils.jsonEncoder.convert(
_replies.map((e) => e.toProto3Json()).toList(),
);
}
void _showExportDialog() {
const style = TextStyle(fontSize: 14);
showDialog(
context: context,
builder: (context) => SimpleDialog(
clipBehavior: .hardEdge,
contentPadding: const .symmetric(vertical: 12),
children: [
ListTile(
dense: true,
title: const Text('导出至剪贴板', style: style),
onTap: () {
Get.back();
exportToClipBoard(onExport: _onExport);
},
),
ListTile(
dense: true,
title: const Text('导出文件至本地', style: style),
onTap: () {
Get.back();
exportToLocalFile(
onExport: _onExport,
localFileName: () => 'reply',
);
},
),
],
),
);
}
Future<void> _onImport(List<dynamic> list) async {
await GStorage.reply!.putAll({
for (var e in list)
e['id'].toString(): (ReplyInfo.create()..mergeFromProto3Json(e))
.writeToBuffer(),
});
if (mounted) {
_initReply();
setState(() {});
}
}
void _showImportDialog() {
const style = TextStyle(fontSize: 14);
showDialog(
context: context,
builder: (context) => SimpleDialog(
clipBehavior: .hardEdge,
contentPadding: const .symmetric(vertical: 12),
children: [
ListTile(
dense: true,
title: const Text('从剪贴板导入', style: style),
onTap: () {
Get.back();
importFromClipBoard<List<dynamic>>(
context,
title: '评论',
onExport: _onExport,
onImport: _onImport,
showConfirmDialog: false,
);
},
),
ListTile(
dense: true,
title: const Text('从本地文件导入', style: style),
onTap: () {
Get.back();
importFromLocalFile<List<dynamic>>(onImport: _onImport);
},
),
],
),
);
}
}