Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3eaa0df12c | ||
|
|
a2976bd058 | ||
|
|
19fccc1325 |
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"))
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
connectivity_plus
|
||||
flutter_secure_storage_windows
|
||||
share_plus
|
||||
url_launcher_windows
|
||||
|
||||
Reference in New Issue
Block a user