mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-18 05:51:34 +02:00
implement-payjoin (#1949)
* Initial Payjoin * Initial Payjoin * More payjoin stuff * Minor fixes * Minor fixes * Minor cleanup * Minor cleanup * Minor cleanup * Minor cleanup * Minor cleanup * Minor cleanup * Fix minor bug causes by data inconsistency in the btc utxos * Minor cleanup * Minor cleanup * Minor cleanup * Minor cleanup * Initial Payjoin * Initial Payjoin * More payjoin stuff * Minor fixes * Minor fixes * Minor cleanup * Minor cleanup * Minor cleanup * Minor cleanup * Minor cleanup * Minor cleanup * Fix minor bug causes by data inconsistency in the btc utxos * Minor cleanup * Minor cleanup * Minor cleanup * Minor cleanup * Fix Rebase issues * Move PJ Receiver to isolate * Add Payjoin Setting * Payjoin Sender are now isolated * Added Payjoin sessions to tx overview. Fix Fee issue with payjoin * Clean up code * Fix taproot for payjoin * Fix CI Errors * Add Payjoin UI elements and details page * Add Payjoin UI elements and details page * Fix Translations * feat: Detect Payjoin URIs in pasted text and show to the User sending Payjoin * feat: rename pjUri to payjoinURI for more code clarity * Update res/values/strings_pl.arb Co-authored-by: cyan <cyjan@mrcyjanek.net> * Update cw_bitcoin/lib/payjoin/manager.dart Co-authored-by: cyan <cyjan@mrcyjanek.net> * Update cw_bitcoin/lib/payjoin/manager.dart Co-authored-by: cyan <cyjan@mrcyjanek.net> * feat: Disable Payjoin per default * feat: Disable Payjoin fully if disabled or no Inputs available * feat: Resume Payjoin if app comes back to foreground * chore: Revert overly aggressive code formats * feat: show correct Payjoin amount for receivers * feat: Improved payjoin status * feat: Show payjoin errors on payjoin details screen * deps: update flutter to 3.27.4 * feat: Revert localisations * bug: Remove duplicate transaction id on payjoin details * style: remove double await in payjoin sender * refactor(cw_bitcoin): Refactor method signatures and convert constructor to factory * refactor(cw_bitcoin): Refactor wallet service and PSBT signer for cleaner code Removed unnecessary `CakeHive` dependency and refactored `BitcoinWallet` initialization to use `payjoinSessionSource`. Improved code readability in `PsbtSigner` by reformatting lines and simplifying constructor methods for `UtxoWithPrivateKey`. * fix: Resume Payjoin Sessions and load PJUri after sleep * feat: Add "Copy Payjoin URL button" to receive screen * fix: Add "Payjoin enabled"-Box below QR Code on the receive screen * fix: Set payjoin_enabled color to black independent of the theme * refactor: Payjoin session management and cleanup unused code. --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> Co-authored-by: cyan <cyjan@mrcyjanek.net>
This commit is contained in:
co-authored by
cyan
Omar Hatem
parent
4a08e18f00
commit
82e3ebf4fa
@@ -1,12 +1,14 @@
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/widgets/anonpay_transaction_row.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/widgets/order_row.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/widgets/payjoin_transaction_row.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/widgets/trade_row.dart';
|
||||
import 'package:cake_wallet/themes/extensions/placeholder_theme.dart';
|
||||
import 'package:cake_wallet/src/widgets/dashboard_card_widget.dart';
|
||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/anonpay_transaction_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/order_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/payjoin_transaction_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/trade_list_item.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/sync_status.dart';
|
||||
@@ -143,6 +145,25 @@ class TransactionsPage extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
if (item is PayjoinTransactionListItem) {
|
||||
final session = item.session;
|
||||
|
||||
return PayjoinTransactionRow(
|
||||
key: item.key,
|
||||
onTap: () => Navigator.of(context).pushNamed(
|
||||
Routes.payjoinDetails,
|
||||
arguments: [item.sessionId, item.transaction],
|
||||
),
|
||||
currency: "BTC",
|
||||
state: item.status,
|
||||
amount: bitcoin!.formatterBitcoinAmountToString(
|
||||
amount: session.amount.toInt()),
|
||||
createdAt: DateFormat('HH:mm')
|
||||
.format(session.inProgressSince!),
|
||||
isSending: session.isSenderSession,
|
||||
);
|
||||
}
|
||||
|
||||
if (item is TradeListItem) {
|
||||
final trade = item.trade;
|
||||
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
||||
import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class PayjoinTransactionRow extends StatelessWidget {
|
||||
PayjoinTransactionRow({
|
||||
required this.createdAt,
|
||||
required this.currency,
|
||||
required this.onTap,
|
||||
required this.amount,
|
||||
required this.state,
|
||||
required this.isSending,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final VoidCallback? onTap;
|
||||
final String createdAt;
|
||||
final String amount;
|
||||
final String currency;
|
||||
final String state;
|
||||
final bool isSending;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
padding: EdgeInsets.fromLTRB(24, 8, 24, 8),
|
||||
color: Colors.transparent,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
_getImage(),
|
||||
SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"${isSending ? S.current.outgoing : S.current.incoming} Payjoin",
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context)
|
||||
.extension<DashboardPageTheme>()!
|
||||
.textColor,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
amount + ' ' + currency,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context)
|
||||
.extension<DashboardPageTheme>()!
|
||||
.textColor,
|
||||
),
|
||||
)
|
||||
]),
|
||||
SizedBox(height: 5),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
createdAt,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Theme.of(context)
|
||||
.extension<CakeTextTheme>()!
|
||||
.dateSectionRowColor,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
state,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Theme.of(context)
|
||||
.extension<CakeTextTheme>()!
|
||||
.dateSectionRowColor,
|
||||
),
|
||||
),
|
||||
])
|
||||
],
|
||||
))
|
||||
],
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
Widget _getImage() => ClipRRect(
|
||||
borderRadius: BorderRadius.circular(50),
|
||||
child: Image.asset('assets/images/payjoin.png', width: 36, height: 36));
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/screens/trade_details/trade_details_list_card.dart';
|
||||
import 'package:cake_wallet/src/screens/trade_details/trade_details_status_item.dart';
|
||||
import 'package:cake_wallet/src/widgets/list_row.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_list.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_list_card.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_list_status_row.dart';
|
||||
import 'package:cake_wallet/utils/show_bar.dart';
|
||||
import 'package:cake_wallet/view_model/payjoin_details_view_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class PayjoinDetailsPage extends BasePage {
|
||||
PayjoinDetailsPage({required this.payjoinDetailsViewModel});
|
||||
|
||||
@override
|
||||
String get title => S.current.payjoin_details;
|
||||
|
||||
final PayjoinDetailsViewModel payjoinDetailsViewModel;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) => PayjoinDetailsPageBody(payjoinDetailsViewModel);
|
||||
}
|
||||
|
||||
class PayjoinDetailsPageBody extends StatefulWidget {
|
||||
PayjoinDetailsPageBody(this.payjoinDetailsViewModel);
|
||||
|
||||
final PayjoinDetailsViewModel payjoinDetailsViewModel;
|
||||
|
||||
@override
|
||||
State<PayjoinDetailsPageBody> createState() => _PayjoinDetailsPageBodyState();
|
||||
}
|
||||
|
||||
class _PayjoinDetailsPageBodyState extends State<PayjoinDetailsPageBody> {
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
widget.payjoinDetailsViewModel.listener.cancel();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SectionStandardList(
|
||||
sectionCount: 1,
|
||||
itemCounter: (int _) => widget.payjoinDetailsViewModel.items.length,
|
||||
itemBuilder: (__, index) {
|
||||
final item = widget.payjoinDetailsViewModel.items[index];
|
||||
|
||||
if (item is DetailsListStatusItem) {
|
||||
return StandardListStatusRow(
|
||||
title: item.title,
|
||||
value: item.value,
|
||||
status: item.status,
|
||||
);
|
||||
}
|
||||
|
||||
if (item is TradeDetailsListCardItem) {
|
||||
return TradeDetailsStandardListCard(
|
||||
id: item.id,
|
||||
create: item.createdAt,
|
||||
pair: item.pair,
|
||||
currentTheme: widget.payjoinDetailsViewModel.settingsStore.currentTheme.type,
|
||||
onTap: item.onTap,
|
||||
);
|
||||
}
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Clipboard.setData(ClipboardData(text: item.value));
|
||||
showBar<void>(context, S.of(context).transaction_details_copied(item.title));
|
||||
},
|
||||
child: ListRow(title: '${item.title}:', value: item.value),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
import 'package:cake_wallet/entities/qr_view_data.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
||||
import 'package:cake_wallet/themes/extensions/picker_theme.dart';
|
||||
import 'package:cake_wallet/themes/extensions/qr_code_theme.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
@@ -91,26 +93,54 @@ class QRWidget extends StatelessWidget {
|
||||
child: Hero(
|
||||
tag: Key(heroTag ?? addressUri.toString()),
|
||||
child: Center(
|
||||
child: AspectRatio(
|
||||
aspectRatio: 1.0,
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(5),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
width: 3,
|
||||
color: Theme.of(context)
|
||||
.extension<DashboardPageTheme>()!
|
||||
.textColor,
|
||||
),
|
||||
),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
width: 3,
|
||||
color: Colors.white,
|
||||
child: Container(
|
||||
padding: EdgeInsets.zero,
|
||||
decoration: BoxDecoration(
|
||||
border: Border(top: BorderSide.none),
|
||||
borderRadius:
|
||||
BorderRadius.all(Radius.circular(5)),
|
||||
color: Colors.white,
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
padding: EdgeInsets.all(3),
|
||||
child: AspectRatio(
|
||||
aspectRatio: 1.0,
|
||||
child: QrImage(
|
||||
data: addressUri.toString(),
|
||||
),
|
||||
),
|
||||
child: QrImage(data: addressUri.toString())),
|
||||
),
|
||||
if (addressListViewModel
|
||||
.payjoinEndpoint.isNotEmpty) ...[
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.center,
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
top: 4,
|
||||
bottom: 4,
|
||||
right: 4,
|
||||
),
|
||||
child: Image.asset(
|
||||
'assets/images/payjoin.png',
|
||||
width: 20,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
S.of(context).payjoin_enabled,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -179,7 +209,25 @@ class QRWidget extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
),
|
||||
if (addressListViewModel.payjoinEndpoint.isNotEmpty) ...[
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 12),
|
||||
child: PrimaryImageButton(
|
||||
onPressed: () {
|
||||
Clipboard.setData(ClipboardData(
|
||||
text: addressListViewModel.payjoinEndpoint));
|
||||
showBar<void>(context, S.of(context).copied_to_clipboard);
|
||||
},
|
||||
image: Image.asset('assets/images/payjoin.png', width: 25,),
|
||||
text: S.of(context).copy_payjoin_url,
|
||||
color: Theme.of(context).cardColor,
|
||||
textColor: Theme.of(context)
|
||||
.extension<CakeTextTheme>()!
|
||||
.buttonTextColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/core/auth_service.dart';
|
||||
import 'package:cake_wallet/core/totp_request_details.dart';
|
||||
import 'package:cake_wallet/utils/device_info.dart';
|
||||
@@ -136,6 +136,10 @@ class RootState extends State<Root> with WidgetsBindingObserver {
|
||||
setState(() => _setInactive(true));
|
||||
}
|
||||
|
||||
if (widget.appStore.wallet?.type == WalletType.bitcoin) {
|
||||
bitcoin!.stopPayjoinSessions(widget.appStore.wallet!);
|
||||
}
|
||||
|
||||
break;
|
||||
case AppLifecycleState.resumed:
|
||||
widget.authService.requireAuth().then((value) {
|
||||
@@ -145,6 +149,10 @@ class RootState extends State<Root> with WidgetsBindingObserver {
|
||||
});
|
||||
}
|
||||
});
|
||||
if (widget.appStore.wallet?.type == WalletType.bitcoin &&
|
||||
widget.appStore.settingsStore.usePayjoin) {
|
||||
bitcoin!.resumePayjoinSessions(widget.appStore.wallet!);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -463,7 +463,7 @@ class SendPage extends BasePage {
|
||||
},
|
||||
);
|
||||
},
|
||||
text: S.of(context).send,
|
||||
text: sendViewModel.payjoinUri != null ? S.of(context).send_payjoin : S.of(context).send,
|
||||
color: Theme.of(context).primaryColor,
|
||||
textColor: Colors.white,
|
||||
isLoading: sendViewModel.state is IsExecutingState ||
|
||||
|
||||
@@ -188,19 +188,23 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
||||
sendViewModel.createOpenCryptoPayTransaction(uri.toString());
|
||||
} else {
|
||||
final paymentRequest = PaymentRequest.fromUri(uri);
|
||||
addressController.text = paymentRequest.address;
|
||||
cryptoAmountController.text = paymentRequest.amount;
|
||||
noteController.text = paymentRequest.note;
|
||||
}
|
||||
},
|
||||
options: [
|
||||
AddressTextFieldOption.paste,
|
||||
AddressTextFieldOption.qrCode,
|
||||
AddressTextFieldOption.addressBook
|
||||
],
|
||||
buttonColor: Theme.of(context).extension<SendPageTheme>()!.textFieldButtonColor,
|
||||
borderColor: Theme.of(context).extension<SendPageTheme>()!.textFieldBorderColor,
|
||||
textStyle:
|
||||
if (sendViewModel.usePayjoin) {
|
||||
sendViewModel.payjoinUri = paymentRequest.pjUri;
|
||||
}
|
||||
addressController.text = paymentRequest.address;
|
||||
cryptoAmountController.text = paymentRequest.amount;
|
||||
noteController.text = paymentRequest.note;}
|
||||
},
|
||||
options: [
|
||||
AddressTextFieldOption.paste,
|
||||
AddressTextFieldOption.qrCode,
|
||||
AddressTextFieldOption.addressBook
|
||||
],
|
||||
buttonColor:
|
||||
Theme.of(context).extension<SendPageTheme>()!.textFieldButtonColor,
|
||||
borderColor:
|
||||
Theme.of(context).extension<SendPageTheme>()!.textFieldBorderColor,
|
||||
textStyle:
|
||||
TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: Colors.white),
|
||||
hintStyle: TextStyle(
|
||||
fontSize: 14,
|
||||
|
||||
@@ -49,6 +49,14 @@ class PrivacyPage extends BasePage {
|
||||
_privacySettingsViewModel.setExchangeApiMode(mode),
|
||||
),
|
||||
),
|
||||
if (_privacySettingsViewModel.canUsePayjoin)
|
||||
SettingsSwitcherCell(
|
||||
title: S.of(context).use_payjoin,
|
||||
value: _privacySettingsViewModel.usePayjoin,
|
||||
onValueChange: (BuildContext _, bool value) {
|
||||
_privacySettingsViewModel.setUsePayjoin(value);
|
||||
},
|
||||
),
|
||||
SettingsSwitcherCell(
|
||||
title: S.current.settings_save_recipient_address,
|
||||
value: _privacySettingsViewModel.shouldSaveRecipientAddress,
|
||||
|
||||
@@ -2,6 +2,8 @@ import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.d
|
||||
|
||||
class DetailsListStatusItem extends StandartListItem {
|
||||
DetailsListStatusItem(
|
||||
{required String title, required String value})
|
||||
{required String title, required String value, this.status})
|
||||
: super(title: title, value: value);
|
||||
|
||||
final String? status; // waiting, action required, created, fetching, finished, success
|
||||
}
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
import 'package:cake_wallet/utils/device_info.dart';
|
||||
import 'package:cake_wallet/entities/contact_base.dart';
|
||||
import 'package:cake_wallet/entities/qr_scanner.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
||||
import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
|
||||
import 'package:cake_wallet/utils/device_info.dart';
|
||||
import 'package:cake_wallet/utils/permission_handler.dart';
|
||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||
import 'package:cw_core/currency.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/entities/qr_scanner.dart';
|
||||
import 'package:cake_wallet/entities/contact_base.dart';
|
||||
import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
|
||||
import 'package:cake_wallet/utils/permission_handler.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
|
||||
enum AddressTextFieldOption { paste, qrCode, addressBook, walletAddresses }
|
||||
|
||||
|
||||
class AddressTextField<T extends Currency> extends StatelessWidget{
|
||||
class AddressTextField<T extends Currency> extends StatelessWidget {
|
||||
AddressTextField({
|
||||
required this.controller,
|
||||
this.isActive = true,
|
||||
@@ -234,9 +233,7 @@ class AddressTextField<T extends Currency> extends StatelessWidget{
|
||||
if (!isCameraPermissionGranted) return;
|
||||
final code = await presentQRScanner(context);
|
||||
if (code == null) return;
|
||||
if (code.isEmpty) {
|
||||
return;
|
||||
}
|
||||
if (code.isEmpty) return;
|
||||
|
||||
try {
|
||||
final uri = Uri.parse(code);
|
||||
@@ -259,7 +256,8 @@ class AddressTextField<T extends Currency> extends StatelessWidget{
|
||||
}
|
||||
|
||||
Future<void> _presetWalletAddressPicker(BuildContext context) async {
|
||||
final address = await Navigator.of(context).pushNamed(Routes.pickerWalletAddress);
|
||||
final address =
|
||||
await Navigator.of(context).pushNamed(Routes.pickerWalletAddress);
|
||||
|
||||
if (address is String) {
|
||||
controller?.text = address;
|
||||
@@ -272,7 +270,13 @@ class AddressTextField<T extends Currency> extends StatelessWidget{
|
||||
final address = clipboard?.text ?? '';
|
||||
|
||||
if (address.isNotEmpty) {
|
||||
controller?.text = address;
|
||||
try {
|
||||
final uri = Uri.parse(address);
|
||||
controller?.text = uri.path;
|
||||
onURIScanned?.call(uri);
|
||||
} catch (_) {
|
||||
controller?.text = address;
|
||||
}
|
||||
}
|
||||
|
||||
onPushPasteButton?.call(context);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator_icon.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -7,10 +6,11 @@ import 'package:cake_wallet/themes/extensions/address_theme.dart';
|
||||
import 'package:cake_wallet/themes/extensions/transaction_trade_theme.dart';
|
||||
|
||||
class StandardListStatusRow extends StatelessWidget {
|
||||
StandardListStatusRow({required this.title, required this.value});
|
||||
StandardListStatusRow({required this.title, required this.value, this.status});
|
||||
|
||||
final String title;
|
||||
final String value;
|
||||
final String? status; // waiting, action required, created, fetching, finished, success
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -43,7 +43,7 @@ class StandardListStatusRow extends StatelessWidget {
|
||||
children: <Widget>[
|
||||
SyncIndicatorIcon(
|
||||
boolMode: false,
|
||||
value: value,
|
||||
value: status ?? value,
|
||||
size: 6,
|
||||
),
|
||||
SizedBox(
|
||||
|
||||
Reference in New Issue
Block a user