Skip to content

Commit 5f0e6e8

Browse files
authored
Allow users to delete messages only for themselves (Delete message for me - feature) (#5967)
* Add `deletedForMe` property to Message model to track message deletion status for the current user * Add `deletedForMe` property to MessageEntity and update database version * Add `deleteMessageForMe` support to ChatClient for user-specific message deletion * Add support for `deleteForMe` parameter in message deletion API and tests * Show "Only visible to you" label only when message is not deleted for me * Add DeleteMessageForMeComponentFactory for user-specific message deletion * Add deletedForMe property to MessageDeletedEvent and update related mappings * Add DeletedForMeDecorator and related functionality for user-specific message deletion in XML SDK * Tests * CHANGELOG * Update kdocs * Introduce DeleteMessageForMeListenerState * Introduce DeleteMessageForMeListenerDatabase * Add withDeletedForMe method to Message builder * Implement retry logic for deleting messages marked as deleted for the user
1 parent 4308a93 commit 5f0e6e8

File tree

68 files changed

+1231
-80
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+1231
-80
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
### ⬆️ Improved
1717

1818
### ✅ Added
19+
- Add support for deleting messages only for the current user. [#5967](https://github.com/GetStream/stream-chat-android/pull/5967)
1920

2021
### ⚠️ Changed
2122

@@ -27,6 +28,7 @@
2728
### ⬆️ Improved
2829

2930
### ✅ Added
31+
- Add support for deleting messages only for the current user. [#5967](https://github.com/GetStream/stream-chat-android/pull/5967)
3032

3133
### ⚠️ Changed
3234

stream-chat-android-client-test/src/main/java/io/getstream/chat/android/client/test/utils/TestDataHelper.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,7 @@ public class TestDataHelper {
383383
message = message1Deleted,
384384
hardDelete = false,
385385
channelMessageCount = 1,
386+
deletedForMe = false,
386387
)
387388
}
388389

@@ -400,6 +401,7 @@ public class TestDataHelper {
400401
message = message1Deleted,
401402
hardDelete = true,
402403
channelMessageCount = 1,
404+
deletedForMe = false,
403405
)
404406
}
405407

stream-chat-android-client/api/stream-chat-android-client.api

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ public final class io/getstream/chat/android/client/ChatClient {
5353
public final fun deleteMessage (Ljava/lang/String;)Lio/getstream/result/call/Call;
5454
public final fun deleteMessage (Ljava/lang/String;Z)Lio/getstream/result/call/Call;
5555
public static synthetic fun deleteMessage$default (Lio/getstream/chat/android/client/ChatClient;Ljava/lang/String;ZILjava/lang/Object;)Lio/getstream/result/call/Call;
56+
public final fun deleteMessageForMe (Ljava/lang/String;)Lio/getstream/result/call/Call;
5657
public final fun deletePoll (Ljava/lang/String;)Lio/getstream/result/call/Call;
5758
public final fun deleteReaction (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lio/getstream/result/call/Call;
5859
public static synthetic fun deleteReaction$default (Lio/getstream/chat/android/client/ChatClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lio/getstream/result/call/Call;
@@ -1583,9 +1584,10 @@ public final class io/getstream/chat/android/client/events/MemberUpdatedEvent :
15831584
}
15841585

15851586
public final class io/getstream/chat/android/client/events/MessageDeletedEvent : io/getstream/chat/android/client/events/CidEvent, io/getstream/chat/android/client/events/HasMessage {
1586-
public fun <init> (Ljava/lang/String;Ljava/util/Date;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/getstream/chat/android/models/Message;Lio/getstream/chat/android/models/User;ZLjava/lang/Integer;)V
1587+
public fun <init> (Ljava/lang/String;Ljava/util/Date;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/getstream/chat/android/models/Message;Lio/getstream/chat/android/models/User;ZLjava/lang/Integer;Z)V
15871588
public final fun component1 ()Ljava/lang/String;
15881589
public final fun component10 ()Ljava/lang/Integer;
1590+
public final fun component11 ()Z
15891591
public final fun component2 ()Ljava/util/Date;
15901592
public final fun component3 ()Ljava/lang/String;
15911593
public final fun component4 ()Ljava/lang/String;
@@ -1594,14 +1596,15 @@ public final class io/getstream/chat/android/client/events/MessageDeletedEvent :
15941596
public final fun component7 ()Lio/getstream/chat/android/models/Message;
15951597
public final fun component8 ()Lio/getstream/chat/android/models/User;
15961598
public final fun component9 ()Z
1597-
public final fun copy (Ljava/lang/String;Ljava/util/Date;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/getstream/chat/android/models/Message;Lio/getstream/chat/android/models/User;ZLjava/lang/Integer;)Lio/getstream/chat/android/client/events/MessageDeletedEvent;
1598-
public static synthetic fun copy$default (Lio/getstream/chat/android/client/events/MessageDeletedEvent;Ljava/lang/String;Ljava/util/Date;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/getstream/chat/android/models/Message;Lio/getstream/chat/android/models/User;ZLjava/lang/Integer;ILjava/lang/Object;)Lio/getstream/chat/android/client/events/MessageDeletedEvent;
1599+
public final fun copy (Ljava/lang/String;Ljava/util/Date;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/getstream/chat/android/models/Message;Lio/getstream/chat/android/models/User;ZLjava/lang/Integer;Z)Lio/getstream/chat/android/client/events/MessageDeletedEvent;
1600+
public static synthetic fun copy$default (Lio/getstream/chat/android/client/events/MessageDeletedEvent;Ljava/lang/String;Ljava/util/Date;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/getstream/chat/android/models/Message;Lio/getstream/chat/android/models/User;ZLjava/lang/Integer;ZILjava/lang/Object;)Lio/getstream/chat/android/client/events/MessageDeletedEvent;
15991601
public fun equals (Ljava/lang/Object;)Z
16001602
public fun getChannelId ()Ljava/lang/String;
16011603
public final fun getChannelMessageCount ()Ljava/lang/Integer;
16021604
public fun getChannelType ()Ljava/lang/String;
16031605
public fun getCid ()Ljava/lang/String;
16041606
public fun getCreatedAt ()Ljava/util/Date;
1607+
public final fun getDeletedForMe ()Z
16051608
public final fun getHardDelete ()Z
16061609
public fun getMessage ()Lio/getstream/chat/android/models/Message;
16071610
public fun getRawCreatedAt ()Ljava/lang/String;
@@ -2962,7 +2965,7 @@ public abstract interface class io/getstream/chat/android/client/persistance/rep
29622965
public abstract fun createRepositoryFactory (Lio/getstream/chat/android/models/User;)Lio/getstream/chat/android/client/persistance/repository/factory/RepositoryFactory;
29632966
}
29642967

2965-
public abstract interface class io/getstream/chat/android/client/plugin/Plugin : io/getstream/chat/android/client/plugin/DependencyResolver, io/getstream/chat/android/client/plugin/listeners/BlockUserListener, io/getstream/chat/android/client/plugin/listeners/ChannelMarkReadListener, io/getstream/chat/android/client/plugin/listeners/CreateChannelListener, io/getstream/chat/android/client/plugin/listeners/DeleteChannelListener, io/getstream/chat/android/client/plugin/listeners/DeleteMessageListener, io/getstream/chat/android/client/plugin/listeners/DeleteReactionListener, io/getstream/chat/android/client/plugin/listeners/DraftMessageListener, io/getstream/chat/android/client/plugin/listeners/EditMessageListener, io/getstream/chat/android/client/plugin/listeners/FetchCurrentUserListener, io/getstream/chat/android/client/plugin/listeners/GetMessageListener, io/getstream/chat/android/client/plugin/listeners/HideChannelListener, io/getstream/chat/android/client/plugin/listeners/LiveLocationListener, io/getstream/chat/android/client/plugin/listeners/MarkAllReadListener, io/getstream/chat/android/client/plugin/listeners/PushPreferencesListener, io/getstream/chat/android/client/plugin/listeners/QueryBlockedUsersListener, io/getstream/chat/android/client/plugin/listeners/QueryChannelListener, io/getstream/chat/android/client/plugin/listeners/QueryChannelsListener, io/getstream/chat/android/client/plugin/listeners/QueryMembersListener, io/getstream/chat/android/client/plugin/listeners/QueryThreadsListener, io/getstream/chat/android/client/plugin/listeners/SendAttachmentListener, io/getstream/chat/android/client/plugin/listeners/SendGiphyListener, io/getstream/chat/android/client/plugin/listeners/SendMessageListener, io/getstream/chat/android/client/plugin/listeners/SendReactionListener, io/getstream/chat/android/client/plugin/listeners/ShuffleGiphyListener, io/getstream/chat/android/client/plugin/listeners/ThreadQueryListener, io/getstream/chat/android/client/plugin/listeners/TypingEventListener, io/getstream/chat/android/client/plugin/listeners/UnblockUserListener {
2968+
public abstract interface class io/getstream/chat/android/client/plugin/Plugin : io/getstream/chat/android/client/plugin/DependencyResolver, io/getstream/chat/android/client/plugin/listeners/BlockUserListener, io/getstream/chat/android/client/plugin/listeners/ChannelMarkReadListener, io/getstream/chat/android/client/plugin/listeners/CreateChannelListener, io/getstream/chat/android/client/plugin/listeners/DeleteChannelListener, io/getstream/chat/android/client/plugin/listeners/DeleteMessageForMeListener, io/getstream/chat/android/client/plugin/listeners/DeleteMessageListener, io/getstream/chat/android/client/plugin/listeners/DeleteReactionListener, io/getstream/chat/android/client/plugin/listeners/DraftMessageListener, io/getstream/chat/android/client/plugin/listeners/EditMessageListener, io/getstream/chat/android/client/plugin/listeners/FetchCurrentUserListener, io/getstream/chat/android/client/plugin/listeners/GetMessageListener, io/getstream/chat/android/client/plugin/listeners/HideChannelListener, io/getstream/chat/android/client/plugin/listeners/LiveLocationListener, io/getstream/chat/android/client/plugin/listeners/MarkAllReadListener, io/getstream/chat/android/client/plugin/listeners/PushPreferencesListener, io/getstream/chat/android/client/plugin/listeners/QueryBlockedUsersListener, io/getstream/chat/android/client/plugin/listeners/QueryChannelListener, io/getstream/chat/android/client/plugin/listeners/QueryChannelsListener, io/getstream/chat/android/client/plugin/listeners/QueryMembersListener, io/getstream/chat/android/client/plugin/listeners/QueryThreadsListener, io/getstream/chat/android/client/plugin/listeners/SendAttachmentListener, io/getstream/chat/android/client/plugin/listeners/SendGiphyListener, io/getstream/chat/android/client/plugin/listeners/SendMessageListener, io/getstream/chat/android/client/plugin/listeners/SendReactionListener, io/getstream/chat/android/client/plugin/listeners/ShuffleGiphyListener, io/getstream/chat/android/client/plugin/listeners/ThreadQueryListener, io/getstream/chat/android/client/plugin/listeners/TypingEventListener, io/getstream/chat/android/client/plugin/listeners/UnblockUserListener {
29662969
public fun getErrorHandler ()Lio/getstream/chat/android/client/errorhandler/ErrorHandler;
29672970
public fun onAttachmentSendRequest (Ljava/lang/String;Ljava/lang/String;Lio/getstream/chat/android/models/Message;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
29682971
public static synthetic fun onAttachmentSendRequest$suspendImpl (Lio/getstream/chat/android/client/plugin/Plugin;Ljava/lang/String;Ljava/lang/String;Lio/getstream/chat/android/models/Message;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
@@ -3108,6 +3111,15 @@ public abstract interface class io/getstream/chat/android/client/plugin/listener
31083111
public abstract fun onDeleteChannelResult (Ljava/lang/String;Ljava/lang/String;Lio/getstream/result/Result;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
31093112
}
31103113

3114+
public abstract interface class io/getstream/chat/android/client/plugin/listeners/DeleteMessageForMeListener {
3115+
public fun onDeleteMessageForMePrecondition (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
3116+
public static synthetic fun onDeleteMessageForMePrecondition$suspendImpl (Lio/getstream/chat/android/client/plugin/listeners/DeleteMessageForMeListener;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
3117+
public fun onDeleteMessageForMeRequest (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
3118+
public static synthetic fun onDeleteMessageForMeRequest$suspendImpl (Lio/getstream/chat/android/client/plugin/listeners/DeleteMessageForMeListener;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
3119+
public fun onDeleteMessageForMeResult (Ljava/lang/String;Lio/getstream/result/Result;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
3120+
public static synthetic fun onDeleteMessageForMeResult$suspendImpl (Lio/getstream/chat/android/client/plugin/listeners/DeleteMessageForMeListener;Ljava/lang/String;Lio/getstream/result/Result;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
3121+
}
3122+
31113123
public abstract interface class io/getstream/chat/android/client/plugin/listeners/DeleteMessageListener {
31123124
public abstract fun onMessageDeletePrecondition (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
31133125
public abstract fun onMessageDeleteRequest (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;

stream-chat-android-client/src/main/java/io/getstream/chat/android/client/ChatClient.kt

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import io.getstream.chat.android.client.api.models.SendActionRequest
4040
import io.getstream.chat.android.client.api.models.identifier.AddDeviceIdentifier
4141
import io.getstream.chat.android.client.api.models.identifier.ConnectUserIdentifier
4242
import io.getstream.chat.android.client.api.models.identifier.DeleteDeviceIdentifier
43+
import io.getstream.chat.android.client.api.models.identifier.DeleteMessageForMeIdentifier
4344
import io.getstream.chat.android.client.api.models.identifier.DeleteMessageIdentifier
4445
import io.getstream.chat.android.client.api.models.identifier.DeleteReactionIdentifier
4546
import io.getstream.chat.android.client.api.models.identifier.GetDevicesIdentifier
@@ -2186,8 +2187,7 @@ internal constructor(
21862187
@JvmOverloads
21872188
public fun deleteMessage(messageId: String, hard: Boolean = false): Call<Message> {
21882189
logger.d { "[deleteMessage] messageId: $messageId, hard: $hard" }
2189-
2190-
return api.deleteMessage(messageId, hard)
2190+
return api.deleteMessage(messageId, hard, deleteForMe = false)
21912191
.doOnStart(userScope) {
21922192
plugins.forEach { listener ->
21932193
logger.v { "[deleteMessage] #doOnStart; plugin: ${listener::class.qualifiedName}" }
@@ -2206,6 +2206,33 @@ internal constructor(
22062206
.share(userScope) { DeleteMessageIdentifier(messageId, hard) }
22072207
}
22082208

2209+
/**
2210+
* Deletes a message for the current user only, making it invisible for them while keeping it visible for others.
2211+
*
2212+
* @param messageId The ID of the message to be deleted.
2213+
*/
2214+
@CheckResult
2215+
public fun deleteMessageForMe(messageId: String): Call<Message> {
2216+
logger.d { "[deleteMessageForMe] messageId: $messageId" }
2217+
return api.deleteMessage(messageId, hard = false, deleteForMe = true)
2218+
.doOnStart(userScope) {
2219+
plugins.forEach { listener ->
2220+
logger.v { "[deleteMessageForMe] #doOnStart; plugin: ${listener::class.qualifiedName}" }
2221+
listener.onDeleteMessageForMeRequest(messageId)
2222+
}
2223+
}
2224+
.doOnResult(userScope) { result ->
2225+
plugins.forEach { listener ->
2226+
logger.v { "[deleteMessageForMe] #doOnResult; plugin: ${listener::class.qualifiedName}" }
2227+
listener.onDeleteMessageForMeResult(messageId, result)
2228+
}
2229+
}
2230+
.precondition(plugins) {
2231+
onDeleteMessageForMePrecondition(messageId)
2232+
}
2233+
.share(userScope) { DeleteMessageForMeIdentifier(messageId) }
2234+
}
2235+
22092236
/**
22102237
* Fetches a single message from the backend.
22112238
*

stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api/ChatApi.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,11 @@ internal interface ChatApi {
181181
fun deleteReaction(messageId: String, reactionType: String): Call<Message>
182182

183183
@CheckResult
184-
fun deleteMessage(messageId: String, hard: Boolean = false): Call<Message>
184+
fun deleteMessage(
185+
messageId: String,
186+
hard: Boolean,
187+
deleteForMe: Boolean,
188+
): Call<Message>
185189

186190
@CheckResult
187191
fun sendAction(request: SendActionRequest): Call<Message>

stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api/QueryParams.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,5 @@ package io.getstream.chat.android.client.api
1818

1919
internal object QueryParams {
2020
internal const val CONNECTION_ID = "connection_id"
21-
internal const val HARD_DELETE = "hard"
2221
internal const val URL = "url"
2322
}

stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api/models/identifier/Identifiers.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,18 @@ internal fun DeleteMessageIdentifier(
196196
return result
197197
}
198198

199+
/**
200+
* Identifier for a [ChatClient.deleteMessageForMe] call.
201+
*/
202+
@Suppress("FunctionName", "MagicNumber")
203+
internal fun DeleteMessageForMeIdentifier(
204+
messageId: String,
205+
): Int {
206+
var result = "DeleteMessageForMe".hashCode()
207+
result = 31 * result + messageId.hashCode()
208+
return result
209+
}
210+
199211
/**
200212
* Identifier for [ChatClient.keystroke] and [ChatClient.stopTyping] calls.
201213
*/

stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/MoshiChatApi.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -370,10 +370,15 @@ constructor(
370370
return messageApi.getMessage(messageId).mapDomain { it.toDomain() }
371371
}
372372

373-
override fun deleteMessage(messageId: String, hard: Boolean): Call<Message> {
373+
override fun deleteMessage(
374+
messageId: String,
375+
hard: Boolean,
376+
deleteForMe: Boolean,
377+
): Call<Message> {
374378
return messageApi.deleteMessage(
375379
messageId = messageId,
376380
hard = if (hard) true else null,
381+
deleteForMe = if (deleteForMe) true else null,
377382
).mapDomain { response ->
378383
response.message.toDomain()
379384
}

stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/endpoint/MessageApi.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
package io.getstream.chat.android.client.api2.endpoint
1818

1919
import io.getstream.chat.android.client.api.AuthenticatedApi
20-
import io.getstream.chat.android.client.api.QueryParams
2120
import io.getstream.chat.android.client.api2.model.requests.PartialUpdateMessageRequest
2221
import io.getstream.chat.android.client.api2.model.requests.QueryDraftMessagesRequest
2322
import io.getstream.chat.android.client.api2.model.requests.QueryDraftsRequest
@@ -105,7 +104,8 @@ internal interface MessageApi {
105104
@DELETE("/messages/{id}")
106105
fun deleteMessage(
107106
@Path("id") messageId: String,
108-
@Query(QueryParams.HARD_DELETE) hard: Boolean?,
107+
@Query("hard") hard: Boolean?,
108+
@Query("delete_for_me") deleteForMe: Boolean?,
109109
): RetrofitCall<MessageResponse>
110110

111111
@POST("/messages/{id}/action")

stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/mapping/DomainMapping.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ internal class DomainMapping(
242242
reminder = reminder?.toDomain(),
243243
sharedLocation = shared_location?.toDomain(),
244244
channelRole = member?.channel_role,
245+
deletedForMe = deleted_for_me ?: false,
245246
extraData = extraData.toMutableMap(),
246247
).let(messageTransformer::transform)
247248
}

0 commit comments

Comments
 (0)