import 'dart:typed_data'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../../core/theme/app_colors.dart'; import '../../../../core/widgets/riotz_scaffold.dart'; import '../../../../core/widgets/riotz_button.dart'; import '../providers/music_providers.dart'; import '../widgets/track_card.dart'; class MusicPage extends ConsumerStatefulWidget { const MusicPage({super.key}); @override ConsumerState createState() => _MusicPageState(); } class _MusicPageState extends ConsumerState { final _titleController = TextEditingController(); final _genreController = TextEditingController(); Uint8List? _selectedMp3Bytes; String? _selectedFileName; String _selectedExtension = 'mp3'; @override void dispose() { _titleController.dispose(); _genreController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final theme = Theme.of(context); final tracksAsync = ref.watch(trackListProvider); final uploadState = ref.watch(musicControllerProvider); ref.listen(musicControllerProvider, (_, next) { next.whenOrNull( data: (_) { if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( const SnackBar( backgroundColor: AppColors.success, content: Text('SONIC CHAOS UPLOADED.', style: TextStyle(color: AppColors.black, fontWeight: FontWeight.bold)), ), ); }, error: (error, _) { if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( SnackBar( backgroundColor: AppColors.bloodRed, content: Text(error.toString().toUpperCase()), ), ); }, ); }); return RiotzScaffold( appBar: AppBar( title: const Text('RIOTZ // AUDIO'), ), body: ListView( padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 24), children: [ Text( 'UPLOAD TRANSMISSION', style: theme.textTheme.labelLarge?.copyWith(color: AppColors.neonRed), ), const SizedBox(height: 16), _UploadComposer( titleController: _titleController, genreController: _genreController, selectedFileName: _selectedFileName, isBusy: uploadState.isLoading, onPickMp3: uploadState.isLoading ? null : _pickMp3, onUpload: uploadState.isLoading ? null : _upload, ), const SizedBox(height: 48), Text( 'LATEST SOUNDS', style: theme.textTheme.labelLarge?.copyWith(color: AppColors.neonRed), ), const SizedBox(height: 16), tracksAsync.when( loading: () => const Center(child: CircularProgressIndicator(color: AppColors.neonRed)), error: (error, _) => Center(child: Text('SIGNAL LOST: $error')), data: (tracks) { if (tracks.isEmpty) { return const Center( child: Padding( padding: EdgeInsets.all(32), child: Text('NO TRACKS FOUND. BROADCAST YOUR SOUND.'), ), ); } return Column( children: tracks.map((track) { return TrackCard(track: track); }).toList(), ); }, ), ], ), ); } Future _pickMp3() async { final result = await FilePicker.platform.pickFiles( type: FileType.custom, allowedExtensions: const ['mp3'], withData: true, ); if (result == null || result.files.isEmpty) return; final file = result.files.first; if (file.bytes == null) { if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('UNREADABLE FILE.')), ); return; } setState(() { _selectedMp3Bytes = file.bytes; _selectedFileName = file.name.toUpperCase(); _selectedExtension = file.name.split('.').last.toLowerCase(); }); } Future _upload() async { final bytes = _selectedMp3Bytes; if (bytes == null) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('SELECT AN MP3 FIRST.')), ); return; } await ref.read(musicControllerProvider.notifier).uploadTrack( bytes: bytes, title: _titleController.text, genreTag: _genreController.text, extension: _selectedExtension, ); if (!mounted) return; setState(() { _selectedMp3Bytes = null; _selectedFileName = null; _titleController.clear(); _genreController.clear(); }); } } class _UploadComposer extends StatelessWidget { const _UploadComposer({ required this.titleController, required this.genreController, required this.selectedFileName, required this.isBusy, required this.onPickMp3, required this.onUpload, }); final TextEditingController titleController; final TextEditingController genreController; final String? selectedFileName; final bool isBusy; final VoidCallback? onPickMp3; final VoidCallback? onUpload; @override Widget build(BuildContext context) { return Container( decoration: BoxDecoration( color: AppColors.surface, border: Border.all(color: AppColors.border), ), padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ TextField( controller: titleController, decoration: const InputDecoration( hintText: 'TRACK TITLE', ), ), const SizedBox(height: 16), TextField( controller: genreController, decoration: const InputDecoration( hintText: 'GENRE (RAGE, PUNK, PHONK)', ), ), const SizedBox(height: 24), if (selectedFileName != null) Padding( padding: const EdgeInsets.only(bottom: 16), child: Text( 'READY: $selectedFileName', style: const TextStyle(color: AppColors.neonRed, fontWeight: FontWeight.bold, fontSize: 10), ), ), Row( children: [ Expanded( child: RiotzButton( label: 'SELECT FILE', style: RiotzButtonStyle.outline, onPressed: onPickMp3, ), ), const SizedBox(width: 12), Expanded( child: RiotzButton( label: 'UPLOAD', isLoading: isBusy, onPressed: onUpload, ), ), ], ), ], ), ); } }