From 524f4c55148a41b6877f1137fac9530d81ece5f0 Mon Sep 17 00:00:00 2001 From: detherminal <76167420+detherminal@users.noreply.github.com> Date: Sun, 14 May 2023 18:06:47 +0300 Subject: [PATCH 1/3] feat: move address books to isar --- lib/db/isar/main_db.dart | 41 + lib/models/isar/models/contact_entry.dart | 24 + lib/models/isar/models/contact_entry.g.dart | 1253 +++++++++++++++++ .../address_book_views/address_book_view.dart | 3 +- lib/services/address_book_service.dart | 129 +- 5 files changed, 1404 insertions(+), 46 deletions(-) create mode 100644 lib/models/isar/models/contact_entry.dart create mode 100644 lib/models/isar/models/contact_entry.g.dart diff --git a/lib/db/isar/main_db.dart b/lib/db/isar/main_db.dart index 0e9d9efc4..b9841de02 100644 --- a/lib/db/isar/main_db.dart +++ b/lib/db/isar/main_db.dart @@ -3,6 +3,7 @@ import 'package:flutter_native_splash/cli_commands.dart'; import 'package:isar/isar.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/contact_entry.dart'; import 'package:stackwallet/models/isar/models/isar_models.dart'; import 'package:stackwallet/models/isar/stack_theme.dart'; import 'package:stackwallet/utilities/amount/amount.dart'; @@ -37,6 +38,7 @@ class MainDB { EthContractSchema, TransactionBlockExplorerSchema, StackThemeSchema, + ContactEntrySchema, ], directory: (await StackFileSystem.applicationIsarDirectory()).path, // inspector: kDebugMode, @@ -47,6 +49,45 @@ class MainDB { return true; } + // contact entries + List getContactEntries(){ + return isar.contactEntrys.where().findAllSync(); + } + + Future 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 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 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 TransactionBlockExplorer? getTransactionBlockExplorer({required Coin coin}) { return isar.transactionBlockExplorers diff --git a/lib/models/isar/models/contact_entry.dart b/lib/models/isar/models/contact_entry.dart new file mode 100644 index 000000000..fac725ac9 --- /dev/null +++ b/lib/models/isar/models/contact_entry.dart @@ -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 addresses; + late final bool isFavorite; + + @Index(unique: true, replace: true) + late final String customId; +} \ No newline at end of file diff --git a/lib/models/isar/models/contact_entry.g.dart b/lib/models/isar/models/contact_entry.g.dart new file mode 100644 index 000000000..0b1d63faf --- /dev/null +++ b/lib/models/isar/models/contact_entry.g.dart @@ -0,0 +1,1253 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'contact_entry.dart'; + +// ************************************************************************** +// IsarCollectionGenerator +// ************************************************************************** + +// coverage:ignore-file +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters + +extension GetContactEntryCollection on Isar { + IsarCollection get contactEntrys => this.collection(); +} + +const ContactEntrySchema = CollectionSchema( + name: r'ContactEntry', + id: -3248212280610531288, + properties: { + r'addresses': PropertySchema( + id: 0, + name: r'addresses', + type: IsarType.stringList, + ), + r'customId': PropertySchema( + id: 1, + name: r'customId', + type: IsarType.string, + ), + r'emojiChar': PropertySchema( + id: 2, + name: r'emojiChar', + type: IsarType.string, + ), + r'isFavorite': PropertySchema( + id: 3, + name: r'isFavorite', + type: IsarType.bool, + ), + r'name': PropertySchema( + id: 4, + name: r'name', + type: IsarType.string, + ) + }, + estimateSize: _contactEntryEstimateSize, + serialize: _contactEntrySerialize, + deserialize: _contactEntryDeserialize, + deserializeProp: _contactEntryDeserializeProp, + idName: r'id', + indexes: { + r'customId': IndexSchema( + id: -7523974382886476007, + name: r'customId', + unique: true, + replace: true, + properties: [ + IndexPropertySchema( + name: r'customId', + type: IndexType.hash, + caseSensitive: true, + ) + ], + ) + }, + links: {}, + embeddedSchemas: {}, + getId: _contactEntryGetId, + getLinks: _contactEntryGetLinks, + attach: _contactEntryAttach, + version: '3.0.5', +); + +int _contactEntryEstimateSize( + ContactEntry object, + List offsets, + Map> allOffsets, +) { + var bytesCount = offsets.last; + bytesCount += 3 + object.addresses.length * 3; + { + for (var i = 0; i < object.addresses.length; i++) { + final value = object.addresses[i]; + bytesCount += value.length * 3; + } + } + bytesCount += 3 + object.customId.length * 3; + { + final value = object.emojiChar; + if (value != null) { + bytesCount += 3 + value.length * 3; + } + } + bytesCount += 3 + object.name.length * 3; + return bytesCount; +} + +void _contactEntrySerialize( + ContactEntry object, + IsarWriter writer, + List offsets, + Map> allOffsets, +) { + writer.writeStringList(offsets[0], object.addresses); + writer.writeString(offsets[1], object.customId); + writer.writeString(offsets[2], object.emojiChar); + writer.writeBool(offsets[3], object.isFavorite); + writer.writeString(offsets[4], object.name); +} + +ContactEntry _contactEntryDeserialize( + Id id, + IsarReader reader, + List offsets, + Map> allOffsets, +) { + final object = ContactEntry( + addresses: reader.readStringList(offsets[0]) ?? [], + customId: reader.readString(offsets[1]), + emojiChar: reader.readStringOrNull(offsets[2]), + isFavorite: reader.readBool(offsets[3]), + name: reader.readString(offsets[4]), + ); + object.id = id; + return object; +} + +P _contactEntryDeserializeProp

