first commit

This commit is contained in:
Lucas Saburido
2026-05-13 16:26:45 +01:00
commit cabf2025cd
252 changed files with 13524 additions and 0 deletions

View File

@@ -0,0 +1,137 @@
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<List<FeedPostModel>> 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<Map<String, dynamic>>.from(posts);
final userIds = postRows
.map((e) => e['user_id'] as String?)
.whereType<String>()
.toSet()
.toList();
final profiles = userIds.isEmpty
? <Map<String, dynamic>>[]
: List<Map<String, dynamic>>.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
? <Map<String, dynamic>>[]
: List<Map<String, dynamic>>.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<void> 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<void> 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<void> deleteOwnPost(String postId) async {
final user = _currentUser;
await _client
.from('posts')
.delete()
.eq('id', postId)
.eq('user_id', user.id);
}
}