@@ -40,6 +40,8 @@ class _WalletSendScreenState extends ConsumerState<WalletSendScreen> {
4040 final FocusNode textFieldFocusNode = FocusNode ();
4141 List percentages = [25 , 50 , 75 , 100 ];
4242 List <Wallet > wallets = [];
43+ MemoType selectedMemoType = MemoType .TEXT ;
44+ String ? memoError;
4345
4446 @override
4547 void initState () {
@@ -182,6 +184,16 @@ class _WalletSendScreenState extends ConsumerState<WalletSendScreen> {
182184 return validAddress && validAmount;
183185 }
184186
187+ String ? _validateMemo (String memo, MemoType type) {
188+ if (type == MemoType .HASH ) {
189+ final isValidHex = RegExp (r'^[0-9a-fA-F]{1,64}$' ).hasMatch (memo);
190+ if (! isValidHex) return 'Invalid Hash (Hex, max 64 chars)' ;
191+ } else {
192+ if (memo.length > 28 ) return 'Invalid Text length, max length is 28' ;
193+ }
194+ return null ;
195+ }
196+
185197 void _selectToAddress (String address) {
186198 toController.text = address;
187199 setState (() {});
@@ -324,24 +336,67 @@ class _WalletSendScreenState extends ConsumerState<WalletSendScreen> {
324336 if (chainType == ChainType .Stellar )
325337 ListTile (
326338 title: TextField (
327- style: Theme .of (context)
328- .textTheme
329- .bodyMedium!
330- .copyWith (
331- color: Theme .of (context).colorScheme.onSurface,
339+ style: Theme .of (context).textTheme.bodyMedium! .copyWith (
340+ color: Theme .of (context).colorScheme.onSurface,
341+ ),
342+ controller: memoController,
343+ decoration: InputDecoration (
344+ labelText: 'Memo' ,
345+ errorText: memoError,
346+ suffixIcon: SizedBox (
347+ width: 100 ,
348+ child: DropdownButtonFormField <MemoType >(
349+ value: selectedMemoType,
350+ alignment: Alignment .centerRight,
351+ decoration: const InputDecoration (
352+ contentPadding: EdgeInsets .only (left: 25 ),
353+ border: InputBorder .none,
332354 ),
333- controller: memoController,
334- decoration: const InputDecoration (
335- labelText: 'Memo' ,
336- )),
355+ onChanged: (MemoType ? newValue) {
356+ setState (() {
357+ selectedMemoType = newValue! ;
358+ if (memoController.text.isNotEmpty) {
359+ memoError = _validateMemo (
360+ memoController.text, selectedMemoType);
361+ } else {
362+ memoError = null ;
363+ }
364+ });
365+ },
366+ items: MemoType .values.map ((MemoType type) {
367+ return DropdownMenuItem <MemoType >(
368+ value: type,
369+ child: Text (
370+ type.name,
371+ style: Theme .of (context)
372+ .textTheme
373+ .bodyLarge!
374+ .copyWith (
375+ color: Theme .of (context)
376+ .colorScheme
377+ .onSurface,
378+ ),
379+ ),
380+ );
381+ }).toList (),
382+ ),
383+ ),
384+ ),
385+ onChanged: (value) {
386+ setState (() {
387+ memoError = _validateMemo (value, selectedMemoType);
388+ });
389+ },
390+ ),
391+ titleAlignment: ListTileTitleAlignment .top,
337392 ),
338393 const SizedBox (height: 40 ),
339394 Padding (
340395 padding:
341396 const EdgeInsets .symmetric (vertical: 8 , horizontal: 10 ),
342397 child: ElevatedButton (
343398 onPressed: () async {
344- if (await _validate ()) {
399+ if (await _validate () && memoError == null ) {
345400 await _send_confirmation ();
346401 }
347402 },
@@ -397,9 +452,14 @@ class _WalletSendScreenState extends ConsumerState<WalletSendScreen> {
397452 if (code.queryParameters.containsKey ('amount' )) {
398453 amountController.text = code.queryParameters['amount' ]! ;
399454 }
400- if (chainType == ChainType .Stellar &&
401- code.queryParameters.containsKey ('message' )) {
402- memoController.text = code.queryParameters['message' ]! ;
455+ if (chainType == ChainType .Stellar ) {
456+ if (code.queryParameters.containsKey ('memo_hash' )) {
457+ selectedMemoType = MemoType .HASH ;
458+ memoController.text = code.queryParameters['memo_hash' ]! ;
459+ } else if (code.queryParameters.containsKey ('message' )) {
460+ selectedMemoType = MemoType .TEXT ;
461+ memoController.text = code.queryParameters['message' ]! ;
462+ }
403463 }
404464 setState (() {});
405465 } else {
@@ -441,6 +501,9 @@ class _WalletSendScreenState extends ConsumerState<WalletSendScreen> {
441501 }
442502
443503 _send_confirmation () async {
504+ final trimmedMemo = memoController.text.trim ();
505+ final memoHash = selectedMemoType == MemoType .HASH ? trimmedMemo : null ;
506+ final memoText = selectedMemoType == MemoType .TEXT ? trimmedMemo : null ;
444507 showModalBottomSheet (
445508 isScrollControlled: true ,
446509 useSafeArea: true ,
@@ -455,10 +518,13 @@ class _WalletSendScreenState extends ConsumerState<WalletSendScreen> {
455518 from: fromController.text.trim (),
456519 to: toController.text.trim (),
457520 amount: amountController.text.trim (),
458- memo: memoController.text.trim (),
521+ memo: memoText,
522+ memoHash: memoHash,
459523 reloadBalance: chainType == ChainType .Stellar
460524 ? _loadStellarBalance
461525 : _loadTFChainBalance,
462526 ));
463527 }
464528}
529+
530+ enum MemoType { TEXT , HASH }
0 commit comments