* feat: rebase btc-addr-types, migrate to bitcoin_base

* feat: allow scanning elect-rs using get_tweaks

* feat: scanning and adding addresses working with getTweaks, add btc SP address type

* chore: pubspec.lock

* chore: pubspec.lock

* fix: scan when switching, fix multiple unspents in same tx

* fix: initial scan

* fix: initial scan

* fix: scanning issues

* fix: sync, storing silent unspents

* chore: deps

* fix: label issues, clear spent utxo

* chore: deps

* fix: build

* fix: missing types

* feat: new electrs API & changes, fixes for last block scanning

* feat: Scan Silent Payments homepage toggle

* chore: build configure

* feat: generic fixes, testnet UI improvements, useSSL on bitcoin nodes

* fix: invalid Object in sendData

* feat: improve addresses page & address book displays

* feat: silent payments labeled addresses disclaimer

* fix: missing i18n

* chore: print

* feat: single block scan, rescan by date working for btc mainnet

* feat: new cake features page replace market page, move sp scan toggle, auto switch node pop up alert

* feat: delete silent addresses

* fix: red dot in non ssl nodes

* fix: inconsistent connection states, fix tx history

* fix: tx & balance displays, cpfp sending

* feat: new rust lib

* chore: node path

* fix: check node based on network

* fix: missing txcount from addresses

* style: padding in feature page cards

* fix: restore not getting all wallet addresses by type

* fix: auto switch node broken

* fix: silent payment txs not being restored

* feat: change scanning to subscription model, sync improvements

* fix: scan re-subscription

* fix: default nodes

* fix: improve scanning by date, fix single block scan

* refactor: common function for input tx selection

* fix: nodes & build

* fix: send all with multiple outs

* refactor: unchanged file

* Update pr_test_build.yml

* chore: upgrade

* chore: merge changes

* refactor: unchanged files [skip ci]

* fix: scan fixes, add date, allow sending while scanning

* feat: sync fixes, sp settings

* feat: fix resyncing

* fix: date from height logic, status disconnected & chain tip get

* fix: params

* feat: electrum migration if using cake electrum

* fix nodes
update versions

* re-enable tron

* update sp_scanner to work on iOS [skip ci]

* fix: wrong socket for old electrum nodes

* Fix unchecked wallet type call

* fix: double balance

* feat: node domain

* fix: menu name

* fix: update tip on set scanning

* fix: connection switching back and forth

* feat: check if node is electrs, and supports sp

* chore: fix build

* minor enhancements

* fixes and enhancements

* solve conflicts with main

* fix: status toggle

* minor enhancement

* Monero.com fixes

* update sp_scanner to include windows and linux

