feat: move address books to isar

This commit is contained in:
detherminal 2023-05-14 18:06:47 +03:00
parent b2c365797c
commit 524f4c5514
5 changed files with 1404 additions and 46 deletions

View file

@ -3,6 +3,7 @@ import 'package:flutter_native_splash/cli_commands.dart';
import 'package:isar/isar.dart'; import 'package:isar/isar.dart';
import 'package:stackwallet/exceptions/main_db/main_db_exception.dart'; import 'package:stackwallet/exceptions/main_db/main_db_exception.dart';
import 'package:stackwallet/models/isar/models/block_explorer.dart'; import 'package:stackwallet/models/isar/models/block_explorer.dart';
import 'package:stackwallet/models/isar/models/contact_entry.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart'; import 'package:stackwallet/models/isar/models/isar_models.dart';
import 'package:stackwallet/models/isar/stack_theme.dart'; import 'package:stackwallet/models/isar/stack_theme.dart';
import 'package:stackwallet/utilities/amount/amount.dart'; import 'package:stackwallet/utilities/amount/amount.dart';
@ -37,6 +38,7 @@ class MainDB {
EthContractSchema, EthContractSchema,
TransactionBlockExplorerSchema, TransactionBlockExplorerSchema,
StackThemeSchema, StackThemeSchema,
ContactEntrySchema,
], ],
directory: (await StackFileSystem.applicationIsarDirectory()).path, directory: (await StackFileSystem.applicationIsarDirectory()).path,
// inspector: kDebugMode, // inspector: kDebugMode,
@ -47,6 +49,45 @@ class MainDB {
return true; return true;
} }
// contact entries
List<ContactEntry> getContactEntries(){
return isar.contactEntrys.where().findAllSync();
}
Future<bool> deleteContactEntry({required String id}) {
try {
return isar.writeTxn(() async {
await isar.contactEntrys.deleteByCustomId(id);
return true;
});
} catch (e) {
throw MainDBException("failed deleteContactEntry: $id", e);
}
}
Future<bool> isContactEntryExists({required String id}) async {
return isar.contactEntrys
.where()
.customIdEqualTo(id)
.count()
.then((value) => value > 0);
}
ContactEntry? getContactEntry({required String id}) {
return isar.contactEntrys.where().customIdEqualTo(id).findFirstSync();
}
Future<bool> putContactEntry({required ContactEntry contactEntry}) async {
try {
return await isar.writeTxn(() async {
await isar.contactEntrys.put(contactEntry);
return true;
});
} catch (e) {
throw MainDBException("failed putContactEntry: $contactEntry", e);
}
}
// tx block explorers // tx block explorers
TransactionBlockExplorer? getTransactionBlockExplorer({required Coin coin}) { TransactionBlockExplorer? getTransactionBlockExplorer({required Coin coin}) {
return isar.transactionBlockExplorers return isar.transactionBlockExplorers

View file

@ -0,0 +1,24 @@
import 'package:isar/isar.dart';
part 'contact_entry.g.dart';
@collection
class ContactEntry {
ContactEntry({
this.emojiChar,
required this.name,
required this.addresses,
required this.isFavorite,
required this.customId,
});
Id id = Isar.autoIncrement;
late final String? emojiChar;
late final String name;
late final List<String> addresses;
late final bool isFavorite;
@Index(unique: true, replace: true)
late final String customId;
}

File diff suppressed because it is too large Load diff

View file

@ -102,8 +102,7 @@ class _AddressBookViewState extends ConsumerState<AddressBookView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType"); debugPrint("BUILD: $runtimeType");
final contacts = final contacts = ref.watch(addressBookServiceProvider.select((value) => value.contacts));
ref.watch(addressBookServiceProvider.select((value) => value.contacts));
final isDesktop = Util.isDesktop; final isDesktop = Util.isDesktop;
return ConditionalParent( return ConditionalParent(

View file

@ -1,40 +1,92 @@
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:stackwallet/db/hive/db.dart'; import 'package:stackwallet/db/isar/main_db.dart';
import 'package:stackwallet/models/contact.dart'; import 'package:stackwallet/models/contact.dart';
import 'package:stackwallet/models/contact_address_entry.dart';
import 'package:stackwallet/models/isar/models/contact_entry.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/logger.dart'; import 'package:stackwallet/utilities/logger.dart';
class AddressBookService extends ChangeNotifier { class AddressBookService extends ChangeNotifier {
Contact getContactById(String id) {
final json = DB.instance ContactEntry turnContactToEntry({required Contact contact}) {
.get<dynamic>(boxName: DB.boxNameAddressBook, key: id) as Map?; String? emojiChar = contact.emojiChar;
if (json == null) { String name = contact.name;
Logging.instance List<String> addresses = [];
.log("Attempted to get non existing contact", level: LogLevel.Fatal); bool isFavorite = contact.isFavorite;
throw Exception('Contact ID "$id" not found!'); String customId = contact.id;
for (ContactAddressEntry contactAddressEntry in contact.addresses) {
String coin = contactAddressEntry.coin.ticker;
String address = contactAddressEntry.address;
String label = contactAddressEntry.label;
String? other = contactAddressEntry.other;
addresses.add("$coin,$address,$label,$other");
}
return ContactEntry(
emojiChar: emojiChar,
name: name,
addresses: addresses,
isFavorite: isFavorite,
customId: customId,
);
}
Contact turnEntryToContact({required ContactEntry contactEntry}) {
String? emojiChar = contactEntry.emojiChar;
String name = contactEntry.name;
List<ContactAddressEntry> addresses = [];
bool isFavorite = contactEntry.isFavorite;
String id = contactEntry.customId;
for (String addressEntry in contactEntry.addresses) {
List<String> addressEntrySplit = addressEntry.split(",");
Coin coin = coinFromTickerCaseInsensitive(addressEntrySplit[0]);
String address = addressEntrySplit[1];
String label = addressEntrySplit[2];
String? other = addressEntrySplit[3];
addresses.add(ContactAddressEntry(
coin: coin,
address: address,
label: label,
other: other,
));
}
return Contact(
emojiChar: emojiChar,
name: name,
addresses: addresses,
isFavorite: isFavorite,
id: id,
);
}
Contact getContactById(String id) {
ContactEntry? contactEntry = MainDB.instance.getContactEntry(id: id);
if (contactEntry == null) {
return Contact(
name: "Contact not found",
addresses: [],
isFavorite: false,
);
} else {
return turnEntryToContact(contactEntry: contactEntry);
} }
return Contact.fromJson(Map<String, dynamic>.from(json));
} }
List<Contact> get contacts { List<Contact> get contacts {
final keys = List<String>.from( List<ContactEntry> contactEntries = MainDB.instance.getContactEntries();
DB.instance.keys<dynamic>(boxName: DB.boxNameAddressBook)); List<Contact> contactsList = [];
final _contacts = keys for (ContactEntry contactEntry in contactEntries) {
.map((id) => Contact.fromJson(Map<String, dynamic>.from(DB.instance contactsList.add(turnEntryToContact(contactEntry: contactEntry));
.get<dynamic>(boxName: DB.boxNameAddressBook, key: id) as Map))) }
.toList(growable: false); return contactsList;
_contacts
.sort((a, b) => a.name.toLowerCase().compareTo(b.name.toLowerCase()));
return _contacts;
} }
Future<List<Contact>>? _addressBookEntries; List<Contact>? _addressBookEntries;
Future<List<Contact>> get addressBookEntries => List<Contact> get addressBookEntries =>
_addressBookEntries ??= _fetchAddressBookEntries(); _addressBookEntries ??= _fetchAddressBookEntries();
// Load address book contact entries // Load address book contact entries
Future<List<Contact>> _fetchAddressBookEntries() async { List<Contact> _fetchAddressBookEntries() {
return contacts; return contacts;
} }
@ -74,43 +126,32 @@ class AddressBookService extends ChangeNotifier {
/// returns false if it provided [contact]'s id already exists in the database /// returns false if it provided [contact]'s id already exists in the database
/// other true if the [contact] was saved /// other true if the [contact] was saved
Future<bool> addContact(Contact contact) async { Future<bool> addContact(Contact contact) async {
if (DB.instance.containsKey<dynamic>( if (await MainDB.instance.isContactEntryExists(id: contact.id)) {
boxName: DB.boxNameAddressBook, key: contact.id)) {
return false; return false;
} else {
await MainDB.instance.putContactEntry(contactEntry: turnContactToEntry(contact: contact));
_refreshAddressBookEntries();
return true;
} }
await DB.instance.put<dynamic>(
boxName: DB.boxNameAddressBook,
key: contact.id,
value: contact.toMap());
Logging.instance.log("add address book entry saved", level: LogLevel.Info);
await _refreshAddressBookEntries();
return true;
} }
/// Edit contact /// Edit contact
Future<bool> editContact(Contact editedContact) async { Future<bool> editContact(Contact editedContact) async {
// over write the contact with edited version // over write the contact with edited version
await DB.instance.put<dynamic>( await MainDB.instance.putContactEntry(contactEntry: turnContactToEntry(contact: editedContact));
boxName: DB.boxNameAddressBook, _refreshAddressBookEntries();
key: editedContact.id,
value: editedContact.toMap());
Logging.instance.log("edit address book entry saved", level: LogLevel.Info);
await _refreshAddressBookEntries();
return true; return true;
} }
/// Remove address book contact entry from db if it exists /// Remove address book contact entry from db if it exists
Future<void> removeContact(String id) async { Future<void> removeContact(String id) async {
await DB.instance.delete<dynamic>(key: id, boxName: DB.boxNameAddressBook); await MainDB.instance.deleteContactEntry(id: id);
await _refreshAddressBookEntries(); _refreshAddressBookEntries();
} }
Future<void> _refreshAddressBookEntries() async { void _refreshAddressBookEntries() {
final newAddressBookEntries = await _fetchAddressBookEntries(); final newAddressBookEntries = _fetchAddressBookEntries();
_addressBookEntries = Future(() => newAddressBookEntries); _addressBookEntries = newAddressBookEntries;
notifyListeners(); notifyListeners();
} }
} }