mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2025-03-23 07:38:49 +00:00
desktop exchange choose from stack address ui
This commit is contained in:
parent
7e8f0db967
commit
04b982fb25
2 changed files with 364 additions and 15 deletions
|
@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:stackwallet/models/exchange/incomplete_exchange.dart';
|
import 'package:stackwallet/models/exchange/incomplete_exchange.dart';
|
||||||
import 'package:stackwallet/pages/exchange_view/choose_from_stack_view.dart';
|
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/subwidgets/desktop_choose_from_stack.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/address_book_address_chooser/address_book_address_chooser.dart';
|
||||||
import 'package:stackwallet/providers/exchange/exchange_send_from_wallet_id_provider.dart';
|
import 'package:stackwallet/providers/exchange/exchange_send_from_wallet_id_provider.dart';
|
||||||
import 'package:stackwallet/providers/global/wallets_provider.dart';
|
import 'package:stackwallet/providers/global/wallets_provider.dart';
|
||||||
|
@ -64,12 +64,21 @@ class _DesktopStep2State extends ConsumerState<DesktopStep2> {
|
||||||
final coin = coinFromTickerCaseInsensitive(
|
final coin = coinFromTickerCaseInsensitive(
|
||||||
model.receiveTicker,
|
model.receiveTicker,
|
||||||
);
|
);
|
||||||
Navigator.of(context)
|
|
||||||
.pushNamed(
|
showDialog<String?>(
|
||||||
ChooseFromStackView.routeName,
|
context: context,
|
||||||
arguments: coin,
|
barrierColor: Colors.transparent,
|
||||||
)
|
builder: (context) => DesktopDialog(
|
||||||
.then((value) async {
|
maxWidth: 720,
|
||||||
|
maxHeight: 670,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(32),
|
||||||
|
child: DesktopChooseFromStack(
|
||||||
|
coin: coin,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
).then((value) async {
|
||||||
if (value is String) {
|
if (value is String) {
|
||||||
final manager =
|
final manager =
|
||||||
ref.read(walletsChangeNotifierProvider).getManager(value);
|
ref.read(walletsChangeNotifierProvider).getManager(value);
|
||||||
|
@ -88,12 +97,21 @@ class _DesktopStep2State extends ConsumerState<DesktopStep2> {
|
||||||
final coin = coinFromTickerCaseInsensitive(
|
final coin = coinFromTickerCaseInsensitive(
|
||||||
model.sendTicker,
|
model.sendTicker,
|
||||||
);
|
);
|
||||||
Navigator.of(context)
|
|
||||||
.pushNamed(
|
showDialog<String?>(
|
||||||
ChooseFromStackView.routeName,
|
context: context,
|
||||||
arguments: coin,
|
barrierColor: Colors.transparent,
|
||||||
)
|
builder: (context) => DesktopDialog(
|
||||||
.then((value) async {
|
maxWidth: 720,
|
||||||
|
maxHeight: 670,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(32),
|
||||||
|
child: DesktopChooseFromStack(
|
||||||
|
coin: coin,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
).then((value) async {
|
||||||
if (value is String) {
|
if (value is String) {
|
||||||
final manager =
|
final manager =
|
||||||
ref.read(walletsChangeNotifierProvider).getManager(value);
|
ref.read(walletsChangeNotifierProvider).getManager(value);
|
||||||
|
@ -366,7 +384,8 @@ class _DesktopStep2State extends ConsumerState<DesktopStep2> {
|
||||||
? const ClipboardIcon()
|
? const ClipboardIcon()
|
||||||
: const XIcon(),
|
: const XIcon(),
|
||||||
),
|
),
|
||||||
if (_toController.text.isEmpty)
|
if (_toController.text.isEmpty &&
|
||||||
|
isStackCoin(model.receiveTicker))
|
||||||
TextFieldIconButton(
|
TextFieldIconButton(
|
||||||
key: const Key("sendViewAddressBookButtonKey"),
|
key: const Key("sendViewAddressBookButtonKey"),
|
||||||
onTap: selectRecipientFromAddressBook,
|
onTap: selectRecipientFromAddressBook,
|
||||||
|
@ -488,7 +507,8 @@ class _DesktopStep2State extends ConsumerState<DesktopStep2> {
|
||||||
? const ClipboardIcon()
|
? const ClipboardIcon()
|
||||||
: const XIcon(),
|
: const XIcon(),
|
||||||
),
|
),
|
||||||
if (_refundController.text.isEmpty)
|
if (_refundController.text.isEmpty &&
|
||||||
|
isStackCoin(model.sendTicker))
|
||||||
TextFieldIconButton(
|
TextFieldIconButton(
|
||||||
key: const Key("sendViewAddressBookButtonKey"),
|
key: const Key("sendViewAddressBookButtonKey"),
|
||||||
onTap: selectRefundFromAddressBook,
|
onTap: selectRefundFromAddressBook,
|
||||||
|
|
|
@ -0,0 +1,329 @@
|
||||||
|
import 'package:decimal/decimal.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
import 'package:stackwallet/providers/providers.dart';
|
||||||
|
import 'package:stackwallet/utilities/assets.dart';
|
||||||
|
import 'package:stackwallet/utilities/constants.dart';
|
||||||
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
|
import 'package:stackwallet/utilities/format.dart';
|
||||||
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
|
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||||
|
import 'package:stackwallet/widgets/animated_text.dart';
|
||||||
|
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
|
||||||
|
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
||||||
|
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
|
||||||
|
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||||
|
import 'package:stackwallet/widgets/stack_text_field.dart';
|
||||||
|
import 'package:stackwallet/widgets/textfield_icon_button.dart';
|
||||||
|
import 'package:stackwallet/widgets/wallet_info_row/sub_widgets/wallet_info_row_coin_icon.dart';
|
||||||
|
|
||||||
|
class DesktopChooseFromStack extends ConsumerStatefulWidget {
|
||||||
|
const DesktopChooseFromStack({
|
||||||
|
Key? key,
|
||||||
|
required this.coin,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final Coin coin;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ConsumerState<DesktopChooseFromStack> createState() =>
|
||||||
|
_DesktopChooseFromStackState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DesktopChooseFromStackState
|
||||||
|
extends ConsumerState<DesktopChooseFromStack> {
|
||||||
|
late final TextEditingController _searchController;
|
||||||
|
late final FocusNode searchFieldFocusNode;
|
||||||
|
|
||||||
|
String _searchTerm = "";
|
||||||
|
|
||||||
|
List<String> filter(List<String> walletIds, String searchTerm) {
|
||||||
|
if (searchTerm.isEmpty) {
|
||||||
|
return walletIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<String> result = [];
|
||||||
|
for (final walletId in walletIds) {
|
||||||
|
final manager =
|
||||||
|
ref.read(walletsChangeNotifierProvider).getManager(walletId);
|
||||||
|
|
||||||
|
if (manager.walletName.toLowerCase().contains(searchTerm.toLowerCase())) {
|
||||||
|
result.add(walletId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
searchFieldFocusNode = FocusNode();
|
||||||
|
_searchController = TextEditingController();
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_searchController.dispose();
|
||||||
|
searchFieldFocusNode.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"Choose from Stack",
|
||||||
|
style: STextStyles.desktopH3(context),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 28,
|
||||||
|
),
|
||||||
|
ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(
|
||||||
|
Constants.size.circularBorderRadius,
|
||||||
|
),
|
||||||
|
child: TextField(
|
||||||
|
autocorrect: false,
|
||||||
|
enableSuggestions: false,
|
||||||
|
controller: _searchController,
|
||||||
|
focusNode: searchFieldFocusNode,
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() {
|
||||||
|
_searchTerm = value;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
style: STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.textFieldActiveText,
|
||||||
|
height: 1.8,
|
||||||
|
),
|
||||||
|
decoration: standardInputDecoration(
|
||||||
|
"Search",
|
||||||
|
searchFieldFocusNode,
|
||||||
|
context,
|
||||||
|
desktopMed: true,
|
||||||
|
).copyWith(
|
||||||
|
prefixIcon: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 12,
|
||||||
|
vertical: 18,
|
||||||
|
),
|
||||||
|
child: SvgPicture.asset(
|
||||||
|
Assets.svg.search,
|
||||||
|
width: 20,
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
suffixIcon: _searchController.text.isNotEmpty
|
||||||
|
? Padding(
|
||||||
|
padding: const EdgeInsets.only(right: 0),
|
||||||
|
child: UnconstrainedBox(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
TextFieldIconButton(
|
||||||
|
child: const XIcon(),
|
||||||
|
onTap: () async {
|
||||||
|
setState(() {
|
||||||
|
_searchController.text = "";
|
||||||
|
_searchTerm = "";
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
|
Flexible(
|
||||||
|
child: Builder(
|
||||||
|
builder: (context) {
|
||||||
|
List<String> walletIds = ref.watch(
|
||||||
|
walletsChangeNotifierProvider.select(
|
||||||
|
(value) => value.getWalletIdsFor(coin: widget.coin),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (walletIds.isEmpty) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
RoundedWhiteContainer(
|
||||||
|
borderColor: Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.background,
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
"No ${widget.coin.ticker.toUpperCase()} wallets",
|
||||||
|
style:
|
||||||
|
STextStyles.desktopTextExtraExtraSmall(context),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
walletIds = filter(walletIds, _searchTerm);
|
||||||
|
|
||||||
|
return ListView.separated(
|
||||||
|
primary: false,
|
||||||
|
itemCount: walletIds.length,
|
||||||
|
separatorBuilder: (_, __) => const SizedBox(
|
||||||
|
height: 5,
|
||||||
|
),
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final manager = ref.watch(walletsChangeNotifierProvider
|
||||||
|
.select((value) => value.getManager(walletIds[index])));
|
||||||
|
|
||||||
|
return RoundedWhiteContainer(
|
||||||
|
borderColor:
|
||||||
|
Theme.of(context).extension<StackColors>()!.background,
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 20,
|
||||||
|
vertical: 14,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
WalletInfoCoinIcon(coin: widget.coin),
|
||||||
|
const SizedBox(
|
||||||
|
width: 12,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
manager.walletName,
|
||||||
|
style: STextStyles.desktopTextExtraExtraSmall(
|
||||||
|
context)
|
||||||
|
.copyWith(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.textDark,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
BalanceDisplay(
|
||||||
|
walletId: walletIds[index],
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 80,
|
||||||
|
),
|
||||||
|
BlueTextButton(
|
||||||
|
text: "Select wallet",
|
||||||
|
onTap: () {
|
||||||
|
Navigator.of(context).pop(manager.walletId);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
const Spacer(),
|
||||||
|
const SizedBox(
|
||||||
|
width: 16,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: SecondaryButton(
|
||||||
|
label: "Cancel",
|
||||||
|
buttonHeight: ButtonHeight.l,
|
||||||
|
onPressed: Navigator.of(context).pop,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BalanceDisplay extends ConsumerStatefulWidget {
|
||||||
|
const BalanceDisplay({
|
||||||
|
Key? key,
|
||||||
|
required this.walletId,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final String walletId;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ConsumerState<BalanceDisplay> createState() => _BalanceDisplayState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _BalanceDisplayState extends ConsumerState<BalanceDisplay> {
|
||||||
|
late final String walletId;
|
||||||
|
|
||||||
|
Decimal? _cachedBalance;
|
||||||
|
|
||||||
|
static const loopedText = [
|
||||||
|
"Loading balance ",
|
||||||
|
"Loading balance. ",
|
||||||
|
"Loading balance.. ",
|
||||||
|
"Loading balance..."
|
||||||
|
];
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
walletId = widget.walletId;
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final manager = ref.watch(walletsChangeNotifierProvider
|
||||||
|
.select((value) => value.getManager(walletId)));
|
||||||
|
final locale = ref.watch(
|
||||||
|
localeServiceChangeNotifierProvider.select((value) => value.locale));
|
||||||
|
|
||||||
|
return FutureBuilder(
|
||||||
|
future: manager.availableBalance,
|
||||||
|
builder: (context, AsyncSnapshot<Decimal> snapshot) {
|
||||||
|
if (snapshot.connectionState == ConnectionState.done &&
|
||||||
|
snapshot.hasData &&
|
||||||
|
snapshot.data != null) {
|
||||||
|
_cachedBalance = snapshot.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_cachedBalance == null) {
|
||||||
|
return AnimatedText(
|
||||||
|
stringsToLoopThrough: loopedText,
|
||||||
|
style: STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||||
|
color: Theme.of(context).extension<StackColors>()!.textSubtitle1,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Text(
|
||||||
|
"${Format.localizedStringAsFixed(
|
||||||
|
value: _cachedBalance!,
|
||||||
|
locale: locale,
|
||||||
|
decimalPlaces: 8,
|
||||||
|
)} ${manager.coin.ticker}",
|
||||||
|
style: STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||||
|
color: Theme.of(context).extension<StackColors>()!.textSubtitle1,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.right,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue