From 7102f4ea54398ad7502ba6f9c6713fee264e3882 Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Sun, 3 May 2020 16:43:30 +0200 Subject: [PATCH] Most things simply work Signed-off-by: Mario Danic --- .../talk/models/json/chat/ChatUtils.java | 20 +++++++ .../newarch/features/chat/ChatPresenter.kt | 2 +- .../talk/newarch/features/chat/ChatView.kt | 17 +++++- .../newarch/features/chat/ChatViewModel.kt | 30 ++++------ .../converters/ChatMessageStatusConverter.kt | 7 ++- .../talk/newarch/local/dao/MessagesDao.kt | 2 +- .../local/models/other/ChatMessageStatus.kt | 7 ++- .../talk/newarch/services/GlobalService.kt | 59 ++++++++++--------- app/src/main/res/layout/controller_chat.xml | 2 + app/src/main/res/layout/rv_chat_item.xml | 10 ++-- 10 files changed, 95 insertions(+), 61 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatUtils.java b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatUtils.java index f5188d795..ffa858f90 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatUtils.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatUtils.java @@ -20,6 +20,9 @@ package com.nextcloud.talk.models.json.chat; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + import java.util.HashMap; public class ChatUtils { @@ -40,4 +43,21 @@ public class ChatUtils { return message; } + + @NotNull + public static Object getParsedMessageForSending(String message, HashMap> messageParameters) { + if (messageParameters != null && messageParameters.size() > 0) { + for (String key : messageParameters.keySet()) { + HashMap individualHashMap = messageParameters.get(key); + if (individualHashMap.get("type").equals("user") || individualHashMap.get("type") + .equals("guest") || individualHashMap.get("type").equals("call")) { + message = message.replace("{" + key + "}", "@" + messageParameters.get(key).get("id")); + } else if (individualHashMap.get("type").equals("file")) { + message = message.replace("{" + key + "}", messageParameters.get(key).get("id")); + } + } + } + + return message; + } } diff --git a/app/src/main/java/com/nextcloud/talk/newarch/features/chat/ChatPresenter.kt b/app/src/main/java/com/nextcloud/talk/newarch/features/chat/ChatPresenter.kt index 18d94b8a0..a2d0741f3 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/features/chat/ChatPresenter.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/features/chat/ChatPresenter.kt @@ -81,7 +81,7 @@ open class ChatPresenter(context: Context, private val onElementClickPa } holder.itemView.messageTime?.text = DateFormatter.format(it.createdAt, DateFormatter.Template.TIME) - holder.itemView.sendingProgressBar.isVisible = it.chatMessageStatus != ChatMessageStatus.RECEIVED + holder.itemView.sendingProgressBar.isVisible = it.chatMessageStatus != ChatMessageStatus.RECEIVED && it.chatMessageStatus != ChatMessageStatus.FAILED holder.itemView.failedToSendNotice.isVisible = it.chatMessageStatus == ChatMessageStatus.FAILED holder.itemView.chatMessage.text = it.text if (TextMatchers.isMessageWithSingleEmoticonOnly(it.text)) { diff --git a/app/src/main/java/com/nextcloud/talk/newarch/features/chat/ChatView.kt b/app/src/main/java/com/nextcloud/talk/newarch/features/chat/ChatView.kt index fe88b3992..07e6fc543 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/features/chat/ChatView.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/features/chat/ChatView.kt @@ -63,6 +63,7 @@ import com.nextcloud.talk.models.json.chat.ChatMessage import com.nextcloud.talk.models.json.conversations.Conversation import com.nextcloud.talk.models.json.mention.Mention import com.nextcloud.talk.newarch.features.chat.interfaces.ImageLoaderInterface +import com.nextcloud.talk.newarch.local.models.User import com.nextcloud.talk.newarch.local.models.getCredentials import com.nextcloud.talk.newarch.local.models.getMaxMessageLength import com.nextcloud.talk.newarch.local.models.toUserEntity @@ -125,6 +126,10 @@ class ChatView(private val bundle: Bundle) : BaseView(), ImageLoaderInterface { showConversationInfoScreen() } + private lateinit var user: User + private lateinit var conversationToken: String + private var conversationPassword: String? = null + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup @@ -134,8 +139,6 @@ class ChatView(private val bundle: Bundle) : BaseView(), ImageLoaderInterface { viewModel = viewModelProvider(factory).get(ChatViewModel::class.java) val view = super.onCreateView(inflater, container) - viewModel.init(bundle.getParcelable(BundleKeys.KEY_USER)!!, bundle.getString(KEY_CONVERSATION_TOKEN)!!, bundle.getString(KEY_CONVERSATION_PASSWORD)) - messagesAdapter = Adapter.builder(this) .addSource(ChatViewLiveDataSource(viewModel.messagesLiveData)) .addSource(ChatDateHeaderSource(activity as Context, ChatElementTypes.DATE_HEADER.ordinal)) @@ -218,6 +221,7 @@ class ChatView(private val bundle: Bundle) : BaseView(), ImageLoaderInterface { if (shouldShowLobby) { view.messagesRecyclerView?.visibility = View.GONE view.messageInputView?.visibility = View.GONE + view.separator?.visibility = View.GONE view.lobbyView?.visibility = View.VISIBLE val timer = conversation.lobbyTimer val unit = if (timer != null && timer != 0L) { @@ -235,8 +239,10 @@ class ChatView(private val bundle: Bundle) : BaseView(), ImageLoaderInterface { if (isReadOnlyConversation) { view.messageInputView?.visibility = View.GONE + view.separator?.visibility = View.GONE } else { view.messageInputView?.visibility = View.VISIBLE + view.separator?.visibility = View.VISIBLE } } } @@ -454,9 +460,14 @@ class ChatView(private val bundle: Bundle) : BaseView(), ImageLoaderInterface { override fun onAttach(view: View) { super.onAttach(view) viewModel.view = this + user = bundle.getParcelable(BundleKeys.KEY_USER)!! + conversationToken = bundle.getString(BundleKeys.KEY_CONVERSATION_TOKEN)!! + conversationPassword = bundle.getString(KEY_CONVERSATION_PASSWORD) + viewModel.user = user + viewModel.conversationPassword = conversationPassword setupViews() toolbar?.setOnClickListener(toolbarOnClickListener) - viewModel.joinConversation() + viewModel.joinConversation(user, conversationToken, conversationPassword) } override fun onDetach(view: View) { diff --git a/app/src/main/java/com/nextcloud/talk/newarch/features/chat/ChatViewModel.kt b/app/src/main/java/com/nextcloud/talk/newarch/features/chat/ChatViewModel.kt index 35ca1bb04..3f6fd7282 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/features/chat/ChatViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/features/chat/ChatViewModel.kt @@ -65,14 +65,14 @@ class ChatViewModel constructor(application: Application, private val messagesRepository: MessagesRepository, private val globalService: GlobalService) : BaseViewModel(application), GlobalServiceInterface { lateinit var user: User - val conversation: MutableLiveData = MutableLiveData() + val conversation: MutableLiveData = MutableLiveData() var pastStartingPoint: Long = -1 val futureStartingPoint: MutableLiveData = MutableLiveData() private var initConversation: Conversation? = null - val messagesLiveData = Transformations.switchMap(futureStartingPoint) { futureStartingPoint -> - conversation.value?.let { - messagesRepository.getMessagesWithUserForConversationSince(it.databaseId!!, futureStartingPoint).map { chatMessagesList -> + val messagesLiveData = Transformations.switchMap(conversation) { conversation -> + conversation?.let { + messagesRepository.getMessagesWithUserForConversation(it.databaseId!!).map { chatMessagesList -> chatMessagesList.map { chatMessage -> chatMessage.activeUser = user.toUserEntity() chatMessage.parentMessage?.activeUser = chatMessage.activeUser @@ -83,7 +83,6 @@ class ChatViewModel constructor(application: Application, } } } - } } @@ -91,14 +90,6 @@ class ChatViewModel constructor(application: Application, var view: Controller? = null - fun init(user: User, conversationToken: String, conversationPassword: String?) { - viewModelScope.launch { - this@ChatViewModel.user = user - this@ChatViewModel.initConversation = conversationsRepository.getConversationForUserWithToken(user.id!!, conversationToken) - this@ChatViewModel.conversationPassword = conversationPassword - } - } - fun sendMessage(editable: Editable, replyTo: Long?) { val messageParameters = hashMapOf>() val mentionSpans = editable.getSpans( @@ -143,7 +134,7 @@ class ChatViewModel constructor(application: Application, chatMessage.actorDisplayName = user.displayName chatMessage.message = editable.toString() chatMessage.systemMessageType = null - chatMessage.chatMessageStatus = ChatMessageStatus.PENDING_MESSAGE_SEND + chatMessage.chatMessageStatus = ChatMessageStatus.PENDING if (replyTo != null) { chatMessage.parentMessage = messagesRepository.getMessageForConversation(conversationDatabaseId, replyTo) } else { @@ -170,11 +161,12 @@ class ChatViewModel constructor(application: Application, } } - fun joinConversation() { - initConversation?.token?.let { - viewModelScope.launch { - globalService.getConversation(it, this@ChatViewModel) - } + fun joinConversation(user: User, conversationToken: String, conversationPassword: String?) { + viewModelScope.launch { + this@ChatViewModel.user = user + this@ChatViewModel.initConversation = conversationsRepository.getConversationForUserWithToken(user.id!!, conversationToken) + this@ChatViewModel.conversationPassword = conversationPassword + globalService.getConversation(conversationToken, this@ChatViewModel) } } diff --git a/app/src/main/java/com/nextcloud/talk/newarch/local/converters/ChatMessageStatusConverter.kt b/app/src/main/java/com/nextcloud/talk/newarch/local/converters/ChatMessageStatusConverter.kt index b27d7884a..158df6c66 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/local/converters/ChatMessageStatusConverter.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/local/converters/ChatMessageStatusConverter.kt @@ -36,9 +36,10 @@ class ChatMessageStatusConverter { return when (value) { 0 -> ChatMessageStatus.SENT 1 -> ChatMessageStatus.RECEIVED - 2 -> ChatMessageStatus.PENDING_MESSAGE_SEND - 3 -> ChatMessageStatus.PENDING_FILE_UPLOAD - 4 -> ChatMessageStatus.PENDING_FILE_SHARE + 2 -> ChatMessageStatus.PENDING + 3 -> ChatMessageStatus.PROCESSING + 4 -> ChatMessageStatus.FAILED_ONCE + 5 -> ChatMessageStatus.FAILED_TWICE else -> ChatMessageStatus.FAILED } } diff --git a/app/src/main/java/com/nextcloud/talk/newarch/local/dao/MessagesDao.kt b/app/src/main/java/com/nextcloud/talk/newarch/local/dao/MessagesDao.kt index 6a2b856b5..ed574e1f5 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/local/dao/MessagesDao.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/local/dao/MessagesDao.kt @@ -40,7 +40,7 @@ abstract class MessagesDao { @Query("SELECT * FROM messages WHERE conversation_id = :conversationId AND id = reference_id") abstract suspend fun getPendingMessages(conversationId: String): List - @Query("SELECT * FROM messages WHERE conversation_id = :conversationId and id = reference_id and message_status != 5 and message_status != 0") + @Query("SELECT * FROM messages WHERE conversation_id = :conversationId and id = reference_id and message_status NOT IN (0, 3, 6)") abstract fun getPendingMessagesLive(conversationId: String): LiveData> @Query( diff --git a/app/src/main/java/com/nextcloud/talk/newarch/local/models/other/ChatMessageStatus.kt b/app/src/main/java/com/nextcloud/talk/newarch/local/models/other/ChatMessageStatus.kt index 2ec337207..a07995897 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/local/models/other/ChatMessageStatus.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/local/models/other/ChatMessageStatus.kt @@ -3,8 +3,9 @@ package com.nextcloud.talk.newarch.local.models.other enum class ChatMessageStatus { SENT, RECEIVED, - PENDING_MESSAGE_SEND, - PENDING_FILE_UPLOAD, - PENDING_FILE_SHARE, + PENDING, + PROCESSING, + FAILED_ONCE, + FAILED_TWICE, FAILED } \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/newarch/services/GlobalService.kt b/app/src/main/java/com/nextcloud/talk/newarch/services/GlobalService.kt index 5dd20b9de..1da27c4fc 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/services/GlobalService.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/services/GlobalService.kt @@ -22,11 +22,13 @@ package com.nextcloud.talk.newarch.services +import android.util.Log import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.Transformations import com.nextcloud.talk.models.json.chat.ChatMessage import com.nextcloud.talk.models.json.chat.ChatOverall +import com.nextcloud.talk.models.json.chat.ChatUtils import com.nextcloud.talk.models.json.conversations.Conversation import com.nextcloud.talk.models.json.conversations.ConversationOverall import com.nextcloud.talk.models.json.generic.GenericOverall @@ -74,16 +76,12 @@ class GlobalService constructor(usersRepository: UsersRepository, } } - private var messagesOperations: ConcurrentHashMap> = ConcurrentHashMap>() init { pendingMessages.observeForever { chatMessages -> for (chatMessage in chatMessages) { - if (!messagesOperations.contains(chatMessage.internalMessageId) || messagesOperations[chatMessage.internalMessageId]?.first != chatMessage) { - messagesOperations[chatMessage.internalMessageId!!] = Pair(chatMessage, 0) - applicationScope.launch { - sendMessage(chatMessage) - } + applicationScope.launch { + sendMessage(chatMessage) } } } @@ -101,30 +99,36 @@ class GlobalService constructor(usersRepository: UsersRepository, suspend fun sendMessage(chatMessage: ChatMessage) { val currentUser = currentUserLiveData.value?.toUser() val conversation = currentConversation.value - val operationChatMessage = messagesOperations[chatMessage.internalMessageId] - operationChatMessage?.let { pair -> + chatMessage.let { chatMessage -> conversation?.let { conversation -> - if (pair.second == 4) { - messagesOperations.remove(pair.first.internalMessageId) - messagesRepository.updateMessageStatus(ChatMessageStatus.FAILED.ordinal, conversation.databaseId!!, pair.first.jsonMessageId!!) - } else { - currentUser?.let { user -> - if (chatMessage.internalConversationId == conversation.databaseId && conversation.databaseUserId == currentUser.id) { - val sendChatMessageUseCase = SendChatMessageUseCase(networkComponents.getRepository(false, user), ApiErrorHandler()) - sendChatMessageUseCase.invoke(applicationScope, parametersOf(user, conversation.token, chatMessage.message, chatMessage.parentMessage?.jsonMessageId, chatMessage.referenceId), object : UseCaseResponse> { - override suspend fun onSuccess(result: Response) { - messagesOperations.remove(pair.first.internalMessageId!!) - messagesRepository.updateMessageStatus(ChatMessageStatus.SENT.ordinal, conversation.databaseId!!, pair.first.jsonMessageId!!) - } + val currentStatus = chatMessage.chatMessageStatus + applicationScope.launch { + //messagesRepository.updateMessageStatus(ChatMessageStatus.PROCESSING.ordinal, conversation.databaseId!!, chatMessage.jsonMessageId!!) + } + currentUser?.let { user -> + if (chatMessage.internalConversationId == conversation.databaseId && conversation.databaseUserId == currentUser.id) { + val sendChatMessageUseCase = SendChatMessageUseCase(networkComponents.getRepository(false, user), ApiErrorHandler()) + val messageToSend = ChatUtils.getParsedMessageForSending(chatMessage.message, chatMessage.messageParameters) + sendChatMessageUseCase.invoke(applicationScope, parametersOf(user, conversation.token, messageToSend, chatMessage.parentMessage?.jsonMessageId, chatMessage.referenceId), object : UseCaseResponse> { + override suspend fun onSuccess(result: Response) { + messagesRepository.updateMessageStatus(ChatMessageStatus.SENT.ordinal, conversation.databaseId!!, chatMessage.jsonMessageId!!) + } - override suspend fun onError(errorModel: ErrorModel?) { - val newValue = operationChatMessage.second + 1 - messagesOperations[pair.first.internalMessageId!!] = Pair(chatMessage, newValue) - sendMessage(chatMessage) + override suspend fun onError(errorModel: ErrorModel?) { + when (currentStatus) { + ChatMessageStatus.PENDING -> { + messagesRepository.updateMessageStatus(ChatMessageStatus.FAILED_ONCE.ordinal, conversation.databaseId!!, chatMessage.jsonMessageId!!) + } + ChatMessageStatus.FAILED_ONCE -> { + messagesRepository.updateMessageStatus(ChatMessageStatus.FAILED_TWICE.ordinal, conversation.databaseId!!, chatMessage.jsonMessageId!!) + } + else -> { + messagesRepository.updateMessageStatus(ChatMessageStatus.FAILED.ordinal, conversation.databaseId!!, chatMessage.jsonMessageId!!) + } } - }) - } + } + }) } } } @@ -134,7 +138,7 @@ class GlobalService constructor(usersRepository: UsersRepository, suspend fun exitConversation(conversationToken: String, globalServiceInterface: GlobalServiceInterface) { val currentUser = currentUserLiveData.value!!.toUser() val exitConversationUseCase = ExitConversationUseCase(networkComponents.getRepository(true, currentUser), ApiErrorHandler()) - exitConversationUseCase.invoke(applicationScope, parametersOf(currentUser, conversationToken), object: UseCaseResponse { + exitConversationUseCase.invoke(applicationScope, parametersOf(currentUser, conversationToken), object : UseCaseResponse { override suspend fun onSuccess(result: GenericOverall) { globalServiceInterface.leftConversationForUser(currentUser, currentConversation.value, GlobalServiceInterface.OperationStatus.STATUS_OK) withContext(Dispatchers.Main) { @@ -147,6 +151,7 @@ class GlobalService constructor(usersRepository: UsersRepository, } }) } + suspend fun getConversation(conversationToken: String, globalServiceInterface: GlobalServiceInterface) { val currentUser = currentUserLiveData.value val getConversationUseCase = GetConversationUseCase(networkComponents.getRepository(true, currentUser!!.toUser()), ApiErrorHandler()) diff --git a/app/src/main/res/layout/controller_chat.xml b/app/src/main/res/layout/controller_chat.xml index 99ca8d74a..2e258915b 100644 --- a/app/src/main/res/layout/controller_chat.xml +++ b/app/src/main/res/layout/controller_chat.xml @@ -42,11 +42,13 @@ android:layout_above="@+id/messageInputView" android:layout_marginLeft="16dp" android:layout_marginRight="16dp" + android:visibility="gone" android:background="@color/controller_chat_separator" /> @@ -91,9 +91,11 @@ android:layout_height="wrap_content" android:layout_marginHorizontal="8dp" android:text="@string/nc_failed_to_send" - android:layout_below="@id/messageTime" + android:layout_alignParentBottom="true" + android:textAlignment="viewEnd" + android:textColor="@color/nc_darkRed" android:id="@+id/failedToSendNotice" - android:visibility="gone" + android:visibility="visible" android:layout_alignParentEnd="true"/>