feat(chat): lighter bubbles, my own name on my avatar
Feedback: my bubbles were too dark, and avatars showed only a person glyph, never the profile name. Now both bubbles are light with dark text (mine a tonal green, the peer's white with a hairline) — the side and the per-person avatar colour carry the "who", so nothing is heavy to read. Load my own display name from ProfileStore so my avatar shows my initial too, not just the peer's; each avatar still falls back to a glyph when no name is known.
This commit is contained in:
parent
73e96a3ce5
commit
d481e62ff7
1 changed files with 30 additions and 14 deletions
|
|
@ -11,6 +11,7 @@ import '../domain/chat_timeline.dart';
|
|||
import '../i18n/strings.g.dart';
|
||||
import '../services/message_store.dart';
|
||||
import '../services/profile_cache.dart';
|
||||
import '../services/profile_store.dart';
|
||||
import '../services/social_connection.dart';
|
||||
import '../services/social_service.dart';
|
||||
import '../services/trust_referents.dart';
|
||||
|
|
@ -62,6 +63,7 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||
TrustCubit? _trust;
|
||||
bool _loading = true;
|
||||
String? _peerName;
|
||||
String? _selfName;
|
||||
String? _peerG1;
|
||||
final _input = TextEditingController();
|
||||
|
||||
|
|
@ -88,6 +90,10 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||
// connected / unreachable) → null transports and the screen degrades.
|
||||
final session = await widget.connection.session();
|
||||
final cachedName = await widget.profileCache?.name(widget.peerPubkey);
|
||||
// My own display name, so my avatar shows my initial (not a bare glyph).
|
||||
final selfName = getIt.isRegistered<ProfileStore>()
|
||||
? await getIt<ProfileStore>().name()
|
||||
: '';
|
||||
final referents = await widget.trustReferents?.all() ?? const <String>{};
|
||||
final wotParams = await widget.wotSettings?.params() ?? WotParams.duniter;
|
||||
if (!mounted) return; // shared session is owned by the connection, not us
|
||||
|
|
@ -110,6 +116,7 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||
_messages = messages;
|
||||
_trust = trust;
|
||||
_peerName = cachedName;
|
||||
_selfName = selfName.isEmpty ? null : selfName;
|
||||
_loading = false;
|
||||
});
|
||||
|
||||
|
|
@ -212,6 +219,7 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||
controller: _input,
|
||||
onSend: _send,
|
||||
peerName: _peerName,
|
||||
selfName: _selfName,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
@ -223,11 +231,13 @@ class _ChatBody extends StatelessWidget {
|
|||
required this.controller,
|
||||
required this.onSend,
|
||||
this.peerName,
|
||||
this.selfName,
|
||||
});
|
||||
|
||||
final TextEditingController controller;
|
||||
final Future<void> Function() onSend;
|
||||
final String? peerName;
|
||||
final String? selfName;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
@ -256,7 +266,9 @@ class _ChatBody extends StatelessWidget {
|
|||
child: Column(
|
||||
children: [
|
||||
const _TrustBanner(),
|
||||
Expanded(child: ChatMessageList(peerName: peerName)),
|
||||
Expanded(
|
||||
child: ChatMessageList(peerName: peerName, selfName: selfName),
|
||||
),
|
||||
_Composer(controller: controller, onSend: onSend),
|
||||
],
|
||||
),
|
||||
|
|
@ -269,11 +281,14 @@ class _ChatBody extends StatelessWidget {
|
|||
/// in place instead of landing below the fold. Reads the [MessagesCubit] from
|
||||
/// context.
|
||||
class ChatMessageList extends StatelessWidget {
|
||||
const ChatMessageList({this.peerName, super.key});
|
||||
const ChatMessageList({this.peerName, this.selfName, super.key});
|
||||
|
||||
/// The peer's display name, for their avatar's initial (null → a glyph).
|
||||
final String? peerName;
|
||||
|
||||
/// My own display name, for my avatar's initial (null → a glyph).
|
||||
final String? selfName;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final t = context.t;
|
||||
|
|
@ -302,6 +317,7 @@ class ChatMessageList extends StatelessWidget {
|
|||
message: message,
|
||||
mine: cubit.isMine(message),
|
||||
peerName: peerName,
|
||||
selfName: selfName,
|
||||
selfPubkey: cubit.selfPubkey,
|
||||
),
|
||||
};
|
||||
|
|
@ -417,22 +433,24 @@ class _MessageRow extends StatelessWidget {
|
|||
required this.message,
|
||||
required this.mine,
|
||||
this.peerName,
|
||||
this.selfName,
|
||||
this.selfPubkey,
|
||||
});
|
||||
|
||||
final PrivateMessage message;
|
||||
final bool mine;
|
||||
final String? peerName;
|
||||
final String? selfName;
|
||||
|
||||
/// My own pubkey, so my messages carry my (distinctly coloured) avatar.
|
||||
final String? selfPubkey;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// 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.
|
||||
// Each avatar shows its owner's initial (falling back to a glyph when no
|
||||
// name is known) on a colour derived from their pubkey.
|
||||
final avatar = mine
|
||||
? PeerAvatar(pubkey: selfPubkey ?? message.fromPubkey)
|
||||
? PeerAvatar(pubkey: selfPubkey ?? message.fromPubkey, name: selfName)
|
||||
: PeerAvatar(pubkey: message.fromPubkey, name: peerName);
|
||||
final bubble = Flexible(
|
||||
child: _Bubble(message: message, mine: mine),
|
||||
|
|
@ -468,9 +486,13 @@ class _Bubble extends StatelessWidget {
|
|||
constraints: BoxConstraints(
|
||||
maxWidth: MediaQuery.of(context).size.width * 0.75,
|
||||
),
|
||||
// Soft, light bubbles both ways (dark text): mine a tonal green, the
|
||||
// peer's white with a hairline. Sides + avatar colours carry the
|
||||
// "who", so the bubbles themselves stay light and easy on the eye.
|
||||
decoration: BoxDecoration(
|
||||
color: mine ? seedGreen : seedPrimaryContainer,
|
||||
color: mine ? seedPrimaryContainer : Colors.white,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: mine ? null : Border.all(color: seedOutline),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
|
|
@ -479,10 +501,7 @@ class _Bubble extends StatelessWidget {
|
|||
// Long text can be selected/copied — useful for addresses, links.
|
||||
SelectableText(
|
||||
message.text,
|
||||
style: TextStyle(
|
||||
color: mine ? Colors.white : seedOnSurface,
|
||||
fontSize: 15,
|
||||
),
|
||||
style: const TextStyle(color: seedOnSurface, fontSize: 15),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
// A subtle timestamp, trailing edge (RTL-aware).
|
||||
|
|
@ -490,10 +509,7 @@ class _Bubble extends StatelessWidget {
|
|||
alignment: AlignmentDirectional.centerEnd,
|
||||
child: Text(
|
||||
time,
|
||||
style: TextStyle(
|
||||
color: mine ? Colors.white70 : seedMuted,
|
||||
fontSize: 11,
|
||||
),
|
||||
style: const TextStyle(color: seedMuted, fontSize: 11),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue