import 'package:flutter/material.dart'; import 'dart:async'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:chepuhagram/domain/services/api_service.dart'; import 'package:chepuhagram/data/datasources/ws_client.dart'; import 'package:provider/provider.dart'; import '/core/constants.dart'; import 'package:cached_network_image/cached_network_image.dart'; class UserProfileScreen extends StatefulWidget { final int userId; final String username; final String name; const UserProfileScreen({ super.key, required this.userId, required this.username, required this.name, }); @override State createState() => _UserProfileScreenState(); } class _UserProfileScreenState extends State { Map? _userData; StreamSubscription? _socketSubscription; bool _isLoading = true; String? _error; Duration? offset; Timer? _onlineTimer; String? firstName; String? lastName; @override void initState() { super.initState(); _loadUserData(); startOnlineUpdates(); DateTime now = DateTime.now(); offset = now.timeZoneOffset; final socketService = Provider.of(context, listen: false); _socketSubscription = socketService.messages.listen(_handleIncomingMessage); } void startOnlineUpdates() { _onlineTimer = Timer.periodic(const Duration(minutes: 1), (_) { _loadUserData(); }); } Future _loadUserData() async { try { final api = ApiService(); final data = await api.getUserById(widget.userId); final prefs = await SharedPreferences.getInstance(); firstName = prefs.containsKey('firstname_${widget.userId}') ? prefs.getString('firstname_${widget.userId}') : null; lastName = prefs.containsKey('lastname_${widget.userId}') ? prefs.getString('lastname_${widget.userId}') : null; if (mounted) { setState(() { _userData = data; _isLoading = false; }); } } catch (e) { if (mounted) { setState(() { _error = e.toString().replaceAll('Exception: ', ''); _isLoading = false; }); } } } @override void dispose() { _onlineTimer?.cancel(); _socketSubscription?.cancel(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Информация о пользователе')), body: _isLoading ? const Center(child: CircularProgressIndicator()) : _error != null ? Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.error_outline, size: 48, color: Colors.red), const SizedBox(height: 16), Text(_error!, textAlign: TextAlign.center), const SizedBox(height: 16), ElevatedButton( onPressed: _loadUserData, child: const Text('Повторить'), ), ], ), ) : _buildUserInfo(), ); } Widget _buildUserInfo() { if (_userData == null) return const SizedBox.shrink(); final String displayFN = firstName ?? _userData?['first_name'] ?? ''; final String displayLN = lastName ?? _userData?['last_name'] ?? ''; final String username = _userData?['username'] ?? ''; final rawAvatarUrl = _userData?['avatar_url']?.toString(); final avatarUrl = rawAvatarUrl != null && rawAvatarUrl.startsWith('/') ? '${AppConstants.baseUrl}$rawAvatarUrl' : rawAvatarUrl; return ListView( padding: const EdgeInsets.all(16), children: [ // Avatar placeholder Center( child: CircleAvatar( radius: 50, backgroundColor: Theme.of(context).primaryColor.withOpacity(0.1), backgroundImage: (avatarUrl != null && _userData?['show_avatar'] == true) ? CachedNetworkImageProvider(avatarUrl) : null, child: (avatarUrl == null || _userData?['show_avatar'] != true) ? Text( (displayFN.isNotEmpty && displayLN.isNotEmpty) ? '${displayFN[0]}${displayLN[0]}'.toUpperCase() : (displayFN.isNotEmpty) ? displayFN[0].toUpperCase() : (username.isNotEmpty) ? username[0].toUpperCase() : '?', style: const TextStyle( fontSize: 32, fontWeight: FontWeight.bold, ), ) : null, ), ), const SizedBox(height: 24), // Name GestureDetector( onTap: () => {_editUserName(displayFN, displayLN)}, child: Row( children: [ const Spacer(), if ((displayFN.isNotEmpty) || (displayLN.isNotEmpty)) Text( '$displayFN $displayLN'.trim(), style: Theme.of(context).textTheme.headlineSmall, textAlign: TextAlign.center, ), const SizedBox(width: 5), Icon(Icons.edit, color: Theme.of(context).colorScheme.onSurface), const Spacer(), ], ), ), const SizedBox(height: 8), // Username if (_userData!['username'] != null && _userData!['username'].isNotEmpty) Text( '@${_userData!['username']}', style: Theme.of( context, ).textTheme.bodyLarge?.copyWith(color: Colors.grey[600]), textAlign: TextAlign.center, ), const SizedBox(height: 8), // Last online status if (_userData!['online'] == true) const Text( 'Онлайн', style: TextStyle(fontSize: 12, color: Colors.greenAccent), textAlign: TextAlign.center, ) else if (DateTime.tryParse(_userData!['last_online']) != null) Text( 'Был(а) в сети ${_formatLastOnline(DateTime.tryParse(_userData!['last_online'])!.add(offset != null ? offset! : Duration.zero))}', style: const TextStyle( fontSize: 12, color: Color.fromARGB(255, 161, 161, 161), ), textAlign: TextAlign.center, ) else const Text( 'Был(а) недавно', style: TextStyle( fontSize: 12, color: Color.fromARGB(255, 161, 161, 161), ), textAlign: TextAlign.center, ), const SizedBox(height: 32), // User ID _buildInfoTile('ID пользователя', _userData!['id'].toString()), // Public Key (if available) if (_userData!['public_key'] != null) _buildInfoTile( 'Публичный ключ', _userData!['public_key'], maxLines: 3, ), // About if (_userData!['about'] != null && _userData!['about'].isNotEmpty) _buildInfoTile('О себе', _userData!['about'], maxLines: 5), // Phone if (_userData!['phone'] != null && _userData!['phone'].isNotEmpty) _buildInfoTile('Телефон', _userData!['phone']), // Email if (_userData!['email'] != null && _userData!['email'].isNotEmpty) _buildInfoTile('Почта', _userData!['email']), const SizedBox(height: 16), if ((_userData!['username'] == null || _userData!['username'].isEmpty) && (_userData!['first_name'] == null || _userData!['first_name'].isEmpty) && (_userData!['last_name'] == null || _userData!['last_name'].isEmpty) && (_userData!['about'] == null || _userData!['about'].isEmpty) && (_userData!['phone'] == null || _userData!['phone'].isEmpty) && (_userData!['email'] == null || _userData!['email'].isEmpty)) const Text( 'Пользователь скрыл дополнительную информацию', style: TextStyle(color: Colors.grey), textAlign: TextAlign.center, ), ], ); } Future _editUserName(String firstname, String lastname) async { final firstnameController = TextEditingController(text: firstname); final lastnameController = TextEditingController(text: lastname); final result = await showDialog( context: context, builder: (ctx) => AlertDialog( title: const Text('Изменить имя пользователя'), content: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, children: [ TextField( controller: firstnameController, minLines: 1, maxLines: 5, autofocus: true, decoration: const InputDecoration(hintText: 'Имя'), textCapitalization: TextCapitalization.words, ), const SizedBox(height: 8), TextField( controller: lastnameController, minLines: 1, maxLines: 5, decoration: const InputDecoration(hintText: 'Фамилия'), textCapitalization: TextCapitalization.words, ), ], ), ), actions: [ TextButton( onPressed: () => Navigator.of(ctx).pop(false), child: const Text('Сбрость'), ), ElevatedButton( onPressed: () => Navigator.of(ctx).pop(true), child: const Text('Сохранить'), ), ], ), ); final prefs = await SharedPreferences.getInstance(); if (result == true) { if (firstname != firstnameController.text) { prefs.setString('firstname_${widget.userId}', firstnameController.text); } if (lastname != lastnameController.text) { prefs.setString('lastname_${widget.userId}', lastnameController.text); } if (mounted) { setState(() {}); } _loadUserData(); } else { prefs.remove('firstname_${widget.userId}'); prefs.remove('lastname_${widget.userId}'); if (mounted) { setState(() {}); } _loadUserData(); } } void _handleIncomingMessage(Map data) async { if (data['type'] == 'user_online') { final userId = int.tryParse(data['user_id']?.toString() ?? ''); if (userId == widget.userId) { if (mounted) { setState(() { _userData = _userData?..['online'] = true; }); } } } if (data['type'] == 'user_offline') { final userId = int.tryParse(data['user_id']?.toString() ?? ''); if (userId == widget.userId) { setState(() { _userData = _userData?..['online'] = false; _userData = _userData ?..['last_online'] = DateTime.now().toIso8601String(); }); } } if (data['type'] == 'user_updated') { print('User updated message received, refreshing contact list'); final userId = int.tryParse(data['user_id']?.toString() ?? ''); if (userId != null && userId == widget.userId) { _loadUserData(); } } } String _formatLastOnline(DateTime lastOnline) { final now = DateTime.now(); final difference = now.difference(lastOnline); if (difference.inSeconds < 60) { return 'только что'; } else if (difference.inMinutes < 60) { return '${difference.inMinutes} минут${_pluralize(difference.inMinutes, "у", "ы", "")} назад'; } else if (difference.inHours < 24) { return '${difference.inHours} час${_pluralize(difference.inHours, "", "а", "ов")} назад'; } else if (difference.inDays < 7) { return '${difference.inDays} ${_pluralize(difference.inDays, "день", "дня", "дней")} назад'; } else { return 'давно'; } } String _pluralize(int count, String form1, String form2, String form5) { final mod10 = count % 10; final mod100 = count % 100; if (mod10 == 1 && mod100 != 11) { return form1; } else if (mod10 >= 2 && mod10 <= 4 && (mod100 < 10 || mod100 >= 20)) { return form2; } else { return form5; } } Widget _buildInfoTile(String label, String value, {int maxLines = 1}) { return Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( label, style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 14, color: Colors.grey, ), ), const SizedBox(height: 4), Text( value, style: const TextStyle(fontSize: 16), maxLines: maxLines, overflow: TextOverflow.ellipsis, ), const Divider(), ], ), ); } }