Skip to content
6 changes: 6 additions & 0 deletions packages/stream_chat/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## Upcoming

🐞 Fixed

- Fixed `skipPush` and `skipEnrichUrl` not preserving during message send or update retry

## 10.0.0-beta.4

🛑️ Breaking
Expand Down
52 changes: 43 additions & 9 deletions packages/stream_chat/lib/src/client/channel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -732,7 +732,10 @@ class Channel {
state!._retryQueue.add([
message.copyWith(
// Update the message state to failed.
state: MessageState.sendingFailed,
state: MessageState.sendingFailed(
skipPush: skipPush,
skipEnrichUrl: skipEnrichUrl,
),
),
]);
}
Expand Down Expand Up @@ -815,14 +818,20 @@ class Channel {
state!._retryQueue.add([
message.copyWith(
// Update the message state to failed.
state: MessageState.updatingFailed,
state: MessageState.updatingFailed(
skipPush: skipPush,
skipEnrichUrl: skipEnrichUrl,
),
),
]);
} else {
// Reset the message to original state if the update fails and is not
// retriable.
state?.updateMessage(originalMessage.copyWith(
state: MessageState.updatingFailed,
state: MessageState.updatingFailed(
skipPush: skipPush,
skipEnrichUrl: skipEnrichUrl,
),
));
}
}
Expand Down Expand Up @@ -885,15 +894,25 @@ class Channel {
state!._retryQueue.add([
message.copyWith(
// Update the message state to failed.
state: MessageState.updatingFailed,
state: MessageState.partialUpdatingFailed(
set: set,
unset: unset,
skipEnrichUrl: skipEnrichUrl,
),
),
]);
} else {
// Reset the message to original state if the update fails and is not
// retriable.
state?.updateMessage(originalMessage.copyWith(
state: MessageState.updatingFailed,
));
state?.updateMessage(
originalMessage.copyWith(
state: MessageState.partialUpdatingFailed(
set: set,
unset: unset,
skipEnrichUrl: skipEnrichUrl,
),
),
);
}
}

