add reply action

This commit is contained in:
Salvatore Giordano
2020-02-27 18:37:51 +01:00
parent 541cafb49f
commit 15c13a7dc9
6 changed files with 144 additions and 76 deletions
+3 -10
View File
@@ -22,15 +22,6 @@ class MyApp extends StatelessWidget {
return MaterialApp(
home: Container(
child: StreamChat(
streamChatThemeData: StreamChatThemeData(
accentColor: Colors.red,
channelTheme: ChannelTheme(
inputGradient: LinearGradient(colors: [
Colors.red,
Colors.yellow,
]),
),
),
client: client,
child: ChannelListPage(),
),
@@ -104,7 +95,9 @@ class ThreadPage extends StatelessWidget {
parentMessage: parent,
),
),
MessageInput(),
MessageInput(
parentMessage: parent,
),
],
),
);
+7 -4
View File
@@ -8,11 +8,11 @@ class MessageInput extends StatefulWidget {
MessageInput({
Key key,
this.onMessageSent,
this.parent,
this.parentMessage,
}) : super(key: key);
final void Function(Message) onMessageSent;
final Message parent;
final Message parentMessage;
@override
_MessageInputState createState() => _MessageInputState();
@@ -45,7 +45,10 @@ class _MessageInputState extends State<MessageInput> {
decoration: BoxDecoration(
color: StreamChatTheme.of(context).channelTheme.inputBackground,
borderRadius: BorderRadius.circular(10.0),
border: Border.all(color: Colors.black.withOpacity(.2)),
border: Border.all(
color: _typingStarted
? Colors.transparent
: Colors.black.withOpacity(.2)),
),
child: Flex(
direction: Axis.horizontal,
@@ -127,7 +130,7 @@ class _MessageInputState extends State<MessageInput> {
.channel
.sendMessage(
Message(
parentId: widget.parent?.id,
parentId: widget.parentMessage?.id,
text: text,
),
)
+18 -8
View File
@@ -40,7 +40,6 @@ class _MessageListViewState extends State<MessageListView> {
bool _topWasVisible = false;
List<Message> _messages = [];
List<Message> _newMessageList = [];
Function _onThreadTap;
@override
@@ -79,9 +78,10 @@ class _MessageListViewState extends State<MessageListView> {
key: ValueKey<String>(
'PARENT-MESSAGE-${widget.parentMessage.id}'),
previousMessage: null,
message: widget.parentMessage.copyWith(replyCount: 0),
message: widget.parentMessage,
nextMessage: null,
onThreadTap: _onThreadTap,
isParent: true,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 32),
@@ -306,7 +306,7 @@ class _MessageListViewState extends State<MessageListView> {
void _getOnThreadTap() {
if (widget.onThreadTap != null) {
_onThreadTap = (message) {
_onThreadTap = (Message message) {
widget.onThreadTap(
message,
widget.threadBuilder != null
@@ -314,14 +314,24 @@ class _MessageListViewState extends State<MessageListView> {
: null);
};
} else if (widget.threadBuilder != null) {
_onThreadTap = (message) {
_onThreadTap = (Message message) {
Navigator.push(
context,
MaterialPageRoute(builder: (_) {
return StreamChannel(
channel: StreamChannel.of(context).channel,
child: widget.threadBuilder(context, message),
);
return StreamBuilder<Message>(
stream: StreamChannel.of(context)
.channel
.state
.messagesStream
.map((messages) =>
messages.firstWhere((m) => m.id == message.id)),
initialData: message,
builder: (_, snapshot) {
return StreamChannel(
channel: StreamChannel.of(context).channel,
child: widget.threadBuilder(context, snapshot.data),
);
});
}),
);
};
+93 -51
View File
@@ -1,3 +1,5 @@
import 'dart:math';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:chewie/chewie.dart';
import 'package:date_format/date_format.dart';
@@ -21,12 +23,14 @@ class MessageWidget extends StatefulWidget {
@required this.message,
@required this.nextMessage,
this.onThreadTap,
this.isParent = false,
}) : super(key: key);
final Message previousMessage;
final Message message;
final Message nextMessage;
final void Function(Message) onThreadTap;
final bool isParent;
@override
_MessageWidgetState createState() => _MessageWidgetState();
@@ -63,7 +67,7 @@ class _MessageWidgetState extends State<MessageWidget>
isMyMessage ? CrossAxisAlignment.end : CrossAxisAlignment.start,
children: <Widget>[
_buildBubble(context, isMyMessage, isLastUser),
_buildThreadIndicator(context),
_buildThreadIndicator(context, isMyMessage),
isNextUser ? SizedBox() : _buildTimestamp(isMyMessage, alignment),
],
),
@@ -125,10 +129,33 @@ class _MessageWidgetState extends State<MessageWidget>
);
}
Widget _buildThreadIndicator(BuildContext context) {
Widget _buildThreadIndicator(BuildContext context, bool isMyMessage) {
var row = [
Text(
'Replies: ${widget.message.replyCount}',
style:
Theme.of(context).textTheme.subtitle.copyWith(color: Colors.blue),
),
Transform(
transform: Matrix4.rotationY(isMyMessage ? 0 : pi),
alignment: Alignment.center,
child: Icon(
Icons.subdirectory_arrow_left,
color: Colors.black12,
),
),
];
if (!isMyMessage) {
row = row.reversed.toList();
}
return widget.message.replyCount > 0
? GestureDetector(
onTap: () {
if (widget.isParent) {
return;
}
if (widget.onThreadTap != null) {
widget.onThreadTap(widget.message);
}
@@ -136,19 +163,7 @@ class _MessageWidgetState extends State<MessageWidget>
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 2.0),
child: Row(
children: <Widget>[
Text(
'Replies: ${widget.message.replyCount}',
style: Theme.of(context)
.textTheme
.subtitle
.copyWith(color: Colors.blue),
),
Icon(
Icons.subdirectory_arrow_left,
color: Colors.black12,
),
],
children: row,
),
),
)
@@ -332,9 +347,16 @@ class _MessageWidgetState extends State<MessageWidget>
void _showMessageBottomSheet(BuildContext context, bool isMyMessage) {
showModalBottomSheet(
clipBehavior: Clip.hardEdge,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(32),
topRight: Radius.circular(32),
),
),
context: context,
builder: (context) {
final fontSize = 40.0;
final fontSize = 30.0;
final textStyle = TextStyle(
fontSize: fontSize,
);
@@ -342,42 +364,49 @@ class _MessageWidgetState extends State<MessageWidget>
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: reactionToEmoji.keys.map((reactionType) {
final ownReactionIndex = widget.message.ownReactions
.indexWhere((reaction) => reaction.type == reactionType);
return Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
IconButton(
iconSize: fontSize + 10,
icon: Text(
reactionToEmoji[reactionType],
style: textStyle,
Container(
color: Colors.black87,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: reactionToEmoji.keys.map((reactionType) {
final ownReactionIndex = widget.message.ownReactions
.indexWhere(
(reaction) => reaction.type == reactionType);
return Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
IconButton(
iconSize: fontSize + 10,
icon: Text(
reactionToEmoji[reactionType],
style: textStyle,
),
onPressed: () {
if (ownReactionIndex != -1) {
removeReaction(reactionType);
} else {
sendReaction(reactionType);
}
},
),
onPressed: () {
if (ownReactionIndex != -1) {
removeReaction(reactionType);
} else {
sendReaction(reactionType);
}
},
),
ownReactionIndex != -1
? Padding(
padding: const EdgeInsets.only(bottom: 4.0),
child: Text(widget
.message.ownReactions[ownReactionIndex].score
.toString()),
)
: SizedBox(),
],
);
}).toList(),
ownReactionIndex != -1
? Padding(
padding: const EdgeInsets.only(bottom: 4.0),
child: Text(
widget.message.ownReactions[ownReactionIndex]
.score
.toString(),
style: TextStyle(color: Colors.white),
),
)
: SizedBox(),
],
);
}).toList(),
),
),
isMyMessage
? FlatButton(
@@ -399,6 +428,19 @@ class _MessageWidgetState extends State<MessageWidget>
},
)
: SizedBox(),
FlatButton(
child: Padding(
padding: const EdgeInsets.all(28.0),
child: Text(
'Start a thread',
style: Theme.of(context).textTheme.headline,
),
),
onPressed: () {
Navigator.pop(context);
widget.onThreadTap(widget.message);
},
),
],
);
});
+6 -1
View File
@@ -69,10 +69,15 @@ class StreamChannelState extends State<StreamChannel> {
Future<void> getReplies(String parentId) async {
_queryMessageController.add(true);
print('PARENT $parentId');
String firstId;
if (widget.channel.state.threads.containsKey(parentId)) {
firstId = widget.channel.state.threads[parentId].first.id;
final thread = widget.channel.state.threads[parentId];
if (thread != null && thread.isNotEmpty) {
firstId = thread?.first?.id;
}
}
return widget.channel
+17 -2
View File
@@ -51,6 +51,8 @@ class StreamChatState extends State<StreamChat> {
builder: (context) => Theme(
data: Theme.of(context).copyWith(
accentColor: StreamChatTheme.of(context).accentColor,
scaffoldBackgroundColor: theme.primaryColor,
backgroundColor: theme.primaryColor,
),
child: WillPopScope(
onWillPop: () async {
@@ -82,8 +84,21 @@ class StreamChatState extends State<StreamChat> {
final theme = defaultTheme.copyWith(
primaryColor: widget.streamChatThemeData?.primaryColor,
channelTheme: defaultTheme.channelTheme.copyWith(
channelHeaderTheme:
widget.streamChatThemeData?.channelTheme?.channelHeaderTheme,
channelHeaderTheme: defaultTheme.channelTheme.channelHeaderTheme
.copyWith(
color: widget.streamChatThemeData?.channelTheme
?.channelHeaderTheme?.color,
lastMessageAt: widget.streamChatThemeData?.channelTheme
?.channelHeaderTheme?.lastMessageAt,
title: widget.streamChatThemeData?.channelTheme
?.channelHeaderTheme?.title,
avatarTheme:
defaultTheme.channelPreviewTheme.avatarTheme.copyWith(
constraints: widget.streamChatThemeData?.channelTheme
?.channelHeaderTheme?.avatarTheme?.constraints,
borderRadius: widget.streamChatThemeData?.channelTheme
?.channelHeaderTheme?.avatarTheme?.borderRadius,
)),
inputBackground:
widget.streamChatThemeData?.channelTheme?.inputBackground,
messageInputButtonIconTheme: widget