( + IsarReader reader, + int propertyId, + int offset, + Map> allOffsets, +) { + switch (propertyId) { + case 0: + return (reader.readStringList(offset) ?? []) as P; + case 1: + return (reader.readString(offset)) as P; + case 2: + return (reader.readStringOrNull(offset)) as P; + case 3: + return (reader.readBool(offset)) as P; + case 4: + return (reader.readString(offset)) as P; + default: + throw IsarError('Unknown property with id $propertyId'); + } +} + +Id _contactEntryGetId(ContactEntry object) { + return object.id; +} + +List> _contactEntryGetLinks(ContactEntry object) { + return []; +} + +void _contactEntryAttach( + IsarCollection col, Id id, ContactEntry object) { + object.id = id; +} + +extension ContactEntryByIndex on IsarCollection { + Future getByCustomId(String customId) { + return getByIndex(r'customId', [customId]); + } + + ContactEntry? getByCustomIdSync(String customId) { + return getByIndexSync(r'customId', [customId]); + } + + Future deleteByCustomId(String customId) { + return deleteByIndex(r'customId', [customId]); + } + + bool deleteByCustomIdSync(String customId) { + return deleteByIndexSync(r'customId', [customId]); + } + + Future> getAllByCustomId(List customIdValues) { + final values = customIdValues.map((e) => [e]).toList(); + return getAllByIndex(r'customId', values); + } + + List getAllByCustomIdSync(List customIdValues) { + final values = customIdValues.map((e) => [e]).toList(); + return getAllByIndexSync(r'customId', values); + } + + Future deleteAllByCustomId(List customIdValues) { + final values = customIdValues.map((e) => [e]).toList(); + return deleteAllByIndex(r'customId', values); + } + + int deleteAllByCustomIdSync(List customIdValues) { + final values = customIdValues.map((e) => [e]).toList(); + return deleteAllByIndexSync(r'customId', values); + } + + Future putByCustomId(ContactEntry object) { + return putByIndex(r'customId', object); + } + + Id putByCustomIdSync(ContactEntry object, {bool saveLinks = true}) { + return putByIndexSync(r'customId', object, saveLinks: saveLinks); + } + + Future> putAllByCustomId(List objects) { + return putAllByIndex(r'customId', objects); + } + + List putAllByCustomIdSync(List objects, + {bool saveLinks = true}) { + return putAllByIndexSync(r'customId', objects, saveLinks: saveLinks); + } +} + +extension ContactEntryQueryWhereSort + on QueryBuilder { + QueryBuilder anyId() { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(const IdWhereClause.any()); + }); + } +} + +extension ContactEntryQueryWhere + on QueryBuilder { + QueryBuilder idEqualTo(Id id) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IdWhereClause.between( + lower: id, + upper: id, + )); + }); + } + + QueryBuilder idNotEqualTo( + Id id) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: false), + ) + .addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: false), + ); + } else { + return query + .addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: false), + ) + .addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: false), + ); + } + }); + } + + QueryBuilder idGreaterThan( + Id id, + {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: include), + ); + }); + } + + QueryBuilder idLessThan(Id id, + {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: include), + ); + }); + } + + QueryBuilder idBetween( + Id lowerId, + Id upperId, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder customIdEqualTo( + String customId) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.equalTo( + indexName: r'customId', + value: [customId], + )); + }); + } + + QueryBuilder + customIdNotEqualTo(String customId) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'customId', + lower: [], + upper: [customId], + includeUpper: false, + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'customId', + lower: [customId], + includeLower: false, + upper: [], + )); + } else { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'customId', + lower: [customId], + includeLower: false, + upper: [], + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'customId', + lower: [], + upper: [customId], + includeUpper: false, + )); + } + }); + } +} + +extension ContactEntryQueryFilter + on QueryBuilder { + QueryBuilder + addressesElementEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'addresses', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + addressesElementGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'addresses', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + addressesElementLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'addresses', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + addressesElementBetween( + String lower, + String upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'addresses', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + addressesElementStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'addresses', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + addressesElementEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'addresses', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + addressesElementContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'addresses', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + addressesElementMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'addresses', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + addressesElementIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'addresses', + value: '', + )); + }); + } + + QueryBuilder + addressesElementIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'addresses', + value: '', + )); + }); + } + + QueryBuilder + addressesLengthEqualTo(int length) { + return QueryBuilder.apply(this, (query) { + return query.listLength( + r'addresses', + length, + true, + length, + true, + ); + }); + } + + QueryBuilder + addressesIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.listLength( + r'addresses', + 0, + true, + 0, + true, + ); + }); + } + + QueryBuilder + addressesIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.listLength( + r'addresses', + 0, + false, + 999999, + true, + ); + }); + } + + QueryBuilder + addressesLengthLessThan( + int length, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.listLength( + r'addresses', + 0, + true, + length, + include, + ); + }); + } + + QueryBuilder + addressesLengthGreaterThan( + int length, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.listLength( + r'addresses', + length, + include, + 999999, + true, + ); + }); + } + + QueryBuilder + addressesLengthBetween( + int lower, + int upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.listLength( + r'addresses', + lower, + includeLower, + upper, + includeUpper, + ); + }); + } + + QueryBuilder + customIdEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'customId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + customIdGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'customId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + customIdLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'customId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + customIdBetween( + String lower, + String upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'customId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + customIdStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'customId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + customIdEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'customId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + customIdContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'customId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + customIdMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'customId', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + customIdIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'customId', + value: '', + )); + }); + } + + QueryBuilder + customIdIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'customId', + value: '', + )); + }); + } + + QueryBuilder + emojiCharIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'emojiChar', + )); + }); + } + + QueryBuilder + emojiCharIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'emojiChar', + )); + }); + } + + QueryBuilder + emojiCharEqualTo( + String? value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'emojiChar', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + emojiCharGreaterThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'emojiChar', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + emojiCharLessThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'emojiChar', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + emojiCharBetween( + String? lower, + String? upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'emojiChar', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + emojiCharStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'emojiChar', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + emojiCharEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'emojiChar', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + emojiCharContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'emojiChar', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + emojiCharMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'emojiChar', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + emojiCharIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'emojiChar', + value: '', + )); + }); + } + + QueryBuilder + emojiCharIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'emojiChar', + value: '', + )); + }); + } + + QueryBuilder idEqualTo( + Id value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'id', + value: value, + )); + }); + } + + QueryBuilder idGreaterThan( + Id value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + )); + }); + } + + QueryBuilder idLessThan( + Id value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + )); + }); + } + + QueryBuilder idBetween( + Id lower, + Id upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder + isFavoriteEqualTo(bool value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'isFavorite', + value: value, + )); + }); + } + + QueryBuilder nameEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'name', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + nameGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'name', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder nameLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'name', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder nameBetween( + String lower, + String upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'name', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + nameStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'name', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder nameEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'name', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder nameContains( + String value, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'name', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder nameMatches( + String pattern, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'name', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + nameIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'name', + value: '', + )); + }); + } + + QueryBuilder + nameIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'name', + value: '', + )); + }); + } +} + +extension ContactEntryQueryObject + on QueryBuilder {} + +extension ContactEntryQueryLinks + on QueryBuilder {} + +extension ContactEntryQuerySortBy + on QueryBuilder { + QueryBuilder sortByCustomId() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'customId', Sort.asc); + }); + } + + QueryBuilder sortByCustomIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'customId', Sort.desc); + }); + } + + QueryBuilder sortByEmojiChar() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'emojiChar', Sort.asc); + }); + } + + QueryBuilder sortByEmojiCharDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'emojiChar', Sort.desc); + }); + } + + QueryBuilder sortByIsFavorite() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isFavorite', Sort.asc); + }); + } + + QueryBuilder + sortByIsFavoriteDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isFavorite', Sort.desc); + }); + } + + QueryBuilder sortByName() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'name', Sort.asc); + }); + } + + QueryBuilder sortByNameDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'name', Sort.desc); + }); + } +} + +extension ContactEntryQuerySortThenBy + on QueryBuilder { + QueryBuilder thenByCustomId() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'customId', Sort.asc); + }); + } + + QueryBuilder thenByCustomIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'customId', Sort.desc); + }); + } + + QueryBuilder thenByEmojiChar() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'emojiChar', Sort.asc); + }); + } + + QueryBuilder thenByEmojiCharDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'emojiChar', Sort.desc); + }); + } + + QueryBuilder thenById() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'id', Sort.asc); + }); + } + + QueryBuilder thenByIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'id', Sort.desc); + }); + } + + QueryBuilder thenByIsFavorite() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isFavorite', Sort.asc); + }); + } + + QueryBuilder + thenByIsFavoriteDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isFavorite', Sort.desc); + }); + } + + QueryBuilder thenByName() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'name', Sort.asc); + }); + } + + QueryBuilder thenByNameDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'name', Sort.desc); + }); + } +} + +extension ContactEntryQueryWhereDistinct + on QueryBuilder { + QueryBuilder distinctByAddresses() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'addresses'); + }); + } + + QueryBuilder distinctByCustomId( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'customId', caseSensitive: caseSensitive); + }); + } + + QueryBuilder distinctByEmojiChar( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'emojiChar', caseSensitive: caseSensitive); + }); + } + + QueryBuilder distinctByIsFavorite() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'isFavorite'); + }); + } + + QueryBuilder distinctByName( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'name', caseSensitive: caseSensitive); + }); + } +} + +extension ContactEntryQueryProperty + on QueryBuilder { + QueryBuilder idProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'id'); + }); + } + + QueryBuilder, QQueryOperations> + addressesProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'addresses'); + }); + } + + QueryBuilder customIdProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'customId'); + }); + } + + QueryBuilder emojiCharProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'emojiChar'); + }); + } + + QueryBuilder isFavoriteProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'isFavorite'); + }); + } + + QueryBuilder nameProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'name'); + }); + } +} diff --git a/lib/pages/address_book_views/address_book_view.dart b/lib/pages/address_book_views/address_book_view.dart index 8e3ef0629..81b7af4ca 100644 --- a/lib/pages/address_book_views/address_book_view.dart +++ b/lib/pages/address_book_views/address_book_view.dart @@ -102,8 +102,7 @@ class _AddressBookViewState extends ConsumerState { @override Widget build(BuildContext context) { debugPrint("BUILD: $runtimeType"); - final contacts = - ref.watch(addressBookServiceProvider.select((value) => value.contacts)); + final contacts = ref.watch(addressBookServiceProvider.select((value) => value.contacts)); final isDesktop = Util.isDesktop; return ConditionalParent( diff --git a/lib/services/address_book_service.dart b/lib/services/address_book_service.dart index e92c0b00b..6889edf6c 100644 --- a/lib/services/address_book_service.dart +++ b/lib/services/address_book_service.dart @@ -1,40 +1,92 @@ import 'package:flutter/cupertino.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_address_entry.dart'; +import 'package:stackwallet/models/isar/models/contact_entry.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/logger.dart'; class AddressBookService extends ChangeNotifier { - Contact getContactById(String id) { - final json = DB.instance - .get(boxName: DB.boxNameAddressBook, key: id) as Map?; - if (json == null) { - Logging.instance - .log("Attempted to get non existing contact", level: LogLevel.Fatal); - throw Exception('Contact ID "$id" not found!'); + + ContactEntry turnContactToEntry({required Contact contact}) { + String? emojiChar = contact.emojiChar; + String name = contact.name; + List addresses = []; + bool isFavorite = contact.isFavorite; + 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 addresses = []; + bool isFavorite = contactEntry.isFavorite; + String id = contactEntry.customId; + for (String addressEntry in contactEntry.addresses) { + List 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.from(json)); } List get contacts { - final keys = List.from( - DB.instance.keys(boxName: DB.boxNameAddressBook)); - final _contacts = keys - .map((id) => Contact.fromJson(Map.from(DB.instance - .get(boxName: DB.boxNameAddressBook, key: id) as Map))) - .toList(growable: false); - _contacts - .sort((a, b) => a.name.toLowerCase().compareTo(b.name.toLowerCase())); - return _contacts; + List contactEntries = MainDB.instance.getContactEntries(); + List contactsList = []; + for (ContactEntry contactEntry in contactEntries) { + contactsList.add(turnEntryToContact(contactEntry: contactEntry)); + } + return contactsList; } - Future>? _addressBookEntries; - Future> get addressBookEntries => + List? _addressBookEntries; + List get addressBookEntries => _addressBookEntries ??= _fetchAddressBookEntries(); // Load address book contact entries - Future> _fetchAddressBookEntries() async { + List _fetchAddressBookEntries() { return contacts; } @@ -74,43 +126,32 @@ class AddressBookService extends ChangeNotifier { /// returns false if it provided [contact]'s id already exists in the database /// other true if the [contact] was saved Future addContact(Contact contact) async { - if (DB.instance.containsKey( - boxName: DB.boxNameAddressBook, key: contact.id)) { + if (await MainDB.instance.isContactEntryExists(id: contact.id)) { return false; + } else { + await MainDB.instance.putContactEntry(contactEntry: turnContactToEntry(contact: contact)); + _refreshAddressBookEntries(); + return true; } - - await DB.instance.put( - 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 Future editContact(Contact editedContact) async { // over write the contact with edited version - await DB.instance.put( - boxName: DB.boxNameAddressBook, - key: editedContact.id, - value: editedContact.toMap()); - - Logging.instance.log("edit address book entry saved", level: LogLevel.Info); - await _refreshAddressBookEntries(); + await MainDB.instance.putContactEntry(contactEntry: turnContactToEntry(contact: editedContact)); + _refreshAddressBookEntries(); return true; } /// Remove address book contact entry from db if it exists Future removeContact(String id) async { - await DB.instance.delete(key: id, boxName: DB.boxNameAddressBook); - await _refreshAddressBookEntries(); + await MainDB.instance.deleteContactEntry(id: id); + _refreshAddressBookEntries(); } - Future _refreshAddressBookEntries() async { - final newAddressBookEntries = await _fetchAddressBookEntries(); - _addressBookEntries = Future(() => newAddressBookEntries); + void _refreshAddressBookEntries() { + final newAddressBookEntries = _fetchAddressBookEntries(); + _addressBookEntries = newAddressBookEntries; notifyListeners(); } } From 52bffbac083510e20a81f1c1192468647c9cc24f Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 15 May 2023 14:12:06 -0600 Subject: [PATCH 2/3] add contacts migration from hive to isar and remove contact class conversion --- lib/db/hive/db.dart | 2 - lib/models/contact.dart | 1 + lib/models/contact_address_entry.dart | 1 + lib/models/contact_address_entry_data.dart | 11 +- lib/models/isar/models/contact_entry.dart | 88 +- lib/models/isar/models/contact_entry.g.dart | 855 +++++++++++++++--- .../address_book_views/address_book_view.dart | 30 +- .../subviews/add_address_book_entry_view.dart | 7 +- .../add_new_contact_address_view.dart | 5 +- .../subviews/contact_details_view.dart | 4 +- .../subviews/edit_contact_address_view.dart | 10 +- .../helpers/restore_create_backup.dart | 37 +- .../all_transactions_view.dart | 6 +- .../desktop_address_book.dart | 36 +- .../subwidgets/desktop_address_card.dart | 4 +- .../subwidgets/desktop_contact_details.dart | 10 +- .../desktop_contact_options_menu_popup.dart | 2 +- .../address_book_address_chooser.dart | 19 +- lib/route_generator.dart | 3 +- lib/services/address_book_service.dart | 112 +-- lib/utilities/db_version_migration.dart | 60 ++ lib/widgets/address_book_card.dart | 10 +- test/address_book_service_test.dart | 174 ---- .../address_book_view_screen_test.mocks.dart | 37 +- ...d_address_book_view_screen_test.mocks.dart | 37 +- ..._entry_details_view_screen_test.mocks.dart | 37 +- ...ess_book_entry_view_screen_test.mocks.dart | 37 +- ...action_details_view_screen_test.mocks.dart | 37 +- ...saction_search_view_screen_test.mocks.dart | 37 +- test/widget_tests/address_book_card_test.dart | 19 +- .../address_book_card_test.mocks.dart | 37 +- 31 files changed, 1096 insertions(+), 669 deletions(-) delete mode 100644 test/address_book_service_test.dart diff --git a/lib/db/hive/db.dart b/lib/db/hive/db.dart index 1efa94cb2..5700f985d 100644 --- a/lib/db/hive/db.dart +++ b/lib/db/hive/db.dart @@ -41,7 +41,6 @@ class DB { String boxNameUsedSerialsCache({required Coin coin}) => "${coin.name}_usedSerialsCache"; - Box? _boxAddressBook; Box? _boxDebugInfo; Box? _boxNodeModels; Box? _boxPrimaryNodes; @@ -100,7 +99,6 @@ class DB { _boxPrefs = await Hive.openBox(boxNamePrefs); } - _boxAddressBook = await Hive.openBox(boxNameAddressBook); _boxDebugInfo = await Hive.openBox(boxNameDebugInfo); if (Hive.isBoxOpen(boxNameNodeModels)) { diff --git a/lib/models/contact.dart b/lib/models/contact.dart index 93ff779bb..37961476a 100644 --- a/lib/models/contact.dart +++ b/lib/models/contact.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'package:stackwallet/models/contact_address_entry.dart'; import 'package:uuid/uuid.dart'; +@Deprecated("Use lib/models/isar/models/contact_entry.dart instead") class Contact { final String? emojiChar; final String name; diff --git a/lib/models/contact_address_entry.dart b/lib/models/contact_address_entry.dart index ce85228b6..d917b47c0 100644 --- a/lib/models/contact_address_entry.dart +++ b/lib/models/contact_address_entry.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; +@Deprecated("Use lib/models/isar/models/contact_entry.dart instead") class ContactAddressEntry { final Coin coin; final String address; diff --git a/lib/models/contact_address_entry_data.dart b/lib/models/contact_address_entry_data.dart index 7c4641302..b67299a3e 100644 --- a/lib/models/contact_address_entry_data.dart +++ b/lib/models/contact_address_entry_data.dart @@ -1,5 +1,5 @@ import 'package:flutter/cupertino.dart'; -import 'package:stackwallet/models/contact_address_entry.dart'; +import 'package:stackwallet/models/isar/models/contact_entry.dart'; import 'package:stackwallet/utilities/address_utils.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; @@ -57,7 +57,7 @@ class AddressEntryData extends ChangeNotifier { } bool get isValidAddress { - if ( coin == null) { + if (coin == null) { return true; } if (_address == null) { @@ -67,8 +67,11 @@ class AddressEntryData extends ChangeNotifier { } ContactAddressEntry buildAddressEntry() { - return ContactAddressEntry( - coin: coin!, address: address!, label: addressLabel!); + return ContactAddressEntry() + ..coinName = coin!.name + ..address = address! + ..other = null + ..label = addressLabel!; } @override diff --git a/lib/models/isar/models/contact_entry.dart b/lib/models/isar/models/contact_entry.dart index fac725ac9..676b33d23 100644 --- a/lib/models/isar/models/contact_entry.dart +++ b/lib/models/isar/models/contact_entry.dart @@ -1,4 +1,5 @@ import 'package:isar/isar.dart'; +import 'package:stackwallet/utilities/enums/coin_enum.dart'; part 'contact_entry.g.dart'; @@ -16,9 +17,92 @@ class ContactEntry { late final String? emojiChar; late final String name; - late final List addresses; + late final List addresses; late final bool isFavorite; @Index(unique: true, replace: true) late final String customId; -} \ No newline at end of file + + ContactEntry copyWith({ + bool shouldCopyEmojiWithNull = false, + String? emojiChar, + String? name, + List? addresses, + bool? isFavorite, + }) { + List _addresses = []; + if (addresses == null) { + for (var e in this.addresses) { + _addresses.add(e.copyWith()); + } + } else { + for (var e in addresses) { + _addresses.add(e.copyWith()); + } + } + String? newEmoji; + if (shouldCopyEmojiWithNull) { + newEmoji = emojiChar; + } else { + newEmoji = emojiChar ?? this.emojiChar; + } + + return ContactEntry( + emojiChar: newEmoji, + name: name ?? this.name, + addresses: _addresses, + isFavorite: isFavorite ?? this.isFavorite, + customId: customId, + ); + } + + Map toMap() { + return { + "emoji": emojiChar, + "name": name, + "addresses": addresses.map((e) => e.toMap()).toList(), + "id": customId, + "isFavorite": isFavorite, + }; + } +} + +@embedded +class ContactAddressEntry { + late final String coinName; + late final String address; + late final String label; + late final String? other; + + @ignore + Coin get coin => Coin.values.byName(coinName); + + ContactAddressEntry(); + + ContactAddressEntry copyWith({ + Coin? coin, + String? address, + String? label, + String? other, + }) { + return ContactAddressEntry() + ..coinName = coin?.name ?? coinName + ..address = address ?? this.address + ..label = label ?? this.label + ..other = other ?? this.other; + } + + Map toMap() { + return { + "label": label, + "address": address, + "coin": coin.name, + "other": other ?? "", + }; + } + + @override + String toString() { + return "AddressBookEntry: ${toMap()}"; + } +} diff --git a/lib/models/isar/models/contact_entry.g.dart b/lib/models/isar/models/contact_entry.g.dart index 0b1d63faf..dc1eb2b61 100644 --- a/lib/models/isar/models/contact_entry.g.dart +++ b/lib/models/isar/models/contact_entry.g.dart @@ -20,7 +20,8 @@ const ContactEntrySchema = CollectionSchema( r'addresses': PropertySchema( id: 0, name: r'addresses', - type: IsarType.stringList, + type: IsarType.objectList, + target: r'ContactAddressEntry', ), r'customId': PropertySchema( id: 1, @@ -64,7 +65,7 @@ const ContactEntrySchema = CollectionSchema( ) }, links: {}, - embeddedSchemas: {}, + embeddedSchemas: {r'ContactAddressEntry': ContactAddressEntrySchema}, getId: _contactEntryGetId, getLinks: _contactEntryGetLinks, attach: _contactEntryAttach, @@ -79,9 +80,11 @@ int _contactEntryEstimateSize( var bytesCount = offsets.last; bytesCount += 3 + object.addresses.length * 3; { + final offsets = allOffsets[ContactAddressEntry]!; for (var i = 0; i < object.addresses.length; i++) { final value = object.addresses[i]; - bytesCount += value.length * 3; + bytesCount += + ContactAddressEntrySchema.estimateSize(value, offsets, allOffsets); } } bytesCount += 3 + object.customId.length * 3; @@ -101,7 +104,12 @@ void _contactEntrySerialize( List offsets, Map> allOffsets, ) { - writer.writeStringList(offsets[0], object.addresses); + writer.writeObjectList( + offsets[0], + allOffsets, + ContactAddressEntrySchema.serialize, + object.addresses, + ); writer.writeString(offsets[1], object.customId); writer.writeString(offsets[2], object.emojiChar); writer.writeBool(offsets[3], object.isFavorite); @@ -115,7 +123,13 @@ ContactEntry _contactEntryDeserialize( Map> allOffsets, ) { final object = ContactEntry( - addresses: reader.readStringList(offsets[0]) ?? [], + addresses: reader.readObjectList( + offsets[0], + ContactAddressEntrySchema.deserialize, + allOffsets, + ContactAddressEntry(), + ) ?? + [], customId: reader.readString(offsets[1]), emojiChar: reader.readStringOrNull(offsets[2]), isFavorite: reader.readBool(offsets[3]), @@ -133,7 +147,13 @@ P _contactEntryDeserializeProp

