Merge pull request #471 from cypherstack/firo/spend-mints

Check if mint txs are confirmed
This commit is contained in:
Diego Salazar 2023-04-10 15:32:45 -06:00 committed by GitHub
commit 47b03bd95e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
50 changed files with 1148 additions and 1037 deletions

View file

@ -1,17 +1,14 @@
import 'dart:convert';
import 'package:stackwallet/utilities/amount/amount.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
class Balance {
final Coin coin;
final Amount total;
final Amount spendable;
final Amount blockedTotal;
final Amount pendingSpendable;
Balance({
required this.coin,
required this.total,
required this.spendable,
required this.blockedTotal,
@ -25,42 +22,40 @@ class Balance {
"pendingSpendable": pendingSpendable.toJsonString(),
});
// need to fall back to parsing from in due to cached balances being previously
// need to fall back to parsing from int due to cached balances being previously
// stored as int values instead of Amounts
factory Balance.fromJson(String json, Coin coin) {
factory Balance.fromJson(String json, int deprecatedValue) {
final decoded = jsonDecode(json);
return Balance(
coin: coin,
total: decoded["total"] is String
? Amount.fromSerializedJsonString(decoded["total"] as String)
: Amount(
rawValue: BigInt.from(decoded["total"] as int),
fractionDigits: coin.decimals,
fractionDigits: deprecatedValue,
),
spendable: decoded["spendable"] is String
? Amount.fromSerializedJsonString(decoded["spendable"] as String)
: Amount(
rawValue: BigInt.from(decoded["spendable"] as int),
fractionDigits: coin.decimals,
fractionDigits: deprecatedValue,
),
blockedTotal: decoded["blockedTotal"] is String
? Amount.fromSerializedJsonString(decoded["blockedTotal"] as String)
: Amount(
rawValue: BigInt.from(decoded["blockedTotal"] as int),
fractionDigits: coin.decimals,
fractionDigits: deprecatedValue,
),
pendingSpendable: decoded["pendingSpendable"] is String
? Amount.fromSerializedJsonString(
decoded["pendingSpendable"] as String)
: Amount(
rawValue: BigInt.from(decoded["pendingSpendable"] as int),
fractionDigits: coin.decimals,
fractionDigits: deprecatedValue,
),
);
}
Map<String, dynamic> toMap() => {
"coin": coin,
"total": total,
"spendable": spendable,
"blockedTotal": blockedTotal,

View file

@ -1,62 +0,0 @@
import 'dart:convert';
import 'package:stackwallet/models/balance.dart';
import 'package:stackwallet/utilities/amount/amount.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
class TokenBalance extends Balance {
TokenBalance({
required this.contractAddress,
required super.total,
required super.spendable,
required super.blockedTotal,
required super.pendingSpendable,
super.coin = Coin.ethereum,
});
final String contractAddress;
@override
String toJsonIgnoreCoin() => jsonEncode({
"contractAddress": contractAddress,
"total": total.toJsonString(),
"spendable": spendable.toJsonString(),
"blockedTotal": blockedTotal.toJsonString(),
"pendingSpendable": pendingSpendable.toJsonString(),
});
factory TokenBalance.fromJson(
String json,
int fractionDigits,
) {
final decoded = jsonDecode(json);
return TokenBalance(
contractAddress: decoded["contractAddress"] as String,
total: decoded["total"] is String
? Amount.fromSerializedJsonString(decoded["total"] as String)
: Amount(
rawValue: BigInt.from(decoded["total"] as int),
fractionDigits: fractionDigits,
),
spendable: decoded["spendable"] is String
? Amount.fromSerializedJsonString(decoded["spendable"] as String)
: Amount(
rawValue: BigInt.from(decoded["spendable"] as int),
fractionDigits: fractionDigits,
),
blockedTotal: decoded["blockedTotal"] is String
? Amount.fromSerializedJsonString(decoded["blockedTotal"] as String)
: Amount(
rawValue: BigInt.from(decoded["blockedTotal"] as int),
fractionDigits: fractionDigits,
),
pendingSpendable: decoded["pendingSpendable"] is String
? Amount.fromSerializedJsonString(
decoded["pendingSpendable"] as String)
: Amount(
rawValue: BigInt.from(decoded["pendingSpendable"] as int),
fractionDigits: fractionDigits,
),
);
}
}

View file

@ -11,6 +11,7 @@ import 'package:stackwallet/pages/add_wallet_views/add_token_view/add_custom_tok
import 'package:stackwallet/pages/add_wallet_views/add_token_view/sub_widgets/add_token_list.dart';
import 'package:stackwallet/pages/add_wallet_views/add_token_view/sub_widgets/add_token_list_element.dart';
import 'package:stackwallet/pages/add_wallet_views/add_token_view/sub_widgets/add_token_text.dart';
import 'package:stackwallet/pages/home_view/home_view.dart';
import 'package:stackwallet/pages_desktop_specific/desktop_home_view.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/services/coins/ethereum/ethereum_wallet.dart';
@ -97,16 +98,25 @@ class _EditWalletTokensViewState extends ConsumerState<EditWalletTokensView> {
if (widget.contractsToMarkSelected == null) {
Navigator.of(context).pop(42);
} else {
Navigator.of(context).popUntil(
ModalRoute.withName(DesktopHomeView.routeName),
);
unawaited(
showFloatingFlushBar(
type: FlushBarType.success,
message: "${ethWallet.walletName} tokens saved",
context: context,
),
);
if (isDesktop) {
Navigator.of(context).popUntil(
ModalRoute.withName(DesktopHomeView.routeName),
);
} else {
await Navigator.of(context).pushNamedAndRemoveUntil(
HomeView.routeName,
(route) => false,
);
}
if (mounted) {
unawaited(
showFloatingFlushBar(
type: FlushBarType.success,
message: "${ethWallet.walletName} tokens saved",
context: context,
),
);
}
}
}
}

View file

@ -8,7 +8,7 @@ import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:stackwallet/widgets/wallet_info_row/sub_widgets/wallet_info_row_balance_future.dart';
import 'package:stackwallet/widgets/wallet_info_row/sub_widgets/wallet_info_row_balance.dart';
import 'package:stackwallet/widgets/wallet_info_row/sub_widgets/wallet_info_row_coin_icon.dart';
class ChooseFromStackView extends ConsumerStatefulWidget {

View file

@ -111,26 +111,28 @@ class _ExchangeCurrencySelectionViewState
return currencies;
}
await showDialog<void>(
context: context,
builder: (context) => StackDialog(
title: "ChangeNOW Error",
message: "Failed to load currency data: ${cn.exception}",
leftButton: SecondaryButton(
label: "Ok",
onPressed: Navigator.of(context, rootNavigator: isDesktop).pop,
if (mounted) {
await showDialog<void>(
context: context,
builder: (context) => StackDialog(
title: "ChangeNOW Error",
message: "Failed to load currency data: ${cn.exception}",
leftButton: SecondaryButton(
label: "Ok",
onPressed: Navigator.of(context, rootNavigator: isDesktop).pop,
),
rightButton: PrimaryButton(
label: "Retry",
onPressed: () async {
Navigator.of(context, rootNavigator: isDesktop).pop();
_currencies = await _showUpdatingCurrencies(
whileFuture: _loadCurrencies());
setState(() {});
},
),
),
rightButton: PrimaryButton(
label: "Retry",
onPressed: () async {
Navigator.of(context, rootNavigator: isDesktop).pop();
_currencies =
await _showUpdatingCurrencies(whileFuture: _loadCurrencies());
setState(() {});
},
),
),
);
);
}
} else {
currencies.addAll(cn.value!);
}
@ -180,13 +182,13 @@ class _ExchangeCurrencySelectionViewState
.where((e) =>
e.name.toLowerCase().contains(text.toLowerCase()) ||
e.ticker.toLowerCase().contains(text.toLowerCase()))
.toList(growable: false);
.toList();
} else {
if (text.isEmpty) {
return _currencies
.where((e) =>
e.ticker.toLowerCase() != widget.pairedTicker!.toLowerCase())
.toList(growable: false);
.toList();
}
return _currencies
@ -194,7 +196,7 @@ class _ExchangeCurrencySelectionViewState
e.ticker.toLowerCase() != widget.pairedTicker!.toLowerCase() &&
(e.name.toLowerCase().contains(text.toLowerCase()) ||
e.ticker.toLowerCase().contains(text.toLowerCase())))
.toList(growable: false);
.toList();
}
}
@ -328,181 +330,111 @@ class _ExchangeCurrencySelectionViewState
height: 12,
),
Flexible(
child: Builder(builder: (context) {
final coins = Coin.values.where((e) =>
e.ticker.toLowerCase() != widget.pairedTicker?.toLowerCase());
child: Builder(
builder: (context) {
final coins = Coin.values.where((e) =>
e.ticker.toLowerCase() !=
widget.pairedTicker?.toLowerCase());
final items = filter(_searchString)
.where((e) => coins
.where((coin) =>
coin.ticker.toLowerCase() == e.ticker.toLowerCase())
.isNotEmpty)
.toList(growable: false);
items.sort((a, b) => a.name.compareTo(b.name));
final items = filter(_searchString);
return RoundedWhiteContainer(
padding: const EdgeInsets.all(0),
child: ListView.builder(
shrinkWrap: true,
primary: isDesktop ? false : null,
itemCount: items.length,
itemBuilder: (builderContext, index) {
final bool hasImageUrl =
items[index].image.startsWith("http");
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: GestureDetector(
onTap: () {
Navigator.of(context).pop(items[index]);
},
child: RoundedWhiteContainer(
child: Row(
children: [
SizedBox(
width: 24,
height: 24,
child: isStackCoin(items[index].ticker)
? getIconForTicker(
items[index].ticker,
size: 24,
)
: hasImageUrl
? SvgPicture.network(
items[index].image,
width: 24,
height: 24,
placeholderBuilder: (_) =>
const LoadingIndicator(),
)
: const SizedBox(
width: 24,
height: 24,
),
),
const SizedBox(
width: 10,
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
items[index].name,
style: STextStyles.largeMedium14(context),
),
const SizedBox(
height: 2,
),
Text(
items[index].ticker.toUpperCase(),
style: STextStyles.smallMed12(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
),
],
final walletCoins = items
.where((currency) => coins
.where((coin) =>
coin.ticker.toLowerCase() ==
currency.ticker.toLowerCase())
.isNotEmpty)
.toList();
// sort alphabetically by name
items.sort((a, b) => a.name.compareTo(b.name));
// reverse sort walletCoins to prepare for next step
walletCoins.sort((a, b) => b.name.compareTo(a.name));
// insert wallet coins at beginning
for (final c in walletCoins) {
items.remove(c);
items.insert(0, c);
}
return RoundedWhiteContainer(
padding: const EdgeInsets.all(0),
child: ListView.builder(
shrinkWrap: true,
primary: isDesktop ? false : null,
itemCount: items.length,
itemBuilder: (builderContext, index) {
final bool hasImageUrl =
items[index].image.startsWith("http");
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: GestureDetector(
onTap: () {
Navigator.of(context).pop(items[index]);
},
child: RoundedWhiteContainer(
child: Row(
children: [
SizedBox(
width: 24,
height: 24,
child: isStackCoin(items[index].ticker)
? getIconForTicker(
items[index].ticker,
size: 24,
)
: hasImageUrl
? SvgPicture.network(
items[index].image,
width: 24,
height: 24,
placeholderBuilder: (_) =>
const LoadingIndicator(),
)
: const SizedBox(
width: 24,
height: 24,
),
),
),
],
const SizedBox(
width: 10,
),
Expanded(
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
items[index].name,
style:
STextStyles.largeMedium14(context),
),
const SizedBox(
height: 2,
),
Text(
items[index].ticker.toUpperCase(),
style: STextStyles.smallMed12(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
),
],
),
),
],
),
),
),
),
);
},
),
);
}),
),
const SizedBox(
height: 20,
),
Text(
"All coins",
style: STextStyles.smallMed12(context),
),
const SizedBox(
height: 12,
),
Flexible(
child: Builder(builder: (context) {
final filtered = filter(_searchString);
filtered.sort((a, b) => a.name.compareTo(b.name));
return RoundedWhiteContainer(
padding: const EdgeInsets.all(0),
child: ListView.builder(
shrinkWrap: true,
primary: isDesktop ? false : null,
itemCount: filtered.length,
itemBuilder: (builderContext, index) {
final bool hasImageUrl =
filtered[index].image.startsWith("http");
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: GestureDetector(
onTap: () {
Navigator.of(context).pop(filtered[index]);
},
child: RoundedWhiteContainer(
child: Row(
children: [
SizedBox(
width: 24,
height: 24,
child: isStackCoin(filtered[index].ticker)
? getIconForTicker(
filtered[index].ticker,
size: 24,
)
: hasImageUrl
? SvgPicture.network(
filtered[index].image,
width: 24,
height: 24,
placeholderBuilder: (_) =>
const LoadingIndicator(),
)
: const SizedBox(
width: 24,
height: 24,
),
),
const SizedBox(
width: 10,
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
filtered[index].name,
style: STextStyles.largeMedium14(context),
),
const SizedBox(
height: 2,
),
Text(
filtered[index].ticker.toUpperCase(),
style: STextStyles.smallMed12(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
),
],
),
),
],
),
),
),
);
},
),
);
}),
);
},
),
);
},
),
),
],
),

View file

@ -149,6 +149,8 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
currency.ticker,
caseSensitive: false,
)
.and()
.tokenContractEqualTo(currency.tokenContract)
.findAll();
final items = [Tuple2(currency.exchangeName, currency)];
@ -631,6 +633,7 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
.getAggregateCurrency(
widget.contract == null ? coin!.ticker : widget.contract!.symbol,
ExchangeRateType.estimated,
widget.contract == null ? null : widget.contract!.address,
)
.then((value) {
if (value != null) {

View file

@ -5,6 +5,7 @@ import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:qr_flutter/qr_flutter.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/receive_view/addresses/wallet_addresses_view.dart';
import 'package:stackwallet/pages/receive_view/generate_receiving_uri_qr_code_view.dart';
@ -25,15 +26,15 @@ import 'package:stackwallet/widgets/rounded_white_container.dart';
class ReceiveView extends ConsumerStatefulWidget {
const ReceiveView({
Key? key,
required this.coin,
required this.walletId,
this.tokenContract,
this.clipboard = const ClipboardWrapper(),
}) : super(key: key);
static const String routeName = "/receiveView";
final Coin coin;
final String walletId;
final EthContract? tokenContract;
final ClipboardInterface clipboard;
@override
@ -86,7 +87,7 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
@override
void initState() {
walletId = widget.walletId;
coin = widget.coin;
coin = ref.read(walletsChangeNotifierProvider).getManager(walletId).coin;
clipboard = widget.clipboard;
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
@ -117,6 +118,8 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
}
});
final ticker = widget.tokenContract?.symbol ?? coin.ticker;
return Background(
child: Scaffold(
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
@ -127,7 +130,7 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
},
),
title: Text(
"Receive ${coin.ticker}",
"Receive $ticker",
style: STextStyles.navBarTitle(context),
),
actions: [
@ -245,7 +248,7 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
Row(
children: [
Text(
"Your ${coin.ticker} address",
"Your $ticker address",
style: STextStyles.itemSubtitle(context),
),
const Spacer(),

View file

@ -539,13 +539,15 @@ class _ConfirmTransactionViewState
),
Builder(
builder: (context) {
final amount =
transactionInfo["recipientAmt"] as Amount;
final coin = ref.watch(
managerProvider.select(
(value) => value.coin,
),
);
final amount = Amount(
rawValue: BigInt.from(
transactionInfo["recipientAmt"] as int),
fractionDigits: coin.decimals);
final externalCalls = ref.watch(
prefsChangeNotifierProvider.select(
(value) => value.externalCalls));
@ -922,7 +924,10 @@ class _ConfirmTransactionViewState
localeServiceChangeNotifierProvider
.select((value) => value.locale),
);
final amount = transactionInfo["recipientAmt"] as Amount;
final amount = Amount(
rawValue: BigInt.from(
transactionInfo["recipientAmt"] as int),
fractionDigits: coin.decimals);
return Text(
"${(amount + fee).localizedStringAsFixed(
locale: locale,

View file

@ -21,6 +21,7 @@ import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/rounded_container.dart';
import 'package:tuple/tuple.dart';
@ -44,8 +45,7 @@ class TokenSummary extends ConsumerWidget {
return Stack(
children: [
RoundedContainer(
color: const Color(0xFFE9EAFF), // todo: fix color
// color: Theme.of(context).extension<StackColors>()!.,
color: Theme.of(context).extension<StackColors>()!.tokenSummaryBG,
padding: const EdgeInsets.all(24),
child: Column(
children: [
@ -54,7 +54,9 @@ class TokenSummary extends ConsumerWidget {
children: [
SvgPicture.asset(
Assets.svg.walletDesktop,
color: const Color(0xFF8488AB), // todo: fix color
color: Theme.of(context)
.extension<StackColors>()!
.tokenSummaryTextSecondary,
width: 12,
height: 12,
),
@ -68,7 +70,9 @@ class TokenSummary extends ConsumerWidget {
),
),
style: STextStyles.w500_12(context).copyWith(
color: const Color(0xFF8488AB), // todo: fix color
color: Theme.of(context)
.extension<StackColors>()!
.tokenSummaryTextSecondary,
),
),
],
@ -88,7 +92,11 @@ class TokenSummary extends ConsumerWidget {
),
)}"
" ${token.symbol}",
style: STextStyles.pageTitleH1(context),
style: STextStyles.pageTitleH1(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.tokenSummaryTextPrimary,
),
),
const SizedBox(
width: 10,
@ -119,7 +127,11 @@ class TokenSummary extends ConsumerWidget {
(value) => value.currency,
),
)}",
style: STextStyles.subtitle500(context),
style: STextStyles.subtitle500(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.tokenSummaryTextPrimary,
),
),
const SizedBox(
height: 20,
@ -137,8 +149,13 @@ class TokenSummary extends ConsumerWidget {
child: WalletRefreshButton(
walletId: walletId,
initialSyncStatus: initialSyncStatus,
tokenContractAddress: ref.watch(tokenServiceProvider
.select((value) => value!.tokenContract.address)),
tokenContractAddress: ref.watch(
tokenServiceProvider.select(
(value) => value!.tokenContract.address,
),
),
overrideIconColor:
Theme.of(context).extension<StackColors>()!.topNavIconPrimary,
),
),
],
@ -192,12 +209,12 @@ class TokenWalletOptions extends StatelessWidget {
ReceiveView.routeName,
arguments: Tuple2(
walletId,
Coin.ethereum,
tokenContract,
),
);
},
subLabel: "Receive",
iconAssetSVG: Assets.svg.receive(context),
iconAssetSVG: Assets.svg.arrowDownLeft,
),
const SizedBox(
width: 16,
@ -214,7 +231,7 @@ class TokenWalletOptions extends StatelessWidget {
);
},
subLabel: "Send",
iconAssetSVG: Assets.svg.send(context),
iconAssetSVG: Assets.svg.arrowUpRight,
),
const SizedBox(
width: 16,
@ -251,12 +268,14 @@ class TokenOptionsButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
final iconSize = subLabel == "Send" || subLabel == "Receive" ? 12.0 : 24.0;
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
RawMaterialButton(
fillColor: Theme.of(context).extension<StackColors>()!.popupBG,
fillColor:
Theme.of(context).extension<StackColors>()!.tokenSummaryButtonBG,
elevation: 0,
focusElevation: 0,
hoverElevation: 0,
@ -270,11 +289,27 @@ class TokenOptionsButton extends StatelessWidget {
onPressed: onPressed,
child: Padding(
padding: const EdgeInsets.all(10),
child: SvgPicture.asset(
iconAssetSVG,
color: const Color(0xFF424A97), // todo: fix color
width: 24,
height: 24,
child: ConditionalParent(
condition: iconSize < 24,
builder: (child) => RoundedContainer(
padding: const EdgeInsets.all(6),
color: Theme.of(context)
.extension<StackColors>()!
.tokenSummaryIcon
.withOpacity(0.4),
radiusMultiplier: 10,
child: Center(
child: child,
),
),
child: SvgPicture.asset(
iconAssetSVG,
color: Theme.of(context)
.extension<StackColors>()!
.tokenSummaryIcon,
width: iconSize,
height: iconSize,
),
),
),
),
@ -283,7 +318,11 @@ class TokenOptionsButton extends StatelessWidget {
),
Text(
subLabel,
style: STextStyles.w500_12(context),
style: STextStyles.w500_12(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.tokenSummaryTextPrimary,
),
)
],
);
@ -303,12 +342,14 @@ class CoinTickerTag extends ConsumerWidget {
return RoundedContainer(
padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 4),
radiusMultiplier: 0.25,
color: const Color(0xFF4D5798), // TODO: color theme for multi themes
color: Theme.of(context).extension<StackColors>()!.ethTagBG,
child: Text(
ref.watch(walletsChangeNotifierProvider
.select((value) => value.getManager(walletId).coin.ticker)),
ref.watch(
walletsChangeNotifierProvider
.select((value) => value.getManager(walletId).coin.ticker),
),
style: STextStyles.w600_12(context).copyWith(
color: Colors.white, // TODO: design is wrong?
color: Theme.of(context).extension<StackColors>()!.ethTagText,
),
),
);

View file

@ -110,6 +110,9 @@ class _TokenViewState extends ConsumerState<TokenView> {
child: AppBarIconButton(
icon: SvgPicture.asset(
Assets.svg.verticalEllipsis,
color: Theme.of(context)
.extension<StackColors>()!
.topNavIconPrimary,
),
onPressed: () {
// todo: context menu

View file

@ -22,6 +22,7 @@ class WalletRefreshButton extends ConsumerStatefulWidget {
this.tokenContractAddress,
this.onPressed,
this.eventBus,
this.overrideIconColor,
}) : super(key: key);
final String walletId;
@ -29,6 +30,7 @@ class WalletRefreshButton extends ConsumerStatefulWidget {
final String? tokenContractAddress;
final VoidCallback? onPressed;
final EventBus? eventBus;
final Color? overrideIconColor;
@override
ConsumerState<WalletRefreshButton> createState() => _RefreshButtonState();
@ -155,11 +157,15 @@ class _RefreshButtonState extends ConsumerState<WalletRefreshButton>
Assets.svg.arrowRotate,
width: isDesktop ? 12 : 24,
height: isDesktop ? 12 : 24,
color: isDesktop
? Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultSearchIconRight
: Theme.of(context).extension<StackColors>()!.textFavoriteCard,
color: widget.overrideIconColor != null
? widget.overrideIconColor!
: isDesktop
? Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultSearchIconRight
: Theme.of(context)
.extension<StackColors>()!
.textFavoriteCard,
),
),
),

View file

@ -800,10 +800,7 @@ class _WalletViewState extends ConsumerState<WalletView> {
unawaited(
Navigator.of(context).pushNamed(
ReceiveView.routeName,
arguments: Tuple2(
walletId,
coin,
),
arguments: walletId,
),
);
}

View file

@ -1,101 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/wallet_card.dart';
class WalletsSheet extends ConsumerWidget {
const WalletsSheet({
Key? key,
required this.coin,
}) : super(key: key);
final Coin coin;
@override
Widget build(BuildContext context, WidgetRef ref) {
final providers = ref
.watch(walletsChangeNotifierProvider
.select((value) => value.getManagerProvidersByCoin()))
.where((e) => e.item1 == coin)
.map((e) => e.item2)
.expand((e) => e)
.toList();
final maxHeight = MediaQuery.of(context).size.height * 0.60;
return Container(
decoration: BoxDecoration(
color: Theme.of(context).extension<StackColors>()!.popupBG,
borderRadius: const BorderRadius.vertical(
top: Radius.circular(20),
),
),
child: LimitedBox(
maxHeight: maxHeight,
child: Padding(
padding: const EdgeInsets.only(
left: 24,
right: 24,
top: 10,
bottom: 0,
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Center(
child: Container(
decoration: BoxDecoration(
color: Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultBG,
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
width: 60,
height: 4,
),
),
const SizedBox(
height: 36,
),
Text(
"${coin.prettyName} (${coin.ticker}) wallets",
style: STextStyles.pageTitleH2(context),
textAlign: TextAlign.left,
),
const SizedBox(
height: 16,
),
Flexible(
child: ListView.builder(
shrinkWrap: true,
itemCount: providers.length,
itemBuilder: (builderContext, index) {
final walletId = ref.watch(
providers[index].select((value) => value.walletId));
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: WalletSheetCard(
walletId: walletId,
popPrevious: true,
),
);
},
),
),
const SizedBox(
height: 24,
),
],
),
),
),
);
}
}

View file

@ -1,73 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/master_wallet_card.dart';
class EthWalletsOverview extends ConsumerStatefulWidget {
const EthWalletsOverview({Key? key}) : super(key: key);
static const routeName = "/ethWalletsOverview";
@override
ConsumerState<EthWalletsOverview> createState() => _EthWalletsOverviewState();
}
class _EthWalletsOverviewState extends ConsumerState<EthWalletsOverview> {
final isDesktop = Util.isDesktop;
final List<String> ethWalletIds = [];
@override
void initState() {
final walletsData =
ref.read(walletsServiceChangeNotifierProvider).fetchWalletsData();
walletsData.removeWhere((key, value) => value.coin != Coin.ethereum);
ethWalletIds.clear();
ethWalletIds.addAll(walletsData.values.map((e) => e.walletId));
super.initState();
}
@override
Widget build(BuildContext context) {
return Background(
child: ConditionalParent(
condition: !isDesktop,
builder: (child) => Scaffold(
backgroundColor:
Theme.of(context).extension<StackColors>()!.background,
appBar: AppBar(
leading: const AppBarBackButton(),
title: Text(
"Ethereum (ETH) wallets",
style: STextStyles.navBarTitle(context),
),
),
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(16),
child: child,
),
),
),
child: ListView.separated(
itemCount: ethWalletIds.length,
separatorBuilder: (_, __) => const SizedBox(
height: 8,
),
itemBuilder: (_, index) => MasterWalletCard(
walletId: ethWalletIds[index],
),
),
),
);
}
}

View file

@ -4,8 +4,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/pages/wallet_view/wallet_view.dart';
import 'package:stackwallet/pages/wallets_sheet/wallets_sheet.dart';
import 'package:stackwallet/pages/wallets_view/eth_wallets_overview.dart';
import 'package:stackwallet/pages/wallets_view/wallets_overview.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/utilities/amount/amount.dart';
import 'package:stackwallet/utilities/assets.dart';
@ -46,13 +45,7 @@ class WalletListItem extends ConsumerWidget {
BorderRadius.circular(Constants.size.circularBorderRadius),
),
onPressed: () async {
if (coin == Coin.ethereum) {
unawaited(
Navigator.of(context).pushNamed(
EthWalletsOverview.routeName,
),
);
} else if (walletCount == 1) {
if (walletCount == 1 && coin != Coin.ethereum) {
final providersByCoin = ref
.watch(walletsChangeNotifierProvider
.select((value) => value.getManagerProvidersByCoin()))
@ -77,15 +70,9 @@ class WalletListItem extends ConsumerWidget {
}
} else {
unawaited(
showModalBottomSheet<dynamic>(
backgroundColor: Colors.transparent,
context: context,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(20),
),
),
builder: (_) => WalletsSheet(coin: coin),
Navigator.of(context).pushNamed(
WalletsOverview.routeName,
arguments: coin,
),
);
}

View file

@ -0,0 +1,308 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/models/isar/models/ethereum/eth_contract.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/dialogs/desktop_expanding_wallet_card.dart';
import 'package:stackwallet/providers/db/main_db_provider.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/services/coins/ethereum/ethereum_wallet.dart';
import 'package:stackwallet/services/coins/manager.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
import 'package:stackwallet/widgets/master_wallet_card.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
import 'package:stackwallet/widgets/textfield_icon_button.dart';
import 'package:stackwallet/widgets/wallet_card.dart';
import 'package:tuple/tuple.dart';
class WalletsOverview extends ConsumerStatefulWidget {
const WalletsOverview({
Key? key,
required this.coin,
this.navigatorState,
}) : super(key: key);
final Coin coin;
final NavigatorState? navigatorState;
static const routeName = "/walletsOverview";
@override
ConsumerState<WalletsOverview> createState() => _EthWalletsOverviewState();
}
class _EthWalletsOverviewState extends ConsumerState<WalletsOverview> {
final isDesktop = Util.isDesktop;
late final TextEditingController _searchController;
late final FocusNode searchFieldFocusNode;
String _searchString = "";
final List<Tuple2<Manager, List<EthContract>>> wallets = [];
List<Tuple2<Manager, List<EthContract>>> _filter(String searchTerm) {
if (searchTerm.isEmpty) {
return wallets;
}
final List<Tuple2<Manager, List<EthContract>>> results = [];
final term = searchTerm.toLowerCase();
for (final tuple in wallets) {
bool includeManager = false;
// search wallet name and total balance
includeManager |= _elementContains(tuple.item1.walletName, term);
includeManager |= _elementContains(
tuple.item1.balance.total.decimal.toString(),
term,
);
final List<EthContract> contracts = [];
for (final contract in tuple.item2) {
if (_elementContains(contract.name, term)) {
contracts.add(contract);
} else if (_elementContains(contract.symbol, term)) {
contracts.add(contract);
} else if (_elementContains(contract.type.name, term)) {
contracts.add(contract);
} else if (_elementContains(contract.address, term)) {
contracts.add(contract);
}
}
if (includeManager || contracts.isNotEmpty) {
results.add(Tuple2(tuple.item1, contracts));
}
}
return results;
}
bool _elementContains(String element, String term) {
return element.toLowerCase().contains(term);
}
@override
void initState() {
_searchController = TextEditingController();
searchFieldFocusNode = FocusNode();
final walletsData =
ref.read(walletsServiceChangeNotifierProvider).fetchWalletsData();
walletsData.removeWhere((key, value) => value.coin != widget.coin);
if (widget.coin == Coin.ethereum) {
for (final data in walletsData.values) {
final List<EthContract> contracts = [];
final manager =
ref.read(walletsChangeNotifierProvider).getManager(data.walletId);
final contractAddresses = (manager.wallet as EthereumWallet)
.getWalletTokenContractAddresses();
// fetch each contract
for (final contractAddress in contractAddresses) {
final contract = ref
.read(
mainDBProvider,
)
.getEthContractSync(
contractAddress,
);
// add it to list if it exists in DB
if (contract != null) {
contracts.add(contract);
}
}
// add tuple to list
wallets.add(
Tuple2(
ref.read(walletsChangeNotifierProvider).getManager(
data.walletId,
),
contracts,
),
);
}
} else {
// add non token wallet tuple to list
for (final data in walletsData.values) {
wallets.add(
Tuple2(
ref.read(walletsChangeNotifierProvider).getManager(
data.walletId,
),
[],
),
);
}
}
super.initState();
}
@override
void dispose() {
_searchController.dispose();
searchFieldFocusNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Background(
child: ConditionalParent(
condition: !isDesktop,
builder: (child) => Scaffold(
backgroundColor:
Theme.of(context).extension<StackColors>()!.background,
appBar: AppBar(
leading: const AppBarBackButton(),
title: Text(
"${widget.coin.prettyName} (${widget.coin.ticker}) wallets",
style: STextStyles.navBarTitle(context),
),
),
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(16),
child: child,
),
),
),
child: Column(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
autocorrect: !isDesktop,
enableSuggestions: !isDesktop,
controller: _searchController,
focusNode: searchFieldFocusNode,
onChanged: (value) {
setState(() {
_searchString = value;
});
},
style: isDesktop
? STextStyles.desktopTextExtraSmall(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textFieldActiveText,
height: 1.8,
)
: STextStyles.field(context),
decoration: standardInputDecoration(
"Search...",
searchFieldFocusNode,
context,
desktopMed: isDesktop,
).copyWith(
prefixIcon: Padding(
padding: EdgeInsets.symmetric(
horizontal: isDesktop ? 12 : 10,
vertical: isDesktop ? 18 : 16,
),
child: SvgPicture.asset(
Assets.svg.search,
width: isDesktop ? 20 : 16,
height: isDesktop ? 20 : 16,
),
),
suffixIcon: _searchController.text.isNotEmpty
? Padding(
padding: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
children: [
TextFieldIconButton(
child: const XIcon(),
onTap: () async {
setState(() {
_searchController.text = "";
_searchString = "";
});
},
),
],
),
),
)
: null,
),
),
),
const SizedBox(
height: 16,
),
Expanded(
child: Builder(
builder: (context) {
final data = _filter(_searchString);
return ListView.separated(
itemBuilder: (_, index) {
final element = data[index];
if (element.item1.hasTokenSupport) {
if (isDesktop) {
return DesktopExpandingWalletCard(
key: Key(
"${element.item1.walletName}_${element.item2.map((e) => e.address).join()}"),
data: element,
navigatorState: widget.navigatorState!,
);
} else {
return MasterWalletCard(
walletId: element.item1.walletId,
);
}
} else {
return ConditionalParent(
condition: isDesktop,
builder: (child) => RoundedWhiteContainer(
padding: const EdgeInsets.symmetric(
vertical: 14,
horizontal: 20,
),
borderColor: Theme.of(context)
.extension<StackColors>()!
.backgroundAppBar,
child: child,
),
child: SimpleWalletCard(
walletId: element.item1.walletId,
popPrevious: isDesktop,
desktopNavigatorState:
isDesktop ? widget.navigatorState : null,
),
);
}
},
separatorBuilder: (_, __) => SizedBox(
height: isDesktop ? 10 : 8,
),
itemCount: data.length,
);
},
),
),
],
),
),
);
}
}

View file

@ -1,435 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/models/isar/models/ethereum/eth_contract.dart';
import 'package:stackwallet/providers/db/main_db_provider.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/services/coins/ethereum/ethereum_wallet.dart';
import 'package:stackwallet/services/coins/manager.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/animated_widgets/rotate_icon.dart';
import 'package:stackwallet/widgets/expandable.dart';
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
import 'package:stackwallet/widgets/textfield_icon_button.dart';
import 'package:stackwallet/widgets/wallet_card.dart';
import 'package:stackwallet/widgets/wallet_info_row/sub_widgets/wallet_info_row_balance_future.dart';
import 'package:stackwallet/widgets/wallet_info_row/sub_widgets/wallet_info_row_coin_icon.dart';
import 'package:tuple/tuple.dart';
class DesktopCoinWalletsDialog extends ConsumerStatefulWidget {
const DesktopCoinWalletsDialog({
Key? key,
required this.coin,
required this.navigatorState,
}) : super(key: key);
final Coin coin;
final NavigatorState navigatorState;
@override
ConsumerState<DesktopCoinWalletsDialog> createState() =>
_DesktopCoinWalletsDialogState();
}
class _DesktopCoinWalletsDialogState
extends ConsumerState<DesktopCoinWalletsDialog> {
final isDesktop = Util.isDesktop;
late final TextEditingController _searchController;
late final FocusNode searchFieldFocusNode;
String _searchString = "";
final List<Tuple2<Manager, List<EthContract>>> wallets = [];
List<Tuple2<Manager, List<EthContract>>> _filter(String searchTerm) {
if (searchTerm.isEmpty) {
return wallets;
}
final List<Tuple2<Manager, List<EthContract>>> results = [];
final term = searchTerm.toLowerCase();
for (final tuple in wallets) {
bool includeManager = false;
// search wallet name and total balance
includeManager |= _elementContains(tuple.item1.walletName, term);
includeManager |= _elementContains(
tuple.item1.balance.total.decimal.toString(),
term,
);
final List<EthContract> contracts = [];
for (final contract in tuple.item2) {
if (_elementContains(contract.name, term)) {
contracts.add(contract);
} else if (_elementContains(contract.symbol, term)) {
contracts.add(contract);
} else if (_elementContains(contract.type.name, term)) {
contracts.add(contract);
} else if (_elementContains(contract.address, term)) {
contracts.add(contract);
}
}
if (includeManager || contracts.isNotEmpty) {
results.add(Tuple2(tuple.item1, contracts));
}
}
return results;
}
bool _elementContains(String element, String term) {
return element.toLowerCase().contains(term);
}
@override
void initState() {
_searchController = TextEditingController();
searchFieldFocusNode = FocusNode();
final walletsData =
ref.read(walletsServiceChangeNotifierProvider).fetchWalletsData();
walletsData.removeWhere((key, value) => value.coin != widget.coin);
if (widget.coin == Coin.ethereum) {
for (final data in walletsData.values) {
final List<EthContract> contracts = [];
final manager =
ref.read(walletsChangeNotifierProvider).getManager(data.walletId);
final contractAddresses = (manager.wallet as EthereumWallet)
.getWalletTokenContractAddresses();
// fetch each contract
for (final contractAddress in contractAddresses) {
final contract = ref
.read(
mainDBProvider,
)
.getEthContractSync(
contractAddress,
);
// add it to list if it exists in DB
if (contract != null) {
contracts.add(contract);
}
}
// add tuple to list
wallets.add(
Tuple2(
ref.read(walletsChangeNotifierProvider).getManager(
data.walletId,
),
contracts,
),
);
}
} else {
// add non token wallet tuple to list
for (final data in walletsData.values) {
wallets.add(
Tuple2(
ref.read(walletsChangeNotifierProvider).getManager(
data.walletId,
),
[],
),
);
}
}
super.initState();
}
@override
void dispose() {
_searchController.dispose();
searchFieldFocusNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
autocorrect: !isDesktop,
enableSuggestions: !isDesktop,
controller: _searchController,
focusNode: searchFieldFocusNode,
onChanged: (value) {
setState(() {
_searchString = value;
});
},
style: isDesktop
? STextStyles.desktopTextExtraSmall(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textFieldActiveText,
height: 1.8,
)
: STextStyles.field(context),
decoration: standardInputDecoration(
"Search...",
searchFieldFocusNode,
context,
desktopMed: isDesktop,
).copyWith(
prefixIcon: Padding(
padding: EdgeInsets.symmetric(
horizontal: isDesktop ? 12 : 10,
vertical: isDesktop ? 18 : 16,
),
child: SvgPicture.asset(
Assets.svg.search,
width: isDesktop ? 20 : 16,
height: isDesktop ? 20 : 16,
),
),
suffixIcon: _searchController.text.isNotEmpty
? Padding(
padding: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
children: [
TextFieldIconButton(
child: const XIcon(),
onTap: () async {
setState(() {
_searchController.text = "";
_searchString = "";
});
},
),
],
),
),
)
: null,
),
),
),
const SizedBox(
height: 16,
),
Expanded(
child: Builder(builder: (context) {
final data = _filter(_searchString);
return ListView.separated(
itemBuilder: (_, index) => widget.coin == Coin.ethereum
? _DesktopWalletCard(
key: Key(
"${data[index].item1.walletName}_${data[index].item2.map((e) => e.address).join()}"),
data: data[index],
navigatorState: widget.navigatorState,
)
: RoundedWhiteContainer(
padding: const EdgeInsets.symmetric(
vertical: 14,
horizontal: 20,
),
borderColor: Theme.of(context)
.extension<StackColors>()!
.backgroundAppBar,
child: WalletSheetCard(
walletId: data[index].item1.walletId,
popPrevious: true,
desktopNavigatorState: widget.navigatorState,
),
),
separatorBuilder: (_, __) => const SizedBox(
height: 10,
),
itemCount: data.length,
);
}),
),
],
);
}
}
class _DesktopWalletCard extends StatefulWidget {
const _DesktopWalletCard({
Key? key,
required this.data,
required this.navigatorState,
}) : super(key: key);
final Tuple2<Manager, List<EthContract>> data;
final NavigatorState navigatorState;
@override
State<_DesktopWalletCard> createState() => _DesktopWalletCardState();
}
class _DesktopWalletCardState extends State<_DesktopWalletCard> {
final expandableController = ExpandableController();
final rotateIconController = RotateIconController();
final List<String> tokenContractAddresses = [];
@override
void initState() {
if (widget.data.item1.hasTokenSupport) {
tokenContractAddresses.addAll(
widget.data.item2.map((e) => e.address),
);
}
super.initState();
}
@override
Widget build(BuildContext context) {
return RoundedWhiteContainer(
padding: EdgeInsets.zero,
borderColor: Theme.of(context).extension<StackColors>()!.backgroundAppBar,
child: Expandable(
initialState: widget.data.item1.hasTokenSupport
? ExpandableState.expanded
: ExpandableState.collapsed,
controller: expandableController,
expandOverride: () {},
header: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 14,
),
child: Row(
children: [
Expanded(
child: Row(
children: [
Expanded(
flex: 2,
child: Row(
children: [
WalletInfoCoinIcon(
coin: widget.data.item1.coin,
),
const SizedBox(
width: 12,
),
Text(
widget.data.item1.walletName,
style: STextStyles.desktopTextExtraSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
),
),
],
),
),
Expanded(
flex: 4,
child: WalletInfoRowBalance(
walletId: widget.data.item1.walletId,
),
),
],
),
),
MaterialButton(
padding: const EdgeInsets.all(5),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
minWidth: 32,
height: 32,
color: Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultBG,
elevation: 0,
hoverElevation: 0,
disabledElevation: 0,
highlightElevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
onPressed: () {
if (expandableController.state == ExpandableState.collapsed) {
rotateIconController.forward?.call();
} else {
rotateIconController.reverse?.call();
}
expandableController.toggle?.call();
},
child: RotateIcon(
controller: rotateIconController,
icon: RotatedBox(
quarterTurns: 2,
child: SvgPicture.asset(
Assets.svg.chevronDown,
width: 14,
),
),
curve: Curves.easeInOut,
),
),
],
),
),
body: ListView(
shrinkWrap: true,
primary: false,
children: [
Container(
width: double.infinity,
height: 1,
color:
Theme.of(context).extension<StackColors>()!.backgroundAppBar,
),
Padding(
padding: const EdgeInsets.only(
left: 32,
right: 14,
top: 14,
bottom: 14,
),
child: WalletSheetCard(
walletId: widget.data.item1.walletId,
popPrevious: true,
desktopNavigatorState: widget.navigatorState,
),
),
...tokenContractAddresses.map(
(e) => Padding(
padding: const EdgeInsets.only(
left: 32,
right: 14,
top: 14,
bottom: 14,
),
child: WalletSheetCard(
walletId: widget.data.item1.walletId,
contractAddress: e,
popPrevious: true,
desktopNavigatorState: widget.navigatorState,
),
),
),
],
),
),
);
}
}

