import 'dart:math'; import 'dart:typed_data'; import 'package:supabase_flutter/supabase_flutter.dart'; import '../../domain/models/feed_post_model.dart'; class FeedService { const FeedService(this._client); final SupabaseClient _client; User get _currentUser { final user = _client.auth.currentUser; if (user == null) { throw StateError('No authenticated user'); } return user; } Future> fetchFeed() async { final user = _currentUser; final posts = await _client .from('posts') .select('id,user_id,caption,image_url,likes_count,created_at') .order('created_at', ascending: false); if (posts.isEmpty) return const []; final postRows = List>.from(posts); final userIds = postRows .map((e) => e['user_id'] as String?) .whereType() .toSet() .toList(); final profiles = userIds.isEmpty ? >[] : List>.from(await _client .from('profiles') .select('user_id,username,avatar_url') .inFilter('user_id', userIds)); final profileByUserId = { for (final profile in profiles) (profile['user_id'] as String): profile, }; final postIds = postRows.map((e) => e['id'] as String).toList(); final myLikes = postIds.isEmpty ? >[] : List>.from(await _client .from('post_likes') .select('post_id') .eq('user_id', user.id) .inFilter('post_id', postIds)); final likedPostIds = myLikes.map((e) => e['post_id'] as String).toSet(); return postRows.map((row) { final profile = profileByUserId[row['user_id']] ?? const {}; return FeedPostModel( id: row['id'] as String, userId: row['user_id'] as String, caption: (row['caption'] as String?) ?? '', imageUrl: (row['image_url'] as String?) ?? '', createdAt: DateTime.parse(row['created_at'] as String), likesCount: (row['likes_count'] as int?) ?? 0, isLiked: likedPostIds.contains(row['id']), username: (profile['username'] as String?) ?? 'riot_user', avatarUrl: (profile['avatar_url'] as String?) ?? '', ); }).toList(); } Future createPost({ required String caption, required Uint8List imageBytes, required String extension, }) async { final user = _currentUser; final ts = DateTime.now().millisecondsSinceEpoch; final random = Random().nextInt(99999); final path = 'posts/${user.id}/$ts-$random.$extension'; await _client.storage.from('post-images').uploadBinary( path, imageBytes, fileOptions: const FileOptions(upsert: false), ); final imageUrl = _client.storage.from('post-images').getPublicUrl(path); await _client.from('posts').insert({ 'user_id': user.id, 'caption': caption.trim(), 'image_url': imageUrl, 'likes_count': 0, }); } Future toggleLike({ required String postId, required bool currentlyLiked, required int currentLikesCount, }) async { final user = _currentUser; if (currentlyLiked) { await _client .from('post_likes') .delete() .eq('post_id', postId) .eq('user_id', user.id); await _client.from('posts').update({ 'likes_count': max(0, currentLikesCount - 1), }).eq('id', postId); return; } await _client.from('post_likes').upsert({ 'post_id': postId, 'user_id': user.id, }); await _client.from('posts').update({ 'likes_count': currentLikesCount + 1, }).eq('id', postId); } Future deleteOwnPost(String postId) async { final user = _currentUser; await _client .from('posts') .delete() .eq('id', postId) .eq('user_id', user.id); } }