Add log export functionality.

This commit is contained in:
2025-07-23 14:35:27 +08:00
parent 6245399810
commit 9d8ade6fe0
3 changed files with 38 additions and 43 deletions

View File

@@ -404,7 +404,8 @@
"Reload": "重载", "Reload": "重载",
"Disable Length Limitation": "禁用长度限制", "Disable Length Limitation": "禁用长度限制",
"Only valid for this run": "仅对本次运行有效", "Only valid for this run": "仅对本次运行有效",
"Logs": "日志" "Logs": "日志",
"Export logs": "导出日志"
}, },
"zh_TW": { "zh_TW": {
"Home": "首頁", "Home": "首頁",
@@ -811,6 +812,7 @@
"Reload": "重載", "Reload": "重載",
"Disable Length Limitation": "禁用長度限制", "Disable Length Limitation": "禁用長度限制",
"Only valid for this run": "僅對本次運行有效", "Only valid for this run": "僅對本次運行有效",
"Logs": "日誌" "Logs": "日誌",
"Export logs": "匯出日誌"
} }
} }

View File

@@ -1,5 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'dart:collection'; import 'dart:collection';
import 'dart:convert';
import 'dart:math' as math; import 'dart:math' as math;
import 'dart:ui' as ui; import 'dart:ui' as ui;
@@ -21,11 +22,13 @@ import 'package:venera/foundation/image_provider/cached_image.dart';
import 'package:venera/foundation/image_provider/history_image_provider.dart'; import 'package:venera/foundation/image_provider/history_image_provider.dart';
import 'package:venera/foundation/image_provider/local_comic_image.dart'; import 'package:venera/foundation/image_provider/local_comic_image.dart';
import 'package:venera/foundation/local.dart'; import 'package:venera/foundation/local.dart';
import 'package:venera/foundation/log.dart';
import 'package:venera/foundation/res.dart'; import 'package:venera/foundation/res.dart';
import 'package:venera/network/cloudflare.dart'; import 'package:venera/network/cloudflare.dart';
import 'package:venera/pages/comic_details_page/comic_page.dart'; import 'package:venera/pages/comic_details_page/comic_page.dart';
import 'package:venera/pages/favorites/favorites_page.dart'; import 'package:venera/pages/favorites/favorites_page.dart';
import 'package:venera/utils/ext.dart'; import 'package:venera/utils/ext.dart';
import 'package:venera/utils/io.dart';
import 'package:venera/utils/tags_translation.dart'; import 'package:venera/utils/tags_translation.dart';
import 'package:venera/utils/translations.dart'; import 'package:venera/utils/translations.dart';

View File

@@ -41,18 +41,22 @@ class NetworkError extends StatelessWidget {
], ],
), ),
), ),
const SizedBox( const SizedBox(height: 8),
height: 8,
),
Text( Text(
cfe == null ? message : "Cloudflare verification required".tl, cfe == null ? message : "Cloudflare verification required".tl,
textAlign: TextAlign.center, textAlign: TextAlign.center,
maxLines: 3, maxLines: 3,
), ),
if (retry != null) TextButton(
const SizedBox( onPressed: () {
height: 12, saveFile(
), data: utf8.encode(Log().toString()),
filename: 'log.txt',
);
},
child: Text("Export logs".tl),
),
const SizedBox(height: 8),
if (retry != null) if (retry != null)
if (cfe != null) if (cfe != null)
FilledButton( FilledButton(
@@ -74,15 +78,11 @@ class NetworkError extends StatelessWidget {
body = Column( body = Column(
children: [ children: [
const Appbar(title: Text("")), const Appbar(title: Text("")),
Expanded( Expanded(child: body),
child: body,
)
], ],
); );
} }
return Material( return Material(child: body);
child: body,
);
} }
} }
@@ -94,9 +94,7 @@ class ListLoadingIndicator extends StatelessWidget {
return const SizedBox( return const SizedBox(
width: double.infinity, width: double.infinity,
height: 80, height: 80,
child: Center( child: Center(child: FiveDotLoadingAnimation()),
child: FiveDotLoadingAnimation(),
),
); );
} }
} }
@@ -108,10 +106,9 @@ class SliverListLoadingIndicator extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
// SliverToBoxAdapter can not been lazy loaded. // SliverToBoxAdapter can not been lazy loaded.
// Use SliverList to make sure the animation can be lazy loaded. // Use SliverList to make sure the animation can be lazy loaded.
return SliverList.list(children: const [ return SliverList.list(
SizedBox(), children: const [SizedBox(), ListLoadingIndicator()],
ListLoadingIndicator(), );
]);
} }
} }
@@ -178,10 +175,7 @@ abstract class LoadingState<T extends StatefulWidget, S extends Object>
} }
Widget buildError() { Widget buildError() {
return NetworkError( return NetworkError(message: error!, retry: retry);
message: error!,
retry: retry,
);
} }
@override @override
@@ -323,11 +317,7 @@ abstract class MultiPageLoadingState<T extends StatefulWidget, S extends Object>
} }
Widget buildError(BuildContext context, String error) { Widget buildError(BuildContext context, String error) {
return NetworkError( return NetworkError(withAppbar: false, message: error, retry: reset);
withAppbar: false,
message: error,
retry: reset,
);
} }
@override @override
@@ -388,7 +378,7 @@ class _FiveDotLoadingAnimationState extends State<FiveDotLoadingAnimation>
Colors.green, Colors.green,
Colors.blue, Colors.blue,
Colors.yellow, Colors.yellow,
Colors.purple Colors.purple,
]; ];
static const _padding = 12.0; static const _padding = 12.0;
@@ -400,16 +390,15 @@ class _FiveDotLoadingAnimationState extends State<FiveDotLoadingAnimation>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AnimatedBuilder( return AnimatedBuilder(
animation: _controller, animation: _controller,
builder: (context, child) { builder: (context, child) {
return SizedBox( return SizedBox(
width: _dotSize * 5 + _padding * 6, width: _dotSize * 5 + _padding * 6,
height: _height, height: _height,
child: Stack( child: Stack(children: List.generate(5, (index) => buildDot(index))),
children: List.generate(5, (index) => buildDot(index)), );
), },
); );
});
} }
Widget buildDot(int index) { Widget buildDot(int index) {
@@ -417,7 +406,8 @@ class _FiveDotLoadingAnimationState extends State<FiveDotLoadingAnimation>
var startValue = index * 0.8; var startValue = index * 0.8;
return Positioned( return Positioned(
left: index * _dotSize + (index + 1) * _padding, left: index * _dotSize + (index + 1) * _padding,
bottom: (math.sin(math.pi / 2 * (value - startValue).clamp(0, 2))) * bottom:
(math.sin(math.pi / 2 * (value - startValue).clamp(0, 2))) *
(_height - _dotSize), (_height - _dotSize),
child: Container( child: Container(
width: _dotSize, width: _dotSize,