import "package:flutter/material.dart"; import "package:flutter_hooks/flutter_hooks.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:intl/intl.dart"; import "package:nexus/controllers/client_controller.dart"; import "package:nexus/controllers/client_state_controller.dart"; import "package:nexus/controllers/power_level_controller.dart"; import "package:nexus/controllers/profile_controller.dart"; import "package:nexus/helpers/extensions/better_when.dart"; import "package:nexus/helpers/extensions/get_localpart.dart"; import "package:nexus/helpers/extensions/mxc_to_https.dart"; import "package:nexus/models/configs/power_level_config.dart"; import "package:nexus/models/content/membership.dart"; import "package:nexus/models/membership_status.dart"; import "package:nexus/models/requests/membership_action.dart"; import "package:nexus/models/requests/set_membership_request.dart"; import "package:nexus/widgets/avatar_or_hash.dart"; import "package:nexus/main.dart"; import "package:nexus/widgets/expandable_image.dart"; import "package:nexus/widgets/form_text_input.dart"; class UserPopover extends ConsumerWidget { final MembershipContent member; final String userId; final String? roomId; const UserPopover(this.member, this.userId, {this.roomId, super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final theme = Theme.of(context); final textTheme = theme.textTheme; final client = ref.watch(ClientController.provider.notifier); void showMembershipDialog(MembershipAction action) => showDialog( context: context, builder: (context) => HookBuilder( builder: (context) { final actionReasonController = useTextEditingController(); return AlertDialog( title: Text("${toBeginningOfSentenceCase(action.name)} $userId"), content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Are you sure you want to ${action.name} $userId?"), SizedBox(height: 12), FormTextInput( required: false, capitalize: true, controller: actionReasonController, title: "Reason for ${action.name} (optional)", ), ], ), actions: [ TextButton( onPressed: Navigator.of(context).pop, child: Text("Cancel"), ), TextButton( onPressed: () { Navigator.of(context).pop(); client .setMembership( SetMembershipRequest( userId: userId, roomId: roomId!, action: action, reason: actionReasonController.text, ), ) .onError(showError); }, child: Text(toBeginningOfSentenceCase(action.name)), ), ], ); }, ), ); return Column( spacing: 16, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Wrap( alignment: WrapAlignment.center, spacing: 16, runSpacing: 8, children: [ ExpandableImage( member.avatarUrl ?.mxcToHttps( ref.watch( ClientStateController.provider.select( (value) => value!.homeserverUrl!, ), ), ) .toString(), child: AvatarOrHash( member.avatarUrl, member.displayName ?? userId.localpart, height: 80, ), ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SelectableText( member.displayName ?? userId.localpart, style: textTheme.headlineSmall, ), SelectableText(userId, style: textTheme.titleSmall), SizedBox(height: 4), ref .watch(ProfileController.provider(userId)) .betterWhen( loading: SizedBox.shrink, data: (profile) => Wrap( spacing: 4, children: [ for (final pronoun in profile.pronouns.where( (pronoun) => pronoun.language == "en", )) Chip( label: Text(pronoun.summary), labelStyle: TextStyle( color: theme.colorScheme.onPrimary, ), color: WidgetStatePropertyAll( theme.colorScheme.primary, ), ), if (profile.timezone != null) Chip( label: Text(profile.timezone!), labelStyle: TextStyle( color: theme.colorScheme.onPrimary, ), color: WidgetStatePropertyAll( theme.colorScheme.primary, ), ), ], ), ), ], ), ], ), if (userId != ref.watch(ClientStateController.provider)?.userId && roomId != null) Wrap( spacing: 8, runSpacing: 8, children: [ FilledButton.icon(onPressed: null, label: Text("Message")), if (ref.watch( PowerLevelController.provider( PowerLevelConfig.membershipAction( action: MembershipAction.kick, roomId: roomId!, targetUser: userId, ), ), ) && member.status == MembershipStatus.join || member.status == MembershipStatus.invite) FilledButton.icon( onPressed: () => showMembershipDialog(MembershipAction.kick), label: Text("Kick"), style: ButtonStyle( backgroundColor: WidgetStatePropertyAll( theme.colorScheme.error, ), foregroundColor: WidgetStatePropertyAll( theme.colorScheme.onError, ), ), ), if (ref.watch( PowerLevelController.provider( PowerLevelConfig.membershipAction( roomId: roomId!, action: MembershipAction.ban, targetUser: userId, ), ), )) ElevatedButton.icon( onPressed: () => showMembershipDialog( member.status == MembershipStatus.ban ? MembershipAction.unban : MembershipAction.ban, ), label: Text( member.status == MembershipStatus.ban ? "Unban" : "Ban", ), style: ButtonStyle( backgroundColor: WidgetStatePropertyAll( theme.colorScheme.errorContainer, ), foregroundColor: WidgetStatePropertyAll( theme.colorScheme.onErrorContainer, ), ), ), ], ), ], ); } }