refactor: deprecate MessageSendingStatus in favor of MessageState.
Signed-off-by: xsahil03x <[email protected]>
This commit is contained in:
@@ -582,7 +582,7 @@ class Channel {
|
||||
// Eg. Updating the message while the previous call is in progress.
|
||||
_messageAttachmentsUploadCompleter
|
||||
.remove(message.id)
|
||||
?.completeError('Message Cancelled');
|
||||
?.completeError(const StreamChatError('Message deleted'));
|
||||
|
||||
final quotedMessage = state!.messages.firstWhereOrNull(
|
||||
(m) => m.id == message.quotedMessageId,
|
||||
@@ -592,7 +592,7 @@ class Channel {
|
||||
localCreatedAt: DateTime.now(),
|
||||
user: _client.state.currentUser,
|
||||
quotedMessage: quotedMessage,
|
||||
status: MessageSendingStatus.sending,
|
||||
state: MessageState.sending,
|
||||
attachments: message.attachments.map(
|
||||
(it) {
|
||||
if (it.uploadState.isSuccess) return it;
|
||||
@@ -630,15 +630,24 @@ class Channel {
|
||||
),
|
||||
);
|
||||
|
||||
final sentMessage = response.message.syncWith(message);
|
||||
final sentMessage = response.message.syncWith(message).copyWith(
|
||||
// Update the message state to updated.
|
||||
state: MessageState.sent,
|
||||
);
|
||||
|
||||
state!.updateMessage(sentMessage);
|
||||
if (cooldown > 0) cooldownStartedAt = DateTime.now();
|
||||
return response;
|
||||
} catch (e) {
|
||||
if (e is StreamChatNetworkError && e.isRetriable) {
|
||||
state!._retryQueue.add([message]);
|
||||
state!._retryQueue.add([
|
||||
message.copyWith(
|
||||
// Update the message state to failed.
|
||||
state: MessageState.sendingFailed,
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
@@ -653,17 +662,18 @@ class Channel {
|
||||
Message message, {
|
||||
bool skipEnrichUrl = false,
|
||||
}) async {
|
||||
_checkInitialized();
|
||||
final originalMessage = message;
|
||||
|
||||
// Cancelling previous completer in case it's called again in the process
|
||||
// Eg. Updating the message while the previous call is in progress.
|
||||
_messageAttachmentsUploadCompleter
|
||||
.remove(message.id)
|
||||
?.completeError('Message Cancelled');
|
||||
?.completeError(const StreamChatError('Message deleted'));
|
||||
|
||||
// ignore: parameter_assignments
|
||||
message = message.copyWith(
|
||||
status: MessageSendingStatus.updating,
|
||||
state: MessageState.updating,
|
||||
localUpdatedAt: DateTime.now(),
|
||||
attachments: message.attachments.map(
|
||||
(it) {
|
||||
@@ -699,19 +709,30 @@ class Channel {
|
||||
),
|
||||
);
|
||||
|
||||
final updatedMessage = response.message
|
||||
.syncWith(message)
|
||||
.copyWith(ownReactions: message.ownReactions);
|
||||
final updateMessage = response.message.syncWith(message).copyWith(
|
||||
// Update the message state to updated.
|
||||
state: MessageState.updated,
|
||||
ownReactions: message.ownReactions,
|
||||
);
|
||||
|
||||
state?.updateMessage(updatedMessage);
|
||||
state?.updateMessage(updateMessage);
|
||||
|
||||
return response;
|
||||
} catch (e) {
|
||||
if (e is StreamChatNetworkError) {
|
||||
if (e.isRetriable) {
|
||||
state!._retryQueue.add([message]);
|
||||
state!._retryQueue.add([
|
||||
message.copyWith(
|
||||
// Update the message state to failed.
|
||||
state: MessageState.updatingFailed,
|
||||
),
|
||||
]);
|
||||
} else {
|
||||
state?.updateMessage(originalMessage);
|
||||
// Reset the message to original state if the update fails and is not
|
||||
// retriable.
|
||||
state?.updateMessage(originalMessage.copyWith(
|
||||
state: MessageState.updatingFailed,
|
||||
));
|
||||
}
|
||||
}
|
||||
rethrow;
|
||||
@@ -729,6 +750,23 @@ class Channel {
|
||||
List<String>? unset,
|
||||
bool skipEnrichUrl = false,
|
||||
}) async {
|
||||
_checkInitialized();
|
||||
final originalMessage = message;
|
||||
|
||||
// Cancelling previous completer in case it's called again in the process
|
||||
// Eg. Updating the message while the previous call is in progress.
|
||||
_messageAttachmentsUploadCompleter
|
||||
.remove(message.id)
|
||||
?.completeError(const StreamChatError('Message deleted'));
|
||||
|
||||
// ignore: parameter_assignments
|
||||
message = message.copyWith(
|
||||
state: MessageState.updating,
|
||||
localUpdatedAt: DateTime.now(),
|
||||
);
|
||||
|
||||
state?.updateMessage(message);
|
||||
|
||||
try {
|
||||
// Wait for the previous update call to finish. Otherwise, the order of
|
||||
// messages will not be maintained.
|
||||
@@ -741,17 +779,33 @@ class Channel {
|
||||
),
|
||||
);
|
||||
|
||||
final updatedMessage = response.message
|
||||
.syncWith(message)
|
||||
.copyWith(ownReactions: message.ownReactions);
|
||||
final updatedMessage = response.message.syncWith(message).copyWith(
|
||||
// Update the message state to updated.
|
||||
state: MessageState.updated,
|
||||
ownReactions: message.ownReactions,
|
||||
);
|
||||
|
||||
state?.updateMessage(updatedMessage);
|
||||
|
||||
return response;
|
||||
} catch (e) {
|
||||
if (e is StreamChatNetworkError && e.isRetriable) {
|
||||
state!._retryQueue.add([message]);
|
||||
if (e is StreamChatNetworkError) {
|
||||
if (e.isRetriable) {
|
||||
state!._retryQueue.add([
|
||||
message.copyWith(
|
||||
// Update the message state to failed.
|
||||
state: MessageState.updatingFailed,
|
||||
),
|
||||
]);
|
||||
} else {
|
||||
// Reset the message to original state if the update fails and is not
|
||||
// retriable.
|
||||
state?.updateMessage(originalMessage.copyWith(
|
||||
state: MessageState.updatingFailed,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
@@ -759,39 +813,49 @@ class Channel {
|
||||
final _deleteMessageLock = Lock();
|
||||
|
||||
/// Deletes the [message] from the channel.
|
||||
Future<EmptyResponse> deleteMessage(Message message, {bool? hard}) async {
|
||||
final hardDelete = hard ?? false;
|
||||
Future<EmptyResponse> deleteMessage(
|
||||
Message message, {
|
||||
bool hard = false,
|
||||
}) async {
|
||||
_checkInitialized();
|
||||
|
||||
// Directly deleting the local messages which are not yet sent to server
|
||||
if (message.status == MessageSendingStatus.sending ||
|
||||
message.status == MessageSendingStatus.failed) {
|
||||
// Directly deleting the local messages which are not yet sent to server.
|
||||
final isSynced = message.state.isCompleted ||
|
||||
message.state.isUpdating ||
|
||||
message.state.isDeleting ||
|
||||
message.state.isUpdatingFailed ||
|
||||
message.state.isDeletingFailed;
|
||||
|
||||
if (!isSynced) {
|
||||
state!.deleteMessage(
|
||||
message.copyWith(
|
||||
type: 'deleted',
|
||||
localDeletedAt: DateTime.now(),
|
||||
status: MessageSendingStatus.sent,
|
||||
state: MessageState.deleted(hard: hard),
|
||||
),
|
||||
hardDelete: hardDelete,
|
||||
hardDelete: hard,
|
||||
);
|
||||
|
||||
// Removing the attachments upload completer to stop the `sendMessage`
|
||||
// waiting for attachments to complete.
|
||||
_messageAttachmentsUploadCompleter
|
||||
.remove(message.id)
|
||||
?.completeError(Exception('Message deleted'));
|
||||
?.completeError(const StreamChatError('Message deleted'));
|
||||
|
||||
// Returning empty response to mark the api call as success.
|
||||
return EmptyResponse();
|
||||
}
|
||||
|
||||
// ignore: parameter_assignments
|
||||
message = message.copyWith(
|
||||
type: 'deleted',
|
||||
deletedAt: DateTime.now(),
|
||||
state: MessageState.deleting(hard: hard),
|
||||
);
|
||||
|
||||
state?.deleteMessage(message, hardDelete: hard);
|
||||
|
||||
try {
|
||||
// ignore: parameter_assignments
|
||||
message = message.copyWith(
|
||||
type: 'deleted',
|
||||
status: MessageSendingStatus.deleting,
|
||||
deletedAt: message.deletedAt ?? DateTime.now(),
|
||||
);
|
||||
|
||||
state?.deleteMessage(message, hardDelete: hardDelete);
|
||||
|
||||
// Wait for the previous delete call to finish. Otherwise, the order of
|
||||
// messages will not be maintained.
|
||||
final response = await _deleteMessageLock.synchronized(
|
||||
@@ -799,15 +863,20 @@ class Channel {
|
||||
);
|
||||
|
||||
final deletedMessage = message.copyWith(
|
||||
status: MessageSendingStatus.sent,
|
||||
state: MessageState.deleted(hard: hard),
|
||||
);
|
||||
|
||||
state?.deleteMessage(deletedMessage, hardDelete: hardDelete);
|
||||
state?.deleteMessage(deletedMessage, hardDelete: hard);
|
||||
|
||||
return response;
|
||||
} catch (e) {
|
||||
if (e is StreamChatNetworkError && e.isRetriable) {
|
||||
state!._retryQueue.add([message]);
|
||||
state!._retryQueue.add([
|
||||
message.copyWith(
|
||||
// Update the message state to failed.
|
||||
state: MessageState.deletingFailed(hard: hard),
|
||||
),
|
||||
]);
|
||||
}
|
||||
rethrow;
|
||||
}
|
||||
@@ -1877,15 +1946,7 @@ class ChannelClientState {
|
||||
/// Retry failed message.
|
||||
Future<void> retryFailedMessages() async {
|
||||
final failedMessages = [...messages, ...threads.values.expand((v) => v)]
|
||||
.where(
|
||||
(message) =>
|
||||
message.status != MessageSendingStatus.sent &&
|
||||
message.createdAt.isBefore(
|
||||
DateTime.now().subtract(const Duration(seconds: 5)),
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
|
||||
.where((it) => it.state.isFailed);
|
||||
_retryQueue.add(failedMessages);
|
||||
}
|
||||
|
||||
|
||||
@@ -109,8 +109,9 @@ class StreamChatClient {
|
||||
|
||||
_retryPolicy = retryPolicy ??
|
||||
RetryPolicy(
|
||||
shouldRetry: (_, attempt, __) => attempt < 5,
|
||||
retryTimeout: (_, attempt, __) => Duration(seconds: attempt),
|
||||
shouldRetry: (_, __, error) {
|
||||
return error is StreamChatNetworkError && error.isRetriable;
|
||||
},
|
||||
);
|
||||
|
||||
state = ClientState(this);
|
||||
@@ -1397,12 +1398,19 @@ class StreamChatClient {
|
||||
);
|
||||
|
||||
/// Deletes the given message
|
||||
Future<EmptyResponse> deleteMessage(String messageId, {bool? hard}) async {
|
||||
final response =
|
||||
await _chatApi.message.deleteMessage(messageId, hard: hard);
|
||||
if (hard == true) {
|
||||
Future<EmptyResponse> deleteMessage(
|
||||
String messageId, {
|
||||
bool hard = false,
|
||||
}) async {
|
||||
final response = await _chatApi.message.deleteMessage(
|
||||
messageId,
|
||||
hard: hard,
|
||||
);
|
||||
|
||||
if (hard) {
|
||||
await chatPersistenceClient?.deleteMessageById(messageId);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,27 +1,54 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:stream_chat/src/client/client.dart';
|
||||
import 'package:stream_chat/src/core/error/error.dart';
|
||||
|
||||
/// The retry options
|
||||
/// When sending/updating/deleting a message any temporary error will trigger the retry policy
|
||||
/// The retry policy exposes 2 methods
|
||||
/// - shouldRetry: returns a boolean if the request should be retried
|
||||
/// - retryTimeout: How many milliseconds to wait till the next attempt
|
||||
/// The retry policy associated to a client.
|
||||
///
|
||||
/// maxRetryAttempts is a hard limit on maximum retry attempts before giving up
|
||||
/// This policy is used to determine if a request should be retried and when.
|
||||
///
|
||||
/// also see:
|
||||
/// - [RetryQueue]
|
||||
class RetryPolicy {
|
||||
/// Instantiate a new RetryPolicy
|
||||
RetryPolicy({
|
||||
required this.shouldRetry,
|
||||
required this.retryTimeout,
|
||||
@Deprecated("Use 'delayFactor' instead.") this.retryTimeout,
|
||||
this.maxRetryAttempts = 6,
|
||||
this.delayFactor = const Duration(milliseconds: 200),
|
||||
this.randomizationFactor = 0.25,
|
||||
this.maxDelay = const Duration(seconds: 30),
|
||||
});
|
||||
|
||||
/// Hard limit on maximum retry attempts before giving up, defaults to 6
|
||||
/// Resets once connection recovers.
|
||||
/// Delay factor to double after every attempt.
|
||||
///
|
||||
/// Defaults to 200 ms, which results in the following delays:
|
||||
///
|
||||
/// 1. 400 ms
|
||||
/// 2. 800 ms
|
||||
/// 3. 1600 ms
|
||||
/// 4. 3200 ms
|
||||
/// 5. 6400 ms
|
||||
/// 6. 12800 ms
|
||||
///
|
||||
/// Before application of [randomizationFactor].
|
||||
final Duration delayFactor;
|
||||
|
||||
/// Percentage the delay should be randomized, given as fraction between
|
||||
/// 0 and 1.
|
||||
///
|
||||
/// If [randomizationFactor] is `0.25` (default) this indicates 25 % of the
|
||||
/// delay should be increased or decreased by 25 %.
|
||||
final double randomizationFactor;
|
||||
|
||||
/// Maximum delay between retries, defaults to 30 seconds.
|
||||
final Duration maxDelay;
|
||||
|
||||
/// Maximum number of attempts before giving up, defaults to 6.
|
||||
final int maxRetryAttempts;
|
||||
|
||||
/// This function evaluates if we should retry the failure
|
||||
final bool Function(
|
||||
/// Function to determine if a retry should be attempted.
|
||||
final FutureOr<bool> Function(
|
||||
StreamChatClient client,
|
||||
int attempt,
|
||||
StreamChatError? error,
|
||||
@@ -29,9 +56,10 @@ class RetryPolicy {
|
||||
|
||||
/// In the case that we want to retry a failed request the retryTimeout
|
||||
/// method is called to determine the timeout
|
||||
@Deprecated("Use 'delayFactor' instead.")
|
||||
final Duration Function(
|
||||
StreamChatClient client,
|
||||
int attempt,
|
||||
StreamChatError? error,
|
||||
) retryTimeout;
|
||||
)? retryTimeout;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ class RetryQueue {
|
||||
}) : client = channel.client {
|
||||
_retryPolicy = client.retryPolicy;
|
||||
_listenConnectionRecovered();
|
||||
_listenFailedEvents();
|
||||
_listenMessageEvents();
|
||||
}
|
||||
|
||||
/// The channel of this queue.
|
||||
@@ -31,153 +31,105 @@ class RetryQueue {
|
||||
final _compositeSubscription = CompositeSubscription();
|
||||
|
||||
final _messageQueue = HeapPriorityQueue(_byDate);
|
||||
bool _isRetrying = false;
|
||||
|
||||
void _listenConnectionRecovered() {
|
||||
client.on(EventType.connectionRecovered).listen((event) {
|
||||
client.on(EventType.connectionRecovered).distinct().listen((event) {
|
||||
if (event.online == true) {
|
||||
_startRetrying();
|
||||
logger?.info('Connection recovered, retrying failed messages');
|
||||
channel.state?.retryFailedMessages();
|
||||
}
|
||||
}).addTo(_compositeSubscription);
|
||||
}
|
||||
|
||||
void _listenFailedEvents() {
|
||||
void _listenMessageEvents() {
|
||||
channel.on().where((event) => event.message != null).listen((event) {
|
||||
final message = event.message!;
|
||||
final containsMessage = _messageQueue.containsMessage(message);
|
||||
if (!containsMessage) return;
|
||||
if (message.status == MessageSendingStatus.sent) {
|
||||
|
||||
if (message.state.isCompleted) {
|
||||
logger?.info('Removing sent message from queue : ${message.id}');
|
||||
_messageQueue.removeMessage(message);
|
||||
return;
|
||||
} else {
|
||||
if ([
|
||||
MessageSendingStatus.failed_update,
|
||||
MessageSendingStatus.failed,
|
||||
MessageSendingStatus.failed_delete,
|
||||
].contains(message.status)) {
|
||||
logger?.info('Adding failed message from event : ${event.type}');
|
||||
add([message]);
|
||||
}
|
||||
return _messageQueue.removeMessage(message);
|
||||
}
|
||||
}).addTo(_compositeSubscription);
|
||||
}
|
||||
|
||||
/// Add a list of messages.
|
||||
void add(List<Message> messages) {
|
||||
if (messages.isEmpty) return;
|
||||
if (!_messageQueue.containsAllMessage(messages)) {
|
||||
logger?.info('Adding ${messages.length} messages');
|
||||
final messageList = _messageQueue.toList();
|
||||
// we should not add message if already available in the queue
|
||||
_messageQueue.addAll(messages.where(
|
||||
(it) => !messageList.any((m) => m.id == it.id),
|
||||
));
|
||||
}
|
||||
void add(Iterable<Message> messages) {
|
||||
assert(
|
||||
messages.every((it) => it.state.isFailed),
|
||||
'Only failed messages can be added to the queue',
|
||||
);
|
||||
|
||||
_startRetrying();
|
||||
// Filter out messages that are already in the queue.
|
||||
final messagesToAdd = messages.where((it) {
|
||||
return !_messageQueue.containsMessage(it);
|
||||
});
|
||||
|
||||
// If there are no messages to add, return.
|
||||
if (messagesToAdd.isEmpty) return;
|
||||
|
||||
logger?.info('Adding ${messagesToAdd.length} messages to the queue');
|
||||
_messageQueue.addAll(messagesToAdd);
|
||||
|
||||
_processQueue();
|
||||
}
|
||||
|
||||
Future<void> _startRetrying() async {
|
||||
if (_isRetrying) return;
|
||||
_isRetrying = true;
|
||||
bool _isProcessing = false;
|
||||
|
||||
Future<void> _processQueue() async {
|
||||
if (_isProcessing) return;
|
||||
_isProcessing = true;
|
||||
|
||||
logger?.info('Started retrying failed messages');
|
||||
while (_messageQueue.isNotEmpty) {
|
||||
logger?.info('${_messageQueue.length} messages remaining in the queue');
|
||||
|
||||
final message = _messageQueue.first;
|
||||
final succeeded = await _runAndRetry(message);
|
||||
if (!succeeded) {
|
||||
_messageQueue.toList().forEach(_sendFailedEvent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
_isRetrying = false;
|
||||
}
|
||||
|
||||
Future<bool> _runAndRetry(Message message) async {
|
||||
var attempt = 1;
|
||||
|
||||
final maxAttempt = _retryPolicy.maxRetryAttempts;
|
||||
// early return in case maxAttempt is less than 0
|
||||
if (attempt > maxAttempt) return false;
|
||||
|
||||
// ignore: literal_only_boolean_expressions
|
||||
while (true) {
|
||||
final retryPolicy = _retryPolicy;
|
||||
try {
|
||||
logger?.info('Message (${message.id}) retry attempt $attempt');
|
||||
await _retryMessage(message);
|
||||
logger?.info('Message (${message.id}) sent successfully');
|
||||
_messageQueue.removeMessage(message);
|
||||
return true;
|
||||
} catch (e) {
|
||||
if (e is! StreamChatNetworkError || !e.isRetriable) {
|
||||
_messageQueue.removeMessage(message);
|
||||
_sendFailedEvent(message);
|
||||
return true;
|
||||
}
|
||||
// retry logic
|
||||
final maxAttempt = _retryPolicy.maxRetryAttempts;
|
||||
if (attempt < maxAttempt) {
|
||||
final shouldRetry = _retryPolicy.shouldRetry(client, attempt, e);
|
||||
if (shouldRetry) {
|
||||
final timeout = _retryPolicy.retryTimeout(client, attempt, e);
|
||||
// temporary failure, continue
|
||||
logger?.info(
|
||||
'API call failed (attempt $attempt), '
|
||||
'retrying in ${timeout.inSeconds} seconds. Error was $e',
|
||||
);
|
||||
await Future.delayed(timeout);
|
||||
attempt += 1;
|
||||
} else {
|
||||
logger?.info(
|
||||
'API call failed (attempt $attempt). '
|
||||
'Giving up for now, will retry when connection recovers. '
|
||||
'Error was $e',
|
||||
);
|
||||
_sendFailedEvent(message);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
logger?.info(
|
||||
'API call failed (attempt $attempt). '
|
||||
'Exceeds maxRetryAttempt : $maxAttempt '
|
||||
'Giving up for now, will retry when connection recovers. '
|
||||
'Error was $e',
|
||||
);
|
||||
_sendFailedEvent(message);
|
||||
break;
|
||||
}
|
||||
await backOff(
|
||||
() => _retryMessage(message),
|
||||
delayFactor: retryPolicy.delayFactor,
|
||||
randomizationFactor: retryPolicy.randomizationFactor,
|
||||
maxDelay: retryPolicy.maxDelay,
|
||||
maxAttempts: retryPolicy.maxRetryAttempts,
|
||||
retryIf: (error, attempt) {
|
||||
if (error is! StreamChatError) return false;
|
||||
return retryPolicy.shouldRetry(client, attempt, error);
|
||||
},
|
||||
);
|
||||
} catch (error) {
|
||||
logger?.severe('Error while retrying message ${message.id}', error);
|
||||
// If we are unable to successfully retry the message, update the state
|
||||
// with the failed state.
|
||||
channel.state?.updateMessage(message);
|
||||
} finally {
|
||||
// remove the message from the queue after it's handled.
|
||||
_messageQueue.removeFirst();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
_isProcessing = false;
|
||||
}
|
||||
|
||||
void _sendFailedEvent(Message message) {
|
||||
final newStatus = message.status == MessageSendingStatus.sending
|
||||
? MessageSendingStatus.failed
|
||||
: message.status == MessageSendingStatus.updating
|
||||
? MessageSendingStatus.failed_update
|
||||
: MessageSendingStatus.failed_delete;
|
||||
channel.state?.updateMessage(message.copyWith(status: newStatus));
|
||||
}
|
||||
|
||||
Future<void> _retryMessage(Message message) async {
|
||||
if (message.status == MessageSendingStatus.failed_update ||
|
||||
message.status == MessageSendingStatus.updating) {
|
||||
await channel.updateMessage(message);
|
||||
} else if (message.status == MessageSendingStatus.failed ||
|
||||
message.status == MessageSendingStatus.sending) {
|
||||
await channel.sendMessage(message);
|
||||
} else if (message.status == MessageSendingStatus.failed_delete ||
|
||||
message.status == MessageSendingStatus.deleting) {
|
||||
await channel.deleteMessage(message);
|
||||
}
|
||||
Future<Object> _retryMessage(Message message) async {
|
||||
return message.state.maybeWhen(
|
||||
failed: (state, _) => state.when(
|
||||
sendingFailed: () => channel.sendMessage(message),
|
||||
updatingFailed: () => channel.updateMessage(message),
|
||||
deletingFailed: (hard) => channel.deleteMessage(message, hard: hard),
|
||||
),
|
||||
orElse: () => throw StateError('Message state is not failed'),
|
||||
);
|
||||
}
|
||||
|
||||
/// Whether our [_messageQueue] has messages or not.
|
||||
bool get hasMessages => _messageQueue.isNotEmpty;
|
||||
|
||||
/// Returns true if the queue contains the given [message].
|
||||
bool contains(Message message) => _messageQueue.containsMessage(message);
|
||||
|
||||
/// Call this method to dispose this object.
|
||||
void dispose() {
|
||||
_messageQueue.clear();
|
||||
@@ -188,33 +140,25 @@ class RetryQueue {
|
||||
final date1 = _getMessageDate(m1);
|
||||
final date2 = _getMessageDate(m2);
|
||||
|
||||
if (date1 == null || date2 == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (date1 == null && date2 == null) return 0;
|
||||
if (date1 == null) return -1;
|
||||
if (date2 == null) return 1;
|
||||
return date1.compareTo(date2);
|
||||
}
|
||||
|
||||
static DateTime? _getMessageDate(Message m1) {
|
||||
switch (m1.status) {
|
||||
case MessageSendingStatus.failed_delete:
|
||||
case MessageSendingStatus.deleting:
|
||||
return m1.deletedAt;
|
||||
|
||||
case MessageSendingStatus.failed:
|
||||
case MessageSendingStatus.sending:
|
||||
return m1.createdAt;
|
||||
|
||||
case MessageSendingStatus.failed_update:
|
||||
case MessageSendingStatus.updating:
|
||||
return m1.updatedAt;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
static DateTime? _getMessageDate(Message message) {
|
||||
return message.state.maybeWhen(
|
||||
failed: (state, _) => state.when(
|
||||
sendingFailed: () => message.createdAt,
|
||||
updatingFailed: () => message.updatedAt,
|
||||
deletingFailed: (_) => message.deletedAt,
|
||||
),
|
||||
orElse: () => null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension _MessageHeapPriorityQueue on HeapPriorityQueue<Message> {
|
||||
extension on HeapPriorityQueue<Message> {
|
||||
void removeMessage(Message message) {
|
||||
final list = toUnorderedList();
|
||||
final index = list.indexWhere((it) => it.id == message.id);
|
||||
@@ -229,11 +173,4 @@ extension _MessageHeapPriorityQueue on HeapPriorityQueue<Message> {
|
||||
if (index == -1) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool containsAllMessage(List<Message> messages) {
|
||||
if (isEmpty) return false;
|
||||
final list = toUnorderedList();
|
||||
final messageIds = messages.map((it) => it.id);
|
||||
return list.every((it) => messageIds.contains(it.id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:stream_chat/src/core/models/attachment.dart';
|
||||
import 'package:stream_chat/src/core/models/message_state.dart';
|
||||
import 'package:stream_chat/src/core/models/reaction.dart';
|
||||
import 'package:stream_chat/src/core/models/user.dart';
|
||||
import 'package:stream_chat/src/core/util/serializer.dart';
|
||||
@@ -15,6 +16,7 @@ class _NullConst {
|
||||
const _nullConst = _NullConst();
|
||||
|
||||
/// Enum defining the status of a sending message.
|
||||
@Deprecated('Use MessageState instead')
|
||||
enum MessageSendingStatus {
|
||||
/// Message is being sent
|
||||
sending,
|
||||
@@ -37,7 +39,45 @@ enum MessageSendingStatus {
|
||||
failed_delete,
|
||||
|
||||
/// Message correctly sent
|
||||
sent,
|
||||
sent;
|
||||
|
||||
/// Returns a [MessageState] from a [MessageSendingStatus]
|
||||
MessageState toMessageState() {
|
||||
switch (this) {
|
||||
case MessageSendingStatus.sending:
|
||||
return MessageState.sending;
|
||||
case MessageSendingStatus.updating:
|
||||
return MessageState.updating;
|
||||
case MessageSendingStatus.deleting:
|
||||
return MessageState.softDeleting;
|
||||
case MessageSendingStatus.failed:
|
||||
return MessageState.sendingFailed;
|
||||
case MessageSendingStatus.failed_update:
|
||||
return MessageState.updatingFailed;
|
||||
case MessageSendingStatus.failed_delete:
|
||||
return MessageState.softDeletingFailed;
|
||||
case MessageSendingStatus.sent:
|
||||
return MessageState.sent;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a [MessageSendingStatus] from a [MessageState].
|
||||
static MessageSendingStatus fromMessageState(MessageState state) {
|
||||
return state.when(
|
||||
initial: () => MessageSendingStatus.sending,
|
||||
outgoing: (it) => it.when(
|
||||
sending: () => MessageSendingStatus.sending,
|
||||
updating: () => MessageSendingStatus.updating,
|
||||
deleting: (_) => MessageSendingStatus.deleting,
|
||||
),
|
||||
completed: (_) => MessageSendingStatus.sent,
|
||||
failed: (it, __) => it.when(
|
||||
sendingFailed: () => MessageSendingStatus.failed,
|
||||
updatingFailed: () => MessageSendingStatus.failed_update,
|
||||
deletingFailed: (_) => MessageSendingStatus.failed_delete,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// The class that contains the information about a message.
|
||||
@@ -75,21 +115,39 @@ class Message extends Equatable {
|
||||
DateTime? pinExpires,
|
||||
this.pinnedBy,
|
||||
this.extraData = const {},
|
||||
this.status = MessageSendingStatus.sending,
|
||||
@Deprecated('Use `state` instead') MessageSendingStatus? status,
|
||||
MessageState? state,
|
||||
this.i18n,
|
||||
}) : id = id ?? const Uuid().v4(),
|
||||
pinExpires = pinExpires?.toUtc(),
|
||||
remoteCreatedAt = createdAt,
|
||||
remoteUpdatedAt = updatedAt,
|
||||
remoteDeletedAt = deletedAt,
|
||||
_quotedMessageId = quotedMessageId;
|
||||
_quotedMessageId = quotedMessageId {
|
||||
var messageState = state ?? const MessageState.initial();
|
||||
// Backward compatibility. TODO: Remove in the next major version
|
||||
if (status != null) {
|
||||
messageState = status.toMessageState();
|
||||
}
|
||||
|
||||
this.state = messageState;
|
||||
}
|
||||
|
||||
/// Create a new instance from JSON.
|
||||
factory Message.fromJson(Map<String, dynamic> json) => _$MessageFromJson(
|
||||
Serializer.moveToExtraDataFromRoot(json, topLevelFields),
|
||||
).copyWith(
|
||||
status: MessageSendingStatus.sent,
|
||||
);
|
||||
factory Message.fromJson(Map<String, dynamic> json) {
|
||||
final message = _$MessageFromJson(
|
||||
Serializer.moveToExtraDataFromRoot(json, topLevelFields),
|
||||
);
|
||||
|
||||
var state = MessageState.sent;
|
||||
if (message.deletedAt != null) {
|
||||
state = MessageState.softDeleted;
|
||||
} else if (message.updatedAt.isAfter(message.createdAt)) {
|
||||
state = MessageState.updated;
|
||||
}
|
||||
|
||||
return message.copyWith(state: state);
|
||||
}
|
||||
|
||||
/// The message ID. This is either created by Stream or set client side when
|
||||
/// the message is added.
|
||||
@@ -99,8 +157,16 @@ class Message extends Equatable {
|
||||
final String? text;
|
||||
|
||||
/// The status of a sending message.
|
||||
@Deprecated('Use `state` instead')
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
final MessageSendingStatus status;
|
||||
MessageSendingStatus get status {
|
||||
return MessageSendingStatus.fromMessageState(state);
|
||||
}
|
||||
|
||||
// TODO: Remove late modifier in the next major version.
|
||||
/// The current state of the message.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
late final MessageState state;
|
||||
|
||||
/// The message type.
|
||||
@JsonKey(includeToJson: false)
|
||||
@@ -316,7 +382,8 @@ class Message extends Equatable {
|
||||
Object? pinExpires = _nullConst,
|
||||
User? pinnedBy,
|
||||
Map<String, Object?>? extraData,
|
||||
MessageSendingStatus? status,
|
||||
@Deprecated('Use `state` instead') MessageSendingStatus? status,
|
||||
MessageState? state,
|
||||
Map<String, String>? i18n,
|
||||
}) {
|
||||
assert(() {
|
||||
@@ -350,6 +417,8 @@ class Message extends Equatable {
|
||||
return true;
|
||||
}(), 'Validate type for quotedMessage');
|
||||
|
||||
final messageState = state ?? status?.toMessageState();
|
||||
|
||||
return Message(
|
||||
id: id ?? this.id,
|
||||
text: text ?? this.text,
|
||||
@@ -386,47 +455,49 @@ class Message extends Equatable {
|
||||
pinExpires == _nullConst ? this.pinExpires : pinExpires as DateTime?,
|
||||
pinnedBy: pinnedBy ?? this.pinnedBy,
|
||||
extraData: extraData ?? this.extraData,
|
||||
status: status ?? this.status,
|
||||
state: messageState ?? this.state,
|
||||
i18n: i18n ?? this.i18n,
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns a new [Message] that is a combination of this message and the
|
||||
/// given [other] message.
|
||||
Message merge(Message other) => copyWith(
|
||||
id: other.id,
|
||||
text: other.text,
|
||||
type: other.type,
|
||||
attachments: other.attachments,
|
||||
mentionedUsers: other.mentionedUsers,
|
||||
silent: other.silent,
|
||||
shadowed: other.shadowed,
|
||||
reactionCounts: other.reactionCounts,
|
||||
reactionScores: other.reactionScores,
|
||||
latestReactions: other.latestReactions,
|
||||
ownReactions: other.ownReactions,
|
||||
parentId: other.parentId,
|
||||
quotedMessage: other.quotedMessage,
|
||||
quotedMessageId: other.quotedMessageId,
|
||||
replyCount: other.replyCount,
|
||||
threadParticipants: other.threadParticipants,
|
||||
showInChannel: other.showInChannel,
|
||||
command: other.command,
|
||||
createdAt: other.remoteCreatedAt,
|
||||
localCreatedAt: other.localCreatedAt,
|
||||
updatedAt: other.remoteUpdatedAt,
|
||||
localUpdatedAt: other.localUpdatedAt,
|
||||
deletedAt: other.remoteDeletedAt,
|
||||
localDeletedAt: other.localDeletedAt,
|
||||
user: other.user,
|
||||
pinned: other.pinned,
|
||||
pinnedAt: other.pinnedAt,
|
||||
pinExpires: other.pinExpires,
|
||||
pinnedBy: other.pinnedBy,
|
||||
extraData: other.extraData,
|
||||
status: other.status,
|
||||
i18n: other.i18n,
|
||||
);
|
||||
Message merge(Message other) {
|
||||
return copyWith(
|
||||
id: other.id,
|
||||
text: other.text,
|
||||
type: other.type,
|
||||
attachments: other.attachments,
|
||||
mentionedUsers: other.mentionedUsers,
|
||||
silent: other.silent,
|
||||
shadowed: other.shadowed,
|
||||
reactionCounts: other.reactionCounts,
|
||||
reactionScores: other.reactionScores,
|
||||
latestReactions: other.latestReactions,
|
||||
ownReactions: other.ownReactions,
|
||||
parentId: other.parentId,
|
||||
quotedMessage: other.quotedMessage,
|
||||
quotedMessageId: other.quotedMessageId,
|
||||
replyCount: other.replyCount,
|
||||
threadParticipants: other.threadParticipants,
|
||||
showInChannel: other.showInChannel,
|
||||
command: other.command,
|
||||
createdAt: other.remoteCreatedAt,
|
||||
localCreatedAt: other.localCreatedAt,
|
||||
updatedAt: other.remoteUpdatedAt,
|
||||
localUpdatedAt: other.localUpdatedAt,
|
||||
deletedAt: other.remoteDeletedAt,
|
||||
localDeletedAt: other.localDeletedAt,
|
||||
user: other.user,
|
||||
pinned: other.pinned,
|
||||
pinnedAt: other.pinnedAt,
|
||||
pinExpires: other.pinExpires,
|
||||
pinnedBy: other.pinnedBy,
|
||||
extraData: other.extraData,
|
||||
state: other.state,
|
||||
i18n: other.i18n,
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns a new [Message] that is [other] with local changes applied to it.
|
||||
///
|
||||
@@ -482,7 +553,7 @@ class Message extends Equatable {
|
||||
pinExpires,
|
||||
pinnedBy,
|
||||
extraData,
|
||||
status,
|
||||
state,
|
||||
i18n,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,299 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'message_state.freezed.dart';
|
||||
|
||||
part 'message_state.g.dart';
|
||||
|
||||
/// Helper extension for [MessageState].
|
||||
extension MessageStateX on MessageState {
|
||||
/// Returns true if the message is in initial state.
|
||||
bool get isInitial => this is MessageInitial;
|
||||
|
||||
/// Returns true if the message is in outgoing state.
|
||||
bool get isOutgoing => this is MessageOutgoing;
|
||||
|
||||
/// Returns true if the message is in completed state.
|
||||
bool get isCompleted => this is MessageCompleted;
|
||||
|
||||
/// Returns true if the message is in failed state.
|
||||
bool get isFailed => this is MessageFailed;
|
||||
|
||||
/// Returns true if the message is in outgoing sending state.
|
||||
bool get isSending {
|
||||
final messageState = this;
|
||||
return messageState is MessageOutgoing && messageState.state is Sending;
|
||||
}
|
||||
|
||||
/// Returns true if the message is in outgoing updating state.
|
||||
bool get isUpdating {
|
||||
final messageState = this;
|
||||
return messageState is MessageOutgoing && messageState.state is Updating;
|
||||
}
|
||||
|
||||
/// Returns true if the message is in outgoing deleting state.
|
||||
bool get isDeleting => isSoftDeleting || isHardDeleting;
|
||||
|
||||
/// Returns true if the message is in outgoing soft deleting state.
|
||||
bool get isSoftDeleting {
|
||||
final messageState = this;
|
||||
if (messageState is! MessageOutgoing) return false;
|
||||
|
||||
final outgoingState = messageState.state;
|
||||
if (outgoingState is! Deleting) return false;
|
||||
|
||||
return !outgoingState.hard;
|
||||
}
|
||||
|
||||
/// Returns true if the message is in outgoing hard deleting state.
|
||||
bool get isHardDeleting {
|
||||
final messageState = this;
|
||||
if (messageState is! MessageOutgoing) return false;
|
||||
|
||||
final outgoingState = messageState.state;
|
||||
if (outgoingState is! Deleting) return false;
|
||||
|
||||
return outgoingState.hard;
|
||||
}
|
||||
|
||||
/// Returns true if the message is in completed sent state.
|
||||
bool get isSent {
|
||||
final messageState = this;
|
||||
return messageState is MessageCompleted && messageState.state is Sent;
|
||||
}
|
||||
|
||||
/// Returns true if the message is in completed updated state.
|
||||
bool get isUpdated {
|
||||
final messageState = this;
|
||||
return messageState is MessageCompleted && messageState.state is Updated;
|
||||
}
|
||||
|
||||
/// Returns true if the message is in completed deleted state.
|
||||
bool get isDeleted => isSoftDeleted || isHardDeleted;
|
||||
|
||||
/// Returns true if the message is in completed soft deleted state.
|
||||
bool get isSoftDeleted {
|
||||
final messageState = this;
|
||||
if (messageState is! MessageCompleted) return false;
|
||||
|
||||
final completedState = messageState.state;
|
||||
if (completedState is! Deleted) return false;
|
||||
|
||||
return !completedState.hard;
|
||||
}
|
||||
|
||||
/// Returns true if the message is in completed hard deleted state.
|
||||
bool get isHardDeleted {
|
||||
final messageState = this;
|
||||
if (messageState is! MessageCompleted) return false;
|
||||
|
||||
final completedState = messageState.state;
|
||||
if (completedState is! Deleted) return false;
|
||||
|
||||
return completedState.hard;
|
||||
}
|
||||
|
||||
/// Returns true if the message is in failed sending state.
|
||||
bool get isSendingFailed {
|
||||
final messageState = this;
|
||||
if (messageState is! MessageFailed) return false;
|
||||
return messageState.state is SendingFailed;
|
||||
}
|
||||
|
||||
/// Returns true if the message is in failed updating state.
|
||||
bool get isUpdatingFailed {
|
||||
final messageState = this;
|
||||
if (messageState is! MessageFailed) return false;
|
||||
return messageState.state is UpdatingFailed;
|
||||
}
|
||||
|
||||
/// Returns true if the message is in failed deleting state.
|
||||
bool get isDeletingFailed => isSoftDeletingFailed || isHardDeletingFailed;
|
||||
|
||||
/// Returns true if the message is in failed soft deleting state.
|
||||
bool get isSoftDeletingFailed {
|
||||
final messageState = this;
|
||||
if (messageState is! MessageFailed) return false;
|
||||
|
||||
final failedState = messageState.state;
|
||||
if (failedState is! DeletingFailed) return false;
|
||||
|
||||
return !failedState.hard;
|
||||
}
|
||||
|
||||
/// Returns true if the message is in failed hard deleting state.
|
||||
bool get isHardDeletingFailed {
|
||||
final messageState = this;
|
||||
if (messageState is! MessageFailed) return false;
|
||||
|
||||
final failedState = messageState.state;
|
||||
if (failedState is! DeletingFailed) return false;
|
||||
|
||||
return failedState.hard;
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the various states a message can be in.
|
||||
@freezed
|
||||
class MessageState with _$MessageState {
|
||||
/// Initial state when the message is created.
|
||||
const factory MessageState.initial() = MessageInitial;
|
||||
|
||||
/// Outgoing state when the message is being sent, updated, or deleted.
|
||||
const factory MessageState.outgoing({
|
||||
required OutgoingState state,
|
||||
}) = MessageOutgoing;
|
||||
|
||||
/// Completed state when the message has been successfully sent, updated, or
|
||||
/// deleted.
|
||||
const factory MessageState.completed({
|
||||
required CompletedState state,
|
||||
}) = MessageCompleted;
|
||||
|
||||
/// Failed state when the message fails to be sent, updated, or deleted.
|
||||
const factory MessageState.failed({
|
||||
required FailedState state,
|
||||
Object? reason,
|
||||
}) = MessageFailed;
|
||||
|
||||
/// Creates a new instance from a json
|
||||
factory MessageState.fromJson(Map<String, dynamic> json) =>
|
||||
_$MessageStateFromJson(json);
|
||||
|
||||
/// Deleting state when the message is being deleted.
|
||||
factory MessageState.deleting({required bool hard}) {
|
||||
return MessageState.outgoing(
|
||||
state: OutgoingState.deleting(hard: hard),
|
||||
);
|
||||
}
|
||||
|
||||
/// Deleting state when the message has been successfully deleted.
|
||||
factory MessageState.deleted({required bool hard}) {
|
||||
return MessageState.completed(
|
||||
state: CompletedState.deleted(hard: hard),
|
||||
);
|
||||
}
|
||||
|
||||
/// Deleting failed state when the message fails to be deleted.
|
||||
factory MessageState.deletingFailed({required bool hard}) {
|
||||
return MessageState.failed(
|
||||
state: FailedState.deletingFailed(hard: hard),
|
||||
);
|
||||
}
|
||||
|
||||
/// Sending state when the message is being sent.
|
||||
static const sending = MessageState.outgoing(
|
||||
state: OutgoingState.sending(),
|
||||
);
|
||||
|
||||
/// Updating state when the message is being updated.
|
||||
static const updating = MessageState.outgoing(
|
||||
state: OutgoingState.updating(),
|
||||
);
|
||||
|
||||
/// Deleting state when the message is being soft deleted.
|
||||
static const softDeleting = MessageState.outgoing(
|
||||
state: OutgoingState.deleting(),
|
||||
);
|
||||
|
||||
/// Hard deleting state when the message is being hard deleted.
|
||||
static const hardDeleting = MessageState.outgoing(
|
||||
state: OutgoingState.deleting(hard: true),
|
||||
);
|
||||
|
||||
/// Sent state when the message has been successfully sent.
|
||||
static const sent = MessageState.completed(
|
||||
state: CompletedState.sent(),
|
||||
);
|
||||
|
||||
/// Updated state when the message has been successfully updated.
|
||||
static const updated = MessageState.completed(
|
||||
state: CompletedState.updated(),
|
||||
);
|
||||
|
||||
/// Deleted state when the message has been successfully soft deleted.
|
||||
static const softDeleted = MessageState.completed(
|
||||
state: CompletedState.deleted(),
|
||||
);
|
||||
|
||||
/// Hard deleted state when the message has been successfully hard deleted.
|
||||
static const hardDeleted = MessageState.completed(
|
||||
state: CompletedState.deleted(hard: true),
|
||||
);
|
||||
|
||||
/// Sending failed state when the message fails to be sent.
|
||||
static const sendingFailed = MessageState.failed(
|
||||
state: FailedState.sendingFailed(),
|
||||
);
|
||||
|
||||
/// Updating failed state when the message fails to be updated.
|
||||
static const updatingFailed = MessageState.failed(
|
||||
state: FailedState.updatingFailed(),
|
||||
);
|
||||
|
||||
/// Deleting failed state when the message fails to be soft deleted.
|
||||
static const softDeletingFailed = MessageState.failed(
|
||||
state: FailedState.deletingFailed(),
|
||||
);
|
||||
|
||||
/// Hard deleting failed state when the message fails to be hard deleted.
|
||||
static const hardDeletingFailed = MessageState.failed(
|
||||
state: FailedState.deletingFailed(hard: true),
|
||||
);
|
||||
}
|
||||
|
||||
/// Represents the state of an outgoing message.
|
||||
@freezed
|
||||
class OutgoingState with _$OutgoingState {
|
||||
/// Sending state when the message is being sent.
|
||||
const factory OutgoingState.sending() = Sending;
|
||||
|
||||
/// Updating state when the message is being updated.
|
||||
const factory OutgoingState.updating() = Updating;
|
||||
|
||||
/// Deleting state when the message is being deleted.
|
||||
const factory OutgoingState.deleting({
|
||||
@Default(false) bool hard,
|
||||
}) = Deleting;
|
||||
|
||||
/// Creates a new instance from a json
|
||||
factory OutgoingState.fromJson(Map<String, dynamic> json) =>
|
||||
_$OutgoingStateFromJson(json);
|
||||
}
|
||||
|
||||
/// Represents the completed state of a message.
|
||||
@freezed
|
||||
class CompletedState with _$CompletedState {
|
||||
/// Sent state when the message has been successfully sent.
|
||||
const factory CompletedState.sent() = Sent;
|
||||
|
||||
/// Updated state when the message has been successfully updated.
|
||||
const factory CompletedState.updated() = Updated;
|
||||
|
||||
/// Deleted state when the message has been successfully deleted.
|
||||
const factory CompletedState.deleted({
|
||||
@Default(false) bool hard,
|
||||
}) = Deleted;
|
||||
|
||||
/// Creates a new instance from a json
|
||||
factory CompletedState.fromJson(Map<String, dynamic> json) =>
|
||||
_$CompletedStateFromJson(json);
|
||||
}
|
||||
|
||||
/// Represents the failed state of a message.
|
||||
@freezed
|
||||
class FailedState with _$FailedState {
|
||||
/// Sending failed state when the message fails to be sent.
|
||||
const factory FailedState.sendingFailed() = SendingFailed;
|
||||
|
||||
/// Updating failed state when the message fails to be updated.
|
||||
const factory FailedState.updatingFailed() = UpdatingFailed;
|
||||
|
||||
/// Deleting failed state when the message fails to be deleted.
|
||||
const factory FailedState.deletingFailed({
|
||||
@Default(false) bool hard,
|
||||
}) = DeletingFailed;
|
||||
|
||||
/// Creates a new instance from a json
|
||||
factory FailedState.fromJson(Map<String, dynamic> json) =>
|
||||
_$FailedStateFromJson(json);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,141 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'message_state.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$MessageInitial _$$MessageInitialFromJson(Map<String, dynamic> json) =>
|
||||
_$MessageInitial(
|
||||
$type: json['runtimeType'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$MessageInitialToJson(_$MessageInitial instance) =>
|
||||
<String, dynamic>{
|
||||
'runtimeType': instance.$type,
|
||||
};
|
||||
|
||||
_$MessageOutgoing _$$MessageOutgoingFromJson(Map<String, dynamic> json) =>
|
||||
_$MessageOutgoing(
|
||||
state: OutgoingState.fromJson(json['state'] as Map<String, dynamic>),
|
||||
$type: json['runtimeType'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$MessageOutgoingToJson(_$MessageOutgoing instance) =>
|
||||
<String, dynamic>{
|
||||
'state': instance.state.toJson(),
|
||||
'runtimeType': instance.$type,
|
||||
};
|
||||
|
||||
_$MessageCompleted _$$MessageCompletedFromJson(Map<String, dynamic> json) =>
|
||||
_$MessageCompleted(
|
||||
state: CompletedState.fromJson(json['state'] as Map<String, dynamic>),
|
||||
$type: json['runtimeType'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$MessageCompletedToJson(_$MessageCompleted instance) =>
|
||||
<String, dynamic>{
|
||||
'state': instance.state.toJson(),
|
||||
'runtimeType': instance.$type,
|
||||
};
|
||||
|
||||
_$MessageFailed _$$MessageFailedFromJson(Map<String, dynamic> json) =>
|
||||
_$MessageFailed(
|
||||
state: FailedState.fromJson(json['state'] as Map<String, dynamic>),
|
||||
reason: json['reason'],
|
||||
$type: json['runtimeType'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$MessageFailedToJson(_$MessageFailed instance) =>
|
||||
<String, dynamic>{
|
||||
'state': instance.state.toJson(),
|
||||
'reason': instance.reason,
|
||||
'runtimeType': instance.$type,
|
||||
};
|
||||
|
||||
_$Sending _$$SendingFromJson(Map<String, dynamic> json) => _$Sending(
|
||||
$type: json['runtimeType'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$SendingToJson(_$Sending instance) => <String, dynamic>{
|
||||
'runtimeType': instance.$type,
|
||||
};
|
||||
|
||||
_$Updating _$$UpdatingFromJson(Map<String, dynamic> json) => _$Updating(
|
||||
$type: json['runtimeType'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$UpdatingToJson(_$Updating instance) =>
|
||||
<String, dynamic>{
|
||||
'runtimeType': instance.$type,
|
||||
};
|
||||
|
||||
_$Deleting _$$DeletingFromJson(Map<String, dynamic> json) => _$Deleting(
|
||||
hard: json['hard'] as bool? ?? false,
|
||||
$type: json['runtimeType'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$DeletingToJson(_$Deleting instance) =>
|
||||
<String, dynamic>{
|
||||
'hard': instance.hard,
|
||||
'runtimeType': instance.$type,
|
||||
};
|
||||
|
||||
_$Sent _$$SentFromJson(Map<String, dynamic> json) => _$Sent(
|
||||
$type: json['runtimeType'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$SentToJson(_$Sent instance) => <String, dynamic>{
|
||||
'runtimeType': instance.$type,
|
||||
};
|
||||
|
||||
_$Updated _$$UpdatedFromJson(Map<String, dynamic> json) => _$Updated(
|
||||
$type: json['runtimeType'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$UpdatedToJson(_$Updated instance) => <String, dynamic>{
|
||||
'runtimeType': instance.$type,
|
||||
};
|
||||
|
||||
_$Deleted _$$DeletedFromJson(Map<String, dynamic> json) => _$Deleted(
|
||||
hard: json['hard'] as bool? ?? false,
|
||||
$type: json['runtimeType'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$DeletedToJson(_$Deleted instance) => <String, dynamic>{
|
||||
'hard': instance.hard,
|
||||
'runtimeType': instance.$type,
|
||||
};
|
||||
|
||||
_$SendingFailed _$$SendingFailedFromJson(Map<String, dynamic> json) =>
|
||||
_$SendingFailed(
|
||||
$type: json['runtimeType'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$SendingFailedToJson(_$SendingFailed instance) =>
|
||||
<String, dynamic>{
|
||||
'runtimeType': instance.$type,
|
||||
};
|
||||
|
||||
_$UpdatingFailed _$$UpdatingFailedFromJson(Map<String, dynamic> json) =>
|
||||
_$UpdatingFailed(
|
||||
$type: json['runtimeType'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$UpdatingFailedToJson(_$UpdatingFailed instance) =>
|
||||
<String, dynamic>{
|
||||
'runtimeType': instance.$type,
|
||||
};
|
||||
|
||||
_$DeletingFailed _$$DeletingFailedFromJson(Map<String, dynamic> json) =>
|
||||
_$DeletingFailed(
|
||||
hard: json['hard'] as bool? ?? false,
|
||||
$type: json['runtimeType'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$DeletingFailedToJson(_$DeletingFailed instance) =>
|
||||
<String, dynamic>{
|
||||
'hard': instance.hard,
|
||||
'runtimeType': instance.$type,
|
||||
};
|
||||
@@ -38,6 +38,7 @@ export 'src/core/models/event.dart';
|
||||
export 'src/core/models/filter.dart' show Filter;
|
||||
export 'src/core/models/member.dart';
|
||||
export 'src/core/models/message.dart';
|
||||
export 'src/core/models/message_state.dart';
|
||||
export 'src/core/models/mute.dart';
|
||||
export 'src/core/models/own_user.dart';
|
||||
export 'src/core/models/reaction.dart';
|
||||
|
||||
Reference in New Issue
Block a user