✨ Transfer
This commit is contained in:
@@ -1186,5 +1186,16 @@
|
|||||||
"transfer": "Transfer",
|
"transfer": "Transfer",
|
||||||
"payment": "Payment",
|
"payment": "Payment",
|
||||||
"systemWallet": "System Wallet",
|
"systemWallet": "System Wallet",
|
||||||
"date": "Date"
|
"date": "Date",
|
||||||
|
"createTransfer": "Create Transfer",
|
||||||
|
"transferAmount": "Transfer Amount",
|
||||||
|
"selectPayee": "Select Payee",
|
||||||
|
"selectedPayee": "Selected Payee",
|
||||||
|
"noPayeeSelected": "No payee selected",
|
||||||
|
"selectPayeeToTransfer": "Select payee to transfer to",
|
||||||
|
"addRemark": "Add Remark",
|
||||||
|
"transferRemark": "Transfer Remark",
|
||||||
|
"addRemarkForTransfer": "Add remark for transfer",
|
||||||
|
"enterPinToConfirmTransfer": "Enter your 6-digit PIN to confirm transfer",
|
||||||
|
"transferCreatedSuccessfully": "Transfer created successfully!"
|
||||||
}
|
}
|
||||||
|
@@ -553,6 +553,451 @@ class _CreateFundSheetState extends State<CreateFundSheet> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CreateTransferSheet extends StatefulWidget {
|
||||||
|
const CreateTransferSheet({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<CreateTransferSheet> createState() => _CreateTransferSheetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CreateTransferSheetState extends State<CreateTransferSheet> {
|
||||||
|
final amountController = TextEditingController();
|
||||||
|
final remarkController = TextEditingController();
|
||||||
|
String selectedCurrency = 'golds';
|
||||||
|
SnAccount? selectedPayee;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
amountController.dispose();
|
||||||
|
remarkController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SheetScaffold(
|
||||||
|
titleText: 'createTransfer'.tr(),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
// Amount Section
|
||||||
|
Text(
|
||||||
|
'transferAmount'.tr(),
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(8),
|
||||||
|
TextField(
|
||||||
|
controller: amountController,
|
||||||
|
keyboardType: TextInputType.numberWithOptions(
|
||||||
|
decimal: true,
|
||||||
|
),
|
||||||
|
inputFormatters: [
|
||||||
|
FilteringTextInputFormatter.allow(
|
||||||
|
RegExp(r'^\d+\.?\d{0,2}'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'enterAmount'.tr(),
|
||||||
|
hintText: '0.00',
|
||||||
|
prefixIcon: Icon(kCurrencyIconData[selectedCurrency]),
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: Theme.of(
|
||||||
|
context,
|
||||||
|
).colorScheme.outline.withOpacity(0.2),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onTapOutside:
|
||||||
|
(_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
|
),
|
||||||
|
|
||||||
|
const Gap(16),
|
||||||
|
|
||||||
|
// Currency Selection
|
||||||
|
Text(
|
||||||
|
'selectCurrency'.tr(),
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(8),
|
||||||
|
DropdownButtonFormField<String>(
|
||||||
|
value: selectedCurrency,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: Theme.of(
|
||||||
|
context,
|
||||||
|
).colorScheme.outline.withOpacity(0.2),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
items:
|
||||||
|
kCurrencyIconData.keys.map((currency) {
|
||||||
|
return DropdownMenuItem(
|
||||||
|
value: currency,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(kCurrencyIconData[currency]),
|
||||||
|
const Gap(8),
|
||||||
|
Text(
|
||||||
|
'walletCurrency${currency[0].toUpperCase()}${currency.substring(1).toLowerCase()}'
|
||||||
|
.tr(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value != null) {
|
||||||
|
setState(() => selectedCurrency = value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
const Gap(16),
|
||||||
|
|
||||||
|
// Payee Selection Section
|
||||||
|
Text(
|
||||||
|
'selectPayee'.tr(),
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(8),
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context).colorScheme.surface,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
border: Border.all(
|
||||||
|
color: Theme.of(
|
||||||
|
context,
|
||||||
|
).colorScheme.outline.withOpacity(0.2),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child:
|
||||||
|
selectedPayee != null
|
||||||
|
? ListTile(
|
||||||
|
contentPadding: const EdgeInsets.only(
|
||||||
|
left: 20,
|
||||||
|
right: 12,
|
||||||
|
),
|
||||||
|
leading: ProfilePictureWidget(
|
||||||
|
file: selectedPayee!.profile.picture,
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
selectedPayee!.nick,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
subtitle: Text(
|
||||||
|
'selectedPayee'.tr(),
|
||||||
|
style: Theme.of(
|
||||||
|
context,
|
||||||
|
).textTheme.bodySmall?.copyWith(
|
||||||
|
color:
|
||||||
|
Theme.of(
|
||||||
|
context,
|
||||||
|
).colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
trailing: IconButton(
|
||||||
|
onPressed:
|
||||||
|
() => setState(() => selectedPayee = null),
|
||||||
|
icon: Icon(
|
||||||
|
Icons.clear,
|
||||||
|
color: Theme.of(context).colorScheme.error,
|
||||||
|
),
|
||||||
|
tooltip: 'Remove payee',
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.person_add_outlined,
|
||||||
|
size: 48,
|
||||||
|
color:
|
||||||
|
Theme.of(
|
||||||
|
context,
|
||||||
|
).colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
const Gap(8),
|
||||||
|
Text(
|
||||||
|
'noPayeeSelected'.tr(),
|
||||||
|
style: Theme.of(
|
||||||
|
context,
|
||||||
|
).textTheme.bodyMedium?.copyWith(
|
||||||
|
color:
|
||||||
|
Theme.of(
|
||||||
|
context,
|
||||||
|
).colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(4),
|
||||||
|
Text(
|
||||||
|
'selectPayeeToTransfer'.tr(),
|
||||||
|
style: Theme.of(
|
||||||
|
context,
|
||||||
|
).textTheme.bodySmall?.copyWith(
|
||||||
|
color:
|
||||||
|
Theme.of(
|
||||||
|
context,
|
||||||
|
).colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).padding(vertical: 32),
|
||||||
|
),
|
||||||
|
const Gap(12),
|
||||||
|
OutlinedButton.icon(
|
||||||
|
onPressed: () async {
|
||||||
|
final payee = await showModalBottomSheet<SnAccount>(
|
||||||
|
context: context,
|
||||||
|
useRootNavigator: true,
|
||||||
|
isScrollControlled: true,
|
||||||
|
builder: (context) => const AccountPickerSheet(),
|
||||||
|
);
|
||||||
|
if (payee != null) {
|
||||||
|
setState(() => selectedPayee = payee);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.person_search),
|
||||||
|
label: Text('selectPayee'.tr()),
|
||||||
|
style: OutlinedButton.styleFrom(
|
||||||
|
minimumSize: const Size(double.infinity, 48),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
const Gap(16),
|
||||||
|
|
||||||
|
// Remark Section
|
||||||
|
Text(
|
||||||
|
'addRemark'.tr(),
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(8),
|
||||||
|
TextField(
|
||||||
|
controller: remarkController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'transferRemark'.tr(),
|
||||||
|
hintText: 'addRemarkForTransfer'.tr(),
|
||||||
|
alignLabelWithHint: true,
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: Theme.of(
|
||||||
|
context,
|
||||||
|
).colorScheme.outline.withOpacity(0.2),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
maxLines: 3,
|
||||||
|
onTapOutside:
|
||||||
|
(_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// Action Buttons
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: OutlinedButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
child: Text('cancel'.tr()),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(8),
|
||||||
|
Expanded(
|
||||||
|
child: FilledButton(
|
||||||
|
onPressed: _createTransfer,
|
||||||
|
child: Text('createTransfer'.tr()),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String?> _showPinVerificationDialog(BuildContext context) async {
|
||||||
|
String enteredPin = '';
|
||||||
|
|
||||||
|
await showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
isScrollControlled: true,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
useSafeArea: true,
|
||||||
|
builder:
|
||||||
|
(context) => Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context).colorScheme.surface,
|
||||||
|
borderRadius: const BorderRadius.vertical(
|
||||||
|
top: Radius.circular(16),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
bottom: MediaQuery.of(context).viewInsets.bottom,
|
||||||
|
),
|
||||||
|
child: SheetScaffold(
|
||||||
|
titleText: 'enterPin'.tr(),
|
||||||
|
heightFactor: 0.5,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(20),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'enterPinToConfirmTransfer'.tr(),
|
||||||
|
style: Theme.of(context).textTheme.titleMedium
|
||||||
|
?.copyWith(fontWeight: FontWeight.w500),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
const Gap(24),
|
||||||
|
OtpTextField(
|
||||||
|
numberOfFields: 6,
|
||||||
|
borderColor:
|
||||||
|
Theme.of(context).colorScheme.outline,
|
||||||
|
focusedBorderColor:
|
||||||
|
Theme.of(context).colorScheme.primary,
|
||||||
|
showFieldAsBox: true,
|
||||||
|
obscureText: true,
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
fieldWidth: 48,
|
||||||
|
fieldHeight: 56,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
borderWidth: 1,
|
||||||
|
textStyle: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.headlineSmall
|
||||||
|
?.copyWith(fontWeight: FontWeight.w600),
|
||||||
|
onSubmit: (pin) {
|
||||||
|
enteredPin = pin;
|
||||||
|
Navigator.of(context).pop(pin);
|
||||||
|
},
|
||||||
|
onCodeChanged: (String code) {
|
||||||
|
enteredPin = code;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(24),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: OutlinedButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
child: Text('cancel'.tr()),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (enteredPin.length == 6) ...[
|
||||||
|
const Gap(12),
|
||||||
|
Expanded(
|
||||||
|
child: FilledButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop(enteredPin);
|
||||||
|
},
|
||||||
|
child: Text('confirm'.tr()),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return enteredPin.isNotEmpty ? enteredPin : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _createTransfer() async {
|
||||||
|
final amount = double.tryParse(amountController.text);
|
||||||
|
|
||||||
|
if (amount == null || amount <= 0) {
|
||||||
|
showErrorAlert('invalidAmount'.tr());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedPayee == null) {
|
||||||
|
showErrorAlert('noPayeeSelected'.tr());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final data = {
|
||||||
|
'amount': amount,
|
||||||
|
'currency': selectedCurrency,
|
||||||
|
'payee_account_id': selectedPayee!.id,
|
||||||
|
'remark':
|
||||||
|
remarkController.text.trim().isEmpty
|
||||||
|
? null
|
||||||
|
: remarkController.text.trim(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Ask for PIN confirmation before creating transfer
|
||||||
|
final enteredPin = await _showPinVerificationDialog(context);
|
||||||
|
if (enteredPin == null || enteredPin.isEmpty) return;
|
||||||
|
|
||||||
|
// Add PIN to the transfer data
|
||||||
|
data['pin_code'] = enteredPin;
|
||||||
|
|
||||||
|
if (mounted) Navigator.of(context).pop(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const Map<String, IconData> kCurrencyIconData = {
|
const Map<String, IconData> kCurrencyIconData = {
|
||||||
'points': Symbols.toll,
|
'points': Symbols.toll,
|
||||||
'golds': Symbols.attach_money,
|
'golds': Symbols.attach_money,
|
||||||
@@ -803,6 +1248,16 @@ class WalletScreen extends HookConsumerWidget {
|
|||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final wallet = ref.watch(walletCurrentProvider);
|
final wallet = ref.watch(walletCurrentProvider);
|
||||||
final tabController = useTabController(initialLength: 2);
|
final tabController = useTabController(initialLength: 2);
|
||||||
|
final currentTabIndex = useState(0);
|
||||||
|
|
||||||
|
useEffect(() {
|
||||||
|
void listener() {
|
||||||
|
currentTabIndex.value = tabController.index;
|
||||||
|
}
|
||||||
|
|
||||||
|
tabController.addListener(listener);
|
||||||
|
return () => tabController.removeListener(listener);
|
||||||
|
}, [tabController]);
|
||||||
|
|
||||||
Future<void> createWallet() async {
|
Future<void> createWallet() async {
|
||||||
final client = ref.read(apiClientProvider);
|
final client = ref.read(apiClientProvider);
|
||||||
@@ -827,11 +1282,24 @@ class WalletScreen extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> createTransfer() async {
|
||||||
|
final result = await showModalBottomSheet<Map<String, dynamic>>(
|
||||||
|
context: context,
|
||||||
|
useRootNavigator: true,
|
||||||
|
isScrollControlled: true,
|
||||||
|
builder: (context) => const CreateTransferSheet(),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result != null && context.mounted) {
|
||||||
|
await _handleTransferCreation(context, ref, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
String getCurrencyTranslationKey(String currency, {bool isShort = false}) {
|
String getCurrencyTranslationKey(String currency, {bool isShort = false}) {
|
||||||
return 'walletCurrency${isShort ? 'Short' : ''}${currency[0].toUpperCase()}${currency.substring(1).toLowerCase()}';
|
return 'walletCurrency${isShort ? 'Short' : ''}${currency[0].toUpperCase()}${currency.substring(1).toLowerCase()}';
|
||||||
}
|
}
|
||||||
|
|
||||||
List<SnWalletPocket> _getAllCurrencies(List<SnWalletPocket> pockets) {
|
List<SnWalletPocket> getAllCurrencies(List<SnWalletPocket> pockets) {
|
||||||
final allCurrencies = <String>{};
|
final allCurrencies = <String>{};
|
||||||
allCurrencies.addAll(kCurrencyIconData.keys);
|
allCurrencies.addAll(kCurrencyIconData.keys);
|
||||||
allCurrencies.addAll(pockets.map((p) => p.currency));
|
allCurrencies.addAll(pockets.map((p) => p.currency));
|
||||||
@@ -859,9 +1327,16 @@ class WalletScreen extends HookConsumerWidget {
|
|||||||
title: Text('wallet').tr(),
|
title: Text('wallet').tr(),
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Symbols.add),
|
icon: Icon(
|
||||||
onPressed: createFund,
|
currentTabIndex.value == 1
|
||||||
tooltip: 'createFund'.tr(),
|
? Symbols.money_bag
|
||||||
|
: Symbols.swap_horiz,
|
||||||
|
),
|
||||||
|
onPressed: currentTabIndex.value == 1 ? createFund : createTransfer,
|
||||||
|
tooltip:
|
||||||
|
currentTabIndex.value == 1
|
||||||
|
? 'createFund'.tr()
|
||||||
|
: 'createTransfer'.tr(),
|
||||||
),
|
),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
],
|
],
|
||||||
@@ -900,7 +1375,7 @@ class WalletScreen extends HookConsumerWidget {
|
|||||||
margin: EdgeInsets.zero,
|
margin: EdgeInsets.zero,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
..._getAllCurrencies(data.pockets).map(
|
...getAllCurrencies(data.pockets).map(
|
||||||
(pocket) => ListTile(
|
(pocket) => ListTile(
|
||||||
leading: Icon(
|
leading: Icon(
|
||||||
kCurrencyIconData[pocket.currency] ??
|
kCurrencyIconData[pocket.currency] ??
|
||||||
@@ -1062,7 +1537,7 @@ class WalletScreen extends HookConsumerWidget {
|
|||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(
|
||||||
Symbols.celebration,
|
Symbols.money_bag,
|
||||||
size: 48,
|
size: 48,
|
||||||
color: Theme.of(context).colorScheme.outline,
|
color: Theme.of(context).colorScheme.outline,
|
||||||
),
|
),
|
||||||
@@ -1103,7 +1578,7 @@ class WalletScreen extends HookConsumerWidget {
|
|||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(
|
||||||
Symbols.celebration,
|
Symbols.money_bag,
|
||||||
color: Theme.of(context).colorScheme.primary,
|
color: Theme.of(context).colorScheme.primary,
|
||||||
),
|
),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
@@ -1325,6 +1800,31 @@ class WalletScreen extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _handleTransferCreation(
|
||||||
|
BuildContext context,
|
||||||
|
WidgetRef ref,
|
||||||
|
Map<String, dynamic> transferData,
|
||||||
|
) async {
|
||||||
|
final client = ref.read(apiClientProvider);
|
||||||
|
try {
|
||||||
|
showLoadingModal(context);
|
||||||
|
await client.post('/id/wallets/transfer', data: transferData);
|
||||||
|
|
||||||
|
if (context.mounted) hideLoadingModal(context);
|
||||||
|
|
||||||
|
// Invalidate providers to refresh data
|
||||||
|
ref.invalidate(transactionListNotifierProvider);
|
||||||
|
ref.invalidate(walletCurrentProvider);
|
||||||
|
if (context.mounted) {
|
||||||
|
showSnackBar('transferCreatedSuccessfully'.tr());
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
showErrorAlert(err);
|
||||||
|
} finally {
|
||||||
|
if (context.mounted) hideLoadingModal(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
String _getFundStatusText(int status) {
|
String _getFundStatusText(int status) {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 0:
|
case 0:
|
||||||
|
Reference in New Issue
Block a user