Expand Down Expand Up @@ -997,8 +1016,23 @@ class Channel {

return message.state.maybeWhen(
failed: (state, _) => state.when(
sendingFailed: () => sendMessage(message),
updatingFailed: () => updateMessage(message),
sendingFailed: (skipPush, skipEnrichUrl) => sendMessage(
message,
skipPush: skipPush,
skipEnrichUrl: skipEnrichUrl,
),
updatingFailed: (skipPush, skipEnrichUrl) => updateMessage(
message,
skipPush: skipPush,
skipEnrichUrl: skipEnrichUrl,
),
partialUpdatingFailed: (set, unset, skipEnrichUrl) =>
partialUpdateMessage(
message,
set: set,
unset: unset,
skipEnrichUrl: skipEnrichUrl,
),
deletingFailed: (hard) => deleteMessage(message, hard: hard),
),
orElse: () {
Expand Down
9 changes: 5 additions & 4 deletions packages/stream_chat/lib/src/client/retry_queue.dart
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,11 @@ class RetryQueue {
}

static DateTime? _getMessageDate(Message message) {
return message.state.maybeWhen(
failed: (state, _) => state.when(
sendingFailed: () => message.createdAt,
updatingFailed: () => message.updatedAt,
return message.state.maybeMap(
failed: (it) => it.state.map(
sendingFailed: (_) => message.createdAt,
updatingFailed: (_) => message.updatedAt,
partialUpdatingFailed: (_) => message.updatedAt,
deletingFailed: (_) => message.deletedAt,
),
orElse: () => null,
Expand Down
126 changes: 99 additions & 27 deletions packages/stream_chat/lib/src/core/models/message_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,11 @@ extension MessageStateX on MessageState {

/// 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;
return switch (this) {
MessageFailed(state: UpdatingFailed()) => true,
MessageFailed(state: PartialUpdatingFailed()) => true,
_ => false,
};
}

/// Returns true if the message is in failed deleting state.
Expand Down Expand Up @@ -182,6 +184,46 @@ sealed class MessageState with _$MessageState {
);
}

/// Sending failed state when the message fails to be sent.
factory MessageState.sendingFailed({
required bool skipPush,
required bool skipEnrichUrl,
}) {
return MessageState.failed(
state: FailedState.sendingFailed(
skipPush: skipPush,
skipEnrichUrl: skipEnrichUrl,
),
);
}

/// Updating failed state when the message fails to be updated.
factory MessageState.updatingFailed({
required bool skipPush,
required bool skipEnrichUrl,
}) {
return MessageState.failed(
state: FailedState.updatingFailed(
skipPush: skipPush,
skipEnrichUrl: skipEnrichUrl,
),
);
}

factory MessageState.partialUpdatingFailed({
Map<String, Object?>? set,
List<String>? unset,
required bool skipEnrichUrl,
}) {
return MessageState.failed(
state: FailedState.partialUpdatingFailed(
set: set,
unset: unset,
skipEnrichUrl: skipEnrichUrl,
),
);
}

/// Sending state when the message is being sent.
static const sending = MessageState.outgoing(
state: OutgoingState.sending(),
Expand Down Expand Up @@ -222,16 +264,6 @@ sealed class MessageState with _$MessageState {
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(),
Expand Down Expand Up @@ -285,10 +317,22 @@ sealed class CompletedState with _$CompletedState {
@freezed
sealed class FailedState with _$FailedState {
/// Sending failed state when the message fails to be sent.
const factory FailedState.sendingFailed() = SendingFailed;
const factory FailedState.sendingFailed({
@Default(false) bool skipPush,
@Default(false) bool skipEnrichUrl,
}) = SendingFailed;

/// Updating failed state when the message fails to be updated.
const factory FailedState.updatingFailed() = UpdatingFailed;
const factory FailedState.updatingFailed({
@Default(false) bool skipPush,
@Default(false) bool skipEnrichUrl,
}) = UpdatingFailed;

const factory FailedState.partialUpdatingFailed({
Map<String, Object?>? set,
List<String>? unset,
@Default(false) bool skipEnrichUrl,
}) = PartialUpdatingFailed;

/// Deleting failed state when the message fails to be deleted.
const factory FailedState.deletingFailed({
Expand Down Expand Up @@ -616,45 +660,66 @@ extension FailedStatePatternMatching on FailedState {
/// @nodoc
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() sendingFailed,
required TResult Function() updatingFailed,
required TResult Function(bool skipPush, bool skipEnrichUrl) sendingFailed,
required TResult Function(bool skipPush, bool skipEnrichUrl) updatingFailed,
required TResult Function(
Map<String, Object?>? set, List<String>? unset, bool skipEnrichUrl)
partialUpdatingFailed,
required TResult Function(bool hard) deletingFailed,
}) {
final failedState = this;
return switch (failedState) {
SendingFailed() => sendingFailed(),
UpdatingFailed() => updatingFailed(),
SendingFailed() =>
sendingFailed(failedState.skipPush, failedState.skipEnrichUrl),
UpdatingFailed() =>
updatingFailed(failedState.skipPush, failedState.skipEnrichUrl),
PartialUpdatingFailed() => partialUpdatingFailed(
failedState.set, failedState.unset, failedState.skipEnrichUrl),
DeletingFailed() => deletingFailed(failedState.hard),
};
}

/// @nodoc
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? sendingFailed,
TResult? Function()? updatingFailed,
TResult? Function(bool skipPush, bool skipEnrichUrl)? sendingFailed,
TResult? Function(bool skipPush, bool skipEnrichUrl)? updatingFailed,
required TResult Function(
Map<String, Object?>? set, List<String>? unset, bool skipEnrichUrl)
partialUpdatingFailed,
TResult? Function(bool hard)? deletingFailed,
}) {
final failedState = this;
return switch (failedState) {
SendingFailed() => sendingFailed?.call(),
UpdatingFailed() => updatingFailed?.call(),
SendingFailed() =>
sendingFailed?.call(failedState.skipPush, failedState.skipEnrichUrl),
UpdatingFailed() =>
updatingFailed?.call(failedState.skipPush, failedState.skipEnrichUrl),
PartialUpdatingFailed() => partialUpdatingFailed(
failedState.set, failedState.unset, failedState.skipEnrichUrl),
DeletingFailed() => deletingFailed?.call(failedState.hard),
};
}

/// @nodoc
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? sendingFailed,
TResult Function()? updatingFailed,
TResult Function(bool skipPush, bool skipEnrichUrl)? sendingFailed,
TResult Function(bool skipPush, bool skipEnrichUrl)? updatingFailed,
required TResult Function(
Map<String, Object?>? set, List<String>? unset, bool skipEnrichUrl)
partialUpdatingFailed,
TResult Function(bool hard)? deletingFailed,
required TResult orElse(),
}) {
final failedState = this;
final result = switch (failedState) {
SendingFailed() => sendingFailed?.call(),
UpdatingFailed() => updatingFailed?.call(),
SendingFailed() =>
sendingFailed?.call(failedState.skipPush, failedState.skipEnrichUrl),
UpdatingFailed() =>
updatingFailed?.call(failedState.skipPush, failedState.skipEnrichUrl),
PartialUpdatingFailed() => partialUpdatingFailed(
failedState.set, failedState.unset, failedState.skipEnrichUrl),
DeletingFailed() => deletingFailed?.call(failedState.hard),
};

Expand All @@ -666,12 +731,15 @@ extension FailedStatePatternMatching on FailedState {
TResult map<TResult extends Object?>({
required TResult Function(SendingFailed value) sendingFailed,
required TResult Function(UpdatingFailed value) updatingFailed,
required TResult Function(PartialUpdatingFailed value)
partialUpdatingFailed,
required TResult Function(DeletingFailed value) deletingFailed,
}) {
final failedState = this;
return switch (failedState) {
SendingFailed() => sendingFailed(failedState),
UpdatingFailed() => updatingFailed(failedState),
PartialUpdatingFailed() => partialUpdatingFailed(failedState),
DeletingFailed() => deletingFailed(failedState),
};
}
Expand All @@ -681,12 +749,14 @@ extension FailedStatePatternMatching on FailedState {
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(SendingFailed value)? sendingFailed,
TResult? Function(UpdatingFailed value)? updatingFailed,
TResult? Function(PartialUpdatingFailed value)? partialUpdatingFailed,
TResult? Function(DeletingFailed value)? deletingFailed,
}) {
final failedState = this;
return switch (failedState) {
SendingFailed() => sendingFailed?.call(failedState),
UpdatingFailed() => updatingFailed?.call(failedState),
PartialUpdatingFailed() => partialUpdatingFailed?.call(failedState),
DeletingFailed() => deletingFailed?.call(failedState),
};
}
Expand All @@ -696,13 +766,15 @@ extension FailedStatePatternMatching on FailedState {
TResult maybeMap<TResult extends Object?>({
TResult Function(SendingFailed value)? sendingFailed,
TResult Function(UpdatingFailed value)? updatingFailed,
TResult Function(PartialUpdatingFailed value)? partialUpdatingFailed,
TResult Function(DeletingFailed value)? deletingFailed,
required TResult orElse(),
}) {
final failedState = this;
final result = switch (failedState) {
SendingFailed() => sendingFailed?.call(failedState),
UpdatingFailed() => updatingFailed?.call(failedState),
PartialUpdatingFailed() => partialUpdatingFailed?.call(failedState),
DeletingFailed() => deletingFailed?.call(failedState),
};

Expand Down
Loading