( ) { switch (propertyId) { case 0: - return (reader.readStringList(offset) ?? []) as P; + return (reader.readObjectList( + offset, + ContactAddressEntrySchema.deserialize, + allOffsets, + ContactAddressEntry(), + ) ?? + []) as P; case 1: return (reader.readString(offset)) as P; case 2: @@ -341,142 +361,6 @@ extension ContactEntryQueryWhere extension ContactEntryQueryFilter on QueryBuilder { - QueryBuilder - addressesElementEqualTo( - String value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'addresses', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - addressesElementGreaterThan( - String value, { - bool include = false, - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'addresses', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - addressesElementLessThan( - String value, { - bool include = false, - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'addresses', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - addressesElementBetween( - String lower, - String upper, { - bool includeLower = true, - bool includeUpper = true, - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'addresses', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - addressesElementStartsWith( - String value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'addresses', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - addressesElementEndsWith( - String value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'addresses', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - addressesElementContains(String value, {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'addresses', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - addressesElementMatches(String pattern, {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'addresses', - wildcard: pattern, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - addressesElementIsEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'addresses', - value: '', - )); - }); - } - - QueryBuilder - addressesElementIsNotEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'addresses', - value: '', - )); - }); - } - QueryBuilder addressesLengthEqualTo(int length) { return QueryBuilder.apply(this, (query) { @@ -1055,7 +939,14 @@ extension ContactEntryQueryFilter } extension ContactEntryQueryObject - on QueryBuilder {} + on QueryBuilder { + QueryBuilder + addressesElement(FilterQuery q) { + return QueryBuilder.apply(this, (query) { + return query.object(q, r'addresses'); + }); + } +} extension ContactEntryQueryLinks on QueryBuilder {} @@ -1178,12 +1069,6 @@ extension ContactEntryQuerySortThenBy extension ContactEntryQueryWhereDistinct on QueryBuilder { - QueryBuilder distinctByAddresses() { - return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(r'addresses'); - }); - } - QueryBuilder distinctByCustomId( {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { @@ -1220,7 +1105,7 @@ extension ContactEntryQueryProperty }); } - QueryBuilder, QQueryOperations> + QueryBuilder, QQueryOperations> addressesProperty() { return QueryBuilder.apply(this, (query) { return query.addPropertyName(r'addresses'); @@ -1251,3 +1136,673 @@ extension ContactEntryQueryProperty }); } } + +// ************************************************************************** +// IsarEmbeddedGenerator +// ************************************************************************** + +// coverage:ignore-file +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters + +const ContactAddressEntrySchema = Schema( + name: r'ContactAddressEntry', + id: 2556413586404997281, + properties: { + r'address': PropertySchema( + id: 0, + name: r'address', + type: IsarType.string, + ), + r'coinName': PropertySchema( + id: 1, + name: r'coinName', + type: IsarType.string, + ), + r'label': PropertySchema( + id: 2, + name: r'label', + type: IsarType.string, + ), + r'other': PropertySchema( + id: 3, + name: r'other', + type: IsarType.string, + ) + }, + estimateSize: _contactAddressEntryEstimateSize, + serialize: _contactAddressEntrySerialize, + deserialize: _contactAddressEntryDeserialize, + deserializeProp: _contactAddressEntryDeserializeProp, +); + +int _contactAddressEntryEstimateSize( + ContactAddressEntry object, + List offsets, + Map> allOffsets, +) { + var bytesCount = offsets.last; + bytesCount += 3 + object.address.length * 3; + bytesCount += 3 + object.coinName.length * 3; + bytesCount += 3 + object.label.length * 3; + { + final value = object.other; + if (value != null) { + bytesCount += 3 + value.length * 3; + } + } + return bytesCount; +} + +void _contactAddressEntrySerialize( + ContactAddressEntry object, + IsarWriter writer, + List offsets, + Map> allOffsets, +) { + writer.writeString(offsets[0], object.address); + writer.writeString(offsets[1], object.coinName); + writer.writeString(offsets[2], object.label); + writer.writeString(offsets[3], object.other); +} + +ContactAddressEntry _contactAddressEntryDeserialize( + Id id, + IsarReader reader, + List offsets, + Map> allOffsets, +) { + final object = ContactAddressEntry(); + object.address = reader.readString(offsets[0]); + object.coinName = reader.readString(offsets[1]); + object.label = reader.readString(offsets[2]); + object.other = reader.readStringOrNull(offsets[3]); + return object; +} + +P _contactAddressEntryDeserializeProp

( + IsarReader reader, + int propertyId, + int offset, + Map> allOffsets, +) { + switch (propertyId) { + case 0: + return (reader.readString(offset)) as P; + case 1: + return (reader.readString(offset)) as P; + case 2: + return (reader.readString(offset)) as P; + case 3: + return (reader.readStringOrNull(offset)) as P; + default: + throw IsarError('Unknown property with id $propertyId'); + } +} + +extension ContactAddressEntryQueryFilter on QueryBuilder { + QueryBuilder + addressEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'address', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + addressGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'address', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + addressLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'address', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + addressBetween( + String lower, + String upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'address', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + addressStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'address', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + addressEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'address', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + addressContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'address', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + addressMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'address', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + addressIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'address', + value: '', + )); + }); + } + + QueryBuilder + addressIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'address', + value: '', + )); + }); + } + + QueryBuilder + coinNameEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'coinName', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + coinNameGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'coinName', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + coinNameLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'coinName', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + coinNameBetween( + String lower, + String upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'coinName', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + coinNameStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'coinName', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + coinNameEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'coinName', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + coinNameContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'coinName', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + coinNameMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'coinName', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + coinNameIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'coinName', + value: '', + )); + }); + } + + QueryBuilder + coinNameIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'coinName', + value: '', + )); + }); + } + + QueryBuilder + labelEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'label', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + labelGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'label', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + labelLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'label', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + labelBetween( + String lower, + String upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'label', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + labelStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'label', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + labelEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'label', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + labelContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'label', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + labelMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'label', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + labelIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'label', + value: '', + )); + }); + } + + QueryBuilder + labelIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'label', + value: '', + )); + }); + } + + QueryBuilder + otherIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'other', + )); + }); + } + + QueryBuilder + otherIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'other', + )); + }); + } + + QueryBuilder + otherEqualTo( + String? value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'other', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + otherGreaterThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'other', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + otherLessThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'other', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + otherBetween( + String? lower, + String? upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'other', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + otherStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'other', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + otherEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'other', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + otherContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'other', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + otherMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'other', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + otherIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'other', + value: '', + )); + }); + } + + QueryBuilder + otherIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'other', + value: '', + )); + }); + } +} + +extension ContactAddressEntryQueryObject on QueryBuilder {} diff --git a/lib/pages/address_book_views/address_book_view.dart b/lib/pages/address_book_views/address_book_view.dart index 81b7af4ca..3faaad48e 100644 --- a/lib/pages/address_book_views/address_book_view.dart +++ b/lib/pages/address_book_views/address_book_view.dart @@ -1,8 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.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/pages/address_book_views/subviews/add_address_book_entry_view.dart'; import 'package:stackwallet/pages/address_book_views/subviews/address_book_filter_view.dart'; import 'package:stackwallet/providers/global/address_book_service_provider.dart'; @@ -73,19 +72,18 @@ class _AddressBookViewState extends ConsumerState { final managers = ref.read(walletsChangeNotifierProvider).managers; for (final manager in managers) { addresses.add( - ContactAddressEntry( - coin: manager.coin, - address: await manager.currentReceivingAddress, - label: "Current Receiving", - other: manager.walletName, - ), + ContactAddressEntry() + ..coinName = manager.coin.name + ..address = await manager.currentReceivingAddress + ..label = "Current Receiving" + ..other = manager.walletName, ); } - final self = Contact( + final self = ContactEntry( name: "My Stack", addresses: addresses, isFavorite: true, - id: "default", + customId: "default", ); await ref.read(addressBookServiceProvider).editContact(self); }); @@ -102,7 +100,8 @@ class _AddressBookViewState extends ConsumerState { @override Widget build(BuildContext context) { debugPrint("BUILD: $runtimeType"); - final contacts = ref.watch(addressBookServiceProvider.select((value) => value.contacts)); + final contacts = + ref.watch(addressBookServiceProvider.select((value) => value.contacts)); final isDesktop = Util.isDesktop; return ConditionalParent( @@ -305,8 +304,8 @@ class _AddressBookViewState extends ConsumerState { .where((element) => element.isFavorite) .map( (e) => AddressBookCard( - key: Key("favContactCard_${e.id}_key"), - contactId: e.id, + key: Key("favContactCard_${e.customId}_key"), + contactId: e.customId, ), ), ], @@ -351,8 +350,9 @@ class _AddressBookViewState extends ConsumerState { .matches(widget.filterTerm ?? _searchTerm, e)) .map( (e) => AddressBookCard( - key: Key("desktopContactCard_${e.id}_key"), - contactId: e.id, + key: + Key("desktopContactCard_${e.customId}_key"), + contactId: e.customId, ), ), ], diff --git a/lib/pages/address_book_views/subviews/add_address_book_entry_view.dart b/lib/pages/address_book_views/subviews/add_address_book_entry_view.dart index 76234a539..74e312da4 100644 --- a/lib/pages/address_book_views/subviews/add_address_book_entry_view.dart +++ b/lib/pages/address_book_views/subviews/add_address_book_entry_view.dart @@ -2,8 +2,7 @@ import 'package:emojis/emoji.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.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/pages/address_book_views/subviews/new_contact_address_entry_form.dart'; import 'package:stackwallet/providers/global/address_book_service_provider.dart'; import 'package:stackwallet/providers/ui/address_book_providers/address_entry_data_provider.dart'; @@ -28,6 +27,7 @@ import 'package:stackwallet/widgets/emoji_select_sheet.dart'; import 'package:stackwallet/widgets/icon_widgets/x_icon.dart'; import 'package:stackwallet/widgets/stack_text_field.dart'; import 'package:stackwallet/widgets/textfield_icon_button.dart'; +import 'package:uuid/uuid.dart'; class AddAddressBookEntryView extends ConsumerStatefulWidget { const AddAddressBookEntryView({ @@ -688,11 +688,12 @@ class _AddAddressBookEntryViewState forms[i].id)) .buildAddressEntry()); } - Contact contact = Contact( + ContactEntry contact = ContactEntry( emojiChar: _selectedEmoji?.char, name: nameController.text, addresses: entries, isFavorite: _isFavorite, + customId: const Uuid().v1(), ); if (await ref diff --git a/lib/pages/address_book_views/subviews/add_new_contact_address_view.dart b/lib/pages/address_book_views/subviews/add_new_contact_address_view.dart index fc41d75cb..f01829ab2 100644 --- a/lib/pages/address_book_views/subviews/add_new_contact_address_view.dart +++ b/lib/pages/address_book_views/subviews/add_new_contact_address_view.dart @@ -1,8 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.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/pages/address_book_views/subviews/new_contact_address_entry_form.dart'; import 'package:stackwallet/providers/global/address_book_service_provider.dart'; import 'package:stackwallet/providers/ui/address_book_providers/address_entry_data_provider.dart'; @@ -208,7 +207,7 @@ class _AddNewContactAddressViewState .read(addressEntryDataProvider(0)) .buildAddressEntry()); - Contact editedContact = + ContactEntry editedContact = contact.copyWith(addresses: entries); if (await ref diff --git a/lib/pages/address_book_views/subviews/contact_details_view.dart b/lib/pages/address_book_views/subviews/contact_details_view.dart index bd5efd1c5..2420c34b0 100644 --- a/lib/pages/address_book_views/subviews/contact_details_view.dart +++ b/lib/pages/address_book_views/subviews/contact_details_view.dart @@ -13,8 +13,8 @@ import 'package:stackwallet/providers/global/address_book_service_provider.dart' import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/providers/ui/address_book_providers/address_entry_data_provider.dart'; import 'package:stackwallet/services/coins/manager.dart'; -import 'package:stackwallet/themes/stack_colors.dart'; import 'package:stackwallet/themes/coin_icon_provider.dart'; +import 'package:stackwallet/themes/stack_colors.dart'; import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/clipboard_interface.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; @@ -197,7 +197,7 @@ class _ContactDetailsViewState extends ConsumerState { onPressed: () { ref .read(addressBookServiceProvider) - .removeContact(_contact.id); + .removeContact(_contact.customId); Navigator.of(context).pop(); Navigator.of(context).pop(); showFloatingFlushBar( diff --git a/lib/pages/address_book_views/subviews/edit_contact_address_view.dart b/lib/pages/address_book_views/subviews/edit_contact_address_view.dart index 87f9fec80..4fd98ff88 100644 --- a/lib/pages/address_book_views/subviews/edit_contact_address_view.dart +++ b/lib/pages/address_book_views/subviews/edit_contact_address_view.dart @@ -1,8 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.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/pages/address_book_views/subviews/new_contact_address_entry_form.dart'; import 'package:stackwallet/providers/global/address_book_service_provider.dart'; import 'package:stackwallet/providers/ui/address_book_providers/address_entry_data_provider.dart'; @@ -49,7 +48,7 @@ class _EditContactAddressViewState late final BarcodeScannerInterface barcodeScanner; late final ClipboardInterface clipboard; - Future save(Contact contact) async { + Future save(ContactEntry contact) async { if (FocusScope.of(context).hasFocus) { FocusScope.of(context).unfocus(); await Future.delayed( @@ -73,7 +72,7 @@ class _EditContactAddressViewState entries.insert(index, editedEntry); - Contact editedContact = contact.copyWith(addresses: entries); + ContactEntry editedContact = contact.copyWith(addresses: entries); if (await ref.read(addressBookServiceProvider).editContact(editedContact)) { if (mounted) { @@ -226,7 +225,8 @@ class _EditContactAddressViewState ); _addresses.remove(entry); - Contact editedContact = contact.copyWith(addresses: _addresses); + ContactEntry editedContact = + contact.copyWith(addresses: _addresses); if (await ref .read(addressBookServiceProvider) .editContact(editedContact)) { diff --git a/lib/pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart b/lib/pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart index 28a926663..b89954521 100644 --- a/lib/pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart +++ b/lib/pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart @@ -5,10 +5,9 @@ import 'dart:typed_data'; import 'package:stack_wallet_backup/stack_wallet_backup.dart'; import 'package:stackwallet/db/hive/db.dart'; -import 'package:stackwallet/models/contact.dart'; -import 'package:stackwallet/models/contact_address_entry.dart'; import 'package:stackwallet/models/exchange/change_now/exchange_transaction.dart'; import 'package:stackwallet/models/exchange/response_objects/trade.dart'; +import 'package:stackwallet/models/isar/models/contact_entry.dart'; import 'package:stackwallet/models/node_model.dart'; import 'package:stackwallet/models/stack_restoring_ui_state.dart'; import 'package:stackwallet/models/trade_wallet_lookup.dart'; @@ -266,7 +265,7 @@ abstract class SWB { ); AddressBookService addressBookService = AddressBookService(); - var addresses = await addressBookService.addressBookEntries; + var addresses = addressBookService.contacts; backupJson['addressBookEntries'] = addresses.map((e) => e.toMap()).toList(); @@ -799,7 +798,7 @@ abstract class SWB { // contacts final addressBookService = AddressBookService(); - final allContactIds = addressBookService.contacts.map((e) => e.id); + final allContactIds = addressBookService.contacts.map((e) => e.customId); if (addressBookEntries == null) { // if no contacts were present before attempted restore then delete any that @@ -823,21 +822,20 @@ abstract class SWB { List addresses = []; for (var address in (contact['addresses'] as List)) { addresses.add( - ContactAddressEntry( - coin: Coin.values - .firstWhere((element) => element.name == address['coin']), - address: address['address'] as String, - label: address['label'] as String, - ), + ContactAddressEntry() + ..coinName = address['coin'] as String + ..address = address['address'] as String + ..label = address['label'] as String + ..other = address['other'] as String?, ); } await addressBookService.editContact( - Contact( + ContactEntry( emojiChar: contact['emoji'] as String?, name: contact['name'] as String, addresses: addresses, isFavorite: contact['isFavorite'] as bool, - id: contact['id'] as String, + customId: contact['id'] as String, ), ); } else { @@ -1026,21 +1024,20 @@ abstract class SWB { List addresses = []; for (var address in (contact['addresses'] as List)) { addresses.add( - ContactAddressEntry( - coin: Coin.values - .firstWhere((element) => element.name == address['coin']), - address: address['address'] as String, - label: address['label'] as String, - ), + ContactAddressEntry() + ..coinName = address['coin'] as String + ..address = address['address'] as String + ..label = address['label'] as String + ..other = address['other'] as String?, ); } await addressBookService.addContact( - Contact( + ContactEntry( emojiChar: contact['emoji'] as String?, name: contact['name'] as String, addresses: addresses, isFavorite: contact['isFavorite'] as bool, - id: contact['id'] as String, + customId: contact['id'] as String, ), ); } diff --git a/lib/pages/wallet_view/transaction_views/all_transactions_view.dart b/lib/pages/wallet_view/transaction_views/all_transactions_view.dart index dd3521bce..8d23a67cf 100644 --- a/lib/pages/wallet_view/transaction_views/all_transactions_view.dart +++ b/lib/pages/wallet_view/transaction_views/all_transactions_view.dart @@ -3,8 +3,8 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; -import 'package:stackwallet/models/contact.dart'; import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart'; +import 'package:stackwallet/models/isar/models/contact_entry.dart'; import 'package:stackwallet/models/transaction_filter.dart'; import 'package:stackwallet/notifications/show_flush_bar.dart'; import 'package:stackwallet/pages/wallet_view/sub_widgets/tx_icon.dart'; @@ -120,8 +120,8 @@ class _TransactionDetailsViewState extends ConsumerState { }).toList(); } - bool _isKeywordMatch(Transaction tx, String keyword, List contacts, - Map notes) { + bool _isKeywordMatch(Transaction tx, String keyword, + List contacts, Map notes) { if (keyword.isEmpty) { return true; } diff --git a/lib/pages_desktop_specific/address_book_view/desktop_address_book.dart b/lib/pages_desktop_specific/address_book_view/desktop_address_book.dart index e45e894c2..905948569 100644 --- a/lib/pages_desktop_specific/address_book_view/desktop_address_book.dart +++ b/lib/pages_desktop_specific/address_book_view/desktop_address_book.dart @@ -1,8 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.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/pages/address_book_views/subviews/add_address_book_entry_view.dart'; import 'package:stackwallet/pages/address_book_views/subviews/address_book_filter_view.dart'; import 'package:stackwallet/pages_desktop_specific/address_book_view/subwidgets/desktop_address_book_scaffold.dart'; @@ -105,19 +104,18 @@ class _DesktopAddressBook extends ConsumerState { final managers = ref.read(walletsChangeNotifierProvider).managers; for (final manager in managers) { addresses.add( - ContactAddressEntry( - coin: manager.coin, - address: await manager.currentReceivingAddress, - label: "Current Receiving", - other: manager.walletName, - ), + ContactAddressEntry() + ..coinName = manager.coin.name + ..address = await manager.currentReceivingAddress + ..label = "Current Receiving" + ..other = manager.walletName, ); } - final self = Contact( + final self = ContactEntry( name: "My Stack", addresses: addresses, isFavorite: true, - id: "default", + customId: "default", ); await ref.read(addressBookServiceProvider).editContact(self); }); @@ -323,14 +321,14 @@ class _DesktopAddressBook extends ConsumerState { .extension()! .accentColorDark .withOpacity( - currentContactId == favorites[i].id + currentContactId == favorites[i].customId ? 0.08 : 0, ), child: RawMaterialButton( onPressed: () { setState(() { - currentContactId = favorites[i].id; + currentContactId = favorites[i].customId; }); }, padding: const EdgeInsets.symmetric( @@ -346,8 +344,8 @@ class _DesktopAddressBook extends ConsumerState { ), child: AddressBookCard( key: Key( - "favContactCard_${favorites[i].id}_key"), - contactId: favorites[i].id, + "favContactCard_${favorites[i].customId}_key"), + contactId: favorites[i].customId, desktopSendFrom: false, ), ), @@ -393,14 +391,16 @@ class _DesktopAddressBook extends ConsumerState { .extension()! .accentColorDark .withOpacity( - currentContactId == allContacts[i].id + currentContactId == + allContacts[i].customId ? 0.08 : 0, ), child: RawMaterialButton( onPressed: () { setState(() { - currentContactId = allContacts[i].id; + currentContactId = + allContacts[i].customId; }); }, padding: const EdgeInsets.symmetric( @@ -416,8 +416,8 @@ class _DesktopAddressBook extends ConsumerState { ), child: AddressBookCard( key: Key( - "favContactCard_${allContacts[i].id}_key"), - contactId: allContacts[i].id, + "favContactCard_${allContacts[i].customId}_key"), + contactId: allContacts[i].customId, desktopSendFrom: false, ), ), diff --git a/lib/pages_desktop_specific/address_book_view/subwidgets/desktop_address_card.dart b/lib/pages_desktop_specific/address_book_view/subwidgets/desktop_address_card.dart index 214cc01e9..f26573c1e 100644 --- a/lib/pages_desktop_specific/address_book_view/subwidgets/desktop_address_card.dart +++ b/lib/pages_desktop_specific/address_book_view/subwidgets/desktop_address_card.dart @@ -2,12 +2,12 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:stackwallet/models/contact_address_entry.dart'; +import 'package:stackwallet/models/isar/models/contact_entry.dart'; import 'package:stackwallet/notifications/show_flush_bar.dart'; import 'package:stackwallet/pages/address_book_views/subviews/edit_contact_address_view.dart'; import 'package:stackwallet/providers/ui/address_book_providers/address_entry_data_provider.dart'; -import 'package:stackwallet/themes/stack_colors.dart'; import 'package:stackwallet/themes/coin_icon_provider.dart'; +import 'package:stackwallet/themes/stack_colors.dart'; import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/clipboard_interface.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; diff --git a/lib/pages_desktop_specific/address_book_view/subwidgets/desktop_contact_details.dart b/lib/pages_desktop_specific/address_book_view/subwidgets/desktop_contact_details.dart index 8b56a9cf5..901c32008 100644 --- a/lib/pages_desktop_specific/address_book_view/subwidgets/desktop_contact_details.dart +++ b/lib/pages_desktop_specific/address_book_view/subwidgets/desktop_contact_details.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:isar/isar.dart'; -import 'package:stackwallet/models/contact.dart'; +import 'package:stackwallet/models/isar/models/contact_entry.dart'; import 'package:stackwallet/models/isar/models/isar_models.dart'; import 'package:stackwallet/pages/address_book_views/subviews/add_new_contact_address_view.dart'; import 'package:stackwallet/pages_desktop_specific/address_book_view/subwidgets/desktop_address_card.dart'; @@ -42,7 +42,7 @@ class DesktopContactDetails extends ConsumerStatefulWidget { class _DesktopContactDetailsState extends ConsumerState { List> _cachedTransactions = []; - bool _contactHasAddress(String address, Contact contact) { + bool _contactHasAddress(String address, ContactEntry contact) { for (final entry in contact.addresses) { if (entry.address == address) { return true; @@ -82,7 +82,7 @@ class _DesktopContactDetailsState extends ConsumerState { @override Widget build(BuildContext context) { // provider hack to prevent trying to update widget with deleted contact - Contact? _contact; + ContactEntry? _contact; try { _contact = ref.watch(addressBookServiceProvider .select((value) => value.getContactById(widget.contactId))); @@ -159,7 +159,7 @@ class _DesktopContactDetailsState extends ConsumerState { barrierColor: Colors.transparent, builder: (context) { return DesktopContactOptionsMenuPopup( - contactId: contact.id, + contactId: contact.customId, ); }, ); @@ -261,7 +261,7 @@ class _DesktopContactDetailsState extends ConsumerState { padding: const EdgeInsets.all(18), child: DesktopAddressCard( entry: contact.addresses[i], - contactId: contact.id, + contactId: contact.customId, ), ), ], diff --git a/lib/pages_desktop_specific/address_book_view/subwidgets/desktop_contact_options_menu_popup.dart b/lib/pages_desktop_specific/address_book_view/subwidgets/desktop_contact_options_menu_popup.dart index 2c3574acf..3d8fb3a6a 100644 --- a/lib/pages_desktop_specific/address_book_view/subwidgets/desktop_contact_options_menu_popup.dart +++ b/lib/pages_desktop_specific/address_book_view/subwidgets/desktop_contact_options_menu_popup.dart @@ -147,7 +147,7 @@ class _DesktopContactOptionsMenuPopupState onPressed: () { ref .read(addressBookServiceProvider) - .removeContact(contact.id); + .removeContact(contact.customId); Navigator.of(context).pop(); showFloatingFlushBar( type: FlushBarType.success, diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/address_book_address_chooser/address_book_address_chooser.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/address_book_address_chooser/address_book_address_chooser.dart index 647097bfe..1078bea5d 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/address_book_address_chooser/address_book_address_chooser.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/address_book_address_chooser/address_book_address_chooser.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:stackwallet/models/contact.dart'; +import 'package:stackwallet/models/isar/models/contact_entry.dart'; import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/address_book_address_chooser/sub_widgets/contact_list_item.dart'; import 'package:stackwallet/providers/global/address_book_service_provider.dart'; import 'package:stackwallet/themes/stack_colors.dart'; @@ -34,7 +34,7 @@ class _AddressBookAddressChooserState extends State { String _searchTerm = ""; - int _compareContactFavorite(Contact a, Contact b) { + int _compareContactFavorite(ContactEntry a, ContactEntry b) { if (a.isFavorite && b.isFavorite) { return 0; } else if (a.isFavorite) { @@ -44,8 +44,8 @@ class _AddressBookAddressChooserState extends State { } } - List pullOutFavorites(List contacts) { - final List favorites = []; + List pullOutFavorites(List contacts) { + final List favorites = []; contacts.removeWhere((contact) { if (contact.isFavorite) { favorites.add(contact); @@ -57,7 +57,7 @@ class _AddressBookAddressChooserState extends State { return favorites; } - List filter(List contacts, String searchTerm) { + List filter(List contacts, String searchTerm) { if (widget.coin != null) { contacts.removeWhere( (e) => e.addresses.where((a) => a.coin == widget.coin!).isEmpty); @@ -75,7 +75,7 @@ class _AddressBookAddressChooserState extends State { return contacts; } - bool _matches(String term, Contact contact) { + bool _matches(String term, ContactEntry contact) { final text = term.toLowerCase(); if (contact.name.toLowerCase().contains(text)) { return true; @@ -191,7 +191,7 @@ class _AddressBookAddressChooserState extends State { ), child: Consumer( builder: (context, ref, _) { - List contacts = ref + List contacts = ref .watch(addressBookServiceProvider .select((value) => value.contacts)) .toList(); @@ -228,7 +228,7 @@ class _AddressBookAddressChooserState extends State { ), ); } else if (index < favorites.length + 1) { - final id = favorites[index - 1].id; + final id = favorites[index - 1].customId; return ContactListItem( key: Key("contactContactListItem_${id}_key"), contactId: id, @@ -248,7 +248,8 @@ class _AddressBookAddressChooserState extends State { ), ); } else { - final id = contacts[index - favorites.length - 2].id; + final id = + contacts[index - favorites.length - 2].customId; return ContactListItem( key: Key("contactContactListItem_${id}_key"), contactId: id, diff --git a/lib/route_generator.dart b/lib/route_generator.dart index 3ff594299..2791974e6 100644 --- a/lib/route_generator.dart +++ b/lib/route_generator.dart @@ -5,7 +5,6 @@ import 'package:isar/isar.dart'; import 'package:stackwallet/models/add_wallet_list_entity/add_wallet_list_entity.dart'; import 'package:stackwallet/models/add_wallet_list_entity/sub_classes/eth_token_entity.dart'; import 'package:stackwallet/models/buy/response_objects/quote.dart'; -import 'package:stackwallet/models/contact_address_entry.dart'; import 'package:stackwallet/models/exchange/incomplete_exchange.dart'; import 'package:stackwallet/models/exchange/response_objects/trade.dart'; import 'package:stackwallet/models/isar/models/isar_models.dart'; @@ -153,6 +152,8 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/widgets/choose_coin_view.dart'; import 'package:tuple/tuple.dart'; +import 'models/isar/models/contact_entry.dart'; + class RouteGenerator { static const bool useMaterialPageRoute = true; diff --git a/lib/services/address_book_service.dart b/lib/services/address_book_service.dart index 6889edf6c..793dccb8b 100644 --- a/lib/services/address_book_service.dart +++ b/lib/services/address_book_service.dart @@ -1,107 +1,33 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:stackwallet/db/isar/main_db.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/logger.dart'; class AddressBookService extends ChangeNotifier { - - ContactEntry turnContactToEntry({required Contact contact}) { - String? emojiChar = contact.emojiChar; - String name = contact.name; - List addresses = []; - bool isFavorite = contact.isFavorite; - 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 addresses = []; - bool isFavorite = contactEntry.isFavorite; - String id = contactEntry.customId; - for (String addressEntry in contactEntry.addresses) { - List 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 getContactById(String id) { ContactEntry? contactEntry = MainDB.instance.getContactEntry(id: id); if (contactEntry == null) { - return Contact( - name: "Contact not found", - addresses: [], - isFavorite: false, - ); + throw Exception('Contact ID "$id" not found!'); } else { - return turnEntryToContact(contactEntry: contactEntry); + return contactEntry; } } - List get contacts { - List contactEntries = MainDB.instance.getContactEntries(); - List contactsList = []; - for (ContactEntry contactEntry in contactEntries) { - contactsList.add(turnEntryToContact(contactEntry: contactEntry)); - } - return contactsList; - } - - List? _addressBookEntries; - List get addressBookEntries => - _addressBookEntries ??= _fetchAddressBookEntries(); - - // Load address book contact entries - List _fetchAddressBookEntries() { - return contacts; - } + List get contacts => MainDB.instance.getContactEntries(); /// search address book entries - //TODO optimize address book search? - Future> search(String text) async { - if (text.isEmpty) return addressBookEntries; - var results = (await addressBookEntries).toList(); + //TODO search using isar queries + Future> search(String text) async { + if (text.isEmpty) return contacts; + var results = contacts.toList(); results.retainWhere((contact) => matches(text, contact)); return results; } - bool matches(String term, Contact contact) { + bool matches(String term, ContactEntry contact) { if (term.isEmpty) { return true; } @@ -125,33 +51,27 @@ class AddressBookService extends ChangeNotifier { /// /// returns false if it provided [contact]'s id already exists in the database /// other true if the [contact] was saved - Future addContact(Contact contact) async { - if (await MainDB.instance.isContactEntryExists(id: contact.id)) { + Future addContact(ContactEntry contact) async { + if (await MainDB.instance.isContactEntryExists(id: contact.customId)) { return false; } else { - await MainDB.instance.putContactEntry(contactEntry: turnContactToEntry(contact: contact)); - _refreshAddressBookEntries(); + await MainDB.instance.putContactEntry(contactEntry: contact); + notifyListeners(); return true; } } /// Edit contact - Future editContact(Contact editedContact) async { + Future editContact(ContactEntry editedContact) async { // over write the contact with edited version - await MainDB.instance.putContactEntry(contactEntry: turnContactToEntry(contact: editedContact)); - _refreshAddressBookEntries(); + await MainDB.instance.putContactEntry(contactEntry: editedContact); + notifyListeners(); return true; } /// Remove address book contact entry from db if it exists Future removeContact(String id) async { await MainDB.instance.deleteContactEntry(id: id); - _refreshAddressBookEntries(); - } - - void _refreshAddressBookEntries() { - final newAddressBookEntries = _fetchAddressBookEntries(); - _addressBookEntries = newAddressBookEntries; notifyListeners(); } } diff --git a/lib/utilities/db_version_migration.dart b/lib/utilities/db_version_migration.dart index 24e8b8582..6c85f1cc9 100644 --- a/lib/utilities/db_version_migration.dart +++ b/lib/utilities/db_version_migration.dart @@ -3,9 +3,12 @@ import 'package:isar/isar.dart'; import 'package:stackwallet/db/hive/db.dart'; import 'package:stackwallet/db/isar/main_db.dart'; import 'package:stackwallet/electrumx_rpc/electrumx.dart'; +import 'package:stackwallet/models/contact.dart'; import 'package:stackwallet/models/exchange/change_now/exchange_transaction.dart'; import 'package:stackwallet/models/exchange/response_objects/trade.dart'; import 'package:stackwallet/models/isar/models/blockchain_data/address.dart'; +import 'package:stackwallet/models/isar/models/contact_entry.dart' + as isar_contact; import 'package:stackwallet/models/isar/models/isar_models.dart' as isar_models; import 'package:stackwallet/models/models.dart'; import 'package:stackwallet/models/node_model.dart'; @@ -290,6 +293,17 @@ class DbVersionMigrator with WalletDB { // try to continue migrating return await migrate(8, secureStore: secureStore); + case 9: + // migrate + await _v9(); + + // update version + await DB.instance.put( + boxName: DB.boxNameDBInfo, key: "hive_data_version", value: 10); + + // try to continue migrating + return await migrate(10, secureStore: secureStore); + default: // finally return return; @@ -447,4 +461,50 @@ class DbVersionMigrator with WalletDB { } } } + + Future _v9() async { + final addressBookBox = await Hive.openBox(DB.boxNameAddressBook); + await MainDB.instance.initMainDB(); + + final keys = List.from(addressBookBox.keys); + final contacts = keys + .map((id) => Contact.fromJson( + Map.from( + addressBookBox.get(id) as Map, + ), + )) + .toList(growable: false); + + final List newContacts = []; + + for (final contact in contacts) { + final List newContactAddressEntries = + []; + + for (final entry in contact.addresses) { + newContactAddressEntries.add( + isar_contact.ContactAddressEntry() + ..coinName = entry.coin.name + ..address = entry.address + ..label = entry.label + ..other = entry.other, + ); + } + + final newContact = isar_contact.ContactEntry( + name: contact.name, + addresses: newContactAddressEntries, + isFavorite: contact.isFavorite, + customId: contact.id, + ); + + newContacts.add(newContact); + } + + await MainDB.instance.isar.writeTxn(() async { + await MainDB.instance.isar.contactEntrys.putAll(newContacts); + }); + + await addressBookBox.deleteFromDisk(); + } } diff --git a/lib/widgets/address_book_card.dart b/lib/widgets/address_book_card.dart index 4fca40e01..43b51e625 100644 --- a/lib/widgets/address_book_card.dart +++ b/lib/widgets/address_book_card.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; -import 'package:stackwallet/models/contact.dart'; +import 'package:stackwallet/models/isar/models/contact_entry.dart'; import 'package:stackwallet/pages/address_book_views/subviews/contact_popup.dart'; import 'package:stackwallet/providers/global/address_book_service_provider.dart'; import 'package:stackwallet/themes/stack_colors.dart'; @@ -48,7 +48,7 @@ class _AddressBookCardState extends ConsumerState { @override Widget build(BuildContext context) { // provider hack to prevent trying to update widget with deleted contact - Contact? _contact; + ContactEntry? _contact; try { _contact = ref.watch(addressBookServiceProvider .select((value) => value.getContactById(contactId))); @@ -81,7 +81,7 @@ class _AddressBookCardState extends ConsumerState { width: 32, height: 32, decoration: BoxDecoration( - color: contact.id == "default" + color: contact.customId == "default" ? Theme.of(context) .extension()! .myStackContactIconBG @@ -90,7 +90,7 @@ class _AddressBookCardState extends ConsumerState { .textFieldDefaultBG, borderRadius: BorderRadius.circular(32), ), - child: contact.id == "default" + child: contact.customId == "default" ? Center( child: SvgPicture.asset( ref.watch( @@ -176,7 +176,7 @@ class _AddressBookCardState extends ConsumerState { useSafeArea: true, barrierDismissible: true, builder: (_) => ContactPopUp( - contactId: contact.id, + contactId: contact.customId, ), ); }, diff --git a/test/address_book_service_test.dart b/test/address_book_service_test.dart deleted file mode 100644 index adc036d5a..000000000 --- a/test/address_book_service_test.dart +++ /dev/null @@ -1,174 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:hive/hive.dart'; -import 'package:hive_test/hive_test.dart'; -import 'package:stackwallet/db/hive/db.dart'; -import 'package:stackwallet/models/contact.dart'; -import 'package:stackwallet/models/contact_address_entry.dart'; -import 'package:stackwallet/services/address_book_service.dart'; -import 'package:stackwallet/utilities/enums/coin_enum.dart'; - -void main() { - group("Empty DB tests", () { - setUp(() async { - await setUpTestHive(); - await Hive.openBox(DB.boxNameAddressBook); - }); - - test("get empty contacts", () { - final service = AddressBookService(); - expect(service.contacts, []); - }); - - test("get empty addressBookEntries", () async { - final service = AddressBookService(); - expect(await service.addressBookEntries, []); - }); - - test("getContactById from empty db", () { - final service = AddressBookService(); - expect(() => service.getContactById("some id"), throwsException); - }); - - tearDown(() async { - await tearDownTestHive(); - }); - }); - - group("Preloaded DB tests", () { - final contactA = Contact(name: "john", addresses: [], isFavorite: true); - final contactB = Contact( - name: "JANE", - addresses: [ - const ContactAddressEntry( - coin: Coin.bitcoin, - address: "some btc address", - label: "rent", - ), - ], - isFavorite: false); - final contactC = Contact( - name: "Bill", - addresses: [ - const ContactAddressEntry( - coin: Coin.monero, - address: "some xmr address", - label: "market", - ), - const ContactAddressEntry( - coin: Coin.epicCash, - address: "some epic address", - label: "gas", - ), - ], - isFavorite: true); - - setUp(() async { - await setUpTestHive(); - await Hive.openBox(DB.boxNameAddressBook); - - await DB.instance.put( - boxName: DB.boxNameAddressBook, - key: contactA.id, - value: contactA.toMap()); - await DB.instance.put( - boxName: DB.boxNameAddressBook, - key: contactB.id, - value: contactB.toMap()); - await DB.instance.put( - boxName: DB.boxNameAddressBook, - key: contactC.id, - value: contactC.toMap()); - }); - - test("getContactById with non existing ID", () { - final service = AddressBookService(); - expect(() => service.getContactById("some id"), throwsException); - }); - - test("getContactById with existing ID", () { - final service = AddressBookService(); - expect( - service.getContactById(contactA.id).toString(), contactA.toString()); - }); - - test("get contacts", () { - final service = AddressBookService(); - expect(service.contacts.toString(), - [contactC, contactB, contactA].toString()); - }); - - test("get addressBookEntries", () async { - final service = AddressBookService(); - expect((await service.addressBookEntries).toString(), - [contactC, contactB, contactA].toString()); - }); - - test("search contacts", () async { - final service = AddressBookService(); - final results = await service.search("j"); - expect(results.toString(), [contactB, contactA].toString()); - - final results2 = await service.search("ja"); - expect(results2.toString(), [contactB].toString()); - - final results3 = await service.search("john"); - expect(results3.toString(), [contactA].toString()); - - final results4 = await service.search("po"); - expect(results4.toString(), [].toString()); - - final results5 = await service.search(""); - expect(results5.toString(), [contactC, contactB, contactA].toString()); - - final results6 = await service.search("epic address"); - expect(results6.toString(), [contactC].toString()); - }); - - test("add new contact", () async { - final service = AddressBookService(); - final contactD = Contact(name: "tim", addresses: [], isFavorite: true); - final result = await service.addContact(contactD); - expect(result, true); - expect(service.contacts.length, 4); - expect( - service.getContactById(contactD.id).toString(), contactD.toString()); - }); - - test("add duplicate contact", () async { - final service = AddressBookService(); - final result = await service.addContact(contactA); - expect(result, false); - expect(service.contacts.length, 3); - expect(service.contacts.toString(), - [contactC, contactB, contactA].toString()); - }); - - test("edit contact", () async { - final service = AddressBookService(); - final editedContact = contactB.copyWith(name: "Mike"); - expect(await service.editContact(editedContact), true); - expect(service.contacts.length, 3); - expect(service.contacts.toString(), - [contactC, contactA, editedContact].toString()); - }); - - test("remove existing contact", () async { - final service = AddressBookService(); - await service.removeContact(contactB.id); - expect(service.contacts.length, 2); - expect(service.contacts.toString(), [contactC, contactA].toString()); - }); - - test("remove non existing contact", () async { - final service = AddressBookService(); - await service.removeContact("some id"); - expect(service.contacts.length, 3); - expect(service.contacts.toString(), - [contactC, contactB, contactA].toString()); - }); - - tearDown(() async { - await tearDownTestHive(); - }); - }); -} diff --git a/test/screen_tests/address_book_view/address_book_view_screen_test.mocks.dart b/test/screen_tests/address_book_view/address_book_view_screen_test.mocks.dart index 754121b87..68c48dd34 100644 --- a/test/screen_tests/address_book_view/address_book_view_screen_test.mocks.dart +++ b/test/screen_tests/address_book_view/address_book_view_screen_test.mocks.dart @@ -7,7 +7,7 @@ import 'dart:async' as _i4; import 'dart:ui' as _i5; import 'package:mockito/mockito.dart' as _i1; -import 'package:stackwallet/models/contact.dart' as _i2; +import 'package:stackwallet/models/isar/models/contact_entry.dart' as _i2; import 'package:stackwallet/services/address_book_service.dart' as _i3; // ignore_for_file: type=lint @@ -21,8 +21,8 @@ import 'package:stackwallet/services/address_book_service.dart' as _i3; // ignore_for_file: camel_case_types // ignore_for_file: subtype_of_sealed_class -class _FakeContact_0 extends _i1.SmartFake implements _i2.Contact { - _FakeContact_0( +class _FakeContactEntry_0 extends _i1.SmartFake implements _i2.ContactEntry { + _FakeContactEntry_0( Object parent, Invocation parentInvocation, ) : super( @@ -37,46 +37,43 @@ class _FakeContact_0 extends _i1.SmartFake implements _i2.Contact { class MockAddressBookService extends _i1.Mock implements _i3.AddressBookService { @override - List<_i2.Contact> get contacts => (super.noSuchMethod( + List<_i2.ContactEntry> get contacts => (super.noSuchMethod( Invocation.getter(#contacts), - returnValue: <_i2.Contact>[], - ) as List<_i2.Contact>); - @override - _i4.Future> get addressBookEntries => (super.noSuchMethod( - Invocation.getter(#addressBookEntries), - returnValue: _i4.Future>.value(<_i2.Contact>[]), - ) as _i4.Future>); + returnValue: <_i2.ContactEntry>[], + ) as List<_i2.ContactEntry>); @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); @override - _i2.Contact getContactById(String? id) => (super.noSuchMethod( + _i2.ContactEntry getContactById(String? id) => (super.noSuchMethod( Invocation.method( #getContactById, [id], ), - returnValue: _FakeContact_0( + returnValue: _FakeContactEntry_0( this, Invocation.method( #getContactById, [id], ), ), - ) as _i2.Contact); + ) as _i2.ContactEntry); @override - _i4.Future> search(String? text) => (super.noSuchMethod( + _i4.Future> search(String? text) => + (super.noSuchMethod( Invocation.method( #search, [text], ), - returnValue: _i4.Future>.value(<_i2.Contact>[]), - ) as _i4.Future>); + returnValue: + _i4.Future>.value(<_i2.ContactEntry>[]), + ) as _i4.Future>); @override bool matches( String? term, - _i2.Contact? contact, + _i2.ContactEntry? contact, ) => (super.noSuchMethod( Invocation.method( @@ -89,7 +86,7 @@ class MockAddressBookService extends _i1.Mock returnValue: false, ) as bool); @override - _i4.Future addContact(_i2.Contact? contact) => (super.noSuchMethod( + _i4.Future addContact(_i2.ContactEntry? contact) => (super.noSuchMethod( Invocation.method( #addContact, [contact], @@ -97,7 +94,7 @@ class MockAddressBookService extends _i1.Mock returnValue: _i4.Future.value(false), ) as _i4.Future); @override - _i4.Future editContact(_i2.Contact? editedContact) => + _i4.Future editContact(_i2.ContactEntry? editedContact) => (super.noSuchMethod( Invocation.method( #editContact, diff --git a/test/screen_tests/address_book_view/subviews/add_address_book_view_screen_test.mocks.dart b/test/screen_tests/address_book_view/subviews/add_address_book_view_screen_test.mocks.dart index 4c690a92d..80a28f732 100644 --- a/test/screen_tests/address_book_view/subviews/add_address_book_view_screen_test.mocks.dart +++ b/test/screen_tests/address_book_view/subviews/add_address_book_view_screen_test.mocks.dart @@ -9,7 +9,7 @@ import 'dart:ui' as _i11; import 'package:barcode_scan2/barcode_scan2.dart' as _i2; import 'package:mockito/mockito.dart' as _i1; import 'package:stackwallet/models/balance.dart' as _i6; -import 'package:stackwallet/models/contact.dart' as _i3; +import 'package:stackwallet/models/isar/models/contact_entry.dart' as _i3; import 'package:stackwallet/models/isar/models/isar_models.dart' as _i14; import 'package:stackwallet/models/models.dart' as _i5; import 'package:stackwallet/services/address_book_service.dart' as _i10; @@ -40,8 +40,8 @@ class _FakeScanResult_0 extends _i1.SmartFake implements _i2.ScanResult { ); } -class _FakeContact_1 extends _i1.SmartFake implements _i3.Contact { - _FakeContact_1( +class _FakeContactEntry_1 extends _i1.SmartFake implements _i3.ContactEntry { + _FakeContactEntry_1( Object parent, Invocation parentInvocation, ) : super( @@ -126,46 +126,43 @@ class MockBarcodeScannerWrapper extends _i1.Mock class MockAddressBookService extends _i1.Mock implements _i10.AddressBookService { @override - List<_i3.Contact> get contacts => (super.noSuchMethod( + List<_i3.ContactEntry> get contacts => (super.noSuchMethod( Invocation.getter(#contacts), - returnValue: <_i3.Contact>[], - ) as List<_i3.Contact>); - @override - _i9.Future> get addressBookEntries => (super.noSuchMethod( - Invocation.getter(#addressBookEntries), - returnValue: _i9.Future>.value(<_i3.Contact>[]), - ) as _i9.Future>); + returnValue: <_i3.ContactEntry>[], + ) as List<_i3.ContactEntry>); @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); @override - _i3.Contact getContactById(String? id) => (super.noSuchMethod( + _i3.ContactEntry getContactById(String? id) => (super.noSuchMethod( Invocation.method( #getContactById, [id], ), - returnValue: _FakeContact_1( + returnValue: _FakeContactEntry_1( this, Invocation.method( #getContactById, [id], ), ), - ) as _i3.Contact); + ) as _i3.ContactEntry); @override - _i9.Future> search(String? text) => (super.noSuchMethod( + _i9.Future> search(String? text) => + (super.noSuchMethod( Invocation.method( #search, [text], ), - returnValue: _i9.Future>.value(<_i3.Contact>[]), - ) as _i9.Future>); + returnValue: + _i9.Future>.value(<_i3.ContactEntry>[]), + ) as _i9.Future>); @override bool matches( String? term, - _i3.Contact? contact, + _i3.ContactEntry? contact, ) => (super.noSuchMethod( Invocation.method( @@ -178,7 +175,7 @@ class MockAddressBookService extends _i1.Mock returnValue: false, ) as bool); @override - _i9.Future addContact(_i3.Contact? contact) => (super.noSuchMethod( + _i9.Future addContact(_i3.ContactEntry? contact) => (super.noSuchMethod( Invocation.method( #addContact, [contact], @@ -186,7 +183,7 @@ class MockAddressBookService extends _i1.Mock returnValue: _i9.Future.value(false), ) as _i9.Future); @override - _i9.Future editContact(_i3.Contact? editedContact) => + _i9.Future editContact(_i3.ContactEntry? editedContact) => (super.noSuchMethod( Invocation.method( #editContact, diff --git a/test/screen_tests/address_book_view/subviews/address_book_entry_details_view_screen_test.mocks.dart b/test/screen_tests/address_book_view/subviews/address_book_entry_details_view_screen_test.mocks.dart index 266112adf..935ea5526 100644 --- a/test/screen_tests/address_book_view/subviews/address_book_entry_details_view_screen_test.mocks.dart +++ b/test/screen_tests/address_book_view/subviews/address_book_entry_details_view_screen_test.mocks.dart @@ -8,7 +8,7 @@ import 'dart:ui' as _i9; import 'package:mockito/mockito.dart' as _i1; import 'package:stackwallet/models/balance.dart' as _i5; -import 'package:stackwallet/models/contact.dart' as _i2; +import 'package:stackwallet/models/isar/models/contact_entry.dart' as _i2; import 'package:stackwallet/models/isar/models/isar_models.dart' as _i12; import 'package:stackwallet/models/models.dart' as _i4; import 'package:stackwallet/services/address_book_service.dart' as _i7; @@ -30,8 +30,8 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart' as _i11; // ignore_for_file: camel_case_types // ignore_for_file: subtype_of_sealed_class -class _FakeContact_0 extends _i1.SmartFake implements _i2.Contact { - _FakeContact_0( +class _FakeContactEntry_0 extends _i1.SmartFake implements _i2.ContactEntry { + _FakeContactEntry_0( Object parent, Invocation parentInvocation, ) : super( @@ -87,46 +87,43 @@ class _FakeAmount_4 extends _i1.SmartFake implements _i6.Amount { class MockAddressBookService extends _i1.Mock implements _i7.AddressBookService { @override - List<_i2.Contact> get contacts => (super.noSuchMethod( + List<_i2.ContactEntry> get contacts => (super.noSuchMethod( Invocation.getter(#contacts), - returnValue: <_i2.Contact>[], - ) as List<_i2.Contact>); - @override - _i8.Future> get addressBookEntries => (super.noSuchMethod( - Invocation.getter(#addressBookEntries), - returnValue: _i8.Future>.value(<_i2.Contact>[]), - ) as _i8.Future>); + returnValue: <_i2.ContactEntry>[], + ) as List<_i2.ContactEntry>); @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); @override - _i2.Contact getContactById(String? id) => (super.noSuchMethod( + _i2.ContactEntry getContactById(String? id) => (super.noSuchMethod( Invocation.method( #getContactById, [id], ), - returnValue: _FakeContact_0( + returnValue: _FakeContactEntry_0( this, Invocation.method( #getContactById, [id], ), ), - ) as _i2.Contact); + ) as _i2.ContactEntry); @override - _i8.Future> search(String? text) => (super.noSuchMethod( + _i8.Future> search(String? text) => + (super.noSuchMethod( Invocation.method( #search, [text], ), - returnValue: _i8.Future>.value(<_i2.Contact>[]), - ) as _i8.Future>); + returnValue: + _i8.Future>.value(<_i2.ContactEntry>[]), + ) as _i8.Future>); @override bool matches( String? term, - _i2.Contact? contact, + _i2.ContactEntry? contact, ) => (super.noSuchMethod( Invocation.method( @@ -139,7 +136,7 @@ class MockAddressBookService extends _i1.Mock returnValue: false, ) as bool); @override - _i8.Future addContact(_i2.Contact? contact) => (super.noSuchMethod( + _i8.Future addContact(_i2.ContactEntry? contact) => (super.noSuchMethod( Invocation.method( #addContact, [contact], @@ -147,7 +144,7 @@ class MockAddressBookService extends _i1.Mock returnValue: _i8.Future.value(false), ) as _i8.Future); @override - _i8.Future editContact(_i2.Contact? editedContact) => + _i8.Future editContact(_i2.ContactEntry? editedContact) => (super.noSuchMethod( Invocation.method( #editContact, diff --git a/test/screen_tests/address_book_view/subviews/edit_address_book_entry_view_screen_test.mocks.dart b/test/screen_tests/address_book_view/subviews/edit_address_book_entry_view_screen_test.mocks.dart index dc9692644..8874682ae 100644 --- a/test/screen_tests/address_book_view/subviews/edit_address_book_entry_view_screen_test.mocks.dart +++ b/test/screen_tests/address_book_view/subviews/edit_address_book_entry_view_screen_test.mocks.dart @@ -8,7 +8,7 @@ import 'dart:ui' as _i9; import 'package:mockito/mockito.dart' as _i1; import 'package:stackwallet/models/balance.dart' as _i5; -import 'package:stackwallet/models/contact.dart' as _i2; +import 'package:stackwallet/models/isar/models/contact_entry.dart' as _i2; import 'package:stackwallet/models/isar/models/isar_models.dart' as _i12; import 'package:stackwallet/models/models.dart' as _i4; import 'package:stackwallet/services/address_book_service.dart' as _i7; @@ -28,8 +28,8 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart' as _i11; // ignore_for_file: camel_case_types // ignore_for_file: subtype_of_sealed_class -class _FakeContact_0 extends _i1.SmartFake implements _i2.Contact { - _FakeContact_0( +class _FakeContactEntry_0 extends _i1.SmartFake implements _i2.ContactEntry { + _FakeContactEntry_0( Object parent, Invocation parentInvocation, ) : super( @@ -85,46 +85,43 @@ class _FakeAmount_4 extends _i1.SmartFake implements _i6.Amount { class MockAddressBookService extends _i1.Mock implements _i7.AddressBookService { @override - List<_i2.Contact> get contacts => (super.noSuchMethod( + List<_i2.ContactEntry> get contacts => (super.noSuchMethod( Invocation.getter(#contacts), - returnValue: <_i2.Contact>[], - ) as List<_i2.Contact>); - @override - _i8.Future> get addressBookEntries => (super.noSuchMethod( - Invocation.getter(#addressBookEntries), - returnValue: _i8.Future>.value(<_i2.Contact>[]), - ) as _i8.Future>); + returnValue: <_i2.ContactEntry>[], + ) as List<_i2.ContactEntry>); @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); @override - _i2.Contact getContactById(String? id) => (super.noSuchMethod( + _i2.ContactEntry getContactById(String? id) => (super.noSuchMethod( Invocation.method( #getContactById, [id], ), - returnValue: _FakeContact_0( + returnValue: _FakeContactEntry_0( this, Invocation.method( #getContactById, [id], ), ), - ) as _i2.Contact); + ) as _i2.ContactEntry); @override - _i8.Future> search(String? text) => (super.noSuchMethod( + _i8.Future> search(String? text) => + (super.noSuchMethod( Invocation.method( #search, [text], ), - returnValue: _i8.Future>.value(<_i2.Contact>[]), - ) as _i8.Future>); + returnValue: + _i8.Future>.value(<_i2.ContactEntry>[]), + ) as _i8.Future>); @override bool matches( String? term, - _i2.Contact? contact, + _i2.ContactEntry? contact, ) => (super.noSuchMethod( Invocation.method( @@ -137,7 +134,7 @@ class MockAddressBookService extends _i1.Mock returnValue: false, ) as bool); @override - _i8.Future addContact(_i2.Contact? contact) => (super.noSuchMethod( + _i8.Future addContact(_i2.ContactEntry? contact) => (super.noSuchMethod( Invocation.method( #addContact, [contact], @@ -145,7 +142,7 @@ class MockAddressBookService extends _i1.Mock returnValue: _i8.Future.value(false), ) as _i8.Future); @override - _i8.Future editContact(_i2.Contact? editedContact) => + _i8.Future editContact(_i2.ContactEntry? editedContact) => (super.noSuchMethod( Invocation.method( #editContact, diff --git a/test/screen_tests/transaction_subviews/transaction_details_view_screen_test.mocks.dart b/test/screen_tests/transaction_subviews/transaction_details_view_screen_test.mocks.dart index 4d7191c2c..73941fed6 100644 --- a/test/screen_tests/transaction_subviews/transaction_details_view_screen_test.mocks.dart +++ b/test/screen_tests/transaction_subviews/transaction_details_view_screen_test.mocks.dart @@ -7,7 +7,7 @@ import 'dart:async' as _i4; import 'dart:ui' as _i5; import 'package:mockito/mockito.dart' as _i1; -import 'package:stackwallet/models/contact.dart' as _i2; +import 'package:stackwallet/models/isar/models/contact_entry.dart' as _i2; import 'package:stackwallet/services/address_book_service.dart' as _i6; import 'package:stackwallet/services/locale_service.dart' as _i7; import 'package:stackwallet/services/notes_service.dart' as _i3; @@ -23,8 +23,8 @@ import 'package:stackwallet/services/notes_service.dart' as _i3; // ignore_for_file: camel_case_types // ignore_for_file: subtype_of_sealed_class -class _FakeContact_0 extends _i1.SmartFake implements _i2.Contact { - _FakeContact_0( +class _FakeContactEntry_0 extends _i1.SmartFake implements _i2.ContactEntry { + _FakeContactEntry_0( Object parent, Invocation parentInvocation, ) : super( @@ -141,46 +141,43 @@ class MockNotesService extends _i1.Mock implements _i3.NotesService { class MockAddressBookService extends _i1.Mock implements _i6.AddressBookService { @override - List<_i2.Contact> get contacts => (super.noSuchMethod( + List<_i2.ContactEntry> get contacts => (super.noSuchMethod( Invocation.getter(#contacts), - returnValue: <_i2.Contact>[], - ) as List<_i2.Contact>); - @override - _i4.Future> get addressBookEntries => (super.noSuchMethod( - Invocation.getter(#addressBookEntries), - returnValue: _i4.Future>.value(<_i2.Contact>[]), - ) as _i4.Future>); + returnValue: <_i2.ContactEntry>[], + ) as List<_i2.ContactEntry>); @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); @override - _i2.Contact getContactById(String? id) => (super.noSuchMethod( + _i2.ContactEntry getContactById(String? id) => (super.noSuchMethod( Invocation.method( #getContactById, [id], ), - returnValue: _FakeContact_0( + returnValue: _FakeContactEntry_0( this, Invocation.method( #getContactById, [id], ), ), - ) as _i2.Contact); + ) as _i2.ContactEntry); @override - _i4.Future> search(String? text) => (super.noSuchMethod( + _i4.Future> search(String? text) => + (super.noSuchMethod( Invocation.method( #search, [text], ), - returnValue: _i4.Future>.value(<_i2.Contact>[]), - ) as _i4.Future>); + returnValue: + _i4.Future>.value(<_i2.ContactEntry>[]), + ) as _i4.Future>); @override bool matches( String? term, - _i2.Contact? contact, + _i2.ContactEntry? contact, ) => (super.noSuchMethod( Invocation.method( @@ -193,7 +190,7 @@ class MockAddressBookService extends _i1.Mock returnValue: false, ) as bool); @override - _i4.Future addContact(_i2.Contact? contact) => (super.noSuchMethod( + _i4.Future addContact(_i2.ContactEntry? contact) => (super.noSuchMethod( Invocation.method( #addContact, [contact], @@ -201,7 +198,7 @@ class MockAddressBookService extends _i1.Mock returnValue: _i4.Future.value(false), ) as _i4.Future); @override - _i4.Future editContact(_i2.Contact? editedContact) => + _i4.Future editContact(_i2.ContactEntry? editedContact) => (super.noSuchMethod( Invocation.method( #editContact, diff --git a/test/screen_tests/transaction_subviews/transaction_search_view_screen_test.mocks.dart b/test/screen_tests/transaction_subviews/transaction_search_view_screen_test.mocks.dart index 86d4ca961..02b5eb2b7 100644 --- a/test/screen_tests/transaction_subviews/transaction_search_view_screen_test.mocks.dart +++ b/test/screen_tests/transaction_subviews/transaction_search_view_screen_test.mocks.dart @@ -7,7 +7,7 @@ import 'dart:async' as _i4; import 'dart:ui' as _i5; import 'package:mockito/mockito.dart' as _i1; -import 'package:stackwallet/models/contact.dart' as _i2; +import 'package:stackwallet/models/isar/models/contact_entry.dart' as _i2; import 'package:stackwallet/services/address_book_service.dart' as _i3; import 'package:stackwallet/services/notes_service.dart' as _i6; @@ -22,8 +22,8 @@ import 'package:stackwallet/services/notes_service.dart' as _i6; // ignore_for_file: camel_case_types // ignore_for_file: subtype_of_sealed_class -class _FakeContact_0 extends _i1.SmartFake implements _i2.Contact { - _FakeContact_0( +class _FakeContactEntry_0 extends _i1.SmartFake implements _i2.ContactEntry { + _FakeContactEntry_0( Object parent, Invocation parentInvocation, ) : super( @@ -38,46 +38,43 @@ class _FakeContact_0 extends _i1.SmartFake implements _i2.Contact { class MockAddressBookService extends _i1.Mock implements _i3.AddressBookService { @override - List<_i2.Contact> get contacts => (super.noSuchMethod( + List<_i2.ContactEntry> get contacts => (super.noSuchMethod( Invocation.getter(#contacts), - returnValue: <_i2.Contact>[], - ) as List<_i2.Contact>); - @override - _i4.Future> get addressBookEntries => (super.noSuchMethod( - Invocation.getter(#addressBookEntries), - returnValue: _i4.Future>.value(<_i2.Contact>[]), - ) as _i4.Future>); + returnValue: <_i2.ContactEntry>[], + ) as List<_i2.ContactEntry>); @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); @override - _i2.Contact getContactById(String? id) => (super.noSuchMethod( + _i2.ContactEntry getContactById(String? id) => (super.noSuchMethod( Invocation.method( #getContactById, [id], ), - returnValue: _FakeContact_0( + returnValue: _FakeContactEntry_0( this, Invocation.method( #getContactById, [id], ), ), - ) as _i2.Contact); + ) as _i2.ContactEntry); @override - _i4.Future> search(String? text) => (super.noSuchMethod( + _i4.Future> search(String? text) => + (super.noSuchMethod( Invocation.method( #search, [text], ), - returnValue: _i4.Future>.value(<_i2.Contact>[]), - ) as _i4.Future>); + returnValue: + _i4.Future>.value(<_i2.ContactEntry>[]), + ) as _i4.Future>); @override bool matches( String? term, - _i2.Contact? contact, + _i2.ContactEntry? contact, ) => (super.noSuchMethod( Invocation.method( @@ -90,7 +87,7 @@ class MockAddressBookService extends _i1.Mock returnValue: false, ) as bool); @override - _i4.Future addContact(_i2.Contact? contact) => (super.noSuchMethod( + _i4.Future addContact(_i2.ContactEntry? contact) => (super.noSuchMethod( Invocation.method( #addContact, [contact], @@ -98,7 +95,7 @@ class MockAddressBookService extends _i1.Mock returnValue: _i4.Future.value(false), ) as _i4.Future); @override - _i4.Future editContact(_i2.Contact? editedContact) => + _i4.Future editContact(_i2.ContactEntry? editedContact) => (super.noSuchMethod( Invocation.method( #editContact, diff --git a/test/widget_tests/address_book_card_test.dart b/test/widget_tests/address_book_card_test.dart index 2c068f73d..faf78be2f 100644 --- a/test/widget_tests/address_book_card_test.dart +++ b/test/widget_tests/address_book_card_test.dart @@ -5,18 +5,17 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.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/models/isar/stack_theme.dart'; import 'package:stackwallet/pages/address_book_views/subviews/contact_popup.dart'; import 'package:stackwallet/providers/global/address_book_service_provider.dart'; import 'package:stackwallet/services/address_book_service.dart'; -import '../sample_data/theme_json.dart'; import 'package:stackwallet/themes/stack_colors.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/util.dart'; import 'package:stackwallet/widgets/address_book_card.dart'; +import '../sample_data/theme_json.dart'; import 'address_book_card_test.mocks.dart'; class MockedFunctions extends Mock { @@ -27,18 +26,20 @@ class MockedFunctions extends Mock { void main() { testWidgets('test returns Contact Address Entry', (widgetTester) async { final service = MockAddressBookService(); - final applicationThemesDirectoryPath = ""; + const applicationThemesDirectoryPath = ""; when(service.getContactById("default")).thenAnswer( - (realInvocation) => Contact( + (realInvocation) => ContactEntry( name: "John Doe", addresses: [ - const ContactAddressEntry( - coin: Coin.bitcoincash, - address: "some bch address", - label: "Bills") + ContactAddressEntry() + ..coinName = Coin.bitcoincash.name + ..address = "some bch address" + ..label = "Bills" + ..other = null ], isFavorite: true, + customId: '', ), ); diff --git a/test/widget_tests/address_book_card_test.mocks.dart b/test/widget_tests/address_book_card_test.mocks.dart index 550095d26..f26ca5dbd 100644 --- a/test/widget_tests/address_book_card_test.mocks.dart +++ b/test/widget_tests/address_book_card_test.mocks.dart @@ -7,7 +7,7 @@ import 'dart:async' as _i4; import 'dart:ui' as _i5; import 'package:mockito/mockito.dart' as _i1; -import 'package:stackwallet/models/contact.dart' as _i2; +import 'package:stackwallet/models/isar/models/contact_entry.dart' as _i2; import 'package:stackwallet/services/address_book_service.dart' as _i3; // ignore_for_file: type=lint @@ -21,8 +21,8 @@ import 'package:stackwallet/services/address_book_service.dart' as _i3; // ignore_for_file: camel_case_types // ignore_for_file: subtype_of_sealed_class -class _FakeContact_0 extends _i1.SmartFake implements _i2.Contact { - _FakeContact_0( +class _FakeContactEntry_0 extends _i1.SmartFake implements _i2.ContactEntry { + _FakeContactEntry_0( Object parent, Invocation parentInvocation, ) : super( @@ -41,46 +41,43 @@ class MockAddressBookService extends _i1.Mock } @override - List<_i2.Contact> get contacts => (super.noSuchMethod( + List<_i2.ContactEntry> get contacts => (super.noSuchMethod( Invocation.getter(#contacts), - returnValue: <_i2.Contact>[], - ) as List<_i2.Contact>); - @override - _i4.Future> get addressBookEntries => (super.noSuchMethod( - Invocation.getter(#addressBookEntries), - returnValue: _i4.Future>.value(<_i2.Contact>[]), - ) as _i4.Future>); + returnValue: <_i2.ContactEntry>[], + ) as List<_i2.ContactEntry>); @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, ) as bool); @override - _i2.Contact getContactById(String? id) => (super.noSuchMethod( + _i2.ContactEntry getContactById(String? id) => (super.noSuchMethod( Invocation.method( #getContactById, [id], ), - returnValue: _FakeContact_0( + returnValue: _FakeContactEntry_0( this, Invocation.method( #getContactById, [id], ), ), - ) as _i2.Contact); + ) as _i2.ContactEntry); @override - _i4.Future> search(String? text) => (super.noSuchMethod( + _i4.Future> search(String? text) => + (super.noSuchMethod( Invocation.method( #search, [text], ), - returnValue: _i4.Future>.value(<_i2.Contact>[]), - ) as _i4.Future>); + returnValue: + _i4.Future>.value(<_i2.ContactEntry>[]), + ) as _i4.Future>); @override bool matches( String? term, - _i2.Contact? contact, + _i2.ContactEntry? contact, ) => (super.noSuchMethod( Invocation.method( @@ -93,7 +90,7 @@ class MockAddressBookService extends _i1.Mock returnValue: false, ) as bool); @override - _i4.Future addContact(_i2.Contact? contact) => (super.noSuchMethod( + _i4.Future addContact(_i2.ContactEntry? contact) => (super.noSuchMethod( Invocation.method( #addContact, [contact], @@ -101,7 +98,7 @@ class MockAddressBookService extends _i1.Mock returnValue: _i4.Future.value(false), ) as _i4.Future); @override - _i4.Future editContact(_i2.Contact? editedContact) => + _i4.Future editContact(_i2.ContactEntry? editedContact) => (super.noSuchMethod( Invocation.method( #editContact, From a2c15ac332c76797466644933bd8e110f45a597c Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 15 May 2023 14:21:09 -0600 Subject: [PATCH 3/3] fix: missing updated data version --- lib/main.dart | 2 +- lib/pages_desktop_specific/password/desktop_login_view.dart | 2 +- lib/utilities/constants.dart | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 94a624eb9..60026022f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -162,7 +162,7 @@ void main() async { int dbVersion = DB.instance.get( boxName: DB.boxNameDBInfo, key: "hive_data_version") as int? ?? 0; - if (dbVersion < Constants.currentHiveDbVersion) { + if (dbVersion < Constants.currentDataVersion) { try { await DbVersionMigrator().migrate( dbVersion, diff --git a/lib/pages_desktop_specific/password/desktop_login_view.dart b/lib/pages_desktop_specific/password/desktop_login_view.dart index 40bd3919c..3a145e542 100644 --- a/lib/pages_desktop_specific/password/desktop_login_view.dart +++ b/lib/pages_desktop_specific/password/desktop_login_view.dart @@ -55,7 +55,7 @@ class _DesktopLoginViewState extends ConsumerState { int dbVersion = DB.instance.get( boxName: DB.boxNameDBInfo, key: "hive_data_version") as int? ?? 0; - if (dbVersion < Constants.currentHiveDbVersion) { + if (dbVersion < Constants.currentDataVersion) { try { await DbVersionMigrator().migrate( dbVersion, diff --git a/lib/utilities/constants.dart b/lib/utilities/constants.dart index 7b9178936..9a82262d0 100644 --- a/lib/utilities/constants.dart +++ b/lib/utilities/constants.dart @@ -42,7 +42,7 @@ abstract class Constants { // Enable Logger.print statements static const bool disableLogger = false; - static const int currentHiveDbVersion = 9; + static const int currentDataVersion = 10; static const int rescanV1 = 1;