diff --git a/lib/pages/chat_list/chat_list_item.dart b/lib/pages/chat_list/chat_list_item.dart index 6385895..3986101 100644 --- a/lib/pages/chat_list/chat_list_item.dart +++ b/lib/pages/chat_list/chat_list_item.dart @@ -73,10 +73,15 @@ class ChatListItem extends StatelessWidget { final space = this.space; return Padding( - padding: const EdgeInsets.symmetric(horizontal: 8), + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), child: Material( - borderRadius: BorderRadius.circular(AppConfig.borderRadius), - clipBehavior: Clip.hardEdge, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(AppConfig.borderRadius), + side: BorderSide( + color: theme.colorScheme.surfaceContainer, + width: 1.5, + ), + ), clipBehavior: Clip.hardEdge, color: backgroundColor, child: FutureBuilder( future: room.name.isEmpty ? room.loadHeroUsers() : null, diff --git a/lib/pages/chat_list/mural_list_item.dart b/lib/pages/chat_list/mural_list_item.dart index 6b04d62..29c67c2 100644 --- a/lib/pages/chat_list/mural_list_item.dart +++ b/lib/pages/chat_list/mural_list_item.dart @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: AGPL-3.0-or-later -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:matrix/matrix.dart'; import 'package:tatchat/config/app_config.dart'; import 'package:tatchat/l10n/l10n.dart'; @@ -45,7 +45,7 @@ class MuralListItem extends StatelessWidget { final isMuted = room.pushRuleState != PushRuleState.notify; final backgroundColor = activeChat ? theme.colorScheme.secondaryContainer - : theme.colorScheme.surfaceContainerLow; + : theme.colorScheme.surface; final description = this.description; final creatorName = this.creatorName; final followers = room.summary.mJoinedMemberCount ?? 0; @@ -56,7 +56,13 @@ class MuralListItem extends StatelessWidget { return Padding( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), child: Material( - borderRadius: BorderRadius.circular(AppConfig.borderRadius), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(AppConfig.borderRadius), + side: BorderSide( + color: theme.colorScheme.surfaceContainer, + width: 1.5, + ), + ), clipBehavior: Clip.hardEdge, color: backgroundColor, child: InkWell( @@ -80,52 +86,80 @@ class MuralListItem extends StatelessWidget { ), const SizedBox(width: 12), Expanded( - flex: 3, - child: Text( - displayname, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: theme.textTheme.titleMedium?.copyWith( - fontWeight: FontWeight.bold, - ), - ), - ), - if (isMuted) - const Padding( - padding: EdgeInsets.only(left: 4), - child: Icon(Icons.notifications_off_outlined, size: 16), - ), - if (room.isFavourite) - Padding( - padding: const EdgeInsets.only(left: 4), - child: Icon( - Icons.push_pin, - size: 16, - color: theme.colorScheme.primary, - ), - ), - if (creatorName != null && creatorName.isNotEmpty) ...[ - const SizedBox(width: 12), - Flexible( - flex: 1, - child: Text( - '${l10n.by} $creatorName', - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: mutedStyle?.copyWith( - fontStyle: FontStyle.italic, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + children: [ + Expanded( + flex: 3, + child: Text( + displayname, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: theme.textTheme.titleMedium?.copyWith( + fontWeight: FontWeight.bold, + ), + ), + ), + if (isMuted) + const Padding( + padding: EdgeInsets.only(left: 4), + child: Icon( + Icons.notifications_off_outlined, + size: 16, + ), + ), + if (room.isFavourite) + Padding( + padding: const EdgeInsets.only(left: 4), + child: Icon( + Icons.push_pin, + size: 16, + color: theme.colorScheme.primary, + ), + ), + ], ), - ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + if (creatorName != null && + creatorName.isNotEmpty) ...[ + // const SizedBox(width: 12), + Flexible( + flex: 1, + child: Text( + '${l10n.by} $creatorName', + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: mutedStyle?.copyWith( + fontStyle: FontStyle.italic, + ), + ), + ), + ], + const Spacer(), + Row( + children: [ + Icon( + Icons.group_outlined, + size: 16, + color: theme.colorScheme.onSurfaceVariant, + ), + const SizedBox(width: 2), + Text( + l10n.countFollowers(followers), + style: mutedStyle, + ), + ], + ), + ], + ), + ], ), - ], - const SizedBox(width: 8), - Icon( - Icons.group_outlined, - size: 16, - color: theme.colorScheme.onSurfaceVariant, ), - const SizedBox(width: 4), - Text(l10n.countFollowers(followers), style: mutedStyle), ], ), if (description != null && description.isNotEmpty) @@ -133,7 +167,7 @@ class MuralListItem extends StatelessWidget { padding: const EdgeInsets.only(top: 8), child: Text( description, - maxLines: 2, + maxLines: 3, overflow: TextOverflow.ellipsis, style: theme.textTheme.bodySmall?.copyWith( color: theme.colorScheme.onSurfaceVariant, diff --git a/lib/widgets/self_profile_dialog.dart b/lib/widgets/self_profile_dialog.dart index 40eb9c3..ca2745d 100644 --- a/lib/widgets/self_profile_dialog.dart +++ b/lib/widgets/self_profile_dialog.dart @@ -31,10 +31,10 @@ import 'package:tatchat/widgets/qr_code_viewer.dart'; /// Shows the profile of the active account: avatar, display name, status, the /// own QR code, the global user ID and an optional QR code scanner. Future showSelfProfileDialog(BuildContext context) => showAdaptiveDialog( - context: context, - barrierDismissible: true, - builder: (context) => const SelfProfileDialog(), -); + context: context, + barrierDismissible: true, + builder: (context) => const SelfProfileDialog(), + ); class SelfProfileDialog extends StatefulWidget { const SelfProfileDialog({super.key}); @@ -236,120 +236,126 @@ class _SelfProfileDialogState extends State { final userId = client.userID; final l10n = L10n.of(context); _profileFuture ??= client.fetchOwnProfile(); + return AlertDialog.adaptive( + insetPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24), content: SingleChildScrollView( - child: ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 420), - child: Column( - mainAxisSize: .min, - crossAxisAlignment: .stretch, - children: [ - FutureBuilder( - future: _profileFuture, - builder: (context, snapshot) { - final profile = snapshot.data; - final avatar = profile?.avatarUrl; - final displayname = - profile?.displayName ?? userId?.localpart ?? l10n.user; - return Column( - children: [ - Center( - child: Stack( - alignment: .center, - children: [ - Avatar( - mxContent: avatar, - name: displayname, - size: 96, - shapeBorder: const CircleBorder(), - onTap: avatar != null - ? () => showDialog( - context: context, - builder: (_) => MxcImageViewer(avatar), - ) - : null, - ), - Positioned( - right: 0, - bottom: 0, - child: Material( - color: theme.colorScheme.primary, - shape: const CircleBorder(), - clipBehavior: Clip.antiAlias, - child: InkWell( - onTap: _editAvatar, - child: SizedBox( - width: 30, - height: 30, - child: Icon( - Icons.edit_outlined, - size: 16, - color: theme.colorScheme.onPrimary, + child: SizedBox( + width: MediaQuery.of(context).size.width > 720 ? 420 : MediaQuery.of(context).size.width, + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 800), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + FutureBuilder( + future: _profileFuture, + builder: (context, snapshot) { + final profile = snapshot.data; + final avatar = profile?.avatarUrl; + final displayname = + profile?.displayName ?? userId?.localpart ?? l10n.user; + return Column( + children: [ + Center( + child: Stack( + alignment: Alignment.center, + children: [ + Avatar( + mxContent: avatar, + name: displayname, + size: 96, + shapeBorder: const CircleBorder(), + onTap: avatar != null + ? () => showDialog( + context: context, + builder: (_) => + MxcImageViewer(avatar), + ) + : null, + ), + Positioned( + right: 0, + bottom: 0, + child: Material( + color: theme.colorScheme.primary, + shape: const CircleBorder(), + clipBehavior: Clip.antiAlias, + child: InkWell( + onTap: _editAvatar, + child: SizedBox( + width: 30, + height: 30, + child: Icon( + Icons.camera_alt_outlined, + size: 18, + color: theme.colorScheme.onPrimary, + ), ), ), ), ), - ), - ], + ], + ), + ), + const SizedBox(height: 16), + _EditableLine( + text: displayname, + style: theme.textTheme.titleLarge?.copyWith( + fontWeight: FontWeight.bold, + ), + tooltip: l10n.editDisplayname, + onEdit: _editDisplayName, + ), + const SizedBox(height: 0), + _EditableLine( + text: _statusMsg?.trim().isNotEmpty == true + ? _statusMsg! + : l10n.setStatus, + style: _statusMsg?.trim().isNotEmpty == true + ? theme.textTheme.bodyMedium + : theme.textTheme.bodySmall?.copyWith( + color: theme.colorScheme.onSurfaceVariant, + fontStyle: FontStyle.italic, + ), + tooltip: l10n.setStatus, + onEdit: _editStatus, + ), + ], + ); + }, + ), + const SizedBox(height: 24), + if (userId != null) ...[ + Center( + child: Material( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + AppConfig.borderRadius, + ), + side: BorderSide( + width: 2, + color: theme.colorScheme.primary, ), ), - const SizedBox(height: 16), - _EditableLine( - text: displayname, - style: theme.textTheme.titleLarge?.copyWith( - fontWeight: FontWeight.bold, + color: Colors.transparent, + clipBehavior: Clip.hardEdge, + child: InkWell( + borderRadius: BorderRadius.circular( + AppConfig.borderRadius, ), - tooltip: l10n.editDisplayname, - onEdit: _editDisplayName, - ), - const SizedBox(height: 8), - _EditableLine( - text: _statusMsg?.trim().isNotEmpty == true - ? _statusMsg! - : l10n.setStatus, - style: _statusMsg?.trim().isNotEmpty == true - ? theme.textTheme.bodyMedium - : theme.textTheme.bodySmall?.copyWith( - color: theme.colorScheme.onSurfaceVariant, - fontStyle: FontStyle.italic, - ), - tooltip: l10n.setStatus, - onEdit: _editStatus, - ), - ], - ); - }, - ), - const SizedBox(height: 24), - if (userId != null) ...[ - Center( - child: Material( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular( - AppConfig.borderRadius, - ), - side: BorderSide( - width: 2, - color: theme.colorScheme.primary, - ), - ), - color: Colors.transparent, - clipBehavior: Clip.hardEdge, - child: InkWell( - borderRadius: BorderRadius.circular( - AppConfig.borderRadius, - ), - onTap: () => showQrCodeViewer(context, userId), - child: Padding( - padding: const EdgeInsets.all(16), - child: ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 160), - child: PrettyQrView.data( - data: 'https://matrix.to/#/$userId', - decoration: PrettyQrDecoration( - shape: PrettyQrSmoothSymbol( - roundFactor: 1, - color: theme.colorScheme.primary, + onTap: () => showQrCodeViewer(context, userId), + child: Padding( + padding: const EdgeInsets.all(16), + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 160), + child: PrettyQrView.data( + data: 'https://matrix.to/#/$userId', + decoration: PrettyQrDecoration( + shape: PrettyQrSmoothSymbol( + roundFactor: 1, + color: theme.colorScheme.primary, + ), ), ), ), @@ -357,72 +363,72 @@ class _SelfProfileDialogState extends State { ), ), ), - ), - const SizedBox(height: 12), - Center( - child: Material( - color: theme.colorScheme.surfaceContainerHigh, - borderRadius: BorderRadius.circular(99), - child: InkWell( + const SizedBox(height: 12), + Center( + child: Material( + color: theme.colorScheme.surfaceContainerHigh, borderRadius: BorderRadius.circular(99), - onTap: _copyUserId, - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 12, - vertical: 8, - ), - child: Row( - mainAxisSize: .min, - children: [ - Flexible( - child: Text( - userId, - maxLines: 1, - overflow: .ellipsis, - style: theme.textTheme.bodySmall?.copyWith( - fontFamily: 'RobotoMono', - color: theme.colorScheme.onSurfaceVariant, + child: InkWell( + borderRadius: BorderRadius.circular(99), + onTap: _copyUserId, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 8, + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Flexible( + child: Text( + userId, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: theme.textTheme.bodySmall?.copyWith( + fontFamily: 'RobotoMono', + color: theme.colorScheme.onSurfaceVariant, + ), ), ), - ), - const SizedBox(width: 8), - Icon( - _copied - ? Icons.check_circle - : Icons.copy_outlined, - size: 14, - color: _copied - ? Colors.green - : theme.colorScheme.onSurfaceVariant, - ), - ], + const SizedBox(width: 8), + Icon( + _copied + ? Icons.check_circle + : Icons.copy_outlined, + size: 14, + color: _copied + ? Colors.green + : theme.colorScheme.onSurfaceVariant, + ), + ], + ), ), ), ), ), - ), - ], - const Divider(height: 32), - if (PlatformInfos.isMobile) + ], + const Divider(height: 32), + if (PlatformInfos.isMobile) + ListTile( + contentPadding: EdgeInsets.zero, + dense: true, + leading: CircleAvatar( + backgroundColor: theme.colorScheme.primaryContainer, + foregroundColor: theme.colorScheme.onPrimaryContainer, + child: const Icon(Icons.qr_code_scanner_outlined), + ), + title: Text(l10n.scanQrCode), + onTap: _openQrScanner, + ), ListTile( contentPadding: EdgeInsets.zero, dense: true, - leading: CircleAvatar( - backgroundColor: theme.colorScheme.primaryContainer, - foregroundColor: theme.colorScheme.onPrimaryContainer, - child: const Icon(Icons.qr_code_scanner_outlined), - ), - title: Text(l10n.scanQrCode), - onTap: _openQrScanner, + leading: const Icon(Icons.settings_outlined), + title: Text(l10n.settings), + onTap: _openSettingsAction, ), - ListTile( - contentPadding: EdgeInsets.zero, - dense: true, - leading: const Icon(Icons.settings_outlined), - title: Text(l10n.settings), - onTap: _openSettingsAction, - ), - ], + ], + ), ), ), ), @@ -448,14 +454,14 @@ class _EditableLine extends StatelessWidget { @override Widget build(BuildContext context) { return Row( - mainAxisAlignment: .center, + mainAxisAlignment: MainAxisAlignment.center, children: [ Flexible( child: Text( text, maxLines: 3, - overflow: .ellipsis, - textAlign: .center, + overflow: TextOverflow.ellipsis, + textAlign: TextAlign.center, style: style, ), ), @@ -473,4 +479,4 @@ class _EditableLine extends StatelessWidget { } } -enum _AvatarAction { camera, file, remove } +enum _AvatarAction { camera, file, remove } \ No newline at end of file