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
2 changes: 1 addition & 1 deletion lib/features/home/screens/home_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class HomeScreen extends ConsumerWidget {
),
),
Positioned(
bottom: 100,
bottom: 80 + MediaQuery.of(context).viewPadding.bottom + 16,
right: 16,
child: const AddOrderButton(),
),
Expand Down
114 changes: 73 additions & 41 deletions lib/features/home/widgets/order_list_item.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ class OrderListItem extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
ref.watch(timeProvider);

// Determine if the premium is positive or negative for the color
// Determine if this is a fixed order (has specific sats amount and is not zero)
final bool isFixedOrder =
order.amount != null && order.amount != "0" && order.amount!.isNotEmpty;
Comment on lines +20 to +22
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify the logic for determining fixed orders.

The logic checks for non-null, non-zero, and non-empty amount, but should we also validate that the amount is a valid number?


🏁 Script executed:

#!/bin/bash
# Description: Search for other usages of order.amount to understand the expected data format and validation patterns

# Search for other places where order.amount is used
rg -A 3 -B 3 "order\.amount" --type dart

# Look for amount validation patterns
rg -A 5 "amount.*validation\|validate.*amount" --type dart

Length of output: 3143


Validate order.amount as a numeric value before treating it as “fixed”

To avoid treating non‐numeric or malformed strings as valid sat amounts, parse and check the numeric value instead of just non‐null/non‐empty:

lib/features/home/widgets/order_list_item.dart (around lines 20–22):

- // Determine if this is a fixed order (has specific sats amount and is not zero)
- final bool isFixedOrder =
-     order.amount != null && order.amount != "0" && order.amount!.isNotEmpty;
+ // Determine if this is a fixed order (valid positive sats amount)
+ final int? satAmount = int.tryParse(order.amount ?? '');
+ final bool isFixedOrder = satAmount != null && satAmount > 0;

This guarantees that only valid, positive integer amounts are treated as fixed orders.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Determine if this is a fixed order (has specific sats amount and is not zero)
final bool isFixedOrder =
order.amount != null && order.amount != "0" && order.amount!.isNotEmpty;
// Determine if this is a fixed order (valid positive sats amount)
final int? satAmount = int.tryParse(order.amount ?? '');
final bool isFixedOrder = satAmount != null && satAmount > 0;
🤖 Prompt for AI Agents
In lib/features/home/widgets/order_list_item.dart around lines 20 to 22, the
current logic to determine if an order is fixed only checks if order.amount is
non-null, non-zero, and non-empty, but does not verify if it is a valid numeric
value. Update the code to parse order.amount as an integer and confirm it is a
positive number before setting isFixedOrder to true. This ensures only valid
numeric amounts are considered fixed orders.


final premiumValue =
order.premium != null ? double.tryParse(order.premium!) ?? 0.0 : 0.0;
final isPremiumPositive = premiumValue >= 0;
Expand Down Expand Up @@ -105,53 +108,82 @@ class OrderListItem extends ConsumerWidget {
),
),

