Cooldown Added in Beneficiary
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
import 'dart:convert';
|
||||
|
||||
class Beneficiary {
|
||||
final String accountNo;
|
||||
final String accountType;
|
||||
final String name;
|
||||
final DateTime? createdAt;
|
||||
final String ifscCode;
|
||||
final String? bankName;
|
||||
final String? branchName;
|
||||
@@ -11,6 +14,7 @@ class Beneficiary {
|
||||
required this.accountNo,
|
||||
required this.accountType,
|
||||
required this.name,
|
||||
this.createdAt,
|
||||
required this.ifscCode,
|
||||
this.bankName,
|
||||
this.branchName,
|
||||
@@ -21,6 +25,7 @@ class Beneficiary {
|
||||
return Beneficiary(
|
||||
accountNo: json['account_no'] ?? json['accountNo'] ?? '',
|
||||
accountType: json['account_type'] ?? json['accountType'] ?? '',
|
||||
createdAt: json['createdAt'] == null ? null : DateTime.tryParse(json['createdAt']),
|
||||
name: json['name'] ?? '',
|
||||
ifscCode: json['ifsc_code'] ?? json['ifscCode'] ?? '',
|
||||
bankName: json['bank_name'] ?? json['bankName'] ?? '',
|
||||
|
||||
90
lib/features/fund_transfer/screens/cooldown.dart
Normal file
90
lib/features/fund_transfer/screens/cooldown.dart
Normal file
@@ -0,0 +1,90 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CooldownTimer extends StatefulWidget {
|
||||
final DateTime createdAt;
|
||||
final VoidCallback onTimerFinish;
|
||||
|
||||
const CooldownTimer({
|
||||
Key? key,
|
||||
required this.createdAt,
|
||||
required this.onTimerFinish,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
_CooldownTimerState createState() => _CooldownTimerState();
|
||||
}
|
||||
|
||||
class _CooldownTimerState extends State<CooldownTimer> {
|
||||
late Timer _timer;
|
||||
late Duration _timeRemaining;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_updateRemainingTime();
|
||||
// Update the timer every second
|
||||
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
||||
_updateRemainingTime();
|
||||
});
|
||||
}
|
||||
|
||||
void _updateRemainingTime() {
|
||||
final cooldownEnd = widget.createdAt.add(const Duration(minutes: 60));
|
||||
final now = DateTime.now();
|
||||
|
||||
if (now.isAfter(cooldownEnd)) {
|
||||
_timeRemaining = Duration.zero;
|
||||
_timer.cancel();
|
||||
// Notify the parent widget that the timer is done
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
widget.onTimerFinish();
|
||||
});
|
||||
} else {
|
||||
_timeRemaining = cooldownEnd.difference(now);
|
||||
}
|
||||
// Trigger a rebuild
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_timer.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
String _formatDuration(Duration duration) {
|
||||
final minutes = duration.inMinutes.remainder(60).toString().padLeft(2, '0');
|
||||
final seconds = duration.inSeconds.remainder(60).toString().padLeft(2, '0');
|
||||
return '$minutes:$seconds';
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (_timeRemaining == Duration.zero) {
|
||||
return const SizedBox.shrink(); // Or some other widget indicating it's enabled
|
||||
}
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Text(
|
||||
'Enabled after:',
|
||||
style: TextStyle(
|
||||
color: Colors.red.shade700,
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
_formatDuration(_timeRemaining),
|
||||
style: TextStyle(
|
||||
color: Colors.red.shade700,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:kmobile/features/fund_transfer/screens/cooldown.dart';
|
||||
import 'package:kmobile/widgets/bank_logos.dart';
|
||||
import 'package:kmobile/data/models/beneficiary.dart';
|
||||
import 'package:kmobile/features/fund_transfer/screens/fund_transfer_amount_screen.dart';
|
||||
@@ -72,6 +73,7 @@ class _FundTransferBeneficiaryScreenState
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Widget _buildBeneficiaryList() {
|
||||
if (_beneficiaries.isEmpty) {
|
||||
return Center(
|
||||
@@ -81,7 +83,22 @@ class _FundTransferBeneficiaryScreenState
|
||||
itemCount: _beneficiaries.length,
|
||||
itemBuilder: (context, index) {
|
||||
final beneficiary = _beneficiaries[index];
|
||||
return ListTile(
|
||||
|
||||
// --- Cooldown Logic ---
|
||||
bool isCoolingDown = false;
|
||||
if (beneficiary.createdAt != null) {
|
||||
final sixtyMinutesAgo =
|
||||
DateTime.now().subtract(const Duration(minutes: 60));
|
||||
isCoolingDown = beneficiary.createdAt!.isAfter(sixtyMinutesAgo);
|
||||
}
|
||||
// --- End of Cooldown Logic ---
|
||||
|
||||
// By wrapping the ListTile in an Opacity widget, we can make it look
|
||||
// disabled while ensuring the onTap callback still works.
|
||||
return Opacity(
|
||||
opacity: isCoolingDown ? 0.5 : 1.0,
|
||||
child: ListTile(
|
||||
// REMOVED the 'enabled' property from here.
|
||||
leading: CircleAvatar(
|
||||
radius: 24,
|
||||
backgroundColor: Colors.transparent,
|
||||
@@ -100,7 +117,25 @@ class _FundTransferBeneficiaryScreenState
|
||||
),
|
||||
],
|
||||
),
|
||||
trailing: isCoolingDown
|
||||
? CooldownTimer(
|
||||
createdAt: beneficiary.createdAt!,
|
||||
onTimerFinish: () {
|
||||
setState(() {});
|
||||
},
|
||||
)
|
||||
: null,
|
||||
onTap: () {
|
||||
if (isCoolingDown) {
|
||||
// This will now execute correctly on tap
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text(
|
||||
'Beneficiary will be enabled after the cooldown period.'),
|
||||
behavior: SnackBarBehavior.floating,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
@@ -112,12 +147,15 @@ class _FundTransferBeneficiaryScreenState
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
|
||||
@@ -1,19 +1,115 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:kmobile/features/auth/screens/tnc_required_screen.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class TncDialog extends StatefulWidget {
|
||||
// Add a callback function for when the user proceeds
|
||||
class TncDialog extends StatefulWidget {
|
||||
final Future<void> Function() onProceed;
|
||||
|
||||
const TncDialog({Key? key, required this.onProceed}) : super(key: key);
|
||||
|
||||
@override
|
||||
_TncDialogState createState() => _TncDialogState();
|
||||
}
|
||||
}
|
||||
|
||||
class _TncDialogState extends State<TncDialog> {
|
||||
class _TncDialogState extends State<TncDialog> {
|
||||
bool _isAgreed = false;
|
||||
bool _isLoading = false;
|
||||
// --- NEW: ScrollController for the TNC text ---
|
||||
final ScrollController _scrollController = ScrollController();
|
||||
|
||||
final String _termsAndConditionsText = """
|
||||
Effective Date: November 10, 2025
|
||||
|
||||
These Terms and Conditions ("Terms") govern your access to and use of The Bank mobile banking application (the "App") and the services
|
||||
provided through it (the "Services").
|
||||
|
||||
By downloading, installing, accessing, or using the App, you agree to be bound by these Terms and our Privacy Policy. If you do not
|
||||
agree to these Terms, you must not download, install, access, or use the App.
|
||||
|
||||
1. Definitions
|
||||
- App: Refers to The Bank mobile banking application.
|
||||
- Bank/We/Us/Our: Refers to The Bank.
|
||||
- User/You/Your: Refers to the individual using the App.
|
||||
- Device: Refers to any compatible mobile phone, tablet, or other device on which you install and use the App.
|
||||
- Security Credentials: Refers to your username, password, PIN, biometric data (e.g., fingerprint, facial recognition), and any other
|
||||
authentication methods used to access the App and Services.
|
||||
|
||||
2. Acceptance of Terms
|
||||
Your use of the App constitutes your acceptance of these Terms. We recommend that you print or save a copy of these Terms for your
|
||||
records.
|
||||
|
||||
3. License to Use
|
||||
We grant you a limited, non-exclusive, non-transferable, revocable license to install and use the App on a Device that you own or
|
||||
control, solely for your personal, non-commercial use in connection with your accounts at The Bank. This license does not permit you to
|
||||
use the App on any Device that you do not own or control.
|
||||
|
||||
4. User Responsibilities
|
||||
You agree to:
|
||||
- Use the App only for lawful purposes and in accordance with these Terms.
|
||||
- Keep your Device and Security Credentials secure and confidential.
|
||||
- Notify us immediately if you suspect any unauthorized use of your Security Credentials or Device, or if your Device is lost or stolen.
|
||||
- Ensure that any information you provide to us through the App is accurate and up-to-date.
|
||||
- Comply with all reasonable instructions we issue regarding the safe use of your Device and the App.
|
||||
- Not use the App in any unlawful manner, for any unlawful purpose, or in any manner inconsistent with these Terms, or act fraudulently
|
||||
or maliciously (e.g., by hacking into or inserting malicious code into the App or your Device's operating system).
|
||||
- Not download the App from anywhere other than an app store approved by us (e.g., Apple App Store, Google Play Store) or install or use
|
||||
it on a jail-broken or rooted device.
|
||||
- Delete the App if you change or dispose of a Device that you use to access the Services.
|
||||
|
||||
5. Security
|
||||
We employ reasonable security measures to protect your information and transactions conducted through the App. However, you acknowledge
|
||||
that no system is entirely secure. You are responsible for maintaining the security of your Device and Security Credentials. We are not
|
||||
liable for damages arising from virus contamination in your IT system or if the parameters of your browser are different from the
|
||||
required technical conditions.
|
||||
|
||||
6. Privacy
|
||||
Your privacy is important to us. Our Privacy Policy explains how we collect, use, and protect your personal information in connection
|
||||
with your use of the App and Services. By using the App, you consent to such collection, use, and protection as described in our Privacy
|
||||
Policy.
|
||||
|
||||
7. Limitations of Liability and Disclaimer of Warranty
|
||||
THE APP AND SERVICES ARE PROVIDED "AS IS" AND "AS AVAILABLE" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT
|
||||
NOT LIMITED TO, THE IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. We do not
|
||||
guarantee continuous, uninterrupted, or secure access to any part of our Service, and operation of the App or the Services may be
|
||||
interfered with by numerous factors outside of our control.
|
||||
|
||||
IN NO EVENT SHALL WE OR OUR AFFILIATES, LICENSORS, OR CONTRACTORS BE LIABLE FOR ANY CLAIM, ARISING FROM OR RELATED TO THE MOBILE BANKING
|
||||
APP OR THE SERVICES, THAT YOU DO NOT STATE IN WRITING IN A COMPLAINT FILED IN A COURT OR ARBITRATION PROCEEDING WITHIN TWO (2) YEARS OF
|
||||
THE DATE THAT THE EVENT GIVING RISE TO THE CLAIM OCCURRED. THESE LIMITATIONS WILL APPLY TO ALL CAUSES OF ACTION, WHETHER ARISING FROM
|
||||
BREACH OF CONTRACT, TORT (INCLUDING NEGLIGENCE) OR ANY OTHER LEGAL THEORY.
|
||||
|
||||
8. Intellectual Property
|
||||
All intellectual property rights in the App and its content (excluding user-generated content) are owned by The Bank or its licensors.
|
||||
You are granted a limited license to use the App as set forth in these Terms, but no ownership rights are transferred to you. You must
|
||||
not remove or tamper with any copyright notice attached to or contained within the App.
|
||||
|
||||
9. Termination
|
||||
We may terminate or suspend your access to the App and Services immediately, without prior notice or liability, for any reason
|
||||
whatsoever, including without limitation if you breach these Terms. You may stop using the App at any time. If you wish to deregister
|
||||
your digital banking access, you need to notify us.
|
||||
|
||||
10. Changes to Terms
|
||||
We reserve the right to modify or replace these Terms at any time. If a revision is material, we will provide at least 30 days' notice
|
||||
prior to any new terms taking effect. Your continued use of the App after any such changes constitutes your acceptance of the new Terms.
|
||||
|
||||
11. Governing Law and Dispute Resolution
|
||||
These Terms shall be governed and construed in accordance with the laws of your local jurisdiction, without regard to its conflict of
|
||||
law provisions. Any dispute arising under these Terms shall be resolved in the courts located in your local jurisdiction.
|
||||
|
||||
12. Contact Information
|
||||
If you have any questions about these Terms, please contact us through the channels provided on our official website or within the App.
|
||||
|
||||
13. Electronic Communications
|
||||
By using the App, you consent to receive electronic communications from us. These communications may include notices about your account,
|
||||
transactional information, and marketing materials.
|
||||
|
||||
14. Third-Party Services
|
||||
The App may integrate with or provide links to third-party services. We are not responsible for the content, privacy policies, or
|
||||
practices of any third-party websites or services.
|
||||
|
||||
15. Indemnification
|
||||
You agree to indemnify and hold harmless The Bank, its affiliates, officers, directors, employees, and agents from any and all claims,
|
||||
liabilities, damages, losses, and expenses, including reasonable attorneys' fees, arising out of or in any way connected with your
|
||||
access to or use of the App and Services.
|
||||
""";
|
||||
|
||||
void _handleProceed() async {
|
||||
if (_isLoading) return;
|
||||
@@ -22,12 +118,8 @@ class _TncDialogState extends State<TncDialog> {
|
||||
_isLoading = true;
|
||||
});
|
||||
|
||||
// Call the provided onProceed function, which will trigger the cubit
|
||||
await widget.onProceed();
|
||||
|
||||
// The dialog will be dismissed by the navigation that happens in the BlocListener
|
||||
// so we don't need to pop here. If for some reason it's still visible,
|
||||
// we can add a mounted check and pop.
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
@@ -35,11 +127,35 @@ class _TncDialogState extends State<TncDialog> {
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_scrollController.dispose(); // --- NEW: Dispose the ScrollController ---
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final screenSize = MediaQuery.of(context).size;
|
||||
|
||||
return AlertDialog(
|
||||
title: const Text('Terms and Conditions'),
|
||||
content: SingleChildScrollView(
|
||||
content: SizedBox(
|
||||
height: screenSize.height * 0.5, // 50% of screen height
|
||||
width: screenSize.width * 0.9, // 90% of screen width
|
||||
// --- MODIFIED: Use a Column to separate scrollable text from fixed checkbox ---
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// --- NEW: Expanded Scrollbar for the TNC text ---
|
||||
Expanded(
|
||||
child: Scrollbar(
|
||||
controller: _scrollController,
|
||||
thumbVisibility: true, // Always show the scrollbar thumb
|
||||
// To place the scrollbar on the left, you might need to wrap
|
||||
// this in a Directionality widget or use a custom scrollbar.
|
||||
// For now, it will appear on the right as is standard.
|
||||
child: SingleChildScrollView(
|
||||
controller: _scrollController,
|
||||
child: _isLoading
|
||||
? const Center(
|
||||
child: Padding(
|
||||
@@ -47,14 +163,12 @@ class _TncDialogState extends State<TncDialog> {
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
)
|
||||
: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'Please read and accept our terms and conditions to continue. '
|
||||
'This is a placeholder for the actual terms and conditions text.'),
|
||||
const SizedBox(height: 16),
|
||||
: Text(_termsAndConditionsText),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16), // Space between text and checkbox
|
||||
// --- MODIFIED: Checkbox Row is now outside the SingleChildScrollView ---
|
||||
Row(
|
||||
children: [
|
||||
Checkbox(
|
||||
@@ -74,7 +188,6 @@ class _TncDialogState extends State<TncDialog> {
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
// Disable button while loading
|
||||
onPressed: _isLoading
|
||||
? null
|
||||
: () {
|
||||
@@ -89,11 +202,10 @@ class _TncDialogState extends State<TncDialog> {
|
||||
child: const Text('Disagree'),
|
||||
),
|
||||
ElevatedButton(
|
||||
// Disable button if not agreed or while loading
|
||||
onPressed: _isAgreed && !_isLoading ? _handleProceed : null,
|
||||
child: const Text('Proceed'),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user