Merge branch 'master' into feature/new-ui

This commit is contained in:
Salvatore Giordano
2020-10-12 15:00:08 +02:00
28 changed files with 538 additions and 93 deletions
+44
View File
@@ -0,0 +1,44 @@
import 'package:flutter/material.dart';
class StreamBackButton extends StatelessWidget {
const StreamBackButton({
Key key,
this.onPressed,
this.icon = Icons.arrow_back_ios_outlined,
}) : super(key: key);
final VoidCallback onPressed;
final IconData icon;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(14.0),
child: RawMaterialButton(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)),
elevation: 0,
highlightElevation: 0,
focusElevation: 0,
disabledElevation: 0,
hoverElevation: 0,
onPressed: () {
if (onPressed != null) {
onPressed();
} else {
Navigator.of(context).pop();
}
},
fillColor: Theme.of(context).brightness == Brightness.dark
? Colors.white.withOpacity(.1)
: Colors.black.withOpacity(.1),
child: Icon(
icon ?? Icons.arrow_back_ios_outlined,
size: 15,
color: Theme.of(context).brightness == Brightness.dark
? Colors.white
: Colors.black,
),
),
);
}
}
+4 -33
View File
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter/src/back_button.dart';
import 'package:stream_chat_flutter/src/channel_info.dart';
import 'package:stream_chat_flutter/src/channel_name.dart';
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
@@ -78,7 +79,9 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget {
final channel = StreamChannel.of(context).channel;
return AppBar(
elevation: 1,
leading: showBackButton ? _buildBackButton(context) : Container(),
leading: showBackButton
? StreamBackButton(onPressed: onBackPressed)
: SizedBox(),
backgroundColor:
StreamChatTheme.of(context).channelTheme.channelHeaderTheme.color,
actions: <Widget>[
@@ -119,38 +122,6 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget {
);
}
Padding _buildBackButton(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(14.0),
child: RawMaterialButton(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)),
elevation: 0,
highlightElevation: 0,
focusElevation: 0,
disabledElevation: 0,
hoverElevation: 0,
onPressed: () {
if (onBackPressed != null) {
onBackPressed();
} else {
Navigator.of(context).pop();
}
},
fillColor: Theme.of(context).brightness == Brightness.dark
? Colors.white.withOpacity(.1)
: Colors.black.withOpacity(.1),
padding: EdgeInsets.all(4),
child: Icon(
Icons.arrow_back_ios,
size: 15,
color: Theme.of(context).brightness == Brightness.dark
? Colors.white
: Colors.black,
),
),
);
}
@override
final Size preferredSize;
}
+7 -3
View File
@@ -1,3 +1,6 @@
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:flutter_svg/flutter_svg.dart';
@@ -528,9 +531,10 @@ class _ChannelListViewState extends State<ChannelListView>
super.didUpdateWidget(oldWidget);
if (widget.filter?.toString() != oldWidget.filter?.toString() ||
widget.sort != oldWidget.sort ||
widget.pagination != oldWidget.pagination ||
widget.options != oldWidget.options) {
jsonEncode(widget.sort) != jsonEncode(oldWidget.sort) ||
widget.pagination?.toJson()?.toString() !=
oldWidget.pagination?.toJson()?.toString() ||
widget.options?.toString() != oldWidget.options?.toString()) {
final channelsBloc = ChannelsBloc.of(context);
channelsBloc.queryChannels(
filter: widget.filter,
+2 -1
View File
@@ -135,7 +135,8 @@ class ChannelsBlocState extends State<ChannelsBloc>
newChannels.insert(0, _hiddenChannels[hiddenIndex]);
_hiddenChannels.removeAt(hiddenIndex);
} else {
if (client.state.channels[e.cid] != null) {
if (client.state?.channels != null &&
client.state?.channels[e.cid] != null) {
newChannels.insert(0, client.state.channels[e.cid]);
}
}
+1
View File
@@ -283,6 +283,7 @@ class MessageInputState extends State<MessageInput> {
prefixText: ' ',
border: InputBorder.none,
),
textCapitalization: TextCapitalization.sentences,
),
),
);
+13 -8
View File
@@ -139,7 +139,7 @@ class MessageListView extends StatefulWidget {
class _MessageListViewState extends State<MessageListView> {
static const _newMessageLoadingOffset = 100;
final ScrollController _scrollController = ScrollController();
bool _isBottom = true;
bool _bottomWasVisible = true;
bool _topWasVisible = false;
List<Message> _messages = [];
List<Message> _newMessageList = [];
@@ -333,7 +333,11 @@ class _MessageListViewState extends State<MessageListView> {
onVisibilityChanged: (visibility) {
final topIsVisible = visibility.visibleBounds != Rect.zero;
if (topIsVisible && !_topWasVisible) {
streamChannel.queryMessages();
if (widget.parentMessage == null) {
streamChannel.queryMessages();
} else {
streamChannel.getReplies(widget.parentMessage.id);
}
}
_topWasVisible = topIsVisible;
},
@@ -368,12 +372,15 @@ class _MessageListViewState extends State<MessageListView> {
return VisibilityDetector(
key: ValueKey<String>('BOTTOM-MESSAGE'),
onVisibilityChanged: (visibility) {
_isBottom = visibility.visibleBounds != Rect.zero;
if (_isBottom && streamChannel.channel.config?.readEvents == true) {
final isVisible = visibility.visibleBounds != Rect.zero;
if (isVisible &&
!_bottomWasVisible &&
streamChannel.channel.config?.readEvents == true) {
if (streamChannel.channel.state.unreadCount > 0) {
streamChannel.channel.markRead();
}
}
_bottomWasVisible = isVisible;
},
child: messageWidget,
);
@@ -433,7 +440,8 @@ class _MessageListViewState extends State<MessageListView> {
final readList = channel.state?.read
?.where((element) => element.user.id != userId)
?.where((read) =>
read.lastRead.isAfter(message.createdAt) &&
(read.lastRead.isAfter(message.createdAt) ||
read.lastRead.isAtSameMomentAs(message.createdAt)) &&
(index == 0 ||
read.lastRead.isBefore(messages[index - 1].createdAt)))
?.toList();
@@ -485,9 +493,6 @@ class _MessageListViewState extends State<MessageListView> {
super.initState();
final streamChannel = StreamChannel.of(context);
if (streamChannel.channel.state.unreadCount > 0) {
streamChannel.channel.markRead();
}
Stream<List<Message>> stream;
+1 -1
View File
@@ -35,7 +35,7 @@ class ReactionPicker extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: reactionAssets.map((reactionAsset) {
final ownReactionIndex = message.ownReactions?.indexWhere(
final ownReactionIndex = message.latestReactions?.indexWhere(
(reaction) => reaction.type == reactionAsset.type) ??
-1;
return IconButton(
+11 -6
View File
@@ -54,7 +54,7 @@ class StreamChannelState extends State<StreamChannel> {
/// Calls [channel.query] updating [queryMessage] stream
void queryMessages() {
if (_paginationEnded) {
if (_queryMessageController.value == true || _paginationEnded) {
return;
}
@@ -65,15 +65,18 @@ class StreamChannelState extends State<StreamChannel> {
firstId = channel.state.messages.first.id;
}
final messageLimit = 50;
widget.channel
.query(
messagesPagination: PaginationParams(
lessThan: firstId,
limit: 100,
limit: messageLimit,
),
preferOffline: true,
)
.then((res) {
if (res.messages.isEmpty) {
if (res.messages.isEmpty || res.messages.length < messageLimit) {
_paginationEnded = true;
}
_queryMessageController.add(false);
@@ -84,7 +87,7 @@ class StreamChannelState extends State<StreamChannel> {
/// Calls [channel.getReplies] updating [queryMessage] stream
Future<void> getReplies(String parentId) async {
if (_paginationEnded) {
if (_queryMessageController.value == true || _paginationEnded) {
return;
}
@@ -99,16 +102,18 @@ class StreamChannelState extends State<StreamChannel> {
}
}
final messageLimit = 50;
return widget.channel
.getReplies(
parentId,
PaginationParams(
lessThan: firstId,
limit: 100,
limit: messageLimit,
),
preferOffline: true,
)
.then((res) {
if (res.messages.isEmpty) {
if (res.messages.isEmpty || res.messages.length < messageLimit) {
_paginationEnded = true;
}
_queryMessageController.add(false);
+10 -33
View File
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter/src/back_button.dart';
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/screenshots/thread_header.png)
@@ -80,7 +81,15 @@ class ThreadHeader extends StatelessWidget implements PreferredSizeWidget {
StreamChatTheme.of(context).channelTheme.channelHeaderTheme.color,
actions: <Widget>[
Container(
child: showBackButton ? _buildBackButton(context) : Container(),
child: showBackButton
? AspectRatio(
aspectRatio: 1,
child: StreamBackButton(
onPressed: onBackPressed,
icon: Icons.close,
),
)
: SizedBox(),
),
],
centerTitle: false,
@@ -104,38 +113,6 @@ class ThreadHeader extends StatelessWidget implements PreferredSizeWidget {
);
}
Padding _buildBackButton(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(14.0),
child: AspectRatio(
aspectRatio: 1,
child: RawMaterialButton(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)),
elevation: 0,
highlightElevation: 0,
focusElevation: 0,
disabledElevation: 0,
hoverElevation: 0,
onPressed: () {
if (onBackPressed != null) {
onBackPressed();
} else {
Navigator.of(context).pop();
}
},
fillColor: Theme.of(context).brightness == Brightness.dark
? Colors.white.withOpacity(.1)
: Colors.black.withOpacity(.1),
padding: EdgeInsets.all(4),
child: Icon(
Icons.close,
size: 15,
),
),
),
);
}
@override
final Size preferredSize;
}
+55
View File
@@ -0,0 +1,55 @@
import 'package:flutter/material.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter/src/user_avatar.dart';
class UserReactionDisplay extends StatelessWidget {
const UserReactionDisplay({
Key key,
@required this.reactionToEmoji,
@required this.message,
this.size = 30,
}) : super(key: key);
final Map<String, String> reactionToEmoji;
final Message message;
final double size;
@override
Widget build(BuildContext context) {
return Container(
color: Colors.black87,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: reactionToEmoji.keys.map((reactionType) {
var firstUserReaction = message.latestReactions.firstWhere(
(element) => element.type == reactionType, orElse: () {
return null;
});
if (firstUserReaction == null) {
return IconButton(
iconSize: size,
icon: Container(),
onPressed: null,
);
}
return IconButton(
iconSize: size,
icon: UserAvatar(
user: firstUserReaction.user,
constraints: BoxConstraints(
maxHeight: size - 5,
maxWidth: size - 5,
),
onTap: (user) {},
),
onPressed: () {},
);
}).toList(),
),
);
}
}