@@ -8,6 +8,22 @@ import 'auth_controller.dart';
88import 'chat_controller.dart' ;
99import 'pet_controller.dart' ;
1010
11+ // ---------------------------------------------------------------------------
12+ // Discovery tab: which of the user's pets is browsing (null = active pet)
13+ // ---------------------------------------------------------------------------
14+
15+ class DiscoveryPetIdNotifier extends Notifier <String ?> {
16+ @override
17+ String ? build () => null ;
18+
19+ void select (String ? petId) => state = petId;
20+ }
21+
22+ final discoveryActivePetIdProvider =
23+ NotifierProvider <DiscoveryPetIdNotifier , String ?>(
24+ DiscoveryPetIdNotifier .new ,
25+ );
26+
1127// ---------------------------------------------------------------------------
1228// State
1329// ---------------------------------------------------------------------------
@@ -74,23 +90,53 @@ class MatchController extends Notifier<MatchState> {
7490 @override
7591 MatchState build () {
7692 final activePet = ref.watch (activePetProvider);
77- if (activePet == null ) {
93+ final myPets = ref.watch (petProvider.select ((s) => s.myPets));
94+
95+ if (myPets.isEmpty) {
7896 _lastLoadedPetId = null ;
97+ Future .microtask (
98+ () => ref.read (discoveryActivePetIdProvider.notifier).select (null ),
99+ );
79100 return MatchState ();
80101 }
81102
82- // Do not return a fresh [MatchState(isLoading: true)] on every rebuild —
83- // that wipes discovery data whenever [activePetProvider] notifies with the
84- // same pet id (new model instance). Only reset loading when the pet id changes.
85- if (_lastLoadedPetId != activePet.id) {
86- _lastLoadedPetId = activePet.id;
87- Future .microtask (() => _load (activePet.id));
88- return MatchState (isLoading: true );
103+ final browsingId = ref.watch (discoveryActivePetIdProvider);
104+ String ? targetId = browsingId ?? activePet? .id;
105+ if (targetId != null && ! myPets.any ((p) => p.id == targetId)) {
106+ targetId = activePet? .id ?? myPets.first.id;
107+ Future .microtask (
108+ () => ref.read (discoveryActivePetIdProvider.notifier).select (targetId),
109+ );
110+ }
111+ targetId ?? = activePet? .id ?? myPets.first.id;
112+
113+ if (_lastLoadedPetId != targetId) {
114+ final hadPriorLoad = _lastLoadedPetId != null ;
115+ _lastLoadedPetId = targetId;
116+ Future .microtask (() => _load (targetId! ));
117+ if (! hadPriorLoad) {
118+ return MatchState (isLoading: true );
119+ }
120+ return MatchState (
121+ isLoading: true ,
122+ filterAnimal: state.filterAnimal,
123+ filterBreed: state.filterBreed,
124+ searchQuery: state.searchQuery,
125+ discoveryPets: const [],
126+ allDiscoveryPets: const [],
127+ myRequests: state.myRequests,
128+ sentRequests: state.sentRequests,
129+ );
89130 }
90131
91132 return state;
92133 }
93134
135+ String ? _resolveDiscoveryTargetPetId () {
136+ return ref.read (discoveryActivePetIdProvider) ??
137+ ref.read (activePetProvider)? .id;
138+ }
139+
94140 // When [silent] is true the loading flag is NOT set, so the UI avoids a
95141 // full-screen spinner. Use this for background refreshes (e.g. after a like).
96142 Future <void > _load (String myPetId, {bool silent = false }) async {
@@ -105,13 +151,27 @@ class MatchController extends Notifier<MatchState> {
105151
106152 _currentDiscoveryPetId = myPetId;
107153
154+ final myPets = ref.read (petProvider).myPets;
155+ PetModel ? viewerPet;
156+ for (final p in myPets) {
157+ if (p.id == myPetId) {
158+ viewerPet = p;
159+ break ;
160+ }
161+ }
162+ final viewerAnimalType = viewerPet? .animalType.trim ();
163+
108164 try {
109165 final futures = await Future .wait ([
110166 matchRepository.fetchDiscoveryPets (
111167 myPetId: myPetId,
112168 userId: userId,
113- filterAnimal : state.filterAnimal ,
169+ allMyPetIds : myPets. map ((p) => p.id). toList () ,
114170 filterBreed: state.filterBreed,
171+ viewerAnimalType:
172+ (viewerAnimalType != null && viewerAnimalType.isNotEmpty)
173+ ? viewerAnimalType
174+ : null ,
115175 ),
116176 matchRepository.fetchMyRequests (myPetId),
117177 matchRepository.fetchSentRequests (myPetId),
@@ -136,9 +196,9 @@ class MatchController extends Notifier<MatchState> {
136196 }
137197
138198 Future <void > refresh () async {
139- final activePet = ref. read (activePetProvider );
140- if (activePet != null ) {
141- await _load (activePet.id );
199+ final target = _resolveDiscoveryTargetPetId ( );
200+ if (target != null ) {
201+ await _load (target );
142202 }
143203 }
144204
@@ -154,12 +214,12 @@ class MatchController extends Notifier<MatchState> {
154214 String ? _currentDiscoveryPetId;
155215
156216 void setFilterBreed (String ? breed) {
157- final activePet = ref. read (activePetProvider );
217+ final target = _resolveDiscoveryTargetPetId ( );
158218 debugPrint (
159- '[MatchController] setFilterBreed($breed ) — activePet=${ activePet ?. name } ' );
160- if (activePet == null ) {
219+ '[MatchController] setFilterBreed($breed ) — targetPetId=$ target ' );
220+ if (target == null ) {
161221 debugPrint (
162- '[MatchController] ⚠️ activePet is NULL — filter change ignored!' );
222+ '[MatchController] ⚠️ No discovery target pet — filter change ignored!' );
163223 return ;
164224 }
165225 if (breed == null || breed.isEmpty) {
@@ -169,16 +229,16 @@ class MatchController extends Notifier<MatchState> {
169229 }
170230 debugPrint (
171231 '[MatchController] State updated: animal=${state .filterAnimal }, breed=${state .filterBreed }' );
172- _load (activePet.id );
232+ _load (target );
173233 }
174234
175235 void setFilterAnimal (String ? animal) {
176- final activePet = ref. read (activePetProvider );
236+ final target = _resolveDiscoveryTargetPetId ( );
177237 debugPrint (
178- '[MatchController] setFilterAnimal($animal ) — activePet=${ activePet ?. name } ' );
179- if (activePet == null ) {
238+ '[MatchController] setFilterAnimal($animal ) — targetPetId=$ target ' );
239+ if (target == null ) {
180240 debugPrint (
181- '[MatchController] ⚠️ activePet is NULL — filter change ignored!' );
241+ '[MatchController] ⚠️ No discovery target pet — filter change ignored!' );
182242 return ;
183243 }
184244 if (animal == null || animal.isEmpty) {
@@ -188,7 +248,7 @@ class MatchController extends Notifier<MatchState> {
188248 }
189249 debugPrint (
190250 '[MatchController] State updated: animal=${state .filterAnimal }, breed=${state .filterBreed }' );
191- _load (activePet.id );
251+ _load (target );
192252 }
193253
194254 void setSearchQuery (String query) {
@@ -250,7 +310,7 @@ class MatchController extends Notifier<MatchState> {
250310 }
251311
252312 try {
253- await matchRepository.sendLikeRequest (
313+ final matchRequestId = await matchRepository.sendLikeRequest (
254314 senderPetId: senderPet.id,
255315 receiverPetId: receiverPetId,
256316 );
@@ -274,7 +334,7 @@ class MatchController extends Notifier<MatchState> {
274334 '${senderPet .name } is interested in breeding with ${receiverPet .name }.' ,
275335 type: 'match_request' ,
276336 entityType: 'match_request' ,
277- entityId: senderPet.id ,
337+ entityId: matchRequestId ,
278338 actorPetId: senderPet.id,
279339 );
280340 }
@@ -283,6 +343,15 @@ class MatchController extends Notifier<MatchState> {
283343 // does NOT show a loading spinner, so the UI transition is seamless.
284344 _load (senderPet.id, silent: true );
285345 return true ;
346+ } on StateError catch (e) {
347+ if (e.message == 'duplicate_match_request' ) {
348+ state = state.copyWith (
349+ error: 'You have already sent a request for this pet.' ,
350+ );
351+ } else {
352+ state = state.copyWith (error: e.toString ());
353+ }
354+ return false ;
286355 } catch (e) {
287356 state = state.copyWith (error: 'Could not send request: $e ' );
288357 return false ;
0 commit comments