feat: add timestamp to P2P chat message bubbles#565
Conversation
- Display local time inside each message bubble in the P2P chat - Timestamp styled inline at bottom-right corner - Applies to all message types: text, image, and file - Bubble width adapts to content size, no unnecessary expansion
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 9 minutes and 50 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
WalkthroughAdded localized time-of-day timestamps to chat and dispute message bubbles; message content widgets are now wrapped in a Column with a conditional timestamp rendered beneath using Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (2)
lib/features/chat/widgets/message_bubble.dart (2)
64-77: Optional: Extract repeated Column wrapper pattern.The same
Columnstructure (withCrossAxisAlignment.end,MainAxisSize.min, content widget + timestamp) is repeated three times for image, file, and text messages. Consider extracting a helper method to reduce duplication.♻️ Example helper extraction
Widget _wrapWithTimestamp(Widget content, BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.end, mainAxisSize: MainAxisSize.min, children: [ content, _buildTimestamp(context), ], ); }Then replace each occurrence with:
child: _wrapWithTimestamp( EncryptedImageMessage(...), context, ),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/features/chat/widgets/message_bubble.dart` around lines 64 - 77, Extract the repeated Column wrapper used around message content and timestamp into a helper like _wrapWithTimestamp(Widget content, BuildContext context) that returns a Column with crossAxisAlignment: CrossAxisAlignment.end and mainAxisSize: MainAxisSize.min and contains the passed content and _buildTimestamp(context); then replace the three repeated Column usages (the ones wrapping EncryptedImageMessage, file message widget, and text message widget) with calls to _wrapWithTimestamp(content, context) to remove duplication and keep calls to chatNotifier getters unchanged.
185-207: Pass locale toDateFormatfor proper i18n support.
DateFormat.Hm()without a locale argument falls back to the system default rather than the app's selected locale. To ensure the time format matches the user's language setting, pass the locale fromBuildContext.♻️ Suggested refactor
- String _formatTime() { + String _formatTime(String locale) { if (message.createdAt == null) return ''; final date = message.createdAt is int ? DateTime.fromMillisecondsSinceEpoch( (message.createdAt as int) * 1000) : message.createdAt as DateTime; - return DateFormat.Hm().format(date.toLocal()); + return DateFormat.Hm(locale).format(date.toLocal()); } - Widget _buildTimestamp() { - final time = _formatTime(); + Widget _buildTimestamp(BuildContext context) { + final locale = Localizations.localeOf(context).toString(); + final time = _formatTime(locale); if (time.isEmpty) return const SizedBox.shrink(); return Padding( padding: const EdgeInsets.only(top: 4), child: Text( time, style: TextStyle( color: AppTheme.cream1.withValues(alpha: 0.6), fontSize: 11, ), ), ); }Then update calls to
_buildTimestamp(context)in thebuildmethod.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/features/chat/widgets/message_bubble.dart` around lines 185 - 207, The timestamp formatting currently uses DateFormat.Hm() without a locale; update _formatTime to accept a BuildContext (or a locale String) and pass the app locale from context into DateFormat.Hm(locale) so time respects i18n; then change _buildTimestamp to take BuildContext and call _formatTime(context) and update all calls to _buildTimestamp(...) accordingly (reference functions: _formatTime, _buildTimestamp) ensuring you import/use the correct locale from the BuildContext.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@lib/features/chat/widgets/message_bubble.dart`:
- Around line 64-77: Extract the repeated Column wrapper used around message
content and timestamp into a helper like _wrapWithTimestamp(Widget content,
BuildContext context) that returns a Column with crossAxisAlignment:
CrossAxisAlignment.end and mainAxisSize: MainAxisSize.min and contains the
passed content and _buildTimestamp(context); then replace the three repeated
Column usages (the ones wrapping EncryptedImageMessage, file message widget, and
text message widget) with calls to _wrapWithTimestamp(content, context) to
remove duplication and keep calls to chatNotifier getters unchanged.
- Around line 185-207: The timestamp formatting currently uses DateFormat.Hm()
without a locale; update _formatTime to accept a BuildContext (or a locale
String) and pass the app locale from context into DateFormat.Hm(locale) so time
respects i18n; then change _buildTimestamp to take BuildContext and call
_formatTime(context) and update all calls to _buildTimestamp(...) accordingly
(reference functions: _formatTime, _buildTimestamp) ensuring you import/use the
correct locale from the BuildContext.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 750f3026-7be6-462c-b5fa-1e6912f1d75c
📒 Files selected for processing (1)
lib/features/chat/widgets/message_bubble.dart
- Replace relative time ago format with HH:mm local time - Unify timestamp style with P2P chat - Add timestamps to multimedia messages (image and file) - Bubble width adapts to content sizefeat: unify dispute chat timestamps with P2P chat style - Replace relative time ago format with HH:mm local time - Unify timestamp style with P2P chat - Add timestamps to multimedia messages (image and file) - Bubble width adapts to content size
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
🧹 Nitpick comments (1)
lib/features/disputes/widgets/dispute_message_bubble.dart (1)
103-116: Consider separate alignment for text content vs timestamp.Using
CrossAxisAlignment.endright-aligns both the message text and the timestamp. While this correctly positions the timestamp at the bottom-right, it also right-aligns the message content, which may look visually odd for multi-line text messages.A more conventional approach would keep text left-aligned while only the timestamp is right-aligned:
💡 Proposed layout refinement
child: Column( - crossAxisAlignment: CrossAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisSize: MainAxisSize.min, children: [ Text( message.content, style: const TextStyle( color: AppTheme.cream1, fontSize: 16, height: 1.4, ), ), - _buildTimestamp(), + Align( + alignment: Alignment.centerRight, + child: _buildTimestamp(), + ), ], ),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/features/disputes/widgets/dispute_message_bubble.dart` around lines 103 - 116, The Column currently uses CrossAxisAlignment.end which right-aligns both the message text and timestamp; change the Column to CrossAxisAlignment.start so the message.content remains left-aligned and keep the timestamp right-aligned by wrapping the timestamp widget returned by _buildTimestamp() in an Align(alignment: Alignment.centerRight) (or place it in a Row with Expanded spacer) so only the timestamp floats to the right while the text stays left.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@lib/features/disputes/widgets/dispute_message_bubble.dart`:
- Around line 103-116: The Column currently uses CrossAxisAlignment.end which
right-aligns both the message text and timestamp; change the Column to
CrossAxisAlignment.start so the message.content remains left-aligned and keep
the timestamp right-aligned by wrapping the timestamp widget returned by
_buildTimestamp() in an Align(alignment: Alignment.centerRight) (or place it in
a Row with Expanded spacer) so only the timestamp floats to the right while the
text stays left.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: fd87ed07-a5cb-4dc7-a85c-a725f186e709
📒 Files selected for processing (1)
lib/features/disputes/widgets/dispute_message_bubble.dart
- Use MediaQuery.alwaysUse24HourFormatOf to read OS time format preference - Show HH:mm (14:30) or h:mm a (2:30 PM) based on device setting - Applied to both P2P and dispute chat message bubbles
There was a problem hiding this comment.
🧹 Nitpick comments (1)
lib/features/chat/widgets/message_bubble.dart (1)
185-193: Dead code:createdAt is intbranch is unreachable.According to
lib/data/models/nostr_event.dart:369-373,createdAtis always constructed as aDateTimeduring deserialization (DateTime.fromMillisecondsSinceEpoch((event['created_at'] as int) * 1000)). Theis inttype check and its corresponding branch will never execute.♻️ Simplify by removing the dead branch
String _formatTime(BuildContext context) { if (message.createdAt == null) return ''; - final date = message.createdAt is int - ? DateTime.fromMillisecondsSinceEpoch( - (message.createdAt as int) * 1000) - : message.createdAt as DateTime; + final date = message.createdAt!; final use24h = MediaQuery.alwaysUse24HourFormatOf(context); return DateFormat(use24h ? 'HH:mm' : 'h:mm a').format(date.toLocal()); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/features/chat/widgets/message_bubble.dart` around lines 185 - 193, The _formatTime method contains a dead branch checking "message.createdAt is int" because createdAt is always a DateTime; remove the int branch and simplify date assignment to cast message.createdAt to DateTime directly (e.g., DateTime date = message.createdAt as DateTime), leaving the MediaQuery.alwaysUse24HourFormatOf(context) logic and DateFormat unchanged; update any null check for message.createdAt to keep early return if needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@lib/features/chat/widgets/message_bubble.dart`:
- Around line 185-193: The _formatTime method contains a dead branch checking
"message.createdAt is int" because createdAt is always a DateTime; remove the
int branch and simplify date assignment to cast message.createdAt to DateTime
directly (e.g., DateTime date = message.createdAt as DateTime), leaving the
MediaQuery.alwaysUse24HourFormatOf(context) logic and DateFormat unchanged;
update any null check for message.createdAt to keep early return if needed.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 78e92e6d-887f-4fd0-8a00-41d0c63ae92f
📒 Files selected for processing (2)
lib/features/chat/widgets/message_bubble.dartlib/features/disputes/widgets/dispute_message_bubble.dart
There was a problem hiding this comment.
Review
Two-file change: adds localized time stamps to message bubbles in P2P chat and dispute chat.
lib/features/chat/widgets/message_bubble.dart
DateFormatwithalwaysUse24HourFormatOf(context)correctly respects the device preferencewithValues(alpha: 0.6)for timestamp opacity — consistent with the designmainAxisSize: MainAxisSize.minprevents unnecessary bubble expansionPadding(top: 4)instead ofSizedBox(height: 6)— more idiomatic- Unused
datetime_extensions_utils.dartimport removed message.createdAtis alwaysDateTimepost-deserialization (nostr_event.dart)
lib/features/disputes/widgets/dispute_message_bubble.dart
message.timestampisDateTimeinDisputeChatmodel —.toLocal()works correctly- Text messages:
CrossAxisAlignment.end+mainAxisSize: MainAxisSize.min— consistent with the bottom-right design goal CrossAxisAlignment.endon media bubbles vs the oldCrossAxisAlignment.start— intentional design choice per the PR objective- Removed
SizedBox(height: 6)and usesPadding(top: 4)— consistent with the other bubble
CodeRabbit nitpicks (non-blocking)
- Column wrapper repeated 3 times — valid observation; extractor helper is optional
- Text alignment on dispute bubbles — right-align on multi-line text is unconventional but intentional per the PR goal
createdAt is intdead code — confirmed unreachable;createdAtis alwaysDateTimeafter deserialization. Can be removed or left as a no-op defensive branch.
LGTM.
fix #562
Summary by CodeRabbit
New Features
Style