mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-06-28 12:29:51 +00:00
Merge remote-tracking branch 'origin/main'
This commit is contained in:
commit
9b1ca4ea2e
19 changed files with 353 additions and 142 deletions
|
@ -74,9 +74,6 @@ android {
|
||||||
release {
|
release {
|
||||||
signingConfig signingConfigs.release
|
signingConfig signingConfigs.release
|
||||||
|
|
||||||
shrinkResources false
|
|
||||||
minifyEnabled false
|
|
||||||
|
|
||||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||||
}
|
}
|
||||||
debug {
|
debug {
|
||||||
|
|
3
android/app/proguard-rules.pro
vendored
3
android/app/proguard-rules.pro
vendored
|
@ -5,4 +5,5 @@
|
||||||
-keep class io.flutter.view.** { *; }
|
-keep class io.flutter.view.** { *; }
|
||||||
-keep class io.flutter.** { *; }
|
-keep class io.flutter.** { *; }
|
||||||
-keep class io.flutter.plugins.** { *; }
|
-keep class io.flutter.plugins.** { *; }
|
||||||
-dontwarn io.flutter.embedding.**
|
-dontwarn io.flutter.embedding.**
|
||||||
|
-dontwarn com.google.android.play.core.splitcompat.SplitCompatApplication
|
|
@ -1,4 +1,7 @@
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:cw_monero/api/wallet.dart';
|
import 'package:cw_monero/api/wallet.dart';
|
||||||
|
import 'package:cw_monero/monero_account_list.dart';
|
||||||
import 'package:monero/monero.dart' as monero;
|
import 'package:monero/monero.dart' as monero;
|
||||||
|
|
||||||
monero.wallet? wptr = null;
|
monero.wallet? wptr = null;
|
||||||
|
@ -15,7 +18,6 @@ monero.WalletListener? getWlptr() {
|
||||||
return _wlptr!;
|
return _wlptr!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
monero.SubaddressAccount? subaddressAccount;
|
monero.SubaddressAccount? subaddressAccount;
|
||||||
|
|
||||||
bool isUpdating = false;
|
bool isUpdating = false;
|
||||||
|
@ -51,8 +53,9 @@ void addAccountSync({required String label}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void setLabelForAccountSync({required int accountIndex, required String label}) {
|
void setLabelForAccountSync({required int accountIndex, required String label}) {
|
||||||
// TODO(mrcyjanek): this may be wrong function?
|
monero.SubaddressAccount_setLabel(subaddressAccount!, accountIndex: accountIndex, label: label);
|
||||||
monero.Wallet_setSubaddressLabel(wptr!, accountIndex: accountIndex, addressIndex: 0, label: label);
|
MoneroAccountListBase.cachedAccounts[wptr!.address] = [];
|
||||||
|
refreshAccounts();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _addAccount(String label) => addAccountSync(label: label);
|
void _addAccount(String label) => addAccountSync(label: label);
|
||||||
|
@ -66,10 +69,10 @@ void _setLabelForAccount(Map<String, dynamic> args) {
|
||||||
|
|
||||||
Future<void> addAccount({required String label}) async {
|
Future<void> addAccount({required String label}) async {
|
||||||
_addAccount(label);
|
_addAccount(label);
|
||||||
await store();
|
unawaited(store());
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> setLabelForAccount({required int accountIndex, required String label}) async {
|
Future<void> setLabelForAccount({required int accountIndex, required String label}) async {
|
||||||
_setLabelForAccount({'accountIndex': accountIndex, 'label': label});
|
_setLabelForAccount({'accountIndex': accountIndex, 'label': label});
|
||||||
await store();
|
unawaited(store());
|
||||||
}
|
}
|
|
@ -45,18 +45,18 @@ abstract class MoneroAccountListBase with Store {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<int, List<Account>> _cachedAccounts = {};
|
static Map<int, List<Account>> cachedAccounts = {};
|
||||||
|
|
||||||
List<Account> getAll() {
|
List<Account> getAll() {
|
||||||
final allAccounts = account_list.getAllAccount();
|
final allAccounts = account_list.getAllAccount();
|
||||||
final currentCount = allAccounts.length;
|
final currentCount = allAccounts.length;
|
||||||
_cachedAccounts[account_list.wptr!.address] ??= [];
|
cachedAccounts[account_list.wptr!.address] ??= [];
|
||||||
|
|
||||||
if (_cachedAccounts[account_list.wptr!.address]!.length == currentCount) {
|
if (cachedAccounts[account_list.wptr!.address]!.length == currentCount) {
|
||||||
return _cachedAccounts[account_list.wptr!.address]!;
|
return cachedAccounts[account_list.wptr!.address]!;
|
||||||
}
|
}
|
||||||
|
|
||||||
_cachedAccounts[account_list.wptr!.address] = allAccounts.map((accountRow) {
|
cachedAccounts[account_list.wptr!.address] = allAccounts.map((accountRow) {
|
||||||
final balance = monero.SubaddressAccountRow_getUnlockedBalance(accountRow);
|
final balance = monero.SubaddressAccountRow_getUnlockedBalance(accountRow);
|
||||||
|
|
||||||
return Account(
|
return Account(
|
||||||
|
@ -66,7 +66,7 @@ abstract class MoneroAccountListBase with Store {
|
||||||
);
|
);
|
||||||
}).toList();
|
}).toList();
|
||||||
|
|
||||||
return _cachedAccounts[account_list.wptr!.address]!;
|
return cachedAccounts[account_list.wptr!.address]!;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> addAccount({required String label}) async {
|
Future<void> addAccount({required String label}) async {
|
||||||
|
|
|
@ -48,8 +48,7 @@ void addAccountSync({required String label}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void setLabelForAccountSync({required int accountIndex, required String label}) {
|
void setLabelForAccountSync({required int accountIndex, required String label}) {
|
||||||
// TODO(mrcyjanek): this may be wrong function?
|
wownero.SubaddressAccount_setLabel(subaddressAccount!, accountIndex: accountIndex, label: label);
|
||||||
wownero.Wallet_setSubaddressLabel(wptr!, accountIndex: accountIndex, addressIndex: 0, label: label);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _addAccount(String label) => addAccountSync(label: label);
|
void _addAccount(String label) => addAccountSync(label: label);
|
||||||
|
|
|
@ -35,9 +35,16 @@ class RobinhoodBuyProvider extends BuyProvider {
|
||||||
|
|
||||||
static const _baseUrl = 'applink.robinhood.com';
|
static const _baseUrl = 'applink.robinhood.com';
|
||||||
static const _cIdBaseUrl = 'exchange-helper.cakewallet.com';
|
static const _cIdBaseUrl = 'exchange-helper.cakewallet.com';
|
||||||
|
static const _apiBaseUrl = 'api.robinhood.com';
|
||||||
|
static const _assetsPath = '/catpay/v1/supported_currencies/';
|
||||||
|
|
||||||
static const List<CryptoCurrency> _notSupportedCrypto = [];
|
static List<CryptoCurrency> _supportedCrypto = [];
|
||||||
static const List<FiatCurrency> _notSupportedFiat = [];
|
static final List<FiatCurrency> _supportedFiat = [FiatCurrency.usd];
|
||||||
|
|
||||||
|
static final List<CryptoCurrency> _notSupportedCrypto = [];
|
||||||
|
|
||||||
|
static final List<FiatCurrency> _notSupportedFiat =
|
||||||
|
FiatCurrency.all.where((fiat) => !_supportedFiat.contains(fiat)).toList();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get title => 'Robinhood Connect';
|
String get title => 'Robinhood Connect';
|
||||||
|
@ -58,6 +65,38 @@ class RobinhoodBuyProvider extends BuyProvider {
|
||||||
|
|
||||||
String get _apiSecret => secrets.exchangeHelperApiKey;
|
String get _apiSecret => secrets.exchangeHelperApiKey;
|
||||||
|
|
||||||
|
Future<List<CryptoCurrency>> getSupportedAssets() async {
|
||||||
|
final uri = Uri.https(_apiBaseUrl, '$_assetsPath', {'applicationId': _applicationId});
|
||||||
|
|
||||||
|
try {
|
||||||
|
final response = await http.get(uri, headers: {'accept': 'application/json'});
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
final responseData = jsonDecode(response.body) as Map<String, dynamic>;
|
||||||
|
final pairs = responseData['cryptoCurrencyPairs'] as List<dynamic>;
|
||||||
|
|
||||||
|
final supportedAssets = <CryptoCurrency>[];
|
||||||
|
|
||||||
|
for (final item in pairs) {
|
||||||
|
String code = item['assetCurrency']['code'] as String;
|
||||||
|
if (code == 'AVAX') code = 'AVAXC';
|
||||||
|
try {
|
||||||
|
final currency = CryptoCurrency.fromString(code);
|
||||||
|
supportedAssets.add(currency);
|
||||||
|
} catch (e) {
|
||||||
|
log('Robinhood: Unknown asset code "$code" - skipped');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return supportedAssets;
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
log('Robinhood: Failed to fetch supported assets: $e');
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<String> getSignature(String message) async {
|
Future<String> getSignature(String message) async {
|
||||||
switch (wallet.type) {
|
switch (wallet.type) {
|
||||||
case WalletType.ethereum:
|
case WalletType.ethereum:
|
||||||
|
@ -156,6 +195,9 @@ class RobinhoodBuyProvider extends BuyProvider {
|
||||||
String? countryCode}) async {
|
String? countryCode}) async {
|
||||||
String? paymentMethod;
|
String? paymentMethod;
|
||||||
|
|
||||||
|
if (_supportedCrypto.isEmpty) _supportedCrypto = await getSupportedAssets();
|
||||||
|
if (_supportedCrypto.isNotEmpty && !(_supportedCrypto.contains(cryptoCurrency))) return null;
|
||||||
|
|
||||||
if (paymentType != null && paymentType != PaymentType.all) {
|
if (paymentType != null && paymentType != PaymentType.all) {
|
||||||
paymentMethod = normalizePaymentMethod(paymentType);
|
paymentMethod = normalizePaymentMethod(paymentType);
|
||||||
if (paymentMethod == null) return null;
|
if (paymentMethod == null) return null;
|
||||||
|
|
|
@ -381,6 +381,7 @@ class CakePayBuyCardDetailPage extends BasePage {
|
||||||
return ConfirmSendingBottomSheet(
|
return ConfirmSendingBottomSheet(
|
||||||
key: ValueKey('send_page_confirm_sending_dialog_key'),
|
key: ValueKey('send_page_confirm_sending_dialog_key'),
|
||||||
currentTheme: currentTheme,
|
currentTheme: currentTheme,
|
||||||
|
walletType: cakePayPurchaseViewModel.sendViewModel.walletType,
|
||||||
paymentId: S.of(popupContext).payment_id,
|
paymentId: S.of(popupContext).payment_id,
|
||||||
paymentIdValue: order?.orderId,
|
paymentIdValue: order?.orderId,
|
||||||
expirationTime: cakePayPurchaseViewModel.formattedRemainingTime,
|
expirationTime: cakePayPurchaseViewModel.formattedRemainingTime,
|
||||||
|
|
|
@ -264,6 +264,7 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
|
||||||
return ConfirmSendingBottomSheet(
|
return ConfirmSendingBottomSheet(
|
||||||
key: ValueKey('exchange_trade_page_confirm_sending_bottom_sheet_key'),
|
key: ValueKey('exchange_trade_page_confirm_sending_bottom_sheet_key'),
|
||||||
currentTheme: widget.currentTheme,
|
currentTheme: widget.currentTheme,
|
||||||
|
walletType: widget.exchangeTradeViewModel.sendViewModel.walletType,
|
||||||
titleText: S.of(bottomSheetContext).confirm_transaction,
|
titleText: S.of(bottomSheetContext).confirm_transaction,
|
||||||
titleIconPath:
|
titleIconPath:
|
||||||
widget.exchangeTradeViewModel.sendViewModel.selectedCryptoCurrency.iconPath,
|
widget.exchangeTradeViewModel.sendViewModel.selectedCryptoCurrency.iconPath,
|
||||||
|
|
|
@ -20,6 +20,7 @@ class AddressListPage extends BasePage {
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
AddressList(
|
AddressList(
|
||||||
addressListViewModel: addressListViewModel,
|
addressListViewModel: addressListViewModel,
|
||||||
|
currentTheme: currentTheme,
|
||||||
onSelect: (String address) async {
|
onSelect: (String address) async {
|
||||||
Navigator.of(context).pop(address);
|
Navigator.of(context).pop(address);
|
||||||
},
|
},
|
||||||
|
|
|
@ -124,7 +124,7 @@ class ReceivePage extends BasePage {
|
||||||
isLight: currentTheme.type == ThemeType.light,
|
isLight: currentTheme.type == ThemeType.light,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
AddressList(addressListViewModel: addressListViewModel),
|
AddressList(addressListViewModel: addressListViewModel, currentTheme: currentTheme),
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.fromLTRB(24, 24, 24, 32),
|
padding: EdgeInsets.fromLTRB(24, 24, 24, 32),
|
||||||
child: Text(
|
child: Text(
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
import 'package:auto_size_text/auto_size_text.dart';
|
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
|
import 'package:cake_wallet/themes/extensions/qr_code_theme.dart';
|
||||||
|
import 'package:cake_wallet/themes/extensions/receive_page_theme.dart';
|
||||||
|
import 'package:cake_wallet/themes/theme_base.dart';
|
||||||
|
import 'package:cake_wallet/utils/address_formatter.dart';
|
||||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
|
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
|
||||||
|
import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||||
|
@ -14,6 +18,8 @@ class AddressCell extends StatelessWidget {
|
||||||
required this.isPrimary,
|
required this.isPrimary,
|
||||||
required this.backgroundColor,
|
required this.backgroundColor,
|
||||||
required this.textColor,
|
required this.textColor,
|
||||||
|
required this.walletType,
|
||||||
|
required this.currentTheme,
|
||||||
this.onTap,
|
this.onTap,
|
||||||
this.onEdit,
|
this.onEdit,
|
||||||
this.onHide,
|
this.onHide,
|
||||||
|
@ -30,6 +36,8 @@ class AddressCell extends StatelessWidget {
|
||||||
required bool isCurrent,
|
required bool isCurrent,
|
||||||
required Color backgroundColor,
|
required Color backgroundColor,
|
||||||
required Color textColor,
|
required Color textColor,
|
||||||
|
required WalletType walletType,
|
||||||
|
required ThemeBase currentTheme,
|
||||||
Function(String)? onTap,
|
Function(String)? onTap,
|
||||||
bool hasBalance = false,
|
bool hasBalance = false,
|
||||||
bool hasReceived = false,
|
bool hasReceived = false,
|
||||||
|
@ -45,6 +53,8 @@ class AddressCell extends StatelessWidget {
|
||||||
isPrimary: item.isPrimary,
|
isPrimary: item.isPrimary,
|
||||||
backgroundColor: backgroundColor,
|
backgroundColor: backgroundColor,
|
||||||
textColor: textColor,
|
textColor: textColor,
|
||||||
|
walletType: walletType,
|
||||||
|
currentTheme: currentTheme,
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
onEdit: onEdit,
|
onEdit: onEdit,
|
||||||
onHide: onHide,
|
onHide: onHide,
|
||||||
|
@ -62,6 +72,8 @@ class AddressCell extends StatelessWidget {
|
||||||
final bool isPrimary;
|
final bool isPrimary;
|
||||||
final Color backgroundColor;
|
final Color backgroundColor;
|
||||||
final Color textColor;
|
final Color textColor;
|
||||||
|
final WalletType walletType;
|
||||||
|
final ThemeBase currentTheme;
|
||||||
final Function(String)? onTap;
|
final Function(String)? onTap;
|
||||||
final Function()? onEdit;
|
final Function()? onEdit;
|
||||||
final Function()? onHide;
|
final Function()? onHide;
|
||||||
|
@ -73,21 +85,6 @@ class AddressCell extends StatelessWidget {
|
||||||
final bool hasBalance;
|
final bool hasBalance;
|
||||||
final bool hasReceived;
|
final bool hasReceived;
|
||||||
|
|
||||||
static const int addressPreviewLength = 8;
|
|
||||||
|
|
||||||
String get formattedAddress {
|
|
||||||
final formatIfCashAddr = address.replaceAll('bitcoincash:', '');
|
|
||||||
|
|
||||||
if (formatIfCashAddr.length <= (name.isNotEmpty ? 16 : 43)) {
|
|
||||||
return formatIfCashAddr;
|
|
||||||
} else {
|
|
||||||
return formatIfCashAddr.substring(0, addressPreviewLength) +
|
|
||||||
'...' +
|
|
||||||
formatIfCashAddr.substring(
|
|
||||||
formatIfCashAddr.length - addressPreviewLength, formatIfCashAddr.length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final Widget cell = InkWell(
|
final Widget cell = InkWell(
|
||||||
|
@ -139,16 +136,14 @@ class AddressCell extends StatelessWidget {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Flexible(
|
Flexible(
|
||||||
child: AutoSizeText(
|
child: AddressFormatter.buildSegmentedAddress(
|
||||||
responsiveLayoutUtil.shouldRenderTabletUI ? address : formattedAddress,
|
address: address,
|
||||||
maxLines: 1,
|
walletType: walletType,
|
||||||
overflow: TextOverflow.ellipsis,
|
shouldTruncate: name.isNotEmpty || address.length > 43 ,
|
||||||
style: TextStyle(
|
evenTextStyle: TextStyle(
|
||||||
fontSize: isChange ? 10 : 14,
|
fontSize: isChange ? 10 : 14,
|
||||||
color: textColor,
|
color: textColor
|
||||||
),
|
))),
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (hasBalance || hasReceived)
|
if (hasBalance || hasReceived)
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import 'dart:math';
|
|
||||||
|
|
||||||
import 'package:cake_wallet/di.dart';
|
import 'package:cake_wallet/di.dart';
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
import 'package:cake_wallet/routes.dart';
|
import 'package:cake_wallet/routes.dart';
|
||||||
|
@ -9,6 +7,7 @@ import 'package:cake_wallet/src/screens/receive/widgets/address_cell.dart';
|
||||||
import 'package:cake_wallet/src/screens/receive/widgets/header_tile.dart';
|
import 'package:cake_wallet/src/screens/receive/widgets/header_tile.dart';
|
||||||
import 'package:cake_wallet/src/widgets/section_divider.dart';
|
import 'package:cake_wallet/src/widgets/section_divider.dart';
|
||||||
import 'package:cake_wallet/themes/extensions/receive_page_theme.dart';
|
import 'package:cake_wallet/themes/extensions/receive_page_theme.dart';
|
||||||
|
import 'package:cake_wallet/themes/theme_base.dart';
|
||||||
import 'package:cake_wallet/utils/list_item.dart';
|
import 'package:cake_wallet/utils/list_item.dart';
|
||||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_account_list_header.dart';
|
import 'package:cake_wallet/view_model/wallet_address_list/wallet_account_list_header.dart';
|
||||||
|
@ -20,16 +19,17 @@ import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||||
import 'package:mobx/mobx.dart';
|
|
||||||
|
|
||||||
class AddressList extends StatefulWidget {
|
class AddressList extends StatefulWidget {
|
||||||
const AddressList({
|
const AddressList({
|
||||||
super.key,
|
super.key,
|
||||||
required this.addressListViewModel,
|
required this.addressListViewModel,
|
||||||
|
required this.currentTheme,
|
||||||
this.onSelect,
|
this.onSelect,
|
||||||
});
|
});
|
||||||
|
|
||||||
final WalletAddressListViewModel addressListViewModel;
|
final WalletAddressListViewModel addressListViewModel;
|
||||||
|
final ThemeBase currentTheme;
|
||||||
final Function(String)? onSelect;
|
final Function(String)? onSelect;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -161,6 +161,8 @@ class _AddressListState extends State<AddressList> {
|
||||||
return AddressCell.fromItem(
|
return AddressCell.fromItem(
|
||||||
item,
|
item,
|
||||||
isCurrent: isCurrent,
|
isCurrent: isCurrent,
|
||||||
|
currentTheme: widget.currentTheme,
|
||||||
|
walletType: widget.addressListViewModel.type,
|
||||||
hasBalance: widget.addressListViewModel.isBalanceAvailable,
|
hasBalance: widget.addressListViewModel.isBalanceAvailable,
|
||||||
hasReceived: widget.addressListViewModel.isReceivedAvailable,
|
hasReceived: widget.addressListViewModel.isReceivedAvailable,
|
||||||
// hasReceived:
|
// hasReceived:
|
||||||
|
|
|
@ -5,6 +5,7 @@ import 'package:cake_wallet/routes.dart';
|
||||||
import 'package:cake_wallet/src/screens/exchange/widgets/currency_picker.dart';
|
import 'package:cake_wallet/src/screens/exchange/widgets/currency_picker.dart';
|
||||||
import 'package:cake_wallet/src/screens/receive/widgets/currency_input_field.dart';
|
import 'package:cake_wallet/src/screens/receive/widgets/currency_input_field.dart';
|
||||||
import 'package:cake_wallet/themes/theme_base.dart';
|
import 'package:cake_wallet/themes/theme_base.dart';
|
||||||
|
import 'package:cake_wallet/utils/address_formatter.dart';
|
||||||
import 'package:cake_wallet/utils/brightness_util.dart';
|
import 'package:cake_wallet/utils/brightness_util.dart';
|
||||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||||
import 'package:cake_wallet/utils/show_bar.dart';
|
import 'package:cake_wallet/utils/show_bar.dart';
|
||||||
|
@ -160,16 +161,15 @@ class QRWidget extends StatelessWidget {
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: AddressFormatter.buildSegmentedAddress(
|
||||||
addressUri.address,
|
address: addressUri.address,
|
||||||
|
walletType: addressListViewModel.type,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
evenTextStyle: TextStyle(
|
||||||
fontSize: 15,
|
fontSize: 15,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
color:
|
color:
|
||||||
Theme.of(context).extension<DashboardPageTheme>()!.textColor),
|
Theme.of(context).extension<DashboardPageTheme>()!.textColor))),
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.only(left: 12),
|
padding: EdgeInsets.only(left: 12),
|
||||||
child: copyImage,
|
child: copyImage,
|
||||||
|
|
|
@ -558,6 +558,7 @@ class SendPage extends BasePage {
|
||||||
key: ValueKey('send_page_confirm_sending_dialog_key'),
|
key: ValueKey('send_page_confirm_sending_dialog_key'),
|
||||||
titleText: S.of(bottomSheetContext).confirm_transaction,
|
titleText: S.of(bottomSheetContext).confirm_transaction,
|
||||||
currentTheme: currentTheme,
|
currentTheme: currentTheme,
|
||||||
|
walletType: sendViewModel.walletType,
|
||||||
titleIconPath: sendViewModel.selectedCryptoCurrency.iconPath,
|
titleIconPath: sendViewModel.selectedCryptoCurrency.iconPath,
|
||||||
currency: sendViewModel.selectedCryptoCurrency,
|
currency: sendViewModel.selectedCryptoCurrency,
|
||||||
amount: S.of(bottomSheetContext).send_amount,
|
amount: S.of(bottomSheetContext).send_amount,
|
||||||
|
|
|
@ -190,6 +190,7 @@ class RBFDetailsPage extends BasePage {
|
||||||
key: ValueKey('rbf_confirm_sending_bottom_sheet'),
|
key: ValueKey('rbf_confirm_sending_bottom_sheet'),
|
||||||
titleText: S.of(bottomSheetContext).confirm_transaction,
|
titleText: S.of(bottomSheetContext).confirm_transaction,
|
||||||
currentTheme: currentTheme,
|
currentTheme: currentTheme,
|
||||||
|
walletType: transactionDetailsViewModel.sendViewModel.walletType,
|
||||||
titleIconPath: transactionDetailsViewModel.sendViewModel.selectedCryptoCurrency.iconPath,
|
titleIconPath: transactionDetailsViewModel.sendViewModel.selectedCryptoCurrency.iconPath,
|
||||||
currency: transactionDetailsViewModel.sendViewModel.selectedCryptoCurrency,
|
currency: transactionDetailsViewModel.sendViewModel.selectedCryptoCurrency,
|
||||||
amount: S.of(bottomSheetContext).send_amount,
|
amount: S.of(bottomSheetContext).send_amount,
|
||||||
|
|
|
@ -9,8 +9,11 @@ import 'package:cake_wallet/src/screens/transaction_details/textfield_list_item.
|
||||||
import 'package:cake_wallet/src/screens/transaction_details/widgets/textfield_list_row.dart';
|
import 'package:cake_wallet/src/screens/transaction_details/widgets/textfield_list_row.dart';
|
||||||
import 'package:cake_wallet/src/widgets/list_row.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.dart';
|
||||||
|
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
||||||
|
import 'package:cake_wallet/utils/address_formatter.dart';
|
||||||
import 'package:cake_wallet/utils/show_bar.dart';
|
import 'package:cake_wallet/utils/show_bar.dart';
|
||||||
import 'package:cake_wallet/view_model/transaction_details_view_model.dart';
|
import 'package:cake_wallet/view_model/transaction_details_view_model.dart';
|
||||||
|
import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||||
|
@ -39,13 +42,28 @@ class TransactionDetailsPage extends BasePage {
|
||||||
final item = transactionDetailsViewModel.items[index];
|
final item = transactionDetailsViewModel.items[index];
|
||||||
|
|
||||||
if (item is StandartListItem) {
|
if (item is StandartListItem) {
|
||||||
|
Widget? addressTextWidget;
|
||||||
|
|
||||||
|
if (item.title.toLowerCase() == 'recipient addresses' ||
|
||||||
|
item.title.toLowerCase() == 'source address') {
|
||||||
|
addressTextWidget = getFormattedAddress(
|
||||||
|
context: context,
|
||||||
|
value: item.value,
|
||||||
|
walletType: transactionDetailsViewModel.sendViewModel.walletType,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
key: item.key,
|
key: item.key,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Clipboard.setData(ClipboardData(text: item.value));
|
Clipboard.setData(ClipboardData(text: item.value));
|
||||||
showBar<void>(context, S.of(context).transaction_details_copied(item.title));
|
showBar<void>(context, S.of(context).transaction_details_copied(item.title));
|
||||||
},
|
},
|
||||||
child: ListRow(title: '${item.title}:', value: item.value),
|
child: ListRow(
|
||||||
|
title: '${item.title}:',
|
||||||
|
value: item.value,
|
||||||
|
textWidget: addressTextWidget,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,4 +109,80 @@ class TransactionDetailsPage extends BasePage {
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget getFormattedAddress({
|
||||||
|
required BuildContext context,
|
||||||
|
required String value,
|
||||||
|
required WalletType walletType,
|
||||||
|
}) {
|
||||||
|
final textStyle = TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
||||||
|
);
|
||||||
|
final List<Widget> children = [];
|
||||||
|
final bool hasDoubleNewline = value.contains('\n\n');
|
||||||
|
|
||||||
|
if (hasDoubleNewline) {
|
||||||
|
final blocks = value
|
||||||
|
.split('\n\n')
|
||||||
|
.map((b) => b.trim())
|
||||||
|
.where((b) => b.isNotEmpty)
|
||||||
|
.toList();
|
||||||
|
for (final block in blocks) {
|
||||||
|
final lines = block
|
||||||
|
.split('\n')
|
||||||
|
.map((l) => l.trim())
|
||||||
|
.where((l) => l.isNotEmpty)
|
||||||
|
.toList();
|
||||||
|
if (lines.length > 1) {
|
||||||
|
children.add(Text(lines.first, style: textStyle));
|
||||||
|
for (int i = 1; i < lines.length; i++) {
|
||||||
|
children.add(
|
||||||
|
AddressFormatter.buildSegmentedAddress(
|
||||||
|
address: lines[i],
|
||||||
|
walletType: walletType,
|
||||||
|
evenTextStyle: textStyle,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
children.add(
|
||||||
|
AddressFormatter.buildSegmentedAddress(
|
||||||
|
address: lines.first,
|
||||||
|
walletType: walletType,
|
||||||
|
evenTextStyle: textStyle,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
children.add(SizedBox(height: 8));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
final lines = value
|
||||||
|
.split('\n')
|
||||||
|
.map((l) => l.trim())
|
||||||
|
.where((l) => l.isNotEmpty)
|
||||||
|
.toList();
|
||||||
|
bool firstLineIsContactName = (lines.length > 1 && lines.first.length < 20);
|
||||||
|
int startIndex = 0;
|
||||||
|
if (firstLineIsContactName) {
|
||||||
|
children.add(Text(lines.first, style: textStyle));
|
||||||
|
startIndex = 1;
|
||||||
|
}
|
||||||
|
for (int i = startIndex; i < lines.length; i++) {
|
||||||
|
children.add(
|
||||||
|
AddressFormatter.buildSegmentedAddress(
|
||||||
|
address: lines[i],
|
||||||
|
walletType: walletType,
|
||||||
|
evenTextStyle: textStyle,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: children,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,11 +5,12 @@ import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
||||||
import 'package:cake_wallet/themes/extensions/filter_theme.dart';
|
import 'package:cake_wallet/themes/extensions/filter_theme.dart';
|
||||||
import 'package:cake_wallet/themes/extensions/sync_indicator_theme.dart';
|
import 'package:cake_wallet/themes/extensions/sync_indicator_theme.dart';
|
||||||
import 'package:cake_wallet/themes/theme_base.dart';
|
import 'package:cake_wallet/themes/theme_base.dart';
|
||||||
|
import 'package:cake_wallet/utils/address_formatter.dart';
|
||||||
import 'package:cake_wallet/view_model/send/output.dart';
|
import 'package:cake_wallet/view_model/send/output.dart';
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:cw_core/pending_transaction.dart';
|
import 'package:cw_core/pending_transaction.dart';
|
||||||
|
import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'dart:math' as math;
|
|
||||||
|
|
||||||
import 'base_bottom_sheet_widget.dart';
|
import 'base_bottom_sheet_widget.dart';
|
||||||
|
|
||||||
|
@ -27,6 +28,7 @@ class ConfirmSendingBottomSheet extends BaseBottomSheet {
|
||||||
final String feeFiatAmount;
|
final String feeFiatAmount;
|
||||||
final List<Output> outputs;
|
final List<Output> outputs;
|
||||||
final VoidCallback onSlideComplete;
|
final VoidCallback onSlideComplete;
|
||||||
|
final WalletType walletType;
|
||||||
final PendingChange? change;
|
final PendingChange? change;
|
||||||
final bool isOpenCryptoPay;
|
final bool isOpenCryptoPay;
|
||||||
|
|
||||||
|
@ -46,6 +48,7 @@ class ConfirmSendingBottomSheet extends BaseBottomSheet {
|
||||||
required this.feeFiatAmount,
|
required this.feeFiatAmount,
|
||||||
required this.outputs,
|
required this.outputs,
|
||||||
required this.onSlideComplete,
|
required this.onSlideComplete,
|
||||||
|
required this.walletType,
|
||||||
this.change,
|
this.change,
|
||||||
this.isOpenCryptoPay = false,
|
this.isOpenCryptoPay = false,
|
||||||
Key? key,
|
Key? key,
|
||||||
|
@ -91,6 +94,7 @@ class ConfirmSendingBottomSheet extends BaseBottomSheet {
|
||||||
itemTitle: paymentId!,
|
itemTitle: paymentId!,
|
||||||
currentTheme: currentTheme,
|
currentTheme: currentTheme,
|
||||||
itemTitleTextStyle: itemTitleTextStyle,
|
itemTitleTextStyle: itemTitleTextStyle,
|
||||||
|
walletType: walletType,
|
||||||
isBatchSending: false,
|
isBatchSending: false,
|
||||||
amount: '',
|
amount: '',
|
||||||
address: paymentIdValue!,
|
address: paymentIdValue!,
|
||||||
|
@ -139,6 +143,7 @@ class ConfirmSendingBottomSheet extends BaseBottomSheet {
|
||||||
name: isBatchSending ? batchContactTitle : contactName,
|
name: isBatchSending ? batchContactTitle : contactName,
|
||||||
address: _address,
|
address: _address,
|
||||||
amount: _amount,
|
amount: _amount,
|
||||||
|
walletType: walletType,
|
||||||
isBatchSending: isBatchSending,
|
isBatchSending: isBatchSending,
|
||||||
itemTitleTextStyle: itemTitleTextStyle,
|
itemTitleTextStyle: itemTitleTextStyle,
|
||||||
itemSubTitleTextStyle: itemSubTitleTextStyle,
|
itemSubTitleTextStyle: itemSubTitleTextStyle,
|
||||||
|
@ -149,6 +154,7 @@ class ConfirmSendingBottomSheet extends BaseBottomSheet {
|
||||||
currentTheme: currentTheme,
|
currentTheme: currentTheme,
|
||||||
itemTitleTextStyle: itemTitleTextStyle,
|
itemTitleTextStyle: itemTitleTextStyle,
|
||||||
isBatchSending: isBatchSending,
|
isBatchSending: isBatchSending,
|
||||||
|
walletType: walletType,
|
||||||
amount: _amount,
|
amount: _amount,
|
||||||
address: _address,
|
address: _address,
|
||||||
itemSubTitleTextStyle: itemSubTitleTextStyle,
|
itemSubTitleTextStyle: itemSubTitleTextStyle,
|
||||||
|
@ -166,6 +172,7 @@ class ConfirmSendingBottomSheet extends BaseBottomSheet {
|
||||||
address: change!.address,
|
address: change!.address,
|
||||||
amount: change!.amount + ' ${currency.title}',
|
amount: change!.amount + ' ${currency.title}',
|
||||||
isBatchSending: true,
|
isBatchSending: true,
|
||||||
|
walletType: walletType,
|
||||||
itemTitleTextStyle: itemTitleTextStyle,
|
itemTitleTextStyle: itemTitleTextStyle,
|
||||||
itemSubTitleTextStyle: itemSubTitleTextStyle,
|
itemSubTitleTextStyle: itemSubTitleTextStyle,
|
||||||
tileBackgroundColor: tileBackgroundColor,
|
tileBackgroundColor: tileBackgroundColor,
|
||||||
|
@ -275,6 +282,7 @@ class AddressTile extends StatelessWidget {
|
||||||
required this.address,
|
required this.address,
|
||||||
required this.itemSubTitleTextStyle,
|
required this.itemSubTitleTextStyle,
|
||||||
required this.tileBackgroundColor,
|
required this.tileBackgroundColor,
|
||||||
|
required this.walletType,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String itemTitle;
|
final String itemTitle;
|
||||||
|
@ -285,18 +293,10 @@ class AddressTile extends StatelessWidget {
|
||||||
final String address;
|
final String address;
|
||||||
final TextStyle itemSubTitleTextStyle;
|
final TextStyle itemSubTitleTextStyle;
|
||||||
final Color tileBackgroundColor;
|
final Color tileBackgroundColor;
|
||||||
|
final WalletType walletType;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final addressTextStyle = TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontFamily: 'Lato',
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: currentTheme.type == ThemeType.bright
|
|
||||||
? Theme.of(context).extension<CakeTextTheme>()!.titleColor.withOpacity(0.5)
|
|
||||||
: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
|
||||||
decoration: TextDecoration.none,
|
|
||||||
);
|
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8),
|
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
@ -313,41 +313,20 @@ class AddressTile extends StatelessWidget {
|
||||||
if (isBatchSending) Text(amount, style: itemTitleTextStyle),
|
if (isBatchSending) Text(amount, style: itemTitleTextStyle),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
buildSegmentedAddress(
|
AddressFormatter.buildSegmentedAddress(
|
||||||
address: address,
|
address: address,
|
||||||
evenTextStyle: addressTextStyle,
|
walletType: walletType,
|
||||||
oddTextStyle: itemSubTitleTextStyle,
|
evenTextStyle: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontFamily: 'Lato',
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
||||||
|
decoration: TextDecoration.none)
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget buildSegmentedAddress({
|
|
||||||
required String address,
|
|
||||||
int chunkSize = 6,
|
|
||||||
required TextStyle evenTextStyle,
|
|
||||||
required TextStyle oddTextStyle,
|
|
||||||
}) {
|
|
||||||
final spans = <TextSpan>[];
|
|
||||||
|
|
||||||
int index = 0;
|
|
||||||
for (int i = 0; i < address.length; i += chunkSize) {
|
|
||||||
final chunk = address.substring(i, math.min(i + chunkSize, address.length));
|
|
||||||
final style = (index % 2 == 0) ? evenTextStyle : oddTextStyle;
|
|
||||||
|
|
||||||
spans.add(
|
|
||||||
TextSpan(text: '$chunk ', style: style),
|
|
||||||
);
|
|
||||||
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return RichText(
|
|
||||||
text: TextSpan(children: spans, style: evenTextStyle),
|
|
||||||
overflow: TextOverflow.visible,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class AddressExpansionTile extends StatelessWidget {
|
class AddressExpansionTile extends StatelessWidget {
|
||||||
|
@ -362,6 +341,7 @@ class AddressExpansionTile extends StatelessWidget {
|
||||||
required this.itemTitleTextStyle,
|
required this.itemTitleTextStyle,
|
||||||
required this.itemSubTitleTextStyle,
|
required this.itemSubTitleTextStyle,
|
||||||
required this.tileBackgroundColor,
|
required this.tileBackgroundColor,
|
||||||
|
required this.walletType,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String contactType;
|
final String contactType;
|
||||||
|
@ -373,19 +353,10 @@ class AddressExpansionTile extends StatelessWidget {
|
||||||
final TextStyle itemTitleTextStyle;
|
final TextStyle itemTitleTextStyle;
|
||||||
final TextStyle itemSubTitleTextStyle;
|
final TextStyle itemSubTitleTextStyle;
|
||||||
final Color tileBackgroundColor;
|
final Color tileBackgroundColor;
|
||||||
|
final WalletType walletType;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final addressTextStyle = TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontFamily: 'Lato',
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: currentTheme.type == ThemeType.bright
|
|
||||||
? Theme.of(context).extension<CakeTextTheme>()!.titleColor.withOpacity(0.5)
|
|
||||||
: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
|
||||||
decoration: TextDecoration.none,
|
|
||||||
);
|
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.all(Radius.circular(10)),
|
borderRadius: BorderRadius.all(Radius.circular(10)),
|
||||||
|
@ -420,10 +391,15 @@ class AddressExpansionTile extends StatelessWidget {
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: buildSegmentedAddress(
|
child: AddressFormatter.buildSegmentedAddress(
|
||||||
address: address,
|
address: address,
|
||||||
evenTextStyle: addressTextStyle,
|
walletType: walletType,
|
||||||
oddTextStyle: itemSubTitleTextStyle,
|
evenTextStyle: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontFamily: 'Lato',
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
||||||
|
decoration: TextDecoration.none)
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -434,30 +410,4 @@ class AddressExpansionTile extends StatelessWidget {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget buildSegmentedAddress({
|
|
||||||
required String address,
|
|
||||||
int chunkSize = 6,
|
|
||||||
required TextStyle evenTextStyle,
|
|
||||||
required TextStyle oddTextStyle,
|
|
||||||
}) {
|
|
||||||
final spans = <TextSpan>[];
|
|
||||||
|
|
||||||
int index = 0;
|
|
||||||
for (int i = 0; i < address.length; i += chunkSize) {
|
|
||||||
final chunk = address.substring(i, math.min(i + chunkSize, address.length));
|
|
||||||
final style = (index % 2 == 0) ? evenTextStyle : oddTextStyle;
|
|
||||||
|
|
||||||
spans.add(
|
|
||||||
TextSpan(text: '$chunk ', style: style),
|
|
||||||
);
|
|
||||||
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return RichText(
|
|
||||||
text: TextSpan(children: spans, style: evenTextStyle),
|
|
||||||
overflow: TextOverflow.visible,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,8 @@ class ListRow extends StatelessWidget {
|
||||||
this.padding,
|
this.padding,
|
||||||
this.color,
|
this.color,
|
||||||
this.hintTextColor,
|
this.hintTextColor,
|
||||||
this.mainTextColor
|
this.mainTextColor,
|
||||||
|
this.textWidget
|
||||||
});
|
});
|
||||||
|
|
||||||
final String title;
|
final String title;
|
||||||
|
@ -24,6 +25,16 @@ class ListRow extends StatelessWidget {
|
||||||
final Color? color;
|
final Color? color;
|
||||||
final Color? hintTextColor;
|
final Color? hintTextColor;
|
||||||
final Color? mainTextColor;
|
final Color? mainTextColor;
|
||||||
|
final Widget? textWidget;
|
||||||
|
|
||||||
|
Widget _getTextWidget (BuildContext context) => textWidget ?? Text(
|
||||||
|
value,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: valueFontSize,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: mainTextColor ?? Theme.of(context).extension<CakeTextTheme>()!.titleColor
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -49,12 +60,7 @@ class ListRow extends StatelessWidget {
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(value,
|
child: _getTextWidget(context)),
|
||||||
style: TextStyle(
|
|
||||||
fontSize: valueFontSize,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: mainTextColor ?? Theme.of(context).extension<CakeTextTheme>()!.titleColor)),
|
|
||||||
),
|
|
||||||
image != null
|
image != null
|
||||||
? Padding(
|
? Padding(
|
||||||
padding: EdgeInsets.only(left: 24),
|
padding: EdgeInsets.only(left: 24),
|
||||||
|
|
117
lib/utils/address_formatter.dart
Normal file
117
lib/utils/address_formatter.dart
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
import 'package:cw_core/wallet_type.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'dart:math' as math;
|
||||||
|
|
||||||
|
class AddressFormatter {
|
||||||
|
static Widget buildSegmentedAddress({
|
||||||
|
required String address,
|
||||||
|
required WalletType walletType,
|
||||||
|
required TextStyle evenTextStyle,
|
||||||
|
TextStyle? oddTextStyle,
|
||||||
|
TextAlign? textAlign,
|
||||||
|
bool shouldTruncate = false,
|
||||||
|
}) {
|
||||||
|
if (shouldTruncate) {
|
||||||
|
return _buildTruncatedAddress(
|
||||||
|
address: address,
|
||||||
|
walletType: walletType,
|
||||||
|
evenTextStyle: evenTextStyle,
|
||||||
|
oddTextStyle: oddTextStyle ?? evenTextStyle.copyWith(color: evenTextStyle.color!.withAlpha(150)),
|
||||||
|
textAlign: textAlign,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return _buildFullSegmentedAddress(
|
||||||
|
address: address,
|
||||||
|
walletType: walletType,
|
||||||
|
evenTextStyle: evenTextStyle,
|
||||||
|
oddTextStyle: oddTextStyle ?? evenTextStyle.copyWith(color: evenTextStyle.color!.withAlpha(128)),
|
||||||
|
textAlign: textAlign,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Widget _buildFullSegmentedAddress({
|
||||||
|
required String address,
|
||||||
|
required WalletType walletType,
|
||||||
|
required TextStyle evenTextStyle,
|
||||||
|
required TextStyle oddTextStyle,
|
||||||
|
TextAlign? textAlign,
|
||||||
|
}) {
|
||||||
|
|
||||||
|
final cleanAddress = address.replaceAll('bitcoincash:', '');
|
||||||
|
final chunkSize = _getChunkSize(walletType);
|
||||||
|
final chunks = <String>[];
|
||||||
|
|
||||||
|
for (int i = 0; i < cleanAddress.length; i += chunkSize) {
|
||||||
|
final chunk = cleanAddress.substring(i, math.min(i + chunkSize, cleanAddress.length));
|
||||||
|
chunks.add(chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
final spans = <TextSpan>[];
|
||||||
|
for (int i = 0; i < chunks.length; i++) {
|
||||||
|
final style = (i % 2 == 0) ? evenTextStyle : oddTextStyle;
|
||||||
|
spans.add(TextSpan(text: '${chunks[i]} ', style: style));
|
||||||
|
}
|
||||||
|
|
||||||
|
return RichText(
|
||||||
|
text: TextSpan(children: spans),
|
||||||
|
textAlign: textAlign ?? TextAlign.start,
|
||||||
|
overflow: TextOverflow.visible,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Widget _buildTruncatedAddress({
|
||||||
|
required String address,
|
||||||
|
required WalletType walletType,
|
||||||
|
required TextStyle evenTextStyle,
|
||||||
|
required TextStyle oddTextStyle,
|
||||||
|
TextAlign? textAlign,
|
||||||
|
}) {
|
||||||
|
|
||||||
|
final cleanAddress = address.replaceAll('bitcoincash:', '');
|
||||||
|
|
||||||
|
final int digitCount = (walletType == WalletType.monero ||
|
||||||
|
walletType == WalletType.wownero ||
|
||||||
|
walletType == WalletType.zano)
|
||||||
|
? 6
|
||||||
|
: 4;
|
||||||
|
|
||||||
|
if (cleanAddress.length <= 2 * digitCount) {
|
||||||
|
return _buildFullSegmentedAddress(
|
||||||
|
address: cleanAddress,
|
||||||
|
walletType: walletType,
|
||||||
|
evenTextStyle: evenTextStyle,
|
||||||
|
oddTextStyle: oddTextStyle,
|
||||||
|
textAlign: textAlign,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final String firstPart = cleanAddress.substring(0, digitCount);
|
||||||
|
final String secondPart = cleanAddress.substring(digitCount, digitCount * 2);
|
||||||
|
final String lastPart = cleanAddress.substring(cleanAddress.length - digitCount);
|
||||||
|
|
||||||
|
final spans = <TextSpan>[
|
||||||
|
TextSpan(text: '$firstPart ', style: evenTextStyle),
|
||||||
|
TextSpan(text: '$secondPart ', style: oddTextStyle),
|
||||||
|
TextSpan(text: '... ', style: oddTextStyle),
|
||||||
|
TextSpan(text: lastPart, style: evenTextStyle),
|
||||||
|
];
|
||||||
|
|
||||||
|
return RichText(
|
||||||
|
text: TextSpan(children: spans),
|
||||||
|
textAlign: textAlign ?? TextAlign.start,
|
||||||
|
overflow: TextOverflow.visible,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _getChunkSize(WalletType walletType) {
|
||||||
|
switch (walletType) {
|
||||||
|
case WalletType.monero:
|
||||||
|
case WalletType.wownero:
|
||||||
|
case WalletType.zano:
|
||||||
|
return 6;
|
||||||
|
default:
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue