diff --git a/lib/app/pet/controller/all_pets_controller.dart b/lib/app/pet/controller/all_pets_controller.dart index 82e52e3..0220501 100644 --- a/lib/app/pet/controller/all_pets_controller.dart +++ b/lib/app/pet/controller/all_pets_controller.dart @@ -1,4 +1,6 @@ import 'package:petjournal/app/pet/models/pet_model.dart'; +import 'package:petjournal/app/settings/controllers/settings_controller.dart'; +import 'package:petjournal/constants/linked_record_type.dart'; import 'package:petjournal/data/mapper/pet_mapper.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:petjournal/data/database/database_service.dart'; @@ -10,6 +12,7 @@ class AllPetsController extends _$AllPetsController { late final DatabaseService _databaseService = // ignore: avoid_manual_providers_as_generated_provider_dependency ref.read(DatabaseService.provider); + late final _settingsFuture = ref.watch(settingsControllerProvider.future); @override Stream> build() { @@ -42,7 +45,11 @@ class AllPetsController extends _$AllPetsController { pet.imageUrl, ); - return newPet == null ? null : PetMapper.mapToModel(newPet); + // create linked journal entry if required + if (newPet == null) return null; + final newPetModel = PetMapper.mapToModel(newPet); + await _createLinkedJournalEntry(newPetModel); + return newPetModel; } else { await _databaseService.updatePet( pet.petId!, @@ -74,4 +81,30 @@ class AllPetsController extends _$AllPetsController { Future deletePet(int id) { return _databaseService.deletePet(id); } + + /// If required, create / update the linked journal entry + Future _createLinkedJournalEntry( + PetModel pet, { + bool createNew = true, + }) async { + final settings = await _settingsFuture; + if (settings.createLinkedJournalEntries) { + if (createNew) { + await _databaseService.createJournalEntryForPet( + entryText: pet.notes ?? '', + petIdList: [pet.petId!], + tags: [], + linkedRecordId: pet.petId, + linkedRecordType: LinkedRecordType.pet, + linkedRecordTitle: 'Pet ${pet.name} created', + ); + } else { + await _databaseService.updateLinkedJournalEntry( + linkedRecordId: pet.petId!, + linkedRecordType: LinkedRecordType.pet, + linkedRecordTitle: 'Pet ${pet.name} created (updated)', + ); + } + } + } } diff --git a/lib/app/pet/controller/all_pets_controller.g.dart b/lib/app/pet/controller/all_pets_controller.g.dart index a7b937f..141d199 100644 --- a/lib/app/pet/controller/all_pets_controller.g.dart +++ b/lib/app/pet/controller/all_pets_controller.g.dart @@ -6,7 +6,7 @@ part of 'all_pets_controller.dart'; // RiverpodGenerator // ************************************************************************** -String _$allPetsControllerHash() => r'51164797a5b90ec654da82efb14c176746bb9c20'; +String _$allPetsControllerHash() => r'9224af2783a2d72e247086ab3ebbced722bf3748'; /// See also [AllPetsController]. @ProviderFor(AllPetsController) diff --git a/lib/app/pet/controller/pet_meds_controller.dart b/lib/app/pet/controller/pet_meds_controller.dart index a3d2524..0cde680 100644 --- a/lib/app/pet/controller/pet_meds_controller.dart +++ b/lib/app/pet/controller/pet_meds_controller.dart @@ -1,4 +1,6 @@ import 'package:petjournal/app/pet/models/pet_med_model.dart'; +import 'package:petjournal/app/settings/controllers/settings_controller.dart'; +import 'package:petjournal/constants/linked_record_type.dart'; import 'package:petjournal/data/mapper/pet_med_mapper.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:petjournal/data/database/database_service.dart'; @@ -10,6 +12,7 @@ class PetMedsController extends _$PetMedsController { late final DatabaseService _databaseService = // ignore: avoid_manual_providers_as_generated_provider_dependency ref.read(DatabaseService.provider); + late final _settingsFuture = ref.watch(settingsControllerProvider.future); @override Stream?> build(int petId) { @@ -29,7 +32,11 @@ class PetMedsController extends _$PetMedsController { petMed.notes, ); - return newPetMed == null ? null : PetMedMapper.mapToModel(newPetMed); + // create linked journal entry if required + if (newPetMed == null) return null; + final newPetMedModel = PetMedMapper.mapToModel(newPetMed); + await _createLinkedJournalEntry(newPetMedModel); + return newPetMedModel; } else { await _databaseService.updatePetMed( petMed.petMedId!, @@ -39,6 +46,9 @@ class PetMedsController extends _$PetMedsController { petMed.endDate, petMed.notes, ); + + await _createLinkedJournalEntry(petMed, createNew: false); + return petMed; } } @@ -46,4 +56,30 @@ class PetMedsController extends _$PetMedsController { Future deletePetMed(int id) { return _databaseService.deletePetMed(id); } + + /// If required, create / update the linked journal entry + Future _createLinkedJournalEntry( + PetMedModel petMed, { + bool createNew = true, + }) async { + final settings = await _settingsFuture; + if (settings.createLinkedJournalEntries) { + if (createNew) { + await _databaseService.createJournalEntryForPet( + entryText: petMed.notes ?? '', + petIdList: [petMed.petId], + tags: [], + linkedRecordId: petMed.petMedId, + linkedRecordType: LinkedRecordType.medication, + linkedRecordTitle: 'Medication ${petMed.name} prescribed', + ); + } else { + await _databaseService.updateLinkedJournalEntry( + linkedRecordId: petMed.petMedId!, + linkedRecordType: LinkedRecordType.medication, + linkedRecordTitle: 'Medication ${petMed.name} prescribed (updated)', + ); + } + } + } } diff --git a/lib/app/pet/controller/pet_meds_controller.g.dart b/lib/app/pet/controller/pet_meds_controller.g.dart index 109ae04..d171b85 100644 --- a/lib/app/pet/controller/pet_meds_controller.g.dart +++ b/lib/app/pet/controller/pet_meds_controller.g.dart @@ -6,7 +6,7 @@ part of 'pet_meds_controller.dart'; // RiverpodGenerator // ************************************************************************** -String _$petMedsControllerHash() => r'84defe8e545c6409741c66870652b47c03098715'; +String _$petMedsControllerHash() => r'1403cf6dd90a2be4f6562003fe7778954a1c15f3'; /// Copied from Dart SDK class _SystemHash { diff --git a/lib/app/pet/controller/pet_vaccinations_controller.dart b/lib/app/pet/controller/pet_vaccinations_controller.dart index 6a567fd..e37ef43 100644 --- a/lib/app/pet/controller/pet_vaccinations_controller.dart +++ b/lib/app/pet/controller/pet_vaccinations_controller.dart @@ -1,4 +1,6 @@ import 'package:petjournal/app/pet/models/pet_vaccination_model.dart'; +import 'package:petjournal/app/settings/controllers/settings_controller.dart'; +import 'package:petjournal/constants/linked_record_type.dart'; import 'package:petjournal/data/mapper/pet_vaccination_mapper.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:petjournal/data/database/database_service.dart'; @@ -10,6 +12,7 @@ class PetVaccinationsController extends _$PetVaccinationsController { late final DatabaseService _databaseService = // ignore: avoid_manual_providers_as_generated_provider_dependency ref.read(DatabaseService.provider); + late final _settingsFuture = ref.watch(settingsControllerProvider.future); @override Stream> build(int petId) { @@ -20,33 +23,38 @@ class PetVaccinationsController extends _$PetVaccinationsController { Future save(PetVaccinationModel petVaccination) async { if (petVaccination.petVaccinationId == null) { - final newPetMed = await _databaseService.createPetVaccination( - petVaccination.petId, - petVaccination.name, - petVaccination.administeredDate, - petVaccination.expiryDate, - petVaccination.reminderDate, - petVaccination.notes, + final newPetVaccination = await _databaseService.createPetVaccination( + petVaccination.petId, + petVaccination.name, + petVaccination.administeredDate, + petVaccination.expiryDate, + petVaccination.reminderDate, + petVaccination.notes, petVaccination.vaccineBatchNumber, petVaccination.vaccineManufacturer, petVaccination.administeredBy, ); - return newPetMed == null - ? null - : PetVaccinationMapper.mapToModel(newPetMed); + // create linked journal entry if required + if (newPetVaccination == null) return null; + final newPetVaccinationModel = PetVaccinationMapper.mapToModel(newPetVaccination); + await _createLinkedJournalEntry(newPetVaccinationModel); + return newPetVaccinationModel; } else { await _databaseService.updatePetVaccination( - petVaccination.petVaccinationId!, - petVaccination.name, - petVaccination.administeredDate, - petVaccination.expiryDate, - petVaccination.reminderDate, - petVaccination.notes, + petVaccination.petVaccinationId!, + petVaccination.name, + petVaccination.administeredDate, + petVaccination.expiryDate, + petVaccination.reminderDate, + petVaccination.notes, petVaccination.vaccineBatchNumber, petVaccination.vaccineManufacturer, petVaccination.administeredBy, ); + + await _createLinkedJournalEntry(petVaccination, createNew: false); + return petVaccination; } } @@ -54,4 +62,31 @@ class PetVaccinationsController extends _$PetVaccinationsController { Future deletePetVaccination(int id) { return _databaseService.deletePetVaccination(id); } + + /// If required, create / update the linked journal entry + Future _createLinkedJournalEntry( + PetVaccinationModel petVaccination, { + bool createNew = true, + }) async { + final settings = await _settingsFuture; + if (settings.createLinkedJournalEntries) { + if (createNew) { + await _databaseService.createJournalEntryForPet( + entryText: petVaccination.notes ?? '', + petIdList: [petVaccination.petId], + tags: [], + linkedRecordId: petVaccination.petVaccinationId, + linkedRecordType: LinkedRecordType.vaccination, + linkedRecordTitle: 'Vaccination ${petVaccination.name} administered', + ); + } else { + await _databaseService.updateLinkedJournalEntry( + linkedRecordId: petVaccination.petVaccinationId!, + linkedRecordType: LinkedRecordType.vaccination, + linkedRecordTitle: + 'Vaccination ${petVaccination.name} administered (updated)', + ); + } + } + } } diff --git a/lib/app/pet/controller/pet_vaccinations_controller.g.dart b/lib/app/pet/controller/pet_vaccinations_controller.g.dart index 8bb726f..092496c 100644 --- a/lib/app/pet/controller/pet_vaccinations_controller.g.dart +++ b/lib/app/pet/controller/pet_vaccinations_controller.g.dart @@ -7,7 +7,7 @@ part of 'pet_vaccinations_controller.dart'; // ************************************************************************** String _$petVaccinationsControllerHash() => - r'a56e563fbb6fab63e0a91d220529bf710b31ded4'; + r'dac04f610e9a722b7eb1d6d974f5e5dada90f4bf'; /// Copied from Dart SDK class _SystemHash { diff --git a/lib/app/pet/controller/pet_weights_controller.dart b/lib/app/pet/controller/pet_weights_controller.dart index 957120a..a837f3f 100644 --- a/lib/app/pet/controller/pet_weights_controller.dart +++ b/lib/app/pet/controller/pet_weights_controller.dart @@ -1,4 +1,6 @@ import 'package:petjournal/app/pet/models/pet_weight_model.dart'; +import 'package:petjournal/app/settings/controllers/settings_controller.dart'; +import 'package:petjournal/constants/linked_record_type.dart'; import 'package:petjournal/data/mapper/pet_weight_mapper.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:petjournal/data/database/database_service.dart'; @@ -10,6 +12,7 @@ class PetWeightsController extends _$PetWeightsController { late final DatabaseService _databaseService = // ignore: avoid_manual_providers_as_generated_provider_dependency ref.read(DatabaseService.provider); + late final _settingsFuture = ref.watch(settingsControllerProvider.future); @override Stream> build(int petId) { @@ -28,9 +31,11 @@ class PetWeightsController extends _$PetWeightsController { petWeight.notes, ); - return newPetWeight == null - ? null - : PetWeightMapper.mapToModel(newPetWeight); + // create linked journal entry if required + if (newPetWeight == null) return null; + final newPetWeightModel = PetWeightMapper.mapToModel(newPetWeight); + await _createLinkedJournalEntry(newPetWeightModel); + return newPetWeightModel; } else { await _databaseService.updatePetWeight( petWeight.petWeightId!, @@ -39,6 +44,9 @@ class PetWeightsController extends _$PetWeightsController { petWeight.weightUnit, petWeight.notes, ); + + await _createLinkedJournalEntry(petWeight, createNew: false); + return petWeight; } } @@ -46,4 +54,31 @@ class PetWeightsController extends _$PetWeightsController { Future deletePetWeight(int id) { return _databaseService.deletePetWeight(id); } + + /// If required, create / update the linked journal entry + Future _createLinkedJournalEntry( + PetWeightModel petWeight, { + bool createNew = true, + }) async { + final settings = await _settingsFuture; + if (settings.createLinkedJournalEntries) { + if (createNew) { + await _databaseService.createJournalEntryForPet( + entryText: petWeight.notes ?? '', + petIdList: [petWeight.petId], + tags: [], + linkedRecordId: petWeight.petWeightId, + linkedRecordType: LinkedRecordType.weight, + linkedRecordTitle: 'Weight ${petWeight.niceName()} recorded', + ); + } else { + await _databaseService.updateLinkedJournalEntry( + linkedRecordId: petWeight.petWeightId!, + linkedRecordType: LinkedRecordType.weight, + linkedRecordTitle: + 'Weight ${petWeight.niceName()} recorded (updated)', + ); + } + } + } } diff --git a/lib/app/pet/controller/pet_weights_controller.g.dart b/lib/app/pet/controller/pet_weights_controller.g.dart index 3855ae3..88307a2 100644 --- a/lib/app/pet/controller/pet_weights_controller.g.dart +++ b/lib/app/pet/controller/pet_weights_controller.g.dart @@ -7,7 +7,7 @@ part of 'pet_weights_controller.dart'; // ************************************************************************** String _$petWeightsControllerHash() => - r'0cbca30502fe8243328fa97e48de2c9d0d6f67b2'; + r'5ad28aa7b0e112414107bb3f61c7eea640e59973'; /// Copied from Dart SDK class _SystemHash { diff --git a/lib/app/pet/models/journal_model.dart b/lib/app/pet/models/journal_model.dart index 9270380..9f371d0 100644 --- a/lib/app/pet/models/journal_model.dart +++ b/lib/app/pet/models/journal_model.dart @@ -1,5 +1,6 @@ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:flutter/foundation.dart'; +import 'package:petjournal/constants/linked_record_type.dart'; part 'journal_model.freezed.dart'; part 'journal_model.g.dart'; @@ -13,6 +14,9 @@ abstract class JournalModel with _$JournalModel { DateTime? lastUpdatedDateTime, required List petIdList, required List tags, + int? linkedRecordId, + LinkedRecordType? linkedRecordType, + String? linkedRecordTitle, }) = _JournalModel; factory JournalModel.fromJson(Map json) => diff --git a/lib/app/pet/models/journal_model.freezed.dart b/lib/app/pet/models/journal_model.freezed.dart index 50a46d6..c82a00f 100644 --- a/lib/app/pet/models/journal_model.freezed.dart +++ b/lib/app/pet/models/journal_model.freezed.dart @@ -16,7 +16,7 @@ T _$identity(T value) => value; /// @nodoc mixin _$JournalModel implements DiagnosticableTreeMixin { - int? get journalEntryId; String get entryText; DateTime? get createdDateTime; DateTime? get lastUpdatedDateTime; List get petIdList; List get tags; + int? get journalEntryId; String get entryText; DateTime? get createdDateTime; DateTime? get lastUpdatedDateTime; List get petIdList; List get tags; int? get linkedRecordId; LinkedRecordType? get linkedRecordType; String? get linkedRecordTitle; /// Create a copy of JournalModel /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @@ -30,21 +30,21 @@ $JournalModelCopyWith get copyWith => _$JournalModelCopyWithImpl Object.hash(runtimeType,journalEntryId,entryText,createdDateTime,lastUpdatedDateTime,const DeepCollectionEquality().hash(petIdList),const DeepCollectionEquality().hash(tags)); +int get hashCode => Object.hash(runtimeType,journalEntryId,entryText,createdDateTime,lastUpdatedDateTime,const DeepCollectionEquality().hash(petIdList),const DeepCollectionEquality().hash(tags),linkedRecordId,linkedRecordType,linkedRecordTitle); @override String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) { - return 'JournalModel(journalEntryId: $journalEntryId, entryText: $entryText, createdDateTime: $createdDateTime, lastUpdatedDateTime: $lastUpdatedDateTime, petIdList: $petIdList, tags: $tags)'; + return 'JournalModel(journalEntryId: $journalEntryId, entryText: $entryText, createdDateTime: $createdDateTime, lastUpdatedDateTime: $lastUpdatedDateTime, petIdList: $petIdList, tags: $tags, linkedRecordId: $linkedRecordId, linkedRecordType: $linkedRecordType, linkedRecordTitle: $linkedRecordTitle)'; } @@ -55,7 +55,7 @@ abstract mixin class $JournalModelCopyWith<$Res> { factory $JournalModelCopyWith(JournalModel value, $Res Function(JournalModel) _then) = _$JournalModelCopyWithImpl; @useResult $Res call({ - int? journalEntryId, String entryText, DateTime? createdDateTime, DateTime? lastUpdatedDateTime, List petIdList, List tags + int? journalEntryId, String entryText, DateTime? createdDateTime, DateTime? lastUpdatedDateTime, List petIdList, List tags, int? linkedRecordId, LinkedRecordType? linkedRecordType, String? linkedRecordTitle }); @@ -72,7 +72,7 @@ class _$JournalModelCopyWithImpl<$Res> /// Create a copy of JournalModel /// with the given fields replaced by the non-null parameter values. -@pragma('vm:prefer-inline') @override $Res call({Object? journalEntryId = freezed,Object? entryText = null,Object? createdDateTime = freezed,Object? lastUpdatedDateTime = freezed,Object? petIdList = null,Object? tags = null,}) { +@pragma('vm:prefer-inline') @override $Res call({Object? journalEntryId = freezed,Object? entryText = null,Object? createdDateTime = freezed,Object? lastUpdatedDateTime = freezed,Object? petIdList = null,Object? tags = null,Object? linkedRecordId = freezed,Object? linkedRecordType = freezed,Object? linkedRecordTitle = freezed,}) { return _then(_self.copyWith( journalEntryId: freezed == journalEntryId ? _self.journalEntryId : journalEntryId // ignore: cast_nullable_to_non_nullable as int?,entryText: null == entryText ? _self.entryText : entryText // ignore: cast_nullable_to_non_nullable @@ -80,7 +80,10 @@ as String,createdDateTime: freezed == createdDateTime ? _self.createdDateTime : as DateTime?,lastUpdatedDateTime: freezed == lastUpdatedDateTime ? _self.lastUpdatedDateTime : lastUpdatedDateTime // ignore: cast_nullable_to_non_nullable as DateTime?,petIdList: null == petIdList ? _self.petIdList : petIdList // ignore: cast_nullable_to_non_nullable as List,tags: null == tags ? _self.tags : tags // ignore: cast_nullable_to_non_nullable -as List, +as List,linkedRecordId: freezed == linkedRecordId ? _self.linkedRecordId : linkedRecordId // ignore: cast_nullable_to_non_nullable +as int?,linkedRecordType: freezed == linkedRecordType ? _self.linkedRecordType : linkedRecordType // ignore: cast_nullable_to_non_nullable +as LinkedRecordType?,linkedRecordTitle: freezed == linkedRecordTitle ? _self.linkedRecordTitle : linkedRecordTitle // ignore: cast_nullable_to_non_nullable +as String?, )); } @@ -91,7 +94,7 @@ as List, @JsonSerializable() class _JournalModel with DiagnosticableTreeMixin implements JournalModel { - const _JournalModel({this.journalEntryId, required this.entryText, this.createdDateTime, this.lastUpdatedDateTime, required final List petIdList, required final List tags}): _petIdList = petIdList,_tags = tags; + const _JournalModel({this.journalEntryId, required this.entryText, this.createdDateTime, this.lastUpdatedDateTime, required final List petIdList, required final List tags, this.linkedRecordId, this.linkedRecordType, this.linkedRecordTitle}): _petIdList = petIdList,_tags = tags; factory _JournalModel.fromJson(Map json) => _$JournalModelFromJson(json); @override final int? journalEntryId; @@ -112,6 +115,9 @@ class _JournalModel with DiagnosticableTreeMixin implements JournalModel { return EqualUnmodifiableListView(_tags); } +@override final int? linkedRecordId; +@override final LinkedRecordType? linkedRecordType; +@override final String? linkedRecordTitle; /// Create a copy of JournalModel /// with the given fields replaced by the non-null parameter values. @@ -127,21 +133,21 @@ Map toJson() { void debugFillProperties(DiagnosticPropertiesBuilder properties) { properties ..add(DiagnosticsProperty('type', 'JournalModel')) - ..add(DiagnosticsProperty('journalEntryId', journalEntryId))..add(DiagnosticsProperty('entryText', entryText))..add(DiagnosticsProperty('createdDateTime', createdDateTime))..add(DiagnosticsProperty('lastUpdatedDateTime', lastUpdatedDateTime))..add(DiagnosticsProperty('petIdList', petIdList))..add(DiagnosticsProperty('tags', tags)); + ..add(DiagnosticsProperty('journalEntryId', journalEntryId))..add(DiagnosticsProperty('entryText', entryText))..add(DiagnosticsProperty('createdDateTime', createdDateTime))..add(DiagnosticsProperty('lastUpdatedDateTime', lastUpdatedDateTime))..add(DiagnosticsProperty('petIdList', petIdList))..add(DiagnosticsProperty('tags', tags))..add(DiagnosticsProperty('linkedRecordId', linkedRecordId))..add(DiagnosticsProperty('linkedRecordType', linkedRecordType))..add(DiagnosticsProperty('linkedRecordTitle', linkedRecordTitle)); } @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is _JournalModel&&(identical(other.journalEntryId, journalEntryId) || other.journalEntryId == journalEntryId)&&(identical(other.entryText, entryText) || other.entryText == entryText)&&(identical(other.createdDateTime, createdDateTime) || other.createdDateTime == createdDateTime)&&(identical(other.lastUpdatedDateTime, lastUpdatedDateTime) || other.lastUpdatedDateTime == lastUpdatedDateTime)&&const DeepCollectionEquality().equals(other._petIdList, _petIdList)&&const DeepCollectionEquality().equals(other._tags, _tags)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is _JournalModel&&(identical(other.journalEntryId, journalEntryId) || other.journalEntryId == journalEntryId)&&(identical(other.entryText, entryText) || other.entryText == entryText)&&(identical(other.createdDateTime, createdDateTime) || other.createdDateTime == createdDateTime)&&(identical(other.lastUpdatedDateTime, lastUpdatedDateTime) || other.lastUpdatedDateTime == lastUpdatedDateTime)&&const DeepCollectionEquality().equals(other._petIdList, _petIdList)&&const DeepCollectionEquality().equals(other._tags, _tags)&&(identical(other.linkedRecordId, linkedRecordId) || other.linkedRecordId == linkedRecordId)&&(identical(other.linkedRecordType, linkedRecordType) || other.linkedRecordType == linkedRecordType)&&(identical(other.linkedRecordTitle, linkedRecordTitle) || other.linkedRecordTitle == linkedRecordTitle)); } @JsonKey(includeFromJson: false, includeToJson: false) @override -int get hashCode => Object.hash(runtimeType,journalEntryId,entryText,createdDateTime,lastUpdatedDateTime,const DeepCollectionEquality().hash(_petIdList),const DeepCollectionEquality().hash(_tags)); +int get hashCode => Object.hash(runtimeType,journalEntryId,entryText,createdDateTime,lastUpdatedDateTime,const DeepCollectionEquality().hash(_petIdList),const DeepCollectionEquality().hash(_tags),linkedRecordId,linkedRecordType,linkedRecordTitle); @override String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) { - return 'JournalModel(journalEntryId: $journalEntryId, entryText: $entryText, createdDateTime: $createdDateTime, lastUpdatedDateTime: $lastUpdatedDateTime, petIdList: $petIdList, tags: $tags)'; + return 'JournalModel(journalEntryId: $journalEntryId, entryText: $entryText, createdDateTime: $createdDateTime, lastUpdatedDateTime: $lastUpdatedDateTime, petIdList: $petIdList, tags: $tags, linkedRecordId: $linkedRecordId, linkedRecordType: $linkedRecordType, linkedRecordTitle: $linkedRecordTitle)'; } @@ -152,7 +158,7 @@ abstract mixin class _$JournalModelCopyWith<$Res> implements $JournalModelCopyWi factory _$JournalModelCopyWith(_JournalModel value, $Res Function(_JournalModel) _then) = __$JournalModelCopyWithImpl; @override @useResult $Res call({ - int? journalEntryId, String entryText, DateTime? createdDateTime, DateTime? lastUpdatedDateTime, List petIdList, List tags + int? journalEntryId, String entryText, DateTime? createdDateTime, DateTime? lastUpdatedDateTime, List petIdList, List tags, int? linkedRecordId, LinkedRecordType? linkedRecordType, String? linkedRecordTitle }); @@ -169,7 +175,7 @@ class __$JournalModelCopyWithImpl<$Res> /// Create a copy of JournalModel /// with the given fields replaced by the non-null parameter values. -@override @pragma('vm:prefer-inline') $Res call({Object? journalEntryId = freezed,Object? entryText = null,Object? createdDateTime = freezed,Object? lastUpdatedDateTime = freezed,Object? petIdList = null,Object? tags = null,}) { +@override @pragma('vm:prefer-inline') $Res call({Object? journalEntryId = freezed,Object? entryText = null,Object? createdDateTime = freezed,Object? lastUpdatedDateTime = freezed,Object? petIdList = null,Object? tags = null,Object? linkedRecordId = freezed,Object? linkedRecordType = freezed,Object? linkedRecordTitle = freezed,}) { return _then(_JournalModel( journalEntryId: freezed == journalEntryId ? _self.journalEntryId : journalEntryId // ignore: cast_nullable_to_non_nullable as int?,entryText: null == entryText ? _self.entryText : entryText // ignore: cast_nullable_to_non_nullable @@ -177,7 +183,10 @@ as String,createdDateTime: freezed == createdDateTime ? _self.createdDateTime : as DateTime?,lastUpdatedDateTime: freezed == lastUpdatedDateTime ? _self.lastUpdatedDateTime : lastUpdatedDateTime // ignore: cast_nullable_to_non_nullable as DateTime?,petIdList: null == petIdList ? _self._petIdList : petIdList // ignore: cast_nullable_to_non_nullable as List,tags: null == tags ? _self._tags : tags // ignore: cast_nullable_to_non_nullable -as List, +as List,linkedRecordId: freezed == linkedRecordId ? _self.linkedRecordId : linkedRecordId // ignore: cast_nullable_to_non_nullable +as int?,linkedRecordType: freezed == linkedRecordType ? _self.linkedRecordType : linkedRecordType // ignore: cast_nullable_to_non_nullable +as LinkedRecordType?,linkedRecordTitle: freezed == linkedRecordTitle ? _self.linkedRecordTitle : linkedRecordTitle // ignore: cast_nullable_to_non_nullable +as String?, )); } diff --git a/lib/app/pet/models/journal_model.g.dart b/lib/app/pet/models/journal_model.g.dart index ec8da76..335a4c8 100644 --- a/lib/app/pet/models/journal_model.g.dart +++ b/lib/app/pet/models/journal_model.g.dart @@ -20,6 +20,12 @@ _JournalModel _$JournalModelFromJson(Map json) => .map((e) => (e as num).toInt()) .toList(), tags: (json['tags'] as List).map((e) => e as String).toList(), + linkedRecordId: (json['linkedRecordId'] as num?)?.toInt(), + linkedRecordType: $enumDecodeNullable( + _$LinkedRecordTypeEnumMap, + json['linkedRecordType'], + ), + linkedRecordTitle: json['linkedRecordTitle'] as String?, ); Map _$JournalModelToJson(_JournalModel instance) => @@ -30,4 +36,14 @@ Map _$JournalModelToJson(_JournalModel instance) => 'lastUpdatedDateTime': instance.lastUpdatedDateTime?.toIso8601String(), 'petIdList': instance.petIdList, 'tags': instance.tags, + 'linkedRecordId': instance.linkedRecordId, + 'linkedRecordType': _$LinkedRecordTypeEnumMap[instance.linkedRecordType], + 'linkedRecordTitle': instance.linkedRecordTitle, }; + +const _$LinkedRecordTypeEnumMap = { + LinkedRecordType.pet: 'pet', + LinkedRecordType.medication: 'medication', + LinkedRecordType.weight: 'weight', + LinkedRecordType.vaccination: 'vaccination', +}; diff --git a/lib/app/pet/models/pet_weight_model.dart b/lib/app/pet/models/pet_weight_model.dart index 340f0a4..7b0f7cd 100644 --- a/lib/app/pet/models/pet_weight_model.dart +++ b/lib/app/pet/models/pet_weight_model.dart @@ -8,6 +8,9 @@ part 'pet_weight_model.g.dart'; @freezed abstract class PetWeightModel with _$PetWeightModel { + // Added constructor. Must not have any parameter + const PetWeightModel._(); + const factory PetWeightModel({ int? petWeightId, required int petId, @@ -19,4 +22,8 @@ abstract class PetWeightModel with _$PetWeightModel { factory PetWeightModel.fromJson(Map json) => _$PetWeightModelFromJson(json); + + String niceName() { + return '$weight ${weightUnit.unitName}'; + } } diff --git a/lib/app/pet/models/pet_weight_model.freezed.dart b/lib/app/pet/models/pet_weight_model.freezed.dart index 7aa3969..2cb5909 100644 --- a/lib/app/pet/models/pet_weight_model.freezed.dart +++ b/lib/app/pet/models/pet_weight_model.freezed.dart @@ -90,8 +90,8 @@ as String?, /// @nodoc @JsonSerializable() -class _PetWeightModel with DiagnosticableTreeMixin implements PetWeightModel { - const _PetWeightModel({this.petWeightId, required this.petId, required this.date, required this.weight, required this.weightUnit, this.notes}); +class _PetWeightModel extends PetWeightModel with DiagnosticableTreeMixin { + const _PetWeightModel({this.petWeightId, required this.petId, required this.date, required this.weight, required this.weightUnit, this.notes}): super._(); factory _PetWeightModel.fromJson(Map json) => _$PetWeightModelFromJson(json); @override final int? petWeightId; diff --git a/lib/app/pet/views/widgets/journal_entry_widget.dart b/lib/app/pet/views/widgets/journal_entry_widget.dart index c7bfdfd..64bbdc6 100644 --- a/lib/app/pet/views/widgets/journal_entry_widget.dart +++ b/lib/app/pet/views/widgets/journal_entry_widget.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:petjournal/app/pet/models/journal_model.dart'; import 'package:petjournal/app/pet/models/pet_model.dart'; +import 'package:petjournal/constants/linked_record_type.dart'; import 'package:petjournal/data/lookups/pet_lookup.dart'; import 'package:petjournal/helpers/date_helper.dart'; @@ -10,6 +11,12 @@ class JournalEntryWidget extends StatelessWidget { final JournalModel journalEntry; final Function(JournalModel) onTap; final int? hidePetId; + final Map linkedRecordIcons = const { + LinkedRecordType.pet: Icon(Icons.pets), + LinkedRecordType.medication: Icon(Icons.medication), + LinkedRecordType.weight: Icon(Icons.balance), + LinkedRecordType.vaccination: Icon(Icons.vaccines), + }; const JournalEntryWidget({ super.key, @@ -30,6 +37,7 @@ class JournalEntryWidget extends StatelessWidget { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ + ?_buildLinkedRecordDetails(journalEntry), Padding( padding: const EdgeInsets.all(8.0), child: Text( @@ -78,6 +86,26 @@ class JournalEntryWidget extends StatelessWidget { ); } + Widget? _buildLinkedRecordDetails(JournalModel journalEntry) { + if (journalEntry.linkedRecordId == null) return null; + + return Row( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: + linkedRecordIcons[journalEntry.linkedRecordType] ?? + const Icon(Icons.pets), + ), + Text( + journalEntry.linkedRecordTitle ?? '', + style: const TextStyle(fontWeight: FontWeight.bold), + overflow: TextOverflow.ellipsis, + ), + ], + ); + } + /// Build the collection of chips highlighting the pets linked to this journal entry Widget? _buildPetChips(List petIdList) { List chips = []; diff --git a/lib/app/settings/controllers/settings_controller.dart b/lib/app/settings/controllers/settings_controller.dart index 45077e0..2c5f5bb 100644 --- a/lib/app/settings/controllers/settings_controller.dart +++ b/lib/app/settings/controllers/settings_controller.dart @@ -41,10 +41,12 @@ class SettingsController extends _$SettingsController { Future saveUserSettings( WeightUnits? defaultWeightUnit, bool? optIntoAnalyticsWarning, + bool? createLinkedJournalEntries, ) async { var updateCount = await _databaseService.saveSettingsUser( defaultWeightUnit, optIntoAnalyticsWarning, + createLinkedJournalEntries, ); if (updateCount == 1) { diff --git a/lib/app/settings/controllers/settings_controller.g.dart b/lib/app/settings/controllers/settings_controller.g.dart index 68c85d2..3f1c294 100644 --- a/lib/app/settings/controllers/settings_controller.g.dart +++ b/lib/app/settings/controllers/settings_controller.g.dart @@ -7,7 +7,7 @@ part of 'settings_controller.dart'; // ************************************************************************** String _$settingsControllerHash() => - r'7a70f24a650ccf91da086671dc7fe908d1527e99'; + r'7139c5d1f6bc82425a46fd2c30854d5c07f464c8'; /// See also [SettingsController]. @ProviderFor(SettingsController) diff --git a/lib/app/settings/models/settings_model.dart b/lib/app/settings/models/settings_model.dart index 2dd6093..1592f97 100644 --- a/lib/app/settings/models/settings_model.dart +++ b/lib/app/settings/models/settings_model.dart @@ -14,6 +14,7 @@ abstract class SettingsModel with _$SettingsModel { required bool onBoardingComplete, required String? lastUsedVersion, required WeightUnits? defaultWeightUnit, + required bool createLinkedJournalEntries, }) = _SettingsModel; factory SettingsModel.fromJson(Map json) => diff --git a/lib/app/settings/models/settings_model.freezed.dart b/lib/app/settings/models/settings_model.freezed.dart index 8cb67fb..ce47e84 100644 --- a/lib/app/settings/models/settings_model.freezed.dart +++ b/lib/app/settings/models/settings_model.freezed.dart @@ -16,7 +16,7 @@ T _$identity(T value) => value; /// @nodoc mixin _$SettingsModel implements DiagnosticableTreeMixin { - bool get acceptedTermsAndConditions; bool get optIntoAnalyticsWarning; bool get onBoardingComplete; String? get lastUsedVersion; WeightUnits? get defaultWeightUnit; + bool get acceptedTermsAndConditions; bool get optIntoAnalyticsWarning; bool get onBoardingComplete; String? get lastUsedVersion; WeightUnits? get defaultWeightUnit; bool get createLinkedJournalEntries; /// Create a copy of SettingsModel /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @@ -30,21 +30,21 @@ $SettingsModelCopyWith get copyWith => _$SettingsModelCopyWithImp void debugFillProperties(DiagnosticPropertiesBuilder properties) { properties ..add(DiagnosticsProperty('type', 'SettingsModel')) - ..add(DiagnosticsProperty('acceptedTermsAndConditions', acceptedTermsAndConditions))..add(DiagnosticsProperty('optIntoAnalyticsWarning', optIntoAnalyticsWarning))..add(DiagnosticsProperty('onBoardingComplete', onBoardingComplete))..add(DiagnosticsProperty('lastUsedVersion', lastUsedVersion))..add(DiagnosticsProperty('defaultWeightUnit', defaultWeightUnit)); + ..add(DiagnosticsProperty('acceptedTermsAndConditions', acceptedTermsAndConditions))..add(DiagnosticsProperty('optIntoAnalyticsWarning', optIntoAnalyticsWarning))..add(DiagnosticsProperty('onBoardingComplete', onBoardingComplete))..add(DiagnosticsProperty('lastUsedVersion', lastUsedVersion))..add(DiagnosticsProperty('defaultWeightUnit', defaultWeightUnit))..add(DiagnosticsProperty('createLinkedJournalEntries', createLinkedJournalEntries)); } @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is SettingsModel&&(identical(other.acceptedTermsAndConditions, acceptedTermsAndConditions) || other.acceptedTermsAndConditions == acceptedTermsAndConditions)&&(identical(other.optIntoAnalyticsWarning, optIntoAnalyticsWarning) || other.optIntoAnalyticsWarning == optIntoAnalyticsWarning)&&(identical(other.onBoardingComplete, onBoardingComplete) || other.onBoardingComplete == onBoardingComplete)&&(identical(other.lastUsedVersion, lastUsedVersion) || other.lastUsedVersion == lastUsedVersion)&&(identical(other.defaultWeightUnit, defaultWeightUnit) || other.defaultWeightUnit == defaultWeightUnit)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is SettingsModel&&(identical(other.acceptedTermsAndConditions, acceptedTermsAndConditions) || other.acceptedTermsAndConditions == acceptedTermsAndConditions)&&(identical(other.optIntoAnalyticsWarning, optIntoAnalyticsWarning) || other.optIntoAnalyticsWarning == optIntoAnalyticsWarning)&&(identical(other.onBoardingComplete, onBoardingComplete) || other.onBoardingComplete == onBoardingComplete)&&(identical(other.lastUsedVersion, lastUsedVersion) || other.lastUsedVersion == lastUsedVersion)&&(identical(other.defaultWeightUnit, defaultWeightUnit) || other.defaultWeightUnit == defaultWeightUnit)&&(identical(other.createLinkedJournalEntries, createLinkedJournalEntries) || other.createLinkedJournalEntries == createLinkedJournalEntries)); } @JsonKey(includeFromJson: false, includeToJson: false) @override -int get hashCode => Object.hash(runtimeType,acceptedTermsAndConditions,optIntoAnalyticsWarning,onBoardingComplete,lastUsedVersion,defaultWeightUnit); +int get hashCode => Object.hash(runtimeType,acceptedTermsAndConditions,optIntoAnalyticsWarning,onBoardingComplete,lastUsedVersion,defaultWeightUnit,createLinkedJournalEntries); @override String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) { - return 'SettingsModel(acceptedTermsAndConditions: $acceptedTermsAndConditions, optIntoAnalyticsWarning: $optIntoAnalyticsWarning, onBoardingComplete: $onBoardingComplete, lastUsedVersion: $lastUsedVersion, defaultWeightUnit: $defaultWeightUnit)'; + return 'SettingsModel(acceptedTermsAndConditions: $acceptedTermsAndConditions, optIntoAnalyticsWarning: $optIntoAnalyticsWarning, onBoardingComplete: $onBoardingComplete, lastUsedVersion: $lastUsedVersion, defaultWeightUnit: $defaultWeightUnit, createLinkedJournalEntries: $createLinkedJournalEntries)'; } @@ -55,7 +55,7 @@ abstract mixin class $SettingsModelCopyWith<$Res> { factory $SettingsModelCopyWith(SettingsModel value, $Res Function(SettingsModel) _then) = _$SettingsModelCopyWithImpl; @useResult $Res call({ - bool acceptedTermsAndConditions, bool optIntoAnalyticsWarning, bool onBoardingComplete, String? lastUsedVersion, WeightUnits? defaultWeightUnit + bool acceptedTermsAndConditions, bool optIntoAnalyticsWarning, bool onBoardingComplete, String? lastUsedVersion, WeightUnits? defaultWeightUnit, bool createLinkedJournalEntries }); @@ -72,14 +72,15 @@ class _$SettingsModelCopyWithImpl<$Res> /// Create a copy of SettingsModel /// with the given fields replaced by the non-null parameter values. -@pragma('vm:prefer-inline') @override $Res call({Object? acceptedTermsAndConditions = null,Object? optIntoAnalyticsWarning = null,Object? onBoardingComplete = null,Object? lastUsedVersion = freezed,Object? defaultWeightUnit = freezed,}) { +@pragma('vm:prefer-inline') @override $Res call({Object? acceptedTermsAndConditions = null,Object? optIntoAnalyticsWarning = null,Object? onBoardingComplete = null,Object? lastUsedVersion = freezed,Object? defaultWeightUnit = freezed,Object? createLinkedJournalEntries = null,}) { return _then(_self.copyWith( acceptedTermsAndConditions: null == acceptedTermsAndConditions ? _self.acceptedTermsAndConditions : acceptedTermsAndConditions // ignore: cast_nullable_to_non_nullable as bool,optIntoAnalyticsWarning: null == optIntoAnalyticsWarning ? _self.optIntoAnalyticsWarning : optIntoAnalyticsWarning // ignore: cast_nullable_to_non_nullable as bool,onBoardingComplete: null == onBoardingComplete ? _self.onBoardingComplete : onBoardingComplete // ignore: cast_nullable_to_non_nullable as bool,lastUsedVersion: freezed == lastUsedVersion ? _self.lastUsedVersion : lastUsedVersion // ignore: cast_nullable_to_non_nullable as String?,defaultWeightUnit: freezed == defaultWeightUnit ? _self.defaultWeightUnit : defaultWeightUnit // ignore: cast_nullable_to_non_nullable -as WeightUnits?, +as WeightUnits?,createLinkedJournalEntries: null == createLinkedJournalEntries ? _self.createLinkedJournalEntries : createLinkedJournalEntries // ignore: cast_nullable_to_non_nullable +as bool, )); } @@ -90,7 +91,7 @@ as WeightUnits?, @JsonSerializable() class _SettingsModel with DiagnosticableTreeMixin implements SettingsModel { - const _SettingsModel({required this.acceptedTermsAndConditions, required this.optIntoAnalyticsWarning, required this.onBoardingComplete, required this.lastUsedVersion, required this.defaultWeightUnit}); + const _SettingsModel({required this.acceptedTermsAndConditions, required this.optIntoAnalyticsWarning, required this.onBoardingComplete, required this.lastUsedVersion, required this.defaultWeightUnit, required this.createLinkedJournalEntries}); factory _SettingsModel.fromJson(Map json) => _$SettingsModelFromJson(json); @override final bool acceptedTermsAndConditions; @@ -98,6 +99,7 @@ class _SettingsModel with DiagnosticableTreeMixin implements SettingsModel { @override final bool onBoardingComplete; @override final String? lastUsedVersion; @override final WeightUnits? defaultWeightUnit; +@override final bool createLinkedJournalEntries; /// Create a copy of SettingsModel /// with the given fields replaced by the non-null parameter values. @@ -113,21 +115,21 @@ Map toJson() { void debugFillProperties(DiagnosticPropertiesBuilder properties) { properties ..add(DiagnosticsProperty('type', 'SettingsModel')) - ..add(DiagnosticsProperty('acceptedTermsAndConditions', acceptedTermsAndConditions))..add(DiagnosticsProperty('optIntoAnalyticsWarning', optIntoAnalyticsWarning))..add(DiagnosticsProperty('onBoardingComplete', onBoardingComplete))..add(DiagnosticsProperty('lastUsedVersion', lastUsedVersion))..add(DiagnosticsProperty('defaultWeightUnit', defaultWeightUnit)); + ..add(DiagnosticsProperty('acceptedTermsAndConditions', acceptedTermsAndConditions))..add(DiagnosticsProperty('optIntoAnalyticsWarning', optIntoAnalyticsWarning))..add(DiagnosticsProperty('onBoardingComplete', onBoardingComplete))..add(DiagnosticsProperty('lastUsedVersion', lastUsedVersion))..add(DiagnosticsProperty('defaultWeightUnit', defaultWeightUnit))..add(DiagnosticsProperty('createLinkedJournalEntries', createLinkedJournalEntries)); } @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is _SettingsModel&&(identical(other.acceptedTermsAndConditions, acceptedTermsAndConditions) || other.acceptedTermsAndConditions == acceptedTermsAndConditions)&&(identical(other.optIntoAnalyticsWarning, optIntoAnalyticsWarning) || other.optIntoAnalyticsWarning == optIntoAnalyticsWarning)&&(identical(other.onBoardingComplete, onBoardingComplete) || other.onBoardingComplete == onBoardingComplete)&&(identical(other.lastUsedVersion, lastUsedVersion) || other.lastUsedVersion == lastUsedVersion)&&(identical(other.defaultWeightUnit, defaultWeightUnit) || other.defaultWeightUnit == defaultWeightUnit)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is _SettingsModel&&(identical(other.acceptedTermsAndConditions, acceptedTermsAndConditions) || other.acceptedTermsAndConditions == acceptedTermsAndConditions)&&(identical(other.optIntoAnalyticsWarning, optIntoAnalyticsWarning) || other.optIntoAnalyticsWarning == optIntoAnalyticsWarning)&&(identical(other.onBoardingComplete, onBoardingComplete) || other.onBoardingComplete == onBoardingComplete)&&(identical(other.lastUsedVersion, lastUsedVersion) || other.lastUsedVersion == lastUsedVersion)&&(identical(other.defaultWeightUnit, defaultWeightUnit) || other.defaultWeightUnit == defaultWeightUnit)&&(identical(other.createLinkedJournalEntries, createLinkedJournalEntries) || other.createLinkedJournalEntries == createLinkedJournalEntries)); } @JsonKey(includeFromJson: false, includeToJson: false) @override -int get hashCode => Object.hash(runtimeType,acceptedTermsAndConditions,optIntoAnalyticsWarning,onBoardingComplete,lastUsedVersion,defaultWeightUnit); +int get hashCode => Object.hash(runtimeType,acceptedTermsAndConditions,optIntoAnalyticsWarning,onBoardingComplete,lastUsedVersion,defaultWeightUnit,createLinkedJournalEntries); @override String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) { - return 'SettingsModel(acceptedTermsAndConditions: $acceptedTermsAndConditions, optIntoAnalyticsWarning: $optIntoAnalyticsWarning, onBoardingComplete: $onBoardingComplete, lastUsedVersion: $lastUsedVersion, defaultWeightUnit: $defaultWeightUnit)'; + return 'SettingsModel(acceptedTermsAndConditions: $acceptedTermsAndConditions, optIntoAnalyticsWarning: $optIntoAnalyticsWarning, onBoardingComplete: $onBoardingComplete, lastUsedVersion: $lastUsedVersion, defaultWeightUnit: $defaultWeightUnit, createLinkedJournalEntries: $createLinkedJournalEntries)'; } @@ -138,7 +140,7 @@ abstract mixin class _$SettingsModelCopyWith<$Res> implements $SettingsModelCopy factory _$SettingsModelCopyWith(_SettingsModel value, $Res Function(_SettingsModel) _then) = __$SettingsModelCopyWithImpl; @override @useResult $Res call({ - bool acceptedTermsAndConditions, bool optIntoAnalyticsWarning, bool onBoardingComplete, String? lastUsedVersion, WeightUnits? defaultWeightUnit + bool acceptedTermsAndConditions, bool optIntoAnalyticsWarning, bool onBoardingComplete, String? lastUsedVersion, WeightUnits? defaultWeightUnit, bool createLinkedJournalEntries }); @@ -155,14 +157,15 @@ class __$SettingsModelCopyWithImpl<$Res> /// Create a copy of SettingsModel /// with the given fields replaced by the non-null parameter values. -@override @pragma('vm:prefer-inline') $Res call({Object? acceptedTermsAndConditions = null,Object? optIntoAnalyticsWarning = null,Object? onBoardingComplete = null,Object? lastUsedVersion = freezed,Object? defaultWeightUnit = freezed,}) { +@override @pragma('vm:prefer-inline') $Res call({Object? acceptedTermsAndConditions = null,Object? optIntoAnalyticsWarning = null,Object? onBoardingComplete = null,Object? lastUsedVersion = freezed,Object? defaultWeightUnit = freezed,Object? createLinkedJournalEntries = null,}) { return _then(_SettingsModel( acceptedTermsAndConditions: null == acceptedTermsAndConditions ? _self.acceptedTermsAndConditions : acceptedTermsAndConditions // ignore: cast_nullable_to_non_nullable as bool,optIntoAnalyticsWarning: null == optIntoAnalyticsWarning ? _self.optIntoAnalyticsWarning : optIntoAnalyticsWarning // ignore: cast_nullable_to_non_nullable as bool,onBoardingComplete: null == onBoardingComplete ? _self.onBoardingComplete : onBoardingComplete // ignore: cast_nullable_to_non_nullable as bool,lastUsedVersion: freezed == lastUsedVersion ? _self.lastUsedVersion : lastUsedVersion // ignore: cast_nullable_to_non_nullable as String?,defaultWeightUnit: freezed == defaultWeightUnit ? _self.defaultWeightUnit : defaultWeightUnit // ignore: cast_nullable_to_non_nullable -as WeightUnits?, +as WeightUnits?,createLinkedJournalEntries: null == createLinkedJournalEntries ? _self.createLinkedJournalEntries : createLinkedJournalEntries // ignore: cast_nullable_to_non_nullable +as bool, )); } diff --git a/lib/app/settings/models/settings_model.g.dart b/lib/app/settings/models/settings_model.g.dart index 9165fae..6887910 100644 --- a/lib/app/settings/models/settings_model.g.dart +++ b/lib/app/settings/models/settings_model.g.dart @@ -16,6 +16,7 @@ _SettingsModel _$SettingsModelFromJson(Map json) => _$WeightUnitsEnumMap, json['defaultWeightUnit'], ), + createLinkedJournalEntries: json['createLinkedJournalEntries'] as bool, ); Map _$SettingsModelToJson(_SettingsModel instance) => @@ -25,6 +26,7 @@ Map _$SettingsModelToJson(_SettingsModel instance) => 'onBoardingComplete': instance.onBoardingComplete, 'lastUsedVersion': instance.lastUsedVersion, 'defaultWeightUnit': _$WeightUnitsEnumMap[instance.defaultWeightUnit], + 'createLinkedJournalEntries': instance.createLinkedJournalEntries, }; const _$WeightUnitsEnumMap = { diff --git a/lib/app/settings/views/settings_screen.dart b/lib/app/settings/views/settings_screen.dart index a4d121d..aea6e99 100644 --- a/lib/app/settings/views/settings_screen.dart +++ b/lib/app/settings/views/settings_screen.dart @@ -87,6 +87,8 @@ class _SettingsScreenState extends ConsumerState { weightUnits: settings.defaultWeightUnit, optIntoAnalyticsWarning: settings.optIntoAnalyticsWarning, + createLinkedJournalEntries: + settings.createLinkedJournalEntries, ); return EditSettingsWidget( userSettings: _userSettings!, @@ -143,7 +145,11 @@ class _SettingsScreenState extends ConsumerState { await ref .read(settingsControllerProvider.notifier) - .saveUserSettings(_userSettings?.weightUnits, _userSettings?.optIntoAnalyticsWarning); + .saveUserSettings( + _userSettings?.weightUnits, + _userSettings?.optIntoAnalyticsWarning, + _userSettings?.createLinkedJournalEntries, + ); // // enable / disable analytics // getIt() @@ -217,12 +223,12 @@ class _SettingsScreenState extends ConsumerState { await databaseService.clearAllData(); } catch (e) { if (!mounted) return; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('Error removing data: $e')), - ); + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text('Error removing data: $e'))); return; } - + // Show success message if (!mounted) return; diff --git a/lib/app/settings/views/widgets/edit_settings_widget.dart b/lib/app/settings/views/widgets/edit_settings_widget.dart index e75d653..bba1eed 100644 --- a/lib/app/settings/views/widgets/edit_settings_widget.dart +++ b/lib/app/settings/views/widgets/edit_settings_widget.dart @@ -2,11 +2,15 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:petjournal/constants/weight_units.dart'; import 'package:petjournal/widgets/analytics_opt_in.dart'; +import 'package:petjournal/widgets/custom_switch.dart'; import 'package:petjournal/widgets/weight_units_dropdown.dart'; class EditSettingsWidget extends ConsumerStatefulWidget { static const editDefaultWeightUnit = Key('weightUnit'); static const editSettingOptIntoAnalyticsKey = Key('optIntoAnalytics'); + static const editSettingCreateLinkedJournalEntriesKey = Key( + 'createLinkedJournalEntries', + ); final EditSettingsData userSettings; final Function(EditSettingsData) onChanged; @@ -24,13 +28,16 @@ class EditSettingsWidget extends ConsumerStatefulWidget { class _EditSettingsWidgetState extends ConsumerState { WeightUnits? _weightUnits; bool _optIntoAnalyticsWarning = false; + bool _createLinkedJournalEntries = true; @override void initState() { super.initState(); - + _weightUnits = widget.userSettings.weightUnits; _optIntoAnalyticsWarning = widget.userSettings.optIntoAnalyticsWarning; + _createLinkedJournalEntries = + widget.userSettings.createLinkedJournalEntries; } @override @@ -58,6 +65,17 @@ class _EditSettingsWidgetState extends ConsumerState { _triggerOnChange(); }, ), + CustomSwitch( + key: EditSettingsWidget.editSettingCreateLinkedJournalEntriesKey, + switchValue: _createLinkedJournalEntries, + labelText: 'Create Linked Journal Entries', + onChanged: (newValue) { + setState(() { + _createLinkedJournalEntries = newValue; + }); + _triggerOnChange(); + }, + ), ], ); } @@ -67,6 +85,7 @@ class _EditSettingsWidgetState extends ConsumerState { EditSettingsData( weightUnits: _weightUnits, optIntoAnalyticsWarning: _optIntoAnalyticsWarning, + createLinkedJournalEntries: _createLinkedJournalEntries, ), ); } @@ -75,9 +94,11 @@ class _EditSettingsWidgetState extends ConsumerState { class EditSettingsData { final WeightUnits? weightUnits; final bool optIntoAnalyticsWarning; + final bool createLinkedJournalEntries; EditSettingsData({ required this.weightUnits, required this.optIntoAnalyticsWarning, + required this.createLinkedJournalEntries, }); } diff --git a/lib/app_startup.g.dart b/lib/app_startup.g.dart index 4d3369e..3fbf5ca 100644 --- a/lib/app_startup.g.dart +++ b/lib/app_startup.g.dart @@ -6,7 +6,7 @@ part of 'app_startup.dart'; // RiverpodGenerator // ************************************************************************** -String _$appStartupHash() => r'18682cb27bf4ba1fb869fde3de4861e4b751fc02'; +String _$appStartupHash() => r'880ec514607ff5573746cb806a679781a76ec3e1'; /// See also [appStartup]. @ProviderFor(appStartup) diff --git a/lib/constants/linked_record_type.dart b/lib/constants/linked_record_type.dart new file mode 100644 index 0000000..b560058 --- /dev/null +++ b/lib/constants/linked_record_type.dart @@ -0,0 +1,10 @@ +enum LinkedRecordType { + pet(niceName: 'Pet'), + medication(niceName: 'Medication'), + weight(niceName: 'Weight'), + vaccination(niceName: 'Vaccination'); + + final String niceName; + + const LinkedRecordType({required this.niceName}); +} diff --git a/lib/constants/weight_units.dart b/lib/constants/weight_units.dart index d5decc5..0978868 100644 --- a/lib/constants/weight_units.dart +++ b/lib/constants/weight_units.dart @@ -1,16 +1,22 @@ enum WeightUnits implements Comparable { - metric(dataValue: 0, niceName: 'Metric (kg)'), - imperial(dataValue: 1, niceName: 'Imperial (lbs)'); + metric(dataValue: 0, niceName: 'Metric (kg)', unitName: 'kg'), + imperial(dataValue: 1, niceName: 'Imperial (lbs)', unitName: 'lbs'); final int dataValue; final String niceName; + final String unitName; - const WeightUnits({required this.dataValue, required this.niceName}); + const WeightUnits({ + required this.dataValue, + required this.niceName, + required this.unitName, + }); static WeightUnits fromDataValue(int dataValue) { return WeightUnits.values.firstWhere( (unit) => unit.dataValue == dataValue, - orElse: () => throw Exception('Unknown data value for WeightUnits: $dataValue'), + orElse: () => + throw Exception('Unknown data value for WeightUnits: $dataValue'), ); } diff --git a/lib/data/database/database_service.dart b/lib/data/database/database_service.dart index 4a665be..1ba7cfc 100644 --- a/lib/data/database/database_service.dart +++ b/lib/data/database/database_service.dart @@ -5,6 +5,7 @@ import 'package:petjournal/constants/defaults.dart'; import 'package:petjournal/constants/pet_sex.dart'; import 'package:petjournal/constants/pet_status.dart'; import 'package:petjournal/constants/weight_units.dart'; +import 'package:petjournal/constants/linked_record_type.dart'; import 'package:petjournal/data/database/tables/journal_entry.dart'; import 'package:petjournal/data/database/tables/journal_entry_details.dart'; import 'package:petjournal/data/database/tables/journal_entry_tag.dart'; @@ -433,6 +434,9 @@ class DatabaseService extends _$DatabaseService { required String entryText, required List petIdList, required List tags, + LinkedRecordType? linkedRecordType, + int? linkedRecordId, + String? linkedRecordTitle, }) async { if (petIdList.isEmpty) { throw Exception('No pets provided'); @@ -442,7 +446,12 @@ class DatabaseService extends _$DatabaseService { try { // Create the journal entry final journalEntry = await into(journalEntries).insertReturningOrNull( - JournalEntriesCompanion.insert(entryText: entryText), + JournalEntriesCompanion.insert( + entryText: entryText, + linkedRecordType: Value.absentIfNull(linkedRecordType), + linkedRecordId: Value.absentIfNull(linkedRecordId), + linkedRecordTitle: Value.absentIfNull(linkedRecordTitle), + ), ); // If the journal entry creation failed, return null @@ -484,6 +493,8 @@ class DatabaseService extends _$DatabaseService { } /// Update an existing journal entry record + /// Note: linked record details are not updated as assume once set a + /// journal entry cannot be moved to a different linked record Future updateJournalEntry({ required int id, required String entryText, @@ -545,6 +556,32 @@ class DatabaseService extends _$DatabaseService { } } + /// Update an linked journal entry record + /// Note: linked record details are not updated as assume once set a + /// journal entry cannot be moved to a different linked record + Future updateLinkedJournalEntry({ + required int linkedRecordId, + required LinkedRecordType linkedRecordType, + required String linkedRecordTitle, + }) async { + try { + return await (update(journalEntries)..where( + (entry) => Expression.and([ + entry.linkedRecordId.equals(linkedRecordId), + entry.linkedRecordType.equals(linkedRecordType.name), + ]), + )) + .write( + JournalEntriesCompanion( + linkedRecordTitle: Value(linkedRecordTitle), + lastUpdatedDateTime: Value(DateTime.now()), + ), + ); + } catch (e) { + throw Exception('Error updating linked journal entry: $e'); + } + } + /// Retrieve a single journal entry by its ID Future getJournalEntry(int id) async { try { @@ -772,6 +809,7 @@ class DatabaseService extends _$DatabaseService { optIntoAnalyticsWarning: false, lastUsedVersion: null, defaultWeightUnit: null, + createLinkedJournalEntries: true, ); try { @@ -810,6 +848,7 @@ class DatabaseService extends _$DatabaseService { Future saveSettingsUser( WeightUnits? defaultWeightUnit, bool? optIntoAnalyticsWarning, + bool? createLinkedJournalEntries, ) async { try { return await (update( @@ -818,6 +857,9 @@ class DatabaseService extends _$DatabaseService { SettingsCompanion( defaultWeightUnit: Value(defaultWeightUnit?.dataValue), optIntoAnalyticsWarning: Value.absentIfNull(optIntoAnalyticsWarning), + createLinkedJournalEntries: Value.absentIfNull( + createLinkedJournalEntries, + ), ), ); } catch (e) { @@ -826,7 +868,7 @@ class DatabaseService extends _$DatabaseService { } Future resetSettingsUser() async { - return saveSettingsUser(null, false); + return saveSettingsUser(null, false, true); } Future testConnection() async { diff --git a/lib/data/database/database_service.g.dart b/lib/data/database/database_service.g.dart index b5c7070..f43e49d 100644 --- a/lib/data/database/database_service.g.dart +++ b/lib/data/database/database_service.g.dart @@ -2989,12 +2989,50 @@ class $JournalEntriesTable extends JournalEntries type: DriftSqlType.dateTime, requiredDuringInsert: false, ); + static const VerificationMeta _linkedRecordIdMeta = const VerificationMeta( + 'linkedRecordId', + ); + @override + late final GeneratedColumn linkedRecordId = GeneratedColumn( + 'linked_record_id', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + @override + late final GeneratedColumnWithTypeConverter + linkedRecordType = + GeneratedColumn( + 'linked_record_type', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ).withConverter( + $JournalEntriesTable.$converterlinkedRecordTypen, + ); + static const VerificationMeta _linkedRecordTitleMeta = const VerificationMeta( + 'linkedRecordTitle', + ); + @override + late final GeneratedColumn linkedRecordTitle = + GeneratedColumn( + 'linked_record_title', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); @override List get $columns => [ journalEntryId, entryText, createdDateTime, lastUpdatedDateTime, + linkedRecordId, + linkedRecordType, + linkedRecordTitle, ]; @override String get aliasedName => _alias ?? actualTableName; @@ -3043,6 +3081,24 @@ class $JournalEntriesTable extends JournalEntries ), ); } + if (data.containsKey('linked_record_id')) { + context.handle( + _linkedRecordIdMeta, + linkedRecordId.isAcceptableOrUnknown( + data['linked_record_id']!, + _linkedRecordIdMeta, + ), + ); + } + if (data.containsKey('linked_record_title')) { + context.handle( + _linkedRecordTitleMeta, + linkedRecordTitle.isAcceptableOrUnknown( + data['linked_record_title']!, + _linkedRecordTitleMeta, + ), + ); + } return context; } @@ -3068,6 +3124,21 @@ class $JournalEntriesTable extends JournalEntries DriftSqlType.dateTime, data['${effectivePrefix}last_updated_date_time'], ), + linkedRecordId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}linked_record_id'], + ), + linkedRecordType: $JournalEntriesTable.$converterlinkedRecordTypen + .fromSql( + attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}linked_record_type'], + ), + ), + linkedRecordTitle: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}linked_record_title'], + ), ); } @@ -3075,6 +3146,15 @@ class $JournalEntriesTable extends JournalEntries $JournalEntriesTable createAlias(String alias) { return $JournalEntriesTable(attachedDatabase, alias); } + + static JsonTypeConverter2 + $converterlinkedRecordType = const EnumNameConverter( + LinkedRecordType.values, + ); + static JsonTypeConverter2 + $converterlinkedRecordTypen = JsonTypeConverter2.asNullable( + $converterlinkedRecordType, + ); } class JournalEntry extends DataClass implements Insertable { @@ -3082,11 +3162,17 @@ class JournalEntry extends DataClass implements Insertable { final String entryText; final DateTime createdDateTime; final DateTime? lastUpdatedDateTime; + final int? linkedRecordId; + final LinkedRecordType? linkedRecordType; + final String? linkedRecordTitle; const JournalEntry({ required this.journalEntryId, required this.entryText, required this.createdDateTime, this.lastUpdatedDateTime, + this.linkedRecordId, + this.linkedRecordType, + this.linkedRecordTitle, }); @override Map toColumns(bool nullToAbsent) { @@ -3097,6 +3183,19 @@ class JournalEntry extends DataClass implements Insertable { if (!nullToAbsent || lastUpdatedDateTime != null) { map['last_updated_date_time'] = Variable(lastUpdatedDateTime); } + if (!nullToAbsent || linkedRecordId != null) { + map['linked_record_id'] = Variable(linkedRecordId); + } + if (!nullToAbsent || linkedRecordType != null) { + map['linked_record_type'] = Variable( + $JournalEntriesTable.$converterlinkedRecordTypen.toSql( + linkedRecordType, + ), + ); + } + if (!nullToAbsent || linkedRecordTitle != null) { + map['linked_record_title'] = Variable(linkedRecordTitle); + } return map; } @@ -3108,6 +3207,15 @@ class JournalEntry extends DataClass implements Insertable { lastUpdatedDateTime: lastUpdatedDateTime == null && nullToAbsent ? const Value.absent() : Value(lastUpdatedDateTime), + linkedRecordId: linkedRecordId == null && nullToAbsent + ? const Value.absent() + : Value(linkedRecordId), + linkedRecordType: linkedRecordType == null && nullToAbsent + ? const Value.absent() + : Value(linkedRecordType), + linkedRecordTitle: linkedRecordTitle == null && nullToAbsent + ? const Value.absent() + : Value(linkedRecordTitle), ); } @@ -3123,6 +3231,12 @@ class JournalEntry extends DataClass implements Insertable { lastUpdatedDateTime: serializer.fromJson( json['lastUpdatedDateTime'], ), + linkedRecordId: serializer.fromJson(json['linkedRecordId']), + linkedRecordType: $JournalEntriesTable.$converterlinkedRecordTypen + .fromJson(serializer.fromJson(json['linkedRecordType'])), + linkedRecordTitle: serializer.fromJson( + json['linkedRecordTitle'], + ), ); } @override @@ -3133,6 +3247,13 @@ class JournalEntry extends DataClass implements Insertable { 'entryText': serializer.toJson(entryText), 'createdDateTime': serializer.toJson(createdDateTime), 'lastUpdatedDateTime': serializer.toJson(lastUpdatedDateTime), + 'linkedRecordId': serializer.toJson(linkedRecordId), + 'linkedRecordType': serializer.toJson( + $JournalEntriesTable.$converterlinkedRecordTypen.toJson( + linkedRecordType, + ), + ), + 'linkedRecordTitle': serializer.toJson(linkedRecordTitle), }; } @@ -3141,6 +3262,9 @@ class JournalEntry extends DataClass implements Insertable { String? entryText, DateTime? createdDateTime, Value lastUpdatedDateTime = const Value.absent(), + Value linkedRecordId = const Value.absent(), + Value linkedRecordType = const Value.absent(), + Value linkedRecordTitle = const Value.absent(), }) => JournalEntry( journalEntryId: journalEntryId ?? this.journalEntryId, entryText: entryText ?? this.entryText, @@ -3148,6 +3272,15 @@ class JournalEntry extends DataClass implements Insertable { lastUpdatedDateTime: lastUpdatedDateTime.present ? lastUpdatedDateTime.value : this.lastUpdatedDateTime, + linkedRecordId: linkedRecordId.present + ? linkedRecordId.value + : this.linkedRecordId, + linkedRecordType: linkedRecordType.present + ? linkedRecordType.value + : this.linkedRecordType, + linkedRecordTitle: linkedRecordTitle.present + ? linkedRecordTitle.value + : this.linkedRecordTitle, ); JournalEntry copyWithCompanion(JournalEntriesCompanion data) { return JournalEntry( @@ -3161,6 +3294,15 @@ class JournalEntry extends DataClass implements Insertable { lastUpdatedDateTime: data.lastUpdatedDateTime.present ? data.lastUpdatedDateTime.value : this.lastUpdatedDateTime, + linkedRecordId: data.linkedRecordId.present + ? data.linkedRecordId.value + : this.linkedRecordId, + linkedRecordType: data.linkedRecordType.present + ? data.linkedRecordType.value + : this.linkedRecordType, + linkedRecordTitle: data.linkedRecordTitle.present + ? data.linkedRecordTitle.value + : this.linkedRecordTitle, ); } @@ -3170,7 +3312,10 @@ class JournalEntry extends DataClass implements Insertable { ..write('journalEntryId: $journalEntryId, ') ..write('entryText: $entryText, ') ..write('createdDateTime: $createdDateTime, ') - ..write('lastUpdatedDateTime: $lastUpdatedDateTime') + ..write('lastUpdatedDateTime: $lastUpdatedDateTime, ') + ..write('linkedRecordId: $linkedRecordId, ') + ..write('linkedRecordType: $linkedRecordType, ') + ..write('linkedRecordTitle: $linkedRecordTitle') ..write(')')) .toString(); } @@ -3181,6 +3326,9 @@ class JournalEntry extends DataClass implements Insertable { entryText, createdDateTime, lastUpdatedDateTime, + linkedRecordId, + linkedRecordType, + linkedRecordTitle, ); @override bool operator ==(Object other) => @@ -3189,7 +3337,10 @@ class JournalEntry extends DataClass implements Insertable { other.journalEntryId == this.journalEntryId && other.entryText == this.entryText && other.createdDateTime == this.createdDateTime && - other.lastUpdatedDateTime == this.lastUpdatedDateTime); + other.lastUpdatedDateTime == this.lastUpdatedDateTime && + other.linkedRecordId == this.linkedRecordId && + other.linkedRecordType == this.linkedRecordType && + other.linkedRecordTitle == this.linkedRecordTitle); } class JournalEntriesCompanion extends UpdateCompanion { @@ -3197,23 +3348,35 @@ class JournalEntriesCompanion extends UpdateCompanion { final Value entryText; final Value createdDateTime; final Value lastUpdatedDateTime; + final Value linkedRecordId; + final Value linkedRecordType; + final Value linkedRecordTitle; const JournalEntriesCompanion({ this.journalEntryId = const Value.absent(), this.entryText = const Value.absent(), this.createdDateTime = const Value.absent(), this.lastUpdatedDateTime = const Value.absent(), + this.linkedRecordId = const Value.absent(), + this.linkedRecordType = const Value.absent(), + this.linkedRecordTitle = const Value.absent(), }); JournalEntriesCompanion.insert({ this.journalEntryId = const Value.absent(), required String entryText, this.createdDateTime = const Value.absent(), this.lastUpdatedDateTime = const Value.absent(), + this.linkedRecordId = const Value.absent(), + this.linkedRecordType = const Value.absent(), + this.linkedRecordTitle = const Value.absent(), }) : entryText = Value(entryText); static Insertable custom({ Expression? journalEntryId, Expression? entryText, Expression? createdDateTime, Expression? lastUpdatedDateTime, + Expression? linkedRecordId, + Expression? linkedRecordType, + Expression? linkedRecordTitle, }) { return RawValuesInsertable({ if (journalEntryId != null) 'journal_entry_id': journalEntryId, @@ -3221,6 +3384,9 @@ class JournalEntriesCompanion extends UpdateCompanion { if (createdDateTime != null) 'created_date_time': createdDateTime, if (lastUpdatedDateTime != null) 'last_updated_date_time': lastUpdatedDateTime, + if (linkedRecordId != null) 'linked_record_id': linkedRecordId, + if (linkedRecordType != null) 'linked_record_type': linkedRecordType, + if (linkedRecordTitle != null) 'linked_record_title': linkedRecordTitle, }); } @@ -3229,12 +3395,18 @@ class JournalEntriesCompanion extends UpdateCompanion { Value? entryText, Value? createdDateTime, Value? lastUpdatedDateTime, + Value? linkedRecordId, + Value? linkedRecordType, + Value? linkedRecordTitle, }) { return JournalEntriesCompanion( journalEntryId: journalEntryId ?? this.journalEntryId, entryText: entryText ?? this.entryText, createdDateTime: createdDateTime ?? this.createdDateTime, lastUpdatedDateTime: lastUpdatedDateTime ?? this.lastUpdatedDateTime, + linkedRecordId: linkedRecordId ?? this.linkedRecordId, + linkedRecordType: linkedRecordType ?? this.linkedRecordType, + linkedRecordTitle: linkedRecordTitle ?? this.linkedRecordTitle, ); } @@ -3255,6 +3427,19 @@ class JournalEntriesCompanion extends UpdateCompanion { lastUpdatedDateTime.value, ); } + if (linkedRecordId.present) { + map['linked_record_id'] = Variable(linkedRecordId.value); + } + if (linkedRecordType.present) { + map['linked_record_type'] = Variable( + $JournalEntriesTable.$converterlinkedRecordTypen.toSql( + linkedRecordType.value, + ), + ); + } + if (linkedRecordTitle.present) { + map['linked_record_title'] = Variable(linkedRecordTitle.value); + } return map; } @@ -3264,7 +3449,10 @@ class JournalEntriesCompanion extends UpdateCompanion { ..write('journalEntryId: $journalEntryId, ') ..write('entryText: $entryText, ') ..write('createdDateTime: $createdDateTime, ') - ..write('lastUpdatedDateTime: $lastUpdatedDateTime') + ..write('lastUpdatedDateTime: $lastUpdatedDateTime, ') + ..write('linkedRecordId: $linkedRecordId, ') + ..write('linkedRecordType: $linkedRecordType, ') + ..write('linkedRecordTitle: $linkedRecordTitle') ..write(')')) .toString(); } @@ -3858,6 +4046,21 @@ class $SettingsTable extends Settings with TableInfo<$SettingsTable, Setting> { type: DriftSqlType.int, requiredDuringInsert: false, ); + static const VerificationMeta _createLinkedJournalEntriesMeta = + const VerificationMeta('createLinkedJournalEntries'); + @override + late final GeneratedColumn createLinkedJournalEntries = + GeneratedColumn( + 'create_linked_journal_entries', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("create_linked_journal_entries" IN (0, 1))', + ), + defaultValue: const Constant(true), + ); @override List get $columns => [ settingsId, @@ -3866,6 +4069,7 @@ class $SettingsTable extends Settings with TableInfo<$SettingsTable, Setting> { optIntoAnalyticsWarning, lastUsedVersion, defaultWeightUnit, + createLinkedJournalEntries, ]; @override String get aliasedName => _alias ?? actualTableName; @@ -3930,6 +4134,15 @@ class $SettingsTable extends Settings with TableInfo<$SettingsTable, Setting> { ), ); } + if (data.containsKey('create_linked_journal_entries')) { + context.handle( + _createLinkedJournalEntriesMeta, + createLinkedJournalEntries.isAcceptableOrUnknown( + data['create_linked_journal_entries']!, + _createLinkedJournalEntriesMeta, + ), + ); + } return context; } @@ -3963,6 +4176,10 @@ class $SettingsTable extends Settings with TableInfo<$SettingsTable, Setting> { DriftSqlType.int, data['${effectivePrefix}default_weight_unit'], ), + createLinkedJournalEntries: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}create_linked_journal_entries'], + )!, ); } @@ -3979,6 +4196,7 @@ class Setting extends DataClass implements Insertable { final bool optIntoAnalyticsWarning; final String? lastUsedVersion; final int? defaultWeightUnit; + final bool createLinkedJournalEntries; const Setting({ required this.settingsId, required this.acceptedTermsAndConditions, @@ -3986,6 +4204,7 @@ class Setting extends DataClass implements Insertable { required this.optIntoAnalyticsWarning, this.lastUsedVersion, this.defaultWeightUnit, + required this.createLinkedJournalEntries, }); @override Map toColumns(bool nullToAbsent) { @@ -4002,6 +4221,9 @@ class Setting extends DataClass implements Insertable { if (!nullToAbsent || defaultWeightUnit != null) { map['default_weight_unit'] = Variable(defaultWeightUnit); } + map['create_linked_journal_entries'] = Variable( + createLinkedJournalEntries, + ); return map; } @@ -4017,6 +4239,7 @@ class Setting extends DataClass implements Insertable { defaultWeightUnit: defaultWeightUnit == null && nullToAbsent ? const Value.absent() : Value(defaultWeightUnit), + createLinkedJournalEntries: Value(createLinkedJournalEntries), ); } @@ -4036,6 +4259,9 @@ class Setting extends DataClass implements Insertable { ), lastUsedVersion: serializer.fromJson(json['lastUsedVersion']), defaultWeightUnit: serializer.fromJson(json['defaultWeightUnit']), + createLinkedJournalEntries: serializer.fromJson( + json['createLinkedJournalEntries'], + ), ); } @override @@ -4052,6 +4278,9 @@ class Setting extends DataClass implements Insertable { ), 'lastUsedVersion': serializer.toJson(lastUsedVersion), 'defaultWeightUnit': serializer.toJson(defaultWeightUnit), + 'createLinkedJournalEntries': serializer.toJson( + createLinkedJournalEntries, + ), }; } @@ -4062,6 +4291,7 @@ class Setting extends DataClass implements Insertable { bool? optIntoAnalyticsWarning, Value lastUsedVersion = const Value.absent(), Value defaultWeightUnit = const Value.absent(), + bool? createLinkedJournalEntries, }) => Setting( settingsId: settingsId ?? this.settingsId, acceptedTermsAndConditions: @@ -4075,6 +4305,8 @@ class Setting extends DataClass implements Insertable { defaultWeightUnit: defaultWeightUnit.present ? defaultWeightUnit.value : this.defaultWeightUnit, + createLinkedJournalEntries: + createLinkedJournalEntries ?? this.createLinkedJournalEntries, ); Setting copyWithCompanion(SettingsCompanion data) { return Setting( @@ -4096,6 +4328,9 @@ class Setting extends DataClass implements Insertable { defaultWeightUnit: data.defaultWeightUnit.present ? data.defaultWeightUnit.value : this.defaultWeightUnit, + createLinkedJournalEntries: data.createLinkedJournalEntries.present + ? data.createLinkedJournalEntries.value + : this.createLinkedJournalEntries, ); } @@ -4107,7 +4342,8 @@ class Setting extends DataClass implements Insertable { ..write('onBoardingComplete: $onBoardingComplete, ') ..write('optIntoAnalyticsWarning: $optIntoAnalyticsWarning, ') ..write('lastUsedVersion: $lastUsedVersion, ') - ..write('defaultWeightUnit: $defaultWeightUnit') + ..write('defaultWeightUnit: $defaultWeightUnit, ') + ..write('createLinkedJournalEntries: $createLinkedJournalEntries') ..write(')')) .toString(); } @@ -4120,6 +4356,7 @@ class Setting extends DataClass implements Insertable { optIntoAnalyticsWarning, lastUsedVersion, defaultWeightUnit, + createLinkedJournalEntries, ); @override bool operator ==(Object other) => @@ -4130,7 +4367,8 @@ class Setting extends DataClass implements Insertable { other.onBoardingComplete == this.onBoardingComplete && other.optIntoAnalyticsWarning == this.optIntoAnalyticsWarning && other.lastUsedVersion == this.lastUsedVersion && - other.defaultWeightUnit == this.defaultWeightUnit); + other.defaultWeightUnit == this.defaultWeightUnit && + other.createLinkedJournalEntries == this.createLinkedJournalEntries); } class SettingsCompanion extends UpdateCompanion { @@ -4140,6 +4378,7 @@ class SettingsCompanion extends UpdateCompanion { final Value optIntoAnalyticsWarning; final Value lastUsedVersion; final Value defaultWeightUnit; + final Value createLinkedJournalEntries; const SettingsCompanion({ this.settingsId = const Value.absent(), this.acceptedTermsAndConditions = const Value.absent(), @@ -4147,6 +4386,7 @@ class SettingsCompanion extends UpdateCompanion { this.optIntoAnalyticsWarning = const Value.absent(), this.lastUsedVersion = const Value.absent(), this.defaultWeightUnit = const Value.absent(), + this.createLinkedJournalEntries = const Value.absent(), }); SettingsCompanion.insert({ this.settingsId = const Value.absent(), @@ -4155,6 +4395,7 @@ class SettingsCompanion extends UpdateCompanion { this.optIntoAnalyticsWarning = const Value.absent(), this.lastUsedVersion = const Value.absent(), this.defaultWeightUnit = const Value.absent(), + this.createLinkedJournalEntries = const Value.absent(), }); static Insertable custom({ Expression? settingsId, @@ -4163,6 +4404,7 @@ class SettingsCompanion extends UpdateCompanion { Expression? optIntoAnalyticsWarning, Expression? lastUsedVersion, Expression? defaultWeightUnit, + Expression? createLinkedJournalEntries, }) { return RawValuesInsertable({ if (settingsId != null) 'settings_id': settingsId, @@ -4174,6 +4416,8 @@ class SettingsCompanion extends UpdateCompanion { 'opt_into_analytics_warning': optIntoAnalyticsWarning, if (lastUsedVersion != null) 'last_used_version': lastUsedVersion, if (defaultWeightUnit != null) 'default_weight_unit': defaultWeightUnit, + if (createLinkedJournalEntries != null) + 'create_linked_journal_entries': createLinkedJournalEntries, }); } @@ -4184,6 +4428,7 @@ class SettingsCompanion extends UpdateCompanion { Value? optIntoAnalyticsWarning, Value? lastUsedVersion, Value? defaultWeightUnit, + Value? createLinkedJournalEntries, }) { return SettingsCompanion( settingsId: settingsId ?? this.settingsId, @@ -4194,6 +4439,8 @@ class SettingsCompanion extends UpdateCompanion { optIntoAnalyticsWarning ?? this.optIntoAnalyticsWarning, lastUsedVersion: lastUsedVersion ?? this.lastUsedVersion, defaultWeightUnit: defaultWeightUnit ?? this.defaultWeightUnit, + createLinkedJournalEntries: + createLinkedJournalEntries ?? this.createLinkedJournalEntries, ); } @@ -4222,6 +4469,11 @@ class SettingsCompanion extends UpdateCompanion { if (defaultWeightUnit.present) { map['default_weight_unit'] = Variable(defaultWeightUnit.value); } + if (createLinkedJournalEntries.present) { + map['create_linked_journal_entries'] = Variable( + createLinkedJournalEntries.value, + ); + } return map; } @@ -4233,7 +4485,8 @@ class SettingsCompanion extends UpdateCompanion { ..write('onBoardingComplete: $onBoardingComplete, ') ..write('optIntoAnalyticsWarning: $optIntoAnalyticsWarning, ') ..write('lastUsedVersion: $lastUsedVersion, ') - ..write('defaultWeightUnit: $defaultWeightUnit') + ..write('defaultWeightUnit: $defaultWeightUnit, ') + ..write('createLinkedJournalEntries: $createLinkedJournalEntries') ..write(')')) .toString(); } @@ -6716,6 +6969,9 @@ typedef $$JournalEntriesTableCreateCompanionBuilder = required String entryText, Value createdDateTime, Value lastUpdatedDateTime, + Value linkedRecordId, + Value linkedRecordType, + Value linkedRecordTitle, }); typedef $$JournalEntriesTableUpdateCompanionBuilder = JournalEntriesCompanion Function({ @@ -6723,6 +6979,9 @@ typedef $$JournalEntriesTableUpdateCompanionBuilder = Value entryText, Value createdDateTime, Value lastUpdatedDateTime, + Value linkedRecordId, + Value linkedRecordType, + Value linkedRecordTitle, }); final class $$JournalEntriesTableReferences @@ -6819,6 +7078,22 @@ class $$JournalEntriesTableFilterComposer builder: (column) => ColumnFilters(column), ); + ColumnFilters get linkedRecordId => $composableBuilder( + column: $table.linkedRecordId, + builder: (column) => ColumnFilters(column), + ); + + ColumnWithTypeConverterFilters + get linkedRecordType => $composableBuilder( + column: $table.linkedRecordType, + builder: (column) => ColumnWithTypeConverterFilters(column), + ); + + ColumnFilters get linkedRecordTitle => $composableBuilder( + column: $table.linkedRecordTitle, + builder: (column) => ColumnFilters(column), + ); + Expression journalEntryTagsRefs( Expression Function($$JournalEntryTagsTableFilterComposer f) f, ) { @@ -6898,6 +7173,21 @@ class $$JournalEntriesTableOrderingComposer column: $table.lastUpdatedDateTime, builder: (column) => ColumnOrderings(column), ); + + ColumnOrderings get linkedRecordId => $composableBuilder( + column: $table.linkedRecordId, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get linkedRecordType => $composableBuilder( + column: $table.linkedRecordType, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get linkedRecordTitle => $composableBuilder( + column: $table.linkedRecordTitle, + builder: (column) => ColumnOrderings(column), + ); } class $$JournalEntriesTableAnnotationComposer @@ -6927,6 +7217,22 @@ class $$JournalEntriesTableAnnotationComposer builder: (column) => column, ); + GeneratedColumn get linkedRecordId => $composableBuilder( + column: $table.linkedRecordId, + builder: (column) => column, + ); + + GeneratedColumnWithTypeConverter + get linkedRecordType => $composableBuilder( + column: $table.linkedRecordType, + builder: (column) => column, + ); + + GeneratedColumn get linkedRecordTitle => $composableBuilder( + column: $table.linkedRecordTitle, + builder: (column) => column, + ); + Expression journalEntryTagsRefs( Expression Function($$JournalEntryTagsTableAnnotationComposer a) f, ) { @@ -7016,11 +7322,18 @@ class $$JournalEntriesTableTableManager Value entryText = const Value.absent(), Value createdDateTime = const Value.absent(), Value lastUpdatedDateTime = const Value.absent(), + Value linkedRecordId = const Value.absent(), + Value linkedRecordType = + const Value.absent(), + Value linkedRecordTitle = const Value.absent(), }) => JournalEntriesCompanion( journalEntryId: journalEntryId, entryText: entryText, createdDateTime: createdDateTime, lastUpdatedDateTime: lastUpdatedDateTime, + linkedRecordId: linkedRecordId, + linkedRecordType: linkedRecordType, + linkedRecordTitle: linkedRecordTitle, ), createCompanionCallback: ({ @@ -7028,11 +7341,18 @@ class $$JournalEntriesTableTableManager required String entryText, Value createdDateTime = const Value.absent(), Value lastUpdatedDateTime = const Value.absent(), + Value linkedRecordId = const Value.absent(), + Value linkedRecordType = + const Value.absent(), + Value linkedRecordTitle = const Value.absent(), }) => JournalEntriesCompanion.insert( journalEntryId: journalEntryId, entryText: entryText, createdDateTime: createdDateTime, lastUpdatedDateTime: lastUpdatedDateTime, + linkedRecordId: linkedRecordId, + linkedRecordType: linkedRecordType, + linkedRecordTitle: linkedRecordTitle, ), withReferenceMapper: (p0) => p0 .map( @@ -7791,6 +8111,7 @@ typedef $$SettingsTableCreateCompanionBuilder = Value optIntoAnalyticsWarning, Value lastUsedVersion, Value defaultWeightUnit, + Value createLinkedJournalEntries, }); typedef $$SettingsTableUpdateCompanionBuilder = SettingsCompanion Function({ @@ -7800,6 +8121,7 @@ typedef $$SettingsTableUpdateCompanionBuilder = Value optIntoAnalyticsWarning, Value lastUsedVersion, Value defaultWeightUnit, + Value createLinkedJournalEntries, }); class $$SettingsTableFilterComposer @@ -7840,6 +8162,11 @@ class $$SettingsTableFilterComposer column: $table.defaultWeightUnit, builder: (column) => ColumnFilters(column), ); + + ColumnFilters get createLinkedJournalEntries => $composableBuilder( + column: $table.createLinkedJournalEntries, + builder: (column) => ColumnFilters(column), + ); } class $$SettingsTableOrderingComposer @@ -7880,6 +8207,11 @@ class $$SettingsTableOrderingComposer column: $table.defaultWeightUnit, builder: (column) => ColumnOrderings(column), ); + + ColumnOrderings get createLinkedJournalEntries => $composableBuilder( + column: $table.createLinkedJournalEntries, + builder: (column) => ColumnOrderings(column), + ); } class $$SettingsTableAnnotationComposer @@ -7920,6 +8252,11 @@ class $$SettingsTableAnnotationComposer column: $table.defaultWeightUnit, builder: (column) => column, ); + + GeneratedColumn get createLinkedJournalEntries => $composableBuilder( + column: $table.createLinkedJournalEntries, + builder: (column) => column, + ); } class $$SettingsTableTableManager @@ -7956,6 +8293,7 @@ class $$SettingsTableTableManager Value optIntoAnalyticsWarning = const Value.absent(), Value lastUsedVersion = const Value.absent(), Value defaultWeightUnit = const Value.absent(), + Value createLinkedJournalEntries = const Value.absent(), }) => SettingsCompanion( settingsId: settingsId, acceptedTermsAndConditions: acceptedTermsAndConditions, @@ -7963,6 +8301,7 @@ class $$SettingsTableTableManager optIntoAnalyticsWarning: optIntoAnalyticsWarning, lastUsedVersion: lastUsedVersion, defaultWeightUnit: defaultWeightUnit, + createLinkedJournalEntries: createLinkedJournalEntries, ), createCompanionCallback: ({ @@ -7972,6 +8311,7 @@ class $$SettingsTableTableManager Value optIntoAnalyticsWarning = const Value.absent(), Value lastUsedVersion = const Value.absent(), Value defaultWeightUnit = const Value.absent(), + Value createLinkedJournalEntries = const Value.absent(), }) => SettingsCompanion.insert( settingsId: settingsId, acceptedTermsAndConditions: acceptedTermsAndConditions, @@ -7979,6 +8319,7 @@ class $$SettingsTableTableManager optIntoAnalyticsWarning: optIntoAnalyticsWarning, lastUsedVersion: lastUsedVersion, defaultWeightUnit: defaultWeightUnit, + createLinkedJournalEntries: createLinkedJournalEntries, ), withReferenceMapper: (p0) => p0 .map((e) => (e.readTable(table), BaseReferences(db, table, e))) diff --git a/lib/data/database/tables/journal_entry.dart b/lib/data/database/tables/journal_entry.dart index a26a434..27d22d5 100644 --- a/lib/data/database/tables/journal_entry.dart +++ b/lib/data/database/tables/journal_entry.dart @@ -1,4 +1,5 @@ import 'package:drift/drift.dart'; +import 'package:petjournal/constants/linked_record_type.dart'; // Table definition for journalEntry table // Need to run 'dart run build_runner build' after making changes to this file @@ -9,4 +10,7 @@ class JournalEntries extends Table { DateTimeColumn get createdDateTime => dateTime().withDefault(currentDateAndTime)(); DateTimeColumn get lastUpdatedDateTime => dateTime().nullable()(); + IntColumn get linkedRecordId => integer().nullable()(); + TextColumn get linkedRecordType => textEnum().nullable()(); + TextColumn get linkedRecordTitle => text().nullable()(); } diff --git a/lib/data/database/tables/setting.dart b/lib/data/database/tables/setting.dart index 9a18adf..29ae9ef 100644 --- a/lib/data/database/tables/setting.dart +++ b/lib/data/database/tables/setting.dart @@ -13,6 +13,8 @@ class Settings extends Table { boolean().withDefault(const Constant(false))(); TextColumn get lastUsedVersion => text().nullable().withLength(max: 10)(); IntColumn get defaultWeightUnit => integer().nullable()(); + BoolColumn get createLinkedJournalEntries => + boolean().withDefault(const Constant(true))(); @override Set get primaryKey => {settingsId}; diff --git a/lib/data/mapper/journal_mapper.dart b/lib/data/mapper/journal_mapper.dart index dc4d3ab..6a46bd3 100644 --- a/lib/data/mapper/journal_mapper.dart +++ b/lib/data/mapper/journal_mapper.dart @@ -10,6 +10,9 @@ class JournalMapper { entryText: journalEntryDetail.journalEntry.entryText, petIdList: journalEntryDetail.pets.map((pet) => pet.petId).toList(), tags: journalEntryDetail.tags.map((tag) => tag.tag).toList(), + linkedRecordId: journalEntryDetail.journalEntry.linkedRecordId, + linkedRecordType: journalEntryDetail.journalEntry.linkedRecordType, + linkedRecordTitle: journalEntryDetail.journalEntry.linkedRecordTitle, ); } diff --git a/lib/data/mapper/settings_mapper.dart b/lib/data/mapper/settings_mapper.dart index 80e49e3..86fb377 100644 --- a/lib/data/mapper/settings_mapper.dart +++ b/lib/data/mapper/settings_mapper.dart @@ -11,6 +11,7 @@ class SettingsMapper { onBoardingComplete: false, lastUsedVersion: null, defaultWeightUnit: null, + createLinkedJournalEntries: true, ); } @@ -20,10 +21,10 @@ class SettingsMapper { optIntoAnalyticsWarning: settings.optIntoAnalyticsWarning, onBoardingComplete: settings.onBoardingComplete, lastUsedVersion: settings.lastUsedVersion, - defaultWeightUnit: - settings.defaultWeightUnit == null - ? null - : WeightUnits.fromDataValue(settings.defaultWeightUnit!), + defaultWeightUnit: settings.defaultWeightUnit == null + ? null + : WeightUnits.fromDataValue(settings.defaultWeightUnit!), + createLinkedJournalEntries: settings.createLinkedJournalEntries, ); } } diff --git a/test/app/home/views/home_screen_test.mocks.dart b/test/app/home/views/home_screen_test.mocks.dart index 58f4527..d2a4edd 100644 --- a/test/app/home/views/home_screen_test.mocks.dart +++ b/test/app/home/views/home_screen_test.mocks.dart @@ -9,12 +9,13 @@ import 'package:drift/drift.dart' as _i2; import 'package:drift/src/runtime/executor/stream_queries.dart' as _i4; import 'package:mockito/mockito.dart' as _i1; import 'package:mockito/src/dummies.dart' as _i6; +import 'package:petjournal/constants/linked_record_type.dart' as _i10; import 'package:petjournal/constants/pet_sex.dart' as _i7; import 'package:petjournal/constants/pet_status.dart' as _i8; import 'package:petjournal/constants/weight_units.dart' as _i9; import 'package:petjournal/data/database/database_service.dart' as _i3; import 'package:petjournal/data/database/tables/journal_entry_details.dart' - as _i10; + as _i11; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -809,12 +810,18 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { required String? entryText, required List? petIdList, required List? tags, + _i10.LinkedRecordType? linkedRecordType, + int? linkedRecordId, + String? linkedRecordTitle, }) => (super.noSuchMethod( Invocation.method(#createJournalEntryForPet, [], { #entryText: entryText, #petIdList: petIdList, #tags: tags, + #linkedRecordType: linkedRecordType, + #linkedRecordId: linkedRecordId, + #linkedRecordTitle: linkedRecordTitle, }), returnValue: _i5.Future<_i3.JournalEntry?>.value(), ) @@ -838,6 +845,22 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { ) as _i5.Future); + @override + _i5.Future updateLinkedJournalEntry({ + required int? linkedRecordId, + required _i10.LinkedRecordType? linkedRecordType, + required String? linkedRecordTitle, + }) => + (super.noSuchMethod( + Invocation.method(#updateLinkedJournalEntry, [], { + #linkedRecordId: linkedRecordId, + #linkedRecordType: linkedRecordType, + #linkedRecordTitle: linkedRecordTitle, + }), + returnValue: _i5.Future.value(0), + ) + as _i5.Future); + @override _i5.Future<_i3.JournalEntry?> getJournalEntry(int? id) => (super.noSuchMethod( @@ -863,14 +886,14 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { as _i5.Stream>); @override - _i5.Stream> getAllJournalEntryDetailsForPet( + _i5.Stream> getAllJournalEntryDetailsForPet( int? petId, ) => (super.noSuchMethod( Invocation.method(#getAllJournalEntryDetailsForPet, [petId]), - returnValue: _i5.Stream>.empty(), + returnValue: _i5.Stream>.empty(), ) - as _i5.Stream>); + as _i5.Stream>); @override _i5.Future deleteJournalEntry(int? id) => @@ -1005,11 +1028,13 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { _i5.Future saveSettingsUser( _i9.WeightUnits? defaultWeightUnit, bool? optIntoAnalyticsWarning, + bool? createLinkedJournalEntries, ) => (super.noSuchMethod( Invocation.method(#saveSettingsUser, [ defaultWeightUnit, optIntoAnalyticsWarning, + createLinkedJournalEntries, ]), returnValue: _i5.Future.value(0), ) diff --git a/test/app/home/views/welcome_screen_test.dart b/test/app/home/views/welcome_screen_test.dart index 331bb49..57bf0c8 100644 --- a/test/app/home/views/welcome_screen_test.dart +++ b/test/app/home/views/welcome_screen_test.dart @@ -42,6 +42,9 @@ void main() { acceptedTermsAndConditions: false, onBoardingComplete: false, optIntoAnalyticsWarning: false, + lastUsedVersion: null, + defaultWeightUnit: null, + createLinkedJournalEntries: true, ), ); diff --git a/test/app/home/views/welcome_screen_test.mocks.dart b/test/app/home/views/welcome_screen_test.mocks.dart index a4ac3e1..51f0fa0 100644 --- a/test/app/home/views/welcome_screen_test.mocks.dart +++ b/test/app/home/views/welcome_screen_test.mocks.dart @@ -11,18 +11,19 @@ import 'package:flutter/widgets.dart' as _i7; import 'package:go_router/src/configuration.dart' as _i6; import 'package:go_router/src/delegate.dart' as _i8; import 'package:go_router/src/information_provider.dart' as _i9; -import 'package:go_router/src/match.dart' as _i18; +import 'package:go_router/src/match.dart' as _i19; import 'package:go_router/src/parser.dart' as _i10; -import 'package:go_router/src/router.dart' as _i17; +import 'package:go_router/src/router.dart' as _i18; import 'package:go_router/src/state.dart' as _i11; import 'package:mockito/mockito.dart' as _i1; import 'package:mockito/src/dummies.dart' as _i12; +import 'package:petjournal/constants/linked_record_type.dart' as _i16; import 'package:petjournal/constants/pet_sex.dart' as _i13; import 'package:petjournal/constants/pet_status.dart' as _i14; import 'package:petjournal/constants/weight_units.dart' as _i15; import 'package:petjournal/data/database/database_service.dart' as _i3; import 'package:petjournal/data/database/tables/journal_entry_details.dart' - as _i16; + as _i17; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -853,12 +854,18 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { required String? entryText, required List? petIdList, required List? tags, + _i16.LinkedRecordType? linkedRecordType, + int? linkedRecordId, + String? linkedRecordTitle, }) => (super.noSuchMethod( Invocation.method(#createJournalEntryForPet, [], { #entryText: entryText, #petIdList: petIdList, #tags: tags, + #linkedRecordType: linkedRecordType, + #linkedRecordId: linkedRecordId, + #linkedRecordTitle: linkedRecordTitle, }), returnValue: _i5.Future<_i3.JournalEntry?>.value(), ) @@ -882,6 +889,22 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { ) as _i5.Future); + @override + _i5.Future updateLinkedJournalEntry({ + required int? linkedRecordId, + required _i16.LinkedRecordType? linkedRecordType, + required String? linkedRecordTitle, + }) => + (super.noSuchMethod( + Invocation.method(#updateLinkedJournalEntry, [], { + #linkedRecordId: linkedRecordId, + #linkedRecordType: linkedRecordType, + #linkedRecordTitle: linkedRecordTitle, + }), + returnValue: _i5.Future.value(0), + ) + as _i5.Future); + @override _i5.Future<_i3.JournalEntry?> getJournalEntry(int? id) => (super.noSuchMethod( @@ -907,14 +930,14 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { as _i5.Stream>); @override - _i5.Stream> getAllJournalEntryDetailsForPet( + _i5.Stream> getAllJournalEntryDetailsForPet( int? petId, ) => (super.noSuchMethod( Invocation.method(#getAllJournalEntryDetailsForPet, [petId]), - returnValue: _i5.Stream>.empty(), + returnValue: _i5.Stream>.empty(), ) - as _i5.Stream>); + as _i5.Stream>); @override _i5.Future deleteJournalEntry(int? id) => @@ -1049,11 +1072,13 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { _i5.Future saveSettingsUser( _i15.WeightUnits? defaultWeightUnit, bool? optIntoAnalyticsWarning, + bool? createLinkedJournalEntries, ) => (super.noSuchMethod( Invocation.method(#saveSettingsUser, [ defaultWeightUnit, optIntoAnalyticsWarning, + createLinkedJournalEntries, ]), returnValue: _i5.Future.value(0), ) @@ -1549,7 +1574,7 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { /// A class which mocks [GoRouter]. /// /// See the documentation for Mockito's code generation for more information. -class MockGoRouter extends _i1.Mock implements _i17.GoRouter { +class MockGoRouter extends _i1.Mock implements _i18.GoRouter { MockGoRouter() { _i1.throwOnMissingStub(this); } @@ -1699,7 +1724,7 @@ class MockGoRouter extends _i1.Mock implements _i17.GoRouter { ); @override - void restore(_i18.RouteMatchList? matchList) => super.noSuchMethod( + void restore(_i19.RouteMatchList? matchList) => super.noSuchMethod( Invocation.method(#restore, [matchList]), returnValueForMissingStub: null, ); diff --git a/test/app/pet/controllers/all_pets_controller_test.dart b/test/app/pet/controllers/all_pets_controller_test.dart index e5fc06e..b7e58c2 100644 --- a/test/app/pet/controllers/all_pets_controller_test.dart +++ b/test/app/pet/controllers/all_pets_controller_test.dart @@ -5,8 +5,11 @@ import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:petjournal/app/pet/controller/all_pets_controller.dart'; import 'package:petjournal/app/pet/models/pet_model.dart'; +import 'package:petjournal/constants/defaults.dart' as defaults; +import 'package:petjournal/constants/linked_record_type.dart'; import 'package:petjournal/constants/pet_sex.dart'; import 'package:petjournal/constants/pet_status.dart'; +import 'package:petjournal/constants/weight_units.dart'; import 'package:petjournal/data/database/database_service.dart'; import 'all_pets_controller_test.mocks.dart'; @@ -38,6 +41,32 @@ void main() { mockDatabaseService = MockDatabaseService(); }); + setupMockDatabaseForJournalEntry(bool createLinkedJournalEntries) { + when(mockDatabaseService.watchSettings()).thenAnswer( + (_) => Stream.value( + Setting( + settingsId: defaults.defaultSettingsId, + acceptedTermsAndConditions: true, + optIntoAnalyticsWarning: true, + onBoardingComplete: true, + defaultWeightUnit: WeightUnits.metric.dataValue, + createLinkedJournalEntries: createLinkedJournalEntries, + ), + ), + ); + + when( + mockDatabaseService.createJournalEntryForPet( + entryText: anyNamed('entryText'), + petIdList: anyNamed('petIdList'), + tags: anyNamed('tags'), + linkedRecordId: anyNamed('linkedRecordId'), + linkedRecordType: anyNamed('linkedRecordType'), + linkedRecordTitle: anyNamed('linkedRecordTitle'), + ), + ).thenAnswer((_) => Future.value()); + } + group('build', () { testWidgets('Should emit empty list When no pets exist', (tester) async { // ARRANGE @@ -320,6 +349,7 @@ void main() { initialPetModel.imageUrl, ), ).thenAnswer((_) => Future.value(initialPet)); + setupMockDatabaseForJournalEntry(false); final container = createContainer( overrides: [ @@ -503,5 +533,202 @@ void main() { await tester.pumpAndSettle(); }); }); + + group('create / update linked Journal Entries', () { + testWidgets('Should create a linked journal entry when call createPet with setting createLinkedJournalEntries = true', ( + tester, + ) async { + final initialPet = Pet( + petId: 1, + name: 'Max', + speciesId: 1, + breed: 'Labrador', + colour: 'Black', + sex: PetSex.male.dataValue, + dob: null, + dobEstimate: false, + diet: 'Regular', + notes: 'Good boy', + history: 'Adopted', + isNeutered: true, + neuterDate: null, + status: PetStatus.active.dataValue, + statusDate: DateTime.now(), + imageUrl: '/files/images/pet_pic.png', + ); + + final initialPetModel = PetModel( + petId: null, + name: initialPet.name, + speciesId: initialPet.speciesId, + breed: initialPet.breed, + colour: initialPet.colour, + petSex: PetSex.fromDataValue(initialPet.sex), + dob: initialPet.dob, + dobEstimate: initialPet.dobEstimate, + diet: initialPet.diet, + notes: initialPet.notes, + history: initialPet.history, + isNeutered: initialPet.isNeutered, + neuterDate: initialPet.neuterDate, + status: PetStatus.fromDataValue(initialPet.status), + statusDate: initialPet.statusDate, + isMicrochipped: false, + imageUrl: initialPet.imageUrl, + ); + + when( + mockDatabaseService.getAllPets(), + ).thenAnswer((_) => Stream.empty()); + when( + mockDatabaseService.createPet( + initialPetModel.name, + initialPetModel.speciesId, + initialPetModel.breed, + initialPetModel.colour, + initialPetModel.petSex, + initialPetModel.dob, + initialPetModel.dobEstimate, + initialPetModel.diet, + initialPetModel.notes, + initialPetModel.history, + initialPetModel.isNeutered, + initialPetModel.neuterDate, + initialPetModel.status, + initialPetModel.isMicrochipped, + initialPetModel.microchipDate, + initialPetModel.microchipNumber, + initialPetModel.microchipCompany, + initialPetModel.microchipNotes, + initialPetModel.imageUrl, + ), + ).thenAnswer((_) => Future.value(initialPet)); + setupMockDatabaseForJournalEntry(true); + + final container = createContainer( + overrides: [ + DatabaseService.provider.overrideWithValue(mockDatabaseService), + ], + ); + + // ACT + final provider = container.read(allPetsControllerProvider.notifier); + PetModel? savedPet = await provider.save(initialPetModel); + + // ASSERT + verify( + mockDatabaseService.createJournalEntryForPet( + entryText: anyNamed('entryText'), + petIdList: anyNamed('petIdList'), + tags: anyNamed('tags'), + linkedRecordId: savedPet!.petId, + linkedRecordType: LinkedRecordType.pet, + linkedRecordTitle: 'Pet ${savedPet.name} created', + ), + ).called(1); + + // Workaround for FakeTimer error + await tester.pumpWidget(Container()); + await tester.pumpAndSettle(); + }); + + testWidgets( + 'Should not create a linked journal entry when call createPet with setting createLinkedJournalEntries = false', + (tester) async { + final initialPet = Pet( + petId: 1, + name: 'Max', + speciesId: 1, + breed: 'Labrador', + colour: 'Black', + sex: PetSex.male.dataValue, + dob: null, + dobEstimate: false, + diet: 'Regular', + notes: 'Good boy', + history: 'Adopted', + isNeutered: true, + neuterDate: null, + status: PetStatus.active.dataValue, + statusDate: DateTime.now(), + imageUrl: '/files/images/pet_pic.png', + ); + + final initialPetModel = PetModel( + petId: null, + name: initialPet.name, + speciesId: initialPet.speciesId, + breed: initialPet.breed, + colour: initialPet.colour, + petSex: PetSex.fromDataValue(initialPet.sex), + dob: initialPet.dob, + dobEstimate: initialPet.dobEstimate, + diet: initialPet.diet, + notes: initialPet.notes, + history: initialPet.history, + isNeutered: initialPet.isNeutered, + neuterDate: initialPet.neuterDate, + status: PetStatus.fromDataValue(initialPet.status), + statusDate: initialPet.statusDate, + isMicrochipped: false, + imageUrl: initialPet.imageUrl, + ); + + when( + mockDatabaseService.getAllPets(), + ).thenAnswer((_) => Stream.empty()); + when( + mockDatabaseService.createPet( + initialPetModel.name, + initialPetModel.speciesId, + initialPetModel.breed, + initialPetModel.colour, + initialPetModel.petSex, + initialPetModel.dob, + initialPetModel.dobEstimate, + initialPetModel.diet, + initialPetModel.notes, + initialPetModel.history, + initialPetModel.isNeutered, + initialPetModel.neuterDate, + initialPetModel.status, + initialPetModel.isMicrochipped, + initialPetModel.microchipDate, + initialPetModel.microchipNumber, + initialPetModel.microchipCompany, + initialPetModel.microchipNotes, + initialPetModel.imageUrl, + ), + ).thenAnswer((_) => Future.value(initialPet)); + setupMockDatabaseForJournalEntry(false); + + final container = createContainer( + overrides: [ + DatabaseService.provider.overrideWithValue(mockDatabaseService), + ], + ); + + // ACT + final provider = container.read(allPetsControllerProvider.notifier); + await provider.save(initialPetModel); + + // ASSERT + verifyNever( + mockDatabaseService.createJournalEntryForPet( + entryText: anyNamed('entryText'), + petIdList: anyNamed('petIdList'), + tags: anyNamed('tags'), + linkedRecordId: anyNamed('linkedRecordId'), + linkedRecordType: anyNamed('linkedRecordType'), + linkedRecordTitle: anyNamed('linkedRecordTitle'), + ), + ); + + // Workaround for FakeTimer error + await tester.pumpWidget(Container()); + await tester.pumpAndSettle(); + }, + ); + }); }); } diff --git a/test/app/pet/controllers/all_pets_controller_test.mocks.dart b/test/app/pet/controllers/all_pets_controller_test.mocks.dart index fcaba04..ca1e0a1 100644 --- a/test/app/pet/controllers/all_pets_controller_test.mocks.dart +++ b/test/app/pet/controllers/all_pets_controller_test.mocks.dart @@ -9,12 +9,13 @@ import 'package:drift/drift.dart' as _i2; import 'package:drift/src/runtime/executor/stream_queries.dart' as _i4; import 'package:mockito/mockito.dart' as _i1; import 'package:mockito/src/dummies.dart' as _i6; +import 'package:petjournal/constants/linked_record_type.dart' as _i10; import 'package:petjournal/constants/pet_sex.dart' as _i7; import 'package:petjournal/constants/pet_status.dart' as _i8; import 'package:petjournal/constants/weight_units.dart' as _i9; import 'package:petjournal/data/database/database_service.dart' as _i3; import 'package:petjournal/data/database/tables/journal_entry_details.dart' - as _i10; + as _i11; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -809,12 +810,18 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { required String? entryText, required List? petIdList, required List? tags, + _i10.LinkedRecordType? linkedRecordType, + int? linkedRecordId, + String? linkedRecordTitle, }) => (super.noSuchMethod( Invocation.method(#createJournalEntryForPet, [], { #entryText: entryText, #petIdList: petIdList, #tags: tags, + #linkedRecordType: linkedRecordType, + #linkedRecordId: linkedRecordId, + #linkedRecordTitle: linkedRecordTitle, }), returnValue: _i5.Future<_i3.JournalEntry?>.value(), ) @@ -838,6 +845,22 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { ) as _i5.Future); + @override + _i5.Future updateLinkedJournalEntry({ + required int? linkedRecordId, + required _i10.LinkedRecordType? linkedRecordType, + required String? linkedRecordTitle, + }) => + (super.noSuchMethod( + Invocation.method(#updateLinkedJournalEntry, [], { + #linkedRecordId: linkedRecordId, + #linkedRecordType: linkedRecordType, + #linkedRecordTitle: linkedRecordTitle, + }), + returnValue: _i5.Future.value(0), + ) + as _i5.Future); + @override _i5.Future<_i3.JournalEntry?> getJournalEntry(int? id) => (super.noSuchMethod( @@ -863,14 +886,14 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { as _i5.Stream>); @override - _i5.Stream> getAllJournalEntryDetailsForPet( + _i5.Stream> getAllJournalEntryDetailsForPet( int? petId, ) => (super.noSuchMethod( Invocation.method(#getAllJournalEntryDetailsForPet, [petId]), - returnValue: _i5.Stream>.empty(), + returnValue: _i5.Stream>.empty(), ) - as _i5.Stream>); + as _i5.Stream>); @override _i5.Future deleteJournalEntry(int? id) => @@ -1005,11 +1028,13 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { _i5.Future saveSettingsUser( _i9.WeightUnits? defaultWeightUnit, bool? optIntoAnalyticsWarning, + bool? createLinkedJournalEntries, ) => (super.noSuchMethod( Invocation.method(#saveSettingsUser, [ defaultWeightUnit, optIntoAnalyticsWarning, + createLinkedJournalEntries, ]), returnValue: _i5.Future.value(0), ) diff --git a/test/app/pet/controllers/journal_controller_test.mocks.dart b/test/app/pet/controllers/journal_controller_test.mocks.dart index bdedba9..44c064e 100644 --- a/test/app/pet/controllers/journal_controller_test.mocks.dart +++ b/test/app/pet/controllers/journal_controller_test.mocks.dart @@ -9,12 +9,13 @@ import 'package:drift/drift.dart' as _i2; import 'package:drift/src/runtime/executor/stream_queries.dart' as _i4; import 'package:mockito/mockito.dart' as _i1; import 'package:mockito/src/dummies.dart' as _i6; +import 'package:petjournal/constants/linked_record_type.dart' as _i10; import 'package:petjournal/constants/pet_sex.dart' as _i7; import 'package:petjournal/constants/pet_status.dart' as _i8; import 'package:petjournal/constants/weight_units.dart' as _i9; import 'package:petjournal/data/database/database_service.dart' as _i3; import 'package:petjournal/data/database/tables/journal_entry_details.dart' - as _i10; + as _i11; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -809,12 +810,18 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { required String? entryText, required List? petIdList, required List? tags, + _i10.LinkedRecordType? linkedRecordType, + int? linkedRecordId, + String? linkedRecordTitle, }) => (super.noSuchMethod( Invocation.method(#createJournalEntryForPet, [], { #entryText: entryText, #petIdList: petIdList, #tags: tags, + #linkedRecordType: linkedRecordType, + #linkedRecordId: linkedRecordId, + #linkedRecordTitle: linkedRecordTitle, }), returnValue: _i5.Future<_i3.JournalEntry?>.value(), ) @@ -838,6 +845,22 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { ) as _i5.Future); + @override + _i5.Future updateLinkedJournalEntry({ + required int? linkedRecordId, + required _i10.LinkedRecordType? linkedRecordType, + required String? linkedRecordTitle, + }) => + (super.noSuchMethod( + Invocation.method(#updateLinkedJournalEntry, [], { + #linkedRecordId: linkedRecordId, + #linkedRecordType: linkedRecordType, + #linkedRecordTitle: linkedRecordTitle, + }), + returnValue: _i5.Future.value(0), + ) + as _i5.Future); + @override _i5.Future<_i3.JournalEntry?> getJournalEntry(int? id) => (super.noSuchMethod( @@ -863,14 +886,14 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { as _i5.Stream>); @override - _i5.Stream> getAllJournalEntryDetailsForPet( + _i5.Stream> getAllJournalEntryDetailsForPet( int? petId, ) => (super.noSuchMethod( Invocation.method(#getAllJournalEntryDetailsForPet, [petId]), - returnValue: _i5.Stream>.empty(), + returnValue: _i5.Stream>.empty(), ) - as _i5.Stream>); + as _i5.Stream>); @override _i5.Future deleteJournalEntry(int? id) => @@ -1005,11 +1028,13 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { _i5.Future saveSettingsUser( _i9.WeightUnits? defaultWeightUnit, bool? optIntoAnalyticsWarning, + bool? createLinkedJournalEntries, ) => (super.noSuchMethod( Invocation.method(#saveSettingsUser, [ defaultWeightUnit, optIntoAnalyticsWarning, + createLinkedJournalEntries, ]), returnValue: _i5.Future.value(0), ) diff --git a/test/app/pet/controllers/pet_controller_test.mocks.dart b/test/app/pet/controllers/pet_controller_test.mocks.dart index 10ae94b..cf034d2 100644 --- a/test/app/pet/controllers/pet_controller_test.mocks.dart +++ b/test/app/pet/controllers/pet_controller_test.mocks.dart @@ -9,12 +9,13 @@ import 'package:drift/drift.dart' as _i2; import 'package:drift/src/runtime/executor/stream_queries.dart' as _i4; import 'package:mockito/mockito.dart' as _i1; import 'package:mockito/src/dummies.dart' as _i6; +import 'package:petjournal/constants/linked_record_type.dart' as _i10; import 'package:petjournal/constants/pet_sex.dart' as _i7; import 'package:petjournal/constants/pet_status.dart' as _i8; import 'package:petjournal/constants/weight_units.dart' as _i9; import 'package:petjournal/data/database/database_service.dart' as _i3; import 'package:petjournal/data/database/tables/journal_entry_details.dart' - as _i10; + as _i11; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -809,12 +810,18 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { required String? entryText, required List? petIdList, required List? tags, + _i10.LinkedRecordType? linkedRecordType, + int? linkedRecordId, + String? linkedRecordTitle, }) => (super.noSuchMethod( Invocation.method(#createJournalEntryForPet, [], { #entryText: entryText, #petIdList: petIdList, #tags: tags, + #linkedRecordType: linkedRecordType, + #linkedRecordId: linkedRecordId, + #linkedRecordTitle: linkedRecordTitle, }), returnValue: _i5.Future<_i3.JournalEntry?>.value(), ) @@ -838,6 +845,22 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { ) as _i5.Future); + @override + _i5.Future updateLinkedJournalEntry({ + required int? linkedRecordId, + required _i10.LinkedRecordType? linkedRecordType, + required String? linkedRecordTitle, + }) => + (super.noSuchMethod( + Invocation.method(#updateLinkedJournalEntry, [], { + #linkedRecordId: linkedRecordId, + #linkedRecordType: linkedRecordType, + #linkedRecordTitle: linkedRecordTitle, + }), + returnValue: _i5.Future.value(0), + ) + as _i5.Future); + @override _i5.Future<_i3.JournalEntry?> getJournalEntry(int? id) => (super.noSuchMethod( @@ -863,14 +886,14 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { as _i5.Stream>); @override - _i5.Stream> getAllJournalEntryDetailsForPet( + _i5.Stream> getAllJournalEntryDetailsForPet( int? petId, ) => (super.noSuchMethod( Invocation.method(#getAllJournalEntryDetailsForPet, [petId]), - returnValue: _i5.Stream>.empty(), + returnValue: _i5.Stream>.empty(), ) - as _i5.Stream>); + as _i5.Stream>); @override _i5.Future deleteJournalEntry(int? id) => @@ -1005,11 +1028,13 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { _i5.Future saveSettingsUser( _i9.WeightUnits? defaultWeightUnit, bool? optIntoAnalyticsWarning, + bool? createLinkedJournalEntries, ) => (super.noSuchMethod( Invocation.method(#saveSettingsUser, [ defaultWeightUnit, optIntoAnalyticsWarning, + createLinkedJournalEntries, ]), returnValue: _i5.Future.value(0), ) diff --git a/test/app/pet/controllers/pet_meds_controller_test.dart b/test/app/pet/controllers/pet_meds_controller_test.dart index 024ca62..2b72adb 100644 --- a/test/app/pet/controllers/pet_meds_controller_test.dart +++ b/test/app/pet/controllers/pet_meds_controller_test.dart @@ -6,6 +6,9 @@ import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:petjournal/app/pet/controller/pet_meds_controller.dart'; import 'package:petjournal/app/pet/models/pet_med_model.dart'; +import 'package:petjournal/constants/defaults.dart' as defaults; +import 'package:petjournal/constants/linked_record_type.dart'; +import 'package:petjournal/constants/weight_units.dart'; import 'package:petjournal/data/database/database_service.dart'; import 'package:petjournal/data/mapper/pet_med_mapper.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -39,6 +42,40 @@ void main() { mockDatabaseService = MockDatabaseService(); }); + setupMockDatabaseForJournalEntry(bool createLinkedJournalEntries) { + when(mockDatabaseService.watchSettings()).thenAnswer( + (_) => Stream.value( + Setting( + settingsId: defaults.defaultSettingsId, + acceptedTermsAndConditions: true, + optIntoAnalyticsWarning: true, + onBoardingComplete: true, + defaultWeightUnit: WeightUnits.metric.dataValue, + createLinkedJournalEntries: createLinkedJournalEntries, + ), + ), + ); + + when( + mockDatabaseService.createJournalEntryForPet( + entryText: anyNamed('entryText'), + petIdList: anyNamed('petIdList'), + tags: anyNamed('tags'), + linkedRecordId: anyNamed('linkedRecordId'), + linkedRecordType: anyNamed('linkedRecordType'), + linkedRecordTitle: anyNamed('linkedRecordTitle'), + ), + ).thenAnswer((_) => Future.value()); + + when( + mockDatabaseService.updateLinkedJournalEntry( + linkedRecordId: anyNamed('linkedRecordId'), + linkedRecordType: anyNamed('linkedRecordType'), + linkedRecordTitle: anyNamed('linkedRecordTitle'), + ), + ).thenAnswer((_) => Future.value(1)); + } + group('build', () { test( 'build should emit PetMedModel list when database returns data', @@ -221,6 +258,7 @@ void main() { initialPetMedModel.notes, ), ).thenAnswer((_) => Future.value(initialPetMed)); + setupMockDatabaseForJournalEntry(false); final container = createContainer( overrides: [ @@ -291,6 +329,7 @@ void main() { initialPetMedModel.notes, ), ).thenAnswer((_) => Future.value(1)); + setupMockDatabaseForJournalEntry(false); final container = createContainer( overrides: [ @@ -354,5 +393,274 @@ void main() { await tester.pumpAndSettle(); }); }); + + group('create / update linked Journal Entries', () { + testWidgets( + 'Should create a linked journal entry when call createPetMed with setting createLinkedJournalEntries = true', + (tester) async { + int petId = 7; + final initialPetMed = PetMed( + petMedId: 1, + pet: petId, + name: 'Antibiotic', + dose: '5mg', + startDate: DateTime(2024, 5, 28), + endDate: DateTime(2024, 6, 1), + notes: 'Take with food', + ); + + final initialPetMedModel = PetMedModel( + petId: initialPetMed.pet, + name: initialPetMed.name, + dose: initialPetMed.dose, + startDate: initialPetMed.startDate, + endDate: initialPetMed.endDate, + notes: initialPetMed.notes, + ); + + when( + mockDatabaseService.getAllPetMedsForPet(petId), + ).thenAnswer((_) => Stream.empty()); + when( + mockDatabaseService.createPetMed( + initialPetMedModel.petId, + initialPetMedModel.name, + initialPetMedModel.dose, + initialPetMedModel.startDate, + initialPetMedModel.endDate, + initialPetMedModel.notes, + ), + ).thenAnswer((_) => Future.value(initialPetMed)); + setupMockDatabaseForJournalEntry(true); + + final container = createContainer( + overrides: [ + DatabaseService.provider.overrideWithValue(mockDatabaseService), + ], + ); + + // ACT + final provider = container.read( + petMedsControllerProvider(petId).notifier, + ); + PetMedModel? savedPetMed = await provider.save(initialPetMedModel); + + // ASSERT + verify( + mockDatabaseService.createJournalEntryForPet( + entryText: anyNamed('entryText'), + petIdList: anyNamed('petIdList'), + tags: anyNamed('tags'), + linkedRecordId: savedPetMed!.petMedId!, + linkedRecordType: LinkedRecordType.medication, + linkedRecordTitle: 'Medication ${savedPetMed.name} prescribed', + ), + ).called(1); + + // Workaround for FakeTimer error + await tester.pumpWidget(Container()); + await tester.pumpAndSettle(); + }, + ); + + testWidgets( + 'Should not create a linked journal entry when call createPetMed with setting createLinkedJournalEntries = false', + (tester) async { + int petId = 7; + final initialPetMed = PetMed( + petMedId: 1, + pet: petId, + name: 'Antibiotic', + dose: '5mg', + startDate: DateTime(2024, 5, 28), + endDate: DateTime(2024, 6, 1), + notes: 'Take with food', + ); + + final initialPetMedModel = PetMedModel( + petId: initialPetMed.pet, + name: initialPetMed.name, + dose: initialPetMed.dose, + startDate: initialPetMed.startDate, + endDate: initialPetMed.endDate, + notes: initialPetMed.notes, + ); + + when( + mockDatabaseService.getAllPetMedsForPet(petId), + ).thenAnswer((_) => Stream.empty()); + when( + mockDatabaseService.createPetMed( + initialPetMedModel.petId, + initialPetMedModel.name, + initialPetMedModel.dose, + initialPetMedModel.startDate, + initialPetMedModel.endDate, + initialPetMedModel.notes, + ), + ).thenAnswer((_) => Future.value(initialPetMed)); + setupMockDatabaseForJournalEntry(false); + + final container = createContainer( + overrides: [ + DatabaseService.provider.overrideWithValue(mockDatabaseService), + ], + ); + + // ACT + final provider = container.read( + petMedsControllerProvider(petId).notifier, + ); + await provider.save(initialPetMedModel); + + // ASSERT + verifyNever( + mockDatabaseService.createJournalEntryForPet( + entryText: anyNamed('entryText'), + petIdList: anyNamed('petIdList'), + tags: anyNamed('tags'), + linkedRecordId: anyNamed('linkedRecordId'), + linkedRecordType: anyNamed('linkedRecordType'), + linkedRecordTitle: anyNamed('linkedRecordTitle'), + ), + ); + + // Workaround for FakeTimer error + await tester.pumpWidget(Container()); + await tester.pumpAndSettle(); + }, + ); + + testWidgets( + 'Should update a linked journal entry when call updatePetMed with setting createLinkedJournalEntries = true', + (tester) async { + int petId = 7; + final initialPetMed = PetMed( + petMedId: 1, + pet: petId, + name: 'Antibiotic', + dose: '5mg', + startDate: DateTime(2024, 5, 28), + endDate: DateTime(2024, 6, 1), + notes: 'Take with food', + ); + + final initialPetMedModel = PetMedModel( + petMedId: initialPetMed.petMedId, + petId: initialPetMed.pet, + name: initialPetMed.name, + dose: initialPetMed.dose, + startDate: initialPetMed.startDate, + endDate: initialPetMed.endDate, + notes: initialPetMed.notes, + ); + + when( + mockDatabaseService.getAllPetMedsForPet(petId), + ).thenAnswer((_) => Stream.empty()); + when( + mockDatabaseService.updatePetMed( + initialPetMedModel.petMedId, + initialPetMedModel.name, + initialPetMedModel.dose, + initialPetMedModel.startDate, + initialPetMedModel.endDate, + initialPetMedModel.notes, + ), + ).thenAnswer((_) => Future.value(1)); + setupMockDatabaseForJournalEntry(true); + + final container = createContainer( + overrides: [ + DatabaseService.provider.overrideWithValue(mockDatabaseService), + ], + ); + + // ACT + final provider = container.read( + petMedsControllerProvider(petId).notifier, + ); + PetMedModel? savedPetMed = await provider.save(initialPetMedModel); + + // ASSERT + verify( + mockDatabaseService.updateLinkedJournalEntry( + linkedRecordId: savedPetMed!.petMedId, + linkedRecordType: LinkedRecordType.medication, + linkedRecordTitle: + 'Medication ${savedPetMed.name} prescribed (updated)', + ), + ).called(1); + + // Workaround for FakeTimer error + await tester.pumpWidget(Container()); + await tester.pumpAndSettle(); + }, + ); + testWidgets('Should not update a linked journal entry when call updatePetMed with setting createLinkedJournalEntries = false', ( + tester, + ) async { + int petId = 7; + final initialPetMed = PetMed( + petMedId: 1, + pet: petId, + name: 'Antibiotic', + dose: '5mg', + startDate: DateTime(2024, 5, 28), + endDate: DateTime(2024, 6, 1), + notes: 'Take with food', + ); + + final initialPetMedModel = PetMedModel( + petMedId: initialPetMed.petMedId, + petId: initialPetMed.pet, + name: initialPetMed.name, + dose: initialPetMed.dose, + startDate: initialPetMed.startDate, + endDate: initialPetMed.endDate, + notes: initialPetMed.notes, + ); + + when( + mockDatabaseService.getAllPetMedsForPet(petId), + ).thenAnswer((_) => Stream.empty()); + when( + mockDatabaseService.updatePetMed( + initialPetMedModel.petMedId, + initialPetMedModel.name, + initialPetMedModel.dose, + initialPetMedModel.startDate, + initialPetMedModel.endDate, + initialPetMedModel.notes, + ), + ).thenAnswer((_) => Future.value(1)); + setupMockDatabaseForJournalEntry(false); + + final container = createContainer( + overrides: [ + DatabaseService.provider.overrideWithValue(mockDatabaseService), + ], + ); + + // ACT + final provider = container.read( + petMedsControllerProvider(petId).notifier, + ); + await provider.save(initialPetMedModel); + + // ASSERT + verifyNever( + mockDatabaseService.updateLinkedJournalEntry( + linkedRecordId: anyNamed('linkedRecordId'), + linkedRecordType: anyNamed('linkedRecordType'), + linkedRecordTitle: anyNamed('linkedRecordTitle'), + ), + ); + + // Workaround for FakeTimer error + await tester.pumpWidget(Container()); + await tester.pumpAndSettle(); + }); + }); }); } diff --git a/test/app/pet/controllers/pet_meds_controller_test.mocks.dart b/test/app/pet/controllers/pet_meds_controller_test.mocks.dart index 70a4831..174ea35 100644 --- a/test/app/pet/controllers/pet_meds_controller_test.mocks.dart +++ b/test/app/pet/controllers/pet_meds_controller_test.mocks.dart @@ -9,12 +9,13 @@ import 'package:drift/drift.dart' as _i2; import 'package:drift/src/runtime/executor/stream_queries.dart' as _i4; import 'package:mockito/mockito.dart' as _i1; import 'package:mockito/src/dummies.dart' as _i6; +import 'package:petjournal/constants/linked_record_type.dart' as _i10; import 'package:petjournal/constants/pet_sex.dart' as _i7; import 'package:petjournal/constants/pet_status.dart' as _i8; import 'package:petjournal/constants/weight_units.dart' as _i9; import 'package:petjournal/data/database/database_service.dart' as _i3; import 'package:petjournal/data/database/tables/journal_entry_details.dart' - as _i10; + as _i11; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -809,12 +810,18 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { required String? entryText, required List? petIdList, required List? tags, + _i10.LinkedRecordType? linkedRecordType, + int? linkedRecordId, + String? linkedRecordTitle, }) => (super.noSuchMethod( Invocation.method(#createJournalEntryForPet, [], { #entryText: entryText, #petIdList: petIdList, #tags: tags, + #linkedRecordType: linkedRecordType, + #linkedRecordId: linkedRecordId, + #linkedRecordTitle: linkedRecordTitle, }), returnValue: _i5.Future<_i3.JournalEntry?>.value(), ) @@ -838,6 +845,22 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { ) as _i5.Future); + @override + _i5.Future updateLinkedJournalEntry({ + required int? linkedRecordId, + required _i10.LinkedRecordType? linkedRecordType, + required String? linkedRecordTitle, + }) => + (super.noSuchMethod( + Invocation.method(#updateLinkedJournalEntry, [], { + #linkedRecordId: linkedRecordId, + #linkedRecordType: linkedRecordType, + #linkedRecordTitle: linkedRecordTitle, + }), + returnValue: _i5.Future.value(0), + ) + as _i5.Future); + @override _i5.Future<_i3.JournalEntry?> getJournalEntry(int? id) => (super.noSuchMethod( @@ -863,14 +886,14 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { as _i5.Stream>); @override - _i5.Stream> getAllJournalEntryDetailsForPet( + _i5.Stream> getAllJournalEntryDetailsForPet( int? petId, ) => (super.noSuchMethod( Invocation.method(#getAllJournalEntryDetailsForPet, [petId]), - returnValue: _i5.Stream>.empty(), + returnValue: _i5.Stream>.empty(), ) - as _i5.Stream>); + as _i5.Stream>); @override _i5.Future deleteJournalEntry(int? id) => @@ -1005,11 +1028,13 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { _i5.Future saveSettingsUser( _i9.WeightUnits? defaultWeightUnit, bool? optIntoAnalyticsWarning, + bool? createLinkedJournalEntries, ) => (super.noSuchMethod( Invocation.method(#saveSettingsUser, [ defaultWeightUnit, optIntoAnalyticsWarning, + createLinkedJournalEntries, ]), returnValue: _i5.Future.value(0), ) diff --git a/test/app/pet/controllers/pet_microchips_controller_test.mocks.dart b/test/app/pet/controllers/pet_microchips_controller_test.mocks.dart index fc8f847..93f8a3c 100644 --- a/test/app/pet/controllers/pet_microchips_controller_test.mocks.dart +++ b/test/app/pet/controllers/pet_microchips_controller_test.mocks.dart @@ -9,12 +9,13 @@ import 'package:drift/drift.dart' as _i2; import 'package:drift/src/runtime/executor/stream_queries.dart' as _i4; import 'package:mockito/mockito.dart' as _i1; import 'package:mockito/src/dummies.dart' as _i6; +import 'package:petjournal/constants/linked_record_type.dart' as _i10; import 'package:petjournal/constants/pet_sex.dart' as _i7; import 'package:petjournal/constants/pet_status.dart' as _i8; import 'package:petjournal/constants/weight_units.dart' as _i9; import 'package:petjournal/data/database/database_service.dart' as _i3; import 'package:petjournal/data/database/tables/journal_entry_details.dart' - as _i10; + as _i11; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -809,12 +810,18 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { required String? entryText, required List? petIdList, required List? tags, + _i10.LinkedRecordType? linkedRecordType, + int? linkedRecordId, + String? linkedRecordTitle, }) => (super.noSuchMethod( Invocation.method(#createJournalEntryForPet, [], { #entryText: entryText, #petIdList: petIdList, #tags: tags, + #linkedRecordType: linkedRecordType, + #linkedRecordId: linkedRecordId, + #linkedRecordTitle: linkedRecordTitle, }), returnValue: _i5.Future<_i3.JournalEntry?>.value(), ) @@ -838,6 +845,22 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { ) as _i5.Future); + @override + _i5.Future updateLinkedJournalEntry({ + required int? linkedRecordId, + required _i10.LinkedRecordType? linkedRecordType, + required String? linkedRecordTitle, + }) => + (super.noSuchMethod( + Invocation.method(#updateLinkedJournalEntry, [], { + #linkedRecordId: linkedRecordId, + #linkedRecordType: linkedRecordType, + #linkedRecordTitle: linkedRecordTitle, + }), + returnValue: _i5.Future.value(0), + ) + as _i5.Future); + @override _i5.Future<_i3.JournalEntry?> getJournalEntry(int? id) => (super.noSuchMethod( @@ -863,14 +886,14 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { as _i5.Stream>); @override - _i5.Stream> getAllJournalEntryDetailsForPet( + _i5.Stream> getAllJournalEntryDetailsForPet( int? petId, ) => (super.noSuchMethod( Invocation.method(#getAllJournalEntryDetailsForPet, [petId]), - returnValue: _i5.Stream>.empty(), + returnValue: _i5.Stream>.empty(), ) - as _i5.Stream>); + as _i5.Stream>); @override _i5.Future deleteJournalEntry(int? id) => @@ -1005,11 +1028,13 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { _i5.Future saveSettingsUser( _i9.WeightUnits? defaultWeightUnit, bool? optIntoAnalyticsWarning, + bool? createLinkedJournalEntries, ) => (super.noSuchMethod( Invocation.method(#saveSettingsUser, [ defaultWeightUnit, optIntoAnalyticsWarning, + createLinkedJournalEntries, ]), returnValue: _i5.Future.value(0), ) diff --git a/test/app/pet/controllers/pet_vaccinations_controller_test.dart b/test/app/pet/controllers/pet_vaccinations_controller_test.dart index 7ef2b93..076f1ab 100644 --- a/test/app/pet/controllers/pet_vaccinations_controller_test.dart +++ b/test/app/pet/controllers/pet_vaccinations_controller_test.dart @@ -7,6 +7,9 @@ import 'package:mockito/mockito.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:petjournal/app/pet/controller/pet_vaccinations_controller.dart'; import 'package:petjournal/app/pet/models/pet_vaccination_model.dart'; +import 'package:petjournal/constants/defaults.dart' as defaults; +import 'package:petjournal/constants/linked_record_type.dart'; +import 'package:petjournal/constants/weight_units.dart'; import 'package:petjournal/data/database/database_service.dart'; import 'package:petjournal/data/mapper/pet_vaccination_mapper.dart'; import 'package:matcher/matcher.dart' as match; @@ -39,6 +42,40 @@ void main() { mockDatabaseService = MockDatabaseService(); }); + setupMockDatabaseForJournalEntry(bool createLinkedJournalEntries) { + when(mockDatabaseService.watchSettings()).thenAnswer( + (_) => Stream.value( + Setting( + settingsId: defaults.defaultSettingsId, + acceptedTermsAndConditions: true, + optIntoAnalyticsWarning: true, + onBoardingComplete: true, + defaultWeightUnit: WeightUnits.metric.dataValue, + createLinkedJournalEntries: createLinkedJournalEntries, + ), + ), + ); + + when( + mockDatabaseService.createJournalEntryForPet( + entryText: anyNamed('entryText'), + petIdList: anyNamed('petIdList'), + tags: anyNamed('tags'), + linkedRecordId: anyNamed('linkedRecordId'), + linkedRecordType: anyNamed('linkedRecordType'), + linkedRecordTitle: anyNamed('linkedRecordTitle'), + ), + ).thenAnswer((_) => Future.value()); + + when( + mockDatabaseService.updateLinkedJournalEntry( + linkedRecordId: anyNamed('linkedRecordId'), + linkedRecordType: anyNamed('linkedRecordType'), + linkedRecordTitle: anyNamed('linkedRecordTitle'), + ), + ).thenAnswer((_) => Future.value(1)); + } + group('build', () { test( 'build should emit PetVaccinationModel list when database returns data', @@ -251,6 +288,7 @@ void main() { initialPetVaccinationModel.administeredBy, ), ).thenAnswer((_) => Future.value(initialPetVaccination)); + setupMockDatabaseForJournalEntry(false); final container = createContainer( overrides: [ @@ -339,6 +377,7 @@ void main() { initialPetVaccinationModel.administeredBy, ), ).thenAnswer((_) => Future.value(1)); + setupMockDatabaseForJournalEntry(false); final container = createContainer( overrides: [ @@ -413,5 +452,315 @@ void main() { }, ); }); + + group('create / update linked Journal Entries', () { + testWidgets( + 'Should create a linked journal entry when call createPetMed with setting createLinkedJournalEntries = true', + (tester) async { + int petId = 7; + final initialPetVaccination = PetVaccination( + petVaccinationId: 1, + pet: petId, + name: 'Rabies', + administeredDate: DateTime(2024, 5, 28), + expiryDate: DateTime(2024, 6, 1), + reminderDate: DateTime(2024, 5, 1), + notes: 'All went well', + vaccineBatchNumber: 'batch 123', + vaccineManufacturer: 'Vaccine Manufacturer', + administeredBy: 'Dr. Smith', + ); + + final initialPetVaccinationModel = PetVaccinationModel( + petId: initialPetVaccination.pet, + name: initialPetVaccination.name, + administeredDate: initialPetVaccination.administeredDate, + expiryDate: initialPetVaccination.expiryDate, + reminderDate: initialPetVaccination.reminderDate, + notes: initialPetVaccination.notes, + vaccineBatchNumber: initialPetVaccination.vaccineBatchNumber, + vaccineManufacturer: initialPetVaccination.vaccineManufacturer, + administeredBy: initialPetVaccination.administeredBy, + ); + + when( + mockDatabaseService.getAllPetVaccinationsForPet(petId), + ).thenAnswer((_) => Stream.empty()); + when( + mockDatabaseService.createPetVaccination( + initialPetVaccinationModel.petId, + initialPetVaccinationModel.name, + initialPetVaccinationModel.administeredDate, + initialPetVaccinationModel.expiryDate, + initialPetVaccinationModel.reminderDate, + initialPetVaccinationModel.notes, + initialPetVaccinationModel.vaccineBatchNumber, + initialPetVaccinationModel.vaccineManufacturer, + initialPetVaccinationModel.administeredBy, + ), + ).thenAnswer((_) => Future.value(initialPetVaccination)); + setupMockDatabaseForJournalEntry(true); + + final container = createContainer( + overrides: [ + DatabaseService.provider.overrideWithValue(mockDatabaseService), + ], + ); + + // ACT + final provider = container.read( + petVaccinationsControllerProvider(petId).notifier, + ); + PetVaccinationModel? savedPetVaccination = await provider.save( + initialPetVaccinationModel, + ); + + // ASSERT + verify( + mockDatabaseService.createJournalEntryForPet( + entryText: anyNamed('entryText'), + petIdList: anyNamed('petIdList'), + tags: anyNamed('tags'), + linkedRecordId: savedPetVaccination!.petVaccinationId!, + linkedRecordType: LinkedRecordType.vaccination, + linkedRecordTitle: 'Vaccination ${savedPetVaccination.name} administered', + ), + ).called(1); + + // Workaround for FakeTimer error + await tester.pumpWidget(Container()); + await tester.pumpAndSettle(); + }, + ); + + testWidgets( + 'Should not create a linked journal entry when call createPetMed with setting createLinkedJournalEntries = false', + (tester) async { + int petId = 7; + final initialPetVaccination = PetVaccination( + petVaccinationId: 1, + pet: petId, + name: 'Rabies', + administeredDate: DateTime(2024, 5, 28), + expiryDate: DateTime(2024, 6, 1), + reminderDate: DateTime(2024, 5, 1), + notes: 'All went well', + vaccineBatchNumber: 'batch 123', + vaccineManufacturer: 'Vaccine Manufacturer', + administeredBy: 'Dr. Smith', + ); + + final initialPetVaccinationModel = PetVaccinationModel( + petId: initialPetVaccination.pet, + name: initialPetVaccination.name, + administeredDate: initialPetVaccination.administeredDate, + expiryDate: initialPetVaccination.expiryDate, + reminderDate: initialPetVaccination.reminderDate, + notes: initialPetVaccination.notes, + vaccineBatchNumber: initialPetVaccination.vaccineBatchNumber, + vaccineManufacturer: initialPetVaccination.vaccineManufacturer, + administeredBy: initialPetVaccination.administeredBy, + ); + + when( + mockDatabaseService.getAllPetVaccinationsForPet(petId), + ).thenAnswer((_) => Stream.empty()); + when( + mockDatabaseService.createPetVaccination( + initialPetVaccinationModel.petId, + initialPetVaccinationModel.name, + initialPetVaccinationModel.administeredDate, + initialPetVaccinationModel.expiryDate, + initialPetVaccinationModel.reminderDate, + initialPetVaccinationModel.notes, + initialPetVaccinationModel.vaccineBatchNumber, + initialPetVaccinationModel.vaccineManufacturer, + initialPetVaccinationModel.administeredBy, + ), + ).thenAnswer((_) => Future.value(initialPetVaccination)); + setupMockDatabaseForJournalEntry(false); + + final container = createContainer( + overrides: [ + DatabaseService.provider.overrideWithValue(mockDatabaseService), + ], + ); + + // ACT + final provider = container.read( + petVaccinationsControllerProvider(petId).notifier, + ); + await provider.save(initialPetVaccinationModel); + + // ASSERT + verifyNever( + mockDatabaseService.createJournalEntryForPet( + entryText: anyNamed('entryText'), + petIdList: anyNamed('petIdList'), + tags: anyNamed('tags'), + linkedRecordId: anyNamed('linkedRecordId'), + linkedRecordType: anyNamed('linkedRecordType'), + linkedRecordTitle: anyNamed('linkedRecordTitle'), + ), + ); + + // Workaround for FakeTimer error + await tester.pumpWidget(Container()); + await tester.pumpAndSettle(); + }, + ); + + testWidgets( + 'Should update a linked journal entry when call updatePetMed with setting createLinkedJournalEntries = true', + (tester) async { + int petId = 7; + final initialPetVaccination = PetVaccination( + petVaccinationId: 1, + pet: petId, + name: 'Rabies', + administeredDate: DateTime(2024, 5, 28), + expiryDate: DateTime(2024, 6, 1), + reminderDate: DateTime(2024, 5, 1), + notes: 'All went well', + vaccineBatchNumber: 'batch 123', + vaccineManufacturer: 'Vaccine Manufacturer', + administeredBy: 'Dr. Smith', + ); + + final initialPetVaccinationModel = PetVaccinationModel( + petVaccinationId: initialPetVaccination.petVaccinationId, + petId: initialPetVaccination.pet, + name: initialPetVaccination.name, + administeredDate: initialPetVaccination.administeredDate, + expiryDate: initialPetVaccination.expiryDate, + reminderDate: initialPetVaccination.reminderDate, + notes: initialPetVaccination.notes, + vaccineBatchNumber: initialPetVaccination.vaccineBatchNumber, + vaccineManufacturer: initialPetVaccination.vaccineManufacturer, + administeredBy: initialPetVaccination.administeredBy, + ); + + when( + mockDatabaseService.getAllPetVaccinationsForPet(petId), + ).thenAnswer((_) => Stream.empty()); + when( + mockDatabaseService.updatePetVaccination( + initialPetVaccinationModel.petVaccinationId, + initialPetVaccinationModel.name, + initialPetVaccinationModel.administeredDate, + initialPetVaccinationModel.expiryDate, + initialPetVaccinationModel.reminderDate, + initialPetVaccinationModel.notes, + initialPetVaccinationModel.vaccineBatchNumber, + initialPetVaccinationModel.vaccineManufacturer, + initialPetVaccinationModel.administeredBy, + ), + ).thenAnswer((_) => Future.value(1)); + setupMockDatabaseForJournalEntry(true); + + final container = createContainer( + overrides: [ + DatabaseService.provider.overrideWithValue(mockDatabaseService), + ], + ); + + // ACT + final provider = container.read( + petVaccinationsControllerProvider(petId).notifier, + ); + PetVaccinationModel? savedPetVaccination = await provider.save( + initialPetVaccinationModel, + ); + + // ASSERT + verify( + mockDatabaseService.updateLinkedJournalEntry( + linkedRecordId: savedPetVaccination!.petVaccinationId, + linkedRecordType: LinkedRecordType.vaccination, + linkedRecordTitle: + 'Vaccination ${savedPetVaccination.name} administered (updated)', + ), + ).called(1); + + // Workaround for FakeTimer error + await tester.pumpWidget(Container()); + await tester.pumpAndSettle(); + }, + ); + testWidgets( + 'Should not update a linked journal entry when call updatePetMed with setting createLinkedJournalEntries = false', + (tester) async { + int petId = 7; + final initialPetVaccination = PetVaccination( + petVaccinationId: 1, + pet: petId, + name: 'Rabies', + administeredDate: DateTime(2024, 5, 28), + expiryDate: DateTime(2024, 6, 1), + reminderDate: DateTime(2024, 5, 1), + notes: 'All went well', + vaccineBatchNumber: 'batch 123', + vaccineManufacturer: 'Vaccine Manufacturer', + administeredBy: 'Dr. Smith', + ); + + final initialPetVaccinationModel = PetVaccinationModel( + petVaccinationId: initialPetVaccination.petVaccinationId, + petId: initialPetVaccination.pet, + name: initialPetVaccination.name, + administeredDate: initialPetVaccination.administeredDate, + expiryDate: initialPetVaccination.expiryDate, + reminderDate: initialPetVaccination.reminderDate, + notes: initialPetVaccination.notes, + vaccineBatchNumber: initialPetVaccination.vaccineBatchNumber, + vaccineManufacturer: initialPetVaccination.vaccineManufacturer, + administeredBy: initialPetVaccination.administeredBy, + ); + + when( + mockDatabaseService.getAllPetVaccinationsForPet(petId), + ).thenAnswer((_) => Stream.empty()); + when( + mockDatabaseService.updatePetVaccination( + initialPetVaccinationModel.petVaccinationId, + initialPetVaccinationModel.name, + initialPetVaccinationModel.administeredDate, + initialPetVaccinationModel.expiryDate, + initialPetVaccinationModel.reminderDate, + initialPetVaccinationModel.notes, + initialPetVaccinationModel.vaccineBatchNumber, + initialPetVaccinationModel.vaccineManufacturer, + initialPetVaccinationModel.administeredBy, + ), + ).thenAnswer((_) => Future.value(1)); + setupMockDatabaseForJournalEntry(false); + + final container = createContainer( + overrides: [ + DatabaseService.provider.overrideWithValue(mockDatabaseService), + ], + ); + + // ACT + final provider = container.read( + petVaccinationsControllerProvider(petId).notifier, + ); + await provider.save(initialPetVaccinationModel); + + // ASSERT + verifyNever( + mockDatabaseService.updateLinkedJournalEntry( + linkedRecordId: anyNamed('linkedRecordId'), + linkedRecordType: anyNamed('linkedRecordType'), + linkedRecordTitle: anyNamed('linkedRecordTitle'), + ), + ); + + // Workaround for FakeTimer error + await tester.pumpWidget(Container()); + await tester.pumpAndSettle(); + }, + ); + }); }); } diff --git a/test/app/pet/controllers/pet_vaccinations_controller_test.mocks.dart b/test/app/pet/controllers/pet_vaccinations_controller_test.mocks.dart index 56cfbcf..60950ec 100644 --- a/test/app/pet/controllers/pet_vaccinations_controller_test.mocks.dart +++ b/test/app/pet/controllers/pet_vaccinations_controller_test.mocks.dart @@ -9,12 +9,13 @@ import 'package:drift/drift.dart' as _i2; import 'package:drift/src/runtime/executor/stream_queries.dart' as _i4; import 'package:mockito/mockito.dart' as _i1; import 'package:mockito/src/dummies.dart' as _i6; +import 'package:petjournal/constants/linked_record_type.dart' as _i10; import 'package:petjournal/constants/pet_sex.dart' as _i7; import 'package:petjournal/constants/pet_status.dart' as _i8; import 'package:petjournal/constants/weight_units.dart' as _i9; import 'package:petjournal/data/database/database_service.dart' as _i3; import 'package:petjournal/data/database/tables/journal_entry_details.dart' - as _i10; + as _i11; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -809,12 +810,18 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { required String? entryText, required List? petIdList, required List? tags, + _i10.LinkedRecordType? linkedRecordType, + int? linkedRecordId, + String? linkedRecordTitle, }) => (super.noSuchMethod( Invocation.method(#createJournalEntryForPet, [], { #entryText: entryText, #petIdList: petIdList, #tags: tags, + #linkedRecordType: linkedRecordType, + #linkedRecordId: linkedRecordId, + #linkedRecordTitle: linkedRecordTitle, }), returnValue: _i5.Future<_i3.JournalEntry?>.value(), ) @@ -838,6 +845,22 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { ) as _i5.Future); + @override + _i5.Future updateLinkedJournalEntry({ + required int? linkedRecordId, + required _i10.LinkedRecordType? linkedRecordType, + required String? linkedRecordTitle, + }) => + (super.noSuchMethod( + Invocation.method(#updateLinkedJournalEntry, [], { + #linkedRecordId: linkedRecordId, + #linkedRecordType: linkedRecordType, + #linkedRecordTitle: linkedRecordTitle, + }), + returnValue: _i5.Future.value(0), + ) + as _i5.Future); + @override _i5.Future<_i3.JournalEntry?> getJournalEntry(int? id) => (super.noSuchMethod( @@ -863,14 +886,14 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { as _i5.Stream>); @override - _i5.Stream> getAllJournalEntryDetailsForPet( + _i5.Stream> getAllJournalEntryDetailsForPet( int? petId, ) => (super.noSuchMethod( Invocation.method(#getAllJournalEntryDetailsForPet, [petId]), - returnValue: _i5.Stream>.empty(), + returnValue: _i5.Stream>.empty(), ) - as _i5.Stream>); + as _i5.Stream>); @override _i5.Future deleteJournalEntry(int? id) => @@ -1005,11 +1028,13 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { _i5.Future saveSettingsUser( _i9.WeightUnits? defaultWeightUnit, bool? optIntoAnalyticsWarning, + bool? createLinkedJournalEntries, ) => (super.noSuchMethod( Invocation.method(#saveSettingsUser, [ defaultWeightUnit, optIntoAnalyticsWarning, + createLinkedJournalEntries, ]), returnValue: _i5.Future.value(0), ) diff --git a/test/app/pet/controllers/pet_weights_controller_test.dart b/test/app/pet/controllers/pet_weights_controller_test.dart index 3aa714b..641ba01 100644 --- a/test/app/pet/controllers/pet_weights_controller_test.dart +++ b/test/app/pet/controllers/pet_weights_controller_test.dart @@ -7,6 +7,8 @@ import 'package:mockito/mockito.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:petjournal/app/pet/controller/pet_weights_controller.dart'; import 'package:petjournal/app/pet/models/pet_weight_model.dart'; +import 'package:petjournal/constants/defaults.dart' as defaults; +import 'package:petjournal/constants/linked_record_type.dart'; import 'package:petjournal/constants/weight_units.dart'; import 'package:petjournal/data/database/database_service.dart'; import 'package:petjournal/data/mapper/pet_weight_mapper.dart'; @@ -40,6 +42,40 @@ void main() { mockDatabaseService = MockDatabaseService(); }); + setupMockDatabaseForJournalEntry(bool createLinkedJournalEntries) { + when(mockDatabaseService.watchSettings()).thenAnswer( + (_) => Stream.value( + Setting( + settingsId: defaults.defaultSettingsId, + acceptedTermsAndConditions: true, + optIntoAnalyticsWarning: true, + onBoardingComplete: true, + defaultWeightUnit: WeightUnits.metric.dataValue, + createLinkedJournalEntries: createLinkedJournalEntries, + ), + ), + ); + + when( + mockDatabaseService.createJournalEntryForPet( + entryText: anyNamed('entryText'), + petIdList: anyNamed('petIdList'), + tags: anyNamed('tags'), + linkedRecordId: anyNamed('linkedRecordId'), + linkedRecordType: anyNamed('linkedRecordType'), + linkedRecordTitle: anyNamed('linkedRecordTitle'), + ), + ).thenAnswer((_) => Future.value()); + + when( + mockDatabaseService.updateLinkedJournalEntry( + linkedRecordId: anyNamed('linkedRecordId'), + linkedRecordType: anyNamed('linkedRecordType'), + linkedRecordTitle: anyNamed('linkedRecordTitle'), + ), + ).thenAnswer((_) => Future.value(1)); + } + group('build', () { test( 'build should emit PetWeightModel list when database returns data', @@ -222,6 +258,7 @@ void main() { initialPetWeightModel.notes, ), ).thenAnswer((_) => Future.value(initialPetWeight)); + setupMockDatabaseForJournalEntry(false); final container = createContainer( overrides: [ @@ -290,6 +327,7 @@ void main() { initialPetWeightModel.notes, ), ).thenAnswer((_) => Future.value(1)); + setupMockDatabaseForJournalEntry(false); final container = createContainer( overrides: [ @@ -354,5 +392,267 @@ void main() { await tester.pumpAndSettle(); }); }); + + group('create / update linked Journal Entries', () { + testWidgets( + 'Should create a linked journal entry when call createPetMed with setting createLinkedJournalEntries = true', + (tester) async { + int petId = 7; + final initialPetWeight = PetWeight( + petWeightId: 1, + pet: petId, + date: DateTime(2024, 5, 28), + weight: 12.5, + weightUnit: 1, + notes: 'Test Weight', + ); + + final initialPetWeightModel = PetWeightModel( + petId: initialPetWeight.pet, + date: initialPetWeight.date, + weight: initialPetWeight.weight, + weightUnit: WeightUnits.fromDataValue(initialPetWeight.weightUnit), + notes: initialPetWeight.notes, + ); + + when( + mockDatabaseService.getAllPetWeightsForPet(petId), + ).thenAnswer((_) => Stream.empty()); + when( + mockDatabaseService.createPetWeight( + initialPetWeightModel.petId, + initialPetWeightModel.date, + initialPetWeightModel.weight, + initialPetWeightModel.weightUnit, + initialPetWeightModel.notes, + ), + ).thenAnswer((_) => Future.value(initialPetWeight)); + setupMockDatabaseForJournalEntry(true); + + final container = createContainer( + overrides: [ + DatabaseService.provider.overrideWithValue(mockDatabaseService), + ], + ); + + // ACT + final provider = container.read( + petWeightsControllerProvider(petId).notifier, + ); + PetWeightModel? savedPetWeight = await provider.save( + initialPetWeightModel, + ); + + // ASSERT + verify( + mockDatabaseService.createJournalEntryForPet( + entryText: anyNamed('entryText'), + petIdList: anyNamed('petIdList'), + tags: anyNamed('tags'), + linkedRecordId: savedPetWeight!.petWeightId!, + linkedRecordType: LinkedRecordType.weight, + linkedRecordTitle: 'Weight ${savedPetWeight.niceName()} recorded', + ), + ).called(1); + + // Workaround for FakeTimer error + await tester.pumpWidget(Container()); + await tester.pumpAndSettle(); + }, + ); + + testWidgets( + 'Should not create a linked journal entry when call createPetMed with setting createLinkedJournalEntries = false', + (tester) async { + int petId = 7; + final initialPetWeight = PetWeight( + petWeightId: 1, + pet: petId, + date: DateTime(2024, 5, 28), + weight: 12.5, + weightUnit: 1, + notes: 'Test Weight', + ); + + final initialPetWeightModel = PetWeightModel( + petId: initialPetWeight.pet, + date: initialPetWeight.date, + weight: initialPetWeight.weight, + weightUnit: WeightUnits.fromDataValue(initialPetWeight.weightUnit), + notes: initialPetWeight.notes, + ); + + when( + mockDatabaseService.getAllPetWeightsForPet(petId), + ).thenAnswer((_) => Stream.empty()); + when( + mockDatabaseService.createPetWeight( + initialPetWeightModel.petId, + initialPetWeightModel.date, + initialPetWeightModel.weight, + initialPetWeightModel.weightUnit, + initialPetWeightModel.notes, + ), + ).thenAnswer((_) => Future.value(initialPetWeight)); + setupMockDatabaseForJournalEntry(false); + + final container = createContainer( + overrides: [ + DatabaseService.provider.overrideWithValue(mockDatabaseService), + ], + ); + + // ACT + final provider = container.read( + petWeightsControllerProvider(petId).notifier, + ); + await provider.save(initialPetWeightModel); + + // ASSERT + verifyNever( + mockDatabaseService.createJournalEntryForPet( + entryText: anyNamed('entryText'), + petIdList: anyNamed('petIdList'), + tags: anyNamed('tags'), + linkedRecordId: anyNamed('linkedRecordId'), + linkedRecordType: anyNamed('linkedRecordType'), + linkedRecordTitle: anyNamed('linkedRecordTitle'), + ), + ); + + // Workaround for FakeTimer error + await tester.pumpWidget(Container()); + await tester.pumpAndSettle(); + }, + ); + + testWidgets( + 'Should update a linked journal entry when call updatePetMed with setting createLinkedJournalEntries = true', + (tester) async { + int petId = 7; + final initialPetWeight = PetWeight( + petWeightId: 1, + pet: petId, + date: DateTime(2024, 5, 28), + weight: 12.5, + weightUnit: 1, + notes: 'Test Weight', + ); + + final initialPetWeightModel = PetWeightModel( + petWeightId: initialPetWeight.petWeightId, + petId: initialPetWeight.pet, + date: initialPetWeight.date, + weight: initialPetWeight.weight, + weightUnit: WeightUnits.fromDataValue(initialPetWeight.weightUnit), + notes: initialPetWeight.notes, + ); + + when( + mockDatabaseService.getAllPetWeightsForPet(petId), + ).thenAnswer((_) => Stream.empty()); + when( + mockDatabaseService.updatePetWeight( + initialPetWeightModel.petWeightId, + initialPetWeightModel.date, + initialPetWeightModel.weight, + initialPetWeightModel.weightUnit, + initialPetWeightModel.notes, + ), + ).thenAnswer((_) => Future.value(1)); + setupMockDatabaseForJournalEntry(true); + + final container = createContainer( + overrides: [ + DatabaseService.provider.overrideWithValue(mockDatabaseService), + ], + ); + + // ACT + final provider = container.read( + petWeightsControllerProvider(petId).notifier, + ); + PetWeightModel? savedPetWeight = await provider.save( + initialPetWeightModel, + ); + + // ASSERT + verify( + mockDatabaseService.updateLinkedJournalEntry( + linkedRecordId: savedPetWeight!.petWeightId, + linkedRecordType: LinkedRecordType.weight, + linkedRecordTitle: + 'Weight ${savedPetWeight.niceName()} recorded (updated)', + ), + ).called(1); + + // Workaround for FakeTimer error + await tester.pumpWidget(Container()); + await tester.pumpAndSettle(); + }, + ); + testWidgets( + 'Should not update a linked journal entry when call updatePetMed with setting createLinkedJournalEntries = false', + (tester) async { + int petId = 7; + final initialPetWeight = PetWeight( + petWeightId: 1, + pet: petId, + date: DateTime(2024, 5, 28), + weight: 12.5, + weightUnit: 1, + notes: 'Test Weight', + ); + + final initialPetWeightModel = PetWeightModel( + petWeightId: initialPetWeight.petWeightId, + petId: initialPetWeight.pet, + date: initialPetWeight.date, + weight: initialPetWeight.weight, + weightUnit: WeightUnits.fromDataValue(initialPetWeight.weightUnit), + notes: initialPetWeight.notes, + ); + + when( + mockDatabaseService.getAllPetWeightsForPet(petId), + ).thenAnswer((_) => Stream.empty()); + when( + mockDatabaseService.updatePetWeight( + initialPetWeightModel.petWeightId, + initialPetWeightModel.date, + initialPetWeightModel.weight, + initialPetWeightModel.weightUnit, + initialPetWeightModel.notes, + ), + ).thenAnswer((_) => Future.value(1)); + setupMockDatabaseForJournalEntry(false); + + final container = createContainer( + overrides: [ + DatabaseService.provider.overrideWithValue(mockDatabaseService), + ], + ); + + // ACT + final provider = container.read( + petWeightsControllerProvider(petId).notifier, + ); + await provider.save(initialPetWeightModel); + + // ASSERT + verifyNever( + mockDatabaseService.updateLinkedJournalEntry( + linkedRecordId: anyNamed('linkedRecordId'), + linkedRecordType: anyNamed('linkedRecordType'), + linkedRecordTitle: anyNamed('linkedRecordTitle'), + ), + ); + + // Workaround for FakeTimer error + await tester.pumpWidget(Container()); + await tester.pumpAndSettle(); + }, + ); + }); }); } diff --git a/test/app/pet/controllers/pet_weights_controller_test.mocks.dart b/test/app/pet/controllers/pet_weights_controller_test.mocks.dart index 0dabff2..6936b6c 100644 --- a/test/app/pet/controllers/pet_weights_controller_test.mocks.dart +++ b/test/app/pet/controllers/pet_weights_controller_test.mocks.dart @@ -9,12 +9,13 @@ import 'package:drift/drift.dart' as _i2; import 'package:drift/src/runtime/executor/stream_queries.dart' as _i4; import 'package:mockito/mockito.dart' as _i1; import 'package:mockito/src/dummies.dart' as _i6; +import 'package:petjournal/constants/linked_record_type.dart' as _i10; import 'package:petjournal/constants/pet_sex.dart' as _i7; import 'package:petjournal/constants/pet_status.dart' as _i8; import 'package:petjournal/constants/weight_units.dart' as _i9; import 'package:petjournal/data/database/database_service.dart' as _i3; import 'package:petjournal/data/database/tables/journal_entry_details.dart' - as _i10; + as _i11; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -809,12 +810,18 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { required String? entryText, required List? petIdList, required List? tags, + _i10.LinkedRecordType? linkedRecordType, + int? linkedRecordId, + String? linkedRecordTitle, }) => (super.noSuchMethod( Invocation.method(#createJournalEntryForPet, [], { #entryText: entryText, #petIdList: petIdList, #tags: tags, + #linkedRecordType: linkedRecordType, + #linkedRecordId: linkedRecordId, + #linkedRecordTitle: linkedRecordTitle, }), returnValue: _i5.Future<_i3.JournalEntry?>.value(), ) @@ -838,6 +845,22 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { ) as _i5.Future); + @override + _i5.Future updateLinkedJournalEntry({ + required int? linkedRecordId, + required _i10.LinkedRecordType? linkedRecordType, + required String? linkedRecordTitle, + }) => + (super.noSuchMethod( + Invocation.method(#updateLinkedJournalEntry, [], { + #linkedRecordId: linkedRecordId, + #linkedRecordType: linkedRecordType, + #linkedRecordTitle: linkedRecordTitle, + }), + returnValue: _i5.Future.value(0), + ) + as _i5.Future); + @override _i5.Future<_i3.JournalEntry?> getJournalEntry(int? id) => (super.noSuchMethod( @@ -863,14 +886,14 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { as _i5.Stream>); @override - _i5.Stream> getAllJournalEntryDetailsForPet( + _i5.Stream> getAllJournalEntryDetailsForPet( int? petId, ) => (super.noSuchMethod( Invocation.method(#getAllJournalEntryDetailsForPet, [petId]), - returnValue: _i5.Stream>.empty(), + returnValue: _i5.Stream>.empty(), ) - as _i5.Stream>); + as _i5.Stream>); @override _i5.Future deleteJournalEntry(int? id) => @@ -1005,11 +1028,13 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { _i5.Future saveSettingsUser( _i9.WeightUnits? defaultWeightUnit, bool? optIntoAnalyticsWarning, + bool? createLinkedJournalEntries, ) => (super.noSuchMethod( Invocation.method(#saveSettingsUser, [ defaultWeightUnit, optIntoAnalyticsWarning, + createLinkedJournalEntries, ]), returnValue: _i5.Future.value(0), ) diff --git a/test/app/pet/models/pet_weight_model_test.dart b/test/app/pet/models/pet_weight_model_test.dart new file mode 100644 index 0000000..9574f17 --- /dev/null +++ b/test/app/pet/models/pet_weight_model_test.dart @@ -0,0 +1,39 @@ +import 'package:petjournal/app/pet/models/pet_weight_model.dart'; +import 'package:petjournal/constants/weight_units.dart'; +import 'package:test/test.dart'; + +void main() { + group('PetWeightModel', () { + test('When weight is set to metric, niceName should return kg', () { + // ARRANGE + PetWeightModel petWeight = PetWeightModel( + petId: 1, + weight: 1.5, + weightUnit: WeightUnits.metric, + date: DateTime.now(), + ); + + // ACT + String niceName = petWeight.niceName(); + + // ASSERT + expect(niceName, '1.5 kg'); + }); + + test('When weight is set to imperial, niceName should return lbs', () { + // ARRANGE + PetWeightModel petWeight = PetWeightModel( + petId: 1, + weight: 7.2, + weightUnit: WeightUnits.imperial, + date: DateTime.now(), + ); + + // ACT + String niceName = petWeight.niceName(); + + // ASSERT + expect(niceName, '7.2 lbs'); + }); + }); +} diff --git a/test/app/pet/views/edit_journal_screen_test.mocks.dart b/test/app/pet/views/edit_journal_screen_test.mocks.dart index 37cac8c..c9a8ccd 100644 --- a/test/app/pet/views/edit_journal_screen_test.mocks.dart +++ b/test/app/pet/views/edit_journal_screen_test.mocks.dart @@ -11,18 +11,19 @@ import 'package:flutter/widgets.dart' as _i7; import 'package:go_router/src/configuration.dart' as _i6; import 'package:go_router/src/delegate.dart' as _i8; import 'package:go_router/src/information_provider.dart' as _i9; -import 'package:go_router/src/match.dart' as _i18; +import 'package:go_router/src/match.dart' as _i19; import 'package:go_router/src/parser.dart' as _i10; -import 'package:go_router/src/router.dart' as _i17; +import 'package:go_router/src/router.dart' as _i18; import 'package:go_router/src/state.dart' as _i11; import 'package:mockito/mockito.dart' as _i1; import 'package:mockito/src/dummies.dart' as _i12; +import 'package:petjournal/constants/linked_record_type.dart' as _i16; import 'package:petjournal/constants/pet_sex.dart' as _i13; import 'package:petjournal/constants/pet_status.dart' as _i14; import 'package:petjournal/constants/weight_units.dart' as _i15; import 'package:petjournal/data/database/database_service.dart' as _i3; import 'package:petjournal/data/database/tables/journal_entry_details.dart' - as _i16; + as _i17; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -853,12 +854,18 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { required String? entryText, required List? petIdList, required List? tags, + _i16.LinkedRecordType? linkedRecordType, + int? linkedRecordId, + String? linkedRecordTitle, }) => (super.noSuchMethod( Invocation.method(#createJournalEntryForPet, [], { #entryText: entryText, #petIdList: petIdList, #tags: tags, + #linkedRecordType: linkedRecordType, + #linkedRecordId: linkedRecordId, + #linkedRecordTitle: linkedRecordTitle, }), returnValue: _i5.Future<_i3.JournalEntry?>.value(), ) @@ -882,6 +889,22 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { ) as _i5.Future); + @override + _i5.Future updateLinkedJournalEntry({ + required int? linkedRecordId, + required _i16.LinkedRecordType? linkedRecordType, + required String? linkedRecordTitle, + }) => + (super.noSuchMethod( + Invocation.method(#updateLinkedJournalEntry, [], { + #linkedRecordId: linkedRecordId, + #linkedRecordType: linkedRecordType, + #linkedRecordTitle: linkedRecordTitle, + }), + returnValue: _i5.Future.value(0), + ) + as _i5.Future); + @override _i5.Future<_i3.JournalEntry?> getJournalEntry(int? id) => (super.noSuchMethod( @@ -907,14 +930,14 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { as _i5.Stream>); @override - _i5.Stream> getAllJournalEntryDetailsForPet( + _i5.Stream> getAllJournalEntryDetailsForPet( int? petId, ) => (super.noSuchMethod( Invocation.method(#getAllJournalEntryDetailsForPet, [petId]), - returnValue: _i5.Stream>.empty(), + returnValue: _i5.Stream>.empty(), ) - as _i5.Stream>); + as _i5.Stream>); @override _i5.Future deleteJournalEntry(int? id) => @@ -1049,11 +1072,13 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { _i5.Future saveSettingsUser( _i15.WeightUnits? defaultWeightUnit, bool? optIntoAnalyticsWarning, + bool? createLinkedJournalEntries, ) => (super.noSuchMethod( Invocation.method(#saveSettingsUser, [ defaultWeightUnit, optIntoAnalyticsWarning, + createLinkedJournalEntries, ]), returnValue: _i5.Future.value(0), ) @@ -1549,7 +1574,7 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { /// A class which mocks [GoRouter]. /// /// See the documentation for Mockito's code generation for more information. -class MockGoRouter extends _i1.Mock implements _i17.GoRouter { +class MockGoRouter extends _i1.Mock implements _i18.GoRouter { MockGoRouter() { _i1.throwOnMissingStub(this); } @@ -1699,7 +1724,7 @@ class MockGoRouter extends _i1.Mock implements _i17.GoRouter { ); @override - void restore(_i18.RouteMatchList? matchList) => super.noSuchMethod( + void restore(_i19.RouteMatchList? matchList) => super.noSuchMethod( Invocation.method(#restore, [matchList]), returnValueForMissingStub: null, ); diff --git a/test/app/pet/views/edit_pet_med_screen_test.dart b/test/app/pet/views/edit_pet_med_screen_test.dart index b3ed2d3..26f2466 100644 --- a/test/app/pet/views/edit_pet_med_screen_test.dart +++ b/test/app/pet/views/edit_pet_med_screen_test.dart @@ -5,8 +5,10 @@ import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:petjournal/app/pet/views/edit_pet_med_screen.dart'; import 'package:petjournal/app/pet/models/pet_med_model.dart'; +import 'package:petjournal/constants/defaults.dart' as defaults; import 'package:petjournal/constants/pet_sex.dart'; import 'package:petjournal/constants/pet_status.dart'; +import 'package:petjournal/constants/weight_units.dart'; import 'package:petjournal/data/database/database_service.dart'; import 'package:go_router/go_router.dart'; @@ -69,6 +71,18 @@ void main() { ); when(mockDb.getPet(any)).thenAnswer((_) async => testPet); + when(mockDb.watchSettings()).thenAnswer( + (_) => Stream.value( + Setting( + settingsId: defaults.defaultSettingsId, + acceptedTermsAndConditions: true, + optIntoAnalyticsWarning: true, + onBoardingComplete: true, + defaultWeightUnit: WeightUnits.metric.dataValue, + createLinkedJournalEntries: false, + ), + ), + ); }); group('EditPetMedScreen', () { diff --git a/test/app/pet/views/edit_pet_med_screen_test.mocks.dart b/test/app/pet/views/edit_pet_med_screen_test.mocks.dart index 6689c3f..d67f3af 100644 --- a/test/app/pet/views/edit_pet_med_screen_test.mocks.dart +++ b/test/app/pet/views/edit_pet_med_screen_test.mocks.dart @@ -11,18 +11,19 @@ import 'package:flutter/widgets.dart' as _i7; import 'package:go_router/src/configuration.dart' as _i6; import 'package:go_router/src/delegate.dart' as _i8; import 'package:go_router/src/information_provider.dart' as _i9; -import 'package:go_router/src/match.dart' as _i18; +import 'package:go_router/src/match.dart' as _i19; import 'package:go_router/src/parser.dart' as _i10; -import 'package:go_router/src/router.dart' as _i17; +import 'package:go_router/src/router.dart' as _i18; import 'package:go_router/src/state.dart' as _i11; import 'package:mockito/mockito.dart' as _i1; import 'package:mockito/src/dummies.dart' as _i12; +import 'package:petjournal/constants/linked_record_type.dart' as _i16; import 'package:petjournal/constants/pet_sex.dart' as _i13; import 'package:petjournal/constants/pet_status.dart' as _i14; import 'package:petjournal/constants/weight_units.dart' as _i15; import 'package:petjournal/data/database/database_service.dart' as _i3; import 'package:petjournal/data/database/tables/journal_entry_details.dart' - as _i16; + as _i17; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -853,12 +854,18 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { required String? entryText, required List? petIdList, required List? tags, + _i16.LinkedRecordType? linkedRecordType, + int? linkedRecordId, + String? linkedRecordTitle, }) => (super.noSuchMethod( Invocation.method(#createJournalEntryForPet, [], { #entryText: entryText, #petIdList: petIdList, #tags: tags, + #linkedRecordType: linkedRecordType, + #linkedRecordId: linkedRecordId, + #linkedRecordTitle: linkedRecordTitle, }), returnValue: _i5.Future<_i3.JournalEntry?>.value(), ) @@ -882,6 +889,22 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { ) as _i5.Future); + @override + _i5.Future updateLinkedJournalEntry({ + required int? linkedRecordId, + required _i16.LinkedRecordType? linkedRecordType, + required String? linkedRecordTitle, + }) => + (super.noSuchMethod( + Invocation.method(#updateLinkedJournalEntry, [], { + #linkedRecordId: linkedRecordId, + #linkedRecordType: linkedRecordType, + #linkedRecordTitle: linkedRecordTitle, + }), + returnValue: _i5.Future.value(0), + ) + as _i5.Future); + @override _i5.Future<_i3.JournalEntry?> getJournalEntry(int? id) => (super.noSuchMethod( @@ -907,14 +930,14 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { as _i5.Stream>); @override - _i5.Stream> getAllJournalEntryDetailsForPet( + _i5.Stream> getAllJournalEntryDetailsForPet( int? petId, ) => (super.noSuchMethod( Invocation.method(#getAllJournalEntryDetailsForPet, [petId]), - returnValue: _i5.Stream>.empty(), + returnValue: _i5.Stream>.empty(), ) - as _i5.Stream>); + as _i5.Stream>); @override _i5.Future deleteJournalEntry(int? id) => @@ -1049,11 +1072,13 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { _i5.Future saveSettingsUser( _i15.WeightUnits? defaultWeightUnit, bool? optIntoAnalyticsWarning, + bool? createLinkedJournalEntries, ) => (super.noSuchMethod( Invocation.method(#saveSettingsUser, [ defaultWeightUnit, optIntoAnalyticsWarning, + createLinkedJournalEntries, ]), returnValue: _i5.Future.value(0), ) @@ -1549,7 +1574,7 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { /// A class which mocks [GoRouter]. /// /// See the documentation for Mockito's code generation for more information. -class MockGoRouter extends _i1.Mock implements _i17.GoRouter { +class MockGoRouter extends _i1.Mock implements _i18.GoRouter { MockGoRouter() { _i1.throwOnMissingStub(this); } @@ -1699,7 +1724,7 @@ class MockGoRouter extends _i1.Mock implements _i17.GoRouter { ); @override - void restore(_i18.RouteMatchList? matchList) => super.noSuchMethod( + void restore(_i19.RouteMatchList? matchList) => super.noSuchMethod( Invocation.method(#restore, [matchList]), returnValueForMissingStub: null, ); diff --git a/test/app/pet/views/edit_pet_screen_test.mocks.dart b/test/app/pet/views/edit_pet_screen_test.mocks.dart index 3c2ca02..d7e6e1e 100644 --- a/test/app/pet/views/edit_pet_screen_test.mocks.dart +++ b/test/app/pet/views/edit_pet_screen_test.mocks.dart @@ -11,18 +11,19 @@ import 'package:flutter/widgets.dart' as _i7; import 'package:go_router/src/configuration.dart' as _i6; import 'package:go_router/src/delegate.dart' as _i8; import 'package:go_router/src/information_provider.dart' as _i9; -import 'package:go_router/src/match.dart' as _i18; +import 'package:go_router/src/match.dart' as _i19; import 'package:go_router/src/parser.dart' as _i10; -import 'package:go_router/src/router.dart' as _i17; +import 'package:go_router/src/router.dart' as _i18; import 'package:go_router/src/state.dart' as _i11; import 'package:mockito/mockito.dart' as _i1; import 'package:mockito/src/dummies.dart' as _i12; +import 'package:petjournal/constants/linked_record_type.dart' as _i16; import 'package:petjournal/constants/pet_sex.dart' as _i13; import 'package:petjournal/constants/pet_status.dart' as _i14; import 'package:petjournal/constants/weight_units.dart' as _i15; import 'package:petjournal/data/database/database_service.dart' as _i3; import 'package:petjournal/data/database/tables/journal_entry_details.dart' - as _i16; + as _i17; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -853,12 +854,18 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { required String? entryText, required List? petIdList, required List? tags, + _i16.LinkedRecordType? linkedRecordType, + int? linkedRecordId, + String? linkedRecordTitle, }) => (super.noSuchMethod( Invocation.method(#createJournalEntryForPet, [], { #entryText: entryText, #petIdList: petIdList, #tags: tags, + #linkedRecordType: linkedRecordType, + #linkedRecordId: linkedRecordId, + #linkedRecordTitle: linkedRecordTitle, }), returnValue: _i5.Future<_i3.JournalEntry?>.value(), ) @@ -882,6 +889,22 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { ) as _i5.Future); + @override + _i5.Future updateLinkedJournalEntry({ + required int? linkedRecordId, + required _i16.LinkedRecordType? linkedRecordType, + required String? linkedRecordTitle, + }) => + (super.noSuchMethod( + Invocation.method(#updateLinkedJournalEntry, [], { + #linkedRecordId: linkedRecordId, + #linkedRecordType: linkedRecordType, + #linkedRecordTitle: linkedRecordTitle, + }), + returnValue: _i5.Future.value(0), + ) + as _i5.Future); + @override _i5.Future<_i3.JournalEntry?> getJournalEntry(int? id) => (super.noSuchMethod( @@ -907,14 +930,14 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { as _i5.Stream>); @override - _i5.Stream> getAllJournalEntryDetailsForPet( + _i5.Stream> getAllJournalEntryDetailsForPet( int? petId, ) => (super.noSuchMethod( Invocation.method(#getAllJournalEntryDetailsForPet, [petId]), - returnValue: _i5.Stream>.empty(), + returnValue: _i5.Stream>.empty(), ) - as _i5.Stream>); + as _i5.Stream>); @override _i5.Future deleteJournalEntry(int? id) => @@ -1049,11 +1072,13 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { _i5.Future saveSettingsUser( _i15.WeightUnits? defaultWeightUnit, bool? optIntoAnalyticsWarning, + bool? createLinkedJournalEntries, ) => (super.noSuchMethod( Invocation.method(#saveSettingsUser, [ defaultWeightUnit, optIntoAnalyticsWarning, + createLinkedJournalEntries, ]), returnValue: _i5.Future.value(0), ) @@ -1549,7 +1574,7 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { /// A class which mocks [GoRouter]. /// /// See the documentation for Mockito's code generation for more information. -class MockGoRouter extends _i1.Mock implements _i17.GoRouter { +class MockGoRouter extends _i1.Mock implements _i18.GoRouter { MockGoRouter() { _i1.throwOnMissingStub(this); } @@ -1699,7 +1724,7 @@ class MockGoRouter extends _i1.Mock implements _i17.GoRouter { ); @override - void restore(_i18.RouteMatchList? matchList) => super.noSuchMethod( + void restore(_i19.RouteMatchList? matchList) => super.noSuchMethod( Invocation.method(#restore, [matchList]), returnValueForMissingStub: null, ); diff --git a/test/app/pet/views/edit_pet_vaccination_screen_test.dart b/test/app/pet/views/edit_pet_vaccination_screen_test.dart index 77500e8..918008c 100644 --- a/test/app/pet/views/edit_pet_vaccination_screen_test.dart +++ b/test/app/pet/views/edit_pet_vaccination_screen_test.dart @@ -5,8 +5,10 @@ import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:petjournal/app/pet/models/pet_vaccination_model.dart'; import 'package:petjournal/app/pet/views/edit_pet_vaccination_screen.dart'; +import 'package:petjournal/constants/defaults.dart' as defaults; import 'package:petjournal/constants/pet_sex.dart'; import 'package:petjournal/constants/pet_status.dart'; +import 'package:petjournal/constants/weight_units.dart'; import 'package:petjournal/data/database/database_service.dart'; import 'package:go_router/go_router.dart'; @@ -78,6 +80,18 @@ void main() { ); when(mockDb.getPet(any)).thenAnswer((_) async => testPet); + when(mockDb.watchSettings()).thenAnswer( + (_) => Stream.value( + Setting( + settingsId: defaults.defaultSettingsId, + acceptedTermsAndConditions: true, + optIntoAnalyticsWarning: true, + onBoardingComplete: true, + defaultWeightUnit: WeightUnits.metric.dataValue, + createLinkedJournalEntries: false, + ), + ), + ); }); group('EditPetVaccinationScreen', () { diff --git a/test/app/pet/views/edit_pet_vaccination_screen_test.mocks.dart b/test/app/pet/views/edit_pet_vaccination_screen_test.mocks.dart index 85e79be..85c3480 100644 --- a/test/app/pet/views/edit_pet_vaccination_screen_test.mocks.dart +++ b/test/app/pet/views/edit_pet_vaccination_screen_test.mocks.dart @@ -11,18 +11,19 @@ import 'package:flutter/widgets.dart' as _i7; import 'package:go_router/src/configuration.dart' as _i6; import 'package:go_router/src/delegate.dart' as _i8; import 'package:go_router/src/information_provider.dart' as _i9; -import 'package:go_router/src/match.dart' as _i18; +import 'package:go_router/src/match.dart' as _i19; import 'package:go_router/src/parser.dart' as _i10; -import 'package:go_router/src/router.dart' as _i17; +import 'package:go_router/src/router.dart' as _i18; import 'package:go_router/src/state.dart' as _i11; import 'package:mockito/mockito.dart' as _i1; import 'package:mockito/src/dummies.dart' as _i12; +import 'package:petjournal/constants/linked_record_type.dart' as _i16; import 'package:petjournal/constants/pet_sex.dart' as _i13; import 'package:petjournal/constants/pet_status.dart' as _i14; import 'package:petjournal/constants/weight_units.dart' as _i15; import 'package:petjournal/data/database/database_service.dart' as _i3; import 'package:petjournal/data/database/tables/journal_entry_details.dart' - as _i16; + as _i17; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -853,12 +854,18 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { required String? entryText, required List? petIdList, required List? tags, + _i16.LinkedRecordType? linkedRecordType, + int? linkedRecordId, + String? linkedRecordTitle, }) => (super.noSuchMethod( Invocation.method(#createJournalEntryForPet, [], { #entryText: entryText, #petIdList: petIdList, #tags: tags, + #linkedRecordType: linkedRecordType, + #linkedRecordId: linkedRecordId, + #linkedRecordTitle: linkedRecordTitle, }), returnValue: _i5.Future<_i3.JournalEntry?>.value(), ) @@ -882,6 +889,22 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { ) as _i5.Future); + @override + _i5.Future updateLinkedJournalEntry({ + required int? linkedRecordId, + required _i16.LinkedRecordType? linkedRecordType, + required String? linkedRecordTitle, + }) => + (super.noSuchMethod( + Invocation.method(#updateLinkedJournalEntry, [], { + #linkedRecordId: linkedRecordId, + #linkedRecordType: linkedRecordType, + #linkedRecordTitle: linkedRecordTitle, + }), + returnValue: _i5.Future.value(0), + ) + as _i5.Future); + @override _i5.Future<_i3.JournalEntry?> getJournalEntry(int? id) => (super.noSuchMethod( @@ -907,14 +930,14 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { as _i5.Stream>); @override - _i5.Stream> getAllJournalEntryDetailsForPet( + _i5.Stream> getAllJournalEntryDetailsForPet( int? petId, ) => (super.noSuchMethod( Invocation.method(#getAllJournalEntryDetailsForPet, [petId]), - returnValue: _i5.Stream>.empty(), + returnValue: _i5.Stream>.empty(), ) - as _i5.Stream>); + as _i5.Stream>); @override _i5.Future deleteJournalEntry(int? id) => @@ -1049,11 +1072,13 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { _i5.Future saveSettingsUser( _i15.WeightUnits? defaultWeightUnit, bool? optIntoAnalyticsWarning, + bool? createLinkedJournalEntries, ) => (super.noSuchMethod( Invocation.method(#saveSettingsUser, [ defaultWeightUnit, optIntoAnalyticsWarning, + createLinkedJournalEntries, ]), returnValue: _i5.Future.value(0), ) @@ -1549,7 +1574,7 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { /// A class which mocks [GoRouter]. /// /// See the documentation for Mockito's code generation for more information. -class MockGoRouter extends _i1.Mock implements _i17.GoRouter { +class MockGoRouter extends _i1.Mock implements _i18.GoRouter { MockGoRouter() { _i1.throwOnMissingStub(this); } @@ -1699,7 +1724,7 @@ class MockGoRouter extends _i1.Mock implements _i17.GoRouter { ); @override - void restore(_i18.RouteMatchList? matchList) => super.noSuchMethod( + void restore(_i19.RouteMatchList? matchList) => super.noSuchMethod( Invocation.method(#restore, [matchList]), returnValueForMissingStub: null, ); diff --git a/test/app/pet/views/edit_pet_weight_screen_test.dart b/test/app/pet/views/edit_pet_weight_screen_test.dart index 54f1ce4..6bbf39b 100644 --- a/test/app/pet/views/edit_pet_weight_screen_test.dart +++ b/test/app/pet/views/edit_pet_weight_screen_test.dart @@ -5,6 +5,7 @@ import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:petjournal/app/pet/views/edit_pet_weight_screen.dart'; import 'package:petjournal/app/pet/models/pet_weight_model.dart'; +import 'package:petjournal/constants/defaults.dart' as defaults; import 'package:petjournal/constants/pet_sex.dart'; import 'package:petjournal/constants/pet_status.dart'; import 'package:petjournal/constants/weight_units.dart'; @@ -69,6 +70,18 @@ void main() { ); when(mockDb.getPet(any)).thenAnswer((_) async => testPet); + when(mockDb.watchSettings()).thenAnswer( + (_) => Stream.value( + Setting( + settingsId: defaults.defaultSettingsId, + acceptedTermsAndConditions: true, + optIntoAnalyticsWarning: true, + onBoardingComplete: true, + defaultWeightUnit: WeightUnits.metric.dataValue, + createLinkedJournalEntries: false, + ), + ), + ); }); group('EditPetWeightScreen', () { diff --git a/test/app/pet/views/edit_pet_weight_screen_test.mocks.dart b/test/app/pet/views/edit_pet_weight_screen_test.mocks.dart index 2c4a143..53b013c 100644 --- a/test/app/pet/views/edit_pet_weight_screen_test.mocks.dart +++ b/test/app/pet/views/edit_pet_weight_screen_test.mocks.dart @@ -11,18 +11,19 @@ import 'package:flutter/widgets.dart' as _i7; import 'package:go_router/src/configuration.dart' as _i6; import 'package:go_router/src/delegate.dart' as _i8; import 'package:go_router/src/information_provider.dart' as _i9; -import 'package:go_router/src/match.dart' as _i18; +import 'package:go_router/src/match.dart' as _i19; import 'package:go_router/src/parser.dart' as _i10; -import 'package:go_router/src/router.dart' as _i17; +import 'package:go_router/src/router.dart' as _i18; import 'package:go_router/src/state.dart' as _i11; import 'package:mockito/mockito.dart' as _i1; import 'package:mockito/src/dummies.dart' as _i12; +import 'package:petjournal/constants/linked_record_type.dart' as _i16; import 'package:petjournal/constants/pet_sex.dart' as _i13; import 'package:petjournal/constants/pet_status.dart' as _i14; import 'package:petjournal/constants/weight_units.dart' as _i15; import 'package:petjournal/data/database/database_service.dart' as _i3; import 'package:petjournal/data/database/tables/journal_entry_details.dart' - as _i16; + as _i17; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -853,12 +854,18 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { required String? entryText, required List? petIdList, required List? tags, + _i16.LinkedRecordType? linkedRecordType, + int? linkedRecordId, + String? linkedRecordTitle, }) => (super.noSuchMethod( Invocation.method(#createJournalEntryForPet, [], { #entryText: entryText, #petIdList: petIdList, #tags: tags, + #linkedRecordType: linkedRecordType, + #linkedRecordId: linkedRecordId, + #linkedRecordTitle: linkedRecordTitle, }), returnValue: _i5.Future<_i3.JournalEntry?>.value(), ) @@ -882,6 +889,22 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { ) as _i5.Future); + @override + _i5.Future updateLinkedJournalEntry({ + required int? linkedRecordId, + required _i16.LinkedRecordType? linkedRecordType, + required String? linkedRecordTitle, + }) => + (super.noSuchMethod( + Invocation.method(#updateLinkedJournalEntry, [], { + #linkedRecordId: linkedRecordId, + #linkedRecordType: linkedRecordType, + #linkedRecordTitle: linkedRecordTitle, + }), + returnValue: _i5.Future.value(0), + ) + as _i5.Future); + @override _i5.Future<_i3.JournalEntry?> getJournalEntry(int? id) => (super.noSuchMethod( @@ -907,14 +930,14 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { as _i5.Stream>); @override - _i5.Stream> getAllJournalEntryDetailsForPet( + _i5.Stream> getAllJournalEntryDetailsForPet( int? petId, ) => (super.noSuchMethod( Invocation.method(#getAllJournalEntryDetailsForPet, [petId]), - returnValue: _i5.Stream>.empty(), + returnValue: _i5.Stream>.empty(), ) - as _i5.Stream>); + as _i5.Stream>); @override _i5.Future deleteJournalEntry(int? id) => @@ -1049,11 +1072,13 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { _i5.Future saveSettingsUser( _i15.WeightUnits? defaultWeightUnit, bool? optIntoAnalyticsWarning, + bool? createLinkedJournalEntries, ) => (super.noSuchMethod( Invocation.method(#saveSettingsUser, [ defaultWeightUnit, optIntoAnalyticsWarning, + createLinkedJournalEntries, ]), returnValue: _i5.Future.value(0), ) @@ -1549,7 +1574,7 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { /// A class which mocks [GoRouter]. /// /// See the documentation for Mockito's code generation for more information. -class MockGoRouter extends _i1.Mock implements _i17.GoRouter { +class MockGoRouter extends _i1.Mock implements _i18.GoRouter { MockGoRouter() { _i1.throwOnMissingStub(this); } @@ -1699,7 +1724,7 @@ class MockGoRouter extends _i1.Mock implements _i17.GoRouter { ); @override - void restore(_i18.RouteMatchList? matchList) => super.noSuchMethod( + void restore(_i19.RouteMatchList? matchList) => super.noSuchMethod( Invocation.method(#restore, [matchList]), returnValueForMissingStub: null, ); diff --git a/test/app/settings/controllers/settings_controller_test.dart b/test/app/settings/controllers/settings_controller_test.dart index 65ce250..54001bf 100644 --- a/test/app/settings/controllers/settings_controller_test.dart +++ b/test/app/settings/controllers/settings_controller_test.dart @@ -48,6 +48,7 @@ void main() { optIntoAnalyticsWarning: true, onBoardingComplete: true, defaultWeightUnit: WeightUnits.metric.dataValue, + createLinkedJournalEntries: true, ); when( @@ -70,6 +71,7 @@ void main() { expect(settings.optIntoAnalyticsWarning, true); expect(settings.onBoardingComplete, true); expect(settings.defaultWeightUnit, WeightUnits.metric); + expect(settings.createLinkedJournalEntries, true); verify(mockDatabaseService.watchSettings()).called(1); // Workaround for FakeTimer error @@ -87,6 +89,8 @@ void main() { acceptedTermsAndConditions: false, optIntoAnalyticsWarning: false, onBoardingComplete: false, + defaultWeightUnit: null, + createLinkedJournalEntries: true, ); when( @@ -109,6 +113,7 @@ void main() { expect(settings.optIntoAnalyticsWarning, false); expect(settings.onBoardingComplete, false); expect(settings.defaultWeightUnit, null); + expect(settings.createLinkedJournalEntries, true); verify(mockDatabaseService.watchSettings()).called(1); await tester.pumpWidget(Container()); @@ -240,7 +245,7 @@ void main() { (tester) async { // ARRANGE when( - mockDatabaseService.saveSettingsUser(any, any), + mockDatabaseService.saveSettingsUser(any, any, any), ).thenAnswer((_) async => 1); final container = createContainer( @@ -252,12 +257,16 @@ void main() { // ACT final success = await container .read(settingsControllerProvider.notifier) - .saveUserSettings(WeightUnits.metric, null); + .saveUserSettings(WeightUnits.metric, null, null); // ASSERT expect(success, true); verify( - mockDatabaseService.saveSettingsUser(WeightUnits.metric, null), + mockDatabaseService.saveSettingsUser( + WeightUnits.metric, + null, + null, + ), ).called(1); await tester.pumpWidget(Container()); @@ -270,7 +279,7 @@ void main() { (tester) async { // ARRANGE when( - mockDatabaseService.saveSettingsUser(any, any), + mockDatabaseService.saveSettingsUser(any, any, any), ).thenAnswer((_) async => 1); final container = createContainer( @@ -282,11 +291,43 @@ void main() { // ACT final success = await container .read(settingsControllerProvider.notifier) - .saveUserSettings(null, true); + .saveUserSettings(null, true, null); // ASSERT expect(success, true); - verify(mockDatabaseService.saveSettingsUser(null, true)).called(1); + verify( + mockDatabaseService.saveSettingsUser(null, true, null), + ).called(1); + + await tester.pumpWidget(Container()); + await tester.pumpAndSettle(); + }, + ); + + testWidgets( + 'Should return true When database successfully saves create journal entry', + (tester) async { + // ARRANGE + when( + mockDatabaseService.saveSettingsUser(any, any, any), + ).thenAnswer((_) async => 1); + + final container = createContainer( + overrides: [ + DatabaseService.provider.overrideWithValue(mockDatabaseService), + ], + ); + + // ACT + final success = await container + .read(settingsControllerProvider.notifier) + .saveUserSettings(null, null, false); + + // ASSERT + expect(success, true); + verify( + mockDatabaseService.saveSettingsUser(null, null, false), + ).called(1); await tester.pumpWidget(Container()); await tester.pumpAndSettle(); @@ -294,11 +335,11 @@ void main() { ); testWidgets( - 'Should return true When database successfully saves weight units and analytics opt in', + 'Should return true When database successfully saves all settings', (tester) async { // ARRANGE when( - mockDatabaseService.saveSettingsUser(any, any), + mockDatabaseService.saveSettingsUser(any, any, any), ).thenAnswer((_) async => 1); final container = createContainer( @@ -310,12 +351,16 @@ void main() { // ACT final success = await container .read(settingsControllerProvider.notifier) - .saveUserSettings(WeightUnits.metric, true); + .saveUserSettings(WeightUnits.metric, true, false); // ASSERT expect(success, true); verify( - mockDatabaseService.saveSettingsUser(WeightUnits.metric, true), + mockDatabaseService.saveSettingsUser( + WeightUnits.metric, + true, + false, + ), ).called(1); await tester.pumpWidget(Container()); @@ -328,7 +373,7 @@ void main() { (tester) async { // ARRANGE when( - mockDatabaseService.saveSettingsUser(any, any), + mockDatabaseService.saveSettingsUser(any, any, any), ).thenAnswer((_) async => 0); final container = createContainer( @@ -340,12 +385,16 @@ void main() { // ACT final success = await container .read(settingsControllerProvider.notifier) - .saveUserSettings(WeightUnits.imperial, true); + .saveUserSettings(WeightUnits.imperial, true, true); // ASSERT expect(success, false); verify( - mockDatabaseService.saveSettingsUser(WeightUnits.imperial, true), + mockDatabaseService.saveSettingsUser( + WeightUnits.imperial, + true, + true, + ), ).called(1); await tester.pumpWidget(Container()); @@ -353,12 +402,10 @@ void main() { }, ); - testWidgets('Should handle null weight units and analytics opt in', ( - tester, - ) async { + testWidgets('Should handle null values', (tester) async { // ARRANGE when( - mockDatabaseService.saveSettingsUser(null, null), + mockDatabaseService.saveSettingsUser(null, null, null), ).thenAnswer((_) async => 1); final container = createContainer( @@ -370,11 +417,13 @@ void main() { // ACT final success = await container .read(settingsControllerProvider.notifier) - .saveUserSettings(null, null); + .saveUserSettings(null, null, null); // ASSERT expect(success, true); - verify(mockDatabaseService.saveSettingsUser(null, null)).called(1); + verify( + mockDatabaseService.saveSettingsUser(null, null, null), + ).called(1); await tester.pumpWidget(Container()); await tester.pumpAndSettle(); diff --git a/test/app/settings/controllers/settings_controller_test.mocks.dart b/test/app/settings/controllers/settings_controller_test.mocks.dart index b2a7cdb..2a3ae80 100644 --- a/test/app/settings/controllers/settings_controller_test.mocks.dart +++ b/test/app/settings/controllers/settings_controller_test.mocks.dart @@ -9,12 +9,13 @@ import 'package:drift/drift.dart' as _i2; import 'package:drift/src/runtime/executor/stream_queries.dart' as _i4; import 'package:mockito/mockito.dart' as _i1; import 'package:mockito/src/dummies.dart' as _i6; +import 'package:petjournal/constants/linked_record_type.dart' as _i10; import 'package:petjournal/constants/pet_sex.dart' as _i7; import 'package:petjournal/constants/pet_status.dart' as _i8; import 'package:petjournal/constants/weight_units.dart' as _i9; import 'package:petjournal/data/database/database_service.dart' as _i3; import 'package:petjournal/data/database/tables/journal_entry_details.dart' - as _i10; + as _i11; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -809,12 +810,18 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { required String? entryText, required List? petIdList, required List? tags, + _i10.LinkedRecordType? linkedRecordType, + int? linkedRecordId, + String? linkedRecordTitle, }) => (super.noSuchMethod( Invocation.method(#createJournalEntryForPet, [], { #entryText: entryText, #petIdList: petIdList, #tags: tags, + #linkedRecordType: linkedRecordType, + #linkedRecordId: linkedRecordId, + #linkedRecordTitle: linkedRecordTitle, }), returnValue: _i5.Future<_i3.JournalEntry?>.value(), ) @@ -838,6 +845,22 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { ) as _i5.Future); + @override + _i5.Future updateLinkedJournalEntry({ + required int? linkedRecordId, + required _i10.LinkedRecordType? linkedRecordType, + required String? linkedRecordTitle, + }) => + (super.noSuchMethod( + Invocation.method(#updateLinkedJournalEntry, [], { + #linkedRecordId: linkedRecordId, + #linkedRecordType: linkedRecordType, + #linkedRecordTitle: linkedRecordTitle, + }), + returnValue: _i5.Future.value(0), + ) + as _i5.Future); + @override _i5.Future<_i3.JournalEntry?> getJournalEntry(int? id) => (super.noSuchMethod( @@ -863,14 +886,14 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { as _i5.Stream>); @override - _i5.Stream> getAllJournalEntryDetailsForPet( + _i5.Stream> getAllJournalEntryDetailsForPet( int? petId, ) => (super.noSuchMethod( Invocation.method(#getAllJournalEntryDetailsForPet, [petId]), - returnValue: _i5.Stream>.empty(), + returnValue: _i5.Stream>.empty(), ) - as _i5.Stream>); + as _i5.Stream>); @override _i5.Future deleteJournalEntry(int? id) => @@ -1005,11 +1028,13 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { _i5.Future saveSettingsUser( _i9.WeightUnits? defaultWeightUnit, bool? optIntoAnalyticsWarning, + bool? createLinkedJournalEntries, ) => (super.noSuchMethod( Invocation.method(#saveSettingsUser, [ defaultWeightUnit, optIntoAnalyticsWarning, + createLinkedJournalEntries, ]), returnValue: _i5.Future.value(0), ) diff --git a/test/app/settings/views/settings_screen_test.dart b/test/app/settings/views/settings_screen_test.dart index 3ee281e..dd2fbd2 100644 --- a/test/app/settings/views/settings_screen_test.dart +++ b/test/app/settings/views/settings_screen_test.dart @@ -19,6 +19,7 @@ Setting createMockSettings({ WeightUnits weightUnit = WeightUnits.metric, bool optIntoAnalyticsWarning = false, bool onBoardingComplete = true, + bool createLinkedJournalEntries = true, }) { return Setting( settingsId: 1, @@ -27,6 +28,7 @@ Setting createMockSettings({ optIntoAnalyticsWarning: optIntoAnalyticsWarning, lastUsedVersion: null, defaultWeightUnit: weightUnit.dataValue, + createLinkedJournalEntries: createLinkedJournalEntries, ); } @@ -108,7 +110,7 @@ void main() { .when(mockDb.watchSettings()) .thenAnswer((_) => Stream.value(mockSettings)); mock - .when(mockDb.saveSettingsUser(mock.any, mock.any)) + .when(mockDb.saveSettingsUser(mock.any, mock.any, mock.any)) .thenAnswer((_) => Future.value(1)); await tester.pumpWidget(createScreen(mockDb)); @@ -126,7 +128,7 @@ void main() { await tester.tap(find.widgetWithText(TextButton, "Save")); mock - .verify(mockDb.saveSettingsUser(WeightUnits.imperial, false)) + .verify(mockDb.saveSettingsUser(WeightUnits.imperial, false, true)) .called(1); }); @@ -139,7 +141,7 @@ void main() { .when(mockDb.watchSettings()) .thenAnswer((_) => Stream.value(mockSettings)); mock - .when(mockDb.saveSettingsUser(mock.any, mock.any)) + .when(mockDb.saveSettingsUser(mock.any, mock.any, mock.any)) .thenAnswer((_) => Future.value(1)); await tester.pumpWidget(createScreen(mockDb)); @@ -166,6 +168,49 @@ void main() { mockDb.saveSettingsUser( WeightUnits.metric, true, // optIntoAnalyticsWarning changed to true + true, + ), + ) + .called(1); + }); + + testWidgets('toggles create linked journal entries', (tester) async { + final mockSettings = createMockSettings( + optIntoAnalyticsWarning: false, + weightUnit: WeightUnits.metric, + createLinkedJournalEntries: false, + ); + mock + .when(mockDb.watchSettings()) + .thenAnswer((_) => Stream.value(mockSettings)); + mock + .when(mockDb.saveSettingsUser(mock.any, mock.any, mock.any)) + .thenAnswer((_) => Future.value(1)); + + await tester.pumpWidget(createScreen(mockDb)); + await tester.pumpAndSettle(); + + // Find and tap the analytics switch + final createLinkedJournalEntryFinder = find.byKey( + EditSettingsWidget.editSettingCreateLinkedJournalEntriesKey, + ); + final scrollableFinder = find.byType(Scrollable).last; + await tester.scrollUntilVisible( + createLinkedJournalEntryFinder, + 10, + scrollable: scrollableFinder, + ); + await tester.tap(createLinkedJournalEntryFinder); + await tester.pump(); + + // Tap the save button + await tester.tap(find.widgetWithText(TextButton, "Save")); + mock + .verify( + mockDb.saveSettingsUser( + WeightUnits.metric, + false, + true, // createLinkedJournalEntries changed to true ), ) .called(1); diff --git a/test/app/settings/views/settings_screen_test.mocks.dart b/test/app/settings/views/settings_screen_test.mocks.dart index c0c8e0b..6ada789 100644 --- a/test/app/settings/views/settings_screen_test.mocks.dart +++ b/test/app/settings/views/settings_screen_test.mocks.dart @@ -11,18 +11,19 @@ import 'package:flutter/widgets.dart' as _i7; import 'package:go_router/src/configuration.dart' as _i6; import 'package:go_router/src/delegate.dart' as _i8; import 'package:go_router/src/information_provider.dart' as _i9; -import 'package:go_router/src/match.dart' as _i18; +import 'package:go_router/src/match.dart' as _i19; import 'package:go_router/src/parser.dart' as _i10; -import 'package:go_router/src/router.dart' as _i17; +import 'package:go_router/src/router.dart' as _i18; import 'package:go_router/src/state.dart' as _i11; import 'package:mockito/mockito.dart' as _i1; import 'package:mockito/src/dummies.dart' as _i12; +import 'package:petjournal/constants/linked_record_type.dart' as _i16; import 'package:petjournal/constants/pet_sex.dart' as _i13; import 'package:petjournal/constants/pet_status.dart' as _i14; import 'package:petjournal/constants/weight_units.dart' as _i15; import 'package:petjournal/data/database/database_service.dart' as _i3; import 'package:petjournal/data/database/tables/journal_entry_details.dart' - as _i16; + as _i17; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -853,12 +854,18 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { required String? entryText, required List? petIdList, required List? tags, + _i16.LinkedRecordType? linkedRecordType, + int? linkedRecordId, + String? linkedRecordTitle, }) => (super.noSuchMethod( Invocation.method(#createJournalEntryForPet, [], { #entryText: entryText, #petIdList: petIdList, #tags: tags, + #linkedRecordType: linkedRecordType, + #linkedRecordId: linkedRecordId, + #linkedRecordTitle: linkedRecordTitle, }), returnValue: _i5.Future<_i3.JournalEntry?>.value(), ) @@ -882,6 +889,22 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { ) as _i5.Future); + @override + _i5.Future updateLinkedJournalEntry({ + required int? linkedRecordId, + required _i16.LinkedRecordType? linkedRecordType, + required String? linkedRecordTitle, + }) => + (super.noSuchMethod( + Invocation.method(#updateLinkedJournalEntry, [], { + #linkedRecordId: linkedRecordId, + #linkedRecordType: linkedRecordType, + #linkedRecordTitle: linkedRecordTitle, + }), + returnValue: _i5.Future.value(0), + ) + as _i5.Future); + @override _i5.Future<_i3.JournalEntry?> getJournalEntry(int? id) => (super.noSuchMethod( @@ -907,14 +930,14 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { as _i5.Stream>); @override - _i5.Stream> getAllJournalEntryDetailsForPet( + _i5.Stream> getAllJournalEntryDetailsForPet( int? petId, ) => (super.noSuchMethod( Invocation.method(#getAllJournalEntryDetailsForPet, [petId]), - returnValue: _i5.Stream>.empty(), + returnValue: _i5.Stream>.empty(), ) - as _i5.Stream>); + as _i5.Stream>); @override _i5.Future deleteJournalEntry(int? id) => @@ -1049,11 +1072,13 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { _i5.Future saveSettingsUser( _i15.WeightUnits? defaultWeightUnit, bool? optIntoAnalyticsWarning, + bool? createLinkedJournalEntries, ) => (super.noSuchMethod( Invocation.method(#saveSettingsUser, [ defaultWeightUnit, optIntoAnalyticsWarning, + createLinkedJournalEntries, ]), returnValue: _i5.Future.value(0), ) @@ -1549,7 +1574,7 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { /// A class which mocks [GoRouter]. /// /// See the documentation for Mockito's code generation for more information. -class MockGoRouter extends _i1.Mock implements _i17.GoRouter { +class MockGoRouter extends _i1.Mock implements _i18.GoRouter { MockGoRouter() { _i1.throwOnMissingStub(this); } @@ -1699,7 +1724,7 @@ class MockGoRouter extends _i1.Mock implements _i17.GoRouter { ); @override - void restore(_i18.RouteMatchList? matchList) => super.noSuchMethod( + void restore(_i19.RouteMatchList? matchList) => super.noSuchMethod( Invocation.method(#restore, [matchList]), returnValueForMissingStub: null, ); diff --git a/test/app/species/views/widgets/species_dropdown_test.mocks.dart b/test/app/species/views/widgets/species_dropdown_test.mocks.dart index d3b6073..1ef55d3 100644 --- a/test/app/species/views/widgets/species_dropdown_test.mocks.dart +++ b/test/app/species/views/widgets/species_dropdown_test.mocks.dart @@ -9,12 +9,13 @@ import 'package:drift/drift.dart' as _i2; import 'package:drift/src/runtime/executor/stream_queries.dart' as _i4; import 'package:mockito/mockito.dart' as _i1; import 'package:mockito/src/dummies.dart' as _i6; +import 'package:petjournal/constants/linked_record_type.dart' as _i10; import 'package:petjournal/constants/pet_sex.dart' as _i7; import 'package:petjournal/constants/pet_status.dart' as _i8; import 'package:petjournal/constants/weight_units.dart' as _i9; import 'package:petjournal/data/database/database_service.dart' as _i3; import 'package:petjournal/data/database/tables/journal_entry_details.dart' - as _i10; + as _i11; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -809,12 +810,18 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { required String? entryText, required List? petIdList, required List? tags, + _i10.LinkedRecordType? linkedRecordType, + int? linkedRecordId, + String? linkedRecordTitle, }) => (super.noSuchMethod( Invocation.method(#createJournalEntryForPet, [], { #entryText: entryText, #petIdList: petIdList, #tags: tags, + #linkedRecordType: linkedRecordType, + #linkedRecordId: linkedRecordId, + #linkedRecordTitle: linkedRecordTitle, }), returnValue: _i5.Future<_i3.JournalEntry?>.value(), ) @@ -838,6 +845,22 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { ) as _i5.Future); + @override + _i5.Future updateLinkedJournalEntry({ + required int? linkedRecordId, + required _i10.LinkedRecordType? linkedRecordType, + required String? linkedRecordTitle, + }) => + (super.noSuchMethod( + Invocation.method(#updateLinkedJournalEntry, [], { + #linkedRecordId: linkedRecordId, + #linkedRecordType: linkedRecordType, + #linkedRecordTitle: linkedRecordTitle, + }), + returnValue: _i5.Future.value(0), + ) + as _i5.Future); + @override _i5.Future<_i3.JournalEntry?> getJournalEntry(int? id) => (super.noSuchMethod( @@ -863,14 +886,14 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { as _i5.Stream>); @override - _i5.Stream> getAllJournalEntryDetailsForPet( + _i5.Stream> getAllJournalEntryDetailsForPet( int? petId, ) => (super.noSuchMethod( Invocation.method(#getAllJournalEntryDetailsForPet, [petId]), - returnValue: _i5.Stream>.empty(), + returnValue: _i5.Stream>.empty(), ) - as _i5.Stream>); + as _i5.Stream>); @override _i5.Future deleteJournalEntry(int? id) => @@ -1005,11 +1028,13 @@ class MockDatabaseService extends _i1.Mock implements _i3.DatabaseService { _i5.Future saveSettingsUser( _i9.WeightUnits? defaultWeightUnit, bool? optIntoAnalyticsWarning, + bool? createLinkedJournalEntries, ) => (super.noSuchMethod( Invocation.method(#saveSettingsUser, [ defaultWeightUnit, optIntoAnalyticsWarning, + createLinkedJournalEntries, ]), returnValue: _i5.Future.value(0), ) diff --git a/test/data/database/database_service_journal_entry_details_test.dart b/test/data/database/database_service_journal_entry_details_test.dart index eb14045..37d6682 100644 --- a/test/data/database/database_service_journal_entry_details_test.dart +++ b/test/data/database/database_service_journal_entry_details_test.dart @@ -1,5 +1,6 @@ import 'package:drift/drift.dart'; import 'package:drift/native.dart'; +import 'package:petjournal/constants/linked_record_type.dart'; import 'package:petjournal/data/database/database_service.dart'; import 'package:petjournal/constants/pet_sex.dart'; import 'package:petjournal/constants/pet_status.dart'; @@ -40,7 +41,7 @@ void main() { null, null, null, - null + null, ); pet2 = await database.createPet( @@ -63,7 +64,7 @@ void main() { null, null, null, - null + null, ); }); @@ -188,7 +189,7 @@ void main() { entryText: 'Test Entry 1', tags: [], ))!; - final journalEntry2 = (await database.createJournalEntryForPet( + (await database.createJournalEntryForPet( petIdList: [pet2!.petId], entryText: 'Test Entry 2', tags: [], @@ -210,6 +211,42 @@ void main() { expect(records[0].journalEntry.entryText, equals('Test Entry 1')); }, ); + + test( + 'getAllJournalEntryDetailsForPet should return one header with linked details', + () async { + // ARRANGE + final journalEntry1 = (await database.createJournalEntryForPet( + petIdList: [pet1!.petId], + entryText: 'Test Entry 1', + tags: [], + linkedRecordId: pet1!.petId, + linkedRecordTitle: pet1!.name, + linkedRecordType: LinkedRecordType.pet, + ))!; + + // ACT + final records = await database + .getAllJournalEntryDetailsForPet(pet1!.petId) + .first; + + // ASSERT + expect(records, match.isNotNull); + expect(records.length, equals(1)); + expect(records[0].pets.length, equals(1)); + expect( + records[0].journalEntry.journalEntryId, + journalEntry1.journalEntryId, + ); + expect(records[0].journalEntry.entryText, equals('Test Entry 1')); + expect(records[0].journalEntry.linkedRecordId, equals(pet1!.petId)); + expect(records[0].journalEntry.linkedRecordTitle, equals(pet1!.name)); + expect( + records[0].journalEntry.linkedRecordType, + equals(LinkedRecordType.pet), + ); + }, + ); }); group('JournalEntryDetails Operations with Pets and Tags', () { diff --git a/test/data/database/database_service_journal_entry_test.dart b/test/data/database/database_service_journal_entry_test.dart index 43e7834..ba3e017 100644 --- a/test/data/database/database_service_journal_entry_test.dart +++ b/test/data/database/database_service_journal_entry_test.dart @@ -1,5 +1,6 @@ import 'package:drift/drift.dart'; import 'package:drift/native.dart'; +import 'package:petjournal/constants/linked_record_type.dart'; import 'package:petjournal/data/database/database_service.dart'; import 'package:petjournal/constants/pet_sex.dart'; import 'package:petjournal/constants/pet_status.dart'; @@ -56,12 +57,18 @@ void main() { petIdList: [pet!.petId], entryText: testText, tags: [], + linkedRecordId: pet.petId, + linkedRecordTitle: pet.name, + linkedRecordType: LinkedRecordType.pet, ); expect(journalEntry, match.isNotNull); expect(journalEntry?.entryText, equals(testText)); expect(journalEntry?.createdDateTime, equals(match.isNotNull)); expect(journalEntry?.lastUpdatedDateTime, equals(match.isNull)); + expect(journalEntry?.linkedRecordId, equals(pet.petId)); + expect(journalEntry?.linkedRecordTitle, equals(pet.name)); + expect(journalEntry?.linkedRecordType, equals(LinkedRecordType.pet)); // Verify entry appears in getAllJournalEntriesForPet final entries = await database @@ -217,6 +224,34 @@ void main() { expect(updated?.entryText, equals('Updated Text')); }); + test('updateJournalEntry should not overwrite linked record', () async { + final pet = await database.getPet(1); + + final entry = await database.createJournalEntryForPet( + petIdList: [pet!.petId], + entryText: 'Original Text', + tags: [], + linkedRecordId: pet.petId, + linkedRecordTitle: pet.name, + linkedRecordType: LinkedRecordType.pet, + ); + expect(entry, match.isNotNull); + + final updatedCount = await database.updateJournalEntry( + id: entry!.journalEntryId, + entryText: 'Updated Text', + petIdList: [pet.petId], + tags: [], + ); + + expect(updatedCount, equals(1)); + + final updated = await database.getJournalEntry(entry.journalEntryId); + expect(updated?.linkedRecordId, equals(pet.petId)); + expect(updated?.linkedRecordTitle, equals(pet.name)); + expect(updated?.linkedRecordType, equals(LinkedRecordType.pet)); + }); + test('updateJournalEntry should return 0 for non-existent entry', () async { final updatedCount = await database.updateJournalEntry( id: 999, @@ -369,4 +404,46 @@ void main() { }, ); }); + + group('JournalEntry Updating LinkedRecords', () { + test( + 'updateLinkedJournalEntry should update linked record details', + () async { + // Get the pet ID from the pet created in setUp + final pet = await database.getPet(1); + expect(pet, match.isNotNull); + + final journalEntry = await database.createJournalEntryForPet( + petIdList: [pet!.petId], + entryText: 'Test Entry', + tags: [], + linkedRecordId: 123, + linkedRecordType: LinkedRecordType.weight, + linkedRecordTitle: 'Linked Record Title', + ); + + final updatedCount = await database.updateLinkedJournalEntry( + linkedRecordId: 123, + linkedRecordType: LinkedRecordType.weight, + linkedRecordTitle: 'Updated Record Title', + ); + + final updatedJournalEntry = await database.getJournalEntry( + journalEntry!.journalEntryId, + ); + + expect(updatedCount, equals(1)); + expect(updatedJournalEntry, match.isNotNull); + expect( + updatedJournalEntry!.linkedRecordTitle, + equals('Updated Record Title'), + ); + expect( + updatedJournalEntry.linkedRecordType, + equals(LinkedRecordType.weight), + ); + expect(updatedJournalEntry.linkedRecordId, equals(123)); + }, + ); + }); } diff --git a/test/data/database/database_service_setting_test.dart b/test/data/database/database_service_setting_test.dart index 018c8ea..96a06ea 100644 --- a/test/data/database/database_service_setting_test.dart +++ b/test/data/database/database_service_setting_test.dart @@ -117,6 +117,7 @@ void main() { final updateCount = await database.saveSettingsUser( WeightUnits.metric, null, + null ); // Assert @@ -125,6 +126,7 @@ void main() { final settings = await database.getSettings(); expect(settings?.defaultWeightUnit, equals(WeightUnits.metric.dataValue)); expect(settings?.optIntoAnalyticsWarning, isFalse); + expect(settings?.createLinkedJournalEntries, isTrue); }); test('saveSettingsUser should update analytics opt in', () async { @@ -132,7 +134,7 @@ void main() { await database.createDefaultSettings(); // Act - final updateCount = await database.saveSettingsUser(null, true); + final updateCount = await database.saveSettingsUser(null, true, null); // Assert expect(updateCount, equals(1)); @@ -140,6 +142,23 @@ void main() { final settings = await database.getSettings(); expect(settings?.defaultWeightUnit, match.isNull); expect(settings?.optIntoAnalyticsWarning, isTrue); + expect(settings?.createLinkedJournalEntries, isTrue); + }); + + test('saveSettingsUser should update create journal entry', () async { + // Arrange + await database.createDefaultSettings(); + + // Act + final updateCount = await database.saveSettingsUser(null, null, false); + + // Assert + expect(updateCount, equals(1)); + + final settings = await database.getSettings(); + expect(settings?.defaultWeightUnit, match.isNull); + expect(settings?.optIntoAnalyticsWarning, match.isFalse); + expect(settings?.createLinkedJournalEntries, isFalse); }); test('saveSettingsUser default weight should allow null', () async { @@ -147,7 +166,7 @@ void main() { await database.createDefaultSettings(); // Act - var updateCount = await database.saveSettingsUser(null, null); + var updateCount = await database.saveSettingsUser(null, null, null); // Assert expect(updateCount, equals(1)); @@ -155,6 +174,7 @@ void main() { final settings = await database.getSettings(); expect(settings?.defaultWeightUnit, match.isNull); expect(settings?.optIntoAnalyticsWarning, match.isFalse); + expect(settings?.createLinkedJournalEntries, isTrue); }); test('resetSettingsUser should set settings to null', () async { @@ -162,7 +182,7 @@ void main() { await database.createDefaultSettings(); // Act - await database.saveSettingsUser(WeightUnits.metric, true); + await database.saveSettingsUser(WeightUnits.metric, true, false); var updateCount = await database.resetSettingsUser(); // Assert @@ -171,6 +191,7 @@ void main() { final settings = await database.getSettings(); expect(settings?.defaultWeightUnit, match.isNull); expect(settings?.optIntoAnalyticsWarning, match.isFalse); + expect(settings?.createLinkedJournalEntries, isTrue); }); test('createDefaultSettings should replace existing settings', () async { diff --git a/test/data/mapper/journal_mapper_test.dart b/test/data/mapper/journal_mapper_test.dart index c50a000..64d951a 100644 --- a/test/data/mapper/journal_mapper_test.dart +++ b/test/data/mapper/journal_mapper_test.dart @@ -1,5 +1,6 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:petjournal/app/pet/models/journal_model.dart'; +import 'package:petjournal/constants/linked_record_type.dart'; import 'package:petjournal/data/database/database_service.dart'; import 'package:petjournal/data/database/tables/journal_entry_details.dart'; import 'package:petjournal/data/mapper/journal_mapper.dart'; @@ -19,6 +20,9 @@ void main() { createdDateTime: DateTime(2025, 1, 1, 12, 30, 45), lastUpdatedDateTime: DateTime(2025, 1, 2, 21, 25, 59), entryText: 'New Entry', + linkedRecordId: 123, + linkedRecordType: LinkedRecordType.pet, + linkedRecordTitle: 'Linked Record Title', ), tags: [ JournalEntryTag( @@ -48,6 +52,54 @@ void main() { expect(result.entryText, 'New Entry'); expect(result.petIdList, [1, 2]); expect(result.tags, ['Tag1', 'Tag2']); + expect(result.linkedRecordId, 123); + expect(result.linkedRecordType, LinkedRecordType.pet); + expect(result.linkedRecordTitle, 'Linked Record Title'); + }, + ); + + test( + 'mapToModel should return correct JournalModel when journalEntryDetails is valid and optional params missing', + () { + // Arrange + final jed = JournalEntryDetails( + journalEntry: JournalEntry( + journalEntryId: 1, + createdDateTime: DateTime(2025, 1, 1, 12, 30, 45), + lastUpdatedDateTime: DateTime(2025, 1, 2, 21, 25, 59), + entryText: 'New Entry', + ), + tags: [ + JournalEntryTag( + journalEntryTagId: 1, + journalEntryId: 1, + tag: 'Tag1', + ), + JournalEntryTag( + journalEntryTagId: 2, + journalEntryId: 1, + tag: 'Tag2', + ), + ], + pets: [ + PetJournalEntry(journalEntryId: 1, petId: 1), + PetJournalEntry(journalEntryId: 1, petId: 2), + ], + ); + + // Act + final result = JournalMapper.mapToModel(jed); + + // Assert + expect(result.journalEntryId, 1); + expect(result.createdDateTime, DateTime(2025, 1, 1, 12, 30, 45)); + expect(result.lastUpdatedDateTime, DateTime(2025, 1, 2, 21, 25, 59)); + expect(result.entryText, 'New Entry'); + expect(result.petIdList, [1, 2]); + expect(result.tags, ['Tag1', 'Tag2']); + expect(result.linkedRecordId, null); + expect(result.linkedRecordType, null); + expect(result.linkedRecordTitle, null); }, ); @@ -183,6 +235,9 @@ void main() { journalEntryId: 1, createdDateTime: DateTime(2025, 1, 1), entryText: 'New Entry 1', + linkedRecordId: 123, + linkedRecordType: LinkedRecordType.vaccination, + linkedRecordTitle: 'Linked Record Title', ), tags: [ JournalEntryTag( @@ -227,6 +282,12 @@ void main() { expect(result[1].entryText, 'New Entry 2'); expect(result[0].tags, ['Tag1', 'Tag2']); expect(result[1].tags, ['Tag3']); + expect(result[0].linkedRecordId, 123); + expect(result[0].linkedRecordType, LinkedRecordType.vaccination); + expect(result[0].linkedRecordTitle, 'Linked Record Title'); + expect(result[1].linkedRecordId, null); + expect(result[1].linkedRecordType, null); + expect(result[1].linkedRecordTitle, null); }, ); }); diff --git a/test/data/mapper/settings_mapper_test.dart b/test/data/mapper/settings_mapper_test.dart index 6379c71..76a5e15 100644 --- a/test/data/mapper/settings_mapper_test.dart +++ b/test/data/mapper/settings_mapper_test.dart @@ -19,29 +19,35 @@ void main() { expect(model.onBoardingComplete, equals(false)); expect(model.lastUsedVersion, match.isNull); expect(model.defaultWeightUnit, match.isNull); + expect(model.createLinkedJournalEntries, equals(true)); }); - test('mapToModel should correctly map all Setting fields to SettingsModel', () { - // Arrange - final settings = Setting( - settingsId: 1, - acceptedTermsAndConditions: true, - optIntoAnalyticsWarning: true, - onBoardingComplete: true, - lastUsedVersion: '1.0.0', - defaultWeightUnit: WeightUnits.metric.dataValue, - ); + test( + 'mapToModel should correctly map all Setting fields to SettingsModel', + () { + // Arrange + final settings = Setting( + settingsId: 1, + acceptedTermsAndConditions: true, + optIntoAnalyticsWarning: true, + onBoardingComplete: true, + lastUsedVersion: '1.0.0', + defaultWeightUnit: WeightUnits.metric.dataValue, + createLinkedJournalEntries: true, + ); - // Act - final model = SettingsMapper.mapToModel(settings); + // Act + final model = SettingsMapper.mapToModel(settings); - // Assert - expect(model.acceptedTermsAndConditions, equals(true)); - expect(model.optIntoAnalyticsWarning, equals(true)); - expect(model.onBoardingComplete, equals(true)); - expect(model.lastUsedVersion, equals('1.0.0')); - expect(model.defaultWeightUnit, equals(WeightUnits.metric)); - }); + // Assert + expect(model.acceptedTermsAndConditions, equals(true)); + expect(model.optIntoAnalyticsWarning, equals(true)); + expect(model.onBoardingComplete, equals(true)); + expect(model.lastUsedVersion, equals('1.0.0')); + expect(model.defaultWeightUnit, equals(WeightUnits.metric)); + expect(model.createLinkedJournalEntries, equals(true)); + }, + ); test('mapToModel should handle null weight unit', () { // Arrange @@ -52,6 +58,7 @@ void main() { onBoardingComplete: true, lastUsedVersion: '1.0.0', defaultWeightUnit: null, + createLinkedJournalEntries: true, ); // Act @@ -71,6 +78,7 @@ void main() { onBoardingComplete: false, lastUsedVersion: null, defaultWeightUnit: weightUnit.dataValue, + createLinkedJournalEntries: true, ); final model = SettingsMapper.mapToModel(settings); @@ -87,6 +95,7 @@ void main() { onBoardingComplete: false, lastUsedVersion: '', defaultWeightUnit: null, + createLinkedJournalEntries: true, ); // Act