fix: tema correto na lista de murais, borda nos itens de chat e ajustes no perfil

This commit is contained in:
2026-09-23 12:14:18 -03:00
parent 48a50744de
commit c0463e51f9
3 changed files with 263 additions and 218 deletions

View File

@@ -73,10 +73,15 @@ class ChatListItem extends StatelessWidget {
final space = this.space; final space = this.space;
return Padding( return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8), padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: Material( child: Material(
borderRadius: BorderRadius.circular(AppConfig.borderRadius), shape: RoundedRectangleBorder(
clipBehavior: Clip.hardEdge, borderRadius: BorderRadius.circular(AppConfig.borderRadius),
side: BorderSide(
color: theme.colorScheme.surfaceContainer,
width: 1.5,
),
), clipBehavior: Clip.hardEdge,
color: backgroundColor, color: backgroundColor,
child: FutureBuilder( child: FutureBuilder(
future: room.name.isEmpty ? room.loadHeroUsers() : null, future: room.name.isEmpty ? room.loadHeroUsers() : null,

View File

@@ -2,7 +2,7 @@
// //
// SPDX-License-Identifier: AGPL-3.0-or-later // 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:matrix/matrix.dart';
import 'package:tatchat/config/app_config.dart'; import 'package:tatchat/config/app_config.dart';
import 'package:tatchat/l10n/l10n.dart'; import 'package:tatchat/l10n/l10n.dart';
@@ -45,7 +45,7 @@ class MuralListItem extends StatelessWidget {
final isMuted = room.pushRuleState != PushRuleState.notify; final isMuted = room.pushRuleState != PushRuleState.notify;
final backgroundColor = activeChat final backgroundColor = activeChat
? theme.colorScheme.secondaryContainer ? theme.colorScheme.secondaryContainer
: theme.colorScheme.surfaceContainerLow; : theme.colorScheme.surface;
final description = this.description; final description = this.description;
final creatorName = this.creatorName; final creatorName = this.creatorName;
final followers = room.summary.mJoinedMemberCount ?? 0; final followers = room.summary.mJoinedMemberCount ?? 0;
@@ -56,7 +56,13 @@ class MuralListItem extends StatelessWidget {
return Padding( return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: Material( 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, clipBehavior: Clip.hardEdge,
color: backgroundColor, color: backgroundColor,
child: InkWell( child: InkWell(
@@ -80,52 +86,80 @@ class MuralListItem extends StatelessWidget {
), ),
const SizedBox(width: 12), const SizedBox(width: 12),
Expanded( Expanded(
flex: 3, child: Column(
child: Text( crossAxisAlignment: CrossAxisAlignment.start,
displayname, mainAxisSize: MainAxisSize.min,
maxLines: 1, children: [
overflow: TextOverflow.ellipsis, Row(
style: theme.textTheme.titleMedium?.copyWith( children: [
fontWeight: FontWeight.bold, Expanded(
), flex: 3,
), child: Text(
), displayname,
if (isMuted) maxLines: 1,
const Padding( overflow: TextOverflow.ellipsis,
padding: EdgeInsets.only(left: 4), style: theme.textTheme.titleMedium?.copyWith(
child: Icon(Icons.notifications_off_outlined, size: 16), fontWeight: FontWeight.bold,
), ),
if (room.isFavourite) ),
Padding( ),
padding: const EdgeInsets.only(left: 4), if (isMuted)
child: Icon( const Padding(
Icons.push_pin, padding: EdgeInsets.only(left: 4),
size: 16, child: Icon(
color: theme.colorScheme.primary, Icons.notifications_off_outlined,
), size: 16,
), ),
if (creatorName != null && creatorName.isNotEmpty) ...[ ),
const SizedBox(width: 12), if (room.isFavourite)
Flexible( Padding(
flex: 1, padding: const EdgeInsets.only(left: 4),
child: Text( child: Icon(
'${l10n.by} $creatorName', Icons.push_pin,
maxLines: 1, size: 16,
overflow: TextOverflow.ellipsis, color: theme.colorScheme.primary,
style: mutedStyle?.copyWith( ),
fontStyle: FontStyle.italic, ),
],
), ),
), 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) if (description != null && description.isNotEmpty)
@@ -133,7 +167,7 @@ class MuralListItem extends StatelessWidget {
padding: const EdgeInsets.only(top: 8), padding: const EdgeInsets.only(top: 8),
child: Text( child: Text(
description, description,
maxLines: 2, maxLines: 3,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: theme.textTheme.bodySmall?.copyWith( style: theme.textTheme.bodySmall?.copyWith(
color: theme.colorScheme.onSurfaceVariant, color: theme.colorScheme.onSurfaceVariant,

View File

@@ -31,10 +31,10 @@ import 'package:tatchat/widgets/qr_code_viewer.dart';
/// Shows the profile of the active account: avatar, display name, status, the /// 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. /// own QR code, the global user ID and an optional QR code scanner.
Future<void> showSelfProfileDialog(BuildContext context) => showAdaptiveDialog( Future<void> showSelfProfileDialog(BuildContext context) => showAdaptiveDialog(
context: context, context: context,
barrierDismissible: true, barrierDismissible: true,
builder: (context) => const SelfProfileDialog(), builder: (context) => const SelfProfileDialog(),
); );
class SelfProfileDialog extends StatefulWidget { class SelfProfileDialog extends StatefulWidget {
const SelfProfileDialog({super.key}); const SelfProfileDialog({super.key});
@@ -236,120 +236,126 @@ class _SelfProfileDialogState extends State<SelfProfileDialog> {
final userId = client.userID; final userId = client.userID;
final l10n = L10n.of(context); final l10n = L10n.of(context);
_profileFuture ??= client.fetchOwnProfile(); _profileFuture ??= client.fetchOwnProfile();
return AlertDialog.adaptive( return AlertDialog.adaptive(
insetPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
content: SingleChildScrollView( content: SingleChildScrollView(
child: ConstrainedBox( child: SizedBox(
constraints: const BoxConstraints(maxWidth: 420), width: MediaQuery.of(context).size.width > 720 ? 420 : MediaQuery.of(context).size.width,
child: Column( child: ConstrainedBox(
mainAxisSize: .min, constraints: const BoxConstraints(maxWidth: 800),
crossAxisAlignment: .stretch, child: Column(
children: [ mainAxisSize: MainAxisSize.min,
FutureBuilder<Profile>( crossAxisAlignment: CrossAxisAlignment.stretch,
future: _profileFuture, children: [
builder: (context, snapshot) { FutureBuilder<Profile>(
final profile = snapshot.data; future: _profileFuture,
final avatar = profile?.avatarUrl; builder: (context, snapshot) {
final displayname = final profile = snapshot.data;
profile?.displayName ?? userId?.localpart ?? l10n.user; final avatar = profile?.avatarUrl;
return Column( final displayname =
children: [ profile?.displayName ?? userId?.localpart ?? l10n.user;
Center( return Column(
child: Stack( children: [
alignment: .center, Center(
children: [ child: Stack(
Avatar( alignment: Alignment.center,
mxContent: avatar, children: [
name: displayname, Avatar(
size: 96, mxContent: avatar,
shapeBorder: const CircleBorder(), name: displayname,
onTap: avatar != null size: 96,
? () => showDialog( shapeBorder: const CircleBorder(),
context: context, onTap: avatar != null
builder: (_) => MxcImageViewer(avatar), ? () => showDialog(
) context: context,
: null, builder: (_) =>
), MxcImageViewer(avatar),
Positioned( )
right: 0, : null,
bottom: 0, ),
child: Material( Positioned(
color: theme.colorScheme.primary, right: 0,
shape: const CircleBorder(), bottom: 0,
clipBehavior: Clip.antiAlias, child: Material(
child: InkWell( color: theme.colorScheme.primary,
onTap: _editAvatar, shape: const CircleBorder(),
child: SizedBox( clipBehavior: Clip.antiAlias,
width: 30, child: InkWell(
height: 30, onTap: _editAvatar,
child: Icon( child: SizedBox(
Icons.edit_outlined, width: 30,
size: 16, height: 30,
color: theme.colorScheme.onPrimary, 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), color: Colors.transparent,
_EditableLine( clipBehavior: Clip.hardEdge,
text: displayname, child: InkWell(
style: theme.textTheme.titleLarge?.copyWith( borderRadius: BorderRadius.circular(
fontWeight: FontWeight.bold, AppConfig.borderRadius,
), ),
tooltip: l10n.editDisplayname, onTap: () => showQrCodeViewer(context, userId),
onEdit: _editDisplayName, child: Padding(
), padding: const EdgeInsets.all(16),
const SizedBox(height: 8), child: ConstrainedBox(
_EditableLine( constraints: const BoxConstraints(maxWidth: 160),
text: _statusMsg?.trim().isNotEmpty == true child: PrettyQrView.data(
? _statusMsg! data: 'https://matrix.to/#/$userId',
: l10n.setStatus, decoration: PrettyQrDecoration(
style: _statusMsg?.trim().isNotEmpty == true shape: PrettyQrSmoothSymbol(
? theme.textTheme.bodyMedium roundFactor: 1,
: theme.textTheme.bodySmall?.copyWith( color: theme.colorScheme.primary,
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,
), ),
), ),
), ),
@@ -357,72 +363,72 @@ class _SelfProfileDialogState extends State<SelfProfileDialog> {
), ),
), ),
), ),
), const SizedBox(height: 12),
const SizedBox(height: 12), Center(
Center( child: Material(
child: Material( color: theme.colorScheme.surfaceContainerHigh,
color: theme.colorScheme.surfaceContainerHigh,
borderRadius: BorderRadius.circular(99),
child: InkWell(
borderRadius: BorderRadius.circular(99), borderRadius: BorderRadius.circular(99),
onTap: _copyUserId, child: InkWell(
child: Padding( borderRadius: BorderRadius.circular(99),
padding: const EdgeInsets.symmetric( onTap: _copyUserId,
horizontal: 12, child: Padding(
vertical: 8, padding: const EdgeInsets.symmetric(
), horizontal: 12,
child: Row( vertical: 8,
mainAxisSize: .min, ),
children: [ child: Row(
Flexible( mainAxisSize: MainAxisSize.min,
child: Text( children: [
userId, Flexible(
maxLines: 1, child: Text(
overflow: .ellipsis, userId,
style: theme.textTheme.bodySmall?.copyWith( maxLines: 1,
fontFamily: 'RobotoMono', overflow: TextOverflow.ellipsis,
color: theme.colorScheme.onSurfaceVariant, style: theme.textTheme.bodySmall?.copyWith(
fontFamily: 'RobotoMono',
color: theme.colorScheme.onSurfaceVariant,
),
), ),
), ),
), const SizedBox(width: 8),
const SizedBox(width: 8), Icon(
Icon( _copied
_copied ? Icons.check_circle
? Icons.check_circle : Icons.copy_outlined,
: Icons.copy_outlined, size: 14,
size: 14, color: _copied
color: _copied ? Colors.green
? Colors.green : theme.colorScheme.onSurfaceVariant,
: theme.colorScheme.onSurfaceVariant, ),
), ],
], ),
), ),
), ),
), ),
), ),
), ],
], const Divider(height: 32),
const Divider(height: 32), if (PlatformInfos.isMobile)
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( ListTile(
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
dense: true, dense: true,
leading: CircleAvatar( leading: const Icon(Icons.settings_outlined),
backgroundColor: theme.colorScheme.primaryContainer, title: Text(l10n.settings),
foregroundColor: theme.colorScheme.onPrimaryContainer, onTap: _openSettingsAction,
child: const Icon(Icons.qr_code_scanner_outlined),
),
title: Text(l10n.scanQrCode),
onTap: _openQrScanner,
), ),
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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Row( return Row(
mainAxisAlignment: .center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Flexible( Flexible(
child: Text( child: Text(
text, text,
maxLines: 3, maxLines: 3,
overflow: .ellipsis, overflow: TextOverflow.ellipsis,
textAlign: .center, textAlign: TextAlign.center,
style: style, style: style,
), ),
), ),
@@ -473,4 +479,4 @@ class _EditableLine extends StatelessWidget {
} }
} }
enum _AvatarAction { camera, file, remove } enum _AvatarAction { camera, file, remove }