Merge pull request #454 from cypherstack/ui-fixes

Desktop address list gui
This commit is contained in:
Diego Salazar 2023-04-03 10:39:28 -06:00 committed by GitHub
commit a715c20236
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 1132 additions and 411 deletions

View file

@ -9,6 +9,7 @@ import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.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/stack_text_field.dart';
import 'package:stackwallet/widgets/textfield_icon_button.dart';
@ -186,10 +187,31 @@ class _SingleFieldEditViewState extends State<SingleFieldEditView> {
condition: isDesktop,
builder: (child) => Padding(
padding: const EdgeInsets.all(32),
child: child,
child: Row(
children: [
Expanded(
child: SecondaryButton(
label: "Cancel",
buttonHeight: ButtonHeight.l,
onPressed: () {
if (mounted) {
Navigator.of(context).pop();
}
},
),
),
const SizedBox(
width: 16,
),
Expanded(
child: child,
),
],
),
),
child: PrimaryButton(
label: "Save",
buttonHeight: isDesktop ? ButtonHeight.l : null,
onPressed: () {
if (mounted) {
Navigator.of(context).pop(_textController.text);

View file

@ -1,13 +1,17 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:isar/isar.dart';
import 'package:stackwallet/db/main_db.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart';
import 'package:stackwallet/pages/receive_view/addresses/address_tag.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/clipboard_interface.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
class AddressCard extends StatefulWidget {
@ -31,6 +35,8 @@ class AddressCard extends StatefulWidget {
}
class _AddressCardState extends State<AddressCard> {
final isDesktop = Util.isDesktop;
late Stream<AddressLabel?> stream;
late final Address address;
@ -74,115 +80,64 @@ class _AddressCardState extends State<AddressCard> {
label = snapshot.data!;
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (label!.value.isNotEmpty)
Text(
label!.value,
style: STextStyles.itemSubtitle(context),
textAlign: TextAlign.left,
return ConditionalParent(
condition: isDesktop,
builder: (child) => Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SvgPicture.asset(
Assets.svg.iconFor(coin: widget.coin),
width: 32,
height: 32,
),
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
//
// CustomTextButton(
// text: "Edit label",
// textSize: 14,
// onTap: () {
// Navigator.of(context).pushNamed(
// EditAddressLabelView.routeName,
// arguments: label!.id,
// );
// },
// ),
// ],
// ),
if (label!.value.isNotEmpty)
const SizedBox(
height: 8,
width: 12,
),
Row(
children: [
Expanded(
child: Text(
address.value,
style: STextStyles.itemSubtitle12(context),
),
Expanded(
child: child,
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (label!.value.isNotEmpty)
Text(
label!.value,
style: STextStyles.itemSubtitle(context),
textAlign: TextAlign.left,
),
],
),
const SizedBox(
height: 10,
),
if (label!.tags != null && label!.tags!.isNotEmpty)
Wrap(
spacing: 10,
runSpacing: 10,
children: label!.tags!
.map(
(e) => AddressTag(
tag: e,
),
)
.toList(),
if (label!.value.isNotEmpty)
SizedBox(
height: isDesktop ? 2 : 8,
),
Row(
children: [
Expanded(
child: Text(
address.value,
style: STextStyles.itemSubtitle12(context),
),
),
],
),
// Row(
// children: [
// Expanded(
// child: SecondaryButton(
// label: "Copy address",
// icon: CopyIcon(
// color: Theme.of(context)
// .extension<StackColors>()!
// .buttonTextSecondary,
// ),
// onPressed: () async {
// await widget.clipboard.setData(
// ClipboardData(
// text: address.value,
// ),
// );
// if (mounted) {
// unawaited(
// showFloatingFlushBar(
// type: FlushBarType.info,
// message: "Copied to clipboard",
// context: context,
// ),
// );
// }
// },
// ),
// ),
// const SizedBox(
// width: 12,
// ),
// Expanded(
// child: SecondaryButton(
// label: "Show QR Code",
// icon: QrCodeIcon(
// color: Theme.of(context)
// .extension<StackColors>()!
// .buttonTextSecondary,
// ),
// onPressed: () {
// showDialog<void>(
// context: context,
// builder: (context) => AddressQrPopup(
// addressString: address.value,
// coin: widget.coin,
// clipboard: widget.clipboard,
// ),
// );
// },
// ),
// ),
// ],
// )
],
const SizedBox(
height: 10,
),
if (label!.tags != null && label!.tags!.isNotEmpty)
Wrap(
spacing: 10,
runSpacing: 10,
children: label!.tags!
.map(
(e) => AddressTag(
tag: e,
),
)
.toList(),
),
],
),
);
},
),

View file

@ -6,6 +6,7 @@ import 'package:stackwallet/db/main_db.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart';
import 'package:stackwallet/pages/receive_view/addresses/address_tag.dart';
import 'package:stackwallet/pages/wallet_view/sub_widgets/no_transactions_found.dart';
import 'package:stackwallet/pages/wallet_view/transaction_views/transaction_details_view.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/utilities/address_utils.dart';
import 'package:stackwallet/utilities/text_styles.dart';
@ -14,8 +15,11 @@ 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/custom_buttons/blue_text_button.dart';
import 'package:stackwallet/widgets/custom_buttons/simple_copy_button.dart';
import 'package:stackwallet/widgets/custom_buttons/simple_edit_button.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:stackwallet/widgets/transaction_card.dart';
@ -44,6 +48,64 @@ class _AddressDetailsViewState extends ConsumerState<AddressDetailsView> {
AddressLabel? label;
void _showDesktopAddressQrCode() {
showDialog<void>(
context: context,
builder: (context) => DesktopDialog(
maxWidth: 480,
maxHeight: 400,
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: const EdgeInsets.only(left: 32),
child: Text(
"Address QR code",
style: STextStyles.desktopH3(context),
),
),
const DesktopDialogCloseButton(),
],
),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Center(
child: RepaintBoundary(
key: _qrKey,
child: QrImage(
data: AddressUtils.buildUriString(
ref.watch(walletsChangeNotifierProvider.select(
(value) =>
value.getManager(widget.walletId).coin)),
address.value,
{},
),
size: 220,
backgroundColor:
Theme.of(context).extension<StackColors>()!.popupBG,
foregroundColor: Theme.of(context)
.extension<StackColors>()!
.accentColorDark,
),
),
),
],
),
),
const SizedBox(
height: 32,
),
],
),
),
);
}
@override
void initState() {
address = MainDB.instance.isar.addresses
@ -73,13 +135,6 @@ class _AddressDetailsViewState extends ConsumerState<AddressDetailsView> {
@override
Widget build(BuildContext context) {
final coin = ref.watch(walletsChangeNotifierProvider
.select((value) => value.getManager(widget.walletId).coin));
final query = MainDB.instance
.getTransactions(widget.walletId)
.filter()
.address((q) => q.valueEqualTo(address.value));
return ConditionalParent(
condition: !isDesktop,
builder: (child) => Background(
@ -96,7 +151,7 @@ class _AddressDetailsViewState extends ConsumerState<AddressDetailsView> {
),
titleSpacing: 0,
title: Text(
"Wallet addresses",
"Address details",
style: STextStyles.navBarTitle(context),
),
),
@ -126,114 +181,198 @@ class _AddressDetailsViewState extends ConsumerState<AddressDetailsView> {
label = snapshot.data!;
}
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Center(
child: RepaintBoundary(
key: _qrKey,
child: QrImage(
data: AddressUtils.buildUriString(
coin,
address.value,
{},
),
size: 220,
backgroundColor:
Theme.of(context).extension<StackColors>()!.background,
foregroundColor: Theme.of(context)
.extension<StackColors>()!
.accentColorDark,
return ConditionalParent(
condition: isDesktop,
builder: (child) {
return Column(
children: [
const SizedBox(
height: 20,
),
),
),
const SizedBox(
height: 16,
),
_Item(
title: "Address",
data: address.value,
button: SimpleCopyButton(
data: address.value,
),
),
const SizedBox(
height: 12,
),
_Item(
title: "Label",
data: label!.value,
button: SimpleEditButton(
editValue: label!.value,
editLabel: 'label',
onValueChanged: (value) {
MainDB.instance.putAddressLabel(
label!.copyWith(
label: value,
RoundedWhiteContainer(
padding: const EdgeInsets.all(24),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Address details",
style: STextStyles.desktopTextExtraExtraSmall(
context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
),
CustomTextButton(
text: "View QR code",
onTap: _showDesktopAddressQrCode,
),
],
),
const SizedBox(
height: 4,
),
RoundedWhiteContainer(
padding: EdgeInsets.zero,
borderColor: Theme.of(context)
.extension<StackColors>()!
.backgroundAppBar,
child: child,
),
const SizedBox(
height: 16,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Transaction history",
style: STextStyles.desktopTextExtraExtraSmall(
context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
),
],
),
const SizedBox(
height: 8,
),
RoundedWhiteContainer(
padding: EdgeInsets.zero,
borderColor: Theme.of(context)
.extension<StackColors>()!
.backgroundAppBar,
child: _AddressDetailsTxList(
walletId: widget.walletId,
address: address,
),
),
],
),
),
],
);
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
if (!isDesktop)
Center(
child: RepaintBoundary(
key: _qrKey,
child: QrImage(
data: AddressUtils.buildUriString(
ref.watch(walletsChangeNotifierProvider.select(
(value) =>
value.getManager(widget.walletId).coin)),
address.value,
{},
),
size: 220,
backgroundColor: Theme.of(context)
.extension<StackColors>()!
.background,
foregroundColor: Theme.of(context)
.extension<StackColors>()!
.accentColorDark,
),
);
},
),
),
if (!isDesktop)
const SizedBox(
height: 16,
),
_Item(
title: "Address",
data: address.value,
button: isDesktop
? IconCopyButton(
data: address.value,
)
: SimpleCopyButton(
data: address.value,
),
),
),
const SizedBox(
height: 12,
),
_Tags(
tags: label!.tags,
),
if (address.derivationPath != null)
const SizedBox(
const _Div(
height: 12,
),
if (address.derivationPath != null)
_Item(
title: "Derivation path",
data: address.derivationPath!.value,
button: Container(),
),
const SizedBox(
height: 12,
),
_Item(
title: "Type",
data: address.type.readableName,
button: Container(),
),
const SizedBox(
height: 12,
),
_Item(
title: "Sub type",
data: address.subType.prettyName,
button: Container(),
),
const SizedBox(
height: 20,
),
Text(
"Transactions",
textAlign: TextAlign.left,
style: STextStyles.itemSubtitle(context).copyWith(
color: Theme.of(context).extension<StackColors>()!.textDark3,
),
),
const SizedBox(
height: 12,
),
if (query.countSync() == 0) const NoTransActionsFound(),
if (query.countSync() > 0)
RoundedWhiteContainer(
padding: EdgeInsets.zero,
child: Column(
mainAxisSize: MainAxisSize.min,
children: query
.findAllSync()
.map((e) => TransactionCard(
transaction: e, walletId: widget.walletId))
.toList(),
title: "Label",
data: label!.value,
button: SimpleEditButton(
editValue: label!.value,
editLabel: 'label',
onValueChanged: (value) {
MainDB.instance.putAddressLabel(
label!.copyWith(
label: value,
),
);
},
),
),
],
const _Div(
height: 12,
),
_Tags(
tags: label!.tags,
),
if (address.derivationPath != null)
const _Div(
height: 12,
),
if (address.derivationPath != null)
_Item(
title: "Derivation path",
data: address.derivationPath!.value,
button: Container(),
),
const _Div(
height: 12,
),
_Item(
title: "Type",
data: address.type.readableName,
button: Container(),
),
const _Div(
height: 12,
),
_Item(
title: "Sub type",
data: address.subType.prettyName,
button: Container(),
),
if (!isDesktop)
const SizedBox(
height: 20,
),
if (!isDesktop)
Text(
"Transactions",
textAlign: TextAlign.left,
style: STextStyles.itemSubtitle(context).copyWith(
color:
Theme.of(context).extension<StackColors>()!.textDark3,
),
),
if (!isDesktop)
const SizedBox(
height: 12,
),
if (!isDesktop)
_AddressDetailsTxList(
walletId: widget.walletId,
address: address,
),
],
),
);
},
),
@ -241,6 +380,85 @@ class _AddressDetailsViewState extends ConsumerState<AddressDetailsView> {
}
}
class _AddressDetailsTxList extends StatelessWidget {
const _AddressDetailsTxList({
Key? key,
required this.walletId,
required this.address,
}) : super(key: key);
final String walletId;
final Address address;
@override
Widget build(BuildContext context) {
final query = MainDB.instance
.getTransactions(walletId)
.filter()
.address((q) => q.valueEqualTo(address.value));
final count = query.countSync();
if (count > 0) {
if (Util.isDesktop) {
final txns = query.findAllSync();
return ListView.separated(
shrinkWrap: true,
primary: false,
itemBuilder: (_, index) => TransactionCard(
transaction: txns[index],
walletId: walletId,
),
separatorBuilder: (_, __) => const _Div(height: 1),
itemCount: count,
);
} else {
return RoundedWhiteContainer(
padding: EdgeInsets.zero,
child: Column(
mainAxisSize: MainAxisSize.min,
children: query
.findAllSync()
.map(
(e) => TransactionCard(
transaction: e,
walletId: walletId,
),
)
.toList(),
),
);
}
} else {
return const NoTransActionsFound();
}
}
}
class _Div extends StatelessWidget {
const _Div({
Key? key,
required this.height,
}) : super(key: key);
final double height;
@override
Widget build(BuildContext context) {
if (Util.isDesktop) {
return Container(
color: Theme.of(context).extension<StackColors>()!.backgroundAppBar,
height: 1,
width: double.infinity,
);
} else {
return SizedBox(
height: height,
);
}
}
}
class _Tags extends StatelessWidget {
const _Tags({
Key? key,
@ -313,37 +531,48 @@ class _Item extends StatelessWidget {
@override
Widget build(BuildContext context) {
return RoundedWhiteContainer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
title,
style: STextStyles.itemSubtitle(context),
),
button,
],
),
const SizedBox(
height: 5,
),
data.isNotEmpty
? SelectableText(
data,
style: STextStyles.w500_14(context),
)
: Text(
"$title will appear here",
style: STextStyles.w500_14(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle3,
),
return ConditionalParent(
condition: !Util.isDesktop,
builder: (child) => RoundedWhiteContainer(
child: child,
),
child: ConditionalParent(
condition: Util.isDesktop,
builder: (child) => Padding(
padding: const EdgeInsets.all(16),
child: child,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
title,
style: STextStyles.itemSubtitle(context),
),
],
button,
],
),
const SizedBox(
height: 5,
),
data.isNotEmpty
? SelectableText(
data,
style: STextStyles.w500_14(context),
)
: Text(
"$title will appear here",
style: STextStyles.w500_14(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle3,
),
),
],
),
),
);
}

