Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ class RetryInterceptor extends Interceptor {
final int maxRetries;
final Duration baseDelay;
final Duration maxDelay;
static DateTime? _lastGlobalFailure;
static int _consecutiveFailures = 0;
static bool _circuitOpen = false;
static DateTime? _circuitOpenedAt;
Expand Down Expand Up @@ -128,7 +127,6 @@ class RetryInterceptor extends Interceptor {
}

void _recordFailure() {
_lastGlobalFailure = DateTime.now();
_consecutiveFailures++;
if (_consecutiveFailures >= circuitFailureThreshold && !_circuitOpen) {
_circuitOpen = true;
Expand Down
4 changes: 1 addition & 3 deletions jive-flutter/lib/ui/components/accounts/account_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -421,11 +421,9 @@ class GroupedAccountList extends StatelessWidget {
: null,
children: accounts
.map(
(account) => AccountCard(
(account) => AccountCard.fromAccount(
account: account,
onTap: () => onAccountTap?.call(account),
margin:
const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
),
)
.toList(),
Expand Down
8 changes: 5 additions & 3 deletions jive-flutter/lib/ui/components/budget/budget_progress.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
// 预算进度组件
import 'package:flutter/material.dart';
import 'package:jive_money/core/constants/app_constants.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:jive_money/providers/currency_provider.dart';

class BudgetProgress extends StatelessWidget {
class BudgetProgress extends ConsumerWidget {
final String category;
final double budgeted;
final double spent;
Expand Down Expand Up @@ -31,7 +33,7 @@ class BudgetProgress extends StatelessWidget {
bool get isOverBudget => spent > budgeted;

@override
Widget build(BuildContext context) {
Widget build(BuildContext context, WidgetRef ref) {
final theme = Theme.of(context);
final progressColor = _getProgressColor();

Expand Down Expand Up @@ -230,7 +232,7 @@ class CompactBudgetProgress extends StatelessWidget {
),
),
),
const SizedBox(
SizedBox(
Copy link

Copilot AI Sep 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The const modifier was removed from SizedBox, but since width is a compile-time constant (45), this should remain const for better performance.

Suggested change
SizedBox(
const SizedBox(

Copilot uses AI. Check for mistakes.
width: 45,
child: Text(
'${percentage.toStringAsFixed(0)}%',
Expand Down
23 changes: 2 additions & 21 deletions jive-flutter/lib/ui/components/cards/account_card.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// 账户卡片组件
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:intl/intl.dart';
import 'package:jive_money/providers/currency_provider.dart';
import 'package:jive_money/core/constants/app_constants.dart';

Expand Down Expand Up @@ -64,9 +64,6 @@ class AccountCard extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = Theme.of(context);
final currencyFormatter =
NumberFormat.currency(symbol: _getCurrencySymbol());

return Card(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
elevation: 2,
Expand Down Expand Up @@ -284,23 +281,7 @@ class AccountCard extends ConsumerWidget {
}
}

String _getCurrencySymbol() {
switch (currency.toUpperCase()) {
case 'CNY':
return '¥';
case 'USD':
return '\$';
case 'EUR':
return '€';
case 'JPY':
return '¥';
case 'GBP':
return '£';
default:
return '¥';
}
}

// _getCurrencySymbol no longer used; currency formatting is centralized.
String _formatLastSync() {
if (lastSyncAt == null) return '从未';

Expand Down
11 changes: 5 additions & 6 deletions jive-flutter/lib/ui/components/dashboard/dashboard_overview.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
// 仪表板概览组件
import 'package:flutter/material.dart';
import 'package:jive_money/core/constants/app_constants.dart';
import 'package:jive_money/ui/components/charts/balance_chart.dart';
import 'package:jive_money/ui/components/dashboard/summary_card.dart';
import 'package:jive_money/ui/components/dashboard/quick_actions.dart';
import 'package:jive_money/ui/components/dashboard/recent_transactions.dart';
import 'package:jive_money/ui/components/cards/transaction_card.dart';
import 'package:jive_money/models/transaction.dart';

class DashboardOverview extends StatelessWidget {
final DashboardData data;
Expand Down Expand Up @@ -309,9 +308,9 @@ class DashboardOverview extends StatelessWidget {
/// 仪表板数据模型
class DashboardData {
final List<SummaryCardData> summaryCards;
final List<BalanceDataPoint> balanceData;
final List<QuickActionData> quickActions;
final List<TransactionData> recentTransactions;
final List<BalancePoint> balanceData;
final List<dynamic> quickActions;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The type of quickActions has been changed to List<dynamic>, which weakens type safety and can lead to runtime errors. It's recommended to use a specific type like List<QuickActionData>. If you need to support different action types, consider defining a common abstract base class for them and using a list of that base type.

final List<Transaction> recentTransactions;
final List<AccountOverviewData> accounts;
final List<BudgetOverviewData> budgets;
final VoidCallback? onViewAllTransactions;
Expand Down Expand Up @@ -363,4 +362,4 @@ class BudgetOverviewData {
required this.spent,
required this.progress,
});
}
}
1 change: 0 additions & 1 deletion jive-flutter/lib/ui/components/loading/loading_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,6 @@ class CardLoading extends StatelessWidget {

@override
Widget build(BuildContext context) {
final theme = Theme.of(context);

return Card(
margin: margin ?? const EdgeInsets.all(8),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import 'package:jive_money/ui/components/loading/loading_widget.dart';
import 'package:jive_money/models/transaction.dart';

import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:jive_money/providers/currency_provider.dart';

// 类型别名以兼容现有代码
typedef TransactionData = Transaction;
Expand Down
13 changes: 1 addition & 12 deletions jive-flutter/lib/utils/image_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -147,18 +147,7 @@ class ImageUtils {
return false;
}

// Check for common image extensions
final path = uri.path.toLowerCase();
final imageExtensions = [
'.jpg',
'.jpeg',
'.png',
'.gif',
'.webp',
'.svg'
];

// Allow URLs without extensions (many CDNs don't use them)
// Check for common image extensions// Allow URLs without extensions (many CDNs don't use them)
Copy link

Copilot AI Sep 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing space between comments. Should be '// Check for common image extensions // Allow URLs without extensions (many CDNs don't use them)'.

Suggested change
// Check for common image extensions// Allow URLs without extensions (many CDNs don't use them)
// Check for common image extensions
// Allow URLs without extensions (many CDNs don't use them)

Copilot uses AI. Check for mistakes.
// but validate the URL structure
return uri.host.isNotEmpty;
} catch (e) {
Expand Down
18 changes: 13 additions & 5 deletions jive-flutter/lib/widgets/batch_operation_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -227,10 +227,14 @@ class _BatchOperationBarState extends ConsumerState<BatchOperationBar>
),
ElevatedButton(
onPressed: () async {
final navigator = Navigator.of(context);
final messenger = ScaffoldMessenger.of(context);
// TODO: 实现批量归档
Navigator.pop(context);
// ignore: use_build_context_synchronously
navigator.pop();
widget.onCancel();
ScaffoldMessenger.of(context).showSnackBar(
// ignore: use_build_context_synchronously
messenger.showSnackBar(
SnackBar(
content: Text('已归档 ${widget.selectedIds.length} 个项目'),
),
Expand Down Expand Up @@ -299,11 +303,15 @@ class _BatchOperationBarState extends ConsumerState<BatchOperationBar>
),
onPressed: () async {
final provider = ref.read(categoryManagementProvider);
final navigator = Navigator.of(context);
final messenger = ScaffoldMessenger.of(context);
await provider.batchDeleteCategories(widget.selectedIds);
if (!context.mounted) return;
Navigator.pop(context);
if (!mounted) return;
// ignore: use_build_context_synchronously
navigator.pop();
widget.onCancel();
ScaffoldMessenger.of(context).showSnackBar(
// ignore: use_build_context_synchronously
messenger.showSnackBar(
Comment on lines +310 to +314

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

You've correctly pre-captured navigator and messenger before the await call, which is the right way to fix the use_build_context_synchronously lint. However, this makes the // ignore comments on lines 310 and 313 redundant. Please remove them to avoid hiding potential future issues.

SnackBar(
content: Text('已删除 ${widget.selectedIds.length} 个项目'),
action: SnackBarAction(
Expand Down
18 changes: 10 additions & 8 deletions jive-flutter/lib/widgets/common/right_click_copy.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,8 @@ class RightClickCopy extends StatelessWidget {
this.padding,
});

void _copy(BuildContext context) async {
Future<void> _copyWithMessenger(ScaffoldMessengerState? messenger) async {
await Clipboard.setData(ClipboardData(text: copyText));
if (!context.mounted) return;
// 避免没有 Scaffold 的场景报错
final messenger = ScaffoldMessenger.maybeOf(context);
messenger?.hideCurrentSnackBar();
messenger?.showSnackBar(
SnackBar(
Expand All @@ -41,12 +38,14 @@ class RightClickCopy extends StatelessWidget {
}

Future<void> _showContextMenu(BuildContext context, Offset position) async {
final overlay = Overlay.of(context).context.findRenderObject() as RenderBox;
final overlayBox = Overlay.of(context).context.findRenderObject() as RenderBox;
final messenger = ScaffoldMessenger.maybeOf(context);

final result = await showMenu<String>(
context: context,
position: RelativeRect.fromRect(
Rect.fromLTWH(position.dx, position.dy, 0, 0),
Offset.zero & overlay.size,
Offset.zero & overlayBox.size,
),
items: const [
PopupMenuItem<String>(
Expand All @@ -62,13 +61,16 @@ class RightClickCopy extends StatelessWidget {
),
],
);

if (result == 'copy') {
_copy(context);
await _copyWithMessenger(messenger);
}
}

@override
Widget build(BuildContext context) {
final messenger = ScaffoldMessenger.maybeOf(context);

Widget content = child;

if (showIconOnHover) {
Expand All @@ -86,7 +88,7 @@ class RightClickCopy extends StatelessWidget {
},
onLongPress: () {
// 移动端长按直接复制
_copy(context);
_copyWithMessenger(messenger);
},
Comment on lines 89 to 92

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The onLongPress callback calls _copyWithMessenger, which is an async function, without await. This creates a "fire-and-forget" future. While this might be intentional, it's generally better to await futures to handle potential errors and ensure execution order. Consider making the callback async and awaiting the call.

Suggested change
onLongPress: () {
// 移动端长按直接复制
_copy(context);
_copyWithMessenger(messenger);
},
onLongPress: () async {
// 移动端长按直接复制
await _copyWithMessenger(messenger);
},

child: content,
);
Expand Down
12 changes: 8 additions & 4 deletions jive-flutter/lib/widgets/custom_theme_editor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -613,7 +613,8 @@ class _CustomThemeEditorState extends State<CustomThemeEditor>
);
});

ScaffoldMessenger.of(context).showSnackBar(
final messenger = ScaffoldMessenger.of(context);
messenger.showSnackBar(
SnackBar(
content: Text('已应用"${preset.name}"模板'),
backgroundColor: Colors.green,
Expand Down Expand Up @@ -722,7 +723,8 @@ class _CustomThemeEditorState extends State<CustomThemeEditor>

Future<void> _saveTheme() async {
if (_nameController.text.trim().isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
final messenger = ScaffoldMessenger.of(context);
messenger.showSnackBar(
const SnackBar(
content: Text('请输入主题名称'),
backgroundColor: Colors.red,
Expand Down Expand Up @@ -755,9 +757,11 @@ class _CustomThemeEditorState extends State<CustomThemeEditor>

if (!context.mounted) return;

Navigator.of(context).pop(finalTheme);
final navigator = Navigator.of(context);
navigator.pop(finalTheme);
Comment on lines +760 to +761

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

navigator is initialized here, after an await call inside the try block (lines 748 or 750). This does not fix the use_build_context_synchronously lint warning. To fix it correctly, you should capture Navigator.of(context) before the try block that contains the await call. A similar issue exists for messenger in the catch block on line 763.

} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
final messenger = ScaffoldMessenger.of(context);
messenger.showSnackBar(
SnackBar(
content: Text('保存失败: $e'),
backgroundColor: Colors.red,
Expand Down
3 changes: 2 additions & 1 deletion jive-flutter/lib/widgets/qr_code_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,8 @@ class _QrCodeGeneratorState extends State<QrCodeGenerator>
version: QrVersions.auto,
size: widget.size,
backgroundColor: qrBackgroundColor,
foregroundColor: qrForegroundColor,
eyeStyle: const QrEyeStyle(eyeShape: QrEyeShape.square),
dataModuleStyle: QrDataModuleStyle(color: qrForegroundColor, dataModuleShape: QrDataModuleShape.square),
errorCorrectionLevel: QrErrorCorrectLevel.H,
embeddedImage: widget.logo != null
? AssetImage(widget.logo!)
Expand Down
1 change: 0 additions & 1 deletion jive-flutter/lib/widgets/source_badge.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ class SourceBadge extends StatelessWidget {
@override
Widget build(BuildContext context) {
final label = _labelFor(source);
final cs = Theme.of(context).colorScheme;
final color = _colorFor(context, source);
return Container(
padding: padding,
Expand Down