Merge branch 'feature/new-ui' into feature/reactions

This commit is contained in:
Salvatore Giordano
2020-10-21 10:19:11 +02:00
6 changed files with 193 additions and 120 deletions
+9 -9
View File
@@ -87,7 +87,7 @@ PODS:
- FMDB (2.7.5): - FMDB (2.7.5):
- FMDB/standard (= 2.7.5) - FMDB/standard (= 2.7.5)
- FMDB/standard (2.7.5) - FMDB/standard (2.7.5)
- GoogleDataTransport (7.4.0): - GoogleDataTransport (7.5.1):
- nanopb (~> 1.30906.0) - nanopb (~> 1.30906.0)
- GoogleUtilities/AppDelegateSwizzler (6.7.2): - GoogleUtilities/AppDelegateSwizzler (6.7.2):
- GoogleUtilities/Environment - GoogleUtilities/Environment
@@ -115,11 +115,11 @@ PODS:
- nanopb/encode (1.30906.0) - nanopb/encode (1.30906.0)
- path_provider (0.0.1): - path_provider (0.0.1):
- Flutter - Flutter
- PromisesObjC (1.2.10) - PromisesObjC (1.2.11)
- Protobuf (3.13.0) - Protobuf (3.13.0)
- SDWebImage (5.9.2): - SDWebImage (5.9.3):
- SDWebImage/Core (= 5.9.2) - SDWebImage/Core (= 5.9.3)
- SDWebImage/Core (5.9.2) - SDWebImage/Core (5.9.3)
- shared_preferences (0.0.1): - shared_preferences (0.0.1):
- Flutter - Flutter
- sqflite (0.0.1): - sqflite (0.0.1):
@@ -247,14 +247,14 @@ SPEC CHECKSUMS:
flutter_keyboard_visibility: 0339d06371254c3eb25eeb90ba8d17dca8f9c069 flutter_keyboard_visibility: 0339d06371254c3eb25eeb90ba8d17dca8f9c069
flutter_local_notifications: 0c0b1ae97e741e1521e4c1629a459d04b9aec743 flutter_local_notifications: 0c0b1ae97e741e1521e4c1629a459d04b9aec743
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
GoogleDataTransport: b7f406340a291370045a270c599e53c6fa6ec20f GoogleDataTransport: f56af7caa4ed338dc8e138a5d7c5973e66440833
GoogleUtilities: 7f2f5a07f888cdb145101d6042bc4422f57e70b3 GoogleUtilities: 7f2f5a07f888cdb145101d6042bc4422f57e70b3
image_picker: 9c3312491f862b28d21ecd8fdf0ee14e601b3f09 image_picker: 9c3312491f862b28d21ecd8fdf0ee14e601b3f09
nanopb: 59317e09cf1f1a0af72f12af412d54edf52603fc nanopb: 59317e09cf1f1a0af72f12af412d54edf52603fc
path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c
PromisesObjC: b14b1c6b68e306650688599de8a45e49fae81151 PromisesObjC: 8c196f5a328c2cba3e74624585467a557dcb482f
Protobuf: 3dac39b34a08151c6d949560efe3f86134a3f748 Protobuf: 3dac39b34a08151c6d949560efe3f86134a3f748
SDWebImage: 0b42b8719ab0c5257177d5894306e8a336b21cbb SDWebImage: a31ee8e90a97303529e03fb0c333eae0eacb88e9
shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d
sqflite: 4001a31ff81d210346b500c55b17f4d6c7589dd0 sqflite: 4001a31ff81d210346b500c55b17f4d6c7589dd0
sqlite3: 8f7d2078ae27778699a622a94b853285793422a2 sqlite3: 8f7d2078ae27778699a622a94b853285793422a2
@@ -268,4 +268,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: eb001256612a59f8f9e4d083ad8b9671e69dd184 PODFILE CHECKSUM: eb001256612a59f8f9e4d083ad8b9671e69dd184
COCOAPODS: 1.8.4 COCOAPODS: 1.10.0.rc.1
+4 -1
View File
@@ -79,7 +79,10 @@ class MessageActionsModal extends StatelessWidget {
child: MessageWidget( child: MessageWidget(
key: Key('MessageWidget'), key: Key('MessageWidget'),
reverse: reverse, reverse: reverse,
message: message, message: message.text.length > 200
? message.copyWith(
text: '${message.text.substring(0, 200)}...')
: message,
messageTheme: messageTheme, messageTheme: messageTheme,
showReactions: false, showReactions: false,
showUsername: false, showUsername: false,
+175 -107
View File
@@ -97,6 +97,7 @@ class MessageListView extends StatefulWidget {
/// Instantiate a new MessageListView /// Instantiate a new MessageListView
MessageListView({ MessageListView({
Key key, Key key,
this.showScrollToBottom = true,
this.messageBuilder, this.messageBuilder,
this.parentMessageBuilder, this.parentMessageBuilder,
this.parentMessage, this.parentMessage,
@@ -120,6 +121,9 @@ class MessageListView extends StatefulWidget {
/// By default it calls [Navigator.push] using the widget built using [threadBuilder] /// By default it calls [Navigator.push] using the widget built using [threadBuilder]
final ThreadTapCallback onThreadTap; final ThreadTapCallback onThreadTap;
/// If true will show a scroll to bottom message when there are new messages and the scroll offset is not zero
final bool showScrollToBottom;
/// Parent message in case of a thread /// Parent message in case of a thread
final Message parentMessage; final Message parentMessage;
@@ -144,132 +148,193 @@ class _MessageListViewState extends State<MessageListView> {
List<Message> _messages = []; List<Message> _messages = [];
List<Message> _newMessageList = []; List<Message> _newMessageList = [];
Function _onThreadTap; Function _onThreadTap;
bool _showScrollToBottom = false;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final streamChannel = StreamChannel.of(context); final streamChannel = StreamChannel.of(context);
/// TODO: find a better solution when (https://github.com/flutter/flutter/issues/21023) is fixed /// TODO: find a better solution when (https://github.com/flutter/flutter/issues/21023) is fixed
return NotificationListener<ScrollNotification>( return Stack(
onNotification: (_) { children: [
if (_scrollController.offset < 150 && _newMessageList.isNotEmpty) { NotificationListener<ScrollNotification>(
setState(() { onNotification: (_) {
_messages.insertAll(0, _newMessageList); if (_scrollController.offset < 150 && _newMessageList.isNotEmpty) {
_newMessageList.clear(); setState(() {
}); _messages.insertAll(0, _newMessageList);
} _newMessageList.clear();
return true; });
}, }
child: ListView.custom( return true;
key: Key('messageListView'), },
physics: widget.scrollPhysics, child: ListView.custom(
keyboardDismissBehavior: widget.keyboardDismissBehavior, key: Key('messageListView'),
controller: _scrollController, physics: widget.scrollPhysics,
reverse: true, keyboardDismissBehavior: widget.keyboardDismissBehavior,
childrenDelegate: SliverChildBuilderDelegate( controller: _scrollController,
(context, i) { reverse: true,
if (i == _messages.length + 1) { childrenDelegate: SliverChildBuilderDelegate(
if (widget.parentMessage != null) { (context, i) {
if (widget.parentMessageBuilder != null) { if (i == _messages.length + 1) {
return widget.parentMessageBuilder( if (widget.parentMessage != null) {
if (widget.parentMessageBuilder != null) {
return widget.parentMessageBuilder(
context,
widget.parentMessage,
);
} else {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
buildParentMessage(widget.parentMessage),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 32),
child: Container(
padding: const EdgeInsets.all(8),
child: Text(
'Start of thread',
textAlign: TextAlign.center,
),
color:
Theme.of(context).accentColor.withAlpha(50),
),
),
],
);
}
} else {
return SizedBox();
}
}
if (i == _messages.length) {
return _buildLoadingIndicator(streamChannel);
}
final message = _messages[i];
final nextMessage = i > 0 ? _messages[i - 1] : null;
Widget messageWidget;
if (i == 0) {
messageWidget = _buildBottomMessage(
context, context,
widget.parentMessage, message,
_messages,
streamChannel,
);
} else if (i == _messages.length - 1) {
messageWidget = _buildTopMessage(
context,
message,
_messages,
streamChannel,
); );
} else { } else {
if (widget.messageBuilder != null) {
messageWidget = Builder(
key: ValueKey<String>('MESSAGE-${message.id}'),
builder: (_) => widget.messageBuilder(
context,
MessageDetails(
context,
message,
_messages,
i,
),
_messages),
);
} else {
messageWidget = buildMessage(message, _messages, i);
}
}
if (nextMessage != null &&
!Jiffy(message.createdAt.toLocal())
.isSame(nextMessage.createdAt.toLocal(), Units.DAY)) {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[ children: <Widget>[
buildParentMessage(widget.parentMessage), messageWidget,
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 32), padding: const EdgeInsets.symmetric(vertical: 12.0),
child: Container( child: widget.dateDividerBuilder != null
padding: const EdgeInsets.all(8), ? widget.dateDividerBuilder(
child: Text( nextMessage.createdAt.toLocal())
'Start of thread', : DateDivider(
textAlign: TextAlign.center, dateTime: nextMessage.createdAt.toLocal(),
), ),
color: Theme.of(context).accentColor.withAlpha(50),
),
), ),
], ],
); );
} }
} else {
return SizedBox();
}
}
if (i == _messages.length) { return messageWidget;
return _buildLoadingIndicator(streamChannel); },
} childCount: _messages.length + 2,
final message = _messages[i]; findChildIndexCallback: (key) {
final nextMessage = i > 0 ? _messages[i - 1] : null; final ValueKey<String> valueKey = key;
final index = _messages
Widget messageWidget; .indexWhere((m) => 'MESSAGE-${m.id}' == valueKey.value);
return index != -1 ? index : null;
if (i == 0) { },
messageWidget = _buildBottomMessage( ),
context, ),
message,
_messages,
streamChannel,
);
} else if (i == _messages.length - 1) {
messageWidget = _buildTopMessage(
context,
message,
_messages,
streamChannel,
);
} else {
if (widget.messageBuilder != null) {
messageWidget = Builder(
key: ValueKey<String>('MESSAGE-${message.id}'),
builder: (_) => widget.messageBuilder(
context,
MessageDetails(
context,
message,
_messages,
i,
),
_messages),
);
} else {
messageWidget = buildMessage(message, _messages, i);
}
}
if (nextMessage != null &&
!Jiffy(message.createdAt.toLocal())
.isSame(nextMessage.createdAt.toLocal(), Units.DAY)) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
messageWidget,
Padding(
padding: const EdgeInsets.symmetric(vertical: 12.0),
child: widget.dateDividerBuilder != null
? widget
.dateDividerBuilder(nextMessage.createdAt.toLocal())
: DateDivider(
dateTime: nextMessage.createdAt.toLocal(),
),
),
],
);
}
return messageWidget;
},
childCount: _messages.length + 2,
findChildIndexCallback: (key) {
final ValueKey<String> valueKey = key;
final index = _messages
.indexWhere((m) => 'MESSAGE-${m.id}' == valueKey.value);
return index != -1 ? index : null;
},
), ),
if (widget.showScrollToBottom)
StreamBuilder<Event>(
stream: streamChannel.channel.on(
EventType.messageNew,
),
builder: (context, _) {
if (!_showScrollToBottom ||
streamChannel.channel.state.unreadCount == 0) {
return SizedBox();
}
return _buildScrollToBottom(streamChannel);
}),
],
);
}
Widget _buildScrollToBottom(StreamChannelState streamChannel) {
return Positioned(
bottom: 8,
right: 8,
width: 40,
height: 40,
child: Stack(
clipBehavior: Clip.none,
children: [
FloatingActionButton(
backgroundColor: Colors.white,
child: Icon(
StreamIcons.down,
color: Colors.black,
),
onPressed: () {
Future.delayed(Duration(milliseconds: 500), () {
setState(() {
_showScrollToBottom = false;
});
});
_scrollController.animateTo(
0,
duration: Duration(seconds: 1),
curve: Curves.easeInOut,
);
},
),
Positioned(
width: 20,
height: 20,
left: 10,
top: -10,
child: CircleAvatar(
radius: 20,
child: Text(streamChannel.channel.state.unreadCount.toString()),
),
),
],
), ),
); );
} }
@@ -381,6 +446,9 @@ class _MessageListViewState extends State<MessageListView> {
} }
} }
_bottomWasVisible = isVisible; _bottomWasVisible = isVisible;
setState(() {
_showScrollToBottom = !isVisible;
});
}, },
child: messageWidget, child: messageWidget,
); );
+4 -1
View File
@@ -70,7 +70,10 @@ class MessageReactionsModal extends StatelessWidget {
child: MessageWidget( child: MessageWidget(
key: Key('MessageWidget'), key: Key('MessageWidget'),
reverse: reverse, reverse: reverse,
message: message, message: message.text.length > 200
? message.copyWith(
text: '${message.text.substring(0, 200)}...')
: message,
messageTheme: messageTheme, messageTheme: messageTheme,
showReactions: false, showReactions: false,
showUsername: false, showUsername: false,
+1 -2
View File
@@ -3,10 +3,9 @@ import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter/src/channel_header.dart'; import 'package:stream_chat_flutter/src/channel_header.dart';
import 'package:stream_chat_flutter/src/channel_preview.dart'; import 'package:stream_chat_flutter/src/channel_preview.dart';
import 'package:stream_chat_flutter/src/message_input.dart'; import 'package:stream_chat_flutter/src/message_input.dart';
import 'package:stream_chat_flutter/src/reaction_icon.dart';
import 'package:stream_chat_flutter/src/stream_icons.dart'; import 'package:stream_chat_flutter/src/stream_icons.dart';
import 'reaction_asset.dart';
/// Inherited widget providing the [StreamChatThemeData] to the widget tree /// Inherited widget providing the [StreamChatThemeData] to the widget tree
class StreamChatTheme extends InheritedWidget { class StreamChatTheme extends InheritedWidget {
final StreamChatThemeData data; final StreamChatThemeData data;