diff --git a/.gitignore b/.gitignore index 8b124cef..b10d3c5a 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 test.properties 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..7495bd19 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 @@ -30,9 +30,14 @@ class File( /** * Represents the content of various types of events emitted during chat operations. * This is a sealed class with different subclasses representing specific types of events. + * + * @property customMessageType the `customMessageType` that will be passed as a parameter into + * [com.pubnub.api.PubNub.publish] or [com.pubnub.api.PubNub.signal] when sending this event. */ @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 +125,7 @@ sealed class EventContent { @Serializable @SerialName("moderation") class Moderation(val channelId: String, val restriction: RestrictionType, val reason: String? = null) : - EventContent() + EventContent(CUSTOM_MESSAGE_TYPE_MODERATED) /** * Represents a text message event, containing the message text and any associated files. @@ -189,3 +194,5 @@ enum class EmitEventMethod { */ PUBLISH } + +private const val CUSTOM_MESSAGE_TYPE_MODERATED = "moderated" 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 28cafa06..cfc4bcc7 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..641145e7 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 = "PUBNUB_INTERNAL_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 db516485..edceb621 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 @@ -710,7 +710,7 @@ class ChannelIntegrationTest : BaseChatIntegrationTest() { val channel01withChat = channel01 val join01 = channel01withChat.join { }.await() val join02 = channel01Chat02.join { }.await() - delayInMillis(1000) + delayInMillis(1500) val whoIsPresent01: Collection = channel01withChat.whoIsPresent().await() val whoIsPresent02: Collection = channel01Chat02.whoIsPresent().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 cabab6b9..4e77dd5d 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 @@ -621,10 +622,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..677cd5d3 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 = "moderated") } 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 {