import 'dart:async'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../services/connection_service.dart'; import '../services/api_service.dart'; import '../services/theme_service.dart'; import '../models/guild.dart'; import '../models/channel.dart'; import '../models/role.dart'; import '../widgets/guild_icon.dart'; class HomeScreen extends StatefulWidget { const HomeScreen({super.key}); @override State createState() => _HomeScreenState(); } class _HomeScreenState extends State { final ApiService _api = ApiService(); List _guilds = []; Guild? _selectedGuild; bool _loading = true; @override void initState() { super.initState(); _loadGuilds(); } Future _loadGuilds() async { final conn = context.read(); if (conn.userId == null) return; _api.updateFromWsUrl(conn.serverUrl); try { final guilds = await _api.getGuilds(conn.userId!); if (mounted) { setState(() { _guilds = guilds; _loading = false; }); } } catch (e) { if (mounted) setState(() => _loading = false); } } void _showCreateGuildDialog() { final nameController = TextEditingController(); showDialog( context: context, builder: (ctx) => AlertDialog( backgroundColor: ThemeService.surfaceSecondary, title: const Text('Create Guild', style: TextStyle(color: ThemeService.textPrimary)), content: TextField( controller: nameController, decoration: const InputDecoration( hintText: 'Guild name', hintStyle: TextStyle(color: ThemeService.textMuted), ), style: const TextStyle(color: ThemeService.textPrimary), ), actions: [ TextButton( onPressed: () => Navigator.pop(ctx), child: const Text('Cancel'), ), ElevatedButton( onPressed: () async { final conn = context.read(); if (nameController.text.isNotEmpty && conn.userId != null) { await _api.createGuild(nameController.text, conn.userId!); if (ctx.mounted) Navigator.pop(ctx); _loadGuilds(); } }, style: ElevatedButton.styleFrom( backgroundColor: ThemeService.primary, ), child: const Text('Create'), ), ], ), ); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: ThemeService.backgroundTertiary, body: Row( children: [ Container( width: 72, color: ThemeService.backgroundTertiary, child: Column( children: [ const SizedBox(height: 12), _buildHomeButton(), const SizedBox(height: 8), const Divider( color: Color(0xFF3F4147), height: 2, indent: 20, endIndent: 20, ), const SizedBox(height: 8), Expanded( child: _loading ? const Center( child: CircularProgressIndicator(strokeWidth: 2)) : ListView.builder( itemCount: _guilds.length + 1, itemBuilder: (context, index) { if (index == 0) return _buildAddGuildButton(); final guild = _guilds[index - 1]; return _buildGuildItem(guild); }, ), ), _buildUserMenu(), ], ), ), if (_selectedGuild != null) Expanded( child: _buildChannelList(), ) else const Expanded( child: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.chat_bubble_outline, color: ThemeService.textMuted, size: 64), SizedBox(height: 16), Text( 'Select a guild', style: TextStyle( color: ThemeService.textMuted, fontSize: 18, ), ), SizedBox(height: 8), Text( 'Choose a guild from the left or create a new one', style: TextStyle( color: ThemeService.textSecondary, fontSize: 13, ), ), ], ), ), ), ], ), ); } Widget _buildHomeButton() { return GestureDetector( onTap: () => setState(() => _selectedGuild = null), child: Container( width: 48, height: 48, decoration: BoxDecoration( color: _selectedGuild == null ? ThemeService.primary : ThemeService.surfacePrimary, borderRadius: BorderRadius.circular(_selectedGuild == null ? 14 : 24), ), child: const Icon(Icons.home, color: Colors.white, size: 24), ), ); } Widget _buildGuildItem(Guild guild) { final selected = _selectedGuild?.id == guild.id; return Padding( padding: const EdgeInsets.symmetric(vertical: 3), child: GestureDetector( onTap: () => _loadGuildDetail(guild), child: GuildIcon( name: guild.name, icon: guild.icon, selected: selected, size: 48, ), ), ); } Widget _buildAddGuildButton() { return Padding( padding: const EdgeInsets.symmetric(vertical: 3), child: GestureDetector( onTap: _showCreateGuildDialog, child: Container( width: 48, height: 48, decoration: BoxDecoration( color: ThemeService.surfacePrimary, borderRadius: BorderRadius.circular(24), border: Border.all( color: ThemeService.success.withOpacity(0.5), width: 2, ), ), child: const Icon(Icons.add, color: ThemeService.success, size: 28), ), ), ); } Future _loadGuildDetail(Guild guild) async { try { final data = await _api.getGuild(guild.id); if (mounted) { setState(() { guild.channels = (data['channels'] as List) .map((c) => Channel.fromJson(c)) .toList(); guild.roles = (data['roles'] as List) .map((r) => Role.fromJson(r)) .toList(); guild.categories = (data['categories'] as List) .map((c) => Category.fromJson(c)) .toList(); _selectedGuild = guild; }); } } catch (_) {} } Widget _buildChannelList() { if (_selectedGuild == null) return const SizedBox(); final guild = _selectedGuild!; final textChannels = guild.channels.where((c) => c.isText).toList(); final voiceChannels = guild.channels.where((c) => c.isVoice).toList(); return Container( color: ThemeService.surfaceSecondary, width: 240, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), decoration: const BoxDecoration( border: Border( bottom: BorderSide(color: Color(0xFF3F4147)), ), ), child: Row( children: [ Expanded( child: Text( guild.name, style: const TextStyle( color: ThemeService.textPrimary, fontWeight: FontWeight.w700, fontSize: 16, ), overflow: TextOverflow.ellipsis, ), ), const Icon(Icons.expand_more, color: ThemeService.textSecondary, size: 20), ], ), ), Expanded( child: ListView( padding: const EdgeInsets.symmetric(vertical: 8), children: [ if (textChannels.isNotEmpty) ...[ _buildChannelSection('TEXT CHANNELS'), ...textChannels.map((c) => _buildChannelItem(c)), ], if (voiceChannels.isNotEmpty) ...[ const SizedBox(height: 16), _buildChannelSection('VOICE CHANNELS'), ...voiceChannels.map((c) => _buildChannelItem(c)), ], ], ), ), ], ), ); } Widget _buildChannelSection(String title) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), child: Row( children: [ const Icon(Icons.keyboard_arrow_down, size: 12, color: ThemeService.textSecondary), const SizedBox(width: 4), Text( title, style: const TextStyle( color: ThemeService.textSecondary, fontSize: 11, fontWeight: FontWeight.w700, letterSpacing: 0.5, ), ), ], ), ); } Widget _buildChannelItem(Channel channel) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 1), child: Material( color: Colors.transparent, child: InkWell( borderRadius: BorderRadius.circular(4), onTap: () { Navigator.pushNamed(context, '/chat', arguments: { 'channel': channel, 'guild': _selectedGuild, }); }, child: Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6), child: Row( children: [ Icon( channel.isText ? Icons.tag : Icons.volume_up, size: 18, color: ThemeService.textMuted, ), const SizedBox(width: 6), Expanded( child: Text( channel.name, style: const TextStyle( color: ThemeService.textSecondary, fontSize: 14, ), overflow: TextOverflow.ellipsis, ), ), ], ), ), ), ), ); } Widget _buildUserMenu() { final conn = context.watch(); return Container( padding: const EdgeInsets.all(8), color: ThemeService.backgroundSecondary, child: Row( children: [ Container( width: 32, height: 32, decoration: BoxDecoration( color: ThemeService.primary, borderRadius: BorderRadius.circular(16), ), child: Center( child: Text( (conn.username ?? '?')[0].toUpperCase(), style: const TextStyle( color: Colors.white, fontWeight: FontWeight.w700), ), ), ), const SizedBox(width: 8), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text( conn.username ?? 'Unknown', style: const TextStyle( color: ThemeService.textPrimary, fontSize: 13, fontWeight: FontWeight.w600, ), ), Text( conn.isConnected ? 'Connected' : 'Disconnected', style: TextStyle( color: conn.isConnected ? ThemeService.success : ThemeService.danger, fontSize: 11, ), ), ], ), ), IconButton( icon: const Icon(Icons.settings, size: 18), color: ThemeService.textSecondary, onPressed: () => Navigator.pushNamed(context, '/settings'), constraints: const BoxConstraints(), padding: const EdgeInsets.all(4), ), ], ), ); } }