Merge branch 'develop' into feat/positioned-list-experiment
This commit is contained in:
@@ -2,7 +2,7 @@ name: stream_flutter_workflow
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true'
|
ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true'
|
||||||
flutter_version: "2.2.2"
|
flutter_version: "2.5.1"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|||||||
@@ -177,6 +177,14 @@ Develop is merged into master after the team performs various automated and QA t
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
# Versioning Policy
|
||||||
|
|
||||||
|
All of the Stream Chat packages follow [semantic versioning (semver)](https://semver.org/).
|
||||||
|
|
||||||
|
See our [versioning policy documentation](https://getstream.io/chat/docs/sdk/flutter/basics/versioning_policy/) for more information.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
# Styleguides 💅
|
# Styleguides 💅
|
||||||
|
|
||||||

|

|
||||||
|
|||||||
@@ -62,6 +62,11 @@ Every package folder includes a fully functional example with setup instructions
|
|||||||
|
|
||||||
We also provide a set of sample apps created using the Stream Flutter SDK at [this location](https://github.com/GetStream/flutter-samples).
|
We also provide a set of sample apps created using the Stream Flutter SDK at [this location](https://github.com/GetStream/flutter-samples).
|
||||||
|
|
||||||
|
## Versioning Policy
|
||||||
|
|
||||||
|
All of the Stream Chat packages follow [semantic versioning (semver)](https://semver.org/).
|
||||||
|
|
||||||
|
See our [versioning policy documentation](https://getstream.io/chat/docs/sdk/flutter/basics/versioning_policy/) for more information.
|
||||||
|
|
||||||
## We are hiring
|
## We are hiring
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
id: versioning_policy
|
||||||
|
sidebar_position: 3
|
||||||
|
title: Versioning Policy
|
||||||
|
---
|
||||||
|
|
||||||
|
All of the Stream Chat packages follow [semantic versioning (semver)](https://semver.org/).
|
||||||
|
|
||||||
|
That means that with a version number x.y.z (major.minor.patch):
|
||||||
|
- When releasing bug fixes (backwards compatible), we make a patch release by changing the z number (ex: 3.6.2 to 3.6.3). A bug fix is defined as an internal change that fixes incorrect behavior.
|
||||||
|
- When releasing new features or non-critical fixes, we make a minor release by changing the y number (ex: 3.6.2 to 3.7.0).
|
||||||
|
- When releasing breaking changes (backward incompatible), we make a major release by changing the x number (ex: 3.6.2 to 4.0.0).
|
||||||
|
|
||||||
|
See the [semantic versioning](https://dart.dev/tools/pub/versioning#semantic-versions) section from the Dart docs for more information.
|
||||||
|
|
||||||
|
This versioning policy does not apply to prerelease packages (below major version of 1). See this
|
||||||
|
[StackOverflow thread](https://stackoverflow.com/questions/66201337/how-do-dart-package-versions-work-how-should-i-version-my-flutter-plugins)
|
||||||
|
for more information on Dart package versioning.
|
||||||
|
|
||||||
|
Whenever possible, we will add deprecation warnings in preparation for future breaking changes.
|
||||||
@@ -104,7 +104,16 @@ Filter.autoComplete('name', 'demo')
|
|||||||
The 'exists' filter matches values that exist, or don't exist, based on the specified boolean value.
|
The 'exists' filter matches values that exist, or don't exist, based on the specified boolean value.
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
Filter.exists('name', true)
|
Filter.exists('name')
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Filter.notExists
|
||||||
|
|
||||||
|
The 'notExists' filter checks if the specified key doesn't exist. This is a simplified call to `Filter.exists`
|
||||||
|
with the value set to false.
|
||||||
|
|
||||||
|
```dart
|
||||||
|
Filter.notExists('name')
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Filter.contains
|
#### Filter.contains
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class MessageSearchPage extends StatelessWidget {
|
|||||||
child: MessageSearchListView(
|
child: MessageSearchListView(
|
||||||
filters: Filter.in_('members', [StreamChat.of(context).user!.id],),
|
filters: Filter.in_('members', [StreamChat.of(context).user!.id],),
|
||||||
messageQuery: 'your query here',
|
messageQuery: 'your query here',
|
||||||
paginationParams: PaginationParams(limit: 20),
|
limit: 20,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -28,9 +28,9 @@ class MessageSearchPage extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: MessageSearchListCore(
|
body: MessageSearchListCore(
|
||||||
messageQuery: _messageFilter,
|
messageQuery: _messageFilter,
|
||||||
filters: _channelsFilter,
|
filters: _channelsFilter,
|
||||||
paginationParams: PaginationParams(limit: 20),
|
limit: 20,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,15 @@
|
|||||||
## Upcoming
|
## 3.1.1
|
||||||
|
|
||||||
|
✅ Added
|
||||||
|
|
||||||
|
- Added `Filter.notExists`.
|
||||||
|
|
||||||
|
🐞 Fixed
|
||||||
|
|
||||||
|
- [[#710]](https://github.com/GetStream/stream-chat-flutter/issues/710) Fixed JWT requiring using `String` as id.
|
||||||
|
- Fixed expired CDN attachment links not updating correctly.
|
||||||
|
|
||||||
|
## 3.0.0
|
||||||
|
|
||||||
🛑️ Breaking Changes from `2.2.1`
|
🛑️ Breaking Changes from `2.2.1`
|
||||||
|
|
||||||
@@ -13,6 +24,19 @@
|
|||||||
✅ Added
|
✅ Added
|
||||||
|
|
||||||
- Added `Filter.contains` and `Filter.empty`
|
- Added `Filter.contains` and `Filter.empty`
|
||||||
|
- Added support for `next`, `previous` value pagination in `client.search`
|
||||||
|
, [read more.](https://getstream.io/chat/docs/other-rest/search/#pagination)
|
||||||
|
- `Attachment` class now has a `fileSize` and `mimeType` property. Setting a `file` will also set the `file_size`
|
||||||
|
, `mime_type` key on `extraData`, so `attachment.fileSize`, `attachment.mimetype`
|
||||||
|
and `attachment.extraData['file_size']`
|
||||||
|
, `attachment.extraData['mime_type]` is same respectively.
|
||||||
|
|
||||||
|
🐞 Fixed
|
||||||
|
|
||||||
|
- [[#659]](https://github.com/GetStream/stream-chat-flutter/issues/659) Fixed unread count not updating correctly.
|
||||||
|
- Fix `Filter.empty()` json encoding.
|
||||||
|
- [[#700]](https://github.com/GetStream/stream-chat-flutter/issues/700) Connecting user without providing `name`
|
||||||
|
uses `id` instead for setting `user.name`.
|
||||||
|
|
||||||
## 2.2.1
|
## 2.2.1
|
||||||
|
|
||||||
|
|||||||
@@ -351,7 +351,7 @@ class Channel {
|
|||||||
Future<bool> get initialized => _initializedCompleter.future;
|
Future<bool> get initialized => _initializedCompleter.future;
|
||||||
|
|
||||||
final _cancelableAttachmentUploadRequest = <String, CancelToken>{};
|
final _cancelableAttachmentUploadRequest = <String, CancelToken>{};
|
||||||
final _messageAttachmentsUploadCompleter = <String, Completer>{};
|
final _messageAttachmentsUploadCompleter = <String, Completer<Message>>{};
|
||||||
|
|
||||||
/// Cancels [attachmentId] upload request. Throws exception if the request
|
/// Cancels [attachmentId] upload request. Throws exception if the request
|
||||||
/// hasn't even started yet, Already completed or Already cancelled.
|
/// hasn't even started yet, Already completed or Already cancelled.
|
||||||
@@ -382,12 +382,10 @@ class Channel {
|
|||||||
String messageId,
|
String messageId,
|
||||||
Iterable<String> attachmentIds,
|
Iterable<String> attachmentIds,
|
||||||
) {
|
) {
|
||||||
final message = [
|
var message = [
|
||||||
...state!.messages,
|
...state!.messages,
|
||||||
...state!.threads.values.expand((messages) => messages),
|
...state!.threads.values.expand((messages) => messages),
|
||||||
].firstWhereOrNull(
|
].firstWhereOrNull((it) => it.id == messageId);
|
||||||
(it) => it.id == messageId,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (message == null) {
|
if (message == null) {
|
||||||
throw const StreamChatError('Error, Message not found');
|
throw const StreamChatError('Error, Message not found');
|
||||||
@@ -409,11 +407,15 @@ class Channel {
|
|||||||
client.logger.info('Found ${attachments.length} attachments');
|
client.logger.info('Found ${attachments.length} attachments');
|
||||||
|
|
||||||
void updateAttachment(Attachment attachment) {
|
void updateAttachment(Attachment attachment) {
|
||||||
final index =
|
final index = message!.attachments.indexWhere(
|
||||||
message.attachments.indexWhere((it) => it.id == attachment.id);
|
(it) => it.id == attachment.id,
|
||||||
|
);
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
message.attachments[index] = attachment;
|
final newAttachments = [...message!.attachments]..[index] = attachment;
|
||||||
state?.addMessage(message);
|
final updatedMessage = message!.copyWith(attachments: newAttachments);
|
||||||
|
state?.addMessage(updatedMessage);
|
||||||
|
// updating original message for next iteration
|
||||||
|
message = message!.merge(updatedMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -476,7 +478,7 @@ class Channel {
|
|||||||
_cancelableAttachmentUploadRequest.remove(it.id);
|
_cancelableAttachmentUploadRequest.remove(it.id);
|
||||||
});
|
});
|
||||||
})).whenComplete(() {
|
})).whenComplete(() {
|
||||||
if (message.attachments.every((it) => it.uploadState.isSuccess)) {
|
if (message!.attachments.every((it) => it.uploadState.isSuccess)) {
|
||||||
_messageAttachmentsUploadCompleter.remove(messageId)?.complete(message);
|
_messageAttachmentsUploadCompleter.remove(messageId)?.complete(message);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -1076,33 +1078,7 @@ class Channel {
|
|||||||
} else {
|
} else {
|
||||||
// remove the passed message if response does
|
// remove the passed message if response does
|
||||||
// not contain message
|
// not contain message
|
||||||
final oldIndex = state!.messages.indexWhere((m) => m.id == messageId);
|
state!.removeMessage(message);
|
||||||
|
|
||||||
// remove regular message if present
|
|
||||||
if (oldIndex != -1) {
|
|
||||||
final oldMessage = state!.messages[oldIndex];
|
|
||||||
state!.updateChannelState(state!._channelState.copyWith(
|
|
||||||
messages: state?.messages?..remove(oldMessage),
|
|
||||||
channel: state?._channelState.channel,
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
// remove thread message if present
|
|
||||||
// also reduces total reply count
|
|
||||||
final oldMessage = state!.threads.values
|
|
||||||
.expand((messages) => messages)
|
|
||||||
.firstWhereOrNull((m) => m.id == messageId);
|
|
||||||
if (oldMessage?.parentId != null) {
|
|
||||||
final parentMessage = state!.messages.firstWhereOrNull(
|
|
||||||
(element) => element.id == oldMessage!.parentId,
|
|
||||||
);
|
|
||||||
if (parentMessage != null) {
|
|
||||||
state!.addMessage(parentMessage.copyWith(
|
|
||||||
replyCount: parentMessage.replyCount! - 1));
|
|
||||||
}
|
|
||||||
state!.updateThreadInfo(oldMessage!.parentId!,
|
|
||||||
state!.threads[oldMessage.parentId!]!..remove(oldMessage));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await _client.chatPersistenceClient?.deleteMessageById(messageId);
|
await _client.chatPersistenceClient?.deleteMessageById(messageId);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
@@ -1521,7 +1497,7 @@ class ChannelClientState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _checkExpiredAttachmentMessages(ChannelState channelState) {
|
void _checkExpiredAttachmentMessages(ChannelState channelState) async {
|
||||||
final expiredAttachmentMessagesId = channelState.messages
|
final expiredAttachmentMessagesId = channelState.messages
|
||||||
.where((m) =>
|
.where((m) =>
|
||||||
!_updatedMessagesIds.contains(m.id) &&
|
!_updatedMessagesIds.contains(m.id) &&
|
||||||
@@ -1532,20 +1508,24 @@ class ChannelClientState {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
final uri = Uri.parse(url);
|
final uri = Uri.parse(url);
|
||||||
if (uri.host != 'stream-io-cdn.com' ||
|
if (!uri.host.endsWith('stream-io-cdn.com') ||
|
||||||
uri.queryParameters['Expires'] == null) {
|
uri.queryParameters['Expires'] == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
final expiration =
|
final secondsFromEpoch =
|
||||||
DateTime.parse(uri.queryParameters['Expires']!);
|
int.parse(uri.queryParameters['Expires']!);
|
||||||
|
final expiration = DateTime.fromMillisecondsSinceEpoch(
|
||||||
|
secondsFromEpoch * 1000);
|
||||||
return expiration.isBefore(DateTime.now());
|
return expiration.isBefore(DateTime.now());
|
||||||
}) ==
|
}) ==
|
||||||
true)
|
true)
|
||||||
.map((e) => e.id)
|
.map((e) => e.id)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
if (expiredAttachmentMessagesId.isNotEmpty == true) {
|
if (expiredAttachmentMessagesId.isNotEmpty == true) {
|
||||||
_channel.getMessagesById(expiredAttachmentMessagesId);
|
await _channel._initializedCompleter.future;
|
||||||
_updatedMessagesIds.addAll(expiredAttachmentMessagesId);
|
_updatedMessagesIds.addAll(expiredAttachmentMessagesId);
|
||||||
|
_channel.getMessagesById(expiredAttachmentMessagesId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1704,7 +1684,7 @@ class ChannelClientState {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a message to this channel.
|
/// Add a [message] to this [channelState].
|
||||||
void addMessage(Message message) {
|
void addMessage(Message message) {
|
||||||
if (message.parentId == null || message.showInChannel == true) {
|
if (message.parentId == null || message.showInChannel == true) {
|
||||||
final newMessages = List<Message>.from(_channelState.messages);
|
final newMessages = List<Message>.from(_channelState.messages);
|
||||||
@@ -1735,6 +1715,35 @@ class ChannelClientState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Remove a [message] from this [channelState].
|
||||||
|
void removeMessage(Message message) {
|
||||||
|
final parentId = message.parentId;
|
||||||
|
// i.e. it's a thread message
|
||||||
|
// 1. Remove the thread message
|
||||||
|
// 2. Reduce total reply count of parent message
|
||||||
|
if (parentId != null) {
|
||||||
|
final allMessages = [...messages];
|
||||||
|
final parentMessage = allMessages.firstWhereOrNull(
|
||||||
|
(it) => it.id == parentId,
|
||||||
|
);
|
||||||
|
|
||||||
|
// return if message not available in the memory
|
||||||
|
if (parentMessage == null) return;
|
||||||
|
final replyCount = parentMessage.replyCount;
|
||||||
|
// return if reply count is null or zero
|
||||||
|
if (replyCount == null || replyCount == 0) return;
|
||||||
|
|
||||||
|
addMessage(parentMessage.copyWith(replyCount: replyCount - 1));
|
||||||
|
updateThreadInfo(parentId, threads[parentId]!..remove(message));
|
||||||
|
} else {
|
||||||
|
// Remove regular message
|
||||||
|
final allMessages = [...messages];
|
||||||
|
if (allMessages.remove(message)) {
|
||||||
|
_channelState = _channelState.copyWith(messages: allMessages);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void _listenReadEvents() {
|
void _listenReadEvents() {
|
||||||
if (_channelState.channel?.config.readEvents == false) {
|
if (_channelState.channel?.config.readEvents == false) {
|
||||||
return;
|
return;
|
||||||
@@ -1862,15 +1871,11 @@ class ChannelClientState {
|
|||||||
|
|
||||||
if (newThreads.containsKey(parentId)) {
|
if (newThreads.containsKey(parentId)) {
|
||||||
newThreads[parentId] = [
|
newThreads[parentId] = [
|
||||||
...newThreads[parentId]
|
|
||||||
?.where(
|
|
||||||
(newMessage) => !messages.any((m) => m.id == newMessage.id))
|
|
||||||
.toList() ??
|
|
||||||
[],
|
|
||||||
...messages,
|
...messages,
|
||||||
];
|
...newThreads[parentId]!.where(
|
||||||
|
(newMessage) => !messages.any((m) => m.id == newMessage.id),
|
||||||
newThreads[parentId]!.sort(_sortByCreatedAt);
|
),
|
||||||
|
]..sort(_sortByCreatedAt);
|
||||||
} else {
|
} else {
|
||||||
newThreads[parentId] = messages;
|
newThreads[parentId] = messages;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -143,9 +143,6 @@ class StreamChatClient {
|
|||||||
|
|
||||||
late final RetryPolicy _retryPolicy;
|
late final RetryPolicy _retryPolicy;
|
||||||
|
|
||||||
/// sync state of the channels present inside state, defaults to false
|
|
||||||
bool _synced = false;
|
|
||||||
|
|
||||||
/// the last dateTime at the which all the channels were synced
|
/// the last dateTime at the which all the channels were synced
|
||||||
DateTime? _lastSyncedAt;
|
DateTime? _lastSyncedAt;
|
||||||
|
|
||||||
@@ -406,10 +403,6 @@ class StreamChatClient {
|
|||||||
if (event.type == EventType.healthCheck) {
|
if (event.type == EventType.healthCheck) {
|
||||||
return _handleHealthCheckEvent(event);
|
return _handleHealthCheckEvent(event);
|
||||||
}
|
}
|
||||||
if (!event.isLocal && _synced) {
|
|
||||||
_lastSyncedAt = event.createdAt;
|
|
||||||
_chatPersistenceClient?.updateLastSyncAt(event.createdAt);
|
|
||||||
}
|
|
||||||
state.updateUser(event.user);
|
state.updateUser(event.user);
|
||||||
return _eventController.add(event);
|
return _eventController.add(event);
|
||||||
}
|
}
|
||||||
@@ -438,8 +431,6 @@ class StreamChatClient {
|
|||||||
type: EventType.connectionRecovered,
|
type: EventType.connectionRecovered,
|
||||||
online: true,
|
online: true,
|
||||||
));
|
));
|
||||||
} else {
|
|
||||||
_synced = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -464,13 +455,11 @@ class StreamChatClient {
|
|||||||
Future<void> sync({List<String>? cids, DateTime? lastSyncAt}) async {
|
Future<void> sync({List<String>? cids, DateTime? lastSyncAt}) async {
|
||||||
cids ??= await _chatPersistenceClient?.getChannelCids();
|
cids ??= await _chatPersistenceClient?.getChannelCids();
|
||||||
if (cids == null || cids.isEmpty) {
|
if (cids == null || cids.isEmpty) {
|
||||||
_synced = true;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
lastSyncAt ??= await _chatPersistenceClient?.getLastSyncAt();
|
lastSyncAt ??= await _chatPersistenceClient?.getLastSyncAt();
|
||||||
if (lastSyncAt == null) {
|
if (lastSyncAt == null) {
|
||||||
_synced = true;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -488,12 +477,10 @@ class StreamChatClient {
|
|||||||
handleEvent(event);
|
handleEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
_synced = true;
|
|
||||||
final now = DateTime.now();
|
final now = DateTime.now();
|
||||||
_lastSyncedAt = now;
|
_lastSyncedAt = now;
|
||||||
_chatPersistenceClient?.updateLastSyncAt(now);
|
_chatPersistenceClient?.updateLastSyncAt(now);
|
||||||
} catch (e, stk) {
|
} catch (e, stk) {
|
||||||
_synced = false;
|
|
||||||
logger.severe('Error during sync', e, stk);
|
logger.severe('Error during sync', e, stk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1325,6 +1312,7 @@ class StreamChatClient {
|
|||||||
// resetting state
|
// resetting state
|
||||||
state.dispose();
|
state.dispose();
|
||||||
state = ClientState(this);
|
state = ClientState(this);
|
||||||
|
_lastSyncedAt = null;
|
||||||
|
|
||||||
// resetting credentials
|
// resetting credentials
|
||||||
_tokenManager.reset();
|
_tokenManager.reset();
|
||||||
@@ -1366,26 +1354,21 @@ class ClientState {
|
|||||||
.where((event) =>
|
.where((event) =>
|
||||||
event.me != null && event.type != EventType.healthCheck)
|
event.me != null && event.type != EventType.healthCheck)
|
||||||
.map((e) => e.me!)
|
.map((e) => e.me!)
|
||||||
.listen((user) {
|
.listen((user) => currentUser = currentUser?.merge(user) ?? user),
|
||||||
currentUser = currentUser?.merge(user) ?? user;
|
|
||||||
final totalUnreadCount = user.totalUnreadCount;
|
|
||||||
_totalUnreadCountController.add(totalUnreadCount);
|
|
||||||
|
|
||||||
final unreadChannels = user.unreadChannels;
|
|
||||||
if (unreadChannels != null) {
|
|
||||||
_unreadChannelsController.add(unreadChannels);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
_client
|
_client
|
||||||
.on()
|
.on()
|
||||||
.map((event) => event.unreadChannels)
|
.map((event) => event.unreadChannels)
|
||||||
.whereType<int>()
|
.whereType<int>()
|
||||||
.listen(_unreadChannelsController.add),
|
.listen((count) {
|
||||||
|
currentUser = currentUser?.copyWith(unreadChannels: count);
|
||||||
|
}),
|
||||||
_client
|
_client
|
||||||
.on()
|
.on()
|
||||||
.map((event) => event.totalUnreadCount)
|
.map((event) => event.totalUnreadCount)
|
||||||
.whereType<int>()
|
.whereType<int>()
|
||||||
.listen(_totalUnreadCountController.add),
|
.listen((count) {
|
||||||
|
currentUser = currentUser?.copyWith(totalUnreadCount: count);
|
||||||
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
_listenChannelDeleted();
|
_listenChannelDeleted();
|
||||||
@@ -1441,6 +1424,7 @@ class ClientState {
|
|||||||
/// Sets the user currently interacting with the client
|
/// Sets the user currently interacting with the client
|
||||||
/// note: this fully overrides the [currentUser]
|
/// note: this fully overrides the [currentUser]
|
||||||
set currentUser(OwnUser? user) {
|
set currentUser(OwnUser? user) {
|
||||||
|
_computeUnreadCounts(user);
|
||||||
_currentUserController.add(user);
|
_currentUserController.add(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1506,6 +1490,18 @@ class ClientState {
|
|||||||
_channelsController.add(newChannels);
|
_channelsController.add(newChannels);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _computeUnreadCounts(OwnUser? user) {
|
||||||
|
final totalUnreadCount = user?.totalUnreadCount;
|
||||||
|
if (totalUnreadCount != null) {
|
||||||
|
_totalUnreadCountController.add(totalUnreadCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
final unreadChannels = user?.unreadChannels;
|
||||||
|
if (unreadChannels != null) {
|
||||||
|
_unreadChannelsController.add(unreadChannels);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final _channelsController = BehaviorSubject<Map<String, Channel>>.seeded({});
|
final _channelsController = BehaviorSubject<Map<String, Channel>>.seeded({});
|
||||||
final _currentUserController = BehaviorSubject<OwnUser?>();
|
final _currentUserController = BehaviorSubject<OwnUser?>();
|
||||||
final _usersController = BehaviorSubject<Map<String, User>>.seeded({});
|
final _usersController = BehaviorSubject<Map<String, User>>.seeded({});
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import 'package:rxdart/rxdart.dart';
|
|||||||
import 'package:stream_chat/src/client/channel.dart';
|
import 'package:stream_chat/src/client/channel.dart';
|
||||||
import 'package:stream_chat/src/client/retry_policy.dart';
|
import 'package:stream_chat/src/client/retry_policy.dart';
|
||||||
import 'package:stream_chat/src/core/error/error.dart';
|
import 'package:stream_chat/src/core/error/error.dart';
|
||||||
import 'package:stream_chat/src/event_type.dart';
|
|
||||||
import 'package:stream_chat/src/core/models/message.dart';
|
import 'package:stream_chat/src/core/models/message.dart';
|
||||||
|
import 'package:stream_chat/src/event_type.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat/stream_chat.dart';
|
||||||
|
|
||||||
/// The retry queue associated to a channel
|
/// The retry queue associated to a channel
|
||||||
|
|||||||
@@ -36,6 +36,10 @@ class GeneralApi {
|
|||||||
PaginationParams? pagination,
|
PaginationParams? pagination,
|
||||||
Filter? messageFilters,
|
Filter? messageFilters,
|
||||||
}) async {
|
}) async {
|
||||||
|
assert(
|
||||||
|
pagination?.offset == null || pagination?.offset == 0 || sort == null,
|
||||||
|
'Cannot specify `offset` with `sort` parameter',
|
||||||
|
);
|
||||||
assert(() {
|
assert(() {
|
||||||
if (query == null && messageFilters == null) {
|
if (query == null && messageFilters == null) {
|
||||||
throw ArgumentError('Provide at least `query` or `messageFilters`');
|
throw ArgumentError('Provide at least `query` or `messageFilters`');
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ class SortOption<T> {
|
|||||||
/// ```
|
/// ```
|
||||||
const SortOption(
|
const SortOption(
|
||||||
this.field, {
|
this.field, {
|
||||||
this.direction = DESC,
|
this.direction = SortOption.DESC,
|
||||||
this.comparator,
|
this.comparator,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -60,12 +60,16 @@ class PaginationParams extends Equatable {
|
|||||||
/// ```
|
/// ```
|
||||||
const PaginationParams({
|
const PaginationParams({
|
||||||
this.limit = 10,
|
this.limit = 10,
|
||||||
this.offset = 0,
|
this.offset,
|
||||||
|
this.next,
|
||||||
this.greaterThan,
|
this.greaterThan,
|
||||||
this.greaterThanOrEqual,
|
this.greaterThanOrEqual,
|
||||||
this.lessThan,
|
this.lessThan,
|
||||||
this.lessThanOrEqual,
|
this.lessThanOrEqual,
|
||||||
});
|
}) : assert(
|
||||||
|
offset == null || offset == 0 || next == null,
|
||||||
|
'Cannot specify non-zero `offset` with `next` parameter',
|
||||||
|
);
|
||||||
|
|
||||||
/// Create a new instance from a json
|
/// Create a new instance from a json
|
||||||
factory PaginationParams.fromJson(Map<String, dynamic> json) =>
|
factory PaginationParams.fromJson(Map<String, dynamic> json) =>
|
||||||
@@ -75,7 +79,10 @@ class PaginationParams extends Equatable {
|
|||||||
final int limit;
|
final int limit;
|
||||||
|
|
||||||
/// The offset of requesting items.
|
/// The offset of requesting items.
|
||||||
final int offset;
|
final int? offset;
|
||||||
|
|
||||||
|
/// A key used to paginate.
|
||||||
|
final String? next;
|
||||||
|
|
||||||
/// Filter on ids greater than the given value.
|
/// Filter on ids greater than the given value.
|
||||||
@JsonKey(name: 'id_gt')
|
@JsonKey(name: 'id_gt')
|
||||||
@@ -100,6 +107,7 @@ class PaginationParams extends Equatable {
|
|||||||
PaginationParams copyWith({
|
PaginationParams copyWith({
|
||||||
int? limit,
|
int? limit,
|
||||||
int? offset,
|
int? offset,
|
||||||
|
String? next,
|
||||||
String? greaterThan,
|
String? greaterThan,
|
||||||
String? greaterThanOrEqual,
|
String? greaterThanOrEqual,
|
||||||
String? lessThan,
|
String? lessThan,
|
||||||
@@ -108,6 +116,7 @@ class PaginationParams extends Equatable {
|
|||||||
PaginationParams(
|
PaginationParams(
|
||||||
limit: limit ?? this.limit,
|
limit: limit ?? this.limit,
|
||||||
offset: offset ?? this.offset,
|
offset: offset ?? this.offset,
|
||||||
|
next: next ?? this.next,
|
||||||
greaterThan: greaterThan ?? this.greaterThan,
|
greaterThan: greaterThan ?? this.greaterThan,
|
||||||
greaterThanOrEqual: greaterThanOrEqual ?? this.greaterThanOrEqual,
|
greaterThanOrEqual: greaterThanOrEqual ?? this.greaterThanOrEqual,
|
||||||
lessThan: lessThan ?? this.lessThan,
|
lessThan: lessThan ?? this.lessThan,
|
||||||
@@ -118,6 +127,7 @@ class PaginationParams extends Equatable {
|
|||||||
List<Object?> get props => [
|
List<Object?> get props => [
|
||||||
limit,
|
limit,
|
||||||
offset,
|
offset,
|
||||||
|
next,
|
||||||
greaterThan,
|
greaterThan,
|
||||||
greaterThanOrEqual,
|
greaterThanOrEqual,
|
||||||
lessThan,
|
lessThan,
|
||||||
|
|||||||
@@ -6,12 +6,11 @@ part of 'requests.dart';
|
|||||||
// JsonSerializableGenerator
|
// JsonSerializableGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
SortOption<T> _$SortOptionFromJson<T>(Map<String, dynamic> json) {
|
SortOption<T> _$SortOptionFromJson<T>(Map<String, dynamic> json) =>
|
||||||
return SortOption<T>(
|
SortOption<T>(
|
||||||
json['field'] as String,
|
json['field'] as String,
|
||||||
direction: json['direction'] as int,
|
direction: json['direction'] as int? ?? SortOption.DESC,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> _$SortOptionToJson<T>(SortOption<T> instance) =>
|
Map<String, dynamic> _$SortOptionToJson<T>(SortOption<T> instance) =>
|
||||||
<String, dynamic>{
|
<String, dynamic>{
|
||||||
@@ -19,21 +18,20 @@ Map<String, dynamic> _$SortOptionToJson<T>(SortOption<T> instance) =>
|
|||||||
'direction': instance.direction,
|
'direction': instance.direction,
|
||||||
};
|
};
|
||||||
|
|
||||||
PaginationParams _$PaginationParamsFromJson(Map<String, dynamic> json) {
|
PaginationParams _$PaginationParamsFromJson(Map<String, dynamic> json) =>
|
||||||
return PaginationParams(
|
PaginationParams(
|
||||||
limit: json['limit'] as int,
|
limit: json['limit'] as int? ?? 10,
|
||||||
offset: json['offset'] as int,
|
offset: json['offset'] as int?,
|
||||||
greaterThan: json['id_gt'] as String?,
|
next: json['next'] as String?,
|
||||||
greaterThanOrEqual: json['id_gte'] as String?,
|
greaterThan: json['id_gt'] as String?,
|
||||||
lessThan: json['id_lt'] as String?,
|
greaterThanOrEqual: json['id_gte'] as String?,
|
||||||
lessThanOrEqual: json['id_lte'] as String?,
|
lessThan: json['id_lt'] as String?,
|
||||||
);
|
lessThanOrEqual: json['id_lte'] as String?,
|
||||||
}
|
);
|
||||||
|
|
||||||
Map<String, dynamic> _$PaginationParamsToJson(PaginationParams instance) {
|
Map<String, dynamic> _$PaginationParamsToJson(PaginationParams instance) {
|
||||||
final val = <String, dynamic>{
|
final val = <String, dynamic>{
|
||||||
'limit': instance.limit,
|
'limit': instance.limit,
|
||||||
'offset': instance.offset,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void writeNotNull(String key, dynamic value) {
|
void writeNotNull(String key, dynamic value) {
|
||||||
@@ -42,6 +40,8 @@ Map<String, dynamic> _$PaginationParamsToJson(PaginationParams instance) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
writeNotNull('offset', instance.offset);
|
||||||
|
writeNotNull('next', instance.next);
|
||||||
writeNotNull('id_gt', instance.greaterThan);
|
writeNotNull('id_gt', instance.greaterThan);
|
||||||
writeNotNull('id_gte', instance.greaterThanOrEqual);
|
writeNotNull('id_gte', instance.greaterThanOrEqual);
|
||||||
writeNotNull('id_lt', instance.lessThan);
|
writeNotNull('id_lt', instance.lessThan);
|
||||||
|
|||||||
@@ -253,6 +253,12 @@ class SearchMessagesResponse extends _BaseResponse {
|
|||||||
@JsonKey(defaultValue: [])
|
@JsonKey(defaultValue: [])
|
||||||
late List<GetMessageResponse> results;
|
late List<GetMessageResponse> results;
|
||||||
|
|
||||||
|
/// Message id of where to start searching from for next [results]
|
||||||
|
late String? next;
|
||||||
|
|
||||||
|
/// Message id of where to start searching from for previous [results]
|
||||||
|
late String? previous;
|
||||||
|
|
||||||
/// Create a new instance from a json
|
/// Create a new instance from a json
|
||||||
static SearchMessagesResponse fromJson(Map<String, dynamic> json) =>
|
static SearchMessagesResponse fromJson(Map<String, dynamic> json) =>
|
||||||
_$SearchMessagesResponseFromJson(json);
|
_$SearchMessagesResponseFromJson(json);
|
||||||
|
|||||||
@@ -6,14 +6,13 @@ part of 'responses.dart';
|
|||||||
// JsonSerializableGenerator
|
// JsonSerializableGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
ErrorResponse _$ErrorResponseFromJson(Map<String, dynamic> json) {
|
ErrorResponse _$ErrorResponseFromJson(Map<String, dynamic> json) =>
|
||||||
return ErrorResponse()
|
ErrorResponse()
|
||||||
..duration = json['duration'] as String?
|
..duration = json['duration'] as String?
|
||||||
..code = json['code'] as int?
|
..code = json['code'] as int?
|
||||||
..message = json['message'] as String?
|
..message = json['message'] as String?
|
||||||
..statusCode = json['StatusCode'] as int?
|
..statusCode = json['StatusCode'] as int?
|
||||||
..moreInfo = json['more_info'] as String?;
|
..moreInfo = json['more_info'] as String?;
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> _$ErrorResponseToJson(ErrorResponse instance) =>
|
Map<String, dynamic> _$ErrorResponseToJson(ErrorResponse instance) =>
|
||||||
<String, dynamic>{
|
<String, dynamic>{
|
||||||
@@ -24,273 +23,253 @@ Map<String, dynamic> _$ErrorResponseToJson(ErrorResponse instance) =>
|
|||||||
'more_info': instance.moreInfo,
|
'more_info': instance.moreInfo,
|
||||||
};
|
};
|
||||||
|
|
||||||
SyncResponse _$SyncResponseFromJson(Map<String, dynamic> json) {
|
SyncResponse _$SyncResponseFromJson(Map<String, dynamic> json) => SyncResponse()
|
||||||
return SyncResponse()
|
..duration = json['duration'] as String?
|
||||||
..duration = json['duration'] as String?
|
..events = (json['events'] as List<dynamic>?)
|
||||||
..events = (json['events'] as List<dynamic>?)
|
?.map((e) => Event.fromJson(e as Map<String, dynamic>))
|
||||||
?.map((e) => Event.fromJson(e as Map<String, dynamic>))
|
.toList() ??
|
||||||
.toList() ??
|
[];
|
||||||
[];
|
|
||||||
}
|
|
||||||
|
|
||||||
QueryChannelsResponse _$QueryChannelsResponseFromJson(
|
QueryChannelsResponse _$QueryChannelsResponseFromJson(
|
||||||
Map<String, dynamic> json) {
|
Map<String, dynamic> json) =>
|
||||||
return QueryChannelsResponse()
|
QueryChannelsResponse()
|
||||||
..duration = json['duration'] as String?
|
..duration = json['duration'] as String?
|
||||||
..channels = (json['channels'] as List<dynamic>?)
|
..channels = (json['channels'] as List<dynamic>?)
|
||||||
?.map((e) => ChannelState.fromJson(e as Map<String, dynamic>))
|
?.map((e) => ChannelState.fromJson(e as Map<String, dynamic>))
|
||||||
.toList() ??
|
.toList() ??
|
||||||
[];
|
[];
|
||||||
}
|
|
||||||
|
|
||||||
TranslateMessageResponse _$TranslateMessageResponseFromJson(
|
TranslateMessageResponse _$TranslateMessageResponseFromJson(
|
||||||
Map<String, dynamic> json) {
|
Map<String, dynamic> json) =>
|
||||||
return TranslateMessageResponse()
|
TranslateMessageResponse()
|
||||||
..duration = json['duration'] as String?
|
..duration = json['duration'] as String?
|
||||||
..message = Message.fromJson(json['message'] as Map<String, dynamic>);
|
..message = Message.fromJson(json['message'] as Map<String, dynamic>);
|
||||||
}
|
|
||||||
|
|
||||||
QueryMembersResponse _$QueryMembersResponseFromJson(Map<String, dynamic> json) {
|
QueryMembersResponse _$QueryMembersResponseFromJson(
|
||||||
return QueryMembersResponse()
|
Map<String, dynamic> json) =>
|
||||||
..duration = json['duration'] as String?
|
QueryMembersResponse()
|
||||||
..members = (json['members'] as List<dynamic>?)
|
..duration = json['duration'] as String?
|
||||||
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
|
..members = (json['members'] as List<dynamic>?)
|
||||||
.toList() ??
|
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
|
||||||
[];
|
.toList() ??
|
||||||
}
|
[];
|
||||||
|
|
||||||
QueryUsersResponse _$QueryUsersResponseFromJson(Map<String, dynamic> json) {
|
QueryUsersResponse _$QueryUsersResponseFromJson(Map<String, dynamic> json) =>
|
||||||
return QueryUsersResponse()
|
QueryUsersResponse()
|
||||||
..duration = json['duration'] as String?
|
..duration = json['duration'] as String?
|
||||||
..users = (json['users'] as List<dynamic>?)
|
..users = (json['users'] as List<dynamic>?)
|
||||||
?.map((e) => User.fromJson(e as Map<String, dynamic>))
|
?.map((e) => User.fromJson(e as Map<String, dynamic>))
|
||||||
.toList() ??
|
.toList() ??
|
||||||
[];
|
[];
|
||||||
}
|
|
||||||
|
|
||||||
QueryReactionsResponse _$QueryReactionsResponseFromJson(
|
QueryReactionsResponse _$QueryReactionsResponseFromJson(
|
||||||
Map<String, dynamic> json) {
|
Map<String, dynamic> json) =>
|
||||||
return QueryReactionsResponse()
|
QueryReactionsResponse()
|
||||||
..duration = json['duration'] as String?
|
..duration = json['duration'] as String?
|
||||||
..reactions = (json['reactions'] as List<dynamic>?)
|
..reactions = (json['reactions'] as List<dynamic>?)
|
||||||
?.map((e) => Reaction.fromJson(e as Map<String, dynamic>))
|
?.map((e) => Reaction.fromJson(e as Map<String, dynamic>))
|
||||||
.toList() ??
|
.toList() ??
|
||||||
[];
|
[];
|
||||||
}
|
|
||||||
|
|
||||||
QueryRepliesResponse _$QueryRepliesResponseFromJson(Map<String, dynamic> json) {
|
QueryRepliesResponse _$QueryRepliesResponseFromJson(
|
||||||
return QueryRepliesResponse()
|
Map<String, dynamic> json) =>
|
||||||
..duration = json['duration'] as String?
|
QueryRepliesResponse()
|
||||||
..messages = (json['messages'] as List<dynamic>?)
|
..duration = json['duration'] as String?
|
||||||
?.map((e) => Message.fromJson(e as Map<String, dynamic>))
|
..messages = (json['messages'] as List<dynamic>?)
|
||||||
.toList() ??
|
?.map((e) => Message.fromJson(e as Map<String, dynamic>))
|
||||||
[];
|
.toList() ??
|
||||||
}
|
[];
|
||||||
|
|
||||||
ListDevicesResponse _$ListDevicesResponseFromJson(Map<String, dynamic> json) {
|
ListDevicesResponse _$ListDevicesResponseFromJson(Map<String, dynamic> json) =>
|
||||||
return ListDevicesResponse()
|
ListDevicesResponse()
|
||||||
..duration = json['duration'] as String?
|
..duration = json['duration'] as String?
|
||||||
..devices = (json['devices'] as List<dynamic>?)
|
..devices = (json['devices'] as List<dynamic>?)
|
||||||
?.map((e) => Device.fromJson(e as Map<String, dynamic>))
|
?.map((e) => Device.fromJson(e as Map<String, dynamic>))
|
||||||
.toList() ??
|
.toList() ??
|
||||||
[];
|
[];
|
||||||
}
|
|
||||||
|
|
||||||
SendFileResponse _$SendFileResponseFromJson(Map<String, dynamic> json) {
|
SendFileResponse _$SendFileResponseFromJson(Map<String, dynamic> json) =>
|
||||||
return SendFileResponse()
|
SendFileResponse()
|
||||||
..duration = json['duration'] as String?
|
..duration = json['duration'] as String?
|
||||||
..file = json['file'] as String;
|
..file = json['file'] as String;
|
||||||
}
|
|
||||||
|
|
||||||
SendImageResponse _$SendImageResponseFromJson(Map<String, dynamic> json) {
|
SendImageResponse _$SendImageResponseFromJson(Map<String, dynamic> json) =>
|
||||||
return SendImageResponse()
|
SendImageResponse()
|
||||||
..duration = json['duration'] as String?
|
..duration = json['duration'] as String?
|
||||||
..file = json['file'] as String;
|
..file = json['file'] as String;
|
||||||
}
|
|
||||||
|
|
||||||
SendReactionResponse _$SendReactionResponseFromJson(Map<String, dynamic> json) {
|
SendReactionResponse _$SendReactionResponseFromJson(
|
||||||
return SendReactionResponse()
|
Map<String, dynamic> json) =>
|
||||||
..duration = json['duration'] as String?
|
SendReactionResponse()
|
||||||
..message = Message.fromJson(json['message'] as Map<String, dynamic>)
|
..duration = json['duration'] as String?
|
||||||
..reaction = Reaction.fromJson(json['reaction'] as Map<String, dynamic>);
|
..message = Message.fromJson(json['message'] as Map<String, dynamic>)
|
||||||
}
|
..reaction = Reaction.fromJson(json['reaction'] as Map<String, dynamic>);
|
||||||
|
|
||||||
ConnectGuestUserResponse _$ConnectGuestUserResponseFromJson(
|
ConnectGuestUserResponse _$ConnectGuestUserResponseFromJson(
|
||||||
Map<String, dynamic> json) {
|
Map<String, dynamic> json) =>
|
||||||
return ConnectGuestUserResponse()
|
ConnectGuestUserResponse()
|
||||||
..duration = json['duration'] as String?
|
..duration = json['duration'] as String?
|
||||||
..accessToken = json['access_token'] as String
|
..accessToken = json['access_token'] as String
|
||||||
..user = User.fromJson(json['user'] as Map<String, dynamic>);
|
..user = User.fromJson(json['user'] as Map<String, dynamic>);
|
||||||
}
|
|
||||||
|
|
||||||
UpdateUsersResponse _$UpdateUsersResponseFromJson(Map<String, dynamic> json) {
|
UpdateUsersResponse _$UpdateUsersResponseFromJson(Map<String, dynamic> json) =>
|
||||||
return UpdateUsersResponse()
|
UpdateUsersResponse()
|
||||||
..duration = json['duration'] as String?
|
..duration = json['duration'] as String?
|
||||||
..users = (json['users'] as Map<String, dynamic>?)?.map(
|
..users = (json['users'] as Map<String, dynamic>?)?.map(
|
||||||
(k, e) => MapEntry(k, User.fromJson(e as Map<String, dynamic>)),
|
(k, e) => MapEntry(k, User.fromJson(e as Map<String, dynamic>)),
|
||||||
) ??
|
) ??
|
||||||
{};
|
{};
|
||||||
}
|
|
||||||
|
|
||||||
UpdateMessageResponse _$UpdateMessageResponseFromJson(
|
UpdateMessageResponse _$UpdateMessageResponseFromJson(
|
||||||
Map<String, dynamic> json) {
|
Map<String, dynamic> json) =>
|
||||||
return UpdateMessageResponse()
|
UpdateMessageResponse()
|
||||||
..duration = json['duration'] as String?
|
..duration = json['duration'] as String?
|
||||||
..message = Message.fromJson(json['message'] as Map<String, dynamic>);
|
..message = Message.fromJson(json['message'] as Map<String, dynamic>);
|
||||||
}
|
|
||||||
|
|
||||||
SendMessageResponse _$SendMessageResponseFromJson(Map<String, dynamic> json) {
|
SendMessageResponse _$SendMessageResponseFromJson(Map<String, dynamic> json) =>
|
||||||
return SendMessageResponse()
|
SendMessageResponse()
|
||||||
..duration = json['duration'] as String?
|
..duration = json['duration'] as String?
|
||||||
..message = Message.fromJson(json['message'] as Map<String, dynamic>);
|
..message = Message.fromJson(json['message'] as Map<String, dynamic>);
|
||||||
}
|
|
||||||
|
|
||||||
GetMessageResponse _$GetMessageResponseFromJson(Map<String, dynamic> json) {
|
GetMessageResponse _$GetMessageResponseFromJson(Map<String, dynamic> json) =>
|
||||||
return GetMessageResponse()
|
GetMessageResponse()
|
||||||
..duration = json['duration'] as String?
|
..duration = json['duration'] as String?
|
||||||
..message = Message.fromJson(json['message'] as Map<String, dynamic>)
|
..message = Message.fromJson(json['message'] as Map<String, dynamic>)
|
||||||
..channel = json['channel'] == null
|
..channel = json['channel'] == null
|
||||||
? null
|
? null
|
||||||
: ChannelModel.fromJson(json['channel'] as Map<String, dynamic>);
|
: ChannelModel.fromJson(json['channel'] as Map<String, dynamic>);
|
||||||
}
|
|
||||||
|
|
||||||
SearchMessagesResponse _$SearchMessagesResponseFromJson(
|
SearchMessagesResponse _$SearchMessagesResponseFromJson(
|
||||||
Map<String, dynamic> json) {
|
Map<String, dynamic> json) =>
|
||||||
return SearchMessagesResponse()
|
SearchMessagesResponse()
|
||||||
..duration = json['duration'] as String?
|
..duration = json['duration'] as String?
|
||||||
..results = (json['results'] as List<dynamic>?)
|
..results = (json['results'] as List<dynamic>?)
|
||||||
?.map((e) => GetMessageResponse.fromJson(e as Map<String, dynamic>))
|
?.map(
|
||||||
.toList() ??
|
(e) => GetMessageResponse.fromJson(e as Map<String, dynamic>))
|
||||||
[];
|
.toList() ??
|
||||||
}
|
[]
|
||||||
|
..next = json['next'] as String?
|
||||||
|
..previous = json['previous'] as String?;
|
||||||
|
|
||||||
GetMessagesByIdResponse _$GetMessagesByIdResponseFromJson(
|
GetMessagesByIdResponse _$GetMessagesByIdResponseFromJson(
|
||||||
Map<String, dynamic> json) {
|
Map<String, dynamic> json) =>
|
||||||
return GetMessagesByIdResponse()
|
GetMessagesByIdResponse()
|
||||||
..duration = json['duration'] as String?
|
..duration = json['duration'] as String?
|
||||||
..messages = (json['messages'] as List<dynamic>?)
|
..messages = (json['messages'] as List<dynamic>?)
|
||||||
?.map((e) => Message.fromJson(e as Map<String, dynamic>))
|
?.map((e) => Message.fromJson(e as Map<String, dynamic>))
|
||||||
.toList() ??
|
.toList() ??
|
||||||
[];
|
[];
|
||||||
}
|
|
||||||
|
|
||||||
UpdateChannelResponse _$UpdateChannelResponseFromJson(
|
UpdateChannelResponse _$UpdateChannelResponseFromJson(
|
||||||
Map<String, dynamic> json) {
|
Map<String, dynamic> json) =>
|
||||||
return UpdateChannelResponse()
|
UpdateChannelResponse()
|
||||||
..duration = json['duration'] as String?
|
..duration = json['duration'] as String?
|
||||||
..channel = ChannelModel.fromJson(json['channel'] as Map<String, dynamic>)
|
..channel = ChannelModel.fromJson(json['channel'] as Map<String, dynamic>)
|
||||||
..members = (json['members'] as List<dynamic>?)
|
..members = (json['members'] as List<dynamic>?)
|
||||||
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
|
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
|
||||||
.toList()
|
.toList()
|
||||||
..message = json['message'] == null
|
..message = json['message'] == null
|
||||||
? null
|
? null
|
||||||
: Message.fromJson(json['message'] as Map<String, dynamic>);
|
: Message.fromJson(json['message'] as Map<String, dynamic>);
|
||||||
}
|
|
||||||
|
|
||||||
PartialUpdateChannelResponse _$PartialUpdateChannelResponseFromJson(
|
PartialUpdateChannelResponse _$PartialUpdateChannelResponseFromJson(
|
||||||
Map<String, dynamic> json) {
|
Map<String, dynamic> json) =>
|
||||||
return PartialUpdateChannelResponse()
|
PartialUpdateChannelResponse()
|
||||||
..duration = json['duration'] as String?
|
..duration = json['duration'] as String?
|
||||||
..channel = ChannelModel.fromJson(json['channel'] as Map<String, dynamic>)
|
..channel = ChannelModel.fromJson(json['channel'] as Map<String, dynamic>)
|
||||||
..members = (json['members'] as List<dynamic>?)
|
..members = (json['members'] as List<dynamic>?)
|
||||||
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
|
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
|
||||||
.toList();
|
.toList();
|
||||||
}
|
|
||||||
|
|
||||||
InviteMembersResponse _$InviteMembersResponseFromJson(
|
InviteMembersResponse _$InviteMembersResponseFromJson(
|
||||||
Map<String, dynamic> json) {
|
Map<String, dynamic> json) =>
|
||||||
return InviteMembersResponse()
|
InviteMembersResponse()
|
||||||
..duration = json['duration'] as String?
|
..duration = json['duration'] as String?
|
||||||
..channel = ChannelModel.fromJson(json['channel'] as Map<String, dynamic>)
|
..channel = ChannelModel.fromJson(json['channel'] as Map<String, dynamic>)
|
||||||
..members = (json['members'] as List<dynamic>?)
|
..members = (json['members'] as List<dynamic>?)
|
||||||
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
|
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
|
||||||
.toList() ??
|
.toList() ??
|
||||||
[]
|
[]
|
||||||
..message = json['message'] == null
|
..message = json['message'] == null
|
||||||
? null
|
? null
|
||||||
: Message.fromJson(json['message'] as Map<String, dynamic>);
|
: Message.fromJson(json['message'] as Map<String, dynamic>);
|
||||||
}
|
|
||||||
|
|
||||||
RemoveMembersResponse _$RemoveMembersResponseFromJson(
|
RemoveMembersResponse _$RemoveMembersResponseFromJson(
|
||||||
Map<String, dynamic> json) {
|
Map<String, dynamic> json) =>
|
||||||
return RemoveMembersResponse()
|
RemoveMembersResponse()
|
||||||
..duration = json['duration'] as String?
|
..duration = json['duration'] as String?
|
||||||
..channel = ChannelModel.fromJson(json['channel'] as Map<String, dynamic>)
|
..channel = ChannelModel.fromJson(json['channel'] as Map<String, dynamic>)
|
||||||
..members = (json['members'] as List<dynamic>?)
|
..members = (json['members'] as List<dynamic>?)
|
||||||
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
|
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
|
||||||
.toList() ??
|
.toList() ??
|
||||||
[]
|
[]
|
||||||
..message = json['message'] == null
|
..message = json['message'] == null
|
||||||
? null
|
? null
|
||||||
: Message.fromJson(json['message'] as Map<String, dynamic>);
|
: Message.fromJson(json['message'] as Map<String, dynamic>);
|
||||||
}
|
|
||||||
|
|
||||||
SendActionResponse _$SendActionResponseFromJson(Map<String, dynamic> json) {
|
SendActionResponse _$SendActionResponseFromJson(Map<String, dynamic> json) =>
|
||||||
return SendActionResponse()
|
SendActionResponse()
|
||||||
..duration = json['duration'] as String?
|
..duration = json['duration'] as String?
|
||||||
..message = json['message'] == null
|
..message = json['message'] == null
|
||||||
? null
|
? null
|
||||||
: Message.fromJson(json['message'] as Map<String, dynamic>);
|
: Message.fromJson(json['message'] as Map<String, dynamic>);
|
||||||
}
|
|
||||||
|
|
||||||
AddMembersResponse _$AddMembersResponseFromJson(Map<String, dynamic> json) {
|
AddMembersResponse _$AddMembersResponseFromJson(Map<String, dynamic> json) =>
|
||||||
return AddMembersResponse()
|
AddMembersResponse()
|
||||||
..duration = json['duration'] as String?
|
..duration = json['duration'] as String?
|
||||||
..channel = ChannelModel.fromJson(json['channel'] as Map<String, dynamic>)
|
..channel = ChannelModel.fromJson(json['channel'] as Map<String, dynamic>)
|
||||||
..members = (json['members'] as List<dynamic>?)
|
..members = (json['members'] as List<dynamic>?)
|
||||||
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
|
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
|
||||||
.toList() ??
|
.toList() ??
|
||||||
[]
|
[]
|
||||||
..message = json['message'] == null
|
..message = json['message'] == null
|
||||||
? null
|
? null
|
||||||
: Message.fromJson(json['message'] as Map<String, dynamic>);
|
: Message.fromJson(json['message'] as Map<String, dynamic>);
|
||||||
}
|
|
||||||
|
|
||||||
AcceptInviteResponse _$AcceptInviteResponseFromJson(Map<String, dynamic> json) {
|
AcceptInviteResponse _$AcceptInviteResponseFromJson(
|
||||||
return AcceptInviteResponse()
|
Map<String, dynamic> json) =>
|
||||||
..duration = json['duration'] as String?
|
AcceptInviteResponse()
|
||||||
..channel = ChannelModel.fromJson(json['channel'] as Map<String, dynamic>)
|
..duration = json['duration'] as String?
|
||||||
..members = (json['members'] as List<dynamic>?)
|
..channel = ChannelModel.fromJson(json['channel'] as Map<String, dynamic>)
|
||||||
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
|
..members = (json['members'] as List<dynamic>?)
|
||||||
.toList() ??
|
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
|
||||||
[]
|
.toList() ??
|
||||||
..message = json['message'] == null
|
[]
|
||||||
? null
|
..message = json['message'] == null
|
||||||
: Message.fromJson(json['message'] as Map<String, dynamic>);
|
? null
|
||||||
}
|
: Message.fromJson(json['message'] as Map<String, dynamic>);
|
||||||
|
|
||||||
RejectInviteResponse _$RejectInviteResponseFromJson(Map<String, dynamic> json) {
|
RejectInviteResponse _$RejectInviteResponseFromJson(
|
||||||
return RejectInviteResponse()
|
Map<String, dynamic> json) =>
|
||||||
..duration = json['duration'] as String?
|
RejectInviteResponse()
|
||||||
..channel = ChannelModel.fromJson(json['channel'] as Map<String, dynamic>)
|
..duration = json['duration'] as String?
|
||||||
..members = (json['members'] as List<dynamic>?)
|
..channel = ChannelModel.fromJson(json['channel'] as Map<String, dynamic>)
|
||||||
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
|
..members = (json['members'] as List<dynamic>?)
|
||||||
.toList() ??
|
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
|
||||||
[]
|
.toList() ??
|
||||||
..message = json['message'] == null
|
[]
|
||||||
? null
|
..message = json['message'] == null
|
||||||
: Message.fromJson(json['message'] as Map<String, dynamic>);
|
? null
|
||||||
}
|
: Message.fromJson(json['message'] as Map<String, dynamic>);
|
||||||
|
|
||||||
EmptyResponse _$EmptyResponseFromJson(Map<String, dynamic> json) {
|
EmptyResponse _$EmptyResponseFromJson(Map<String, dynamic> json) =>
|
||||||
return EmptyResponse()..duration = json['duration'] as String?;
|
EmptyResponse()..duration = json['duration'] as String?;
|
||||||
}
|
|
||||||
|
|
||||||
ChannelStateResponse _$ChannelStateResponseFromJson(Map<String, dynamic> json) {
|
ChannelStateResponse _$ChannelStateResponseFromJson(
|
||||||
return ChannelStateResponse()
|
Map<String, dynamic> json) =>
|
||||||
..duration = json['duration'] as String?
|
ChannelStateResponse()
|
||||||
..channel = ChannelModel.fromJson(json['channel'] as Map<String, dynamic>)
|
..duration = json['duration'] as String?
|
||||||
..messages = (json['messages'] as List<dynamic>?)
|
..channel = ChannelModel.fromJson(json['channel'] as Map<String, dynamic>)
|
||||||
?.map((e) => Message.fromJson(e as Map<String, dynamic>))
|
..messages = (json['messages'] as List<dynamic>?)
|
||||||
.toList() ??
|
?.map((e) => Message.fromJson(e as Map<String, dynamic>))
|
||||||
[]
|
.toList() ??
|
||||||
..members = (json['members'] as List<dynamic>?)
|
[]
|
||||||
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
|
..members = (json['members'] as List<dynamic>?)
|
||||||
.toList() ??
|
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
|
||||||
[]
|
.toList() ??
|
||||||
..watcherCount = json['watcher_count'] as int? ?? 0
|
[]
|
||||||
..read = (json['read'] as List<dynamic>?)
|
..watcherCount = json['watcher_count'] as int? ?? 0
|
||||||
?.map((e) => Read.fromJson(e as Map<String, dynamic>))
|
..read = (json['read'] as List<dynamic>?)
|
||||||
.toList() ??
|
?.map((e) => Read.fromJson(e as Map<String, dynamic>))
|
||||||
[];
|
.toList() ??
|
||||||
}
|
[];
|
||||||
|
|||||||
@@ -38,7 +38,9 @@ class AuthInterceptor extends Interceptor {
|
|||||||
'Authorization': token.rawValue,
|
'Authorization': token.rawValue,
|
||||||
'stream-auth-type': token.authType.raw,
|
'stream-auth-type': token.authType.raw,
|
||||||
};
|
};
|
||||||
options..queryParameters.addAll(params)..headers.addAll(headers);
|
options
|
||||||
|
..queryParameters.addAll(params)
|
||||||
|
..headers.addAll(headers);
|
||||||
return handler.next(options);
|
return handler.next(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,12 +46,16 @@ class Token extends Equatable {
|
|||||||
/// Creates a [Token] instance from the provided [rawValue] if it's valid.
|
/// Creates a [Token] instance from the provided [rawValue] if it's valid.
|
||||||
factory Token.fromRawValue(String rawValue) {
|
factory Token.fromRawValue(String rawValue) {
|
||||||
final jwtBody = JsonWebToken.unverified(rawValue);
|
final jwtBody = JsonWebToken.unverified(rawValue);
|
||||||
final userId = jwtBody.claims.getTyped<String>('user_id');
|
final userId = jwtBody.claims.getTyped('user_id');
|
||||||
assert(
|
assert(
|
||||||
userId != null,
|
userId != null,
|
||||||
'Invalid `token`, It should contain `user_id`',
|
'Invalid `token`, It should contain `user_id`',
|
||||||
);
|
);
|
||||||
return Token._(rawValue: rawValue, userId: userId!, authType: AuthType.jwt);
|
return Token._(
|
||||||
|
rawValue: rawValue,
|
||||||
|
userId: userId!.toString(),
|
||||||
|
authType: AuthType.jwt,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The token which can be used during the development.
|
/// The token which can be used during the development.
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ class Action {
|
|||||||
final String name;
|
final String name;
|
||||||
|
|
||||||
/// The style of the action
|
/// The style of the action
|
||||||
@JsonKey(defaultValue: 'default')
|
|
||||||
final String style;
|
final String style;
|
||||||
|
|
||||||
/// The test of the action
|
/// The test of the action
|
||||||
|
|||||||
@@ -6,15 +6,13 @@ part of 'action.dart';
|
|||||||
// JsonSerializableGenerator
|
// JsonSerializableGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
Action _$ActionFromJson(Map<String, dynamic> json) {
|
Action _$ActionFromJson(Map<String, dynamic> json) => Action(
|
||||||
return Action(
|
name: json['name'] as String,
|
||||||
name: json['name'] as String,
|
style: json['style'] as String? ?? 'default',
|
||||||
style: json['style'] as String? ?? 'default',
|
text: json['text'] as String,
|
||||||
text: json['text'] as String,
|
type: json['type'] as String,
|
||||||
type: json['type'] as String,
|
value: json['value'] as String?,
|
||||||
value: json['value'] as String?,
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> _$ActionToJson(Action instance) => <String, dynamic>{
|
Map<String, dynamic> _$ActionToJson(Action instance) => <String, dynamic>{
|
||||||
'name': instance.name,
|
'name': instance.name,
|
||||||
|
|||||||
@@ -33,13 +33,20 @@ class Attachment extends Equatable {
|
|||||||
this.authorIcon,
|
this.authorIcon,
|
||||||
this.assetUrl,
|
this.assetUrl,
|
||||||
List<Action>? actions,
|
List<Action>? actions,
|
||||||
this.extraData = const {},
|
Map<String, Object?> extraData = const {},
|
||||||
this.file,
|
this.file,
|
||||||
UploadState? uploadState,
|
UploadState? uploadState,
|
||||||
}) : id = id ?? const Uuid().v4(),
|
}) : id = id ?? const Uuid().v4(),
|
||||||
title = title ?? file?.name,
|
title = title ?? file?.name,
|
||||||
localUri = file?.path != null ? Uri.parse(file!.path!) : null,
|
localUri = file?.path != null ? Uri.parse(file!.path!) : null,
|
||||||
actions = actions ?? [] {
|
actions = actions ?? [],
|
||||||
|
// For backwards compatibility,
|
||||||
|
// set 'file_size', 'mime_type' in [extraData].
|
||||||
|
extraData = {
|
||||||
|
...extraData,
|
||||||
|
if (file?.size != null) 'file_size': file?.size,
|
||||||
|
if (file?.mimeType != null) 'mime_type': file?.mimeType?.mimeType,
|
||||||
|
} {
|
||||||
this.uploadState = uploadState ??
|
this.uploadState = uploadState ??
|
||||||
((assetUrl != null || imageUrl != null)
|
((assetUrl != null || imageUrl != null)
|
||||||
? const UploadState.success()
|
? const UploadState.success()
|
||||||
@@ -110,10 +117,7 @@ class Attachment extends Equatable {
|
|||||||
late final UploadState uploadState;
|
late final UploadState uploadState;
|
||||||
|
|
||||||
/// Map of custom channel extraData
|
/// Map of custom channel extraData
|
||||||
@JsonKey(
|
@JsonKey(includeIfNull: false)
|
||||||
includeIfNull: false,
|
|
||||||
defaultValue: {},
|
|
||||||
)
|
|
||||||
final Map<String, Object?> extraData;
|
final Map<String, Object?> extraData;
|
||||||
|
|
||||||
/// The attachment ID.
|
/// The attachment ID.
|
||||||
@@ -121,6 +125,18 @@ class Attachment extends Equatable {
|
|||||||
/// This is created locally for uniquely identifying a attachment.
|
/// This is created locally for uniquely identifying a attachment.
|
||||||
final String id;
|
final String id;
|
||||||
|
|
||||||
|
/// Shortcut for file size.
|
||||||
|
///
|
||||||
|
/// {@macro fileSize}
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
int? get fileSize => extraData['file_size'] as int?;
|
||||||
|
|
||||||
|
/// Shortcut for file mimeType.
|
||||||
|
///
|
||||||
|
/// {@macro mimeType}
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
String? get mimeType => extraData['mime_type'] as String?;
|
||||||
|
|
||||||
/// Known top level fields.
|
/// Known top level fields.
|
||||||
/// Useful for [Serializer] methods.
|
/// Useful for [Serializer] methods.
|
||||||
static const topLevelFields = [
|
static const topLevelFields = [
|
||||||
|
|||||||
@@ -6,39 +6,37 @@ part of 'attachment.dart';
|
|||||||
// JsonSerializableGenerator
|
// JsonSerializableGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
Attachment _$AttachmentFromJson(Map<String, dynamic> json) {
|
Attachment _$AttachmentFromJson(Map<String, dynamic> json) => Attachment(
|
||||||
return Attachment(
|
id: json['id'] as String?,
|
||||||
id: json['id'] as String?,
|
type: json['type'] as String?,
|
||||||
type: json['type'] as String?,
|
titleLink: json['title_link'] as String?,
|
||||||
titleLink: json['title_link'] as String?,
|
title: json['title'] as String?,
|
||||||
title: json['title'] as String?,
|
thumbUrl: json['thumb_url'] as String?,
|
||||||
thumbUrl: json['thumb_url'] as String?,
|
text: json['text'] as String?,
|
||||||
text: json['text'] as String?,
|
pretext: json['pretext'] as String?,
|
||||||
pretext: json['pretext'] as String?,
|
ogScrapeUrl: json['og_scrape_url'] as String?,
|
||||||
ogScrapeUrl: json['og_scrape_url'] as String?,
|
imageUrl: json['image_url'] as String?,
|
||||||
imageUrl: json['image_url'] as String?,
|
footerIcon: json['footer_icon'] as String?,
|
||||||
footerIcon: json['footer_icon'] as String?,
|
footer: json['footer'] as String?,
|
||||||
footer: json['footer'] as String?,
|
fields: json['fields'],
|
||||||
fields: json['fields'],
|
fallback: json['fallback'] as String?,
|
||||||
fallback: json['fallback'] as String?,
|
color: json['color'] as String?,
|
||||||
color: json['color'] as String?,
|
authorName: json['author_name'] as String?,
|
||||||
authorName: json['author_name'] as String?,
|
authorLink: json['author_link'] as String?,
|
||||||
authorLink: json['author_link'] as String?,
|
authorIcon: json['author_icon'] as String?,
|
||||||
authorIcon: json['author_icon'] as String?,
|
assetUrl: json['asset_url'] as String?,
|
||||||
assetUrl: json['asset_url'] as String?,
|
actions: (json['actions'] as List<dynamic>?)
|
||||||
actions: (json['actions'] as List<dynamic>?)
|
?.map((e) => Action.fromJson(e as Map<String, dynamic>))
|
||||||
?.map((e) => Action.fromJson(e as Map<String, dynamic>))
|
.toList() ??
|
||||||
.toList() ??
|
[],
|
||||||
[],
|
extraData: json['extra_data'] as Map<String, dynamic>? ?? const {},
|
||||||
extraData: json['extra_data'] as Map<String, dynamic>? ?? {},
|
file: json['file'] == null
|
||||||
file: json['file'] == null
|
? null
|
||||||
? null
|
: AttachmentFile.fromJson(json['file'] as Map<String, dynamic>),
|
||||||
: AttachmentFile.fromJson(json['file'] as Map<String, dynamic>),
|
uploadState: json['upload_state'] == null
|
||||||
uploadState: json['upload_state'] == null
|
? null
|
||||||
? null
|
: UploadState.fromJson(json['upload_state'] as Map<String, dynamic>),
|
||||||
: UploadState.fromJson(json['upload_state'] as Map<String, dynamic>),
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> _$AttachmentToJson(Attachment instance) {
|
Map<String, dynamic> _$AttachmentToJson(Attachment instance) {
|
||||||
final val = <String, dynamic>{};
|
final val = <String, dynamic>{};
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'dart:typed_data';
|
|||||||
|
|
||||||
import 'package:dio/dio.dart' show MultipartFile;
|
import 'package:dio/dio.dart' show MultipartFile;
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
import 'package:http_parser/http_parser.dart';
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
import 'package:stream_chat/src/core/platform_detector/platform_detector.dart';
|
import 'package:stream_chat/src/core/platform_detector/platform_detector.dart';
|
||||||
import 'package:stream_chat/src/core/util/extension.dart';
|
import 'package:stream_chat/src/core/util/extension.dart';
|
||||||
@@ -65,7 +66,7 @@ class AttachmentFile {
|
|||||||
AttachmentFile({
|
AttachmentFile({
|
||||||
required this.size,
|
required this.size,
|
||||||
this.path,
|
this.path,
|
||||||
this.name,
|
String? name,
|
||||||
this.bytes,
|
this.bytes,
|
||||||
}) : assert(
|
}) : assert(
|
||||||
path != null || bytes != null,
|
path != null || bytes != null,
|
||||||
@@ -74,7 +75,12 @@ class AttachmentFile {
|
|||||||
assert(
|
assert(
|
||||||
!CurrentPlatform.isWeb || bytes != null,
|
!CurrentPlatform.isWeb || bytes != null,
|
||||||
'File by path is not supported in web, Please provide bytes',
|
'File by path is not supported in web, Please provide bytes',
|
||||||
);
|
),
|
||||||
|
assert(
|
||||||
|
name?.contains('.') ?? true,
|
||||||
|
'Invalid file name, should also contain file extension',
|
||||||
|
),
|
||||||
|
_name = name;
|
||||||
|
|
||||||
/// Create a new instance from a json
|
/// Create a new instance from a json
|
||||||
factory AttachmentFile.fromJson(Map<String, dynamic> json) =>
|
factory AttachmentFile.fromJson(Map<String, dynamic> json) =>
|
||||||
@@ -87,8 +93,10 @@ class AttachmentFile {
|
|||||||
/// ```
|
/// ```
|
||||||
final String? path;
|
final String? path;
|
||||||
|
|
||||||
|
final String? _name;
|
||||||
|
|
||||||
/// File name including its extension.
|
/// File name including its extension.
|
||||||
final String? name;
|
String? get name => _name ?? path?.split('/').last;
|
||||||
|
|
||||||
/// Byte data for this file. Particularly useful if you want to manipulate
|
/// Byte data for this file. Particularly useful if you want to manipulate
|
||||||
/// its data or easily upload to somewhere else.
|
/// its data or easily upload to somewhere else.
|
||||||
@@ -101,26 +109,26 @@ class AttachmentFile {
|
|||||||
/// File extension for this file.
|
/// File extension for this file.
|
||||||
String? get extension => name?.split('.').last;
|
String? get extension => name?.split('.').last;
|
||||||
|
|
||||||
|
/// The mime type of this file.
|
||||||
|
MediaType? get mimeType => name?.mimeType;
|
||||||
|
|
||||||
/// Serialize to json
|
/// Serialize to json
|
||||||
Map<String, dynamic> toJson() => _$AttachmentFileToJson(this);
|
Map<String, dynamic> toJson() => _$AttachmentFileToJson(this);
|
||||||
|
|
||||||
/// Converts this into a [MultipartFile]
|
/// Converts this into a [MultipartFile]
|
||||||
Future<MultipartFile> toMultipartFile() async {
|
Future<MultipartFile> toMultipartFile() async {
|
||||||
final filename = path?.split('/').last ?? name;
|
MultipartFile multiPartFile;
|
||||||
final mimeType = filename?.mimeType;
|
|
||||||
|
|
||||||
late MultipartFile multiPartFile;
|
|
||||||
|
|
||||||
if (CurrentPlatform.isWeb) {
|
if (CurrentPlatform.isWeb) {
|
||||||
multiPartFile = MultipartFile.fromBytes(
|
multiPartFile = MultipartFile.fromBytes(
|
||||||
bytes!,
|
bytes!,
|
||||||
filename: filename,
|
filename: name,
|
||||||
contentType: mimeType,
|
contentType: mimeType,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
multiPartFile = await MultipartFile.fromFile(
|
multiPartFile = await MultipartFile.fromFile(
|
||||||
path!,
|
path!,
|
||||||
filename: filename,
|
filename: name,
|
||||||
contentType: mimeType,
|
contentType: mimeType,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
|
// coverage:ignore-file
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides
|
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target
|
||||||
|
|
||||||
part of 'attachment_file.dart';
|
part of 'attachment_file.dart';
|
||||||
|
|
||||||
@@ -13,7 +14,7 @@ final _privateConstructorUsedError = UnsupportedError(
|
|||||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more informations: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
|
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more informations: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
|
||||||
|
|
||||||
UploadState _$UploadStateFromJson(Map<String, dynamic> json) {
|
UploadState _$UploadStateFromJson(Map<String, dynamic> json) {
|
||||||
switch (json['runtimeType'] as String) {
|
switch (json['runtimeType'] as String?) {
|
||||||
case 'preparing':
|
case 'preparing':
|
||||||
return Preparing.fromJson(json);
|
return Preparing.fromJson(json);
|
||||||
case 'inProgress':
|
case 'inProgress':
|
||||||
@@ -24,7 +25,8 @@ UploadState _$UploadStateFromJson(Map<String, dynamic> json) {
|
|||||||
return Failed.fromJson(json);
|
return Failed.fromJson(json);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw FallThroughError();
|
throw CheckedFromJsonException(json, 'runtimeType', 'UploadState',
|
||||||
|
'Invalid union type "${json['runtimeType']}"!');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,6 +74,14 @@ mixin _$UploadState {
|
|||||||
}) =>
|
}) =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
|
TResult Function()? preparing,
|
||||||
|
TResult Function(int uploaded, int total)? inProgress,
|
||||||
|
TResult Function()? success,
|
||||||
|
TResult Function(String error)? failed,
|
||||||
|
}) =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
@optionalTypeArgs
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function()? preparing,
|
TResult Function()? preparing,
|
||||||
TResult Function(int uploaded, int total)? inProgress,
|
TResult Function(int uploaded, int total)? inProgress,
|
||||||
@@ -89,6 +99,14 @@ mixin _$UploadState {
|
|||||||
}) =>
|
}) =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
|
TResult Function(Preparing value)? preparing,
|
||||||
|
TResult Function(InProgress value)? inProgress,
|
||||||
|
TResult Function(Success value)? success,
|
||||||
|
TResult Function(Failed value)? failed,
|
||||||
|
}) =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
@optionalTypeArgs
|
||||||
TResult maybeMap<TResult extends Object?>({
|
TResult maybeMap<TResult extends Object?>({
|
||||||
TResult Function(Preparing value)? preparing,
|
TResult Function(Preparing value)? preparing,
|
||||||
TResult Function(InProgress value)? inProgress,
|
TResult Function(InProgress value)? inProgress,
|
||||||
@@ -138,7 +156,7 @@ class _$Preparing implements Preparing {
|
|||||||
const _$Preparing();
|
const _$Preparing();
|
||||||
|
|
||||||
factory _$Preparing.fromJson(Map<String, dynamic> json) =>
|
factory _$Preparing.fromJson(Map<String, dynamic> json) =>
|
||||||
_$_$PreparingFromJson(json);
|
_$$PreparingFromJson(json);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
@@ -164,6 +182,17 @@ class _$Preparing implements Preparing {
|
|||||||
return preparing();
|
return preparing();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
|
TResult Function()? preparing,
|
||||||
|
TResult Function(int uploaded, int total)? inProgress,
|
||||||
|
TResult Function()? success,
|
||||||
|
TResult Function(String error)? failed,
|
||||||
|
}) {
|
||||||
|
return preparing?.call();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
@@ -190,6 +219,17 @@ class _$Preparing implements Preparing {
|
|||||||
return preparing(this);
|
return preparing(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
|
TResult Function(Preparing value)? preparing,
|
||||||
|
TResult Function(InProgress value)? inProgress,
|
||||||
|
TResult Function(Success value)? success,
|
||||||
|
TResult Function(Failed value)? failed,
|
||||||
|
}) {
|
||||||
|
return preparing?.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeMap<TResult extends Object?>({
|
TResult maybeMap<TResult extends Object?>({
|
||||||
@@ -207,7 +247,7 @@ class _$Preparing implements Preparing {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
return _$_$PreparingToJson(this)..['runtimeType'] = 'preparing';
|
return _$$PreparingToJson(this)..['runtimeType'] = 'preparing';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,7 +298,7 @@ class _$InProgress implements InProgress {
|
|||||||
const _$InProgress({required this.uploaded, required this.total});
|
const _$InProgress({required this.uploaded, required this.total});
|
||||||
|
|
||||||
factory _$InProgress.fromJson(Map<String, dynamic> json) =>
|
factory _$InProgress.fromJson(Map<String, dynamic> json) =>
|
||||||
_$_$InProgressFromJson(json);
|
_$$InProgressFromJson(json);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final int uploaded;
|
final int uploaded;
|
||||||
@@ -303,6 +343,17 @@ class _$InProgress implements InProgress {
|
|||||||
return inProgress(uploaded, total);
|
return inProgress(uploaded, total);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
|
TResult Function()? preparing,
|
||||||
|
TResult Function(int uploaded, int total)? inProgress,
|
||||||
|
TResult Function()? success,
|
||||||
|
TResult Function(String error)? failed,
|
||||||
|
}) {
|
||||||
|
return inProgress?.call(uploaded, total);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
@@ -329,6 +380,17 @@ class _$InProgress implements InProgress {
|
|||||||
return inProgress(this);
|
return inProgress(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
|
TResult Function(Preparing value)? preparing,
|
||||||
|
TResult Function(InProgress value)? inProgress,
|
||||||
|
TResult Function(Success value)? success,
|
||||||
|
TResult Function(Failed value)? failed,
|
||||||
|
}) {
|
||||||
|
return inProgress?.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeMap<TResult extends Object?>({
|
TResult maybeMap<TResult extends Object?>({
|
||||||
@@ -346,7 +408,7 @@ class _$InProgress implements InProgress {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
return _$_$InProgressToJson(this)..['runtimeType'] = 'inProgress';
|
return _$$InProgressToJson(this)..['runtimeType'] = 'inProgress';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -386,7 +448,7 @@ class _$Success implements Success {
|
|||||||
const _$Success();
|
const _$Success();
|
||||||
|
|
||||||
factory _$Success.fromJson(Map<String, dynamic> json) =>
|
factory _$Success.fromJson(Map<String, dynamic> json) =>
|
||||||
_$_$SuccessFromJson(json);
|
_$$SuccessFromJson(json);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
@@ -412,6 +474,17 @@ class _$Success implements Success {
|
|||||||
return success();
|
return success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
|
TResult Function()? preparing,
|
||||||
|
TResult Function(int uploaded, int total)? inProgress,
|
||||||
|
TResult Function()? success,
|
||||||
|
TResult Function(String error)? failed,
|
||||||
|
}) {
|
||||||
|
return success?.call();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
@@ -438,6 +511,17 @@ class _$Success implements Success {
|
|||||||
return success(this);
|
return success(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
|
TResult Function(Preparing value)? preparing,
|
||||||
|
TResult Function(InProgress value)? inProgress,
|
||||||
|
TResult Function(Success value)? success,
|
||||||
|
TResult Function(Failed value)? failed,
|
||||||
|
}) {
|
||||||
|
return success?.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeMap<TResult extends Object?>({
|
TResult maybeMap<TResult extends Object?>({
|
||||||
@@ -455,7 +539,7 @@ class _$Success implements Success {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
return _$_$SuccessToJson(this)..['runtimeType'] = 'success';
|
return _$$SuccessToJson(this)..['runtimeType'] = 'success';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -500,7 +584,7 @@ class _$Failed implements Failed {
|
|||||||
const _$Failed({required this.error});
|
const _$Failed({required this.error});
|
||||||
|
|
||||||
factory _$Failed.fromJson(Map<String, dynamic> json) =>
|
factory _$Failed.fromJson(Map<String, dynamic> json) =>
|
||||||
_$_$FailedFromJson(json);
|
_$$FailedFromJson(json);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final String error;
|
final String error;
|
||||||
@@ -538,6 +622,17 @@ class _$Failed implements Failed {
|
|||||||
return failed(error);
|
return failed(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
|
TResult Function()? preparing,
|
||||||
|
TResult Function(int uploaded, int total)? inProgress,
|
||||||
|
TResult Function()? success,
|
||||||
|
TResult Function(String error)? failed,
|
||||||
|
}) {
|
||||||
|
return failed?.call(error);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
@@ -564,6 +659,17 @@ class _$Failed implements Failed {
|
|||||||
return failed(this);
|
return failed(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
|
TResult Function(Preparing value)? preparing,
|
||||||
|
TResult Function(InProgress value)? inProgress,
|
||||||
|
TResult Function(Success value)? success,
|
||||||
|
TResult Function(Failed value)? failed,
|
||||||
|
}) {
|
||||||
|
return failed?.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeMap<TResult extends Object?>({
|
TResult maybeMap<TResult extends Object?>({
|
||||||
@@ -581,7 +687,7 @@ class _$Failed implements Failed {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
return _$_$FailedToJson(this)..['runtimeType'] = 'failed';
|
return _$$FailedToJson(this)..['runtimeType'] = 'failed';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,14 +6,13 @@ part of 'attachment_file.dart';
|
|||||||
// JsonSerializableGenerator
|
// JsonSerializableGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
AttachmentFile _$AttachmentFileFromJson(Map<String, dynamic> json) {
|
AttachmentFile _$AttachmentFileFromJson(Map<String, dynamic> json) =>
|
||||||
return AttachmentFile(
|
AttachmentFile(
|
||||||
size: json['size'] as int?,
|
size: json['size'] as int?,
|
||||||
path: json['path'] as String?,
|
path: json['path'] as String?,
|
||||||
name: json['name'] as String?,
|
name: json['name'] as String?,
|
||||||
bytes: _fromString(json['bytes'] as String?),
|
bytes: _fromString(json['bytes'] as String?),
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> _$AttachmentFileToJson(AttachmentFile instance) =>
|
Map<String, dynamic> _$AttachmentFileToJson(AttachmentFile instance) =>
|
||||||
<String, dynamic>{
|
<String, dynamic>{
|
||||||
@@ -23,39 +22,31 @@ Map<String, dynamic> _$AttachmentFileToJson(AttachmentFile instance) =>
|
|||||||
'size': instance.size,
|
'size': instance.size,
|
||||||
};
|
};
|
||||||
|
|
||||||
_$Preparing _$_$PreparingFromJson(Map<String, dynamic> json) {
|
_$Preparing _$$PreparingFromJson(Map<String, dynamic> json) => _$Preparing();
|
||||||
return _$Preparing();
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> _$_$PreparingToJson(_$Preparing instance) =>
|
Map<String, dynamic> _$$PreparingToJson(_$Preparing instance) =>
|
||||||
<String, dynamic>{};
|
<String, dynamic>{};
|
||||||
|
|
||||||
_$InProgress _$_$InProgressFromJson(Map<String, dynamic> json) {
|
_$InProgress _$$InProgressFromJson(Map<String, dynamic> json) => _$InProgress(
|
||||||
return _$InProgress(
|
uploaded: json['uploaded'] as int,
|
||||||
uploaded: json['uploaded'] as int,
|
total: json['total'] as int,
|
||||||
total: json['total'] as int,
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> _$_$InProgressToJson(_$InProgress instance) =>
|
Map<String, dynamic> _$$InProgressToJson(_$InProgress instance) =>
|
||||||
<String, dynamic>{
|
<String, dynamic>{
|
||||||
'uploaded': instance.uploaded,
|
'uploaded': instance.uploaded,
|
||||||
'total': instance.total,
|
'total': instance.total,
|
||||||
};
|
};
|
||||||
|
|
||||||
_$Success _$_$SuccessFromJson(Map<String, dynamic> json) {
|
_$Success _$$SuccessFromJson(Map<String, dynamic> json) => _$Success();
|
||||||
return _$Success();
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> _$_$SuccessToJson(_$Success instance) =>
|
Map<String, dynamic> _$$SuccessToJson(_$Success instance) =>
|
||||||
<String, dynamic>{};
|
<String, dynamic>{};
|
||||||
|
|
||||||
_$Failed _$_$FailedFromJson(Map<String, dynamic> json) {
|
_$Failed _$$FailedFromJson(Map<String, dynamic> json) => _$Failed(
|
||||||
return _$Failed(
|
error: json['error'] as String,
|
||||||
error: json['error'] as String,
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> _$_$FailedToJson(_$Failed instance) => <String, dynamic>{
|
Map<String, dynamic> _$$FailedToJson(_$Failed instance) => <String, dynamic>{
|
||||||
'error': instance.error,
|
'error': instance.error,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -31,15 +31,12 @@ class ChannelConfig {
|
|||||||
_$ChannelConfigFromJson(json);
|
_$ChannelConfigFromJson(json);
|
||||||
|
|
||||||
/// Moderation configuration
|
/// Moderation configuration
|
||||||
@JsonKey(defaultValue: 'flag')
|
|
||||||
final String automod;
|
final String automod;
|
||||||
|
|
||||||
/// List of available commands
|
/// List of available commands
|
||||||
@JsonKey(defaultValue: [])
|
|
||||||
final List<Command> commands;
|
final List<Command> commands;
|
||||||
|
|
||||||
/// True if the channel should send connect events
|
/// True if the channel should send connect events
|
||||||
@JsonKey(defaultValue: false)
|
|
||||||
final bool connectEvents;
|
final bool connectEvents;
|
||||||
|
|
||||||
/// Date of channel creation
|
/// Date of channel creation
|
||||||
@@ -49,43 +46,33 @@ class ChannelConfig {
|
|||||||
final DateTime updatedAt;
|
final DateTime updatedAt;
|
||||||
|
|
||||||
/// Max channel message length
|
/// Max channel message length
|
||||||
@JsonKey(defaultValue: 0)
|
|
||||||
final int maxMessageLength;
|
final int maxMessageLength;
|
||||||
|
|
||||||
/// Duration of message retention
|
/// Duration of message retention
|
||||||
@JsonKey(defaultValue: '')
|
|
||||||
final String messageRetention;
|
final String messageRetention;
|
||||||
|
|
||||||
/// True if users can be muted
|
/// True if users can be muted
|
||||||
@JsonKey(defaultValue: false)
|
|
||||||
final bool mutes;
|
final bool mutes;
|
||||||
|
|
||||||
/// True if reaction are active for this channel
|
/// True if reaction are active for this channel
|
||||||
@JsonKey(defaultValue: false)
|
|
||||||
final bool reactions;
|
final bool reactions;
|
||||||
|
|
||||||
/// True if readEvents are active for this channel
|
/// True if readEvents are active for this channel
|
||||||
@JsonKey(defaultValue: false)
|
|
||||||
final bool readEvents;
|
final bool readEvents;
|
||||||
|
|
||||||
/// True if reply message are active for this channel
|
/// True if reply message are active for this channel
|
||||||
@JsonKey(defaultValue: false)
|
|
||||||
final bool replies;
|
final bool replies;
|
||||||
|
|
||||||
/// True if it's possible to perform a search in this channel
|
/// True if it's possible to perform a search in this channel
|
||||||
@JsonKey(defaultValue: false)
|
|
||||||
final bool search;
|
final bool search;
|
||||||
|
|
||||||
/// True if typing events should be sent for this channel
|
/// True if typing events should be sent for this channel
|
||||||
@JsonKey(defaultValue: false)
|
|
||||||
final bool typingEvents;
|
final bool typingEvents;
|
||||||
|
|
||||||
/// True if it's possible to upload files to this channel
|
/// True if it's possible to upload files to this channel
|
||||||
@JsonKey(defaultValue: false)
|
|
||||||
final bool uploads;
|
final bool uploads;
|
||||||
|
|
||||||
/// True if urls appears as attachments
|
/// True if urls appears as attachments
|
||||||
@JsonKey(defaultValue: false)
|
|
||||||
final bool urlEnrichment;
|
final bool urlEnrichment;
|
||||||
|
|
||||||
/// Serialize to json
|
/// Serialize to json
|
||||||
|
|||||||
@@ -6,32 +6,31 @@ part of 'channel_config.dart';
|
|||||||
// JsonSerializableGenerator
|
// JsonSerializableGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
ChannelConfig _$ChannelConfigFromJson(Map<String, dynamic> json) {
|
ChannelConfig _$ChannelConfigFromJson(Map<String, dynamic> json) =>
|
||||||
return ChannelConfig(
|
ChannelConfig(
|
||||||
automod: json['automod'] as String? ?? 'flag',
|
automod: json['automod'] as String? ?? 'flag',
|
||||||
commands: (json['commands'] as List<dynamic>?)
|
commands: (json['commands'] as List<dynamic>?)
|
||||||
?.map((e) => Command.fromJson(e as Map<String, dynamic>))
|
?.map((e) => Command.fromJson(e as Map<String, dynamic>))
|
||||||
.toList() ??
|
.toList() ??
|
||||||
[],
|
const [],
|
||||||
connectEvents: json['connect_events'] as bool? ?? false,
|
connectEvents: json['connect_events'] as bool? ?? false,
|
||||||
createdAt: json['created_at'] == null
|
createdAt: json['created_at'] == null
|
||||||
? null
|
? null
|
||||||
: DateTime.parse(json['created_at'] as String),
|
: DateTime.parse(json['created_at'] as String),
|
||||||
updatedAt: json['updated_at'] == null
|
updatedAt: json['updated_at'] == null
|
||||||
? null
|
? null
|
||||||
: DateTime.parse(json['updated_at'] as String),
|
: DateTime.parse(json['updated_at'] as String),
|
||||||
maxMessageLength: json['max_message_length'] as int? ?? 0,
|
maxMessageLength: json['max_message_length'] as int? ?? 0,
|
||||||
messageRetention: json['message_retention'] as String? ?? '',
|
messageRetention: json['message_retention'] as String? ?? '',
|
||||||
mutes: json['mutes'] as bool? ?? false,
|
mutes: json['mutes'] as bool? ?? false,
|
||||||
reactions: json['reactions'] as bool? ?? false,
|
reactions: json['reactions'] as bool? ?? false,
|
||||||
readEvents: json['read_events'] as bool? ?? false,
|
readEvents: json['read_events'] as bool? ?? false,
|
||||||
replies: json['replies'] as bool? ?? false,
|
replies: json['replies'] as bool? ?? false,
|
||||||
search: json['search'] as bool? ?? false,
|
search: json['search'] as bool? ?? false,
|
||||||
typingEvents: json['typing_events'] as bool? ?? false,
|
typingEvents: json['typing_events'] as bool? ?? false,
|
||||||
uploads: json['uploads'] as bool? ?? false,
|
uploads: json['uploads'] as bool? ?? false,
|
||||||
urlEnrichment: json['url_enrichment'] as bool? ?? false,
|
urlEnrichment: json['url_enrichment'] as bool? ?? false,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> _$ChannelConfigToJson(ChannelConfig instance) =>
|
Map<String, dynamic> _$ChannelConfigToJson(ChannelConfig instance) =>
|
||||||
<String, dynamic>{
|
<String, dynamic>{
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
import 'package:stream_chat/src/core/models/channel_config.dart';
|
import 'package:stream_chat/src/core/models/channel_config.dart';
|
||||||
import 'package:stream_chat/src/core/util/serializer.dart';
|
|
||||||
import 'package:stream_chat/src/core/models/user.dart';
|
import 'package:stream_chat/src/core/models/user.dart';
|
||||||
|
import 'package:stream_chat/src/core/util/serializer.dart';
|
||||||
|
|
||||||
part 'channel_model.g.dart';
|
part 'channel_model.g.dart';
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ class ChannelModel {
|
|||||||
final User? createdBy;
|
final User? createdBy;
|
||||||
|
|
||||||
/// True if this channel is frozen
|
/// True if this channel is frozen
|
||||||
@JsonKey(includeIfNull: false, defaultValue: false)
|
@JsonKey(includeIfNull: false)
|
||||||
final bool frozen;
|
final bool frozen;
|
||||||
|
|
||||||
/// The date of the last message
|
/// The date of the last message
|
||||||
@@ -79,18 +79,15 @@ class ChannelModel {
|
|||||||
final DateTime? deletedAt;
|
final DateTime? deletedAt;
|
||||||
|
|
||||||
/// The count of this channel members
|
/// The count of this channel members
|
||||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly, defaultValue: 0)
|
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
||||||
final int memberCount;
|
final int memberCount;
|
||||||
|
|
||||||
/// The number of seconds in a cooldown
|
/// The number of seconds in a cooldown
|
||||||
@JsonKey(includeIfNull: false, defaultValue: 0)
|
@JsonKey(includeIfNull: false)
|
||||||
final int cooldown;
|
final int cooldown;
|
||||||
|
|
||||||
/// Map of custom channel extraData
|
/// Map of custom channel extraData
|
||||||
@JsonKey(
|
@JsonKey(includeIfNull: false)
|
||||||
includeIfNull: false,
|
|
||||||
defaultValue: {},
|
|
||||||
)
|
|
||||||
final Map<String, Object?> extraData;
|
final Map<String, Object?> extraData;
|
||||||
|
|
||||||
/// The team the channel belongs to
|
/// The team the channel belongs to
|
||||||
|
|||||||
@@ -6,36 +6,34 @@ part of 'channel_model.dart';
|
|||||||
// JsonSerializableGenerator
|
// JsonSerializableGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
ChannelModel _$ChannelModelFromJson(Map<String, dynamic> json) {
|
ChannelModel _$ChannelModelFromJson(Map<String, dynamic> json) => ChannelModel(
|
||||||
return ChannelModel(
|
id: json['id'] as String?,
|
||||||
id: json['id'] as String?,
|
type: json['type'] as String?,
|
||||||
type: json['type'] as String?,
|
cid: json['cid'] as String?,
|
||||||
cid: json['cid'] as String?,
|
config: json['config'] == null
|
||||||
config: json['config'] == null
|
? null
|
||||||
? null
|
: ChannelConfig.fromJson(json['config'] as Map<String, dynamic>),
|
||||||
: ChannelConfig.fromJson(json['config'] as Map<String, dynamic>),
|
createdBy: json['created_by'] == null
|
||||||
createdBy: json['created_by'] == null
|
? null
|
||||||
? null
|
: User.fromJson(json['created_by'] as Map<String, dynamic>),
|
||||||
: User.fromJson(json['created_by'] as Map<String, dynamic>),
|
frozen: json['frozen'] as bool? ?? false,
|
||||||
frozen: json['frozen'] as bool? ?? false,
|
lastMessageAt: json['last_message_at'] == null
|
||||||
lastMessageAt: json['last_message_at'] == null
|
? null
|
||||||
? null
|
: DateTime.parse(json['last_message_at'] as String),
|
||||||
: DateTime.parse(json['last_message_at'] as String),
|
createdAt: json['created_at'] == null
|
||||||
createdAt: json['created_at'] == null
|
? null
|
||||||
? null
|
: DateTime.parse(json['created_at'] as String),
|
||||||
: DateTime.parse(json['created_at'] as String),
|
updatedAt: json['updated_at'] == null
|
||||||
updatedAt: json['updated_at'] == null
|
? null
|
||||||
? null
|
: DateTime.parse(json['updated_at'] as String),
|
||||||
: DateTime.parse(json['updated_at'] as String),
|
deletedAt: json['deleted_at'] == null
|
||||||
deletedAt: json['deleted_at'] == null
|
? null
|
||||||
? null
|
: DateTime.parse(json['deleted_at'] as String),
|
||||||
: DateTime.parse(json['deleted_at'] as String),
|
memberCount: json['member_count'] as int? ?? 0,
|
||||||
memberCount: json['member_count'] as int? ?? 0,
|
extraData: json['extra_data'] as Map<String, dynamic>? ?? const {},
|
||||||
extraData: json['extra_data'] as Map<String, dynamic>? ?? {},
|
team: json['team'] as String?,
|
||||||
team: json['team'] as String?,
|
cooldown: json['cooldown'] as int? ?? 0,
|
||||||
cooldown: json['cooldown'] as int? ?? 0,
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> _$ChannelModelToJson(ChannelModel instance) {
|
Map<String, dynamic> _$ChannelModelToJson(ChannelModel instance) {
|
||||||
final val = <String, dynamic>{
|
final val = <String, dynamic>{
|
||||||
|
|||||||
@@ -25,26 +25,21 @@ class ChannelState {
|
|||||||
final ChannelModel? channel;
|
final ChannelModel? channel;
|
||||||
|
|
||||||
/// A paginated list of channel messages
|
/// A paginated list of channel messages
|
||||||
@JsonKey(defaultValue: <Message>[])
|
|
||||||
final List<Message> messages;
|
final List<Message> messages;
|
||||||
|
|
||||||
/// A paginated list of channel members
|
/// A paginated list of channel members
|
||||||
@JsonKey(defaultValue: <Member>[])
|
|
||||||
final List<Member> members;
|
final List<Member> members;
|
||||||
|
|
||||||
/// A paginated list of pinned messages
|
/// A paginated list of pinned messages
|
||||||
@JsonKey(defaultValue: <Message>[])
|
|
||||||
final List<Message> pinnedMessages;
|
final List<Message> pinnedMessages;
|
||||||
|
|
||||||
/// The count of users watching the channel
|
/// The count of users watching the channel
|
||||||
final int? watcherCount;
|
final int? watcherCount;
|
||||||
|
|
||||||
/// A paginated list of users watching the channel
|
/// A paginated list of users watching the channel
|
||||||
@JsonKey(defaultValue: <User>[])
|
|
||||||
final List<User> watchers;
|
final List<User> watchers;
|
||||||
|
|
||||||
/// The list of channel reads
|
/// The list of channel reads
|
||||||
@JsonKey(defaultValue: <Read>[])
|
|
||||||
final List<Read> read;
|
final List<Read> read;
|
||||||
|
|
||||||
/// Create a new instance from a json
|
/// Create a new instance from a json
|
||||||
|
|||||||
@@ -6,34 +6,32 @@ part of 'channel_state.dart';
|
|||||||
// JsonSerializableGenerator
|
// JsonSerializableGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
ChannelState _$ChannelStateFromJson(Map<String, dynamic> json) {
|
ChannelState _$ChannelStateFromJson(Map<String, dynamic> json) => ChannelState(
|
||||||
return ChannelState(
|
channel: json['channel'] == null
|
||||||
channel: json['channel'] == null
|
? null
|
||||||
? null
|
: ChannelModel.fromJson(json['channel'] as Map<String, dynamic>),
|
||||||
: ChannelModel.fromJson(json['channel'] as Map<String, dynamic>),
|
messages: (json['messages'] as List<dynamic>?)
|
||||||
messages: (json['messages'] as List<dynamic>?)
|
?.map((e) => Message.fromJson(e as Map<String, dynamic>))
|
||||||
?.map((e) => Message.fromJson(e as Map<String, dynamic>))
|
.toList() ??
|
||||||
.toList() ??
|
const [],
|
||||||
[],
|
members: (json['members'] as List<dynamic>?)
|
||||||
members: (json['members'] as List<dynamic>?)
|
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
|
||||||
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
|
.toList() ??
|
||||||
.toList() ??
|
const [],
|
||||||
[],
|
pinnedMessages: (json['pinned_messages'] as List<dynamic>?)
|
||||||
pinnedMessages: (json['pinned_messages'] as List<dynamic>?)
|
?.map((e) => Message.fromJson(e as Map<String, dynamic>))
|
||||||
?.map((e) => Message.fromJson(e as Map<String, dynamic>))
|
.toList() ??
|
||||||
.toList() ??
|
const [],
|
||||||
[],
|
watcherCount: json['watcher_count'] as int?,
|
||||||
watcherCount: json['watcher_count'] as int?,
|
watchers: (json['watchers'] as List<dynamic>?)
|
||||||
watchers: (json['watchers'] as List<dynamic>?)
|
?.map((e) => User.fromJson(e as Map<String, dynamic>))
|
||||||
?.map((e) => User.fromJson(e as Map<String, dynamic>))
|
.toList() ??
|
||||||
.toList() ??
|
const [],
|
||||||
[],
|
read: (json['read'] as List<dynamic>?)
|
||||||
read: (json['read'] as List<dynamic>?)
|
?.map((e) => Read.fromJson(e as Map<String, dynamic>))
|
||||||
?.map((e) => Read.fromJson(e as Map<String, dynamic>))
|
.toList() ??
|
||||||
.toList() ??
|
const [],
|
||||||
[],
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> _$ChannelStateToJson(ChannelState instance) =>
|
Map<String, dynamic> _$ChannelStateToJson(ChannelState instance) =>
|
||||||
<String, dynamic>{
|
<String, dynamic>{
|
||||||
|
|||||||
@@ -6,13 +6,11 @@ part of 'command.dart';
|
|||||||
// JsonSerializableGenerator
|
// JsonSerializableGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
Command _$CommandFromJson(Map<String, dynamic> json) {
|
Command _$CommandFromJson(Map<String, dynamic> json) => Command(
|
||||||
return Command(
|
name: json['name'] as String,
|
||||||
name: json['name'] as String,
|
description: json['description'] as String,
|
||||||
description: json['description'] as String,
|
args: json['args'] as String,
|
||||||
args: json['args'] as String,
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> _$CommandToJson(Command instance) => <String, dynamic>{
|
Map<String, dynamic> _$CommandToJson(Command instance) => <String, dynamic>{
|
||||||
'name': instance.name,
|
'name': instance.name,
|
||||||
|
|||||||
@@ -6,12 +6,10 @@ part of 'device.dart';
|
|||||||
// JsonSerializableGenerator
|
// JsonSerializableGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
Device _$DeviceFromJson(Map<String, dynamic> json) {
|
Device _$DeviceFromJson(Map<String, dynamic> json) => Device(
|
||||||
return Device(
|
id: json['id'] as String,
|
||||||
id: json['id'] as String,
|
pushProvider: json['push_provider'] as String,
|
||||||
pushProvider: json['push_provider'] as String,
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> _$DeviceToJson(Device instance) => <String, dynamic>{
|
Map<String, dynamic> _$DeviceToJson(Device instance) => <String, dynamic>{
|
||||||
'id': instance.id,
|
'id': instance.id,
|
||||||
|
|||||||
@@ -92,7 +92,6 @@ class Event {
|
|||||||
final bool isLocal;
|
final bool isLocal;
|
||||||
|
|
||||||
/// Map of custom channel extraData
|
/// Map of custom channel extraData
|
||||||
@JsonKey(defaultValue: {})
|
|
||||||
final Map<String, Object?> extraData;
|
final Map<String, Object?> extraData;
|
||||||
|
|
||||||
/// Known top level fields.
|
/// Known top level fields.
|
||||||
@@ -184,7 +183,7 @@ class EventChannel extends ChannelModel {
|
|||||||
DateTime? deletedAt,
|
DateTime? deletedAt,
|
||||||
required int memberCount,
|
required int memberCount,
|
||||||
Map<String, Object?>? extraData,
|
Map<String, Object?>? extraData,
|
||||||
required int cooldown,
|
int cooldown = 0,
|
||||||
String? team,
|
String? team,
|
||||||
}) : super(
|
}) : super(
|
||||||
id: id,
|
id: id,
|
||||||
|
|||||||
@@ -6,42 +6,40 @@ part of 'event.dart';
|
|||||||
// JsonSerializableGenerator
|
// JsonSerializableGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
Event _$EventFromJson(Map<String, dynamic> json) {
|
Event _$EventFromJson(Map<String, dynamic> json) => Event(
|
||||||
return Event(
|
type: json['type'] as String? ?? 'local.event',
|
||||||
type: json['type'] as String,
|
cid: json['cid'] as String?,
|
||||||
cid: json['cid'] as String?,
|
connectionId: json['connection_id'] as String?,
|
||||||
connectionId: json['connection_id'] as String?,
|
createdAt: json['created_at'] == null
|
||||||
createdAt: json['created_at'] == null
|
? null
|
||||||
? null
|
: DateTime.parse(json['created_at'] as String),
|
||||||
: DateTime.parse(json['created_at'] as String),
|
me: json['me'] == null
|
||||||
me: json['me'] == null
|
? null
|
||||||
? null
|
: OwnUser.fromJson(json['me'] as Map<String, dynamic>),
|
||||||
: OwnUser.fromJson(json['me'] as Map<String, dynamic>),
|
user: json['user'] == null
|
||||||
user: json['user'] == null
|
? null
|
||||||
? null
|
: User.fromJson(json['user'] as Map<String, dynamic>),
|
||||||
: User.fromJson(json['user'] as Map<String, dynamic>),
|
message: json['message'] == null
|
||||||
message: json['message'] == null
|
? null
|
||||||
? null
|
: Message.fromJson(json['message'] as Map<String, dynamic>),
|
||||||
: Message.fromJson(json['message'] as Map<String, dynamic>),
|
totalUnreadCount: json['total_unread_count'] as int?,
|
||||||
totalUnreadCount: json['total_unread_count'] as int?,
|
unreadChannels: json['unread_channels'] as int?,
|
||||||
unreadChannels: json['unread_channels'] as int?,
|
reaction: json['reaction'] == null
|
||||||
reaction: json['reaction'] == null
|
? null
|
||||||
? null
|
: Reaction.fromJson(json['reaction'] as Map<String, dynamic>),
|
||||||
: Reaction.fromJson(json['reaction'] as Map<String, dynamic>),
|
online: json['online'] as bool?,
|
||||||
online: json['online'] as bool?,
|
channel: json['channel'] == null
|
||||||
channel: json['channel'] == null
|
? null
|
||||||
? null
|
: EventChannel.fromJson(json['channel'] as Map<String, dynamic>),
|
||||||
: EventChannel.fromJson(json['channel'] as Map<String, dynamic>),
|
member: json['member'] == null
|
||||||
member: json['member'] == null
|
? null
|
||||||
? null
|
: Member.fromJson(json['member'] as Map<String, dynamic>),
|
||||||
: Member.fromJson(json['member'] as Map<String, dynamic>),
|
channelId: json['channel_id'] as String?,
|
||||||
channelId: json['channel_id'] as String?,
|
channelType: json['channel_type'] as String?,
|
||||||
channelType: json['channel_type'] as String?,
|
parentId: json['parent_id'] as String?,
|
||||||
parentId: json['parent_id'] as String?,
|
extraData: json['extra_data'] as Map<String, dynamic>? ?? const {},
|
||||||
extraData: json['extra_data'] as Map<String, dynamic>? ?? {},
|
isLocal: json['is_local'] as bool? ?? false,
|
||||||
isLocal: json['is_local'] as bool? ?? false,
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> _$EventToJson(Event instance) => <String, dynamic>{
|
Map<String, dynamic> _$EventToJson(Event instance) => <String, dynamic>{
|
||||||
'type': instance.type,
|
'type': instance.type,
|
||||||
@@ -64,30 +62,28 @@ Map<String, dynamic> _$EventToJson(Event instance) => <String, dynamic>{
|
|||||||
'extra_data': instance.extraData,
|
'extra_data': instance.extraData,
|
||||||
};
|
};
|
||||||
|
|
||||||
EventChannel _$EventChannelFromJson(Map<String, dynamic> json) {
|
EventChannel _$EventChannelFromJson(Map<String, dynamic> json) => EventChannel(
|
||||||
return EventChannel(
|
members: (json['members'] as List<dynamic>?)
|
||||||
members: (json['members'] as List<dynamic>?)
|
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
|
||||||
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
|
.toList(),
|
||||||
.toList(),
|
id: json['id'] as String?,
|
||||||
id: json['id'] as String?,
|
type: json['type'] as String?,
|
||||||
type: json['type'] as String?,
|
cid: json['cid'] as String,
|
||||||
cid: json['cid'] as String,
|
config: ChannelConfig.fromJson(json['config'] as Map<String, dynamic>),
|
||||||
config: ChannelConfig.fromJson(json['config'] as Map<String, dynamic>),
|
createdBy: json['created_by'] == null
|
||||||
createdBy: json['created_by'] == null
|
? null
|
||||||
? null
|
: User.fromJson(json['created_by'] as Map<String, dynamic>),
|
||||||
: User.fromJson(json['created_by'] as Map<String, dynamic>),
|
frozen: json['frozen'] as bool? ?? false,
|
||||||
frozen: json['frozen'] as bool? ?? false,
|
lastMessageAt: json['last_message_at'] == null
|
||||||
lastMessageAt: json['last_message_at'] == null
|
? null
|
||||||
? null
|
: DateTime.parse(json['last_message_at'] as String),
|
||||||
: DateTime.parse(json['last_message_at'] as String),
|
createdAt: DateTime.parse(json['created_at'] as String),
|
||||||
createdAt: DateTime.parse(json['created_at'] as String),
|
updatedAt: DateTime.parse(json['updated_at'] as String),
|
||||||
updatedAt: DateTime.parse(json['updated_at'] as String),
|
deletedAt: json['deleted_at'] == null
|
||||||
deletedAt: json['deleted_at'] == null
|
? null
|
||||||
? null
|
: DateTime.parse(json['deleted_at'] as String),
|
||||||
: DateTime.parse(json['deleted_at'] as String),
|
memberCount: json['member_count'] as int,
|
||||||
memberCount: json['member_count'] as int? ?? 0,
|
extraData: json['extra_data'] as Map<String, dynamic>?,
|
||||||
extraData: json['extra_data'] as Map<String, dynamic>? ?? {},
|
cooldown: json['cooldown'] as int? ?? 0,
|
||||||
cooldown: json['cooldown'] as int? ?? 0,
|
team: json['team'] as String?,
|
||||||
team: json['team'] as String?,
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -102,6 +102,12 @@ class Filter extends Equatable {
|
|||||||
this.key,
|
this.key,
|
||||||
}) : operator = operator.rawValue;
|
}) : operator = operator.rawValue;
|
||||||
|
|
||||||
|
/// An empty filter
|
||||||
|
const Filter.empty()
|
||||||
|
: value = const <String, Object?>{},
|
||||||
|
operator = null,
|
||||||
|
key = null;
|
||||||
|
|
||||||
/// Combines the provided filters and matches the values
|
/// Combines the provided filters and matches the values
|
||||||
/// matched by all filters.
|
/// matched by all filters.
|
||||||
factory Filter.and(List<Filter> filters) =>
|
factory Filter.and(List<Filter> filters) =>
|
||||||
@@ -161,6 +167,9 @@ class Filter extends Equatable {
|
|||||||
factory Filter.exists(String key, {bool exists = true}) =>
|
factory Filter.exists(String key, {bool exists = true}) =>
|
||||||
Filter._(operator: FilterOperator.exists, key: key, value: exists);
|
Filter._(operator: FilterOperator.exists, key: key, value: exists);
|
||||||
|
|
||||||
|
/// Matches values that don't exist.
|
||||||
|
factory Filter.notExists(String key) => Filter.exists(key, exists: false);
|
||||||
|
|
||||||
/// Matches any list that contains the specified values
|
/// Matches any list that contains the specified values
|
||||||
factory Filter.contains(String key, Object value) =>
|
factory Filter.contains(String key, Object value) =>
|
||||||
Filter._(operator: FilterOperator.contains, key: key, value: value);
|
Filter._(operator: FilterOperator.contains, key: key, value: value);
|
||||||
@@ -172,9 +181,6 @@ class Filter extends Equatable {
|
|||||||
String? key,
|
String? key,
|
||||||
}) = Filter.__;
|
}) = Filter.__;
|
||||||
|
|
||||||
/// An empty filter
|
|
||||||
factory Filter.empty() => const Filter.raw(value: {});
|
|
||||||
|
|
||||||
/// Creates a custom [Filter] from a raw map value
|
/// Creates a custom [Filter] from a raw map value
|
||||||
///
|
///
|
||||||
/// ```dart
|
/// ```dart
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ class Member extends Equatable {
|
|||||||
final DateTime? inviteRejectedAt;
|
final DateTime? inviteRejectedAt;
|
||||||
|
|
||||||
/// True if the user has been invited to the channel
|
/// True if the user has been invited to the channel
|
||||||
@JsonKey(defaultValue: false)
|
|
||||||
final bool invited;
|
final bool invited;
|
||||||
|
|
||||||
/// The role of the user in the channel
|
/// The role of the user in the channel
|
||||||
@@ -52,15 +51,12 @@ class Member extends Equatable {
|
|||||||
final String? userId;
|
final String? userId;
|
||||||
|
|
||||||
/// True if the user is a moderator of the channel
|
/// True if the user is a moderator of the channel
|
||||||
@JsonKey(defaultValue: false)
|
|
||||||
final bool isModerator;
|
final bool isModerator;
|
||||||
|
|
||||||
/// True if the member is banned from the channel
|
/// True if the member is banned from the channel
|
||||||
@JsonKey(defaultValue: false)
|
|
||||||
final bool banned;
|
final bool banned;
|
||||||
|
|
||||||
/// True if the member is shadow banned from the channel
|
/// True if the member is shadow banned from the channel
|
||||||
@JsonKey(defaultValue: false)
|
|
||||||
final bool shadowBanned;
|
final bool shadowBanned;
|
||||||
|
|
||||||
/// The date of creation
|
/// The date of creation
|
||||||
|
|||||||
@@ -6,31 +6,29 @@ part of 'member.dart';
|
|||||||
// JsonSerializableGenerator
|
// JsonSerializableGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
Member _$MemberFromJson(Map<String, dynamic> json) {
|
Member _$MemberFromJson(Map<String, dynamic> json) => Member(
|
||||||
return Member(
|
user: json['user'] == null
|
||||||
user: json['user'] == null
|
? null
|
||||||
? null
|
: User.fromJson(json['user'] as Map<String, dynamic>),
|
||||||
: User.fromJson(json['user'] as Map<String, dynamic>),
|
inviteAcceptedAt: json['invite_accepted_at'] == null
|
||||||
inviteAcceptedAt: json['invite_accepted_at'] == null
|
? null
|
||||||
? null
|
: DateTime.parse(json['invite_accepted_at'] as String),
|
||||||
: DateTime.parse(json['invite_accepted_at'] as String),
|
inviteRejectedAt: json['invite_rejected_at'] == null
|
||||||
inviteRejectedAt: json['invite_rejected_at'] == null
|
? null
|
||||||
? null
|
: DateTime.parse(json['invite_rejected_at'] as String),
|
||||||
: DateTime.parse(json['invite_rejected_at'] as String),
|
invited: json['invited'] as bool? ?? false,
|
||||||
invited: json['invited'] as bool? ?? false,
|
role: json['role'] as String?,
|
||||||
role: json['role'] as String?,
|
userId: json['user_id'] as String?,
|
||||||
userId: json['user_id'] as String?,
|
isModerator: json['is_moderator'] as bool? ?? false,
|
||||||
isModerator: json['is_moderator'] as bool? ?? false,
|
createdAt: json['created_at'] == null
|
||||||
createdAt: json['created_at'] == null
|
? null
|
||||||
? null
|
: DateTime.parse(json['created_at'] as String),
|
||||||
: DateTime.parse(json['created_at'] as String),
|
updatedAt: json['updated_at'] == null
|
||||||
updatedAt: json['updated_at'] == null
|
? null
|
||||||
? null
|
: DateTime.parse(json['updated_at'] as String),
|
||||||
: DateTime.parse(json['updated_at'] as String),
|
banned: json['banned'] as bool? ?? false,
|
||||||
banned: json['banned'] as bool? ?? false,
|
shadowBanned: json['shadow_banned'] as bool? ?? false,
|
||||||
shadowBanned: json['shadow_banned'] as bool? ?? false,
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> _$MemberToJson(Member instance) => <String, dynamic>{
|
Map<String, dynamic> _$MemberToJson(Member instance) => <String, dynamic>{
|
||||||
'user': instance.user?.toJson(),
|
'user': instance.user?.toJson(),
|
||||||
|
|||||||
@@ -98,23 +98,16 @@ class Message extends Equatable {
|
|||||||
@JsonKey(
|
@JsonKey(
|
||||||
includeIfNull: false,
|
includeIfNull: false,
|
||||||
toJson: Serializer.readOnly,
|
toJson: Serializer.readOnly,
|
||||||
defaultValue: 'regular',
|
|
||||||
)
|
)
|
||||||
final String type;
|
final String type;
|
||||||
|
|
||||||
/// The list of attachments, either provided by the user or generated from a
|
/// The list of attachments, either provided by the user or generated from a
|
||||||
/// command or as a result of URL scraping.
|
/// command or as a result of URL scraping.
|
||||||
@JsonKey(
|
@JsonKey(includeIfNull: false)
|
||||||
includeIfNull: false,
|
|
||||||
defaultValue: [],
|
|
||||||
)
|
|
||||||
final List<Attachment> attachments;
|
final List<Attachment> attachments;
|
||||||
|
|
||||||
/// The list of user mentioned in the message
|
/// The list of user mentioned in the message
|
||||||
@JsonKey(
|
@JsonKey(toJson: User.toIds)
|
||||||
toJson: User.toIds,
|
|
||||||
defaultValue: [],
|
|
||||||
)
|
|
||||||
final List<User> mentionedUsers;
|
final List<User> mentionedUsers;
|
||||||
|
|
||||||
/// A map describing the count of number of every reaction
|
/// A map describing the count of number of every reaction
|
||||||
@@ -155,14 +148,12 @@ class Message extends Equatable {
|
|||||||
final bool? showInChannel;
|
final bool? showInChannel;
|
||||||
|
|
||||||
/// If true the message is silent
|
/// If true the message is silent
|
||||||
@JsonKey(defaultValue: false)
|
|
||||||
final bool silent;
|
final bool silent;
|
||||||
|
|
||||||
/// If true the message is shadowed
|
/// If true the message is shadowed
|
||||||
@JsonKey(
|
@JsonKey(
|
||||||
includeIfNull: false,
|
includeIfNull: false,
|
||||||
toJson: Serializer.readOnly,
|
toJson: Serializer.readOnly,
|
||||||
defaultValue: false,
|
|
||||||
)
|
)
|
||||||
final bool shadowed;
|
final bool shadowed;
|
||||||
|
|
||||||
@@ -183,7 +174,6 @@ class Message extends Equatable {
|
|||||||
final User? user;
|
final User? user;
|
||||||
|
|
||||||
/// If true the message is pinned
|
/// If true the message is pinned
|
||||||
@JsonKey(defaultValue: false)
|
|
||||||
final bool pinned;
|
final bool pinned;
|
||||||
|
|
||||||
/// Reserved field indicating when the message was pinned
|
/// Reserved field indicating when the message was pinned
|
||||||
@@ -200,10 +190,7 @@ class Message extends Equatable {
|
|||||||
final User? pinnedBy;
|
final User? pinnedBy;
|
||||||
|
|
||||||
/// Message custom extraData
|
/// Message custom extraData
|
||||||
@JsonKey(
|
@JsonKey(includeIfNull: false)
|
||||||
includeIfNull: false,
|
|
||||||
defaultValue: {},
|
|
||||||
)
|
|
||||||
final Map<String, Object?> extraData;
|
final Map<String, Object?> extraData;
|
||||||
|
|
||||||
/// True if the message is a system info
|
/// True if the message is a system info
|
||||||
|
|||||||
@@ -6,72 +6,70 @@ part of 'message.dart';
|
|||||||
// JsonSerializableGenerator
|
// JsonSerializableGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
Message _$MessageFromJson(Map<String, dynamic> json) {
|
Message _$MessageFromJson(Map<String, dynamic> json) => Message(
|
||||||
return Message(
|
id: json['id'] as String?,
|
||||||
id: json['id'] as String?,
|
text: json['text'] as String?,
|
||||||
text: json['text'] as String?,
|
type: json['type'] as String? ?? 'regular',
|
||||||
type: json['type'] as String? ?? 'regular',
|
attachments: (json['attachments'] as List<dynamic>?)
|
||||||
attachments: (json['attachments'] as List<dynamic>?)
|
?.map((e) => Attachment.fromJson(e as Map<String, dynamic>))
|
||||||
?.map((e) => Attachment.fromJson(e as Map<String, dynamic>))
|
.toList() ??
|
||||||
.toList() ??
|
const [],
|
||||||
[],
|
mentionedUsers: (json['mentioned_users'] as List<dynamic>?)
|
||||||
mentionedUsers: (json['mentioned_users'] as List<dynamic>?)
|
?.map((e) => User.fromJson(e as Map<String, dynamic>))
|
||||||
?.map((e) => User.fromJson(e as Map<String, dynamic>))
|
.toList() ??
|
||||||
.toList() ??
|
const [],
|
||||||
[],
|
silent: json['silent'] as bool? ?? false,
|
||||||
silent: json['silent'] as bool? ?? false,
|
shadowed: json['shadowed'] as bool? ?? false,
|
||||||
shadowed: json['shadowed'] as bool? ?? false,
|
reactionCounts: (json['reaction_counts'] as Map<String, dynamic>?)?.map(
|
||||||
reactionCounts: (json['reaction_counts'] as Map<String, dynamic>?)?.map(
|
(k, e) => MapEntry(k, e as int),
|
||||||
(k, e) => MapEntry(k, e as int),
|
),
|
||||||
),
|
reactionScores: (json['reaction_scores'] as Map<String, dynamic>?)?.map(
|
||||||
reactionScores: (json['reaction_scores'] as Map<String, dynamic>?)?.map(
|
(k, e) => MapEntry(k, e as int),
|
||||||
(k, e) => MapEntry(k, e as int),
|
),
|
||||||
),
|
latestReactions: (json['latest_reactions'] as List<dynamic>?)
|
||||||
latestReactions: (json['latest_reactions'] as List<dynamic>?)
|
?.map((e) => Reaction.fromJson(e as Map<String, dynamic>))
|
||||||
?.map((e) => Reaction.fromJson(e as Map<String, dynamic>))
|
.toList(),
|
||||||
.toList(),
|
ownReactions: (json['own_reactions'] as List<dynamic>?)
|
||||||
ownReactions: (json['own_reactions'] as List<dynamic>?)
|
?.map((e) => Reaction.fromJson(e as Map<String, dynamic>))
|
||||||
?.map((e) => Reaction.fromJson(e as Map<String, dynamic>))
|
.toList(),
|
||||||
.toList(),
|
parentId: json['parent_id'] as String?,
|
||||||
parentId: json['parent_id'] as String?,
|
quotedMessage: json['quoted_message'] == null
|
||||||
quotedMessage: json['quoted_message'] == null
|
? null
|
||||||
? null
|
: Message.fromJson(json['quoted_message'] as Map<String, dynamic>),
|
||||||
: Message.fromJson(json['quoted_message'] as Map<String, dynamic>),
|
quotedMessageId: json['quoted_message_id'] as String?,
|
||||||
quotedMessageId: json['quoted_message_id'] as String?,
|
replyCount: json['reply_count'] as int? ?? 0,
|
||||||
replyCount: json['reply_count'] as int?,
|
threadParticipants: (json['thread_participants'] as List<dynamic>?)
|
||||||
threadParticipants: (json['thread_participants'] as List<dynamic>?)
|
?.map((e) => User.fromJson(e as Map<String, dynamic>))
|
||||||
?.map((e) => User.fromJson(e as Map<String, dynamic>))
|
.toList(),
|
||||||
.toList(),
|
showInChannel: json['show_in_channel'] as bool?,
|
||||||
showInChannel: json['show_in_channel'] as bool?,
|
command: json['command'] as String?,
|
||||||
command: json['command'] as String?,
|
createdAt: json['created_at'] == null
|
||||||
createdAt: json['created_at'] == null
|
? null
|
||||||
? null
|
: DateTime.parse(json['created_at'] as String),
|
||||||
: DateTime.parse(json['created_at'] as String),
|
updatedAt: json['updated_at'] == null
|
||||||
updatedAt: json['updated_at'] == null
|
? null
|
||||||
? null
|
: DateTime.parse(json['updated_at'] as String),
|
||||||
: DateTime.parse(json['updated_at'] as String),
|
user: json['user'] == null
|
||||||
user: json['user'] == null
|
? null
|
||||||
? null
|
: User.fromJson(json['user'] as Map<String, dynamic>),
|
||||||
: User.fromJson(json['user'] as Map<String, dynamic>),
|
pinned: json['pinned'] as bool? ?? false,
|
||||||
pinned: json['pinned'] as bool? ?? false,
|
pinnedAt: json['pinned_at'] == null
|
||||||
pinnedAt: json['pinned_at'] == null
|
? null
|
||||||
? null
|
: DateTime.parse(json['pinned_at'] as String),
|
||||||
: DateTime.parse(json['pinned_at'] as String),
|
pinExpires: json['pin_expires'] == null
|
||||||
pinExpires: json['pin_expires'] == null
|
? null
|
||||||
? null
|
: DateTime.parse(json['pin_expires'] as String),
|
||||||
: DateTime.parse(json['pin_expires'] as String),
|
pinnedBy: json['pinned_by'] == null
|
||||||
pinnedBy: json['pinned_by'] == null
|
? null
|
||||||
? null
|
: User.fromJson(json['pinned_by'] as Map<String, dynamic>),
|
||||||
: User.fromJson(json['pinned_by'] as Map<String, dynamic>),
|
extraData: json['extra_data'] as Map<String, dynamic>? ?? const {},
|
||||||
extraData: json['extra_data'] as Map<String, dynamic>? ?? {},
|
deletedAt: json['deleted_at'] == null
|
||||||
deletedAt: json['deleted_at'] == null
|
? null
|
||||||
? null
|
: DateTime.parse(json['deleted_at'] as String),
|
||||||
: DateTime.parse(json['deleted_at'] as String),
|
i18n: (json['i18n'] as Map<String, dynamic>?)?.map(
|
||||||
i18n: (json['i18n'] as Map<String, dynamic>?)?.map(
|
(k, e) => MapEntry(k, e as String),
|
||||||
(k, e) => MapEntry(k, e as String),
|
),
|
||||||
),
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> _$MessageToJson(Message instance) {
|
Map<String, dynamic> _$MessageToJson(Message instance) {
|
||||||
final val = <String, dynamic>{
|
final val = <String, dynamic>{
|
||||||
|
|||||||
@@ -6,11 +6,9 @@ part of 'mute.dart';
|
|||||||
// JsonSerializableGenerator
|
// JsonSerializableGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
Mute _$MuteFromJson(Map<String, dynamic> json) {
|
Mute _$MuteFromJson(Map<String, dynamic> json) => Mute(
|
||||||
return Mute(
|
user: User.fromJson(json['user'] as Map<String, dynamic>),
|
||||||
user: User.fromJson(json['user'] as Map<String, dynamic>),
|
channel: ChannelModel.fromJson(json['channel'] as Map<String, dynamic>),
|
||||||
channel: ChannelModel.fromJson(json['channel'] as Map<String, dynamic>),
|
createdAt: DateTime.parse(json['created_at'] as String),
|
||||||
createdAt: DateTime.parse(json['created_at'] as String),
|
updatedAt: DateTime.parse(json['updated_at'] as String),
|
||||||
updatedAt: DateTime.parse(json['updated_at'] as String),
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class OwnUser extends User {
|
|||||||
this.devices = const [],
|
this.devices = const [],
|
||||||
this.mutes = const [],
|
this.mutes = const [],
|
||||||
this.totalUnreadCount = 0,
|
this.totalUnreadCount = 0,
|
||||||
this.unreadChannels,
|
this.unreadChannels = 0,
|
||||||
this.channelMutes = const [],
|
this.channelMutes = const [],
|
||||||
required String id,
|
required String id,
|
||||||
String? role,
|
String? role,
|
||||||
@@ -54,8 +54,6 @@ class OwnUser extends User {
|
|||||||
factory OwnUser.fromUser(User user) => OwnUser(
|
factory OwnUser.fromUser(User user) => OwnUser(
|
||||||
id: user.id,
|
id: user.id,
|
||||||
role: user.role,
|
role: user.role,
|
||||||
name: user.name,
|
|
||||||
image: user.image,
|
|
||||||
createdAt: user.createdAt,
|
createdAt: user.createdAt,
|
||||||
updatedAt: user.updatedAt,
|
updatedAt: user.updatedAt,
|
||||||
lastActive: user.lastActive,
|
lastActive: user.lastActive,
|
||||||
@@ -116,8 +114,6 @@ class OwnUser extends User {
|
|||||||
return copyWith(
|
return copyWith(
|
||||||
id: other.id,
|
id: other.id,
|
||||||
role: other.role,
|
role: other.role,
|
||||||
name: other.name,
|
|
||||||
image: other.image,
|
|
||||||
banned: other.banned,
|
banned: other.banned,
|
||||||
channelMutes: other.channelMutes,
|
channelMutes: other.channelMutes,
|
||||||
createdAt: other.createdAt,
|
createdAt: other.createdAt,
|
||||||
@@ -135,24 +131,24 @@ class OwnUser extends User {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// List of user devices.
|
/// List of user devices.
|
||||||
@JsonKey(includeIfNull: false, defaultValue: <Device>[])
|
@JsonKey(includeIfNull: false)
|
||||||
final List<Device> devices;
|
final List<Device> devices;
|
||||||
|
|
||||||
/// List of users muted by the user.
|
/// List of users muted by the user.
|
||||||
@JsonKey(includeIfNull: false, defaultValue: <Mute>[])
|
@JsonKey(includeIfNull: false)
|
||||||
final List<Mute> mutes;
|
final List<Mute> mutes;
|
||||||
|
|
||||||
/// List of users muted by the user.
|
/// List of users muted by the user.
|
||||||
@JsonKey(includeIfNull: false, defaultValue: <Mute>[])
|
@JsonKey(includeIfNull: false)
|
||||||
final List<Mute> channelMutes;
|
final List<Mute> channelMutes;
|
||||||
|
|
||||||
/// Total unread messages by the user.
|
/// Total unread messages by the user.
|
||||||
@JsonKey(includeIfNull: false, defaultValue: 0)
|
@JsonKey(includeIfNull: false)
|
||||||
final int totalUnreadCount;
|
final int totalUnreadCount;
|
||||||
|
|
||||||
/// Total unread channels by the user.
|
/// Total unread channels by the user.
|
||||||
@JsonKey(includeIfNull: false)
|
@JsonKey(includeIfNull: false)
|
||||||
final int? unreadChannels;
|
final int unreadChannels;
|
||||||
|
|
||||||
/// Known top level fields.
|
/// Known top level fields.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -6,39 +6,37 @@ part of 'own_user.dart';
|
|||||||
// JsonSerializableGenerator
|
// JsonSerializableGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
OwnUser _$OwnUserFromJson(Map<String, dynamic> json) {
|
OwnUser _$OwnUserFromJson(Map<String, dynamic> json) => OwnUser(
|
||||||
return OwnUser(
|
devices: (json['devices'] as List<dynamic>?)
|
||||||
devices: (json['devices'] as List<dynamic>?)
|
?.map((e) => Device.fromJson(e as Map<String, dynamic>))
|
||||||
?.map((e) => Device.fromJson(e as Map<String, dynamic>))
|
.toList() ??
|
||||||
.toList() ??
|
const [],
|
||||||
[],
|
mutes: (json['mutes'] as List<dynamic>?)
|
||||||
mutes: (json['mutes'] as List<dynamic>?)
|
?.map((e) => Mute.fromJson(e as Map<String, dynamic>))
|
||||||
?.map((e) => Mute.fromJson(e as Map<String, dynamic>))
|
.toList() ??
|
||||||
.toList() ??
|
const [],
|
||||||
[],
|
totalUnreadCount: json['total_unread_count'] as int? ?? 0,
|
||||||
totalUnreadCount: json['total_unread_count'] as int? ?? 0,
|
unreadChannels: json['unread_channels'] as int? ?? 0,
|
||||||
unreadChannels: json['unread_channels'] as int?,
|
channelMutes: (json['channel_mutes'] as List<dynamic>?)
|
||||||
channelMutes: (json['channel_mutes'] as List<dynamic>?)
|
?.map((e) => Mute.fromJson(e as Map<String, dynamic>))
|
||||||
?.map((e) => Mute.fromJson(e as Map<String, dynamic>))
|
.toList() ??
|
||||||
.toList() ??
|
const [],
|
||||||
[],
|
id: json['id'] as String,
|
||||||
id: json['id'] as String,
|
role: json['role'] as String?,
|
||||||
role: json['role'] as String?,
|
createdAt: json['created_at'] == null
|
||||||
createdAt: json['created_at'] == null
|
? null
|
||||||
? null
|
: DateTime.parse(json['created_at'] as String),
|
||||||
: DateTime.parse(json['created_at'] as String),
|
updatedAt: json['updated_at'] == null
|
||||||
updatedAt: json['updated_at'] == null
|
? null
|
||||||
? null
|
: DateTime.parse(json['updated_at'] as String),
|
||||||
: DateTime.parse(json['updated_at'] as String),
|
lastActive: json['last_active'] == null
|
||||||
lastActive: json['last_active'] == null
|
? null
|
||||||
? null
|
: DateTime.parse(json['last_active'] as String),
|
||||||
: DateTime.parse(json['last_active'] as String),
|
online: json['online'] as bool? ?? false,
|
||||||
online: json['online'] as bool? ?? false,
|
extraData: json['extra_data'] as Map<String, dynamic>? ?? const {},
|
||||||
extraData: json['extra_data'] as Map<String, dynamic>? ?? {},
|
banned: json['banned'] as bool? ?? false,
|
||||||
banned: json['banned'] as bool? ?? false,
|
teams:
|
||||||
teams:
|
(json['teams'] as List<dynamic>?)?.map((e) => e as String).toList() ??
|
||||||
(json['teams'] as List<dynamic>?)?.map((e) => e as String).toList() ??
|
const [],
|
||||||
[],
|
language: json['language'] as String?,
|
||||||
language: json['language'] as String?,
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
import 'package:stream_chat/src/core/util/serializer.dart';
|
|
||||||
import 'package:stream_chat/src/core/models/user.dart';
|
import 'package:stream_chat/src/core/models/user.dart';
|
||||||
|
import 'package:stream_chat/src/core/util/serializer.dart';
|
||||||
|
|
||||||
part 'reaction.g.dart';
|
part 'reaction.g.dart';
|
||||||
|
|
||||||
@@ -41,7 +41,6 @@ class Reaction {
|
|||||||
final User? user;
|
final User? user;
|
||||||
|
|
||||||
/// The score of the reaction (ie. number of reactions sent)
|
/// The score of the reaction (ie. number of reactions sent)
|
||||||
@JsonKey(defaultValue: 0)
|
|
||||||
final int score;
|
final int score;
|
||||||
|
|
||||||
/// The userId that sent the reaction
|
/// The userId that sent the reaction
|
||||||
@@ -49,10 +48,7 @@ class Reaction {
|
|||||||
final String? userId;
|
final String? userId;
|
||||||
|
|
||||||
/// Reaction custom extraData
|
/// Reaction custom extraData
|
||||||
@JsonKey(
|
@JsonKey(includeIfNull: false)
|
||||||
includeIfNull: false,
|
|
||||||
defaultValue: {},
|
|
||||||
)
|
|
||||||
final Map<String, Object?> extraData;
|
final Map<String, Object?> extraData;
|
||||||
|
|
||||||
/// Map of custom user extraData
|
/// Map of custom user extraData
|
||||||
|
|||||||
@@ -6,21 +6,19 @@ part of 'reaction.dart';
|
|||||||
// JsonSerializableGenerator
|
// JsonSerializableGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
Reaction _$ReactionFromJson(Map<String, dynamic> json) {
|
Reaction _$ReactionFromJson(Map<String, dynamic> json) => Reaction(
|
||||||
return Reaction(
|
messageId: json['message_id'] as String?,
|
||||||
messageId: json['message_id'] as String?,
|
createdAt: json['created_at'] == null
|
||||||
createdAt: json['created_at'] == null
|
? null
|
||||||
? null
|
: DateTime.parse(json['created_at'] as String),
|
||||||
: DateTime.parse(json['created_at'] as String),
|
type: json['type'] as String,
|
||||||
type: json['type'] as String,
|
user: json['user'] == null
|
||||||
user: json['user'] == null
|
? null
|
||||||
? null
|
: User.fromJson(json['user'] as Map<String, dynamic>),
|
||||||
: User.fromJson(json['user'] as Map<String, dynamic>),
|
userId: json['user_id'] as String?,
|
||||||
userId: json['user_id'] as String?,
|
score: json['score'] as int? ?? 0,
|
||||||
score: json['score'] as int? ?? 0,
|
extraData: json['extra_data'] as Map<String, dynamic>? ?? const {},
|
||||||
extraData: json['extra_data'] as Map<String, dynamic>? ?? {},
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> _$ReactionToJson(Reaction instance) {
|
Map<String, dynamic> _$ReactionToJson(Reaction instance) {
|
||||||
final val = <String, dynamic>{
|
final val = <String, dynamic>{
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ class Read {
|
|||||||
final User user;
|
final User user;
|
||||||
|
|
||||||
/// Number of unread messages
|
/// Number of unread messages
|
||||||
@JsonKey(defaultValue: 0)
|
|
||||||
final int unreadMessages;
|
final int unreadMessages;
|
||||||
|
|
||||||
/// Serialize to json
|
/// Serialize to json
|
||||||
|
|||||||
@@ -6,13 +6,11 @@ part of 'read.dart';
|
|||||||
// JsonSerializableGenerator
|
// JsonSerializableGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
Read _$ReadFromJson(Map<String, dynamic> json) {
|
Read _$ReadFromJson(Map<String, dynamic> json) => Read(
|
||||||
return Read(
|
lastRead: DateTime.parse(json['last_read'] as String),
|
||||||
lastRead: DateTime.parse(json['last_read'] as String),
|
user: User.fromJson(json['user'] as Map<String, dynamic>),
|
||||||
user: User.fromJson(json['user'] as Map<String, dynamic>),
|
unreadMessages: json['unread_messages'] as int? ?? 0,
|
||||||
unreadMessages: json['unread_messages'] as int? ?? 0,
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> _$ReadToJson(Read instance) => <String, dynamic>{
|
Map<String, dynamic> _$ReadToJson(Read instance) => <String, dynamic>{
|
||||||
'last_read': instance.lastRead.toIso8601String(),
|
'last_read': instance.lastRead.toIso8601String(),
|
||||||
|
|||||||
@@ -100,7 +100,6 @@ class User extends Equatable {
|
|||||||
@JsonKey(
|
@JsonKey(
|
||||||
includeIfNull: false,
|
includeIfNull: false,
|
||||||
toJson: Serializer.readOnly,
|
toJson: Serializer.readOnly,
|
||||||
defaultValue: <String>[],
|
|
||||||
)
|
)
|
||||||
final List<String> teams;
|
final List<String> teams;
|
||||||
|
|
||||||
@@ -118,19 +117,20 @@ class User extends Equatable {
|
|||||||
|
|
||||||
/// True if user is online.
|
/// True if user is online.
|
||||||
@JsonKey(
|
@JsonKey(
|
||||||
includeIfNull: false, toJson: Serializer.readOnly, defaultValue: false)
|
includeIfNull: false,
|
||||||
|
toJson: Serializer.readOnly,
|
||||||
|
)
|
||||||
final bool online;
|
final bool online;
|
||||||
|
|
||||||
/// True if user is banned from the chat.
|
/// True if user is banned from the chat.
|
||||||
@JsonKey(
|
@JsonKey(
|
||||||
includeIfNull: false, toJson: Serializer.readOnly, defaultValue: false)
|
includeIfNull: false,
|
||||||
|
toJson: Serializer.readOnly,
|
||||||
|
)
|
||||||
final bool banned;
|
final bool banned;
|
||||||
|
|
||||||
/// Map of custom user extraData.
|
/// Map of custom user extraData.
|
||||||
@JsonKey(
|
@JsonKey(includeIfNull: false)
|
||||||
includeIfNull: false,
|
|
||||||
defaultValue: {},
|
|
||||||
)
|
|
||||||
final Map<String, Object?> extraData;
|
final Map<String, Object?> extraData;
|
||||||
|
|
||||||
/// The language this user prefers.
|
/// The language this user prefers.
|
||||||
|
|||||||
@@ -6,28 +6,26 @@ part of 'user.dart';
|
|||||||
// JsonSerializableGenerator
|
// JsonSerializableGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
User _$UserFromJson(Map<String, dynamic> json) {
|
User _$UserFromJson(Map<String, dynamic> json) => User(
|
||||||
return User(
|
id: json['id'] as String,
|
||||||
id: json['id'] as String,
|
role: json['role'] as String?,
|
||||||
role: json['role'] as String?,
|
createdAt: json['created_at'] == null
|
||||||
createdAt: json['created_at'] == null
|
? null
|
||||||
? null
|
: DateTime.parse(json['created_at'] as String),
|
||||||
: DateTime.parse(json['created_at'] as String),
|
updatedAt: json['updated_at'] == null
|
||||||
updatedAt: json['updated_at'] == null
|
? null
|
||||||
? null
|
: DateTime.parse(json['updated_at'] as String),
|
||||||
: DateTime.parse(json['updated_at'] as String),
|
lastActive: json['last_active'] == null
|
||||||
lastActive: json['last_active'] == null
|
? null
|
||||||
? null
|
: DateTime.parse(json['last_active'] as String),
|
||||||
: DateTime.parse(json['last_active'] as String),
|
extraData: json['extra_data'] as Map<String, dynamic>? ?? const {},
|
||||||
extraData: json['extra_data'] as Map<String, dynamic>? ?? {},
|
online: json['online'] as bool? ?? false,
|
||||||
online: json['online'] as bool? ?? false,
|
banned: json['banned'] as bool? ?? false,
|
||||||
banned: json['banned'] as bool? ?? false,
|
teams:
|
||||||
teams:
|
(json['teams'] as List<dynamic>?)?.map((e) => e as String).toList() ??
|
||||||
(json['teams'] as List<dynamic>?)?.map((e) => e as String).toList() ??
|
const [],
|
||||||
[],
|
language: json['language'] as String?,
|
||||||
language: json['language'] as String?,
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> _$UserToJson(User instance) {
|
Map<String, dynamic> _$UserToJson(User instance) {
|
||||||
final val = <String, dynamic>{
|
final val = <String, dynamic>{
|
||||||
|
|||||||
@@ -6,15 +6,15 @@ import 'package:logging/logging.dart';
|
|||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
import 'package:rxdart/rxdart.dart';
|
import 'package:rxdart/rxdart.dart';
|
||||||
import 'package:stream_chat/src/core/error/error.dart';
|
import 'package:stream_chat/src/core/error/error.dart';
|
||||||
import 'package:stream_chat/src/ws/connection_status.dart';
|
|
||||||
import 'package:stream_chat/src/core/http/token.dart';
|
import 'package:stream_chat/src/core/http/token.dart';
|
||||||
import 'package:stream_chat/src/core/http/token_manager.dart';
|
import 'package:stream_chat/src/core/http/token_manager.dart';
|
||||||
import 'package:stream_chat/src/core/models/event.dart';
|
import 'package:stream_chat/src/core/models/event.dart';
|
||||||
import 'package:stream_chat/src/core/models/user.dart';
|
import 'package:stream_chat/src/core/models/user.dart';
|
||||||
import 'package:stream_chat/src/event_type.dart';
|
import 'package:stream_chat/src/event_type.dart';
|
||||||
|
import 'package:stream_chat/src/ws/connection_status.dart';
|
||||||
import 'package:stream_chat/src/ws/timer_helper.dart';
|
import 'package:stream_chat/src/ws/timer_helper.dart';
|
||||||
import 'package:web_socket_channel/web_socket_channel.dart';
|
|
||||||
import 'package:web_socket_channel/status.dart' as status;
|
import 'package:web_socket_channel/status.dart' as status;
|
||||||
|
import 'package:web_socket_channel/web_socket_channel.dart';
|
||||||
|
|
||||||
/// Typedef which exposes an [Event] as the only parameter.
|
/// Typedef which exposes an [Event] as the only parameter.
|
||||||
typedef EventHandler = void Function(Event);
|
typedef EventHandler = void Function(Event);
|
||||||
|
|||||||
@@ -3,4 +3,4 @@ import 'package:stream_chat/src/client/client.dart';
|
|||||||
/// Current package version
|
/// Current package version
|
||||||
/// Used in [StreamChatClient] to build the `x-stream-client` header
|
/// Used in [StreamChatClient] to build the `x-stream-client` header
|
||||||
// ignore: constant_identifier_names
|
// ignore: constant_identifier_names
|
||||||
const PACKAGE_VERSION = '2.2.1';
|
const PACKAGE_VERSION = '3.1.1';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
name: stream_chat
|
name: stream_chat
|
||||||
homepage: https://getstream.io/
|
homepage: https://getstream.io/
|
||||||
description: The official Dart client for Stream Chat, a service for building chat applications.
|
description: The official Dart client for Stream Chat, a service for building chat applications.
|
||||||
version: 2.2.1
|
version: 3.1.1
|
||||||
repository: https://github.com/GetStream/stream-chat-flutter
|
repository: https://github.com/GetStream/stream-chat-flutter
|
||||||
issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues
|
issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues
|
||||||
|
|
||||||
@@ -28,6 +28,6 @@ dependencies:
|
|||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
build_runner: ^2.0.1
|
build_runner: ^2.0.1
|
||||||
freezed: ^0.14.1+3
|
freezed: ^0.14.1+3
|
||||||
json_serializable: ^4.1.0
|
json_serializable: ^5.0.2
|
||||||
mocktail: ^0.1.1
|
mocktail: ^0.1.1
|
||||||
test: ^1.17.7
|
test: ^1.18.2
|
||||||
|
|||||||
@@ -334,20 +334,87 @@ void main() {
|
|||||||
expectLater(
|
expectLater(
|
||||||
// skipping first seed message list -> [] messages
|
// skipping first seed message list -> [] messages
|
||||||
channel.state?.messagesStream.skip(1),
|
channel.state?.messagesStream.skip(1),
|
||||||
emitsInOrder([
|
emitsInOrder(
|
||||||
[
|
[
|
||||||
isSameMessageAs(
|
// preparing attachments to upload
|
||||||
message.copyWith(status: MessageSendingStatus.sending),
|
[
|
||||||
matchSendingStatus: true,
|
isSameMessageAs(
|
||||||
),
|
message.copyWith(
|
||||||
|
status: MessageSendingStatus.sending,
|
||||||
|
attachments: [
|
||||||
|
...attachments.map((it) => it.copyWith(
|
||||||
|
uploadState: const UploadState.preparing()))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
matchSendingStatus: true,
|
||||||
|
matchAttachments: true,
|
||||||
|
matchAttachmentsUploadState: true,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
// 0th attachment is successfully uploaded
|
||||||
|
[
|
||||||
|
isSameMessageAs(
|
||||||
|
message.copyWith(
|
||||||
|
status: MessageSendingStatus.sending,
|
||||||
|
attachments: [...attachments]..[0] =
|
||||||
|
attachments[0].copyWith(
|
||||||
|
uploadState: const UploadState.success(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
matchSendingStatus: true,
|
||||||
|
matchAttachments: true,
|
||||||
|
matchAttachmentsUploadState: true,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
// 0th and 1st attachment is successfully uploaded
|
||||||
|
[
|
||||||
|
isSameMessageAs(
|
||||||
|
message.copyWith(
|
||||||
|
status: MessageSendingStatus.sending,
|
||||||
|
attachments: [...attachments]
|
||||||
|
..[0] = attachments[0].copyWith(
|
||||||
|
uploadState: const UploadState.success(),
|
||||||
|
)
|
||||||
|
..[1] = attachments[1].copyWith(
|
||||||
|
uploadState: const UploadState.success(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
matchSendingStatus: true,
|
||||||
|
matchAttachments: true,
|
||||||
|
matchAttachmentsUploadState: true,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
// all the attachments are successfully uploaded
|
||||||
|
[
|
||||||
|
isSameMessageAs(
|
||||||
|
message.copyWith(
|
||||||
|
status: MessageSendingStatus.sending,
|
||||||
|
attachments: [
|
||||||
|
...attachments.map((it) =>
|
||||||
|
it.copyWith(uploadState: const UploadState.success()))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
matchSendingStatus: true,
|
||||||
|
matchAttachments: true,
|
||||||
|
matchAttachmentsUploadState: true,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
isSameMessageAs(
|
||||||
|
message.copyWith(
|
||||||
|
status: MessageSendingStatus.sent,
|
||||||
|
attachments: [
|
||||||
|
...attachments.map((it) =>
|
||||||
|
it.copyWith(uploadState: const UploadState.success()))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
matchSendingStatus: true,
|
||||||
|
matchAttachments: true,
|
||||||
|
matchAttachmentsUploadState: true,
|
||||||
|
),
|
||||||
|
],
|
||||||
],
|
],
|
||||||
[
|
),
|
||||||
isSameMessageAs(
|
|
||||||
message.copyWith(status: MessageSendingStatus.sent),
|
|
||||||
matchSendingStatus: true,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
]),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
final res = await channel.sendMessage(message);
|
final res = await channel.sendMessage(message);
|
||||||
@@ -472,20 +539,87 @@ void main() {
|
|||||||
expectLater(
|
expectLater(
|
||||||
// skipping first seed message list -> [] messages
|
// skipping first seed message list -> [] messages
|
||||||
channel.state?.messagesStream.skip(1),
|
channel.state?.messagesStream.skip(1),
|
||||||
emitsInOrder([
|
emitsInOrder(
|
||||||
[
|
[
|
||||||
isSameMessageAs(
|
// preparing attachments to upload
|
||||||
message.copyWith(status: MessageSendingStatus.updating),
|
[
|
||||||
matchSendingStatus: true,
|
isSameMessageAs(
|
||||||
),
|
message.copyWith(
|
||||||
|
status: MessageSendingStatus.updating,
|
||||||
|
attachments: [
|
||||||
|
...attachments.map((it) => it.copyWith(
|
||||||
|
uploadState: const UploadState.preparing()))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
matchSendingStatus: true,
|
||||||
|
matchAttachments: true,
|
||||||
|
matchAttachmentsUploadState: true,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
// 0th attachment is successfully uploaded
|
||||||
|
[
|
||||||
|
isSameMessageAs(
|
||||||
|
message.copyWith(
|
||||||
|
status: MessageSendingStatus.updating,
|
||||||
|
attachments: [...attachments]..[0] =
|
||||||
|
attachments[0].copyWith(
|
||||||
|
uploadState: const UploadState.success(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
matchSendingStatus: true,
|
||||||
|
matchAttachments: true,
|
||||||
|
matchAttachmentsUploadState: true,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
// 0th and 1st attachment is successfully uploaded
|
||||||
|
[
|
||||||
|
isSameMessageAs(
|
||||||
|
message.copyWith(
|
||||||
|
status: MessageSendingStatus.updating,
|
||||||
|
attachments: [...attachments]
|
||||||
|
..[0] = attachments[0].copyWith(
|
||||||
|
uploadState: const UploadState.success(),
|
||||||
|
)
|
||||||
|
..[1] = attachments[1].copyWith(
|
||||||
|
uploadState: const UploadState.success(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
matchSendingStatus: true,
|
||||||
|
matchAttachments: true,
|
||||||
|
matchAttachmentsUploadState: true,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
// all the attachments are successfully uploaded
|
||||||
|
[
|
||||||
|
isSameMessageAs(
|
||||||
|
message.copyWith(
|
||||||
|
status: MessageSendingStatus.updating,
|
||||||
|
attachments: [
|
||||||
|
...attachments.map((it) =>
|
||||||
|
it.copyWith(uploadState: const UploadState.success()))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
matchSendingStatus: true,
|
||||||
|
matchAttachments: true,
|
||||||
|
matchAttachmentsUploadState: true,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
isSameMessageAs(
|
||||||
|
message.copyWith(
|
||||||
|
status: MessageSendingStatus.sent,
|
||||||
|
attachments: [
|
||||||
|
...attachments.map((it) =>
|
||||||
|
it.copyWith(uploadState: const UploadState.success()))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
matchSendingStatus: true,
|
||||||
|
matchAttachments: true,
|
||||||
|
matchAttachmentsUploadState: true,
|
||||||
|
),
|
||||||
|
],
|
||||||
],
|
],
|
||||||
[
|
),
|
||||||
isSameMessageAs(
|
|
||||||
message.copyWith(status: MessageSendingStatus.sent),
|
|
||||||
matchSendingStatus: true,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
]),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
final res = await channel.updateMessage(message);
|
final res = await channel.updateMessage(message);
|
||||||
|
|||||||
@@ -2313,5 +2313,27 @@ void main() {
|
|||||||
)).called(1);
|
)).called(1);
|
||||||
verifyNoMoreInteractions(api.message);
|
verifyNoMoreInteractions(api.message);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
'''setting the `currentUser` should also compute and update the unreadCounts''',
|
||||||
|
() {
|
||||||
|
final state = client.state;
|
||||||
|
final initialUser = OwnUser.fromUser(user);
|
||||||
|
|
||||||
|
expect(state.currentUser, initialUser);
|
||||||
|
expect(state.totalUnreadCount, 0);
|
||||||
|
expect(state.unreadChannels, 0);
|
||||||
|
|
||||||
|
final updateUser = initialUser.copyWith(
|
||||||
|
totalUnreadCount: 33,
|
||||||
|
unreadChannels: 33,
|
||||||
|
);
|
||||||
|
state.currentUser = updateUser;
|
||||||
|
|
||||||
|
expect(state.currentUser, updateUser);
|
||||||
|
expect(state.totalUnreadCount, 33);
|
||||||
|
expect(state.unreadChannels, 33);
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,6 +86,24 @@ void main() {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
test(
|
||||||
|
'should throw if `pagination.offset` and `sort` both are provided',
|
||||||
|
() async {
|
||||||
|
final filter = Filter.in_('cid', const ['test-cid-1', 'test-cid-2']);
|
||||||
|
const sort = [SortOption<ChannelModel>('test-field')];
|
||||||
|
const pagination = PaginationParams(offset: 10);
|
||||||
|
try {
|
||||||
|
await generalApi.searchMessages(
|
||||||
|
filter,
|
||||||
|
sort: sort,
|
||||||
|
pagination: pagination,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
expect(e, isA<AssertionError>());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
test('should run successfully with `query`', () async {
|
test('should run successfully with `query`', () async {
|
||||||
final filter = Filter.in_('cid', const ['test-cid-1', 'test-cid-2']);
|
final filter = Filter.in_('cid', const ['test-cid-1', 'test-cid-2']);
|
||||||
const query = 'test-query';
|
const query = 'test-query';
|
||||||
|
|||||||
@@ -9,11 +9,23 @@ void main() {
|
|||||||
expect(j, {'field': 'name', 'direction': -1});
|
expect(j, {'field': 'name', 'direction': -1});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('PaginationParams', () {
|
group('PaginationParams', () {
|
||||||
const option = PaginationParams();
|
test('default', () {
|
||||||
final j = option.toJson();
|
const option = PaginationParams();
|
||||||
expect(j, containsPair('limit', 10));
|
final j = option.toJson();
|
||||||
expect(j, containsPair('offset', 0));
|
expect(j, containsPair('limit', 10));
|
||||||
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
'should throw if non-zero `offset` and `next` both are provided',
|
||||||
|
() {
|
||||||
|
try {
|
||||||
|
PaginationParams(offset: 10, next: 'next-message-id');
|
||||||
|
} catch (e) {
|
||||||
|
expect(e, isA<AssertionError>());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:test/test.dart';
|
|
||||||
import 'package:stream_chat/src/core/api/responses.dart';
|
import 'package:stream_chat/src/core/api/responses.dart';
|
||||||
import 'package:stream_chat/src/core/models/device.dart';
|
import 'package:stream_chat/src/core/models/device.dart';
|
||||||
import 'package:stream_chat/src/core/models/member.dart';
|
import 'package:stream_chat/src/core/models/member.dart';
|
||||||
@@ -8,6 +7,7 @@ import 'package:stream_chat/src/core/models/message.dart';
|
|||||||
import 'package:stream_chat/src/core/models/reaction.dart';
|
import 'package:stream_chat/src/core/models/reaction.dart';
|
||||||
import 'package:stream_chat/src/core/models/read.dart';
|
import 'package:stream_chat/src/core/models/read.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat/stream_chat.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group('src/api/responses', () {
|
group('src/api/responses', () {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:stream_chat/src/core/models/action.dart';
|
import 'package:stream_chat/src/core/models/action.dart';
|
||||||
import 'package:stream_chat/src/core/models/attachment.dart';
|
import 'package:stream_chat/src/core/models/attachment.dart';
|
||||||
|
import 'package:stream_chat/src/core/models/attachment_file.dart';
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
import '../../utils.dart';
|
import '../../utils.dart';
|
||||||
@@ -41,5 +42,59 @@ void main() {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('fileName, mimeType property and extraData manipulation', () {
|
||||||
|
final file = AttachmentFile(size: 3, path: 'myfolder/myfile.txt');
|
||||||
|
final attachment = Attachment(file: file);
|
||||||
|
|
||||||
|
expect(attachment.fileSize, 3);
|
||||||
|
expect(attachment.mimeType, 'text/plain');
|
||||||
|
expect(attachment.toJson(), {
|
||||||
|
'title': 'myfile.txt',
|
||||||
|
'actions': [],
|
||||||
|
'file_size': 3,
|
||||||
|
'mime_type': 'text/plain'
|
||||||
|
});
|
||||||
|
expect(Attachment.fromJson(attachment.toJson()).toJson(), {
|
||||||
|
'title': 'myfile.txt',
|
||||||
|
'actions': [],
|
||||||
|
'file_size': 3,
|
||||||
|
'mime_type': 'text/plain'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Setting the size and mimeType using extraData should work fine
|
||||||
|
var newAttachment = Attachment(
|
||||||
|
extraData: const {
|
||||||
|
'file_size': 6,
|
||||||
|
'mime_type': 'application/pdf',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(newAttachment.extraData['file_size'], 6);
|
||||||
|
expect(newAttachment.extraData['mime_type'], 'application/pdf');
|
||||||
|
expect(newAttachment.fileSize, 6);
|
||||||
|
expect(newAttachment.mimeType, 'application/pdf');
|
||||||
|
|
||||||
|
// switching a new file should update size and mimeType
|
||||||
|
final fileTwo = AttachmentFile(size: 12, path: 'myfolder/fileTwo.pdf');
|
||||||
|
newAttachment = attachment.copyWith(file: fileTwo);
|
||||||
|
|
||||||
|
expect(newAttachment.extraData['file_size'], 12);
|
||||||
|
expect(newAttachment.extraData['mime_type'], 'application/pdf');
|
||||||
|
expect(newAttachment.fileSize, 12);
|
||||||
|
expect(newAttachment.mimeType, 'application/pdf');
|
||||||
|
|
||||||
|
// if file is available, should override size and mimeType.
|
||||||
|
final fileThree = AttachmentFile(size: 9, path: 'myfolder/fileThree.png');
|
||||||
|
newAttachment = attachment.copyWith(file: fileThree, extraData: {
|
||||||
|
'file_size': 88,
|
||||||
|
'mime_type': 'application/pdf',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(newAttachment.extraData['file_size'], 9);
|
||||||
|
expect(newAttachment.extraData['mime_type'], 'image/png');
|
||||||
|
expect(newAttachment.fileSize, 9);
|
||||||
|
expect(newAttachment.mimeType, 'image/png');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:test/test.dart';
|
|
||||||
import 'package:stream_chat/src/core/models/filter.dart';
|
import 'package:stream_chat/src/core/models/filter.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group('operators', () {
|
group('operators', () {
|
||||||
@@ -114,7 +114,7 @@ void main() {
|
|||||||
|
|
||||||
test('notExists', () {
|
test('notExists', () {
|
||||||
const key = 'testKey';
|
const key = 'testKey';
|
||||||
final filter = Filter.exists(key, exists: false);
|
final filter = Filter.notExists(key);
|
||||||
expect(filter.key, key);
|
expect(filter.key, key);
|
||||||
expect(filter.value, isFalse);
|
expect(filter.value, isFalse);
|
||||||
expect(filter.operator, FilterOperator.exists.rawValue);
|
expect(filter.operator, FilterOperator.exists.rawValue);
|
||||||
@@ -139,7 +139,7 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('empty', () {
|
test('empty', () {
|
||||||
final filter = Filter.empty();
|
const filter = Filter.empty();
|
||||||
expect(filter.value, {});
|
expect(filter.value, {});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -226,6 +226,12 @@ void main() {
|
|||||||
json.encode(value),
|
json.encode(value),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('empty', () {
|
||||||
|
const filter = Filter.empty();
|
||||||
|
final encoded = json.encode(filter);
|
||||||
|
expect(encoded, '{}');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('groupedFilter', () {
|
test('groupedFilter', () {
|
||||||
|
|||||||
@@ -175,5 +175,26 @@ void main() {
|
|||||||
expect(newUser.teams, ['team1', 'team2']);
|
expect(newUser.teams, ['team1', 'team2']);
|
||||||
expect(newUser.language, 'fr');
|
expect(newUser.language, 'fr');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
'fromUser should not override name with id if not available in extraData',
|
||||||
|
() {
|
||||||
|
final user = User(id: 'test-id');
|
||||||
|
expect(user.id, 'test-id');
|
||||||
|
expect(user.name, 'test-id');
|
||||||
|
|
||||||
|
final encodedUser = user.toJson();
|
||||||
|
expect(encodedUser['id'], 'test-id');
|
||||||
|
expect(encodedUser['name'], null);
|
||||||
|
|
||||||
|
final ownUser = OwnUser.fromUser(user);
|
||||||
|
expect(user.id, 'test-id');
|
||||||
|
expect(user.name, 'test-id');
|
||||||
|
|
||||||
|
final encodedOwnUser = ownUser.toJson();
|
||||||
|
expect(encodedOwnUser['id'], 'test-id');
|
||||||
|
expect(encodedOwnUser['name'], null);
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:test/test.dart';
|
|
||||||
import 'package:stream_chat/src/core/util/serializer.dart';
|
import 'package:stream_chat/src/core/util/serializer.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group('src/models/serialization', () {
|
group('src/models/serialization', () {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:test/test.dart';
|
|
||||||
import 'package:stream_chat/src/core/util/extension.dart';
|
import 'package:stream_chat/src/core/util/extension.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
test('`.withNullifyer` converts the type into non-nullable', () {
|
test('`.withNullifyer` converts the type into non-nullable', () {
|
||||||
|
|||||||
@@ -6,11 +6,11 @@ import 'package:rxdart/rxdart.dart';
|
|||||||
import 'package:stream_chat/src/core/api/channel_api.dart';
|
import 'package:stream_chat/src/core/api/channel_api.dart';
|
||||||
import 'package:stream_chat/src/core/api/device_api.dart';
|
import 'package:stream_chat/src/core/api/device_api.dart';
|
||||||
import 'package:stream_chat/src/core/api/general_api.dart';
|
import 'package:stream_chat/src/core/api/general_api.dart';
|
||||||
|
import 'package:stream_chat/src/core/api/guest_api.dart';
|
||||||
import 'package:stream_chat/src/core/api/message_api.dart';
|
import 'package:stream_chat/src/core/api/message_api.dart';
|
||||||
import 'package:stream_chat/src/core/api/moderation_api.dart';
|
import 'package:stream_chat/src/core/api/moderation_api.dart';
|
||||||
import 'package:stream_chat/src/core/api/stream_chat_api.dart';
|
import 'package:stream_chat/src/core/api/stream_chat_api.dart';
|
||||||
import 'package:stream_chat/src/core/api/user_api.dart';
|
import 'package:stream_chat/src/core/api/user_api.dart';
|
||||||
import 'package:stream_chat/src/core/api/guest_api.dart';
|
|
||||||
import 'package:stream_chat/src/core/http/token.dart';
|
import 'package:stream_chat/src/core/http/token.dart';
|
||||||
import 'package:stream_chat/src/core/http/token_manager.dart';
|
import 'package:stream_chat/src/core/http/token_manager.dart';
|
||||||
import 'package:stream_chat/src/ws/websocket.dart';
|
import 'package:stream_chat/src/ws/websocket.dart';
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:dio/dio.dart' show MultipartFile;
|
import 'package:dio/dio.dart' show MultipartFile;
|
||||||
import 'package:stream_chat/src/client/channel.dart';
|
import 'package:stream_chat/src/client/channel.dart';
|
||||||
|
import 'package:stream_chat/src/core/models/attachment.dart';
|
||||||
import 'package:stream_chat/src/core/models/channel_state.dart';
|
import 'package:stream_chat/src/core/models/channel_state.dart';
|
||||||
import 'package:stream_chat/src/core/models/event.dart';
|
import 'package:stream_chat/src/core/models/event.dart';
|
||||||
import 'package:stream_chat/src/core/models/message.dart';
|
import 'package:stream_chat/src/core/models/message.dart';
|
||||||
@@ -46,12 +47,16 @@ Matcher isSameMessageAs(
|
|||||||
bool matchText = false,
|
bool matchText = false,
|
||||||
bool matchReactions = false,
|
bool matchReactions = false,
|
||||||
bool matchSendingStatus = false,
|
bool matchSendingStatus = false,
|
||||||
|
bool matchAttachments = false,
|
||||||
|
bool matchAttachmentsUploadState = false,
|
||||||
}) =>
|
}) =>
|
||||||
_IsSameMessageAs(
|
_IsSameMessageAs(
|
||||||
targetMessage: targetMessage,
|
targetMessage: targetMessage,
|
||||||
matchText: matchText,
|
matchText: matchText,
|
||||||
matchReactions: matchReactions,
|
matchReactions: matchReactions,
|
||||||
matchSendingStatus: matchSendingStatus,
|
matchSendingStatus: matchSendingStatus,
|
||||||
|
matchAttachments: matchAttachments,
|
||||||
|
matchAttachmentsUploadState: matchAttachmentsUploadState,
|
||||||
);
|
);
|
||||||
|
|
||||||
class _IsSameMessageAs extends Matcher {
|
class _IsSameMessageAs extends Matcher {
|
||||||
@@ -60,12 +65,16 @@ class _IsSameMessageAs extends Matcher {
|
|||||||
this.matchText = false,
|
this.matchText = false,
|
||||||
this.matchReactions = false,
|
this.matchReactions = false,
|
||||||
this.matchSendingStatus = false,
|
this.matchSendingStatus = false,
|
||||||
|
this.matchAttachments = false,
|
||||||
|
this.matchAttachmentsUploadState = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
final Message targetMessage;
|
final Message targetMessage;
|
||||||
final bool matchText;
|
final bool matchText;
|
||||||
final bool matchReactions;
|
final bool matchReactions;
|
||||||
final bool matchSendingStatus;
|
final bool matchSendingStatus;
|
||||||
|
final bool matchAttachments;
|
||||||
|
final bool matchAttachmentsUploadState;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Description describe(Description description) =>
|
Description describe(Description description) =>
|
||||||
@@ -96,6 +105,56 @@ class _IsSameMessageAs extends Matcher {
|
|||||||
?.map((it) => '${it.type}-${it.messageId}')
|
?.map((it) => '${it.type}-${it.messageId}')
|
||||||
.toList());
|
.toList());
|
||||||
}
|
}
|
||||||
|
if (matchAttachments) {
|
||||||
|
bool matchAttachments() {
|
||||||
|
final attachments = message.attachments;
|
||||||
|
final targetAttachments = targetMessage.attachments;
|
||||||
|
if (identical(attachments, targetAttachments)) return true;
|
||||||
|
final length = attachments.length;
|
||||||
|
if (length != targetAttachments.length) return false;
|
||||||
|
for (var i = 0; i < length; i++) {
|
||||||
|
if (!isSameAttachmentAs(
|
||||||
|
attachments[i],
|
||||||
|
matchUploadState: matchAttachmentsUploadState,
|
||||||
|
).matches(targetAttachments[i], matchState)) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
matches &= matchAttachments();
|
||||||
|
}
|
||||||
|
return matches;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Matcher isSameAttachmentAs(
|
||||||
|
Attachment targetAttachment, {
|
||||||
|
bool matchUploadState = false,
|
||||||
|
}) =>
|
||||||
|
_IsSameAttachmentAs(
|
||||||
|
targetAttachment: targetAttachment,
|
||||||
|
matchUploadState: matchUploadState,
|
||||||
|
);
|
||||||
|
|
||||||
|
class _IsSameAttachmentAs extends Matcher {
|
||||||
|
const _IsSameAttachmentAs({
|
||||||
|
required this.targetAttachment,
|
||||||
|
this.matchUploadState = false,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Attachment targetAttachment;
|
||||||
|
final bool matchUploadState;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Description describe(Description description) =>
|
||||||
|
description.add('is same attachment as $targetAttachment');
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool matches(covariant Attachment attachment, Map matchState) {
|
||||||
|
var matches = attachment.id == targetAttachment.id;
|
||||||
|
if (matchUploadState) {
|
||||||
|
matches &= attachment.uploadState == targetAttachment.uploadState;
|
||||||
|
}
|
||||||
return matches;
|
return matches;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:mocktail/mocktail.dart';
|
||||||
import 'package:stream_chat/src/core/http/token_manager.dart';
|
import 'package:stream_chat/src/core/http/token_manager.dart';
|
||||||
|
import 'package:stream_chat/src/ws/websocket.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat/stream_chat.dart';
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
import 'package:mocktail/mocktail.dart';
|
|
||||||
import 'package:stream_chat/src/ws/websocket.dart';
|
|
||||||
import 'package:web_socket_channel/web_socket_channel.dart';
|
import 'package:web_socket_channel/web_socket_channel.dart';
|
||||||
|
|
||||||
import '../fakes.dart';
|
import '../fakes.dart';
|
||||||
|
|||||||
@@ -1,3 +1,98 @@
|
|||||||
|
## Upcoming
|
||||||
|
|
||||||
|
- Updated Dart SDK constraints to `>=2.14.0 <3.0.0`
|
||||||
|
|
||||||
|
## 3.1.1
|
||||||
|
|
||||||
|
- Updated `stream_chat_flutter_core` dependency to [`3.1.1`](https://pub.dev/packages/stream_chat_flutter_core/changelog).
|
||||||
|
- Updated `file_picker`, `image_gallery_saver`, and `video_thumbnail` to the latest versions.
|
||||||
|
|
||||||
|
🐞 Fixed
|
||||||
|
|
||||||
|
- [[#687]](https://github.com/GetStream/stream-chat-flutter/issues/687): Fix Users losing their place in the conversation after replying in threads.
|
||||||
|
- Fixed floating date stream subscription causing "Bad state: stream has already been listened.” error.
|
||||||
|
- Fixed `String` capitalize extension not working on empty strings.
|
||||||
|
|
||||||
|
✅ Added
|
||||||
|
|
||||||
|
- Added `MessageInput.customOverlays` property to add custom overlays to the message input.
|
||||||
|
- Added `MessageInput.mentionAllAppUsers` property to mention all app users in the message input.
|
||||||
|
- The `MessageInput` now supports local search for channels with less than 100 members.
|
||||||
|
- Added `MessageListView.paginationLoadingIndicatorBuilder` to override the default loading indicator shown while paginating the message list.
|
||||||
|
- Added new `linkBackgroundColor` in `MessageTheme` for setting background colors of link attachments.
|
||||||
|
|
||||||
|
⚠️ Deprecated
|
||||||
|
|
||||||
|
- `MessageInput.mentionsTileBuilder` is now deprecated in favor of `MessageInput.userMentionsTileBuilder`.
|
||||||
|
- `MentionTile` is now deprecated in favor of `UserMentionsTile`.
|
||||||
|
|
||||||
|
## 3.0.0
|
||||||
|
|
||||||
|
- Updated `stream_chat_flutter_core` dependency to [`3.0.0`](https://pub.dev/packages/stream_chat_flutter_core/changelog).
|
||||||
|
|
||||||
|
🛑️ Breaking Changes from `2.2.1`
|
||||||
|
|
||||||
|
- `UserListView` `filter` property now is non-nullable.
|
||||||
|
|
||||||
|
🐞 Fixed
|
||||||
|
|
||||||
|
- [[#668]](https://github.com/GetStream/stream-chat-flutter/issues/668): Fix `MessageInput` rendering errors in case
|
||||||
|
there are no actions available to show.
|
||||||
|
- [[#349]](https://github.com/GetStream/stream-chat-flutter/issues/349): Fix `MessageInput` attachment render overflow error.
|
||||||
|
- `MessageInput` overlays now follow the `MessageInput` focus.
|
||||||
|
- [[#674]](https://github.com/GetStream/stream-chat-flutter/issues/674): Check scrollController is attached before calling jump in MessageListView.
|
||||||
|
- Fixed `MessageListView` header and footer when `reverse: false`.
|
||||||
|
|
||||||
|
🔄 Changed
|
||||||
|
|
||||||
|
- Animation curves changed from default `Curves.linear` to `Curves.easeOut` and `Curves.easeIn` for attachment controls.
|
||||||
|
- Removed default padding in `DateDivider` in `MessageListView`
|
||||||
|
|
||||||
|
✅ Added
|
||||||
|
|
||||||
|
- Added `MessageInput.customPortalOptions` property to add custom overlays to the `MessageInput`.
|
||||||
|
|
||||||
|
## 2.2.1
|
||||||
|
|
||||||
|
⚠️ Deprecated
|
||||||
|
|
||||||
|
- `MessageSearchListView` `paginationParams` property is now deprecated in favor of `limit`.
|
||||||
|
```dart
|
||||||
|
// previous
|
||||||
|
paginationParams = const PaginationParams(limit: 30)
|
||||||
|
|
||||||
|
// new
|
||||||
|
limit = 30
|
||||||
|
```
|
||||||
|
- `UserListView` `pagination` property is now deprecated in favor of `limit`.
|
||||||
|
```dart
|
||||||
|
// previous
|
||||||
|
pagination = const PaginationParams(limit: 30)
|
||||||
|
|
||||||
|
// new
|
||||||
|
limit = 30
|
||||||
|
```
|
||||||
|
- `ChannelListView` `pagination` property is now deprecated in favor of `limit`.
|
||||||
|
```dart
|
||||||
|
// previous
|
||||||
|
pagination = const PaginationParams(limit: 30)
|
||||||
|
|
||||||
|
// new
|
||||||
|
limit = 30
|
||||||
|
```
|
||||||
|
|
||||||
|
🔄 Changed
|
||||||
|
|
||||||
|
- `UserListViewCore` filter property now has a default value.
|
||||||
|
```dart
|
||||||
|
filter = const Filter.empty()
|
||||||
|
```
|
||||||
|
|
||||||
|
🐞 Fixed
|
||||||
|
|
||||||
|
- Fixed `MessageSearchListView` pagination.
|
||||||
|
- Fixed `MessageWidget` attachment tap callbacks.
|
||||||
|
|
||||||
## 2.2.1
|
## 2.2.1
|
||||||
|
|
||||||
- Updated `stream_chat_flutter_core` dependency to 2.2.1
|
- Updated `stream_chat_flutter_core` dependency to 2.2.1
|
||||||
@@ -7,14 +102,13 @@
|
|||||||
✅ Added
|
✅ Added
|
||||||
|
|
||||||
- [#516](https://github.com/GetStream/stream-chat-flutter/issues/516):
|
- [#516](https://github.com/GetStream/stream-chat-flutter/issues/516):
|
||||||
Added `StreamChatThemeData.placeholderUserImage` for building a widget when the `UserAvatar` image
|
Added `StreamChatThemeData.placeholderUserImage` for building a widget when the `UserAvatar` image is loading
|
||||||
is loading
|
|
||||||
- Added a `backgroundColor` property to the following widgets:
|
- Added a `backgroundColor` property to the following widgets:
|
||||||
- `ChannelHeader`
|
- `ChannelHeader`
|
||||||
- `ChannelListHeader`
|
- `ChannelListHeader`
|
||||||
- `GalleryHeader`
|
- `GalleryHeader`
|
||||||
- `GalleryFooter`
|
- `GalleryFooter`
|
||||||
- `ThreadHeader`
|
- `ThreadHeader`
|
||||||
- Added `MessageInput.attachmentLimit` in order to limit the no. of attachments that can be sent with a single message.
|
- Added `MessageInput.attachmentLimit` in order to limit the no. of attachments that can be sent with a single message.
|
||||||
- Added `MessageInput.onAttachmentLimitExceed` callback which will be called when the `attachmentLimit` is exceeded.
|
- Added `MessageInput.onAttachmentLimitExceed` callback which will be called when the `attachmentLimit` is exceeded.
|
||||||
This will override the default error alert behaviour.
|
This will override the default error alert behaviour.
|
||||||
@@ -34,9 +128,8 @@ You can call `.copyWith` to customize just a subset of properties.
|
|||||||
|
|
||||||
🔄 Changed
|
🔄 Changed
|
||||||
|
|
||||||
Theming has been upgraded! Most theme classes now have `InheritedTheme` classes associated with
|
Theming has been upgraded! Most theme classes now have `InheritedTheme` classes associated with them, and have been
|
||||||
them, and have been upgraded with some goodies like `lerp` functions. Here's the full naming
|
upgraded with some goodies like `lerp` functions. Here's the full naming breakdown:
|
||||||
breakdown:
|
|
||||||
|
|
||||||
* `AvatarTheme` is now `AvatarThemeData`
|
* `AvatarTheme` is now `AvatarThemeData`
|
||||||
* `ChannelHeaderTheme` is now `ChannelHeaderThemeData`
|
* `ChannelHeaderTheme` is now `ChannelHeaderThemeData`
|
||||||
@@ -53,18 +146,18 @@ breakdown:
|
|||||||
|
|
||||||
🐞 Fixed
|
🐞 Fixed
|
||||||
|
|
||||||
- Fixed `MessageInput` textField case where `input` is not enabled if the file picked from the
|
- Fixed `MessageInput` textField case where `input` is not enabled if the file picked from the camera is null.
|
||||||
camera is null.
|
|
||||||
- Fixed date dividers position/alignment in non reversed `MessageListView`.
|
- Fixed date dividers position/alignment in non reversed `MessageListView`.
|
||||||
- Fixed `MessageListView` not opening to the right initialMessage if `StreamChannel.initialMessageId` is set.
|
- Fixed `MessageListView` not opening to the right initialMessage if `StreamChannel.initialMessageId` is set.
|
||||||
- Fixed null check errors when accessing `message.text` in `MessageWidget` and `MessageListView`; this occurred when sending a message with no text.
|
- Fixed null check errors when accessing `message.text` in `MessageWidget` and `MessageListView`; this occurred when
|
||||||
|
sending a message with no text.
|
||||||
|
|
||||||
## 2.1.2
|
## 2.1.2
|
||||||
|
|
||||||
🐞 Fixed
|
🐞 Fixed
|
||||||
|
|
||||||
- [#590](https://github.com/GetStream/stream-chat-flutter/issues/590): livestream use case, no
|
- [#590](https://github.com/GetStream/stream-chat-flutter/issues/590): livestream use case, no members when sending
|
||||||
members when sending message
|
message
|
||||||
|
|
||||||
## 2.1.1
|
## 2.1.1
|
||||||
|
|
||||||
@@ -82,8 +175,7 @@ breakdown:
|
|||||||
🔄 Changed
|
🔄 Changed
|
||||||
|
|
||||||
- `StreamChat.of(context).user` is now deprecated in favor of `StreamChat.of(context).currentUser`.
|
- `StreamChat.of(context).user` is now deprecated in favor of `StreamChat.of(context).currentUser`.
|
||||||
- `StreamChat.of(context).userStream` is now deprecated in favor
|
- `StreamChat.of(context).userStream` is now deprecated in favor of `StreamChat.of(context).currentUserStream`.
|
||||||
of `StreamChat.of(context).currentUserStream`.
|
|
||||||
|
|
||||||
🐞 Fixed
|
🐞 Fixed
|
||||||
|
|
||||||
@@ -136,8 +228,7 @@ You can call `.copyWith` to customize just a subset of properties
|
|||||||
|
|
||||||
- Added video compress options (frame and quality) to `MessageInput`
|
- Added video compress options (frame and quality) to `MessageInput`
|
||||||
- TypingIndicator now has a property called `parentId` to show typing indicator specific to threads
|
- TypingIndicator now has a property called `parentId` to show typing indicator specific to threads
|
||||||
- [#493](https://github.com/GetStream/stream-chat-flutter/pull/493): add support for messageListView
|
- [#493](https://github.com/GetStream/stream-chat-flutter/pull/493): add support for messageListView header/footer
|
||||||
header/footer
|
|
||||||
- `MessageWidget` accepts a `userAvatarBuilder`
|
- `MessageWidget` accepts a `userAvatarBuilder`
|
||||||
- Added pinMessage ui support
|
- Added pinMessage ui support
|
||||||
- Added `MessageListView.threadSeparatorBuilder` property
|
- Added `MessageListView.threadSeparatorBuilder` property
|
||||||
@@ -146,12 +237,10 @@ You can call `.copyWith` to customize just a subset of properties
|
|||||||
|
|
||||||
🐞 Fixed
|
🐞 Fixed
|
||||||
|
|
||||||
- [#483](https://github.com/GetStream/stream-chat-flutter/issues/483): Keyboard covers input text
|
- [#483](https://github.com/GetStream/stream-chat-flutter/issues/483): Keyboard covers input text box when editing
|
||||||
box when editing message
|
message
|
||||||
- Modals are shown using the nearest `Navigator` to make using the SDK easier in a nested navigator
|
- Modals are shown using the nearest `Navigator` to make using the SDK easier in a nested navigator use case
|
||||||
use case
|
- [#484](https://github.com/GetStream/stream-chat-flutter/issues/484): messages don't update without a reload
|
||||||
- [#484](https://github.com/GetStream/stream-chat-flutter/issues/484): messages don't update without
|
|
||||||
a reload
|
|
||||||
- `MessageListView` not rendering if the user is not a member of the channel
|
- `MessageListView` not rendering if the user is not a member of the channel
|
||||||
- Fix `MessageInput` overflow when there are no actions
|
- Fix `MessageInput` overflow when there are no actions
|
||||||
- Minor fixes and improvements
|
- Minor fixes and improvements
|
||||||
@@ -204,18 +293,15 @@ You can call `.copyWith` to customize just a subset of properties.
|
|||||||
✅ Added
|
✅ Added
|
||||||
|
|
||||||
- TypingIndicator now has a property called `parentId` to show typing indicator specific to threads
|
- TypingIndicator now has a property called `parentId` to show typing indicator specific to threads
|
||||||
- [#493](https://github.com/GetStream/stream-chat-flutter/pull/493): add support for messageListView
|
- [#493](https://github.com/GetStream/stream-chat-flutter/pull/493): add support for messageListView header/footer
|
||||||
header/footer
|
|
||||||
- `MessageWidget` accepts a `userAvatarBuilder`
|
- `MessageWidget` accepts a `userAvatarBuilder`
|
||||||
|
|
||||||
🐞 Fixed
|
🐞 Fixed
|
||||||
|
|
||||||
- [#483](https://github.com/GetStream/stream-chat-flutter/issues/483): Keyboard covers input text
|
- [#483](https://github.com/GetStream/stream-chat-flutter/issues/483): Keyboard covers input text box when editing
|
||||||
box when editing message
|
message
|
||||||
- Modals are shown using the nearest `Navigator` to make using the SDK easier in a nested navigator
|
- Modals are shown using the nearest `Navigator` to make using the SDK easier in a nested navigator use case
|
||||||
use case
|
- [#484](https://github.com/GetStream/stream-chat-flutter/issues/484): messages don't update without a reload
|
||||||
- [#484](https://github.com/GetStream/stream-chat-flutter/issues/484): messages don't update without
|
|
||||||
a reload
|
|
||||||
- `MessageListView` not rendering if the user is not a member of the channel
|
- `MessageListView` not rendering if the user is not a member of the channel
|
||||||
|
|
||||||
## 2.0.0-nullsafety.7
|
## 2.0.0-nullsafety.7
|
||||||
@@ -285,8 +371,7 @@ You can call `.copyWith` to customize just a subset of properties.
|
|||||||
- Show error messages as system and keep them in the message input
|
- Show error messages as system and keep them in the message input
|
||||||
- Remove notification badge logic
|
- Remove notification badge logic
|
||||||
- Use shimmer while loading images
|
- Use shimmer while loading images
|
||||||
- Polished `StreamChatTheme` adding more options and a new `MessageInputTheme` dedicated
|
- Polished `StreamChatTheme` adding more options and a new `MessageInputTheme` dedicated to `MessageInput`
|
||||||
to `MessageInput`
|
|
||||||
- Add possibility to specify custom message actions using `MessageWidget.customActions`
|
- Add possibility to specify custom message actions using `MessageWidget.customActions`
|
||||||
- Added `MessageListView.onAttachmentTap` callback
|
- Added `MessageListView.onAttachmentTap` callback
|
||||||
- Fixed message newline issue
|
- Fixed message newline issue
|
||||||
@@ -343,8 +428,7 @@ You can call `.copyWith` to customize just a subset of properties.
|
|||||||
- Improved api documentation
|
- Improved api documentation
|
||||||
- Updated `stream_chat` dependency to `^1.0.0-beta`
|
- Updated `stream_chat` dependency to `^1.0.0-beta`
|
||||||
- Extracted sample app into dedicated [repo](https://github.com/GetStream/flutter-samples)
|
- Extracted sample app into dedicated [repo](https://github.com/GetStream/flutter-samples)
|
||||||
- Reimplemented existing widgets
|
- Reimplemented existing widgets using [stream_chat_flutter_core](https://pub.dev/packages/stream_chat_flutter_core)
|
||||||
using [stream_chat_flutter_core](https://pub.dev/packages/stream_chat_flutter_core)
|
|
||||||
|
|
||||||
## 0.2.21
|
## 0.2.21
|
||||||
|
|
||||||
@@ -361,8 +445,8 @@ You can call `.copyWith` to customize just a subset of properties.
|
|||||||
|
|
||||||
## 0.2.20+2
|
## 0.2.20+2
|
||||||
|
|
||||||
- Added `shouldAddChannel` to ChannelsBloc in order to check if a channel has to be added to the
|
- Added `shouldAddChannel` to ChannelsBloc in order to check if a channel has to be added to the list when a new message
|
||||||
list when a new message arrives
|
arrives
|
||||||
|
|
||||||
## 0.2.20+1
|
## 0.2.20+1
|
||||||
|
|
||||||
@@ -396,8 +480,7 @@ You can call `.copyWith` to customize just a subset of properties.
|
|||||||
|
|
||||||
## 0.2.16
|
## 0.2.16
|
||||||
|
|
||||||
- Do not wrap channel preview builder. Users will have to implement they're custom onTap/onLongPress
|
- Do not wrap channel preview builder. Users will have to implement they're custom onTap/onLongPress implementation
|
||||||
implementation
|
|
||||||
- Make public autofocus field of the TextField of message_input
|
- Make public autofocus field of the TextField of message_input
|
||||||
|
|
||||||
## 0.2.15
|
## 0.2.15
|
||||||
@@ -582,11 +665,10 @@ You can call `.copyWith` to customize just a subset of properties.
|
|||||||
|
|
||||||
## 0.2.1-alpha+1
|
## 0.2.1-alpha+1
|
||||||
|
|
||||||
- Removed the additional `Navigator` in `StreamChat` widget. It was added to make the app have
|
- Removed the additional `Navigator` in `StreamChat` widget. It was added to make the app have the `StreamChat` widget
|
||||||
the `StreamChat` widget as ancestor in every route. Now the recommended way to add `StreamChat` to
|
as ancestor in every route. Now the recommended way to add `StreamChat` to your app is using the `builder` property of
|
||||||
your app is using the `builder` property of your `MaterialApp` widget. Otherwise you can use it in
|
your `MaterialApp` widget. Otherwise you can use it in the usual way, but you need to add a `StreamChat` widget to
|
||||||
the usual way, but you need to add a `StreamChat` widget to every route of your app.
|
every route of your app. Read [this issue](https://github.com/GetStream/stream-chat-flutter/issues/47) for more
|
||||||
Read [this issue](https://github.com/GetStream/stream-chat-flutter/issues/47) for more
|
|
||||||
information.
|
information.
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
@@ -688,8 +770,8 @@ Widget build(BuildContext context) {
|
|||||||
|
|
||||||
- Add gesture (vertical drag down) to close the keyboard
|
- Add gesture (vertical drag down) to close the keyboard
|
||||||
|
|
||||||
- Add keyboard type parameters (set it to TextInputType.text to show the submit button that will
|
- Add keyboard type parameters (set it to TextInputType.text to show the submit button that will even close the
|
||||||
even close the keyboard)
|
keyboard)
|
||||||
|
|
||||||
The property showVideoFullScreen was added mainly because of this issue brianegan/chewie#261
|
The property showVideoFullScreen was added mainly because of this issue brianegan/chewie#261
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,6 @@
|
|||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1.0</string>
|
<string>1.0</string>
|
||||||
<key>MinimumOSVersion</key>
|
<key>MinimumOSVersion</key>
|
||||||
<string>8.0</string>
|
<string>9.0</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -106,9 +106,7 @@ class ChannelListPage extends StatelessWidget {
|
|||||||
[StreamChat.of(context).currentUser!.id],
|
[StreamChat.of(context).currentUser!.id],
|
||||||
),
|
),
|
||||||
sort: const [SortOption('last_message_at')],
|
sort: const [SortOption('last_message_at')],
|
||||||
pagination: const PaginationParams(
|
limit: 20,
|
||||||
limit: 20,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -83,9 +83,7 @@ class ChannelListPage extends StatelessWidget {
|
|||||||
[StreamChat.of(context).currentUser!.id],
|
[StreamChat.of(context).currentUser!.id],
|
||||||
),
|
),
|
||||||
sort: const [SortOption('last_message_at')],
|
sort: const [SortOption('last_message_at')],
|
||||||
pagination: const PaginationParams(
|
limit: 20,
|
||||||
limit: 20,
|
|
||||||
),
|
|
||||||
channelWidget: const ChannelPage(),
|
channelWidget: const ChannelPage(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -85,9 +85,7 @@ class ChannelListPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
channelPreviewBuilder: _channelPreviewBuilder,
|
channelPreviewBuilder: _channelPreviewBuilder,
|
||||||
// sort: [SortOption('last_message_at')],
|
// sort: [SortOption('last_message_at')],
|
||||||
pagination: const PaginationParams(
|
limit: 20,
|
||||||
limit: 20,
|
|
||||||
),
|
|
||||||
channelWidget: const ChannelPage(),
|
channelWidget: const ChannelPage(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -69,9 +69,7 @@ class ChannelListPage extends StatelessWidget {
|
|||||||
[StreamChat.of(context).currentUser!.id],
|
[StreamChat.of(context).currentUser!.id],
|
||||||
),
|
),
|
||||||
sort: const [SortOption('last_message_at')],
|
sort: const [SortOption('last_message_at')],
|
||||||
pagination: const PaginationParams(
|
limit: 20,
|
||||||
limit: 20,
|
|
||||||
),
|
|
||||||
channelWidget: const ChannelPage(),
|
channelWidget: const ChannelPage(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -75,9 +75,7 @@ class ChannelListPage extends StatelessWidget {
|
|||||||
[StreamChat.of(context).currentUser!.id],
|
[StreamChat.of(context).currentUser!.id],
|
||||||
),
|
),
|
||||||
sort: const [SortOption('last_message_at')],
|
sort: const [SortOption('last_message_at')],
|
||||||
pagination: const PaginationParams(
|
limit: 20,
|
||||||
limit: 20,
|
|
||||||
),
|
|
||||||
channelWidget: const ChannelPage(),
|
channelWidget: const ChannelPage(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ Future<void> main() async {
|
|||||||
|
|
||||||
await client.connectUser(
|
await client.connectUser(
|
||||||
User(id: 'super-band-9'),
|
User(id: 'super-band-9'),
|
||||||
'''eyJ0eXAiO«iJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A''',
|
'''eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A''',
|
||||||
);
|
);
|
||||||
|
|
||||||
runApp(
|
runApp(
|
||||||
@@ -102,9 +102,7 @@ class ChannelListPage extends StatelessWidget {
|
|||||||
[StreamChat.of(context).currentUser!.id],
|
[StreamChat.of(context).currentUser!.id],
|
||||||
),
|
),
|
||||||
sort: const [SortOption('last_message_at')],
|
sort: const [SortOption('last_message_at')],
|
||||||
pagination: const PaginationParams(
|
limit: 20,
|
||||||
limit: 20,
|
|
||||||
),
|
|
||||||
channelWidget: const ChannelPage(),
|
channelWidget: const ChannelPage(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -19,42 +19,36 @@ class AttachmentTitle extends StatelessWidget {
|
|||||||
final Attachment attachment;
|
final Attachment attachment;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => GestureDetector(
|
Widget build(BuildContext context) {
|
||||||
onTap: () {
|
final normalizedTitleLink = attachment.titleLink?.replaceFirst(
|
||||||
if (attachment.titleLink != null) {
|
RegExp(r'https?://(www\.)?'),
|
||||||
launchURL(context, attachment.titleLink);
|
'',
|
||||||
}
|
);
|
||||||
},
|
return GestureDetector(
|
||||||
child: Padding(
|
onTap: () {
|
||||||
padding: const EdgeInsets.all(8),
|
final titleLink = attachment.titleLink;
|
||||||
child: Column(
|
if (titleLink != null) launchURL(context, titleLink);
|
||||||
mainAxisSize: MainAxisSize.min,
|
},
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
child: Padding(
|
||||||
children: <Widget>[
|
padding: const EdgeInsets.all(8),
|
||||||
if (attachment.title != null)
|
child: Column(
|
||||||
Text(
|
mainAxisSize: MainAxisSize.min,
|
||||||
attachment.title!,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
overflow: TextOverflow.ellipsis,
|
children: <Widget>[
|
||||||
style: messageTheme.messageTextStyle?.copyWith(
|
if (attachment.title != null)
|
||||||
color: StreamChatTheme.of(context).colorTheme.accentPrimary,
|
Text(
|
||||||
fontWeight: FontWeight.bold,
|
attachment.title!,
|
||||||
),
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: messageTheme.messageTextStyle?.copyWith(
|
||||||
|
color: StreamChatTheme.of(context).colorTheme.accentPrimary,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
if (attachment.titleLink != null ||
|
),
|
||||||
attachment.ogScrapeUrl != null)
|
if (normalizedTitleLink != null)
|
||||||
Text(
|
Text(normalizedTitleLink, style: messageTheme.messageTextStyle),
|
||||||
Uri.parse(attachment.titleLink ?? attachment.ogScrapeUrl!)
|
],
|
||||||
.authority
|
|
||||||
.split('.')
|
|
||||||
.reversed
|
|
||||||
.take(2)
|
|
||||||
.toList()
|
|
||||||
.reversed
|
|
||||||
.join('.'),
|
|
||||||
style: messageTheme.messageTextStyle,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:stream_chat_flutter/src/extension.dart';
|
||||||
import 'package:stream_chat_flutter/src/upload_progress_indicator.dart';
|
import 'package:stream_chat_flutter/src/upload_progress_indicator.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
import 'package:stream_chat_flutter/src/extension.dart';
|
|
||||||
|
|
||||||
/// Widget to build in progress
|
/// Widget to build in progress
|
||||||
typedef InProgressBuilder = Widget Function(BuildContext, int, int);
|
typedef InProgressBuilder = Widget Function(BuildContext, int, int);
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:shimmer/shimmer.dart';
|
import 'package:shimmer/shimmer.dart';
|
||||||
|
import 'package:stream_chat_flutter/src/attachment/attachment_widget.dart';
|
||||||
|
import 'package:stream_chat_flutter/src/extension.dart';
|
||||||
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
||||||
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
||||||
import 'package:stream_chat_flutter/src/upload_progress_indicator.dart';
|
import 'package:stream_chat_flutter/src/upload_progress_indicator.dart';
|
||||||
import 'package:stream_chat_flutter/src/utils.dart';
|
import 'package:stream_chat_flutter/src/utils.dart';
|
||||||
import 'package:stream_chat_flutter/src/video_thumbnail_image.dart';
|
import 'package:stream_chat_flutter/src/video_thumbnail_image.dart';
|
||||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
import 'package:stream_chat_flutter/src/extension.dart';
|
|
||||||
|
|
||||||
import 'package:stream_chat_flutter/src/attachment/attachment_widget.dart';
|
|
||||||
|
|
||||||
/// Widget for displaying file attachments
|
/// Widget for displaying file attachments
|
||||||
class FileAttachment extends AttachmentWidget {
|
class FileAttachment extends AttachmentWidget {
|
||||||
@@ -258,7 +257,8 @@ class FileAttachment extends AttachmentWidget {
|
|||||||
visualDensity: VisualDensity.compact,
|
visualDensity: VisualDensity.compact,
|
||||||
splashRadius: 16,
|
splashRadius: 16,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
launchURL(context, attachment.assetUrl);
|
final assetUrl = attachment.assetUrl;
|
||||||
|
if (assetUrl != null) launchURL(context, assetUrl);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ import 'package:cached_network_image/cached_network_image.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:shimmer/shimmer.dart';
|
import 'package:shimmer/shimmer.dart';
|
||||||
import 'package:stream_chat_flutter/src/attachment/attachment_widget.dart';
|
import 'package:stream_chat_flutter/src/attachment/attachment_widget.dart';
|
||||||
|
import 'package:stream_chat_flutter/src/extension.dart';
|
||||||
import 'package:stream_chat_flutter/src/visible_footnote.dart';
|
import 'package:stream_chat_flutter/src/visible_footnote.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';
|
||||||
import 'package:stream_chat_flutter/src/extension.dart';
|
|
||||||
|
|
||||||
/// Widget for showing a GIF attachment
|
/// Widget for showing a GIF attachment
|
||||||
class GiphyAttachment extends AttachmentWidget {
|
class GiphyAttachment extends AttachmentWidget {
|
||||||
@@ -98,7 +98,13 @@ class GiphyAttachment extends AttachmentWidget {
|
|||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(2),
|
padding: const EdgeInsets.all(2),
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () => onAttachmentTap ?? _onImageTap(context),
|
onTap: () {
|
||||||
|
if (onAttachmentTap != null) {
|
||||||
|
onAttachmentTap?.call();
|
||||||
|
} else {
|
||||||
|
_onImageTap(context);
|
||||||
|
}
|
||||||
|
},
|
||||||
child: CachedNetworkImage(
|
child: CachedNetworkImage(
|
||||||
height: size?.height,
|
height: size?.height,
|
||||||
width: size?.width,
|
width: size?.width,
|
||||||
@@ -253,21 +259,12 @@ class GiphyAttachment extends AttachmentWidget {
|
|||||||
Widget _buildSentAttachment(BuildContext context, String imageUrl) =>
|
Widget _buildSentAttachment(BuildContext context, String imageUrl) =>
|
||||||
SizedBox(
|
SizedBox(
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () async {
|
onTap: () {
|
||||||
final res =
|
if (onAttachmentTap != null) {
|
||||||
await Navigator.push(context, MaterialPageRoute(builder: (_) {
|
onAttachmentTap?.call();
|
||||||
final channel = StreamChannel.of(context).channel;
|
} else {
|
||||||
return StreamChannel(
|
_onImageTap(context);
|
||||||
channel: channel,
|
}
|
||||||
child: FullScreenMedia(
|
|
||||||
mediaAttachments: [attachment],
|
|
||||||
userName: message.user?.name,
|
|
||||||
message: message,
|
|
||||||
onShowMessage: onShowMessage,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}));
|
|
||||||
if (res != null) onReturnAction!(res);
|
|
||||||
},
|
},
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
|
|||||||
+7
-5
@@ -10,6 +10,7 @@ class UrlAttachment extends StatelessWidget {
|
|||||||
Key? key,
|
Key? key,
|
||||||
required this.urlAttachment,
|
required this.urlAttachment,
|
||||||
required this.hostDisplayName,
|
required this.hostDisplayName,
|
||||||
|
required this.messageTheme,
|
||||||
this.textPadding = const EdgeInsets.symmetric(
|
this.textPadding = const EdgeInsets.symmetric(
|
||||||
horizontal: 16,
|
horizontal: 16,
|
||||||
vertical: 8,
|
vertical: 8,
|
||||||
@@ -25,15 +26,16 @@ class UrlAttachment extends StatelessWidget {
|
|||||||
/// Padding for text
|
/// Padding for text
|
||||||
final EdgeInsets textPadding;
|
final EdgeInsets textPadding;
|
||||||
|
|
||||||
|
/// [MessageThemeData] for showing image title
|
||||||
|
final MessageThemeData messageTheme;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final chatThemeData = StreamChatTheme.of(context);
|
final chatThemeData = StreamChatTheme.of(context);
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
launchURL(
|
final titleLink = urlAttachment.titleLink;
|
||||||
context,
|
if (titleLink != null) launchURL(context, titleLink);
|
||||||
urlAttachment.ogScrapeUrl,
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
@@ -60,7 +62,7 @@ class UrlAttachment extends StatelessWidget {
|
|||||||
borderRadius: const BorderRadius.only(
|
borderRadius: const BorderRadius.only(
|
||||||
topRight: Radius.circular(16),
|
topRight: Radius.circular(16),
|
||||||
),
|
),
|
||||||
color: chatThemeData.colorTheme.linkBg,
|
color: messageTheme.linkBackgroundColor,
|
||||||
),
|
),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(
|
padding: const EdgeInsets.only(
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stream_chat_flutter/src/channel_info.dart';
|
import 'package:stream_chat_flutter/src/channel_info.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
|
||||||
import 'package:stream_chat_flutter/src/extension.dart';
|
import 'package:stream_chat_flutter/src/extension.dart';
|
||||||
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
/// Bottom Sheet with options
|
/// Bottom Sheet with options
|
||||||
class ChannelBottomSheet extends StatefulWidget {
|
class ChannelBottomSheet extends StatefulWidget {
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:stream_chat_flutter/src/back_button.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_info.dart';
|
||||||
import 'package:stream_chat_flutter/src/channel_name.dart';
|
import 'package:stream_chat_flutter/src/channel_name.dart';
|
||||||
|
import 'package:stream_chat_flutter/src/extension.dart';
|
||||||
import 'package:stream_chat_flutter/src/info_tile.dart';
|
import 'package:stream_chat_flutter/src/info_tile.dart';
|
||||||
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
import 'package:stream_chat_flutter/src/stream_chat_theme.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';
|
||||||
import 'package:stream_chat_flutter/src/extension.dart';
|
|
||||||
|
|
||||||
/// 
|
/// 
|
||||||
/// 
|
/// 
|
||||||
@@ -137,12 +138,17 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
|
||||||
return InfoTile(
|
return InfoTile(
|
||||||
showMessage: showConnectionStateTile && showStatus,
|
showMessage: showConnectionStateTile && showStatus,
|
||||||
message: statusString,
|
message: statusString,
|
||||||
child: AppBar(
|
child: AppBar(
|
||||||
textTheme: Theme.of(context).textTheme,
|
toolbarTextStyle: theme.textTheme.bodyText2,
|
||||||
brightness: Theme.of(context).brightness,
|
titleTextStyle: theme.textTheme.headline6,
|
||||||
|
systemOverlayStyle: theme.brightness == Brightness.dark
|
||||||
|
? SystemUiOverlayStyle.light
|
||||||
|
: SystemUiOverlayStyle.dark,
|
||||||
elevation: 1,
|
elevation: 1,
|
||||||
leading: leadingWidget,
|
leading: leadingWidget,
|
||||||
backgroundColor: backgroundColor ?? channelHeaderTheme.color,
|
backgroundColor: backgroundColor ?? channelHeaderTheme.color,
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import 'package:collection/collection.dart' show IterableExtension;
|
import 'package:collection/collection.dart' show IterableExtension;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:jiffy/jiffy.dart';
|
import 'package:jiffy/jiffy.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
|
||||||
import 'package:stream_chat_flutter/src/extension.dart';
|
import 'package:stream_chat_flutter/src/extension.dart';
|
||||||
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
/// Widget which shows channel info
|
/// Widget which shows channel info
|
||||||
class ChannelInfo extends StatelessWidget {
|
class ChannelInfo extends StatelessWidget {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:stream_chat_flutter/src/extension.dart';
|
import 'package:stream_chat_flutter/src/extension.dart';
|
||||||
import 'package:stream_chat_flutter/src/stream_neumorphic_button.dart';
|
import 'package:stream_chat_flutter/src/stream_neumorphic_button.dart';
|
||||||
@@ -121,12 +122,16 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
|
|
||||||
final chatThemeData = StreamChatTheme.of(context);
|
final chatThemeData = StreamChatTheme.of(context);
|
||||||
final channelListHeaderThemeData = ChannelListHeaderTheme.of(context);
|
final channelListHeaderThemeData = ChannelListHeaderTheme.of(context);
|
||||||
|
final theme = Theme.of(context);
|
||||||
return InfoTile(
|
return InfoTile(
|
||||||
showMessage: showConnectionStateTile && showStatus,
|
showMessage: showConnectionStateTile && showStatus,
|
||||||
message: statusString,
|
message: statusString,
|
||||||
child: AppBar(
|
child: AppBar(
|
||||||
textTheme: Theme.of(context).textTheme,
|
toolbarTextStyle: theme.textTheme.bodyText2,
|
||||||
brightness: Theme.of(context).brightness,
|
titleTextStyle: theme.textTheme.headline6,
|
||||||
|
systemOverlayStyle: theme.brightness == Brightness.dark
|
||||||
|
? SystemUiOverlayStyle.light
|
||||||
|
: SystemUiOverlayStyle.dark,
|
||||||
elevation: 1,
|
elevation: 1,
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
backgroundColor ?? channelListHeaderThemeData.color,
|
backgroundColor ?? channelListHeaderThemeData.color,
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||||
import 'package:shimmer/shimmer.dart';
|
import 'package:shimmer/shimmer.dart';
|
||||||
import 'package:stream_chat_flutter/src/channel_bottom_sheet.dart';
|
import 'package:stream_chat_flutter/src/channel_bottom_sheet.dart';
|
||||||
|
import 'package:stream_chat_flutter/src/extension.dart';
|
||||||
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
||||||
import 'package:stream_chat_flutter/src/theme/themes.dart';
|
import 'package:stream_chat_flutter/src/theme/themes.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';
|
||||||
import 'package:stream_chat_flutter/src/extension.dart';
|
|
||||||
|
|
||||||
/// Callback called when tapping on a channel
|
/// Callback called when tapping on a channel
|
||||||
typedef ChannelTapCallback = void Function(Channel, Widget?);
|
typedef ChannelTapCallback = void Function(Channel, Widget?);
|
||||||
@@ -59,7 +59,7 @@ typedef ViewInfoCallback = void Function(Channel);
|
|||||||
/// Modify it to change the widget appearance.
|
/// Modify it to change the widget appearance.
|
||||||
class ChannelListView extends StatefulWidget {
|
class ChannelListView extends StatefulWidget {
|
||||||
/// Instantiate a new ChannelListView
|
/// Instantiate a new ChannelListView
|
||||||
const ChannelListView({
|
ChannelListView({
|
||||||
Key? key,
|
Key? key,
|
||||||
this.filter,
|
this.filter,
|
||||||
this.sort,
|
this.sort,
|
||||||
@@ -68,9 +68,12 @@ class ChannelListView extends StatefulWidget {
|
|||||||
this.presence = false,
|
this.presence = false,
|
||||||
this.memberLimit,
|
this.memberLimit,
|
||||||
this.messageLimit,
|
this.messageLimit,
|
||||||
this.pagination = const PaginationParams(
|
@Deprecated(
|
||||||
limit: 25,
|
"'pagination' is deprecated and shouldn't be used. "
|
||||||
),
|
"This property is no longer used, Please use 'limit' instead",
|
||||||
|
)
|
||||||
|
this.pagination,
|
||||||
|
int? limit,
|
||||||
this.onChannelTap,
|
this.onChannelTap,
|
||||||
this.onChannelLongPress,
|
this.onChannelLongPress,
|
||||||
this.channelWidget,
|
this.channelWidget,
|
||||||
@@ -92,7 +95,8 @@ class ChannelListView extends StatefulWidget {
|
|||||||
this.onDeletePressed,
|
this.onDeletePressed,
|
||||||
this.swipeActions,
|
this.swipeActions,
|
||||||
this.channelListController,
|
this.channelListController,
|
||||||
}) : super(key: key);
|
}) : limit = limit ?? pagination?.limit ?? 25,
|
||||||
|
super(key: key);
|
||||||
|
|
||||||
/// If true a default swipe to action behaviour will be added to this widget
|
/// If true a default swipe to action behaviour will be added to this widget
|
||||||
final bool swipeToAction;
|
final bool swipeToAction;
|
||||||
@@ -129,7 +133,14 @@ class ChannelListView extends StatefulWidget {
|
|||||||
/// limit: the number of channels to return (max is 30)
|
/// limit: the number of channels to return (max is 30)
|
||||||
/// offset: the offset (max is 1000)
|
/// offset: the offset (max is 1000)
|
||||||
/// message_limit: how many messages should be included to each channel
|
/// message_limit: how many messages should be included to each channel
|
||||||
final PaginationParams pagination;
|
@Deprecated(
|
||||||
|
"'pagination' is deprecated and shouldn't be used. "
|
||||||
|
"This property is no longer used, Please use 'limit' instead",
|
||||||
|
)
|
||||||
|
final PaginationParams? pagination;
|
||||||
|
|
||||||
|
/// The amount of channels requested per API call.
|
||||||
|
final int limit;
|
||||||
|
|
||||||
/// Function called when tapping on a channel
|
/// Function called when tapping on a channel
|
||||||
/// By default it calls [Navigator.push] building a [MaterialPageRoute]
|
/// By default it calls [Navigator.push] building a [MaterialPageRoute]
|
||||||
@@ -218,7 +229,7 @@ class _ChannelListViewState extends State<ChannelListView> {
|
|||||||
presence: widget.presence,
|
presence: widget.presence,
|
||||||
memberLimit: widget.memberLimit,
|
memberLimit: widget.memberLimit,
|
||||||
messageLimit: widget.messageLimit,
|
messageLimit: widget.messageLimit,
|
||||||
pagination: widget.pagination,
|
limit: widget.limit,
|
||||||
channelListController: _channelListController,
|
channelListController: _channelListController,
|
||||||
listBuilder: widget.listBuilder ?? _buildListView,
|
listBuilder: widget.listBuilder ?? _buildListView,
|
||||||
emptyBuilder: widget.emptyBuilder ?? _buildEmptyWidget,
|
emptyBuilder: widget.emptyBuilder ?? _buildEmptyWidget,
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
|
import 'package:stream_chat_flutter/src/extension.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';
|
||||||
import 'package:stream_chat_flutter/src/extension.dart';
|
|
||||||
|
|
||||||
/// It shows the current [Channel] name using a [Text] widget.
|
/// It shows the current [Channel] name using a [Text] widget.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -0,0 +1,210 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:stream_chat_flutter/src/extension.dart';
|
||||||
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
|
/// Overlay for displaying commands that can be used
|
||||||
|
class CommandsOverlay extends StatelessWidget {
|
||||||
|
/// Constructor for creating a [CommandsOverlay]
|
||||||
|
const CommandsOverlay({
|
||||||
|
required this.text,
|
||||||
|
required this.onCommandResult,
|
||||||
|
required this.size,
|
||||||
|
required this.channel,
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
/// The size of the overlay
|
||||||
|
final Size size;
|
||||||
|
|
||||||
|
/// Query for searching commands
|
||||||
|
final String text;
|
||||||
|
|
||||||
|
/// The channel to search for users
|
||||||
|
final Channel channel;
|
||||||
|
|
||||||
|
/// Callback called when a command is selected
|
||||||
|
final ValueChanged<Command> onCommandResult;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final _streamChatTheme = StreamChatTheme.of(context);
|
||||||
|
final commands = channel.config?.commands
|
||||||
|
.where((c) => c.name.contains(text.replaceFirst('/', '')))
|
||||||
|
.toList() ??
|
||||||
|
[];
|
||||||
|
|
||||||
|
if (commands.isEmpty) {
|
||||||
|
return const SizedBox();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.all(4),
|
||||||
|
child: Card(
|
||||||
|
elevation: 2,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
color: _streamChatTheme.colorTheme.barsBg,
|
||||||
|
clipBehavior: Clip.hardEdge,
|
||||||
|
child: Container(
|
||||||
|
constraints: BoxConstraints.loose(size),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: _streamChatTheme.colorTheme.barsBg,
|
||||||
|
borderRadius: BorderRadius.circular(8)),
|
||||||
|
child: ListView(
|
||||||
|
padding: const EdgeInsets.all(0),
|
||||||
|
shrinkWrap: true,
|
||||||
|
children: [
|
||||||
|
if (commands.isNotEmpty)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 8),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 8,
|
||||||
|
),
|
||||||
|
child: StreamSvgIcon.lightning(
|
||||||
|
color: _streamChatTheme.colorTheme.accentPrimary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
context.translations.instantCommandsLabel,
|
||||||
|
style: TextStyle(
|
||||||
|
color: _streamChatTheme.colorTheme.textHighEmphasis
|
||||||
|
.withOpacity(.5),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 10,
|
||||||
|
),
|
||||||
|
...commands
|
||||||
|
.map(
|
||||||
|
(c) => InkWell(
|
||||||
|
onTap: () {
|
||||||
|
onCommandResult(c);
|
||||||
|
},
|
||||||
|
child: SizedBox(
|
||||||
|
height: 40,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
const SizedBox(
|
||||||
|
width: 16,
|
||||||
|
),
|
||||||
|
_buildCommandIcon(_streamChatTheme, c.name),
|
||||||
|
const SizedBox(
|
||||||
|
width: 8,
|
||||||
|
),
|
||||||
|
Text.rich(
|
||||||
|
TextSpan(
|
||||||
|
text: c.name.capitalize(),
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.bold),
|
||||||
|
children: [
|
||||||
|
TextSpan(
|
||||||
|
text: ' /${c.name} ${c.args}',
|
||||||
|
style: _streamChatTheme.textTheme.body
|
||||||
|
.copyWith(
|
||||||
|
// ignore: lines_longer_than_80_chars
|
||||||
|
color: _streamChatTheme
|
||||||
|
// ignore: lines_longer_than_80_chars
|
||||||
|
.colorTheme
|
||||||
|
.textLowEmphasis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildCommandIcon(
|
||||||
|
StreamChatThemeData _streamChatTheme, String iconType) {
|
||||||
|
switch (iconType) {
|
||||||
|
case 'giphy':
|
||||||
|
return CircleAvatar(
|
||||||
|
radius: 12,
|
||||||
|
child: StreamSvgIcon.giphyIcon(
|
||||||
|
size: 24,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
case 'ban':
|
||||||
|
return CircleAvatar(
|
||||||
|
backgroundColor: _streamChatTheme.colorTheme.accentPrimary,
|
||||||
|
radius: 12,
|
||||||
|
child: StreamSvgIcon.iconUserDelete(
|
||||||
|
size: 16,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
case 'flag':
|
||||||
|
return CircleAvatar(
|
||||||
|
backgroundColor: _streamChatTheme.colorTheme.accentPrimary,
|
||||||
|
radius: 12,
|
||||||
|
child: StreamSvgIcon.flag(
|
||||||
|
size: 14,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
case 'imgur':
|
||||||
|
return CircleAvatar(
|
||||||
|
backgroundColor: _streamChatTheme.colorTheme.accentPrimary,
|
||||||
|
radius: 12,
|
||||||
|
child: ClipOval(
|
||||||
|
child: StreamSvgIcon.imgur(
|
||||||
|
size: 24,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
case 'mute':
|
||||||
|
return CircleAvatar(
|
||||||
|
backgroundColor: _streamChatTheme.colorTheme.accentPrimary,
|
||||||
|
radius: 12,
|
||||||
|
child: StreamSvgIcon.mute(
|
||||||
|
size: 16,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
case 'unban':
|
||||||
|
return CircleAvatar(
|
||||||
|
backgroundColor: _streamChatTheme.colorTheme.accentPrimary,
|
||||||
|
radius: 12,
|
||||||
|
child: StreamSvgIcon.userAdd(
|
||||||
|
size: 16,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
case 'unmute':
|
||||||
|
return CircleAvatar(
|
||||||
|
backgroundColor: _streamChatTheme.colorTheme.accentPrimary,
|
||||||
|
radius: 12,
|
||||||
|
child: StreamSvgIcon.volumeUp(
|
||||||
|
size: 16,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return CircleAvatar(
|
||||||
|
backgroundColor: _streamChatTheme.colorTheme.accentPrimary,
|
||||||
|
radius: 12,
|
||||||
|
child: StreamSvgIcon.lightning(
|
||||||
|
size: 16,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:jiffy/jiffy.dart';
|
import 'package:jiffy/jiffy.dart';
|
||||||
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
|
||||||
import 'package:stream_chat_flutter/src/extension.dart';
|
import 'package:stream_chat_flutter/src/extension.dart';
|
||||||
|
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
||||||
|
|
||||||
/// It shows a date divider depending on the date difference
|
/// It shows a date divider depending on the date difference
|
||||||
class DateDivider extends StatelessWidget {
|
class DateDivider extends StatelessWidget {
|
||||||
|
|||||||
@@ -26,7 +26,8 @@
|
|||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
import 'package:collection/collection.dart' show IterableExtension;
|
import 'package:collection/collection.dart'
|
||||||
|
show IterableExtension, ListEquality;
|
||||||
|
|
||||||
/// All Groups
|
/// All Groups
|
||||||
enum EmojiGroup {
|
enum EmojiGroup {
|
||||||
@@ -114240,7 +114241,6 @@ final emojiRegex = RegExp(
|
|||||||
class Emoji {
|
class Emoji {
|
||||||
static const variationSelector16 = 65039;
|
static const variationSelector16 = 65039;
|
||||||
static const ZWJ = 8205;
|
static const ZWJ = 8205;
|
||||||
|
|
||||||
final String? name;
|
final String? name;
|
||||||
final String? char;
|
final String? char;
|
||||||
final String? shortName;
|
final String? shortName;
|
||||||
@@ -114252,14 +114252,39 @@ class Emoji {
|
|||||||
|
|
||||||
/// Emoji class.
|
/// Emoji class.
|
||||||
/// [name] of emoji. [char] and character of emoji. [shortName] and a digest name of emoji, [emojiGroup] is emoji's group and [emojiSubgroup] is emoji's subgroup. [keywords] list of keywords for emoji. [modifiable] `true` if emoji has skin.
|
/// [name] of emoji. [char] and character of emoji. [shortName] and a digest name of emoji, [emojiGroup] is emoji's group and [emojiSubgroup] is emoji's subgroup. [keywords] list of keywords for emoji. [modifiable] `true` if emoji has skin.
|
||||||
Emoji(
|
Emoji({
|
||||||
{this.name,
|
this.name,
|
||||||
this.char,
|
this.char,
|
||||||
this.shortName,
|
this.shortName,
|
||||||
this.emojiGroup,
|
this.emojiGroup,
|
||||||
this.emojiSubgroup,
|
this.emojiSubgroup,
|
||||||
this.keywords = const [],
|
this.keywords = const [],
|
||||||
this.modifiable = false});
|
this.modifiable = false,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
other is Emoji &&
|
||||||
|
runtimeType == other.runtimeType &&
|
||||||
|
name == other.name &&
|
||||||
|
char == other.char &&
|
||||||
|
shortName == other.shortName &&
|
||||||
|
emojiGroup == other.emojiGroup &&
|
||||||
|
emojiSubgroup == other.emojiSubgroup &&
|
||||||
|
const ListEquality().equals(keywords, other.keywords) &&
|
||||||
|
modifiable == other.modifiable;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(
|
||||||
|
name.hashCode,
|
||||||
|
char.hashCode,
|
||||||
|
shortName.hashCode,
|
||||||
|
emojiGroup.hashCode,
|
||||||
|
emojiSubgroup.hashCode,
|
||||||
|
keywords.hashCode,
|
||||||
|
modifiable.hashCode,
|
||||||
|
);
|
||||||
|
|
||||||
/// Runes of Emoji Character
|
/// Runes of Emoji Character
|
||||||
List<int> get charRunes {
|
List<int> get charRunes {
|
||||||
@@ -114339,9 +114364,11 @@ class Emoji {
|
|||||||
return _emojis.firstWhereOrNull((Emoji emoji) => emoji.name == name);
|
return _emojis.firstWhereOrNull((Emoji emoji) => emoji.name == name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns Emoji by [name] as short name.
|
/// Returns Emoji by [shortName] as short name.
|
||||||
static Emoji? byShortName(String name) {
|
static Emoji? byShortName(String shortName) {
|
||||||
return _emojis.firstWhereOrNull((Emoji emoji) => emoji.char == name);
|
return _emojis.firstWhereOrNull(
|
||||||
|
(Emoji emoji) => emoji.shortName == shortName,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns list of Emojis in a same [group]
|
/// Returns list of Emojis in a same [group]
|
||||||
|
|||||||
@@ -0,0 +1,118 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:stream_chat_flutter/src/emoji/emoji.dart';
|
||||||
|
import 'package:stream_chat_flutter/src/extension.dart';
|
||||||
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
import 'package:substring_highlight/substring_highlight.dart';
|
||||||
|
|
||||||
|
/// Overlay for displaying emoji that can be used
|
||||||
|
class EmojiOverlay extends StatelessWidget {
|
||||||
|
/// Constructor for creating a [EmojiOverlay]
|
||||||
|
const EmojiOverlay({
|
||||||
|
required this.query,
|
||||||
|
required this.onEmojiResult,
|
||||||
|
required this.size,
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
/// The size of the overlay
|
||||||
|
final Size size;
|
||||||
|
|
||||||
|
/// Query for searching emoji
|
||||||
|
final String query;
|
||||||
|
|
||||||
|
/// Callback called when an emoji is selected
|
||||||
|
final ValueChanged<Emoji> onEmojiResult;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final _streamChatTheme = StreamChatTheme.of(context);
|
||||||
|
final _emojiNames =
|
||||||
|
Emoji.all().where((it) => it.name != null).map((e) => e.name!);
|
||||||
|
|
||||||
|
final emojis = _emojiNames
|
||||||
|
.where((e) => e.contains(query))
|
||||||
|
.map(Emoji.byName)
|
||||||
|
.where((e) => e != null);
|
||||||
|
|
||||||
|
if (emojis.isEmpty) {
|
||||||
|
return const SizedBox();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Card(
|
||||||
|
margin: const EdgeInsets.all(8),
|
||||||
|
elevation: 2,
|
||||||
|
color: _streamChatTheme.colorTheme.barsBg,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
clipBehavior: Clip.hardEdge,
|
||||||
|
child: Container(
|
||||||
|
constraints: BoxConstraints.loose(size),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
boxShadow: const [
|
||||||
|
BoxShadow(
|
||||||
|
spreadRadius: -8,
|
||||||
|
blurRadius: 5,
|
||||||
|
offset: Offset(0, -4),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
color: _streamChatTheme.colorTheme.barsBg,
|
||||||
|
),
|
||||||
|
child: ListView.builder(
|
||||||
|
padding: const EdgeInsets.all(0),
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: emojis.length + 1,
|
||||||
|
itemBuilder: (context, i) {
|
||||||
|
if (i == 0) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 8, top: 8),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
|
child: StreamSvgIcon.smile(
|
||||||
|
color: _streamChatTheme.colorTheme.accentPrimary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Flexible(
|
||||||
|
child: Text(
|
||||||
|
context.translations.emojiMatchingQueryText(
|
||||||
|
query,
|
||||||
|
),
|
||||||
|
style: TextStyle(
|
||||||
|
color: _streamChatTheme.colorTheme.textHighEmphasis
|
||||||
|
.withOpacity(.5),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final emoji = emojis.elementAt(i - 1)!;
|
||||||
|
final themeData = Theme.of(context);
|
||||||
|
return ListTile(
|
||||||
|
title: SubstringHighlight(
|
||||||
|
text:
|
||||||
|
// ignore: lines_longer_than_80_chars
|
||||||
|
"${emoji.char} ${emoji.name!.replaceAll('_', ' ')}",
|
||||||
|
term: query,
|
||||||
|
textStyleHighlight: themeData.textTheme.headline6!.copyWith(
|
||||||
|
fontSize: 14.5,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
textStyle: themeData.textTheme.headline6!.copyWith(
|
||||||
|
fontSize: 14.5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
onEmojiResult(emoji);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:characters/characters.dart';
|
import 'package:characters/characters.dart';
|
||||||
|
import 'package:diacritic/diacritic.dart';
|
||||||
import 'package:file_picker/file_picker.dart';
|
import 'package:file_picker/file_picker.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stream_chat_flutter/src/emoji/emoji.dart';
|
import 'package:stream_chat_flutter/src/emoji/emoji.dart';
|
||||||
@@ -11,7 +12,7 @@ final _emojiChars = Emoji.chars();
|
|||||||
extension StringExtension on String {
|
extension StringExtension on String {
|
||||||
/// Returns the capitalized string
|
/// Returns the capitalized string
|
||||||
String capitalize() =>
|
String capitalize() =>
|
||||||
'${this[0].toUpperCase()}${substring(1).toLowerCase()}';
|
isNotEmpty ? '${this[0].toUpperCase()}${substring(1).toLowerCase()}' : '';
|
||||||
|
|
||||||
/// Returns whether the string contains only emoji's or not.
|
/// Returns whether the string contains only emoji's or not.
|
||||||
///
|
///
|
||||||
@@ -24,6 +25,12 @@ extension StringExtension on String {
|
|||||||
final characters = trim().characters;
|
final characters = trim().characters;
|
||||||
return characters.every(_emojiChars.contains);
|
return characters.every(_emojiChars.contains);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Removes accents and diacritics from the given String.
|
||||||
|
String get diacriticsInsensitive => removeDiacritics(this);
|
||||||
|
|
||||||
|
/// Levenshtein distance between this and [t].
|
||||||
|
int levenshteinDistance(String t) => levenshtein(this, t);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// List extension
|
/// List extension
|
||||||
@@ -170,3 +177,49 @@ extension IconButtonX on IconButton {
|
|||||||
icon: icon ?? this.icon,
|
icon: icon ?? this.icon,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Extensions on List<User>
|
||||||
|
extension UserListX on List<User> {
|
||||||
|
/// It does an search on a list of [User] and returns users with
|
||||||
|
/// `id` or `name` containing the [query].
|
||||||
|
///
|
||||||
|
/// Results are returned sorted by their edit distance from the
|
||||||
|
/// searched string, distance is calculated using the [levenshtein] algorithm.
|
||||||
|
List<User> search(String query) {
|
||||||
|
String normalize(String input) => input.toLowerCase().diacriticsInsensitive;
|
||||||
|
|
||||||
|
final normalizedQuery = normalize(query);
|
||||||
|
|
||||||
|
final matchingUsers = <User, int>{}; // User:lDistance
|
||||||
|
|
||||||
|
for (final user in this) {
|
||||||
|
final normalizedId = normalize(user.id);
|
||||||
|
final normalizedUserName = normalize(user.name);
|
||||||
|
final lDistance = normalizedUserName.levenshteinDistance(normalizedQuery);
|
||||||
|
final containsId = normalizedId.contains(normalizedQuery);
|
||||||
|
final containsName = normalizedUserName.contains(normalizedQuery);
|
||||||
|
if (lDistance < 3 || containsId || containsName) {
|
||||||
|
matchingUsers[user] = lDistance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final entries = matchingUsers.entries.toList(growable: false)
|
||||||
|
..sort((prev, curr) {
|
||||||
|
bool containsQuery(User user) =>
|
||||||
|
normalize(user.id).contains(normalizedQuery) ||
|
||||||
|
normalize(user.name).contains(normalizedQuery);
|
||||||
|
|
||||||
|
final containsInPrev = containsQuery(prev.key);
|
||||||
|
final containsInCurr = containsQuery(curr.key);
|
||||||
|
|
||||||
|
if (containsInPrev && !containsInCurr) {
|
||||||
|
return -1;
|
||||||
|
} else if (!containsInPrev && containsInCurr) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return prev.value.compareTo(curr.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
return entries.map((e) => e.key).toList(growable: false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ import 'package:cached_network_image/cached_network_image.dart';
|
|||||||
import 'package:chewie/chewie.dart';
|
import 'package:chewie/chewie.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:photo_view/photo_view.dart';
|
import 'package:photo_view/photo_view.dart';
|
||||||
|
import 'package:stream_chat_flutter/src/extension.dart';
|
||||||
import 'package:stream_chat_flutter/src/gallery_footer.dart';
|
import 'package:stream_chat_flutter/src/gallery_footer.dart';
|
||||||
import 'package:stream_chat_flutter/src/gallery_header.dart';
|
import 'package:stream_chat_flutter/src/gallery_header.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
import 'package:video_player/video_player.dart';
|
import 'package:video_player/video_player.dart';
|
||||||
import 'package:stream_chat_flutter/src/extension.dart';
|
|
||||||
|
|
||||||
/// Return action for coming back from pages
|
/// Return action for coming back from pages
|
||||||
enum ReturnActionType {
|
enum ReturnActionType {
|
||||||
@@ -112,6 +112,8 @@ class _FullScreenMediaState extends State<FullScreenMedia>
|
|||||||
attachment.assetUrl ??
|
attachment.assetUrl ??
|
||||||
attachment.thumbUrl;
|
attachment.thumbUrl;
|
||||||
return PhotoView(
|
return PhotoView(
|
||||||
|
loadingBuilder: (context, image) =>
|
||||||
|
const Offstage(),
|
||||||
imageProvider: (imageUrl == null &&
|
imageProvider: (imageUrl == null &&
|
||||||
attachment.localUri != null &&
|
attachment.localUri != null &&
|
||||||
attachment.file?.bytes != null)
|
attachment.file?.bytes != null)
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:share_plus/share_plus.dart';
|
import 'package:share_plus/share_plus.dart';
|
||||||
|
import 'package:stream_chat_flutter/src/extension.dart';
|
||||||
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
||||||
import 'package:stream_chat_flutter/src/theme/themes.dart';
|
import 'package:stream_chat_flutter/src/theme/themes.dart';
|
||||||
import 'package:stream_chat_flutter/src/video_thumbnail_image.dart';
|
import 'package:stream_chat_flutter/src/video_thumbnail_image.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';
|
||||||
import 'package:stream_chat_flutter/src/extension.dart';
|
|
||||||
|
|
||||||
/// Footer widget for media display
|
/// Footer widget for media display
|
||||||
class GalleryFooter extends StatefulWidget implements PreferredSizeWidget {
|
class GalleryFooter extends StatefulWidget implements PreferredSizeWidget {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:stream_chat_flutter/src/attachment_actions_modal.dart';
|
import 'package:stream_chat_flutter/src/attachment_actions_modal.dart';
|
||||||
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
||||||
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
||||||
@@ -57,9 +58,13 @@ class GalleryHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final galleryHeaderThemeData = GalleryHeaderTheme.of(context);
|
final galleryHeaderThemeData = GalleryHeaderTheme.of(context);
|
||||||
|
final theme = Theme.of(context);
|
||||||
return AppBar(
|
return AppBar(
|
||||||
textTheme: Theme.of(context).textTheme,
|
toolbarTextStyle: theme.textTheme.bodyText2,
|
||||||
brightness: Theme.of(context).brightness,
|
titleTextStyle: theme.textTheme.headline6,
|
||||||
|
systemOverlayStyle: theme.brightness == Brightness.dark
|
||||||
|
? SystemUiOverlayStyle.light
|
||||||
|
: SystemUiOverlayStyle.dark,
|
||||||
elevation: 1,
|
elevation: 1,
|
||||||
leading: showBackButton
|
leading: showBackButton
|
||||||
? IconButton(
|
? IconButton(
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ class ImageGroup extends StatelessWidget {
|
|||||||
required this.size,
|
required this.size,
|
||||||
this.onReturnAction,
|
this.onReturnAction,
|
||||||
this.onShowMessage,
|
this.onShowMessage,
|
||||||
|
this.onAttachmentTap,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
/// List of attachments to show
|
/// List of attachments to show
|
||||||
@@ -23,6 +24,9 @@ class ImageGroup extends StatelessWidget {
|
|||||||
/// Callback when attachment is returned to from other screens
|
/// Callback when attachment is returned to from other screens
|
||||||
final ValueChanged<ReturnActionType>? onReturnAction;
|
final ValueChanged<ReturnActionType>? onReturnAction;
|
||||||
|
|
||||||
|
/// Callback when attachment is tapped
|
||||||
|
final void Function(Message message, Attachment attachment)? onAttachmentTap;
|
||||||
|
|
||||||
/// Message which images are attached to
|
/// Message which images are attached to
|
||||||
final Message message;
|
final Message message;
|
||||||
|
|
||||||
@@ -117,6 +121,10 @@ class ImageGroup extends StatelessWidget {
|
|||||||
BuildContext context,
|
BuildContext context,
|
||||||
int index,
|
int index,
|
||||||
) async {
|
) async {
|
||||||
|
if (onAttachmentTap != null) {
|
||||||
|
return onAttachmentTap!(message, images[index]);
|
||||||
|
}
|
||||||
|
|
||||||
final channel = StreamChannel.of(context).channel;
|
final channel = StreamChannel.of(context).channel;
|
||||||
|
|
||||||
final res = await Navigator.push(
|
final res = await Navigator.push(
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
|||||||
/// This widget is used for showing user tiles for mentions
|
/// This widget is used for showing user tiles for mentions
|
||||||
/// Use [title], [subtitle], [leading], [trailing] for
|
/// Use [title], [subtitle], [leading], [trailing] for
|
||||||
/// substituting widgets in respective positions
|
/// substituting widgets in respective positions
|
||||||
|
@Deprecated('Use `UserMentionTile` instead. Will be removed in future release')
|
||||||
class MentionTile extends StatelessWidget {
|
class MentionTile extends StatelessWidget {
|
||||||
/// Constructor for creating a [MentionTile] widget
|
/// Constructor for creating a [MentionTile] widget
|
||||||
const MentionTile(
|
const MentionTile(
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -169,6 +169,7 @@ class MessageListView extends StatefulWidget {
|
|||||||
this.messageListController,
|
this.messageListController,
|
||||||
this.reverse = true,
|
this.reverse = true,
|
||||||
this.paginationLimit = 20,
|
this.paginationLimit = 20,
|
||||||
|
this.paginationLoadingIndicatorBuilder,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
/// Function used to build a custom message widget
|
/// Function used to build a custom message widget
|
||||||
@@ -284,6 +285,9 @@ class MessageListView extends StatefulWidget {
|
|||||||
/// Use [ChannelListController.paginateData] pagination.
|
/// Use [ChannelListController.paginateData] pagination.
|
||||||
final MessageListController? messageListController;
|
final MessageListController? messageListController;
|
||||||
|
|
||||||
|
/// Builder used to build the loading indicator shown while paginating.
|
||||||
|
final WidgetBuilder? paginationLoadingIndicatorBuilder;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_MessageListViewState createState() => _MessageListViewState();
|
_MessageListViewState createState() => _MessageListViewState();
|
||||||
}
|
}
|
||||||
@@ -293,7 +297,6 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
void Function(Message)? _onThreadTap;
|
void Function(Message)? _onThreadTap;
|
||||||
bool _showScrollToBottom = false;
|
bool _showScrollToBottom = false;
|
||||||
late final ItemPositionsListener _itemPositionListener;
|
late final ItemPositionsListener _itemPositionListener;
|
||||||
late final Stream<Iterable<ItemPosition>> _itemPositionStream;
|
|
||||||
int? _messageListLength;
|
int? _messageListLength;
|
||||||
StreamChannelState? streamChannel;
|
StreamChannelState? streamChannel;
|
||||||
late StreamChatThemeData _streamTheme;
|
late StreamChatThemeData _streamTheme;
|
||||||
@@ -499,14 +502,18 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
return _buildThreadSeparator();
|
return _buildThreadSeparator();
|
||||||
}
|
}
|
||||||
if (i == itemCount - 3) {
|
if (i == itemCount - 3) {
|
||||||
if (widget.headerBuilder == null) {
|
if (widget.reverse
|
||||||
|
? widget.headerBuilder == null
|
||||||
|
: widget.footerBuilder == null) {
|
||||||
if (_isThreadConversation) return const Offstage();
|
if (_isThreadConversation) return const Offstage();
|
||||||
return const SizedBox(height: 52);
|
return const SizedBox(height: 52);
|
||||||
}
|
}
|
||||||
return const SizedBox(height: 8);
|
return const SizedBox(height: 8);
|
||||||
}
|
}
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
if (widget.footerBuilder == null) {
|
if (widget.reverse
|
||||||
|
? widget.footerBuilder == null
|
||||||
|
: widget.headerBuilder == null) {
|
||||||
return const SizedBox(height: 30);
|
return const SizedBox(height: 30);
|
||||||
}
|
}
|
||||||
return const SizedBox(height: 8);
|
return const SizedBox(height: 8);
|
||||||
@@ -530,13 +537,13 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
? widget.dateDividerBuilder!(
|
? widget.dateDividerBuilder!(
|
||||||
nextMessage.createdAt.toLocal(),
|
nextMessage.createdAt.toLocal(),
|
||||||
)
|
)
|
||||||
: DateDivider(
|
: Padding(
|
||||||
dateTime: nextMessage.createdAt.toLocal(),
|
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||||
|
child: DateDivider(
|
||||||
|
dateTime: nextMessage.createdAt.toLocal(),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
return Padding(
|
return divider;
|
||||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
|
||||||
child: divider,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
final timeDiff =
|
final timeDiff =
|
||||||
Jiffy(nextMessage.createdAt.toLocal()).diff(
|
Jiffy(nextMessage.createdAt.toLocal()).diff(
|
||||||
@@ -565,27 +572,42 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (i == itemCount - 2) {
|
if (i == itemCount - 2) {
|
||||||
return widget.headerBuilder?.call(context) ??
|
if (widget.reverse) {
|
||||||
const Offstage();
|
return widget.headerBuilder?.call(context) ??
|
||||||
|
const Offstage();
|
||||||
|
} else {
|
||||||
|
return widget.footerBuilder?.call(context) ??
|
||||||
|
const Offstage();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final indicatorBuilder =
|
||||||
|
widget.paginationLoadingIndicatorBuilder;
|
||||||
|
|
||||||
if (i == itemCount - 3) {
|
if (i == itemCount - 3) {
|
||||||
return _buildLoadingIndicator(
|
return _loadingIndicator(
|
||||||
streamChannel!,
|
streamChannel!,
|
||||||
QueryDirection.top,
|
QueryDirection.top,
|
||||||
|
indicatorBuilder: indicatorBuilder,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i == 1) {
|
if (i == 1) {
|
||||||
return _buildLoadingIndicator(
|
return _loadingIndicator(
|
||||||
streamChannel!,
|
streamChannel!,
|
||||||
QueryDirection.bottom,
|
QueryDirection.bottom,
|
||||||
|
indicatorBuilder: indicatorBuilder,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
return widget.footerBuilder?.call(context) ??
|
if (widget.reverse) {
|
||||||
const Offstage();
|
return widget.footerBuilder?.call(context) ??
|
||||||
|
const Offstage();
|
||||||
|
} else {
|
||||||
|
return widget.headerBuilder?.call(context) ??
|
||||||
|
const Offstage();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const bottomMessageIndex = 2; // 1 -> loader // 0 -> footer
|
const bottomMessageIndex = 2; // 1 -> loader // 0 -> footer
|
||||||
@@ -657,7 +679,8 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
right: 0,
|
right: 0,
|
||||||
child: BetterStreamBuilder<Iterable<ItemPosition>>(
|
child: BetterStreamBuilder<Iterable<ItemPosition>>(
|
||||||
initialData: _itemPositionListener.itemPositions.value,
|
initialData: _itemPositionListener.itemPositions.value,
|
||||||
stream: _itemPositionStream,
|
stream: _valueListenableToStreamAdapter(
|
||||||
|
_itemPositionListener.itemPositions),
|
||||||
comparator: (a, b) {
|
comparator: (a, b) {
|
||||||
if (a == null || b == null) {
|
if (a == null || b == null) {
|
||||||
return false;
|
return false;
|
||||||
@@ -808,15 +831,17 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildLoadingIndicator(
|
Widget _loadingIndicator(
|
||||||
StreamChannelState streamChannel,
|
StreamChannelState streamChannel,
|
||||||
QueryDirection direction,
|
QueryDirection direction, {
|
||||||
) =>
|
WidgetBuilder? indicatorBuilder,
|
||||||
|
}) =>
|
||||||
_LoadingIndicator(
|
_LoadingIndicator(
|
||||||
direction: direction,
|
direction: direction,
|
||||||
streamTheme: _streamTheme,
|
streamTheme: _streamTheme,
|
||||||
streamChannel: streamChannel,
|
streamChannel: streamChannel,
|
||||||
isThreadConversation: _isThreadConversation,
|
isThreadConversation: _isThreadConversation,
|
||||||
|
indicatorBuilder: indicatorBuilder,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildBottomMessage(
|
Widget _buildBottomMessage(
|
||||||
@@ -996,7 +1021,7 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
final isOnlyEmoji = message.text?.isOnlyEmoji ?? false;
|
final isOnlyEmoji = message.text?.isOnlyEmoji ?? false;
|
||||||
|
|
||||||
final hasUrlAttachment =
|
final hasUrlAttachment =
|
||||||
message.attachments.any((it) => it.ogScrapeUrl != null) == true;
|
message.attachments.any((it) => it.titleLink != null) == true;
|
||||||
|
|
||||||
final borderSide =
|
final borderSide =
|
||||||
isOnlyEmoji || hasUrlAttachment || (isMyMessage && !hasFileAttachment)
|
isOnlyEmoji || hasUrlAttachment || (isMyMessage && !hasFileAttachment)
|
||||||
@@ -1185,8 +1210,6 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
_scrollController = widget.scrollController ?? ItemScrollController();
|
_scrollController = widget.scrollController ?? ItemScrollController();
|
||||||
_itemPositionListener =
|
_itemPositionListener =
|
||||||
widget.itemPositionListener ?? ItemPositionsListener.create();
|
widget.itemPositionListener ?? ItemPositionsListener.create();
|
||||||
_itemPositionStream =
|
|
||||||
_valueListenableToStreamAdapter(_itemPositionListener.itemPositions);
|
|
||||||
|
|
||||||
_getOnThreadTap();
|
_getOnThreadTap();
|
||||||
super.initState();
|
super.initState();
|
||||||
@@ -1204,10 +1227,12 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
initialAlignment = _initialAlignment;
|
initialAlignment = _initialAlignment;
|
||||||
|
|
||||||
WidgetsBinding.instance!.addPostFrameCallback((timeStamp) {
|
WidgetsBinding.instance!.addPostFrameCallback((timeStamp) {
|
||||||
_scrollController?.jumpTo(
|
if (_scrollController?.isAttached == true) {
|
||||||
index: initialIndex,
|
_scrollController?.jumpTo(
|
||||||
alignment: initialAlignment,
|
index: initialIndex,
|
||||||
);
|
alignment: initialAlignment,
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
_messageNewListener =
|
_messageNewListener =
|
||||||
@@ -1216,8 +1241,9 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
_bottomPaginationActive = false;
|
_bottomPaginationActive = false;
|
||||||
_topPaginationActive = false;
|
_topPaginationActive = false;
|
||||||
}
|
}
|
||||||
if (event.message!.user!.id ==
|
if (event.message?.parentId == widget.parentMessage?.id &&
|
||||||
streamChannel!.channel.client.state.currentUser!.id) {
|
event.message!.user!.id ==
|
||||||
|
streamChannel!.channel.client.state.currentUser!.id) {
|
||||||
WidgetsBinding.instance!.addPostFrameCallback((_) {
|
WidgetsBinding.instance!.addPostFrameCallback((_) {
|
||||||
_scrollController?.jumpTo(
|
_scrollController?.jumpTo(
|
||||||
index: 0,
|
index: 0,
|
||||||
@@ -1280,12 +1306,14 @@ class _LoadingIndicator extends StatelessWidget {
|
|||||||
required this.isThreadConversation,
|
required this.isThreadConversation,
|
||||||
required this.direction,
|
required this.direction,
|
||||||
required this.streamChannel,
|
required this.streamChannel,
|
||||||
|
this.indicatorBuilder,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final StreamChatThemeData streamTheme;
|
final StreamChatThemeData streamTheme;
|
||||||
final bool isThreadConversation;
|
final bool isThreadConversation;
|
||||||
final QueryDirection direction;
|
final QueryDirection direction;
|
||||||
final StreamChannelState streamChannel;
|
final StreamChannelState streamChannel;
|
||||||
|
final WidgetBuilder? indicatorBuilder;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -1304,12 +1332,13 @@ class _LoadingIndicator extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
builder: (context, data) {
|
builder: (context, data) {
|
||||||
if (!data) return const Offstage();
|
if (!data) return const Offstage();
|
||||||
return const Center(
|
return indicatorBuilder?.call(context) ??
|
||||||
child: Padding(
|
const Center(
|
||||||
padding: EdgeInsets.all(8),
|
child: Padding(
|
||||||
child: CircularProgressIndicator(),
|
padding: EdgeInsets.all(8),
|
||||||
),
|
child: CircularProgressIndicator(),
|
||||||
);
|
),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:stream_chat_flutter/src/extension.dart';
|
||||||
import 'package:stream_chat_flutter/src/reaction_bubble.dart';
|
import 'package:stream_chat_flutter/src/reaction_bubble.dart';
|
||||||
import 'package:stream_chat_flutter/src/reaction_picker.dart';
|
import 'package:stream_chat_flutter/src/reaction_picker.dart';
|
||||||
import 'package:stream_chat_flutter/src/stream_chat.dart';
|
import 'package:stream_chat_flutter/src/stream_chat.dart';
|
||||||
@@ -8,7 +9,6 @@ import 'package:stream_chat_flutter/src/theme/themes.dart';
|
|||||||
import 'package:stream_chat_flutter/src/user_avatar.dart';
|
import 'package:stream_chat_flutter/src/user_avatar.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';
|
||||||
import 'package:stream_chat_flutter/src/extension.dart';
|
|
||||||
|
|
||||||
/// Modal widget for displaying message reactions
|
/// Modal widget for displaying message reactions
|
||||||
class MessageReactionsModal extends StatelessWidget {
|
class MessageReactionsModal extends StatelessWidget {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:jiffy/jiffy.dart';
|
import 'package:jiffy/jiffy.dart';
|
||||||
|
import 'package:stream_chat_flutter/src/extension.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';
|
||||||
import 'package:stream_chat_flutter/src/extension.dart';
|
|
||||||
|
|
||||||
/// It shows the current [Message] preview.
|
/// It shows the current [Message] preview.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:stream_chat_flutter/src/extension.dart';
|
||||||
import 'package:stream_chat_flutter/src/info_tile.dart';
|
import 'package:stream_chat_flutter/src/info_tile.dart';
|
||||||
import 'package:stream_chat_flutter/src/message_search_item.dart';
|
import 'package:stream_chat_flutter/src/message_search_item.dart';
|
||||||
import 'package:stream_chat_flutter/src/theme/themes.dart';
|
import 'package:stream_chat_flutter/src/theme/themes.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';
|
||||||
import 'package:stream_chat_flutter/src/extension.dart';
|
|
||||||
|
|
||||||
/// Callback called when tapping on a user
|
/// Callback called when tapping on a user
|
||||||
typedef MessageSearchItemTapCallback = void Function(GetMessageResponse);
|
typedef MessageSearchItemTapCallback = void Function(GetMessageResponse);
|
||||||
@@ -30,13 +30,13 @@ typedef EmptyMessageSearchBuilder = Widget Function(
|
|||||||
/// Widget build(BuildContext context) {
|
/// Widget build(BuildContext context) {
|
||||||
/// return Scaffold(
|
/// return Scaffold(
|
||||||
/// body: MessageSearchListView(
|
/// body: MessageSearchListView(
|
||||||
/// messageQuery: _channelQuery,
|
/// messageQuery: _channelQuery,
|
||||||
/// filters: {
|
/// filters: {
|
||||||
/// 'members': {
|
/// 'members': {
|
||||||
/// r'$in': [user.id]
|
/// r'$in': [user.id]
|
||||||
/// }
|
/// }
|
||||||
/// },
|
/// },
|
||||||
/// paginationParams: PaginationParams(limit: 20),
|
/// limit: 20,
|
||||||
/// ),
|
/// ),
|
||||||
/// );
|
/// );
|
||||||
/// }
|
/// }
|
||||||
@@ -58,7 +58,7 @@ class MessageSearchListView extends StatefulWidget {
|
|||||||
required this.filters,
|
required this.filters,
|
||||||
this.messageQuery,
|
this.messageQuery,
|
||||||
this.sortOptions,
|
this.sortOptions,
|
||||||
this.paginationParams,
|
this.limit = 30,
|
||||||
this.messageFilters,
|
this.messageFilters,
|
||||||
this.separatorBuilder,
|
this.separatorBuilder,
|
||||||
this.itemBuilder,
|
this.itemBuilder,
|
||||||
@@ -89,11 +89,8 @@ class MessageSearchListView extends StatefulWidget {
|
|||||||
/// Direction can be ascending or descending.
|
/// Direction can be ascending or descending.
|
||||||
final List<SortOption>? sortOptions;
|
final List<SortOption>? sortOptions;
|
||||||
|
|
||||||
/// Pagination parameters
|
/// The amount of messages requested per API call.
|
||||||
/// limit: the number of users to return (max is 30)
|
final int limit;
|
||||||
/// offset: the offset (max is 1000)
|
|
||||||
/// message_limit: how many messages should be included to each channel
|
|
||||||
final PaginationParams? paginationParams;
|
|
||||||
|
|
||||||
/// The message query filters to use.
|
/// The message query filters to use.
|
||||||
/// You can query on any of the custom fields you've defined on the [Channel].
|
/// You can query on any of the custom fields you've defined on the [Channel].
|
||||||
@@ -152,7 +149,7 @@ class _MessageSearchListViewState extends State<MessageSearchListView> {
|
|||||||
filters: widget.filters,
|
filters: widget.filters,
|
||||||
sortOptions: widget.sortOptions,
|
sortOptions: widget.sortOptions,
|
||||||
messageQuery: widget.messageQuery,
|
messageQuery: widget.messageQuery,
|
||||||
paginationParams: widget.paginationParams,
|
limit: widget.limit,
|
||||||
messageFilters: widget.messageFilters,
|
messageFilters: widget.messageFilters,
|
||||||
messageSearchListController: _messageSearchListController,
|
messageSearchListController: _messageSearchListController,
|
||||||
emptyBuilder: widget.emptyBuilder ??
|
emptyBuilder: widget.emptyBuilder ??
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import 'package:flutter/rendering.dart';
|
|||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_portal/flutter_portal.dart';
|
import 'package:flutter_portal/flutter_portal.dart';
|
||||||
import 'package:jiffy/jiffy.dart';
|
import 'package:jiffy/jiffy.dart';
|
||||||
|
import 'package:stream_chat_flutter/src/attachment/url_attachment.dart';
|
||||||
import 'package:stream_chat_flutter/src/extension.dart';
|
import 'package:stream_chat_flutter/src/extension.dart';
|
||||||
import 'package:stream_chat_flutter/src/image_group.dart';
|
import 'package:stream_chat_flutter/src/image_group.dart';
|
||||||
import 'package:stream_chat_flutter/src/message_action.dart';
|
import 'package:stream_chat_flutter/src/message_action.dart';
|
||||||
@@ -15,7 +16,6 @@ import 'package:stream_chat_flutter/src/message_reactions_modal.dart';
|
|||||||
import 'package:stream_chat_flutter/src/quoted_message_widget.dart';
|
import 'package:stream_chat_flutter/src/quoted_message_widget.dart';
|
||||||
import 'package:stream_chat_flutter/src/reaction_bubble.dart';
|
import 'package:stream_chat_flutter/src/reaction_bubble.dart';
|
||||||
import 'package:stream_chat_flutter/src/theme/themes.dart';
|
import 'package:stream_chat_flutter/src/theme/themes.dart';
|
||||||
import 'package:stream_chat_flutter/src/url_attachment.dart';
|
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
/// Widget builder for building attachments
|
/// Widget builder for building attachments
|
||||||
@@ -96,7 +96,7 @@ class MessageWidget extends StatefulWidget {
|
|||||||
this.bottomRowBuilder,
|
this.bottomRowBuilder,
|
||||||
this.deletedBottomRowBuilder,
|
this.deletedBottomRowBuilder,
|
||||||
this.onReturnAction,
|
this.onReturnAction,
|
||||||
Map<String, AttachmentBuilder>? customAttachmentBuilders,
|
this.customAttachmentBuilders,
|
||||||
this.readList,
|
this.readList,
|
||||||
this.padding,
|
this.padding,
|
||||||
this.textPadding = const EdgeInsets.symmetric(
|
this.textPadding = const EdgeInsets.symmetric(
|
||||||
@@ -133,6 +133,7 @@ class MessageWidget extends StatefulWidget {
|
|||||||
messageTheme: messageTheme,
|
messageTheme: messageTheme,
|
||||||
onShowMessage: onShowMessage,
|
onShowMessage: onShowMessage,
|
||||||
onReturnAction: onReturnAction,
|
onReturnAction: onReturnAction,
|
||||||
|
onAttachmentTap: onAttachmentTap,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
border,
|
border,
|
||||||
@@ -214,6 +215,11 @@ class MessageWidget extends StatefulWidget {
|
|||||||
),
|
),
|
||||||
onShowMessage: onShowMessage,
|
onShowMessage: onShowMessage,
|
||||||
onReturnAction: onReturnAction,
|
onReturnAction: onReturnAction,
|
||||||
|
onAttachmentTap: onAttachmentTap != null
|
||||||
|
? () {
|
||||||
|
onAttachmentTap(message, attachment);
|
||||||
|
}
|
||||||
|
: null,
|
||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
),
|
),
|
||||||
@@ -243,6 +249,11 @@ class MessageWidget extends StatefulWidget {
|
|||||||
mediaQueryData.size.width * 0.8,
|
mediaQueryData.size.width * 0.8,
|
||||||
mediaQueryData.size.height * 0.3,
|
mediaQueryData.size.height * 0.3,
|
||||||
),
|
),
|
||||||
|
onAttachmentTap: onAttachmentTap != null
|
||||||
|
? () {
|
||||||
|
onAttachmentTap(message, attachment);
|
||||||
|
}
|
||||||
|
: null,
|
||||||
),
|
),
|
||||||
border,
|
border,
|
||||||
reverse,
|
reverse,
|
||||||
@@ -395,6 +406,9 @@ class MessageWidget extends StatefulWidget {
|
|||||||
/// Builder for respective attachment types
|
/// Builder for respective attachment types
|
||||||
final Map<String, AttachmentBuilder> attachmentBuilders;
|
final Map<String, AttachmentBuilder> attachmentBuilders;
|
||||||
|
|
||||||
|
/// Builder for respective attachment types (user facing builder)
|
||||||
|
final Map<String, AttachmentBuilder>? customAttachmentBuilders;
|
||||||
|
|
||||||
/// Center user avatar with bottom of the message
|
/// Center user avatar with bottom of the message
|
||||||
final bool translateUserAvatar;
|
final bool translateUserAvatar;
|
||||||
|
|
||||||
@@ -519,7 +533,7 @@ class MessageWidget extends StatefulWidget {
|
|||||||
showPinButton: showPinButton ?? this.showPinButton,
|
showPinButton: showPinButton ?? this.showPinButton,
|
||||||
showPinHighlight: showPinHighlight ?? this.showPinHighlight,
|
showPinHighlight: showPinHighlight ?? this.showPinHighlight,
|
||||||
customAttachmentBuilders:
|
customAttachmentBuilders:
|
||||||
customAttachmentBuilders ?? attachmentBuilders,
|
customAttachmentBuilders ?? this.customAttachmentBuilders,
|
||||||
translateUserAvatar: translateUserAvatar ?? this.translateUserAvatar,
|
translateUserAvatar: translateUserAvatar ?? this.translateUserAvatar,
|
||||||
onQuotedMessageTap: onQuotedMessageTap ?? this.onQuotedMessageTap,
|
onQuotedMessageTap: onQuotedMessageTap ?? this.onQuotedMessageTap,
|
||||||
onMessageTap: onMessageTap ?? this.onMessageTap,
|
onMessageTap: onMessageTap ?? this.onMessageTap,
|
||||||
@@ -567,11 +581,11 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
bool get isOnlyEmoji => widget.message.text?.isOnlyEmoji == true;
|
bool get isOnlyEmoji => widget.message.text?.isOnlyEmoji == true;
|
||||||
|
|
||||||
bool get hasNonUrlAttachments => widget.message.attachments
|
bool get hasNonUrlAttachments => widget.message.attachments
|
||||||
.where((it) => it.ogScrapeUrl == null)
|
.where((it) => it.titleLink == null || it.type == 'giphy')
|
||||||
.isNotEmpty;
|
.isNotEmpty;
|
||||||
|
|
||||||
bool get hasUrlAttachments =>
|
bool get hasUrlAttachments => widget.message.attachments
|
||||||
widget.message.attachments.any((it) => it.ogScrapeUrl != null) == true;
|
.any((it) => it.titleLink != null && it.type != 'giphy');
|
||||||
|
|
||||||
bool get showBottomRow =>
|
bool get showBottomRow =>
|
||||||
showThreadReplyIndicator ||
|
showThreadReplyIndicator ||
|
||||||
@@ -984,9 +998,9 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
|
|
||||||
Widget _buildUrlAttachment() {
|
Widget _buildUrlAttachment() {
|
||||||
final urlAttachment = widget.message.attachments
|
final urlAttachment = widget.message.attachments
|
||||||
.firstWhere((element) => element.ogScrapeUrl != null);
|
.firstWhere((element) => element.titleLink != null);
|
||||||
|
|
||||||
final host = Uri.parse(urlAttachment.ogScrapeUrl!).host;
|
final host = Uri.parse(urlAttachment.titleLink!).host;
|
||||||
final splitList = host.split('.');
|
final splitList = host.split('.');
|
||||||
final hostName = splitList.length == 3 ? splitList[1] : splitList[0];
|
final hostName = splitList.length == 3 ? splitList[1] : splitList[0];
|
||||||
final hostDisplayName = urlAttachment.authorName?.capitalize() ??
|
final hostDisplayName = urlAttachment.authorName?.capitalize() ??
|
||||||
@@ -997,6 +1011,7 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
urlAttachment: urlAttachment,
|
urlAttachment: urlAttachment,
|
||||||
hostDisplayName: hostDisplayName,
|
hostDisplayName: hostDisplayName,
|
||||||
textPadding: widget.textPadding,
|
textPadding: widget.textPadding,
|
||||||
|
messageTheme: widget.messageTheme,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1155,7 +1170,9 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
final attachmentGroups = <String, List<Attachment>>{};
|
final attachmentGroups = <String, List<Attachment>>{};
|
||||||
|
|
||||||
widget.message.attachments
|
widget.message.attachments
|
||||||
.where((element) => element.ogScrapeUrl == null && element.type != null)
|
.where((element) =>
|
||||||
|
(element.titleLink == null && element.type != null) ||
|
||||||
|
element.type == 'giphy')
|
||||||
.forEach((e) {
|
.forEach((e) {
|
||||||
if (attachmentGroups[e.type] == null) {
|
if (attachmentGroups[e.type] == null) {
|
||||||
attachmentGroups[e.type!] = [];
|
attachmentGroups[e.type!] = [];
|
||||||
@@ -1335,7 +1352,7 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (hasUrlAttachments) {
|
if (hasUrlAttachments) {
|
||||||
return _streamChatTheme.colorTheme.linkBg;
|
return widget.messageTheme.linkBackgroundColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isOnlyEmoji) {
|
if (isOnlyEmoji) {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user