View file

@ -0,0 +1,192 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/models/isar/models/ethereum/eth_contract.dart';
import 'package:stackwallet/services/coins/manager.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/animated_widgets/rotate_icon.dart';
import 'package:stackwallet/widgets/expandable.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:stackwallet/widgets/wallet_card.dart';
import 'package:stackwallet/widgets/wallet_info_row/sub_widgets/wallet_info_row_balance.dart';
import 'package:stackwallet/widgets/wallet_info_row/sub_widgets/wallet_info_row_coin_icon.dart';
import 'package:tuple/tuple.dart';
class DesktopExpandingWalletCard extends StatefulWidget {
const DesktopExpandingWalletCard({
Key? key,
required this.data,
required this.navigatorState,
}) : super(key: key);
final Tuple2<Manager, List<EthContract>> data;
final NavigatorState navigatorState;
@override
State<DesktopExpandingWalletCard> createState() =>
_DesktopExpandingWalletCardState();
}
class _DesktopExpandingWalletCardState
extends State<DesktopExpandingWalletCard> {
final expandableController = ExpandableController();
final rotateIconController = RotateIconController();
final List<String> tokenContractAddresses = [];
@override
void initState() {
if (widget.data.item1.hasTokenSupport) {
tokenContractAddresses.addAll(
widget.data.item2.map((e) => e.address),
);
}
super.initState();
}
@override
Widget build(BuildContext context) {
return RoundedWhiteContainer(
padding: EdgeInsets.zero,
borderColor: Theme.of(context).extension<StackColors>()!.backgroundAppBar,
child: Expandable(
initialState: widget.data.item1.hasTokenSupport
? ExpandableState.expanded
: ExpandableState.collapsed,
controller: expandableController,
onExpandWillChange: (toState) {
if (toState == ExpandableState.expanded) {
rotateIconController.forward?.call();
} else {
rotateIconController.reverse?.call();
}
},
header: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 14,
),
child: Row(
children: [
Expanded(
child: Row(
children: [
Expanded(
flex: 2,
child: Row(
children: [
WalletInfoCoinIcon(
coin: widget.data.item1.coin,
),
const SizedBox(
width: 12,
),
Text(
widget.data.item1.walletName,
style: STextStyles.desktopTextExtraSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
),
),
],
),
),
Expanded(
flex: 4,
child: WalletInfoRowBalance(
walletId: widget.data.item1.walletId,
),
),
],
),
),
MaterialButton(
padding: const EdgeInsets.all(5),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
minWidth: 32,
height: 32,
color: Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultBG,
elevation: 0,
hoverElevation: 0,
disabledElevation: 0,
highlightElevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
onPressed: () {
if (expandableController.state == ExpandableState.collapsed) {
rotateIconController.forward?.call();
} else {
rotateIconController.reverse?.call();
}
expandableController.toggle?.call();
},
child: RotateIcon(
controller: rotateIconController,
icon: RotatedBox(
quarterTurns: 2,
child: SvgPicture.asset(
Assets.svg.chevronDown,
width: 14,
),
),
curve: Curves.easeInOut,
),
),
],
),
),
body: ListView(
shrinkWrap: true,
primary: false,
children: [
Container(
width: double.infinity,
height: 1,
color:
Theme.of(context).extension<StackColors>()!.backgroundAppBar,
),
Padding(
padding: const EdgeInsets.only(
left: 32,
right: 14,
top: 14,
bottom: 14,
),
child: SimpleWalletCard(
walletId: widget.data.item1.walletId,
popPrevious: true,
desktopNavigatorState: widget.navigatorState,
),
),
...tokenContractAddresses.map(
(e) => Padding(
padding: const EdgeInsets.only(
left: 32,
right: 14,
top: 14,
bottom: 14,
),
child: SimpleWalletCard(
walletId: widget.data.item1.walletId,
contractAddress: e,
popPrevious: true,
desktopNavigatorState: widget.navigatorState,
),
),
),
],
),
),
);
}
}

