mirror of
https://github.com/GopeedLab/gopeed.git
synced 2026-11-08 17:40:28 +00:00
perf(extensions): load store summaries and check updates asynchronously (#1583)
This commit is contained in:
41 files changed
+1567
-257
No files matched your search
@@ -3,7 +3,7 @@ import 'package:shadcn_flutter/shadcn_flutter.dart';
|
||||
import '../../../../l10n/l10n.dart';
|
||||
import '../../../../shared/theme/app_design_tokens.dart';
|
||||
import '../../../../shared/theme/app_palette.dart';
|
||||
import '../../../../shared/widgets/app_text_field.dart';
|
||||
import '../../../../shared/widgets/app_search_field.dart';
|
||||
|
||||
class HistorySearchField extends StatelessWidget {
|
||||
const HistorySearchField({super.key, required this.controller, required this.onChanged});
|
||||
@@ -18,7 +18,7 @@ class HistorySearchField extends StatelessWidget {
|
||||
return ConstrainedBox(
|
||||
// Allow large accessibility text to grow instead of clipping the line.
|
||||
constraints: const BoxConstraints(minHeight: 38),
|
||||
child: AppTextField(
|
||||
child: AppSearchField(
|
||||
key: const ValueKey('create-history-filter'),
|
||||
controller: controller,
|
||||
style: textStyle,
|
||||
@@ -30,7 +30,7 @@ class HistorySearchField extends StatelessWidget {
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
features: [InputFeature.leading(Icon(Icons.search_rounded, size: 15, color: palette.textMuted))],
|
||||
iconSize: 15,
|
||||
onChanged: onChanged,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import 'package:shadcn_flutter/shadcn_flutter.dart';
|
||||
|
||||
import '../../../../l10n/l10n.dart';
|
||||
import '../../../../shared/theme/app_palette.dart';
|
||||
import '../../../../shared/widgets/app_tooltip.dart';
|
||||
import '../../../../shared/widgets/app_empty_state.dart';
|
||||
|
||||
/// Keeps the action inside the localized sentence, including RTL word order.
|
||||
class TaskEmptyCreateHint extends StatelessWidget {
|
||||
const TaskEmptyCreateHint({super.key, required this.onCreateTask});
|
||||
|
||||
@@ -12,38 +10,12 @@ class TaskEmptyCreateHint extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
const actionMarker = '\uFFFC';
|
||||
final parts = context.l10n.emptyTaskCreateHint(actionMarker).split(actionMarker);
|
||||
return Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(text: parts.first.trimRight()),
|
||||
WidgetSpan(
|
||||
alignment: PlaceholderAlignment.middle,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
child: AppTooltip(
|
||||
message: context.l10n.create,
|
||||
child: Semantics(
|
||||
label: context.l10n.create,
|
||||
child: SizedBox.square(
|
||||
dimension: 30,
|
||||
child: IconButton.outline(
|
||||
key: const ValueKey('tasks-empty-create-button'),
|
||||
density: ButtonDensity.compact,
|
||||
onPressed: onCreateTask,
|
||||
icon: const Icon(Icons.add, size: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
TextSpan(text: parts.last.trimLeft()),
|
||||
],
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(color: AppPalette.of(context).textMuted, fontSize: 13),
|
||||
return AppEmptyStateAction(
|
||||
buttonKey: const ValueKey('tasks-empty-create-button'),
|
||||
icon: Icons.add,
|
||||
label: context.l10n.create,
|
||||
hint: context.l10n.emptyTaskCreateHint,
|
||||
onPressed: onCreateTask,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import '../../../../shared/theme/app_palette.dart';
|
||||
import '../../../../shared/theme/app_design_tokens.dart';
|
||||
import 'task_empty_create_hint.dart';
|
||||
import '../../../../l10n/l10n.dart';
|
||||
import '../../../../shared/widgets/app_empty_state.dart';
|
||||
import 'task_empty_create_hint.dart';
|
||||
|
||||
class TaskEmptyState extends StatelessWidget {
|
||||
const TaskEmptyState({super.key, this.message, this.onCreateTask});
|
||||
@@ -13,101 +12,11 @@ class TaskEmptyState extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final palette = AppPalette.of(context);
|
||||
return Center(
|
||||
child: SingleChildScrollView(
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 360),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
CustomPaint(
|
||||
key: const ValueKey('task-empty-illustration'),
|
||||
size: const Size(148, 108),
|
||||
painter: _TaskEmptyIllustrationPainter(palette: palette),
|
||||
),
|
||||
const SizedBox(height: 18),
|
||||
Text(
|
||||
message ?? context.l10n.emptyTaskList,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(color: palette.textPrimary, fontSize: 17, fontWeight: FontWeight.w700),
|
||||
),
|
||||
if (onCreateTask != null) ...[
|
||||
const SizedBox(height: AppDesignTokens.space24),
|
||||
TaskEmptyCreateHint(onCreateTask: onCreateTask!),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
return AppEmptyState(
|
||||
message: message ?? context.l10n.emptyTaskList,
|
||||
illustration: AppEmptyIllustrationType.inbox,
|
||||
illustrationKey: const ValueKey('task-empty-illustration'),
|
||||
action: onCreateTask == null ? null : TaskEmptyCreateHint(onCreateTask: onCreateTask!),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _TaskEmptyIllustrationPainter extends CustomPainter {
|
||||
const _TaskEmptyIllustrationPainter({required this.palette});
|
||||
|
||||
final AppPalette palette;
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final borderPaint = Paint()
|
||||
..color = palette.border
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = 1.4;
|
||||
final sheetRect = RRect.fromRectAndRadius(Rect.fromLTWH(size.width / 2 - 34, 6, 68, 72), const Radius.circular(7));
|
||||
|
||||
canvas.drawRRect(sheetRect.shift(const Offset(0, 3)), Paint()..color = palette.textMuted.withValues(alpha: 0.06));
|
||||
canvas.drawRRect(sheetRect, Paint()..color = palette.cardBg);
|
||||
canvas.drawRRect(sheetRect, borderPaint);
|
||||
|
||||
final rowPaint = Paint()
|
||||
..color = palette.textMuted.withValues(alpha: 0.34)
|
||||
..strokeWidth = 2
|
||||
..strokeCap = StrokeCap.round;
|
||||
final centerX = size.width / 2;
|
||||
for (var row = 0; row < 2; row++) {
|
||||
final y = 20.0 + row * 14;
|
||||
canvas.drawCircle(Offset(centerX - 22, y), 2.5, Paint()..color = palette.surfaceSoft);
|
||||
canvas.drawLine(Offset(centerX - 14, y), Offset(centerX + 21, y), rowPaint);
|
||||
}
|
||||
|
||||
final emptyBadgePaint = Paint()
|
||||
..color = palette.surfaceSoft
|
||||
..style = PaintingStyle.fill;
|
||||
canvas.drawCircle(Offset(centerX, 56), 11, emptyBadgePaint);
|
||||
canvas.drawCircle(Offset(centerX, 56), 11, borderPaint);
|
||||
canvas.drawLine(Offset(centerX - 4, 56), Offset(centerX + 4, 56), rowPaint);
|
||||
|
||||
final trayPath = Path()
|
||||
..moveTo(26, 63)
|
||||
..lineTo(49, 63)
|
||||
..lineTo(57, 75)
|
||||
..quadraticBezierTo(59, 78, 63, 78)
|
||||
..lineTo(85, 78)
|
||||
..quadraticBezierTo(89, 78, 91, 75)
|
||||
..lineTo(99, 63)
|
||||
..lineTo(122, 63)
|
||||
..lineTo(115, 96)
|
||||
..quadraticBezierTo(114, 101, 108, 101)
|
||||
..lineTo(40, 101)
|
||||
..quadraticBezierTo(34, 101, 33, 96)
|
||||
..close();
|
||||
canvas.drawPath(trayPath, Paint()..color = palette.surfaceSoft);
|
||||
final trayPaint = Paint()
|
||||
..color = palette.textSecondary.withValues(alpha: 0.72)
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = 1.8
|
||||
..strokeCap = StrokeCap.round
|
||||
..strokeJoin = StrokeJoin.round;
|
||||
canvas.drawPath(trayPath, trayPaint);
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(_TaskEmptyIllustrationPainter oldDelegate) {
|
||||
return oldDelegate.palette != palette;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user