WIP desktop send view coin control

This commit is contained in:
julian 2023-03-15 12:01:10 -06:00
parent 8838f68524
commit 6c1d9d2912
9 changed files with 608 additions and 125 deletions

3
assets/svg/list-ul.svg Normal file
View file

@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0.832031 4.08279C0.832031 3.10291 1.62635 2.30859 2.60622 2.30859C3.5861 2.30859 4.38042 3.10291 4.38042 4.08279C4.38042 5.06229 3.5861 5.85698 2.60622 5.85698C1.62635 5.85698 0.832031 5.06229 0.832031 4.08279ZM17.9826 2.89999C18.6368 2.89999 19.1654 3.42966 19.1654 4.08279C19.1654 4.73702 18.6368 5.26558 17.9826 5.26558H7.33741C6.68317 5.26558 6.15461 4.73702 6.15461 4.08279C6.15461 3.42966 6.68317 2.89999 7.33741 2.89999H17.9826ZM17.9826 8.81397C18.6368 8.81397 19.1654 9.34253 19.1654 9.99677C19.1654 10.651 18.6368 11.1796 17.9826 11.1796H7.33741C6.68317 11.1796 6.15461 10.651 6.15461 9.99677C6.15461 9.34253 6.68317 8.81397 7.33741 8.81397H17.9826ZM17.9826 14.7279C18.6368 14.7279 19.1654 15.2565 19.1654 15.9107C19.1654 16.565 18.6368 17.0935 17.9826 17.0935H7.33741C6.68317 17.0935 6.15461 16.565 6.15461 15.9107C6.15461 15.2565 6.68317 14.7279 7.33741 14.7279H17.9826ZM0.832031 15.9107C0.832031 14.9312 1.62635 14.1365 2.60622 14.1365C3.5861 14.1365 4.38042 14.9312 4.38042 15.9107C4.38042 16.8902 3.5861 17.6849 2.60622 17.6849C1.62635 17.6849 0.832031 16.8902 0.832031 15.9107ZM4.38042 9.99677C4.38042 10.9763 3.5861 11.771 2.60622 11.771C1.62635 11.771 0.832031 10.9763 0.832031 9.99677C0.832031 9.01726 1.62635 8.22257 2.60622 8.22257C3.5861 8.22257 4.38042 9.01726 4.38042 9.99677Z" fill="#232323"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -1,9 +1,15 @@
import 'package:decimal/decimal.dart';
import 'package:flutter_native_splash/cli_commands.dart';
import 'package:isar/isar.dart';
import 'package:stackwallet/exceptions/main_db/main_db_exception.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/stack_file_system.dart';
import 'package:tuple/tuple.dart';
part 'queries/queries.dart';
class MainDB {
MainDB._();
static MainDB? _instance;

101
lib/db/queries/queries.dart Normal file
View file

@ -0,0 +1,101 @@
part of 'package:stackwallet/db/main_db.dart';
enum CCFilter {
all,
available,
frozen;
@override
String toString() {
if (this == all) {
return "Show $name outputs";
}
return "${name.capitalize()} outputs";
}
}
enum CCSortDescriptor {
age,
address,
value;
@override
String toString() {
return name.capitalize();
}
}
extension MainDBQueries on MainDB {
List<Id> queryUTXOsSync({
required String walletId,
required CCFilter filter,
required CCSortDescriptor sort,
required String searchTerm,
required Coin coin,
}) {
var preSort = getUTXOs(walletId).filter().group((q) {
final qq = q.group(
(q) => q.usedIsNull().or().usedEqualTo(false),
);
switch (filter) {
case CCFilter.frozen:
return qq.and().isBlockedEqualTo(true);
case CCFilter.available:
return qq.and().isBlockedEqualTo(false);
case CCFilter.all:
return qq;
}
});
if (searchTerm.isNotEmpty) {
preSort = preSort.and().group(
(q) {
var qq = q.addressContains(searchTerm, caseSensitive: false);
qq = qq.or().nameContains(searchTerm, caseSensitive: false);
qq = qq.or().group(
(q) => q
.isBlockedEqualTo(true)
.and()
.blockedReasonContains(searchTerm, caseSensitive: false),
);
qq = qq.or().txidContains(searchTerm, caseSensitive: false);
qq = qq.or().blockHashContains(searchTerm, caseSensitive: false);
final maybeDecimal = Decimal.tryParse(searchTerm);
if (maybeDecimal != null) {
qq = qq.or().valueEqualTo(
Format.decimalAmountToSatoshis(
maybeDecimal,
coin,
),
);
}
final maybeInt = int.tryParse(searchTerm);
if (maybeInt != null) {
qq = qq.or().valueEqualTo(maybeInt);
}
return qq;
},
);
}
final List<Id> ids;
switch (sort) {
case CCSortDescriptor.age:
ids = preSort.sortByBlockHeight().idProperty().findAllSync();
break;
case CCSortDescriptor.address:
ids = preSort.sortByAddress().idProperty().findAllSync();
break;
case CCSortDescriptor.value:
ids = preSort.sortByValueDesc().idProperty().findAllSync();
break;
}
return ids;
}
}

View file

@ -0,0 +1,365 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:isar/isar.dart';
import 'package:stackwallet/db/main_db.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/utxo.dart';
import 'package:stackwallet/pages_desktop_specific/coin_control/utxo_row.dart';
import 'package:stackwallet/providers/global/wallets_provider.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/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
import 'package:stackwallet/widgets/rounded_container.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
import 'package:stackwallet/widgets/textfield_icon_button.dart';
import 'package:stackwallet/widgets/toggle.dart';
final desktopUseUTXOs = StateProvider.autoDispose((ref) => <UTXO>{});
class DesktopCoinControlUseDialog extends ConsumerStatefulWidget {
const DesktopCoinControlUseDialog({
Key? key,
required this.walletId,
}) : super(key: key);
final String walletId;
@override
ConsumerState<DesktopCoinControlUseDialog> createState() =>
_DesktopCoinControlUseDialogState();
}
class _DesktopCoinControlUseDialogState
extends ConsumerState<DesktopCoinControlUseDialog> {
late final TextEditingController _searchController;
late final Coin coin;
final searchFieldFocusNode = FocusNode();
final Set<UtxoRowData> _selectedUTXOs = {};
String _searchString = "";
CCFilter _filter = CCFilter.available;
CCSortDescriptor _sort = CCSortDescriptor.age;
@override
void initState() {
_searchController = TextEditingController();
coin = ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.coin;
super.initState();
}
@override
void dispose() {
_searchController.dispose();
searchFieldFocusNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final ids = MainDB.instance.queryUTXOsSync(
walletId: widget.walletId,
filter: _filter,
sort: _sort,
searchTerm: _searchString,
coin: coin,
);
return DesktopDialog(
maxWidth: 700,
maxHeight: MediaQuery.of(context).size.height - 128,
child: Column(
children: [
Row(
children: [
const AppBarBackButton(
size: 40,
iconSize: 24,
),
Text(
"Coin control",
style: STextStyles.desktopH3(context),
),
],
),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Column(
children: [
RoundedContainer(
color: Colors.transparent,
borderColor: Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultBG,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"This option allows you to control, freeze, and utilize "
"outputs at your discretion.",
style:
STextStyles.desktopTextExtraExtraSmall(context),
),
],
),
),
const SizedBox(
height: 16,
),
Row(
children: [
Expanded(
child: ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
autocorrect: false,
enableSuggestions: false,
controller: _searchController,
focusNode: searchFieldFocusNode,
onChanged: (value) {
setState(() {
_searchString = value;
});
},
style: STextStyles.desktopTextExtraSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textFieldActiveText,
height: 1.8,
),
decoration: standardInputDecoration(
"Search...",
searchFieldFocusNode,
context,
desktopMed: true,
).copyWith(
prefixIcon: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 18,
),
child: SvgPicture.asset(
Assets.svg.search,
width: 20,
height: 20,
),
),
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(
width: 16,
),
SizedBox(
height: 56,
width: 240,
child: Toggle(
isOn: _filter == CCFilter.frozen,
onColor: Theme.of(context)
.extension<StackColors>()!
.rateTypeToggleDesktopColorOn,
offColor: Theme.of(context)
.extension<StackColors>()!
.rateTypeToggleDesktopColorOff,
onIcon: Assets.svg.coinControl.unBlocked,
onText: "Available",
offIcon: Assets.svg.coinControl.blocked,
offText: "Frozen",
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
onValueChanged: (value) {
setState(() {
if (value) {
_filter = CCFilter.frozen;
} else {
_filter = CCFilter.available;
}
});
},
),
),
const SizedBox(
width: 16,
),
SizedBox(
height: 56,
width: 56,
child: TextButton(
style: Theme.of(context)
.extension<StackColors>()!
.getSecondaryEnabledButtonStyle(context)
?.copyWith(
shape: MaterialStateProperty.all(
RoundedRectangleBorder(
side: BorderSide(
color: Theme.of(context)
.extension<StackColors>()!
.buttonBackBorderSecondary,
width: 1,
),
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
),
),
onPressed: () {},
child: SvgPicture.asset(
Assets.svg.list,
width: 20,
height: 20,
),
),
),
],
),
const SizedBox(
height: 16,
),
Expanded(
child: ListView.separated(
shrinkWrap: true,
primary: false,
itemCount: ids.length,
separatorBuilder: (context, _) => const SizedBox(
height: 10,
),
itemBuilder: (context, index) {
final utxo = MainDB.instance.isar.utxos
.where()
.idEqualTo(ids[index])
.findFirstSync()!;
final data = UtxoRowData(utxo.id, false);
data.selected = _selectedUTXOs.contains(data);
return UtxoRow(
key: Key(
"${utxo.walletId}_${utxo.id}_${utxo.isBlocked}"),
data: data,
compact: true,
walletId: widget.walletId,
onSelectionChanged: (value) {
setState(() {
if (data.selected) {
_selectedUTXOs.add(value);
} else {
_selectedUTXOs.remove(value);
}
});
},
);
},
),
),
const SizedBox(
height: 16,
),
RoundedContainer(
color: Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultBG,
padding: const EdgeInsets.all(16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Selected amount",
style: STextStyles.desktopTextExtraExtraSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
),
),
Text(
"LOL",
style: STextStyles.desktopTextExtraExtraSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
),
),
],
),
),
const SizedBox(
height: 16,
),
Row(
children: [
Expanded(
child: SecondaryButton(
enabled: _selectedUTXOs.isNotEmpty,
buttonHeight: ButtonHeight.l,
label: _selectedUTXOs.isEmpty
? "Clear selection"
: "Clear selection (${_selectedUTXOs.length})",
onPressed: () => setState(() {
_selectedUTXOs.clear();
}),
),
),
const SizedBox(
width: 20,
),
Expanded(
child: PrimaryButton(
enabled: _selectedUTXOs.isNotEmpty,
buttonHeight: ButtonHeight.l,
label: "Use coins",
),
),
],
),
const SizedBox(
height: 16,
),
],
),
),
),
],
),
);
}
}

