Skip to content
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,4 @@ freeline_project_description.json

/gradle/verification-keyring.gpg
/.claude/settings.local.json
/.artifacts/
25 changes: 0 additions & 25 deletions .idea/codeStyles/Project.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ import com.nextcloud.talk.events.ProximitySensorEvent
import com.nextcloud.talk.events.WebSocketCommunicationEvent
import com.nextcloud.talk.models.ExternalSignalingServer
import com.nextcloud.talk.models.domain.ConversationModel
import com.nextcloud.talk.models.domain.ConversationModel.Companion.checkIfVoiceRoom
import com.nextcloud.talk.models.json.capabilities.CapabilitiesOverall
import com.nextcloud.talk.models.json.conversations.Conversation
import com.nextcloud.talk.models.json.conversations.RoomOverall
Expand All @@ -117,6 +116,7 @@ import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.CapabilitiesUtil
import com.nextcloud.talk.utils.CapabilitiesUtil.hasSpreedFeatureCapability
import com.nextcloud.talk.utils.CapabilitiesUtil.isCallRecordingAvailable
import com.nextcloud.talk.utils.ConversationUtils.checkIfVoiceRoom
import com.nextcloud.talk.utils.NotificationUtils.getCallRingtoneUri
import com.nextcloud.talk.utils.ReceiverFlag
import com.nextcloud.talk.utils.SpreedFeatures
Expand Down
11 changes: 10 additions & 1 deletion app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ import com.nextcloud.talk.models.json.chatprobeattachmentfolder.ProbeConversatio
import com.nextcloud.talk.models.json.conversations.RoomOverall
import com.nextcloud.talk.models.json.conversations.RoomsOverall
import com.nextcloud.talk.models.json.generic.GenericOverall
import com.nextcloud.talk.models.json.hovercard.HoverCardOverall
import com.nextcloud.talk.models.json.invitation.InvitationOverall
import com.nextcloud.talk.models.json.participants.AddParticipantOverall
import com.nextcloud.talk.models.json.participants.TalkBan
import com.nextcloud.talk.models.json.participants.TalkBanOverall
import com.nextcloud.talk.models.json.profile.ProfileOverall
import com.nextcloud.talk.models.json.hovercard.HoverCardOverall
import com.nextcloud.talk.models.json.reactions.ReactionsOverall
import com.nextcloud.talk.models.json.status.StatusOverall
import com.nextcloud.talk.models.json.status.predefined.PredefinedStatusOverall
Expand All @@ -46,6 +46,7 @@ import retrofit2.Response
import retrofit2.http.Body
import retrofit2.http.DELETE
import retrofit2.http.Field
import retrofit2.http.FieldMap
import retrofit2.http.FormUrlEncoded
import retrofit2.http.GET
import retrofit2.http.Header
Expand Down Expand Up @@ -82,6 +83,14 @@ interface NcApiCoroutines {
@QueryMap options: Map<String, String>?
): RoomOverall

@FormUrlEncoded
@POST
suspend fun createRoomForm(
@Header("Authorization") authorization: String?,
@Url url: String?,
@FieldMap options: Map<String, String>?
): RoomOverall

@POST
suspend fun createRoomWithBody(
@Header("Authorization") authorization: String?,
Expand Down
12 changes: 8 additions & 4 deletions app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,6 @@ import com.nextcloud.talk.jobs.UploadAndShareFilesWorker
import com.nextcloud.talk.location.LocationPickerActivity
import com.nextcloud.talk.models.ExternalSignalingServer
import com.nextcloud.talk.models.domain.ConversationModel
import com.nextcloud.talk.models.domain.ConversationModel.Companion.checkIfVoiceRoom
import com.nextcloud.talk.models.json.capabilities.SpreedCapability
import com.nextcloud.talk.models.json.chat.ChatMessageJson
import com.nextcloud.talk.models.json.conversations.ConversationEnums
Expand Down Expand Up @@ -175,6 +174,7 @@ import com.nextcloud.talk.utils.CapabilitiesUtil.retentionOfClassifiedRoom
import com.nextcloud.talk.utils.CapabilitiesUtil.retentionOfSIPRoom
import com.nextcloud.talk.utils.ContactUtils
import com.nextcloud.talk.utils.ConversationUtils
import com.nextcloud.talk.utils.ConversationUtils.checkIfVoiceRoom
import com.nextcloud.talk.utils.DateConstants
import com.nextcloud.talk.utils.DateUtils
import com.nextcloud.talk.utils.DisplayUtils
Expand Down Expand Up @@ -1971,7 +1971,8 @@ class ChatActivity :
!isChatThread() &&
!ConversationUtils.isNoteToSelfConversation(conversation) &&
!isReadOnlyConversation() &&
!shouldShowLobby()
!shouldShowLobby() &&
!ConversationUtils.isChannel(conversation, spreedCapabilities)

private fun isSearchAvailable(capabilitiesReady: Boolean, conversation: ConversationModel?): Boolean =
capabilitiesReady &&
Expand Down Expand Up @@ -2243,8 +2244,11 @@ class ChatActivity :
}

private fun checkShowMessageInputView() {
val permissions = participantPermissionsFlow.value
val isChannel = ConversationUtils.isChannel(currentConversation, spreedCapabilities)

if (isReadOnlyConversation() ||
participantPermissionsFlow.value?.hasChatPermission() == false
(permissions?.hasChatPermission() == false && (!isChannel || permissions.hasReactPermission() == false))
) {
binding.fragmentContainerActivityChat.visibility = View.GONE
} else {
Expand Down Expand Up @@ -2816,7 +2820,7 @@ class ChatActivity :
)
}

public override fun onDestroy() {
override fun onDestroy() {
super.onDestroy()
logConversationInfos("onDestroy")

Expand Down
35 changes: 31 additions & 4 deletions app/src/main/java/com/nextcloud/talk/chat/MessageInputFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ import com.nextcloud.talk.users.UserManager
import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.CapabilitiesUtil
import com.nextcloud.talk.utils.CharPolicy
import com.nextcloud.talk.utils.ConversationUtils
import com.nextcloud.talk.utils.DateUtils
import com.nextcloud.talk.utils.DisplayUtils
import com.nextcloud.talk.utils.EmojiTextInputEditText
Expand Down Expand Up @@ -184,6 +185,12 @@ class MessageInputFragment : Fragment() {
val threadTitle = text.toString()
chatActivity.chatViewModel.messageDraft.threadTitle = threadTitle
}

viewLifecycleOwner.lifecycleScope.launch {
chatActivity.participantPermissionsFlow.collect {
handleButtonsVisibility()
}
}
}

@Suppress("LongMethod")
Expand All @@ -198,6 +205,7 @@ class MessageInputFragment : Fragment() {
initVoiceRecordButton()
initThreadHandling()
updateScheduledMessagesAvailability(hasScheduledMessages)
setReactionsOnly(state.spreedCapabilities)
}

is ChatViewModel.GetCapabilitiesInitialLoadState -> {
Expand All @@ -208,6 +216,7 @@ class MessageInputFragment : Fragment() {
initThreadHandling()
updateScheduledMessagesAvailability(hasScheduledMessages)
restoreState()
setReactionsOnly(state.spreedCapabilities)
}

else -> {}
Expand Down Expand Up @@ -343,6 +352,17 @@ class MessageInputFragment : Fragment() {
}
}

private fun setReactionsOnly(spreedCapabilities: SpreedCapability) {
val isReactionOnly = isReactionOnlyMode(spreedCapabilities)
if (isReactionOnly) {
binding.fragmentMessageInputView.setVisible(false)
binding.reactionOnlyAffordance.setVisible(true)
} else {
binding.fragmentMessageInputView.setVisible(true)
binding.reactionOnlyAffordance.setVisible(false)
}
}

private fun handleUI(isOnline: Boolean, connectionGained: Boolean) {
if (isOnline) {
if (connectionGained) {
Expand Down Expand Up @@ -706,16 +726,16 @@ class MessageInputFragment : Fragment() {
}
}

fun View.setVisible(isVisible: Boolean) {
visibility = if (isVisible) View.VISIBLE else View.GONE
}

private fun handleButtonsVisibility() {
if (!this::binding.isInitialized) {
Log.w(TAG, "binding not initialized in handleButtonsVisibility")
return
}

fun View.setVisible(isVisible: Boolean) {
visibility = if (isVisible) View.VISIBLE else View.GONE
}

val isEditModeActive = binding.fragmentEditView.editMessageView.isVisible
val isThreadCreateModeActive = binding.fragmentCreateThreadView.createThreadView.isVisible
val inputContainsText = binding.fragmentMessageInputView.messageInput.text.isNotEmpty()
Expand Down Expand Up @@ -1287,6 +1307,13 @@ class MessageInputFragment : Fragment() {
return jsonId != null
}

private fun isReactionOnlyMode(spreedCapabilities: SpreedCapability): Boolean {
val conversation = chatActivity.currentConversation
val permissions = chatActivity.participantPermissionsFlow.value
val isChannel = ConversationUtils.isChannel(conversation, spreedCapabilities)
return isChannel && permissions?.hasChatPermission() == false && permissions.hasReactPermission() == true
}

companion object {
fun newInstance() = MessageInputFragment()
private val TAG: String = MessageInputFragment::class.java.simpleName
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -529,9 +529,7 @@ class OfflineFirstChatRepository @Inject constructor(
val queriedMessageId = fieldMap["lastKnownMessageId"]
val lookIntoFuture = fieldMap["lookIntoFuture"] == 1

val result = pullMessagesFlow(bundle).first()

when (result) {
when (val result = pullMessagesFlow(bundle).first()) {
is ChatPullResult.Success -> {
newXChatLastCommonRead = result.lastCommonRead
updateUiForLastCommonRead()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,8 @@ internal fun buildMessageActionsState(
val messageIsEditable = hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.EDIT_MESSAGES) &&
(messageHasRegularText || messageHasCaptions) &&
!isOlderThanTwentyFourHours &&
isUserAllowedByPrivileges
isUserAllowedByPrivileges &&
hasChatPermission
val isMessageEditable = isNoTimeLimitOnNoteToSelf || messageIsEditable || isMessageBotOneToOne

val hasDeleteMessagesUnlimitedCapability =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import com.nextcloud.talk.conversationlist.DirectShareHelper
import com.nextcloud.talk.conversationlist.data.OfflineConversationsRepository
import com.nextcloud.talk.conversationlist.data.network.OfflineFirstConversationsRepository
import com.nextcloud.talk.conversationlist.viewmodels.ConversationsListViewModel.Companion.FOLLOWED_THREADS_EXIST
import com.nextcloud.talk.dagger.modules.ApplicationScope
import com.nextcloud.talk.data.database.mappers.toDomainModel
import com.nextcloud.talk.data.database.model.ChatMessageEntity
import com.nextcloud.talk.data.user.model.User
Expand Down Expand Up @@ -80,7 +81,6 @@ import io.reactivex.Observer
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import com.nextcloud.talk.dagger.modules.ApplicationScope
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
Expand Down Expand Up @@ -496,6 +496,11 @@ class ChatViewModel @AssistedInject constructor(
replay = 1
)

private val isChannelFlow: Flow<Boolean> =
combine(conversationAndUserFlow, spreedCapabilities) { (conversation, _), capabilities ->
ConversationUtils.isChannel(conversation, capabilities)
}.distinctUntilChanged()

// ------------------------------
// Messages
// ------------------------------
Expand Down Expand Up @@ -531,10 +536,10 @@ class ChatViewModel @AssistedInject constructor(
.distinctUntilChanged()
.mapToChatMessages(user.userId!!)
}
}
.map { messages ->
messages.let(::handleSystemMessages)
.let(::handleThreadMessages)
.combine(isChannelFlow) { messages, isChannel ->
handleSystemMessages(messages, isChannel)
.let(::handleThreadMessages)
}
}
.distinctUntilChanged()

Expand Down Expand Up @@ -1367,7 +1372,11 @@ class ChatViewModel @AssistedInject constructor(
}
Log.d(TAG, "fetchNewMessagesWithRetry: no new messages after $POST_UPLOAD_FETCH_MAX_ATTEMPTS attempts")
}
private fun handleSystemMessages(chatMessageList: List<ChatMessage>): List<ChatMessage> {
private fun handleSystemMessages(chatMessageList: List<ChatMessage>, isChannel: Boolean): List<ChatMessage> {
if (isChannel) {
return chatMessageList.filter { !it.isSystemMessage }
}

fun shouldRemoveMessage(currentMessage: MutableMap.MutableEntry<Int, ChatMessage>): Boolean =
isInfoMessageAboutDeletion(currentMessage) ||
isReactionsMessage(currentMessage) ||
Expand Down
Loading
Loading