-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathfeed_controller.dart
More file actions
executable file
·144 lines (132 loc) · 4.6 KB
/
Copy pathfeed_controller.dart
File metadata and controls
executable file
·144 lines (132 loc) · 4.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../models/post_model.dart';
import '../models/pet_model.dart';
import '../repositories/feed_repository.dart';
// ---------------------------------------------------------------------------
// State wrapper
// ---------------------------------------------------------------------------
class FeedState {
final List<PostModel> posts;
final bool isLoading;
final String? error;
FeedState({
this.posts = const [],
this.isLoading = false,
this.error,
});
FeedState copyWith({
List<PostModel>? posts,
bool? isLoading,
String? error,
bool clearError = false,
}) {
return FeedState(
posts: posts ?? this.posts,
isLoading: isLoading ?? this.isLoading,
error: clearError ? null : (error ?? this.error),
);
}
}
// ---------------------------------------------------------------------------
// Notifier
// ---------------------------------------------------------------------------
class FeedNotifier extends Notifier<FeedState> {
FeedRepository get _repository => ref.read(feedRepositoryProvider);
@override
FeedState build() {
_fetchPosts();
return FeedState(isLoading: true);
}
Future<void> _fetchPosts() async {
try {
final posts = await _repository.fetchPosts();
state = state.copyWith(posts: posts, isLoading: false, clearError: true);
} catch (e) {
state = state.copyWith(isLoading: false, error: e.toString());
}
}
Future<void> refresh() => _fetchPosts();
// -------------------------------------------------------------------------
// Toggle Like (optimistic update then sync with real IDs from server)
// -------------------------------------------------------------------------
Future<void> toggleLike(String postId, String currentPetId) async {
// Optimistic update
state = state.copyWith(
posts: state.posts.map((post) {
if (post.id != postId) return post;
final newLikes = List<String>.from(post.likedByPetIds);
if (newLikes.contains(currentPetId)) {
newLikes.remove(currentPetId);
} else {
newLikes.add(currentPetId);
}
return post.copyWith(likedByPetIds: newLikes);
}).toList(),
);
try {
final updatedLikes =
await _repository.toggleLike(postId, currentPetId);
state = state.copyWith(
posts: state.posts.map((post) {
if (post.id != postId) return post;
return post.copyWith(likedByPetIds: updatedLikes);
}).toList(),
);
} catch (_) {
// Revert optimistic update on failure
state = state.copyWith(
posts: state.posts.map((post) {
if (post.id != postId) return post;
final revertedLikes = List<String>.from(post.likedByPetIds);
if (revertedLikes.contains(currentPetId)) {
revertedLikes.remove(currentPetId);
} else {
revertedLikes.add(currentPetId);
}
return post.copyWith(likedByPetIds: revertedLikes);
}).toList(),
);
}
}
// -------------------------------------------------------------------------
// Add Post (media URL already uploaded by caller)
// -------------------------------------------------------------------------
Future<void> addPost(PetModel pet, String mediaUrl, String caption) async {
try {
final newPost = await _repository.createPost(
petId: pet.id,
mediaUrl: mediaUrl,
caption: caption,
);
state = state.copyWith(posts: [newPost, ...state.posts]);
} catch (e) {
state = state.copyWith(error: 'Failed to create post: $e');
}
}
// -------------------------------------------------------------------------
// Add Comment
// -------------------------------------------------------------------------
Future<void> addComment(
String postId, String petId, String petName, String text) async {
try {
final newComment = await _repository.addComment(
postId: postId,
petId: petId,
text: text,
);
state = state.copyWith(
posts: state.posts.map((post) {
if (post.id != postId) return post;
return post.copyWith(
comments: [...post.comments, newComment]);
}).toList(),
);
} catch (e) {
state = state.copyWith(error: 'Failed to add comment: $e');
}
}
}
// ---------------------------------------------------------------------------
// Provider
// ---------------------------------------------------------------------------
final feedProvider = NotifierProvider<FeedNotifier, FeedState>(FeedNotifier.new);