import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import '../bloc/captura/captura_cubit.dart'; import '../bloc/captura/captura_state.dart'; import '../bloc/etiquetas/etiquetas_cubit.dart'; import '../bloc/app/app_cubit.dart'; import '../bloc/app/app_state.dart'; import '../widgets/audio_recorder.dart'; import '../../core/utils/hash_utils.dart'; class CapturaPage extends StatelessWidget { const CapturaPage({super.key}); @override Widget build(BuildContext context) { return BlocBuilder( builder: (context, state) { return Scaffold( body: SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Hash section Card( child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ const Icon(Icons.tag, size: 20), const SizedBox(width: 8), const Text('Hash', style: TextStyle(fontWeight: FontWeight.bold)), const Spacer(), IconButton( icon: const Icon(Icons.copy, size: 20), onPressed: () { Clipboard.setData(ClipboardData(text: state.contenedor.hash)); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Hash copiado')), ); }, ), IconButton( icon: const Icon(Icons.refresh, size: 20), onPressed: () => context.read().regenerateHash(), ), ], ), Text( HashUtils.truncateHash(state.contenedor.hash, length: 32), style: Theme.of(context).textTheme.bodySmall?.copyWith( fontFamily: 'monospace', ), ), ], ), ), ), const SizedBox(height: 16), // Title & Description TextField( decoration: const InputDecoration( labelText: 'Título (opcional)', prefixIcon: Icon(Icons.title), ), onChanged: (v) => context.read().setTitulo(v), ), const SizedBox(height: 12), TextField( decoration: const InputDecoration( labelText: 'Descripción (opcional)', prefixIcon: Icon(Icons.description), ), maxLines: 2, onChanged: (v) => context.read().setDescripcion(v), ), const SizedBox(height: 16), // Capture buttons Card( child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('Capturar', style: TextStyle(fontWeight: FontWeight.bold)), const SizedBox(height: 12), Wrap( spacing: 12, runSpacing: 12, children: [ _CaptureButton( icon: Icons.camera_alt, label: 'Foto', onPressed: () => context.read().capturePhoto(), ), AudioRecorderButton( onRecorded: (bytes, name) { context.read().addAudioFile(bytes, name); }, ), _CaptureButton( icon: Icons.videocam, label: 'Video', onPressed: () => context.read().captureVideo(), ), _CaptureButton( icon: Icons.attach_file, label: 'Archivo', onPressed: () => context.read().pickFile(), ), _CaptureButton( icon: state.contenedor.gps != null ? Icons.location_on : Icons.location_off, label: 'GPS', onPressed: () { if (state.contenedor.gps != null) { context.read().clearGps(); } else { context.read().captureGps(); } }, selected: state.contenedor.gps != null, ), ], ), ], ), ), ), const SizedBox(height: 16), // Attached files if (state.contenedor.archivos.isNotEmpty) ...[ Card( child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Archivos (${state.contenedor.archivos.length})', style: const TextStyle(fontWeight: FontWeight.bold), ), const SizedBox(height: 8), ...state.contenedor.archivos.asMap().entries.map((e) { final archivo = e.value; return ListTile( leading: Icon(_getFileIcon(archivo.tipo)), title: Text(archivo.nombre), subtitle: Text(archivo.sizeFormatted), trailing: IconButton( icon: const Icon(Icons.close), onPressed: () => context.read().removeArchivo(e.key), ), ); }), ], ), ), ), const SizedBox(height: 16), ], // Etiquetas summary BlocBuilder( builder: (context, etState) { final seleccionadas = (etState as dynamic).seleccionadas as List; if (seleccionadas.isEmpty) { return const SizedBox.shrink(); } return Card( child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Etiquetas (${seleccionadas.length})', style: const TextStyle(fontWeight: FontWeight.bold), ), const SizedBox(height: 8), Wrap( spacing: 8, runSpacing: 8, children: seleccionadas .map((h) => Chip( label: Text(HashUtils.truncateHash(h)), onDeleted: () => context.read().removeEtiqueta(h), )) .toList(), ), ], ), ), ); }, ), // GPS info if (state.contenedor.gps != null) ...[ const SizedBox(height: 16), Card( child: ListTile( leading: const Icon(Icons.location_on, color: Colors.green), title: const Text('Ubicación'), subtitle: Text(state.contenedor.gps!.toString()), ), ), ], const SizedBox(height: 24), // Send button BlocBuilder( builder: (context, appState) { final destino = appState.destinoActivo; return FilledButton.icon( onPressed: destino != null && state.status != CapturaStatus.sending ? () { final etiquetas = context .read() .state .seleccionadas; context.read().setEtiquetas(etiquetas); context.read().enviar(destino); } : null, icon: state.status == CapturaStatus.sending ? const SizedBox( width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2), ) : const Icon(Icons.send), label: Text(state.status == CapturaStatus.sending ? 'Enviando...' : 'Enviar'), ); }, ), if (state.status == CapturaStatus.error && state.errorMessage != null) Padding( padding: const EdgeInsets.only(top: 16), child: Text( state.errorMessage!, style: TextStyle(color: Theme.of(context).colorScheme.error), ), ), ], ), ), ); }, ); } IconData _getFileIcon(dynamic tipo) { switch (tipo.toString()) { case 'FileType.image': return Icons.image; case 'FileType.audio': return Icons.audiotrack; case 'FileType.video': return Icons.videocam; default: return Icons.insert_drive_file; } } } class _CaptureButton extends StatelessWidget { final IconData icon; final String label; final VoidCallback onPressed; final bool selected; const _CaptureButton({ required this.icon, required this.label, required this.onPressed, this.selected = false, }); @override Widget build(BuildContext context) { return FilledButton.tonal( onPressed: onPressed, style: FilledButton.styleFrom( backgroundColor: selected ? Theme.of(context).colorScheme.primaryContainer : null, ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(icon, size: 18), const SizedBox(width: 4), Text(label), ], ), ); } }