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
}