Merge branch 'develop' into v4
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
|
||||
- Minor fixes and improvements.
|
||||
|
||||
## Upcoming
|
||||
## 3.6.0
|
||||
|
||||
🐞 Fixed
|
||||
|
||||
@@ -19,10 +19,12 @@
|
||||
channel update.
|
||||
- [[#1054]](https://github.com/GetStream/stream-chat-flutter/issues/1054) Fix `Unsupported operation: Cannot remove from an unmodifiable list`.
|
||||
- [[#1033]](https://github.com/GetStream/stream-chat-flutter/issues/1033) Hard delete from dashboard does not delete message from client.
|
||||
- Send only `user_id` while reconnecting.
|
||||
|
||||
✅ Added
|
||||
|
||||
- Handle `event.message` in `channel.truncate` events
|
||||
- Added additional parameters to `channel.truncate`
|
||||
|
||||
## 3.5.1
|
||||
|
||||
|
||||
@@ -1055,10 +1055,23 @@ class Channel {
|
||||
return _client.deleteChannel(id!, type);
|
||||
}
|
||||
|
||||
/// Removes all messages from the channel.
|
||||
Future<EmptyResponse> truncate() async {
|
||||
/// Removes all messages from the channel up to [truncatedAt] or now if
|
||||
/// [truncatedAt] is not provided.
|
||||
/// If [skipPush] is true, no push notification will be sent.
|
||||
/// [Message] is the system message that will be sent to the channel.
|
||||
Future<EmptyResponse> truncate({
|
||||
Message? message,
|
||||
bool? skipPush,
|
||||
DateTime? truncatedAt,
|
||||
}) async {
|
||||
_checkInitialized();
|
||||
return _client.truncateChannel(id!, type);
|
||||
return _client.truncateChannel(
|
||||
id!,
|
||||
type,
|
||||
message: message,
|
||||
skipPush: skipPush,
|
||||
truncatedAt: truncatedAt,
|
||||
);
|
||||
}
|
||||
|
||||
/// Accept invitation to the channel.
|
||||
|
||||
@@ -328,7 +328,9 @@ class StreamChatClient {
|
||||
_chatPersistenceClient = _originalChatPersistenceClient;
|
||||
await _chatPersistenceClient!.connect(ownUser.id);
|
||||
}
|
||||
final connectedUser = await openConnection();
|
||||
final connectedUser = await openConnection(
|
||||
includeUserDetailsInConnectCall: true,
|
||||
);
|
||||
return state.currentUser = connectedUser;
|
||||
} catch (e, stk) {
|
||||
if (e is StreamWebSocketError && e.isRetriable) {
|
||||
@@ -341,7 +343,11 @@ class StreamChatClient {
|
||||
}
|
||||
|
||||
/// Creates a new WebSocket connection with the current user.
|
||||
Future<OwnUser> openConnection() async {
|
||||
/// If [includeUserDetailsInConnectCall] is true it will include the current
|
||||
/// user details in the connect call.
|
||||
Future<OwnUser> openConnection({
|
||||
bool includeUserDetailsInConnectCall = false,
|
||||
}) async {
|
||||
assert(
|
||||
state.currentUser != null,
|
||||
'User is not set on client, '
|
||||
@@ -371,7 +377,10 @@ class StreamChatClient {
|
||||
_ws.connectionStatusStream.skip(1).listen(_connectionStatusHandler);
|
||||
|
||||
try {
|
||||
final event = await _ws.connect(user);
|
||||
final event = await _ws.connect(
|
||||
user,
|
||||
includeUserDetails: includeUserDetailsInConnectCall,
|
||||
);
|
||||
return user.merge(event.me);
|
||||
} catch (e, stk) {
|
||||
logger.severe('error connecting ws', e, stk);
|
||||
@@ -940,14 +949,23 @@ class StreamChatClient {
|
||||
channelType,
|
||||
);
|
||||
|
||||
/// Removes all messages from the channel
|
||||
/// Removes all messages from the channel up to [truncatedAt] or now if
|
||||
/// [truncatedAt] is not provided.
|
||||
/// If [skipPush] is true, no push notification will be sent.
|
||||
/// [Message] is the system message that will be sent to the channel.
|
||||
Future<EmptyResponse> truncateChannel(
|
||||
String channelId,
|
||||
String channelType,
|
||||
) =>
|
||||
String channelType, {
|
||||
Message? message,
|
||||
bool? skipPush,
|
||||
DateTime? truncatedAt,
|
||||
}) =>
|
||||
_chatApi.channel.truncateChannel(
|
||||
channelId,
|
||||
channelType,
|
||||
message: message,
|
||||
skipPush: skipPush,
|
||||
truncatedAt: truncatedAt,
|
||||
);
|
||||
|
||||
/// Mutes the channel
|
||||
|
||||
@@ -265,11 +265,18 @@ class ChannelApi {
|
||||
/// Removes all messages from the channel
|
||||
Future<EmptyResponse> truncateChannel(
|
||||
String channelId,
|
||||
String channelType,
|
||||
) async {
|
||||
String channelType, {
|
||||
Message? message,
|
||||
bool? skipPush,
|
||||
DateTime? truncatedAt,
|
||||
}) async {
|
||||
final response = await _client.post(
|
||||
'${_getChannelUrl(channelId, channelType)}/truncate',
|
||||
data: {},
|
||||
data: {
|
||||
if (message != null) 'message': message,
|
||||
if (skipPush != null) 'skip_push': skipPush,
|
||||
if (truncatedAt != null) 'truncated_at': truncatedAt,
|
||||
},
|
||||
);
|
||||
return EmptyResponse.fromJson(response.data);
|
||||
}
|
||||
|
||||
@@ -147,12 +147,15 @@ class WebSocket with TimerHelper {
|
||||
}
|
||||
}
|
||||
|
||||
Future<Uri> _buildUri({bool refreshToken = false}) async {
|
||||
Future<Uri> _buildUri({
|
||||
bool refreshToken = false,
|
||||
bool includeUserDetails = true,
|
||||
}) async {
|
||||
final user = _user!;
|
||||
final token = await tokenManager.loadToken(refresh: refreshToken);
|
||||
final params = {
|
||||
'user_id': user.id,
|
||||
'user_details': user,
|
||||
if (includeUserDetails) 'user_details': user,
|
||||
'user_token': token.rawValue,
|
||||
'server_determines_connection_id': true,
|
||||
};
|
||||
@@ -176,7 +179,10 @@ class WebSocket with TimerHelper {
|
||||
bool _connectRequestInProgress = false;
|
||||
|
||||
/// Connect the WS using the parameters passed in the constructor
|
||||
Future<Event> connect(User user) async {
|
||||
Future<Event> connect(
|
||||
User user, {
|
||||
bool includeUserDetails = false,
|
||||
}) async {
|
||||
if (_connectRequestInProgress) {
|
||||
throw const StreamWebSocketError('''
|
||||
You've called connect twice,
|
||||
@@ -191,7 +197,9 @@ class WebSocket with TimerHelper {
|
||||
connectionCompleter = Completer<Event>();
|
||||
|
||||
try {
|
||||
final uri = await _buildUri();
|
||||
final uri = await _buildUri(
|
||||
includeUserDetails: includeUserDetails,
|
||||
);
|
||||
_initWebSocketChannel(uri);
|
||||
} catch (e, stk) {
|
||||
_onConnectionError(e, stk);
|
||||
@@ -219,7 +227,10 @@ class WebSocket with TimerHelper {
|
||||
setTimer(
|
||||
Duration(milliseconds: delay),
|
||||
() async {
|
||||
final uri = await _buildUri(refreshToken: refreshToken);
|
||||
final uri = await _buildUri(
|
||||
refreshToken: refreshToken,
|
||||
includeUserDetails: false,
|
||||
);
|
||||
try {
|
||||
_initWebSocketChannel(uri);
|
||||
} catch (e, stk) {
|
||||
|
||||
@@ -124,7 +124,10 @@ class FakeWebSocket extends Fake implements WebSocket {
|
||||
Completer<Event>? connectionCompleter;
|
||||
|
||||
@override
|
||||
Future<Event> connect(User user) async {
|
||||
Future<Event> connect(
|
||||
User user, {
|
||||
bool? includeUserDetails = true,
|
||||
}) async {
|
||||
connectionStatus = ConnectionStatus.connecting;
|
||||
final event = Event(
|
||||
type: EventType.healthCheck,
|
||||
@@ -167,7 +170,10 @@ class FakeWebSocketWithConnectionError extends Fake implements WebSocket {
|
||||
Completer<Event>? connectionCompleter;
|
||||
|
||||
@override
|
||||
Future<Event> connect(User user) async {
|
||||
Future<Event> connect(
|
||||
User user, {
|
||||
bool? includeUserDetails = true,
|
||||
}) async {
|
||||
connectionStatus = ConnectionStatus.connecting;
|
||||
const error = StreamWebSocketError('Error Connecting');
|
||||
connectionCompleter = Completer()..completeError(error);
|
||||
|
||||
@@ -14,10 +14,14 @@
|
||||
- Added OpenGraph preview support for links in `StreamMessageInput`.
|
||||
- Removed video compression.
|
||||
|
||||
## 3.6.0
|
||||
|
||||
🐞 Fixed
|
||||
|
||||
- Minor fixes and improvements
|
||||
-[[#892]](https://github.com/GetStream/stream-chat-flutter/issues/892): Fix default `initialAlignment` in `MessageListView`.
|
||||
- Fix `MessageInputTheme.inputBackgroundColor` color not being used in some widgets of `MessageInput`
|
||||
- Removed dependency on `visibility_detector`
|
||||
|
||||
## 3.5.1
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
|
||||
-1
@@ -6,7 +6,6 @@ import 'dart:async';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:collection/collection.dart' show IterableExtension;
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
|
||||
@@ -470,6 +470,7 @@ class MessageInputState extends State<MessageInput> {
|
||||
if (widget.editMessage == null) {
|
||||
child = Material(
|
||||
elevation: 8,
|
||||
color: _messageInputTheme.inputBackgroundColor,
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
@@ -669,6 +670,7 @@ class MessageInputState extends State<MessageInput> {
|
||||
gradient: _focusNode.hasFocus
|
||||
? _messageInputTheme.activeBorderGradient
|
||||
: _messageInputTheme.idleBorderGradient,
|
||||
color: _messageInputTheme.inputBackgroundColor,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(1.5),
|
||||
|
||||
@@ -8,7 +8,6 @@ import 'package:stream_chat_flutter/scrollable_positioned_list/scrollable_positi
|
||||
import 'package:stream_chat_flutter/src/extension.dart';
|
||||
import 'package:stream_chat_flutter/src/swipeable.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
import 'package:visibility_detector/visibility_detector.dart';
|
||||
|
||||
/// Widget builder for message
|
||||
/// [defaultMessageWidget] is the default [StreamMessageWidget] configuration
|
||||
@@ -975,26 +974,7 @@ class _StreamMessageListViewState extends State<StreamMessageListView> {
|
||||
int index,
|
||||
) {
|
||||
final messageWidget = buildMessage(message, messages, index);
|
||||
return VisibilityDetector(
|
||||
key: ValueKey('visibility: ${message.id}'),
|
||||
onVisibilityChanged: (visibility) {
|
||||
final isVisible = visibility.visibleBounds != Rect.zero;
|
||||
if (isVisible) {
|
||||
final channel = streamChannel.channel;
|
||||
if (_upToDate &&
|
||||
channel.config?.readEvents == true &&
|
||||
channel.state!.unreadCount > 0) {
|
||||
streamChannel.channel.markRead();
|
||||
}
|
||||
}
|
||||
if (mounted) {
|
||||
if (_showScrollToBottom.value == isVisible) {
|
||||
_showScrollToBottom.value = !isVisible;
|
||||
}
|
||||
}
|
||||
},
|
||||
child: messageWidget,
|
||||
);
|
||||
return messageWidget;
|
||||
}
|
||||
|
||||
Widget buildParentMessage(
|
||||
@@ -1316,6 +1296,8 @@ class _StreamMessageListViewState extends State<StreamMessageListView> {
|
||||
_scrollController = widget.scrollController ?? ItemScrollController();
|
||||
_itemPositionListener =
|
||||
widget.itemPositionListener ?? ItemPositionsListener.create();
|
||||
_itemPositionListener.itemPositions
|
||||
.addListener(_handleItemPositionsChanged);
|
||||
|
||||
_getOnThreadTap();
|
||||
super.initState();
|
||||
@@ -1365,6 +1347,34 @@ class _StreamMessageListViewState extends State<StreamMessageListView> {
|
||||
super.didChangeDependencies();
|
||||
}
|
||||
|
||||
void _handleItemPositionsChanged() {
|
||||
final _itemPositions = _itemPositionListener.itemPositions.value.toList();
|
||||
final _firstItemIndex =
|
||||
_itemPositions.indexWhere((element) => element.index == 1);
|
||||
var _isFirstItemVisible = false;
|
||||
if (_firstItemIndex != -1) {
|
||||
final _firstItem = _itemPositions[_firstItemIndex];
|
||||
_isFirstItemVisible =
|
||||
_firstItem.itemLeadingEdge > 0 && _firstItem.itemTrailingEdge < 1;
|
||||
}
|
||||
if (_isFirstItemVisible) {
|
||||
// most recent message is visible
|
||||
final channel = streamChannel?.channel;
|
||||
if (channel != null) {
|
||||
if (_upToDate &&
|
||||
channel.config?.readEvents == true &&
|
||||
channel.state!.unreadCount > 0) {
|
||||
streamChannel!.channel.markRead();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mounted) {
|
||||
if (_showScrollToBottom.value == _isFirstItemVisible) {
|
||||
_showScrollToBottom.value = !_isFirstItemVisible;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _getOnThreadTap() {
|
||||
if (widget.onThreadTap != null) {
|
||||
_onThreadTap = (Message message) {
|
||||
@@ -1402,6 +1412,8 @@ class _StreamMessageListViewState extends State<StreamMessageListView> {
|
||||
streamChannel!.reloadChannel();
|
||||
}
|
||||
_messageNewListener?.cancel();
|
||||
_itemPositionListener.itemPositions
|
||||
.removeListener(_handleItemPositionsChanged);
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,8 +40,7 @@ dependencies:
|
||||
substring_highlight: ^1.0.26
|
||||
url_launcher: ^6.0.3
|
||||
video_player: ^2.1.0
|
||||
video_thumbnail: ^0.4.3
|
||||
visibility_detector: ^0.2.0
|
||||
video_thumbnail: ^0.5.0
|
||||
|
||||
flutter:
|
||||
assets:
|
||||
|
||||
@@ -9,6 +9,10 @@
|
||||
|
||||
- Updated `stream_chat` dependency to [`4.0.0-beta.0`](https://pub.dev/packages/stream_chat/changelog).
|
||||
|
||||
## 3.6.0
|
||||
|
||||
- Updated `stream_chat` dependency to [`3.6.0`](https://pub.dev/packages/stream_chat/changelog).
|
||||
|
||||
## 3.5.1
|
||||
|
||||
- Updated `stream_chat` dependency to [`3.5.1`](https://pub.dev/packages/stream_chat/changelog).
|
||||
|
||||
Reference in New Issue
Block a user