feat: cartões de publicação em canais e corrreções de salas arquivadas
- Canais: publicações viram cartões de largura total mantendo todas as funções do balão (reações, resposta, editar, excluir, comentários em thread). Composer de publicação fica restrito a admins no feed; membros comentam via tela de thread. Canais novos usam events_default 0 para permitir comentários e reações. - Ao sair/excluir uma sala, a cópia local é removida do banco imediatamente; salas já deixadas no servidor não apresentam mais erro ao excluir de novo. - Classificação mural/canal carregada direto do sync (room_type em importantStateEvents + postLoad) sem precisar abrir a sala. - Diálogos com raio e padding reduzidos (dialogTheme, text input e ok/cancel). - Ícones do app regenerados (logo_background) e dependência linkify.
This commit is contained in:
@@ -55,6 +55,12 @@ class Message extends StatelessWidget {
|
||||
final bool isCollapsed;
|
||||
final Set<String> bigEmojis;
|
||||
|
||||
/// Renders the message as a full-width publication card (used in canal
|
||||
/// rooms). Keeps every message feature (reply, reactions, edit, delete,
|
||||
/// thread…), but drops the left/right bubble layout and stretches the
|
||||
/// "bubble" over the whole available width.
|
||||
final bool fullWidthCard;
|
||||
|
||||
const Message(
|
||||
this.event, {
|
||||
this.nextEvent,
|
||||
@@ -79,6 +85,7 @@ class Message extends StatelessWidget {
|
||||
this.onExpand,
|
||||
required this.enterThread,
|
||||
this.isCollapsed = false,
|
||||
this.fullWidthCard = false,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@@ -105,7 +112,12 @@ class Message extends StatelessWidget {
|
||||
|
||||
final client = Matrix.of(context).client;
|
||||
final ownMessage = event.senderId == client.userID;
|
||||
final alignment = ownMessage ? Alignment.topRight : Alignment.topLeft;
|
||||
final card = fullWidthCard;
|
||||
final alignment = card
|
||||
? Alignment.topLeft
|
||||
: ownMessage
|
||||
? Alignment.topRight
|
||||
: Alignment.topLeft;
|
||||
|
||||
var color = theme.colorScheme.surfaceContainerHigh;
|
||||
final displayTime =
|
||||
@@ -128,6 +140,10 @@ class Message extends StatelessWidget {
|
||||
previousEvent!.senderId == event.senderId &&
|
||||
previousEvent!.originServerTs.sameEnvironment(event.originServerTs);
|
||||
|
||||
// In card mode the sender name moves inside the card, so show it even for
|
||||
// own messages (otherwise a publication feed would be anonymous).
|
||||
final showSenderName = !nextEventSameSender && (card || !ownMessage);
|
||||
|
||||
final textColor = ownMessage
|
||||
? theme.onBubbleColor
|
||||
: theme.colorScheme.onSurface;
|
||||
@@ -136,21 +152,27 @@ class Message extends StatelessWidget {
|
||||
? theme.colorScheme.onTertiaryContainer
|
||||
: theme.colorScheme.tertiary;
|
||||
|
||||
final rowMainAxisAlignment = ownMessage
|
||||
final rowMainAxisAlignment = card
|
||||
? MainAxisAlignment.start
|
||||
: ownMessage
|
||||
? MainAxisAlignment.end
|
||||
: MainAxisAlignment.start;
|
||||
|
||||
final displayEvent = event.getDisplayEvent(timeline);
|
||||
const hardCorner = Radius.circular(3);
|
||||
const roundedCorner = Radius.circular(AppConfig.borderRadius);
|
||||
final borderRadius = BorderRadius.only(
|
||||
topLeft: !ownMessage ? hardCorner : roundedCorner,
|
||||
topRight: ownMessage && nextEventSameSender ? hardCorner : roundedCorner,
|
||||
bottomLeft: !ownMessage && previousEventSameSender
|
||||
? hardCorner
|
||||
: roundedCorner,
|
||||
bottomRight: ownMessage ? hardCorner : roundedCorner,
|
||||
);
|
||||
final borderRadius = card
|
||||
? BorderRadius.all(roundedCorner)
|
||||
: BorderRadius.only(
|
||||
topLeft: !ownMessage ? hardCorner : roundedCorner,
|
||||
topRight: ownMessage && nextEventSameSender
|
||||
? hardCorner
|
||||
: roundedCorner,
|
||||
bottomLeft: !ownMessage && previousEventSameSender
|
||||
? hardCorner
|
||||
: roundedCorner,
|
||||
bottomRight: ownMessage ? hardCorner : roundedCorner,
|
||||
);
|
||||
const avatarSize = Avatar.defaultSize;
|
||||
final noBubble =
|
||||
({
|
||||
@@ -229,9 +251,9 @@ class Message extends StatelessWidget {
|
||||
: SwipeDirection.startToEnd,
|
||||
onSwipe: (_) => onSwipe(),
|
||||
child: Container(
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: FluffyThemes.maxTimelineWidth,
|
||||
),
|
||||
constraints: card
|
||||
? const BoxConstraints()
|
||||
: const BoxConstraints(maxWidth: FluffyThemes.maxTimelineWidth),
|
||||
padding: EdgeInsets.only(
|
||||
left: 8.0,
|
||||
right: 8.0,
|
||||
@@ -242,7 +264,11 @@ class Message extends StatelessWidget {
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: .min,
|
||||
crossAxisAlignment: ownMessage ? .end : .start,
|
||||
crossAxisAlignment: card
|
||||
? CrossAxisAlignment.stretch
|
||||
: ownMessage
|
||||
? .end
|
||||
: .start,
|
||||
children: <Widget>[
|
||||
Stack(
|
||||
clipBehavior: Clip.none,
|
||||
@@ -275,7 +301,9 @@ class Message extends StatelessWidget {
|
||||
crossAxisAlignment: .start,
|
||||
mainAxisAlignment: rowMainAxisAlignment,
|
||||
children: [
|
||||
if (longPressSelect && !event.redacted)
|
||||
if (card)
|
||||
const SizedBox.shrink()
|
||||
else if (longPressSelect && !event.redacted)
|
||||
SizedBox(
|
||||
height: avatarSize,
|
||||
width: avatarSize,
|
||||
@@ -318,70 +346,76 @@ class Message extends StatelessWidget {
|
||||
crossAxisAlignment: .start,
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 8.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: ownMessage ? .end : .start,
|
||||
children: [
|
||||
if (sender.powerLevel.role !=
|
||||
PowerLevelRole.user &&
|
||||
!nextEventSameSender &&
|
||||
!ownMessage &&
|
||||
!event.room.isDirectChat)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
right: 2.0,
|
||||
if (!card)
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 8.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: card
|
||||
? .start
|
||||
: ownMessage
|
||||
? .end
|
||||
: .start,
|
||||
children: [
|
||||
if (sender.powerLevel.role !=
|
||||
PowerLevelRole.user &&
|
||||
!nextEventSameSender &&
|
||||
!ownMessage &&
|
||||
!event.room.isDirectChat)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
right: 2.0,
|
||||
),
|
||||
child: Icon(
|
||||
sender.powerLevel.role ==
|
||||
PowerLevelRole.moderator
|
||||
? Icons.add_moderator_outlined
|
||||
: Icons.admin_panel_settings,
|
||||
size: 14,
|
||||
color: theme
|
||||
.colorScheme
|
||||
.onPrimaryContainer,
|
||||
),
|
||||
),
|
||||
child: Icon(
|
||||
sender.powerLevel.role ==
|
||||
PowerLevelRole.moderator
|
||||
? Icons.add_moderator_outlined
|
||||
: Icons.admin_panel_settings,
|
||||
size: 14,
|
||||
color: theme
|
||||
.colorScheme
|
||||
.onPrimaryContainer,
|
||||
),
|
||||
),
|
||||
if ((!nextEventSameSender) && !ownMessage)
|
||||
FutureBuilder<User?>(
|
||||
future: event.fetchSenderUser(),
|
||||
builder: (context, snapshot) {
|
||||
final displayname =
|
||||
snapshot.data?.calcDisplayname() ??
|
||||
sender.calcDisplayname();
|
||||
return ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: 200,
|
||||
),
|
||||
child: Text(
|
||||
displayname,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: event.room.isDirectChat
|
||||
? Colors.transparent
|
||||
: (theme.brightness ==
|
||||
Brightness.light
|
||||
? displayname
|
||||
.colorScheme
|
||||
.primary
|
||||
: displayname
|
||||
.colorScheme
|
||||
.primaryContainer),
|
||||
fontSize: 11,
|
||||
shadows: event.room.isDirectChat
|
||||
? null
|
||||
: wallpaperTextShadow,
|
||||
if ((!nextEventSameSender) && !ownMessage)
|
||||
FutureBuilder<User?>(
|
||||
future: event.fetchSenderUser(),
|
||||
builder: (context, snapshot) {
|
||||
final displayname =
|
||||
snapshot.data
|
||||
?.calcDisplayname() ??
|
||||
sender.calcDisplayname();
|
||||
return ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: 200,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
child: Text(
|
||||
displayname,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: event.room.isDirectChat
|
||||
? Colors.transparent
|
||||
: (theme.brightness ==
|
||||
Brightness.light
|
||||
? displayname
|
||||
.colorScheme
|
||||
.primary
|
||||
: displayname
|
||||
.colorScheme
|
||||
.primaryContainer),
|
||||
fontSize: 11,
|
||||
shadows: event.room.isDirectChat
|
||||
? null
|
||||
: wallpaperTextShadow,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Container(
|
||||
alignment: alignment,
|
||||
@@ -438,6 +472,7 @@ class Message extends StatelessWidget {
|
||||
),
|
||||
animateIn: animateIn,
|
||||
child: Container(
|
||||
width: card ? double.infinity : null,
|
||||
decoration: BoxDecoration(
|
||||
color: noBubble
|
||||
? Colors.transparent
|
||||
@@ -458,15 +493,108 @@ class Message extends StatelessWidget {
|
||||
AppConfig.borderRadius,
|
||||
),
|
||||
),
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth:
|
||||
FluffyThemes.columnWidth * 1.5,
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: card
|
||||
? double.infinity
|
||||
: FluffyThemes.columnWidth * 1.5,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: .min,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
if (card && showSenderName)
|
||||
Container(
|
||||
padding:
|
||||
const EdgeInsets.fromLTRB(
|
||||
16,
|
||||
12,
|
||||
16,
|
||||
0,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
if (sender
|
||||
.powerLevel
|
||||
.role !=
|
||||
PowerLevelRole.user)
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.only(
|
||||
right: 2.0,
|
||||
),
|
||||
child: Icon(
|
||||
sender
|
||||
.powerLevel
|
||||
.role ==
|
||||
PowerLevelRole
|
||||
.moderator
|
||||
? Icons
|
||||
.add_moderator_outlined
|
||||
: Icons
|
||||
.admin_panel_settings,
|
||||
size: 14,
|
||||
color: theme
|
||||
.colorScheme
|
||||
.onPrimaryContainer,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: FutureBuilder<User?>(
|
||||
future: event
|
||||
.fetchSenderUser(),
|
||||
builder: (context, snapshot) {
|
||||
final displayname =
|
||||
snapshot.data
|
||||
?.calcDisplayname() ??
|
||||
sender
|
||||
.calcDisplayname();
|
||||
return Text(
|
||||
displayname,
|
||||
style: TextStyle(
|
||||
fontWeight:
|
||||
FontWeight
|
||||
.bold,
|
||||
color:
|
||||
theme.brightness ==
|
||||
Brightness
|
||||
.light
|
||||
? displayname
|
||||
.colorScheme
|
||||
.primary
|
||||
: displayname
|
||||
.colorScheme
|
||||
.primaryContainer,
|
||||
fontSize: 12,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow:
|
||||
TextOverflow
|
||||
.ellipsis,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
if (displayTime &&
|
||||
event
|
||||
.status
|
||||
.isSent) ...[
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
event.originServerTs
|
||||
.localizedTimeOfDay(
|
||||
context,
|
||||
),
|
||||
style: TextStyle(
|
||||
color:
|
||||
eventStateTextColor,
|
||||
fontSize: 11,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
if (event.inReplyToEventId(
|
||||
includingFallback: false,
|
||||
) !=
|
||||
@@ -557,7 +685,9 @@ class Message extends StatelessWidget {
|
||||
child: !hasReactions
|
||||
? const SizedBox.shrink()
|
||||
: Container(
|
||||
alignment: ownMessage
|
||||
alignment: card
|
||||
? Alignment.centerLeft
|
||||
: ownMessage
|
||||
? Alignment.centerRight
|
||||
: Alignment.centerLeft,
|
||||
padding: EdgeInsets.only(
|
||||
@@ -569,7 +699,11 @@ class Message extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: ownMessage ? .end : .start,
|
||||
mainAxisAlignment: card
|
||||
? .start
|
||||
: ownMessage
|
||||
? .end
|
||||
: .start,
|
||||
children: [
|
||||
const SizedBox(width: 8),
|
||||
if (event.status.isSent &&
|
||||
@@ -643,7 +777,9 @@ class Message extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
Align(
|
||||
alignment: ownMessage
|
||||
alignment: card
|
||||
? Alignment.bottomLeft
|
||||
: ownMessage
|
||||
? Alignment.bottomRight
|
||||
: Alignment.bottomLeft,
|
||||
child: AnimatedSize(
|
||||
@@ -861,19 +997,24 @@ class Message extends StatelessWidget {
|
||||
child: threadChildren.isEmpty
|
||||
? const SizedBox.shrink()
|
||||
: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 2.0,
|
||||
bottom: 8.0,
|
||||
left: avatarSize + 8,
|
||||
),
|
||||
padding: const EdgeInsets.only(top: 2.0, bottom: 8.0)
|
||||
.add(
|
||||
EdgeInsets.only(
|
||||
left: card ? 8.0 : avatarSize + 8,
|
||||
),
|
||||
),
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: FluffyThemes.columnWidth * 1.5,
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: card
|
||||
? double.infinity
|
||||
: FluffyThemes.columnWidth * 1.5,
|
||||
),
|
||||
child: TextButton.icon(
|
||||
style: TextButton.styleFrom(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius,
|
||||
),
|
||||
),
|
||||
foregroundColor:
|
||||
theme.colorScheme.onSecondaryContainer,
|
||||
|
||||
Reference in New Issue
Block a user