View file

@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/dialogs/desktop_coin_wallets_dialog.dart';
import 'package:stackwallet/pages/wallets_view/wallets_overview.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/utilities/amount/amount.dart';
import 'package:stackwallet/utilities/assets.dart';
@ -103,7 +103,7 @@ class _DesktopWalletSummaryRowState extends State<DesktopWalletSummaryRow> {
right: 32,
bottom: 32,
),
child: DesktopCoinWalletsDialog(
child: WalletsOverview(
coin: widget.coin,
navigatorState: Navigator.of(context),
),

View file

@ -106,7 +106,7 @@ import 'package:stackwallet/pages/wallet_view/transaction_views/edit_note_view.d
import 'package:stackwallet/pages/wallet_view/transaction_views/transaction_details_view.dart';
import 'package:stackwallet/pages/wallet_view/transaction_views/transaction_search_filter_view.dart';
import 'package:stackwallet/pages/wallet_view/wallet_view.dart';
import 'package:stackwallet/pages/wallets_view/eth_wallets_overview.dart';
import 'package:stackwallet/pages/wallets_view/wallets_overview.dart';
import 'package:stackwallet/pages/wallets_view/wallets_view.dart';
import 'package:stackwallet/pages_desktop_specific/address_book_view/desktop_address_book.dart';
import 'package:stackwallet/pages_desktop_specific/addresses/desktop_wallet_addresses_view.dart';
@ -280,14 +280,19 @@ class RouteGenerator {
),
);
case EthWalletsOverview.routeName:
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => const EthWalletsOverview(),
settings: RouteSettings(
name: settings.name,
),
);
case WalletsOverview.routeName:
if (args is Coin) {
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => WalletsOverview(
coin: args,
),
settings: RouteSettings(
name: settings.name,
),
);
}
return _routeError("${settings.name} invalid args: ${args.toString()}");
case TokenContractDetailsView.routeName:
if (args is Tuple2<String, String>) {
@ -1014,12 +1019,22 @@ class RouteGenerator {
return _routeError("${settings.name} invalid args: ${args.toString()}");
case ReceiveView.routeName:
if (args is Tuple2<String, Coin>) {
if (args is String) {
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => ReceiveView(
walletId: args,
),
settings: RouteSettings(
name: settings.name,
),
);
} else if (args is Tuple2<String, EthContract?>) {
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => ReceiveView(
walletId: args.item1,
coin: args.item2,
tokenContract: args.item2,
),
settings: RouteSettings(
name: settings.name,

View file

@ -1978,7 +1978,6 @@ class EpicCashWallet extends CoinServiceAPI
(jsonBalances['amount_awaiting_finalization'] as double).toString();
_balance = Balance(
coin: coin,
total: Amount.fromDecimal(
Decimal.parse(total) + Decimal.parse(awaiting),
fractionDigits: coin.decimals,

View file

@ -10,7 +10,6 @@ import 'package:stackwallet/models/balance.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart';
import 'package:stackwallet/models/node_model.dart';
import 'package:stackwallet/models/paymint/fee_object_model.dart';
import 'package:stackwallet/models/token_balance.dart';
import 'package:stackwallet/services/coins/coin_service.dart';
import 'package:stackwallet/services/ethereum/ethereum_api.dart';
import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart';
@ -78,14 +77,13 @@ class EthereumWallet extends CoinServiceAPI with WalletCache, WalletDB {
);
}
TokenBalance getCachedTokenBalance(EthContract contract) {
Balance getCachedTokenBalance(EthContract contract) {
final jsonString = DB.instance.get<dynamic>(
boxName: _walletId,
key: TokenCacheKeys.tokenBalance(contract.address),
) as String?;
if (jsonString == null) {
return TokenBalance(
contractAddress: contract.address,
return Balance(
total: Amount(
rawValue: BigInt.zero,
fractionDigits: contract.decimals,
@ -104,7 +102,7 @@ class EthereumWallet extends CoinServiceAPI with WalletCache, WalletDB {
),
);
}
return TokenBalance.fromJson(
return Balance.fromJson(
jsonString,
contract.decimals,
);
@ -222,7 +220,6 @@ class EthereumWallet extends CoinServiceAPI with WalletCache, WalletDB {
web3.Web3Client client = getEthClient();
web3.EtherAmount ethBalance = await client.getBalance(_credentials.address);
_balance = Balance(
coin: coin,
total: Amount(
rawValue: ethBalance.getInWei,
fractionDigits: coin.decimals,

View file

@ -2413,7 +2413,14 @@ class FiroWallet extends CoinServiceAPI
if (!jindexes!.contains(lelantusCoinsList[i].index) &&
transactions
.where((e) => e.txid == lelantusCoinsList[i].txId)
.isEmpty) {
.isEmpty &&
!(lelantusTransactionsd
.where((e) => e.txid == lelantusCoinsList[i].txId)
.isNotEmpty &&
lelantusTransactionsd
.where((e) => e.txid == lelantusCoinsList[i].txId)
.first
.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS))) {
isUnconfirmed = true;
}
@ -2515,7 +2522,6 @@ class FiroWallet extends CoinServiceAPI
}
_balancePrivate = Balance(
coin: coin,
total: Amount(
rawValue:
BigInt.from(intLelantusBalance + unconfirmedLelantusBalance),
@ -3803,7 +3809,6 @@ class FiroWallet extends CoinServiceAPI
// finally update public balance
_balance = Balance(
coin: coin,
total: satoshiBalanceTotal,
spendable: satoshiBalanceSpendable,
blockedTotal: satoshiBalanceBlocked,

View file

@ -747,7 +747,6 @@ class MoneroWallet extends CoinServiceAPI with WalletCache, WalletDB {
final total = await _totalBalance;
final available = await _availableBalance;
_balance = Balance(
coin: coin,
total: total,
spendable: available,
blockedTotal: Amount(

View file

@ -774,7 +774,6 @@ class WowneroWallet extends CoinServiceAPI with WalletCache, WalletDB {
final total = await _totalBalance;
final available = await _availableBalance;
_balance = Balance(
coin: coin,
total: total,
spendable: available,
blockedTotal: Amount(

View file

@ -1,5 +1,5 @@
import 'package:stackwallet/models/balance.dart';
import 'package:stackwallet/models/isar/models/ethereum/eth_contract.dart';
import 'package:stackwallet/models/token_balance.dart';
import 'package:stackwallet/services/ethereum/ethereum_api.dart';
import 'package:stackwallet/services/mixins/eth_token_cache.dart';
import 'package:stackwallet/utilities/amount/amount.dart';
@ -21,8 +21,7 @@ class CachedEthTokenBalance with EthTokenCache {
if (response.value != null) {
await updateCachedBalance(
TokenBalance(
contractAddress: token.address,
Balance(
total: response.value!,
spendable: response.value!,
blockedTotal: Amount(

View file

@ -1,6 +1,5 @@
import 'dart:async';
import 'package:decimal/decimal.dart';
import 'package:ethereum_addresses/ethereum_addresses.dart';
import 'package:flutter/widgets.dart';
import 'package:http/http.dart';
@ -8,10 +7,10 @@ import 'package:isar/isar.dart';
import 'package:stackwallet/db/isar/main_db.dart';
import 'package:stackwallet/dto/ethereum/eth_token_tx_dto.dart';
import 'package:stackwallet/dto/ethereum/eth_token_tx_extra_dto.dart';
import 'package:stackwallet/models/balance.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart';
import 'package:stackwallet/models/node_model.dart';
import 'package:stackwallet/models/paymint/fee_object_model.dart';
import 'package:stackwallet/models/token_balance.dart';
import 'package:stackwallet/services/coins/ethereum/ethereum_wallet.dart';
import 'package:stackwallet/services/ethereum/ethereum_api.dart';
import 'package:stackwallet/services/event_bus/events/global/updated_in_background_event.dart';
@ -40,7 +39,6 @@ class EthTokenWallet extends ChangeNotifier with EthTokenCache {
// late web3dart.EthereumAddress _contractAddress;
late web3dart.EthPrivateKey _credentials;
late web3dart.DeployedContract _deployedContract;
late web3dart.ContractFunction _balanceFunction;
late web3dart.ContractFunction _sendFunction;
late web3dart.Web3Client _client;
@ -60,8 +58,8 @@ class EthTokenWallet extends ChangeNotifier with EthTokenCache {
EthContract get tokenContract => _tokenContract;
EthContract _tokenContract;
TokenBalance get balance => _balance ??= getCachedBalance();
TokenBalance? _balance;
Balance get balance => _balance ??= getCachedBalance();
Balance? _balance;
Coin get coin => Coin.ethereum;
@ -259,12 +257,12 @@ class EthTokenWallet extends ChangeNotifier with EthTokenCache {
final contractAddress =
web3dart.EthereumAddress.fromHex(tokenContract.address);
// if (tokenContract.abi == null) {
_tokenContract = await _updateTokenABI(
forContract: tokenContract,
usingContractAddress: contractAddress.hex,
);
// }
if (tokenContract.abi == null) {
_tokenContract = await _updateTokenABI(
forContract: tokenContract,
usingContractAddress: contractAddress.hex,
);
}
String? mnemonicString = await ethWallet.mnemonicString;
@ -284,7 +282,6 @@ class EthTokenWallet extends ChangeNotifier with EthTokenCache {
);
try {
_balanceFunction = _deployedContract.function('balanceOf');
_sendFunction = _deployedContract.function('transfer');
} catch (_) {
//====================================================================
@ -359,7 +356,6 @@ class EthTokenWallet extends ChangeNotifier with EthTokenCache {
contractAddress,
);
_balanceFunction = _deployedContract.function('balanceOf');
_sendFunction = _deployedContract.function('transfer');
_client = await getEthClient();
@ -405,35 +401,33 @@ class EthTokenWallet extends ChangeNotifier with EthTokenCache {
}
Future<void> refreshCachedBalance() async {
final balanceRequest = await _client.call(
contract: _deployedContract,
function: _balanceFunction,
params: [_credentials.address],
);
String _balance = balanceRequest.first.toString();
final newBalance = TokenBalance(
final response = await EthereumAPI.getWalletTokenBalance(
address: _credentials.address.hex,
contractAddress: tokenContract.address,
total: Amount.fromDecimal(
Decimal.parse(_balance),
fractionDigits: tokenContract.decimals,
),
spendable: Amount.fromDecimal(
Decimal.parse(_balance),
fractionDigits: tokenContract.decimals,
),
blockedTotal: Amount(
rawValue: BigInt.zero,
fractionDigits: tokenContract.decimals,
),
pendingSpendable: Amount(
rawValue: BigInt.zero,
fractionDigits: tokenContract.decimals,
),
);
await updateCachedBalance(newBalance);
notifyListeners();
if (response.value != null) {
await updateCachedBalance(
Balance(
total: response.value!,
spendable: response.value!,
blockedTotal: Amount(
rawValue: BigInt.zero,
fractionDigits: tokenContract.decimals,
),
pendingSpendable: Amount(
rawValue: BigInt.zero,
fractionDigits: tokenContract.decimals,
),
),
);
notifyListeners();
} else {
Logging.instance.log(
"CachedEthTokenBalance.fetchAndUpdateCachedBalance failed: ${response.exception}",
level: LogLevel.Warning,
);
}
}
Future<List<Transaction>> get transactions => ethWallet.db

View file

@ -61,10 +61,12 @@ class ExchangeDataLoadingService {
final sendCurrency = await getAggregateCurrency(
"BTC",
state.exchangeRateType,
null,
);
final receiveCurrency = await getAggregateCurrency(
"XMR",
state.exchangeRateType,
null,
);
state.setCurrencies(sendCurrency, receiveCurrency);
}
@ -72,7 +74,10 @@ class ExchangeDataLoadingService {
}
Future<AggregateCurrency?> getAggregateCurrency(
String ticker, ExchangeRateType rateType) async {
String ticker,
ExchangeRateType rateType,
String? contract,
) async {
final currencies = await ExchangeDataLoadingService.instance.isar.currencies
.filter()
.group((q) => rateType == ExchangeRateType.fixed
@ -89,6 +94,8 @@ class ExchangeDataLoadingService {
ticker,
caseSensitive: false,
)
.and()
.tokenContractEqualTo(contract)
.findAll();
final items = currencies

View file

@ -76,7 +76,6 @@ mixin CoinControlInterface {
}
final balance = Balance(
coin: _coin,
total: satoshiBalanceTotal,
spendable: satoshiBalanceSpendable,
blockedTotal: satoshiBalanceBlocked,

View file

@ -1,6 +1,6 @@
import 'package:stackwallet/db/hive/db.dart';
import 'package:stackwallet/models/balance.dart';
import 'package:stackwallet/models/isar/models/ethereum/eth_contract.dart';
import 'package:stackwallet/models/token_balance.dart';
import 'package:stackwallet/utilities/amount/amount.dart';
abstract class TokenCacheKeys {
@ -19,14 +19,13 @@ mixin EthTokenCache {
}
// token balance cache
TokenBalance getCachedBalance() {
Balance getCachedBalance() {
final jsonString = DB.instance.get<dynamic>(
boxName: _walletId,
key: TokenCacheKeys.tokenBalance(_token.address),
) as String?;
if (jsonString == null) {
return TokenBalance(
contractAddress: _token.address,
return Balance(
total: Amount(
rawValue: BigInt.zero,
fractionDigits: _token.decimals,
@ -45,13 +44,13 @@ mixin EthTokenCache {
),
);
}
return TokenBalance.fromJson(
return Balance.fromJson(
jsonString,
_token.decimals,
);
}
Future<void> updateCachedBalance(TokenBalance balance) async {
Future<void> updateCachedBalance(Balance balance) async {
await DB.instance.put<dynamic>(
boxName: _walletId,
key: TokenCacheKeys.tokenBalance(_token.address),

View file

@ -70,7 +70,6 @@ mixin WalletCache {
) as String?;
if (jsonString == null) {
return Balance(
coin: _coin,
total: Amount(rawValue: BigInt.zero, fractionDigits: _coin.decimals),
spendable:
Amount(rawValue: BigInt.zero, fractionDigits: _coin.decimals),
@ -80,7 +79,7 @@ mixin WalletCache {
Amount(rawValue: BigInt.zero, fractionDigits: _coin.decimals),
);
}
return Balance.fromJson(jsonString, _coin);
return Balance.fromJson(jsonString, _coin.decimals);
}
Future<void> updateCachedBalance(Balance balance) async {
@ -99,7 +98,6 @@ mixin WalletCache {
) as String?;
if (jsonString == null) {
return Balance(
coin: _coin,
total: Amount(rawValue: BigInt.zero, fractionDigits: _coin.decimals),
spendable:
Amount(rawValue: BigInt.zero, fractionDigits: _coin.decimals),
@ -109,7 +107,7 @@ mixin WalletCache {
Amount(rawValue: BigInt.zero, fractionDigits: _coin.decimals),
);
}
return Balance.fromJson(jsonString, _coin);
return Balance.fromJson(jsonString, _coin.decimals);
}
Future<void> updateCachedBalanceSecondary(Balance balance) async {

View file

@ -335,6 +335,26 @@ class ChanColors extends StackColorTheme {
@override
Color get rateTypeToggleDesktopColorOff => buttonBackSecondary;
// token view colors
@override
Color get ethTagText => const Color(0xFFFFFFFF);
@override
Color get ethTagBG => const Color(0xFF4D5798);
@override
Color get ethWalletTagText => const Color(0xFF4D5798);
@override
Color get ethWalletTagBG => const Color(0xFFF0F3FD);
@override
Color get tokenSummaryTextPrimary => const Color(0xFF232323);
@override
Color get tokenSummaryTextSecondary => const Color(0xFF8488AB);
@override
Color get tokenSummaryBG => const Color(0xFFE9EAFF);
@override
Color get tokenSummaryButtonBG => const Color(0xFFFFFFFF);
@override
Color get tokenSummaryIcon => const Color(0xFF424A97);
@override
BoxShadow get standardBoxShadow => BoxShadow(
color: shadow,

View file

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/theme/chan_colors.dart';
import 'package:stackwallet/utilities/theme/dark_colors.dart';
import 'package:stackwallet/utilities/theme/forest_colors.dart';
import 'package:stackwallet/utilities/theme/fruit_sorbet_colors.dart';
@ -7,9 +8,7 @@ import 'package:stackwallet/utilities/theme/light_colors.dart';
import 'package:stackwallet/utilities/theme/ocean_breeze_colors.dart';
import 'package:stackwallet/utilities/theme/oled_black_colors.dart';
import 'package:stackwallet/utilities/theme/oled_chans_colors.dart';
import 'chan_colors.dart';
import 'orange_colors.dart';
import 'package:stackwallet/utilities/theme/orange_colors.dart';
enum ThemeType {
light,
@ -285,6 +284,17 @@ abstract class StackColorTheme {
Color get rateTypeToggleDesktopColorOn;
Color get rateTypeToggleDesktopColorOff;
// token view colors
Color get ethTagText;
Color get ethTagBG;
Color get ethWalletTagText;
Color get ethWalletTagBG;
Color get tokenSummaryTextPrimary;
Color get tokenSummaryTextSecondary;
Color get tokenSummaryBG;
Color get tokenSummaryButtonBG;
Color get tokenSummaryIcon;
BoxShadow get standardBoxShadow;
BoxShadow? get homeViewButtonBarBoxShadow;
}

View file

@ -335,6 +335,26 @@ class DarkColors extends StackColorTheme {
@override
Color get rateTypeToggleDesktopColorOff => buttonBackSecondary;
// token view colors
@override
Color get ethTagText => const Color(0xFFFFFFFF);
@override
Color get ethTagBG => const Color(0xFF5761A2);
@override
Color get ethWalletTagText => const Color(0xFFE7EBFF);
@override
Color get ethWalletTagBG => const Color(0xFF414868);
@override
Color get tokenSummaryTextPrimary => const Color(0xFFFFFFFF);
@override
Color get tokenSummaryTextSecondary => const Color(0xFFC9D0FF);
@override
Color get tokenSummaryBG => const Color(0xFF464C73);
@override
Color get tokenSummaryButtonBG => const Color(0xFFC9D0FF);
@override
Color get tokenSummaryIcon => const Color(0xFF252C78);
@override
BoxShadow get standardBoxShadow => BoxShadow(
color: shadow,

View file

@ -335,6 +335,26 @@ class ForestColors extends StackColorTheme {
@override
Color get rateTypeToggleDesktopColorOff => buttonBackSecondary;
// token view colors
@override
Color get ethTagText => const Color(0xFFFFFFFF);
@override
Color get ethTagBG => const Color(0xFF4D5798);
@override
Color get ethWalletTagText => const Color(0xFF4D5798);
@override
Color get ethWalletTagBG => const Color(0xFFEBEFFE);
@override
Color get tokenSummaryTextPrimary => const Color(0xFF232323);
@override
Color get tokenSummaryTextSecondary => const Color(0xFF4D5798);
@override
Color get tokenSummaryBG => const Color(0xFFFFFFFF);
@override
Color get tokenSummaryButtonBG => const Color(0xFFE9FBEF);
@override
Color get tokenSummaryIcon => const Color(0xFF22867A);
@override
BoxShadow get standardBoxShadow => BoxShadow(
color: shadow,

View file

@ -335,6 +335,26 @@ class FruitSorbetColors extends StackColorTheme {
@override
Color get rateTypeToggleDesktopColorOff => popupBG;
// token view colors
@override
Color get ethTagText => const Color(0xFFFFFFFF);
@override
Color get ethTagBG => const Color(0xFF4D5798);
@override
Color get ethWalletTagText => const Color(0xFF4D5798);
@override
Color get ethWalletTagBG => const Color(0xFFEBEFFE);
@override
Color get tokenSummaryTextPrimary => const Color(0xFF232323);
@override
Color get tokenSummaryTextSecondary => const Color(0xFF4D5798);
@override
Color get tokenSummaryBG => const Color(0xFFFFF8EE);
@override
Color get tokenSummaryButtonBG => const Color(0xFFFEDED4);
@override
Color get tokenSummaryIcon => const Color(0xFFF62A45);
@override
BoxShadow get standardBoxShadow => BoxShadow(
color: shadow,

View file

@ -335,6 +335,26 @@ class LightColors extends StackColorTheme {
@override
Color get rateTypeToggleDesktopColorOff => buttonBackSecondary;
// token view colors
@override
Color get ethTagText => const Color(0xFFFFFFFF);
@override
Color get ethTagBG => const Color(0xFF4D5798);
@override
Color get ethWalletTagText => const Color(0xFF4D5798);
@override
Color get ethWalletTagBG => const Color(0xFFF0F3FD);
@override
Color get tokenSummaryTextPrimary => const Color(0xFF232323);
@override
Color get tokenSummaryTextSecondary => const Color(0xFF8488AB);
@override
Color get tokenSummaryBG => const Color(0xFFE9EAFF);
@override
Color get tokenSummaryButtonBG => const Color(0xFFFFFFFF);
@override
Color get tokenSummaryIcon => const Color(0xFF424A97);
@override
BoxShadow get standardBoxShadow => BoxShadow(
color: shadow,

View file

@ -342,6 +342,26 @@ class OceanBreezeColors extends StackColorTheme {
@override
Color get rateTypeToggleDesktopColorOff => buttonBackSecondary;
// token view colors
@override
Color get ethTagText => const Color(0xFFFFFFFF);
@override
Color get ethTagBG => const Color(0xFF4D5798);
@override
Color get ethWalletTagText => const Color(0xFF4D5798);
@override
Color get ethWalletTagBG => const Color(0xFFEBEFFE);
@override
Color get tokenSummaryTextPrimary => const Color(0xFF232323);
@override
Color get tokenSummaryTextSecondary => const Color(0xFF4D5798);
@override
Color get tokenSummaryBG => const Color(0xFFFFFFFF);
@override
Color get tokenSummaryButtonBG => const Color(0xFFEDF4F9);
@override
Color get tokenSummaryIcon => const Color(0xFF197287);
@override
BoxShadow get standardBoxShadow => BoxShadow(
color: shadow,

View file

@ -338,6 +338,26 @@ class OledBlackColors extends StackColorTheme {
@override
Color get rateTypeToggleDesktopColorOff => buttonBackSecondary;
// token view colors
@override
Color get ethTagText => const Color(0xFFFFFFFF);
@override
Color get ethTagBG => const Color(0xFF5761A2);
@override
Color get ethWalletTagText => const Color(0xFFDEDEDE);
@override
Color get ethWalletTagBG => const Color(0xFF222539);
@override
Color get tokenSummaryTextPrimary => const Color(0xFFFFFFFF);
@override
Color get tokenSummaryTextSecondary => const Color(0xFFC9D0FF);
@override
Color get tokenSummaryBG => const Color(0xFF292D45);
@override
Color get tokenSummaryButtonBG => const Color(0xFFC9D0FF);
@override
Color get tokenSummaryIcon => const Color(0xFF252C78);
@override
BoxShadow get standardBoxShadow => BoxShadow(
color: shadow,

View file

@ -335,6 +335,26 @@ class DarkChansColors extends StackColorTheme {
@override
Color get rateTypeToggleDesktopColorOff => buttonBackSecondary;
// token view colors
@override
Color get ethTagText => const Color(0xFFFFFFFF);
@override
Color get ethTagBG => const Color(0xFF5761A2);
@override
Color get ethWalletTagText => const Color(0xFFDEDEDE);
@override
Color get ethWalletTagBG => const Color(0xFF222539);
@override
Color get tokenSummaryTextPrimary => const Color(0xFFFFFFFF);
@override
Color get tokenSummaryTextSecondary => const Color(0xFFC9D0FF);
@override
Color get tokenSummaryBG => const Color(0xFF292D45);
@override
Color get tokenSummaryButtonBG => const Color(0xFFC9D0FF);
@override
Color get tokenSummaryIcon => const Color(0xFF252C78);
@override
BoxShadow get standardBoxShadow => BoxShadow(
color: shadow,

View file

@ -335,6 +335,26 @@ class OrangeColors extends StackColorTheme {
@override
Color get rateTypeToggleDesktopColorOff => buttonBackSecondary;
// token view colors
@override
Color get ethTagText => const Color(0xFFFFFFFF);
@override
Color get ethTagBG => const Color(0xFF4D5798);
@override
Color get ethWalletTagText => const Color(0xFF4D5798);
@override
Color get ethWalletTagBG => const Color(0xFFEBEFFE);
@override
Color get tokenSummaryTextPrimary => const Color(0xFF232323);
@override
Color get tokenSummaryTextSecondary => const Color(0xFF4D5798);
@override
Color get tokenSummaryBG => const Color(0xFFFFFFFF);
@override
Color get tokenSummaryButtonBG => const Color(0xAAFFC58F);
@override
Color get tokenSummaryIcon => const Color(0xFFF36B43);
@override
BoxShadow get standardBoxShadow => BoxShadow(
color: shadow,

View file

@ -187,6 +187,17 @@ class StackColors extends ThemeExtension<StackColors> {
final Color rateTypeToggleDesktopColorOn;
final Color rateTypeToggleDesktopColorOff;
// token view colors
final Color ethTagText;
final Color ethTagBG;
final Color ethWalletTagText;
final Color ethWalletTagBG;
final Color tokenSummaryTextPrimary;
final Color tokenSummaryTextSecondary;
final Color tokenSummaryBG;
final Color tokenSummaryButtonBG;
final Color tokenSummaryIcon;
final BoxShadow standardBoxShadow;
final BoxShadow? homeViewButtonBarBoxShadow;
@ -337,6 +348,15 @@ class StackColors extends ThemeExtension<StackColors> {
required this.rateTypeToggleDesktopColorOff,
required this.standardBoxShadow,
required this.homeViewButtonBarBoxShadow,
required this.ethTagText,
required this.ethTagBG,
required this.ethWalletTagText,
required this.ethWalletTagBG,
required this.tokenSummaryTextPrimary,
required this.tokenSummaryTextSecondary,
required this.tokenSummaryBG,
required this.tokenSummaryButtonBG,
required this.tokenSummaryIcon,
});
factory StackColors.fromStackColorTheme(StackColorTheme colorTheme) {
@ -490,6 +510,15 @@ class StackColors extends ThemeExtension<StackColors> {
rateTypeToggleDesktopColorOff: colorTheme.rateTypeToggleDesktopColorOff,
homeViewButtonBarBoxShadow: colorTheme.homeViewButtonBarBoxShadow,
standardBoxShadow: colorTheme.standardBoxShadow,
ethTagText: colorTheme.ethTagText,
ethTagBG: colorTheme.ethTagBG,
ethWalletTagText: colorTheme.ethWalletTagText,
ethWalletTagBG: colorTheme.ethWalletTagBG,
tokenSummaryTextPrimary: colorTheme.tokenSummaryTextPrimary,
tokenSummaryTextSecondary: colorTheme.tokenSummaryTextSecondary,
tokenSummaryBG: colorTheme.tokenSummaryBG,
tokenSummaryButtonBG: colorTheme.tokenSummaryButtonBG,
tokenSummaryIcon: colorTheme.tokenSummaryIcon,
);
}
@ -639,6 +668,15 @@ class StackColors extends ThemeExtension<StackColors> {
Color? rateTypeToggleColorOff,
Color? rateTypeToggleDesktopColorOn,
Color? rateTypeToggleDesktopColorOff,
Color? ethTagText,
Color? ethTagBG,
Color? ethWalletTagText,
Color? ethWalletTagBG,
Color? tokenSummaryTextPrimary,
Color? tokenSummaryTextSecondary,
Color? tokenSummaryBG,
Color? tokenSummaryButtonBG,
Color? tokenSummaryIcon,
BoxShadow? homeViewButtonBarBoxShadow,
BoxShadow? standardBoxShadow,
}) {
@ -833,6 +871,17 @@ class StackColors extends ThemeExtension<StackColors> {
rateTypeToggleDesktopColorOn ?? this.rateTypeToggleDesktopColorOn,
rateTypeToggleDesktopColorOff:
rateTypeToggleDesktopColorOff ?? this.rateTypeToggleDesktopColorOff,
ethTagText: ethTagText ?? this.ethTagText,
ethTagBG: ethTagBG ?? this.ethTagBG,
ethWalletTagText: ethWalletTagText ?? this.ethWalletTagText,
ethWalletTagBG: ethWalletTagBG ?? this.ethWalletTagBG,
tokenSummaryTextPrimary:
tokenSummaryTextPrimary ?? this.tokenSummaryTextPrimary,
tokenSummaryTextSecondary:
tokenSummaryTextSecondary ?? this.tokenSummaryTextSecondary,
tokenSummaryBG: tokenSummaryBG ?? this.tokenSummaryBG,
tokenSummaryButtonBG: tokenSummaryButtonBG ?? this.tokenSummaryButtonBG,
tokenSummaryIcon: tokenSummaryIcon ?? this.tokenSummaryIcon,
homeViewButtonBarBoxShadow:
homeViewButtonBarBoxShadow ?? this.homeViewButtonBarBoxShadow,
standardBoxShadow: standardBoxShadow ?? this.standardBoxShadow,
@ -1557,6 +1606,51 @@ class StackColors extends ThemeExtension<StackColors> {
other.rateTypeToggleDesktopColorOff,
t,
)!,
ethTagText: Color.lerp(
ethTagText,
other.ethTagText,
t,
)!,
ethTagBG: Color.lerp(
ethTagBG,
other.ethTagBG,
t,
)!,
ethWalletTagText: Color.lerp(
ethWalletTagText,
other.ethWalletTagText,
t,
)!,
ethWalletTagBG: Color.lerp(
ethWalletTagBG,
other.ethWalletTagBG,
t,
)!,
tokenSummaryTextPrimary: Color.lerp(
tokenSummaryTextPrimary,
other.tokenSummaryTextPrimary,
t,
)!,
tokenSummaryTextSecondary: Color.lerp(
tokenSummaryTextSecondary,
other.tokenSummaryTextSecondary,
t,
)!,
tokenSummaryBG: Color.lerp(
tokenSummaryBG,
other.tokenSummaryBG,
t,
)!,
tokenSummaryButtonBG: Color.lerp(
tokenSummaryButtonBG,
other.tokenSummaryButtonBG,
t,
)!,
tokenSummaryIcon: Color.lerp(
tokenSummaryIcon,
other.tokenSummaryIcon,
t,
)!,
);
}

View file

@ -3,7 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/wallet_info_row/sub_widgets/wallet_info_row_balance_future.dart';
import 'package:stackwallet/widgets/wallet_info_row/sub_widgets/wallet_info_row_balance.dart';
import 'package:stackwallet/widgets/wallet_info_row/sub_widgets/wallet_info_row_coin_icon.dart';
class EthWalletRadio extends ConsumerStatefulWidget {

View file

@ -49,7 +49,13 @@ class _MasterWalletCardState extends ConsumerState<MasterWalletCard> {
padding: EdgeInsets.zero,
child: Expandable(
controller: expandableController,
expandOverride: () {},
onExpandWillChange: (toState) {
if (toState == ExpandableState.expanded) {
rotateIconController.forward?.call();
} else {
rotateIconController.reverse?.call();
}
},
header: Padding(
padding: const EdgeInsets.all(12),
child: Row(
@ -110,7 +116,7 @@ class _MasterWalletCardState extends ConsumerState<MasterWalletCard> {
padding: const EdgeInsets.all(
7,
),
child: WalletSheetCard(
child: SimpleWalletCard(
walletId: widget.walletId,
popPrevious: true,
),
@ -122,7 +128,7 @@ class _MasterWalletCardState extends ConsumerState<MasterWalletCard> {
right: 7,
bottom: 7,
),
child: WalletSheetCard(
child: SimpleWalletCard(
walletId: widget.walletId,
contractAddress: e,
popPrevious: true,

View file

@ -21,8 +21,8 @@ import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:stackwallet/widgets/wallet_info_row/wallet_info_row.dart';
import 'package:tuple/tuple.dart';
class WalletSheetCard extends ConsumerWidget {
const WalletSheetCard({
class SimpleWalletCard extends ConsumerWidget {
const SimpleWalletCard({
Key? key,
required this.walletId,
this.contractAddress,
@ -113,7 +113,7 @@ class WalletSheetCard extends ConsumerWidget {
child: MaterialButton(
// splashColor: Theme.of(context).extension<StackColors>()!.highlight,
key: Key("walletsSheetItemButtonKey_$walletId"),
padding: const EdgeInsets.all(5),
padding: const EdgeInsets.all(10),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(

View file

@ -8,7 +8,7 @@ import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
import 'package:stackwallet/widgets/wallet_info_row/sub_widgets/wallet_info_row_balance_future.dart';
import 'package:stackwallet/widgets/wallet_info_row/sub_widgets/wallet_info_row_balance.dart';
import 'package:stackwallet/widgets/wallet_info_row/sub_widgets/wallet_info_row_coin_icon.dart';
class WalletInfoRow extends ConsumerWidget {

View file

@ -84,7 +84,7 @@ void main() {
),
home: mockingjay.MockNavigatorProvider(
navigator: navigator,
child: const WalletSheetCard(
child: const SimpleWalletCard(
walletId: "wallet id",
)),
),
@ -122,7 +122,7 @@ void main() {
mockito.when(wallets.getManagerProvider("wallet id")).thenAnswer(
(realInvocation) => ChangeNotifierProvider((ref) => manager));
const walletSheetCard = WalletSheetCard(
const walletSheetCard = SimpleWalletCard(
walletId: "wallet id",
);

View file

@ -15,7 +15,7 @@ import 'package:stackwallet/utilities/amount/amount.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/theme/light_colors.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/wallet_info_row/sub_widgets/wallet_info_row_balance_future.dart';
import 'package:stackwallet/widgets/wallet_info_row/sub_widgets/wallet_info_row_balance.dart';
import 'wallet_info_row_balance_future_test.mocks.dart';

View file

@ -15,7 +15,7 @@ import 'package:stackwallet/utilities/amount/amount.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/theme/light_colors.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/wallet_info_row/sub_widgets/wallet_info_row_balance_future.dart';
import 'package:stackwallet/widgets/wallet_info_row/sub_widgets/wallet_info_row_balance.dart';
import 'package:stackwallet/widgets/wallet_info_row/wallet_info_row.dart';
import 'wallet_info_row_test.mocks.dart';