View file

@ -1,6 +1,4 @@
import 'package:decimal/decimal.dart';
import 'package:flutter/material.dart';
import 'package:flutter_native_splash/cli_commands.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:isar/isar.dart';
@ -12,7 +10,6 @@ import 'package:stackwallet/providers/global/wallets_provider.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/format.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
@ -24,32 +21,6 @@ import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
import 'package:stackwallet/widgets/textfield_icon_button.dart';
enum CCFilter {
all,
available,
frozen;
@override
String toString() {
if (this == all) {
return "Show $name outputs";
}
return "${name.capitalize()} outputs";
}
}
enum CCSortDescriptor {
age,
address,
value;
@override
String toString() {
return name.toString().capitalize();
}
}
class DesktopCoinControlView extends ConsumerStatefulWidget {
const DesktopCoinControlView({
Key? key,
@ -99,68 +70,13 @@ class _DesktopCoinControlViewState
Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType");
var preSort = MainDB.instance.getUTXOs(widget.walletId).filter().group((q) {
final qq = q.group(
(q) => q.usedIsNull().or().usedEqualTo(false),
);
switch (_filter) {
case CCFilter.frozen:
return qq.and().isBlockedEqualTo(true);
case CCFilter.available:
return qq.and().isBlockedEqualTo(false);
case CCFilter.all:
return qq;
}
});
if (_searchString.isNotEmpty) {
preSort = preSort.and().group(
(q) {
var qq = q.addressContains(_searchString, caseSensitive: false);
qq = qq.or().nameContains(_searchString, caseSensitive: false);
qq = qq.or().group(
(q) => q
.isBlockedEqualTo(true)
.and()
.blockedReasonContains(_searchString, caseSensitive: false),
);
qq = qq.or().txidContains(_searchString, caseSensitive: false);
qq = qq.or().blockHashContains(_searchString, caseSensitive: false);
final maybeDecimal = Decimal.tryParse(_searchString);
if (maybeDecimal != null) {
qq = qq.or().valueEqualTo(
Format.decimalAmountToSatoshis(
maybeDecimal,
coin,
),
);
}
final maybeInt = int.tryParse(_searchString);
if (maybeInt != null) {
qq = qq.or().valueEqualTo(maybeInt);
}
return qq;
},
);
}
final List<Id> ids;
switch (_sort) {
case CCSortDescriptor.age:
ids = preSort.sortByBlockHeight().idProperty().findAllSync();
break;
case CCSortDescriptor.address:
ids = preSort.sortByAddress().idProperty().findAllSync();
break;
case CCSortDescriptor.value:
ids = preSort.sortByValueDesc().idProperty().findAllSync();
break;
}
final ids = MainDB.instance.queryUTXOsSync(
walletId: widget.walletId,
filter: _filter,
sort: _sort,
searchTerm: _searchString,
coin: coin,
);
return DesktopScaffold(
appBar: DesktopAppBar(

View file

@ -9,9 +9,11 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/format.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/custom_buttons/blue_text_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/icon_widgets/utxo_status_icon.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:stackwallet/widgets/rounded_container.dart';
class UtxoRowData {
UtxoRowData(this.utxoId, this.selected);
@ -39,11 +41,13 @@ class UtxoRow extends ConsumerStatefulWidget {
required this.data,
required this.walletId,
this.onSelectionChanged,
this.compact = false,
}) : super(key: key);
final String walletId;
final UtxoRowData data;
final void Function(UtxoRowData)? onSelectionChanged;
final bool compact;
@override
ConsumerState<UtxoRow> createState() => _UtxoRowState();
@ -53,6 +57,16 @@ class _UtxoRowState extends ConsumerState<UtxoRow> {
late Stream<UTXO?> stream;
late UTXO utxo;
void _details() async {
await showDialog<String?>(
context: context,
builder: (context) => UtxoDetailsView(
utxoId: utxo.id,
walletId: widget.walletId,
),
);
}
@override
void initState() {
utxo = MainDB.instance.isar.utxos
@ -81,7 +95,11 @@ class _UtxoRowState extends ConsumerState<UtxoRow> {
utxo = snapshot.data!;
}
return RoundedWhiteContainer(
return RoundedContainer(
borderColor: widget.compact
? Theme.of(context).extension<StackColors>()!.textFieldDefaultBG
: null,
color: Theme.of(context).extension<StackColors>()!.popupBG,
boxShadow: widget.data.selected
? [
Theme.of(context).extension<StackColors>()!.standardBoxShadow,
@ -117,46 +135,70 @@ class _UtxoRowState extends ConsumerState<UtxoRow> {
const SizedBox(
width: 10,
),
Text(
"${Format.satoshisToAmount(
utxo.value,
coin: coin,
).toStringAsFixed(coin.decimals)} ${coin.ticker}",
textAlign: TextAlign.right,
style: STextStyles.w600_14(context),
),
const SizedBox(
width: 10,
),
if (!widget.compact)
Text(
"${Format.satoshisToAmount(
utxo.value,
coin: coin,
).toStringAsFixed(coin.decimals)} ${coin.ticker}",
textAlign: TextAlign.right,
style: STextStyles.w600_14(context),
),
if (!widget.compact)
const SizedBox(
width: 10,
),
Expanded(
flex: 13,
child: Text(
utxo.name.isNotEmpty ? utxo.name : utxo.address ?? utxo.txid,
textAlign: TextAlign.center,
style: STextStyles.w500_12(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
child: ConditionalParent(
condition: widget.compact,
builder: (child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
"${Format.satoshisToAmount(
utxo.value,
coin: coin,
).toStringAsFixed(coin.decimals)} ${coin.ticker}",
textAlign: TextAlign.right,
style: STextStyles.w600_14(context),
),
const SizedBox(
height: 2,
),
child,
],
);
},
child: Text(
utxo.name.isNotEmpty
? utxo.name
: utxo.address ?? utxo.txid,
textAlign:
widget.compact ? TextAlign.left : TextAlign.center,
style: STextStyles.w500_12(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
),
),
),
const SizedBox(
width: 10,
),
SecondaryButton(
width: 120,
buttonHeight: ButtonHeight.xs,
label: "Details",
onPressed: () async {
await showDialog<String?>(
context: context,
builder: (context) => UtxoDetailsView(
utxoId: utxo.id,
walletId: widget.walletId,
widget.compact
? CustomTextButton(
text: "Details",
onTap: _details,
)
: SecondaryButton(
width: 120,
buttonHeight: ButtonHeight.xs,
label: "Details",
onPressed: _details,
),
);
},
),
],
),
);

View file

@ -13,6 +13,7 @@ import 'package:stackwallet/models/send_view_auto_fill_data.dart';
import 'package:stackwallet/pages/send_view/confirm_transaction_view.dart';
import 'package:stackwallet/pages/send_view/sub_widgets/building_transaction_dialog.dart';
import 'package:stackwallet/pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart';
import 'package:stackwallet/pages_desktop_specific/coin_control/desktop_coin_control_use_dialog.dart';
import 'package:stackwallet/pages_desktop_specific/desktop_home_view.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/address_book_address_chooser/address_book_address_chooser.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_fee_dropdown.dart';
@ -44,6 +45,7 @@ import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/icon_widgets/addressbook_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/clipboard_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
import 'package:stackwallet/widgets/rounded_container.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
import 'package:stackwallet/widgets/textfield_icon_button.dart';
@ -679,6 +681,18 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
}
}
void _showDesktopCoinControl() async {
if (_amountToSend == null) {
//
}
final result = await showDialog(
context: context,
builder: (context) => DesktopCoinControlUseDialog(
walletId: widget.walletId,
),
);
}
@override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((_) {
@ -776,6 +790,17 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
});
}
final showCoinControl = ref.watch(
prefsChangeNotifierProvider.select(
(value) => value.enableCoinControl,
),
) &&
ref.watch(
provider.select(
(value) => value.hasCoinControlSupport,
),
);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -1052,6 +1077,29 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
),
),
),
if (showCoinControl)
const SizedBox(
height: 10,
),
if (showCoinControl)
RoundedContainer(
color: Colors.transparent,
borderColor:
Theme.of(context).extension<StackColors>()!.textFieldDefaultBG,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Coin control",
style: STextStyles.desktopTextExtraExtraSmall(context),
),
CustomTextButton(
text: "Select coins",
onTap: _showDesktopCoinControl,
),
],
),
),
const SizedBox(
height: 20,
),

View file

@ -236,6 +236,7 @@ class _SVG {
String get walletFa => "assets/svg/wallet-fa.svg";
String get exchange3 => "assets/svg/exchange-3.svg";
String get messageQuestion => "assets/svg/message-question-1.svg";
String get list => "assets/svg/list-ul.svg";
// TODO provide proper assets
String get bitcoinTestnet => "assets/svg/coin_icons/Bitcoin.svg";

View file

@ -313,6 +313,7 @@ flutter:
- assets/svg/box-auto.svg
- assets/svg/framed-address-book.svg
- assets/svg/framed-gear.svg
- assets/svg/list-ul.svg
# coin icons