mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-20 06:08:34 +02:00
Merge branch 'main' into CW-122-Rework-filter-on-the-transactions-list-screen
This commit is contained in:
@@ -21,6 +21,12 @@ class WalletMenu {
|
||||
height: 16, width: 16),
|
||||
handler: () => Navigator.of(context).pushNamed(Routes.walletList),
|
||||
),
|
||||
WalletMenuItem(
|
||||
title: S.current.address_book_menu,
|
||||
image: Image.asset('assets/images/open_book_menu.png',
|
||||
height: 16, width: 16),
|
||||
handler: () => Navigator.of(context).pushNamed(Routes.addressBook),
|
||||
),
|
||||
WalletMenuItem(
|
||||
title: S.current.security_and_backup,
|
||||
image:
|
||||
@@ -29,18 +35,12 @@ class WalletMenu {
|
||||
Navigator.of(context).pushNamed(Routes.securityBackupPage);
|
||||
}),
|
||||
WalletMenuItem(
|
||||
title: S.current.privacy,
|
||||
title: S.current.privacy_settings,
|
||||
image:
|
||||
Image.asset('assets/images/eye_menu.png', height: 16, width: 16),
|
||||
Image.asset('assets/images/privacy_menu.png', height: 16, width: 16),
|
||||
handler: () {
|
||||
Navigator.of(context).pushNamed(Routes.privacyPage);
|
||||
}),
|
||||
WalletMenuItem(
|
||||
title: S.current.address_book_menu,
|
||||
image: Image.asset('assets/images/open_book_menu.png',
|
||||
height: 16, width: 16),
|
||||
handler: () => Navigator.of(context).pushNamed(Routes.addressBook),
|
||||
),
|
||||
WalletMenuItem(
|
||||
title: S.current.display_settings,
|
||||
image: Image.asset('assets/images/eye_menu.png',
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:cake_wallet/core/execution_state.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/screens/ionia/widgets/card_item.dart';
|
||||
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||
@@ -18,12 +19,11 @@ class IoniaCustomRedeemPage extends BasePage {
|
||||
) : _amountFieldFocus = FocusNode(),
|
||||
_amountController = TextEditingController() {
|
||||
_amountController.addListener(() {
|
||||
ioniaCustomRedeemViewModel.updateAmount(_amountController.text);
|
||||
ioniaCustomRedeemViewModel.updateAmount(_amountController.text);
|
||||
});
|
||||
}
|
||||
|
||||
final IoniaCustomRedeemViewModel ioniaCustomRedeemViewModel;
|
||||
|
||||
|
||||
@override
|
||||
String get title => S.current.custom_redeem_amount;
|
||||
@@ -50,7 +50,7 @@ class IoniaCustomRedeemPage extends BasePage {
|
||||
disableScroll: true,
|
||||
config: KeyboardActionsConfig(
|
||||
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
|
||||
keyboardBarColor: Theme.of(context).accentTextTheme!.bodyText1!.backgroundColor!,
|
||||
keyboardBarColor: Theme.of(context).accentTextTheme.bodyText1!.backgroundColor!,
|
||||
nextFocus: false,
|
||||
actions: [
|
||||
KeyboardActionsItem(
|
||||
@@ -67,10 +67,11 @@ class IoniaCustomRedeemPage extends BasePage {
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 25),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(bottomLeft: Radius.circular(24), bottomRight: Radius.circular(24)),
|
||||
borderRadius: BorderRadius.only(
|
||||
bottomLeft: Radius.circular(24), bottomRight: Radius.circular(24)),
|
||||
gradient: LinearGradient(colors: [
|
||||
Theme.of(context).primaryTextTheme!.subtitle1!.color!,
|
||||
Theme.of(context).primaryTextTheme!.subtitle1!.decorationColor!,
|
||||
Theme.of(context).primaryTextTheme.subtitle1!.color!,
|
||||
Theme.of(context).primaryTextTheme.subtitle1!.decorationColor!,
|
||||
], begin: Alignment.topLeft, end: Alignment.bottomRight),
|
||||
),
|
||||
child: Column(
|
||||
@@ -85,11 +86,11 @@ class IoniaCustomRedeemPage extends BasePage {
|
||||
inputFormatters: [FilteringTextInputFormatter.deny(RegExp('[\-|\ ]'))],
|
||||
hintText: '1000',
|
||||
placeholderTextStyle: TextStyle(
|
||||
color: Theme.of(context).primaryTextTheme!.headline5!.color!,
|
||||
color: Theme.of(context).primaryTextTheme.headline5!.color!,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 36,
|
||||
),
|
||||
borderColor: Theme.of(context).primaryTextTheme!.headline5!.color!,
|
||||
borderColor: Theme.of(context).primaryTextTheme.headline5!.color!,
|
||||
textColor: Colors.white,
|
||||
textStyle: TextStyle(
|
||||
color: Colors.white,
|
||||
@@ -114,14 +115,17 @@ class IoniaCustomRedeemPage extends BasePage {
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
Observer(builder: (_)=>
|
||||
!ioniaCustomRedeemViewModel.disableRedeem ?
|
||||
Center(
|
||||
child: Text('\$${giftCard.remainingAmount} - \$${ioniaCustomRedeemViewModel.amount} = \$${ioniaCustomRedeemViewModel.formattedRemaining} ${S.of(context).remaining}',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).primaryTextTheme!.headline5!.color!,
|
||||
),),
|
||||
) : SizedBox.shrink(),
|
||||
Observer(
|
||||
builder: (_) => !ioniaCustomRedeemViewModel.disableRedeem
|
||||
? Center(
|
||||
child: Text(
|
||||
'\$${giftCard.remainingAmount} - \$${ioniaCustomRedeemViewModel.amount} = \$${ioniaCustomRedeemViewModel.formattedRemaining} ${S.of(context).remaining}',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).primaryTextTheme.headline5!.color!,
|
||||
),
|
||||
),
|
||||
)
|
||||
: SizedBox.shrink(),
|
||||
),
|
||||
SizedBox(height: 24),
|
||||
],
|
||||
@@ -131,30 +135,37 @@ class IoniaCustomRedeemPage extends BasePage {
|
||||
padding: const EdgeInsets.all(24.0),
|
||||
child: CardItem(
|
||||
title: giftCard.legalName,
|
||||
backgroundColor: Theme.of(context).accentTextTheme!.headline1!.backgroundColor!.withOpacity(0.1),
|
||||
backgroundColor: Theme.of(context)
|
||||
.accentTextTheme
|
||||
.headline1!
|
||||
.backgroundColor!
|
||||
.withOpacity(0.1),
|
||||
discount: giftCard.remainingAmount,
|
||||
isAmount: true,
|
||||
discountBackground: AssetImage('assets/images/red_badge_discount.png'),
|
||||
titleColor: Theme.of(context).accentTextTheme!.headline1!.backgroundColor!,
|
||||
titleColor: Theme.of(context).accentTextTheme.headline1!.backgroundColor!,
|
||||
subtitleColor: Theme.of(context).hintColor,
|
||||
subTitle: S.of(context).online,
|
||||
logoUrl: giftCard.logoUrl,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
bottomSection: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(bottom: 12),
|
||||
child: PrimaryButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(_amountController.text);
|
||||
},
|
||||
isDisabled: ioniaCustomRedeemViewModel.disableRedeem,
|
||||
text: S.of(context).add_custom_redemption,
|
||||
color: Theme.of(context).accentTextTheme!.bodyText1!.color!,
|
||||
textColor: Colors.white,
|
||||
Observer(
|
||||
builder: (_) => Padding(
|
||||
padding: EdgeInsets.only(bottom: 12),
|
||||
child: LoadingPrimaryButton(
|
||||
isLoading: ioniaCustomRedeemViewModel.redeemState is IsExecutingState,
|
||||
isDisabled: ioniaCustomRedeemViewModel.disableRedeem,
|
||||
text: S.of(context).add_custom_redemption,
|
||||
color: Theme.of(context).accentTextTheme.bodyText1!.color!,
|
||||
textColor: Colors.white,
|
||||
onPressed: () => ioniaCustomRedeemViewModel.addCustomRedeem().then((value) {
|
||||
Navigator.of(context).pop(ioniaCustomRedeemViewModel.remaining.toString());
|
||||
}),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 30),
|
||||
|
||||
@@ -32,7 +32,7 @@ class IoniaGiftCardDetailPage extends BasePage {
|
||||
|
||||
final _backButton = Icon(
|
||||
Icons.arrow_back_ios,
|
||||
color: Theme.of(context).primaryTextTheme!.headline6!.color!,
|
||||
color: Theme.of(context).primaryTextTheme.headline6!.color!,
|
||||
size: 16,
|
||||
);
|
||||
return Padding(
|
||||
@@ -43,7 +43,7 @@ class IoniaGiftCardDetailPage extends BasePage {
|
||||
child: ButtonTheme(
|
||||
minWidth: double.minPositive,
|
||||
child: TextButton(
|
||||
// FIX-ME: Style
|
||||
// FIX-ME: Style
|
||||
//highlightColor: Colors.transparent,
|
||||
//splashColor: Colors.transparent,
|
||||
//padding: EdgeInsets.all(0),
|
||||
@@ -61,7 +61,8 @@ class IoniaGiftCardDetailPage extends BasePage {
|
||||
Widget middle(BuildContext context) {
|
||||
return Text(
|
||||
viewModel.giftCard.legalName,
|
||||
style: textMediumSemiBold(color: Theme.of(context).accentTextTheme!.headline1!.backgroundColor!),
|
||||
style:
|
||||
textMediumSemiBold(color: Theme.of(context).accentTextTheme.headline1!.backgroundColor!),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -102,20 +103,21 @@ class IoniaGiftCardDetailPage extends BasePage {
|
||||
title: S.of(context).gift_card_number,
|
||||
subTitle: viewModel.giftCard.cardNumber,
|
||||
),
|
||||
if (viewModel.giftCard.cardPin?.isNotEmpty ?? false)
|
||||
...[Divider(height: 30),
|
||||
if (viewModel.giftCard.cardPin.isNotEmpty) ...[
|
||||
Divider(height: 30),
|
||||
buildIoniaTile(
|
||||
context,
|
||||
title: S.of(context).pin_number,
|
||||
subTitle: viewModel.giftCard.cardPin,
|
||||
)],
|
||||
)
|
||||
],
|
||||
Divider(height: 30),
|
||||
Observer(builder: (_) =>
|
||||
buildIoniaTile(
|
||||
context,
|
||||
title: S.of(context).amount,
|
||||
subTitle: viewModel.remainingAmount.toStringAsFixed(2) ?? '0.00',
|
||||
)),
|
||||
Observer(
|
||||
builder: (_) => buildIoniaTile(
|
||||
context,
|
||||
title: S.of(context).amount,
|
||||
subTitle: viewModel.remainingAmount.toStringAsFixed(2),
|
||||
)),
|
||||
Divider(height: 50),
|
||||
TextIconButton(
|
||||
label: S.of(context).how_to_use_card,
|
||||
@@ -130,29 +132,28 @@ class IoniaGiftCardDetailPage extends BasePage {
|
||||
if (!viewModel.giftCard.isEmpty) {
|
||||
return Column(
|
||||
children: [
|
||||
//PrimaryButton(
|
||||
// onPressed: () async {
|
||||
// final amount = await Navigator.of(context)
|
||||
// .pushNamed(Routes.ioniaMoreOptionsPage, arguments: [viewModel.giftCard]) as String;
|
||||
// if (amount != null) {
|
||||
// viewModel.updateRemaining(double.parse(amount));
|
||||
// }
|
||||
// },
|
||||
// text: S.of(context).more_options,
|
||||
// color: Theme.of(context).accentTextTheme!.caption!.color!,
|
||||
// textColor: Theme.of(context).primaryTextTheme!.headline6!.color!,
|
||||
//),
|
||||
//SizedBox(height: 12),
|
||||
PrimaryButton(
|
||||
onPressed: () async {
|
||||
await Navigator.of(context).pushNamed(
|
||||
Routes.ioniaMoreOptionsPage,
|
||||
arguments: [viewModel.giftCard]) as String?;
|
||||
viewModel.refeshCard();
|
||||
},
|
||||
text: S.of(context).more_options,
|
||||
color: Theme.of(context).accentTextTheme.caption!.color!,
|
||||
textColor: Theme.of(context).primaryTextTheme.headline6!.color!,
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
LoadingPrimaryButton(
|
||||
isLoading: viewModel.redeemState is IsExecutingState,
|
||||
onPressed: () => viewModel.redeem().then(
|
||||
(_) {
|
||||
Navigator.of(context)
|
||||
.pushNamedAndRemoveUntil(Routes.ioniaManageCardsPage, (route) => route.isFirst);
|
||||
Navigator.of(context).pushNamedAndRemoveUntil(
|
||||
Routes.ioniaManageCardsPage, (route) => route.isFirst);
|
||||
},
|
||||
),
|
||||
text: S.of(context).mark_as_redeemed,
|
||||
color: Theme.of(context).accentTextTheme!.bodyText1!.color!,
|
||||
color: Theme.of(context).accentTextTheme.bodyText1!.color!,
|
||||
textColor: Colors.white,
|
||||
),
|
||||
],
|
||||
@@ -168,12 +169,11 @@ class IoniaGiftCardDetailPage extends BasePage {
|
||||
|
||||
Widget buildIoniaTile(BuildContext context, {required String title, required String subTitle}) {
|
||||
return IoniaTile(
|
||||
title: title,
|
||||
subTitle: subTitle,
|
||||
onTap: () {
|
||||
Clipboard.setData(ClipboardData(text: subTitle));
|
||||
showBar<void>(context,
|
||||
S.of(context).transaction_details_copied(title));
|
||||
title: title,
|
||||
subTitle: subTitle,
|
||||
onTap: () {
|
||||
Clipboard.setData(ClipboardData(text: subTitle));
|
||||
showBar<void>(context, S.of(context).transaction_details_copied(title));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -184,10 +184,10 @@ class IoniaGiftCardDetailPage extends BasePage {
|
||||
showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return IoniaAlertModal(
|
||||
return IoniaAlertModal(
|
||||
title: S.of(context).how_to_use_card,
|
||||
content: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: viewModel.giftCard.instructions
|
||||
.map((instruction) {
|
||||
return [
|
||||
@@ -196,13 +196,13 @@ class IoniaGiftCardDetailPage extends BasePage {
|
||||
child: Text(
|
||||
instruction.header,
|
||||
style: textLargeSemiBold(
|
||||
color: Theme.of(context).textTheme!.headline3!.color!,
|
||||
color: Theme.of(context).textTheme.headline3!.color!,
|
||||
),
|
||||
)),
|
||||
Text(
|
||||
instruction.body,
|
||||
style: textMedium(
|
||||
color: Theme.of(context).textTheme!.headline3!.color!,
|
||||
color: Theme.of(context).textTheme.headline3!.color!,
|
||||
),
|
||||
)
|
||||
];
|
||||
@@ -210,7 +210,7 @@ class IoniaGiftCardDetailPage extends BasePage {
|
||||
.expand((e) => e)
|
||||
.toList()),
|
||||
actionTitle: S.of(context).send_got_it,
|
||||
);
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/typography.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
|
||||
class IoniaMoreOptionsPage extends BasePage {
|
||||
IoniaMoreOptionsPage(this.giftCard);
|
||||
|
||||
@@ -16,7 +15,7 @@ class IoniaMoreOptionsPage extends BasePage {
|
||||
return Text(
|
||||
S.current.more_options,
|
||||
style: textMediumSemiBold(
|
||||
color: Theme.of(context).accentTextTheme!.headline1!.backgroundColor!,
|
||||
color: Theme.of(context).accentTextTheme.headline1!.backgroundColor!,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -27,40 +26,45 @@ class IoniaMoreOptionsPage extends BasePage {
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
SizedBox(height: 10,),
|
||||
Center(child: Text(S.of(context).choose_from_available_options, style: textMedium(
|
||||
color: Theme.of(context).primaryTextTheme!.headline6!.color!,
|
||||
),)),
|
||||
SizedBox(height: 40,),
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
final amount = await Navigator.of(context).pushNamed(Routes.ioniaCustomRedeemPage, arguments: [giftCard]) as String;
|
||||
if(amount.isNotEmpty){
|
||||
Navigator.pop(context, amount);
|
||||
}
|
||||
},
|
||||
child: _GradiantContainer(
|
||||
content: Padding(
|
||||
padding: const EdgeInsets.only(top: 24, left: 20, right: 24, bottom: 50),
|
||||
child: Text(
|
||||
S.of(context).custom_redeem_amount,
|
||||
style: textXLargeSemiBold(),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
Center(
|
||||
child: Text(
|
||||
S.of(context).choose_from_available_options,
|
||||
style: textMedium(
|
||||
color: Theme.of(context).primaryTextTheme.headline6!.color!,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 40),
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
final amount = await Navigator.of(context)
|
||||
.pushNamed(Routes.ioniaCustomRedeemPage, arguments: [giftCard]) as String?;
|
||||
if (amount != null && amount.isNotEmpty) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
child: _GradiantContainer(
|
||||
content: Padding(
|
||||
padding: const EdgeInsets.only(top: 24, left: 20, right: 24, bottom: 50),
|
||||
child: Text(
|
||||
S.of(context).custom_redeem_amount,
|
||||
style: textXLargeSemiBold(),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _GradiantContainer extends StatelessWidget {
|
||||
const _GradiantContainer({
|
||||
Key? key,
|
||||
required this.content
|
||||
}) : super(key: key);
|
||||
const _GradiantContainer({Key? key, required this.content}) : super(key: key);
|
||||
|
||||
final Widget content;
|
||||
|
||||
|
||||
@@ -1,23 +1,28 @@
|
||||
import 'dart:async';
|
||||
import 'package:cake_wallet/core/auth_service.dart';
|
||||
import 'package:cake_wallet/utils/payment_request.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cake_wallet/store/authentication_store.dart';
|
||||
import 'package:cake_wallet/entities/qr_scanner.dart';
|
||||
import 'package:uni_links/uni_links.dart';
|
||||
|
||||
class Root extends StatefulWidget {
|
||||
Root(
|
||||
{required Key key,
|
||||
required this.authenticationStore,
|
||||
required this.appStore,
|
||||
required this.child,
|
||||
required this.navigatorKey})
|
||||
: super(key: key);
|
||||
Root({
|
||||
required Key key,
|
||||
required this.authenticationStore,
|
||||
required this.appStore,
|
||||
required this.child,
|
||||
required this.navigatorKey,
|
||||
required this.authService,
|
||||
}) : super(key: key);
|
||||
|
||||
final AuthenticationStore authenticationStore;
|
||||
final AppStore appStore;
|
||||
final GlobalKey<NavigatorState> navigatorKey;
|
||||
final AuthService authService;
|
||||
final Widget child;
|
||||
|
||||
@override
|
||||
@@ -26,22 +31,56 @@ class Root extends StatefulWidget {
|
||||
|
||||
class RootState extends State<Root> with WidgetsBindingObserver {
|
||||
RootState()
|
||||
: _isInactiveController = StreamController<bool>.broadcast(),
|
||||
_isInactive = false,
|
||||
_postFrameCallback = false;
|
||||
: _isInactiveController = StreamController<bool>.broadcast(),
|
||||
_isInactive = false,
|
||||
_requestAuth = true,
|
||||
_postFrameCallback = false;
|
||||
|
||||
Stream<bool> get isInactive => _isInactiveController.stream;
|
||||
StreamController<bool> _isInactiveController;
|
||||
bool _isInactive;
|
||||
bool _postFrameCallback;
|
||||
bool _requestAuth;
|
||||
|
||||
StreamSubscription<Uri?>? stream;
|
||||
Uri? launchUri;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_requestAuth = widget.authService.requireAuth();
|
||||
_isInactiveController = StreamController<bool>.broadcast();
|
||||
_isInactive = false;
|
||||
_postFrameCallback = false;
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
super.initState();
|
||||
|
||||
initUniLinks();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
stream?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
/// handle app links while the app is already started
|
||||
/// whether its in the foreground or in the background.
|
||||
Future<void> initUniLinks() async {
|
||||
try {
|
||||
stream = uriLinkStream.listen((Uri? uri) {
|
||||
handleDeepLinking(uri);
|
||||
});
|
||||
|
||||
handleDeepLinking(await getInitialUri());
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
|
||||
void handleDeepLinking(Uri? uri) {
|
||||
if (uri == null || !mounted) return;
|
||||
|
||||
launchUri = uri;
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -52,11 +91,15 @@ class RootState extends State<Root> with WidgetsBindingObserver {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_isInactive &&
|
||||
widget.authenticationStore.state == AuthenticationState.allowed) {
|
||||
if (!_isInactive && widget.authenticationStore.state == AuthenticationState.allowed) {
|
||||
setState(() => _setInactive(true));
|
||||
}
|
||||
|
||||
break;
|
||||
case AppLifecycleState.resumed:
|
||||
setState(() {
|
||||
_requestAuth = widget.authService.requireAuth();
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -65,7 +108,7 @@ class RootState extends State<Root> with WidgetsBindingObserver {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (_isInactive && !_postFrameCallback) {
|
||||
if (_isInactive && !_postFrameCallback && _requestAuth) {
|
||||
_postFrameCallback = true;
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
widget.navigatorKey.currentState?.pushNamed(Routes.unlock,
|
||||
@@ -75,9 +118,19 @@ class RootState extends State<Root> with WidgetsBindingObserver {
|
||||
}
|
||||
|
||||
_reset();
|
||||
auth.close();
|
||||
auth.close(
|
||||
route: launchUri != null ? Routes.send : null,
|
||||
arguments: PaymentRequest.fromUri(launchUri),
|
||||
);
|
||||
launchUri = null;
|
||||
});
|
||||
});
|
||||
} else if (launchUri != null) {
|
||||
widget.navigatorKey.currentState?.pushNamed(
|
||||
Routes.send,
|
||||
arguments: PaymentRequest.fromUri(launchUri),
|
||||
);
|
||||
launchUri = null;
|
||||
}
|
||||
|
||||
return WillPopScope(onWillPop: () async => false, child: widget.child);
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:cake_wallet/src/screens/send/widgets/send_card.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||
import 'package:cake_wallet/src/widgets/picker.dart';
|
||||
import 'package:cake_wallet/src/widgets/template_tile.dart';
|
||||
import 'package:cake_wallet/utils/payment_request.dart';
|
||||
import 'package:cake_wallet/view_model/send/output.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
@@ -25,11 +26,15 @@ import 'package:smooth_page_indicator/smooth_page_indicator.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
|
||||
class SendPage extends BasePage {
|
||||
SendPage({required this.sendViewModel}) : _formKey = GlobalKey<FormState>();
|
||||
SendPage({
|
||||
required this.sendViewModel,
|
||||
this.initialPaymentRequest,
|
||||
}) : _formKey = GlobalKey<FormState>();
|
||||
|
||||
final SendViewModel sendViewModel;
|
||||
final GlobalKey<FormState> _formKey;
|
||||
final controller = PageController(initialPage: 0);
|
||||
final PaymentRequest? initialPaymentRequest;
|
||||
|
||||
bool _effectsInstalled = false;
|
||||
|
||||
@@ -116,6 +121,7 @@ class SendPage extends BasePage {
|
||||
key: output.key,
|
||||
output: output,
|
||||
sendViewModel: sendViewModel,
|
||||
initialPaymentRequest: initialPaymentRequest,
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:cake_wallet/entities/priority_for_wallet_type.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/utils/payment_request.dart';
|
||||
import 'package:cw_core/transaction_priority.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
@@ -20,21 +21,28 @@ class SendCard extends StatefulWidget {
|
||||
SendCard({
|
||||
Key? key,
|
||||
required this.output,
|
||||
required this.sendViewModel}) : super(key: key);
|
||||
required this.sendViewModel,
|
||||
this.initialPaymentRequest,
|
||||
}) : super(key: key);
|
||||
|
||||
final Output output;
|
||||
final SendViewModel sendViewModel;
|
||||
final PaymentRequest? initialPaymentRequest;
|
||||
|
||||
@override
|
||||
SendCardState createState() => SendCardState(
|
||||
output: output,
|
||||
sendViewModel: sendViewModel
|
||||
sendViewModel: sendViewModel,
|
||||
initialPaymentRequest: initialPaymentRequest,
|
||||
);
|
||||
}
|
||||
|
||||
class SendCardState extends State<SendCard>
|
||||
with AutomaticKeepAliveClientMixin<SendCard> {
|
||||
SendCardState({required this.output, required this.sendViewModel})
|
||||
SendCardState({
|
||||
required this.output,
|
||||
required this.sendViewModel,
|
||||
this.initialPaymentRequest})
|
||||
: addressController = TextEditingController(),
|
||||
cryptoAmountController = TextEditingController(),
|
||||
fiatAmountController = TextEditingController(),
|
||||
@@ -49,6 +57,7 @@ class SendCardState extends State<SendCard>
|
||||
|
||||
final Output output;
|
||||
final SendViewModel sendViewModel;
|
||||
final PaymentRequest? initialPaymentRequest;
|
||||
|
||||
final TextEditingController addressController;
|
||||
final TextEditingController cryptoAmountController;
|
||||
@@ -61,6 +70,27 @@ class SendCardState extends State<SendCard>
|
||||
|
||||
bool _effectsInstalled = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
/// if the current wallet doesn't match the one in the qr code
|
||||
if (initialPaymentRequest != null &&
|
||||
sendViewModel.walletCurrencyName != initialPaymentRequest!.scheme.toLowerCase()) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
||||
showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithOneAction(
|
||||
alertTitle: S.of(context).error,
|
||||
alertContent: S.of(context).unmatched_currencies,
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop());
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
@@ -512,8 +542,12 @@ class SendCardState extends State<SendCard>
|
||||
}
|
||||
|
||||
void _setEffects(BuildContext context) {
|
||||
addressController.text = output.address;
|
||||
cryptoAmountController.text = output.cryptoAmount;
|
||||
if (output.address.isNotEmpty) {
|
||||
addressController.text = output.address;
|
||||
}
|
||||
if (output.cryptoAmount.isNotEmpty) {
|
||||
cryptoAmountController.text = output.cryptoAmount;
|
||||
}
|
||||
fiatAmountController.text = output.fiatAmount;
|
||||
noteController.text = output.note;
|
||||
extractedAddressController.text = output.extractedAddress;
|
||||
@@ -605,6 +639,13 @@ class SendCardState extends State<SendCard>
|
||||
extractedAddressController.text = extractedAddress;
|
||||
});
|
||||
|
||||
if (initialPaymentRequest != null &&
|
||||
sendViewModel.walletCurrencyName == initialPaymentRequest!.scheme.toLowerCase()) {
|
||||
addressController.text = initialPaymentRequest!.address;
|
||||
cryptoAmountController.text = initialPaymentRequest!.amount;
|
||||
noteController.text = initialPaymentRequest!.note;
|
||||
}
|
||||
|
||||
_effectsInstalled = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import 'package:cake_wallet/entities/pin_code_required_duration.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_list.dart';
|
||||
import 'package:cake_wallet/view_model/settings/security_settings_view_model.dart';
|
||||
@@ -25,22 +27,26 @@ class SecurityBackupPage extends BasePage {
|
||||
child: Column(mainAxisSize: MainAxisSize.min, children: [
|
||||
SettingsCellWithArrow(
|
||||
title: S.current.show_keys,
|
||||
handler: (_) => Navigator.of(context).pushNamed(Routes.auth,
|
||||
arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) {
|
||||
if (isAuthenticatedSuccessfully) {
|
||||
auth.close(route: Routes.showKeys);
|
||||
}
|
||||
}),
|
||||
handler: (_) => _securitySettingsViewModel.checkPinCodeRiquired()
|
||||
? Navigator.of(context).pushNamed(Routes.auth,
|
||||
arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) {
|
||||
if (isAuthenticatedSuccessfully) {
|
||||
auth.close(route: Routes.showKeys);
|
||||
}
|
||||
})
|
||||
: Navigator.of(context).pushNamed(Routes.showKeys),
|
||||
),
|
||||
StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
|
||||
SettingsCellWithArrow(
|
||||
title: S.current.create_backup,
|
||||
handler: (_) => Navigator.of(context).pushNamed(Routes.auth,
|
||||
arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) {
|
||||
if (isAuthenticatedSuccessfully) {
|
||||
auth.close(route: Routes.backup);
|
||||
}
|
||||
}),
|
||||
handler: (_) => _securitySettingsViewModel.checkPinCodeRiquired()
|
||||
? Navigator.of(context).pushNamed(Routes.auth,
|
||||
arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) {
|
||||
if (isAuthenticatedSuccessfully) {
|
||||
auth.close(route: Routes.backup);
|
||||
}
|
||||
})
|
||||
: Navigator.of(context).pushNamed(Routes.backup),
|
||||
),
|
||||
StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
|
||||
SettingsCellWithArrow(
|
||||
@@ -65,10 +71,12 @@ class SecurityBackupPage extends BasePage {
|
||||
arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) async {
|
||||
if (isAuthenticatedSuccessfully) {
|
||||
if (await _securitySettingsViewModel.biometricAuthenticated()) {
|
||||
_securitySettingsViewModel.setAllowBiometricalAuthentication(isAuthenticatedSuccessfully);
|
||||
_securitySettingsViewModel
|
||||
.setAllowBiometricalAuthentication(isAuthenticatedSuccessfully);
|
||||
}
|
||||
} else {
|
||||
_securitySettingsViewModel.setAllowBiometricalAuthentication(isAuthenticatedSuccessfully);
|
||||
_securitySettingsViewModel
|
||||
.setAllowBiometricalAuthentication(isAuthenticatedSuccessfully);
|
||||
}
|
||||
|
||||
auth.close();
|
||||
@@ -78,6 +86,16 @@ class SecurityBackupPage extends BasePage {
|
||||
}
|
||||
});
|
||||
}),
|
||||
Observer(builder: (_) {
|
||||
return SettingsPickerCell<PinCodeRequiredDuration>(
|
||||
title: S.current.require_pin_after,
|
||||
items: PinCodeRequiredDuration.values,
|
||||
selectedItem: _securitySettingsViewModel.pinCodeRequiredDuration,
|
||||
onItemSelected: (PinCodeRequiredDuration code) {
|
||||
_securitySettingsViewModel.setPinCodeRequiredDuration(code);
|
||||
},
|
||||
);
|
||||
}),
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -220,68 +220,88 @@ class WalletListBodyState extends State<WalletListBody> {
|
||||
}
|
||||
|
||||
Future<void> _loadWallet(WalletListItem wallet) async {
|
||||
await Navigator.of(context).pushNamed(Routes.auth, arguments:
|
||||
(bool isAuthenticatedSuccessfully, AuthPageState auth) async {
|
||||
if (!isAuthenticatedSuccessfully) {
|
||||
return;
|
||||
}
|
||||
if (await widget.walletListViewModel.checkIfAuthRequired()) {
|
||||
await Navigator.of(context).pushNamed(Routes.auth,
|
||||
arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) async {
|
||||
if (!isAuthenticatedSuccessfully) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
auth.changeProcessText(S.of(context).wallet_list_loading_wallet(wallet.name));
|
||||
await widget.walletListViewModel.loadWallet(wallet);
|
||||
auth.hideProgressText();
|
||||
auth.close();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
Navigator.of(context).pop();
|
||||
});
|
||||
} catch (e) {
|
||||
auth.changeProcessText(
|
||||
S.of(context).wallet_list_failed_to_load(wallet.name, e.toString()));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
try {
|
||||
auth.changeProcessText(
|
||||
S.of(context).wallet_list_loading_wallet(wallet.name));
|
||||
changeProcessText(S.of(context).wallet_list_loading_wallet(wallet.name));
|
||||
await widget.walletListViewModel.loadWallet(wallet);
|
||||
auth.hideProgressText();
|
||||
auth.close();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
Navigator.of(context).pop();
|
||||
});
|
||||
hideProgressText();
|
||||
Navigator.of(context).pop();
|
||||
} catch (e) {
|
||||
auth.changeProcessText(S
|
||||
.of(context)
|
||||
.wallet_list_failed_to_load(wallet.name, e.toString()));
|
||||
changeProcessText(S.of(context).wallet_list_failed_to_load(wallet.name, e.toString()));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _removeWallet(WalletListItem wallet) async {
|
||||
await Navigator.of(context).pushNamed(Routes.auth, arguments:
|
||||
(bool isAuthenticatedSuccessfully, AuthPageState auth) async {
|
||||
if (!isAuthenticatedSuccessfully) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool confirmed = false;
|
||||
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithTwoActions(
|
||||
alertTitle: S.of(context).delete_wallet,
|
||||
alertContent: S.of(context).delete_wallet_confirm_message(wallet.name),
|
||||
leftButtonText: S.of(context).cancel,
|
||||
rightButtonText: S.of(context).delete,
|
||||
actionLeftButton: () => Navigator.of(context).pop(),
|
||||
actionRightButton: () {
|
||||
confirmed = true;
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
if (confirmed) {
|
||||
try {
|
||||
auth.changeProcessText(
|
||||
S.of(context).wallet_list_removing_wallet(wallet.name));
|
||||
await widget.walletListViewModel.remove(wallet);
|
||||
} catch (e) {
|
||||
auth.changeProcessText(S
|
||||
.of(context)
|
||||
.wallet_list_failed_to_remove(wallet.name, e.toString()));
|
||||
if (widget.walletListViewModel.checkIfAuthRequired()) {
|
||||
await Navigator.of(context).pushNamed(Routes.auth,
|
||||
arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) async {
|
||||
if (!isAuthenticatedSuccessfully) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
_onSuccessfulAuth(wallet, auth);
|
||||
});
|
||||
} else {
|
||||
_onSuccessfulAuth(wallet, null);
|
||||
}
|
||||
}
|
||||
|
||||
auth.close();
|
||||
});
|
||||
void _onSuccessfulAuth(WalletListItem wallet, AuthPageState? auth) async {
|
||||
bool confirmed = false;
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithTwoActions(
|
||||
alertTitle: S.of(context).delete_wallet,
|
||||
alertContent: S.of(context).delete_wallet_confirm_message(wallet.name),
|
||||
leftButtonText: S.of(context).cancel,
|
||||
rightButtonText: S.of(context).delete,
|
||||
actionLeftButton: () => Navigator.of(context).pop(),
|
||||
actionRightButton: () {
|
||||
confirmed = true;
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
if (confirmed) {
|
||||
try {
|
||||
auth != null
|
||||
? auth.changeProcessText(S.of(context).wallet_list_removing_wallet(wallet.name))
|
||||
: changeProcessText(S.of(context).wallet_list_removing_wallet(wallet.name));
|
||||
await widget.walletListViewModel.remove(wallet);
|
||||
} catch (e) {
|
||||
auth != null
|
||||
? auth.changeProcessText(
|
||||
S.of(context).wallet_list_failed_to_remove(wallet.name, e.toString()),
|
||||
)
|
||||
: changeProcessText(
|
||||
S.of(context).wallet_list_failed_to_remove(wallet.name, e.toString()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
auth?.close();
|
||||
}
|
||||
|
||||
void changeProcessText(String text) {
|
||||
@@ -294,16 +314,16 @@ class WalletListBodyState extends State<WalletListBody> {
|
||||
}
|
||||
|
||||
ActionPane _actionPane(WalletListItem wallet) => ActionPane(
|
||||
motion: const ScrollMotion(),
|
||||
extentRatio: 0.3,
|
||||
children: [
|
||||
SlidableAction(
|
||||
onPressed: (_) => _removeWallet(wallet),
|
||||
backgroundColor: Colors.red,
|
||||
foregroundColor: Colors.white,
|
||||
icon: CupertinoIcons.delete,
|
||||
label: S.of(context).delete,
|
||||
),
|
||||
],
|
||||
);
|
||||
motion: const ScrollMotion(),
|
||||
extentRatio: 0.3,
|
||||
children: [
|
||||
SlidableAction(
|
||||
onPressed: (_) => _removeWallet(wallet),
|
||||
backgroundColor: Colors.red,
|
||||
foregroundColor: Colors.white,
|
||||
icon: CupertinoIcons.delete,
|
||||
label: S.of(context).delete,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user