fix rebuild message list view

This commit is contained in:
Salvatore Giordano
2021-06-01 15:42:45 +02:00
parent 847e5120af
commit 06bf2cdfe5
10 changed files with 65 additions and 26 deletions
+10 -5
View File
@@ -70,8 +70,11 @@ class Channel {
true; true;
/// Returns true if the channel is muted as a stream /// Returns true if the channel is muted as a stream
Stream<bool>? get isMutedStream => _client.state.userStream.map((event) => Stream<bool>? get isMutedStream => _client.state.userStream
event!.channelMutes.any((element) => element.channel.cid == cid) == true); .map((event) =>
event!.channelMutes.any((element) => element.channel.cid == cid) ==
true)
.distinct();
/// True if the channel is a group /// True if the channel is a group
bool get isGroup => memberCount != 2; bool get isGroup => memberCount != 2;
@@ -1604,7 +1607,7 @@ class ChannelClientState {
/// Channel message list as a stream /// Channel message list as a stream
Stream<List<Message>?> get messagesStream => channelStateStream Stream<List<Message>?> get messagesStream => channelStateStream
.map((cs) => cs.messages) .map((cs) => cs.messages)
.distinct((prev, next) => const ListEquality().equals(prev, next)); .distinct(const ListEquality().equals);
/// Channel pinned message list /// Channel pinned message list
List<Message>? get pinnedMessages => _channelState.pinnedMessages.toList(); List<Message>? get pinnedMessages => _channelState.pinnedMessages.toList();
@@ -1634,7 +1637,7 @@ class ChannelClientState {
_channel.client.state.usersStream, _channel.client.state.usersStream,
(members, users) => (members, users) =>
members!.map((e) => e!.copyWith(user: users[e.user!.id])).toList(), members!.map((e) => e!.copyWith(user: users[e.user!.id])).toList(),
); ).distinct(const ListEquality().equals);
/// Channel watcher count /// Channel watcher count
int? get watcherCount => _channelState.watcherCount; int? get watcherCount => _channelState.watcherCount;
@@ -1804,7 +1807,9 @@ class ChannelClientState {
List<User> get typingEvents => _typingEventsController.value; List<User> get typingEvents => _typingEventsController.value;
/// Channel related typing users stream /// Channel related typing users stream
Stream<List<User>> get typingEventsStream => _typingEventsController.stream; Stream<List<User>> get typingEventsStream =>
_typingEventsController.stream.distinct(const ListEquality().equals);
final BehaviorSubject<List<User>> _typingEventsController = final BehaviorSubject<List<User>> _typingEventsController =
BehaviorSubject.seeded([]); BehaviorSubject.seeded([]);
+1 -1
View File
@@ -219,7 +219,7 @@ class StreamChatClient {
/// This notifies the connection status of the websocket connection. /// This notifies the connection status of the websocket connection.
/// Listen to this to get notified when the websocket tries to reconnect. /// Listen to this to get notified when the websocket tries to reconnect.
Stream<ConnectionStatus> get wsConnectionStatusStream => Stream<ConnectionStatus> get wsConnectionStatusStream =>
_wsConnectionStatusController.stream; _wsConnectionStatusController.stream.distinct();
/// The current user token /// The current user token
String? token; String? token;
@@ -1,3 +1,4 @@
import 'package:equatable/equatable.dart';
import 'package:json_annotation/json_annotation.dart'; import 'package:json_annotation/json_annotation.dart';
import 'package:stream_chat/src/models/user.dart'; import 'package:stream_chat/src/models/user.dart';
@@ -6,7 +7,7 @@ part 'member.g.dart';
/// The class that contains the information about the user membership /// The class that contains the information about the user membership
/// in a channel /// in a channel
@JsonSerializable() @JsonSerializable()
class Member { class Member extends Equatable {
/// Constructor used for json serialization /// Constructor used for json serialization
Member({ Member({
this.user, this.user,
@@ -98,4 +99,19 @@ class Member {
/// Serialize to json /// Serialize to json
Map<String, dynamic> toJson() => _$MemberToJson(this); Map<String, dynamic> toJson() => _$MemberToJson(this);
@override
List<Object?> get props => [
user,
inviteAcceptedAt,
inviteRejectedAt,
invited,
role,
userId,
isModerator,
banned,
shadowBanned,
createdAt,
updatedAt,
];
} }
+15 -1
View File
@@ -1,3 +1,4 @@
import 'package:equatable/equatable.dart';
import 'package:json_annotation/json_annotation.dart'; import 'package:json_annotation/json_annotation.dart';
import 'package:stream_chat/src/models/serialization.dart'; import 'package:stream_chat/src/models/serialization.dart';
@@ -5,7 +6,7 @@ part 'user.g.dart';
/// The class that defines the user model /// The class that defines the user model
@JsonSerializable() @JsonSerializable()
class User { class User extends Equatable {
/// Constructor used for json serialization /// Constructor used for json serialization
User({ User({
required this.id, required this.id,
@@ -125,4 +126,17 @@ class User {
banned: banned ?? this.banned, banned: banned ?? this.banned,
teams: teams ?? this.teams, teams: teams ?? this.teams,
); );
@override
List<Object?> get props => [
id,
role,
teams,
createdAt,
updatedAt,
lastActive,
online,
banned,
extraData,
];
} }
@@ -3,10 +3,10 @@ analyzer:
- extension-methods - extension-methods
exclude: exclude:
- lib/**/*.g.dart - lib/**/*.g.dart
- example/** # - example/**
- lib/src/emoji - lib/src/emoji
- lib/**/*.freezed.dart - lib/**/*.freezed.dart
- test/** # - test/**
linter: linter:
rules: rules:
@@ -27,7 +27,6 @@ class ChannelInfo extends StatelessWidget {
final client = StreamChat.of(context).client; final client = StreamChat.of(context).client;
return StreamBuilder<List<Member>>( return StreamBuilder<List<Member>>(
stream: channel.state?.membersStream, stream: channel.state?.membersStream,
initialData: channel.state?.members,
builder: (context, snapshot) => ConnectionStatusBuilder( builder: (context, snapshot) => ConnectionStatusBuilder(
statusBuilder: (context, status) { statusBuilder: (context, status) {
switch (status) { switch (status) {
@@ -1,3 +1,4 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
@@ -39,7 +40,6 @@ class ConnectionStatusBuilder extends StatelessWidget {
final stream = connectionStatusStream ?? final stream = connectionStatusStream ??
StreamChat.of(context).client.wsConnectionStatusStream; StreamChat.of(context).client.wsConnectionStatusStream;
return StreamBuilder<ConnectionStatus>( return StreamBuilder<ConnectionStatus>(
initialData: initialStatus,
stream: stream, stream: stream,
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.hasError) { if (snapshot.hasError) {
@@ -690,7 +690,7 @@ class _MessageListViewState extends State<MessageListView> {
? streamChannel.queryTopMessages ? streamChannel.queryTopMessages
: streamChannel.queryBottomMessages; : streamChannel.queryBottomMessages;
return StreamBuilder<bool>( return StreamBuilder<bool>(
key: const Key('LOADING-INDICATOR'), key: Key('LOADING-INDICATOR $direction'),
stream: stream, stream: stream,
initialData: false, initialData: false,
builder: (context, snapshot) { builder: (context, snapshot) {
@@ -789,7 +789,9 @@ class _MessageListViewState extends State<MessageListView> {
} }
} }
if (mounted) { if (mounted) {
setState(() => _showScrollToBottom = !isVisible); if (_showScrollToBottom == isVisible) {
setState(() => _showScrollToBottom = !isVisible);
}
} }
}, },
child: messageWidget, child: messageWidget,
@@ -33,8 +33,15 @@ class TypingIndicator extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final channelState = final channelState =
channel?.state ?? StreamChannel.of(context).channel.state!; channel?.state ?? StreamChannel.of(context).channel.state!;
final altWidget = Align(
key: const Key('alternative'),
alignment: alignment,
child: Container(
child: alternativeWidget ?? const Offstage(),
),
);
return StreamBuilder<List<User>>( return StreamBuilder<List<User>>(
initialData: channelState.typingEvents,
stream: channelState.typingEventsStream, stream: channelState.typingEventsStream,
builder: (context, snapshot) => AnimatedSwitcher( builder: (context, snapshot) => AnimatedSwitcher(
duration: const Duration(milliseconds: 300), duration: const Duration(milliseconds: 300),
@@ -62,13 +69,7 @@ class TypingIndicator extends StatelessWidget {
), ),
), ),
) )
: Align( : altWidget,
key: const Key('alternative'),
alignment: alignment,
child: Container(
child: alternativeWidget ?? const Offstage(),
),
),
), ),
); );
} }
@@ -135,10 +135,12 @@ class MessageListCoreState extends State<MessageListCore> {
} }
return StreamBuilder<List<Message>?>( return StreamBuilder<List<Message>?>(
stream: messagesStream?.map((messages) => stream: messagesStream?.map(
messages?.where(widget.messageFilter ?? defaultFilter).toList( (messages) =>
growable: false, messages?.where(widget.messageFilter ?? defaultFilter).toList(
)), growable: false,
),
),
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.hasError) { if (snapshot.hasError) {
return widget.errorWidgetBuilder(context, snapshot.error!); return widget.errorWidgetBuilder(context, snapshot.error!);