Compare commits
2 Commits
6c91093198
...
44dbfc36d9
Author | SHA1 | Date | |
---|---|---|---|
44dbfc36d9
|
|||
5dbe7371cb
|
@@ -481,6 +481,7 @@
|
|||||||
"pinCode": "PIN Code",
|
"pinCode": "PIN Code",
|
||||||
"biometric": "Biometric",
|
"biometric": "Biometric",
|
||||||
"enterPinToConfirm": "Enter your 6-digit PIN to confirm payment",
|
"enterPinToConfirm": "Enter your 6-digit PIN to confirm payment",
|
||||||
|
"enterPin": "Enter your PIN code",
|
||||||
"clearPin": "Clear PIN",
|
"clearPin": "Clear PIN",
|
||||||
"useBiometricToConfirm": "Use biometric authentication to confirm payment",
|
"useBiometricToConfirm": "Use biometric authentication to confirm payment",
|
||||||
"touchSensorToAuthenticate": "Touch the sensor to authenticate",
|
"touchSensorToAuthenticate": "Touch the sensor to authenticate",
|
||||||
@@ -1176,5 +1177,14 @@
|
|||||||
"noRecipientsSelected": "No recipients selected",
|
"noRecipientsSelected": "No recipients selected",
|
||||||
"selectRecipientsToSendFund": "Select recipients to send the fund to",
|
"selectRecipientsToSendFund": "Select recipients to send the fund to",
|
||||||
"addRecipient": "Add Recipient",
|
"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": "删除被回收的文件",
|
"deleteRecycledFiles": "删除被回收的文件",
|
||||||
"recycledFilesDeleted": "被回收文件成功删除",
|
"recycledFilesDeleted": "被回收文件成功删除",
|
||||||
"failedToDeleteRecycledFiles": "删除被回收文件失败",
|
"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/account.dart';
|
||||||
import 'package:island/models/wallet.dart';
|
import 'package:island/models/wallet.dart';
|
||||||
import 'package:island/pods/network.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/account/account_picker.dart';
|
||||||
import 'package:island/widgets/app_scaffold.dart';
|
import 'package:island/widgets/app_scaffold.dart';
|
||||||
import 'package:island/widgets/content/cloud_files.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(
|
: Column(
|
||||||
@@ -469,7 +439,7 @@ class _CreateFundSheetState extends State<CreateFundSheet> {
|
|||||||
bottom: MediaQuery.of(context).viewInsets.bottom,
|
bottom: MediaQuery.of(context).viewInsets.bottom,
|
||||||
),
|
),
|
||||||
child: SheetScaffold(
|
child: SheetScaffold(
|
||||||
titleText: 'enterPinToConfirm'.tr(),
|
titleText: 'enterPin'.tr(),
|
||||||
heightFactor: 0.5,
|
heightFactor: 0.5,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(20),
|
padding: const EdgeInsets.all(20),
|
||||||
@@ -480,7 +450,7 @@ class _CreateFundSheetState extends State<CreateFundSheet> {
|
|||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'enterPinToConfirm'.tr(),
|
'enterPinToConfirmPayment'.tr(),
|
||||||
style: Theme.of(context).textTheme.titleMedium
|
style: Theme.of(context).textTheme.titleMedium
|
||||||
?.copyWith(fontWeight: FontWeight.w500),
|
?.copyWith(fontWeight: FontWeight.w500),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
@@ -659,6 +629,173 @@ Future<SnWalletFund> walletFund(Ref ref, String fundId) async {
|
|||||||
return SnWalletFund.fromJson(resp.data);
|
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 {
|
class WalletScreen extends HookConsumerWidget {
|
||||||
const WalletScreen({super.key});
|
const WalletScreen({super.key});
|
||||||
|
|
||||||
@@ -694,6 +831,29 @@ class WalletScreen extends HookConsumerWidget {
|
|||||||
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) {
|
||||||
|
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(
|
return AppScaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text('wallet').tr(),
|
title: Text('wallet').tr(),
|
||||||
@@ -740,7 +900,7 @@ class WalletScreen extends HookConsumerWidget {
|
|||||||
margin: EdgeInsets.zero,
|
margin: EdgeInsets.zero,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
...data.pockets.map(
|
..._getAllCurrencies(data.pockets).map(
|
||||||
(pocket) => ListTile(
|
(pocket) => ListTile(
|
||||||
leading: Icon(
|
leading: Icon(
|
||||||
kCurrencyIconData[pocket.currency] ??
|
kCurrencyIconData[pocket.currency] ??
|
||||||
@@ -764,6 +924,8 @@ class WalletScreen extends HookConsumerWidget {
|
|||||||
).padding(horizontal: 12, top: 12),
|
).padding(horizontal: 12, top: 12),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
SliverGap(8),
|
||||||
|
|
||||||
// Tab Bar
|
// Tab Bar
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: TabBar(
|
child: TabBar(
|
||||||
@@ -802,27 +964,41 @@ class WalletScreen extends HookConsumerWidget {
|
|||||||
final isIncome =
|
final isIncome =
|
||||||
transaction.payeeWalletId == wallet.value?.id;
|
transaction.payeeWalletId == wallet.value?.id;
|
||||||
|
|
||||||
return ListTile(
|
return InkWell(
|
||||||
key: ValueKey(transaction.id),
|
onTap: () {
|
||||||
leading: Icon(
|
showModalBottomSheet(
|
||||||
isIncome
|
context: context,
|
||||||
? Symbols.payment_arrow_down
|
useRootNavigator: true,
|
||||||
: Symbols.paid,
|
isScrollControlled: true,
|
||||||
),
|
builder:
|
||||||
title: Text(
|
(context) => TransactionDetailSheet(
|
||||||
transaction.remarks ?? '',
|
transaction: transaction,
|
||||||
maxLines: 1,
|
),
|
||||||
overflow: TextOverflow.ellipsis,
|
);
|
||||||
),
|
},
|
||||||
subtitle: Text(
|
child: ListTile(
|
||||||
DateFormat.yMd().add_Hm().format(
|
key: ValueKey(transaction.id),
|
||||||
transaction.createdAt,
|
leading: Icon(
|
||||||
|
isIncome
|
||||||
|
? Symbols.payment_arrow_down
|
||||||
|
: Symbols.paid,
|
||||||
),
|
),
|
||||||
),
|
title: Text(
|
||||||
trailing: Text(
|
transaction.remarks ?? '',
|
||||||
'${isIncome ? '+' : '-'}${transaction.amount.toStringAsFixed(2)} ${transaction.currency}',
|
maxLines: 1,
|
||||||
style: TextStyle(
|
overflow: TextOverflow.ellipsis,
|
||||||
color: isIncome ? Colors.green : Colors.red,
|
),
|
||||||
|
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 {
|
class AccountPfcGestureDetector extends StatelessWidget {
|
||||||
final String uname;
|
final String? uname;
|
||||||
final Widget child;
|
final Widget child;
|
||||||
const AccountPfcGestureDetector({
|
const AccountPfcGestureDetector({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -202,7 +202,13 @@ class AccountPfcGestureDetector extends StatelessWidget {
|
|||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
child: child,
|
child: child,
|
||||||
onTapDown: (details) {
|
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",
|
"description": "Description",
|
||||||
"pinCode": "PIN Code",
|
"pinCode": "PIN Code",
|
||||||
"biometric": "Biometric",
|
"biometric": "Biometric",
|
||||||
"enterPinToConfirm": "Enter your 6-digit PIN to confirm payment",
|
"enterPinToConfirmPayment": "Enter your 6-digit PIN to confirm payment",
|
||||||
"clearPin": "Clear PIN",
|
"clearPin": "Clear PIN",
|
||||||
"useBiometricToConfirm": "Use biometric authentication to confirm payment",
|
"useBiometricToConfirm": "Use biometric authentication to confirm payment",
|
||||||
"touchSensorToAuthenticate": "Touch the sensor to authenticate",
|
"touchSensorToAuthenticate": "Touch the sensor to authenticate",
|
||||||
|
@@ -385,7 +385,7 @@ class _PaymentContentState extends ConsumerState<_PaymentContent> {
|
|||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'enterPinToConfirm'.tr(),
|
'enterPinToConfirmPayment'.tr(),
|
||||||
style: Theme.of(
|
style: Theme.of(
|
||||||
context,
|
context,
|
||||||
).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w500),
|
).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w500),
|
||||||
|
Reference in New Issue
Block a user