View file

@ -0,0 +1,149 @@
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/isar_models.dart';
import 'package:stackwallet/pages/receive_view/addresses/address_details_view.dart';
import 'package:stackwallet/pages_desktop_specific/addresses/sub_widgets/desktop_address_list.dart';
import 'package:stackwallet/utilities/assets.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_app_bar.dart';
import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
final desktopSelectedAddressId = StateProvider.autoDispose<Id?>((ref) => null);
class DesktopWalletAddressesView extends ConsumerStatefulWidget {
const DesktopWalletAddressesView({
Key? key,
required this.walletId,
}) : super(key: key);
static const String routeName = "/desktopWalletAddressesView";
final String walletId;
@override
ConsumerState<DesktopWalletAddressesView> createState() =>
_DesktopWalletAddressesViewState();
}
class _DesktopWalletAddressesViewState
extends ConsumerState<DesktopWalletAddressesView> {
static const _headerHeight = 70.0;
static const _columnWidth0 = 489.0;
late final Stream<void> addressCollectionWatcher;
void _onAddressCollectionWatcherEvent() {
WidgetsBinding.instance.addPostFrameCallback((_) {
setState(() {});
});
}
@override
void initState() {
addressCollectionWatcher =
MainDB.instance.isar.addresses.watchLazy(fireImmediately: true);
addressCollectionWatcher.listen((_) => _onAddressCollectionWatcherEvent());
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return DesktopScaffold(
appBar: DesktopAppBar(
background: Theme.of(context).extension<StackColors>()!.popupBG,
leading: Expanded(
child: Row(
children: [
const SizedBox(
width: 32,
),
AppBarIconButton(
size: 32,
color: Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultBG,
shadows: const [],
icon: SvgPicture.asset(
Assets.svg.arrowLeft,
width: 18,
height: 18,
color: Theme.of(context)
.extension<StackColors>()!
.topNavIconPrimary,
),
onPressed: Navigator.of(context).pop,
),
const SizedBox(
width: 12,
),
Text(
"Address list",
style: STextStyles.desktopH3(context),
),
const Spacer(),
],
),
),
useSpacers: false,
isCompactHeight: true,
),
body: Padding(
padding: const EdgeInsets.all(24),
child: Column(
children: [
Expanded(
child: Row(
children: [
SizedBox(
width: _columnWidth0,
child: DesktopAddressList(
searchHeight: _headerHeight,
walletId: widget.walletId,
),
),
const SizedBox(
width: 16,
),
Expanded(
child: Column(
children: [
const SizedBox(
height: _headerHeight,
),
if (ref.watch(desktopSelectedAddressId.state).state !=
null)
Expanded(
child: SingleChildScrollView(
child: AddressDetailsView(
key: Key(
"currentDesktopAddressDetails_key_${ref.watch(desktopSelectedAddressId.state).state}"),
walletId: widget.walletId,
addressId: ref
.watch(desktopSelectedAddressId.state)
.state!,
),
),
),
],
),
),
],
),
),
],
),
),
);
}
}

