wip: new contact emoji selection and crypto selection

This commit is contained in:
ryleedavis 2022-11-14 13:29:43 -07:00
parent 9df0569bb1
commit 9417d78c81

View file

@ -15,15 +15,17 @@ import 'package:stackwallet/utilities/clipboard_interface.dart';
import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.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'; import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart'; import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
import 'package:stackwallet/widgets/emoji_select_sheet.dart'; import 'package:stackwallet/widgets/emoji_select_sheet.dart';
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart'; import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
import 'package:stackwallet/widgets/stack_text_field.dart'; import 'package:stackwallet/widgets/stack_text_field.dart';
import 'package:stackwallet/widgets/textfield_icon_button.dart'; import 'package:stackwallet/widgets/textfield_icon_button.dart';
import 'package:stackwallet/utilities/util.dart';
class AddAddressBookEntryView extends ConsumerStatefulWidget { class AddAddressBookEntryView extends ConsumerStatefulWidget {
const AddAddressBookEntryView({ const AddAddressBookEntryView({
Key? key, Key? key,
@ -108,395 +110,537 @@ class _AddAddressBookEntryViewState
Widget build(BuildContext context) { Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType"); debugPrint("BUILD: $runtimeType");
return Scaffold( final isDesktop = Util.isDesktop;
backgroundColor: Theme.of(context).extension<StackColors>()!.background, return ConditionalParent(
appBar: AppBar( condition: !isDesktop,
leading: AppBarBackButton( builder: (child) {
onPressed: () async { return Scaffold(
if (FocusScope.of(context).hasFocus) { backgroundColor:
FocusScope.of(context).unfocus(); Theme.of(context).extension<StackColors>()!.background,
await Future<void>.delayed(const Duration(milliseconds: 75)); appBar: AppBar(
} leading: AppBarBackButton(
if (mounted) { onPressed: () async {
Navigator.of(context).pop(); if (FocusScope.of(context).hasFocus) {
} FocusScope.of(context).unfocus();
}, await Future<void>.delayed(
), const Duration(milliseconds: 75));
title: Text( }
"New contact", if (mounted) {
style: STextStyles.navBarTitle(context), Navigator.of(context).pop();
), }
actions: [
Padding(
padding: const EdgeInsets.only(
top: 10,
bottom: 10,
right: 10,
),
child: AspectRatio(
aspectRatio: 1,
child: AppBarIconButton(
key: const Key("addAddressBookEntryFavoriteButtonKey"),
size: 36,
shadows: const [],
color: Theme.of(context).extension<StackColors>()!.background,
icon: SvgPicture.asset(
Assets.svg.star,
color: _isFavorite
? Theme.of(context)
.extension<StackColors>()!
.favoriteStarActive
: Theme.of(context)
.extension<StackColors>()!
.favoriteStarInactive,
width: 20,
height: 20,
),
onPressed: () {
setState(() {
_isFavorite = !_isFavorite;
});
}, },
), ),
), title: Text(
), "New contact",
], style: STextStyles.navBarTitle(context),
),
body: LayoutBuilder(
builder: (context, constraint) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: SingleChildScrollView(
controller: scrollController,
padding: const EdgeInsets.only(
// top: 8,
left: 4,
right: 4,
bottom: 16,
), ),
child: ConstrainedBox( actions: [
constraints: BoxConstraints( Padding(
// subtract top and bottom padding set in parent padding: const EdgeInsets.only(
minHeight: constraint.maxHeight - 16, // - 8, top: 10,
), bottom: 10,
child: IntrinsicHeight( right: 10,
child: Column( ),
children: [ child: AspectRatio(
const SizedBox( aspectRatio: 1,
height: 4, child: AppBarIconButton(
key: const Key("addAddressBookEntryFavoriteButtonKey"),
size: 36,
shadows: const [],
color: Theme.of(context)
.extension<StackColors>()!
.background,
icon: SvgPicture.asset(
Assets.svg.star,
color: _isFavorite
? Theme.of(context)
.extension<StackColors>()!
.favoriteStarActive
: Theme.of(context)
.extension<StackColors>()!
.favoriteStarInactive,
width: 20,
height: 20,
), ),
GestureDetector( onPressed: () {
onTap: () { setState(() {
if (_selectedEmoji != null) { _isFavorite = !_isFavorite;
});
},
),
),
),
],
),
body: child);
},
child: ConditionalParent(
condition: isDesktop,
builder: (child) {
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: const EdgeInsets.all(32),
child: Row(
children: [
Text(
"New contact",
style: STextStyles.desktopH3(context),
textAlign: TextAlign.center,
),
const SizedBox(width: 10),
AppBarIconButton(
key:
const Key("addAddressBookEntryFavoriteButtonKey"),
size: 36,
shadows: const [],
color: Theme.of(context)
.extension<StackColors>()!
.background,
icon: SvgPicture.asset(
Assets.svg.star,
color: _isFavorite
? Theme.of(context)
.extension<StackColors>()!
.favoriteStarActive
: Theme.of(context)
.extension<StackColors>()!
.favoriteStarInactive,
width: 20,
height: 20,
),
onPressed: () {
setState(() { setState(() {
_selectedEmoji = null; _isFavorite = !_isFavorite;
}); });
return; },
} ),
showModalBottomSheet<dynamic>( ],
backgroundColor: Colors.transparent, ),
context: context, ),
shape: const RoundedRectangleBorder( const DesktopDialogCloseButton(),
borderRadius: BorderRadius.vertical( ],
top: Radius.circular(20), ),
), Expanded(child: child),
), ],
builder: (_) => const EmojiSelectSheet(), );
).then((value) { },
if (value is Emoji) { child: LayoutBuilder(
builder: (context, constraint) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: SingleChildScrollView(
controller: scrollController,
padding: const EdgeInsets.only(
// top: 8,
left: 4,
right: 4,
bottom: 16,
),
child: ConstrainedBox(
constraints: BoxConstraints(
// subtract top and bottom padding set in parent
minHeight: constraint.maxHeight - 16, // - 8,
),
child: IntrinsicHeight(
child: Column(
children: [
const SizedBox(
height: 4,
),
GestureDetector(
onTap: () {
if (_selectedEmoji != null) {
setState(() { setState(() {
_selectedEmoji = value; _selectedEmoji = null;
}); });
return;
} }
});
}, ///TODO if desktop make dialog
child: SizedBox( !isDesktop
height: 48, ? showModalBottomSheet<dynamic>(
width: 48, backgroundColor: Colors.transparent,
child: Stack( context: context,
children: [ shape: const RoundedRectangleBorder(
Container( borderRadius: BorderRadius.vertical(
height: 48, top: Radius.circular(20),
width: 48, ),
decoration: BoxDecoration( ),
borderRadius: BorderRadius.circular(24), builder: (_) => const EmojiSelectSheet(),
color: Theme.of(context) ).then((value) {
.extension<StackColors>()! if (value is Emoji) {
.textFieldActiveBG, setState(() {
), _selectedEmoji = value;
child: Center( });
child: _selectedEmoji == null }
? SvgPicture.asset( })
Assets.svg.user, : showDialog<dynamic>(
height: 24, context: context,
width: 24, builder: (context) {
) return DesktopDialog(
: Text( maxHeight: 700,
_selectedEmoji!.char, maxWidth: 700,
style: child: Column(
STextStyles.pageTitleH1(context), 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) {
Align( if (value is Emoji) {
alignment: Alignment.bottomRight, setState(() {
child: Container( _selectedEmoji = value;
height: 14, });
width: 14, }
});
},
child: SizedBox(
height: 48,
width: 48,
child: Stack(
children: [
Container(
height: 48,
width: 48,
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(14), borderRadius: BorderRadius.circular(24),
color: Theme.of(context) color: Theme.of(context)
.extension<StackColors>()! .extension<StackColors>()!
.accentColorDark), .textFieldActiveBG,
),
child: Center( child: Center(
child: _selectedEmoji == null child: _selectedEmoji == null
? SvgPicture.asset( ? SvgPicture.asset(
Assets.svg.plus, Assets.svg.user,
color: Theme.of(context) height: 24,
.extension<StackColors>()! width: 24,
.textWhite,
width: 12,
height: 12,
) )
: SvgPicture.asset( : Text(
Assets.svg.thickX, _selectedEmoji!.char,
color: Theme.of(context) style: STextStyles.pageTitleH1(
.extension<StackColors>()! context),
.textWhite,
width: 8,
height: 8,
), ),
), ),
), ),
) Align(
], alignment: Alignment.bottomRight,
), child: Container(
), height: 14,
), width: 14,
const SizedBox( decoration: BoxDecoration(
height: 8, borderRadius: BorderRadius.circular(14),
), color: Theme.of(context)
ClipRRect( .extension<StackColors>()!
borderRadius: BorderRadius.circular( .accentColorDark),
Constants.size.circularBorderRadius, child: Center(
), child: _selectedEmoji == null
child: TextField( ? SvgPicture.asset(
autocorrect: Util.isDesktop ? false : true, Assets.svg.plus,
enableSuggestions: Util.isDesktop ? false : true, color: Theme.of(context)
controller: nameController, .extension<StackColors>()!
focusNode: nameFocusNode, .textWhite,
style: STextStyles.field(context), width: 12,
decoration: standardInputDecoration( height: 12,
"Enter contact name", )
nameFocusNode, : SvgPicture.asset(
context, Assets.svg.thickX,
).copyWith( color: Theme.of(context)
suffixIcon: ref .extension<StackColors>()!
.read(contactNameIsNotEmptyStateProvider .textWhite,
.state) width: 8,
.state height: 8,
? 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 (forms.length <= 1)
const SizedBox( const SizedBox(
height: 8, height: 8,
), ),
if (forms.length <= 1) forms[0], ClipRRect(
if (forms.length > 1) borderRadius: BorderRadius.circular(
for (int i = 0; i < forms.length; i++) Constants.size.circularBorderRadius,
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(
height: 12,
),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
"Address ${i + 1}",
style: STextStyles.smallMed12(context),
),
BlueTextButton(
onTap: () {
_removeForm(forms[i].id);
},
text: "Remove",
),
],
),
const SizedBox(
height: 8,
),
forms[i],
],
), ),
const SizedBox( child: TextField(
height: 16, autocorrect: Util.isDesktop ? false : true,
), enableSuggestions: Util.isDesktop ? false : true,
BlueTextButton( controller: nameController,
onTap: () { focusNode: nameFocusNode,
_addForm(); style: STextStyles.field(context),
scrollController.animateTo( decoration: standardInputDecoration(
scrollController.position.maxScrollExtent + 500, "Enter contact name",
duration: const Duration(milliseconds: 500), nameFocusNode,
curve: Curves.easeInOut, context,
); ).copyWith(
}, suffixIcon: ref
text: "+ Add another address", .read(contactNameIsNotEmptyStateProvider
), .state)
// GestureDetector( .state
// ? Padding(
// child: Text( padding: const EdgeInsets.only(right: 0),
// "+ Add another address", child: UnconstrainedBox(
// style: STextStyles.largeMedium14(context), child: Row(
// ), children: [
// ), TextFieldIconButton(
const SizedBox( child: const XIcon(),
height: 16, onTap: () async {
), setState(() {
const Spacer(), nameController.text = "";
Row( });
children: [ },
Expanded( ),
child: TextButton( ],
style: Theme.of(context) ),
.extension<StackColors>()! ),
.getSecondaryEnabledButtonColor(context), )
child: Text( : null,
"Cancel",
style: STextStyles.button(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark),
),
onPressed: () async {
if (FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(
const Duration(milliseconds: 75));
}
if (mounted) {
Navigator.of(context).pop();
}
},
), ),
onChanged: (newValue) {
ref
.read(
contactNameIsNotEmptyStateProvider.state)
.state = newValue.isNotEmpty;
},
), ),
),
if (forms.length <= 1)
const SizedBox( const SizedBox(
width: 16, height: 8,
), ),
Expanded( if (forms.length <= 1) forms[0],
child: Builder( if (forms.length > 1)
builder: (context) { for (int i = 0; i < forms.length; i++)
bool nameExists = ref Column(
.watch(contactNameIsNotEmptyStateProvider crossAxisAlignment: CrossAxisAlignment.start,
.state) children: [
.state; const SizedBox(
height: 12,
bool validForms = ref.watch( ),
validContactStateProvider(forms Row(
.map((e) => e.id) mainAxisAlignment:
.toList(growable: false))); MainAxisAlignment.spaceBetween,
children: [
bool shouldEnableSave = Text(
validForms && nameExists; "Address ${i + 1}",
style: STextStyles.smallMed12(context),
return TextButton(
style: shouldEnableSave
? Theme.of(context)
.extension<StackColors>()!
.getPrimaryEnabledButtonColor(context)
: Theme.of(context)
.extension<StackColors>()!
.getPrimaryDisabledButtonColor(
context),
onPressed: shouldEnableSave
? () async {
if (FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(
const Duration(milliseconds: 75),
);
}
List<ContactAddressEntry> entries =
[];
for (int i = 0;
i < forms.length;
i++) {
entries.add(ref
.read(addressEntryDataProvider(
forms[i].id))
.buildAddressEntry());
}
Contact contact = Contact(
emojiChar: _selectedEmoji?.char,
name: nameController.text,
addresses: entries,
isFavorite: _isFavorite,
);
if (await ref
.read(addressBookServiceProvider)
.addContact(contact)) {
if (mounted) {
Navigator.of(context).pop();
}
// TODO show success notification
} else {
// TODO show error notification
}
}
: null,
child: Text(
"Save",
style: STextStyles.button(context).copyWith(
color: shouldEnableSave
? Theme.of(context)
.extension<StackColors>()!
.buttonTextPrimary
: Theme.of(context)
.extension<StackColors>()!
.buttonTextPrimaryDisabled,
), ),
), BlueTextButton(
); onTap: () {
}, _removeForm(forms[i].id);
},
text: "Remove",
),
],
),
const SizedBox(
height: 8,
),
forms[i],
],
), ),
), const SizedBox(
], height: 16,
), ),
], BlueTextButton(
onTap: () {
_addForm();
scrollController.animateTo(
scrollController.position.maxScrollExtent + 500,
duration: const Duration(milliseconds: 500),
curve: Curves.easeInOut,
);
},
text: "+ Add another address",
),
// GestureDetector(
//
// child: Text(
// "+ Add another address",
// style: STextStyles.largeMedium14(context),
// ),
// ),
const SizedBox(
height: 16,
),
const Spacer(),
Row(
children: [
Expanded(
child: TextButton(
style: Theme.of(context)
.extension<StackColors>()!
.getSecondaryEnabledButtonColor(context),
child: Text(
"Cancel",
style: STextStyles.button(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark),
),
onPressed: () async {
if (FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(
const Duration(milliseconds: 75));
}
if (mounted) {
Navigator.of(context).pop();
}
},
),
),
const SizedBox(
width: 16,
),
Expanded(
child: Builder(
builder: (context) {
bool nameExists = ref
.watch(contactNameIsNotEmptyStateProvider
.state)
.state;
bool validForms = ref.watch(
validContactStateProvider(forms
.map((e) => e.id)
.toList(growable: false)));
bool shouldEnableSave =
validForms && nameExists;
return TextButton(
style: shouldEnableSave
? Theme.of(context)
.extension<StackColors>()!
.getPrimaryEnabledButtonColor(
context)
: Theme.of(context)
.extension<StackColors>()!
.getPrimaryDisabledButtonColor(
context),
onPressed: shouldEnableSave
? () async {
if (FocusScope.of(context)
.hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(
const Duration(
milliseconds: 75),
);
}
List<ContactAddressEntry> entries =
[];
for (int i = 0;
i < forms.length;
i++) {
entries.add(ref
.read(
addressEntryDataProvider(
forms[i].id))
.buildAddressEntry());
}
Contact contact = Contact(
emojiChar: _selectedEmoji?.char,
name: nameController.text,
addresses: entries,
isFavorite: _isFavorite,
);
if (await ref
.read(
addressBookServiceProvider)
.addContact(contact)) {
if (mounted) {
Navigator.of(context).pop();
}
// TODO show success notification
} else {
// TODO show error notification
}
}
: null,
child: Text(
"Save",
style:
STextStyles.button(context).copyWith(
color: shouldEnableSave
? Theme.of(context)
.extension<StackColors>()!
.buttonTextPrimary
: Theme.of(context)
.extension<StackColors>()!
.buttonTextPrimaryDisabled,
),
),
);
},
),
),
],
),
],
),
), ),
), ),
), ),
), );
); },
}, ),
), ),
); );
} }