128 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			128 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
| import 'dart:math' as math;
 | |
| 
 | |
| import 'package:flutter/material.dart';
 | |
| import 'package:flutter_hooks/flutter_hooks.dart';
 | |
| import 'package:flutter_popup_card/flutter_popup_card.dart';
 | |
| import 'package:gap/gap.dart';
 | |
| import 'package:hooks_riverpod/hooks_riverpod.dart';
 | |
| import 'package:island/pods/chat/call.dart';
 | |
| import 'package:island/widgets/account/account_nameplate.dart';
 | |
| import 'package:livekit_client/livekit_client.dart';
 | |
| import 'package:material_symbols_icons/material_symbols_icons.dart';
 | |
| import 'package:styled_widget/styled_widget.dart';
 | |
| 
 | |
| class CallParticipantCard extends HookConsumerWidget {
 | |
|   final CallParticipantLive live;
 | |
|   const CallParticipantCard({super.key, required this.live});
 | |
| 
 | |
|   @override
 | |
|   Widget build(BuildContext context, WidgetRef ref) {
 | |
|     final width =
 | |
|         math.min(MediaQuery.of(context).size.width - 80, 360).toDouble();
 | |
|     final callNotifier = ref.watch(callNotifierProvider.notifier);
 | |
| 
 | |
|     final volumeSliderValue = useState(callNotifier.getParticipantVolume(live));
 | |
| 
 | |
|     return PopupCard(
 | |
|       elevation: 8,
 | |
|       shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)),
 | |
|       child: SizedBox(
 | |
|         width: width,
 | |
|         child: Column(
 | |
|           mainAxisSize: MainAxisSize.min,
 | |
|           crossAxisAlignment: CrossAxisAlignment.stretch,
 | |
|           children: [
 | |
|             Column(
 | |
|               spacing: 4,
 | |
|               children: [
 | |
|                 Row(
 | |
|                   children: [
 | |
|                     const Icon(Symbols.sound_detection_loud_sound, size: 16),
 | |
|                     const Gap(8),
 | |
|                     Expanded(
 | |
|                       child: Slider(
 | |
|                         max: 2,
 | |
|                         value: volumeSliderValue.value,
 | |
|                         onChanged: (value) {
 | |
|                           volumeSliderValue.value = value;
 | |
|                         },
 | |
|                         onChangeEnd: (value) {
 | |
|                           callNotifier.setParticipantVolume(live, value);
 | |
|                         },
 | |
|                         year2023: true,
 | |
|                         padding: EdgeInsets.zero,
 | |
|                       ),
 | |
|                     ),
 | |
|                     const Gap(16),
 | |
|                     SizedBox(
 | |
|                       width: 40,
 | |
|                       child: Text(
 | |
|                         '${(volumeSliderValue.value * 100).toStringAsFixed(0)}%',
 | |
|                       ),
 | |
|                     ),
 | |
|                   ],
 | |
|                 ),
 | |
|                 Row(
 | |
|                   children: [
 | |
|                     const Icon(Symbols.wifi, size: 16),
 | |
|                     const Gap(8),
 | |
|                     Text(switch (live.remoteParticipant.connectionQuality) {
 | |
|                       ConnectionQuality.excellent => 'Excellent',
 | |
|                       ConnectionQuality.good => 'Good',
 | |
|                       ConnectionQuality.poor => 'Bad',
 | |
|                       ConnectionQuality.lost => 'Lost',
 | |
|                       _ => 'Connecting',
 | |
|                     }),
 | |
|                   ],
 | |
|                 ),
 | |
|               ],
 | |
|             ).padding(horizontal: 20, top: 16),
 | |
|             AccountNameplate(
 | |
|               name: live.participant.identity,
 | |
|               isOutlined: false,
 | |
|             ),
 | |
|           ],
 | |
|         ),
 | |
|       ),
 | |
|     );
 | |
|   }
 | |
| }
 | |
| 
 | |
| class CallParticipantGestureDetector extends StatelessWidget {
 | |
|   final CallParticipantLive participant;
 | |
|   final Widget child;
 | |
|   const CallParticipantGestureDetector({
 | |
|     super.key,
 | |
|     required this.participant,
 | |
|     required this.child,
 | |
|   });
 | |
| 
 | |
|   @override
 | |
|   Widget build(BuildContext context) {
 | |
|     return GestureDetector(
 | |
|       child: child,
 | |
|       onTapDown: (details) {
 | |
|         showCallParticipantCard(
 | |
|           context,
 | |
|           participant,
 | |
|           offset: details.localPosition,
 | |
|         );
 | |
|       },
 | |
|     );
 | |
|   }
 | |
| }
 | |
| 
 | |
| Future<void> showCallParticipantCard(
 | |
|   BuildContext context,
 | |
|   CallParticipantLive participant, {
 | |
|   Offset? offset,
 | |
| }) async {
 | |
|   await showPopupCard<void>(
 | |
|     offset: offset ?? Offset.zero,
 | |
|     context: context,
 | |
|     builder: (context) => CallParticipantCard(live: participant),
 | |
|     alignment: Alignment.center,
 | |
|     dimBackground: true,
 | |
|   );
 | |
| }
 |