import 'package:flutter/foundation.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; import 'package:uuid/uuid.dart'; import '../models/programmer_task_comment.model.dart'; import 'supabase_provider.dart'; /// Bucket reused for programmer-task comment attachments (public read; /// authenticated write; owner/privileged delete — already hardened by /// 20260926120000_task_attachment_write_hardening.sql). const _attachmentBucket = 'task_attachments'; /// A file selected for upload with a comment. typedef CommentUpload = ({String name, Uint8List bytes}); /// Comments for a task (newest first), streamed live. final programmerTaskCommentsProvider = StreamProvider.family, String>((ref, taskId) { final client = ref.watch(supabaseClientProvider); return client .from('programmer_task_comments') .stream(primaryKey: ['id']) .eq('task_id', taskId) .order('created_at', ascending: false) .map((rows) => rows.map(ProgrammerTaskComment.fromMap).toList()); }); final programmerTaskCommentsControllerProvider = Provider((ref) { return ProgrammerTaskCommentsController(ref.watch(supabaseClientProvider)); }); class ProgrammerTaskCommentsController { ProgrammerTaskCommentsController(this._client); final dynamic _client; /// Public URL for a stored attachment path. String attachmentUrl(String path) => _client.storage.from(_attachmentBucket).getPublicUrl(path); /// Adds a comment, uploading any [files] to the attachment bucket first. /// Requires either a non-empty [body] or at least one file. Future addComment({ required String taskId, required String body, List files = const [], }) async { final userId = _client.auth.currentUser?.id; if (userId == null) throw Exception('Not authenticated'); final attachments = >[]; for (final file in files) { final path = 'programmer_tasks/$taskId/comments/${const Uuid().v4()}_${file.name}'; await _client.storage.from(_attachmentBucket).uploadBinary( path, file.bytes, fileOptions: const FileOptions(upsert: true), ); attachments.add({'path': path, 'name': file.name}); } await _client.from('programmer_task_comments').insert({ 'task_id': taskId, 'author_id': userId, 'body': body, 'attachments': attachments, }); } }