439 lines
15 KiB
Dart
439 lines
15 KiB
Dart
import 'package:awesome_snackbar_content/awesome_snackbar_content.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:onufitness/constants/asset_constants.dart';
|
|
import 'package:onufitness/constants/color_constant.dart';
|
|
import 'package:onufitness/constants/text_constant.dart';
|
|
import 'package:onufitness/routes/route_constant.dart';
|
|
import 'package:onufitness/screens/chat/controllers/chat_controller.dart';
|
|
import 'package:onufitness/services/agora/call_services.dart';
|
|
import 'package:onufitness/widgets/others/new_custom_sneakbar.dart';
|
|
|
|
Widget chatAppBar({
|
|
required BuildContext context,
|
|
required ChatController chatCtrl,
|
|
required String userId,
|
|
required String receiverName,
|
|
String? targetUserAvatarUrl,
|
|
bool showOnlineDot = false,
|
|
bool isGroupChat = false,
|
|
}) {
|
|
final agoraCallService = AgoraCallService.instance;
|
|
|
|
// Function to check if call can be made..............................................................................
|
|
void checkAndMakeCall(CallType callType) {
|
|
if (agoraCallService.callState.value != CallState.idle) {
|
|
AwesomeCustomSnackbar.show(
|
|
context: context,
|
|
title: 'Call In Progress',
|
|
message:
|
|
'You are already in a call. Please end the current call first.',
|
|
contentType: ContentType.warning,
|
|
);
|
|
return;
|
|
}
|
|
|
|
if (receiverName.isEmpty && userId.isEmpty) {
|
|
AwesomeCustomSnackbar.show(
|
|
context: context,
|
|
title: 'Invalid User',
|
|
message: 'Cannot initiate call. User information is not available.',
|
|
contentType: ContentType.warning,
|
|
);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
agoraCallService.makeCall(
|
|
context: context,
|
|
targetUserId: userId,
|
|
targatedUserName: receiverName,
|
|
targatedUserProfilePic: targetUserAvatarUrl,
|
|
callType: callType,
|
|
);
|
|
} catch (e) {
|
|
AwesomeCustomSnackbar.show(
|
|
context: context,
|
|
title: 'Call Failed',
|
|
message: 'Unable to initiate call. Please try again later.',
|
|
contentType: ContentType.warning,
|
|
);
|
|
}
|
|
}
|
|
|
|
return Column(
|
|
children: [
|
|
// Call Indicator - Above the AppBar
|
|
Obx(() {
|
|
if (agoraCallService.callState.value == CallState.connected ||
|
|
agoraCallService.callState.value == CallState.calling ||
|
|
agoraCallService.callState.value == CallState.ringing) {
|
|
return Container(
|
|
width: double.infinity,
|
|
|
|
decoration: BoxDecoration(
|
|
gradient: LinearGradient(
|
|
colors: _getCallGradientColors(
|
|
agoraCallService.callState.value,
|
|
),
|
|
begin: Alignment.centerLeft,
|
|
end: Alignment.centerRight,
|
|
),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: _getCallStatusColor(
|
|
agoraCallService.callState.value,
|
|
).withValues(alpha: 0.3),
|
|
blurRadius: 8,
|
|
offset: Offset(0, 2),
|
|
),
|
|
],
|
|
),
|
|
child: Material(
|
|
color: Colors.transparent,
|
|
child: InkWell(
|
|
onTap: () => _navigateToCallScreen(agoraCallService),
|
|
child: Padding(
|
|
padding: EdgeInsets.symmetric(
|
|
horizontal: 15.w,
|
|
vertical: 12.h,
|
|
),
|
|
child: Row(
|
|
children: [
|
|
// Call Icon
|
|
Container(
|
|
padding: EdgeInsets.all(6.sp),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white.withValues(alpha: 0.2),
|
|
shape: BoxShape.circle,
|
|
),
|
|
child: Icon(
|
|
agoraCallService.currentCallType.value ==
|
|
CallType.video
|
|
? Icons.videocam
|
|
: Icons.call,
|
|
color: Colors.white,
|
|
size: 15.sp,
|
|
),
|
|
),
|
|
SizedBox(width: 12.w),
|
|
|
|
// Call Status and Details
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Text(
|
|
_getCallStatusText(agoraCallService),
|
|
style: TextStyle(
|
|
color: Colors.white,
|
|
fontSize: smallSizeText,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
if (agoraCallService.callState.value ==
|
|
CallState.connected)
|
|
Obx(
|
|
() => Text(
|
|
_formatDuration(
|
|
agoraCallService.callDuration.value,
|
|
),
|
|
style: TextStyle(
|
|
color: Colors.white.withValues(alpha: 0.9),
|
|
fontSize: verySmallSizeText,
|
|
fontWeight: FontWeight.w400,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
|
|
// Action Buttons
|
|
Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
// Return to Call Button
|
|
_buildActionButton(
|
|
icon: Icons.open_in_full,
|
|
onTap:
|
|
() => _navigateToCallScreen(agoraCallService),
|
|
tooltip: 'Return to call',
|
|
),
|
|
SizedBox(width: 8),
|
|
|
|
// End Call Button (only show for connected calls)
|
|
if (agoraCallService.callState.value ==
|
|
CallState.connected)
|
|
_buildActionButton(
|
|
icon: Icons.call_end,
|
|
onTap: () => agoraCallService.endCall(),
|
|
tooltip: 'End call',
|
|
isDestructive: true,
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
return SizedBox.shrink();
|
|
}),
|
|
|
|
// Main AppBar content
|
|
Container(
|
|
padding: EdgeInsets.only(top: 15.h, bottom: 10.h),
|
|
decoration: BoxDecoration(
|
|
color: Color(primaryColor),
|
|
image: DecorationImage(
|
|
image: AssetImage(AssetConstants.chatAppBarBackgroundImage),
|
|
fit: BoxFit.cover,
|
|
opacity: 0.5,
|
|
),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
IconButton(
|
|
icon: Icon(Icons.arrow_back_ios, color: Colors.black),
|
|
onPressed: () async {
|
|
Get.back();
|
|
chatCtrl.selectedUserId.value = "";
|
|
await chatCtrl.markAllMessagesAsRead(userId);
|
|
await chatCtrl.unsuscribePresence(userId);
|
|
},
|
|
),
|
|
|
|
// User Avatar with Online Indicator
|
|
Stack(
|
|
children: [
|
|
CircleAvatar(
|
|
radius: 20.r,
|
|
backgroundColor: Colors.grey.shade300,
|
|
backgroundImage:
|
|
(targetUserAvatarUrl != null &&
|
|
targetUserAvatarUrl.isNotEmpty)
|
|
? NetworkImage(targetUserAvatarUrl)
|
|
: null,
|
|
child:
|
|
(targetUserAvatarUrl == null ||
|
|
targetUserAvatarUrl.isEmpty)
|
|
? Text(
|
|
receiverName.isNotEmpty
|
|
? receiverName[0].toUpperCase()
|
|
: 'U',
|
|
style: const TextStyle(
|
|
color: Colors.white,
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
)
|
|
: null,
|
|
),
|
|
|
|
// if (showOnlineDot && !isGroupChat)
|
|
// Positioned(
|
|
// right: 2,
|
|
// bottom: 2,
|
|
// child: Container(
|
|
// width: 12,
|
|
// height: 12,
|
|
// decoration: BoxDecoration(
|
|
// color: Colors.green,
|
|
// shape: BoxShape.circle,
|
|
// border: Border.all(color: Colors.white, width: 2),
|
|
// ),
|
|
// ),
|
|
// ),
|
|
],
|
|
),
|
|
|
|
const SizedBox(width: 12),
|
|
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
receiverName.isNotEmpty ? receiverName : "Unknown User",
|
|
style: TextStyle(
|
|
color: Colors.black,
|
|
fontWeight: FontWeight.w600,
|
|
fontSize: smallSizeText,
|
|
),
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
if (!isGroupChat)
|
|
Text(
|
|
showOnlineDot ? "Online" : "Last seen recently",
|
|
style: TextStyle(
|
|
fontSize: verySmallSizeText,
|
|
color: showOnlineDot ? Colors.green : Colors.grey,
|
|
fontWeight: FontWeight.w400,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
|
|
// Action Buttons
|
|
Obx(() {
|
|
bool isCallActive =
|
|
agoraCallService.callState.value != CallState.idle;
|
|
return Padding(
|
|
padding: EdgeInsets.only(right: 15.sp),
|
|
child: Row(
|
|
children: [
|
|
if (!isGroupChat && chatCtrl.isMyFriend.value)
|
|
AppBarAction(
|
|
icon: Icons.call,
|
|
onPressed: () => checkAndMakeCall(CallType.voice),
|
|
isEnabled: showOnlineDot && !isCallActive,
|
|
),
|
|
if (!isGroupChat && chatCtrl.isMyFriend.value)
|
|
AppBarAction(
|
|
icon: Icons.videocam,
|
|
onPressed: () => checkAndMakeCall(CallType.video),
|
|
isEnabled: showOnlineDot && !isCallActive,
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
// Helper function to navigate to appropriate call screen
|
|
void _navigateToCallScreen(AgoraCallService agoraCallService) {
|
|
final callState = agoraCallService.callState.value;
|
|
|
|
if (callState == CallState.connected) {
|
|
Get.toNamed(RouteConstant.activeCallScreen);
|
|
} else if (callState == CallState.calling) {
|
|
Get.toNamed(RouteConstant.outgoingCallScreen);
|
|
} else if (callState == CallState.ringing) {
|
|
Get.toNamed(RouteConstant.incomingCallScreen);
|
|
}
|
|
}
|
|
|
|
// Helper function to get call gradient colors
|
|
List<Color> _getCallGradientColors(CallState callState) {
|
|
switch (callState) {
|
|
case CallState.connected:
|
|
return [Color(0xFF4CAF50), Color(0xFF66BB6A)];
|
|
case CallState.calling:
|
|
return [Color(0xFFFF9800), Color(0xFFFFB74D)];
|
|
case CallState.ringing:
|
|
return [Color(0xFF2196F3), Color(0xFF64B5F6)];
|
|
default:
|
|
return [Colors.grey, Colors.grey.shade400];
|
|
}
|
|
}
|
|
|
|
// Helper function to get call status color
|
|
Color _getCallStatusColor(CallState callState) {
|
|
switch (callState) {
|
|
case CallState.connected:
|
|
return Color(0xFF4CAF50);
|
|
case CallState.calling:
|
|
return Color(0xFFFF9800);
|
|
case CallState.ringing:
|
|
return Color(0xFF2196F3);
|
|
default:
|
|
return Colors.grey;
|
|
}
|
|
}
|
|
|
|
// Helper function to build action buttons
|
|
Widget _buildActionButton({
|
|
required IconData icon,
|
|
required VoidCallback onTap,
|
|
required String tooltip,
|
|
bool isDestructive = false,
|
|
}) {
|
|
return Tooltip(
|
|
message: tooltip,
|
|
child: InkWell(
|
|
onTap: onTap,
|
|
borderRadius: BorderRadius.circular(20),
|
|
child: Container(
|
|
padding: EdgeInsets.all(8),
|
|
decoration: BoxDecoration(
|
|
color:
|
|
isDestructive
|
|
? Colors.red.withValues(alpha: 0.2)
|
|
: Colors.white.withValues(alpha: 0.2),
|
|
borderRadius: BorderRadius.circular(20),
|
|
),
|
|
child: Icon(
|
|
icon,
|
|
color: isDestructive ? Colors.red.shade100 : Colors.white,
|
|
size: 15.sp,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
// Helper function to get call status text
|
|
String _getCallStatusText(AgoraCallService agoraCallService) {
|
|
switch (agoraCallService.callState.value) {
|
|
case CallState.calling:
|
|
return 'Calling ${agoraCallService.callerName.value}...';
|
|
case CallState.ringing:
|
|
return 'Incoming call from ${agoraCallService.callerName.value}';
|
|
case CallState.connected:
|
|
return 'On call with ${agoraCallService.callerName.value}';
|
|
default:
|
|
return '';
|
|
}
|
|
}
|
|
|
|
// Helper function to format call duration
|
|
String _formatDuration(int seconds) {
|
|
int hours = seconds ~/ 3600;
|
|
int minutes = (seconds % 3600) ~/ 60;
|
|
int remainingSeconds = seconds % 60;
|
|
|
|
if (hours > 0) {
|
|
return '${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}:${remainingSeconds.toString().padLeft(2, '0')}';
|
|
}
|
|
return '${minutes.toString().padLeft(2, '0')}:${remainingSeconds.toString().padLeft(2, '0')}';
|
|
}
|
|
|
|
// App Bar Action Widget
|
|
class AppBarAction extends StatelessWidget {
|
|
final IconData icon;
|
|
final VoidCallback onPressed;
|
|
final bool isEnabled;
|
|
|
|
const AppBarAction({
|
|
super.key,
|
|
required this.icon,
|
|
required this.onPressed,
|
|
this.isEnabled = true,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return IconButton(
|
|
// icon: Icon(icon, color: isEnabled ? Colors.black : Colors.grey.shade500),
|
|
icon: Icon(icon, color: Colors.black),
|
|
onPressed: onPressed,
|
|
);
|
|
}
|
|
}
|
|
|
|
//............................................................................................................................
|