Merge branch 'develop' of https://github.com/squaddy/stream-chat-flutter into bugfix/deleting_thread_messages

This commit is contained in:
Bartek Stoliński
2022-03-01 10:56:34 +01:00
6 changed files with 152 additions and 127 deletions
+1
View File
@@ -12,6 +12,7 @@
- [[#897]](https://github.com/GetStream/stream-chat-flutter/issues/897) Fixed error type mis-match in `AuthInterceptor`.
- [[#891]](https://github.com/GetStream/stream-chat-flutter/pull/891) Fixed reply counter for parent message not
updating correctly after deleting thread message.
- Fix `channelState.copyWith` with respect to pinnedMessages.
## 3.4.0
@@ -1816,8 +1816,24 @@ class ChannelClientState {
newMessages.add(message);
}
final newPinnedMessages = [...pinnedMessages];
final oldPinnedIndex =
newPinnedMessages.indexWhere((m) => m.id == message.id);
// Handle pinned messages
if (message.pinned) {
if (oldPinnedIndex != -1) {
newPinnedMessages[oldPinnedIndex] = message;
} else {
newPinnedMessages.add(message);
}
} else {
newPinnedMessages.removeWhere((m) => m.id == message.id);
}
_channelState = _channelState.copyWith(
messages: newMessages..sort(_sortByCreatedAt),
pinnedMessages: newPinnedMessages,
channel: _channelState.channel?.copyWith(
lastMessageAt: message.createdAt,
),
@@ -1904,11 +1920,12 @@ class ChannelClientState {
.distinct(const ListEquality().equals);
/// Channel pinned message list.
List<Message> get pinnedMessages => _channelState.pinnedMessages.toList();
List<Message> get pinnedMessages => _channelState.pinnedMessages;
/// Channel pinned message list as a stream.
Stream<List<Message>> get pinnedMessagesStream =>
channelStateStream.map((cs) => cs.pinnedMessages.toList());
Stream<List<Message>> get pinnedMessagesStream => channelStateStream
.map((cs) => cs.pinnedMessages)
.distinct(const ListEquality().equals);
/// Get channel last message.
Message? get lastMessage =>
@@ -2219,7 +2236,7 @@ class ChannelClientState {
.toList();
updateChannelState(_channelState.copyWith(
pinnedMessages: pinnedMessages.where(_pinIsValid()).toList(),
pinnedMessages: pinnedMessages.where(_pinIsValid).toList(),
messages: expiredMessages,
));
}
@@ -2256,7 +2273,7 @@ class ChannelClientState {
}
}
bool Function(Message) _pinIsValid() {
bool _pinIsValid(Message message) {
final now = DateTime.now();
return (Message m) => m.pinExpires!.isAfter(now);
return message.pinExpires!.isAfter(now);
}
@@ -56,7 +56,7 @@ class ChannelState {
ChannelModel? channel,
List<Message>? messages,
List<Member>? members,
List<Message>? pinnedMessages,
List<Message> pinnedMessages = _emptyPinnedMessages,
int? watcherCount,
List<User>? watchers,
List<Read>? read,
@@ -69,7 +69,7 @@ class ChannelState {
// FIXME: Use non-nullable by default instead of empty list.
pinnedMessages: pinnedMessages == _emptyPinnedMessages
? this.pinnedMessages
: pinnedMessages ?? _emptyPinnedMessages,
: pinnedMessages,
watcherCount: watcherCount ?? this.watcherCount,
watchers: watchers ?? this.watchers,
read: read ?? this.read,
@@ -5,6 +5,7 @@
- [[#888]](https://github.com/GetStream/stream-chat-flutter/issues/888) Fix `unban` command not working in `MessageInput`.
- [[#805]](https://github.com/GetStream/stream-chat-flutter/issues/805) Updated chewie dependency version to 1.3.0
- Fix `showScrollToBottom` in `MessageListView` not respecting false value.
- Fix default `Channel` route not opening from `ChannelListView` when `ChannelAvatar` is tapped
## 3.4.0
- Updated `stream_chat_flutter_core` dependency to [`3.4.0`](https://pub.dev/packages/stream_chat_flutter_core/changelog).
@@ -599,7 +599,9 @@ class _ChannelListViewState extends State<ChannelListView> {
child: ChannelPreview(
onLongPress: widget.onChannelLongPress,
channel: channel,
onImageTap: () => widget.onImageTap?.call(channel),
onImageTap: widget.onImageTap != null
? () => widget.onImageTap!(channel)
: null,
onTap: (channel) => onTap(channel, widget.channelWidget),
),
),
@@ -612,7 +614,7 @@ class _ChannelListViewState extends State<ChannelListView> {
if (widget.onChannelTap != null) {
onTap = widget.onChannelTap!;
} else {
onTap = (client, _) {
onTap = (channel, _) {
if (widget.channelWidget == null) {
return;
}
@@ -620,7 +622,7 @@ class _ChannelListViewState extends State<ChannelListView> {
context,
MaterialPageRoute(
builder: (context) => StreamChannel(
channel: client,
channel: channel,
child: widget.channelWidget!,
),
),
@@ -65,24 +65,35 @@ class FullScreenMedia extends StatefulWidget {
class _FullScreenMediaState extends State<FullScreenMedia>
with SingleTickerProviderStateMixin {
bool _optionsShown = true;
late final AnimationController _controller;
late final AnimationController _animationController;
late final PageController _pageController;
late int _currentPage;
late final _curvedAnimation = CurvedAnimation(
parent: _animationController,
curve: Curves.easeOut,
reverseCurve: Curves.easeIn,
);
final _opacityTween = Tween<double>(begin: 1, end: 0);
late final _opacityAnimation = _opacityTween.animate(
CurvedAnimation(
parent: _animationController,
curve: const Interval(0, 0.6, curve: Curves.easeOut),
),
);
late final ValueNotifier<int> _currentPage = ValueNotifier(widget.startIndex);
final videoPackages = <String, VideoPackage>{};
@override
void initState() {
super.initState();
_controller = AnimationController(
_animationController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 300),
);
_pageController = PageController(initialPage: widget.startIndex);
_currentPage = widget.startIndex;
for (var i = 0; i < widget.mediaAttachments.length; i++) {
final attachment = widget.mediaAttachments[i];
if (attachment.type != 'video') continue;
@@ -116,41 +127,38 @@ class _FullScreenMediaState extends State<FullScreenMedia>
resizeToAvoidBottomInset: false,
body: Stack(
children: [
AnimatedBuilder(
animation: _controller,
builder: (context, snapshot) => PageView.builder(
controller: _pageController,
onPageChanged: (val) {
setState(() {
_currentPage = val;
});
PageView.builder(
controller: _pageController,
onPageChanged: (val) {
_currentPage.value = val;
if (videoPackages.isEmpty) {
return;
if (videoPackages.isEmpty) {
return;
}
final currentAttachment = widget.mediaAttachments[val];
for (final e in videoPackages.values) {
if (e._attachment != currentAttachment) {
e._chewieController?.pause();
}
}
final currentAttachment = widget.mediaAttachments[val];
for (final e in videoPackages.values) {
if (e._attachment != currentAttachment) {
e._chewieController?.pause();
}
}
if (widget.autoplayVideos &&
currentAttachment.type == 'video') {
final controller = videoPackages[currentAttachment.id]!;
controller._chewieController?.play();
}
},
itemBuilder: (context, index) {
final attachment = widget.mediaAttachments[index];
if (attachment.type == 'image' ||
attachment.type == 'giphy') {
final imageUrl = attachment.imageUrl ??
attachment.assetUrl ??
attachment.thumbUrl;
return PhotoView(
if (widget.autoplayVideos &&
currentAttachment.type == 'video') {
final controller = videoPackages[currentAttachment.id]!;
controller._chewieController?.play();
}
},
itemBuilder: (context, index) {
final attachment = widget.mediaAttachments[index];
if (attachment.type == 'image' || attachment.type == 'giphy') {
final imageUrl = attachment.imageUrl ??
attachment.assetUrl ??
attachment.thumbUrl;
return AnimatedBuilder(
animation: _curvedAnimation,
builder: (context, child) => PhotoView(
loadingBuilder: (context, image) => const Offstage(),
imageProvider: (imageUrl == null &&
attachment.localUri != null &&
@@ -166,97 +174,91 @@ class _FullScreenMediaState extends State<FullScreenMedia>
color: ColorTween(
begin: ChannelHeaderTheme.of(context).color,
end: Colors.black,
).lerp(_controller.value),
).lerp(_curvedAnimation.value),
),
onTapUp: (a, b, c) {
setState(() {
_optionsShown = !_optionsShown;
});
if (_controller.isCompleted) {
_controller.reverse();
if (_animationController.isCompleted) {
_animationController.reverse();
} else {
_controller.forward();
_animationController.forward();
}
},
);
} else if (attachment.type == 'video') {
final controller = videoPackages[attachment.id]!;
if (!controller.initialized) {
return const Center(
child: CircularProgressIndicator(),
);
}
return InkWell(
onTap: () {
setState(() {
_optionsShown = !_optionsShown;
});
if (_controller.isCompleted) {
_controller.reverse();
} else {
_controller.forward();
}
},
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 50,
),
child: Chewie(
controller: controller.chewieController!,
),
),
),
);
} else if (attachment.type == 'video') {
final controller = videoPackages[attachment.id]!;
if (!controller.initialized) {
return const Center(
child: CircularProgressIndicator(),
);
}
return Container();
},
itemCount: widget.mediaAttachments.length,
),
),
AnimatedOpacity(
opacity: _optionsShown ? 1.0 : 0.0,
duration: const Duration(milliseconds: 300),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
GalleryHeader(
userName: widget.userName,
sentAt: context.translations.sentAtText(
date: widget.message.createdAt,
time: widget.message.createdAt,
return InkWell(
onTap: () {
if (_animationController.isCompleted) {
_animationController.reverse();
} else {
_animationController.forward();
}
},
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 50,
),
child: Chewie(
controller: controller.chewieController!,
),
),
onBackPressed: () {
Navigator.of(context).pop();
},
message: widget.message,
currentIndex: _currentPage,
onShowMessage: () {
widget.onShowMessage?.call(
widget.message,
StreamChannel.of(context).channel,
);
},
attachmentActionsModalBuilder:
widget.attachmentActionsModalBuilder,
),
if (!widget.message.isEphemeral)
GalleryFooter(
currentPage: _currentPage,
totalPages: widget.mediaAttachments.length,
mediaAttachments: widget.mediaAttachments,
);
}
return const SizedBox();
},
itemCount: widget.mediaAttachments.length,
),
FadeTransition(
opacity: _opacityAnimation,
child: ValueListenableBuilder<int>(
valueListenable: _currentPage,
builder: (context, value, child) => Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
GalleryHeader(
userName: widget.userName,
sentAt: context.translations.sentAtText(
date: widget.message.createdAt,
time: widget.message.createdAt,
),
onBackPressed: () {
Navigator.of(context).pop();
},
message: widget.message,
mediaSelectedCallBack: (val) {
setState(() {
_currentPage = val;
currentIndex: value,
onShowMessage: () {
widget.onShowMessage?.call(
widget.message,
StreamChannel.of(context).channel,
);
},
attachmentActionsModalBuilder:
widget.attachmentActionsModalBuilder,
),
if (!widget.message.isEphemeral)
GalleryFooter(
currentPage: value,
totalPages: widget.mediaAttachments.length,
mediaAttachments: widget.mediaAttachments,
message: widget.message,
mediaSelectedCallBack: (val) {
_currentPage.value = val;
_pageController.animateToPage(
val,
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
Navigator.pop(context);
});
},
),
],
},
),
],
),
),
),
],
@@ -264,9 +266,11 @@ class _FullScreenMediaState extends State<FullScreenMedia>
);
@override
void dispose() async {
void dispose() {
_animationController.dispose();
_pageController.dispose();
for (final package in videoPackages.values) {
await package.dispose();
package.dispose();
}
super.dispose();
}