View file

@ -0,0 +1,232 @@
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/isar_models.dart';
import 'package:stackwallet/pages/receive_view/addresses/address_card.dart';
import 'package:stackwallet/pages_desktop_specific/addresses/desktop_wallet_addresses_view.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/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/utilities/util.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';
class DesktopAddressList extends ConsumerStatefulWidget {
const DesktopAddressList({
Key? key,
required this.walletId,
this.searchHeight,
}) : super(key: key);
final String walletId;
final double? searchHeight;
@override
ConsumerState<DesktopAddressList> createState() => _DesktopAddressListState();
}
class _DesktopAddressListState extends ConsumerState<DesktopAddressList> {
final bool isDesktop = Util.isDesktop;
String _searchString = "";
late final TextEditingController _searchController;
final searchFieldFocusNode = FocusNode();
List<Id> _search(String term) {
if (term.isEmpty) {
return MainDB.instance
.getAddresses(widget.walletId)
.filter()
.group((q) => q
.subTypeEqualTo(AddressSubType.change)
.or()
.subTypeEqualTo(AddressSubType.receiving)
.or()
.subTypeEqualTo(AddressSubType.paynymReceive)
.or()
.subTypeEqualTo(AddressSubType.paynymNotification))
.and()
.not()
.typeEqualTo(AddressType.nonWallet)
.sortByDerivationIndex()
.idProperty()
.findAllSync();
}
final labels = MainDB.instance
.getAddressLabels(widget.walletId)
.filter()
.group(
(q) => q
.valueContains(term, caseSensitive: false)
.or()
.addressStringContains(term, caseSensitive: false)
.or()
.group(
(q) => q
.tagsIsNotNull()
.and()
.tagsElementContains(term, caseSensitive: false),
),
)
.findAllSync();
if (labels.isEmpty) {
return [];
}
return MainDB.instance
.getAddresses(widget.walletId)
.filter()
.anyOf<AddressLabel, Address>(
labels, (q, e) => q.valueEqualTo(e.addressString))
.group((q) => q
.subTypeEqualTo(AddressSubType.change)
.or()
.subTypeEqualTo(AddressSubType.receiving)
.or()
.subTypeEqualTo(AddressSubType.paynymReceive)
.or()
.subTypeEqualTo(AddressSubType.paynymNotification))
.and()
.not()
.typeEqualTo(AddressType.nonWallet)
.sortByDerivationIndex()
.idProperty()
.findAllSync();
}
@override
void initState() {
_searchController = TextEditingController();
super.initState();
}
@override
void dispose() {
_searchController.dispose();
searchFieldFocusNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final coin = ref.watch(walletsChangeNotifierProvider
.select((value) => value.getManager(widget.walletId).coin));
final ids = _search(_searchString);
return Column(
children: [
SizedBox(
height: widget.searchHeight!,
child: Center(
child: 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: 20,
),
Expanded(
child: SingleChildScrollView(
child: RoundedWhiteContainer(
padding: EdgeInsets.zero,
child: ListView.separated(
shrinkWrap: true,
itemCount: ids.length,
separatorBuilder: (_, __) => Container(
height: 1,
color: Theme.of(context)
.extension<StackColors>()!
.backgroundAppBar,
),
itemBuilder: (_, index) => Padding(
padding: const EdgeInsets.all(4),
child: AddressCard(
key: Key("addressCardDesktop_key_${ids[index]}"),
walletId: widget.walletId,
addressId: ids[index],
coin: coin,
onPressed: () {
ref.read(desktopSelectedAddressId.state).state =
ids[index];
},
),
),
),
),
),
),
],
);
}
}

View file

@ -4,13 +4,13 @@ import 'package:event_bus/event_bus.dart';
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/wallet_view/sub_widgets/delete_wallet_button.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_features.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_summary.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/my_wallet.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/network_info_button.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/recent_desktop_transactions.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_keys_button.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_options_button.dart';
import 'package:stackwallet/providers/global/auto_swb_service_provider.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/ui/transaction_filter_provider.dart';
@ -208,7 +208,7 @@ class _DesktopWalletViewState extends ConsumerState<DesktopWalletView> {
const SizedBox(
width: 2,
),
DeleteWalletButton(
WalletOptionsButton(
walletId: widget.walletId,
),
const SizedBox(

View file

@ -1,164 +0,0 @@
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/wallet_view/sub_widgets/desktop_delete_wallet_dialog.dart';
import 'package:stackwallet/route_generator.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';
class DeleteWalletButton extends ConsumerStatefulWidget {
const DeleteWalletButton({
Key? key,
required this.walletId,
}) : super(key: key);
final String walletId;
@override
ConsumerState<DeleteWalletButton> createState() => _DeleteWalletButton();
}
class _DeleteWalletButton extends ConsumerState<DeleteWalletButton> {
late final String walletId;
@override
void initState() {
walletId = widget.walletId;
super.initState();
}
@override
Widget build(BuildContext context) {
return RawMaterialButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(1000),
),
onPressed: () async {
final shouldOpenDeleteDialog = await showDialog<bool?>(
context: context,
barrierColor: Colors.transparent,
builder: (context) {
return DeletePopupButton(
onTap: () async {
Navigator.of(context).pop(true);
},
);
},
);
if (shouldOpenDeleteDialog == true) {
final result = await showDialog<bool?>(
context: context,
barrierDismissible: false,
builder: (context) => Navigator(
initialRoute: DesktopDeleteWalletDialog.routeName,
onGenerateRoute: RouteGenerator.generateRoute,
onGenerateInitialRoutes: (_, __) {
return [
RouteGenerator.generateRoute(
RouteSettings(
name: DesktopDeleteWalletDialog.routeName,
arguments: walletId,
),
),
];
},
),
);
if (result == true) {
if (mounted) {
Navigator.of(context).pop();
}
}
}
},
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 19,
horizontal: 32,
),
child: Row(
children: [
SvgPicture.asset(
Assets.svg.ellipsis,
width: 20,
height: 20,
color: Theme.of(context)
.extension<StackColors>()!
.buttonTextSecondary,
),
],
),
),
);
}
}
class DeletePopupButton extends StatefulWidget {
const DeletePopupButton({
Key? key,
this.onTap,
}) : super(key: key);
final VoidCallback? onTap;
@override
State<DeletePopupButton> createState() => _DeletePopupButtonState();
}
class _DeletePopupButtonState extends State<DeletePopupButton> {
@override
Widget build(BuildContext context) {
return Stack(
children: [
Positioned(
top: 24,
left: MediaQuery.of(context).size.width - 234,
child: MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: widget.onTap,
child: Container(
width: 210,
height: 70,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius * 2,
),
color: Theme.of(context).extension<StackColors>()!.popupBG,
boxShadow: [
Theme.of(context)
.extension<StackColors>()!
.standardBoxShadow,
],
),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
const SizedBox(width: 24),
SvgPicture.asset(
Assets.svg.trash,
),
const SizedBox(width: 14),
Text(
"Delete wallet",
style: STextStyles.desktopTextExtraExtraSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark),
),
],
),
),
),
),
),
],
);
}
}

