Compare commits
2 Commits
6c91093198
...
44dbfc36d9
Author | SHA1 | Date | |
---|---|---|---|
44dbfc36d9
|
|||
5dbe7371cb
|
@@ -481,6 +481,7 @@
|
||||
"pinCode": "PIN Code",
|
||||
"biometric": "Biometric",
|
||||
"enterPinToConfirm": "Enter your 6-digit PIN to confirm payment",
|
||||
"enterPin": "Enter your PIN code",
|
||||
"clearPin": "Clear PIN",
|
||||
"useBiometricToConfirm": "Use biometric authentication to confirm payment",
|
||||
"touchSensorToAuthenticate": "Touch the sensor to authenticate",
|
||||
@@ -1176,5 +1177,14 @@
|
||||
"noRecipientsSelected": "No recipients selected",
|
||||
"selectRecipientsToSendFund": "Select recipients to send the fund to",
|
||||
"addRecipient": "Add Recipient",
|
||||
"addMoreRecipients": "Add More Recipients"
|
||||
"addMoreRecipients": "Add More Recipients",
|
||||
"transactionDetails": "Transaction Details",
|
||||
"remarks": "Remarks",
|
||||
"payer": "Payer",
|
||||
"payee": "Payee",
|
||||
"transactionType": "Transaction Type",
|
||||
"transfer": "Transfer",
|
||||
"payment": "Payment",
|
||||
"systemWallet": "System Wallet",
|
||||
"date": "Date"
|
||||
}
|
||||
|
@@ -1075,5 +1075,6 @@
|
||||
"deleteRecycledFiles": "删除被回收的文件",
|
||||
"recycledFilesDeleted": "被回收文件成功删除",
|
||||
"failedToDeleteRecycledFiles": "删除被回收文件失败",
|
||||
"upload": "上传"
|
||||
"upload": "上传",
|
||||
"systemWallet": "中央统筹"
|
||||
}
|
@@ -8,6 +8,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:island/models/account.dart';
|
||||
import 'package:island/models/wallet.dart';
|
||||
import 'package:island/pods/network.dart';
|
||||
import 'package:island/widgets/account/account_pfc.dart';
|
||||
import 'package:island/widgets/account/account_picker.dart';
|
||||
import 'package:island/widgets/app_scaffold.dart';
|
||||
import 'package:island/widgets/content/cloud_files.dart';
|
||||
@@ -284,37 +285,6 @@ class _CreateFundSheetState extends State<CreateFundSheet> {
|
||||
),
|
||||
);
|
||||
}),
|
||||
if (selectedRecipients.length < 10)
|
||||
OutlinedButton.icon(
|
||||
onPressed: () async {
|
||||
final recipient =
|
||||
await showModalBottomSheet<SnAccount>(
|
||||
context: context,
|
||||
useRootNavigator: true,
|
||||
isScrollControlled: true,
|
||||
builder:
|
||||
(context) =>
|
||||
const AccountPickerSheet(),
|
||||
);
|
||||
if (recipient != null &&
|
||||
!selectedRecipients.contains(
|
||||
recipient,
|
||||
)) {
|
||||
setState(
|
||||
() =>
|
||||
selectedRecipients.add(recipient),
|
||||
);
|
||||
}
|
||||
},
|
||||
icon: const Icon(Icons.person_add),
|
||||
label: Text('addRecipient'.tr()),
|
||||
style: OutlinedButton.styleFrom(
|
||||
minimumSize: const Size(
|
||||
double.infinity,
|
||||
48,
|
||||
),
|
||||
),
|
||||
).padding(all: 16),
|
||||
],
|
||||
)
|
||||
: Column(
|
||||
@@ -469,7 +439,7 @@ class _CreateFundSheetState extends State<CreateFundSheet> {
|
||||
bottom: MediaQuery.of(context).viewInsets.bottom,
|
||||
),
|
||||
child: SheetScaffold(
|
||||
titleText: 'enterPinToConfirm'.tr(),
|
||||
titleText: 'enterPin'.tr(),
|
||||
heightFactor: 0.5,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
@@ -480,7 +450,7 @@ class _CreateFundSheetState extends State<CreateFundSheet> {
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'enterPinToConfirm'.tr(),
|
||||
'enterPinToConfirmPayment'.tr(),
|
||||
style: Theme.of(context).textTheme.titleMedium
|
||||
?.copyWith(fontWeight: FontWeight.w500),
|
||||
textAlign: TextAlign.center,
|
||||
@@ -659,6 +629,173 @@ Future<SnWalletFund> walletFund(Ref ref, String fundId) async {
|
||||
return SnWalletFund.fromJson(resp.data);
|
||||
}
|
||||
|
||||
class TransactionDetailSheet extends StatelessWidget {
|
||||
final SnTransaction transaction;
|
||||
|
||||
const TransactionDetailSheet({super.key, required this.transaction});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isIncome =
|
||||
transaction.payeeWalletId == null ||
|
||||
transaction.payeeWallet?.accountId == null;
|
||||
|
||||
return SheetScaffold(
|
||||
titleText: 'transactionDetails'.tr(),
|
||||
heightFactor: 0.75,
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Amount
|
||||
Text(
|
||||
'amount'.tr(),
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
const Gap(4),
|
||||
Text(
|
||||
'${transaction.amount.toStringAsFixed(2)} ${transaction.currency}',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: isIncome ? Colors.green : Colors.red,
|
||||
),
|
||||
),
|
||||
const Gap(16),
|
||||
|
||||
// Remarks
|
||||
if (transaction.remarks != null &&
|
||||
transaction.remarks!.isNotEmpty) ...[
|
||||
Text(
|
||||
'remarks'.tr(),
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
const Gap(4),
|
||||
Text(
|
||||
transaction.remarks!,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const Gap(16),
|
||||
],
|
||||
|
||||
// Date
|
||||
Text(
|
||||
'date'.tr(),
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
const Gap(4),
|
||||
Text(
|
||||
DateFormat.yMd().add_Hm().format(transaction.createdAt),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const Gap(16),
|
||||
|
||||
// Payer
|
||||
Text(
|
||||
'payer'.tr(),
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
const Gap(4),
|
||||
AccountPfcGestureDetector(
|
||||
uname: transaction.payerWallet?.account?.name,
|
||||
child: Row(
|
||||
spacing: 8,
|
||||
children: [
|
||||
if (transaction.payerWallet?.account != null)
|
||||
ProfilePictureWidget(
|
||||
file: transaction.payerWallet!.account!.profile.picture,
|
||||
radius: 12,
|
||||
),
|
||||
Text(
|
||||
transaction.payerWallet?.account?.nick ??
|
||||
'systemWallet'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Gap(16),
|
||||
|
||||
// Payee
|
||||
Text(
|
||||
'payee'.tr(),
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
const Gap(4),
|
||||
AccountPfcGestureDetector(
|
||||
uname: transaction.payeeWallet?.account?.name,
|
||||
child: Row(
|
||||
spacing: 8,
|
||||
children: [
|
||||
if (transaction.payeeWallet?.account != null)
|
||||
ProfilePictureWidget(
|
||||
file: transaction.payeeWallet!.account!.profile.picture,
|
||||
radius: 12,
|
||||
),
|
||||
Text(
|
||||
transaction.payeeWallet?.account?.nick ??
|
||||
'systemWallet'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Gap(16),
|
||||
|
||||
// Transaction Type
|
||||
Text(
|
||||
'transactionType'.tr(),
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
const Gap(4),
|
||||
Text(
|
||||
_getTransactionTypeText(transaction.type),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String _getTransactionTypeText(int type) {
|
||||
// Assuming types: 0: transfer, 1: payment, etc. Adjust based on actual types
|
||||
switch (type) {
|
||||
case 0:
|
||||
return 'transfer'.tr();
|
||||
case 1:
|
||||
return 'payment'.tr();
|
||||
default:
|
||||
return 'unknown'.tr();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class WalletScreen extends HookConsumerWidget {
|
||||
const WalletScreen({super.key});
|
||||
|
||||
@@ -694,6 +831,29 @@ class WalletScreen extends HookConsumerWidget {
|
||||
return 'walletCurrency${isShort ? 'Short' : ''}${currency[0].toUpperCase()}${currency.substring(1).toLowerCase()}';
|
||||
}
|
||||
|
||||
List<SnWalletPocket> _getAllCurrencies(List<SnWalletPocket> pockets) {
|
||||
final allCurrencies = <String>{};
|
||||
allCurrencies.addAll(kCurrencyIconData.keys);
|
||||
allCurrencies.addAll(pockets.map((p) => p.currency));
|
||||
|
||||
return allCurrencies.map((currency) {
|
||||
final existingPocket = pockets.firstWhere(
|
||||
(p) => p.currency == currency,
|
||||
orElse:
|
||||
() => SnWalletPocket(
|
||||
id: '',
|
||||
currency: currency,
|
||||
amount: 0.0,
|
||||
walletId: '',
|
||||
createdAt: DateTime.now(),
|
||||
updatedAt: DateTime.now(),
|
||||
deletedAt: null,
|
||||
),
|
||||
);
|
||||
return existingPocket;
|
||||
}).toList();
|
||||
}
|
||||
|
||||
return AppScaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('wallet').tr(),
|
||||
@@ -740,7 +900,7 @@ class WalletScreen extends HookConsumerWidget {
|
||||
margin: EdgeInsets.zero,
|
||||
child: Column(
|
||||
children: [
|
||||
...data.pockets.map(
|
||||
..._getAllCurrencies(data.pockets).map(
|
||||
(pocket) => ListTile(
|
||||
leading: Icon(
|
||||
kCurrencyIconData[pocket.currency] ??
|
||||
@@ -764,6 +924,8 @@ class WalletScreen extends HookConsumerWidget {
|
||||
).padding(horizontal: 12, top: 12),
|
||||
),
|
||||
|
||||
SliverGap(8),
|
||||
|
||||
// Tab Bar
|
||||
SliverToBoxAdapter(
|
||||
child: TabBar(
|
||||
@@ -802,27 +964,41 @@ class WalletScreen extends HookConsumerWidget {
|
||||
final isIncome =
|
||||
transaction.payeeWalletId == wallet.value?.id;
|
||||
|
||||
return ListTile(
|
||||
key: ValueKey(transaction.id),
|
||||
leading: Icon(
|
||||
isIncome
|
||||
? Symbols.payment_arrow_down
|
||||
: Symbols.paid,
|
||||
),
|
||||
title: Text(
|
||||
transaction.remarks ?? '',
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
subtitle: Text(
|
||||
DateFormat.yMd().add_Hm().format(
|
||||
transaction.createdAt,
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
useRootNavigator: true,
|
||||
isScrollControlled: true,
|
||||
builder:
|
||||
(context) => TransactionDetailSheet(
|
||||
transaction: transaction,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: ListTile(
|
||||
key: ValueKey(transaction.id),
|
||||
leading: Icon(
|
||||
isIncome
|
||||
? Symbols.payment_arrow_down
|
||||
: Symbols.paid,
|
||||
),
|
||||
),
|
||||
trailing: Text(
|
||||
'${isIncome ? '+' : '-'}${transaction.amount.toStringAsFixed(2)} ${transaction.currency}',
|
||||
style: TextStyle(
|
||||
color: isIncome ? Colors.green : Colors.red,
|
||||
title: Text(
|
||||
transaction.remarks ?? '',
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
subtitle: Text(
|
||||
DateFormat.yMd().add_Hm().format(
|
||||
transaction.createdAt,
|
||||
),
|
||||
),
|
||||
trailing: Text(
|
||||
'${isIncome ? '+' : '-'}${transaction.amount.toStringAsFixed(2)} ${transaction.currency}',
|
||||
style: TextStyle(
|
||||
color:
|
||||
isIncome ? Colors.green : Colors.red,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@@ -189,7 +189,7 @@ class AccountProfileCard extends HookConsumerWidget {
|
||||
}
|
||||
|
||||
class AccountPfcGestureDetector extends StatelessWidget {
|
||||
final String uname;
|
||||
final String? uname;
|
||||
final Widget child;
|
||||
const AccountPfcGestureDetector({
|
||||
super.key,
|
||||
@@ -202,7 +202,13 @@ class AccountPfcGestureDetector extends StatelessWidget {
|
||||
return GestureDetector(
|
||||
child: child,
|
||||
onTapDown: (details) {
|
||||
showAccountProfileCard(context, uname, offset: details.localPosition);
|
||||
if (uname != null) {
|
||||
showAccountProfileCard(
|
||||
context,
|
||||
uname!,
|
||||
offset: details.localPosition,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@@ -188,7 +188,7 @@ Add these keys to your localization files:
|
||||
"description": "Description",
|
||||
"pinCode": "PIN Code",
|
||||
"biometric": "Biometric",
|
||||
"enterPinToConfirm": "Enter your 6-digit PIN to confirm payment",
|
||||
"enterPinToConfirmPayment": "Enter your 6-digit PIN to confirm payment",
|
||||
"clearPin": "Clear PIN",
|
||||
"useBiometricToConfirm": "Use biometric authentication to confirm payment",
|
||||
"touchSensorToAuthenticate": "Touch the sensor to authenticate",
|
||||
|
@@ -385,7 +385,7 @@ class _PaymentContentState extends ConsumerState<_PaymentContent> {
|
||||
return Column(
|
||||
children: [
|
||||
Text(
|
||||
'enterPinToConfirm'.tr(),
|
||||
'enterPinToConfirmPayment'.tr(),
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w500),
|
||||
|
Reference in New Issue
Block a user