Skip to content

Commit b39a6a4

Browse files
Feat/media reply preview (#770)
* fix: media preview spacings * feat: show media in chat input reply preview * feat: show media in message reply box * test: add tests to message reply box
1 parent 9af0cf5 commit b39a6a4

File tree

6 files changed

+587
-104
lines changed

6 files changed

+587
-104
lines changed

lib/ui/chat/widgets/chat_input_reply_preview.dart

Lines changed: 55 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
22
import 'package:flutter_screenutil/flutter_screenutil.dart';
33
import 'package:gap/gap.dart';
44
import 'package:whitenoise/domain/models/message_model.dart';
5+
import 'package:whitenoise/ui/chat/widgets/message_media_tile.dart';
56
import 'package:whitenoise/ui/core/themes/assets.dart';
67
import 'package:whitenoise/ui/core/themes/src/extensions.dart';
78
import 'package:whitenoise/ui/core/ui/wn_image.dart';
@@ -36,49 +37,65 @@ class ChatInputReplyPreview extends StatelessWidget {
3637
),
3738
),
3839
),
39-
child: Column(
40-
crossAxisAlignment: CrossAxisAlignment.stretch,
40+
child: Row(
4141
children: [
42-
Row(
43-
mainAxisAlignment: MainAxisAlignment.spaceBetween,
44-
children: [
45-
Text(
46-
replyingTo?.sender.displayName ??
47-
editingMessage?.sender.displayName ??
48-
'shared.unknownUser'.tr(),
49-
style: TextStyle(
50-
color: context.colors.mutedForeground,
51-
fontSize: 12.sp,
52-
fontWeight: FontWeight.w600,
53-
),
42+
if (replyingTo?.mediaAttachments.isNotEmpty ?? false) ...[
43+
Padding(
44+
padding: EdgeInsets.only(right: 8.w),
45+
child: MessageMediaTile(
46+
mediaFile: replyingTo!.mediaAttachments.first,
47+
size: 32,
5448
),
55-
GestureDetector(
56-
onTap: onCancel,
57-
child: Container(
58-
width: 24.w,
59-
height: 24.w,
60-
alignment: Alignment.center,
61-
child: WnImage(
62-
AssetsPaths.icClose,
63-
width: 16.w,
64-
height: 16.w,
65-
color: context.colors.mutedForeground,
49+
),
50+
],
51+
Expanded(
52+
child: Row(
53+
crossAxisAlignment: CrossAxisAlignment.start,
54+
children: [
55+
Expanded(
56+
child: Column(
57+
crossAxisAlignment: CrossAxisAlignment.stretch,
58+
children: [
59+
Text(
60+
replyingTo?.sender.displayName ??
61+
editingMessage?.sender.displayName ??
62+
'shared.unknownUser'.tr(),
63+
style: TextStyle(
64+
color: context.colors.mutedForeground,
65+
fontSize: 12.sp,
66+
fontWeight: FontWeight.w600,
67+
),
68+
),
69+
Gap(2.h),
70+
Text(
71+
replyingTo?.content ?? editingMessage?.content ?? 'chats.quoteText'.tr(),
72+
style: TextStyle(
73+
color: context.colors.primary,
74+
fontSize: 12.sp,
75+
fontWeight: FontWeight.w600,
76+
),
77+
maxLines: 1,
78+
overflow: TextOverflow.ellipsis,
79+
),
80+
],
6681
),
6782
),
68-
),
69-
],
70-
),
71-
72-
Gap(4.h),
73-
Text(
74-
replyingTo?.content ?? editingMessage?.content ?? 'chats.quoteText'.tr(),
75-
style: TextStyle(
76-
color: context.colors.primary,
77-
fontSize: 12.sp,
78-
fontWeight: FontWeight.w600,
83+
GestureDetector(
84+
onTap: onCancel,
85+
child: Container(
86+
width: 24.w,
87+
height: 24.w,
88+
alignment: Alignment.center,
89+
child: WnImage(
90+
AssetsPaths.icClose,
91+
width: 16.w,
92+
height: 16.w,
93+
color: context.colors.mutedForeground,
94+
),
95+
),
96+
),
97+
],
7998
),
80-
maxLines: 1,
81-
overflow: TextOverflow.ellipsis,
8299
),
83100
],
84101
),

lib/ui/chat/widgets/media_preview.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ class _MediaPreviewState extends State<MediaPreview> {
9090
if (widget.mediaItems.isEmpty) return const SizedBox.shrink();
9191

9292
return Container(
93-
padding: EdgeInsets.symmetric(horizontal: 14.w, vertical: widget.isReply ? 8.h : 16.h),
93+
padding: EdgeInsets.symmetric(horizontal: 6.w, vertical: widget.isReply ? 4.h : 14.h),
9494
child: SizedBox(
9595
height: _imageHeight.h,
9696
child: Stack(
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:flutter_riverpod/flutter_riverpod.dart';
3+
import 'package:flutter_screenutil/flutter_screenutil.dart';
4+
import 'package:gap/gap.dart';
5+
import 'package:whitenoise/config/providers/localization_provider.dart';
6+
import 'package:whitenoise/domain/models/message_model.dart';
7+
import 'package:whitenoise/ui/chat/widgets/message_media_tile.dart';
8+
import 'package:whitenoise/ui/core/themes/src/extensions.dart';
9+
import 'package:whitenoise/utils/message_utils.dart';
10+
11+
class MessageReplyBox extends ConsumerWidget {
12+
const MessageReplyBox({super.key, this.replyingTo, this.onTap});
13+
final MessageModel? replyingTo;
14+
final VoidCallback? onTap;
15+
16+
@override
17+
Widget build(BuildContext context, WidgetRef ref) {
18+
// Watch localization changes
19+
ref.watch(currentLocaleProvider);
20+
if (replyingTo == null) {
21+
return const SizedBox.shrink();
22+
}
23+
return Container(
24+
margin: EdgeInsets.only(bottom: 8.h),
25+
child: Material(
26+
color: context.colors.secondary,
27+
child: InkWell(
28+
onTap: onTap,
29+
child: Container(
30+
padding: EdgeInsets.all(8.w),
31+
decoration: BoxDecoration(
32+
border: Border(
33+
left: BorderSide(
34+
color: context.colors.mutedForeground,
35+
width: 3.0,
36+
),
37+
),
38+
),
39+
child: Row(
40+
children: [
41+
if (replyingTo?.mediaAttachments.isNotEmpty ?? false) ...[
42+
Padding(
43+
padding: EdgeInsets.only(right: 8.w),
44+
child: MessageMediaTile(
45+
mediaFile: replyingTo!.mediaAttachments.first,
46+
size: 32.w,
47+
),
48+
),
49+
],
50+
Expanded(
51+
child: Column(
52+
crossAxisAlignment: CrossAxisAlignment.stretch,
53+
children: [
54+
Text(
55+
MessageUtils.getDisplayName(replyingTo, null),
56+
style: TextStyle(
57+
color: context.colors.mutedForeground,
58+
fontSize: 12.sp,
59+
fontWeight: FontWeight.w600,
60+
),
61+
maxLines: 1,
62+
overflow: TextOverflow.ellipsis,
63+
),
64+
Gap(4.h),
65+
Text(
66+
replyingTo?.content ?? '',
67+
style: TextStyle(
68+
color: context.colors.primary,
69+
fontSize: 12.sp,
70+
fontWeight: FontWeight.w600,
71+
),
72+
maxLines: 1,
73+
overflow: TextOverflow.ellipsis,
74+
),
75+
],
76+
),
77+
),
78+
],
79+
),
80+
),
81+
),
82+
),
83+
);
84+
}
85+
}

lib/ui/chat/widgets/message_widget.dart

Lines changed: 2 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,18 @@
11
import 'dart:io';
22

33
import 'package:flutter/material.dart';
4-
import 'package:flutter_riverpod/flutter_riverpod.dart';
54
import 'package:flutter_screenutil/flutter_screenutil.dart';
65
import 'package:gap/gap.dart';
76

8-
import 'package:whitenoise/config/providers/localization_provider.dart';
97
import 'package:whitenoise/config/states/chat_search_state.dart';
108
import 'package:whitenoise/domain/models/message_model.dart';
119
import 'package:whitenoise/ui/chat/widgets/chat_bubble/bubble.dart';
1210
import 'package:whitenoise/ui/chat/widgets/media_modal.dart';
1311
import 'package:whitenoise/ui/chat/widgets/message_media_grid.dart';
12+
import 'package:whitenoise/ui/chat/widgets/message_reply_box.dart';
1413
import 'package:whitenoise/ui/core/themes/src/extensions.dart';
1514
import 'package:whitenoise/ui/core/ui/wn_avatar.dart';
1615
import 'package:whitenoise/ui/core/ui/wn_image.dart';
17-
import 'package:whitenoise/utils/message_utils.dart';
1816

1917
class MessageWidget extends StatelessWidget {
2018
final MessageModel message;
@@ -131,7 +129,7 @@ class MessageWidget extends StatelessWidget {
131129
),
132130
Gap(4.h),
133131
],
134-
ReplyBox(
132+
MessageReplyBox(
135133
replyingTo: message.replyTo,
136134
onTap:
137135
message.replyTo != null ? () => onReplyTap?.call(message.replyTo!.id) : null,
@@ -480,64 +478,3 @@ class TimeAndStatus extends StatelessWidget {
480478
);
481479
}
482480
}
483-
484-
class ReplyBox extends ConsumerWidget {
485-
const ReplyBox({super.key, this.replyingTo, this.onTap});
486-
final MessageModel? replyingTo;
487-
final VoidCallback? onTap;
488-
489-
@override
490-
Widget build(BuildContext context, WidgetRef ref) {
491-
// Watch localization changes
492-
ref.watch(currentLocaleProvider);
493-
if (replyingTo == null) {
494-
return const SizedBox.shrink();
495-
}
496-
return Container(
497-
margin: EdgeInsets.only(bottom: 8.h),
498-
child: Material(
499-
color: context.colors.secondary,
500-
child: InkWell(
501-
onTap: onTap,
502-
child: Container(
503-
padding: EdgeInsets.all(8.w),
504-
decoration: BoxDecoration(
505-
border: Border(
506-
left: BorderSide(
507-
color: context.colors.mutedForeground,
508-
width: 3.0,
509-
),
510-
),
511-
),
512-
child: Column(
513-
crossAxisAlignment: CrossAxisAlignment.stretch,
514-
children: [
515-
Text(
516-
MessageUtils.getDisplayName(replyingTo, null),
517-
style: TextStyle(
518-
color: context.colors.mutedForeground,
519-
fontSize: 12.sp,
520-
fontWeight: FontWeight.w600,
521-
),
522-
maxLines: 1,
523-
overflow: TextOverflow.ellipsis,
524-
),
525-
Gap(4.h),
526-
Text(
527-
replyingTo?.content ?? '',
528-
style: TextStyle(
529-
color: context.colors.primary,
530-
fontSize: 12.sp,
531-
fontWeight: FontWeight.w600,
532-
),
533-
maxLines: 1,
534-
overflow: TextOverflow.ellipsis,
535-
),
536-
],
537-
),
538-
),
539-
),
540-
),
541-
);
542-
}
543-
}

0 commit comments

Comments
 (0)