From ce27caa83e84edeadb7aa0b1bfdaccccea48d501 Mon Sep 17 00:00:00 2001 From: Martin Macheiner Date: Mon, 9 Oct 2023 21:36:59 +0200 Subject: [PATCH 01/13] Create Realm classes to perform migration from previous app version --- .gitignore | 4 + android/app/build.gradle | 3 +- lib/src/data/book/realm/realm_book.dart | 49 +++ lib/src/data/book/realm/realm_book.g.dart | 371 ++++++++++++++++++++++ lib/src/data/book/realm/realm_helper.dart | 28 ++ pubspec.yaml | 5 +- 6 files changed, 456 insertions(+), 4 deletions(-) create mode 100644 lib/src/data/book/realm/realm_book.dart create mode 100644 lib/src/data/book/realm/realm_book.g.dart create mode 100644 lib/src/data/book/realm/realm_helper.dart diff --git a/.gitignore b/.gitignore index a901f0a..90407e1 100644 --- a/.gitignore +++ b/.gitignore @@ -53,3 +53,7 @@ UNTRANSLATED.txt # Generated code *.g.dart *.freezed.dart +# All except the generated code for the realm classes +# Realm and riverpod code generation does not work nicely together. +# Keep the one file from Realm to avoid ongoing code generation issues. +!lib/src/data/book/realm/realm_book.g.dart \ No newline at end of file diff --git a/android/app/build.gradle b/android/app/build.gradle index c64ec3c..ed1b987 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -37,8 +37,7 @@ android { } defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "at.shockbytes.dantex" + applicationId "at.shockbytes.dante" minSdkVersion 21 targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() diff --git a/lib/src/data/book/realm/realm_book.dart b/lib/src/data/book/realm/realm_book.dart new file mode 100644 index 0000000..a33f0c3 --- /dev/null +++ b/lib/src/data/book/realm/realm_book.dart @@ -0,0 +1,49 @@ + + +import 'package:realm/realm.dart'; + +part 'realm_book.g.dart'; + +@RealmModel() +class _RealmBook { + + @PrimaryKey() + late int id; + late String? title = ''; + late String? subTitle = ''; + late String? author = ''; + late int pageCount = 0; + late String? publishedDate = ''; + late int position= 0; + late String? isbn = ''; + late String? thumbnailAddress = ''; + late String? googleBooksLink = ''; + late int startDate = 0; + late int endDate = 0; + late int ordinalState = 0; + late int wishlistDate = 0; + late String? language = 'NA'; + late int rating = 0; + late int currentPage = 0; + late String? notes = ''; + late String? summary = ''; + late List<_RealmBookLabel> labels = []; +} + +@RealmModel() +class _RealmBookLabel { + late int bookId = -1; + late String? title = ''; + late String? hexColor = ''; +} + +@RealmModel() +class _RealmPageRecord { + + @PrimaryKey() + late String? recordId = ''; // of type "bookId-timestamp" + late int bookId = -1; + late int fromPage = 0; + late int toPage = 0; + late int timestamp = 0; +} \ No newline at end of file diff --git a/lib/src/data/book/realm/realm_book.g.dart b/lib/src/data/book/realm/realm_book.g.dart new file mode 100644 index 0000000..9b4230d --- /dev/null +++ b/lib/src/data/book/realm/realm_book.g.dart @@ -0,0 +1,371 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'realm_book.dart'; + +// ************************************************************************** +// RealmObjectGenerator +// ************************************************************************** + +class RealmBook extends _RealmBook + with RealmEntity, RealmObjectBase, RealmObject { + static var _defaultsSet = false; + + RealmBook( + int id, { + String? title = '', + String? subTitle = '', + String? author = '', + int pageCount = 0, + String? publishedDate = '', + int position = 0, + String? isbn = '', + String? thumbnailAddress = '', + String? googleBooksLink = '', + int startDate = 0, + int endDate = 0, + int ordinalState = 0, + int wishlistDate = 0, + String? language = 'NA', + int rating = 0, + int currentPage = 0, + String? notes = '', + String? summary = '', + Iterable labels = const [], + }) { + if (!_defaultsSet) { + _defaultsSet = RealmObjectBase.setDefaults({ + 'title': '', + 'subTitle': '', + 'author': '', + 'pageCount': 0, + 'publishedDate': '', + 'position': 0, + 'isbn': '', + 'thumbnailAddress': '', + 'googleBooksLink': '', + 'startDate': 0, + 'endDate': 0, + 'ordinalState': 0, + 'wishlistDate': 0, + 'language': 'NA', + 'rating': 0, + 'currentPage': 0, + 'notes': '', + 'summary': '', + }); + } + RealmObjectBase.set(this, 'id', id); + RealmObjectBase.set(this, 'title', title); + RealmObjectBase.set(this, 'subTitle', subTitle); + RealmObjectBase.set(this, 'author', author); + RealmObjectBase.set(this, 'pageCount', pageCount); + RealmObjectBase.set(this, 'publishedDate', publishedDate); + RealmObjectBase.set(this, 'position', position); + RealmObjectBase.set(this, 'isbn', isbn); + RealmObjectBase.set(this, 'thumbnailAddress', thumbnailAddress); + RealmObjectBase.set(this, 'googleBooksLink', googleBooksLink); + RealmObjectBase.set(this, 'startDate', startDate); + RealmObjectBase.set(this, 'endDate', endDate); + RealmObjectBase.set(this, 'ordinalState', ordinalState); + RealmObjectBase.set(this, 'wishlistDate', wishlistDate); + RealmObjectBase.set(this, 'language', language); + RealmObjectBase.set(this, 'rating', rating); + RealmObjectBase.set(this, 'currentPage', currentPage); + RealmObjectBase.set(this, 'notes', notes); + RealmObjectBase.set(this, 'summary', summary); + RealmObjectBase.set>( + this, 'labels', RealmList(labels)); + } + + RealmBook._(); + + @override + int get id => RealmObjectBase.get(this, 'id') as int; + @override + set id(int value) => RealmObjectBase.set(this, 'id', value); + + @override + String? get title => RealmObjectBase.get(this, 'title') as String?; + @override + set title(String? value) => RealmObjectBase.set(this, 'title', value); + + @override + String? get subTitle => + RealmObjectBase.get(this, 'subTitle') as String?; + @override + set subTitle(String? value) => RealmObjectBase.set(this, 'subTitle', value); + + @override + String? get author => RealmObjectBase.get(this, 'author') as String?; + @override + set author(String? value) => RealmObjectBase.set(this, 'author', value); + + @override + int get pageCount => RealmObjectBase.get(this, 'pageCount') as int; + @override + set pageCount(int value) => RealmObjectBase.set(this, 'pageCount', value); + + @override + String? get publishedDate => + RealmObjectBase.get(this, 'publishedDate') as String?; + @override + set publishedDate(String? value) => + RealmObjectBase.set(this, 'publishedDate', value); + + @override + int get position => RealmObjectBase.get(this, 'position') as int; + @override + set position(int value) => RealmObjectBase.set(this, 'position', value); + + @override + String? get isbn => RealmObjectBase.get(this, 'isbn') as String?; + @override + set isbn(String? value) => RealmObjectBase.set(this, 'isbn', value); + + @override + String? get thumbnailAddress => + RealmObjectBase.get(this, 'thumbnailAddress') as String?; + @override + set thumbnailAddress(String? value) => + RealmObjectBase.set(this, 'thumbnailAddress', value); + + @override + String? get googleBooksLink => + RealmObjectBase.get(this, 'googleBooksLink') as String?; + @override + set googleBooksLink(String? value) => + RealmObjectBase.set(this, 'googleBooksLink', value); + + @override + int get startDate => RealmObjectBase.get(this, 'startDate') as int; + @override + set startDate(int value) => RealmObjectBase.set(this, 'startDate', value); + + @override + int get endDate => RealmObjectBase.get(this, 'endDate') as int; + @override + set endDate(int value) => RealmObjectBase.set(this, 'endDate', value); + + @override + int get ordinalState => RealmObjectBase.get(this, 'ordinalState') as int; + @override + set ordinalState(int value) => + RealmObjectBase.set(this, 'ordinalState', value); + + @override + int get wishlistDate => RealmObjectBase.get(this, 'wishlistDate') as int; + @override + set wishlistDate(int value) => + RealmObjectBase.set(this, 'wishlistDate', value); + + @override + String? get language => + RealmObjectBase.get(this, 'language') as String?; + @override + set language(String? value) => RealmObjectBase.set(this, 'language', value); + + @override + int get rating => RealmObjectBase.get(this, 'rating') as int; + @override + set rating(int value) => RealmObjectBase.set(this, 'rating', value); + + @override + int get currentPage => RealmObjectBase.get(this, 'currentPage') as int; + @override + set currentPage(int value) => RealmObjectBase.set(this, 'currentPage', value); + + @override + String? get notes => RealmObjectBase.get(this, 'notes') as String?; + @override + set notes(String? value) => RealmObjectBase.set(this, 'notes', value); + + @override + String? get summary => + RealmObjectBase.get(this, 'summary') as String?; + @override + set summary(String? value) => RealmObjectBase.set(this, 'summary', value); + + @override + RealmList get labels => + RealmObjectBase.get(this, 'labels') + as RealmList; + @override + set labels(covariant RealmList value) => + throw RealmUnsupportedSetError(); + + @override + Stream> get changes => + RealmObjectBase.getChanges(this); + + @override + RealmBook freeze() => RealmObjectBase.freezeObject(this); + + static SchemaObject get schema => _schema ??= _initSchema(); + static SchemaObject? _schema; + static SchemaObject _initSchema() { + RealmObjectBase.registerFactory(RealmBook._); + return const SchemaObject(ObjectType.realmObject, RealmBook, 'RealmBook', [ + SchemaProperty('id', RealmPropertyType.int, primaryKey: true), + SchemaProperty('title', RealmPropertyType.string, optional: true), + SchemaProperty('subTitle', RealmPropertyType.string, optional: true), + SchemaProperty('author', RealmPropertyType.string, optional: true), + SchemaProperty('pageCount', RealmPropertyType.int), + SchemaProperty('publishedDate', RealmPropertyType.string, optional: true), + SchemaProperty('position', RealmPropertyType.int), + SchemaProperty('isbn', RealmPropertyType.string, optional: true), + SchemaProperty('thumbnailAddress', RealmPropertyType.string, + optional: true), + SchemaProperty('googleBooksLink', RealmPropertyType.string, + optional: true), + SchemaProperty('startDate', RealmPropertyType.int), + SchemaProperty('endDate', RealmPropertyType.int), + SchemaProperty('ordinalState', RealmPropertyType.int), + SchemaProperty('wishlistDate', RealmPropertyType.int), + SchemaProperty('language', RealmPropertyType.string, optional: true), + SchemaProperty('rating', RealmPropertyType.int), + SchemaProperty('currentPage', RealmPropertyType.int), + SchemaProperty('notes', RealmPropertyType.string, optional: true), + SchemaProperty('summary', RealmPropertyType.string, optional: true), + SchemaProperty('labels', RealmPropertyType.object, + linkTarget: 'RealmBookLabel', + collectionType: RealmCollectionType.list), + ]); + } +} + +class RealmBookLabel extends _RealmBookLabel + with RealmEntity, RealmObjectBase, RealmObject { + static var _defaultsSet = false; + + RealmBookLabel({ + int bookId = -1, + String? title = '', + String? hexColor = '', + }) { + if (!_defaultsSet) { + _defaultsSet = RealmObjectBase.setDefaults({ + 'bookId': -1, + 'title': '', + 'hexColor': '', + }); + } + RealmObjectBase.set(this, 'bookId', bookId); + RealmObjectBase.set(this, 'title', title); + RealmObjectBase.set(this, 'hexColor', hexColor); + } + + RealmBookLabel._(); + + @override + int get bookId => RealmObjectBase.get(this, 'bookId') as int; + @override + set bookId(int value) => RealmObjectBase.set(this, 'bookId', value); + + @override + String? get title => RealmObjectBase.get(this, 'title') as String?; + @override + set title(String? value) => RealmObjectBase.set(this, 'title', value); + + @override + String? get hexColor => + RealmObjectBase.get(this, 'hexColor') as String?; + @override + set hexColor(String? value) => RealmObjectBase.set(this, 'hexColor', value); + + @override + Stream> get changes => + RealmObjectBase.getChanges(this); + + @override + RealmBookLabel freeze() => RealmObjectBase.freezeObject(this); + + static SchemaObject get schema => _schema ??= _initSchema(); + static SchemaObject? _schema; + static SchemaObject _initSchema() { + RealmObjectBase.registerFactory(RealmBookLabel._); + return const SchemaObject( + ObjectType.realmObject, RealmBookLabel, 'RealmBookLabel', [ + SchemaProperty('bookId', RealmPropertyType.int), + SchemaProperty('title', RealmPropertyType.string, optional: true), + SchemaProperty('hexColor', RealmPropertyType.string, optional: true), + ]); + } +} + +class RealmPageRecord extends _RealmPageRecord + with RealmEntity, RealmObjectBase, RealmObject { + static var _defaultsSet = false; + + RealmPageRecord( + String? recordId, { + int bookId = -1, + int fromPage = 0, + int toPage = 0, + int timestamp = 0, + }) { + if (!_defaultsSet) { + _defaultsSet = RealmObjectBase.setDefaults({ + 'recordId': '', + 'bookId': -1, + 'fromPage': 0, + 'toPage': 0, + 'timestamp': 0, + }); + } + RealmObjectBase.set(this, 'recordId', recordId); + RealmObjectBase.set(this, 'bookId', bookId); + RealmObjectBase.set(this, 'fromPage', fromPage); + RealmObjectBase.set(this, 'toPage', toPage); + RealmObjectBase.set(this, 'timestamp', timestamp); + } + + RealmPageRecord._(); + + @override + String? get recordId => + RealmObjectBase.get(this, 'recordId') as String; + @override + set recordId(String? value) => RealmObjectBase.set(this, 'recordId', value); + + @override + int get bookId => RealmObjectBase.get(this, 'bookId') as int; + @override + set bookId(int value) => RealmObjectBase.set(this, 'bookId', value); + + @override + int get fromPage => RealmObjectBase.get(this, 'fromPage') as int; + @override + set fromPage(int value) => RealmObjectBase.set(this, 'fromPage', value); + + @override + int get toPage => RealmObjectBase.get(this, 'toPage') as int; + @override + set toPage(int value) => RealmObjectBase.set(this, 'toPage', value); + + @override + int get timestamp => RealmObjectBase.get(this, 'timestamp') as int; + @override + set timestamp(int value) => RealmObjectBase.set(this, 'timestamp', value); + + @override + Stream> get changes => + RealmObjectBase.getChanges(this); + + @override + RealmPageRecord freeze() => + RealmObjectBase.freezeObject(this); + + static SchemaObject get schema => _schema ??= _initSchema(); + static SchemaObject? _schema; + static SchemaObject _initSchema() { + RealmObjectBase.registerFactory(RealmPageRecord._); + return const SchemaObject( + ObjectType.realmObject, RealmPageRecord, 'RealmPageRecord', [ + SchemaProperty('recordId', RealmPropertyType.string, primaryKey: true, optional: true), + SchemaProperty('bookId', RealmPropertyType.int), + SchemaProperty('fromPage', RealmPropertyType.int), + SchemaProperty('toPage', RealmPropertyType.int), + SchemaProperty('timestamp', RealmPropertyType.int), + ]); + } +} diff --git a/lib/src/data/book/realm/realm_helper.dart b/lib/src/data/book/realm/realm_helper.dart new file mode 100644 index 0000000..b881578 --- /dev/null +++ b/lib/src/data/book/realm/realm_helper.dart @@ -0,0 +1,28 @@ +import 'package:dantex/src/data/book/realm/realm_book.dart'; +import 'package:realm/realm.dart'; + +class RealmHelper { + void init() { + print('reading books!'); + var config = Configuration.local( + [ + RealmBook.schema, + RealmBookLabel.schema, + RealmPageRecord.schema, + ], + schemaVersion: 9, + ); + var realm = Realm(config); + realm.all().forEach((RealmBook book) { + print('----------'); + print(book.id); + print(book.author); + print(book.pageCount); + print(book.subTitle); + print(book.title); + print('----------'); + print(book); + print('----------'); + }); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index aaa583a..55830fe 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,7 @@ description: Dante - Book Tracker Cross Platform implementation. publish_to: 'none' -version: 1.0.0 +version: 5.0.0+52 environment: sdk: '>=3.0.6 <4.0.0' @@ -53,6 +53,7 @@ dependencies: extension_google_sign_in_as_googleapis_auth: ^2.0.11 fl_chart: ^0.65.0 flutter_staggered_grid_view: ^0.7.0 + realm: ^1.5.0 dev_dependencies: flutter_test: @@ -61,7 +62,7 @@ dev_dependencies: flutter_lints: ^3.0.0 mockito: ^5.4.2 build_runner: ^2.4.6 - riverpod_generator: ^2.3.2 + riverpod_generator: ^2.3.3 riverpod_lint: ^2.0.1 custom_lint: ^0.5.3 freezed: ^2.4.5 From 672e6b7a5e0f053624113875b575eaade5fbc6a5 Mon Sep 17 00:00:00 2001 From: Martin Macheiner Date: Mon, 9 Oct 2023 22:11:06 +0200 Subject: [PATCH 02/13] Introduce MigrationRunner --- lib/src/data/book/book_repository.dart | 4 +- .../data/book/firebase_book_repository.dart | 2 +- .../data/book/migration/migration_runner.dart | 71 +++++++++++ .../data/book/migration/migration_score.dart | 13 ++ .../book/realm/realm_book_repository.dart | 118 ++++++++++++++++++ lib/src/data/book/realm/realm_helper.dart | 28 ----- 6 files changed, 206 insertions(+), 30 deletions(-) create mode 100644 lib/src/data/book/migration/migration_runner.dart create mode 100644 lib/src/data/book/migration/migration_score.dart create mode 100644 lib/src/data/book/realm/realm_book_repository.dart delete mode 100644 lib/src/data/book/realm/realm_helper.dart diff --git a/lib/src/data/book/book_repository.dart b/lib/src/data/book/book_repository.dart index 6e7f9c7..3a4a162 100644 --- a/lib/src/data/book/book_repository.dart +++ b/lib/src/data/book/book_repository.dart @@ -6,7 +6,9 @@ import 'package:dantex/src/data/book/search_criteria.dart'; abstract class BookRepository { Stream> getBooksForState(BookState state); - Stream> getAllBooks(); + Stream> listenAllBooks(); + + Future> getAllBooks(); Stream getBook(String id); diff --git a/lib/src/data/book/firebase_book_repository.dart b/lib/src/data/book/firebase_book_repository.dart index e6b8f8e..e32ab9d 100644 --- a/lib/src/data/book/firebase_book_repository.dart +++ b/lib/src/data/book/firebase_book_repository.dart @@ -69,7 +69,7 @@ class FirebaseBookRepository implements BookRepository { } @override - Stream getBook(String id) { + Stream getBook(String id) async { return _booksRef().child(id).onValue.map((DatabaseEvent event) { final Map? data = event.snapshot.toMap(); diff --git a/lib/src/data/book/migration/migration_runner.dart b/lib/src/data/book/migration/migration_runner.dart new file mode 100644 index 0000000..35a0b0f --- /dev/null +++ b/lib/src/data/book/migration/migration_runner.dart @@ -0,0 +1,71 @@ +import 'package:dantex/src/data/book/book_repository.dart'; +import 'package:dantex/src/data/book/entity/book.dart'; +import 'package:dantex/src/data/book/migration/migration_score.dart'; + +/// Runs the migration from the old Android app to the new cross platform app. +/// The runner essentially fetches all data from Realm and pipes it to Firebase. +class MigrationRunner { + final BookRepository _target; + final BookRepository _source; + + MigrationRunner( + BookRepository target, + BookRepository source, + ) : _target = target, + _source = source; + + Future migrationStatus() async { + // TODO Check if a migration need to be performed + return MigrationStatus.required; + } + + Future migrate() async { + ({int migratedBooks, int booksToMigrate}) booksStat = await _migrateBooks(); + + ({int migratedPageRecords, int pageRecordsToMigrate}) pageRecordsStats = await _migratePageRecords(); + + return MigrationScore( + booksToMigrate: booksStat.booksToMigrate, + migratedBooks: booksStat.migratedBooks, + pageRecordsToMigrate: pageRecordsStats.pageRecordsToMigrate, + migratedPageRecords: pageRecordsStats.migratedPageRecords, + ); + } + + /// Returns no. of migrated instances and potential no. of migrations + Future<({int migratedBooks, int booksToMigrate})> _migrateBooks() async { + List booksToMigrate = await _source.getAllBooks(); + + int migratedBooks = 0; + for (int i = 0; i < booksToMigrate.length; i++) { + Book book = booksToMigrate[i]; + + try { + await _target.create(book); + migratedBooks++; + } catch (e) { + // TODO Log error + } + } + + return ( + migratedBooks: migratedBooks, + booksToMigrate: booksToMigrate.length, + ); + } + + /// Returns no. of migrated instances and potential no. of migrations + Future<({int migratedPageRecords, int pageRecordsToMigrate})> _migratePageRecords() async { + // TODO + return ( + migratedPageRecords: 0, + pageRecordsToMigrate: 0, + ); + } +} + +enum MigrationStatus { + required, + doneButFailed, + done, +} \ No newline at end of file diff --git a/lib/src/data/book/migration/migration_score.dart b/lib/src/data/book/migration/migration_score.dart new file mode 100644 index 0000000..d646b8c --- /dev/null +++ b/lib/src/data/book/migration/migration_score.dart @@ -0,0 +1,13 @@ +class MigrationScore { + final int booksToMigrate; + final int migratedBooks; + final int pageRecordsToMigrate; + final int migratedPageRecords; + + MigrationScore({ + required this.booksToMigrate, + required this.migratedBooks, + required this.pageRecordsToMigrate, + required this.migratedPageRecords, +}); +} diff --git a/lib/src/data/book/realm/realm_book_repository.dart b/lib/src/data/book/realm/realm_book_repository.dart new file mode 100644 index 0000000..e111ef3 --- /dev/null +++ b/lib/src/data/book/realm/realm_book_repository.dart @@ -0,0 +1,118 @@ +import 'package:dantex/src/core/book_core.dart'; +import 'package:dantex/src/data/book/book_repository.dart'; +import 'package:dantex/src/data/book/entity/book.dart'; +import 'package:dantex/src/data/book/entity/book_label.dart'; +import 'package:dantex/src/data/book/entity/book_state.dart'; +import 'package:dantex/src/data/book/realm/realm_book.dart'; +import 'package:realm/realm.dart'; + +class RealmBookRepository implements BookRepository { + final Realm _realm = Realm( + Configuration.local( + [ + RealmBook.schema, + RealmBookLabel.schema, + RealmPageRecord.schema, + ], + schemaVersion: 9, // 9 is the current schema version of the old app + ), + ); + + @override + Future create(Book book) { + // Not required. + throw UnimplementedError(); + } + + @override + Future delete(BookId id) { + // Not required. + throw UnimplementedError(); + } + + @override + Stream> listenAllBooks() { + // Not required. + throw UnimplementedError(); + } + + @override + Future getBook(BookId id) { + // Not required. + throw UnimplementedError(); + } + + @override + Future> getAllBooks() async { + return _realm + .all() + .where(_hasRealmBookRequiredData) + .map( + (rb) => Book( + id: rb.id.toString(), + title: rb.title!, + subTitle: rb.subTitle!, + author: rb.author!, + state: BookState.values[rb.ordinalState], + pageCount: rb.pageCount, + currentPage: rb.currentPage, + publishedDate: rb.publishedDate!, + position: rb.position, + isbn: rb.isbn!, + thumbnailAddress: rb.thumbnailAddress, + startDate: rb.startDate, + endDate: rb.endDate, + wishlistDate: rb.wishlistDate, + language: rb.language!, + rating: rb.rating, + notes: rb.notes, + summary: rb.summary, + labels: rb.labels + .where(_hasRealmBookLabelRequiredData) + .map( + (rbl) => BookLabel( + bookId: rbl.bookId.toString(), + title: rbl.title!, + hexColor: rbl.hexColor!, + ), + ) + .toList(), + ), + ) + .toList(); + } + + bool _hasRealmBookRequiredData(RealmBook book) { + // TODO Check this + return true; + } + + bool _hasRealmBookLabelRequiredData(RealmBookLabel bookLabel) { + // TODO Check this + return true; + } + + @override + Stream> getBooksForState(BookState state) { + // Not required. + throw UnimplementedError(); + } + + @override + Stream> search(SearchCriteria criteria) { + // Not required. + throw UnimplementedError(); + } + + @override + Future update(Book book) { + // Not required. + throw UnimplementedError(); + } + + @override + Future updateCurrentPage(BookId bookId, int currentPage) { + // Not required. + throw UnimplementedError(); + } +} diff --git a/lib/src/data/book/realm/realm_helper.dart b/lib/src/data/book/realm/realm_helper.dart deleted file mode 100644 index b881578..0000000 --- a/lib/src/data/book/realm/realm_helper.dart +++ /dev/null @@ -1,28 +0,0 @@ -import 'package:dantex/src/data/book/realm/realm_book.dart'; -import 'package:realm/realm.dart'; - -class RealmHelper { - void init() { - print('reading books!'); - var config = Configuration.local( - [ - RealmBook.schema, - RealmBookLabel.schema, - RealmPageRecord.schema, - ], - schemaVersion: 9, - ); - var realm = Realm(config); - realm.all().forEach((RealmBook book) { - print('----------'); - print(book.id); - print(book.author); - print(book.pageCount); - print(book.subTitle); - print(book.title); - print('----------'); - print(book); - print('----------'); - }); - } -} From cc80d83e25c4b53cf5793728b4cbc8bfb4856fd1 Mon Sep 17 00:00:00 2001 From: Martin Macheiner Date: Tue, 17 Oct 2023 21:24:54 +0200 Subject: [PATCH 03/13] Setup skeleton for page records --- lib/src/data/book/entity/page_record.dart | 17 ++++ .../book/firebase_page_record_repository.dart | 47 ++++++++++ .../data/book/migration/migration_runner.dart | 94 ++++++++++++++----- .../data/book/migration/migration_score.dart | 17 +++- lib/src/data/book/page_record_repository.dart | 24 +++++ .../realm/realm_page_record_repository.dart | 47 ++++++++++ 6 files changed, 220 insertions(+), 26 deletions(-) create mode 100644 lib/src/data/book/entity/page_record.dart create mode 100644 lib/src/data/book/firebase_page_record_repository.dart create mode 100644 lib/src/data/book/page_record_repository.dart create mode 100644 lib/src/data/book/realm/realm_page_record_repository.dart diff --git a/lib/src/data/book/entity/page_record.dart b/lib/src/data/book/entity/page_record.dart new file mode 100644 index 0000000..ca6190d --- /dev/null +++ b/lib/src/data/book/entity/page_record.dart @@ -0,0 +1,17 @@ +class PageRecord { + final String bookId; + final int fromPage; + final int toPage; + final int timestamp; + + PageRecord({ + required this.bookId, + required this.fromPage, + required this.toPage, + required this.timestamp, + }); + + DateTime get dateTime => DateTime.fromMillisecondsSinceEpoch(timestamp); + + int get diffPages => toPage - fromPage; +} diff --git a/lib/src/data/book/firebase_page_record_repository.dart b/lib/src/data/book/firebase_page_record_repository.dart new file mode 100644 index 0000000..21bbbbf --- /dev/null +++ b/lib/src/data/book/firebase_page_record_repository.dart @@ -0,0 +1,47 @@ +import 'package:dantex/src/data/book/entity/page_record.dart'; +import 'package:dantex/src/data/book/page_record_repository.dart'; + +// TODO Implement this class +class FirebasePageRecordRepository implements PageRecordRepository { + @override + Future> allPageRecords() { + // TODO: implement allPageRecords + throw UnimplementedError(); + } + + @override + Future deleteAllPageRecordsForBookId(String bookId) { + // TODO: implement deleteAllPageRecordsForBookId + throw UnimplementedError(); + } + + @override + Future deletePageRecordForBook(PageRecord pageRecord) { + // TODO: implement deletePageRecordForBook + throw UnimplementedError(); + } + + @override + Future insertPageRecordForBookId( + String bookId, + int fromPage, + int toPage, + int nowInMillis, + ) { + // TODO: implement insertPageRecordForBookId + throw UnimplementedError(); + } + + @override + Future> pageRecordsForBook(String bookId) { + // TODO: implement pageRecordsForBook + throw UnimplementedError(); + } + + @override + Future updatePageRecord( + PageRecord pageRecord, int? fromPage, int? toPage) { + // TODO: implement updatePageRecord + throw UnimplementedError(); + } +} diff --git a/lib/src/data/book/migration/migration_runner.dart b/lib/src/data/book/migration/migration_runner.dart index 35a0b0f..21aa411 100644 --- a/lib/src/data/book/migration/migration_runner.dart +++ b/lib/src/data/book/migration/migration_runner.dart @@ -1,28 +1,54 @@ import 'package:dantex/src/data/book/book_repository.dart'; import 'package:dantex/src/data/book/entity/book.dart'; +import 'package:dantex/src/data/book/entity/page_record.dart'; import 'package:dantex/src/data/book/migration/migration_score.dart'; +import 'package:dantex/src/data/book/page_record_repository.dart'; /// Runs the migration from the old Android app to the new cross platform app. /// The runner essentially fetches all data from Realm and pipes it to Firebase. class MigrationRunner { - final BookRepository _target; - final BookRepository _source; + final BookRepository _bookTarget; + final BookRepository _bookSource; + final PageRecordRepository _pageRecordTarget; + final PageRecordRepository _pageRecordSource; MigrationRunner( - BookRepository target, - BookRepository source, - ) : _target = target, - _source = source; + BookRepository bookTarget, + BookRepository bookSource, + PageRecordRepository pageRecordTarget, + PageRecordRepository pageRecordSource, + ) : _bookTarget = bookTarget, + _bookSource = bookSource, + _pageRecordTarget = pageRecordTarget, + _pageRecordSource = pageRecordSource; - Future migrationStatus() async { - // TODO Check if a migration need to be performed + // TODO Call this method + Future migrateIfRequired() async { + final MigrationStatus status = await _migrationStatus(); + + if (status == MigrationStatus.required) { + final MigrationScore score = await _migrate(); + final MigrationStatus newStatus = score.statusFromScore(); + _updateMigrationStatus(newStatus); + return newStatus; + } + + return status; + } + + Future _migrationStatus() async { + // TODO Load from SettingsRepository return MigrationStatus.required; } - Future migrate() async { - ({int migratedBooks, int booksToMigrate}) booksStat = await _migrateBooks(); + Future _migrate() async { + final ({int migratedBooks, int booksToMigrate}) booksStat = + await _migrateBooks(); - ({int migratedPageRecords, int pageRecordsToMigrate}) pageRecordsStats = await _migratePageRecords(); + final ({ + int migratedPageRecords, + int pageRecordsToMigrate + }) pageRecordsStats = await _migratePageRecords(); return MigrationScore( booksToMigrate: booksStat.booksToMigrate, @@ -34,17 +60,17 @@ class MigrationRunner { /// Returns no. of migrated instances and potential no. of migrations Future<({int migratedBooks, int booksToMigrate})> _migrateBooks() async { - List booksToMigrate = await _source.getAllBooks(); + final List booksToMigrate = await _bookSource.getAllBooks(); int migratedBooks = 0; for (int i = 0; i < booksToMigrate.length; i++) { - Book book = booksToMigrate[i]; + final Book book = booksToMigrate[i]; try { - await _target.create(book); + await _bookTarget.create(book); migratedBooks++; } catch (e) { - // TODO Log error + // TODO Await logging facility } } @@ -55,17 +81,35 @@ class MigrationRunner { } /// Returns no. of migrated instances and potential no. of migrations - Future<({int migratedPageRecords, int pageRecordsToMigrate})> _migratePageRecords() async { - // TODO + Future<({int migratedPageRecords, int pageRecordsToMigrate})> + _migratePageRecords() async { + final List pageRecordsToMigrate = + await _pageRecordSource.allPageRecords(); + + int migratedPageRecords = 0; + for (int i = 0; i < pageRecordsToMigrate.length; i++) { + final PageRecord record = pageRecordsToMigrate[i]; + + try { + await _pageRecordTarget.insertPageRecordForBookId( + record.bookId, + record.fromPage, + record.toPage, + record.timestamp, + ); + migratedPageRecords++; + } catch (e) { + // TODO Await logging facility + } + } + return ( - migratedPageRecords: 0, - pageRecordsToMigrate: 0, + migratedPageRecords: migratedPageRecords, + pageRecordsToMigrate: pageRecordsToMigrate.length, ); } -} -enum MigrationStatus { - required, - doneButFailed, - done, -} \ No newline at end of file + void _updateMigrationStatus(MigrationStatus newStatus) { + // TODO Save to SettingsRepository + } +} diff --git a/lib/src/data/book/migration/migration_score.dart b/lib/src/data/book/migration/migration_score.dart index d646b8c..4379127 100644 --- a/lib/src/data/book/migration/migration_score.dart +++ b/lib/src/data/book/migration/migration_score.dart @@ -9,5 +9,20 @@ class MigrationScore { required this.migratedBooks, required this.pageRecordsToMigrate, required this.migratedPageRecords, -}); + }); + + bool isSuccessful() { + return booksToMigrate == migratedBooks && + pageRecordsToMigrate == migratedPageRecords; + } + + MigrationStatus statusFromScore() { + return isSuccessful() ? MigrationStatus.migrated : MigrationStatus.failed; + } +} + +enum MigrationStatus { + required, + migrated, + failed, } diff --git a/lib/src/data/book/page_record_repository.dart b/lib/src/data/book/page_record_repository.dart new file mode 100644 index 0000000..510738a --- /dev/null +++ b/lib/src/data/book/page_record_repository.dart @@ -0,0 +1,24 @@ +import 'package:dantex/src/data/book/entity/page_record.dart'; + +abstract class PageRecordRepository { + Future insertPageRecordForBookId( + String bookId, + int fromPage, + int toPage, + int nowInMillis, + ); + + Future updatePageRecord( + PageRecord pageRecord, + int? fromPage, + int? toPage, + ); + + Future deletePageRecordForBook(PageRecord pageRecord); + + Future deleteAllPageRecordsForBookId(String bookId); + + Future> pageRecordsForBook(String bookId); + + Future> allPageRecords(); +} diff --git a/lib/src/data/book/realm/realm_page_record_repository.dart b/lib/src/data/book/realm/realm_page_record_repository.dart new file mode 100644 index 0000000..8c5dfdc --- /dev/null +++ b/lib/src/data/book/realm/realm_page_record_repository.dart @@ -0,0 +1,47 @@ +import 'package:dantex/src/data/book/entity/page_record.dart'; +import 'package:dantex/src/data/book/page_record_repository.dart'; + +// TODO Implement this class +class RealmPageRecordRepository implements PageRecordRepository { + @override + Future> allPageRecords() { + // TODO: implement allPageRecords + throw UnimplementedError(); + } + + @override + Future deleteAllPageRecordsForBookId(String bookId) { + // TODO: implement deleteAllPageRecordsForBookId + throw UnimplementedError(); + } + + @override + Future deletePageRecordForBook(PageRecord pageRecord) { + // TODO: implement deletePageRecordForBook + throw UnimplementedError(); + } + + @override + Future insertPageRecordForBookId( + String bookId, + int fromPage, + int toPage, + int nowInMillis, + ) { + // TODO: implement insertPageRecordForBookId + throw UnimplementedError(); + } + + @override + Future> pageRecordsForBook(String bookId) { + // TODO: implement pageRecordsForBook + throw UnimplementedError(); + } + + @override + Future updatePageRecord( + PageRecord pageRecord, int? fromPage, int? toPage) { + // TODO: implement updatePageRecord + throw UnimplementedError(); + } +} From 84274f1a4a9e790a1b4c25a0fca5730d964a57dd Mon Sep 17 00:00:00 2001 From: Martin Macheiner Date: Tue, 17 Oct 2023 21:32:07 +0200 Subject: [PATCH 04/13] Implement RealmPageRecordRepository --- .../book/realm/realm_book_repository.dart | 9 +++- .../realm/realm_page_record_repository.dart | 42 ++++++++++++++----- 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/lib/src/data/book/realm/realm_book_repository.dart b/lib/src/data/book/realm/realm_book_repository.dart index e111ef3..623d99b 100644 --- a/lib/src/data/book/realm/realm_book_repository.dart +++ b/lib/src/data/book/realm/realm_book_repository.dart @@ -7,7 +7,9 @@ import 'package:dantex/src/data/book/realm/realm_book.dart'; import 'package:realm/realm.dart'; class RealmBookRepository implements BookRepository { - final Realm _realm = Realm( + + /* TODO Inject this + final Realm _realm = Realm( Configuration.local( [ RealmBook.schema, @@ -17,6 +19,11 @@ class RealmBookRepository implements BookRepository { schemaVersion: 9, // 9 is the current schema version of the old app ), ); + */ + + final Realm _realm; + + RealmBookRepository(this._realm); @override Future create(Book book) { diff --git a/lib/src/data/book/realm/realm_page_record_repository.dart b/lib/src/data/book/realm/realm_page_record_repository.dart index 8c5dfdc..2377bab 100644 --- a/lib/src/data/book/realm/realm_page_record_repository.dart +++ b/lib/src/data/book/realm/realm_page_record_repository.dart @@ -1,23 +1,42 @@ import 'package:dantex/src/data/book/entity/page_record.dart'; import 'package:dantex/src/data/book/page_record_repository.dart'; +import 'package:dantex/src/data/book/realm/realm_book.dart'; +import 'package:realm/realm.dart'; -// TODO Implement this class class RealmPageRecordRepository implements PageRecordRepository { + final Realm _realm; + + RealmPageRecordRepository(this._realm); + @override - Future> allPageRecords() { - // TODO: implement allPageRecords - throw UnimplementedError(); + Future> allPageRecords() async { + return _realm + .all() + .where(_hasPageRecordRequiredData) + .map( + (rpg) => PageRecord( + bookId: rpg.bookId.toString(), + fromPage: rpg.fromPage, + toPage: rpg.toPage, + timestamp: rpg.timestamp, + ), + ) + .toList(); + } + + bool _hasPageRecordRequiredData(RealmPageRecord pageRecord) { + return pageRecord.bookId != -1 && pageRecord.timestamp != 0; } @override Future deleteAllPageRecordsForBookId(String bookId) { - // TODO: implement deleteAllPageRecordsForBookId + // Not required. throw UnimplementedError(); } @override Future deletePageRecordForBook(PageRecord pageRecord) { - // TODO: implement deletePageRecordForBook + // Not required. throw UnimplementedError(); } @@ -28,20 +47,23 @@ class RealmPageRecordRepository implements PageRecordRepository { int toPage, int nowInMillis, ) { - // TODO: implement insertPageRecordForBookId + // Not required. throw UnimplementedError(); } @override Future> pageRecordsForBook(String bookId) { - // TODO: implement pageRecordsForBook + // Not required. throw UnimplementedError(); } @override Future updatePageRecord( - PageRecord pageRecord, int? fromPage, int? toPage) { - // TODO: implement updatePageRecord + PageRecord pageRecord, + int? fromPage, + int? toPage, + ) { + // Not required. throw UnimplementedError(); } } From 9edfdc9b09f2188cfd57ab113ce195f31b68421c Mon Sep 17 00:00:00 2001 From: Martin Macheiner Date: Wed, 1 Nov 2023 20:25:47 +0100 Subject: [PATCH 05/13] Resolve rebasing issues --- .../data/book/migration/migration_runner.dart | 7 ++- lib/src/data/book/realm/realm_book.g.dart | 5 ++- .../book/realm/realm_book_repository.dart | 44 +++++++++++++++++-- 3 files changed, 48 insertions(+), 8 deletions(-) diff --git a/lib/src/data/book/migration/migration_runner.dart b/lib/src/data/book/migration/migration_runner.dart index 21aa411..ed85636 100644 --- a/lib/src/data/book/migration/migration_runner.dart +++ b/lib/src/data/book/migration/migration_runner.dart @@ -3,6 +3,7 @@ import 'package:dantex/src/data/book/entity/book.dart'; import 'package:dantex/src/data/book/entity/page_record.dart'; import 'package:dantex/src/data/book/migration/migration_score.dart'; import 'package:dantex/src/data/book/page_record_repository.dart'; +import 'package:logger/logger.dart'; /// Runs the migration from the old Android app to the new cross platform app. /// The runner essentially fetches all data from Realm and pipes it to Firebase. @@ -11,8 +12,10 @@ class MigrationRunner { final BookRepository _bookSource; final PageRecordRepository _pageRecordTarget; final PageRecordRepository _pageRecordSource; + final Logger _logger; MigrationRunner( + this._logger, BookRepository bookTarget, BookRepository bookSource, PageRecordRepository pageRecordTarget, @@ -70,7 +73,7 @@ class MigrationRunner { await _bookTarget.create(book); migratedBooks++; } catch (e) { - // TODO Await logging facility + _logger.f(e); } } @@ -99,7 +102,7 @@ class MigrationRunner { ); migratedPageRecords++; } catch (e) { - // TODO Await logging facility + _logger.f(e); } } diff --git a/lib/src/data/book/realm/realm_book.g.dart b/lib/src/data/book/realm/realm_book.g.dart index 9b4230d..5630a6d 100644 --- a/lib/src/data/book/realm/realm_book.g.dart +++ b/lib/src/data/book/realm/realm_book.g.dart @@ -323,7 +323,7 @@ class RealmPageRecord extends _RealmPageRecord @override String? get recordId => - RealmObjectBase.get(this, 'recordId') as String; + RealmObjectBase.get(this, 'recordId') as String?; @override set recordId(String? value) => RealmObjectBase.set(this, 'recordId', value); @@ -361,7 +361,8 @@ class RealmPageRecord extends _RealmPageRecord RealmObjectBase.registerFactory(RealmPageRecord._); return const SchemaObject( ObjectType.realmObject, RealmPageRecord, 'RealmPageRecord', [ - SchemaProperty('recordId', RealmPropertyType.string, primaryKey: true, optional: true), + SchemaProperty('recordId', RealmPropertyType.string, + optional: true, primaryKey: true), SchemaProperty('bookId', RealmPropertyType.int), SchemaProperty('fromPage', RealmPropertyType.int), SchemaProperty('toPage', RealmPropertyType.int), diff --git a/lib/src/data/book/realm/realm_book_repository.dart b/lib/src/data/book/realm/realm_book_repository.dart index 623d99b..b299d59 100644 --- a/lib/src/data/book/realm/realm_book_repository.dart +++ b/lib/src/data/book/realm/realm_book_repository.dart @@ -32,7 +32,7 @@ class RealmBookRepository implements BookRepository { } @override - Future delete(BookId id) { + Future delete(String id) { // Not required. throw UnimplementedError(); } @@ -44,7 +44,7 @@ class RealmBookRepository implements BookRepository { } @override - Future getBook(BookId id) { + Future getBook(String id) { // Not required. throw UnimplementedError(); } @@ -78,7 +78,7 @@ class RealmBookRepository implements BookRepository { .where(_hasRealmBookLabelRequiredData) .map( (rbl) => BookLabel( - bookId: rbl.bookId.toString(), + id: rbl.bookId.toString(), title: rbl.title!, hexColor: rbl.hexColor!, ), @@ -118,7 +118,43 @@ class RealmBookRepository implements BookRepository { } @override - Future updateCurrentPage(BookId bookId, int currentPage) { + Future updateCurrentPage(String bookId, int currentPage) { + // Not required. + throw UnimplementedError(); + } + + @override + Future addLabelToBook(String bookId, BookLabel label) { + // Not required. + throw UnimplementedError(); + } + + @override + Future addToForLater(Book book) { + // Not required. + throw UnimplementedError(); + } + + @override + Future addToRead(Book book) { + // Not required. + throw UnimplementedError(); + } + + @override + Future addToReading(Book book) { + // Not required. + throw UnimplementedError(); + } + + @override + Future addToWishlist(Book book) { + // Not required. + throw UnimplementedError(); + } + + @override + Future removeLabelFromBook(String bookId, String labelId) { // Not required. throw UnimplementedError(); } From 6c9d858a1995d400a2ec3f794f9503c8954292b8 Mon Sep 17 00:00:00 2001 From: Martin Macheiner Date: Wed, 1 Nov 2023 20:40:34 +0100 Subject: [PATCH 06/13] Save migration status to SettingsRepository --- .../data/book/migration/migration_runner.dart | 25 ++++++++++++------- .../data/settings/settings_repository.dart | 12 ++++++--- ...hared_preferences_settings_repository.dart | 21 ++++++++++++++++ 3 files changed, 45 insertions(+), 13 deletions(-) diff --git a/lib/src/data/book/migration/migration_runner.dart b/lib/src/data/book/migration/migration_runner.dart index ed85636..0053306 100644 --- a/lib/src/data/book/migration/migration_runner.dart +++ b/lib/src/data/book/migration/migration_runner.dart @@ -1,8 +1,11 @@ +import 'dart:async'; + import 'package:dantex/src/data/book/book_repository.dart'; import 'package:dantex/src/data/book/entity/book.dart'; import 'package:dantex/src/data/book/entity/page_record.dart'; import 'package:dantex/src/data/book/migration/migration_score.dart'; import 'package:dantex/src/data/book/page_record_repository.dart'; +import 'package:dantex/src/data/settings/settings_repository.dart'; import 'package:logger/logger.dart'; /// Runs the migration from the old Android app to the new cross platform app. @@ -13,9 +16,11 @@ class MigrationRunner { final PageRecordRepository _pageRecordTarget; final PageRecordRepository _pageRecordSource; final Logger _logger; + final SettingsRepository _settingsRepository; MigrationRunner( this._logger, + this._settingsRepository, BookRepository bookTarget, BookRepository bookSource, PageRecordRepository pageRecordTarget, @@ -29,19 +34,21 @@ class MigrationRunner { Future migrateIfRequired() async { final MigrationStatus status = await _migrationStatus(); - if (status == MigrationStatus.required) { - final MigrationScore score = await _migrate(); - final MigrationStatus newStatus = score.statusFromScore(); - _updateMigrationStatus(newStatus); - return newStatus; + // If status is not required, return the current status. + if (status != MigrationStatus.required) { + return status; } - return status; + final MigrationScore score = await _migrate(); + final MigrationStatus newStatus = score.statusFromScore(); + _updateMigrationStatus(newStatus); + return newStatus; } + /// Load migration status from settings. If nothing is saved, return required. Future _migrationStatus() async { - // TODO Load from SettingsRepository - return MigrationStatus.required; + return _settingsRepository.getRealmMigrationStatus() ?? + MigrationStatus.required; } Future _migrate() async { @@ -113,6 +120,6 @@ class MigrationRunner { } void _updateMigrationStatus(MigrationStatus newStatus) { - // TODO Save to SettingsRepository + unawaited(_settingsRepository.setRealmMigrationStatus(newStatus)); } } diff --git a/lib/src/data/settings/settings_repository.dart b/lib/src/data/settings/settings_repository.dart index b289312..1fc054d 100644 --- a/lib/src/data/settings/settings_repository.dart +++ b/lib/src/data/settings/settings_repository.dart @@ -1,21 +1,25 @@ import 'package:dantex/src/data/book/book_sort_strategy.dart'; import 'package:dantex/src/ui/timeline/timeline_sort.dart'; +import 'package:dantex/src/data/book/migration/migration_score.dart'; import 'package:flutter/material.dart'; abstract class SettingsRepository { - void setThemeMode(ThemeMode mode); + Future setThemeMode(ThemeMode mode); ThemeMode getThemeMode(); Stream observeThemeMode(); - void setSortingStrategy(BookSortStrategy strategy); + Future setSortingStrategy(BookSortStrategy strategy); BookSortStrategy getSortingStrategy(); - void setIsTrackingEnabled({required bool isTrackingEnabled}); + Future setIsTrackingEnabled({required bool isTrackingEnabled}); bool isTrackingEnabled(); - void setIsRandomBooksEnabled({required bool isRandomBooksEnabled}); + Future setIsRandomBooksEnabled({required bool isRandomBooksEnabled}); bool isRandomBooksEnabled(); void setTimelineSortStrategy(TimelineSortStrategy sort); TimelineSortStrategy getTimelineSortStrategy(); + + Future setRealmMigrationStatus(MigrationStatus newStatus); + MigrationStatus? getRealmMigrationStatus(); } diff --git a/lib/src/data/settings/shared_preferences_settings_repository.dart b/lib/src/data/settings/shared_preferences_settings_repository.dart index f88e411..0635f90 100644 --- a/lib/src/data/settings/shared_preferences_settings_repository.dart +++ b/lib/src/data/settings/shared_preferences_settings_repository.dart @@ -1,4 +1,5 @@ import 'package:dantex/src/data/book/book_sort_strategy.dart'; +import 'package:dantex/src/data/book/migration/migration_score.dart'; import 'package:dantex/src/data/settings/settings_repository.dart'; import 'package:dantex/src/ui/timeline/timeline_sort.dart'; import 'package:flutter/material.dart'; @@ -13,6 +14,7 @@ class SharedPreferencesSettingsRepository implements SettingsRepository { static const _keyThemeMode = 'key_theme_mode'; static const _keySortStrategy = 'key_sort_strategy'; static const _keyTimelineSortStrategy = 'key_timeline_sort_strategy'; + static const _keyRealmMigrationStatus = 'key_realm_migration'; final BehaviorSubject _themeModeSubject = BehaviorSubject(); @@ -89,4 +91,23 @@ class SharedPreferencesSettingsRepository implements SettingsRepository { ) async { await _sp.setInt(_keyTimelineSortStrategy, sortStrategy.index); } + + @override + MigrationStatus? getRealmMigrationStatus() { + final String? rawRealmMigrationStatus = _sp.getString( + _keyRealmMigrationStatus, + ); + if (rawRealmMigrationStatus == null) { + return null; + } + + return MigrationStatus.values.firstWhere( + (element) => element.name == rawRealmMigrationStatus, + ); + } + + @override + Future setRealmMigrationStatus(MigrationStatus newStatus) async { + await _sp.setString(_keyRealmMigrationStatus, newStatus.name); + } } From 5c331f9014e5c708733dbd59d28ec739b5f2e7da Mon Sep 17 00:00:00 2001 From: Martin Macheiner Date: Wed, 1 Nov 2023 21:18:11 +0100 Subject: [PATCH 07/13] Implement page records repository --- lib/src/data/book/entity/page_record.dart | 30 ++++--- .../book/firebase_book_label_repository.dart | 8 +- .../data/book/firebase_book_repository.dart | 7 +- .../book/firebase_page_record_repository.dart | 90 +++++++++++++++---- lib/src/data/book/firebase_utils.dart | 7 ++ .../data/book/migration/migration_runner.dart | 2 +- lib/src/data/book/page_record_repository.dart | 4 +- .../realm/realm_page_record_repository.dart | 7 +- 8 files changed, 105 insertions(+), 50 deletions(-) create mode 100644 lib/src/data/book/firebase_utils.dart diff --git a/lib/src/data/book/entity/page_record.dart b/lib/src/data/book/entity/page_record.dart index ca6190d..1ef4641 100644 --- a/lib/src/data/book/entity/page_record.dart +++ b/lib/src/data/book/entity/page_record.dart @@ -1,17 +1,23 @@ -class PageRecord { - final String bookId; - final int fromPage; - final int toPage; - final int timestamp; +import 'package:freezed_annotation/freezed_annotation.dart'; - PageRecord({ - required this.bookId, - required this.fromPage, - required this.toPage, - required this.timestamp, - }); +part 'page_record.freezed.dart'; +part 'page_record.g.dart'; - DateTime get dateTime => DateTime.fromMillisecondsSinceEpoch(timestamp); +@freezed +class PageRecord with _$PageRecord { + + const PageRecord._(); + + const factory PageRecord({ + required String id, + required String bookId, + required int fromPage, + required int toPage, + required DateTime dateTime, + }) = _PageRecord; int get diffPages => toPage - fromPage; + + factory PageRecord.fromJson(Map json) => + _$PageRecordFromJson(json); } diff --git a/lib/src/data/book/firebase_book_label_repository.dart b/lib/src/data/book/firebase_book_label_repository.dart index 27e7a39..e529886 100644 --- a/lib/src/data/book/firebase_book_label_repository.dart +++ b/lib/src/data/book/firebase_book_label_repository.dart @@ -49,10 +49,4 @@ class FirebaseBookLabelRepository implements BookLabelRepository { final user = _fbAuth.currentUser!.uid; return _fbDb.ref('users/$user/labels/'); } -} - -extension DataSnapshotExtension on DataSnapshot { - Map? toMap() { - return (value != null) ? (value as Map).cast() : null; - } -} +} \ No newline at end of file diff --git a/lib/src/data/book/firebase_book_repository.dart b/lib/src/data/book/firebase_book_repository.dart index e32ab9d..451a8a7 100644 --- a/lib/src/data/book/firebase_book_repository.dart +++ b/lib/src/data/book/firebase_book_repository.dart @@ -3,6 +3,7 @@ import 'package:dantex/src/data/book/entity/book.dart'; import 'package:dantex/src/data/book/entity/book_label.dart'; import 'package:dantex/src/data/book/entity/book_state.dart'; import 'package:dantex/src/data/book/search_criteria.dart'; +import 'package:dantex/src/data/book/firebase_utils.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_database/firebase_database.dart'; import 'package:rxdart/rxdart.dart'; @@ -217,9 +218,3 @@ class FirebaseBookRepository implements BookRepository { } } } - -extension DataSnapshotExtension on DataSnapshot { - Map? toMap() { - return (value != null) ? (value as Map).cast() : null; - } -} diff --git a/lib/src/data/book/firebase_page_record_repository.dart b/lib/src/data/book/firebase_page_record_repository.dart index 21bbbbf..cd57fcf 100644 --- a/lib/src/data/book/firebase_page_record_repository.dart +++ b/lib/src/data/book/firebase_page_record_repository.dart @@ -1,24 +1,29 @@ import 'package:dantex/src/data/book/entity/page_record.dart'; +import 'package:dantex/src/data/book/firebase_utils.dart'; import 'package:dantex/src/data/book/page_record_repository.dart'; +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:firebase_database/firebase_database.dart'; -// TODO Implement this class class FirebasePageRecordRepository implements PageRecordRepository { + final FirebaseAuth _fbAuth; + final FirebaseDatabase _fbDb; + + FirebasePageRecordRepository(this._fbAuth, this._fbDb); + @override Future> allPageRecords() { - // TODO: implement allPageRecords - throw UnimplementedError(); + return _rootRef().get().then(_fromDataSnapshot); } @override - Future deleteAllPageRecordsForBookId(String bookId) { - // TODO: implement deleteAllPageRecordsForBookId - throw UnimplementedError(); + Future deleteAllPageRecordsForBookId(String bookId) async { + final List records = await pageRecordsForBook(bookId); + records.forEach(deletePageRecord); } @override - Future deletePageRecordForBook(PageRecord pageRecord) { - // TODO: implement deletePageRecordForBook - throw UnimplementedError(); + Future deletePageRecord(PageRecord pageRecord) async { + await _rootRef().child(pageRecord.id).remove(); } @override @@ -26,22 +31,69 @@ class FirebasePageRecordRepository implements PageRecordRepository { String bookId, int fromPage, int toPage, - int nowInMillis, - ) { - // TODO: implement insertPageRecordForBookId - throw UnimplementedError(); + DateTime now, + ) async { + final DatabaseReference newRef = _rootRef().push(); + + final Map data = PageRecord( + id: newRef.key!, + bookId: bookId, + fromPage: fromPage, + toPage: toPage, + dateTime: now, + ).toJson(); + + return newRef.set(data); } @override - Future> pageRecordsForBook(String bookId) { - // TODO: implement pageRecordsForBook - throw UnimplementedError(); + Future> pageRecordsForBook(String bookId) async { + return _rootRef() + .equalTo('bookId', key: bookId) + .get() + .then(_fromDataSnapshot); } @override Future updatePageRecord( - PageRecord pageRecord, int? fromPage, int? toPage) { - // TODO: implement updatePageRecord - throw UnimplementedError(); + PageRecord pageRecord, + int? fromPage, + int? toPage, + ) { + return _rootRef().child(pageRecord.id).set( + pageRecord.copyWith( + // Use currently set values, if values are null. + fromPage: fromPage ?? pageRecord.fromPage, + toPage: toPage ?? pageRecord.toPage, + ), + ); + } + + List _fromDataSnapshot(DataSnapshot snapshot) { + final Map? data = snapshot.toMap(); + + if (data == null) { + return []; + } + + return data + .map( + (key, value) { + final Map pageRecordMap = + (value as Map).cast(); + final PageRecord pageRecord = PageRecord.fromJson(pageRecordMap); + return MapEntry(key, pageRecord); + }, + ) + .values + .toList(); + } + + DatabaseReference _rootRef() { + // At this point we can assume that the customer is already logged in, + // even as anonymous user + final user = _fbAuth.currentUser!.uid; + return _fbDb.ref('users/$user/page-records'); } } + diff --git a/lib/src/data/book/firebase_utils.dart b/lib/src/data/book/firebase_utils.dart new file mode 100644 index 0000000..21e17c1 --- /dev/null +++ b/lib/src/data/book/firebase_utils.dart @@ -0,0 +1,7 @@ +import 'package:firebase_database/firebase_database.dart'; + +extension DataSnapshotExtension on DataSnapshot { + Map? toMap() { + return (value != null) ? (value as Map).cast() : null; + } +} diff --git a/lib/src/data/book/migration/migration_runner.dart b/lib/src/data/book/migration/migration_runner.dart index 0053306..fc49a96 100644 --- a/lib/src/data/book/migration/migration_runner.dart +++ b/lib/src/data/book/migration/migration_runner.dart @@ -105,7 +105,7 @@ class MigrationRunner { record.bookId, record.fromPage, record.toPage, - record.timestamp, + record.dateTime, ); migratedPageRecords++; } catch (e) { diff --git a/lib/src/data/book/page_record_repository.dart b/lib/src/data/book/page_record_repository.dart index 510738a..d68e58c 100644 --- a/lib/src/data/book/page_record_repository.dart +++ b/lib/src/data/book/page_record_repository.dart @@ -5,7 +5,7 @@ abstract class PageRecordRepository { String bookId, int fromPage, int toPage, - int nowInMillis, + DateTime now, ); Future updatePageRecord( @@ -14,7 +14,7 @@ abstract class PageRecordRepository { int? toPage, ); - Future deletePageRecordForBook(PageRecord pageRecord); + Future deletePageRecord(PageRecord pageRecord); Future deleteAllPageRecordsForBookId(String bookId); diff --git a/lib/src/data/book/realm/realm_page_record_repository.dart b/lib/src/data/book/realm/realm_page_record_repository.dart index 2377bab..4e39e37 100644 --- a/lib/src/data/book/realm/realm_page_record_repository.dart +++ b/lib/src/data/book/realm/realm_page_record_repository.dart @@ -15,10 +15,11 @@ class RealmPageRecordRepository implements PageRecordRepository { .where(_hasPageRecordRequiredData) .map( (rpg) => PageRecord( + id: rpg.recordId ?? 'not-available', bookId: rpg.bookId.toString(), fromPage: rpg.fromPage, toPage: rpg.toPage, - timestamp: rpg.timestamp, + dateTime: DateTime.fromMillisecondsSinceEpoch(rpg.timestamp), ), ) .toList(); @@ -35,7 +36,7 @@ class RealmPageRecordRepository implements PageRecordRepository { } @override - Future deletePageRecordForBook(PageRecord pageRecord) { + Future deletePageRecord(PageRecord pageRecord) { // Not required. throw UnimplementedError(); } @@ -45,7 +46,7 @@ class RealmPageRecordRepository implements PageRecordRepository { String bookId, int fromPage, int toPage, - int nowInMillis, + DateTime now, ) { // Not required. throw UnimplementedError(); From 65214f14d54eeefd8f0d55e357037b23bab4e5b2 Mon Sep 17 00:00:00 2001 From: Martin Macheiner Date: Thu, 2 Nov 2023 19:32:31 +0100 Subject: [PATCH 08/13] Wire up dependency injection for migration runner --- .../book/realm/realm_book_repository.dart | 27 ++++------ lib/src/providers/migration.dart | 49 +++++++++++++++++++ lib/src/providers/repository.dart | 11 +++++ 3 files changed, 70 insertions(+), 17 deletions(-) create mode 100644 lib/src/providers/migration.dart diff --git a/lib/src/data/book/realm/realm_book_repository.dart b/lib/src/data/book/realm/realm_book_repository.dart index b299d59..664a088 100644 --- a/lib/src/data/book/realm/realm_book_repository.dart +++ b/lib/src/data/book/realm/realm_book_repository.dart @@ -8,19 +8,6 @@ import 'package:realm/realm.dart'; class RealmBookRepository implements BookRepository { - /* TODO Inject this - final Realm _realm = Realm( - Configuration.local( - [ - RealmBook.schema, - RealmBookLabel.schema, - RealmPageRecord.schema, - ], - schemaVersion: 9, // 9 is the current schema version of the old app - ), - ); - */ - final Realm _realm; RealmBookRepository(this._realm); @@ -90,13 +77,19 @@ class RealmBookRepository implements BookRepository { } bool _hasRealmBookRequiredData(RealmBook book) { - // TODO Check this - return true; + return + book.id > -1 && + book.title != null && + book.subTitle != null && + book.author != null && + book.publishedDate != null && + book.language != null; } bool _hasRealmBookLabelRequiredData(RealmBookLabel bookLabel) { - // TODO Check this - return true; + return bookLabel.bookId > -1 && + bookLabel.title?.isNotEmpty == true && + bookLabel.hexColor?.isNotEmpty == true; } @override diff --git a/lib/src/providers/migration.dart b/lib/src/providers/migration.dart new file mode 100644 index 0000000..b116606 --- /dev/null +++ b/lib/src/providers/migration.dart @@ -0,0 +1,49 @@ +import 'package:dantex/src/data/book/book_repository.dart'; +import 'package:dantex/src/data/book/migration/migration_runner.dart'; +import 'package:dantex/src/data/book/page_record_repository.dart'; +import 'package:dantex/src/data/book/realm/realm_book.dart'; +import 'package:dantex/src/data/book/realm/realm_book_repository.dart'; +import 'package:dantex/src/data/book/realm/realm_page_record_repository.dart'; +import 'package:dantex/src/providers/repository.dart'; +import 'package:dantex/src/providers/service.dart'; +import 'package:realm/realm.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'migration.g.dart'; + +@riverpod +MigrationRunner migrationRunner(MigrationRunnerRef ref) => MigrationRunner( + ref.watch(loggerProvider), + ref.watch(settingsRepositoryProvider), + ref.watch(bookRepositoryProvider), + ref.watch(_legacyBookRepositoryProvider), + ref.watch(pageRecordRepositoryProvider), + ref.watch(_legacyPageRecordRepositoryProvider), + ); + +@riverpod +BookRepository _legacyBookRepository(_LegacyBookRepositoryRef ref) => + RealmBookRepository( + ref.watch(_realmProvider), + ); + +@riverpod +PageRecordRepository _legacyPageRecordRepository( + _LegacyPageRecordRepositoryRef ref, +) => + RealmPageRecordRepository( + ref.watch(_realmProvider), + ); + +@riverpod +Realm _realm(_RealmRef ref) => Realm( + Configuration.local( + [ + RealmBook.schema, + RealmBookLabel.schema, + RealmPageRecord.schema, + ], + // 9 is the current schema version of the old app. + schemaVersion: 9, + ), + ); diff --git a/lib/src/providers/repository.dart b/lib/src/providers/repository.dart index 698feb4..af5ed0d 100644 --- a/lib/src/providers/repository.dart +++ b/lib/src/providers/repository.dart @@ -4,6 +4,8 @@ import 'package:dantex/src/data/book/firebase_book_label_repository.dart'; import 'package:dantex/src/data/book/firebase_book_repository.dart'; import 'package:dantex/src/data/recommendations/default_recommendations_repository.dart'; import 'package:dantex/src/data/recommendations/recommendations_repository.dart'; +import 'package:dantex/src/data/book/firebase_page_record_repository.dart'; +import 'package:dantex/src/data/book/page_record_repository.dart'; import 'package:dantex/src/data/settings/settings_repository.dart'; import 'package:dantex/src/data/settings/shared_preferences_settings_repository.dart'; import 'package:dantex/src/providers/api.dart'; @@ -34,6 +36,15 @@ BookLabelRepository bookLabelRepository(BookLabelRepositoryRef ref) => ref.watch(firebaseDatabaseProvider), ); +@riverpod +PageRecordRepository pageRecordRepository( + PageRecordRepositoryRef ref, +) => + FirebasePageRecordRepository( + ref.watch(firebaseAuthProvider), + ref.watch(firebaseDatabaseProvider), + ); + @riverpod SettingsRepository settingsRepository(SettingsRepositoryRef ref) => SharedPreferencesSettingsRepository( From 308d6f12a856cf01c14c5974e8d14c453d920e6d Mon Sep 17 00:00:00 2001 From: Martin Macheiner Date: Sun, 7 Jan 2024 11:38:24 +0100 Subject: [PATCH 09/13] Run plugins when user is authenticated --- .../firebase_authentication_repository.dart | 19 +++++++++++++++++-- .../on_user_authenticated_plugin.dart | 8 ++++++++ .../dummy_on_user_authenticated_plugin.dart | 12 ++++++++++++ lib/src/providers/authentication.dart | 7 +++++++ 4 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 lib/src/data/authentication/on_user_authenticated_plugin.dart create mode 100644 lib/src/data/authentication/plugin/dummy_on_user_authenticated_plugin.dart diff --git a/lib/src/data/authentication/firebase_authentication_repository.dart b/lib/src/data/authentication/firebase_authentication_repository.dart index 1104065..150f9c5 100644 --- a/lib/src/data/authentication/firebase_authentication_repository.dart +++ b/lib/src/data/authentication/firebase_authentication_repository.dart @@ -1,13 +1,18 @@ import 'package:collection/collection.dart'; import 'package:dantex/src/data/authentication/authentication_repository.dart'; import 'package:dantex/src/data/authentication/entity/dante_user.dart'; +import 'package:dantex/src/data/authentication/on_user_authenticated_plugin.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/foundation.dart' show kIsWeb; class FirebaseAuthenticationRepository implements AuthenticationRepository { final FirebaseAuth _fbAuth; + final List _onUserAuthenticatedPlugins; - FirebaseAuthenticationRepository(this._fbAuth); + FirebaseAuthenticationRepository( + this._fbAuth, + this._onUserAuthenticatedPlugins, + ); @override Stream get authStateChanges => @@ -35,7 +40,7 @@ class FirebaseAuthenticationRepository implements AuthenticationRepository { return null; } - return DanteUser( + final DanteUser user = DanteUser( givenName: fbUser.displayName, displayName: fbUser.displayName, email: fbUser.email, @@ -44,6 +49,10 @@ class FirebaseAuthenticationRepository implements AuthenticationRepository { userId: fbUser.uid, source: _retrieveAuthenticationSource(fbUser), ); + + _callOnUserAuthenticatedPlugins(user); + + return user; } AuthenticationSource _retrieveAuthenticationSource(User user) { @@ -58,6 +67,12 @@ class FirebaseAuthenticationRepository implements AuthenticationRepository { } } + void _callOnUserAuthenticatedPlugins(DanteUser user) { + for (var plugin in _onUserAuthenticatedPlugins) { + plugin.onUserAuthenticated(user); + } + } + bool _isAnonymous(User user) => user.isAnonymous; bool _isGoogleUser(User user) => diff --git a/lib/src/data/authentication/on_user_authenticated_plugin.dart b/lib/src/data/authentication/on_user_authenticated_plugin.dart new file mode 100644 index 0000000..7135da6 --- /dev/null +++ b/lib/src/data/authentication/on_user_authenticated_plugin.dart @@ -0,0 +1,8 @@ + + +import 'package:dantex/src/data/authentication/entity/dante_user.dart'; + +abstract class OnUserAuthenticatedPlugin { + + void onUserAuthenticated(DanteUser user); +} \ No newline at end of file diff --git a/lib/src/data/authentication/plugin/dummy_on_user_authenticated_plugin.dart b/lib/src/data/authentication/plugin/dummy_on_user_authenticated_plugin.dart new file mode 100644 index 0000000..f9ef58b --- /dev/null +++ b/lib/src/data/authentication/plugin/dummy_on_user_authenticated_plugin.dart @@ -0,0 +1,12 @@ + + +import 'package:dantex/src/data/authentication/entity/dante_user.dart'; +import 'package:dantex/src/data/authentication/on_user_authenticated_plugin.dart'; + +class DummyOnUserAuthenticatedPlugin extends OnUserAuthenticatedPlugin { + @override + void onUserAuthenticated(DanteUser user) { + print('User ${user.email} authenticated'); + } + +} \ No newline at end of file diff --git a/lib/src/providers/authentication.dart b/lib/src/providers/authentication.dart index 5d63fa7..ce59b89 100644 --- a/lib/src/providers/authentication.dart +++ b/lib/src/providers/authentication.dart @@ -1,6 +1,8 @@ import 'package:dantex/src/data/authentication/authentication_repository.dart'; import 'package:dantex/src/data/authentication/entity/dante_user.dart'; import 'package:dantex/src/data/authentication/firebase_authentication_repository.dart'; +import 'package:dantex/src/data/authentication/on_user_authenticated_plugin.dart'; +import 'package:dantex/src/data/authentication/plugin/dummy_on_user_authenticated_plugin.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_database/firebase_database.dart'; @@ -33,8 +35,13 @@ AuthenticationRepository authenticationRepository( ) => FirebaseAuthenticationRepository( ref.watch(firebaseAuthProvider), + _provideOnUserAuthenticatedPlugins(), ); +List _provideOnUserAuthenticatedPlugins() => [ + DummyOnUserAuthenticatedPlugin(), + ]; + @riverpod Future user(UserRef ref) { // Rebuild this provider when there is an auth state change. From c4e3be6c7e8f6cab4fe5a0f77f84e138367d79f7 Mon Sep 17 00:00:00 2001 From: Martin Macheiner Date: Sat, 13 Jan 2024 21:10:15 +0100 Subject: [PATCH 10/13] Resolve merge conflicts --- lib/src/data/book/book_repository.dart | 4 +- .../book/firebase_book_label_repository.dart | 1 + .../data/book/firebase_book_repository.dart | 4 +- ...ation_score.dart => migration_result.dart} | 4 +- .../data/book/migration/migration_runner.dart | 10 +- lib/src/data/book/realm/realm_book.g.dart | 1 + .../book/realm/realm_book_repository.dart | 111 +++--- .../data/settings/settings_repository.dart | 2 +- ...hared_preferences_settings_repository.dart | 2 +- pubspec.lock | 316 +++++++++++------- ...rebase_authentication_repository_test.dart | 69 +++- ..._authentication_repository_test.mocks.dart | 8 +- .../firebase_book_repository_test.mocks.dart | 14 +- 13 files changed, 327 insertions(+), 219 deletions(-) rename lib/src/data/book/migration/{migration_score.dart => migration_result.dart} (93%) diff --git a/lib/src/data/book/book_repository.dart b/lib/src/data/book/book_repository.dart index 3a4a162..6e7f9c7 100644 --- a/lib/src/data/book/book_repository.dart +++ b/lib/src/data/book/book_repository.dart @@ -6,9 +6,7 @@ import 'package:dantex/src/data/book/search_criteria.dart'; abstract class BookRepository { Stream> getBooksForState(BookState state); - Stream> listenAllBooks(); - - Future> getAllBooks(); + Stream> getAllBooks(); Stream getBook(String id); diff --git a/lib/src/data/book/firebase_book_label_repository.dart b/lib/src/data/book/firebase_book_label_repository.dart index e529886..0d716af 100644 --- a/lib/src/data/book/firebase_book_label_repository.dart +++ b/lib/src/data/book/firebase_book_label_repository.dart @@ -1,5 +1,6 @@ import 'package:dantex/src/data/book/book_label_repository.dart'; import 'package:dantex/src/data/book/entity/book_label.dart'; +import 'package:dantex/src/data/book/firebase_utils.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_database/firebase_database.dart'; diff --git a/lib/src/data/book/firebase_book_repository.dart b/lib/src/data/book/firebase_book_repository.dart index 451a8a7..836ccdd 100644 --- a/lib/src/data/book/firebase_book_repository.dart +++ b/lib/src/data/book/firebase_book_repository.dart @@ -2,8 +2,8 @@ import 'package:dantex/src/data/book/book_repository.dart'; import 'package:dantex/src/data/book/entity/book.dart'; import 'package:dantex/src/data/book/entity/book_label.dart'; import 'package:dantex/src/data/book/entity/book_state.dart'; -import 'package:dantex/src/data/book/search_criteria.dart'; import 'package:dantex/src/data/book/firebase_utils.dart'; +import 'package:dantex/src/data/book/search_criteria.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_database/firebase_database.dart'; import 'package:rxdart/rxdart.dart'; @@ -70,7 +70,7 @@ class FirebaseBookRepository implements BookRepository { } @override - Stream getBook(String id) async { + Stream getBook(String id) { return _booksRef().child(id).onValue.map((DatabaseEvent event) { final Map? data = event.snapshot.toMap(); diff --git a/lib/src/data/book/migration/migration_score.dart b/lib/src/data/book/migration/migration_result.dart similarity index 93% rename from lib/src/data/book/migration/migration_score.dart rename to lib/src/data/book/migration/migration_result.dart index 4379127..741c39f 100644 --- a/lib/src/data/book/migration/migration_score.dart +++ b/lib/src/data/book/migration/migration_result.dart @@ -1,10 +1,10 @@ -class MigrationScore { +class MigrationResult { final int booksToMigrate; final int migratedBooks; final int pageRecordsToMigrate; final int migratedPageRecords; - MigrationScore({ + MigrationResult({ required this.booksToMigrate, required this.migratedBooks, required this.pageRecordsToMigrate, diff --git a/lib/src/data/book/migration/migration_runner.dart b/lib/src/data/book/migration/migration_runner.dart index fc49a96..6f34870 100644 --- a/lib/src/data/book/migration/migration_runner.dart +++ b/lib/src/data/book/migration/migration_runner.dart @@ -3,7 +3,7 @@ import 'dart:async'; import 'package:dantex/src/data/book/book_repository.dart'; import 'package:dantex/src/data/book/entity/book.dart'; import 'package:dantex/src/data/book/entity/page_record.dart'; -import 'package:dantex/src/data/book/migration/migration_score.dart'; +import 'package:dantex/src/data/book/migration/migration_result.dart'; import 'package:dantex/src/data/book/page_record_repository.dart'; import 'package:dantex/src/data/settings/settings_repository.dart'; import 'package:logger/logger.dart'; @@ -39,7 +39,7 @@ class MigrationRunner { return status; } - final MigrationScore score = await _migrate(); + final MigrationResult score = await _migrate(); final MigrationStatus newStatus = score.statusFromScore(); _updateMigrationStatus(newStatus); return newStatus; @@ -51,7 +51,7 @@ class MigrationRunner { MigrationStatus.required; } - Future _migrate() async { + Future _migrate() async { final ({int migratedBooks, int booksToMigrate}) booksStat = await _migrateBooks(); @@ -60,7 +60,7 @@ class MigrationRunner { int pageRecordsToMigrate }) pageRecordsStats = await _migratePageRecords(); - return MigrationScore( + return MigrationResult( booksToMigrate: booksStat.booksToMigrate, migratedBooks: booksStat.migratedBooks, pageRecordsToMigrate: pageRecordsStats.pageRecordsToMigrate, @@ -70,7 +70,7 @@ class MigrationRunner { /// Returns no. of migrated instances and potential no. of migrations Future<({int migratedBooks, int booksToMigrate})> _migrateBooks() async { - final List booksToMigrate = await _bookSource.getAllBooks(); + final List booksToMigrate = await _bookSource.getAllBooks().first; int migratedBooks = 0; for (int i = 0; i < booksToMigrate.length; i++) { diff --git a/lib/src/data/book/realm/realm_book.g.dart b/lib/src/data/book/realm/realm_book.g.dart index 5630a6d..15ea08a 100644 --- a/lib/src/data/book/realm/realm_book.g.dart +++ b/lib/src/data/book/realm/realm_book.g.dart @@ -6,6 +6,7 @@ part of 'realm_book.dart'; // RealmObjectGenerator // ************************************************************************** +// ignore_for_file: type=lint class RealmBook extends _RealmBook with RealmEntity, RealmObjectBase, RealmObject { static var _defaultsSet = false; diff --git a/lib/src/data/book/realm/realm_book_repository.dart b/lib/src/data/book/realm/realm_book_repository.dart index 664a088..3066657 100644 --- a/lib/src/data/book/realm/realm_book_repository.dart +++ b/lib/src/data/book/realm/realm_book_repository.dart @@ -1,13 +1,13 @@ -import 'package:dantex/src/core/book_core.dart'; import 'package:dantex/src/data/book/book_repository.dart'; import 'package:dantex/src/data/book/entity/book.dart'; import 'package:dantex/src/data/book/entity/book_label.dart'; import 'package:dantex/src/data/book/entity/book_state.dart'; import 'package:dantex/src/data/book/realm/realm_book.dart'; +import 'package:dantex/src/data/book/search_criteria.dart'; import 'package:realm/realm.dart'; -class RealmBookRepository implements BookRepository { +class RealmBookRepository implements BookRepository { final Realm _realm; RealmBookRepository(this._realm); @@ -25,65 +25,60 @@ class RealmBookRepository implements BookRepository { } @override - Stream> listenAllBooks() { - // Not required. - throw UnimplementedError(); - } - - @override - Future getBook(String id) { + Stream getBook(String id) { // Not required. throw UnimplementedError(); } @override - Future> getAllBooks() async { - return _realm - .all() - .where(_hasRealmBookRequiredData) - .map( - (rb) => Book( - id: rb.id.toString(), - title: rb.title!, - subTitle: rb.subTitle!, - author: rb.author!, - state: BookState.values[rb.ordinalState], - pageCount: rb.pageCount, - currentPage: rb.currentPage, - publishedDate: rb.publishedDate!, - position: rb.position, - isbn: rb.isbn!, - thumbnailAddress: rb.thumbnailAddress, - startDate: rb.startDate, - endDate: rb.endDate, - wishlistDate: rb.wishlistDate, - language: rb.language!, - rating: rb.rating, - notes: rb.notes, - summary: rb.summary, - labels: rb.labels - .where(_hasRealmBookLabelRequiredData) - .map( - (rbl) => BookLabel( - id: rbl.bookId.toString(), - title: rbl.title!, - hexColor: rbl.hexColor!, - ), - ) - .toList(), - ), - ) - .toList(); + Stream> getAllBooks() { + return Stream.value( + _realm + .all() + .where(_hasRealmBookRequiredData) + .map( + (rb) => Book( + id: rb.id.toString(), + title: rb.title!, + subTitle: rb.subTitle!, + author: rb.author!, + state: BookState.values[rb.ordinalState], + pageCount: rb.pageCount, + currentPage: rb.currentPage, + publishedDate: rb.publishedDate!, + position: rb.position, + isbn: rb.isbn!, + thumbnailAddress: rb.thumbnailAddress, + startDate: DateTime.fromMillisecondsSinceEpoch(rb.startDate), + endDate: DateTime.fromMillisecondsSinceEpoch(rb.endDate), + forLaterDate: DateTime.fromMillisecondsSinceEpoch(rb.wishlistDate), + language: rb.language!, + rating: rb.rating, + notes: rb.notes, + summary: rb.summary, + labels: rb.labels + .where(_hasRealmBookLabelRequiredData) + .map( + (rbl) => BookLabel( + id: rbl.bookId.toString(), + title: rbl.title!, + hexColor: rbl.hexColor!, + ), + ) + .toList(), + ), + ) + .toList(), + ); } bool _hasRealmBookRequiredData(RealmBook book) { - return - book.id > -1 && - book.title != null && - book.subTitle != null && - book.author != null && - book.publishedDate != null && - book.language != null; + return book.id > -1 && + book.title != null && + book.subTitle != null && + book.author != null && + book.publishedDate != null && + book.language != null; } bool _hasRealmBookLabelRequiredData(RealmBookLabel bookLabel) { @@ -151,4 +146,14 @@ class RealmBookRepository implements BookRepository { // Not required. throw UnimplementedError(); } + + @override + Future mergeBooks(List books) { + throw UnimplementedError(); + } + + @override + Future overwriteBooks(List books) { + throw UnimplementedError(); + } } diff --git a/lib/src/data/settings/settings_repository.dart b/lib/src/data/settings/settings_repository.dart index 1fc054d..e09b1a5 100644 --- a/lib/src/data/settings/settings_repository.dart +++ b/lib/src/data/settings/settings_repository.dart @@ -1,6 +1,6 @@ import 'package:dantex/src/data/book/book_sort_strategy.dart'; import 'package:dantex/src/ui/timeline/timeline_sort.dart'; -import 'package:dantex/src/data/book/migration/migration_score.dart'; +import 'package:dantex/src/data/book/migration/migration_result.dart'; import 'package:flutter/material.dart'; abstract class SettingsRepository { diff --git a/lib/src/data/settings/shared_preferences_settings_repository.dart b/lib/src/data/settings/shared_preferences_settings_repository.dart index 0635f90..a5c72cd 100644 --- a/lib/src/data/settings/shared_preferences_settings_repository.dart +++ b/lib/src/data/settings/shared_preferences_settings_repository.dart @@ -1,5 +1,5 @@ import 'package:dantex/src/data/book/book_sort_strategy.dart'; -import 'package:dantex/src/data/book/migration/migration_score.dart'; +import 'package:dantex/src/data/book/migration/migration_result.dart'; import 'package:dantex/src/data/settings/settings_repository.dart'; import 'package:dantex/src/ui/timeline/timeline_sort.dart'; import 'package:flutter/material.dart'; diff --git a/pubspec.lock b/pubspec.lock index 7878921..b653531 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -21,10 +21,10 @@ packages: dependency: transitive description: name: _flutterfire_internals - sha256: eb0ac20f704799b986049fbb3c1c16421eca319a1b872378d669513e12452ba5 + sha256: f5628cd9c92ed11083f425fd1f8f1bc60ecdda458c81d73b143aeda036c35fe7 url: "https://pub.dev" source: hosted - version: "1.3.14" + version: "1.3.16" analyzer: dependency: transitive description: @@ -45,10 +45,10 @@ packages: dependency: transitive description: name: archive - sha256: "7b875fd4a20b165a3084bd2d210439b22ebc653f21cea4842729c0c30c82596b" + sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d" url: "https://pub.dev" source: hosted - version: "3.4.9" + version: "3.4.10" args: dependency: transitive description: @@ -81,6 +81,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.4.1" + build_cli_annotations: + dependency: transitive + description: + name: build_cli_annotations + sha256: b59d2769769efd6c9ff6d4c4cede0be115a566afc591705c2040b707534b1172 + url: "https://pub.dev" + source: hosted + version: "2.1.0" build_config: dependency: transitive description: @@ -101,18 +109,18 @@ packages: dependency: transitive description: name: build_resolvers - sha256: "64e12b0521812d1684b1917bc80945625391cb9bdd4312536b1d69dcb6133ed8" + sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.2" build_runner: dependency: "direct dev" description: name: build_runner - sha256: "67d591d602906ef9201caf93452495ad1812bea2074f04e25dbd7c133785821b" + sha256: "581bacf68f89ec8792f5e5a0b2c4decd1c948e97ce659dc783688c8a88fbec21" url: "https://pub.dev" source: hosted - version: "2.4.7" + version: "2.4.8" build_runner_core: dependency: transitive description: @@ -133,34 +141,42 @@ packages: dependency: transitive description: name: built_value - sha256: "69acb7007eb2a31dc901512bfe0f7b767168be34cb734835d54c070bfa74c1b2" + sha256: c9aabae0718ec394e5bc3c7272e6bb0dc0b32201a08fe185ec1d8401d3e39309 url: "https://pub.dev" source: hosted - version: "8.8.0" + version: "8.8.1" cached_network_image: dependency: "direct main" description: name: cached_network_image - sha256: f98972704692ba679db144261172a8e20feb145636c617af0eb4022132a6797f + sha256: "28ea9690a8207179c319965c13cd8df184d5ee721ae2ce60f398ced1219cea1f" url: "https://pub.dev" source: hosted - version: "3.3.0" + version: "3.3.1" cached_network_image_platform_interface: dependency: transitive description: name: cached_network_image_platform_interface - sha256: "56aa42a7a01e3c9db8456d9f3f999931f1e05535b5a424271e9a38cabf066613" + sha256: "9e90e78ae72caa874a323d78fa6301b3fb8fa7ea76a8f96dc5b5bf79f283bf2f" url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "4.0.0" cached_network_image_web: dependency: transitive description: name: cached_network_image_web - sha256: "759b9a9f8f6ccbb66c185df805fac107f05730b1dab9c64626d1008cca532257" + sha256: "42a835caa27c220d1294311ac409a43361088625a4f23c820b006dd9bffb3316" url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.1" + cancellation_token: + dependency: transitive + description: + name: cancellation_token + sha256: ad95acf9d4b2f3563e25dc937f63587e46a70ce534e910b65d10e115490f1027 + url: "https://pub.dev" + source: hosted + version: "2.0.1" carousel_slider: dependency: "direct main" description: @@ -197,10 +213,10 @@ packages: dependency: transitive description: name: cli_util - sha256: b8db3080e59b2503ca9e7922c3df2072cf13992354d5e944074ffa836fba43b7 + sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19 url: "https://pub.dev" source: hosted - version: "0.4.0" + version: "0.4.1" clock: dependency: transitive description: @@ -213,10 +229,10 @@ packages: dependency: transitive description: name: code_builder - sha256: b2151ce26a06171005b379ecff6e08d34c470180ffe16b8e14b6d52be292b55f + sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37 url: "https://pub.dev" source: hosted - version: "4.8.0" + version: "4.10.0" collection: dependency: "direct main" description: @@ -253,26 +269,26 @@ packages: dependency: "direct dev" description: name: custom_lint - sha256: "198ec6b8e084d22f508a76556c9afcfb71706ad3f42b083fe0ee923351a96d90" + sha256: dfb893ff17c83cf08676c6b64df11d3e53d80590978d7c1fb242afff3ba6dedb url: "https://pub.dev" source: hosted - version: "0.5.7" + version: "0.5.8" custom_lint_builder: dependency: transitive description: name: custom_lint_builder - sha256: dfcfa987d2bd9d0ba751ef4bdef0f6c1aa0062f2a67fe716fd5f3f8b709d6418 + sha256: "8df6634b38a36a6c6cb74a9c0eb02e9ba0b0ab89b29e38e6daa86e8ed2c6288d" url: "https://pub.dev" source: hosted - version: "0.5.7" + version: "0.5.8" custom_lint_core: dependency: transitive description: name: custom_lint_core - sha256: f84c3fe2f27ef3b8831953e477e59d4a29c2952623f9eac450d7b40d9cdd94cc + sha256: "2b235be098d157e244f18ea905a15a18c16a205e30553888fac6544bbf52f03f" url: "https://pub.dev" source: hosted - version: "0.5.7" + version: "0.5.8" dart_style: dependency: transitive description: @@ -333,10 +349,10 @@ packages: dependency: "direct main" description: name: extension_google_sign_in_as_googleapis_auth - sha256: c56c934a50670358721e50852b223fca9bd54eb03004e5af160431d79acbba3f + sha256: bcf4f8dedcc1e66ce5fe98fbd98cc86ed25ad7fce0511e8d6cdc46ccbf421e8e url: "https://pub.dev" source: hosted - version: "2.0.11" + version: "2.0.12" fake_async: dependency: transitive description: @@ -365,58 +381,58 @@ packages: dependency: "direct main" description: name: firebase_analytics - sha256: "6325991042226a515406b4fc9204d3102e54a514b72fffb945b625d9f69c5196" + sha256: "0240076090d77045d757aecb090616066d23b343840d4c21074094d6fe40a184" url: "https://pub.dev" source: hosted - version: "10.7.2" + version: "10.8.0" firebase_analytics_platform_interface: dependency: transitive description: name: firebase_analytics_platform_interface - sha256: "38ad4bec1fa0fe16577e6f178e4c4280f029181b18e16bf68782c527c7d82bfe" + sha256: "6d9baa077d16b47ef5f19d982c4fc475597991aa53b0c601216faa3e1cdab45f" url: "https://pub.dev" source: hosted - version: "3.8.2" + version: "3.9.0" firebase_analytics_web: dependency: transitive description: name: firebase_analytics_web - sha256: "6c0deb691a26db2006ded65fd8999a204dcfe8ef67d7d75b7f55ebc08e1d89a4" + sha256: "89a740249bce9d52a99db4e501be6087ca6749c73c47cff2b174802be10abd81" url: "https://pub.dev" source: hosted - version: "0.5.5+9" + version: "0.5.5+12" firebase_auth: dependency: "direct main" description: name: firebase_auth - sha256: "869ff488c7b467e273d7be223f52d3d026576b6e1da92dcd136ff627ae0a8c67" + sha256: "279b2773ff61afd9763202cb5582e2b995ee57419d826b9af6517302a59b672f" url: "https://pub.dev" source: hosted - version: "4.15.0" + version: "4.16.0" firebase_auth_platform_interface: dependency: transitive description: name: firebase_auth_platform_interface - sha256: ecf9f78ae1a7a1297de01ec975e9e2cfe5b543589b27cc5969849d9a8dc46999 + sha256: "3c9cfaccb7549492edf5b0c67c6dd1c6727c7830891aa6727f2fb225f0226626" url: "https://pub.dev" source: hosted - version: "7.0.6" + version: "7.0.9" firebase_auth_web: dependency: transitive description: name: firebase_auth_web - sha256: "96f89e2340cdf373109cb29afec401c170aa2d98fb0833687793c8017e36f435" + sha256: c7b1379ccef7abf4b6816eede67a868c44142198e42350f51c01d8fc03f95a7d url: "https://pub.dev" source: hosted - version: "5.8.9" + version: "5.8.13" firebase_core: dependency: "direct main" description: name: firebase_core - sha256: d301561d614487688d797717bef013a264c517d1d09e4c5c1325c3a64c835efb + sha256: "96607c0e829a581c2a483c658f04e8b159964c3bae2730f73297070bc85d40bb" url: "https://pub.dev" source: hosted - version: "2.24.0" + version: "2.24.2" firebase_core_platform_interface: dependency: transitive description: @@ -429,74 +445,74 @@ packages: dependency: transitive description: name: firebase_core_web - sha256: "10159d9ee42c79f4548971d92f3f0fcd5791f6738cda3583a4e3b2c8b244c018" + sha256: d585bdf3c656c3f7821ba1bd44da5f13365d22fcecaf5eb75c4295246aaa83c0 url: "https://pub.dev" source: hosted - version: "2.9.0" + version: "2.10.0" firebase_crashlytics: dependency: "direct main" description: name: firebase_crashlytics - sha256: "60ef0016c0c2a7d16bf02468e3b27cd0ad4606f6d35535998dde3150cc0bc771" + sha256: "5125b7f3fcef2bfdd7e071afe7edcefd9597968003e44e073456c773d91694ee" url: "https://pub.dev" source: hosted - version: "3.4.6" + version: "3.4.9" firebase_crashlytics_platform_interface: dependency: transitive description: name: firebase_crashlytics_platform_interface - sha256: d185100facc6f7c43c5718103111488d008c52df8c19cbc5e5f9d2115d734909 + sha256: "359197344def001589c84f8d1d57c05f6e2e773f559205610ce58c25e2045a57" url: "https://pub.dev" source: hosted - version: "3.6.14" + version: "3.6.16" firebase_database: dependency: "direct main" description: name: firebase_database - sha256: "373b54cc0a890f4f1547380d31232a718aa5e4ded1a8819e6c4709d7df8286e6" + sha256: "8568ad41f9312ab1f162f70c1e3e7cb7420b8bc8d07e4d543e575bb0cb41f8a5" url: "https://pub.dev" source: hosted - version: "10.3.6" + version: "10.4.0" firebase_database_platform_interface: dependency: transitive description: name: firebase_database_platform_interface - sha256: "2e3edb4552848585aa0031c0d493699305b40da756ebe507686ca6bbe96369eb" + sha256: "4366ade2390f8799a317bb13af29c2a1fdfc84f4d04372094756b86a6cbfd305" url: "https://pub.dev" source: hosted - version: "0.2.5+14" + version: "0.2.5+16" firebase_database_web: dependency: transitive description: name: firebase_database_web - sha256: "55ec085db984291668c232d8760b82c86c33f5e4a459721a2df0438c2ece5859" + sha256: "4920a83b917493b37fd408cbb01c289ef8a422d9ed48982f908a9850290262f9" url: "https://pub.dev" source: hosted - version: "0.2.3+14" + version: "0.2.3+16" firebase_storage: dependency: "direct main" description: name: firebase_storage - sha256: ef1974043d48b0aa081ad055944ec74cad3a665545849cc6232f8ac1691fd901 + sha256: "75e6cb6bed65138b5bbd86bfd7cf9bc9a175fb0c31aacc400e9203df117ffbe6" url: "https://pub.dev" source: hosted - version: "11.5.3" + version: "11.6.0" firebase_storage_platform_interface: dependency: transitive description: name: firebase_storage_platform_interface - sha256: "7b5aa0bf53de4a983aa3df57ab2bc0509d75fe2d269a0029a814aa84484798d5" + sha256: "545a3a8edf337850403bb0fa03c8074a53deb87c0107d19755c77a82ce07919e" url: "https://pub.dev" source: hosted - version: "5.1.1" + version: "5.1.3" firebase_storage_web: dependency: transitive description: name: firebase_storage_web - sha256: bc5ede7fd6dfe7e23821cce007cd551cf77f47f44f8c3ea4f270dea0372051dc + sha256: ee6870ff79aa304b8996ba18a4aefe1e8b3fc31fd385eab6574180267aa8d393 url: "https://pub.dev" source: hosted - version: "3.6.15" + version: "3.6.17" fixnum: dependency: transitive description: @@ -625,10 +641,10 @@ packages: dependency: "direct dev" description: name: freezed - sha256: "21bf2825311de65501d22e563e3d7605dff57fb5e6da982db785ae5372ff018a" + sha256: "6c5031daae12c7072b3a87eff98983076434b4889ef2a44384d0cae3f82372ba" url: "https://pub.dev" source: hosted - version: "2.4.5" + version: "2.4.6" freezed_annotation: dependency: "direct main" description: @@ -657,10 +673,10 @@ packages: dependency: "direct main" description: name: go_router - sha256: c247a4f76071c3b97bb5ae8912968870d5565644801c5e09f3bc961b4d874895 + sha256: c5fa45fa502ee880839e3b2152d987c44abae26d064a2376d4aad434cf0f7b15 url: "https://pub.dev" source: hosted - version: "12.1.1" + version: "12.1.3" google_fonts: dependency: "direct main" description: @@ -673,50 +689,50 @@ packages: dependency: transitive description: name: google_identity_services_web - sha256: "000b7a31e1fa17ee04b6c0553a2b2ea18f9f9352e4dcc0c9fcc785cf10f2484e" + sha256: "0c56c2c5d60d6dfaf9725f5ad4699f04749fb196ee5a70487a46ef184837ccf6" url: "https://pub.dev" source: hosted - version: "0.2.2" + version: "0.3.0+2" google_sign_in: dependency: "direct main" description: name: google_sign_in - sha256: "8f8b94880f2753ccb796744259da529674e49b9af2e372abf6978c590c0ebfef" + sha256: "0b8787cb9c1a68ad398e8010e8c8766bfa33556d2ab97c439fb4137756d7308f" url: "https://pub.dev" source: hosted - version: "6.1.6" + version: "6.2.1" google_sign_in_android: dependency: transitive description: name: google_sign_in_android - sha256: "6031f59074a337fdd81be821aba84cee3a41338c6e958499a5cd34d3e1db80ef" + sha256: bfd42c81c30c6faba16e0f62968d5505a87504aaa672b3155ee931461abb0a49 url: "https://pub.dev" source: hosted - version: "6.1.20" + version: "6.1.21" google_sign_in_ios: dependency: transitive description: name: google_sign_in_ios - sha256: "8edfde9698b5951f3d02632eceb39cc283865c3cff0b03216bf951089f10345b" + sha256: f3336d9e44d4d28063ac90271f6db5caf99f0480cb07281330e7a432edb95226 url: "https://pub.dev" source: hosted - version: "5.6.3" + version: "5.7.3" google_sign_in_platform_interface: dependency: transitive description: name: google_sign_in_platform_interface - sha256: "35ceee5f0eadc1c07b0b4af7553246e315c901facbb7d3dadf734ba2693ceec4" + sha256: "1f6e5787d7a120cc0359ddf315c92309069171306242e181c09472d1b00a2971" url: "https://pub.dev" source: hosted - version: "2.4.2" + version: "2.4.5" google_sign_in_web: dependency: transitive description: name: google_sign_in_web - sha256: "794f5494a945d6dd2654c52f979594ecd2558e5c82ce8272295ba371c93015e6" + sha256: a278ea2d01013faf341cbb093da880d0f2a552bbd1cb6ee90b5bebac9ba69d77 url: "https://pub.dev" source: hosted - version: "0.12.2+1" + version: "0.12.3+2" googleapis: dependency: "direct main" description: @@ -777,10 +793,10 @@ packages: dependency: transitive description: name: image - sha256: "028f61960d56f26414eb616b48b04eb37d700cbe477b7fb09bf1d7ce57fd9271" + sha256: "004a2e90ce080f8627b5a04aecb4cdfac87d2c3f3b520aa291260be5a32c033d" url: "https://pub.dev" source: hosted - version: "4.1.3" + version: "4.1.4" intl: dependency: "direct main" description: @@ -889,10 +905,18 @@ packages: dependency: "direct dev" description: name: mockito - sha256: "4b693867cee1853c9d1d7ecc1871f27f39b2ef2c13c0d8d8507dfe5bebd8aaf1" + sha256: "6841eed20a7befac0ce07df8116c8b8233ed1f4486a7647c7fc5a02ae6163917" + url: "https://pub.dev" + source: hosted + version: "5.4.4" + objectid: + dependency: transitive + description: + name: objectid + sha256: fd4a0b9fe07df25c446948b786e7ab2f363b2f461afa78632cab179d7613b9b3 url: "https://pub.dev" source: hosted - version: "5.4.3" + version: "3.0.0" octo_image: dependency: transitive description: @@ -929,26 +953,26 @@ packages: dependency: transitive description: name: path_provider - sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa + sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72 + sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668" url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.2.2" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d" + sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.3.2" path_provider_linux: dependency: transitive description: @@ -961,10 +985,10 @@ packages: dependency: transitive description: name: path_provider_platform_interface - sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c" + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" path_provider_windows: dependency: transitive description: @@ -993,18 +1017,18 @@ packages: dependency: transitive description: name: platform - sha256: "0a279f0707af40c890e80b1e9df8bb761694c074ba7e1d4ab1bc4b728e200b59" + sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" url: "https://pub.dev" source: hosted - version: "3.1.3" + version: "3.1.4" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface - sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8 + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" url: "https://pub.dev" source: hosted - version: "2.1.7" + version: "2.1.8" pointycastle: dependency: transitive description: @@ -1045,14 +1069,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.3" - quiver: + realm: + dependency: "direct main" + description: + name: realm + sha256: "9381e73a045487876b39e291f7c96ac2c553fe1cbeb7ca1c1c960fd93353b6b6" + url: "https://pub.dev" + source: hosted + version: "1.6.1" + realm_common: dependency: transitive description: - name: quiver - sha256: b1c1ac5ce6688d77f65f3375a9abb9319b3cb32486bdc7a1e0fdf004d7ba4e47 + name: realm_common + sha256: c1ca3cf5cf1d143684c313b7faa82699442e41d943bf9818a04b72987fd365d3 url: "https://pub.dev" source: hosted - version: "3.2.1" + version: "1.6.1" + realm_generator: + dependency: transitive + description: + name: realm_generator + sha256: c0886b4abb269f5241da289f7a74441f5d93113532270b986d6c3bc4b4f5958f + url: "https://pub.dev" + source: hosted + version: "1.6.1" riverpod: dependency: transitive description: @@ -1101,6 +1141,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.27.7" + sane_uuid: + dependency: transitive + description: + name: sane_uuid + sha256: "5e83f796a7d19d38d3ba3a940642998fdd8c4a4049be135ed25404e37f76a18c" + url: "https://pub.dev" + source: hosted + version: "1.0.0-alpha.5" settings_ui: dependency: "direct main" description: @@ -1129,10 +1177,10 @@ packages: dependency: transitive description: name: shared_preferences_foundation - sha256: "7bf53a9f2d007329ee6f3df7268fd498f8373602f943c975598bbb34649b62a7" + sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c" url: "https://pub.dev" source: hosted - version: "2.3.4" + version: "2.3.5" shared_preferences_linux: dependency: transitive description: @@ -1145,10 +1193,10 @@ packages: dependency: transitive description: name: shared_preferences_platform_interface - sha256: d4ec5fc9ebb2f2e056c617112aa75dcf92fc2e4faaf2ae999caa297473f75d8a + sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.3.2" shared_preferences_web: dependency: transitive description: @@ -1190,10 +1238,10 @@ packages: dependency: transitive description: name: source_gen - sha256: fc0da689e5302edb6177fdd964efcb7f58912f43c28c2047a808f5bfff643d16 + sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.5.0" source_helper: dependency: transitive description: @@ -1278,10 +1326,18 @@ packages: dependency: transitive description: name: synchronized - sha256: "5fcbd27688af6082f5abd611af56ee575342c30e87541d0245f7ff99faa02c60" + sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558" url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.1.0+1" + tar: + dependency: transitive + description: + name: tar + sha256: "1680219f82dfa81c8d0e76e849b7b34ea969c721f55a8ebd294a9a95e740dd42" + url: "https://pub.dev" + source: hosted + version: "1.0.3" term_glyph: dependency: transitive description: @@ -1326,34 +1382,34 @@ packages: dependency: "direct main" description: name: url_launcher - sha256: b1c9e98774adf8820c96fbc7ae3601231d324a7d5ebd8babe27b6dfac91357ba + sha256: d25bb0ca00432a5e1ee40e69c36c85863addf7cc45e433769d61bed3fe81fd96 url: "https://pub.dev" source: hosted - version: "6.2.1" + version: "6.2.3" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: "31222ffb0063171b526d3e569079cf1f8b294075ba323443fdc690842bfd4def" + sha256: "507dc655b1d9cb5ebc756032eb785f114e415f91557b73bf60b7e201dfedeb2f" url: "https://pub.dev" source: hosted - version: "6.2.0" + version: "6.2.2" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: bba3373219b7abb6b5e0d071b0fe66dfbe005d07517a68e38d4fc3638f35c6d3 + sha256: "75bb6fe3f60070407704282a2d295630cab232991eb52542b18347a8a941df03" url: "https://pub.dev" source: hosted - version: "6.2.1" + version: "6.2.4" url_launcher_linux: dependency: transitive description: name: url_launcher_linux - sha256: "9f2d390e096fdbe1e6e6256f97851e51afc2d9c423d3432f1d6a02a8a9a8b9fd" + sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811 url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.1.1" url_launcher_macos: dependency: transitive description: @@ -1366,58 +1422,58 @@ packages: dependency: transitive description: name: url_launcher_platform_interface - sha256: "980e8d9af422f477be6948bdfb68df8433be71f5743a188968b0c1b887807e50" + sha256: a932c3a8082e118f80a475ce692fde89dc20fddb24c57360b96bc56f7035de1f url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.3.1" url_launcher_web: dependency: transitive description: name: url_launcher_web - sha256: "138bd45b3a456dcfafc46d1a146787424f8d2edfbf2809c9324361e58f851cf7" + sha256: fff0932192afeedf63cdd50ecbb1bc825d31aed259f02bb8dba0f3b729a5e88b url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.2.3" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - sha256: "7754a1ad30ee896b265f8d14078b0513a4dba28d358eabb9d5f339886f4a1adc" + sha256: ecf9725510600aa2bb6d7ddabe16357691b6d2805f66216a97d1b881e21beff7 url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.1.1" uuid: dependency: "direct main" description: name: uuid - sha256: df5a4d8f22ee4ccd77f8839ac7cb274ebc11ef9adcce8b92be14b797fe889921 + sha256: cd210a09f7c18cbe5a02511718e0334de6559871052c90a90c0cca46a4aa81c8 url: "https://pub.dev" source: hosted - version: "4.2.1" + version: "4.3.3" vector_graphics: dependency: transitive description: name: vector_graphics - sha256: "0f0c746dd2d6254a0057218ff980fc7f5670fd0fcf5e4db38a490d31eed4ad43" + sha256: "18f6690295af52d081f6808f2f7c69f0eed6d7e23a71539d75f4aeb8f0062172" url: "https://pub.dev" source: hosted - version: "1.1.9+1" + version: "1.1.9+2" vector_graphics_codec: dependency: transitive description: name: vector_graphics_codec - sha256: "0edf6d630d1bfd5589114138ed8fada3234deacc37966bec033d3047c29248b7" + sha256: "531d20465c10dfac7f5cd90b60bbe4dd9921f1ec4ca54c83ebb176dbacb7bb2d" url: "https://pub.dev" source: hosted - version: "1.1.9+1" + version: "1.1.9+2" vector_graphics_compiler: dependency: transitive description: name: vector_graphics_compiler - sha256: d24333727332d9bd20990f1483af4e09abdb9b1fc7c3db940b56ab5c42790c26 + sha256: "03012b0a33775c5530576b70240308080e1d5050f0faf000118c20e6463bc0ad" url: "https://pub.dev" source: hosted - version: "1.1.9+1" + version: "1.1.9+2" vector_math: dependency: transitive description: @@ -1462,18 +1518,18 @@ packages: dependency: transitive description: name: win32 - sha256: b0f37db61ba2f2e9b7a78a1caece0052564d1bc70668156cf3a29d676fe4e574 + sha256: "464f5674532865248444b4c3daca12bd9bf2d7c47f759ce2617986e7229494a8" url: "https://pub.dev" source: hosted - version: "5.1.1" + version: "5.2.0" xdg_directories: dependency: transitive description: name: xdg_directories - sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2" + sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d url: "https://pub.dev" source: hosted - version: "1.0.3" + version: "1.0.4" xml: dependency: transitive description: diff --git a/test/data/authentication/firebase_authentication_repository_test.dart b/test/data/authentication/firebase_authentication_repository_test.dart index fa0b04d..d07bafc 100644 --- a/test/data/authentication/firebase_authentication_repository_test.dart +++ b/test/data/authentication/firebase_authentication_repository_test.dart @@ -2,6 +2,7 @@ import 'package:dantex/src/data/authentication/entity/dante_user.dart'; import 'package:dantex/src/data/authentication/firebase_authentication_repository.dart'; +import 'package:dantex/src/data/authentication/on_user_authenticated_plugin.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; @@ -18,10 +19,11 @@ import 'firebase_authentication_repository_test.mocks.dart'; void main() { const String testEmail = 'test@mail.com'; const String testPassword = 'password'; + const List onAuthPlugins = []; test('Get account returns DanteUser', () async { final fbAuth = MockFirebaseAuth(); - final fbAuthRepo = FirebaseAuthenticationRepository(fbAuth); + final fbAuthRepo = FirebaseAuthenticationRepository(fbAuth, onAuthPlugins); final User user = MockUser(); final UserInfo userInfo = MockUserInfo(); final DanteUser danteUser = DanteUser( @@ -56,7 +58,10 @@ void main() { group('Login', () { test('Login with Google returns user credential on success', () async { final fbAuth = MockFirebaseAuth(); - final firebaseAuthRepo = FirebaseAuthenticationRepository(fbAuth); + final firebaseAuthRepo = FirebaseAuthenticationRepository( + fbAuth, + onAuthPlugins, + ); final userCred = MockUserCredential(); when(fbAuth.signInWithProvider(any)).thenAnswer((_) async => userCred); @@ -67,7 +72,10 @@ void main() { test('Login anonymously returns User Credential on success', () async { final fbAuth = MockFirebaseAuth(); - final firebaseAuthRepo = FirebaseAuthenticationRepository(fbAuth); + final firebaseAuthRepo = FirebaseAuthenticationRepository( + fbAuth, + onAuthPlugins, + ); final userCred = MockUserCredential(); when(fbAuth.signInAnonymously()).thenAnswer((_) async => userCred); @@ -78,7 +86,10 @@ void main() { test('Login with email returns User Credential on success', () async { final fbAuth = MockFirebaseAuth(); - final firebaseAuthRepo = FirebaseAuthenticationRepository(fbAuth); + final firebaseAuthRepo = FirebaseAuthenticationRepository( + fbAuth, + onAuthPlugins, + ); final userCred = MockUserCredential(); when( @@ -106,7 +117,10 @@ void main() { test('logout returns void on success', () async { final fbAuth = MockFirebaseAuth(); - final firebaseAuthRepo = FirebaseAuthenticationRepository(fbAuth); + final firebaseAuthRepo = FirebaseAuthenticationRepository( + fbAuth, + onAuthPlugins, + ); when(fbAuth.signOut()).thenAnswer((_) async => Future); @@ -118,7 +132,10 @@ void main() { 'fetchSignInMethodsForEmail maps sign in methods to AuthenticationSource', () async { final fbAuth = MockFirebaseAuth(); - final firebaseAuthRepo = FirebaseAuthenticationRepository(fbAuth); + final firebaseAuthRepo = FirebaseAuthenticationRepository( + fbAuth, + onAuthPlugins, + ); when(fbAuth.fetchSignInMethodsForEmail(testEmail)) .thenAnswer((_) async => ['google.com', 'password', 'apple.com']); @@ -137,7 +154,10 @@ void main() { test('Create mail account returns User Credential on success', () async { final fbAuth = MockFirebaseAuth(); - final firebaseAuthRepo = FirebaseAuthenticationRepository(fbAuth); + final firebaseAuthRepo = FirebaseAuthenticationRepository( + fbAuth, + onAuthPlugins, + ); final userCred = MockUserCredential(); when( @@ -165,7 +185,10 @@ void main() { test('Upgrade anonymous account returns User Credential on success', () async { final fbAuth = MockFirebaseAuth(); - final firebaseAuthRepo = FirebaseAuthenticationRepository(fbAuth); + final firebaseAuthRepo = FirebaseAuthenticationRepository( + fbAuth, + onAuthPlugins, + ); final user = MockUser(); final userCred = MockUserCredential(); @@ -190,7 +213,10 @@ void main() { 'Upgrade anonymous account throws FirebaseAuthException error when user not found', () async { final fbAuth = MockFirebaseAuth(); - final firebaseAuthRepo = FirebaseAuthenticationRepository(fbAuth); + final firebaseAuthRepo = FirebaseAuthenticationRepository( + fbAuth, + onAuthPlugins, + ); when( fbAuth.currentUser, @@ -207,7 +233,10 @@ void main() { test('Update user password returns void on success', () { final fbAuth = MockFirebaseAuth(); - final firebaseAuthRepo = FirebaseAuthenticationRepository(fbAuth); + final firebaseAuthRepo = FirebaseAuthenticationRepository( + fbAuth, + onAuthPlugins, + ); final user = MockUser(); when(fbAuth.currentUser).thenReturn(user); @@ -223,7 +252,10 @@ void main() { 'Upgrade password throws FirebaseAuthException error when user not found', () async { final fbAuth = MockFirebaseAuth(); - final firebaseAuthRepo = FirebaseAuthenticationRepository(fbAuth); + final firebaseAuthRepo = FirebaseAuthenticationRepository( + fbAuth, + onAuthPlugins, + ); when( fbAuth.currentUser, @@ -239,7 +271,10 @@ void main() { test('Send password reset request returns void on success', () { final fbAuth = MockFirebaseAuth(); - final firebaseAuthRepo = FirebaseAuthenticationRepository(fbAuth); + final firebaseAuthRepo = FirebaseAuthenticationRepository( + fbAuth, + onAuthPlugins, + ); when(fbAuth.sendPasswordResetEmail(email: testEmail)) .thenAnswer((_) async => Future); @@ -250,7 +285,10 @@ void main() { test('Delete user returns void on success', () async { final fbAuth = MockFirebaseAuth(); - final firebaseAuthRepo = FirebaseAuthenticationRepository(fbAuth); + final firebaseAuthRepo = FirebaseAuthenticationRepository( + fbAuth, + onAuthPlugins, + ); final User user = MockUser(); when(fbAuth.currentUser).thenReturn(user); @@ -263,7 +301,10 @@ void main() { test('Auth state changes returns DanteUser', () { final fbAuth = MockFirebaseAuth(); - final firebaseAuthRepo = FirebaseAuthenticationRepository(fbAuth); + final firebaseAuthRepo = FirebaseAuthenticationRepository( + fbAuth, + onAuthPlugins, + ); final User user = MockUser(); final UserInfo userInfo = MockUserInfo(); final DanteUser danteUser = DanteUser( diff --git a/test/data/authentication/firebase_authentication_repository_test.mocks.dart b/test/data/authentication/firebase_authentication_repository_test.mocks.dart index bc01842..5fa1a5d 100644 --- a/test/data/authentication/firebase_authentication_repository_test.mocks.dart +++ b/test/data/authentication/firebase_authentication_repository_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.3 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in dantex/test/data/authentication/firebase_authentication_repository_test.dart. // Do not manually edit this file. @@ -162,8 +162,9 @@ class MockFirebaseAuth extends _i1.Mock implements _i4.FirebaseAuth { @override _i5.Future useAuthEmulator( String? host, - int? port, - ) => + int? port, { + bool? automaticHostMapping = true, + }) => (super.noSuchMethod( Invocation.method( #useAuthEmulator, @@ -171,6 +172,7 @@ class MockFirebaseAuth extends _i1.Mock implements _i4.FirebaseAuth { host, port, ], + {#automaticHostMapping: automaticHostMapping}, ), returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), diff --git a/test/data/book/firebase_book_repository_test.mocks.dart b/test/data/book/firebase_book_repository_test.mocks.dart index 84623b4..69d9918 100644 --- a/test/data/book/firebase_book_repository_test.mocks.dart +++ b/test/data/book/firebase_book_repository_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.3 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in dantex/test/data/book/firebase_book_repository_test.dart. // Do not manually edit this file. @@ -227,8 +227,9 @@ class MockFirebaseAuth extends _i1.Mock implements _i4.FirebaseAuth { @override _i6.Future useAuthEmulator( String? host, - int? port, - ) => + int? port, { + bool? automaticHostMapping = true, + }) => (super.noSuchMethod( Invocation.method( #useAuthEmulator, @@ -236,6 +237,7 @@ class MockFirebaseAuth extends _i1.Mock implements _i4.FirebaseAuth { host, port, ], + {#automaticHostMapping: automaticHostMapping}, ), returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), @@ -761,8 +763,9 @@ class MockFirebaseDatabase extends _i1.Mock implements _i5.FirebaseDatabase { @override void useDatabaseEmulator( String? host, - int? port, - ) => + int? port, { + bool? automaticHostMapping = true, + }) => super.noSuchMethod( Invocation.method( #useDatabaseEmulator, @@ -770,6 +773,7 @@ class MockFirebaseDatabase extends _i1.Mock implements _i5.FirebaseDatabase { host, port, ], + {#automaticHostMapping: automaticHostMapping}, ), returnValueForMissingStub: null, ); From 241940bfc548280a44b818485988d88d85f28418 Mon Sep 17 00:00:00 2001 From: Martin Macheiner Date: Sat, 13 Jan 2024 21:33:01 +0100 Subject: [PATCH 11/13] Run migration on user authentication --- .../dummy_on_user_authenticated_plugin.dart | 1 + ...igration_on_user_authenticated_plugin.dart | 21 +++++++++++++++++++ .../data/book/migration/migration_result.dart | 7 ++++--- .../data/book/migration/migration_runner.dart | 21 ++++++++++++++++--- lib/src/providers/authentication.dart | 12 +++++++++-- 5 files changed, 54 insertions(+), 8 deletions(-) create mode 100644 lib/src/data/authentication/plugin/realm_migration_on_user_authenticated_plugin.dart diff --git a/lib/src/data/authentication/plugin/dummy_on_user_authenticated_plugin.dart b/lib/src/data/authentication/plugin/dummy_on_user_authenticated_plugin.dart index f9ef58b..d55ec45 100644 --- a/lib/src/data/authentication/plugin/dummy_on_user_authenticated_plugin.dart +++ b/lib/src/data/authentication/plugin/dummy_on_user_authenticated_plugin.dart @@ -3,6 +3,7 @@ import 'package:dantex/src/data/authentication/entity/dante_user.dart'; import 'package:dantex/src/data/authentication/on_user_authenticated_plugin.dart'; +// TODO Remove later class DummyOnUserAuthenticatedPlugin extends OnUserAuthenticatedPlugin { @override void onUserAuthenticated(DanteUser user) { diff --git a/lib/src/data/authentication/plugin/realm_migration_on_user_authenticated_plugin.dart b/lib/src/data/authentication/plugin/realm_migration_on_user_authenticated_plugin.dart new file mode 100644 index 0000000..36a03fc --- /dev/null +++ b/lib/src/data/authentication/plugin/realm_migration_on_user_authenticated_plugin.dart @@ -0,0 +1,21 @@ +import 'dart:async'; + +import 'package:dantex/src/data/authentication/entity/dante_user.dart'; +import 'package:dantex/src/data/authentication/on_user_authenticated_plugin.dart'; +import 'package:dantex/src/data/book/migration/migration_runner.dart'; + +class RealmMigrationOnUserAuthenticatedPlugin + implements OnUserAuthenticatedPlugin { + final MigrationRunner _migrationRunner; + + RealmMigrationOnUserAuthenticatedPlugin( + this._migrationRunner, + ); + + @override + void onUserAuthenticated(DanteUser user) { + unawaited( + _migrationRunner.migrateIfRequired(), + ); + } +} diff --git a/lib/src/data/book/migration/migration_result.dart b/lib/src/data/book/migration/migration_result.dart index 741c39f..5a3e8df 100644 --- a/lib/src/data/book/migration/migration_result.dart +++ b/lib/src/data/book/migration/migration_result.dart @@ -22,7 +22,8 @@ class MigrationResult { } enum MigrationStatus { - required, - migrated, - failed, + required, // A migration needs to run + migrated, // A migration has been executed + failed, // The migration failed + unsupportedPlatform, // Migrations will only run on Android } diff --git a/lib/src/data/book/migration/migration_runner.dart b/lib/src/data/book/migration/migration_runner.dart index 6f34870..936f181 100644 --- a/lib/src/data/book/migration/migration_runner.dart +++ b/lib/src/data/book/migration/migration_runner.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:io'; import 'package:dantex/src/data/book/book_repository.dart'; import 'package:dantex/src/data/book/entity/book.dart'; @@ -30,8 +31,11 @@ class MigrationRunner { _pageRecordTarget = pageRecordTarget, _pageRecordSource = pageRecordSource; - // TODO Call this method Future migrateIfRequired() async { + if (!Platform.isAndroid) { + return MigrationStatus.unsupportedPlatform; + } + final MigrationStatus status = await _migrationStatus(); // If status is not required, return the current status. @@ -39,9 +43,11 @@ class MigrationRunner { return status; } - final MigrationResult score = await _migrate(); - final MigrationStatus newStatus = score.statusFromScore(); + final MigrationResult result = await _migrate(); + final MigrationStatus newStatus = result.statusFromScore(); + _logResult(result); _updateMigrationStatus(newStatus); + return newStatus; } @@ -119,6 +125,15 @@ class MigrationRunner { ); } + void _logResult(MigrationResult r) { + _logger.i( + 'Migration result status: ${r.statusFromScore().name}\n' + '----------------------\n' + 'Migrated books: ${r.migratedBooks} / ${r.booksToMigrate}\n' + 'Migrated pages: ${r.migratedPageRecords} / ${r.pageRecordsToMigrate}\n', + ); + } + void _updateMigrationStatus(MigrationStatus newStatus) { unawaited(_settingsRepository.setRealmMigrationStatus(newStatus)); } diff --git a/lib/src/providers/authentication.dart b/lib/src/providers/authentication.dart index ce59b89..d9cb961 100644 --- a/lib/src/providers/authentication.dart +++ b/lib/src/providers/authentication.dart @@ -3,6 +3,8 @@ import 'package:dantex/src/data/authentication/entity/dante_user.dart'; import 'package:dantex/src/data/authentication/firebase_authentication_repository.dart'; import 'package:dantex/src/data/authentication/on_user_authenticated_plugin.dart'; import 'package:dantex/src/data/authentication/plugin/dummy_on_user_authenticated_plugin.dart'; +import 'package:dantex/src/data/authentication/plugin/realm_migration_on_user_authenticated_plugin.dart'; +import 'package:dantex/src/providers/migration.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_database/firebase_database.dart'; @@ -35,11 +37,17 @@ AuthenticationRepository authenticationRepository( ) => FirebaseAuthenticationRepository( ref.watch(firebaseAuthProvider), - _provideOnUserAuthenticatedPlugins(), + _provideOnUserAuthenticatedPlugins(ref), ); -List _provideOnUserAuthenticatedPlugins() => [ +List _provideOnUserAuthenticatedPlugins( + AuthenticationRepositoryRef ref, +) => + [ DummyOnUserAuthenticatedPlugin(), + RealmMigrationOnUserAuthenticatedPlugin( + ref.read(migrationRunnerProvider), + ), ]; @riverpod From f0b915449a41da42da02758d44f59e4eef8de1fd Mon Sep 17 00:00:00 2001 From: Martin Macheiner Date: Sat, 13 Jan 2024 21:37:55 +0100 Subject: [PATCH 12/13] Build for iOS --- ios/Flutter/AppFrameworkInfo.plist | 2 +- ios/Podfile.lock | 61 +++++++++++-------- ios/Runner.xcodeproj/project.pbxproj | 18 ++++++ .../dummy_on_user_authenticated_plugin.dart | 13 ---- .../data/book/migration/migration_runner.dart | 3 + lib/src/providers/authentication.dart | 1 - 6 files changed, 56 insertions(+), 42 deletions(-) delete mode 100644 lib/src/data/authentication/plugin/dummy_on_user_authenticated_plugin.dart diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist index 9625e10..7c56964 100644 --- a/ios/Flutter/AppFrameworkInfo.plist +++ b/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 11.0 + 12.0 diff --git a/ios/Podfile.lock b/ios/Podfile.lock index f2de4d8..14021d4 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -24,26 +24,26 @@ PODS: - Firebase/Storage (10.18.0): - Firebase/CoreOnly - FirebaseStorage (~> 10.18.0) - - firebase_analytics (10.7.2): + - firebase_analytics (10.8.0): - Firebase/Analytics (= 10.18.0) - firebase_core - Flutter - - firebase_auth (4.15.0): + - firebase_auth (4.16.0): - Firebase/Auth (= 10.18.0) - firebase_core - Flutter - - firebase_core (2.24.0): + - firebase_core (2.24.2): - Firebase/CoreOnly (= 10.18.0) - Flutter - - firebase_crashlytics (3.4.6): + - firebase_crashlytics (3.4.9): - Firebase/Crashlytics (= 10.18.0) - firebase_core - Flutter - - firebase_database (10.3.6): + - firebase_database (10.4.0): - Firebase/Database (= 10.18.0) - firebase_core - Flutter - - firebase_storage (11.5.3): + - firebase_storage (11.6.0): - Firebase/Storage (= 10.18.0) - firebase_core - Flutter @@ -123,7 +123,8 @@ PODS: - FMDB/standard (2.7.5) - google_sign_in_ios (0.0.1): - Flutter - - GoogleSignIn (~> 6.2) + - FlutterMacOS + - GoogleSignIn (~> 7.0) - GoogleAppMeasurement (10.18.0): - GoogleAppMeasurement/AdIdSupport (= 10.18.0) - GoogleUtilities/AppDelegateSwizzler (~> 7.11) @@ -148,10 +149,10 @@ PODS: - GoogleUtilities/Environment (~> 7.7) - nanopb (< 2.30910.0, >= 2.30908.0) - PromisesObjC (< 3.0, >= 1.2) - - GoogleSignIn (6.2.4): + - GoogleSignIn (7.0.0): - AppAuth (~> 1.5) - - GTMAppAuth (~> 1.3) - - GTMSessionFetcher/Core (< 3.0, >= 1.1) + - GTMAppAuth (< 3.0, >= 1.3) + - GTMSessionFetcher/Core (< 4.0, >= 1.1) - GoogleUtilities/AppDelegateSwizzler (7.12.0): - GoogleUtilities/Environment - GoogleUtilities/Logger @@ -171,9 +172,9 @@ PODS: - GoogleUtilities/Logger - GoogleUtilities/UserDefaults (7.12.0): - GoogleUtilities/Logger - - GTMAppAuth (1.3.1): + - GTMAppAuth (2.0.0): - AppAuth/Core (~> 1.6) - - GTMSessionFetcher/Core (< 3.0, >= 1.5) + - GTMSessionFetcher/Core (< 4.0, >= 1.5) - GTMSessionFetcher/Core (2.3.0) - leveldb-library (1.22.2) - nanopb (2.30909.1): @@ -187,6 +188,8 @@ PODS: - PromisesObjC (2.3.1) - PromisesSwift (2.3.1): - PromisesObjC (= 2.3.1) + - realm (1.6.1): + - Flutter - RecaptchaInterop (100.0.0) - shared_preferences_foundation (0.0.1): - Flutter @@ -206,8 +209,9 @@ DEPENDENCIES: - firebase_storage (from `.symlinks/plugins/firebase_storage/ios`) - Flutter (from `Flutter`) - flutter_barcode_scanner (from `.symlinks/plugins/flutter_barcode_scanner/ios`) - - google_sign_in_ios (from `.symlinks/plugins/google_sign_in_ios/ios`) + - google_sign_in_ios (from `.symlinks/plugins/google_sign_in_ios/darwin`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) + - realm (from `.symlinks/plugins/realm/ios`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - sqflite (from `.symlinks/plugins/sqflite/ios`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) @@ -260,9 +264,11 @@ EXTERNAL SOURCES: flutter_barcode_scanner: :path: ".symlinks/plugins/flutter_barcode_scanner/ios" google_sign_in_ios: - :path: ".symlinks/plugins/google_sign_in_ios/ios" + :path: ".symlinks/plugins/google_sign_in_ios/darwin" path_provider_foundation: :path: ".symlinks/plugins/path_provider_foundation/darwin" + realm: + :path: ".symlinks/plugins/realm/ios" shared_preferences_foundation: :path: ".symlinks/plugins/shared_preferences_foundation/darwin" sqflite: @@ -273,12 +279,12 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: AppAuth: 3bb1d1cd9340bd09f5ed189fb00b1cc28e1e8570 Firebase: 414ad272f8d02dfbf12662a9d43f4bba9bec2a06 - firebase_analytics: a8c0d8ba83f213c9b775b5f61d36dadaafb1b239 - firebase_auth: 02d1a6340f81020cc302fb8fea8518908c546cec - firebase_core: f802c5c1f6caff9b8d38b591a36e7b25f8878936 - firebase_crashlytics: 0db78a5b6badc630f21a833d0c9ab20ab5d81948 - firebase_database: 18b34a683c878f836ac5a403ce29242065d5acd0 - firebase_storage: 95b470ab32351049dfd3d97b595460ce7a189971 + firebase_analytics: b3d6dd14c61549a29abb10e9843446a4d8bb53eb + firebase_auth: 8e9ec02991ca4659111cc671c84d0c010b6bfb26 + firebase_core: 0af4a2b24f62071f9bf283691c0ee41556dcb3f5 + firebase_crashlytics: 4b91b8ad60ee7c168fe88979f84c9573a729de7a + firebase_database: 5d420ac53c48f3394445c8b83c530a42d149c3d4 + firebase_storage: 2b932fa5461f4efac36a2dcfbe240898b190b5b1 FirebaseAnalytics: 4d310b35c48eaa4a058ddc04bdca6bdb5dc0fe80 FirebaseAppCheckInterop: 3cd914842ba46f4304050874cd284de82f154ffd FirebaseAuth: 12314b438fa76048540c8fb86d6cfc9e08595176 @@ -292,25 +298,26 @@ SPEC CHECKSUMS: FirebaseSessions: f90fe9212ee2818641eda051c0835c9c4e30d9ae FirebaseSharedSwift: 62e248642c0582324d0390706cadd314687c116b FirebaseStorage: 8333c4b183764cdd170d9539a61322b71c23adff - Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 + Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 flutter_barcode_scanner: 7a1144744c28dc0c57a8de7218ffe5ec59a9e4bf FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a - google_sign_in_ios: 1256ff9d941db546373826966720b0c24804bcdd + google_sign_in_ios: 1bfaf6607b44cd1b24c4d4bc39719870440f9ce1 GoogleAppMeasurement: 70ce9aa438cff1cfb31ea3e660bcc67734cb716e GoogleDataTransport: 57c22343ab29bc686febbf7cbb13bad167c2d8fe - GoogleSignIn: 5651ce3a61e56ca864160e79b484cd9ed3f49b7a + GoogleSignIn: b232380cf495a429b8095d3178a8d5855b42e842 GoogleUtilities: 0759d1a57ebb953965c2dfe0ba4c82e95ccc2e34 - GTMAppAuth: 0ff230db599948a9ad7470ca667337803b3fc4dd + GTMAppAuth: 99fb010047ba3973b7026e45393f51f27ab965ae GTMSessionFetcher: 3a63d75eecd6aa32c2fc79f578064e1214dfdec2 leveldb-library: f03246171cce0484482ec291f88b6d563699ee06 nanopb: d4d75c12cd1316f4a64e3c6963f879ecd4b5e0d5 - path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 + path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c PromisesObjC: c50d2056b5253dadbd6c2bea79b0674bd5a52fa4 PromisesSwift: 28dca69a9c40779916ac2d6985a0192a5cb4a265 + realm: d2cda90107b12761cef58f16a606f4d14333b071 RecaptchaInterop: 7d1a4a01a6b2cb1610a47ef3f85f0c411434cb21 - shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126 + shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695 sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a - url_launcher_ios: bf5ce03e0e2088bad9cc378ea97fa0ed5b49673b + url_launcher_ios: bbd758c6e7f9fd7b5b1d4cde34d2b95fcce5e812 PODFILE CHECKSUM: 7adbc9d59f05e1b01f554ea99b6c79e97f2214a2 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 6e65f1a..a95464c 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -146,6 +146,7 @@ 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 2E18E51AD90C59402E14BDEC /* [CP] Embed Pods Frameworks */, BE07253FC1FD406B03000781 /* [firebase_crashlytics] Crashlytics Upload Symbols */, + D826D48A3F0835A21664F0B1 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -296,6 +297,23 @@ shellPath = /bin/sh; shellScript = "\"$PODS_ROOT/FirebaseCrashlytics/upload-symbols\" --flutter-project \"$PROJECT_DIR/firebase_app_id_file.json\" "; }; + D826D48A3F0835A21664F0B1 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ diff --git a/lib/src/data/authentication/plugin/dummy_on_user_authenticated_plugin.dart b/lib/src/data/authentication/plugin/dummy_on_user_authenticated_plugin.dart deleted file mode 100644 index d55ec45..0000000 --- a/lib/src/data/authentication/plugin/dummy_on_user_authenticated_plugin.dart +++ /dev/null @@ -1,13 +0,0 @@ - - -import 'package:dantex/src/data/authentication/entity/dante_user.dart'; -import 'package:dantex/src/data/authentication/on_user_authenticated_plugin.dart'; - -// TODO Remove later -class DummyOnUserAuthenticatedPlugin extends OnUserAuthenticatedPlugin { - @override - void onUserAuthenticated(DanteUser user) { - print('User ${user.email} authenticated'); - } - -} \ No newline at end of file diff --git a/lib/src/data/book/migration/migration_runner.dart b/lib/src/data/book/migration/migration_runner.dart index 936f181..6b32aac 100644 --- a/lib/src/data/book/migration/migration_runner.dart +++ b/lib/src/data/book/migration/migration_runner.dart @@ -33,6 +33,9 @@ class MigrationRunner { Future migrateIfRequired() async { if (!Platform.isAndroid) { + _logger.i( + 'No migration will run, as the underlying platform is not Android.', + ); return MigrationStatus.unsupportedPlatform; } diff --git a/lib/src/providers/authentication.dart b/lib/src/providers/authentication.dart index d9cb961..9a6bbbd 100644 --- a/lib/src/providers/authentication.dart +++ b/lib/src/providers/authentication.dart @@ -44,7 +44,6 @@ List _provideOnUserAuthenticatedPlugins( AuthenticationRepositoryRef ref, ) => [ - DummyOnUserAuthenticatedPlugin(), RealmMigrationOnUserAuthenticatedPlugin( ref.read(migrationRunnerProvider), ), From 254eef19c6eedb1f83785077d4bfa4b46b2b405d Mon Sep 17 00:00:00 2001 From: Martin Macheiner Date: Sat, 13 Jan 2024 21:38:13 +0100 Subject: [PATCH 13/13] Remove unused import --- lib/src/providers/authentication.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/src/providers/authentication.dart b/lib/src/providers/authentication.dart index 9a6bbbd..204ae0d 100644 --- a/lib/src/providers/authentication.dart +++ b/lib/src/providers/authentication.dart @@ -2,7 +2,6 @@ import 'package:dantex/src/data/authentication/authentication_repository.dart'; import 'package:dantex/src/data/authentication/entity/dante_user.dart'; import 'package:dantex/src/data/authentication/firebase_authentication_repository.dart'; import 'package:dantex/src/data/authentication/on_user_authenticated_plugin.dart'; -import 'package:dantex/src/data/authentication/plugin/dummy_on_user_authenticated_plugin.dart'; import 'package:dantex/src/data/authentication/plugin/realm_migration_on_user_authenticated_plugin.dart'; import 'package:dantex/src/providers/migration.dart'; import 'package:firebase_auth/firebase_auth.dart';