diff --git a/.gitignore b/.gitignore index ac2b0bbd..befe076e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ build/ !gradle/wrapper/gradle-wrapper.jar !**/src/main/**/build/ !**/src/test/**/build/ -*.podspec +**/*.podspec node_modules js-chat/dist diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c358654d..f6cb71be 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,7 +7,7 @@ dokka = "1.9.20" kotlinx_datetime = "0.6.0" kotlinx_coroutines = "1.8.1" kotlinx_serialization = "1.7.1" -pubnub = "10.1.0" +pubnub = "10.2.0" [libraries] pubnub-kotlin-api = { module = "com.pubnub:pubnub-kotlin-api", version.ref = "pubnub" } diff --git a/pubnub-chat-api/api/pubnub-chat-api.api b/pubnub-chat-api/api/pubnub-chat-api.api index 51a83709..7e3582ec 100644 --- a/pubnub-chat-api/api/pubnub-chat-api.api +++ b/pubnub-chat-api/api/pubnub-chat-api.api @@ -519,6 +519,9 @@ public final class com/pubnub/chat/types/EmitEventMethod : java/lang/Enum { public abstract class com/pubnub/chat/types/EventContent { public static final field Companion Lcom/pubnub/chat/types/EventContent$Companion; public synthetic fun (ILkotlinx/serialization/internal/SerializationConstructorMarker;)V + public synthetic fun (Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun getCustomMessageType ()Ljava/lang/String; public static final synthetic fun write$Self (Lcom/pubnub/chat/types/EventContent;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V } diff --git a/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/types/Types.kt b/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/types/Types.kt index e79ebb38..7c627a29 100644 --- a/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/types/Types.kt +++ b/pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/types/Types.kt @@ -32,7 +32,9 @@ class File( * This is a sealed class with different subclasses representing specific types of events. */ @Serializable -sealed class EventContent { +sealed class EventContent( + @Transient val customMessageType: String? = null +) { /** * Represents a typing event that indicates whether a user is typing. * @@ -120,7 +122,7 @@ sealed class EventContent { @Serializable @SerialName("moderation") class Moderation(val channelId: String, val restriction: RestrictionType, val reason: String? = null) : - EventContent() + EventContent("moderation") /** * Represents a text message event, containing the message text and any associated files. diff --git a/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/ChatImpl.kt b/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/ChatImpl.kt index fe0b0843..68ef9fce 100644 --- a/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/ChatImpl.kt +++ b/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/ChatImpl.kt @@ -497,9 +497,17 @@ class ChatImpl( ): PNFuture { val emitMethod = payload::class.getEmitMethod() ?: (payload as? EventContent.Custom)?.method return if (emitMethod == EmitEventMethod.SIGNAL) { - pubNub.signal(channel = channelId, message = payload.encodeForSending(mergePayloadWith)) + pubNub.signal( + channel = channelId, + message = payload.encodeForSending(mergePayloadWith), + customMessageType = payload.customMessageType + ) } else { - pubNub.publish(channel = channelId, message = payload.encodeForSending(mergePayloadWith)) + pubNub.publish( + channel = channelId, + message = payload.encodeForSending(mergePayloadWith), + customMessageType = payload.customMessageType + ) } } @@ -699,7 +707,7 @@ class ChatImpl( pubNub.removeChannelMembers(channel = channel, uuids = listOf(userId)) .alsoAsync { _ -> emitEvent( - channelId = userId, + channelId = INTERNAL_USER_MODERATION_CHANNEL_PREFIX + userId, payload = EventContent.Moderation( channelId = channel, restriction = RestrictionType.LIFT, @@ -710,16 +718,16 @@ class ChatImpl( } else { val custom = createCustomObject( mapOf( - "ban" to restriction.ban, - "mute" to restriction.mute, - "reason" to restriction.reason + RESTRICTION_BAN to restriction.ban, + RESTRICTION_MUTE to restriction.mute, + RESTRICTION_REASON to restriction.reason ) ) val uuids = listOf(PNMember.Partial(uuidId = userId, custom = custom, null)) pubNub.setChannelMembers(channel = channel, uuids = uuids) .alsoAsync { _ -> emitEvent( - channelId = userId, + channelId = INTERNAL_USER_MODERATION_CHANNEL_PREFIX + userId, payload = EventContent.Moderation( channelId = channel, restriction = if (restriction.ban) { diff --git a/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/Constants.kt b/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/Constants.kt index 6927d31f..e1f3e179 100644 --- a/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/Constants.kt +++ b/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/Constants.kt @@ -16,6 +16,7 @@ internal const val ORIGINAL_PUBLISHER = "originalPublisher" internal const val ORIGINAL_CHANNEL_ID = "originalChannelId" internal const val HTTP_ERROR_404 = 404 internal const val INTERNAL_MODERATION_PREFIX = "PUBNUB_INTERNAL_MODERATION_" +internal const val INTERNAL_USER_MODERATION_CHANNEL_PREFIX = "moderation." internal const val PUBNUB_INTERNAL_AUTOMODERATED = "PUBNUB_INTERNAL_AUTOMODERATED" internal const val INTERNAL_MODERATOR_DATA_ID = "PUBNUB_INTERNAL_MODERATOR" internal const val INTERNAL_MODERATOR_DATA_TYPE = "internal" diff --git a/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/integration/ChannelIntegrationTest.kt b/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/integration/ChannelIntegrationTest.kt index 7ebe6031..9bec7821 100644 --- a/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/integration/ChannelIntegrationTest.kt +++ b/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/integration/ChannelIntegrationTest.kt @@ -44,6 +44,7 @@ class ChannelIntegrationTest : BaseChatIntegrationTest() { @Test fun getPinnedMessage() = runTest { val timetoken = channel01.sendText("Text text text").await() + delayInMillis(250) val message = channel01.getMessage(timetoken.timetoken).await()!! val updatedChannel = channel01.pinMessage(message).await() diff --git a/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/integration/ChatIntegrationTest.kt b/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/integration/ChatIntegrationTest.kt index a46bbce8..2a14ba59 100644 --- a/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/integration/ChatIntegrationTest.kt +++ b/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/integration/ChatIntegrationTest.kt @@ -15,6 +15,7 @@ import com.pubnub.chat.User import com.pubnub.chat.config.ChatConfiguration import com.pubnub.chat.config.PushNotificationsConfig import com.pubnub.chat.internal.ChatImpl +import com.pubnub.chat.internal.INTERNAL_USER_MODERATION_CHANNEL_PREFIX import com.pubnub.chat.internal.UserImpl import com.pubnub.chat.internal.error.PubNubErrorMessage import com.pubnub.chat.internal.utils.cyrb53a @@ -619,10 +620,10 @@ class ChatIntegrationTest : BaseChatIntegrationTest() { val restrictionUnban = Restriction(userId = userId, channelId = channelId, ban = false, mute = false, reason = "ok") pubnub.test(backgroundScope, checkAllEvents = false) { var removeListenerAndUnsubscribe: AutoCloseable? = null - pubnub.awaitSubscribe(channels = listOf(userId)) { + pubnub.awaitSubscribe(channels = listOf(INTERNAL_USER_MODERATION_CHANNEL_PREFIX + userId)) { removeListenerAndUnsubscribe = chat.listenForEvents( type = EventContent.Moderation::class, - channelId = userId + channelId = INTERNAL_USER_MODERATION_CHANNEL_PREFIX + userId ) { event: Event -> val restrictionType: RestrictionType = event.payload.restriction if (restrictionType == RestrictionType.BAN) { diff --git a/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/ChatTest.kt b/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/ChatTest.kt index 240edb93..c57910c7 100644 --- a/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/ChatTest.kt +++ b/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/ChatTest.kt @@ -56,10 +56,12 @@ import com.pubnub.chat.config.ChatConfiguration import com.pubnub.chat.config.PushNotificationsConfig import com.pubnub.chat.internal.ChatImpl import com.pubnub.chat.internal.ChatInternal +import com.pubnub.chat.internal.INTERNAL_USER_MODERATION_CHANNEL_PREFIX import com.pubnub.chat.internal.message.MessageImpl import com.pubnub.chat.internal.timer.TimerManager import com.pubnub.chat.message.GetUnreadMessagesCounts import com.pubnub.chat.restrictions.Restriction +import com.pubnub.chat.restrictions.RestrictionType import com.pubnub.chat.types.ChannelType import com.pubnub.chat.types.EventContent import com.pubnub.chat.types.GetEventsHistoryResult @@ -650,6 +652,31 @@ class ChatTest : BaseTest() { assertEquals(forwardedChannelId, actualForwardedChannelId) } + @Test + fun shouldPassCustomMessageTypeOfEventContentToPublish() { + every { pubnub.publish(any(), any(), any(), any(), any(), any(), any(), any()) } returns publishEndpoint + every { publishEndpoint.async(any()) } calls { (callback1: Consumer>) -> + callback1.accept(Result.success(PNPublishResult(timetoken))) + } + val payload = EventContent.Moderation("a", RestrictionType.BAN, null) + + objectUnderTest.emitEvent( + channelId = channelId, + payload = payload, + ).async { result -> + assertTrue(result.isSuccess) + assertEquals(timetoken, result.getOrNull()?.timetoken) + } + + verify { + pubnub.publish( + channel = channelId, + message = mapOf("type" to "moderation", "channelId" to "a", "restriction" to "banned", "reason" to null), + customMessageType = payload.customMessageType + ) + } + } + @Test fun shouldCalSignalWhenEmitEventWithMethodSignal() { every { pubnub.signal(any(), any()) } returns signalEndpoint @@ -1336,7 +1363,7 @@ class ChatTest : BaseTest() { every { manageChannelMembersEndpoint.async(any()) } calls { (callback: Consumer>) -> callback.accept(Result.success(pnMemberArrayResult)) } - every { pubnub.publish(channel = capture(userIdSlot), message = any()) } returns publishEndpoint + every { pubnub.publish(channel = capture(userIdSlot), message = any(), customMessageType = "moderation") } returns publishEndpoint every { publishEndpoint.async(any()) } calls { (callback1: Consumer>) -> callback1.accept(Result.success(PNPublishResult(timetoken))) } @@ -1350,7 +1377,7 @@ class ChatTest : BaseTest() { val actualModerationEventChannelId = userIdSlot.get() assertEquals(restrictedUserId, actualRestrictedUserId) assertEquals("PUBNUB_INTERNAL_MODERATION_$restrictedChannelId", actualRestrictedChannelId) - assertEquals(restrictedUserId, actualModerationEventChannelId) + assertEquals(INTERNAL_USER_MODERATION_CHANNEL_PREFIX + restrictedUserId, actualModerationEventChannelId) } @Test @@ -1390,7 +1417,8 @@ class ChatTest : BaseTest() { every { pubnub.publish( channel = capture(userIdSlot), - message = capture(encodedMessageSlot) + message = capture(encodedMessageSlot), + customMessageType = any() ) } returns publishEndpoint every { publishEndpoint.async(any()) } calls { (callback1: Consumer>) -> @@ -1409,7 +1437,7 @@ class ChatTest : BaseTest() { assertEquals(reason, actualRestriction.get("reason")) assertEquals("banned", actualEncodedMessageSlot["restriction"]) assertEquals("PUBNUB_INTERNAL_MODERATION_$restrictedChannelId", actualRestrictedChannelId) - assertEquals(restrictedUserId, actualModerationEventChannelId) + assertEquals(INTERNAL_USER_MODERATION_CHANNEL_PREFIX + restrictedUserId, actualModerationEventChannelId) } private fun createMessage(chId: String = channelId, uId: String = userId): Message { diff --git a/pubnub-kotlin b/pubnub-kotlin index 50a9202c..602625a5 160000 --- a/pubnub-kotlin +++ b/pubnub-kotlin @@ -1 +1 @@ -Subproject commit 50a9202c630875c40317d6ead2a42e54b2d06328 +Subproject commit 602625a566eeea80124bbd789bb191c647cf28c9