View file

@ -0,0 +1,275 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/pages_desktop_specific/addresses/desktop_wallet_addresses_view.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_delete_wallet_dialog.dart';
import 'package:stackwallet/route_generator.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';
enum _WalletOptions {
addressList,
deleteWallet;
String get prettyName {
switch (this) {
case _WalletOptions.addressList:
return "Address list";
case _WalletOptions.deleteWallet:
return "Delete wallet";
}
}
}
class WalletOptionsButton extends ConsumerStatefulWidget {
const WalletOptionsButton({
Key? key,
required this.walletId,
}) : super(key: key);
final String walletId;
@override
ConsumerState<WalletOptionsButton> createState() =>
_WalletOptionsButtonState();
}
class _WalletOptionsButtonState extends ConsumerState<WalletOptionsButton> {
late final String walletId;
@override
void initState() {
walletId = widget.walletId;
super.initState();
}
@override
Widget build(BuildContext context) {
return RawMaterialButton(
constraints: const BoxConstraints(
minHeight: 32,
minWidth: 32,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(1000),
),
onPressed: () async {
final func = await showDialog<_WalletOptions?>(
context: context,
barrierColor: Colors.transparent,
builder: (context) {
return WalletOptionsPopupMenu(
onDeletePressed: () async {
Navigator.of(context).pop(_WalletOptions.deleteWallet);
},
onAddressListPressed: () async {
Navigator.of(context).pop(_WalletOptions.addressList);
},
);
},
);
if (mounted && func != null) {
switch (func) {
case _WalletOptions.addressList:
unawaited(
Navigator.of(context).pushNamed(
DesktopWalletAddressesView.routeName,
arguments: walletId,
),
);
break;
case _WalletOptions.deleteWallet:
final result = await showDialog<bool?>(
context: context,
barrierDismissible: false,
builder: (context) => Navigator(
initialRoute: DesktopDeleteWalletDialog.routeName,
onGenerateRoute: RouteGenerator.generateRoute,
onGenerateInitialRoutes: (_, __) {
return [
RouteGenerator.generateRoute(
RouteSettings(
name: DesktopDeleteWalletDialog.routeName,
arguments: walletId,
),
),
];
},
),
);
if (result == true) {
if (mounted) {
Navigator.of(context).pop();
}
}
break;
}
}
},
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 19,
horizontal: 32,
),
child: Row(
children: [
SvgPicture.asset(
Assets.svg.ellipsis,
width: 20,
height: 20,
color: Theme.of(context)
.extension<StackColors>()!
.buttonTextSecondary,
),
],
),
),
);
}
}
class WalletOptionsPopupMenu extends StatelessWidget {
const WalletOptionsPopupMenu({
Key? key,
required this.onDeletePressed,
required this.onAddressListPressed,
}) : super(key: key);
final VoidCallback onDeletePressed;
final VoidCallback onAddressListPressed;
@override
Widget build(BuildContext context) {
return Stack(
children: [
Positioned(
top: 24,
left: MediaQuery.of(context).size.width - 234,
child: Container(
width: 220,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius * 2,
),
color: Theme.of(context).extension<StackColors>()!.popupBG,
boxShadow: [
Theme.of(context).extension<StackColors>()!.standardBoxShadow,
],
),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TransparentButton(
onPressed: onAddressListPressed,
child: Padding(
padding: const EdgeInsets.all(8),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
SvgPicture.asset(
Assets.svg.list,
width: 20,
height: 20,
color: Theme.of(context)
.extension<StackColors>()!
.textFieldActiveSearchIconLeft,
),
const SizedBox(width: 14),
Expanded(
child: Text(
_WalletOptions.addressList.prettyName,
style: STextStyles.desktopTextExtraExtraSmall(
context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
),
),
),
],
),
),
),
const SizedBox(
height: 8,
),
TransparentButton(
onPressed: onDeletePressed,
child: Padding(
padding: const EdgeInsets.all(8),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
SvgPicture.asset(
Assets.svg.trash,
width: 20,
height: 20,
color: Theme.of(context)
.extension<StackColors>()!
.textFieldActiveSearchIconLeft,
),
const SizedBox(width: 14),
Expanded(
child: Text(
_WalletOptions.deleteWallet.prettyName,
style: STextStyles.desktopTextExtraExtraSmall(
context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
),
),
),
],
),
),
),
],
),
),
),
),
],
);
}
}
class TransparentButton extends StatelessWidget {
const TransparentButton({
Key? key,
required this.child,
this.onPressed,
}) : super(key: key);
final Widget child;
final VoidCallback? onPressed;
@override
Widget build(BuildContext context) {
return RawMaterialButton(
constraints: const BoxConstraints(
minHeight: 32,
minWidth: 32,
),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
onPressed: onPressed,
child: child,
);
}
}