---------

Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
This commit is contained in:
Rafael
2024-05-29 17:43:48 +03:00
committed by GitHub
co-authored by Omar Hatem
parent faa49d21e8
commit 96b9b60f50
122 changed files with 3889 additions and 1252 deletions
@@ -4,7 +4,7 @@ import 'package:cake_wallet/entities/preferences_key.dart';
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/entities/main_actions.dart';
import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_sidebar_wrapper.dart';
import 'package:cake_wallet/src/screens/dashboard/pages/market_place_page.dart';
import 'package:cake_wallet/src/screens/dashboard/pages/cake_features_page.dart';
import 'package:cake_wallet/src/screens/wallet_connect/widgets/modals/bottom_sheet_listener.dart';
import 'package:cake_wallet/src/widgets/gradient_background.dart';
import 'package:cake_wallet/src/widgets/services_updates_widget.dart';
@@ -12,7 +12,7 @@ import 'package:cake_wallet/src/widgets/vulnerable_seeds_popup.dart';
import 'package:cake_wallet/themes/extensions/sync_indicator_theme.dart';
import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/utils/version_comparator.dart';
import 'package:cake_wallet/view_model/dashboard/market_place_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/cake_features_view_model.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/yat_emoji_id.dart';
@@ -330,10 +330,10 @@ class _DashboardPageView extends BasePage {
if (dashboardViewModel.shouldShowMarketPlaceInDashboard) {
pages.add(
Semantics(
label: S.of(context).market_place,
child: MarketPlacePage(
label: 'Cake ${S.of(context).features}',
child: CakeFeaturesPage(
dashboardViewModel: dashboardViewModel,
marketPlaceViewModel: getIt.get<MarketPlaceViewModel>(),
cakeFeaturesViewModel: getIt.get<CakeFeaturesViewModel>(),
),
),
);
@@ -1,9 +1,9 @@
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/entities/main_actions.dart';
import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_action_button.dart';
import 'package:cake_wallet/src/screens/dashboard/pages/market_place_page.dart';
import 'package:cake_wallet/src/screens/dashboard/pages/cake_features_page.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/market_place_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/cake_features_view_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
@@ -74,9 +74,9 @@ class DesktopDashboardActions extends StatelessWidget {
],
),
Expanded(
child: MarketPlacePage(
child: CakeFeaturesPage(
dashboardViewModel: dashboardViewModel,
marketPlaceViewModel: getIt.get<MarketPlaceViewModel>(),
cakeFeaturesViewModel: getIt.get<CakeFeaturesViewModel>(),
),
),
],
@@ -30,6 +30,7 @@ class DesktopWalletSelectionDropDown extends StatefulWidget {
class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionDropDown> {
final moneroIcon = Image.asset('assets/images/monero_logo.png', height: 24, width: 24);
final bitcoinIcon = Image.asset('assets/images/bitcoin.png', height: 24, width: 24);
final tBitcoinIcon = Image.asset('assets/images/tbtc.png', height: 24, width: 24);
final litecoinIcon = Image.asset('assets/images/litecoin_icon.png', height: 24, width: 24);
final havenIcon = Image.asset('assets/images/haven_logo.png', height: 24, width: 24);
final ethereumIcon = Image.asset('assets/images/eth_icon.png', height: 24, width: 24);
@@ -68,8 +69,11 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: 500),
child: DropDownItemWidget(
title: wallet.name,
image: wallet.isEnabled ? _imageFor(type: wallet.type) : nonWalletTypeIcon),
title: wallet.name,
image: wallet.isEnabled
? _imageFor(type: wallet.type, isTestnet: wallet.isTestnet)
: nonWalletTypeIcon,
),
),
onSelected: () => _onSelectedWallet(wallet),
))
@@ -120,16 +124,16 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD
WidgetsBinding.instance.addPostFrameCallback((_) async {
final confirmed = await showPopUp<bool>(
context: context,
builder: (dialogContext) {
return AlertWithTwoActions(
alertTitle: S.of(context).change_wallet_alert_title,
alertContent: S.of(context).change_wallet_alert_content(selectedWallet.name),
leftButtonText: S.of(context).cancel,
rightButtonText: S.of(context).change,
actionLeftButton: () => Navigator.of(dialogContext).pop(false),
actionRightButton: () => Navigator.of(dialogContext).pop(true));
}) ??
context: context,
builder: (dialogContext) {
return AlertWithTwoActions(
alertTitle: S.of(context).change_wallet_alert_title,
alertContent: S.of(context).change_wallet_alert_content(selectedWallet.name),
leftButtonText: S.of(context).cancel,
rightButtonText: S.of(context).change,
actionLeftButton: () => Navigator.of(dialogContext).pop(false),
actionRightButton: () => Navigator.of(dialogContext).pop(true));
}) ??
false;
if (confirmed) {
@@ -138,9 +142,12 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD
});
}
Image _imageFor({required WalletType type}) {
Image _imageFor({required WalletType type, bool? isTestnet}) {
switch (type) {
case WalletType.bitcoin:
if (isTestnet == true) {
return tBitcoinIcon;
}
return bitcoinIcon;
case WalletType.monero:
return moneroIcon;
@@ -160,7 +167,7 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD
return polygonIcon;
case WalletType.solana:
return solanaIcon;
case WalletType.tron:
case WalletType.tron:
return tronIcon;
default:
return nonWalletTypeIcon;
@@ -168,24 +175,25 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD
}
Future<void> _loadWallet(WalletListItem wallet) async {
widget._authService.authenticateAction(context,
onAuthSuccess: (isAuthenticatedSuccessfully) async {
if (!isAuthenticatedSuccessfully) {
return;
}
widget._authService.authenticateAction(
context,
onAuthSuccess: (isAuthenticatedSuccessfully) async {
if (!isAuthenticatedSuccessfully) {
return;
}
try {
if (context.mounted) {
changeProcessText(S.of(context).wallet_list_loading_wallet(wallet.name));
try {
if (context.mounted) {
changeProcessText(S.of(context).wallet_list_loading_wallet(wallet.name));
}
await widget.walletListViewModel.loadWallet(wallet);
hideProgressText();
setState(() {});
} catch (e) {
if (context.mounted) {
changeProcessText(S.of(context).wallet_list_failed_to_load(wallet.name, e.toString()));
}
}
await widget.walletListViewModel.loadWallet(wallet);
hideProgressText();
setState(() {});
} catch (e) {
if (context.mounted) {
changeProcessText(S.of(context).wallet_list_failed_to_load(wallet.name, e.toString()));
}
}
},
conditionToDetermineIfToUse2FA:
widget.walletListViewModel.shouldRequireTOTP2FAForAccessingWallet,
@@ -198,17 +206,16 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD
context,
route: Routes.newWallet,
arguments: widget.walletListViewModel.currentWalletType,
conditionToDetermineIfToUse2FA: widget
.walletListViewModel.shouldRequireTOTP2FAForCreatingNewWallets,
conditionToDetermineIfToUse2FA:
widget.walletListViewModel.shouldRequireTOTP2FAForCreatingNewWallets,
);
} else {
widget._authService.authenticateAction(
context,
route: Routes.newWalletType,
conditionToDetermineIfToUse2FA: widget
.walletListViewModel.shouldRequireTOTP2FAForCreatingNewWallets,
conditionToDetermineIfToUse2FA:
widget.walletListViewModel.shouldRequireTOTP2FAForCreatingNewWallets,
);
}
}
@@ -187,6 +187,11 @@ class AddressPage extends BasePage {
}
reaction((_) => receiveOptionViewModel.selectedReceiveOption, (ReceivePageOption option) {
if (bitcoin!.isBitcoinReceivePageOption(option)) {
addressListViewModel.setAddressType(bitcoin!.getOptionToType(option));
return;
}
switch (option) {
case ReceivePageOption.anonPayInvoice:
Navigator.pushNamed(
+183 -63
View File
@@ -1,15 +1,18 @@
import 'dart:math';
import 'package:auto_size_text/auto_size_text.dart';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/reactions/wallet_connect.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/dashboard/pages/nft_listing_page.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/home_screen_account_widget.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
import 'package:cake_wallet/src/screens/exchange_trade/information_page.dart';
import 'package:cake_wallet/src/widgets/dashboard_card_widget.dart';
import 'package:cake_wallet/src/widgets/introducing_card.dart';
import 'package:cake_wallet/src/widgets/standard_switch.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/themes/extensions/balance_page_theme.dart';
import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart';
@@ -21,6 +24,7 @@ import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:url_launcher/url_launcher.dart';
class BalancePage extends StatelessWidget {
BalancePage({
@@ -221,30 +225,136 @@ class CryptoBalanceWidget extends StatelessWidget {
itemBuilder: (__, index) {
final balance =
dashboardViewModel.balanceViewModel.formattedBalances.elementAt(index);
return BalanceRowWidget(
availableBalanceLabel:
'${dashboardViewModel.balanceViewModel.availableBalanceLabel}',
availableBalance: balance.availableBalance,
availableFiatBalance: balance.fiatAvailableBalance,
additionalBalanceLabel:
'${dashboardViewModel.balanceViewModel.additionalBalanceLabel}',
additionalBalance: balance.additionalBalance,
additionalFiatBalance: balance.fiatAdditionalBalance,
frozenBalance: balance.frozenBalance,
frozenFiatBalance: balance.fiatFrozenBalance,
currency: balance.asset,
hasAdditionalBalance:
dashboardViewModel.balanceViewModel.hasAdditionalBalance,
);
return Observer(builder: (_) {
return BalanceRowWidget(
availableBalanceLabel:
'${dashboardViewModel.balanceViewModel.availableBalanceLabel}',
availableBalance: balance.availableBalance,
availableFiatBalance: balance.fiatAvailableBalance,
additionalBalanceLabel:
'${dashboardViewModel.balanceViewModel.additionalBalanceLabel}',
additionalBalance: balance.additionalBalance,
additionalFiatBalance: balance.fiatAdditionalBalance,
frozenBalance: balance.frozenBalance,
frozenFiatBalance: balance.fiatFrozenBalance,
currency: balance.asset,
hasAdditionalBalance:
dashboardViewModel.balanceViewModel.hasAdditionalBalance,
isTestnet: dashboardViewModel.isTestnet,
);
});
},
);
},
)
),
Observer(builder: (context) {
return Column(
children: [
if (dashboardViewModel.showSilentPaymentsCard) ...[
SizedBox(height: 10),
Padding(
padding: const EdgeInsets.fromLTRB(16, 0, 16, 8),
child: DashBoardRoundedCardWidget(
customBorder: 30,
title: S.of(context).silent_payments,
subTitle: S.of(context).enable_silent_payments_scanning,
hint: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => launchUrl(
Uri.parse(
"https://guides.cakewallet.com/docs/cryptos/bitcoin/#silent-payments"),
mode: LaunchMode.externalApplication,
),
child: Row(
children: [
Text(
S.of(context).what_is_silent_payments,
style: TextStyle(
fontSize: 12,
fontFamily: 'Lato',
fontWeight: FontWeight.w400,
color: Theme.of(context)
.extension<BalancePageTheme>()!
.labelTextColor,
height: 1,
),
softWrap: true,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: Icon(Icons.help_outline,
size: 16,
color: Theme.of(context)
.extension<BalancePageTheme>()!
.labelTextColor),
)
],
),
),
Observer(
builder: (_) => StandardSwitch(
value: dashboardViewModel.silentPaymentsScanningActive,
onTaped: () => _toggleSilentPaymentsScanning(context),
),
)
],
),
],
),
onTap: () => _toggleSilentPaymentsScanning(context),
icon: Icon(
Icons.lock,
color:
Theme.of(context).extension<DashboardPageTheme>()!.pageTitleTextColor,
size: 50,
),
),
),
]
],
);
}),
],
),
),
);
}
Future<void> _toggleSilentPaymentsScanning(BuildContext context) async {
final isSilentPaymentsScanningActive = dashboardViewModel.silentPaymentsScanningActive;
final newValue = !isSilentPaymentsScanningActive;
dashboardViewModel.silentPaymentsScanningActive = newValue;
final needsToSwitch = !isSilentPaymentsScanningActive &&
await bitcoin!.getNodeIsElectrsSPEnabled(dashboardViewModel.wallet) == false;
if (needsToSwitch) {
return showPopUp<void>(
context: context,
builder: (BuildContext context) => AlertWithTwoActions(
alertTitle: S.of(context).change_current_node_title,
alertContent: S.of(context).confirm_silent_payments_switch_node,
rightButtonText: S.of(context).ok,
leftButtonText: S.of(context).cancel,
actionRightButton: () {
dashboardViewModel.setSilentPaymentsScanning(newValue);
Navigator.of(context).pop();
},
actionLeftButton: () {
dashboardViewModel.silentPaymentsScanningActive = isSilentPaymentsScanningActive;
Navigator.of(context).pop();
},
));
}
return dashboardViewModel.setSilentPaymentsScanning(newValue);
}
}
class BalanceRowWidget extends StatelessWidget {
@@ -259,6 +369,7 @@ class BalanceRowWidget extends StatelessWidget {
required this.frozenFiatBalance,
required this.currency,
required this.hasAdditionalBalance,
required this.isTestnet,
super.key,
});
@@ -272,6 +383,7 @@ class BalanceRowWidget extends StatelessWidget {
final String frozenFiatBalance;
final CryptoCurrency currency;
final bool hasAdditionalBalance;
final bool isTestnet;
// void _showBalanceDescription(BuildContext context) {
// showPopUp<void>(
@@ -346,14 +458,24 @@ class BalanceRowWidget extends StatelessWidget {
maxLines: 1,
textAlign: TextAlign.start),
SizedBox(height: 6),
Text('${availableFiatBalance}',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16,
fontFamily: 'Lato',
fontWeight: FontWeight.w500,
color: Theme.of(context).extension<BalancePageTheme>()!.textColor,
height: 1)),
if (isTestnet)
Text(S.current.testnet_coins_no_value,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14,
fontFamily: 'Lato',
fontWeight: FontWeight.w400,
color: Theme.of(context).extension<BalancePageTheme>()!.textColor,
height: 1)),
if (!isTestnet)
Text('${availableFiatBalance}',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16,
fontFamily: 'Lato',
fontWeight: FontWeight.w500,
color: Theme.of(context).extension<BalancePageTheme>()!.textColor,
height: 1)),
],
),
),
@@ -362,27 +484,23 @@ class BalanceRowWidget extends StatelessWidget {
child: Center(
child: Column(
children: [
Container(
clipBehavior: Clip.antiAlias,
decoration: BoxDecoration(shape: BoxShape.circle),
child: CakeImageWidget(
imageUrl: currency.iconPath,
height: 40,
width: 40,
displayOnError: Container(
height: 30.0,
width: 30.0,
child: Center(
child: Text(
currency.title.substring(0, min(currency.title.length, 2)),
style: TextStyle(fontSize: 11),
),
),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.grey.shade400,
CakeImageWidget(
imageUrl: currency.iconPath,
height: 40,
width: 40,
displayOnError: Container(
height: 30.0,
width: 30.0,
child: Center(
child: Text(
currency.title.substring(0, min(currency.title.length, 2)),
style: TextStyle(fontSize: 11),
),
),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.grey.shade400,
),
),
),
const SizedBox(height: 10),
@@ -449,17 +567,18 @@ class BalanceRowWidget extends StatelessWidget {
textAlign: TextAlign.center,
),
SizedBox(height: 4),
Text(
frozenFiatBalance,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
fontFamily: 'Lato',
fontWeight: FontWeight.w400,
color: Theme.of(context).extension<BalancePageTheme>()!.textColor,
height: 1,
if (!isTestnet)
Text(
frozenFiatBalance,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
fontFamily: 'Lato',
fontWeight: FontWeight.w400,
color: Theme.of(context).extension<BalancePageTheme>()!.textColor,
height: 1,
),
),
),
],
),
),
@@ -493,17 +612,18 @@ class BalanceRowWidget extends StatelessWidget {
textAlign: TextAlign.center,
),
SizedBox(height: 4),
Text(
'${additionalFiatBalance}',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
fontFamily: 'Lato',
fontWeight: FontWeight.w400,
color: Theme.of(context).extension<BalancePageTheme>()!.textColor,
height: 1,
if (!isTestnet)
Text(
'${additionalFiatBalance}',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
fontFamily: 'Lato',
fontWeight: FontWeight.w400,
color: Theme.of(context).extension<BalancePageTheme>()!.textColor,
height: 1,
),
),
),
],
),
],
@@ -1,23 +1,20 @@
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/widgets/dashboard_card_widget.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/market_place_view_model.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cake_wallet/view_model/dashboard/cake_features_view_model.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart';
import 'package:flutter_svg/flutter_svg.dart';
class MarketPlacePage extends StatelessWidget {
MarketPlacePage({
class CakeFeaturesPage extends StatelessWidget {
CakeFeaturesPage({
required this.dashboardViewModel,
required this.marketPlaceViewModel,
required this.cakeFeaturesViewModel,
});
final DashboardViewModel dashboardViewModel;
final MarketPlaceViewModel marketPlaceViewModel;
final CakeFeaturesViewModel cakeFeaturesViewModel;
final _scrollController = ScrollController();
@override
@@ -37,7 +34,7 @@ class MarketPlacePage extends StatelessWidget {
children: [
SizedBox(height: 50),
Text(
S.of(context).market_place,
'Cake ${S.of(context).features}',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.w500,
@@ -59,15 +56,21 @@ class MarketPlacePage extends StatelessWidget {
// ),
SizedBox(height: 20),
DashBoardRoundedCardWidget(
onTap: () => _launchUrl("buy.cakepay.com"),
title: S.of(context).cake_pay_web_cards_title,
subTitle: S.of(context).cake_pay_web_cards_subtitle,
onTap: () => _launchMarketPlaceUrl("buy.cakepay.com"),
svgPicture: SvgPicture.asset(
'assets/images/cards.svg',
height: 125,
width: 125,
fit: BoxFit.cover,
),
),
const SizedBox(height: 20),
SizedBox(height: 10),
DashBoardRoundedCardWidget(
title: "NanoGPT",
subTitle: S.of(context).nanogpt_subtitle,
onTap: () => _launchMarketPlaceUrl("cake.nano-gpt.com"),
onTap: () => _launchUrl("cake.nano-gpt.com"),
),
],
),
@@ -79,41 +82,12 @@ class MarketPlacePage extends StatelessWidget {
);
}
void _launchMarketPlaceUrl(String url) async {
void _launchUrl(String url) {
try {
launchUrl(
Uri.https(url),
mode: LaunchMode.externalApplication,
);
} catch (e) {
print(e);
}
}
// TODO: Remove ionia flow/files if we will discard it
void _navigatorToGiftCardsPage(BuildContext context) {
final walletType = dashboardViewModel.type;
switch (walletType) {
case WalletType.haven:
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: S.of(context).error,
alertContent: S.of(context).gift_cards_unavailable,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop());
});
break;
default:
marketPlaceViewModel.isIoniaUserAuthenticated().then((value) {
if (value) {
Navigator.pushNamed(context, Routes.ioniaManageCardsPage);
return;
}
Navigator.of(context).pushNamed(Routes.ioniaWelcomePage);
});
}
} catch (_) {}
}
}
@@ -1,3 +1,4 @@
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/widgets/setting_action_button.dart';
import 'package:cake_wallet/src/widgets/setting_actions.dart';
import 'package:cake_wallet/themes/extensions/menu_theme.dart';
@@ -180,6 +181,11 @@ class MenuWidgetState extends State<MenuWidget> {
final item = SettingActions.all[index];
if (!widget.dashboardViewModel.hasSilentPayments &&
item.name(context) == S.of(context).silent_payments_settings) {
return Container();
}
final isLastTile = index == itemCount - 1;
return SettingActionButton(
@@ -10,8 +10,7 @@ import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/generated/i18n.dart';
class PresentReceiveOptionPicker extends StatelessWidget {
PresentReceiveOptionPicker(
{required this.receiveOptionViewModel, required this.color});
PresentReceiveOptionPicker({required this.receiveOptionViewModel, required this.color});
final ReceiveOptionViewModel receiveOptionViewModel;
final Color color;
@@ -43,17 +42,17 @@ class PresentReceiveOptionPicker extends StatelessWidget {
Text(
S.current.receive,
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.bold,
fontFamily: 'Lato',
color: color),
fontSize: 18.0, fontWeight: FontWeight.bold, fontFamily: 'Lato', color: color),
),
Observer(
builder: (_) => Text(receiveOptionViewModel.selectedReceiveOption.toString(),
style: TextStyle(
fontSize: 10.0,
fontWeight: FontWeight.w500,
color: color)))
builder: (_) => Text(
receiveOptionViewModel.selectedReceiveOption
.toString()
.replaceAll(RegExp(r'silent payments', caseSensitive: false),
S.current.silent_payments)
.replaceAll(
RegExp(r'default', caseSensitive: false), S.current.string_default),
style: TextStyle(fontSize: 10.0, fontWeight: FontWeight.w500, color: color)))
],
),
SizedBox(width: 5),
@@ -73,65 +72,75 @@ class PresentReceiveOptionPicker extends StatelessWidget {
backgroundColor: Colors.transparent,
body: Stack(
alignment: AlignmentDirectional.center,
children:[ AlertBackground(
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Spacer(),
Container(
margin: EdgeInsets.symmetric(horizontal: 24),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30),
color: Theme.of(context).colorScheme.background,
),
child: Padding(
padding: const EdgeInsets.only(top: 24, bottom: 24),
child: (ListView.separated(
padding: EdgeInsets.zero,
shrinkWrap: true,
itemCount: receiveOptionViewModel.options.length,
itemBuilder: (_, index) {
final option = receiveOptionViewModel.options[index];
return InkWell(
onTap: () {
Navigator.pop(popUpContext);
children: [
AlertBackground(
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Spacer(),
Container(
margin: EdgeInsets.symmetric(horizontal: 24),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30),
color: Theme.of(context).colorScheme.background,
),
child: Padding(
padding: const EdgeInsets.only(top: 24, bottom: 24),
child: (ListView.separated(
padding: EdgeInsets.zero,
shrinkWrap: true,
itemCount: receiveOptionViewModel.options.length,
itemBuilder: (_, index) {
final option = receiveOptionViewModel.options[index];
return InkWell(
onTap: () {
Navigator.pop(popUpContext);
receiveOptionViewModel.selectReceiveOption(option);
},
child: Padding(
padding: const EdgeInsets.only(left: 24, right: 24),
child: Observer(builder: (_) {
final value = receiveOptionViewModel.selectedReceiveOption;
receiveOptionViewModel.selectReceiveOption(option);
},
child: Padding(
padding: const EdgeInsets.only(left: 24, right: 24),
child: Observer(builder: (_) {
final value = receiveOptionViewModel.selectedReceiveOption;
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(option.toString(),
textAlign: TextAlign.left,
style: textSmall(
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
).copyWith(
fontWeight:
value == option ? FontWeight.w800 : FontWeight.w500,
)),
RoundedCheckbox(
value: value == option,
)
],
);
}),
),
);
},
separatorBuilder: (_, index) => SizedBox(height: 30),
)),
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
option
.toString()
.replaceAll(
RegExp(r'silent payments', caseSensitive: false),
S.current.silent_payments)
.replaceAll(RegExp(r'default', caseSensitive: false),
S.current.string_default),
textAlign: TextAlign.left,
style: textSmall(
color: Theme.of(context)
.extension<CakeTextTheme>()!
.titleColor,
).copyWith(
fontWeight:
value == option ? FontWeight.w800 : FontWeight.w500,
)),
RoundedCheckbox(
value: value == option,
)
],
);
}),
),
);
},
separatorBuilder: (_, index) => SizedBox(height: 30),
)),
),
),
),
Spacer()
],
Spacer()
],
),
),
),
AlertCloseButton(onTap: () => Navigator.of(popUpContext).pop(), bottom: 40)
],
),
+139 -111
View File
@@ -67,8 +67,7 @@ class ReceivePage extends BasePage {
@override
Widget Function(BuildContext, Widget) get rootWrapper =>
(BuildContext context, Widget scaffold) =>
GradientBackground(scaffold: scaffold);
(BuildContext context, Widget scaffold) => GradientBackground(scaffold: scaffold);
@override
Widget trailing(BuildContext context) {
@@ -99,115 +98,144 @@ class ReceivePage extends BasePage {
@override
Widget body(BuildContext context) {
return KeyboardActions(
config: KeyboardActionsConfig(
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
keyboardBarColor: Theme.of(context).extension<KeyboardTheme>()!.keyboardBarColor,
nextFocus: false,
actions: [
KeyboardActionsItem(
focusNode: _cryptoAmountFocus,
toolbarButtons: [(_) => KeyboardDoneButton()],
)
]),
child: SingleChildScrollView(
child: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.fromLTRB(24, 50, 24, 24),
child: QRWidget(
addressListViewModel: addressListViewModel,
formKey: _formKey,
heroTag: _heroTag,
amountTextFieldFocusNode: _cryptoAmountFocus,
amountController: _amountController,
isLight: currentTheme.type == ThemeType.light),
),
Observer(
builder: (_) => ListView.separated(
padding: EdgeInsets.all(0),
separatorBuilder: (context, _) => const HorizontalSectionDivider(),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: addressListViewModel.items.length,
itemBuilder: (context, index) {
final item = addressListViewModel.items[index];
Widget cell = Container();
if (item is WalletAccountListHeader) {
cell = HeaderTile(
showTrailingButton: true,
walletAddressListViewModel: addressListViewModel,
trailingButtonTap: () async {
if (addressListViewModel.type == WalletType.monero ||
addressListViewModel.type == WalletType.haven) {
await showPopUp<void>(
context: context,
builder: (_) => getIt.get<MoneroAccountListPage>());
} else {
await showPopUp<void>(
context: context,
builder: (_) => getIt.get<NanoAccountListPage>());
}
},
title: S.of(context).accounts,
trailingIcon: Icon(
Icons.arrow_forward_ios,
size: 14,
color: Theme.of(context).extension<ReceivePageTheme>()!.iconsColor,
));
}
if (item is WalletAddressListHeader) {
cell = HeaderTile(
title: S.of(context).addresses,
walletAddressListViewModel: addressListViewModel,
showTrailingButton: !addressListViewModel.isAutoGenerateSubaddressEnabled,
showSearchButton: true,
trailingButtonTap: () =>
Navigator.of(context).pushNamed(Routes.newSubaddress),
trailingIcon: Icon(
Icons.add,
size: 20,
color: Theme.of(context)
.extension<ReceivePageTheme>()!
.iconsColor,
));
}
if (item is WalletAddressListItem) {
cell = Observer(builder: (_) {
final isCurrent =
item.address == addressListViewModel.address.address;
final backgroundColor = isCurrent
? Theme.of(context).extension<ReceivePageTheme>()!.currentTileBackgroundColor
: Theme.of(context).extension<ReceivePageTheme>()!.tilesBackgroundColor;
final textColor = isCurrent
? Theme.of(context).extension<ReceivePageTheme>()!.currentTileTextColor
: Theme.of(context).extension<ReceivePageTheme>()!.tilesTextColor;
return AddressCell.fromItem(item,
isCurrent: isCurrent,
hasBalance: addressListViewModel.isElectrumWallet,
backgroundColor: backgroundColor,
textColor: textColor,
onTap: (_) => addressListViewModel.setAddress(item),
onEdit: () => Navigator.of(context)
.pushNamed(Routes.newSubaddress, arguments: item));
});
}
return index != 0
? cell
: ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(30),
topRight: Radius.circular(30)),
child: cell,
);
})),
],
return KeyboardActions(
config: KeyboardActionsConfig(
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
keyboardBarColor: Theme.of(context).extension<KeyboardTheme>()!.keyboardBarColor,
nextFocus: false,
actions: [
KeyboardActionsItem(
focusNode: _cryptoAmountFocus,
toolbarButtons: [(_) => KeyboardDoneButton()],
)
]),
child: SingleChildScrollView(
child: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.fromLTRB(24, 50, 24, 24),
child: QRWidget(
addressListViewModel: addressListViewModel,
formKey: _formKey,
heroTag: _heroTag,
amountTextFieldFocusNode: _cryptoAmountFocus,
amountController: _amountController,
isLight: currentTheme.type == ThemeType.light),
),
));
Observer(
builder: (_) => ListView.separated(
padding: EdgeInsets.all(0),
separatorBuilder: (context, _) => const HorizontalSectionDivider(),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: addressListViewModel.items.length,
itemBuilder: (context, index) {
final item = addressListViewModel.items[index];
Widget cell = Container();
if (item is WalletAccountListHeader) {
cell = HeaderTile(
showTrailingButton: true,
walletAddressListViewModel: addressListViewModel,
trailingButtonTap: () async {
if (addressListViewModel.type == WalletType.monero ||
addressListViewModel.type == WalletType.haven) {
await showPopUp<void>(
context: context,
builder: (_) => getIt.get<MoneroAccountListPage>());
} else {
await showPopUp<void>(
context: context,
builder: (_) => getIt.get<NanoAccountListPage>());
}
},
title: S.of(context).accounts,
trailingIcon: Icon(
Icons.arrow_forward_ios,
size: 14,
color: Theme.of(context).extension<ReceivePageTheme>()!.iconsColor,
));
}
if (item is WalletAddressListHeader) {
final hasTitle = item.title != null;
cell = HeaderTile(
title: hasTitle ? item.title! : S.of(context).addresses,
walletAddressListViewModel: addressListViewModel,
showTrailingButton:
!addressListViewModel.isAutoGenerateSubaddressEnabled && !hasTitle,
showSearchButton: true,
trailingButtonTap: () =>
Navigator.of(context).pushNamed(Routes.newSubaddress),
trailingIcon: hasTitle
? null
: Icon(
Icons.add,
size: 20,
color:
Theme.of(context).extension<ReceivePageTheme>()!.iconsColor,
),
);
}
if (item is WalletAddressListItem) {
cell = Observer(builder: (_) {
final isCurrent = item.address == addressListViewModel.address.address;
final backgroundColor = isCurrent
? Theme.of(context)
.extension<ReceivePageTheme>()!
.currentTileBackgroundColor
: Theme.of(context)
.extension<ReceivePageTheme>()!
.tilesBackgroundColor;
final textColor = isCurrent
? Theme.of(context)
.extension<ReceivePageTheme>()!
.currentTileTextColor
: Theme.of(context).extension<ReceivePageTheme>()!.tilesTextColor;
return AddressCell.fromItem(
item,
isCurrent: isCurrent,
hasBalance: addressListViewModel.isElectrumWallet,
backgroundColor: backgroundColor,
textColor: textColor,
onTap: item.isOneTimeReceiveAddress == true
? null
: (_) => addressListViewModel.setAddress(item),
onEdit: item.isOneTimeReceiveAddress == true || item.isPrimary
? null
: () => Navigator.of(context)
.pushNamed(Routes.newSubaddress, arguments: item),
onDelete: !addressListViewModel.isSilentPayments || item.isPrimary
? null
: () => addressListViewModel.deleteAddress(item),
);
});
}
return index != 0
? cell
: ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(30), topRight: Radius.circular(30)),
child: cell,
);
})),
Padding(
padding: EdgeInsets.fromLTRB(24, 24, 24, 32),
child: Text(
addressListViewModel.isSilentPayments
? S.of(context).silent_payments_disclaimer
: S.of(context).electrum_address_disclaimer,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 15,
color: Theme.of(context).extension<BalancePageTheme>()!.labelTextColor)),
),
],
),
));
}
}
@@ -15,18 +15,22 @@ class AddressCell extends StatelessWidget {
required this.textColor,
this.onTap,
this.onEdit,
this.onDelete,
this.txCount,
this.balance,
this.isChange = false,
this.hasBalance = false});
factory AddressCell.fromItem(WalletAddressListItem item,
{required bool isCurrent,
required Color backgroundColor,
required Color textColor,
Function(String)? onTap,
bool hasBalance = false,
Function()? onEdit}) =>
factory AddressCell.fromItem(
WalletAddressListItem item, {
required bool isCurrent,
required Color backgroundColor,
required Color textColor,
Function(String)? onTap,
bool hasBalance = false,
Function()? onEdit,
Function()? onDelete,
}) =>
AddressCell(
address: item.address,
name: item.name ?? '',
@@ -36,6 +40,7 @@ class AddressCell extends StatelessWidget {
textColor: textColor,
onTap: onTap,
onEdit: onEdit,
onDelete: onDelete,
txCount: item.txCount,
balance: item.balance,
isChange: item.isChange,
@@ -49,6 +54,7 @@ class AddressCell extends StatelessWidget {
final Color textColor;
final Function(String)? onTap;
final Function()? onEdit;
final Function()? onDelete;
final int? txCount;
final String? balance;
final bool isChange;
@@ -64,7 +70,8 @@ class AddressCell extends StatelessWidget {
} else {
return formatIfCashAddr.substring(0, addressPreviewLength) +
'...' +
formatIfCashAddr.substring(formatIfCashAddr.length - addressPreviewLength, formatIfCashAddr.length);
formatIfCashAddr.substring(
formatIfCashAddr.length - addressPreviewLength, formatIfCashAddr.length);
}
}
@@ -139,7 +146,7 @@ class AddressCell extends StatelessWidget {
mainAxisSize: MainAxisSize.max,
children: [
Text(
'Balance: $balance',
'${S.of(context).balance}: $balance',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
@@ -180,7 +187,7 @@ class AddressCell extends StatelessWidget {
ActionPane _actionPane(BuildContext context) => ActionPane(
motion: const ScrollMotion(),
extentRatio: 0.3,
extentRatio: onDelete != null ? 0.4 : 0.3,
children: [
SlidableAction(
onPressed: (_) => onEdit?.call(),
@@ -189,6 +196,14 @@ class AddressCell extends StatelessWidget {
icon: Icons.edit,
label: S.of(context).edit,
),
if (onDelete != null)
SlidableAction(
onPressed: (_) => onDelete!.call(),
backgroundColor: Colors.red,
foregroundColor: Colors.white,
icon: Icons.delete,
label: S.of(context).delete,
),
],
);
}
+52 -11
View File
@@ -1,3 +1,7 @@
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/main.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/view_model/rescan_view_model.dart';
@@ -11,7 +15,8 @@ class RescanPage extends BasePage {
: _blockchainHeightWidgetKey = GlobalKey<BlockchainHeightState>();
@override
String get title => S.current.rescan;
String get title =>
_rescanViewModel.isSilentPaymentsScan ? S.current.silent_payments_scanning : S.current.rescan;
final GlobalKey<BlockchainHeightState> _blockchainHeightWidgetKey;
final RescanViewModel _rescanViewModel;
@@ -19,20 +24,28 @@ class RescanPage extends BasePage {
Widget body(BuildContext context) {
return Padding(
padding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
child:
Column(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
BlockchainHeightWidget(key: _blockchainHeightWidgetKey,
onHeightOrDateEntered: (value) =>
_rescanViewModel.isButtonEnabled = value),
child: Column(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
Observer(
builder: (_) => BlockchainHeightWidget(
key: _blockchainHeightWidgetKey,
onHeightOrDateEntered: (value) => _rescanViewModel.isButtonEnabled = value,
isSilentPaymentsScan: _rescanViewModel.isSilentPaymentsScan,
doSingleScan: _rescanViewModel.doSingleScan,
toggleSingleScan: () =>
_rescanViewModel.doSingleScan = !_rescanViewModel.doSingleScan,
)),
Observer(
builder: (_) => LoadingPrimaryButton(
isLoading:
_rescanViewModel.state == RescanWalletState.rescaning,
isLoading: _rescanViewModel.state == RescanWalletState.rescaning,
text: S.of(context).rescan,
onPressed: () async {
await _rescanViewModel.rescanCurrentWallet(
restoreHeight:
_blockchainHeightWidgetKey.currentState!.height);
if (_rescanViewModel.isSilentPaymentsScan) {
return _toggleSilentPaymentsScanning(context);
}
_rescanViewModel.rescanCurrentWallet(
restoreHeight: _blockchainHeightWidgetKey.currentState!.height);
Navigator.of(context).pop();
},
color: Theme.of(context).primaryColor,
@@ -42,4 +55,32 @@ class RescanPage extends BasePage {
]),
);
}
Future<void> _toggleSilentPaymentsScanning(BuildContext context) async {
final height = _blockchainHeightWidgetKey.currentState!.height;
Navigator.of(context).pop();
final needsToSwitch =
await bitcoin!.getNodeIsElectrsSPEnabled(_rescanViewModel.wallet) == false;
if (needsToSwitch) {
return showPopUp<void>(
context: navigatorKey.currentState!.context,
builder: (BuildContext _dialogContext) => AlertWithTwoActions(
alertTitle: S.of(_dialogContext).change_current_node_title,
alertContent: S.of(_dialogContext).confirm_silent_payments_switch_node,
rightButtonText: S.of(_dialogContext).ok,
leftButtonText: S.of(_dialogContext).cancel,
actionRightButton: () async {
Navigator.of(_dialogContext).pop();
_rescanViewModel.rescanCurrentWallet(restoreHeight: height);
},
actionLeftButton: () => Navigator.of(_dialogContext).pop(),
));
}
_rescanViewModel.rescanCurrentWallet(restoreHeight: height);
}
}
+5
View File
@@ -1,3 +1,4 @@
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/core/auth_service.dart';
import 'package:cake_wallet/entities/contact_record.dart';
import 'package:cake_wallet/core/execution_state.dart';
@@ -420,6 +421,10 @@ class SendPage extends BasePage {
return;
}
if (sendViewModel.isElectrumWallet) {
bitcoin!.updateFeeRates(sendViewModel.wallet);
}
reaction((_) => sendViewModel.state, (ExecutionState state) {
if (dialogContext != null && dialogContext?.mounted == true) {
Navigator.of(dialogContext!).pop();
@@ -39,7 +39,9 @@ class ConnectionSyncPage extends BasePage {
),
if (dashboardViewModel.hasRescan) ...[
SettingsCellWithArrow(
title: S.current.rescan,
title: dashboardViewModel.hasSilentPayments
? S.current.silent_payments_scanning
: S.current.rescan,
handler: (context) => Navigator.of(context).pushNamed(Routes.rescan),
),
if (DeviceInfo.instance.isMobile && FeatureFlag.isBackgroundSyncEnabled) ...[
@@ -0,0 +1,50 @@
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart';
import 'package:cake_wallet/view_model/settings/silent_payments_settings_view_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
class SilentPaymentsSettingsPage extends BasePage {
SilentPaymentsSettingsPage(this._silentPaymentsSettingsViewModel);
@override
String get title => S.current.silent_payments_settings;
final SilentPaymentsSettingsViewModel _silentPaymentsSettingsViewModel;
@override
Widget body(BuildContext context) {
return SingleChildScrollView(
child: Observer(builder: (_) {
return Container(
padding: EdgeInsets.only(top: 10),
child: Column(
children: [
SettingsSwitcherCell(
title: S.current.silent_payments_display_card,
value: _silentPaymentsSettingsViewModel.silentPaymentsCardDisplay,
onValueChange: (_, bool value) {
_silentPaymentsSettingsViewModel.setSilentPaymentsCardDisplay(value);
},
),
SettingsSwitcherCell(
title: S.current.silent_payments_always_scan,
value: _silentPaymentsSettingsViewModel.silentPaymentsAlwaysScan,
onValueChange: (_, bool value) {
_silentPaymentsSettingsViewModel.setSilentPaymentsAlwaysScan(value);
},
),
SettingsCellWithArrow(
title: S.current.silent_payments_scanning,
handler: (BuildContext context) => Navigator.of(context).pushNamed(Routes.rescan),
),
],
),
);
}),
);
}
}
@@ -57,6 +57,7 @@ class UnspentCoinsListFormState extends State<UnspentCoinsListForm> {
isSending: item.isSending,
isFrozen: item.isFrozen,
isChange: item.isChange,
isSilentPayment: item.isSilentPayment,
onCheckBoxTap: item.isFrozen
? null
: () async {
@@ -12,6 +12,7 @@ class UnspentCoinsListItem extends StatelessWidget {
required this.isSending,
required this.isFrozen,
required this.isChange,
required this.isSilentPayment,
this.onCheckBoxTap,
});
@@ -21,18 +22,16 @@ class UnspentCoinsListItem extends StatelessWidget {
final bool isSending;
final bool isFrozen;
final bool isChange;
final bool isSilentPayment;
final Function()? onCheckBoxTap;
@override
Widget build(BuildContext context) {
final unselectedItemColor = Theme.of(context).cardColor;
final selectedItemColor = Theme.of(context).primaryColor;
final itemColor = isSending
? selectedItemColor
: unselectedItemColor;
final amountColor = isSending
? Colors.white
: Theme.of(context).extension<CakeTextTheme>()!.buttonTextColor;
final itemColor = isSending ? selectedItemColor : unselectedItemColor;
final amountColor =
isSending ? Colors.white : Theme.of(context).extension<CakeTextTheme>()!.buttonTextColor;
final addressColor = isSending
? Colors.white.withOpacity(0.5)
: Theme.of(context).extension<CakeTextTheme>()!.buttonSecondaryTextColor;
@@ -121,6 +120,23 @@ class UnspentCoinsListItem extends StatelessWidget {
),
),
),
if (isSilentPayment)
Container(
height: 17,
padding: EdgeInsets.only(left: 6, right: 6),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(8.5)),
color: Colors.white),
alignment: Alignment.center,
child: Text(
S.of(context).silent_payments,
style: TextStyle(
color: itemColor,
fontSize: 7,
fontWeight: FontWeight.w600,
),
),
),
],
),
),
@@ -96,6 +96,7 @@ class WalletListBody extends StatefulWidget {
class WalletListBodyState extends State<WalletListBody> {
final moneroIcon = Image.asset('assets/images/monero_logo.png', height: 24, width: 24);
final bitcoinIcon = Image.asset('assets/images/bitcoin.png', height: 24, width: 24);
final tBitcoinIcon = Image.asset('assets/images/tbtc.png', height: 24, width: 24);
final litecoinIcon = Image.asset('assets/images/litecoin_icon.png', height: 24, width: 24);
final nonWalletTypeIcon = Image.asset('assets/images/close.png', height: 24, width: 24);
final havenIcon = Image.asset('assets/images/haven_logo.png', height: 24, width: 24);
@@ -162,7 +163,10 @@ class WalletListBodyState extends State<WalletListBody> {
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
wallet.isEnabled
? _imageFor(type: wallet.type)
? _imageFor(
type: wallet.type,
isTestnet: wallet.isTestnet,
)
: nonWalletTypeIcon,
SizedBox(width: 10),
Flexible(
@@ -297,9 +301,12 @@ class WalletListBodyState extends State<WalletListBody> {
);
}
Image _imageFor({required WalletType type}) {
Image _imageFor({required WalletType type, bool? isTestnet}) {
switch (type) {
case WalletType.bitcoin:
if (isTestnet == true) {
return tBitcoinIcon;
}
return bitcoinIcon;
case WalletType.monero:
return moneroIcon;
+52 -14
View File
@@ -1,3 +1,5 @@
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/src/widgets/standard_switch.dart';
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/utils/date_picker.dart';
import 'package:flutter/material.dart';
@@ -12,13 +14,19 @@ class BlockchainHeightWidget extends StatefulWidget {
this.onHeightChange,
this.focusNode,
this.onHeightOrDateEntered,
this.hasDatePicker = true})
: super(key: key);
this.hasDatePicker = true,
this.isSilentPaymentsScan = false,
this.toggleSingleScan,
this.doSingleScan = false,
}) : super(key: key);
final Function(int)? onHeightChange;
final Function(bool)? onHeightOrDateEntered;
final FocusNode? focusNode;
final bool hasDatePicker;
final bool isSilentPaymentsScan;
final bool doSingleScan;
final Function()? toggleSingleScan;
@override
State<StatefulWidget> createState() => BlockchainHeightState();
@@ -64,9 +72,10 @@ class BlockchainHeightState extends State<BlockchainHeightWidget> {
child: BaseTextFormField(
focusNode: widget.focusNode,
controller: restoreHeightController,
keyboardType: TextInputType.numberWithOptions(
signed: false, decimal: false),
hintText: S.of(context).widgets_restore_from_blockheight,
keyboardType: TextInputType.numberWithOptions(signed: false, decimal: false),
hintText: widget.isSilentPaymentsScan
? S.of(context).silent_payments_scan_from_height
: S.of(context).widgets_restore_from_blockheight,
)))
],
),
@@ -78,8 +87,7 @@ class BlockchainHeightState extends State<BlockchainHeightWidget> {
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.w500,
color:
Theme.of(context).extension<CakeTextTheme>()!.titleColor),
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor),
),
),
Row(
@@ -91,22 +99,47 @@ class BlockchainHeightState extends State<BlockchainHeightWidget> {
child: IgnorePointer(
child: BaseTextFormField(
controller: dateController,
hintText: S.of(context).widgets_restore_from_date,
hintText: widget.isSilentPaymentsScan
? S.of(context).silent_payments_scan_from_date
: S.of(context).widgets_restore_from_date,
)),
),
))
],
),
if (widget.isSilentPaymentsScan)
Padding(
padding: EdgeInsets.only(top: 24),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
S.of(context).scan_one_block,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.normal,
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
),
),
Padding(
padding: const EdgeInsets.only(right: 8),
child: StandardSwitch(
value: widget.doSingleScan,
onTaped: () => widget.toggleSingleScan?.call(),
),
)
],
),
),
Padding(
padding: EdgeInsets.only(left: 40, right: 40, top: 24),
child: Text(
S.of(context).restore_from_date_or_blockheight,
widget.isSilentPaymentsScan
? S.of(context).silent_payments_scan_from_date_or_blockheight
: S.of(context).restore_from_date_or_blockheight,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.normal,
color: Theme.of(context).hintColor
),
fontSize: 12, fontWeight: FontWeight.normal, color: Theme.of(context).hintColor),
),
)
]
@@ -123,7 +156,12 @@ class BlockchainHeightState extends State<BlockchainHeightWidget> {
lastDate: now);
if (date != null) {
final height = monero!.getHeightByDate(date: date);
int height;
if (widget.isSilentPaymentsScan) {
height = bitcoin!.getHeightByDate(date: date);
} else {
height = monero!.getHeightByDate(date: date);
}
setState(() {
dateController.text = DateFormat('yyyy-MM-dd').format(date);
restoreHeightController.text = '$height';
+49 -18
View File
@@ -2,19 +2,28 @@ import 'package:cake_wallet/themes/extensions/balance_page_theme.dart';
import 'package:cake_wallet/themes/extensions/sync_indicator_theme.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart';
import 'package:flutter_svg/flutter_svg.dart';
class DashBoardRoundedCardWidget extends StatelessWidget {
DashBoardRoundedCardWidget({
required this.onTap,
required this.title,
required this.subTitle,
this.hint,
this.svgPicture,
this.icon,
this.onClose,
this.customBorder,
});
final VoidCallback onTap;
final VoidCallback? onClose;
final String title;
final String subTitle;
final Widget? hint;
final SvgPicture? svgPicture;
final Icon? icon;
final double? customBorder;
@override
Widget build(BuildContext context) {
@@ -26,34 +35,56 @@ class DashBoardRoundedCardWidget extends StatelessWidget {
child: Stack(
children: [
Container(
padding: EdgeInsets.fromLTRB(20, 20, 40, 20),
padding: EdgeInsets.all(20),
width: double.infinity,
decoration: BoxDecoration(
color: Theme.of(context).extension<SyncIndicatorTheme>()!.syncedBackgroundColor,
borderRadius: BorderRadius.circular(20),
borderRadius: BorderRadius.circular(customBorder ?? 20),
border: Border.all(
color: Theme.of(context).extension<BalancePageTheme>()!.cardBorderColor,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
color: Theme.of(context).extension<DashboardPageTheme>()!.cardTextColor,
fontSize: 24,
fontWeight: FontWeight.w900,
),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
color:
Theme.of(context).extension<DashboardPageTheme>()!.cardTextColor,
fontSize: 24,
fontWeight: FontWeight.w900,
),
softWrap: true,
),
SizedBox(height: 5),
Text(
subTitle,
style: TextStyle(
color: Theme.of(context)
.extension<DashboardPageTheme>()!
.cardTextColor,
fontWeight: FontWeight.w500,
fontFamily: 'Lato'),
softWrap: true,
),
],
),
),
if (svgPicture != null) svgPicture!,
if (icon != null) icon!
],
),
SizedBox(height: 5),
Text(
subTitle,
style: TextStyle(
color: Theme.of(context).extension<DashboardPageTheme>()!.cardTextColor,
fontWeight: FontWeight.w500,
fontFamily: 'Lato'),
)
if (hint != null) ...[
SizedBox(height: 10),
hint!,
]
],
),
),
+10
View File
@@ -17,6 +17,7 @@ class SettingActions {
connectionSettingAction,
walletSettingAction,
addressBookSettingAction,
silentPaymentsSettingAction,
securityBackupSettingAction,
privacySettingAction,
displaySettingAction,
@@ -35,6 +36,15 @@ class SettingActions {
supportSettingAction,
];
static SettingActions silentPaymentsSettingAction = SettingActions._(
name: (context) => S.of(context).silent_payments_settings,
image: 'assets/images/bitcoin_menu.png',
onTap: (BuildContext context) {
Navigator.pop(context);
Navigator.of(context).pushNamed(Routes.silentPaymentsSettings);
},
);
static SettingActions connectionSettingAction = SettingActions._(
name: (context) => S.of(context).connection_sync,
image: 'assets/images/nodes_menu.png',