237 lines
6.9 KiB
Dart
237 lines
6.9 KiB
Dart
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<MusicPage> createState() => _MusicPageState();
|
|
}
|
|
|
|
class _MusicPageState extends ConsumerState<MusicPage> {
|
|
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<void> _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<void> _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,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|