You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
cloudsolutions-atoms/lib/modules/cx_module/chat/call/incoming_call_dialog.dart

217 lines
6.4 KiB
Dart

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/string_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/new_views/common_widgets/default_app_bar.dart';
import '../chat_provider.dart';
import '../model/call_session.dart';
import 'audio_call_page.dart';
import 'video_call_page.dart';
/// Native-style full-screen incoming call dialog matching audio call page design
class IncomingCallDialog extends StatefulWidget {
final CallSession call;
const IncomingCallDialog({
Key? key,
required this.call,
}) : super(key: key);
static void show(BuildContext context, CallSession call) {
Navigator.of(context).push(
MaterialPageRoute(
fullscreenDialog: true,
builder: (context) => IncomingCallDialog(call: call),
),
);
}
@override
State<IncomingCallDialog> createState() => _IncomingCallDialogState();
}
class _IncomingCallDialogState extends State<IncomingCallDialog> {
@override
void initState() {
super.initState();
// Listen for call status changes
WidgetsBinding.instance.addPostFrameCallback((_) {
final chatProvider = context.read<ChatProvider>();
chatProvider.addListener(_checkCallStatus);
});
}
void _checkCallStatus() {
if (!mounted) return; // Early exit if widget is unmounted
final chatProvider = context.read<ChatProvider>();
// If call status is idle (call cancelled/ended), automatically dismiss dialog
if (chatProvider.callStatus == CallStatus.idle) {
// Remove listener before popping
chatProvider.removeListener(_checkCallStatus);
Navigator.of(context).pop();
}
}
@override
void dispose() {
// Clean up listener
try {
context.read<ChatProvider>().removeListener(_checkCallStatus);
} catch (e) {
// Provider might already be disposed
}
super.dispose();
}
@override
Widget build(BuildContext context) {
final chatProvider = context.read<ChatProvider>();
return PopScope(
canPop: false, // Prevent dismissing by back button
child: Scaffold(
backgroundColor: AppColor.backgroundTabBarDark,
body: Column(
children: [
const Spacer(flex: 2),
_buildContactInfo(context, widget.call),
8.height,
_buildIncomingCallStatus(context),
const Spacer(flex: 4),
_buildActions(context, chatProvider, widget.call),
32.height,
],
),
),
);
}
Widget _buildContactInfo(BuildContext context, CallSession session) {
return Column(
children: [
Container(
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: AppColor.whiteF8d,
border: Border.all(color: AppColor.white10.withOpacity(0.2), width: 1),
),
child: ClipOval(
child: session.peerAvatar != null
? CachedNetworkImage(
imageUrl: session.peerAvatar!,
fit: BoxFit.cover,
placeholder: (context, url) => Center(
child: CircularProgressIndicator(
color: Colors.white.withOpacity(0.5),
),
),
errorWidget: (context, url, error) => 'call_user_avatar'.toSvgAsset(height: 48, width: 48),
)
: 'call_user_avatar'.toSvgAsset(height: 48, width: 48),
),
),
24.height,
Text(
session.peerName,
style: AppTextStyles.heading2.copyWith(
color: Colors.white,
fontWeight: FontWeight.w600,
),
textAlign: TextAlign.center,
),
],
);
}
Widget _buildIncomingCallStatus(BuildContext context) {
return Column(
children: [
Text(
widget.call.type == CallType.video ? 'Atoms Video Call' : 'Atoms Audio Call',
style: AppTextStyles.heading5.copyWith(
color: AppColor.neutral100,
fontWeight: FontWeight.w400,
),
),
8.height,
Text(
'Incoming call...',
style: AppTextStyles.heading5.copyWith(
color: AppColor.neutral100,
fontWeight: FontWeight.w400,
),
),
],
);
}
Widget _buildActions(BuildContext context, ChatProvider chatProvider, CallSession session) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
// Decline button
_buildActionButton(
icon: 'end_call',
label: 'Decline',
onPressed: () async {
Navigator.of(context).pop();
await chatProvider.declineCall('declined');
},
),
// Accept button
_buildActionButton(
icon: 'calling_icon',
label: 'Accept',
onPressed: () async {
// Just dismiss the dialog - acceptCall() will handle navigation
Navigator.of(context).pop();
await chatProvider.acceptCall();
},
),
],
);
}
Widget _buildActionButton({
required String icon,
required String label,
required VoidCallback onPressed,
}) {
return Column(
children: [
Container(
padding: const EdgeInsets.all(18),
decoration: BoxDecoration(
color: icon == 'end_call' ? AppColor.red30 : Colors.green,
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.3),
blurRadius: 8,
offset: const Offset(0, 4),
),
],
),
alignment: Alignment.center,
child: icon.toSvgAsset(height: 32, width: 32, color: AppColor.white10),
).onPress(onPressed),
8.height,
Text(
label,
style: AppTextStyles.heading5.copyWith(
color: AppColor.neutral100,
fontWeight: FontWeight.w400,
),
),
],
);
}
}