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
8 changes: 2 additions & 6 deletions app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4155,15 +4155,11 @@ class ChatActivity :
}

fun cancelReply() {
messageInputViewModel.reply(null)
chatViewModel.messageDraft.quotedMessageText = null
chatViewModel.messageDraft.quotedDisplayName = null
chatViewModel.messageDraft.quotedImageUrl = null
chatViewModel.messageDraft.quotedJsonId = null
messageInputViewModel.cancelReply()
}

fun cancelCreateThread() {
chatViewModel.clearThreadTitle()
messageInputViewModel.cancelCreateThread()
}

companion object {
Expand Down
174 changes: 90 additions & 84 deletions app/src/main/java/com/nextcloud/talk/chat/MessageInputFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,9 @@ import com.nextcloud.talk.utils.bundle.BundleKeys
import com.nextcloud.talk.utils.message.MessageUtils
import com.nextcloud.talk.utils.text.Spans
import com.otaliastudios.autocomplete.Autocomplete
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.Objects
import javax.inject.Inject

Expand Down Expand Up @@ -124,6 +122,10 @@ class MessageInputFragment : Fragment() {
private lateinit var spreedCapabilities: SpreedCapability
private var hasSharedText = false

private var lastQuotedJsonId: Int? = null
private var lastEditMessageId: Int? = null
private var lastIsThreadCreationInProgress: Boolean = false

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
sharedApplication!!.componentApplication.inject(this)
Expand Down Expand Up @@ -151,17 +153,23 @@ class MessageInputFragment : Fragment() {
if (mentionAutocomplete != null && mentionAutocomplete!!.isPopupShowing) {
mentionAutocomplete?.dismissPopup()
}

val messageText = binding.fragmentMessageInputView.messageInput.text
if (messageText.isNotEmpty()) {
chatActivity.chatViewModel.messageDraft.messageText = messageText.toString()
chatActivity.chatViewModel.saveMessageDraft()
}
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initObservers()

binding.fragmentCreateThreadView.createThreadView.findViewById<EmojiTextInputEditText>(
R.id
.createThreadInput
R.id.createThreadInput
).doAfterTextChanged { text ->
val threadTitle = text.toString()
messageInputViewModel.updateThreadTitle(threadTitle)
chatActivity.chatViewModel.messageDraft.threadTitle = threadTitle
}

Expand All @@ -170,6 +178,63 @@ class MessageInputFragment : Fragment() {
handleButtonsVisibility()
}
}

observeInputState()
}

private fun observeInputState() {
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
messageInputViewModel.inputState.collect { state ->
updateUiFromState(state)
chatActivity.chatViewModel.messageDraft = messageInputViewModel.toDraft()
}
}
}
}

private fun updateUiFromState(state: MessageInputViewModel.MessageInputState) {
val input = binding.fragmentMessageInputView.messageInput
val currentText = input.text.toString()
if (currentText != state.messageText) {
input.setText(state.messageText)
input.setSelection(state.messageCursor.coerceAtMost(state.messageText.length))
}

if (state.quotedJsonId != lastQuotedJsonId) {
lastQuotedJsonId = state.quotedJsonId
if (state.quotedJsonId != null) {
replyToMessage(
state.quotedMessageText,
state.quotedDisplayName,
state.quotedImageUrl
)
} else {
clearReplyUi()
}
}

val editMessageId = state.editMessage?.jsonMessageId
if (editMessageId != lastEditMessageId) {
lastEditMessageId = editMessageId
if (state.editMessage != null) {
setEditUI(state.editMessage)
} else {
clearEditUI()
}
}

if (state.isThreadCreationInProgress != lastIsThreadCreationInProgress) {
lastIsThreadCreationInProgress = state.isThreadCreationInProgress
binding.fragmentCreateThreadView.createThreadView.isVisible = state.isThreadCreationInProgress
}

val threadInput = binding.fragmentCreateThreadView.createThreadView.findViewById<EmojiTextInputEditText>(
R.id.createThreadInput
)
if (threadInput.text.toString() != state.threadTitle) {
threadInput.setText(state.threadTitle)
}
}

@Suppress("LongMethod")
Expand All @@ -192,30 +257,14 @@ class MessageInputFragment : Fragment() {
initVoiceRecordButton()
initThreadHandling()
updateScheduledMessagesAvailability(hasScheduledMessages)
restoreState()
setReactionsOnly(state.spreedCapabilities)
initInputState()
}

else -> {}
}
}

messageInputViewModel.getReplyChatMessage.observe(viewLifecycleOwner) { message ->
message?.let {
chatActivity.chatViewModel.messageDraft.quotedMessageText = message.getRichText()
chatActivity.chatViewModel.messageDraft.quotedDisplayName = message.actorDisplayName
// chatActivity.chatViewModel.messageDraft.quotedImageUrl = message.imageUrl
chatActivity.chatViewModel.messageDraft.quotedImageUrl = "" // TODO
chatActivity.chatViewModel.messageDraft.quotedJsonId = message.jsonMessageId
replyToMessage(
quotedMessageText = message.getRichText(),
quotedActorDisplayName = message.actorDisplayName,
// quotedImageUrl = message.imageUrl
quotedImageUrl = "" // TODO
)
} ?: clearReplyUi()
}

chatActivity.chatViewModel.scheduledMessagesCount.observe(viewLifecycleOwner) { count ->
if (chatActivity.conversationThreadId != null && chatActivity.conversationThreadId!! > 0) {
val threadId = chatActivity.conversationThreadId
Expand All @@ -231,32 +280,6 @@ class MessageInputFragment : Fragment() {
}
}

viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
messageInputViewModel.getEditChatMessage.collect { message ->
message?.let { setEditUI(it) } ?: clearEditUI()
}
}
}

messageInputViewModel.createThreadViewState.observe(viewLifecycleOwner) { state ->
when (state) {
is MessageInputViewModel.CreateThreadStartState ->
binding.fragmentCreateThreadView.createThreadView.visibility = View.GONE

is MessageInputViewModel.CreateThreadEditState -> {
binding.fragmentCreateThreadView.createThreadView.visibility = View.VISIBLE
binding.fragmentCreateThreadView.createThreadView
.findViewById<EmojiTextInputEditText>(R.id.createThreadInput)?.setText(
chatActivity.chatViewModel.messageDraft.threadTitle
)
}

else -> {}
}
initVoiceRecordButton()
}

chatActivity.chatViewModel.leaveRoomViewState.observe(viewLifecycleOwner) { state ->
when (state) {
is ChatViewModel.LeaveRoomSuccessState -> sendStopTypingMessage()
Expand Down Expand Up @@ -365,32 +388,16 @@ class MessageInputFragment : Fragment() {
cancelCreateThread()
}

private fun restoreState() {
viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO) {
private fun initInputState() {
viewLifecycleOwner.lifecycleScope.launch {
if (!hasSharedText) {
chatActivity.chatViewModel.updateMessageDraft()
}

withContext(Dispatchers.Main) {
val draft = chatActivity.chatViewModel.messageDraft
binding.fragmentMessageInputView.messageInput.setText(draft.messageText)
binding.fragmentMessageInputView.messageInput.setSelection(draft.messageCursor)

if (draft.threadTitle?.isNotEmpty() == true) {
messageInputViewModel.startThreadCreation()
}

if (draft.messageText != "") {
binding.fragmentMessageInputView.messageInput.requestFocus()
}

if (isInReplyState()) {
replyToMessage(
chatActivity.chatViewModel.messageDraft.quotedMessageText,
chatActivity.chatViewModel.messageDraft.quotedDisplayName,
chatActivity.chatViewModel.messageDraft.quotedImageUrl
)
}
if (messageInputViewModel.inputState.value.messageText.isEmpty() &&
messageInputViewModel.inputState.value.quotedJsonId == null &&
messageInputViewModel.inputState.value.editMessage == null
) {
messageInputViewModel.initFromDraft(chatActivity.chatViewModel.messageDraft)
}
}
}
Expand Down Expand Up @@ -456,6 +463,9 @@ class MessageInputFragment : Fragment() {
override fun afterTextChanged(s: Editable) {
val cursor = binding.fragmentMessageInputView.messageInput.selectionStart
val text = binding.fragmentMessageInputView.messageInput.text.toString()
messageInputViewModel.updateMessageText(text)
messageInputViewModel.updateMessageCursor(cursor)

chatActivity.chatViewModel.messageDraft.messageCursor = cursor
chatActivity.chatViewModel.messageDraft.messageText = text
handleButtonsVisibility()
Expand Down Expand Up @@ -1065,12 +1075,15 @@ class MessageInputFragment : Fragment() {
}

private fun clearEditUI() {
binding.fragmentMessageInputView.editMessageButton.visibility = View.GONE
binding.fragmentMessageInputView.inputEditText.setText("")
binding.fragmentEditView.editMessageView.visibility = View.GONE
binding.fragmentMessageInputView.messageSendButton.visibility = View.VISIBLE
binding.fragmentMessageInputView.recordAudioButton.visibility = View.VISIBLE
binding.fragmentMessageInputView.submitThreadButton.visibility = View.VISIBLE
binding.fragmentMessageInputView.editMessageButton.visibility = View.GONE
binding.fragmentMessageInputView.attachmentButton.visibility = View.VISIBLE
messageInputViewModel.edit(null)
handleButtonsVisibility()
binding.fragmentMessageInputView.scheduledMessagesButton.visibility = View.VISIBLE
messageInputViewModel.cancelEdit()
lastEditMessageId = null
}

private fun themeMessageInputView() {
Expand Down Expand Up @@ -1139,25 +1152,18 @@ class MessageInputFragment : Fragment() {
}

private fun cancelCreateThread() {
chatActivity.cancelCreateThread()
messageInputViewModel.stopThreadCreation()
binding.fragmentCreateThreadView.createThreadView.visibility = View.GONE
messageInputViewModel.cancelCreateThread()
}

private fun cancelReply() {
chatActivity.cancelReply()
clearReplyUi()
messageInputViewModel.cancelReply()
}

private fun clearReplyUi() {
val quote = binding.fragmentMessageInputView.findViewById<RelativeLayout>(R.id.quotedChatMessageView)
quote.visibility = View.GONE
binding.fragmentMessageInputView.findViewById<ImageButton>(R.id.attachmentButton)?.visibility = View.VISIBLE
}

private fun isInReplyState(): Boolean {
val jsonId = chatActivity.chatViewModel.messageDraft.quotedJsonId
return jsonId != null
lastQuotedJsonId = null
}

private fun isReactionOnlyMode(spreedCapabilities: SpreedCapability): Boolean {
Expand Down
Loading
Loading