feat: Implement real-time messaging with socket listeners and enable starting chats from agent details.
This commit is contained in:
@@ -7,6 +7,7 @@ import 'package:real_estate_mobile/core/widgets/s3_image.dart';
|
||||
import 'package:real_estate_mobile/features/agents/data/models/agent_profile.dart';
|
||||
import 'package:real_estate_mobile/features/agents/presentation/providers/agent_detail_provider.dart';
|
||||
import 'package:real_estate_mobile/features/auth/presentation/providers/auth_provider.dart';
|
||||
import 'package:real_estate_mobile/features/messaging/presentation/providers/messaging_provider.dart';
|
||||
|
||||
class AgentDetailScreen extends ConsumerStatefulWidget {
|
||||
final String agentId;
|
||||
@@ -203,55 +204,121 @@ class _AgentDetailScreenState extends ConsumerState<AgentDetailScreen> {
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 38),
|
||||
child: Container(
|
||||
height: 50,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
border: Border.all(
|
||||
color: AppColors.primaryDark.withValues(alpha: 0.1),
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
const SizedBox(width: 16),
|
||||
// Status dot
|
||||
Container(
|
||||
width: 12,
|
||||
height: 12,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: isAvailable
|
||||
? const Color(0xFF22C55E)
|
||||
: Colors.white,
|
||||
border: Border.all(
|
||||
color: isAvailable
|
||||
? const Color(0xFF22C55E)
|
||||
: AppColors.primaryDark.withValues(alpha: 0.3),
|
||||
width: 1.5,
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
height: 50,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
border: Border.all(
|
||||
color: AppColors.primaryDark.withValues(alpha: 0.1),
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
const SizedBox(width: 16),
|
||||
// Status dot
|
||||
Container(
|
||||
width: 12,
|
||||
height: 12,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: isAvailable
|
||||
? const Color(0xFF22C55E)
|
||||
: Colors.white,
|
||||
border: Border.all(
|
||||
color: isAvailable
|
||||
? const Color(0xFF22C55E)
|
||||
: AppColors.primaryDark.withValues(alpha: 0.3),
|
||||
width: 1.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
isAvailable ? 'Available.' : 'Unavailable.',
|
||||
style: const TextStyle(
|
||||
fontFamily: 'Fractul',
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.primaryDark,
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
// Connect/Pending/Unlink button
|
||||
_buildConnectButton(
|
||||
connState,
|
||||
isAvailable,
|
||||
() => _handleConnect(state),
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
isAvailable ? 'Available.' : 'Unavailable.',
|
||||
style: const TextStyle(
|
||||
fontFamily: 'Fractul',
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.primaryDark,
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
// Connect/Pending/Unlink button
|
||||
_buildConnectButton(
|
||||
connState,
|
||||
isAvailable,
|
||||
() => _handleConnect(state),
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
),
|
||||
// Start Message button when connected
|
||||
if (connState == 'accepted') ...[
|
||||
const SizedBox(height: 12),
|
||||
_buildStartMessageButton(agent),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
bool _isStartingConversation = false;
|
||||
|
||||
void _handleStartMessage(AgentProfile agent) async {
|
||||
if (_isStartingConversation) return;
|
||||
setState(() => _isStartingConversation = true);
|
||||
// Capture notifier before async gap to avoid using ref after dispose
|
||||
final notifier = ref.read(messagingProvider.notifier);
|
||||
try {
|
||||
final conversationId = await notifier.startConversation(agent.id);
|
||||
if (!mounted) return;
|
||||
if (conversationId != null) {
|
||||
context.push('/messages/chat/$conversationId');
|
||||
}
|
||||
} finally {
|
||||
if (mounted) setState(() => _isStartingConversation = false);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildStartMessageButton(AgentProfile agent) {
|
||||
return GestureDetector(
|
||||
onTap: _isStartingConversation ? null : () => _handleStartMessage(agent),
|
||||
child: Container(
|
||||
height: 46,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.accentOrange,
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: _isStartingConversation
|
||||
? const SizedBox(
|
||||
width: 20,
|
||||
height: 20,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
color: Colors.white,
|
||||
),
|
||||
)
|
||||
: const Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.chat_bubble_outline, size: 18, color: Colors.white),
|
||||
SizedBox(width: 8),
|
||||
Text(
|
||||
'Start Message',
|
||||
style: TextStyle(
|
||||
fontFamily: 'Fractul',
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user