import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import '../l10n/app_localizations.dart'; import '../../domain/entities/server_config.dart'; import '../bloc/settings_bloc.dart'; import '../bloc/settings_event.dart'; import '../bloc/settings_state.dart'; class SettingsPage extends StatelessWidget { const SettingsPage({super.key}); @override Widget build(BuildContext context) { return BlocBuilder( builder: (context, state) { return state.when( initial: () => const Center(child: CircularProgressIndicator()), loading: () => const Center(child: CircularProgressIndicator()), loaded: (apiUrl, serverConfig, languageCode, error) => _SettingsContent( apiUrl: apiUrl, serverConfig: serverConfig, languageCode: languageCode, error: error, ), error: (message) => Center(child: Text(message)), ); }, ); } } class _SettingsContent extends StatelessWidget { final String apiUrl; final ServerConfig? serverConfig; final String languageCode; final String? error; const _SettingsContent({ required this.apiUrl, required this.serverConfig, required this.languageCode, this.error, }); @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context)!; return ListView( children: [ _SectionHeader(title: l10n.serverSettings), _ApiUrlTile( apiUrl: apiUrl, onSave: (url) { context.read().add(SettingsApiUrlSaved(url)); }, ), const Divider(), _SectionHeader(title: l10n.language), _LanguageTile( currentCode: languageCode, onChanged: (code) { context.read().add(SettingsLanguageChanged(code)); }, ), const Divider(), if (serverConfig != null) ...[ _SectionHeader(title: l10n.serverConfig), _ConfigModuleCard( title: l10n.system, icon: Icons.computer, children: [ _ConfigItem(label: l10n.domainUrl, value: serverConfig!.system.domainUrl), _ConfigItem(label: l10n.enableRegistration, value: serverConfig!.system.enableRegistration ? l10n.enabled : l10n.disabled), ], ), _ConfigModuleCard( title: l10n.messages, icon: Icons.message, children: [ _ConfigItem(label: l10n.enabled, value: serverConfig!.messages.allowMedia ? l10n.enabled : l10n.disabled), _ConfigItem(label: 'Max media size', value: '${(serverConfig!.messages.maxMediaSizeBytes / 1024 / 1024).toStringAsFixed(1)} MB'), ], ), _ConfigModuleCard( title: l10n.calls, icon: Icons.call, children: [ _ConfigItem(label: l10n.enabled, value: serverConfig!.webRtc.enabled ? l10n.enabled : l10n.disabled), _ConfigItem(label: 'Voice calls', value: serverConfig!.webRtc.enableVoiceCalls ? l10n.enabled : l10n.disabled), _ConfigItem(label: 'Video calls', value: serverConfig!.webRtc.enableVideoCalls ? l10n.enabled : l10n.disabled), ], ), _ConfigModuleCard( title: l10n.stories, icon: Icons.auto_stories, children: [ _ConfigItem(label: l10n.enabled, value: serverConfig!.stories.enabled ? l10n.enabled : l10n.disabled), _ConfigItem(label: 'Lifetime', value: '${serverConfig!.stories.storyLifetimeHours}h'), ], ), _ConfigModuleCard( title: l10n.chatsModule, icon: Icons.chat_bubble, children: [ _ConfigItem(label: 'Groups', value: serverConfig!.chats.supportGroups ? l10n.enabled : l10n.disabled), _ConfigItem(label: 'Max participants', value: '${serverConfig!.chats.maxGroupParticipants}'), ], ), if (serverConfig!.federation.enabled) _ConfigModuleCard( title: l10n.federation, icon: Icons.public, children: [ _ConfigItem(label: 'Description', value: serverConfig!.federation.serverDescription), ], ), const Divider(), ], if (serverConfig == null) ListTile( leading: const Icon(Icons.refresh), title: Text(l10n.retry), onTap: () { context.read().add(const SettingsRefreshConfig()); }, ), _SectionHeader(title: l10n.settings), _SettingsTile( icon: Icons.notifications_outlined, title: l10n.notifications, onTap: () {}, ), _SettingsTile( icon: Icons.security, title: l10n.privacy, onTap: () {}, ), _SettingsTile( icon: Icons.volume_up, title: l10n.soundsAndVibration, onTap: () {}, ), _SettingsTile( icon: Icons.storage, title: l10n.dataAndStorage, onTap: () {}, ), const Divider(), _SettingsTile( icon: Icons.info_outline, title: l10n.aboutApp, subtitle: '${l10n.version} 1.0.0', onTap: () {}, ), _SettingsTile( icon: Icons.help_outline, title: l10n.help, onTap: () {}, ), const Divider(), Padding( padding: const EdgeInsets.all(16), child: OutlinedButton( onPressed: () {}, style: OutlinedButton.styleFrom( foregroundColor: Colors.red, side: const BorderSide(color: Colors.red), ), child: Padding( padding: const EdgeInsets.symmetric(vertical: 12), child: Text(l10n.logout), ), ), ), ], ); } } class _SectionHeader extends StatelessWidget { final String title; const _SectionHeader({required this.title}); @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.fromLTRB(16, 16, 16, 8), child: Text( title.toUpperCase(), style: TextStyle( fontSize: 13, fontWeight: FontWeight.w600, color: Theme.of(context).colorScheme.primary, letterSpacing: 0.5, ), ), ); } } class _ApiUrlTile extends StatefulWidget { final String apiUrl; final ValueChanged onSave; const _ApiUrlTile({ required this.apiUrl, required this.onSave, }); @override State<_ApiUrlTile> createState() => _ApiUrlTileState(); } class _ApiUrlTileState extends State<_ApiUrlTile> { late final TextEditingController _controller; bool _isEditing = false; @override void initState() { super.initState(); _controller = TextEditingController(text: widget.apiUrl); } @override void didUpdateWidget(covariant _ApiUrlTile oldWidget) { super.didUpdateWidget(oldWidget); if (oldWidget.apiUrl != widget.apiUrl) { _controller.text = widget.apiUrl; } } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context)!; if (_isEditing) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: Row( children: [ Expanded( child: TextField( controller: _controller, decoration: InputDecoration( labelText: l10n.apiUrl, hintText: l10n.apiUrlHint, border: const OutlineInputBorder(), contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), ), keyboardType: TextInputType.url, ), ), const SizedBox(width: 8), IconButton( icon: const Icon(Icons.check, color: Colors.green), onPressed: () { widget.onSave(_controller.text.trim()); setState(() => _isEditing = false); }, ), IconButton( icon: const Icon(Icons.close, color: Colors.red), onPressed: () { _controller.text = widget.apiUrl; setState(() => _isEditing = false); }, ), ], ), ); } return ListTile( leading: const Icon(Icons.link), title: Text(l10n.apiUrl), subtitle: Text( widget.apiUrl.isEmpty ? l10n.apiUrlHint : widget.apiUrl, style: TextStyle( color: widget.apiUrl.isEmpty ? Colors.grey : null, ), ), trailing: const Icon(Icons.edit), onTap: () => setState(() => _isEditing = true), ); } } class _LanguageTile extends StatelessWidget { final String currentCode; final ValueChanged onChanged; const _LanguageTile({ required this.currentCode, required this.onChanged, }); @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context)!; return ListTile( leading: const Icon(Icons.language), title: Text(l10n.language), subtitle: Text(currentCode == 'ru' ? l10n.russian : l10n.english), trailing: DropdownButton( value: currentCode, underline: const SizedBox(), items: [ DropdownMenuItem(value: 'ru', child: Text(l10n.russian)), DropdownMenuItem(value: 'en', child: Text(l10n.english)), ], onChanged: (value) { if (value != null) onChanged(value); }, ), ); } } class _ConfigModuleCard extends StatelessWidget { final String title; final IconData icon; final List children; const _ConfigModuleCard({ required this.title, required this.icon, required this.children, }); @override Widget build(BuildContext context) { return Card( margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), child: ExpansionTile( leading: Icon(icon, color: Theme.of(context).colorScheme.primary), title: Text(title), children: [ Padding( padding: const EdgeInsets.fromLTRB(16, 0, 16, 12), child: Column( children: children, ), ), ], ), ); } } class _ConfigItem extends StatelessWidget { final String label; final String value; const _ConfigItem({ required this.label, required this.value, }); @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.symmetric(vertical: 4), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( label, style: TextStyle( color: Colors.grey[600], fontSize: 13, ), ), Text( value, style: const TextStyle( fontWeight: FontWeight.w500, fontSize: 13, ), ), ], ), ); } } class _SettingsTile extends StatelessWidget { final IconData icon; final String title; final String? subtitle; final VoidCallback onTap; const _SettingsTile({ required this.icon, required this.title, this.subtitle, required this.onTap, }); @override Widget build(BuildContext context) { return ListTile( leading: Icon(icon), title: Text(title), subtitle: subtitle != null ? Text(subtitle!) : null, trailing: const Icon(Icons.chevron_right), onTap: onTap, ); } }