Compare commits

...
8 changed files with 123 additions and 111 deletions
+1
View File
@@ -290,6 +290,7 @@
"receiveNow": "Receive now",
"receiveLater": "Receive later",
"tokenSavedForLater": "Token saved to claim later",
"noConnectionTokenSaved": "No connection. Token saved to claim later.",
"pendingTokenLimitReached": "Pending tokens limit reached (max 50)",
"filterToReceive": "To receive",
"noPendingTokens": "No pending tokens",
+1
View File
@@ -410,6 +410,7 @@
"receiveNow": "Recibir ahora",
"receiveLater": "Recibir después",
"tokenSavedForLater": "Token guardado para reclamar después",
"noConnectionTokenSaved": "Sin conexión. Token guardado para reclamar después.",
"pendingTokenLimitReached": "Límite de tokens pendientes alcanzado (max 50)",
"filterToReceive": "Para recibir",
"noPendingTokens": "Sin tokens pendientes",
+80 -111
View File
@@ -3,6 +3,7 @@ import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'package:lucide_icons/lucide_icons.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
import '../../core/constants/colors.dart';
import '../../core/constants/dimensions.dart';
import '../../core/utils/formatters.dart';
@@ -114,20 +115,10 @@ class _ReceiveScreenState extends State<ReceiveScreen> {
),
),
// Botones guardar para después y reclamar (fijo abajo)
// Botón recibir (fijo abajo)
Padding(
padding: const EdgeInsets.all(AppDimensions.paddingMedium),
child: Column(
children: [
// Botón principal: Guardar para después
if (_isValidToken && _tokenInfo != null && !_isProcessing) ...[
_buildReceiveLaterButton(),
const SizedBox(height: AppDimensions.paddingSmall),
],
// Botón secundario: Reclamar ahora
_buildClaimButton(),
],
),
child: _buildReceiveButton(),
),
],
);
@@ -404,53 +395,91 @@ class _ReceiveScreenState extends State<ReceiveScreen> {
);
}
Widget _buildClaimButton() {
Widget _buildReceiveButton() {
final l10n = L10n.of(context)!;
// Botón principal (naranja) para reclamar - abajo, cerca de los dedos
return PrimaryButton(
text: _isProcessing ? l10n.claiming : l10n.receiveNow,
onPressed: _isValidToken && !_isProcessing ? _claimToken : null,
text: _isProcessing ? l10n.claiming : l10n.receive,
onPressed: _isValidToken && !_isProcessing ? _receiveToken : null,
);
}
Widget _buildReceiveLaterButton() {
final l10n = L10n.of(context)!;
// Botón secundario (outline) para guardar para después
return GestureDetector(
onTap: _saveForLater,
child: Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(vertical: 14),
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.08),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: Colors.white.withValues(alpha: 0.2),
width: 1,
/// Verifica si hay conexión a internet
Future<bool> _checkConnectivity() async {
final result = await Connectivity().checkConnectivity();
return !result.contains(ConnectivityResult.none);
}
/// Recibe el token: si hay conexión lo reclama, si no lo guarda para después
Future<void> _receiveToken() async {
if (_isProcessing) return;
setState(() => _isProcessing = true);
try {
final hasConnection = await _checkConnectivity();
if (hasConnection) {
await _claimToken();
} else {
await _saveForLaterOffline();
}
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Error: ${e.toString()}'),
backgroundColor: AppColors.error,
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
LucideIcons.clock,
color: AppColors.textSecondary,
size: 18,
);
}
} finally {
if (mounted && !_showSuccess) {
setState(() => _isProcessing = false);
}
}
}
/// Guarda el token para después cuando no hay conexión
Future<void> _saveForLaterOffline() async {
final l10n = L10n.of(context)!;
final walletProvider = context.read<WalletProvider>();
try {
final pending = await walletProvider.addPendingToken(
_tokenController.text.trim(),
);
if (pending != null) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(l10n.noConnectionTokenSaved),
backgroundColor: AppColors.warning,
duration: const Duration(seconds: 4),
),
const SizedBox(width: 8),
Text(
l10n.receiveLater,
style: TextStyle(
fontFamily: 'Inter',
fontSize: 16,
fontWeight: FontWeight.w500,
color: AppColors.textSecondary,
),
);
Navigator.pop(context);
}
} else {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(l10n.pendingTokenLimitReached),
backgroundColor: AppColors.warning,
duration: const Duration(seconds: 3),
),
],
),
),
);
);
}
}
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Error: ${e.toString()}'),
backgroundColor: AppColors.error,
),
);
}
}
}
Future<void> _pasteFromClipboard() async {
@@ -487,61 +516,8 @@ class _ReceiveScreenState extends State<ReceiveScreen> {
});
}
Future<void> _saveForLater() async {
if (_isProcessing) return; // Guard contra doble-tap
setState(() => _isProcessing = true);
final l10n = L10n.of(context)!;
final walletProvider = context.read<WalletProvider>();
try {
final pending = await walletProvider.addPendingToken(
_tokenController.text.trim(),
);
if (pending != null) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(l10n.tokenSavedForLater),
backgroundColor: AppColors.success,
duration: const Duration(seconds: 2),
),
);
Navigator.pop(context);
}
} else {
// Límite alcanzado
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(l10n.pendingTokenLimitReached),
backgroundColor: AppColors.warning,
duration: const Duration(seconds: 3),
),
);
}
}
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Error: ${e.toString()}'),
backgroundColor: AppColors.error,
),
);
}
} finally {
if (mounted) {
setState(() => _isProcessing = false);
}
}
}
Future<void> _claimToken() async {
setState(() {
_isProcessing = true;
_errorMessage = null;
});
@@ -561,7 +537,6 @@ class _ReceiveScreenState extends State<ReceiveScreen> {
_receivedAmount = amountReceived;
_receivedUnit = detectedUnit; // Usar unidad del token
_showSuccess = true;
_isProcessing = false;
});
// Disparar confetti
@@ -583,12 +558,6 @@ class _ReceiveScreenState extends State<ReceiveScreen> {
setState(() {
_errorMessage = errorMessage;
});
} finally {
if (mounted && !_showSuccess) {
setState(() {
_isProcessing = false;
});
}
}
}
}
@@ -5,6 +5,7 @@
import FlutterMacOS
import Foundation
import connectivity_plus
import flutter_secure_storage_macos
import path_provider_foundation
import share_plus
@@ -13,6 +14,7 @@ import sqflite_darwin
import url_launcher_macos
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
+32
View File
@@ -106,6 +106,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.19.0"
connectivity_plus:
dependency: "direct main"
description:
name: connectivity_plus
sha256: b5e72753cf63becce2c61fd04dfe0f1c430cc5278b53a1342dc5ad839eab29ec
url: "https://pub.dev"
source: hosted
version: "6.1.5"
connectivity_plus_platform_interface:
dependency: transitive
description:
name: connectivity_plus_platform_interface
sha256: "42657c1715d48b167930d5f34d00222ac100475f73d10162ddf43e714932f204"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
convert:
dependency: transitive
description:
@@ -146,6 +162,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.8"
dbus:
dependency: transitive
description:
name: dbus
sha256: d0c98dcd4f5169878b6cf8f6e0a52403a9dff371a3e2f019697accbf6f44a270
url: "https://pub.dev"
source: hosted
version: "0.7.12"
fake_async:
dependency: transitive
description:
@@ -446,6 +470,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.0"
nm:
dependency: transitive
description:
name: nm
sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254"
url: "https://pub.dev"
source: hosted
version: "0.5.0"
path:
dependency: transitive
description:
+3
View File
@@ -52,6 +52,9 @@ dependencies:
# UUID generator for pending tokens
uuid: ^4.3.3
# Connectivity detection
connectivity_plus: ^6.0.5
dev_dependencies:
flutter_test:
sdk: flutter
@@ -6,11 +6,14 @@
#include "generated_plugin_registrant.h"
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
#include <share_plus/share_plus_windows_plugin_c_api.h>
#include <url_launcher_windows/url_launcher_windows.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
SharePlusWindowsPluginCApiRegisterWithRegistrar(
+1
View File
@@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
connectivity_plus
flutter_secure_storage_windows
share_plus
url_launcher_windows