diff --git a/lib/widgets/adaptive_dialogs/adaptive_dialog_action.dart b/lib/widgets/adaptive_dialogs/adaptive_dialog_action.dart index 40fc3f96d..eeec67686 100644 --- a/lib/widgets/adaptive_dialogs/adaptive_dialog_action.dart +++ b/lib/widgets/adaptive_dialogs/adaptive_dialog_action.dart @@ -82,3 +82,73 @@ class AdaptiveDialogAction extends StatelessWidget { } } } + +class AdaptiveDialogInkWell extends StatelessWidget { + final Widget child; + final VoidCallback onTap; + const AdaptiveDialogInkWell({ + super.key, + required this.onTap, + required this.child, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + if ({TargetPlatform.iOS, TargetPlatform.macOS}.contains(theme.platform)) { + return CupertinoButton( + onPressed: onTap, + borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2), + color: theme.colorScheme.surfaceBright, + padding: EdgeInsets.all(8), + child: child, + ); + } + return Material( + color: theme.colorScheme.surfaceBright, + borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2), + child: InkWell( + borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2), + onTap: onTap, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Center(child: child), + ), + ), + ); + } +} + +class AdaptiveIconTextButton extends StatelessWidget { + final String label; + final IconData icon; + final VoidCallback onTap; + const AdaptiveIconTextButton({ + super.key, + required this.label, + required this.icon, + required this.onTap, + }); + + @override + Widget build(BuildContext context) { + final color = Theme.of(context).colorScheme.secondary; + return Expanded( + child: AdaptiveDialogInkWell( + onTap: onTap, + child: Column( + mainAxisSize: .min, + children: [ + Icon(icon, color: color), + Text( + label, + style: TextStyle(fontSize: 12, color: color), + maxLines: 1, + overflow: .ellipsis, + ), + ], + ), + ), + ); + } +} diff --git a/lib/widgets/adaptive_dialogs/public_room_dialog.dart b/lib/widgets/adaptive_dialogs/public_room_dialog.dart index 4d5ac8587..975507564 100644 --- a/lib/widgets/adaptive_dialogs/public_room_dialog.dart +++ b/lib/widgets/adaptive_dialogs/public_room_dialog.dart @@ -6,7 +6,9 @@ import 'package:go_router/go_router.dart'; import 'package:matrix/matrix.dart'; import 'package:fluffychat/l10n/l10n.dart'; +import 'package:fluffychat/utils/fluffy_share.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart'; +import 'package:fluffychat/widgets/adaptive_dialogs/show_text_input_dialog.dart'; import '../../config/themes.dart'; import '../../utils/url_launcher.dart'; import '../avatar.dart'; @@ -90,15 +92,8 @@ class PublicRoomDialog extends StatelessWidget { final roomLink = roomAlias ?? chunk?.roomId; var copied = false; return AlertDialog.adaptive( - title: ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 256), - child: Text( - chunk?.name ?? roomAlias?.localpart ?? chunk?.roomId ?? 'Unknown', - textAlign: TextAlign.center, - ), - ), content: ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 256, maxHeight: 256), + constraints: const BoxConstraints(maxWidth: 256), child: FutureBuilder( future: _search(context), builder: (context, snapshot) { @@ -109,125 +104,196 @@ class PublicRoomDialog extends StatelessWidget { final topic = profile?.topic; return SingleChildScrollView( child: Column( - spacing: 8, + spacing: 16, mainAxisSize: .min, crossAxisAlignment: .stretch, children: [ - if (roomLink != null) - HoverBuilder( - builder: (context, hovered) => StatefulBuilder( - builder: (context, setState) => MouseRegion( - cursor: SystemMouseCursors.click, - child: GestureDetector( - onTap: () { - Clipboard.setData(ClipboardData(text: roomLink)); - setState(() { - copied = true; - }); - }, - child: RichText( - text: TextSpan( - children: [ - WidgetSpan( - child: Padding( - padding: const EdgeInsets.only( - right: 4.0, - ), - child: AnimatedScale( - duration: - FluffyThemes.animationDuration, - curve: FluffyThemes.animationCurve, - scale: hovered - ? 1.33 - : copied - ? 1.25 - : 1.0, - child: Icon( - copied - ? Icons.check_circle - : Icons.copy, - size: 12, - color: copied ? Colors.green : null, + Row( + spacing: 12, + children: [ + Avatar( + mxContent: avatar, + name: profile?.name ?? roomLink, + size: Avatar.defaultSize * 1.5, + onTap: avatar != null + ? () => showDialog( + context: context, + builder: (_) => MxcImageViewer(avatar), + ) + : null, + ), + Expanded( + child: Column( + crossAxisAlignment: .start, + children: [ + Text( + profile?.name ?? + roomLink ?? + profile?.roomId ?? + ' - ', + maxLines: 1, + overflow: .ellipsis, + style: TextStyle(fontSize: 16), + ), + const SizedBox(height: 8), + if (roomLink != null) + HoverBuilder( + builder: (context, hovered) => StatefulBuilder( + builder: (context, setState) => MouseRegion( + cursor: SystemMouseCursors.click, + child: GestureDetector( + onTap: () { + Clipboard.setData( + ClipboardData(text: roomLink), + ); + setState(() { + copied = true; + }); + }, + child: RichText( + text: TextSpan( + children: [ + WidgetSpan( + child: Padding( + padding: const EdgeInsets.only( + right: 4.0, + ), + child: AnimatedScale( + duration: FluffyThemes + .animationDuration, + curve: FluffyThemes + .animationCurve, + scale: hovered + ? 1.33 + : copied + ? 1.25 + : 1.0, + child: Icon( + copied + ? Icons.check_circle + : Icons.copy, + size: 12, + color: copied + ? Colors.green + : null, + ), + ), + ), + ), + TextSpan(text: roomLink), + ], + style: theme.textTheme.bodyMedium + ?.copyWith(fontSize: 10), ), + textAlign: TextAlign.center, ), ), ), - TextSpan(text: roomLink), - ], - style: theme.textTheme.bodyMedium?.copyWith( - fontSize: 10, ), ), - textAlign: TextAlign.center, + + if (profile?.numJoinedMembers != null) + Text( + L10n.of(context).countParticipants( + profile?.numJoinedMembers ?? 0, + ), + style: const TextStyle(fontSize: 10), + textAlign: TextAlign.center, + ), + ], + ), + ), + ], + ), + if (topic != null && topic.isNotEmpty) + ConstrainedBox( + constraints: BoxConstraints(maxHeight: 200), + child: Scrollbar( + thumbVisibility: true, + trackVisibility: true, + child: SingleChildScrollView( + child: SelectableLinkify( + text: topic, + textScaleFactor: MediaQuery.textScalerOf( + context, + ).scale(1), + textAlign: .start, + options: const LinkifyOptions(humanize: false), + linkStyle: TextStyle( + color: theme.colorScheme.primary, + decoration: TextDecoration.underline, + decorationColor: theme.colorScheme.primary, ), + onOpen: (url) => + UrlLauncher(context, url.url).launchUrl(), ), ), ), ), - Center( - child: Avatar( - mxContent: avatar, - name: profile?.name ?? roomLink, - size: Avatar.defaultSize * 2, - onTap: avatar != null - ? () => showDialog( - context: context, - builder: (_) => MxcImageViewer(avatar), - ) - : null, + + Row( + mainAxisAlignment: .spaceBetween, + spacing: 4, + children: [ + AdaptiveIconTextButton( + label: L10n.of(context).report, + icon: Icons.gavel_outlined, + onTap: () async { + Navigator.of(context).pop(); + final reason = await showTextInputDialog( + context: context, + title: L10n.of(context).whyDoYouWantToReportThis, + okLabel: L10n.of(context).report, + cancelLabel: L10n.of(context).cancel, + hintText: L10n.of(context).reason, + ); + if (reason == null || reason.isEmpty) return; + await showFutureLoadingDialog( + context: context, + future: () => Matrix.of(context).client.reportRoom( + chunk?.roomId ?? roomAlias!, + reason, + ), + ); + }, + ), + AdaptiveIconTextButton( + label: L10n.of(context).copy, + icon: Icons.copy_outlined, + onTap: () => + Clipboard.setData(ClipboardData(text: roomLink!)), + ), + AdaptiveIconTextButton( + label: L10n.of(context).share, + icon: Icons.adaptive.share, + onTap: () => FluffyShare.share( + 'https://matrix.to/#/$roomLink', + context, + ), + ), + ], + ), + AdaptiveDialogInkWell( + onTap: () => _joinRoom(context), + child: Text( + chunk?.joinRule == 'knock' && + Matrix.of( + context, + ).client.getRoomById(chunk!.roomId) == + null + ? L10n.of(context).knock + : chunk?.roomType == 'm.space' + ? L10n.of(context).joinSpace + : L10n.of(context).joinRoom, + style: TextStyle(color: theme.colorScheme.secondary), ), ), - if (profile?.numJoinedMembers != null) - Text( - L10n.of( - context, - ).countParticipants(profile?.numJoinedMembers ?? 0), - style: const TextStyle(fontSize: 10), - textAlign: TextAlign.center, - ), - if (topic != null && topic.isNotEmpty) - SelectableLinkify( - text: topic, - textScaleFactor: MediaQuery.textScalerOf( - context, - ).scale(1), - textAlign: TextAlign.center, - options: const LinkifyOptions(humanize: false), - linkStyle: TextStyle( - color: theme.colorScheme.primary, - decoration: TextDecoration.underline, - decorationColor: theme.colorScheme.primary, - ), - onOpen: (url) => - UrlLauncher(context, url.url).launchUrl(), - ), ], ), ); }, ), ), - actions: [ - AdaptiveDialogAction( - bigButtons: true, - borderRadius: AdaptiveDialogAction.topRadius, - onPressed: () => _joinRoom(context), - child: Text( - chunk?.joinRule == 'knock' && - Matrix.of(context).client.getRoomById(chunk!.roomId) == null - ? L10n.of(context).knock - : chunk?.roomType == 'm.space' - ? L10n.of(context).joinSpace - : L10n.of(context).joinRoom, - ), - ), - AdaptiveDialogAction( - bigButtons: true, - borderRadius: AdaptiveDialogAction.bottomRadius, - onPressed: Navigator.of(context).pop, - child: Text(L10n.of(context).close), - ), - ], ); } } diff --git a/lib/widgets/adaptive_dialogs/user_dialog.dart b/lib/widgets/adaptive_dialogs/user_dialog.dart index a5f65acb2..a6dd97af4 100644 --- a/lib/widgets/adaptive_dialogs/user_dialog.dart +++ b/lib/widgets/adaptive_dialogs/user_dialog.dart @@ -1,4 +1,3 @@ -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -6,11 +5,11 @@ import 'package:flutter_linkify/flutter_linkify.dart'; import 'package:go_router/go_router.dart'; import 'package:matrix/matrix.dart'; -import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/utils/date_time_extension.dart'; import 'package:fluffychat/utils/fluffy_share.dart'; +import 'package:fluffychat/widgets/adaptive_dialogs/adaptive_dialog_action.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/show_text_input_dialog.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/presence_builder.dart'; @@ -20,6 +19,8 @@ import '../hover_builder.dart'; import '../matrix.dart'; import '../mxc_image_viewer.dart'; +// ignore: unused_import + class UserDialog extends StatelessWidget { static Future show({ required BuildContext context, @@ -162,23 +163,35 @@ class UserDialog extends StatelessWidget { ), if (statusMsg != null) - SelectableLinkify( - text: statusMsg, - textScaleFactor: MediaQuery.textScalerOf(context).scale(1), - textAlign: TextAlign.start, - options: const LinkifyOptions(humanize: false), - linkStyle: TextStyle( - color: theme.colorScheme.primary, - decoration: TextDecoration.underline, - decorationColor: theme.colorScheme.primary, + ConstrainedBox( + constraints: BoxConstraints(maxHeight: 200), + child: Scrollbar( + thumbVisibility: true, + trackVisibility: true, + child: SingleChildScrollView( + child: SelectableLinkify( + text: statusMsg, + textScaleFactor: MediaQuery.textScalerOf( + context, + ).scale(1), + textAlign: TextAlign.start, + options: const LinkifyOptions(humanize: false), + linkStyle: TextStyle( + color: theme.colorScheme.primary, + decoration: TextDecoration.underline, + decorationColor: theme.colorScheme.primary, + ), + onOpen: (url) => + UrlLauncher(context, url.url).launchUrl(), + ), + ), ), - onOpen: (url) => UrlLauncher(context, url.url).launchUrl(), ), Row( mainAxisAlignment: .spaceBetween, spacing: 4, children: [ - _AdaptiveIconTextButton( + AdaptiveIconTextButton( label: L10n.of(context).block, icon: Icons.block_outlined, onTap: () { @@ -190,7 +203,7 @@ class UserDialog extends StatelessWidget { ); }, ), - _AdaptiveIconTextButton( + AdaptiveIconTextButton( label: L10n.of(context).report, icon: Icons.gavel_outlined, onTap: () async { @@ -211,14 +224,14 @@ class UserDialog extends StatelessWidget { ); }, ), - _AdaptiveIconTextButton( + AdaptiveIconTextButton( label: L10n.of(context).share, icon: Icons.adaptive.share, onTap: () => FluffyShare.share(profile.userId, context), ), ], ), - _AdaptiveDialogInkWell( + AdaptiveDialogInkWell( onTap: () async { final router = GoRouter.of(context); final roomIdResult = await showFutureLoadingDialog( @@ -242,68 +255,3 @@ class UserDialog extends StatelessWidget { ); } } - -class _AdaptiveDialogInkWell extends StatelessWidget { - final Widget child; - final VoidCallback onTap; - const _AdaptiveDialogInkWell({required this.onTap, required this.child}); - - @override - Widget build(BuildContext context) { - final theme = Theme.of(context); - if ({TargetPlatform.iOS, TargetPlatform.macOS}.contains(theme.platform)) { - return CupertinoButton( - onPressed: onTap, - borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2), - color: theme.colorScheme.surfaceBright, - padding: EdgeInsets.all(8), - child: child, - ); - } - return Material( - color: theme.colorScheme.surfaceBright, - borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2), - child: InkWell( - borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2), - onTap: onTap, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Center(child: child), - ), - ), - ); - } -} - -class _AdaptiveIconTextButton extends StatelessWidget { - final String label; - final IconData icon; - final VoidCallback onTap; - const _AdaptiveIconTextButton({ - required this.label, - required this.icon, - required this.onTap, - }); - - @override - Widget build(BuildContext context) { - final color = Theme.of(context).colorScheme.secondary; - return Expanded( - child: _AdaptiveDialogInkWell( - onTap: onTap, - child: Column( - mainAxisSize: .min, - children: [ - Icon(icon, color: color), - Text( - label, - style: TextStyle(fontSize: 12, color: color), - maxLines: 1, - overflow: .ellipsis, - ), - ], - ), - ), - ); - } -}