Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import io.getstream.chat.android.client.events.ChannelUpdatedEvent
import io.getstream.chat.android.client.events.ChannelUserBannedEvent
import io.getstream.chat.android.client.events.ChannelVisibleEvent
import io.getstream.chat.android.client.events.ConnectedEvent
import io.getstream.chat.android.client.events.DraftMessageDeletedEvent
import io.getstream.chat.android.client.events.DraftMessageUpdatedEvent
import io.getstream.chat.android.client.events.MarkAllReadEvent
import io.getstream.chat.android.client.events.MemberAddedEvent
import io.getstream.chat.android.client.events.MemberRemovedEvent
Expand Down Expand Up @@ -63,6 +65,7 @@ import io.getstream.chat.android.client.parser2.adapters.internal.StreamDateForm
import io.getstream.chat.android.client.query.QueryChannelsSpec
import io.getstream.chat.android.models.Answer
import io.getstream.chat.android.models.Channel
import io.getstream.chat.android.models.DraftMessage
import io.getstream.chat.android.models.EventType
import io.getstream.chat.android.models.FilterObject
import io.getstream.chat.android.models.Member
Expand All @@ -81,6 +84,7 @@ import io.getstream.chat.android.randomBoolean
import io.getstream.chat.android.randomCID
import io.getstream.chat.android.randomChannel
import io.getstream.chat.android.randomDate
import io.getstream.chat.android.randomDraftMessage
import io.getstream.chat.android.randomInt
import io.getstream.chat.android.randomMember
import io.getstream.chat.android.randomMessage
Expand Down Expand Up @@ -832,6 +836,26 @@ public fun randomPollDeletedEvent(
)
}

public fun randomDraftMessageUpdatedEvent(
createdAt: Date = randomDate(),
draftMessage: DraftMessage = randomDraftMessage(),
): DraftMessageUpdatedEvent = DraftMessageUpdatedEvent(
type = EventType.DRAFT_MESSAGE_UPDATED,
createdAt = createdAt,
rawCreatedAt = streamFormatter.format(createdAt),
draftMessage = draftMessage,
)

public fun randomDraftMessageDeletedEvent(
createdAt: Date = randomDate(),
draftMessage: DraftMessage = randomDraftMessage(),
): DraftMessageDeletedEvent = DraftMessageDeletedEvent(
type = EventType.DRAFT_MESSAGE_DELETED,
createdAt = createdAt,
rawCreatedAt = streamFormatter.format(createdAt),
draftMessage = draftMessage,
)

