Files
buzz/mobile/lib/shared/widgets/message_author_meta.dart
69107dc3bf Polish mobile message threads and composer (#5645)
## Summary

- refine mobile message metadata, search spacing, and Activity filter
semantics
- add channel-parity Latest navigation and stable tail following to
threads
- synchronize Android composer/keyboard geometry and keep Latest spacing
stable across IME transitions

## Validation

- `bin/just mobile-check`
- `bin/just mobile-test` (1,276 tests)
- Pixel 10 install/launch and channel/thread keyboard, Latest, tail, and
back-navigation review
- signed iPhone install/launch workflow

## Snapshots

See the review snapshots below.

---------

Signed-off-by: kenny lopez <klopez4212@gmail.com>
Signed-off-by: Kenny Lopez <klopez4212@gmail.com>
Signed-off-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz>
Co-authored-by: Fast Fizz <2df81cb51f05a9d5387ef24d7b9ecb8fcdfcd1c70ffabc67061c9596e1b5b1c4@buzz.block.builderlab.xyz>
Co-authored-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz>
2026-08-15 09:14:14 +01:00

124 lines
3.7 KiB
Dart

import 'package:flutter/material.dart';
import '../theme/theme.dart';
/// A consistent inline author row for message-oriented surfaces.
class MessageAuthorMeta extends StatelessWidget {
/// Primary author name shown at the start of the row.
final String displayName;
/// Optional secondary username, hidden when blank or equal to [displayName].
final String? username;
/// Timestamp label shown after the author metadata.
final String timestamp;
/// Color applied to [displayName].
final Color nameColor;
/// Color applied to the username and [timestamp].
final Color metadataColor;
/// Optional callback invoked when [displayName] is tapped.
final VoidCallback? onAuthorTap;
/// Optional key assigned to the display-name text.
final Key? displayNameKey;
/// Optional key assigned to the username text.
final Key? usernameKey;
/// Optional key assigned to the timestamp text.
final Key? timestampKey;
/// Base text style for [displayName], with [nameColor] applied.
final TextStyle nameStyle;
/// Base text style for secondary metadata, with [metadataColor] applied.
final TextStyle metadataStyle;
/// Base text style for [timestamp], with [metadataColor] applied.
final TextStyle timestampStyle;
/// Creates an inline author row with optional username and tap handling.
const MessageAuthorMeta({
super.key,
required this.displayName,
required this.timestamp,
required this.nameColor,
required this.metadataColor,
this.username,
this.onAuthorTap,
this.displayNameKey,
this.usernameKey,
this.timestampKey,
this.nameStyle = messageUsernameTextStyle,
this.metadataStyle = messageMetadataTextStyle,
this.timestampStyle = messageTimestampTextStyle,
});
@override
Widget build(BuildContext context) {
final normalizedUsername = username?.trim();
final showUsername =
normalizedUsername != null &&
normalizedUsername.isNotEmpty &&
normalizedUsername != displayName.trim();
final resolvedNameStyle = nameStyle.copyWith(color: nameColor);
final resolvedMetadataStyle = metadataStyle.copyWith(color: metadataColor);
final resolvedTimestampStyle = timestampStyle.copyWith(
color: metadataColor,
);
Widget authorName = Text(
displayName,
key: displayNameKey,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: resolvedNameStyle,
);
if (onAuthorTap != null) {
authorName = GestureDetector(onTap: onAuthorTap, child: authorName);
}
return LayoutBuilder(
builder: (context, constraints) {
final metadataMaxWidth = constraints.hasBoundedWidth
? constraints.maxWidth / (showUsername ? 3 : 2)
: double.infinity;
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Flexible(child: authorName),
if (showUsername) ...[
const SizedBox(width: Grid.half),
ConstrainedBox(
constraints: BoxConstraints(maxWidth: metadataMaxWidth),
child: Text(
normalizedUsername,
key: usernameKey,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: resolvedMetadataStyle,
),
),
],
const SizedBox(width: Grid.xxs),
ConstrainedBox(
constraints: BoxConstraints(maxWidth: metadataMaxWidth),
child: Text(
timestamp,
key: timestampKey,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: resolvedTimestampStyle,
),
),
],
);
},
);
}
}