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 <omarh.ismail1@gmail.com>

Serhii committed Oct 5, 2024 at 00:50 UTC dfccedddb21b9581fe45b9a35ca5d1b4bacdec11
5 files changed +117 -49
lib/core/backup_service.dart
+29 -4
@@ -2,14 +2,13 @@ import 'dart:convert';
2 import 'dart:io';
3 import 'dart:typed_data';
4 import 'package:cake_wallet/core/secure_storage.dart';
5 +import 'package:cake_wallet/entities/transaction_description.dart';
6 import 'package:cake_wallet/themes/theme_list.dart';
7 import 'package:cw_core/root_dir.dart';
8 import 'package:cake_wallet/utils/device_info.dart';
8 -import 'package:cw_core/root_dir.dart';
9 import 'package:cw_core/wallet_type.dart';
10 import 'package:flutter/foundation.dart';
11 import 'package:hive/hive.dart';
12 -import 'package:path_provider/path_provider.dart';
12 import 'package:cryptography/cryptography.dart';
13 import 'package:shared_preferences/shared_preferences.dart';
14 import 'package:archive/archive_io.dart';
@@ -24,8 +23,8 @@ import 'package:cake_wallet/wallet_types.g.dart';
23 import 'package:cake_backup/backup.dart' as cake_backup;
24
25 class BackupService {
27 - BackupService(
28 - this._secureStorage, this._walletInfoSource, this._keyService, this._sharedPreferences)
26 + BackupService(this._secureStorage, this._walletInfoSource, this._transactionDescriptionBox,
27 + this._keyService, this._sharedPreferences)
28 : _cipher = Cryptography.instance.chacha20Poly1305Aead(),
29 _correctWallets = <WalletInfo>[];
30
@@ -38,6 +37,7 @@ class BackupService {
37 final SecureStorage _secureStorage;
38 final SharedPreferences _sharedPreferences;
39 final Box<WalletInfo> _walletInfoSource;
40 + final Box<TransactionDescription> _transactionDescriptionBox;
41 final KeyService _keyService;
42 List<WalletInfo> _correctWallets;
43
@@ -86,6 +86,13 @@ class BackupService {
86 final preferencesDump = await _exportPreferencesJSON();
87 final preferencesDumpFile = File('${tmpDir.path}/~_preferences_dump_TMP');
88 final keychainDumpFile = File('${tmpDir.path}/~_keychain_dump_TMP');
89 + final transactionDescriptionDumpFile =
90 + File('${tmpDir.path}/~_transaction_descriptions_dump_TMP');
91 +
92 + final transactionDescriptionData = _transactionDescriptionBox
93 + .toMap()
94 + .map((key, value) => MapEntry(key.toString(), value.toJson()));
95 + final transactionDescriptionDump = jsonEncode(transactionDescriptionData);
96
97 if (tmpDir.existsSync()) {
98 tmpDir.deleteSync(recursive: true);
@@ -107,8 +114,10 @@ class BackupService {
114 });
115 await keychainDumpFile.writeAsBytes(keychainDump.toList());
116 await preferencesDumpFile.writeAsString(preferencesDump);
117 + await transactionDescriptionDumpFile.writeAsString(transactionDescriptionDump);
118 await zipEncoder.addFile(preferencesDumpFile, '~_preferences_dump');
119 await zipEncoder.addFile(keychainDumpFile, '~_keychain_dump');
120 + await zipEncoder.addFile(transactionDescriptionDumpFile, '~_transaction_descriptions_dump');
121 zipEncoder.close();
122
123 final content = File(archivePath).readAsBytesSync();
@@ -160,6 +169,7 @@ class BackupService {
169 await _verifyWallets();
170 await _importKeychainDumpV2(password);
171 await _importPreferencesDump();
172 + await _importTransactionDescriptionDump();
173 }
174
175 Future<void> _verifyWallets() async {
@@ -184,6 +194,21 @@ class BackupService {
194 return await CakeHive.openBox<WalletInfo>(WalletInfo.boxName);
195 }
196
197 + Future<void> _importTransactionDescriptionDump() async {
198 + final appDir = await getAppDir();
199 + final transactionDescriptionFile = File('${appDir.path}/~_transaction_descriptions_dump');
200 +
201 + if (!transactionDescriptionFile.existsSync()) {
202 + return;
203 + }
204 +
205 + final jsonData =
206 + json.decode(transactionDescriptionFile.readAsStringSync()) as Map<String, dynamic>;
207 + final descriptionsMap = jsonData.map((key, value) =>
208 + MapEntry(key, TransactionDescription.fromJson(value as Map<String, dynamic>)));
209 + await _transactionDescriptionBox.putAll(descriptionsMap);
210 + }
211 +
212 Future<void> _importPreferencesDump() async {
213 final appDir = await getAppDir();
214 final preferencesFile = File('${appDir.path}/~_preferences_dump');
lib/di.dart
+1
@@ -1149,6 +1149,7 @@ Future<void> setup({
1149 getIt.registerFactory(() => CakeFeaturesViewModel(getIt.get<CakePayService>()));
1150
1151 getIt.registerFactory(() => BackupService(getIt.get<SecureStorage>(), _walletInfoSource,
1152 + _transactionDescriptionBox,
1153 getIt.get<KeyService>(), getIt.get<SharedPreferences>()));
1154
1155 getIt.registerFactory(() => BackupViewModel(
lib/entities/transaction_description.dart
+14
@@ -21,4 +21,18 @@ class TransactionDescription extends HiveObject {
21 String? transactionNote;
22
23 String get note => transactionNote ?? '';
24 +
25 + Map<String, dynamic> toJson() => {
26 + 'id': id,
27 + 'recipientAddress': recipientAddress,
28 + 'transactionNote': transactionNote,
29 + };
30 +
31 + factory TransactionDescription.fromJson(Map<String, dynamic> json) {
32 + return TransactionDescription(
33 + id: json['id'] as String,
34 + recipientAddress: json['recipientAddress'] as String?,
35 + transactionNote: json['transactionNote'] as String?,
36 + );
37 + }
38 }
lib/src/screens/transaction_details/widgets/textfield_list_row.dart
+73 -44
@@ -1,27 +1,49 @@
1 -import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
2 -import 'package:flutter/material.dart';
1 import 'package:cake_wallet/generated/i18n.dart';
2 +import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
3 import 'package:cake_wallet/themes/extensions/transaction_trade_theme.dart';
4 +import 'package:flutter/material.dart';
5
6 -class TextFieldListRow extends StatelessWidget {
6 +class TextFieldListRow extends StatefulWidget {
7 TextFieldListRow(
8 {required this.title,
9 required this.value,
10 this.titleFontSize = 14,
11 this.valueFontSize = 16,
12 - this.onSubmitted,
13 - this.onTapOutside})
14 - : _textController = TextEditingController() {
15 - _textController.text = value;
16 - }
12 + this.onSubmitted});
13
14 final String title;
15 final String value;
16 final double titleFontSize;
17 final double valueFontSize;
18 final Function(String value)? onSubmitted;
23 - final Function(String value)? onTapOutside;
24 - final TextEditingController _textController;
19 +
20 + @override
21 + _TextFieldListRowState createState() => _TextFieldListRowState();
22 +}
23 +
24 +class _TextFieldListRowState extends State<TextFieldListRow> {
25 + late TextEditingController _textController;
26 + late FocusNode _focusNode;
27 +
28 + @override
29 + void initState() {
30 + super.initState();
31 + _textController = TextEditingController(text: widget.value);
32 + _focusNode = FocusNode();
33 +
34 + _focusNode.addListener(() {
35 + if (!_focusNode.hasFocus) {
36 + widget.onSubmitted?.call(_textController.text);
37 + }
38 + });
39 + }
40 +
41 + @override
42 + void dispose() {
43 + _textController.dispose();
44 + _focusNode.dispose();
45 + super.dispose();
46 + }
47
48 @override
49 Widget build(BuildContext context) {
@@ -29,41 +51,48 @@ class TextFieldListRow extends StatelessWidget {
51 width: double.infinity,
52 color: Theme.of(context).colorScheme.background,
53 child: Padding(
32 - padding:
33 - const EdgeInsets.only(left: 24, top: 16, bottom: 16, right: 24),
54 + padding: const EdgeInsets.only(left: 24, top: 16, bottom: 16, right: 24),
55 child: Column(
35 - crossAxisAlignment: CrossAxisAlignment.start,
36 - children: <Widget>[
37 - Text(title,
38 - style: TextStyle(
39 - fontSize: titleFontSize,
40 - fontWeight: FontWeight.w500,
41 - color: Theme.of(context).extension<TransactionTradeTheme>()!.detailsTitlesColor),
42 - textAlign: TextAlign.left),
43 - TextField(
44 - controller: _textController,
45 - keyboardType: TextInputType.multiline,
46 - textInputAction: TextInputAction.done,
47 - maxLines: null,
48 - textAlign: TextAlign.start,
49 - style: TextStyle(
50 - fontSize: valueFontSize,
51 - fontWeight: FontWeight.w500,
52 - color:
53 - Theme.of(context).extension<CakeTextTheme>()!.titleColor),
54 - decoration: InputDecoration(
55 - isDense: true,
56 - contentPadding: EdgeInsets.only(top: 12, bottom: 0),
57 - hintText: S.of(context).enter_your_note,
58 - hintStyle: TextStyle(
59 - fontSize: valueFontSize,
60 - fontWeight: FontWeight.w500,
61 - color: Theme.of(context).extension<TransactionTradeTheme>()!.detailsTitlesColor),
62 - border: InputBorder.none),
63 - onTapOutside: (_) => onTapOutside?.call(_textController.text),
64 - onSubmitted: (value) => onSubmitted?.call(value),
65 - )
66 - ]),
56 + crossAxisAlignment: CrossAxisAlignment.start,
57 + children: <Widget>[
58 + Text(
59 + widget.title,
60 + style: TextStyle(
61 + fontSize: widget.titleFontSize,
62 + fontWeight: FontWeight.w500,
63 + color: Theme.of(context).extension<TransactionTradeTheme>()!.detailsTitlesColor,
64 + ),
65 + textAlign: TextAlign.left,
66 + ),
67 + TextField(
68 + controller: _textController,
69 + focusNode: _focusNode,
70 + keyboardType: TextInputType.multiline,
71 + textInputAction: TextInputAction.done,
72 + maxLines: null,
73 + textAlign: TextAlign.start,
74 + style: TextStyle(
75 + fontSize: widget.valueFontSize,
76 + fontWeight: FontWeight.w500,
77 + color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
78 + ),
79 + decoration: InputDecoration(
80 + isDense: true,
81 + contentPadding: EdgeInsets.only(top: 12, bottom: 0),
82 + hintText: S.of(context).enter_your_note,
83 + hintStyle: TextStyle(
84 + fontSize: widget.valueFontSize,
85 + fontWeight: FontWeight.w500,
86 + color: Theme.of(context).extension<TransactionTradeTheme>()!.detailsTitlesColor,
87 + ),
88 + border: InputBorder.none,
89 + ),
90 + onSubmitted: (value) {
91 + widget.onSubmitted?.call(value);
92 + },
93 + ),
94 + ],
95 + ),
96 ),
97 );
98 }
lib/src/screens/unspent_coins/unspent_coins_details_page.dart
-1
@@ -44,7 +44,6 @@ class UnspentCoinsDetailsPage extends BasePage {
44 return TextFieldListRow(
45 title: item.title,
46 value: item.value,
47 - onTapOutside: item.onSubmitted,
47 onSubmitted: item.onSubmitted,
48 );
49 }