Skip to content
Open
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
7 changes: 6 additions & 1 deletion app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3975,6 +3975,10 @@ class ChatActivity :
}

override fun onClickReaction(chatMessage: ChatMessage, emoji: String) {
if (!participantPermissions.hasReactPermission()) {
Snackbar.make(binding.root, R.string.reaction_forbidden, Snackbar.LENGTH_LONG).show()
return
}
VibrationUtils.vibrateShort(context)
if (chatMessage.reactionsSelf?.contains(emoji) == true) {
chatViewModel.deleteReaction(roomToken, chatMessage, emoji)
Expand All @@ -3993,7 +3997,7 @@ class ChatActivity :
roomToken,
chatMessage,
conversationUser,
participantPermissions.hasChatPermission(),
participantPermissions.hasReactPermission(),
ncApi
).show()
}
Expand Down Expand Up @@ -4028,6 +4032,7 @@ class ChatActivity :
currentConversation,
isShowMessageDeletionButton(message),
participantPermissions.hasChatPermission(),
participantPermissions.hasReactPermission(),
spreedCapabilities
).show()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ class MessageActionsDialog(
private val currentConversation: ConversationModel?,
private val showMessageDeletionButton: Boolean,
private val hasChatPermission: Boolean,
private val hasReactPermission: Boolean,
private val spreedCapabilities: SpreedCapability
) : BottomSheetDialog(chatActivity) {

Expand Down Expand Up @@ -138,7 +139,7 @@ class MessageActionsDialog(

viewThemeUtils.material.colorBottomSheetBackground(dialogMessageActionsBinding.root)
viewThemeUtils.material.colorBottomSheetDragHandle(dialogMessageActionsBinding.bottomSheetDragHandle)
initEmojiBar(hasChatPermission)
initEmojiBar(hasReactPermission)
initMenuItemCopy(!message.isDeleted)
initMenuItems(networkMonitor.isOnline.value)
}
Expand Down Expand Up @@ -264,9 +265,10 @@ class MessageActionsDialog(
}
}

