wesal/frontend/lib/services/post_service.dart

411 lines
12 KiB
Dart

import 'dart:convert';
import '../models/post_models.dart';
import '../models/comment_models.dart';
import 'http_service.dart';
import 'auth_service.dart';
import 'cache_service.dart';
class PostService {
static bool _pollingStarted = false;
/// Get all posts with caching - returns cached data if available and fresh
static Future<Map<String, dynamic>> getAllPosts({
bool forceRefresh = false,
}) async {
// Return cached data if available and not forcing refresh
if (!forceRefresh) {
final cached = CacheService.getCached<List<Post>>(
CacheService.postsAllKey,
);
if (cached != null &&
!CacheService.isCacheExpired(
CacheService.postsAllKey,
CacheService.postsCacheDuration,
)) {
return {
'success': true,
'message': 'Loaded from cache',
'posts': cached,
};
}
// Return stale data while fetching fresh data in background
final stale = CacheService.getCachedStale<List<Post>>(
CacheService.postsAllKey,
);
if (stale != null && !forceRefresh) {
// Trigger background refresh
_fetchAndCachePosts();
return {
'success': true,
'message': 'Loaded from cache (refreshing in background)',
'posts': stale,
};
}
}
// Fetch fresh data
return await _fetchAndCachePosts();
}
static Future<Map<String, dynamic>> _fetchAndCachePosts([bool fromPolling = false]) async {
try {
final response = await HttpService.get('/posts/all');
if (response.statusCode == 200) {
final responseData = jsonDecode(response.body);
final posts =
(responseData['data'] as List?)
?.map((post) => Post.fromJson(post))
.toList() ??
[];
// Only cache when not called from polling to prevent conflicts
if (!fromPolling) {
CacheService.setCached(CacheService.postsAllKey, posts);
}
return {
'success': responseData['status'] ?? false,
'message': responseData['message'] ?? '',
'posts': posts,
};
} else if (response.statusCode == 401 || response.statusCode == 403) {
await AuthService.handleAuthenticationError();
return {
'success': false,
'message': 'Session expired. Please login again.',
'posts': <Post>[],
};
} else {
try {
final responseData = jsonDecode(response.body);
return {
'success': false,
'message': responseData['message'] ?? 'Failed to fetch posts',
'posts': <Post>[],
};
} catch (e) {
return {
'success': false,
'message': 'Server error (${response.statusCode})',
'posts': <Post>[],
};
}
}
} catch (e) {
print('Error fetching posts: $e');
return {
'success': false,
'message': 'Network error: $e',
'posts': <Post>[],
};
}
}
/// Start background polling for posts
static void startPolling() {
_pollingStarted = true;
CacheService.startPolling(
CacheService.postsAllKey,
() => _fetchAndCachePosts(true), // Mark as from polling
CacheService.postsPollingInterval,
CacheService.postsCacheDuration,
);
}
/// Stop background polling for posts
static void stopPolling() {
CacheService.stopPolling(CacheService.postsAllKey);
_pollingStarted = false;
}
/// Get posts stream for real-time updates
static Stream<List<Post>> getPostsStream() {
return CacheService.getStream<List<Post>>(CacheService.postsAllKey);
}
/// Force notify stream with current cached data (useful for initial stream setup)
static void notifyStreamWithCurrentData() {
final cached = CacheService.getCachedStale<List<Post>>(
CacheService.postsAllKey,
);
if (cached != null) {
// Re-notify the stream with current data
CacheService.notifyStreamListeners<List<Post>>(
CacheService.postsAllKey,
cached,
);
}
}
/// Check if polling is active
static bool get isPollingActive => _pollingStarted;
/// Invalidate posts cache (useful after creating/updating posts)
static void invalidateCache() {
CacheService.clearCache(CacheService.postsAllKey);
}
static Future<Map<String, dynamic>> getUserPosts() async {
try {
final response = await HttpService.get('/posts/user');
final responseData = jsonDecode(response.body);
if (response.statusCode == 200) {
return {
'success': responseData['status'] ?? false,
'message': responseData['message'] ?? '',
'posts':
(responseData['data'] as List?)
?.map((post) => Post.fromJson(post))
.toList() ??
[],
};
} else {
return {
'success': false,
'message': responseData['message'] ?? 'Failed to fetch user posts',
'posts': <Post>[],
};
}
} catch (e) {
print('Error fetching user posts: $e');
return {
'success': false,
'message': 'Network error: $e',
'posts': <Post>[],
};
}
}
static Future<Map<String, dynamic>> createPost(String body) async {
try {
final createPostRequest = CreatePostRequest(body: body);
final response = await HttpService.post(
'/posts/create',
createPostRequest.toJson(),
);
final responseData = jsonDecode(response.body);
if (response.statusCode == 200 || response.statusCode == 201) {
// Invalidate posts cache since we added a new post
invalidateCache();
return {
'success': responseData['status'] ?? false,
'message': responseData['message'] ?? '',
'post': responseData['data'] != null
? Post.fromJson(responseData['data'])
: null,
};
} else {
return {
'success': false,
'message': responseData['message'] ?? 'Failed to create post',
'post': null,
};
}
} catch (e) {
print('Error creating post: $e');
return {'success': false, 'message': 'Network error: $e', 'post': null};
}
}
static Future<Map<String, dynamic>> likePost(String postId) async {
try {
final response = await HttpService.post('/posts/like', {
'postId': postId,
});
final responseData = jsonDecode(response.body);
if (response.statusCode == 200) {
return {
'success': responseData['status'] ?? false,
'message': responseData['message'] ?? '',
'post': responseData['data'] != null
? Post.fromJson(responseData['data'])
: null,
};
} else {
return {
'success': false,
'message': responseData['message'] ?? 'Failed to like post',
'post': null,
};
}
} catch (e) {
print('Error liking post: $e');
return {'success': false, 'message': 'Network error: $e', 'post': null};
}
}
static Future<Map<String, dynamic>> unlikePost(String postId) async {
try {
final response = await HttpService.post('/posts/unlike', {
'postId': postId,
});
final responseData = jsonDecode(response.body);
if (response.statusCode == 200) {
return {
'success': responseData['status'] ?? false,
'message': responseData['message'] ?? '',
'post': responseData['data'] != null
? Post.fromJson(responseData['data'])
: null,
};
} else {
return {
'success': false,
'message': responseData['message'] ?? 'Failed to unlike post',
'post': null,
};
}
} catch (e) {
print('Error unliking post: $e');
return {'success': false, 'message': 'Network error: $e', 'post': null};
}
}
static Future<Map<String, dynamic>> getPost(String postId) async {
try {
final response = await HttpService.get('/posts/get?id=$postId');
final responseData = jsonDecode(response.body);
if (response.statusCode == 200) {
return {
'success': responseData['status'] ?? false,
'message': responseData['message'] ?? '',
'post': responseData['data'] != null
? DetailedPost.fromJson(responseData['data'])
: null,
};
} else {
return {
'success': false,
'message': responseData['message'] ?? 'Failed to fetch post',
'post': null,
};
}
} catch (e) {
print('Error fetching post: $e');
return {'success': false, 'message': 'Network error: $e', 'post': null};
}
}
static Future<Map<String, dynamic>> getComments(String postId) async {
try {
final response = await HttpService.get(
'/posts/comments/list?postId=$postId',
);
if (response.statusCode == 200) {
final responseData = jsonDecode(response.body);
final commentsResponse = CommentsResponse.fromJson(responseData);
if (commentsResponse.status) {
return {'success': true, 'comments': commentsResponse.data};
} else {
return {
'success': false,
'message': commentsResponse.message ?? 'Failed to fetch comments',
'comments': <Comment>[],
};
}
} else if (response.statusCode == 401 || response.statusCode == 403) {
await AuthService.handleAuthenticationError();
return {
'success': false,
'message': 'Session expired. Please login again.',
'comments': <Comment>[],
};
} else {
try {
final responseData = jsonDecode(response.body);
return {
'success': false,
'message': responseData['message'] ?? 'Failed to fetch comments',
'comments': <Comment>[],
};
} catch (e) {
return {
'success': false,
'message': 'Server error (${response.statusCode})',
'comments': <Comment>[],
};
}
}
} catch (e) {
print('Error fetching comments: $e');
return {
'success': false,
'message': 'Network error: $e',
'comments': <Comment>[],
};
}
}
static Future<Map<String, dynamic>> createComment({
required String postId,
required String body,
String? replyComment,
}) async {
try {
final requestBody = {'postId': int.parse(postId), 'body': body};
if (replyComment != null) {
requestBody['replyComment'] = int.parse(replyComment);
}
final response = await HttpService.post(
'/posts/comments/create',
requestBody,
);
if (response.statusCode == 200 || response.statusCode == 201) {
final responseData = jsonDecode(response.body);
if (responseData['status'] == true) {
return {
'success': true,
'comment': Comment.fromJson(responseData['data']),
};
} else {
return {
'success': false,
'message': responseData['message'] ?? 'Failed to create comment',
};
}
} else if (response.statusCode == 401 || response.statusCode == 403) {
await AuthService.handleAuthenticationError();
return {
'success': false,
'message': 'Session expired. Please login again.',
};
} else {
try {
final responseData = jsonDecode(response.body);
return {
'success': false,
'message': responseData['message'] ?? 'Failed to create comment',
};
} catch (e) {
return {
'success': false,
'message': 'Server error (${response.statusCode})',
};
}
}
} catch (e) {
print('Error creating comment: $e');
return {'success': false, 'message': 'Network error: $e'};
}
}
}