import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:image_picker/image_picker.dart'; import '../../../../core/router/app_routes.dart'; import '../../../../core/theme/app_colors.dart'; import '../../../../core/widgets/riotz_scaffold.dart'; import '../../../../core/widgets/riotz_button.dart'; import '../../../../features/auth/presentation/providers/auth_providers.dart'; import '../../../../features/admin/presentation/providers/admin_providers.dart'; import '../../../../features/feed/domain/models/feed_post_model.dart'; import '../../../../features/feed/presentation/providers/feed_providers.dart'; class HomePage extends ConsumerStatefulWidget { const HomePage({super.key}); @override ConsumerState createState() => _HomePageState(); } class _HomePageState extends ConsumerState { final _captionController = TextEditingController(); final _picker = ImagePicker(); Uint8List? _selectedImageBytes; String _selectedImageExt = 'jpg'; @override void dispose() { _captionController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final theme = Theme.of(context); final user = ref.watch(currentUserProvider); final isAdmin = ref.watch(isAdminProvider); final feedAsync = ref.watch(feedPostsProvider); final feedActionState = ref.watch(feedControllerProvider); final isBusy = feedActionState.isLoading; ref.listen(feedControllerProvider, (_, next) { next.whenOrNull( data: (_) { if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( const SnackBar( backgroundColor: AppColors.success, content: Text('CHAOS PROPAGATED.', 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 // FEED'), actions: [ IconButton( icon: const Icon(Icons.logout, color: AppColors.white), onPressed: () async { await ref.read(authControllerProvider.notifier).logout(); if (context.mounted) context.go(AppRoutes.login); }, ), ], ), body: RefreshIndicator( color: AppColors.neonRed, backgroundColor: AppColors.black, onRefresh: () async => ref.invalidate(feedPostsProvider), child: ListView( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 24), children: [ Text( 'WELCOME, ${user?.email?.split('@').first.toUpperCase() ?? 'RIOTER'}', style: theme.textTheme.labelLarge?.copyWith(color: AppColors.neonRed), ), const SizedBox(height: 24), // Composer _PostComposer( captionController: _captionController, selectedImageBytes: _selectedImageBytes, onPickImage: isBusy ? null : _pickImage, onCreatePost: isBusy ? null : _createPost, isBusy: isBusy, ), const SizedBox(height: 32), // Navigation Shortcuts (Chaotic Grid) Wrap( spacing: 8, runSpacing: 8, children: [ _NavChip(label: 'PROFILE', onTap: () => context.push(AppRoutes.profile)), _NavChip(label: 'MUSIC', onTap: () => context.push(AppRoutes.music)), _NavChip(label: 'DISCOVER', onTap: () => context.push(AppRoutes.discover)), if (isAdmin) _NavChip(label: 'ADMIN', onTap: () => context.push(AppRoutes.admin), isAccent: true), ], ), const SizedBox(height: 32), feedAsync.when( loading: () => const Center(child: CircularProgressIndicator(color: AppColors.neonRed)), error: (error, _) => Center(child: Text('SYSTEM ERROR: $error')), data: (posts) { if (posts.isEmpty) { return const Center(child: Text('NO CHAOS YET. START A RIOT.')); } return ListView.separated( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: posts.length, separatorBuilder: (_, _) => const SizedBox(height: 24), itemBuilder: (context, index) { final post = posts[index]; return _FeedPostCard( post: post, isOwnPost: user?.id == post.userId, onLike: () => ref.read(feedControllerProvider.notifier).toggleLike(post), onDelete: () => ref.read(feedControllerProvider.notifier).deleteOwnPost(post.id), ); }, ); }, ), ], ), ), ); } Future _pickImage() async { final image = await _picker.pickImage(source: ImageSource.gallery, imageQuality: 80); if (image == null) return; final bytes = await image.readAsBytes(); setState(() { _selectedImageBytes = bytes; _selectedImageExt = image.name.split('.').last; }); } Future _createPost() async { if (_selectedImageBytes == null) return; await ref.read(feedControllerProvider.notifier).createPost( caption: _captionController.text, imageBytes: _selectedImageBytes!, extension: _selectedImageExt, ); _captionController.clear(); setState(() => _selectedImageBytes = null); } } class _NavChip extends StatelessWidget { const _NavChip({required this.label, required this.onTap, this.isAccent = false}); final String label; final VoidCallback onTap; final bool isAccent; @override Widget build(BuildContext context) { return InkWell( onTap: onTap, child: Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: BoxDecoration( border: Border.all(color: isAccent ? AppColors.neonRed : AppColors.white), ), child: Text( label, style: TextStyle( color: isAccent ? AppColors.neonRed : AppColors.white, fontWeight: FontWeight.bold, fontSize: 12, ), ), ), ); } } class _PostComposer extends StatelessWidget { const _PostComposer({ required this.captionController, required this.selectedImageBytes, required this.onPickImage, required this.onCreatePost, required this.isBusy, }); final TextEditingController captionController; final Uint8List? selectedImageBytes; final VoidCallback? onPickImage; final VoidCallback? onCreatePost; final bool isBusy; @override Widget build(BuildContext context) { return Container( decoration: BoxDecoration( border: Border.all(color: AppColors.border), color: AppColors.surface, ), padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ TextField( controller: captionController, maxLines: 3, decoration: const InputDecoration( hintText: 'WHAT\'S THE WORD ON THE STREET?', border: InputBorder.none, enabledBorder: InputBorder.none, focusedBorder: InputBorder.none, ), ), if (selectedImageBytes != null) ...[ const SizedBox(height: 12), Image.memory(selectedImageBytes!, height: 200, width: double.infinity, fit: BoxFit.cover), ], const SizedBox(height: 16), Row( children: [ IconButton( icon: const Icon(Icons.add_a_photo_outlined, color: AppColors.white), onPressed: onPickImage, ), const Spacer(), RiotzButton( label: 'POST', onPressed: onCreatePost, isLoading: isBusy, fullWidth: false, ), ], ), ], ), ); } } class _FeedPostCard extends StatelessWidget { const _FeedPostCard({ required this.post, required this.isOwnPost, required this.onLike, required this.onDelete, }); final FeedPostModel post; final bool isOwnPost; final VoidCallback onLike; final VoidCallback onDelete; @override Widget build(BuildContext context) { final theme = Theme.of(context); return Container( decoration: const BoxDecoration( border: Border(bottom: BorderSide(color: AppColors.border)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Container( width: 40, height: 40, decoration: BoxDecoration( color: AppColors.surfaceLight, border: Border.all(color: AppColors.border), ), child: post.avatarUrl.isNotEmpty ? Image.network(post.avatarUrl, fit: BoxFit.cover) : const Icon(Icons.person, color: AppColors.grey), ), const SizedBox(width: 12), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(post.username.toUpperCase(), style: theme.textTheme.labelLarge), Text('RIOTER', style: theme.textTheme.bodySmall?.copyWith(color: AppColors.neonRed, fontSize: 10)), ], ), const Spacer(), if (isOwnPost) IconButton( icon: const Icon(Icons.more_vert, color: AppColors.grey), onPressed: onDelete, ), ], ), const SizedBox(height: 16), if (post.imageUrl.isNotEmpty) Image.network(post.imageUrl, width: double.infinity, fit: BoxFit.cover), const SizedBox(height: 16), if (post.caption.isNotEmpty) Padding( padding: const EdgeInsets.only(bottom: 12), child: Text(post.caption, style: theme.textTheme.bodyLarge), ), Row( children: [ IconButton( padding: EdgeInsets.zero, constraints: const BoxConstraints(), icon: Icon( post.isLiked ? Icons.favorite : Icons.favorite_border, color: post.isLiked ? AppColors.neonRed : AppColors.white, size: 20, ), onPressed: onLike, ), const SizedBox(width: 8), Text('${post.likesCount}', style: theme.textTheme.labelLarge), const SizedBox(width: 24), const Icon(Icons.chat_bubble_outline, color: AppColors.white, size: 20), const SizedBox(width: 8), const Text('0', style: TextStyle(fontWeight: FontWeight.bold)), ], ), const SizedBox(height: 16), ], ), ); } }