View file

@ -272,6 +272,7 @@ class _ThemeToggle extends ConsumerState<ThemeToggle> {
child: SvgPicture.asset(
assetNameFor(ThemeType.values[i]),
height: 160,
width: 200,
),
),
const SizedBox(

View file

@ -99,6 +99,7 @@ import 'package:stackwallet/pages/wallet_view/transaction_views/transaction_sear
import 'package:stackwallet/pages/wallet_view/wallet_view.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';
import 'package:stackwallet/pages_desktop_specific/coin_control/desktop_coin_control_view.dart';
// import 'package:stackwallet/pages_desktop_specific/desktop_exchange/desktop_all_buys_view.dart';
import 'package:stackwallet/pages_desktop_specific/desktop_buy/desktop_buy_view.dart';
@ -1320,6 +1321,20 @@ class RouteGenerator {
}
return _routeError("${settings.name} invalid args: ${args.toString()}");
case DesktopWalletAddressesView.routeName:
if (args is String) {
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => DesktopWalletAddressesView(
walletId: args,
),
settings: RouteSettings(
name: settings.name,
),
);
}
return _routeError("${settings.name} invalid args: ${args.toString()}");
case DesktopCoinControlView.routeName:
if (args is String) {
return getRoute(

View file

@ -52,7 +52,7 @@ class SimpleEditButton extends StatelessWidget {
builder: (context) {
return DesktopDialog(
maxWidth: 580,
maxHeight: 360,
maxHeight: 300,
child: SingleFieldEditView(
initialValue: editValue!,
label: editLabel!,

View file

@ -31,6 +31,12 @@ class RoundedContainer extends StatelessWidget {
return ConditionalParent(
condition: onPressed != null,
builder: (child) => RawMaterialButton(
fillColor: color,
elevation: 0,
highlightElevation: 0,
disabledElevation: 0,
hoverElevation: 0,
focusElevation: 0,
padding: const EdgeInsets.all(0),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
shape: RoundedRectangleBorder(
@ -45,7 +51,7 @@ class RoundedContainer extends StatelessWidget {
width: width,
height: height,
decoration: BoxDecoration(
color: color,
color: onPressed != null ? Colors.transparent : color,
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius * radiusMultiplier,
),

View file

@ -337,6 +337,7 @@ flutter:
- assets/svg/forest-theme.svg
- assets/svg/chanstheme.svg
- assets/svg/darkChansTheme.svg
- assets/svg/orange-theme.svg
# light theme specific
- assets/svg/themed/light/