mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2024-11-18 02:07:43 +00:00
commit
4fb35be79f
34 changed files with 2624 additions and 1897 deletions
11
assets/svg/configuration.svg
Normal file
11
assets/svg/configuration.svg
Normal file
|
@ -0,0 +1,11 @@
|
|||
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="48" height="48" rx="24" fill="#E0E3E3"/>
|
||||
<g clip-path="url(#clip0_5813_29086)">
|
||||
<path d="M13.5 30.5625C13.5 29.8365 14.0878 29.25 14.8125 29.25H17.0544C17.5605 28.0893 18.7172 27.2812 20.0625 27.2812C21.4078 27.2812 22.5275 28.0893 23.0689 29.25H33.1875C33.9135 29.25 34.5 29.8365 34.5 30.5625C34.5 31.2885 33.9135 31.875 33.1875 31.875H23.0689C22.5275 33.0357 21.4078 33.8438 20.0625 33.8438C18.7172 33.8438 17.5605 33.0357 17.0544 31.875H14.8125C14.0878 31.875 13.5 31.2885 13.5 30.5625ZM21.375 30.5625C21.375 29.8365 20.7885 29.25 20.0625 29.25C19.3365 29.25 18.75 29.8365 18.75 30.5625C18.75 31.2885 19.3365 31.875 20.0625 31.875C20.7885 31.875 21.375 31.2885 21.375 30.5625ZM27.9375 20.7188C29.2828 20.7188 30.4025 21.5268 30.9439 22.6875H33.1875C33.9135 22.6875 34.5 23.274 34.5 24C34.5 24.726 33.9135 25.3125 33.1875 25.3125H30.9439C30.4025 26.4732 29.2828 27.2812 27.9375 27.2812C26.5922 27.2812 25.4355 26.4732 24.9311 25.3125H14.8125C14.0878 25.3125 13.5 24.726 13.5 24C13.5 23.274 14.0878 22.6875 14.8125 22.6875H24.9311C25.4355 21.5268 26.5922 20.7188 27.9375 20.7188ZM29.25 24C29.25 23.274 28.6635 22.6875 27.9375 22.6875C27.2115 22.6875 26.625 23.274 26.625 24C26.625 24.726 27.2115 25.3125 27.9375 25.3125C28.6635 25.3125 29.25 24.726 29.25 24ZM33.1875 16.125C33.9135 16.125 34.5 16.7128 34.5 17.4375C34.5 18.1635 33.9135 18.75 33.1875 18.75H24.3814C23.84 19.9107 22.7203 20.7188 21.375 20.7188C20.0297 20.7188 18.873 19.9107 18.3686 18.75H14.8125C14.0878 18.75 13.5 18.1635 13.5 17.4375C13.5 16.7128 14.0878 16.125 14.8125 16.125H18.3686C18.873 14.9663 20.0297 14.1562 21.375 14.1562C22.7203 14.1562 23.84 14.9663 24.3814 16.125H33.1875ZM20.0625 17.4375C20.0625 18.1635 20.649 18.75 21.375 18.75C22.101 18.75 22.6875 18.1635 22.6875 17.4375C22.6875 16.7128 22.101 16.125 21.375 16.125C20.649 16.125 20.0625 16.7128 20.0625 17.4375Z" fill="black"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_5813_29086">
|
||||
<rect width="21" height="21" fill="white" transform="translate(13.5 13.5)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
|
@ -76,7 +76,7 @@ void main() async {
|
|||
|
||||
if (Util.isDesktop) {
|
||||
setWindowTitle('Stack Wallet');
|
||||
setWindowMinSize(const Size(1200, 900));
|
||||
setWindowMinSize(const Size(1200, 1100));
|
||||
setWindowMaxSize(Size.infinite);
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,9 @@ 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/address_book_card.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/loading_indicator.dart';
|
||||
|
@ -21,8 +23,6 @@ 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/utilities/util.dart';
|
||||
|
||||
class AddressBookView extends ConsumerStatefulWidget {
|
||||
const AddressBookView({Key? key, this.coin}) : super(key: key);
|
||||
|
||||
|
@ -103,288 +103,302 @@ class _AddressBookViewState extends ConsumerState<AddressBookView> {
|
|||
final addressBookEntriesFuture = ref.watch(
|
||||
addressBookServiceProvider.select((value) => value.addressBookEntries));
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
title: Text(
|
||||
"Address book",
|
||||
style: STextStyles.navBarTitle(context),
|
||||
),
|
||||
actions: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 10,
|
||||
bottom: 10,
|
||||
right: 10,
|
||||
final isDesktop = Util.isDesktop;
|
||||
return ConditionalParent(
|
||||
condition: !isDesktop,
|
||||
builder: (child) {
|
||||
return Scaffold(
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
child: AspectRatio(
|
||||
aspectRatio: 1,
|
||||
child: AppBarIconButton(
|
||||
key: const Key("addressBookFilterViewButton"),
|
||||
size: 36,
|
||||
shadows: const [],
|
||||
color: Theme.of(context).extension<StackColors>()!.background,
|
||||
icon: SvgPicture.asset(
|
||||
Assets.svg.filter,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark,
|
||||
width: 20,
|
||||
height: 20,
|
||||
title: Text(
|
||||
"Address book",
|
||||
style: STextStyles.navBarTitle(context),
|
||||
),
|
||||
actions: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 10,
|
||||
bottom: 10,
|
||||
right: 10,
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pushNamed(
|
||||
AddressBookFilterView.routeName,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 10,
|
||||
bottom: 10,
|
||||
right: 10,
|
||||
),
|
||||
child: AspectRatio(
|
||||
aspectRatio: 1,
|
||||
child: AppBarIconButton(
|
||||
key: const Key("addressBookAddNewContactViewButton"),
|
||||
size: 36,
|
||||
shadows: const [],
|
||||
color: Theme.of(context).extension<StackColors>()!.background,
|
||||
icon: SvgPicture.asset(
|
||||
Assets.svg.plus,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark,
|
||||
width: 20,
|
||||
height: 20,
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pushNamed(
|
||||
AddAddressBookEntryView.routeName,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: LayoutBuilder(
|
||||
builder: (builderContext, constraints) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 12,
|
||||
top: 12,
|
||||
right: 12,
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight: constraints.maxHeight - 24,
|
||||
),
|
||||
child: IntrinsicHeight(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
child: TextField(
|
||||
autocorrect: Util.isDesktop ? false : true,
|
||||
enableSuggestions: Util.isDesktop ? false : true,
|
||||
controller: _searchController,
|
||||
focusNode: _searchFocusNode,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_searchTerm = value;
|
||||
});
|
||||
},
|
||||
style: STextStyles.field(context),
|
||||
decoration: standardInputDecoration(
|
||||
"Search",
|
||||
_searchFocusNode,
|
||||
context,
|
||||
).copyWith(
|
||||
prefixIcon: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
vertical: 16,
|
||||
),
|
||||
child: SvgPicture.asset(
|
||||
Assets.svg.search,
|
||||
width: 16,
|
||||
height: 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 = "";
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
Text(
|
||||
"Favorites",
|
||||
style: STextStyles.smallMed12(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
FutureBuilder(
|
||||
future: addressBookEntriesFuture,
|
||||
builder: (_, AsyncSnapshot<List<Contact>> snapshot) {
|
||||
if (snapshot.connectionState ==
|
||||
ConnectionState.done &&
|
||||
snapshot.hasData) {
|
||||
_cacheFav = snapshot.data!;
|
||||
}
|
||||
if (_cacheFav == null) {
|
||||
// TODO proper loading animation
|
||||
return const LoadingIndicator();
|
||||
} else {
|
||||
if (_cacheFav!.isNotEmpty) {
|
||||
return RoundedWhiteContainer(
|
||||
padding: const EdgeInsets.all(0),
|
||||
child: Column(
|
||||
children: [
|
||||
..._cacheFav!
|
||||
.where((element) => element.addresses
|
||||
.where((e) => ref.watch(
|
||||
addressBookFilterProvider
|
||||
.select((value) => value
|
||||
.coins
|
||||
.contains(e.coin))))
|
||||
.isNotEmpty)
|
||||
.where((e) =>
|
||||
e.isFavorite &&
|
||||
ref
|
||||
.read(
|
||||
addressBookServiceProvider)
|
||||
.matches(_searchTerm, e))
|
||||
.where(
|
||||
(element) => element.isFavorite)
|
||||
.map(
|
||||
(e) => AddressBookCard(
|
||||
key: Key(
|
||||
"favContactCard_${e.id}_key"),
|
||||
contactId: e.id,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return RoundedWhiteContainer(
|
||||
child: Center(
|
||||
child: Text(
|
||||
"Your favorite contacts will appear here",
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
Text(
|
||||
"All contacts",
|
||||
style: STextStyles.smallMed12(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
FutureBuilder(
|
||||
future: addressBookEntriesFuture,
|
||||
builder: (_, AsyncSnapshot<List<Contact>> snapshot) {
|
||||
if (snapshot.connectionState ==
|
||||
ConnectionState.done &&
|
||||
snapshot.hasData) {
|
||||
_cache = snapshot.data!;
|
||||
}
|
||||
if (_cache == null) {
|
||||
// TODO proper loading animation
|
||||
return const LoadingIndicator();
|
||||
} else {
|
||||
if (_cache!.isNotEmpty) {
|
||||
return RoundedWhiteContainer(
|
||||
padding: const EdgeInsets.all(0),
|
||||
child: Column(
|
||||
children: [
|
||||
..._cache!
|
||||
.where((element) => element.addresses
|
||||
.where((e) => ref.watch(
|
||||
addressBookFilterProvider
|
||||
.select((value) => value
|
||||
.coins
|
||||
.contains(e.coin))))
|
||||
.isNotEmpty)
|
||||
.where((e) => ref
|
||||
.read(addressBookServiceProvider)
|
||||
.matches(_searchTerm, e))
|
||||
.where(
|
||||
(element) => !element.isFavorite)
|
||||
.map(
|
||||
(e) => AddressBookCard(
|
||||
key: Key(
|
||||
"contactCard_${e.id}_key"),
|
||||
contactId: e.id,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return RoundedWhiteContainer(
|
||||
child: Center(
|
||||
child: Text(
|
||||
"Your contacts will appear here",
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
child: AspectRatio(
|
||||
aspectRatio: 1,
|
||||
child: AppBarIconButton(
|
||||
key: const Key("addressBookFilterViewButton"),
|
||||
size: 36,
|
||||
shadows: const [],
|
||||
color:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
icon: SvgPicture.asset(
|
||||
Assets.svg.filter,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark,
|
||||
width: 20,
|
||||
height: 20,
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pushNamed(
|
||||
AddressBookFilterView.routeName,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 10,
|
||||
bottom: 10,
|
||||
right: 10,
|
||||
),
|
||||
child: AspectRatio(
|
||||
aspectRatio: 1,
|
||||
child: AppBarIconButton(
|
||||
key: const Key("addressBookAddNewContactViewButton"),
|
||||
size: 36,
|
||||
shadows: const [],
|
||||
color:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
icon: SvgPicture.asset(
|
||||
Assets.svg.plus,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark,
|
||||
width: 20,
|
||||
height: 20,
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pushNamed(
|
||||
AddAddressBookEntryView.routeName,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: LayoutBuilder(
|
||||
builder: (builderContext, constraints) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 12,
|
||||
top: 12,
|
||||
right: 12,
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight: constraints.maxHeight - 24,
|
||||
),
|
||||
child: IntrinsicHeight(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight: MediaQuery.of(context).size.height - 271,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
child: !isDesktop
|
||||
? TextField(
|
||||
autocorrect: Util.isDesktop ? false : true,
|
||||
enableSuggestions: Util.isDesktop ? false : true,
|
||||
controller: _searchController,
|
||||
focusNode: _searchFocusNode,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_searchTerm = value;
|
||||
});
|
||||
},
|
||||
style: STextStyles.field(context),
|
||||
decoration: standardInputDecoration(
|
||||
"Search",
|
||||
_searchFocusNode,
|
||||
context,
|
||||
).copyWith(
|
||||
prefixIcon: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
vertical: 16,
|
||||
),
|
||||
child: SvgPicture.asset(
|
||||
Assets.svg.search,
|
||||
width: 16,
|
||||
height: 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 = "";
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
);
|
||||
},
|
||||
if (!isDesktop) const SizedBox(height: 16),
|
||||
Text(
|
||||
"Favorites",
|
||||
style: STextStyles.smallMed12(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
FutureBuilder(
|
||||
future: addressBookEntriesFuture,
|
||||
builder: (_, AsyncSnapshot<List<Contact>> snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done &&
|
||||
snapshot.hasData) {
|
||||
_cacheFav = snapshot.data!;
|
||||
}
|
||||
if (_cacheFav == null) {
|
||||
// TODO proper loading animation
|
||||
return const LoadingIndicator();
|
||||
} else {
|
||||
if (_cacheFav!.isNotEmpty) {
|
||||
return RoundedWhiteContainer(
|
||||
padding: EdgeInsets.all(!isDesktop ? 0 : 15),
|
||||
child: Column(
|
||||
children: [
|
||||
..._cacheFav!
|
||||
.where((element) => element.addresses
|
||||
.where((e) => ref.watch(
|
||||
addressBookFilterProvider.select(
|
||||
(value) =>
|
||||
value.coins.contains(e.coin))))
|
||||
.isNotEmpty)
|
||||
.where((e) =>
|
||||
e.isFavorite &&
|
||||
ref
|
||||
.read(addressBookServiceProvider)
|
||||
.matches(_searchTerm, e))
|
||||
.where((element) => element.isFavorite)
|
||||
.map(
|
||||
(e) => AddressBookCard(
|
||||
key: Key("favContactCard_${e.id}_key"),
|
||||
contactId: e.id,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return RoundedWhiteContainer(
|
||||
child: Center(
|
||||
child: Text(
|
||||
"Your favorite contacts will appear here",
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
Text(
|
||||
"All contacts",
|
||||
style: STextStyles.smallMed12(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
FutureBuilder(
|
||||
future: addressBookEntriesFuture,
|
||||
builder: (_, AsyncSnapshot<List<Contact>> snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done &&
|
||||
snapshot.hasData) {
|
||||
_cache = snapshot.data!;
|
||||
}
|
||||
if (_cache == null) {
|
||||
// TODO proper loading animation
|
||||
return const LoadingIndicator();
|
||||
} else {
|
||||
if (_cache!.isNotEmpty) {
|
||||
return Column(
|
||||
children: [
|
||||
RoundedWhiteContainer(
|
||||
padding: EdgeInsets.all(!isDesktop ? 0 : 15),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
children: [
|
||||
..._cache!
|
||||
.where((element) => element.addresses
|
||||
.where((e) => ref.watch(
|
||||
addressBookFilterProvider.select(
|
||||
(value) => value.coins
|
||||
.contains(e.coin))))
|
||||
.isNotEmpty)
|
||||
.where((e) => ref
|
||||
.read(addressBookServiceProvider)
|
||||
.matches(_searchTerm, e))
|
||||
.where((element) => !element.isFavorite)
|
||||
.map(
|
||||
(e) => AddressBookCard(
|
||||
key: Key(
|
||||
"desktopContactCard_${e.id}_key"),
|
||||
contactId: e.id,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
} else {
|
||||
return RoundedWhiteContainer(
|
||||
child: Center(
|
||||
child: Text(
|
||||
"Your contacts will appear here",
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -224,7 +224,11 @@ class _AddAddressBookEntryViewState
|
|||
const DesktopDialogCloseButton(),
|
||||
],
|
||||
),
|
||||
Expanded(child: child),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
child: child,
|
||||
)),
|
||||
],
|
||||
);
|
||||
},
|
||||
|
@ -248,216 +252,473 @@ class _AddAddressBookEntryViewState
|
|||
child: IntrinsicHeight(
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
if (_selectedEmoji != null) {
|
||||
setState(() {
|
||||
_selectedEmoji = null;
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (!isDesktop) const SizedBox(height: 4),
|
||||
isDesktop
|
||||
? Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
if (_selectedEmoji != null) {
|
||||
setState(() {
|
||||
_selectedEmoji = null;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
///TODO if desktop make dialog
|
||||
!isDesktop
|
||||
? showModalBottomSheet<dynamic>(
|
||||
backgroundColor: Colors.transparent,
|
||||
context: context,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(
|
||||
top: Radius.circular(20),
|
||||
),
|
||||
),
|
||||
builder: (_) => const EmojiSelectSheet(),
|
||||
).then((value) {
|
||||
if (value is Emoji) {
|
||||
setState(() {
|
||||
_selectedEmoji = value;
|
||||
});
|
||||
}
|
||||
})
|
||||
: showDialog<dynamic>(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return DesktopDialog(
|
||||
maxHeight: 700,
|
||||
maxWidth: 700,
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.all(32),
|
||||
child: Text(
|
||||
"Select emoji",
|
||||
style:
|
||||
STextStyles.desktopH3(
|
||||
context),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
///TODO if desktop make dialog
|
||||
!isDesktop
|
||||
? showModalBottomSheet<dynamic>(
|
||||
backgroundColor:
|
||||
Colors.transparent,
|
||||
context: context,
|
||||
shape:
|
||||
const RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.vertical(
|
||||
top: Radius.circular(20),
|
||||
),
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
child: LayoutBuilder(
|
||||
builder:
|
||||
(context, constraints) {
|
||||
return SingleChildScrollView(
|
||||
scrollDirection:
|
||||
Axis.vertical,
|
||||
child: ConstrainedBox(
|
||||
constraints:
|
||||
BoxConstraints(
|
||||
minHeight: constraints
|
||||
.maxHeight,
|
||||
minWidth: constraints
|
||||
.maxWidth,
|
||||
),
|
||||
child: IntrinsicHeight(
|
||||
child: Column(
|
||||
children: const [
|
||||
Padding(
|
||||
padding: EdgeInsets
|
||||
.symmetric(
|
||||
horizontal:
|
||||
32),
|
||||
// child:
|
||||
// EmojiSelectSheet(),
|
||||
),
|
||||
builder: (_) =>
|
||||
const EmojiSelectSheet(),
|
||||
).then((value) {
|
||||
if (value is Emoji) {
|
||||
setState(() {
|
||||
_selectedEmoji = value;
|
||||
});
|
||||
}
|
||||
})
|
||||
: showDialog<dynamic>(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return DesktopDialog(
|
||||
maxHeight: 700,
|
||||
maxWidth: 700,
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets
|
||||
.all(32),
|
||||
child: Text(
|
||||
"Select emoji",
|
||||
style: STextStyles
|
||||
.desktopH3(
|
||||
context),
|
||||
textAlign:
|
||||
TextAlign
|
||||
.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
child: LayoutBuilder(
|
||||
builder: (context,
|
||||
constraints) {
|
||||
return SingleChildScrollView(
|
||||
scrollDirection:
|
||||
Axis.vertical,
|
||||
child:
|
||||
ConstrainedBox(
|
||||
constraints:
|
||||
BoxConstraints(
|
||||
minHeight:
|
||||
constraints
|
||||
.maxHeight,
|
||||
minWidth:
|
||||
constraints
|
||||
.maxWidth,
|
||||
),
|
||||
child:
|
||||
IntrinsicHeight(
|
||||
child: Column(
|
||||
children: const [
|
||||
Padding(
|
||||
padding:
|
||||
EdgeInsets.symmetric(horizontal: 32),
|
||||
// child:
|
||||
// EmojiSelectSheet(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}).then((value) {
|
||||
if (value is Emoji) {
|
||||
setState(() {
|
||||
_selectedEmoji = value;
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
child: SizedBox(
|
||||
height: 56,
|
||||
width: 56,
|
||||
child: Stack(
|
||||
children: [
|
||||
Container(
|
||||
height: 56,
|
||||
width: 56,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius:
|
||||
BorderRadius.circular(24),
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldActiveBG,
|
||||
),
|
||||
child: Center(
|
||||
child: _selectedEmoji == null
|
||||
? SvgPicture.asset(
|
||||
Assets.svg.user,
|
||||
height: 30,
|
||||
width: 30,
|
||||
)
|
||||
: Text(
|
||||
_selectedEmoji!.char,
|
||||
style: STextStyles
|
||||
.pageTitleH1(context),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.bottomRight,
|
||||
child: Container(
|
||||
height: 14,
|
||||
width: 14,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius:
|
||||
BorderRadius.circular(14),
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark),
|
||||
child: Center(
|
||||
child: _selectedEmoji == null
|
||||
? SvgPicture.asset(
|
||||
Assets.svg.plus,
|
||||
color: Theme.of(context)
|
||||
.extension<
|
||||
StackColors>()!
|
||||
.textWhite,
|
||||
width: 12,
|
||||
height: 12,
|
||||
)
|
||||
: SvgPicture.asset(
|
||||
Assets.svg.thickX,
|
||||
color: Theme.of(context)
|
||||
.extension<
|
||||
StackColors>()!
|
||||
.textWhite,
|
||||
width: 8,
|
||||
height: 8,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}).then((value) {
|
||||
if (value is Emoji) {
|
||||
setState(() {
|
||||
_selectedEmoji = value;
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
child: SizedBox(
|
||||
height: 48,
|
||||
width: 48,
|
||||
child: Stack(
|
||||
children: [
|
||||
Container(
|
||||
height: 48,
|
||||
width: 48,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldActiveBG,
|
||||
),
|
||||
child: Center(
|
||||
child: _selectedEmoji == null
|
||||
? SvgPicture.asset(
|
||||
Assets.svg.user,
|
||||
height: 24,
|
||||
width: 24,
|
||||
)
|
||||
: Text(
|
||||
_selectedEmoji!.char,
|
||||
style: STextStyles.pageTitleH1(
|
||||
context),
|
||||
),
|
||||
),
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.bottomRight,
|
||||
child: Container(
|
||||
height: 14,
|
||||
width: 14,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark),
|
||||
child: Center(
|
||||
child: _selectedEmoji == null
|
||||
? SvgPicture.asset(
|
||||
Assets.svg.plus,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textWhite,
|
||||
width: 12,
|
||||
height: 12,
|
||||
)
|
||||
: SvgPicture.asset(
|
||||
Assets.svg.thickX,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textWhite,
|
||||
width: 8,
|
||||
height: 8,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
child: TextField(
|
||||
autocorrect: Util.isDesktop ? false : true,
|
||||
enableSuggestions: Util.isDesktop ? false : true,
|
||||
controller: nameController,
|
||||
focusNode: nameFocusNode,
|
||||
style: STextStyles.field(context),
|
||||
decoration: standardInputDecoration(
|
||||
"Enter contact name",
|
||||
nameFocusNode,
|
||||
context,
|
||||
).copyWith(
|
||||
suffixIcon: ref
|
||||
.read(contactNameIsNotEmptyStateProvider
|
||||
.state)
|
||||
.state
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(right: 0),
|
||||
child: UnconstrainedBox(
|
||||
child: Row(
|
||||
children: [
|
||||
TextFieldIconButton(
|
||||
child: const XIcon(),
|
||||
onTap: () async {
|
||||
setState(() {
|
||||
nameController.text = "";
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
SizedBox(
|
||||
width: isDesktop ? 450 : null,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
onChanged: (newValue) {
|
||||
ref
|
||||
.read(
|
||||
contactNameIsNotEmptyStateProvider.state)
|
||||
.state = newValue.isNotEmpty;
|
||||
},
|
||||
),
|
||||
),
|
||||
child: TextField(
|
||||
autocorrect:
|
||||
Util.isDesktop ? false : true,
|
||||
enableSuggestions:
|
||||
Util.isDesktop ? false : true,
|
||||
controller: nameController,
|
||||
focusNode: nameFocusNode,
|
||||
style: STextStyles.field(context),
|
||||
decoration: standardInputDecoration(
|
||||
"Enter contact name",
|
||||
nameFocusNode,
|
||||
context,
|
||||
).copyWith(
|
||||
labelStyle:
|
||||
STextStyles.fieldLabel(context),
|
||||
suffixIcon: ref
|
||||
.read(
|
||||
contactNameIsNotEmptyStateProvider
|
||||
.state)
|
||||
.state
|
||||
? Padding(
|
||||
padding:
|
||||
const EdgeInsets.only(
|
||||
right: 0),
|
||||
child: UnconstrainedBox(
|
||||
child: Row(
|
||||
children: [
|
||||
TextFieldIconButton(
|
||||
child: const XIcon(),
|
||||
onTap: () async {
|
||||
setState(() {
|
||||
nameController
|
||||
.text = "";
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
onChanged: (newValue) {
|
||||
ref
|
||||
.read(
|
||||
contactNameIsNotEmptyStateProvider
|
||||
.state)
|
||||
.state = newValue.isNotEmpty;
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: Column(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
if (_selectedEmoji != null) {
|
||||
setState(() {
|
||||
_selectedEmoji = null;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
///TODO if desktop make dialog
|
||||
!isDesktop
|
||||
? showModalBottomSheet<dynamic>(
|
||||
backgroundColor:
|
||||
Colors.transparent,
|
||||
context: context,
|
||||
shape:
|
||||
const RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.vertical(
|
||||
top: Radius.circular(20),
|
||||
),
|
||||
),
|
||||
builder: (_) =>
|
||||
const EmojiSelectSheet(),
|
||||
).then((value) {
|
||||
if (value is Emoji) {
|
||||
setState(() {
|
||||
_selectedEmoji = value;
|
||||
});
|
||||
}
|
||||
})
|
||||
: showDialog<dynamic>(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return DesktopDialog(
|
||||
maxHeight: 700,
|
||||
maxWidth: 700,
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets
|
||||
.all(32),
|
||||
child: Text(
|
||||
"Select emoji",
|
||||
style: STextStyles
|
||||
.desktopH3(
|
||||
context),
|
||||
textAlign:
|
||||
TextAlign
|
||||
.center,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
child: LayoutBuilder(
|
||||
builder: (context,
|
||||
constraints) {
|
||||
return SingleChildScrollView(
|
||||
scrollDirection:
|
||||
Axis.vertical,
|
||||
child:
|
||||
ConstrainedBox(
|
||||
constraints:
|
||||
BoxConstraints(
|
||||
minHeight:
|
||||
constraints
|
||||
.maxHeight,
|
||||
minWidth:
|
||||
constraints
|
||||
.maxWidth,
|
||||
),
|
||||
child:
|
||||
IntrinsicHeight(
|
||||
child: Column(
|
||||
children: const [
|
||||
Padding(
|
||||
padding:
|
||||
EdgeInsets.symmetric(horizontal: 32),
|
||||
// child:
|
||||
// EmojiSelectSheet(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}).then((value) {
|
||||
if (value is Emoji) {
|
||||
setState(() {
|
||||
_selectedEmoji = value;
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
child: SizedBox(
|
||||
height: 48,
|
||||
width: 48,
|
||||
child: Stack(
|
||||
children: [
|
||||
Container(
|
||||
height: 48,
|
||||
width: 48,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius:
|
||||
BorderRadius.circular(24),
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldActiveBG,
|
||||
),
|
||||
child: Center(
|
||||
child: _selectedEmoji == null
|
||||
? SvgPicture.asset(
|
||||
Assets.svg.user,
|
||||
height: 24,
|
||||
width: 24,
|
||||
)
|
||||
: Text(
|
||||
_selectedEmoji!.char,
|
||||
style: STextStyles
|
||||
.pageTitleH1(context),
|
||||
),
|
||||
),
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.bottomRight,
|
||||
child: Container(
|
||||
height: 14,
|
||||
width: 14,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius:
|
||||
BorderRadius.circular(14),
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark),
|
||||
child: Center(
|
||||
child: _selectedEmoji == null
|
||||
? SvgPicture.asset(
|
||||
Assets.svg.plus,
|
||||
color: Theme.of(context)
|
||||
.extension<
|
||||
StackColors>()!
|
||||
.textWhite,
|
||||
width: 12,
|
||||
height: 12,
|
||||
)
|
||||
: SvgPicture.asset(
|
||||
Assets.svg.thickX,
|
||||
color: Theme.of(context)
|
||||
.extension<
|
||||
StackColors>()!
|
||||
.textWhite,
|
||||
width: 8,
|
||||
height: 8,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
child: TextField(
|
||||
autocorrect:
|
||||
Util.isDesktop ? false : true,
|
||||
enableSuggestions:
|
||||
Util.isDesktop ? false : true,
|
||||
controller: nameController,
|
||||
focusNode: nameFocusNode,
|
||||
style: STextStyles.field(context),
|
||||
decoration: standardInputDecoration(
|
||||
"Enter contact name",
|
||||
nameFocusNode,
|
||||
context,
|
||||
).copyWith(
|
||||
suffixIcon: ref
|
||||
.read(
|
||||
contactNameIsNotEmptyStateProvider
|
||||
.state)
|
||||
.state
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
right: 0),
|
||||
child: UnconstrainedBox(
|
||||
child: Row(
|
||||
children: [
|
||||
TextFieldIconButton(
|
||||
child: const XIcon(),
|
||||
onTap: () async {
|
||||
setState(() {
|
||||
nameController
|
||||
.text = "";
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
onChanged: (newValue) {
|
||||
ref
|
||||
.read(
|
||||
contactNameIsNotEmptyStateProvider
|
||||
.state)
|
||||
.state = newValue.isNotEmpty;
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (!isDesktop) const SizedBox(height: 8),
|
||||
if (forms.length <= 1)
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
|
|
|
@ -14,14 +14,13 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
|||
import 'package:stackwallet/utilities/logger.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/clipboard_icon.dart';
|
||||
import 'package:stackwallet/widgets/icon_widgets/qrcode_icon.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';
|
||||
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
|
||||
class NewContactAddressEntryForm extends ConsumerStatefulWidget {
|
||||
const NewContactAddressEntryForm({
|
||||
Key? key,
|
||||
|
@ -70,6 +69,7 @@ class _NewContactAddressEntryFormState
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isDesktop = Util.isDesktop;
|
||||
return Column(
|
||||
children: [
|
||||
TextField(
|
||||
|
@ -168,6 +168,7 @@ class _NewContactAddressEntryFormState
|
|||
addressLabelFocusNode,
|
||||
context,
|
||||
).copyWith(
|
||||
labelStyle: isDesktop ? STextStyles.fieldLabel(context) : null,
|
||||
suffixIcon: addressLabelController.text.isNotEmpty
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(right: 0),
|
||||
|
@ -212,6 +213,7 @@ class _NewContactAddressEntryFormState
|
|||
addressFocusNode,
|
||||
context,
|
||||
).copyWith(
|
||||
labelStyle: isDesktop ? STextStyles.fieldLabel(context) : null,
|
||||
suffixIcon: UnconstrainedBox(
|
||||
child: Row(
|
||||
children: [
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
// import 'package:document_file_save_plus/document_file_save_plus.dart';
|
||||
import 'package:decimal/decimal.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
|
@ -71,19 +73,53 @@ class _GenerateUriQrCodeViewState extends State<GenerateUriQrCodeView> {
|
|||
await image.toByteData(format: ui.ImageByteFormat.png);
|
||||
Uint8List pngBytes = byteData!.buffer.asUint8List();
|
||||
|
||||
// if (shouldSaveInsteadOfShare) {
|
||||
// await DocumentFileSavePlus.saveFile(
|
||||
// pngBytes,
|
||||
// "receive_qr_code_${DateTime.now().toLocal().toIso8601String()}.png",
|
||||
// "image/png");
|
||||
// } else {
|
||||
final tempDir = await getTemporaryDirectory();
|
||||
final file = await File("${tempDir.path}/qrcode.png").create();
|
||||
await file.writeAsBytes(pngBytes);
|
||||
if (shouldSaveInsteadOfShare) {
|
||||
if (Util.isDesktop) {
|
||||
final dir = Directory("${Platform.environment['HOME']}");
|
||||
if (!dir.existsSync()) {
|
||||
throw Exception(
|
||||
"Home dir not found while trying to open filepicker on QR image save");
|
||||
}
|
||||
final path = await FilePicker.platform.saveFile(
|
||||
fileName: "qrcode.png",
|
||||
initialDirectory: dir.path,
|
||||
);
|
||||
|
||||
await Share.shareFiles(["${tempDir.path}/qrcode.png"],
|
||||
text: "Receive URI QR Code");
|
||||
// }
|
||||
if (path != null) {
|
||||
final file = File(path);
|
||||
if (file.existsSync()) {
|
||||
unawaited(
|
||||
showFloatingFlushBar(
|
||||
type: FlushBarType.warning,
|
||||
message: "$path already exists!",
|
||||
context: context,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
await file.writeAsBytes(pngBytes);
|
||||
unawaited(
|
||||
showFloatingFlushBar(
|
||||
type: FlushBarType.success,
|
||||
message: "$path saved!",
|
||||
context: context,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// await DocumentFileSavePlus.saveFile(
|
||||
// pngBytes,
|
||||
// "receive_qr_code_${DateTime.now().toLocal().toIso8601String()}.png",
|
||||
// "image/png");
|
||||
}
|
||||
} else {
|
||||
final tempDir = await getTemporaryDirectory();
|
||||
final file = await File("${tempDir.path}/qrcode.png").create();
|
||||
await file.writeAsBytes(pngBytes);
|
||||
|
||||
await Share.shareFiles(["${tempDir.path}/qrcode.png"],
|
||||
text: "Receive URI QR Code");
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint(e.toString());
|
||||
}
|
||||
|
@ -511,6 +547,7 @@ class _GenerateUriQrCodeViewState extends State<GenerateUriQrCodeView> {
|
|||
borderColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.background,
|
||||
width: isDesktop ? 370 : null,
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
|
@ -542,31 +579,37 @@ class _GenerateUriQrCodeViewState extends State<GenerateUriQrCodeView> {
|
|||
height: 12,
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: isDesktop
|
||||
? MainAxisAlignment.center
|
||||
: MainAxisAlignment.start,
|
||||
children: [
|
||||
SecondaryButton(
|
||||
width: 170,
|
||||
desktopMed: true,
|
||||
onPressed: () async {
|
||||
await _capturePng(false);
|
||||
},
|
||||
label: "Share",
|
||||
icon: SvgPicture.asset(
|
||||
Assets.svg.share,
|
||||
width: 20,
|
||||
height: 20,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.buttonTextSecondary,
|
||||
if (!isDesktop)
|
||||
SecondaryButton(
|
||||
width: 170,
|
||||
desktopMed: true,
|
||||
onPressed: () async {
|
||||
await _capturePng(false);
|
||||
},
|
||||
label: "Share",
|
||||
icon: SvgPicture.asset(
|
||||
Assets.svg.share,
|
||||
width: 20,
|
||||
height: 20,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.buttonTextSecondary,
|
||||
),
|
||||
),
|
||||
if (!isDesktop)
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
PrimaryButton(
|
||||
width: 170,
|
||||
desktopMed: true,
|
||||
onPressed: () async {
|
||||
// TODO: add save functionality instead of share
|
||||
// save works on linux at the moment
|
||||
await _capturePng(true);
|
||||
},
|
||||
label: "Save",
|
||||
|
|
|
@ -14,11 +14,10 @@ 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/rounded_white_container.dart';
|
||||
import 'package:stackwallet/widgets/stack_text_field.dart';
|
||||
import 'package:stackwallet/widgets/textfield_icon_button.dart';
|
||||
|
||||
import '../../../widgets/rounded_white_container.dart';
|
||||
|
||||
class BaseCurrencySettingsView extends ConsumerStatefulWidget {
|
||||
const BaseCurrencySettingsView({Key? key}) : super(key: key);
|
||||
|
||||
|
@ -37,14 +36,20 @@ class _CurrencyViewState extends ConsumerState<BaseCurrencySettingsView> {
|
|||
final _searchFocusNode = FocusNode();
|
||||
|
||||
void onTap(int index) {
|
||||
if (currenciesWithoutSelected[index] == current || current.isEmpty) {
|
||||
// ignore if already selected currency
|
||||
return;
|
||||
if (Util.isDesktop) {
|
||||
setState(() {
|
||||
current = currenciesWithoutSelected[index];
|
||||
});
|
||||
} else {
|
||||
if (currenciesWithoutSelected[index] == current || current.isEmpty) {
|
||||
// ignore if already selected currency
|
||||
return;
|
||||
}
|
||||
current = currenciesWithoutSelected[index];
|
||||
currenciesWithoutSelected.remove(current);
|
||||
currenciesWithoutSelected.insert(0, current);
|
||||
ref.read(prefsChangeNotifierProvider).currency = current;
|
||||
}
|
||||
current = currenciesWithoutSelected[index];
|
||||
currenciesWithoutSelected.remove(current);
|
||||
currenciesWithoutSelected.insert(0, current);
|
||||
ref.read(prefsChangeNotifierProvider).currency = current;
|
||||
}
|
||||
|
||||
BorderRadius? _borderRadius(int index) {
|
||||
|
@ -82,6 +87,15 @@ class _CurrencyViewState extends ConsumerState<BaseCurrencySettingsView> {
|
|||
@override
|
||||
void initState() {
|
||||
_searchController = TextEditingController();
|
||||
if (Util.isDesktop) {
|
||||
currenciesWithoutSelected =
|
||||
ref.read(baseCurrenciesProvider).map.keys.toList();
|
||||
current = ref.read(prefsChangeNotifierProvider).currency;
|
||||
if (current.isNotEmpty) {
|
||||
currenciesWithoutSelected.remove(current);
|
||||
currenciesWithoutSelected.insert(0, current);
|
||||
}
|
||||
}
|
||||
super.initState();
|
||||
}
|
||||
|
||||
|
@ -94,20 +108,25 @@ class _CurrencyViewState extends ConsumerState<BaseCurrencySettingsView> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
current = ref
|
||||
.watch(prefsChangeNotifierProvider.select((value) => value.currency));
|
||||
|
||||
currenciesWithoutSelected = ref
|
||||
.watch(baseCurrenciesProvider.select((value) => value.map))
|
||||
.keys
|
||||
.toList();
|
||||
if (current.isNotEmpty) {
|
||||
currenciesWithoutSelected.remove(current);
|
||||
currenciesWithoutSelected.insert(0, current);
|
||||
}
|
||||
currenciesWithoutSelected = _filtered();
|
||||
final isDesktop = Util.isDesktop;
|
||||
|
||||
if (!isDesktop) {
|
||||
current = ref
|
||||
.watch(prefsChangeNotifierProvider.select((value) => value.currency));
|
||||
|
||||
currenciesWithoutSelected = ref
|
||||
.watch(baseCurrenciesProvider.select((value) => value.map))
|
||||
.keys
|
||||
.toList();
|
||||
|
||||
if (current.isNotEmpty) {
|
||||
currenciesWithoutSelected.remove(current);
|
||||
currenciesWithoutSelected.insert(0, current);
|
||||
}
|
||||
}
|
||||
|
||||
currenciesWithoutSelected = _filtered();
|
||||
|
||||
return ConditionalParent(
|
||||
condition: !isDesktop,
|
||||
builder: (child) {
|
||||
|
@ -181,7 +200,20 @@ class _CurrencyViewState extends ConsumerState<BaseCurrencySettingsView> {
|
|||
child: PrimaryButton(
|
||||
label: "Save changes",
|
||||
desktopMed: true,
|
||||
onPressed: Navigator.of(context).pop,
|
||||
onPressed: () {
|
||||
ref.read(prefsChangeNotifierProvider).currency =
|
||||
current;
|
||||
|
||||
if (ref
|
||||
.read(prefsChangeNotifierProvider)
|
||||
.externalCalls) {
|
||||
ref
|
||||
.read(priceAnd24hChangeNotifierProvider)
|
||||
.updatePrice();
|
||||
}
|
||||
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
|
@ -18,6 +18,7 @@ import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
|||
import 'package:stackwallet/utilities/util.dart';
|
||||
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.dart';
|
||||
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
||||
import 'package:stackwallet/widgets/progress_bar.dart';
|
||||
|
@ -523,7 +524,7 @@ class _RestoreFromFileViewState extends State<CreateBackupView> {
|
|||
|
||||
if (mounted) {
|
||||
// pop encryption progress dialog
|
||||
Navigator.of(context).pop();
|
||||
if (!isDesktop) Navigator.of(context).pop();
|
||||
|
||||
if (result) {
|
||||
await showDialog<dynamic>(
|
||||
|
@ -607,14 +608,52 @@ class _RestoreFromFileViewState extends State<CreateBackupView> {
|
|||
return;
|
||||
}
|
||||
|
||||
unawaited(showDialog<dynamic>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (_) => const StackDialog(
|
||||
title: "Encrypting backup",
|
||||
message: "This shouldn't take long",
|
||||
unawaited(
|
||||
showDialog<dynamic>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (_) {
|
||||
if (Util.isDesktop) {
|
||||
return DesktopDialog(
|
||||
maxHeight: double.infinity,
|
||||
maxWidth: 450,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(
|
||||
32,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Encrypting initial backup",
|
||||
style:
|
||||
STextStyles.desktopH3(
|
||||
context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 40,
|
||||
),
|
||||
Text(
|
||||
"This shouldn't take long",
|
||||
style: STextStyles
|
||||
.desktopTextExtraExtraSmall(
|
||||
context),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return const StackDialog(
|
||||
title: "Encrypting initial backup",
|
||||
message: "This shouldn't take long",
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
));
|
||||
);
|
||||
// make sure the dialog is able to be displayed for at least 1 second
|
||||
await Future<void>.delayed(
|
||||
const Duration(seconds: 1));
|
||||
|
@ -637,7 +676,7 @@ class _RestoreFromFileViewState extends State<CreateBackupView> {
|
|||
|
||||
if (mounted) {
|
||||
// pop encryption progress dialog
|
||||
Navigator.of(context).pop();
|
||||
if (!isDesktop) Navigator.of(context).pop();
|
||||
|
||||
if (result) {
|
||||
await showDialog<dynamic>(
|
||||
|
@ -648,9 +687,67 @@ class _RestoreFromFileViewState extends State<CreateBackupView> {
|
|||
title: "Backup saved to:",
|
||||
message: fileToSave,
|
||||
)
|
||||
: const StackOkDialog(
|
||||
title:
|
||||
"Backup creation succeeded"),
|
||||
: !isDesktop
|
||||
? const StackOkDialog(
|
||||
title:
|
||||
"Backup creation succeeded")
|
||||
: DesktopDialog(
|
||||
maxHeight: double.infinity,
|
||||
maxWidth: 500,
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.only(
|
||||
left: 32,
|
||||
right: 32,
|
||||
bottom: 32,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize:
|
||||
MainAxisSize.min,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment
|
||||
.start,
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 26),
|
||||
Text(
|
||||
"Stack backup saved to: \n",
|
||||
style: STextStyles
|
||||
.desktopH3(
|
||||
context),
|
||||
),
|
||||
Text(
|
||||
fileToSave,
|
||||
style: STextStyles
|
||||
.desktopTextExtraExtraSmall(
|
||||
context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 40,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
// const Spacer(),
|
||||
Expanded(
|
||||
child:
|
||||
PrimaryButton(
|
||||
label: "Ok",
|
||||
desktopMed:
|
||||
true,
|
||||
onPressed:
|
||||
() {
|
||||
// Navigator.of(
|
||||
// context)
|
||||
// .pop();
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
passwordController.text = "";
|
||||
passwordRepeatController.text = "";
|
||||
|
|
|
@ -424,6 +424,7 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
|||
passwordFocusNode,
|
||||
context,
|
||||
).copyWith(
|
||||
labelStyle: isDesktop ? STextStyles.fieldLabel(context) : null,
|
||||
suffixIcon: UnconstrainedBox(
|
||||
child: Row(
|
||||
children: [
|
||||
|
@ -555,6 +556,7 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
|||
passwordRepeatFocusNode,
|
||||
context,
|
||||
).copyWith(
|
||||
labelStyle: isDesktop ? STextStyles.fieldLabel(context) : null,
|
||||
suffixIcon: UnconstrainedBox(
|
||||
child: Row(
|
||||
children: [
|
||||
|
@ -631,7 +633,11 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
|||
|
||||
return DropdownMenuItem(
|
||||
value: e,
|
||||
child: Text(message),
|
||||
child: Text(
|
||||
message,
|
||||
style:
|
||||
STextStyles.desktopTextExtraExtraSmall(context),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
|
@ -17,14 +17,13 @@ class SupportView extends StatelessWidget {
|
|||
}) : super(key: key);
|
||||
|
||||
static const String routeName = "/support";
|
||||
final double iconSize = 28;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isDesktop = Util.isDesktop;
|
||||
|
||||
debugPrint("BUILD: $runtimeType");
|
||||
|
||||
final isDesktop = Util.isDesktop;
|
||||
|
||||
return ConditionalParent(
|
||||
condition: !isDesktop,
|
||||
builder: (child) {
|
||||
|
@ -64,321 +63,168 @@ class SupportView extends StatelessWidget {
|
|||
: const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
RoundedWhiteContainer(
|
||||
padding: const EdgeInsets.all(0),
|
||||
child: RawMaterialButton(
|
||||
// splashColor: Theme.of(context).extension<StackColors>()!.highlight,
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
if (!isDesktop) {
|
||||
launchUrl(
|
||||
Uri.parse("https://t.me/stackwallet"),
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 20,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
Assets.socials.telegram,
|
||||
width: iconSize,
|
||||
height: iconSize,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
Text(
|
||||
"Telegram",
|
||||
style: STextStyles.titleBold12(context),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
],
|
||||
),
|
||||
BlueTextButton(
|
||||
text: isDesktop ? "@stackwallet" : "",
|
||||
onTap: () {
|
||||
launchUrl(
|
||||
Uri.parse("https://t.me/stackwallet"),
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
AboutItem(
|
||||
linkUrl: "https://t.me/stackwallet",
|
||||
label: "Telegram",
|
||||
buttonText: "@stackwallet",
|
||||
iconAsset: Assets.socials.telegram,
|
||||
isDesktop: isDesktop,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
RoundedWhiteContainer(
|
||||
padding: const EdgeInsets.all(0),
|
||||
child: RawMaterialButton(
|
||||
// splashColor: Theme.of(context).extension<StackColors>()!.highlight,
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
if (!isDesktop) {
|
||||
launchUrl(
|
||||
Uri.parse("https://discord.gg/RZMG3yUm"),
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 20,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
Assets.socials.discord,
|
||||
width: iconSize,
|
||||
height: iconSize,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
Text(
|
||||
"Discord",
|
||||
style: STextStyles.titleBold12(context),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
],
|
||||
),
|
||||
BlueTextButton(
|
||||
text: isDesktop ? "Stack Wallet" : "",
|
||||
onTap: () {
|
||||
launchUrl(
|
||||
Uri.parse(
|
||||
"https://discord.gg/RZMG3yUm"), //expired link?
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
AboutItem(
|
||||
linkUrl: "https://discord.gg/RZMG3yUm",
|
||||
label: "Discord",
|
||||
buttonText: "Stack Wallet",
|
||||
iconAsset: Assets.socials.discord,
|
||||
isDesktop: isDesktop,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
RoundedWhiteContainer(
|
||||
padding: const EdgeInsets.all(0),
|
||||
child: RawMaterialButton(
|
||||
// splashColor: Theme.of(context).extension<StackColors>()!.highlight,
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
if (!isDesktop) {
|
||||
launchUrl(
|
||||
Uri.parse("https://www.reddit.com/r/stackwallet/"),
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 20,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
Assets.socials.reddit,
|
||||
width: iconSize,
|
||||
height: iconSize,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
Text(
|
||||
"Reddit",
|
||||
style: STextStyles.titleBold12(context),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
],
|
||||
),
|
||||
BlueTextButton(
|
||||
text: isDesktop ? "r/stackwallet" : "",
|
||||
onTap: () {
|
||||
launchUrl(
|
||||
Uri.parse("https://www.reddit.com/r/stackwallet/"),
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
AboutItem(
|
||||
linkUrl: "https://www.reddit.com/r/stackwallet/",
|
||||
label: "Reddit",
|
||||
buttonText: "r/stackwallet",
|
||||
iconAsset: Assets.socials.reddit,
|
||||
isDesktop: isDesktop,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
RoundedWhiteContainer(
|
||||
padding: const EdgeInsets.all(0),
|
||||
child: RawMaterialButton(
|
||||
// splashColor: Theme.of(context).extension<StackColors>()!.highlight,
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
if (!isDesktop) {
|
||||
launchUrl(
|
||||
Uri.parse("https://twitter.com/stack_wallet"),
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 20,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
Assets.socials.twitter,
|
||||
width: iconSize,
|
||||
height: iconSize,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
Text(
|
||||
"Twitter",
|
||||
style: STextStyles.titleBold12(context),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
],
|
||||
),
|
||||
BlueTextButton(
|
||||
text: isDesktop ? "@stack_wallet" : "",
|
||||
onTap: () {
|
||||
launchUrl(
|
||||
Uri.parse("https://twitter.com/stack_wallet"),
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
AboutItem(
|
||||
linkUrl: "https://twitter.com/stack_wallet",
|
||||
label: "Twitter",
|
||||
buttonText: "@stack_wallet",
|
||||
iconAsset: Assets.socials.twitter,
|
||||
isDesktop: isDesktop,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
RoundedWhiteContainer(
|
||||
padding: const EdgeInsets.all(0),
|
||||
child: RawMaterialButton(
|
||||
// splashColor: Theme.of(context).extension<StackColors>()!.highlight,
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
if (!isDesktop) {
|
||||
launchUrl(
|
||||
Uri.parse("mailto:support@stackwallet.com"),
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 20,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
Assets.svg.envelope,
|
||||
width: iconSize,
|
||||
height: iconSize,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
Text(
|
||||
"Email",
|
||||
style: STextStyles.titleBold12(context),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
],
|
||||
),
|
||||
BlueTextButton(
|
||||
text: isDesktop ? "support@stackwallet.com" : "",
|
||||
onTap: () {
|
||||
launchUrl(
|
||||
Uri.parse("mailto:support@stackwallet.com"),
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
AboutItem(
|
||||
linkUrl: "mailto:support@stackwallet.com",
|
||||
label: "Email",
|
||||
buttonText: "support@stackwallet.com",
|
||||
iconAsset: Assets.svg.envelope,
|
||||
isDesktop: isDesktop,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AboutItem extends StatelessWidget {
|
||||
const AboutItem({
|
||||
Key? key,
|
||||
required this.linkUrl,
|
||||
required this.label,
|
||||
required this.buttonText,
|
||||
required this.iconAsset,
|
||||
required this.isDesktop,
|
||||
}) : super(key: key);
|
||||
|
||||
final String linkUrl;
|
||||
final String label;
|
||||
final String buttonText;
|
||||
final String iconAsset;
|
||||
final bool isDesktop;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final double iconSize = isDesktop ? 20 : 28;
|
||||
|
||||
return RoundedWhiteContainer(
|
||||
padding: const EdgeInsets.all(0),
|
||||
child: ConditionalParent(
|
||||
condition: !isDesktop,
|
||||
builder: (child) => RawMaterialButton(
|
||||
// splashColor: Theme.of(context).extension<StackColors>()!.highlight,
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
launchUrl(
|
||||
Uri.parse(linkUrl),
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 20,
|
||||
),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: isDesktop
|
||||
? const EdgeInsets.symmetric(
|
||||
horizontal: 20,
|
||||
vertical: 15,
|
||||
)
|
||||
: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 20,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
ConditionalParent(
|
||||
condition: isDesktop,
|
||||
builder: (child) => Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10000),
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.buttonBackSecondary,
|
||||
),
|
||||
child: Center(
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
child: SvgPicture.asset(
|
||||
iconAsset,
|
||||
width: iconSize,
|
||||
height: iconSize,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.bottomNavIconIcon,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
Text(
|
||||
label,
|
||||
style: STextStyles.titleBold12(context),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
BlueTextButton(
|
||||
text: buttonText,
|
||||
onTap: () {
|
||||
launchUrl(
|
||||
Uri.parse(linkUrl),
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -203,15 +203,18 @@ class _CreatePasswordViewState extends ConsumerState<CreatePasswordView> {
|
|||
height: 32,
|
||||
width: 32,
|
||||
child: Center(
|
||||
child: SvgPicture.asset(
|
||||
hidePassword
|
||||
? Assets.svg.eye
|
||||
: Assets.svg.eyeSlash,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark3,
|
||||
width: 24,
|
||||
height: 19,
|
||||
child: MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: SvgPicture.asset(
|
||||
hidePassword
|
||||
? Assets.svg.eye
|
||||
: Assets.svg.eyeSlash,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark3,
|
||||
width: 24,
|
||||
height: 19,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -354,22 +357,25 @@ class _CreatePasswordViewState extends ConsumerState<CreatePasswordView> {
|
|||
height: 32,
|
||||
width: 32,
|
||||
child: Center(
|
||||
child: SvgPicture.asset(
|
||||
fieldsMatch && passwordStrength == 1
|
||||
? Assets.svg.checkCircle
|
||||
: hidePassword
|
||||
? Assets.svg.eye
|
||||
: Assets.svg.eyeSlash,
|
||||
color: fieldsMatch &&
|
||||
passwordStrength == 1
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorGreen
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark3,
|
||||
width: 24,
|
||||
height: 19,
|
||||
child: MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: SvgPicture.asset(
|
||||
fieldsMatch && passwordStrength == 1
|
||||
? Assets.svg.checkCircle
|
||||
: hidePassword
|
||||
? Assets.svg.eye
|
||||
: Assets.svg.eyeSlash,
|
||||
color: fieldsMatch &&
|
||||
passwordStrength == 1
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorGreen
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark3,
|
||||
width: 24,
|
||||
height: 19,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -17,6 +17,7 @@ import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
|||
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
|
||||
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
||||
import 'package:stackwallet/widgets/loading_indicator.dart';
|
||||
import 'package:stackwallet/widgets/stack_text_field.dart';
|
||||
|
||||
class DesktopLoginView extends ConsumerStatefulWidget {
|
||||
|
@ -45,6 +46,15 @@ class _DesktopLoginViewState extends ConsumerState<DesktopLoginView> {
|
|||
|
||||
Future<void> login() async {
|
||||
try {
|
||||
unawaited(
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => const LoadingIndicator(
|
||||
width: 200,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await ref
|
||||
.read(storageCryptoHandlerProvider)
|
||||
.initFromExisting(passwordController.text);
|
||||
|
@ -55,6 +65,9 @@ class _DesktopLoginViewState extends ConsumerState<DesktopLoginView> {
|
|||
|
||||
// if no errors passphrase is correct
|
||||
if (mounted) {
|
||||
// pop loading indicator
|
||||
Navigator.of(context).pop();
|
||||
|
||||
unawaited(
|
||||
Navigator.of(context).pushNamedAndRemoveUntil(
|
||||
DesktopHomeView.routeName,
|
||||
|
@ -63,6 +76,9 @@ class _DesktopLoginViewState extends ConsumerState<DesktopLoginView> {
|
|||
);
|
||||
}
|
||||
} catch (e) {
|
||||
// pop loading indicator
|
||||
Navigator.of(context).pop();
|
||||
|
||||
await showFloatingFlushBar(
|
||||
type: FlushBarType.warning,
|
||||
message: e.toString(),
|
||||
|
@ -159,15 +175,18 @@ class _DesktopLoginViewState extends ConsumerState<DesktopLoginView> {
|
|||
hidePassword = !hidePassword;
|
||||
});
|
||||
},
|
||||
child: SvgPicture.asset(
|
||||
hidePassword
|
||||
? Assets.svg.eye
|
||||
: Assets.svg.eyeSlash,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark3,
|
||||
width: 24,
|
||||
height: 24,
|
||||
child: MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: SvgPicture.asset(
|
||||
hidePassword
|
||||
? Assets.svg.eye
|
||||
: Assets.svg.eyeSlash,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark3,
|
||||
width: 24,
|
||||
height: 24,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:stackwallet/models/contact.dart';
|
||||
import 'package:stackwallet/pages/address_book_views/address_book_view.dart';
|
||||
import 'package:stackwallet/pages/address_book_views/subviews/add_address_book_entry_view.dart';
|
||||
import 'package:stackwallet/pages/address_book_views/subviews/address_book_filter_view.dart';
|
||||
import 'package:stackwallet/providers/global/wallets_provider.dart';
|
||||
|
@ -9,11 +10,11 @@ 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/desktop/desktop_app_bar.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
||||
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
|
||||
import 'package:stackwallet/widgets/rounded_container.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';
|
||||
|
||||
|
@ -36,7 +37,7 @@ class _DesktopAddressBook extends ConsumerState<DesktopAddressBook> {
|
|||
|
||||
late bool hasContacts = false;
|
||||
|
||||
String filter = "";
|
||||
String _searchTerm = "";
|
||||
|
||||
Future<void> selectCryptocurrency() async {
|
||||
await showDialog<dynamic>(
|
||||
|
@ -123,25 +124,25 @@ class _DesktopAddressBook extends ConsumerState<DesktopAddressBook> {
|
|||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
child: TextField(
|
||||
autocorrect: false,
|
||||
enableSuggestions: false,
|
||||
autocorrect: Util.isDesktop ? false : true,
|
||||
enableSuggestions: Util.isDesktop ? false : true,
|
||||
controller: _searchController,
|
||||
focusNode: _searchFocusNode,
|
||||
onChanged: (newString) {
|
||||
setState(() => filter = newString);
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_searchTerm = value;
|
||||
});
|
||||
},
|
||||
style: STextStyles.field(context),
|
||||
decoration: standardInputDecoration(
|
||||
"Search...",
|
||||
"Search",
|
||||
_searchFocusNode,
|
||||
context,
|
||||
).copyWith(
|
||||
labelStyle: STextStyles.fieldLabel(context)
|
||||
.copyWith(fontSize: 16),
|
||||
prefixIcon: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
vertical: 16,
|
||||
vertical: 20,
|
||||
),
|
||||
child: SvgPicture.asset(
|
||||
Assets.svg.search,
|
||||
|
@ -160,7 +161,6 @@ class _DesktopAddressBook extends ConsumerState<DesktopAddressBook> {
|
|||
onTap: () async {
|
||||
setState(() {
|
||||
_searchController.text = "";
|
||||
filter = "";
|
||||
});
|
||||
},
|
||||
),
|
||||
|
@ -239,18 +239,11 @@ class _DesktopAddressBook extends ConsumerState<DesktopAddressBook> {
|
|||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 26),
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 26),
|
||||
child: SizedBox(
|
||||
width: 489,
|
||||
child: RoundedWhiteContainer(
|
||||
child: Center(
|
||||
child: Text(
|
||||
"Your contacts will appear here",
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
),
|
||||
),
|
||||
),
|
||||
child: AddressBookView(),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
|
@ -292,13 +292,13 @@ class _DesktopWalletViewState extends ConsumerState<DesktopWalletView> {
|
|||
eventBus: eventBus,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 32,
|
||||
width: 2,
|
||||
),
|
||||
WalletKeysButton(
|
||||
walletId: walletId,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 32,
|
||||
width: 12,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -0,0 +1,369 @@
|
|||
import 'package:decimal/decimal.dart';
|
||||
import 'package:dropdown_button2/dropdown_button2.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:stackwallet/models/models.dart';
|
||||
import 'package:stackwallet/pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart';
|
||||
import 'package:stackwallet/providers/global/wallets_provider.dart';
|
||||
import 'package:stackwallet/providers/ui/fee_rate_type_state_provider.dart';
|
||||
import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart';
|
||||
import 'package:stackwallet/services/coins/firo/firo_wallet.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/enums/fee_rate_type_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/animated_text.dart';
|
||||
|
||||
class DesktopFeeDropDown extends ConsumerStatefulWidget {
|
||||
const DesktopFeeDropDown({
|
||||
Key? key,
|
||||
required this.walletId,
|
||||
}) : super(key: key);
|
||||
|
||||
final String walletId;
|
||||
|
||||
@override
|
||||
ConsumerState<DesktopFeeDropDown> createState() => _DesktopFeeDropDownState();
|
||||
}
|
||||
|
||||
class _DesktopFeeDropDownState extends ConsumerState<DesktopFeeDropDown> {
|
||||
late final String walletId;
|
||||
|
||||
FeeObject? feeObject;
|
||||
FeeRateType feeRateType = FeeRateType.average;
|
||||
|
||||
final stringsToLoopThrough = [
|
||||
"Calculating",
|
||||
"Calculating.",
|
||||
"Calculating..",
|
||||
"Calculating...",
|
||||
];
|
||||
|
||||
Future<Decimal> feeFor({
|
||||
required int amount,
|
||||
required FeeRateType feeRateType,
|
||||
required int feeRate,
|
||||
required Coin coin,
|
||||
}) async {
|
||||
switch (feeRateType) {
|
||||
case FeeRateType.fast:
|
||||
if (ref.read(feeSheetSessionCacheProvider).fast[amount] == null) {
|
||||
final manager =
|
||||
ref.read(walletsChangeNotifierProvider).getManager(walletId);
|
||||
|
||||
if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
|
||||
ref.read(publicPrivateBalanceStateProvider.state).state !=
|
||||
"Private") {
|
||||
ref.read(feeSheetSessionCacheProvider).fast[amount] =
|
||||
Format.satoshisToAmount(await (manager.wallet as FiroWallet)
|
||||
.estimateFeeForPublic(amount, feeRate));
|
||||
} else {
|
||||
ref.read(feeSheetSessionCacheProvider).fast[amount] =
|
||||
Format.satoshisToAmount(
|
||||
await manager.estimateFeeFor(amount, feeRate));
|
||||
}
|
||||
}
|
||||
return ref.read(feeSheetSessionCacheProvider).fast[amount]!;
|
||||
|
||||
case FeeRateType.average:
|
||||
if (ref.read(feeSheetSessionCacheProvider).average[amount] == null) {
|
||||
final manager =
|
||||
ref.read(walletsChangeNotifierProvider).getManager(walletId);
|
||||
|
||||
if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
|
||||
ref.read(publicPrivateBalanceStateProvider.state).state !=
|
||||
"Private") {
|
||||
ref.read(feeSheetSessionCacheProvider).average[amount] =
|
||||
Format.satoshisToAmount(await (manager.wallet as FiroWallet)
|
||||
.estimateFeeForPublic(amount, feeRate));
|
||||
} else {
|
||||
ref.read(feeSheetSessionCacheProvider).average[amount] =
|
||||
Format.satoshisToAmount(
|
||||
await manager.estimateFeeFor(amount, feeRate));
|
||||
}
|
||||
}
|
||||
return ref.read(feeSheetSessionCacheProvider).average[amount]!;
|
||||
|
||||
case FeeRateType.slow:
|
||||
if (ref.read(feeSheetSessionCacheProvider).slow[amount] == null) {
|
||||
final manager =
|
||||
ref.read(walletsChangeNotifierProvider).getManager(walletId);
|
||||
|
||||
if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
|
||||
ref.read(publicPrivateBalanceStateProvider.state).state !=
|
||||
"Private") {
|
||||
ref.read(feeSheetSessionCacheProvider).slow[amount] =
|
||||
Format.satoshisToAmount(await (manager.wallet as FiroWallet)
|
||||
.estimateFeeForPublic(amount, feeRate));
|
||||
} else {
|
||||
ref.read(feeSheetSessionCacheProvider).slow[amount] =
|
||||
Format.satoshisToAmount(
|
||||
await manager.estimateFeeFor(amount, feeRate));
|
||||
}
|
||||
}
|
||||
return ref.read(feeSheetSessionCacheProvider).slow[amount]!;
|
||||
}
|
||||
}
|
||||
|
||||
String estimatedTimeToBeIncludedInNextBlock(
|
||||
int targetBlockTime, int estimatedNumberOfBlocks) {
|
||||
int time = targetBlockTime * estimatedNumberOfBlocks;
|
||||
|
||||
int hours = (time / 3600).floor();
|
||||
if (hours > 1) {
|
||||
return "~$hours hours";
|
||||
} else if (hours == 1) {
|
||||
return "~$hours hour";
|
||||
}
|
||||
|
||||
// less than an hour
|
||||
|
||||
final string = (time / 60).toStringAsFixed(1);
|
||||
|
||||
if (string == "1.0") {
|
||||
return "~1 minute";
|
||||
} else {
|
||||
if (string.endsWith(".0")) {
|
||||
return "~${(time / 60).floor()} minutes";
|
||||
}
|
||||
return "~$string minutes";
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
walletId = widget.walletId;
|
||||
super.initState();
|
||||
}
|
||||
|
||||
String? labelSlow;
|
||||
String? labelAverage;
|
||||
String? labelFast;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
debugPrint("BUILD: $runtimeType");
|
||||
|
||||
final manager = ref.watch(walletsChangeNotifierProvider
|
||||
.select((value) => value.getManager(walletId)));
|
||||
|
||||
return FutureBuilder(
|
||||
future: manager.fees,
|
||||
builder: (context, AsyncSnapshot<FeeObject> snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done &&
|
||||
snapshot.hasData) {
|
||||
feeObject = snapshot.data!;
|
||||
}
|
||||
return DropdownButtonHideUnderline(
|
||||
child: DropdownButton2(
|
||||
offset: const Offset(0, -10),
|
||||
isExpanded: true,
|
||||
dropdownElevation: 0,
|
||||
value: ref.watch(feeRateTypeStateProvider.state).state,
|
||||
// selectedItemBuilder: (s) {
|
||||
// return [
|
||||
// ...FeeRateType.values.map(
|
||||
// (e) => DropdownMenuItem(
|
||||
// value: e,
|
||||
// child: FeeDropDownChild(
|
||||
// feeObject: feeObject,
|
||||
// feeRateType: e,
|
||||
// walletId: walletId,
|
||||
// amount: amount,
|
||||
// feeFor: feeFor,
|
||||
// isSelected: true,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ];
|
||||
// },
|
||||
items: [
|
||||
...FeeRateType.values.map(
|
||||
(e) => DropdownMenuItem(
|
||||
value: e,
|
||||
child: FeeDropDownChild(
|
||||
feeObject: feeObject,
|
||||
feeRateType: e,
|
||||
walletId: walletId,
|
||||
feeFor: feeFor,
|
||||
isSelected: false,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
onChanged: (newRateType) {
|
||||
if (newRateType is FeeRateType) {
|
||||
ref.read(feeRateTypeStateProvider.state).state = newRateType;
|
||||
}
|
||||
},
|
||||
icon: SvgPicture.asset(
|
||||
Assets.svg.chevronDown,
|
||||
width: 12,
|
||||
height: 6,
|
||||
color: Theme.of(context).extension<StackColors>()!.textDark3,
|
||||
),
|
||||
buttonPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 8,
|
||||
),
|
||||
buttonDecoration: BoxDecoration(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldDefaultBG,
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
),
|
||||
dropdownDecoration: BoxDecoration(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldDefaultBG,
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
final sendAmountProvider =
|
||||
StateProvider.autoDispose<Decimal>((_) => Decimal.zero);
|
||||
|
||||
class FeeDropDownChild extends ConsumerWidget {
|
||||
const FeeDropDownChild({
|
||||
Key? key,
|
||||
required this.feeObject,
|
||||
required this.feeRateType,
|
||||
required this.walletId,
|
||||
required this.feeFor,
|
||||
required this.isSelected,
|
||||
}) : super(key: key);
|
||||
|
||||
final FeeObject? feeObject;
|
||||
final FeeRateType feeRateType;
|
||||
final String walletId;
|
||||
final Future<Decimal> Function({
|
||||
required int amount,
|
||||
required FeeRateType feeRateType,
|
||||
required int feeRate,
|
||||
required Coin coin,
|
||||
}) feeFor;
|
||||
final bool isSelected;
|
||||
|
||||
static const stringsToLoopThrough = [
|
||||
"Calculating",
|
||||
"Calculating.",
|
||||
"Calculating..",
|
||||
"Calculating...",
|
||||
];
|
||||
|
||||
String estimatedTimeToBeIncludedInNextBlock(
|
||||
int targetBlockTime, int estimatedNumberOfBlocks) {
|
||||
int time = targetBlockTime * estimatedNumberOfBlocks;
|
||||
|
||||
int hours = (time / 3600).floor();
|
||||
if (hours > 1) {
|
||||
return "~$hours hours";
|
||||
} else if (hours == 1) {
|
||||
return "~$hours hour";
|
||||
}
|
||||
|
||||
// less than an hour
|
||||
|
||||
final string = (time / 60).toStringAsFixed(1);
|
||||
|
||||
if (string == "1.0") {
|
||||
return "~1 minute";
|
||||
} else {
|
||||
if (string.endsWith(".0")) {
|
||||
return "~${(time / 60).floor()} minutes";
|
||||
}
|
||||
return "~$string minutes";
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
debugPrint("BUILD: $runtimeType : $feeRateType");
|
||||
|
||||
final manager = ref.watch(walletsChangeNotifierProvider
|
||||
.select((value) => value.getManager(walletId)));
|
||||
|
||||
if (feeObject == null) {
|
||||
return AnimatedText(
|
||||
stringsToLoopThrough: stringsToLoopThrough,
|
||||
style: STextStyles.desktopTextExtraExtraSmall(context).copyWith(
|
||||
color:
|
||||
Theme.of(context).extension<StackColors>()!.textFieldActiveText,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return FutureBuilder(
|
||||
future: feeFor(
|
||||
coin: manager.coin,
|
||||
feeRateType: FeeRateType.fast,
|
||||
feeRate: feeRateType == FeeRateType.fast
|
||||
? feeObject!.fast
|
||||
: feeRateType == FeeRateType.slow
|
||||
? feeObject!.slow
|
||||
: feeObject!.medium,
|
||||
amount: Format.decimalAmountToSatoshis(
|
||||
ref.watch(sendAmountProvider.state).state,
|
||||
),
|
||||
),
|
||||
builder: (_, AsyncSnapshot<Decimal> snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done &&
|
||||
snapshot.hasData) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"${feeRateType.prettyName} (~${snapshot.data!} ${manager.coin.ticker})",
|
||||
style:
|
||||
STextStyles.desktopTextExtraExtraSmall(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldActiveText,
|
||||
),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
if (feeObject != null)
|
||||
Text(
|
||||
estimatedTimeToBeIncludedInNextBlock(
|
||||
Constants.targetBlockTimeInSeconds(manager.coin),
|
||||
feeRateType == FeeRateType.fast
|
||||
? feeObject!.numberOfBlocksFast
|
||||
: feeRateType == FeeRateType.slow
|
||||
? feeObject!.numberOfBlocksSlow
|
||||
: feeObject!.numberOfBlocksAverage,
|
||||
),
|
||||
style: STextStyles.desktopTextExtraExtraSmall(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldActiveSearchIconRight,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
} else {
|
||||
return AnimatedText(
|
||||
stringsToLoopThrough: stringsToLoopThrough,
|
||||
style: STextStyles.desktopTextExtraExtraSmall(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldActiveText,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@ import 'package:stackwallet/pages/send_view/sub_widgets/building_transaction_dia
|
|||
import 'package:stackwallet/pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/desktop_home_view.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/address_book_address_chooser/address_book_address_chooser.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/desktop_fee_dropdown.dart';
|
||||
import 'package:stackwallet/providers/providers.dart';
|
||||
import 'package:stackwallet/providers/ui/fee_rate_type_state_provider.dart';
|
||||
import 'package:stackwallet/providers/ui/preview_tx_button_state_provider.dart';
|
||||
|
@ -94,11 +95,6 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
|||
late VoidCallback onCryptoAmountChanged;
|
||||
|
||||
Future<void> previewSend() async {
|
||||
// wait for keyboard to disappear
|
||||
FocusScope.of(context).unfocus();
|
||||
await Future<void>.delayed(
|
||||
const Duration(milliseconds: 100),
|
||||
);
|
||||
final manager =
|
||||
ref.read(walletsChangeNotifierProvider).getManager(walletId);
|
||||
|
||||
|
@ -794,35 +790,25 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
|||
_addressToggleFlag = true;
|
||||
}
|
||||
|
||||
// _cryptoFocus.addListener(() {
|
||||
// if (!_cryptoFocus.hasFocus && !_baseFocus.hasFocus) {
|
||||
// if (_amountToSend == null) {
|
||||
// setState(() {
|
||||
// _calculateFeesFuture = calculateFees(0);
|
||||
// });
|
||||
// } else {
|
||||
// setState(() {
|
||||
// _calculateFeesFuture =
|
||||
// calculateFees(Format.decimalAmountToSatoshis(_amountToSend!));
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// _baseFocus.addListener(() {
|
||||
// if (!_cryptoFocus.hasFocus && !_baseFocus.hasFocus) {
|
||||
// if (_amountToSend == null) {
|
||||
// setState(() {
|
||||
// _calculateFeesFuture = calculateFees(0);
|
||||
// });
|
||||
// } else {
|
||||
// setState(() {
|
||||
// _calculateFeesFuture =
|
||||
// calculateFees(Format.decimalAmountToSatoshis(_amountToSend!));
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
_cryptoFocus.addListener(() {
|
||||
if (!_cryptoFocus.hasFocus && !_baseFocus.hasFocus) {
|
||||
if (_amountToSend == null) {
|
||||
ref.refresh(sendAmountProvider);
|
||||
} else {
|
||||
ref.read(sendAmountProvider.state).state = _amountToSend!;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
_baseFocus.addListener(() {
|
||||
if (!_cryptoFocus.hasFocus && !_baseFocus.hasFocus) {
|
||||
if (_amountToSend == null) {
|
||||
ref.refresh(sendAmountProvider);
|
||||
} else {
|
||||
ref.read(sendAmountProvider.state).state = _amountToSend!;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
@ -1371,6 +1357,24 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
|||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Text(
|
||||
"Transaction fee (estimated)",
|
||||
style: STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldActiveSearchIconRight,
|
||||
),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
DesktopFeeDropDown(
|
||||
walletId: walletId,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 36,
|
||||
),
|
||||
|
|
|
@ -98,25 +98,26 @@ class _NetworkInfoButtonState extends ConsumerState<NetworkInfoButton> {
|
|||
|
||||
Widget _buildNetworkIcon(WalletSyncStatus status, BuildContext context) {
|
||||
const size = 24.0;
|
||||
final color = _getColor(status, context);
|
||||
switch (status) {
|
||||
case WalletSyncStatus.unableToSync:
|
||||
return SvgPicture.asset(
|
||||
Assets.svg.radioProblem,
|
||||
color: Theme.of(context).extension<StackColors>()!.accentColorRed,
|
||||
color: color,
|
||||
width: size,
|
||||
height: size,
|
||||
);
|
||||
case WalletSyncStatus.synced:
|
||||
return SvgPicture.asset(
|
||||
Assets.svg.radio,
|
||||
color: Theme.of(context).extension<StackColors>()!.accentColorGreen,
|
||||
color: color,
|
||||
width: size,
|
||||
height: size,
|
||||
);
|
||||
case WalletSyncStatus.syncing:
|
||||
return SvgPicture.asset(
|
||||
Assets.svg.radioSyncing,
|
||||
color: Theme.of(context).extension<StackColors>()!.accentColorYellow,
|
||||
color: color,
|
||||
width: size,
|
||||
height: size,
|
||||
);
|
||||
|
@ -125,35 +126,46 @@ class _NetworkInfoButtonState extends ConsumerState<NetworkInfoButton> {
|
|||
|
||||
Widget _buildText(WalletSyncStatus status, BuildContext context) {
|
||||
String label;
|
||||
Color color;
|
||||
|
||||
switch (status) {
|
||||
case WalletSyncStatus.unableToSync:
|
||||
label = "Unable to sync";
|
||||
color = Theme.of(context).extension<StackColors>()!.accentColorRed;
|
||||
break;
|
||||
case WalletSyncStatus.synced:
|
||||
label = "Synchronised";
|
||||
color = Theme.of(context).extension<StackColors>()!.accentColorGreen;
|
||||
label = "Synchronized";
|
||||
break;
|
||||
case WalletSyncStatus.syncing:
|
||||
label = "Synchronising";
|
||||
color = Theme.of(context).extension<StackColors>()!.accentColorYellow;
|
||||
label = "Synchronizing";
|
||||
break;
|
||||
}
|
||||
|
||||
return Text(
|
||||
label,
|
||||
style: STextStyles.desktopMenuItemSelected(context).copyWith(
|
||||
color: color,
|
||||
color: _getColor(status, context),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Color _getColor(WalletSyncStatus status, BuildContext context) {
|
||||
switch (status) {
|
||||
case WalletSyncStatus.unableToSync:
|
||||
return Theme.of(context).extension<StackColors>()!.accentColorRed;
|
||||
case WalletSyncStatus.synced:
|
||||
return Theme.of(context).extension<StackColors>()!.accentColorGreen;
|
||||
case WalletSyncStatus.syncing:
|
||||
return Theme.of(context).extension<StackColors>()!.accentColorYellow;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
return RawMaterialButton(
|
||||
hoverColor: _getColor(_currentSyncStatus, context).withOpacity(0.1),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(1000),
|
||||
),
|
||||
onPressed: () {
|
||||
if (Util.isDesktop) {
|
||||
// showDialog<void>(
|
||||
// context: context,
|
||||
|
@ -265,8 +277,11 @@ class _NetworkInfoButtonState extends ConsumerState<NetworkInfoButton> {
|
|||
);
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 16,
|
||||
horizontal: 32,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
_buildNetworkIcon(_currentSyncStatus, context),
|
||||
|
|
|
@ -39,85 +39,93 @@ class _SendReceiveTabMenuState extends State<SendReceiveTabMenu> {
|
|||
return Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: () => _onChanged(0),
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
Text(
|
||||
"Send",
|
||||
style: STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||
color: _selectedIndex == 0
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorBlue
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
child: MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: GestureDetector(
|
||||
onTap: () => _onChanged(0),
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 19,
|
||||
),
|
||||
Container(
|
||||
height: 2,
|
||||
decoration: BoxDecoration(
|
||||
color: _selectedIndex == 0
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorBlue
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.background,
|
||||
Text(
|
||||
"Send",
|
||||
style:
|
||||
STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||
color: _selectedIndex == 0
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorBlue
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
const SizedBox(
|
||||
height: 19,
|
||||
),
|
||||
Container(
|
||||
height: 2,
|
||||
decoration: BoxDecoration(
|
||||
color: _selectedIndex == 0
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorBlue
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.background,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: () => _onChanged(1),
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
Text(
|
||||
"Receive",
|
||||
style: STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||
color: _selectedIndex == 1
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorBlue
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
child: MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: GestureDetector(
|
||||
onTap: () => _onChanged(1),
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 19,
|
||||
),
|
||||
Container(
|
||||
height: 2,
|
||||
decoration: BoxDecoration(
|
||||
color: _selectedIndex == 1
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorBlue
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.background,
|
||||
Text(
|
||||
"Receive",
|
||||
style:
|
||||
STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||
color: _selectedIndex == 1
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorBlue
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
const SizedBox(
|
||||
height: 19,
|
||||
),
|
||||
Container(
|
||||
height: 2,
|
||||
decoration: BoxDecoration(
|
||||
color: _selectedIndex == 1
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorBlue
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.background,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -16,8 +16,11 @@ class WalletKeysButton extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
return RawMaterialButton(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(1000),
|
||||
),
|
||||
onPressed: () {
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
|
@ -36,17 +39,12 @@ class WalletKeysButton extends StatelessWidget {
|
|||
},
|
||||
),
|
||||
);
|
||||
|
||||
// showDialog<void>(
|
||||
// context: context,
|
||||
// barrierDismissible: false,
|
||||
// builder: (context) => UnlockWalletKeysDesktop(
|
||||
// walletId: walletId,
|
||||
// ),
|
||||
// );
|
||||
},
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 19,
|
||||
horizontal: 32,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
|
|
|
@ -35,10 +35,13 @@ class _AdvancedSettings extends ConsumerState<AdvancedSettings> {
|
|||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
Assets.svg.circleLanguage,
|
||||
width: 48,
|
||||
height: 48,
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: SvgPicture.asset(
|
||||
Assets.svg.circleSliders,
|
||||
width: 48,
|
||||
height: 48,
|
||||
),
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
|
|
|
@ -52,10 +52,13 @@ class _AppearanceOptionSettings
|
|||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
Assets.svg.circleSun,
|
||||
width: 48,
|
||||
height: 48,
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: SvgPicture.asset(
|
||||
Assets.svg.circleSun,
|
||||
width: 48,
|
||||
height: 48,
|
||||
),
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
|
|
|
@ -492,7 +492,11 @@ class _CreateAutoBackup extends ConsumerState<CreateAutoBackup> {
|
|||
|
||||
return DropdownMenuItem(
|
||||
value: e,
|
||||
child: Text(message),
|
||||
child: Text(
|
||||
message,
|
||||
style: STextStyles.desktopTextExtraExtraSmall(
|
||||
context),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
|
@ -4,9 +4,9 @@ import 'package:flutter_svg/svg.dart';
|
|||
import 'package:stackwallet/pages/settings_views/global_settings_view/currency_view.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/desktop/desktop_dialog.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
|
||||
class CurrencySettings extends ConsumerStatefulWidget {
|
||||
|
@ -18,6 +18,41 @@ class CurrencySettings extends ConsumerStatefulWidget {
|
|||
ConsumerState<CurrencySettings> createState() => _CurrencySettings();
|
||||
}
|
||||
|
||||
Future<void> chooseCurrency(BuildContext context) async {
|
||||
await showDialog<dynamic>(
|
||||
context: context,
|
||||
useSafeArea: false,
|
||||
barrierDismissible: true,
|
||||
builder: (context) {
|
||||
return DesktopDialog(
|
||||
maxHeight: 800,
|
||||
maxWidth: 600,
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(32),
|
||||
child: Text(
|
||||
"Select currency",
|
||||
style: STextStyles.desktopH3(context),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
const DesktopDialogCloseButton(),
|
||||
],
|
||||
),
|
||||
const Expanded(
|
||||
child: BaseCurrencySettingsView(),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
class _CurrencySettings extends ConsumerState<CurrencySettings> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -32,10 +67,13 @@ class _CurrencySettings extends ConsumerState<CurrencySettings> {
|
|||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
Assets.svg.circleDollarSign,
|
||||
width: 48,
|
||||
height: 48,
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: SvgPicture.asset(
|
||||
Assets.svg.circleDollarSign,
|
||||
width: 48,
|
||||
height: 48,
|
||||
),
|
||||
),
|
||||
Center(
|
||||
child: Padding(
|
||||
|
@ -50,8 +88,8 @@ class _CurrencySettings extends ConsumerState<CurrencySettings> {
|
|||
),
|
||||
TextSpan(
|
||||
text:
|
||||
"\n\nProtect your Stack Wallet with a strong password. Stack Wallet does not store "
|
||||
"your password, and is therefore NOT able to restore it. Keep your password safe and secure.",
|
||||
"\n\nSelect a fiat currency to evaluate your crypto assets. We use CoinGecko conversion rates "
|
||||
"when displaying your balance and transaction amounts.",
|
||||
style:
|
||||
STextStyles.desktopTextExtraExtraSmall(context),
|
||||
),
|
||||
|
@ -62,12 +100,20 @@ class _CurrencySettings extends ConsumerState<CurrencySettings> {
|
|||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: const [
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.all(
|
||||
padding: const EdgeInsets.all(
|
||||
10,
|
||||
),
|
||||
child: NewPasswordButton(),
|
||||
child: PrimaryButton(
|
||||
width: 210,
|
||||
desktopMed: true,
|
||||
enabled: true,
|
||||
label: "Change currency",
|
||||
onPressed: () {
|
||||
chooseCurrency(context);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -79,71 +125,3 @@ class _CurrencySettings extends ConsumerState<CurrencySettings> {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
class NewPasswordButton extends ConsumerWidget {
|
||||
const NewPasswordButton({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
Future<void> chooseCurrency(BuildContext context) async {
|
||||
// await showDialog<dynamic>(
|
||||
// context: context,
|
||||
// useSafeArea: false,
|
||||
// barrierDismissible: true,
|
||||
// builder: (context) {
|
||||
// return CurrencyDialog();
|
||||
// },
|
||||
// );
|
||||
await showDialog<dynamic>(
|
||||
context: context,
|
||||
useSafeArea: false,
|
||||
barrierDismissible: true,
|
||||
builder: (context) {
|
||||
return DesktopDialog(
|
||||
maxHeight: 800,
|
||||
maxWidth: 600,
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(32),
|
||||
child: Text(
|
||||
"Select currency",
|
||||
style: STextStyles.desktopH3(context),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
const DesktopDialogCloseButton(),
|
||||
],
|
||||
),
|
||||
const Expanded(
|
||||
child: BaseCurrencySettingsView(),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return SizedBox(
|
||||
width: 200,
|
||||
height: 48,
|
||||
child: TextButton(
|
||||
style: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.getPrimaryEnabledButtonColor(context),
|
||||
onPressed: () {
|
||||
chooseCurrency(context);
|
||||
},
|
||||
child: Text(
|
||||
"Change currency",
|
||||
style: STextStyles.button(context),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import 'package:flutter_svg/svg.dart';
|
|||
import 'package:stackwallet/pages_desktop_specific/home/settings_menu/language_settings/language_dialog.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/desktop/primary_button.dart';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
|
||||
class LanguageOptionSettings extends ConsumerStatefulWidget {
|
||||
|
@ -17,6 +17,17 @@ class LanguageOptionSettings extends ConsumerStatefulWidget {
|
|||
_LanguageOptionSettings();
|
||||
}
|
||||
|
||||
Future<void> chooseLanguage(BuildContext context) async {
|
||||
await showDialog<dynamic>(
|
||||
context: context,
|
||||
useSafeArea: false,
|
||||
barrierDismissible: true,
|
||||
builder: (context) {
|
||||
return const LanguageDialog();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
class _LanguageOptionSettings extends ConsumerState<LanguageOptionSettings> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -31,10 +42,13 @@ class _LanguageOptionSettings extends ConsumerState<LanguageOptionSettings> {
|
|||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
Assets.svg.circleLanguage,
|
||||
width: 48,
|
||||
height: 48,
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: SvgPicture.asset(
|
||||
Assets.svg.circleLanguage,
|
||||
width: 48,
|
||||
height: 48,
|
||||
),
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
|
@ -63,12 +77,20 @@ class _LanguageOptionSettings extends ConsumerState<LanguageOptionSettings> {
|
|||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: const [
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.all(
|
||||
10,
|
||||
),
|
||||
child: ChangeLanguageButton(),
|
||||
child: PrimaryButton(
|
||||
width: 210,
|
||||
desktopMed: true,
|
||||
enabled: true,
|
||||
label: "Change language",
|
||||
onPressed: () {
|
||||
chooseLanguage(context);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -80,40 +102,3 @@ class _LanguageOptionSettings extends ConsumerState<LanguageOptionSettings> {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ChangeLanguageButton extends ConsumerWidget {
|
||||
const ChangeLanguageButton({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
Future<void> chooseLanguage(BuildContext context) async {
|
||||
await showDialog<dynamic>(
|
||||
context: context,
|
||||
useSafeArea: false,
|
||||
barrierDismissible: true,
|
||||
builder: (context) {
|
||||
return const LanguageDialog();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return SizedBox(
|
||||
width: 200,
|
||||
height: 48,
|
||||
child: TextButton(
|
||||
style: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.getPrimaryEnabledButtonColor(context),
|
||||
onPressed: () {
|
||||
chooseLanguage(context);
|
||||
},
|
||||
child: Text(
|
||||
"Change language",
|
||||
style: STextStyles.button(context),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:stackwallet/pages/settings_views/global_settings_view/manage_nodes_views/coin_nodes_view.dart';
|
||||
import 'package:stackwallet/providers/global/node_service_provider.dart';
|
||||
import 'package:stackwallet/providers/global/prefs_provider.dart';
|
||||
import 'package:stackwallet/providers/providers.dart';
|
||||
import 'package:stackwallet/route_generator.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
|
@ -11,8 +10,10 @@ 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/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 NodesSettings extends ConsumerStatefulWidget {
|
||||
const NodesSettings({Key? key}) : super(key: key);
|
||||
|
@ -29,8 +30,22 @@ class _NodesSettings extends ConsumerState<NodesSettings> {
|
|||
late final TextEditingController searchNodeController;
|
||||
late final FocusNode searchNodeFocusNode;
|
||||
|
||||
late final ScrollController nodeScrollController;
|
||||
|
||||
String filter = "";
|
||||
|
||||
List<Coin> _search(String filter, List<Coin> coins) {
|
||||
if (filter.isEmpty) {
|
||||
return coins;
|
||||
}
|
||||
return coins
|
||||
.where((coin) =>
|
||||
coin.prettyName.contains(filter) ||
|
||||
coin.name.contains(filter) ||
|
||||
coin.ticker.toLowerCase().contains(filter.toLowerCase()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_coins = _coins.toList();
|
||||
|
@ -39,6 +54,8 @@ class _NodesSettings extends ConsumerState<NodesSettings> {
|
|||
searchNodeController = TextEditingController();
|
||||
searchNodeFocusNode = FocusNode();
|
||||
|
||||
nodeScrollController = ScrollController();
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
|
@ -46,12 +63,15 @@ class _NodesSettings extends ConsumerState<NodesSettings> {
|
|||
void dispose() {
|
||||
searchNodeController.dispose();
|
||||
searchNodeFocusNode.dispose();
|
||||
nodeScrollController.dispose();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
debugPrint("BUILD: $runtimeType");
|
||||
|
||||
bool showTestNet = ref.watch(
|
||||
prefsChangeNotifierProvider.select((value) => value.showTestNetCoins),
|
||||
);
|
||||
|
@ -60,191 +80,218 @@ class _NodesSettings extends ConsumerState<NodesSettings> {
|
|||
? _coins
|
||||
: _coins.sublist(0, _coins.length - kTestNetCoinCount);
|
||||
|
||||
debugPrint("BUILD: $runtimeType");
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
right: 30,
|
||||
),
|
||||
child: RoundedWhiteContainer(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
Assets.svg.circleNode,
|
||||
width: 48,
|
||||
height: 48,
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: RichText(
|
||||
textAlign: TextAlign.start,
|
||||
text: TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: "Nodes",
|
||||
style: STextStyles.desktopTextSmall(context),
|
||||
),
|
||||
TextSpan(
|
||||
text: "\n\nSelect a coin to see nodes",
|
||||
style: STextStyles.desktopTextExtraExtraSmall(
|
||||
context),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
child: TextField(
|
||||
autocorrect: Util.isDesktop ? false : true,
|
||||
enableSuggestions: Util.isDesktop ? false : true,
|
||||
controller: searchNodeController,
|
||||
focusNode: searchNodeFocusNode,
|
||||
onChanged: (newString) {
|
||||
setState(() => filter = newString);
|
||||
},
|
||||
style: STextStyles.field(context),
|
||||
decoration: standardInputDecoration(
|
||||
"Search",
|
||||
searchNodeFocusNode,
|
||||
context,
|
||||
).copyWith(
|
||||
prefixIcon: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
vertical: 16,
|
||||
),
|
||||
child: SvgPicture.asset(
|
||||
Assets.svg.search,
|
||||
width: 16,
|
||||
height: 16,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: RoundedWhiteContainer(
|
||||
padding: const EdgeInsets.all(0),
|
||||
borderColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
child: ListView.separated(
|
||||
primary: false,
|
||||
shrinkWrap: true,
|
||||
itemBuilder: (context, index) {
|
||||
final coin = coins[index];
|
||||
final count = ref
|
||||
.watch(nodeServiceChangeNotifierProvider
|
||||
.select((value) => value.getNodesFor(coin)))
|
||||
.length;
|
||||
coins = _search(filter, coins);
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(0),
|
||||
child: RawMaterialButton(
|
||||
// splashColor: Theme.of(context).extension<StackColors>()!.highlight,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
right: 32,
|
||||
bottom: 32,
|
||||
),
|
||||
child: RoundedWhiteContainer(
|
||||
radiusMultiplier: 2,
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
Assets.svg.circleNode,
|
||||
width: 48,
|
||||
height: 48,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
Text(
|
||||
"Nodes",
|
||||
style: STextStyles.desktopTextSmall(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
Text(
|
||||
"Select a coin to see nodes",
|
||||
style: STextStyles.desktopTextExtraExtraSmall(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
child: TextField(
|
||||
autocorrect: Util.isDesktop ? false : true,
|
||||
enableSuggestions: Util.isDesktop ? false : true,
|
||||
controller: searchNodeController,
|
||||
focusNode: searchNodeFocusNode,
|
||||
onChanged: (newString) {
|
||||
setState(() => filter = newString);
|
||||
},
|
||||
style: STextStyles.field(context),
|
||||
decoration: standardInputDecoration(
|
||||
"Search",
|
||||
searchNodeFocusNode,
|
||||
context,
|
||||
).copyWith(
|
||||
prefixIcon: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
vertical: 16,
|
||||
),
|
||||
child: SvgPicture.asset(
|
||||
Assets.svg.search,
|
||||
width: 16,
|
||||
height: 16,
|
||||
),
|
||||
),
|
||||
materialTapTargetSize:
|
||||
MaterialTapTargetSize.shrinkWrap,
|
||||
onPressed: () {
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
builder: (context) => Navigator(
|
||||
initialRoute: CoinNodesView.routeName,
|
||||
onGenerateRoute: RouteGenerator.generateRoute,
|
||||
onGenerateInitialRoutes: (_, __) {
|
||||
return [
|
||||
FadePageRoute(
|
||||
CoinNodesView(
|
||||
coin: coin,
|
||||
rootNavigator: true,
|
||||
),
|
||||
const RouteSettings(
|
||||
name: CoinNodesView.routeName,
|
||||
),
|
||||
),
|
||||
];
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(
|
||||
12.0,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
Assets.svg.iconFor(coin: coin),
|
||||
width: 24,
|
||||
height: 24,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
suffixIcon: searchNodeController.text.isNotEmpty
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(right: 0),
|
||||
child: UnconstrainedBox(
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
"${coin.prettyName} nodes",
|
||||
style: STextStyles.titleBold12(
|
||||
context),
|
||||
),
|
||||
Text(
|
||||
count > 1
|
||||
? "$count nodes"
|
||||
: "Default",
|
||||
style: STextStyles.label(context),
|
||||
TextFieldIconButton(
|
||||
child: const XIcon(),
|
||||
onTap: () async {
|
||||
setState(() {
|
||||
searchNodeController.text = "";
|
||||
filter = "";
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
child: SvgPicture.asset(
|
||||
Assets.svg.chevronRight,
|
||||
alignment: Alignment.centerRight,
|
||||
),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Flexible(
|
||||
child: RoundedWhiteContainer(
|
||||
padding: const EdgeInsets.all(0),
|
||||
borderColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.background,
|
||||
child: ListView.separated(
|
||||
controller: nodeScrollController,
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
scrollDirection: Axis.vertical,
|
||||
primary: false,
|
||||
shrinkWrap: true,
|
||||
itemBuilder: (context, index) {
|
||||
final coin = coins[index];
|
||||
final count = ref
|
||||
.watch(nodeServiceChangeNotifierProvider
|
||||
.select((value) => value.getNodesFor(coin)))
|
||||
.length;
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(0),
|
||||
child: RawMaterialButton(
|
||||
// splashColor: Theme.of(context).extension<StackColors>()!.highlight,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
),
|
||||
materialTapTargetSize:
|
||||
MaterialTapTargetSize.shrinkWrap,
|
||||
onPressed: () {
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
builder: (context) => Navigator(
|
||||
initialRoute: CoinNodesView.routeName,
|
||||
onGenerateRoute:
|
||||
RouteGenerator.generateRoute,
|
||||
onGenerateInitialRoutes: (_, __) {
|
||||
return [
|
||||
FadePageRoute(
|
||||
CoinNodesView(
|
||||
coin: coin,
|
||||
rootNavigator: true,
|
||||
),
|
||||
const RouteSettings(
|
||||
name: CoinNodesView.routeName,
|
||||
),
|
||||
),
|
||||
];
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(
|
||||
12.0,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
Assets.svg.iconFor(coin: coin),
|
||||
width: 24,
|
||||
height: 24,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"${coin.prettyName} nodes",
|
||||
style: STextStyles.titleBold12(
|
||||
context),
|
||||
),
|
||||
Text(
|
||||
count > 1
|
||||
? "$count nodes"
|
||||
: "Default",
|
||||
style: STextStyles.label(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
child: SvgPicture.asset(
|
||||
Assets.svg.chevronRight,
|
||||
alignment: Alignment.centerRight,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
separatorBuilder: (context, index) => Container(
|
||||
height: 1,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.background,
|
||||
);
|
||||
},
|
||||
separatorBuilder: (context, index) => Container(
|
||||
height: 1,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.background,
|
||||
),
|
||||
itemCount: coins.length,
|
||||
),
|
||||
itemCount: coins.length,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -139,392 +139,385 @@ class _SecuritySettings extends ConsumerState<SecuritySettings> {
|
|||
debugPrint("BUILD: $runtimeType");
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: RoundedWhiteContainer(
|
||||
radiusMultiplier: 2,
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
Assets.svg.circleLock,
|
||||
width: 48,
|
||||
height: 48,
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
Text(
|
||||
"Change Password",
|
||||
style: STextStyles.desktopTextSmall(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
Text(
|
||||
"Protect your Stack Wallet with a strong password. Stack Wallet does not store "
|
||||
"your password, and is therefore NOT able to restore it. Keep your password safe and secure.",
|
||||
style:
|
||||
STextStyles.desktopTextExtraExtraSmall(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
changePassword
|
||||
? SizedBox(
|
||||
width: 512,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Current password",
|
||||
style: STextStyles
|
||||
.desktopTextExtraExtraSmall(
|
||||
context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark3),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
child: TextField(
|
||||
key: const Key(
|
||||
"desktopSecurityRestoreFromFilePasswordFieldKey"),
|
||||
focusNode: passwordCurrentFocusNode,
|
||||
controller: passwordCurrentController,
|
||||
style: STextStyles.field(context),
|
||||
obscureText: hidePassword,
|
||||
enableSuggestions: false,
|
||||
autocorrect: false,
|
||||
decoration: standardInputDecoration(
|
||||
"Enter current password",
|
||||
passwordCurrentFocusNode,
|
||||
context,
|
||||
).copyWith(
|
||||
labelStyle:
|
||||
STextStyles.fieldLabel(context),
|
||||
suffixIcon: UnconstrainedBox(
|
||||
child: Row(
|
||||
children: [
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
GestureDetector(
|
||||
key: const Key(
|
||||
"desktopSecurityRestoreFromFilePasswordFieldShowPasswordButtonKey"),
|
||||
onTap: () async {
|
||||
setState(() {
|
||||
hidePassword =
|
||||
!hidePassword;
|
||||
});
|
||||
},
|
||||
child: SvgPicture.asset(
|
||||
hidePassword
|
||||
? Assets.svg.eye
|
||||
: Assets.svg.eyeSlash,
|
||||
color: Theme.of(context)
|
||||
.extension<
|
||||
StackColors>()!
|
||||
.textDark3,
|
||||
width: 16,
|
||||
height: 16,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
onChanged: (newValue) {
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
"New password",
|
||||
style: STextStyles
|
||||
.desktopTextExtraExtraSmall(
|
||||
context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark3),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
child: TextField(
|
||||
key: const Key(
|
||||
"desktopSecurityCreateNewPasswordFieldKey1"),
|
||||
focusNode: passwordFocusNode,
|
||||
controller: passwordController,
|
||||
style: STextStyles.field(context),
|
||||
obscureText: hidePassword,
|
||||
enableSuggestions: false,
|
||||
autocorrect: false,
|
||||
decoration: standardInputDecoration(
|
||||
"Enter new password",
|
||||
passwordFocusNode,
|
||||
context,
|
||||
).copyWith(
|
||||
labelStyle:
|
||||
STextStyles.fieldLabel(context),
|
||||
suffixIcon: UnconstrainedBox(
|
||||
child: Row(
|
||||
children: [
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
GestureDetector(
|
||||
key: const Key(
|
||||
"desktopSecurityCreateNewPasswordButtonKey1"),
|
||||
onTap: () async {
|
||||
setState(() {
|
||||
hidePassword =
|
||||
!hidePassword;
|
||||
});
|
||||
},
|
||||
child: SvgPicture.asset(
|
||||
hidePassword
|
||||
? Assets.svg.eye
|
||||
: Assets.svg.eyeSlash,
|
||||
color: Theme.of(context)
|
||||
.extension<
|
||||
StackColors>()!
|
||||
.textDark3,
|
||||
width: 16,
|
||||
height: 16,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
onChanged: (newValue) {
|
||||
if (newValue.isEmpty) {
|
||||
setState(() {
|
||||
passwordFeedback = "";
|
||||
});
|
||||
return;
|
||||
}
|
||||
final result =
|
||||
zxcvbn.evaluate(newValue);
|
||||
String suggestionsAndTips = "";
|
||||
for (var sug in result
|
||||
.feedback.suggestions!
|
||||
.toSet()) {
|
||||
suggestionsAndTips += "$sug\n";
|
||||
}
|
||||
suggestionsAndTips +=
|
||||
result.feedback.warning!;
|
||||
String feedback =
|
||||
// "Password Strength: ${((result.score! / 4.0) * 100).toInt()}%\n"
|
||||
suggestionsAndTips;
|
||||
|
||||
passwordStrength = result.score! / 4;
|
||||
|
||||
// hack fix to format back string returned from zxcvbn
|
||||
if (feedback
|
||||
.contains("phrasesNo need")) {
|
||||
feedback = feedback.replaceFirst(
|
||||
"phrasesNo need",
|
||||
"phrases\nNo need");
|
||||
}
|
||||
|
||||
if (feedback.endsWith("\n")) {
|
||||
feedback = feedback.substring(
|
||||
0, feedback.length - 2);
|
||||
}
|
||||
|
||||
setState(() {
|
||||
passwordFeedback = feedback;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
if (passwordFocusNode.hasFocus ||
|
||||
passwordRepeatFocusNode.hasFocus ||
|
||||
passwordController.text.isNotEmpty)
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: 12,
|
||||
right: 12,
|
||||
top: passwordFeedback.isNotEmpty
|
||||
? 4
|
||||
: 0,
|
||||
),
|
||||
child: passwordFeedback.isNotEmpty
|
||||
? Text(
|
||||
passwordFeedback,
|
||||
style: STextStyles.infoSmall(
|
||||
context),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
if (passwordFocusNode.hasFocus ||
|
||||
passwordRepeatFocusNode.hasFocus ||
|
||||
passwordController.text.isNotEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 12,
|
||||
right: 12,
|
||||
top: 10,
|
||||
),
|
||||
child: ProgressBar(
|
||||
key: const Key(
|
||||
"desktopSecurityCreateStackBackUpProgressBar"),
|
||||
width: 450,
|
||||
height: 5,
|
||||
fillColor: passwordStrength < 0.51
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorRed
|
||||
: passwordStrength < 1
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorYellow
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorGreen,
|
||||
backgroundColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.buttonBackSecondary,
|
||||
percent: passwordStrength < 0.25
|
||||
? 0.03
|
||||
: passwordStrength,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
"Confirm new password",
|
||||
style: STextStyles
|
||||
.desktopTextExtraExtraSmall(
|
||||
context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark3),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
child: TextField(
|
||||
key: const Key(
|
||||
"desktopSecurityCreateNewPasswordFieldKey2"),
|
||||
focusNode: passwordRepeatFocusNode,
|
||||
controller: passwordRepeatController,
|
||||
style: STextStyles.field(context),
|
||||
obscureText: hidePassword,
|
||||
enableSuggestions: false,
|
||||
autocorrect: false,
|
||||
decoration: standardInputDecoration(
|
||||
"Confirm new password",
|
||||
passwordRepeatFocusNode,
|
||||
context,
|
||||
).copyWith(
|
||||
labelStyle:
|
||||
STextStyles.fieldLabel(context),
|
||||
suffixIcon: UnconstrainedBox(
|
||||
child: Row(
|
||||
children: [
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
GestureDetector(
|
||||
key: const Key(
|
||||
"desktopSecurityCreateNewPasswordButtonKey2"),
|
||||
onTap: () async {
|
||||
setState(() {
|
||||
hidePassword =
|
||||
!hidePassword;
|
||||
});
|
||||
},
|
||||
child: SvgPicture.asset(
|
||||
hidePassword
|
||||
? Assets.svg.eye
|
||||
: Assets.svg.eyeSlash,
|
||||
color: Theme.of(context)
|
||||
.extension<
|
||||
StackColors>()!
|
||||
.textDark3,
|
||||
width: 16,
|
||||
height: 16,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
onChanged: (newValue) {
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
PrimaryButton(
|
||||
width: 160,
|
||||
desktopMed: true,
|
||||
enabled: shouldEnableSave,
|
||||
label: "Save changes",
|
||||
onPressed: () async {
|
||||
final didChangePW =
|
||||
await attemptChangePW();
|
||||
if (didChangePW) {
|
||||
setState(() {
|
||||
changePassword = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
: PrimaryButton(
|
||||
width: 210,
|
||||
desktopMed: true,
|
||||
enabled: true,
|
||||
label: "Set up new password",
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
changePassword = true;
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
right: 30,
|
||||
),
|
||||
child: RoundedWhiteContainer(
|
||||
// radiusMultiplier: 2,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: SvgPicture.asset(
|
||||
Assets.svg.circleLock,
|
||||
width: 48,
|
||||
height: 48,
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Change Password",
|
||||
style: STextStyles.desktopTextSmall(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
Text(
|
||||
"Protect your Stack Wallet with a strong password. Stack Wallet does not store "
|
||||
"your password, and is therefore NOT able to restore it. Keep your password safe and secure.",
|
||||
style: STextStyles.desktopTextExtraExtraSmall(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
changePassword
|
||||
? SizedBox(
|
||||
width: 512,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Current password",
|
||||
style:
|
||||
STextStyles.desktopTextExtraExtraSmall(
|
||||
context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark3),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
child: TextField(
|
||||
key: const Key(
|
||||
"desktopSecurityRestoreFromFilePasswordFieldKey"),
|
||||
focusNode: passwordCurrentFocusNode,
|
||||
controller: passwordCurrentController,
|
||||
style: STextStyles.field(context),
|
||||
obscureText: hidePassword,
|
||||
enableSuggestions: false,
|
||||
autocorrect: false,
|
||||
decoration: standardInputDecoration(
|
||||
"Enter current password",
|
||||
passwordCurrentFocusNode,
|
||||
context,
|
||||
).copyWith(
|
||||
labelStyle:
|
||||
STextStyles.fieldLabel(context),
|
||||
suffixIcon: UnconstrainedBox(
|
||||
child: Row(
|
||||
children: [
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
GestureDetector(
|
||||
key: const Key(
|
||||
"desktopSecurityRestoreFromFilePasswordFieldShowPasswordButtonKey"),
|
||||
onTap: () async {
|
||||
setState(() {
|
||||
hidePassword =
|
||||
!hidePassword;
|
||||
});
|
||||
},
|
||||
child: SvgPicture.asset(
|
||||
hidePassword
|
||||
? Assets.svg.eye
|
||||
: Assets.svg.eyeSlash,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark3,
|
||||
width: 16,
|
||||
height: 16,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
onChanged: (newValue) {
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
"New password",
|
||||
style:
|
||||
STextStyles.desktopTextExtraExtraSmall(
|
||||
context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark3),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
child: TextField(
|
||||
key: const Key(
|
||||
"desktopSecurityCreateNewPasswordFieldKey1"),
|
||||
focusNode: passwordFocusNode,
|
||||
controller: passwordController,
|
||||
style: STextStyles.field(context),
|
||||
obscureText: hidePassword,
|
||||
enableSuggestions: false,
|
||||
autocorrect: false,
|
||||
decoration: standardInputDecoration(
|
||||
"Enter new password",
|
||||
passwordFocusNode,
|
||||
context,
|
||||
).copyWith(
|
||||
labelStyle:
|
||||
STextStyles.fieldLabel(context),
|
||||
suffixIcon: UnconstrainedBox(
|
||||
child: Row(
|
||||
children: [
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
GestureDetector(
|
||||
key: const Key(
|
||||
"desktopSecurityCreateNewPasswordButtonKey1"),
|
||||
onTap: () async {
|
||||
setState(() {
|
||||
hidePassword =
|
||||
!hidePassword;
|
||||
});
|
||||
},
|
||||
child: SvgPicture.asset(
|
||||
hidePassword
|
||||
? Assets.svg.eye
|
||||
: Assets.svg.eyeSlash,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark3,
|
||||
width: 16,
|
||||
height: 16,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
onChanged: (newValue) {
|
||||
if (newValue.isEmpty) {
|
||||
setState(() {
|
||||
passwordFeedback = "";
|
||||
});
|
||||
return;
|
||||
}
|
||||
final result =
|
||||
zxcvbn.evaluate(newValue);
|
||||
String suggestionsAndTips = "";
|
||||
for (var sug in result
|
||||
.feedback.suggestions!
|
||||
.toSet()) {
|
||||
suggestionsAndTips += "$sug\n";
|
||||
}
|
||||
suggestionsAndTips +=
|
||||
result.feedback.warning!;
|
||||
String feedback =
|
||||
// "Password Strength: ${((result.score! / 4.0) * 100).toInt()}%\n"
|
||||
suggestionsAndTips;
|
||||
|
||||
passwordStrength = result.score! / 4;
|
||||
|
||||
// hack fix to format back string returned from zxcvbn
|
||||
if (feedback
|
||||
.contains("phrasesNo need")) {
|
||||
feedback = feedback.replaceFirst(
|
||||
"phrasesNo need",
|
||||
"phrases\nNo need");
|
||||
}
|
||||
|
||||
if (feedback.endsWith("\n")) {
|
||||
feedback = feedback.substring(
|
||||
0, feedback.length - 2);
|
||||
}
|
||||
|
||||
setState(() {
|
||||
passwordFeedback = feedback;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
if (passwordFocusNode.hasFocus ||
|
||||
passwordRepeatFocusNode.hasFocus ||
|
||||
passwordController.text.isNotEmpty)
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: 12,
|
||||
right: 12,
|
||||
top:
|
||||
passwordFeedback.isNotEmpty ? 4 : 0,
|
||||
),
|
||||
child: passwordFeedback.isNotEmpty
|
||||
? Text(
|
||||
passwordFeedback,
|
||||
style: STextStyles.infoSmall(
|
||||
context),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
if (passwordFocusNode.hasFocus ||
|
||||
passwordRepeatFocusNode.hasFocus ||
|
||||
passwordController.text.isNotEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 12,
|
||||
right: 12,
|
||||
top: 10,
|
||||
),
|
||||
child: ProgressBar(
|
||||
key: const Key(
|
||||
"desktopSecurityCreateStackBackUpProgressBar"),
|
||||
width: 450,
|
||||
height: 5,
|
||||
fillColor: passwordStrength < 0.51
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorRed
|
||||
: passwordStrength < 1
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorYellow
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorGreen,
|
||||
backgroundColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.buttonBackSecondary,
|
||||
percent: passwordStrength < 0.25
|
||||
? 0.03
|
||||
: passwordStrength,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
"Confirm new password",
|
||||
style:
|
||||
STextStyles.desktopTextExtraExtraSmall(
|
||||
context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark3),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
child: TextField(
|
||||
key: const Key(
|
||||
"desktopSecurityCreateNewPasswordFieldKey2"),
|
||||
focusNode: passwordRepeatFocusNode,
|
||||
controller: passwordRepeatController,
|
||||
style: STextStyles.field(context),
|
||||
obscureText: hidePassword,
|
||||
enableSuggestions: false,
|
||||
autocorrect: false,
|
||||
decoration: standardInputDecoration(
|
||||
"Confirm new password",
|
||||
passwordRepeatFocusNode,
|
||||
context,
|
||||
).copyWith(
|
||||
labelStyle:
|
||||
STextStyles.fieldLabel(context),
|
||||
suffixIcon: UnconstrainedBox(
|
||||
child: Row(
|
||||
children: [
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
GestureDetector(
|
||||
key: const Key(
|
||||
"desktopSecurityCreateNewPasswordButtonKey2"),
|
||||
onTap: () async {
|
||||
setState(() {
|
||||
hidePassword =
|
||||
!hidePassword;
|
||||
});
|
||||
},
|
||||
child: SvgPicture.asset(
|
||||
hidePassword
|
||||
? Assets.svg.eye
|
||||
: Assets.svg.eyeSlash,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark3,
|
||||
width: 16,
|
||||
height: 16,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
onChanged: (newValue) {
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
PrimaryButton(
|
||||
width: 160,
|
||||
desktopMed: true,
|
||||
enabled: shouldEnableSave,
|
||||
label: "Save changes",
|
||||
onPressed: () async {
|
||||
final didChangePW =
|
||||
await attemptChangePW();
|
||||
if (didChangePW) {
|
||||
setState(() {
|
||||
changePassword = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
: PrimaryButton(
|
||||
width: 210,
|
||||
desktopMed: true,
|
||||
enabled: true,
|
||||
label: "Set up new password",
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
changePassword = true;
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
width: 40,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
|
|
@ -2,13 +2,12 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter/src/widgets/framework.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:stackwallet/pages/settings_views/global_settings_view/syncing_preferences_views/syncing_options_view.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/desktop/primary_button.dart';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
|
||||
import '../../../pages/settings_views/global_settings_view/syncing_preferences_views/syncing_options_view.dart';
|
||||
|
||||
class SyncingPreferencesSettings extends ConsumerStatefulWidget {
|
||||
const SyncingPreferencesSettings({Key? key}) : super(key: key);
|
||||
|
||||
|
@ -34,10 +33,13 @@ class _SyncingPreferencesSettings
|
|||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
Assets.svg.circleArrowRotate,
|
||||
width: 48,
|
||||
height: 48,
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: SvgPicture.asset(
|
||||
Assets.svg.circleArrowRotate,
|
||||
width: 48,
|
||||
height: 48,
|
||||
),
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
|
@ -54,7 +56,7 @@ class _SyncingPreferencesSettings
|
|||
),
|
||||
TextSpan(
|
||||
text:
|
||||
"\nSet up your syncing preferences for all wallets in your Stack.",
|
||||
"\n\nSet up your syncing preferences for all wallets in your Stack.",
|
||||
style: STextStyles.desktopTextExtraExtraSmall(
|
||||
context),
|
||||
),
|
||||
|
@ -67,18 +69,24 @@ class _SyncingPreferencesSettings
|
|||
|
||||
///TODO: ONLY SHOW SYNC OPTIONS ON BUTTON PRESS
|
||||
Column(
|
||||
children: [
|
||||
children: const [
|
||||
SyncingOptionsView(),
|
||||
],
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: const [
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.all(
|
||||
padding: const EdgeInsets.all(
|
||||
10,
|
||||
),
|
||||
child: ChangePrefButton(),
|
||||
child: PrimaryButton(
|
||||
width: 210,
|
||||
desktopMed: true,
|
||||
enabled: true,
|
||||
label: "Change preferences",
|
||||
onPressed: () {},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -90,26 +98,3 @@ class _SyncingPreferencesSettings
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ChangePrefButton extends ConsumerWidget {
|
||||
const ChangePrefButton({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return SizedBox(
|
||||
width: 200,
|
||||
height: 48,
|
||||
child: TextButton(
|
||||
style: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.getPrimaryEnabledButtonColor(context),
|
||||
onPressed: () {},
|
||||
child: Text(
|
||||
"Change preferences",
|
||||
style: STextStyles.button(context),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,6 +59,7 @@ class _SVG {
|
|||
String txExchangeFailed(BuildContext context) =>
|
||||
"assets/svg/${Theme.of(context).extension<StackColors>()!.themeType.name}/tx-exchange-icon-failed.svg";
|
||||
|
||||
String get circleSliders => "assets/svg/configuration.svg";
|
||||
String get circlePlus => "assets/svg/plus-circle.svg";
|
||||
String get framedGear => "assets/svg/framed-gear.svg";
|
||||
String get framedAddressBook => "assets/svg/framed-address-book.svg";
|
||||
|
|
|
@ -9,7 +9,6 @@ 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/conditional_parent.dart';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
|
||||
class AddressBookCard extends ConsumerStatefulWidget {
|
||||
|
@ -59,111 +58,108 @@ class _AddressBookCardState extends ConsumerState<AddressBookCard> {
|
|||
}
|
||||
}
|
||||
|
||||
return ConditionalParent(
|
||||
condition: !isDesktop,
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 32,
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
color: contact.id == "default"
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.myStackContactIconBG
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldDefaultBG,
|
||||
borderRadius: BorderRadius.circular(32),
|
||||
return RoundedWhiteContainer(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: RawMaterialButton(
|
||||
// splashColor: Theme.of(context).extension<StackColors>()!.highlight,
|
||||
padding: const EdgeInsets.all(0),
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
useSafeArea: true,
|
||||
barrierDismissible: true,
|
||||
builder: (_) => ContactPopUp(
|
||||
contactId: contact.id,
|
||||
),
|
||||
child: contact.id == "default"
|
||||
? Center(
|
||||
child: SvgPicture.asset(
|
||||
Assets.svg.stackIcon(context),
|
||||
width: 20,
|
||||
),
|
||||
)
|
||||
: contact.emojiChar != null
|
||||
);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 32,
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
color: contact.id == "default"
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.myStackContactIconBG
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldDefaultBG,
|
||||
borderRadius: BorderRadius.circular(32),
|
||||
),
|
||||
child: contact.id == "default"
|
||||
? Center(
|
||||
child: Text(contact.emojiChar!),
|
||||
)
|
||||
: Center(
|
||||
child: SvgPicture.asset(
|
||||
Assets.svg.user,
|
||||
width: 18,
|
||||
Assets.svg.stackIcon(context),
|
||||
width: 20,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
if (isDesktop)
|
||||
Text(
|
||||
contact.name,
|
||||
style: STextStyles.itemSubtitle12(context),
|
||||
),
|
||||
if (isDesktop)
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
if (isDesktop)
|
||||
Text(
|
||||
coinsString,
|
||||
style: STextStyles.label(context),
|
||||
),
|
||||
if (!isDesktop)
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
)
|
||||
: contact.emojiChar != null
|
||||
? Center(
|
||||
child: Text(contact.emojiChar!),
|
||||
)
|
||||
: Center(
|
||||
child: SvgPicture.asset(
|
||||
Assets.svg.user,
|
||||
width: 18,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
if (isDesktop)
|
||||
Text(
|
||||
contact.name,
|
||||
style: STextStyles.itemSubtitle12(context),
|
||||
),
|
||||
if (isDesktop)
|
||||
const SizedBox(
|
||||
height: 4,
|
||||
width: 16,
|
||||
),
|
||||
if (isDesktop)
|
||||
Text(
|
||||
coinsString,
|
||||
style: STextStyles.label(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (isDesktop) const Spacer(),
|
||||
if (isDesktop)
|
||||
SvgPicture.asset(
|
||||
widget.indicatorDown == true
|
||||
? Assets.svg.chevronDown
|
||||
: Assets.svg.chevronUp,
|
||||
width: 10,
|
||||
height: 5,
|
||||
color: Theme.of(context).extension<StackColors>()!.textSubtitle2,
|
||||
),
|
||||
],
|
||||
),
|
||||
builder: (child) => RoundedWhiteContainer(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: RawMaterialButton(
|
||||
// splashColor: Theme.of(context).extension<StackColors>()!.highlight,
|
||||
padding: const EdgeInsets.all(0),
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
useSafeArea: true,
|
||||
barrierDismissible: true,
|
||||
builder: (_) => ContactPopUp(
|
||||
contactId: contact.id,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: child,
|
||||
if (!isDesktop)
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
contact.name,
|
||||
style: STextStyles.itemSubtitle12(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
Text(
|
||||
coinsString,
|
||||
style: STextStyles.label(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (isDesktop) const Spacer(),
|
||||
// if (isDesktop)
|
||||
// SvgPicture.asset(
|
||||
// widget.indicatorDown == true
|
||||
// ? Assets.svg.chevronDown
|
||||
// : Assets.svg.chevronUp,
|
||||
// width: 10,
|
||||
// height: 5,
|
||||
// color:
|
||||
// Theme.of(context).extension<StackColors>()!.textSubtitle2,
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -89,11 +89,14 @@ class _ExpandableState extends State<Expandable> with TickerProviderStateMixin {
|
|||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: toggle,
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
child: widget.header,
|
||||
MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: GestureDetector(
|
||||
onTap: toggle,
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
child: widget.header,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizeTransition(
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:stackwallet/widgets/table_view/table_view_row.dart';
|
||||
|
||||
class TableView extends StatefulWidget {
|
||||
const TableView({
|
||||
|
@ -9,7 +8,7 @@ class TableView extends StatefulWidget {
|
|||
this.shrinkWrap = false,
|
||||
}) : super(key: key);
|
||||
|
||||
final List<TableViewRow> rows;
|
||||
final List<Widget> rows;
|
||||
final double rowSpacing;
|
||||
final bool shrinkWrap;
|
||||
|
||||
|
|
|
@ -26,53 +26,58 @@ class WalletInfoRow extends ConsumerWidget {
|
|||
.getManagerProvider(walletId));
|
||||
|
||||
if (Util.isDesktop) {
|
||||
return GestureDetector(
|
||||
onTap: onPressed,
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 4,
|
||||
child: Row(
|
||||
children: [
|
||||
WalletInfoCoinIcon(coin: manager.coin),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
Text(
|
||||
manager.walletName,
|
||||
style: STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||
color:
|
||||
Theme.of(context).extension<StackColors>()!.textDark,
|
||||
return MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: GestureDetector(
|
||||
onTap: onPressed,
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 4,
|
||||
child: Row(
|
||||
children: [
|
||||
WalletInfoCoinIcon(coin: manager.coin),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
Text(
|
||||
manager.walletName,
|
||||
style:
|
||||
STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 4,
|
||||
child: WalletInfoRowBalanceFuture(
|
||||
walletId: walletId,
|
||||
Expanded(
|
||||
flex: 4,
|
||||
child: WalletInfoRowBalanceFuture(
|
||||
walletId: walletId,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 6,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
Assets.svg.chevronRight,
|
||||
width: 20,
|
||||
height: 20,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
Expanded(
|
||||
flex: 6,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
Assets.svg.chevronRight,
|
||||
width: 20,
|
||||
height: 20,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -315,6 +315,7 @@ flutter:
|
|||
- assets/svg/keys.svg
|
||||
- assets/svg/arrow-down.svg
|
||||
- assets/svg/plus-circle.svg
|
||||
- assets/svg/configuration.svg
|
||||
# coin icons
|
||||
- assets/svg/coin_icons/Bitcoin.svg
|
||||
- assets/svg/coin_icons/Litecoin.svg
|
||||
|
|
|
@ -70,7 +70,7 @@ void main() {
|
|||
await widgetTester.tap(find.byType(RawMaterialButton));
|
||||
expect(find.byType(ContactPopUp), findsOneWidget);
|
||||
} else if (Util.isDesktop) {
|
||||
expect(find.byType(RawMaterialButton), findsNothing);
|
||||
expect(find.byType(RawMaterialButton), findsOneWidget);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue