mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2024-11-16 17:27:39 +00:00
transaction note provider
This commit is contained in:
parent
335c2b9993
commit
758c3def5f
15 changed files with 243 additions and 338 deletions
|
@ -33,4 +33,14 @@ class TransactionNote {
|
|||
late String txid;
|
||||
|
||||
late String value;
|
||||
|
||||
TransactionNote copyWith({
|
||||
String? value,
|
||||
}) {
|
||||
return TransactionNote(
|
||||
walletId: walletId,
|
||||
txid: txid,
|
||||
value: value ?? this.value,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,11 +13,13 @@ import 'dart:async';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:stackwallet/models/exchange/response_objects/trade.dart';
|
||||
import 'package:stackwallet/models/isar/models/isar_models.dart';
|
||||
import 'package:stackwallet/models/trade_wallet_lookup.dart';
|
||||
import 'package:stackwallet/pages/pinpad_views/lock_screen_view.dart';
|
||||
import 'package:stackwallet/pages/send_view/sub_widgets/sending_transaction_dialog.dart';
|
||||
import 'package:stackwallet/pages/wallet_view/wallet_view.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_auth_send.dart';
|
||||
import 'package:stackwallet/providers/db/main_db_provider.dart';
|
||||
import 'package:stackwallet/providers/providers.dart';
|
||||
import 'package:stackwallet/route_generator.dart';
|
||||
import 'package:stackwallet/themes/stack_colors.dart';
|
||||
|
@ -129,9 +131,13 @@ class _ConfirmChangeNowSendViewState
|
|||
txid = (results.first as TxData).txid!;
|
||||
|
||||
// save note
|
||||
await ref
|
||||
.read(notesServiceChangeNotifierProvider(walletId))
|
||||
.editOrAddNote(txid: txid, note: note);
|
||||
await ref.read(mainDBProvider).putTransactionNote(
|
||||
TransactionNote(
|
||||
walletId: walletId,
|
||||
txid: txid,
|
||||
value: note,
|
||||
),
|
||||
);
|
||||
|
||||
await ref.read(tradeSentFromStackLookupProvider).save(
|
||||
tradeWalletLookup: TradeWalletLookup(
|
||||
|
|
|
@ -84,8 +84,6 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
|
|||
late final Transaction? transactionIfSentFromStack;
|
||||
late final String? walletId;
|
||||
|
||||
String _note = "";
|
||||
|
||||
bool isStackCoin(String ticker) {
|
||||
try {
|
||||
coinFromTickerCaseInsensitive(ticker);
|
||||
|
@ -950,7 +948,9 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
|
|||
maxHeight: 360,
|
||||
child: EditTradeNoteView(
|
||||
tradeId: tradeId,
|
||||
note: _note,
|
||||
note: ref
|
||||
.read(tradeNoteServiceProvider)
|
||||
.getNote(tradeId: tradeId),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
@ -1036,7 +1036,6 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
|
|||
txid:
|
||||
transactionIfSentFromStack!.txid,
|
||||
walletId: walletId!,
|
||||
note: _note,
|
||||
),
|
||||
);
|
||||
},
|
||||
|
@ -1047,10 +1046,9 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
|
|||
onTap: () {
|
||||
Navigator.of(context).pushNamed(
|
||||
EditNoteView.routeName,
|
||||
arguments: Tuple3(
|
||||
arguments: Tuple2(
|
||||
transactionIfSentFromStack!.txid,
|
||||
walletId!,
|
||||
_note,
|
||||
walletId,
|
||||
),
|
||||
);
|
||||
},
|
||||
|
@ -1079,22 +1077,19 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
|
|||
const SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
FutureBuilder(
|
||||
future: ref.watch(
|
||||
notesServiceChangeNotifierProvider(walletId!).select(
|
||||
(value) => value.getNoteFor(
|
||||
txid: transactionIfSentFromStack!.txid))),
|
||||
builder:
|
||||
(builderContext, AsyncSnapshot<String> snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done &&
|
||||
snapshot.hasData) {
|
||||
_note = snapshot.data ?? "";
|
||||
}
|
||||
return SelectableText(
|
||||
_note,
|
||||
style: STextStyles.itemSubtitle12(context),
|
||||
);
|
||||
},
|
||||
SelectableText(
|
||||
ref
|
||||
.watch(
|
||||
pTransactionNote(
|
||||
(
|
||||
txid: transactionIfSentFromStack!.txid,
|
||||
walletId: walletId!,
|
||||
),
|
||||
),
|
||||
)
|
||||
?.value ??
|
||||
"",
|
||||
style: STextStyles.itemSubtitle12(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -16,6 +16,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_libepiccash/lib.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:stackwallet/models/isar/models/transaction_note.dart';
|
||||
import 'package:stackwallet/notifications/show_flush_bar.dart';
|
||||
import 'package:stackwallet/pages/pinpad_views/lock_screen_view.dart';
|
||||
import 'package:stackwallet/pages/send_view/sub_widgets/sending_transaction_dialog.dart';
|
||||
|
@ -23,6 +24,7 @@ import 'package:stackwallet/pages/token_view/token_view.dart';
|
|||
import 'package:stackwallet/pages/wallet_view/wallet_view.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/coin_control/desktop_coin_control_use_dialog.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_auth_send.dart';
|
||||
import 'package:stackwallet/providers/db/main_db_provider.dart';
|
||||
import 'package:stackwallet/providers/providers.dart';
|
||||
import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart';
|
||||
import 'package:stackwallet/route_generator.dart';
|
||||
|
@ -169,9 +171,13 @@ class _ConfirmTransactionViewState
|
|||
ref.refresh(desktopUseUTXOs);
|
||||
|
||||
// save note
|
||||
await ref
|
||||
.read(notesServiceChangeNotifierProvider(walletId))
|
||||
.editOrAddNote(txid: txid, note: note);
|
||||
await ref.read(mainDBProvider).putTransactionNote(
|
||||
TransactionNote(
|
||||
walletId: walletId,
|
||||
txid: txid,
|
||||
value: note,
|
||||
),
|
||||
);
|
||||
|
||||
if (widget.isTokenTx) {
|
||||
unawaited(ref.read(tokenServiceProvider)!.refresh());
|
||||
|
|
|
@ -16,6 +16,7 @@ import 'package:flutter_svg/svg.dart';
|
|||
import 'package:isar/isar.dart';
|
||||
import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart';
|
||||
import 'package:stackwallet/models/isar/models/contact_entry.dart';
|
||||
import 'package:stackwallet/models/isar/models/transaction_note.dart';
|
||||
import 'package:stackwallet/models/transaction_filter.dart';
|
||||
import 'package:stackwallet/notifications/show_flush_bar.dart';
|
||||
import 'package:stackwallet/pages/token_view/token_view.dart';
|
||||
|
@ -105,8 +106,13 @@ class _TransactionDetailsViewState extends ConsumerState<AllTransactionsView> {
|
|||
// debugPrint("FILTER: $filter");
|
||||
|
||||
final contacts = ref.read(addressBookServiceProvider).contacts;
|
||||
final notes =
|
||||
ref.read(notesServiceChangeNotifierProvider(walletId)).notesSync;
|
||||
final notes = ref
|
||||
.read(mainDBProvider)
|
||||
.isar
|
||||
.transactionNotes
|
||||
.where()
|
||||
.walletIdEqualTo(walletId)
|
||||
.findAllSync();
|
||||
|
||||
return transactions.where((tx) {
|
||||
if (!filter.sent && !filter.received) {
|
||||
|
@ -144,7 +150,7 @@ class _TransactionDetailsViewState extends ConsumerState<AllTransactionsView> {
|
|||
}
|
||||
|
||||
bool _isKeywordMatch(Transaction tx, String keyword,
|
||||
List<ContactEntry> contacts, Map<String, String> notes) {
|
||||
List<ContactEntry> contacts, List<TransactionNote> notes) {
|
||||
if (keyword.isEmpty) {
|
||||
return true;
|
||||
}
|
||||
|
@ -164,9 +170,15 @@ class _TransactionDetailsViewState extends ConsumerState<AllTransactionsView> {
|
|||
contains |=
|
||||
tx.address.value?.value.toLowerCase().contains(keyword) ?? false;
|
||||
|
||||
TransactionNote? note;
|
||||
final matchingNotes = notes.where((e) => e.txid == tx.txid);
|
||||
if (matchingNotes.isNotEmpty) {
|
||||
note = matchingNotes.first;
|
||||
}
|
||||
|
||||
// check if note contains
|
||||
contains |= notes[tx.txid] != null &&
|
||||
notes[tx.txid]!.toLowerCase().contains(keyword);
|
||||
contains |= note != null &&
|
||||
note.value.toLowerCase().contains(keyword);
|
||||
|
||||
// check if txid contains
|
||||
contains |= tx.txid.toLowerCase().contains(keyword);
|
||||
|
@ -193,8 +205,13 @@ class _TransactionDetailsViewState extends ConsumerState<AllTransactionsView> {
|
|||
}
|
||||
text = text.toLowerCase();
|
||||
final contacts = ref.read(addressBookServiceProvider).contacts;
|
||||
final notes =
|
||||
ref.read(notesServiceChangeNotifierProvider(walletId)).notesSync;
|
||||
final notes = ref
|
||||
.read(mainDBProvider)
|
||||
.isar
|
||||
.transactionNotes
|
||||
.where()
|
||||
.walletIdEqualTo(walletId)
|
||||
.findAllSync();
|
||||
|
||||
return transactions
|
||||
.where((tx) => _isKeywordMatch(tx, text, contacts, notes))
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:stackwallet/models/isar/models/transaction_note.dart';
|
||||
import 'package:stackwallet/providers/db/main_db_provider.dart';
|
||||
import 'package:stackwallet/providers/providers.dart';
|
||||
import 'package:stackwallet/themes/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
|
@ -29,14 +31,12 @@ class EditNoteView extends ConsumerStatefulWidget {
|
|||
Key? key,
|
||||
required this.txid,
|
||||
required this.walletId,
|
||||
required this.note,
|
||||
}) : super(key: key);
|
||||
|
||||
static const String routeName = "/editNote";
|
||||
|
||||
final String txid;
|
||||
final String walletId;
|
||||
final String note;
|
||||
|
||||
@override
|
||||
ConsumerState<EditNoteView> createState() => _EditNoteViewState();
|
||||
|
@ -48,11 +48,19 @@ class _EditNoteViewState extends ConsumerState<EditNoteView> {
|
|||
|
||||
late final bool isDesktop;
|
||||
|
||||
TransactionNote? _note;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
isDesktop = Util.isDesktop;
|
||||
_noteController = TextEditingController();
|
||||
_noteController.text = widget.note;
|
||||
|
||||
_note = ref.read(
|
||||
pTransactionNote(
|
||||
(txid: widget.txid, walletId: widget.walletId),
|
||||
),
|
||||
);
|
||||
_noteController.text = _note?.value ?? "";
|
||||
super.initState();
|
||||
}
|
||||
|
||||
|
@ -185,13 +193,15 @@ class _EditNoteViewState extends ConsumerState<EditNoteView> {
|
|||
child: PrimaryButton(
|
||||
label: "Save",
|
||||
onPressed: () async {
|
||||
await ref
|
||||
.read(notesServiceChangeNotifierProvider(
|
||||
widget.walletId))
|
||||
.editOrAddNote(
|
||||
txid: widget.txid,
|
||||
note: _noteController.text,
|
||||
await ref.read(mainDBProvider).putTransactionNote(
|
||||
_note?.copyWith(value: _noteController.text) ??
|
||||
TransactionNote(
|
||||
walletId: widget.walletId,
|
||||
txid: widget.walletId,
|
||||
value: _noteController.text,
|
||||
),
|
||||
);
|
||||
|
||||
if (mounted) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
|
@ -201,12 +211,13 @@ class _EditNoteViewState extends ConsumerState<EditNoteView> {
|
|||
if (!isDesktop)
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
await ref
|
||||
.read(
|
||||
notesServiceChangeNotifierProvider(widget.walletId))
|
||||
.editOrAddNote(
|
||||
txid: widget.txid,
|
||||
note: _noteController.text,
|
||||
await ref.read(mainDBProvider).putTransactionNote(
|
||||
_note?.copyWith(value: _noteController.text) ??
|
||||
TransactionNote(
|
||||
walletId: widget.walletId,
|
||||
txid: widget.walletId,
|
||||
value: _noteController.text,
|
||||
),
|
||||
);
|
||||
if (mounted) {
|
||||
Navigator.of(context).pop();
|
||||
|
|
|
@ -216,8 +216,6 @@ class _TransactionDetailsViewState
|
|||
}
|
||||
}
|
||||
|
||||
String _note = "";
|
||||
|
||||
Future<bool> showExplorerWarning(String explorer) async {
|
||||
final bool? shouldContinue = await showDialog<bool>(
|
||||
context: context,
|
||||
|
@ -879,7 +877,6 @@ class _TransactionDetailsViewState
|
|||
child: EditNoteView(
|
||||
txid: _transaction.txid,
|
||||
walletId: walletId,
|
||||
note: _note,
|
||||
),
|
||||
);
|
||||
},
|
||||
|
@ -890,10 +887,9 @@ class _TransactionDetailsViewState
|
|||
onTap: () {
|
||||
Navigator.of(context).pushNamed(
|
||||
EditNoteView.routeName,
|
||||
arguments: Tuple3(
|
||||
arguments: Tuple2(
|
||||
_transaction.txid,
|
||||
walletId,
|
||||
_note,
|
||||
),
|
||||
);
|
||||
},
|
||||
|
@ -924,36 +920,28 @@ class _TransactionDetailsViewState
|
|||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
FutureBuilder(
|
||||
future: ref.watch(
|
||||
notesServiceChangeNotifierProvider(
|
||||
walletId)
|
||||
.select((value) => value.getNoteFor(
|
||||
txid: (coin == Coin.epicCash)
|
||||
? _transaction.slateId!
|
||||
: _transaction.txid))),
|
||||
builder: (builderContext,
|
||||
AsyncSnapshot<String> snapshot) {
|
||||
if (snapshot.connectionState ==
|
||||
ConnectionState.done &&
|
||||
snapshot.hasData) {
|
||||
_note = snapshot.data ?? "";
|
||||
}
|
||||
return SelectableText(
|
||||
_note,
|
||||
style: isDesktop
|
||||
? STextStyles
|
||||
.desktopTextExtraExtraSmall(
|
||||
context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark,
|
||||
)
|
||||
: STextStyles.itemSubtitle12(
|
||||
context),
|
||||
);
|
||||
},
|
||||
SelectableText(
|
||||
ref
|
||||
.watch(
|
||||
pTransactionNote(
|
||||
(
|
||||
txid: _transaction.txid,
|
||||
walletId: walletId
|
||||
),
|
||||
),
|
||||
)
|
||||
?.value ??
|
||||
"",
|
||||
style: isDesktop
|
||||
? STextStyles
|
||||
.desktopTextExtraExtraSmall(
|
||||
context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark,
|
||||
)
|
||||
: STextStyles.itemSubtitle12(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -17,6 +17,7 @@ import 'package:isar/isar.dart';
|
|||
import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart';
|
||||
import 'package:stackwallet/models/isar/models/blockchain_data/v2/transaction_v2.dart';
|
||||
import 'package:stackwallet/models/isar/models/contact_entry.dart';
|
||||
import 'package:stackwallet/models/isar/models/isar_models.dart';
|
||||
import 'package:stackwallet/models/transaction_filter.dart';
|
||||
import 'package:stackwallet/pages/wallet_view/sub_widgets/tx_icon.dart';
|
||||
import 'package:stackwallet/pages/wallet_view/transaction_views/transaction_search_filter_view.dart';
|
||||
|
@ -101,8 +102,13 @@ class _AllTransactionsV2ViewState extends ConsumerState<AllTransactionsV2View> {
|
|||
// debugPrint("FILTER: $filter");
|
||||
|
||||
final contacts = ref.read(addressBookServiceProvider).contacts;
|
||||
final notes =
|
||||
ref.read(notesServiceChangeNotifierProvider(walletId)).notesSync;
|
||||
final notes = ref
|
||||
.read(mainDBProvider)
|
||||
.isar
|
||||
.transactionNotes
|
||||
.where()
|
||||
.walletIdEqualTo(walletId)
|
||||
.findAllSync();
|
||||
|
||||
return transactions.where((tx) {
|
||||
if (!filter.sent && !filter.received) {
|
||||
|
@ -139,7 +145,7 @@ class _AllTransactionsV2ViewState extends ConsumerState<AllTransactionsV2View> {
|
|||
TransactionV2 tx,
|
||||
String keyword,
|
||||
List<ContactEntry> contacts,
|
||||
Map<String, String> notes,
|
||||
List<TransactionNote> notes,
|
||||
) {
|
||||
if (keyword.isEmpty) {
|
||||
return true;
|
||||
|
@ -164,9 +170,15 @@ class _AllTransactionsV2ViewState extends ConsumerState<AllTransactionsV2View> {
|
|||
.where((e) => e.toLowerCase().contains(keyword))
|
||||
.isNotEmpty;
|
||||
|
||||
TransactionNote? note;
|
||||
final matchingNotes = notes.where((e) => e.txid == tx.txid);
|
||||
if (matchingNotes.isNotEmpty) {
|
||||
note = matchingNotes.first;
|
||||
}
|
||||
|
||||
// check if note contains
|
||||
contains |= notes[tx.txid] != null &&
|
||||
notes[tx.txid]!.toLowerCase().contains(keyword);
|
||||
contains |= note != null &&
|
||||
note.value.toLowerCase().contains(keyword);
|
||||
|
||||
// check if txid contains
|
||||
contains |= tx.txid.toLowerCase().contains(keyword);
|
||||
|
@ -193,8 +205,13 @@ class _AllTransactionsV2ViewState extends ConsumerState<AllTransactionsV2View> {
|
|||
}
|
||||
text = text.toLowerCase();
|
||||
final contacts = ref.read(addressBookServiceProvider).contacts;
|
||||
final notes =
|
||||
ref.read(notesServiceChangeNotifierProvider(walletId)).notesSync;
|
||||
final notes = ref
|
||||
.read(mainDBProvider)
|
||||
.isar
|
||||
.transactionNotes
|
||||
.where()
|
||||
.walletIdEqualTo(walletId)
|
||||
.findAllSync();
|
||||
|
||||
return transactions
|
||||
.where((tx) => _isKeywordMatch(tx, text, contacts, notes))
|
||||
|
|
|
@ -256,8 +256,6 @@ class _TransactionV2DetailsViewState
|
|||
}
|
||||
}
|
||||
|
||||
String _note = "";
|
||||
|
||||
Future<bool> showExplorerWarning(String explorer) async {
|
||||
final bool? shouldContinue = await showDialog<bool>(
|
||||
context: context,
|
||||
|
@ -927,7 +925,6 @@ class _TransactionV2DetailsViewState
|
|||
child: EditNoteView(
|
||||
txid: _transaction.txid,
|
||||
walletId: walletId,
|
||||
note: _note,
|
||||
),
|
||||
);
|
||||
},
|
||||
|
@ -938,10 +935,9 @@ class _TransactionV2DetailsViewState
|
|||
onTap: () {
|
||||
Navigator.of(context).pushNamed(
|
||||
EditNoteView.routeName,
|
||||
arguments: Tuple3(
|
||||
arguments: Tuple2(
|
||||
_transaction.txid,
|
||||
walletId,
|
||||
_note,
|
||||
),
|
||||
);
|
||||
},
|
||||
|
@ -972,34 +968,28 @@ class _TransactionV2DetailsViewState
|
|||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
FutureBuilder(
|
||||
future: ref.watch(
|
||||
notesServiceChangeNotifierProvider(
|
||||
walletId)
|
||||
.select((value) => value.getNoteFor(
|
||||
txid: _transaction.txid))),
|
||||
builder: (builderContext,
|
||||
AsyncSnapshot<String> snapshot) {
|
||||
if (snapshot.connectionState ==
|
||||
ConnectionState.done &&
|
||||
snapshot.hasData) {
|
||||
_note = snapshot.data ?? "";
|
||||
}
|
||||
return SelectableText(
|
||||
_note,
|
||||
style: isDesktop
|
||||
? STextStyles
|
||||
.desktopTextExtraExtraSmall(
|
||||
context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark,
|
||||
)
|
||||
: STextStyles.itemSubtitle12(
|
||||
context),
|
||||
);
|
||||
},
|
||||
SelectableText(
|
||||
ref
|
||||
.watch(
|
||||
pTransactionNote(
|
||||
(
|
||||
txid: _transaction.txid,
|
||||
walletId: walletId
|
||||
),
|
||||
),
|
||||
)
|
||||
?.value ??
|
||||
"",
|
||||
style: isDesktop
|
||||
? STextStyles
|
||||
.desktopTextExtraExtraSmall(
|
||||
context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark,
|
||||
)
|
||||
: STextStyles.itemSubtitle12(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -31,4 +31,4 @@ export './ui/home_view_index_provider.dart';
|
|||
export './ui/verify_recovery_phrase/correct_word_provider.dart';
|
||||
export './ui/verify_recovery_phrase/random_index_provider.dart';
|
||||
export './ui/verify_recovery_phrase/selected_word_provider.dart';
|
||||
export './wallet/notes_service_provider.dart';
|
||||
export './wallet/transaction_note_provider.dart';
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
/*
|
||||
* This file is part of Stack Wallet.
|
||||
*
|
||||
* Copyright (c) 2023 Cypher Stack
|
||||
* All Rights Reserved.
|
||||
* The code is distributed under GPLv3 license, see LICENSE file for details.
|
||||
* Generated by Cypher Stack on 2023-05-26
|
||||
*
|
||||
*/
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:stackwallet/services/notes_service.dart';
|
||||
|
||||
int _count = 0;
|
||||
|
||||
final notesServiceChangeNotifierProvider =
|
||||
ChangeNotifierProvider.family<NotesService, String>((_, walletId) {
|
||||
if (kDebugMode) {
|
||||
_count++;
|
||||
}
|
||||
|
||||
return NotesService(walletId: walletId);
|
||||
});
|
75
lib/providers/wallet/transaction_note_provider.dart
Normal file
75
lib/providers/wallet/transaction_note_provider.dart
Normal file
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* This file is part of Stack Wallet.
|
||||
*
|
||||
* Copyright (c) 2023 Cypher Stack
|
||||
* All Rights Reserved.
|
||||
* The code is distributed under GPLv3 license, see LICENSE file for details.
|
||||
* Generated by Cypher Stack on 2023-05-26
|
||||
*
|
||||
*/
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:stackwallet/models/isar/models/transaction_note.dart';
|
||||
import 'package:stackwallet/providers/db/main_db_provider.dart';
|
||||
|
||||
class _TransactionNoteWatcher extends ChangeNotifier {
|
||||
final ({String walletId, String txid}) key;
|
||||
late final StreamSubscription<List<TransactionNote>> _streamSubscription;
|
||||
|
||||
TransactionNote? _value;
|
||||
|
||||
TransactionNote? get value => _value;
|
||||
|
||||
_TransactionNoteWatcher(this._value, this.key, Isar isar) {
|
||||
_streamSubscription = isar.transactionNotes
|
||||
.where()
|
||||
.txidWalletIdEqualTo(key.txid, key.walletId)
|
||||
.watch(fireImmediately: true)
|
||||
.listen((event) {
|
||||
print("AAAAAA $event");
|
||||
if (event.isEmpty) {
|
||||
_value = null;
|
||||
} else {
|
||||
_value = event.first;
|
||||
}
|
||||
notifyListeners();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_streamSubscription.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
final _wiProvider = ChangeNotifierProvider.family<_TransactionNoteWatcher,
|
||||
({String walletId, String txid})>(
|
||||
(ref, key) {
|
||||
final isar = ref.watch(mainDBProvider).isar;
|
||||
|
||||
final watcher = _TransactionNoteWatcher(
|
||||
isar.transactionNotes
|
||||
.where()
|
||||
.txidWalletIdEqualTo(key.txid, key.walletId)
|
||||
.findFirstSync(),
|
||||
key,
|
||||
isar,
|
||||
);
|
||||
|
||||
ref.onDispose(() => watcher.dispose());
|
||||
|
||||
return watcher;
|
||||
},
|
||||
);
|
||||
|
||||
final pTransactionNote =
|
||||
Provider.family<TransactionNote?, ({String walletId, String txid})>(
|
||||
(ref, key) {
|
||||
return ref.watch(_wiProvider(key)).value;
|
||||
},
|
||||
);
|
|
@ -906,13 +906,12 @@ class RouteGenerator {
|
|||
return _routeError("${settings.name} invalid args: ${args.toString()}");
|
||||
|
||||
case EditNoteView.routeName:
|
||||
if (args is Tuple3<String, String, String>) {
|
||||
if (args is Tuple2<String, String>) {
|
||||
return getRoute(
|
||||
shouldUseMaterialRoute: useMaterialPageRoute,
|
||||
builder: (_) => EditNoteView(
|
||||
txid: args.item1,
|
||||
walletId: args.item2,
|
||||
note: args.item3,
|
||||
),
|
||||
settings: RouteSettings(
|
||||
name: settings.name,
|
||||
|
|
|
@ -1,85 +0,0 @@
|
|||
/*
|
||||
* This file is part of Stack Wallet.
|
||||
*
|
||||
* Copyright (c) 2023 Cypher Stack
|
||||
* All Rights Reserved.
|
||||
* The code is distributed under GPLv3 license, see LICENSE file for details.
|
||||
* Generated by Cypher Stack on 2023-05-26
|
||||
*
|
||||
*/
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stackwallet/db/hive/db.dart';
|
||||
import 'package:stackwallet/utilities/logger.dart';
|
||||
|
||||
class NotesService extends ChangeNotifier {
|
||||
final String walletId;
|
||||
|
||||
NotesService({required this.walletId});
|
||||
|
||||
Map<String, String> get notesSync {
|
||||
final notes =
|
||||
DB.instance.get<dynamic>(boxName: walletId, key: 'notes') as Map?;
|
||||
return notes == null ? <String, String>{} : Map<String, String>.from(notes);
|
||||
}
|
||||
|
||||
/// Holds transaction notes
|
||||
/// map of contact <txid, note>
|
||||
/// txid is used as key due to uniqueness
|
||||
Future<Map<String, String>>? _notes;
|
||||
Future<Map<String, String>> get notes => _notes ??= _fetchNotes();
|
||||
|
||||
// fetch notes map
|
||||
Future<Map<String, String>> _fetchNotes() async {
|
||||
final notes =
|
||||
DB.instance.get<dynamic>(boxName: walletId, key: 'notes') as Map?;
|
||||
|
||||
return notes == null ? <String, String>{} : Map<String, String>.from(notes);
|
||||
}
|
||||
|
||||
/// search notes
|
||||
//TODO optimize notes search?
|
||||
Future<Map<String, String>> search(String text) async {
|
||||
if (text.isEmpty) return notes;
|
||||
var results = Map<String, String>.from(await notes);
|
||||
results.removeWhere(
|
||||
(key, value) => (!key.contains(text) && !value.contains(text)));
|
||||
return results;
|
||||
}
|
||||
|
||||
/// fetch note given a transaction ID
|
||||
Future<String> getNoteFor({required String txid}) async {
|
||||
final note = (await notes)[txid];
|
||||
return note ?? "";
|
||||
}
|
||||
|
||||
/// edit or add new note for the given [txid]
|
||||
Future<void> editOrAddNote(
|
||||
{required String txid, required String note}) async {
|
||||
final _notes = await notes;
|
||||
|
||||
_notes[txid] = note;
|
||||
await DB.instance
|
||||
.put<dynamic>(boxName: walletId, key: 'notes', value: _notes);
|
||||
//todo: check if this is needed
|
||||
Logging.instance.log("editOrAddNote: tx note saved", level: LogLevel.Info);
|
||||
await _refreshNotes();
|
||||
}
|
||||
|
||||
/// Remove note from db
|
||||
Future<void> deleteNote({required String txid}) async {
|
||||
final entries =
|
||||
DB.instance.get<dynamic>(boxName: walletId, key: 'notes') as Map;
|
||||
entries.remove(txid);
|
||||
await DB.instance
|
||||
.put<dynamic>(boxName: walletId, key: 'notes', value: entries);
|
||||
Logging.instance.log("tx note removed", level: LogLevel.Info);
|
||||
await _refreshNotes();
|
||||
}
|
||||
|
||||
Future<void> _refreshNotes() async {
|
||||
final newNotes = await _fetchNotes();
|
||||
_notes = Future(() => newNotes);
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:hive_test/hive_test.dart';
|
||||
import 'package:stackwallet/services/notes_service.dart';
|
||||
|
||||
void main() {
|
||||
setUp(() async {
|
||||
await setUpTestHive();
|
||||
final wallets = await Hive.openBox<dynamic>('wallets');
|
||||
await wallets.put('names', {"My Firo Wallet": "wallet_id"});
|
||||
await wallets.put('currentWalletName', "My Firo Wallet");
|
||||
final wallet = await Hive.openBox<dynamic>("wallet_id");
|
||||
await wallet.put("notes", {"txid1": "note1", "txid2": "note2"});
|
||||
});
|
||||
|
||||
test("get null notes", () async {
|
||||
final service = NotesService(walletId: 'wallet_id');
|
||||
final wallet = await Hive.openBox<dynamic>("wallet_id");
|
||||
await wallet.put("notes", null);
|
||||
expect(await service.notes, <String, String>{});
|
||||
});
|
||||
|
||||
test("get empty notes", () async {
|
||||
final service = NotesService(walletId: 'wallet_id');
|
||||
final wallet = await Hive.openBox<dynamic>("wallet_id");
|
||||
await wallet.put("notes", <String, String>{});
|
||||
expect(await service.notes, <String, String>{});
|
||||
});
|
||||
|
||||
test("get some notes", () async {
|
||||
final service = NotesService(walletId: 'wallet_id');
|
||||
expect(await service.notes, {"txid1": "note1", "txid2": "note2"});
|
||||
});
|
||||
|
||||
test("search finds none", () async {
|
||||
final service = NotesService(walletId: 'wallet_id');
|
||||
expect(await service.search("some"), <String, String>{});
|
||||
});
|
||||
|
||||
test("empty search", () async {
|
||||
final service = NotesService(walletId: 'wallet_id');
|
||||
expect(await service.search(""), {"txid1": "note1", "txid2": "note2"});
|
||||
});
|
||||
|
||||
test("search finds some", () async {
|
||||
final service = NotesService(walletId: 'wallet_id');
|
||||
expect(await service.search("note"), {"txid1": "note1", "txid2": "note2"});
|
||||
});
|
||||
|
||||
test("search finds one", () async {
|
||||
final service = NotesService(walletId: 'wallet_id');
|
||||
expect(await service.search("2"), {"txid2": "note2"});
|
||||
});
|
||||
|
||||
test("get note for existing txid", () async {
|
||||
final service = NotesService(walletId: 'wallet_id');
|
||||
expect(await service.getNoteFor(txid: "txid1"), "note1");
|
||||
});
|
||||
|
||||
test("get note for non existing txid", () async {
|
||||
final service = NotesService(walletId: 'wallet_id');
|
||||
expect(await service.getNoteFor(txid: "txid"), "");
|
||||
});
|
||||
|
||||
test("add new note", () async {
|
||||
final service = NotesService(walletId: 'wallet_id');
|
||||
await service.editOrAddNote(txid: "txid3", note: "note3");
|
||||
expect(await service.notes,
|
||||
{"txid1": "note1", "txid2": "note2", "txid3": "note3"});
|
||||
});
|
||||
|
||||
test("add or overwrite note for new txid", () async {
|
||||
final service = NotesService(walletId: 'wallet_id');
|
||||
await service.editOrAddNote(txid: "txid3", note: "note3");
|
||||
expect(await service.notes,
|
||||
{"txid1": "note1", "txid2": "note2", "txid3": "note3"});
|
||||
});
|
||||
|
||||
test("add or overwrite note for existing txid", () async {
|
||||
final service = NotesService(walletId: 'wallet_id');
|
||||
await service.editOrAddNote(txid: "txid2", note: "note3");
|
||||
expect(await service.notes, {"txid1": "note1", "txid2": "note3"});
|
||||
});
|
||||
|
||||
test("delete existing note", () async {
|
||||
final service = NotesService(walletId: 'wallet_id');
|
||||
await service.deleteNote(txid: "txid2");
|
||||
expect(await service.notes, {"txid1": "note1"});
|
||||
});
|
||||
|
||||
test("delete non existing note", () async {
|
||||
final service = NotesService(walletId: 'wallet_id');
|
||||
await service.deleteNote(txid: "txid5");
|
||||
expect(await service.notes, {"txid1": "note1", "txid2": "note2"});
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
await tearDownTestHive();
|
||||
});
|
||||
}
|
Loading…
Reference in a new issue