From dfccedddb21b9581fe45b9a35ca5d1b4bacdec11 Mon Sep 17 00:00:00 2001 From: Serhii Date: Sat, 5 Oct 2024 00:50:36 +0300 Subject: [PATCH] Cw 769 fix transaction notes not showing (#1718) * use focusNode instead of onTapOutside for TextFieldListRow * add a transaction description box to the backup * fix --------- Co-authored-by: OmarHatem --- lib/core/backup_service.dart | 33 ++++- lib/di.dart | 1 + lib/entities/transaction_description.dart | 14 +++ .../widgets/textfield_list_row.dart | 117 +++++++++++------- .../unspent_coins_details_page.dart | 1 - 5 files changed, 117 insertions(+), 49 deletions(-) diff --git a/lib/core/backup_service.dart b/lib/core/backup_service.dart index 42e24d3c7..26e2136dc 100644 --- a/lib/core/backup_service.dart +++ b/lib/core/backup_service.dart @@ -2,14 +2,13 @@ import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; import 'package:cake_wallet/core/secure_storage.dart'; +import 'package:cake_wallet/entities/transaction_description.dart'; import 'package:cake_wallet/themes/theme_list.dart'; import 'package:cw_core/root_dir.dart'; import 'package:cake_wallet/utils/device_info.dart'; -import 'package:cw_core/root_dir.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:flutter/foundation.dart'; import 'package:hive/hive.dart'; -import 'package:path_provider/path_provider.dart'; import 'package:cryptography/cryptography.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:archive/archive_io.dart'; @@ -24,8 +23,8 @@ import 'package:cake_wallet/wallet_types.g.dart'; import 'package:cake_backup/backup.dart' as cake_backup; class BackupService { - BackupService( - this._secureStorage, this._walletInfoSource, this._keyService, this._sharedPreferences) + BackupService(this._secureStorage, this._walletInfoSource, this._transactionDescriptionBox, + this._keyService, this._sharedPreferences) : _cipher = Cryptography.instance.chacha20Poly1305Aead(), _correctWallets = []; @@ -38,6 +37,7 @@ class BackupService { final SecureStorage _secureStorage; final SharedPreferences _sharedPreferences; final Box _walletInfoSource; + final Box _transactionDescriptionBox; final KeyService _keyService; List _correctWallets; @@ -86,6 +86,13 @@ class BackupService { final preferencesDump = await _exportPreferencesJSON(); final preferencesDumpFile = File('${tmpDir.path}/~_preferences_dump_TMP'); final keychainDumpFile = File('${tmpDir.path}/~_keychain_dump_TMP'); + final transactionDescriptionDumpFile = + File('${tmpDir.path}/~_transaction_descriptions_dump_TMP'); + + final transactionDescriptionData = _transactionDescriptionBox + .toMap() + .map((key, value) => MapEntry(key.toString(), value.toJson())); + final transactionDescriptionDump = jsonEncode(transactionDescriptionData); if (tmpDir.existsSync()) { tmpDir.deleteSync(recursive: true); @@ -107,8 +114,10 @@ class BackupService { }); await keychainDumpFile.writeAsBytes(keychainDump.toList()); await preferencesDumpFile.writeAsString(preferencesDump); + await transactionDescriptionDumpFile.writeAsString(transactionDescriptionDump); await zipEncoder.addFile(preferencesDumpFile, '~_preferences_dump'); await zipEncoder.addFile(keychainDumpFile, '~_keychain_dump'); + await zipEncoder.addFile(transactionDescriptionDumpFile, '~_transaction_descriptions_dump'); zipEncoder.close(); final content = File(archivePath).readAsBytesSync(); @@ -160,6 +169,7 @@ class BackupService { await _verifyWallets(); await _importKeychainDumpV2(password); await _importPreferencesDump(); + await _importTransactionDescriptionDump(); } Future _verifyWallets() async { @@ -184,6 +194,21 @@ class BackupService { return await CakeHive.openBox(WalletInfo.boxName); } + Future _importTransactionDescriptionDump() async { + final appDir = await getAppDir(); + final transactionDescriptionFile = File('${appDir.path}/~_transaction_descriptions_dump'); + + if (!transactionDescriptionFile.existsSync()) { + return; + } + + final jsonData = + json.decode(transactionDescriptionFile.readAsStringSync()) as Map; + final descriptionsMap = jsonData.map((key, value) => + MapEntry(key, TransactionDescription.fromJson(value as Map))); + await _transactionDescriptionBox.putAll(descriptionsMap); + } + Future _importPreferencesDump() async { final appDir = await getAppDir(); final preferencesFile = File('${appDir.path}/~_preferences_dump'); diff --git a/lib/di.dart b/lib/di.dart index 4a1952729..5e5417b34 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -1149,6 +1149,7 @@ Future setup({ getIt.registerFactory(() => CakeFeaturesViewModel(getIt.get())); getIt.registerFactory(() => BackupService(getIt.get(), _walletInfoSource, + _transactionDescriptionBox, getIt.get(), getIt.get())); getIt.registerFactory(() => BackupViewModel( diff --git a/lib/entities/transaction_description.dart b/lib/entities/transaction_description.dart index 088f9c480..2ac573652 100644 --- a/lib/entities/transaction_description.dart +++ b/lib/entities/transaction_description.dart @@ -21,4 +21,18 @@ class TransactionDescription extends HiveObject { String? transactionNote; String get note => transactionNote ?? ''; + + Map toJson() => { + 'id': id, + 'recipientAddress': recipientAddress, + 'transactionNote': transactionNote, + }; + + factory TransactionDescription.fromJson(Map json) { + return TransactionDescription( + id: json['id'] as String, + recipientAddress: json['recipientAddress'] as String?, + transactionNote: json['transactionNote'] as String?, + ); + } } diff --git a/lib/src/screens/transaction_details/widgets/textfield_list_row.dart b/lib/src/screens/transaction_details/widgets/textfield_list_row.dart index ff5513502..a86645ecb 100644 --- a/lib/src/screens/transaction_details/widgets/textfield_list_row.dart +++ b/lib/src/screens/transaction_details/widgets/textfield_list_row.dart @@ -1,27 +1,49 @@ -import 'package:cake_wallet/themes/extensions/cake_text_theme.dart'; -import 'package:flutter/material.dart'; import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/themes/extensions/cake_text_theme.dart'; import 'package:cake_wallet/themes/extensions/transaction_trade_theme.dart'; +import 'package:flutter/material.dart'; -class TextFieldListRow extends StatelessWidget { +class TextFieldListRow extends StatefulWidget { TextFieldListRow( {required this.title, required this.value, this.titleFontSize = 14, this.valueFontSize = 16, - this.onSubmitted, - this.onTapOutside}) - : _textController = TextEditingController() { - _textController.text = value; - } + this.onSubmitted}); final String title; final String value; final double titleFontSize; final double valueFontSize; final Function(String value)? onSubmitted; - final Function(String value)? onTapOutside; - final TextEditingController _textController; + + @override + _TextFieldListRowState createState() => _TextFieldListRowState(); +} + +class _TextFieldListRowState extends State { + late TextEditingController _textController; + late FocusNode _focusNode; + + @override + void initState() { + super.initState(); + _textController = TextEditingController(text: widget.value); + _focusNode = FocusNode(); + + _focusNode.addListener(() { + if (!_focusNode.hasFocus) { + widget.onSubmitted?.call(_textController.text); + } + }); + } + + @override + void dispose() { + _textController.dispose(); + _focusNode.dispose(); + super.dispose(); + } @override Widget build(BuildContext context) { @@ -29,41 +51,48 @@ class TextFieldListRow extends StatelessWidget { width: double.infinity, color: Theme.of(context).colorScheme.background, child: Padding( - padding: - const EdgeInsets.only(left: 24, top: 16, bottom: 16, right: 24), + padding: const EdgeInsets.only(left: 24, top: 16, bottom: 16, right: 24), child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(title, - style: TextStyle( - fontSize: titleFontSize, - fontWeight: FontWeight.w500, - color: Theme.of(context).extension()!.detailsTitlesColor), - textAlign: TextAlign.left), - TextField( - controller: _textController, - keyboardType: TextInputType.multiline, - textInputAction: TextInputAction.done, - maxLines: null, - textAlign: TextAlign.start, - style: TextStyle( - fontSize: valueFontSize, - fontWeight: FontWeight.w500, - color: - Theme.of(context).extension()!.titleColor), - decoration: InputDecoration( - isDense: true, - contentPadding: EdgeInsets.only(top: 12, bottom: 0), - hintText: S.of(context).enter_your_note, - hintStyle: TextStyle( - fontSize: valueFontSize, - fontWeight: FontWeight.w500, - color: Theme.of(context).extension()!.detailsTitlesColor), - border: InputBorder.none), - onTapOutside: (_) => onTapOutside?.call(_textController.text), - onSubmitted: (value) => onSubmitted?.call(value), - ) - ]), + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + widget.title, + style: TextStyle( + fontSize: widget.titleFontSize, + fontWeight: FontWeight.w500, + color: Theme.of(context).extension()!.detailsTitlesColor, + ), + textAlign: TextAlign.left, + ), + TextField( + controller: _textController, + focusNode: _focusNode, + keyboardType: TextInputType.multiline, + textInputAction: TextInputAction.done, + maxLines: null, + textAlign: TextAlign.start, + style: TextStyle( + fontSize: widget.valueFontSize, + fontWeight: FontWeight.w500, + color: Theme.of(context).extension()!.titleColor, + ), + decoration: InputDecoration( + isDense: true, + contentPadding: EdgeInsets.only(top: 12, bottom: 0), + hintText: S.of(context).enter_your_note, + hintStyle: TextStyle( + fontSize: widget.valueFontSize, + fontWeight: FontWeight.w500, + color: Theme.of(context).extension()!.detailsTitlesColor, + ), + border: InputBorder.none, + ), + onSubmitted: (value) { + widget.onSubmitted?.call(value); + }, + ), + ], + ), ), ); } diff --git a/lib/src/screens/unspent_coins/unspent_coins_details_page.dart b/lib/src/screens/unspent_coins/unspent_coins_details_page.dart index 61689b52a..165995ba0 100644 --- a/lib/src/screens/unspent_coins/unspent_coins_details_page.dart +++ b/lib/src/screens/unspent_coins/unspent_coins_details_page.dart @@ -44,7 +44,6 @@ class UnspentCoinsDetailsPage extends BasePage { return TextFieldListRow( title: item.title, value: item.value, - onTapOutside: item.onSubmitted, onSubmitted: item.onSubmitted, ); }