public fun randomPollUpdatedEvent(
createdAt: Date = randomDate(),
cid: String = randomCID(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2880,6 +2880,12 @@ internal constructor(
return message.ensureId().let { processedDraftMessage ->
api.createDraftMessage(channelType, channelId, processedDraftMessage)
.retry(userScope, retryPolicy)
.doOnStart(userScope) {
logger.v { "[createDraftMessage] #doOnStart; cid: $channelType:$channelId" }
plugins.forEach { listener ->
listener.onCreateDraftMessageRequest(channelType, channelId, processedDraftMessage)
}
}
.doOnResult(userScope) { result ->
logger.i { "[createDraftMessage] result: ${result.stringify { it.toString() }}" }
plugins.forEach { listener ->
Expand Down Expand Up @@ -2908,6 +2914,12 @@ internal constructor(
): Call<Unit> {
return api.deleteDraftMessage(channelType, channelId, message)
.retry(userScope, retryPolicy)
.doOnStart(userScope) {
logger.v { "[deleteDraftMessages] #doOnStart; cid: $channelType:$channelId" }
plugins.forEach { listener ->
listener.onDeleteDraftMessagesRequest(channelType, channelId, message)
}
}
.doOnResult(userScope) { result ->
logger.i { "[deleteDraftMessages] result: ${result.stringify { it.toString() }}" }
plugins.forEach { listener ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

package io.getstream.chat.android.client.internal.offline.plugin.listener.internal

import io.getstream.chat.android.client.errors.isPermanent
import io.getstream.chat.android.client.persistance.repository.MessageRepository
import io.getstream.chat.android.client.plugin.listeners.DraftMessageListener
import io.getstream.chat.android.models.DraftMessage
Expand All @@ -25,15 +24,31 @@ import io.getstream.chat.android.models.FilterObject
import io.getstream.chat.android.models.QueryDraftsResult
import io.getstream.chat.android.models.querysort.QuerySorter
import io.getstream.result.Result
import io.getstream.result.onErrorSuspend
import io.getstream.result.onSuccessSuspend

internal class DraftMessageListenerDatabase(
private val messageRepository: MessageRepository,
) : DraftMessageListener {

/**
* Method called when a request to create a draft message in the API happens
* Method called before the request to create a draft message in the API is launched. Persists the draft upfront so
* it is not lost if the process dies while the request is in flight.
*
* @param channelType The type of the channel
* @param channelId The id of the channel
* @param message The draft message to be created
*/
override suspend fun onCreateDraftMessageRequest(
channelType: String,
channelId: String,
message: DraftMessage,
) {
messageRepository.insertDraftMessage(message)
}

/**
* Method called when a request to create a draft message in the API happens. Replaces the draft persisted by
* [onCreateDraftMessageRequest] with the server copy, leaving it untouched on failure.
*
* @param result The result of the create draft message request
* @param channelType The type of the channel
Expand All @@ -46,17 +61,28 @@ internal class DraftMessageListenerDatabase(
channelId: String,
message: DraftMessage,
) {
result
.onSuccessSuspend { draftMessage -> messageRepository.insertDraftMessage(draftMessage) }
.onErrorSuspend { error ->
message.takeUnless { error.isPermanent() }?.let { draftMessage ->
messageRepository.insertDraftMessage(draftMessage)
}
}
result.onSuccessSuspend { draftMessage -> messageRepository.insertDraftMessage(draftMessage) }
}

/**
* Method called when a request to delete draft messages in the API happens
* Method called before the request to delete draft messages in the API is launched. Removes the draft upfront so it
* stays deleted if the process dies while the request is in flight.
*
* @param channelType The type of the channel
* @param channelId The id of the channel
* @param message The draft message to be deleted
*/
override suspend fun onDeleteDraftMessagesRequest(
channelType: String,
channelId: String,
message: DraftMessage,
) {
messageRepository.deleteDraftMessage(message)
}

/**
* Method called when a request to delete draft messages in the API happens. No-op, as the draft is already removed
* by [onDeleteDraftMessagesRequest].
*
* @param result The result of the delete draft messages request
* @param channelType The type of the channel
Expand All @@ -69,13 +95,7 @@ internal class DraftMessageListenerDatabase(
channelId: String,
message: DraftMessage,
) {
result
.onSuccessSuspend { messageRepository.deleteDraftMessage(message) }
.onErrorSuspend { error ->
message.takeUnless { error.isPermanent() }?.let { draftMessage ->
messageRepository.deleteDraftMessage(draftMessage)
}
}
/* No-Op */
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,13 @@ internal class DatabaseMessageRepository(
messageDao.deleteDraftMessage(message.id)
}

override suspend fun deleteDraftMessage(cid: String, parentId: String?) {
when (parentId) {
null -> messageDao.deleteDraftMessageByCid(cid)
else -> messageDao.deleteDraftMessageByParentId(parentId)
}
}

override suspend fun selectDraftMessages(): List<DraftMessage> = messageDao.selectDraftMessages()
.map { it.toModel(::selectMessage) }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,12 @@ internal interface MessageDao {
@Query("DELETE FROM $DRAFT_MESSAGE_ENTITY_TABLE_NAME WHERE id = :messageId")
suspend fun deleteDraftMessage(messageId: String)

@Query("DELETE FROM $DRAFT_MESSAGE_ENTITY_TABLE_NAME WHERE cid = :cid AND parentId IS NULL")
suspend fun deleteDraftMessageByCid(cid: String)

@Query("DELETE FROM $DRAFT_MESSAGE_ENTITY_TABLE_NAME WHERE parentId = :parentId")
suspend fun deleteDraftMessageByParentId(parentId: String)

@Query(
"SELECT * from $MESSAGE_ENTITY_TABLE_NAME " +
"WHERE cid = :cid " +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -933,6 +933,13 @@ internal class EventHandlerSequential(
is PollDeletedEvent -> {
repos.deletePoll(event.poll.id)
}
is DraftMessageUpdatedEvent -> {
repos.insertDraftMessage(event.draftMessage)
}
is DraftMessageDeletedEvent -> {
// The event carries an empty message id, so the draft is identified by its channel/thread.
repos.deleteDraftMessage(event.draftMessage.cid, event.draftMessage.parentId)
}
is UserMessagesDeletedEvent -> {
deleteMessagesFromUser(event.cid, event.user.id, event.hardDelete, event.createdAt)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

package io.getstream.chat.android.client.internal.state.plugin.listener.internal

import io.getstream.chat.android.client.errors.isPermanent
import io.getstream.chat.android.client.internal.state.plugin.state.global.internal.MutableGlobalState
import io.getstream.chat.android.client.plugin.listeners.DraftMessageListener
import io.getstream.chat.android.models.DraftMessage
Expand All @@ -34,8 +33,24 @@ internal class DraftMessageListenerState(
) : DraftMessageListener {

/**
* Keeps a reference of the [DraftMessage] in the [MutableGlobalState] when the request to create a draft message
* is successful.
* Keeps a reference of the [DraftMessage] in the [MutableGlobalState] before the request to create a draft message
* is launched, so it shows up right away.
*
* @param channelType The type of the channel in which message is created.
* @param channelId The id of the the channel in which message is created.
* @param message [DraftMessage] to be created.
*/
override suspend fun onCreateDraftMessageRequest(
channelType: String,
channelId: String,
message: DraftMessage,
) {
mutableGlobalState.updateDraftMessage(message)
}

/**
* Replaces the reference of the [DraftMessage] in the [MutableGlobalState] with the server copy when the request to
* create a draft message is successful, leaving it untouched on failure.
*
* @param result [Result] response from the original request.
* @param channelType The type of the channel in which message is created.
Expand All @@ -48,18 +63,28 @@ internal class DraftMessageListenerState(
channelId: String,
message: DraftMessage,
) {
result
.onSuccess { draftMessage -> mutableGlobalState.updateDraftMessage(draftMessage) }
.onError { error ->
message.takeUnless { error.isPermanent() }?.let { draftMessage ->
mutableGlobalState.updateDraftMessage(draftMessage)
}
}
result.onSuccess { draftMessage -> mutableGlobalState.updateDraftMessage(draftMessage) }
}

/**
* Removes the reference of the [DraftMessage] from the [MutableGlobalState] when the request to delete
* a draft message is successful.
* Removes the reference of the [DraftMessage] from the [MutableGlobalState] before the request to delete a draft
* message is launched, so it disappears right away.
*
* @param channelType The type of the channel in which message is updated.
* @param channelId The id of the the channel in which message is updated.
* @param message [DraftMessage] to be deleted.
*/
override suspend fun onDeleteDraftMessagesRequest(
channelType: String,
channelId: String,
message: DraftMessage,
) {
mutableGlobalState.removeDraftMessage(message)
}

/**
* Method called when a request to delete draft messages in the API happens. No-op, as the draft is already removed
* by [onDeleteDraftMessagesRequest].
*
* @param result [Result] response from the original request.
* @param channelType The type of the channel in which message is updated.
Expand All @@ -72,13 +97,7 @@ internal class DraftMessageListenerState(
channelId: String,
message: DraftMessage,
) {
result
.onSuccess { mutableGlobalState.removeDraftMessage(message) }
.onError { error ->
message.takeUnless { error.isPermanent() }?.let { draftMessage ->
mutableGlobalState.removeDraftMessage(draftMessage)
}
}
/* No-Op */
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,12 @@ public interface MessageRepository {
*/
public suspend fun deleteDraftMessage(message: DraftMessage)

/**
* Deletes the draft message of a channel, or of a thread when [parentId] is not null. Used when the draft is only
* identified by the channel/thread it belongs to, as happens with the `draft.deleted` event.
*/
public suspend fun deleteDraftMessage(cid: String, parentId: String?)

/**
* Evict messages from the repository.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ internal object NoOpMessageRepository : MessageRepository {
override suspend fun selectMessageBySyncState(syncStatus: SyncStatus): List<Message> = emptyList()
override suspend fun selectMessagesWithPoll(pollId: String): List<Message> = emptyList()
override suspend fun deleteDraftMessage(message: DraftMessage) { /* No-Op */ }
override suspend fun deleteDraftMessage(cid: String, parentId: String?) { /* No-Op */ }
override suspend fun selectDraftMessages(): List<DraftMessage> = emptyList()
override suspend fun selectDraftMessagesByCid(cid: String): DraftMessage? = null
override suspend fun selectDraftMessageByParentId(parentId: String): DraftMessage? = null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,14 @@ public interface Plugin :
/* No-Op */
}

override suspend fun onCreateDraftMessageRequest(
channelType: String,
channelId: String,
message: DraftMessage,
) {
/* No-Op */
}

override suspend fun onCreateDraftMessageResult(
result: Result<DraftMessage>,
channelType: String,
Expand All @@ -455,6 +463,14 @@ public interface Plugin :
/* No-Op */
}

override suspend fun onDeleteDraftMessagesRequest(
channelType: String,
channelId: String,
message: DraftMessage,
) {
/* No-Op */
}

override suspend fun onDeleteDraftMessagesResult(
result: Result<Unit>,
channelType: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,19 @@ import io.getstream.result.Result
@InternalStreamChatApi
public interface DraftMessageListener {

/**
* Side effect to be invoked before the original request is launched.
*
* @param channelType The type of the channel in which message is created.
* @param channelId The id of the the channel in which message is created.
* @param message [DraftMessage] to be created.
*/
public suspend fun onCreateDraftMessageRequest(
channelType: String,
channelId: String,
message: DraftMessage,
)

/**
* Side effect to be invoked when the original request is completed with a response.
*
Expand All @@ -45,6 +58,19 @@ public interface DraftMessageListener {
message: DraftMessage,
)

/**
* Side effect to be invoked before the original request is launched.
*
* @param channelType The type of the channel in which message is updated.
* @param channelId The id of the the channel in which message is updated.
* @param message [DraftMessage] to be deleted.
*/
public suspend fun onDeleteDraftMessagesRequest(
channelType: String,
channelId: String,
message: DraftMessage,
)

/**
* Side effect to be invoked when the original request is completed with a response.
*
Expand Down
Loading
Loading