mirror of
https://github.com/block/buzz.git
synced 2026-10-27 20:41:57 +00:00
Align outgoing messages and galleries to the right, reclaim the outgoing avatar gutter, and match desktop calendar dividers with balanced spacing. Standardize navigation and sheet icons at 24 points, restore native Edit/Done styling, and align section menus with trailing channel indicators. Validation: clean analysis; 3,513 mobile tests and push-configuration checks passed; iPhone Release build installed and tested by the owner. Includes regression coverage for message placement, date spacing, gallery direction, and section alignment. --------- Signed-off-by: kenny lopez <klopez4212@gmail.com> Co-authored-by: fin <5856f6900e8e0a4e0ea1f4f8d2ee33e4753d2119bc122f94ecafc660b46c0511@buzz.block.builderlab.xyz>
140 lines
4.3 KiB
Dart
140 lines
4.3 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;
|
|
|
|
/// Whether metadata includes the timestamp alongside the author.
|
|
final bool showTimestamp;
|
|
|
|
/// 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;
|
|
|
|
/// Mirrors metadata order without changing text reading direction.
|
|
final bool reversed;
|
|
|
|
/// Positions the metadata within the available message width.
|
|
final MainAxisAlignment alignment;
|
|
|
|
/// 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.alignment = MainAxisAlignment.start,
|
|
this.reversed = false,
|
|
this.showTimestamp = true,
|
|
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;
|
|
|
|
final children = <Widget>[
|
|
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,
|
|
),
|
|
),
|
|
],
|
|
if (showTimestamp) ...[
|
|
const SizedBox(width: Grid.xxs),
|
|
ConstrainedBox(
|
|
constraints: BoxConstraints(maxWidth: metadataMaxWidth),
|
|
child: Text(
|
|
timestamp,
|
|
key: timestampKey,
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
style: resolvedTimestampStyle,
|
|
),
|
|
),
|
|
],
|
|
];
|
|
return Row(
|
|
mainAxisAlignment: alignment,
|
|
crossAxisAlignment: CrossAxisAlignment.center,
|
|
children: reversed ? children.reversed.toList() : children,
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|