feat(chat): sender avatar on both sides, coloured per person

Feedback: avatars only on the left, all the same colour, so it was hard
to tell who said what. In a 1:1 only the peer had an avatar (one colour).
Now each message carries its sender's avatar on their own side — mine
trailing, the peer's leading — each tinted by a deterministic colour from
their pubkey. Two sides, two colours: who-said-what is obvious at a glance.
This commit is contained in:
vjrj 2026-07-11 06:59:24 +02:00
parent 4af90876f4
commit dc40640682
2 changed files with 36 additions and 19 deletions

View file

@ -302,6 +302,7 @@ class ChatMessageList extends StatelessWidget {
message: message,
mine: cubit.isMine(message),
peerName: peerName,
selfPubkey: cubit.selfPubkey,
),
};
},
@ -408,32 +409,44 @@ class _DaySeparator extends StatelessWidget {
}
}
/// One message line: my messages align to the trailing edge with no avatar;
/// the peer's align to the leading edge with their avatar (1:1 convention).
/// One message line. Each sender gets their own coloured avatar on their side
/// mine on the trailing edge, the peer's on the leading edge — so at a glance
/// the two sides are two people, not one column of look-alike bubbles.
class _MessageRow extends StatelessWidget {
const _MessageRow({required this.message, required this.mine, this.peerName});
const _MessageRow({
required this.message,
required this.mine,
this.peerName,
this.selfPubkey,
});
final PrivateMessage message;
final bool mine;
final String? peerName;
/// My own pubkey, so my messages carry my (distinctly coloured) avatar.
final String? selfPubkey;
@override
Widget build(BuildContext context) {
if (mine) {
return Align(
alignment: AlignmentDirectional.centerEnd,
child: _Bubble(message: message, mine: true),
);
}
// My avatar has no name (it's me) → a glyph on my colour; the peer's shows
// their initial on theirs. Different pubkeys different colours.
final avatar = mine
? PeerAvatar(pubkey: selfPubkey ?? message.fromPubkey)
: PeerAvatar(pubkey: message.fromPubkey, name: peerName);
final bubble = Flexible(
child: _Bubble(message: message, mine: mine),
);
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
PeerAvatar(pubkey: message.fromPubkey, name: peerName),
const SizedBox(width: 8),
Flexible(child: _Bubble(message: message, mine: false)),
],
mainAxisAlignment: mine
? MainAxisAlignment.end
: MainAxisAlignment.start,
children: mine
? [bubble, const SizedBox(width: 8), avatar]
: [avatar, const SizedBox(width: 8), bubble],
),
);
}