import 'dart:async'; import 'package:flutter/material.dart'; import 'package:gwid/api/api_service.dart'; import 'package:gwid/models/contact.dart'; class SearchContactScreen extends StatefulWidget { const SearchContactScreen({super.key}); @override State createState() => _SearchContactScreenState(); } class _SearchContactScreenState extends State { final TextEditingController _phoneController = TextEditingController(); StreamSubscription? _apiSubscription; bool _isLoading = false; Contact? _foundContact; String? _errorMessage; @override void initState() { super.initState(); _listenToApiMessages(); } @override void dispose() { _phoneController.dispose(); _apiSubscription?.cancel(); super.dispose(); } void _listenToApiMessages() { _apiSubscription = ApiService.instance.messages.listen((message) { if (!mounted) return; if (message['type'] == 'contact_found') { setState(() { _isLoading = false; _errorMessage = null; }); final payload = message['payload']; final contactData = payload['contact']; if (contactData != null) { _foundContact = Contact.fromJson(contactData); } ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: const Text('Контакт найден!'), backgroundColor: Colors.green, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), behavior: SnackBarBehavior.floating, margin: const EdgeInsets.all(10), ), ); } if (message['type'] == 'contact_not_found') { setState(() { _isLoading = false; _foundContact = null; }); final payload = message['payload']; String errorMessage = 'Контакт не найден'; if (payload != null) { if (payload['localizedMessage'] != null) { errorMessage = payload['localizedMessage']; } else if (payload['message'] != null) { errorMessage = payload['message']; } } setState(() { _errorMessage = errorMessage; }); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(errorMessage), backgroundColor: Colors.orange, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), behavior: SnackBarBehavior.floating, margin: const EdgeInsets.all(10), ), ); } }); } void _searchContact() async { final phone = _phoneController.text.trim(); if (phone.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: const Text('Введите номер телефона'), backgroundColor: Colors.orange, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), behavior: SnackBarBehavior.floating, margin: const EdgeInsets.all(10), ), ); return; } if (!phone.startsWith('+') || phone.length < 10) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: const Text('Введите номер телефона в формате +7XXXXXXXXXX'), backgroundColor: Colors.orange, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), behavior: SnackBarBehavior.floating, margin: const EdgeInsets.all(10), ), ); return; } setState(() { _isLoading = true; _foundContact = null; _errorMessage = null; }); try { await ApiService.instance.searchContactByPhone(phone); } catch (e) { setState(() { _isLoading = false; }); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Ошибка поиска контакта: ${e.toString()}'), backgroundColor: Theme.of(context).colorScheme.error, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), behavior: SnackBarBehavior.floating, margin: const EdgeInsets.all(10), ), ); } } void _startChat() { if (_foundContact != null) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Создание чата с ${_foundContact!.name}'), backgroundColor: Colors.blue, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), behavior: SnackBarBehavior.floating, margin: const EdgeInsets.all(10), ), ); } } @override Widget build(BuildContext context) { final colors = Theme.of(context).colorScheme; return Scaffold( appBar: AppBar( title: const Text('Найти контакт'), backgroundColor: colors.surface, foregroundColor: colors.onSurface, ), body: Stack( children: [ SingleChildScrollView( padding: const EdgeInsets.all(20.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: colors.primaryContainer.withOpacity(0.3), borderRadius: BorderRadius.circular(12), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(Icons.person_search, color: colors.primary), const SizedBox(width: 8), Text( 'Поиск контакта', style: TextStyle( fontWeight: FontWeight.bold, color: colors.primary, ), ), ], ), const SizedBox(height: 8), Text( 'Введите номер телефона для поиска контакта. ' 'Пользователь должен быть зарегистрирован в системе ' 'и разрешить поиск по номеру телефона.', style: TextStyle(color: colors.onSurfaceVariant), ), ], ), ), const SizedBox(height: 24), Text( 'Номер телефона', style: Theme.of( context, ).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold), ), const SizedBox(height: 16), TextField( controller: _phoneController, keyboardType: TextInputType.phone, decoration: InputDecoration( labelText: 'Номер телефона', hintText: '+7XXXXXXXXXX', border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), ), prefixIcon: const Icon(Icons.phone), ), ), const SizedBox(height: 8), Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: colors.surfaceContainerHighest, borderRadius: BorderRadius.circular(8), border: Border.all(color: colors.outline.withOpacity(0.3)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon( Icons.info_outline, size: 16, color: colors.primary, ), const SizedBox(width: 8), Text( 'Формат номера:', style: TextStyle( fontWeight: FontWeight.w600, color: colors.primary, fontSize: 14, ), ), ], ), const SizedBox(height: 8), Text( '• Номер должен начинаться с "+"\n' '• Пример: +79999999990\n' '• Минимум 10 цифр после "+"', style: TextStyle( color: colors.onSurfaceVariant, fontSize: 13, height: 1.4, ), ), ], ), ), const SizedBox(height: 24), SizedBox( width: double.infinity, child: ElevatedButton.icon( onPressed: _isLoading ? null : _searchContact, icon: _isLoading ? SizedBox( width: 20, height: 20, child: CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation( colors.onPrimary, ), ), ) : const Icon(Icons.search), label: Text(_isLoading ? 'Поиск...' : 'Найти контакт'), style: ElevatedButton.styleFrom( minimumSize: const Size(double.infinity, 50), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), ), ), if (_foundContact != null) ...[ const SizedBox(height: 24), Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.green.withOpacity(0.1), borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.green.withOpacity(0.3)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(Icons.check_circle, color: Colors.green), const SizedBox(width: 8), Text( 'Контакт найден', style: TextStyle( fontWeight: FontWeight.bold, color: Colors.green, ), ), ], ), const SizedBox(height: 12), Row( children: [ CircleAvatar( radius: 24, backgroundImage: _foundContact!.photoBaseUrl != null ? NetworkImage(_foundContact!.photoBaseUrl!) : null, child: _foundContact!.photoBaseUrl == null ? Text( _foundContact!.name.isNotEmpty ? _foundContact!.name[0].toUpperCase() : '?', style: TextStyle( color: colors.onSurface, fontWeight: FontWeight.w600, ), ) : null, ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( _foundContact!.name, style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 16, ), ), if (_foundContact!.description?.isNotEmpty == true) Text( _foundContact!.description!, style: TextStyle( color: colors.onSurfaceVariant, fontSize: 14, ), ), ], ), ), ], ), const SizedBox(height: 16), SizedBox( width: double.infinity, child: ElevatedButton.icon( onPressed: _startChat, icon: const Icon(Icons.chat), label: const Text('Написать сообщение'), style: ElevatedButton.styleFrom( backgroundColor: Colors.green, foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), ), ), ], ), ), ], if (_errorMessage != null) ...[ const SizedBox(height: 24), Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.orange.withOpacity(0.1), borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.orange.withOpacity(0.3)), ), child: Row( children: [ Icon(Icons.warning, color: Colors.orange), const SizedBox(width: 8), Expanded( child: Text( _errorMessage!, style: TextStyle( color: Colors.orange.shade800, fontWeight: FontWeight.w500, ), ), ), ], ), ), ], ], ), ), if (_isLoading) Container( color: Colors.black.withOpacity(0.5), child: const Center(child: CircularProgressIndicator()), ), ], ), ); } }