private fun initEmojiBar(hasChatPermission: Boolean) {
private fun initEmojiBar(hasReactPermission: Boolean) {
if (hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.REACTIONS) &&
isPermitted(hasChatPermission) &&
hasReactPermission &&
isConversationWritable() &&
isReactableMessageType(message)
) {
val recentEmojiManager = RecentEmojiManager(context, MAX_RECENTS)
Expand Down Expand Up @@ -353,9 +355,8 @@ class MessageActionsDialog(
}
}

private fun isPermitted(hasChatPermission: Boolean): Boolean =
hasChatPermission &&
ConversationEnums.ConversationReadOnlyState.CONVERSATION_READ_ONLY !=
private fun isConversationWritable(): Boolean =
ConversationEnums.ConversationReadOnlyState.CONVERSATION_READ_ONLY !=
currentConversation?.conversationReadOnlyState

private fun isReactableMessageType(message: ChatMessage): Boolean =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class ShowReactionsDialog(
private val roomToken: String,
private val chatMessage: ChatMessage,
private val user: User?,
private val hasChatPermission: Boolean,
private val hasReactPermission: Boolean,
private val ncApi: NcApi
) : BottomSheetDialog(activity),
ReactionItemClickListener {
Expand Down Expand Up @@ -185,7 +185,7 @@ class ShowReactionsDialog(
}

override fun onClick(reactionItem: ReactionItem) {
if (hasChatPermission && reactionItem.reactionVoter.actorId?.equals(user?.userId) == true) {
if (hasReactPermission && reactionItem.reactionVoter.actorId?.equals(user?.userId) == true) {
deleteReaction(chatMessage, reactionItem.reaction!!)
adapter?.list?.remove(reactionItem)
dismiss()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ enum class SpreedFeatures(val value: String) {
IMPORTANT_CONVERSATIONS("important-conversations"),
THREADS("threads"),
PINNED_MESSAGES("pinned-messages"),
SCHEDULED_MESSAGES("scheduled-messages")
SCHEDULED_MESSAGES("scheduled-messages"),
REACT_PERMISSION("react-permission")
}

@Suppress("TooManyFunctions")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class ParticipantPermissions(
private val canPublishVideo = (conversation.permissions and PUBLISH_VIDEO) == PUBLISH_VIDEO
val canPublishScreen = (conversation.permissions and PUBLISH_SCREEN) == PUBLISH_SCREEN
private val hasChatPermission = (conversation.permissions and CHAT) == CHAT
private val hasReactPermission = (conversation.permissions and REACT) == REACT

private fun hasConversationPermissions(): Boolean =
CapabilitiesUtil.hasSpreedFeatureCapability(
Expand Down Expand Up @@ -70,6 +71,15 @@ class ParticipantPermissions(
return true
}

fun hasReactPermission(): Boolean {
if (CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.REACT_PERMISSION)) {
// Server supports separate react permission - check REACT bit
return hasReactPermission
}
// Older server without react-permission capability - fall back to chat permission
return hasChatPermission()
}

companion object {

val TAG = ParticipantPermissions::class.simpleName
Expand All @@ -82,5 +92,6 @@ class ParticipantPermissions(
const val PUBLISH_VIDEO = 32
const val PUBLISH_SCREEN = 64
const val CHAT = 128
const val REACT = 256
}
}
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,7 @@ How to translate with transifex:
<string name="read_storage_no_permission">Sharing files from storage is not possible without permissions</string>
<string name="open_in_files_app">Open in Files app</string>
<string name="send_to_forbidden">You are not allowed to share content to this chat</string>
<string name="reaction_forbidden">You are not allowed to add or remove reactions in this conversation</string>

<string name="typing_is_typing">is typing …</string>
<string name="typing_are_typing">are typing …</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,101 @@ class ParticipantPermissionsTest : TestCase() {
assertTrue(attendeePermissions.canPublishVideo())
}

@Test
fun test_reactPermissionWithReactCapability() {
val spreedCapability = SpreedCapability()
// Server with react-permission also supports chat-permission
spreedCapability.features = listOf("chat-permission", "react-permission")
val conversation = createConversation()

// With react-permission capability, only REACT bit matters
conversation.permissions = ParticipantPermissions.REACT or
ParticipantPermissions.CUSTOM

val user = User()
user.id = 1

val attendeePermissions =
ParticipantPermissions(
spreedCapability,
ConversationModel.mapToConversationModel(conversation, user)
)

assertTrue(attendeePermissions.hasReactPermission())
assertFalse(attendeePermissions.hasChatPermission())
}

@Test
fun test_reactPermissionDeniedWithReactCapability() {
val spreedCapability = SpreedCapability()
// Server with react-permission also supports chat-permission
spreedCapability.features = listOf("chat-permission", "react-permission")
val conversation = createConversation()

// With react-permission capability, only CHAT but no REACT - should NOT allow reactions
conversation.permissions = ParticipantPermissions.CHAT or
ParticipantPermissions.CUSTOM

val user = User()
user.id = 1

val attendeePermissions =
ParticipantPermissions(
spreedCapability,
ConversationModel.mapToConversationModel(conversation, user)
)

assertFalse(attendeePermissions.hasReactPermission())
assertTrue(attendeePermissions.hasChatPermission())
}

@Test
fun test_reactPermissionFallbackToChatOnOlderServer() {
val spreedCapability = SpreedCapability()
// Older server without react-permission capability but with chat-permission
spreedCapability.features = listOf("chat-permission")
val conversation = createConversation()

// Only CHAT permission set - should allow reactions as fallback for older servers
conversation.permissions = ParticipantPermissions.CHAT or
ParticipantPermissions.CUSTOM

val user = User()
user.id = 1

val attendeePermissions =
ParticipantPermissions(
spreedCapability,
ConversationModel.mapToConversationModel(conversation, user)
)

assertTrue(attendeePermissions.hasReactPermission())
assertTrue(attendeePermissions.hasChatPermission())
}

@Test
fun test_reactPermissionDeniedOnOlderServerWithoutChatPermission() {
val spreedCapability = SpreedCapability()
// Older server without react-permission capability but with chat-permission
spreedCapability.features = listOf("chat-permission")
val conversation = createConversation()

// No CHAT permission - should deny reactions on older servers
conversation.permissions = ParticipantPermissions.CUSTOM

val user = User()
user.id = 1

val attendeePermissions =
ParticipantPermissions(
spreedCapability,
ConversationModel.mapToConversationModel(conversation, user)
)

assertFalse(attendeePermissions.hasReactPermission())
assertFalse(attendeePermissions.hasChatPermission())
}

private fun createConversation() =
Conversation(
token = "test",
Expand Down
Loading