// Second row: Amount and currency with flag and percentage
// Second row: Amount and currency with flag and percentage (if not fixed)
Padding(
padding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
child: Row(
crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.alphabetic,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Large amount with more contrast
Text(
order.fiatAmount.toString(),
style: const TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: Colors.white,
height: 1.1,
),
),
const SizedBox(width: 8),
Row(
crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.alphabetic,
children: [
// Large amount with more contrast
Text(
order.fiatAmount.toString(),
style: const TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: Colors.white,
height: 1.1,
),
),
const SizedBox(width: 8),

// Currency code and flag
Text(
'${order.currency ?? "CUP"} ',
style: const TextStyle(
fontSize: 18,
color: Colors.white,
),
),
Text(
() {
final String currencyCode = order.currency ?? 'CUP';
return CurrencyUtils.getFlagFromCurrency(
currencyCode) ??
'';
}(),
style: const TextStyle(fontSize: 18),
// Currency code and flag
Text(
'${order.currency ?? "CUP"} ',
style: const TextStyle(
fontSize: 18,
color: Colors.white,
),
),
Text(
() {
final String currencyCode = order.currency ?? 'CUP';
return CurrencyUtils.getFlagFromCurrency(
currencyCode) ??
'';
}(),
style: const TextStyle(fontSize: 18),
),
const SizedBox(width: 4),
],
),
const SizedBox(width: 4),

// Percentage with more vibrant color
Text(
premiumText,
style: TextStyle(
fontSize: 16,
color: premiumColor,
fontWeight: FontWeight.w600,
),
// Display sats amount for all orders (simplified)
Padding(
padding: const EdgeInsets.only(top: 4),
child: isFixedOrder
? Text(
'For ${order.amount!} sats',
style: TextStyle(
fontSize: 14,
color: Colors.white70,
fontWeight: FontWeight.w500,
),
)
: Row(
children: [
Text(
'Market Price ',
style: TextStyle(
fontSize: 14,
color: Colors.white70,
fontWeight: FontWeight.w500,
),
),
Text(
premiumText,
style: TextStyle(
fontSize: 14,
color: premiumColor,
fontWeight: FontWeight.w500,
),
),
],
),
),
],
),
Expand Down
8 changes: 8 additions & 0 deletions lib/features/order/screens/add_order_screen.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:mostro_mobile/data/models/enums/order_type.dart';
Expand Down Expand Up @@ -187,8 +188,15 @@ class _AddOrderScreenState extends ConsumerState<AddOrderScreen> {
if (!_marketRate && (value == null || value.isEmpty)) {
return 'Please enter sats amount';
}
if (!_marketRate && !RegExp(r'^[0-9]+$').hasMatch(value!)) {
return 'Please enter numbers only';
}
return null;
},
// Restricting input to numbers only
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
],
),
),
],
Expand Down
71 changes: 65 additions & 6 deletions lib/features/order/widgets/form_section.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class FormSection extends StatelessWidget {
final Color iconBackgroundColor;
final Widget child;
final Widget? extraContent;
final String? infoTooltip;

const FormSection({
super.key,
Expand All @@ -15,6 +16,7 @@ class FormSection extends StatelessWidget {
required this.iconBackgroundColor,
required this.child,
this.extraContent,
this.infoTooltip,
});

@override
Expand All @@ -29,12 +31,69 @@ class FormSection extends StatelessWidget {
children: [
Padding(
padding: const EdgeInsets.fromLTRB(16, 16, 16, 8),
child: Text(
title,
style: TextStyle(
color: Colors.white.withOpacity(0.7),
fontSize: 14,
),
child: Row(
children: [
Text(
title,
style: TextStyle(
color: Colors.white.withOpacity(0.7),
fontSize: 14,
),
),
if (infoTooltip != null) ...[
const SizedBox(width: 4),
GestureDetector(
onTap: () {
showDialog(
context: context,
builder: (context) => Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
backgroundColor: const Color(0xFF1E2230),
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 12.0),
child: Text(
infoTooltip!,
style: const TextStyle(color: Colors.white, fontSize: 16, height: 1.4),
textAlign: TextAlign.center,
),
),
const SizedBox(height: 16),
SizedBox(
width: double.infinity,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF8CC63F),
foregroundColor: Colors.black,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
padding: const EdgeInsets.symmetric(vertical: 12),
),
onPressed: () => Navigator.of(context).pop(),
child: const Text('OK', style: TextStyle(fontWeight: FontWeight.bold)),
),
),
],
),
),
),
);
},
child: Icon(
Icons.info_outline,
size: 14,
color: AppTheme.textSubtle,
),
),
],
],
),
),
Container(
Expand Down
17 changes: 3 additions & 14 deletions lib/features/order/widgets/premium_section.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ class PremiumSection extends StatelessWidget {

@override
Widget build(BuildContext context) {
// Define the premium value display as the icon
// Define the premium value display as the icon - showing only whole numbers
final premiumValueIcon = Text(
value.toStringAsFixed(1),
value.round().toString(),
style: const TextStyle(color: AppTheme.textPrimary, fontSize: 14),
);

Expand All @@ -25,6 +25,7 @@ class PremiumSection extends StatelessWidget {
title: 'Premium (%) ',
icon: premiumValueIcon,
iconBackgroundColor: AppTheme.purpleAccent, // Purple color for premium
infoTooltip: 'Adjust how much above or below the market price you want your offer. By default, it\'s set to 0%, with no premium or discount, so if you don\'t want to change the price, you can leave it as is.',
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expand Down Expand Up @@ -64,18 +65,6 @@ class PremiumSection extends StatelessWidget {
),
],
),
// Add an info icon as extra content
extraContent: Padding(
padding: const EdgeInsets.only(right: 16, bottom: 8),
child: Align(
alignment: Alignment.centerRight,
child: Icon(
Icons.info_outline,
size: 14,
color: AppTheme.textSubtle,
),
),
),
);
}
}
19 changes: 4 additions & 15 deletions lib/features/order/widgets/price_type_section.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,13 @@ class PriceTypeSection extends StatelessWidget {
title: 'Price type',
icon: priceTypeIcon,
iconBackgroundColor: AppTheme.purpleAccent.withOpacity(0.3), // Purple color consistent with other sections
infoTooltip: '• Select Market Price if you want to use the price that Bitcoin has when someone takes your offer.\n• Select Fixed Price if you want to define the exact amount of Bitcoin you will exchange.',
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'Market price',
style: TextStyle(
Text(
isMarketRate ? 'Market price' : 'Fixed price',
style: const TextStyle(
color: AppTheme.textPrimary, fontWeight: FontWeight.w500),
),
Row(
Expand All @@ -53,18 +54,6 @@ class PriceTypeSection extends StatelessWidget {
),
],
),
// Add info icon as extra content
extraContent: Padding(
padding: const EdgeInsets.only(right: 16, bottom: 8),
child: Align(
alignment: Alignment.centerRight,
child: Icon(
Icons.info_outline,
size: 14,
color: AppTheme.textSubtle,
),
),
),
);
}
}
Loading