2022-12-21 19:46:50 +00:00
|
|
|
import 'dart:async';
|
|
|
|
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:flutter/services.dart';
|
|
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
|
|
import 'package:stackwallet/models/paynym/paynym_account.dart';
|
|
|
|
import 'package:stackwallet/pages/paynym/subwidgets/featured_paynyms_widget.dart';
|
|
|
|
import 'package:stackwallet/pages/paynym/subwidgets/paynym_card.dart';
|
|
|
|
import 'package:stackwallet/providers/global/paynym_api_provider.dart';
|
|
|
|
import 'package:stackwallet/utilities/barcode_scanner_interface.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/conditional_parent.dart';
|
|
|
|
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
2023-01-04 18:32:40 +00:00
|
|
|
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
|
|
|
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
|
2022-12-21 19:46:50 +00:00
|
|
|
import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
|
2023-01-04 18:32:40 +00:00
|
|
|
import 'package:stackwallet/widgets/desktop/paynym_search_button.dart';
|
2022-12-21 19:46:50 +00:00
|
|
|
import 'package:stackwallet/widgets/desktop/secondary_button.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/loading_indicator.dart';
|
2023-01-04 18:32:40 +00:00
|
|
|
import 'package:stackwallet/widgets/rounded_container.dart';
|
2022-12-21 19:46:50 +00:00
|
|
|
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 AddNewPaynymFollowView extends ConsumerStatefulWidget {
|
|
|
|
const AddNewPaynymFollowView({
|
|
|
|
Key? key,
|
|
|
|
required this.walletId,
|
|
|
|
}) : super(key: key);
|
|
|
|
|
|
|
|
final String walletId;
|
|
|
|
|
|
|
|
static const String routeName = "/addNewPaynymFollow";
|
|
|
|
|
|
|
|
@override
|
|
|
|
ConsumerState<AddNewPaynymFollowView> createState() =>
|
|
|
|
_AddNewPaynymFollowViewState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class _AddNewPaynymFollowViewState
|
|
|
|
extends ConsumerState<AddNewPaynymFollowView> {
|
|
|
|
late final TextEditingController _searchController;
|
|
|
|
late final FocusNode searchFieldFocusNode;
|
|
|
|
|
|
|
|
String _searchString = "";
|
|
|
|
|
|
|
|
bool _didSearch = false;
|
|
|
|
PaynymAccount? _searchResult;
|
|
|
|
|
2023-01-04 18:32:40 +00:00
|
|
|
final isDesktop = Util.isDesktop;
|
|
|
|
|
2022-12-21 19:46:50 +00:00
|
|
|
Future<void> _search() async {
|
|
|
|
_didSearch = true;
|
|
|
|
bool didPopLoading = false;
|
|
|
|
unawaited(
|
|
|
|
showDialog<void>(
|
|
|
|
barrierDismissible: false,
|
|
|
|
context: context,
|
|
|
|
builder: (context) => const LoadingIndicator(
|
|
|
|
width: 200,
|
|
|
|
),
|
|
|
|
).then((_) => didPopLoading = true),
|
|
|
|
);
|
|
|
|
|
2022-12-21 23:02:14 +00:00
|
|
|
final paynymAccount = await ref.read(paynymAPIProvider).nym(_searchString);
|
2022-12-21 19:46:50 +00:00
|
|
|
|
|
|
|
if (mounted) {
|
|
|
|
if (!didPopLoading) {
|
|
|
|
Navigator.of(context).pop();
|
|
|
|
}
|
|
|
|
|
|
|
|
setState(() {
|
2023-01-02 21:16:01 +00:00
|
|
|
_searchResult = paynymAccount.value;
|
2022-12-21 19:46:50 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-04 18:32:40 +00:00
|
|
|
Future<void> _clear() async {
|
|
|
|
_searchString = "";
|
|
|
|
setState(() {
|
|
|
|
_searchController.text = "";
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> _paste() async {
|
|
|
|
final ClipboardData? data = await Clipboard.getData(Clipboard.kTextPlain);
|
|
|
|
if (data?.text != null && data!.text!.isNotEmpty) {
|
|
|
|
String content = data.text!.trim();
|
|
|
|
if (content.contains("\n")) {
|
|
|
|
content = content.substring(
|
|
|
|
0,
|
|
|
|
content.indexOf(
|
|
|
|
"\n",
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
_searchString = content;
|
|
|
|
setState(() {
|
|
|
|
_searchController.text = content;
|
|
|
|
_searchController.selection = TextSelection.collapsed(
|
|
|
|
offset: content.length,
|
|
|
|
);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> _scanQr() async {
|
|
|
|
try {
|
|
|
|
if (!isDesktop && FocusScope.of(context).hasFocus) {
|
|
|
|
FocusScope.of(context).unfocus();
|
|
|
|
await Future<void>.delayed(const Duration(milliseconds: 75));
|
|
|
|
}
|
|
|
|
|
|
|
|
final qrResult = await const BarcodeScannerWrapper().scan();
|
|
|
|
|
|
|
|
final pCodeString = qrResult.rawContent;
|
|
|
|
|
|
|
|
_searchString = pCodeString;
|
|
|
|
|
|
|
|
setState(() {
|
|
|
|
_searchController.text = pCodeString;
|
|
|
|
_searchController.selection = TextSelection.collapsed(
|
|
|
|
offset: pCodeString.length,
|
|
|
|
);
|
|
|
|
});
|
|
|
|
} catch (_) {
|
|
|
|
// scan failed
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-21 19:46:50 +00:00
|
|
|
@override
|
|
|
|
void initState() {
|
|
|
|
_searchController = TextEditingController();
|
|
|
|
searchFieldFocusNode = FocusNode();
|
|
|
|
super.initState();
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
void dispose() {
|
|
|
|
_searchController.dispose();
|
|
|
|
searchFieldFocusNode.dispose();
|
|
|
|
super.dispose();
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
debugPrint("BUILD: $runtimeType");
|
|
|
|
|
2023-01-04 18:32:40 +00:00
|
|
|
return ConditionalParent(
|
|
|
|
condition: !isDesktop,
|
|
|
|
builder: (child) => MasterScaffold(
|
|
|
|
isDesktop: isDesktop,
|
|
|
|
appBar: AppBar(
|
|
|
|
leading: AppBarBackButton(
|
|
|
|
onPressed: () {
|
|
|
|
Navigator.of(context).pop();
|
|
|
|
},
|
|
|
|
),
|
|
|
|
titleSpacing: 0,
|
|
|
|
title: Text(
|
|
|
|
"Add new",
|
|
|
|
style: STextStyles.navBarTitle(context),
|
|
|
|
overflow: TextOverflow.ellipsis,
|
|
|
|
),
|
2022-12-21 19:46:50 +00:00
|
|
|
),
|
2023-01-04 18:32:40 +00:00
|
|
|
body: SafeArea(
|
2022-12-21 19:46:50 +00:00
|
|
|
child: LayoutBuilder(
|
|
|
|
builder: (context, constraints) => SingleChildScrollView(
|
|
|
|
child: ConstrainedBox(
|
|
|
|
constraints: BoxConstraints(
|
|
|
|
minHeight: constraints.maxHeight,
|
|
|
|
),
|
|
|
|
child: IntrinsicHeight(
|
2023-01-04 18:32:40 +00:00
|
|
|
child: Padding(
|
|
|
|
padding: const EdgeInsets.all(16),
|
|
|
|
child: child,
|
|
|
|
),
|
2022-12-21 19:46:50 +00:00
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
2023-01-04 18:32:40 +00:00
|
|
|
),
|
|
|
|
child: ConditionalParent(
|
|
|
|
condition: isDesktop,
|
|
|
|
builder: (child) => DesktopDialog(
|
|
|
|
maxWidth: 580,
|
|
|
|
maxHeight: double.infinity,
|
2022-12-21 19:46:50 +00:00
|
|
|
child: Column(
|
|
|
|
children: [
|
2023-01-04 18:32:40 +00:00
|
|
|
Row(
|
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
|
children: [
|
|
|
|
Padding(
|
|
|
|
padding: const EdgeInsets.only(left: 32),
|
|
|
|
child: Text(
|
|
|
|
"Add new",
|
|
|
|
style: STextStyles.desktopH3(context),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
const DesktopDialogCloseButton(),
|
|
|
|
],
|
2022-12-21 19:46:50 +00:00
|
|
|
),
|
2023-01-04 18:32:40 +00:00
|
|
|
Padding(
|
|
|
|
padding: const EdgeInsets.only(
|
|
|
|
left: 32,
|
|
|
|
right: 32,
|
|
|
|
bottom: 32,
|
|
|
|
),
|
|
|
|
child: child,
|
2022-12-21 19:46:50 +00:00
|
|
|
),
|
2023-01-04 18:32:40 +00:00
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
child: Column(
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
children: [
|
|
|
|
const SizedBox(
|
|
|
|
height: 10,
|
|
|
|
),
|
|
|
|
Text(
|
|
|
|
"Featured PayNyms",
|
|
|
|
style: isDesktop
|
|
|
|
? STextStyles.desktopTextExtraExtraSmall(context)
|
|
|
|
: STextStyles.sectionLabelMedium12(context),
|
|
|
|
),
|
|
|
|
const SizedBox(
|
|
|
|
height: 12,
|
|
|
|
),
|
|
|
|
FeaturedPaynymsWidget(
|
|
|
|
walletId: widget.walletId,
|
|
|
|
),
|
|
|
|
const SizedBox(
|
|
|
|
height: 24,
|
|
|
|
),
|
|
|
|
Text(
|
|
|
|
"Add new",
|
|
|
|
style: isDesktop
|
|
|
|
? STextStyles.desktopTextExtraExtraSmall(context)
|
|
|
|
: STextStyles.sectionLabelMedium12(context),
|
|
|
|
),
|
|
|
|
const SizedBox(
|
|
|
|
height: 12,
|
|
|
|
),
|
|
|
|
if (isDesktop)
|
|
|
|
Row(
|
|
|
|
children: [
|
|
|
|
Expanded(
|
|
|
|
child: Stack(
|
|
|
|
children: [
|
|
|
|
RoundedContainer(
|
|
|
|
padding: const EdgeInsets.all(0),
|
|
|
|
color: Theme.of(context)
|
|
|
|
.extension<StackColors>()!
|
|
|
|
.textFieldDefaultBG,
|
|
|
|
height: 56,
|
|
|
|
child: Center(
|
|
|
|
child: TextField(
|
|
|
|
autocorrect: !isDesktop,
|
|
|
|
enableSuggestions: !isDesktop,
|
|
|
|
controller: _searchController,
|
|
|
|
focusNode: searchFieldFocusNode,
|
|
|
|
onChanged: (value) {
|
|
|
|
setState(() {
|
|
|
|
_searchString = value;
|
|
|
|
});
|
|
|
|
},
|
|
|
|
style: STextStyles.desktopTextExtraExtraSmall(
|
|
|
|
context)
|
|
|
|
.copyWith(
|
|
|
|
color: Theme.of(context)
|
|
|
|
.extension<StackColors>()!
|
|
|
|
.textFieldActiveText,
|
|
|
|
// height: 1.8,
|
|
|
|
),
|
|
|
|
decoration: InputDecoration(
|
|
|
|
hintText: "Paste payment code",
|
|
|
|
hoverColor: Colors.transparent,
|
|
|
|
fillColor: Colors.transparent,
|
|
|
|
contentPadding: const EdgeInsets.all(16),
|
|
|
|
hintStyle:
|
|
|
|
STextStyles.desktopTextFieldLabel(context)
|
|
|
|
.copyWith(
|
|
|
|
fontSize: 14,
|
|
|
|
),
|
|
|
|
enabledBorder: InputBorder.none,
|
|
|
|
focusedBorder: InputBorder.none,
|
|
|
|
errorBorder: InputBorder.none,
|
|
|
|
disabledBorder: InputBorder.none,
|
|
|
|
focusedErrorBorder: InputBorder.none,
|
|
|
|
suffixIcon: Padding(
|
|
|
|
padding: const EdgeInsets.only(right: 8),
|
|
|
|
child: UnconstrainedBox(
|
|
|
|
child: Row(
|
|
|
|
children: [
|
|
|
|
_searchController.text.isNotEmpty
|
|
|
|
? TextFieldIconButton(
|
|
|
|
onTap: _clear,
|
|
|
|
child: RoundedContainer(
|
|
|
|
padding:
|
|
|
|
const EdgeInsets.all(8),
|
|
|
|
color: Theme.of(context)
|
|
|
|
.extension<StackColors>()!
|
|
|
|
.buttonBackSecondary,
|
|
|
|
child: const XIcon(),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
: TextFieldIconButton(
|
|
|
|
key: const Key(
|
|
|
|
"paynymPasteAddressFieldButtonKey"),
|
|
|
|
onTap: _paste,
|
|
|
|
child: RoundedContainer(
|
|
|
|
padding:
|
|
|
|
const EdgeInsets.all(8),
|
|
|
|
color: Theme.of(context)
|
|
|
|
.extension<StackColors>()!
|
|
|
|
.buttonBackSecondary,
|
|
|
|
child: const ClipboardIcon(),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
TextFieldIconButton(
|
|
|
|
key: const Key(
|
|
|
|
"paynymScanQrButtonKey"),
|
|
|
|
onTap: _scanQr,
|
|
|
|
child: RoundedContainer(
|
|
|
|
padding: const EdgeInsets.all(8),
|
|
|
|
color: Theme.of(context)
|
|
|
|
.extension<StackColors>()!
|
|
|
|
.buttonBackSecondary,
|
|
|
|
child: const QrCodeIcon(),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
const SizedBox(
|
|
|
|
width: 10,
|
|
|
|
),
|
|
|
|
PaynymSearchButton(onPressed: _search),
|
|
|
|
],
|
2022-12-21 19:46:50 +00:00
|
|
|
),
|
2023-01-04 18:32:40 +00:00
|
|
|
if (!isDesktop)
|
2022-12-21 19:46:50 +00:00
|
|
|
ClipRRect(
|
|
|
|
borderRadius: BorderRadius.circular(
|
|
|
|
Constants.size.circularBorderRadius,
|
|
|
|
),
|
|
|
|
child: TextField(
|
|
|
|
autocorrect: !isDesktop,
|
|
|
|
enableSuggestions: !isDesktop,
|
|
|
|
controller: _searchController,
|
|
|
|
focusNode: searchFieldFocusNode,
|
|
|
|
onChanged: (value) {
|
|
|
|
setState(() {
|
|
|
|
_searchString = value;
|
|
|
|
});
|
|
|
|
},
|
2023-01-04 18:32:40 +00:00
|
|
|
style: STextStyles.field(context),
|
2022-12-21 19:46:50 +00:00
|
|
|
decoration: standardInputDecoration(
|
|
|
|
"Paste payment code",
|
|
|
|
searchFieldFocusNode,
|
|
|
|
context,
|
|
|
|
desktopMed: isDesktop,
|
|
|
|
).copyWith(
|
|
|
|
suffixIcon: Padding(
|
|
|
|
padding: const EdgeInsets.only(right: 8),
|
|
|
|
child: UnconstrainedBox(
|
|
|
|
child: Row(
|
|
|
|
children: [
|
|
|
|
_searchController.text.isNotEmpty
|
|
|
|
? TextFieldIconButton(
|
2023-01-04 18:32:40 +00:00
|
|
|
onTap: _clear,
|
2022-12-21 19:46:50 +00:00
|
|
|
child: const XIcon(),
|
|
|
|
)
|
|
|
|
: TextFieldIconButton(
|
|
|
|
key: const Key(
|
|
|
|
"paynymPasteAddressFieldButtonKey"),
|
2023-01-04 18:32:40 +00:00
|
|
|
onTap: _paste,
|
2022-12-21 19:46:50 +00:00
|
|
|
child: const ClipboardIcon(),
|
|
|
|
),
|
|
|
|
TextFieldIconButton(
|
|
|
|
key: const Key("paynymScanQrButtonKey"),
|
2023-01-04 18:32:40 +00:00
|
|
|
onTap: _scanQr,
|
2022-12-21 19:46:50 +00:00
|
|
|
child: const QrCodeIcon(),
|
|
|
|
)
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
2023-01-04 18:32:40 +00:00
|
|
|
if (!isDesktop)
|
2022-12-21 19:46:50 +00:00
|
|
|
const SizedBox(
|
|
|
|
height: 12,
|
|
|
|
),
|
2023-01-04 18:32:40 +00:00
|
|
|
if (!isDesktop)
|
2022-12-21 19:46:50 +00:00
|
|
|
SecondaryButton(
|
|
|
|
label: "Search",
|
|
|
|
onPressed: _search,
|
|
|
|
),
|
2023-01-04 18:32:40 +00:00
|
|
|
if (_didSearch)
|
|
|
|
const SizedBox(
|
|
|
|
height: 20,
|
|
|
|
),
|
|
|
|
if (_didSearch && _searchResult == null)
|
|
|
|
RoundedWhiteContainer(
|
|
|
|
child: Row(
|
|
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
|
|
children: [
|
|
|
|
Text(
|
|
|
|
"Nothing found. Please check the payment code.",
|
|
|
|
style: STextStyles.label(context),
|
|
|
|
),
|
|
|
|
],
|
2022-12-21 19:46:50 +00:00
|
|
|
),
|
2023-01-04 18:32:40 +00:00
|
|
|
),
|
|
|
|
if (_didSearch && _searchResult != null)
|
|
|
|
RoundedWhiteContainer(
|
|
|
|
padding: const EdgeInsets.all(0),
|
|
|
|
child: PaynymCard(
|
|
|
|
label: _searchResult!.nymName,
|
|
|
|
paymentCodeString: _searchResult!.codes.first.code,
|
|
|
|
walletId: widget.walletId,
|
2022-12-21 19:46:50 +00:00
|
|
|
),
|
2023-01-04 18:32:40 +00:00
|
|
|
),
|
|
|
|
],
|
2022-12-21 19:46:50 +00:00
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|