From d29e9072f8f8a9d92b514ddda360eae3ad698bd7 Mon Sep 17 00:00:00 2001 From: shaulascr Date: Fri, 2 May 2025 09:28:33 +0700 Subject: [PATCH] fixed chat (hide attach + adjust attach product) --- .../api/response/chat/ChatHistoryResponse.kt | 3 +- .../api/response/chat/SendChatResponse.kt | 2 +- .../api/response/chat/UpdateChatResponse.kt | 2 +- .../data/api/retrofit/ApiService.kt | 3 +- .../data/repository/ChatRepository.kt | 131 ++++++++++++ .../data/repository/UserRepository.kt | 198 +++++++++--------- .../alya/ecommerce_serang/di/ChatModule.kt | 9 +- .../ecommerce_serang/ui/chat/ChatActivity.kt | 10 +- .../ecommerce_serang/ui/chat/ChatAdapter.kt | 4 +- .../ui/chat/ChatListFragment.kt | 6 +- .../ecommerce_serang/ui/chat/ChatViewModel.kt | 43 ++-- 11 files changed, 276 insertions(+), 135 deletions(-) create mode 100644 app/src/main/java/com/alya/ecommerce_serang/data/repository/ChatRepository.kt diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/response/chat/ChatHistoryResponse.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/chat/ChatHistoryResponse.kt index 4de7329..95fbd16 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/data/api/response/chat/ChatHistoryResponse.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/chat/ChatHistoryResponse.kt @@ -1,7 +1,6 @@ package com.alya.ecommerce_serang.data.api.response.chat import com.google.gson.annotations.SerializedName -import java.io.File data class ChatHistoryResponse( @@ -15,7 +14,7 @@ data class ChatHistoryResponse( data class ChatItem( @field:SerializedName("attachment") - val attachment: File? = null, + val attachment: String? = null, @field:SerializedName("product_id") val productId: Int, diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/response/chat/SendChatResponse.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/chat/SendChatResponse.kt index ff520bd..61daec2 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/data/api/response/chat/SendChatResponse.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/chat/SendChatResponse.kt @@ -14,7 +14,7 @@ data class SendChatResponse( data class ChatLine( @field:SerializedName("attachment") - val attachment: String, + val attachment: String? = null, @field:SerializedName("product_id") val productId: Int, diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/response/chat/UpdateChatResponse.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/chat/UpdateChatResponse.kt index 0ea26ae..6cb3912 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/data/api/response/chat/UpdateChatResponse.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/chat/UpdateChatResponse.kt @@ -14,7 +14,7 @@ data class UpdateChatResponse( data class Address( @field:SerializedName("attachment") - val attachment: Any, + val attachment: String? = null, @field:SerializedName("product_id") val productId: Int, diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/retrofit/ApiService.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/retrofit/ApiService.kt index 52ab2fc..41dade8 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/data/api/retrofit/ApiService.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/retrofit/ApiService.kt @@ -223,12 +223,13 @@ interface ApiService { @Part chatimg: MultipartBody.Part? ): Response + @PUT("chatstatus") suspend fun updateChatStatus( @Body request: UpdateChatRequest ): Response - @GET("chatdetail/{chatRoomId}") + @GET("chat/{chatRoomId}") suspend fun getChatDetail( @Path("chatRoomId") chatRoomId: Int ): Response diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/repository/ChatRepository.kt b/app/src/main/java/com/alya/ecommerce_serang/data/repository/ChatRepository.kt new file mode 100644 index 0000000..751f93b --- /dev/null +++ b/app/src/main/java/com/alya/ecommerce_serang/data/repository/ChatRepository.kt @@ -0,0 +1,131 @@ +package com.alya.ecommerce_serang.data.repository + +import android.util.Log +import com.alya.ecommerce_serang.data.api.dto.UpdateChatRequest +import com.alya.ecommerce_serang.data.api.dto.UserProfile +import com.alya.ecommerce_serang.data.api.response.chat.ChatHistoryResponse +import com.alya.ecommerce_serang.data.api.response.chat.SendChatResponse +import com.alya.ecommerce_serang.data.api.response.chat.UpdateChatResponse +import com.alya.ecommerce_serang.data.api.retrofit.ApiService +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.MultipartBody +import okhttp3.RequestBody +import java.io.File +import javax.inject.Inject + +class ChatRepository @Inject constructor( + private val apiService: ApiService +) { + private val TAG = "ChatRepository" + + suspend fun fetchUserProfile(): Result { + return try { + val response = apiService.getUserProfile() + if (response.isSuccessful) { + response.body()?.user?.let { + Result.Success(it) // ✅ Returning only UserProfile + } ?: Result.Error(Exception("User data not found")) + } else { + Result.Error(Exception("Error fetching profile: ${response.code()}")) + } + } catch (e: Exception) { + Result.Error(e) + } + } + + suspend fun sendChatMessage( + storeId: Int, + message: String, + productId: Int, + imageFile: File? = null + ): Result { + return try { + // Create request bodies for text fields + val storeIdBody = RequestBody.create("text/plain".toMediaTypeOrNull(), storeId.toString()) + val messageBody = RequestBody.create("text/plain".toMediaTypeOrNull(), message) + val productIdBody = RequestBody.create("text/plain".toMediaTypeOrNull(), productId.toString()) + + // Create multipart body for the image file + val imageMultipart = if (imageFile != null && imageFile.exists()) { + // Log detailed file information + Log.d(TAG, "Image file: ${imageFile.absolutePath}") + Log.d(TAG, "Image file size: ${imageFile.length()} bytes") + Log.d(TAG, "Image file exists: ${imageFile.exists()}") + Log.d(TAG, "Image file can read: ${imageFile.canRead()}") + + val requestFile = RequestBody.create("image/*".toMediaTypeOrNull(), imageFile) + MultipartBody.Part.createFormData("chatimg", imageFile.name, requestFile) + } else { + // Pass null when no image is provided + null + } + + // Log request info + Log.d(TAG, "Sending message to store ID: $storeId, product ID: $productId") + Log.d(TAG, "Message content: $message") + Log.d(TAG, "Has image: ${imageFile != null && imageFile.exists()}") + + // Make the API call + val response = apiService.sendChatLine( + storeId = storeIdBody, + message = messageBody, + productId = productIdBody, + chatimg = imageMultipart + ) + + if (response.isSuccessful) { + response.body()?.let { + Result.Success(it) + } ?: Result.Error(Exception("Send chat response is empty")) + } else { + val errorBody = response.errorBody()?.string() ?: "Unknown error" + Log.e(TAG, "HTTP Error: ${response.code()}, Body: $errorBody") + Result.Error(Exception("API Error: ${response.code()} - $errorBody")) + } + } catch (e: Exception) { + Log.e(TAG, "Exception sending message", e) + e.printStackTrace() + Result.Error(e) + } + } + + suspend fun updateMessageStatus( + messageId: Int, + status: String + ): Result { + return try { + val requestBody = UpdateChatRequest( + id = messageId, + status = status + ) + + val response = apiService.updateChatStatus(requestBody) + + if (response.isSuccessful) { + response.body()?.let { + Result.Success(it) + } ?: Result.Error(Exception("Update status response is empty")) + } else { + Result.Error(Exception(response.errorBody()?.string() ?: "Unknown error")) + } + } catch (e: Exception) { + Result.Error(e) + } + } + + suspend fun getChatHistory(chatRoomId: Int): Result { + return try { + val response = apiService.getChatDetail(chatRoomId) + + if (response.isSuccessful) { + response.body()?.let { + Result.Success(it) + } ?: Result.Error(Exception("Chat history response is empty")) + } else { + Result.Error(Exception(response.errorBody()?.string() ?: "Unknown error")) + } + } catch (e: Exception) { + Result.Error(e) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/repository/UserRepository.kt b/app/src/main/java/com/alya/ecommerce_serang/data/repository/UserRepository.kt index 62910ea..dfcd46b 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/data/repository/UserRepository.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/data/repository/UserRepository.kt @@ -3,19 +3,10 @@ package com.alya.ecommerce_serang.data.repository import com.alya.ecommerce_serang.data.api.dto.LoginRequest import com.alya.ecommerce_serang.data.api.dto.OtpRequest import com.alya.ecommerce_serang.data.api.dto.RegisterRequest -import com.alya.ecommerce_serang.data.api.dto.UpdateChatRequest import com.alya.ecommerce_serang.data.api.dto.UserProfile import com.alya.ecommerce_serang.data.api.response.auth.LoginResponse import com.alya.ecommerce_serang.data.api.response.auth.OtpResponse -import com.alya.ecommerce_serang.data.api.response.chat.ChatHistoryResponse -import com.alya.ecommerce_serang.data.api.response.chat.SendChatResponse -import com.alya.ecommerce_serang.data.api.response.chat.UpdateChatResponse import com.alya.ecommerce_serang.data.api.retrofit.ApiService -import okhttp3.MediaType.Companion.toMediaTypeOrNull -import okhttp3.MultipartBody -import okhttp3.RequestBody.Companion.asRequestBody -import okhttp3.RequestBody.Companion.toRequestBody -import java.io.File class UserRepository(private val apiService: ApiService) { @@ -65,100 +56,101 @@ class UserRepository(private val apiService: ApiService) { } } - suspend fun sendChatMessage( - storeId: Int, - message: String, - productId: Int, - imageFile: File? = null - ): Result { - return try { - // Create request bodies for text fields - val storeIdBody = storeId.toString().toRequestBody("text/plain".toMediaTypeOrNull()) - val messageBody = message.toRequestBody("text/plain".toMediaTypeOrNull()) - val productIdBody = productId.toString().toRequestBody("text/plain".toMediaTypeOrNull()) - - // Create multipart body for the image file - val imageMultipart = if (imageFile != null && imageFile.exists()) { - val requestFile = imageFile.asRequestBody("image/*".toMediaTypeOrNull()) - MultipartBody.Part.createFormData("chatimg", imageFile.name, requestFile) - } else { - // Create an empty part if no image is provided - val emptyRequest = "".toRequestBody("text/plain".toMediaTypeOrNull()) - MultipartBody.Part.createFormData("chatimg", "", emptyRequest) - } - - // Make the API call - val response = apiService.sendChatLine( - storeId = storeIdBody, - message = messageBody, - productId = productIdBody, - chatimg = imageMultipart - ) - - if (response.isSuccessful) { - response.body()?.let { - Result.Success(it) - } ?: Result.Error(Exception("Send chat response is empty")) - } else { - Result.Error(Exception(response.errorBody()?.string() ?: "Unknown error")) - } - } catch (e: Exception) { - Result.Error(e) - } - } - - /** - * Updates the status of a message (sent, delivered, read) - * - * @param messageId The ID of the message to update - * @param status The new status to set - * @return Result containing the updated message details or error - */ - suspend fun updateMessageStatus( - messageId: Int, - status: String - ): Result { - return try { - val requestBody = UpdateChatRequest( - id = messageId, - status = status - ) - - val response = apiService.updateChatStatus(requestBody) - - if (response.isSuccessful) { - response.body()?.let { - Result.Success(it) - } ?: Result.Error(Exception("Update status response is empty")) - } else { - Result.Error(Exception(response.errorBody()?.string() ?: "Unknown error")) - } - } catch (e: Exception) { - Result.Error(e) - } - } - - /** - * Gets the chat history for a specific chat room - * - * @param chatRoomId The ID of the chat room - * @return Result containing the list of chat messages or error - */ - suspend fun getChatHistory(chatRoomId: Int): Result { - return try { - val response = apiService.getChatDetail(chatRoomId) - - if (response.isSuccessful) { - response.body()?.let { - Result.Success(it) - } ?: Result.Error(Exception("Chat history response is empty")) - } else { - Result.Error(Exception(response.errorBody()?.string() ?: "Unknown error")) - } - } catch (e: Exception) { - Result.Error(e) - } - } +// suspend fun sendChatMessage( +// storeId: Int, +// message: String, +// productId: Int, +// imageFile: File? = null +// ): Result { +// return try { +// // Create multipart request builder +// val requestBodyBuilder = MultipartBody.Builder().setType(MultipartBody.FORM) +// +// // Add text fields +// requestBodyBuilder.addFormDataPart("store_id", storeId.toString()) +// requestBodyBuilder.addFormDataPart("message", message) +// requestBodyBuilder.addFormDataPart("product_id", productId.toString()) +// +// // Add image if it exists +// if (imageFile != null && imageFile.exists()) { +// val requestFile = imageFile.asRequestBody("image/*".toMediaTypeOrNull()) +// requestBodyBuilder.addFormDataPart("chatimg", imageFile.name, requestFile) +// } +// +// // Build the final request body +// val requestBody = requestBodyBuilder.build() +// +// // Make the API call using a custom endpoint that takes a plain MultipartBody +// val response = apiService.sendChatLineWithBody(requestBody) +// +// if (response.isSuccessful) { +// response.body()?.let { +// Result.Success(it) +// } ?: Result.Error(Exception("Send chat response is empty")) +// } else { +// val errorBody = response.errorBody()?.string() ?: "Unknown error" +// Log.e("ChatRepository", "HTTP Error: ${response.code()}, Body: $errorBody") +// Result.Error(Exception("API Error: ${response.code()} - $errorBody")) +// } +// } catch (e: Exception) { +// Log.e("ChatRepository", "Exception sending message", e) +// e.printStackTrace() +// Result.Error(e) +// } +// } +// +// /** +// * Updates the status of a message (sent, delivered, read) +// * +// * @param messageId The ID of the message to update +// * @param status The new status to set +// * @return Result containing the updated message details or error +// */ +// suspend fun updateMessageStatus( +// messageId: Int, +// status: String +// ): Result { +// return try { +// val requestBody = UpdateChatRequest( +// id = messageId, +// status = status +// ) +// +// val response = apiService.updateChatStatus(requestBody) +// +// if (response.isSuccessful) { +// response.body()?.let { +// Result.Success(it) +// } ?: Result.Error(Exception("Update status response is empty")) +// } else { +// Result.Error(Exception(response.errorBody()?.string() ?: "Unknown error")) +// } +// } catch (e: Exception) { +// Result.Error(e) +// } +// } +// +// /** +// * Gets the chat history for a specific chat room +// * +// * @param chatRoomId The ID of the chat room +// * @return Result containing the list of chat messages or error +// */ +// suspend fun getChatHistory(chatRoomId: Int): Result { +// return try { +// val response = apiService.getChatDetail(chatRoomId) +// +// if (response.isSuccessful) { +// response.body()?.let { +// Result.Success(it) +// } ?: Result.Error(Exception("Chat history response is empty")) +// } else { +// Result.Error(Exception(response.errorBody()?.string() ?: "Unknown error")) +// } +// } catch (e: Exception) { +// Result.Error(e) +// } +// } } diff --git a/app/src/main/java/com/alya/ecommerce_serang/di/ChatModule.kt b/app/src/main/java/com/alya/ecommerce_serang/di/ChatModule.kt index 154f9d8..89d5d8b 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/di/ChatModule.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/di/ChatModule.kt @@ -1,6 +1,7 @@ package com.alya.ecommerce_serang.di import com.alya.ecommerce_serang.data.api.retrofit.ApiService +import com.alya.ecommerce_serang.data.repository.ChatRepository import com.alya.ecommerce_serang.data.repository.UserRepository import com.alya.ecommerce_serang.ui.chat.SocketIOService import com.alya.ecommerce_serang.utils.SessionManager @@ -16,7 +17,13 @@ object ChatModule { @Provides @Singleton - fun provideChatRepository(apiService: ApiService): UserRepository { + fun provideChatRepository(apiService: ApiService): ChatRepository { + return ChatRepository(apiService) + } + + @Provides + @Singleton + fun provideUserRepository(apiService: ApiService): UserRepository { return UserRepository(apiService) } diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/chat/ChatActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/chat/ChatActivity.kt index ac62ef3..619185e 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/chat/ChatActivity.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/chat/ChatActivity.kt @@ -27,6 +27,8 @@ import androidx.lifecycle.Observer import androidx.recyclerview.widget.LinearLayoutManager import com.alya.ecommerce_serang.BuildConfig.BASE_URL import com.alya.ecommerce_serang.R +import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig +import com.alya.ecommerce_serang.data.api.retrofit.ApiService import com.alya.ecommerce_serang.databinding.ActivityChatBinding import com.alya.ecommerce_serang.ui.auth.LoginActivity import com.alya.ecommerce_serang.utils.Constants @@ -47,6 +49,9 @@ class ChatActivity : AppCompatActivity() { @Inject lateinit var sessionManager: SessionManager + @Inject + lateinit var apiService: ApiService + private lateinit var chatAdapter: ChatAdapter private val viewModel: ChatViewModel by viewModels() @@ -92,8 +97,10 @@ class ChatActivity : AppCompatActivity() { setContentView(binding.root) sessionManager = SessionManager(this) + apiService = ApiConfig.getApiService(sessionManager) + Log.d("ChatActivity", "Token in storage: '${sessionManager.getToken()}'") - Log.d("ChatActivity", "User ID in storage: '${sessionManager.getUserId()}'") +// Log.d("ChatActivity", "User ID in storage: '${sessionManager.getUserId()}'") WindowCompat.setDecorFitsSystemWindows(window, false) enableEdgeToEdge() @@ -121,7 +128,6 @@ class ChatActivity : AppCompatActivity() { // Check if user is logged in - val userId = sessionManager.getUserId() val token = sessionManager.getToken() if (token.isEmpty()) { diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/chat/ChatAdapter.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/chat/ChatAdapter.kt index a0d5ffc..0e2f083 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/chat/ChatAdapter.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/chat/ChatAdapter.kt @@ -76,7 +76,7 @@ class ChatAdapter : ListAdapter(ChatMess binding.imgStatus.setImageResource(statusIcon) // Handle attachment if exists - if (message.attachment.isNotEmpty()) { + if (message.attachment?.isNotEmpty() == true) { binding.imgAttachment.visibility = View.VISIBLE Glide.with(binding.root.context) .load(BASE_URL + message.attachment) @@ -101,7 +101,7 @@ class ChatAdapter : ListAdapter(ChatMess binding.tvTimestamp.text = message.time // Handle attachment if exists - if (message.attachment.isNotEmpty()) { + if (message.attachment?.isNotEmpty() == true) { binding.imgAttachment.visibility = View.VISIBLE Glide.with(binding.root.context) .load(BASE_URL + message.attachment) diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/chat/ChatListFragment.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/chat/ChatListFragment.kt index a43fe72..38979d2 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/chat/ChatListFragment.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/chat/ChatListFragment.kt @@ -8,7 +8,7 @@ import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig -import com.alya.ecommerce_serang.data.repository.UserRepository +import com.alya.ecommerce_serang.data.repository.ChatRepository import com.alya.ecommerce_serang.databinding.FragmentChatListBinding import com.alya.ecommerce_serang.utils.BaseViewModelFactory import com.alya.ecommerce_serang.utils.SessionManager @@ -23,8 +23,8 @@ class ChatListFragment : Fragment() { private val viewModel: com.alya.ecommerce_serang.ui.chat.ChatViewModel by viewModels { BaseViewModelFactory { val apiService = ApiConfig.getApiService(sessionManager) - val userRepository = UserRepository(apiService) - ChatViewModel(userRepository, socketService, sessionManager) + val chatRepository = ChatRepository(apiService) + ChatViewModel(chatRepository, socketService, sessionManager) } } override fun onCreate(savedInstanceState: Bundle?) { diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/chat/ChatViewModel.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/chat/ChatViewModel.kt index bcabe72..1ebaca8 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/chat/ChatViewModel.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/chat/ChatViewModel.kt @@ -7,8 +7,8 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.alya.ecommerce_serang.data.api.response.chat.ChatItem import com.alya.ecommerce_serang.data.api.response.chat.ChatLine +import com.alya.ecommerce_serang.data.repository.ChatRepository import com.alya.ecommerce_serang.data.repository.Result -import com.alya.ecommerce_serang.data.repository.UserRepository import com.alya.ecommerce_serang.utils.Constants import com.alya.ecommerce_serang.utils.SessionManager import dagger.hilt.android.lifecycle.HiltViewModel @@ -20,7 +20,7 @@ import javax.inject.Inject @HiltViewModel class ChatViewModel @Inject constructor( - private val chatRepository: UserRepository, + private val chatRepository: ChatRepository, private val socketService: SocketIOService, private val sessionManager: SessionManager ) : ViewModel() { @@ -37,10 +37,9 @@ class ChatViewModel @Inject constructor( // Store and product parameters private var storeId: Int = 0 private var productId: Int = 0 - private var currentUserId: Int? = 0 + private var currentUserId: Int? = null private var defaultUserId: Int = 0 - // Product details for display private var productName: String = "" private var productPrice: String = "" @@ -52,7 +51,7 @@ class ChatViewModel @Inject constructor( private var selectedImageFile: File? = null init { - // Try to get current user ID from SessionManager + // Try to get current user ID from the repository viewModelScope.launch { when (val result = chatRepository.fetchUserProfile()) { is Result.Success -> { @@ -60,7 +59,7 @@ class ChatViewModel @Inject constructor( Log.e(TAG, "User ID: $currentUserId") // Move the validation and subsequent logic inside the coroutine - if (currentUserId == 0) { + if (currentUserId == null || currentUserId == 0) { Log.e(TAG, "Error: User ID is not set or invalid") updateState { it.copy(error = "User authentication error. Please login again.") } } else { @@ -188,7 +187,7 @@ class ChatViewModel @Inject constructor( /** * Loads chat history */ - fun loadChatHistory(chatRoomId : Int) { + fun loadChatHistory(chatRoomId: Int) { if (chatRoomId <= 0) { Log.e(TAG, "Cannot load chat history: Chat room ID is 0") return @@ -198,7 +197,7 @@ class ChatViewModel @Inject constructor( updateState { it.copy(isLoading = true) } when (val result = chatRepository.getChatHistory(chatRoomId)) { - is com.alya.ecommerce_serang.data.repository.Result.Success -> { + is Result.Success -> { val messages = result.data.chat.map { chatLine -> convertChatLineToUiMessageHistory(chatLine) } @@ -218,7 +217,7 @@ class ChatViewModel @Inject constructor( .filter { it.senderId != currentUserId && it.status != Constants.STATUS_READ } .forEach { updateMessageStatus(it.id, Constants.STATUS_READ) } } - is com.alya.ecommerce_serang.data.repository.Result.Error -> { + is Result.Error -> { updateState { it.copy( isLoading = false, @@ -238,7 +237,7 @@ class ChatViewModel @Inject constructor( * Sends a chat message */ fun sendMessage(message: String) { - if (message.isBlank() && selectedImageFile == null) return + if (message.isBlank()) return if (storeId == 0 || productId == 0) { Log.e(TAG, "Cannot send message: Store ID or Product ID is 0") @@ -255,7 +254,7 @@ class ChatViewModel @Inject constructor( productId = productId, imageFile = selectedImageFile )) { - is com.alya.ecommerce_serang.data.repository.Result.Success -> { + is Result.Success -> { // Add new message to the list val chatLine = result.data.chatLine val newMessage = convertChatLineToUiMessage(chatLine) @@ -293,16 +292,22 @@ class ChatViewModel @Inject constructor( // Clear the image attachment selectedImageFile = null } - is com.alya.ecommerce_serang.data.repository.Result.Error -> { + is Result.Error -> { + val errorMsg = if (result.exception.message.isNullOrEmpty() || result.exception.message == "{}") { + "Failed to send message. Please try again." + } else { + result.exception.message + } + updateState { it.copy( isSending = false, - error = result.exception.message + error = errorMsg ) } Log.e(TAG, "Error sending message: ${result.exception.message}") } - is com.alya.ecommerce_serang.data.repository.Result.Loading -> { + is Result.Loading -> { updateState { it.copy(isSending = true) } } } @@ -317,7 +322,7 @@ class ChatViewModel @Inject constructor( try { val result = chatRepository.updateMessageStatus(messageId, status) - if (result is com.alya.ecommerce_serang.data.repository.Result.Success) { + if (result is Result.Success) { // Update local message status val currentMessages = _state.value?.messages ?: listOf() val updatedMessages = currentMessages.map { message -> @@ -330,7 +335,7 @@ class ChatViewModel @Inject constructor( updateState { it.copy(messages = updatedMessages) } Log.d(TAG, "Message status updated: $messageId -> $status") - } else if (result is com.alya.ecommerce_serang.data.repository.Result.Error) { + } else if (result is Result.Error) { Log.e(TAG, "Error updating message status: ${result.exception.message}") } } catch (e: Exception) { @@ -386,7 +391,7 @@ class ChatViewModel @Inject constructor( return ChatUiMessage( id = chatLine.id, message = chatLine.message, - attachment = chatLine.attachment, + attachment = chatLine.attachment ?: "", // Handle null attachment status = chatLine.status, time = formattedTime, isSentByMe = chatLine.senderId == currentUserId @@ -408,7 +413,7 @@ class ChatViewModel @Inject constructor( } return ChatUiMessage( - attachment = "", + attachment = chatItem.attachment, // Handle null attachment id = chatItem.id, message = chatItem.message, status = chatItem.status, @@ -451,7 +456,7 @@ data class ChatUiState( data class ChatUiMessage( val id: Int, val message: String, - val attachment: String, + val attachment: String?, val status: String, val time: String, val isSentByMe: Boolean