mirror of
https://github.com/shaulascr/ecommerce_serang.git
synced 2025-08-13 18:52:20 +00:00
fixed chat (hide attach + adjust attach product)
This commit is contained in:
@ -1,7 +1,6 @@
|
|||||||
package com.alya.ecommerce_serang.data.api.response.chat
|
package com.alya.ecommerce_serang.data.api.response.chat
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
data class ChatHistoryResponse(
|
data class ChatHistoryResponse(
|
||||||
|
|
||||||
@ -15,7 +14,7 @@ data class ChatHistoryResponse(
|
|||||||
data class ChatItem(
|
data class ChatItem(
|
||||||
|
|
||||||
@field:SerializedName("attachment")
|
@field:SerializedName("attachment")
|
||||||
val attachment: File? = null,
|
val attachment: String? = null,
|
||||||
|
|
||||||
@field:SerializedName("product_id")
|
@field:SerializedName("product_id")
|
||||||
val productId: Int,
|
val productId: Int,
|
||||||
|
@ -14,7 +14,7 @@ data class SendChatResponse(
|
|||||||
data class ChatLine(
|
data class ChatLine(
|
||||||
|
|
||||||
@field:SerializedName("attachment")
|
@field:SerializedName("attachment")
|
||||||
val attachment: String,
|
val attachment: String? = null,
|
||||||
|
|
||||||
@field:SerializedName("product_id")
|
@field:SerializedName("product_id")
|
||||||
val productId: Int,
|
val productId: Int,
|
||||||
|
@ -14,7 +14,7 @@ data class UpdateChatResponse(
|
|||||||
data class Address(
|
data class Address(
|
||||||
|
|
||||||
@field:SerializedName("attachment")
|
@field:SerializedName("attachment")
|
||||||
val attachment: Any,
|
val attachment: String? = null,
|
||||||
|
|
||||||
@field:SerializedName("product_id")
|
@field:SerializedName("product_id")
|
||||||
val productId: Int,
|
val productId: Int,
|
||||||
|
@ -223,12 +223,13 @@ interface ApiService {
|
|||||||
@Part chatimg: MultipartBody.Part?
|
@Part chatimg: MultipartBody.Part?
|
||||||
): Response<SendChatResponse>
|
): Response<SendChatResponse>
|
||||||
|
|
||||||
|
|
||||||
@PUT("chatstatus")
|
@PUT("chatstatus")
|
||||||
suspend fun updateChatStatus(
|
suspend fun updateChatStatus(
|
||||||
@Body request: UpdateChatRequest
|
@Body request: UpdateChatRequest
|
||||||
): Response<UpdateChatResponse>
|
): Response<UpdateChatResponse>
|
||||||
|
|
||||||
@GET("chatdetail/{chatRoomId}")
|
@GET("chat/{chatRoomId}")
|
||||||
suspend fun getChatDetail(
|
suspend fun getChatDetail(
|
||||||
@Path("chatRoomId") chatRoomId: Int
|
@Path("chatRoomId") chatRoomId: Int
|
||||||
): Response<ChatHistoryResponse>
|
): Response<ChatHistoryResponse>
|
||||||
|
@ -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<UserProfile?> {
|
||||||
|
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<SendChatResponse> {
|
||||||
|
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<UpdateChatResponse> {
|
||||||
|
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<ChatHistoryResponse> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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.LoginRequest
|
||||||
import com.alya.ecommerce_serang.data.api.dto.OtpRequest
|
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.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.dto.UserProfile
|
||||||
import com.alya.ecommerce_serang.data.api.response.auth.LoginResponse
|
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.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 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) {
|
class UserRepository(private val apiService: ApiService) {
|
||||||
|
|
||||||
@ -65,100 +56,101 @@ class UserRepository(private val apiService: ApiService) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun sendChatMessage(
|
// suspend fun sendChatMessage(
|
||||||
storeId: Int,
|
// storeId: Int,
|
||||||
message: String,
|
// message: String,
|
||||||
productId: Int,
|
// productId: Int,
|
||||||
imageFile: File? = null
|
// imageFile: File? = null
|
||||||
): Result<SendChatResponse> {
|
// ): Result<SendChatResponse> {
|
||||||
return try {
|
// return try {
|
||||||
// Create request bodies for text fields
|
// // Create multipart request builder
|
||||||
val storeIdBody = storeId.toString().toRequestBody("text/plain".toMediaTypeOrNull())
|
// val requestBodyBuilder = MultipartBody.Builder().setType(MultipartBody.FORM)
|
||||||
val messageBody = message.toRequestBody("text/plain".toMediaTypeOrNull())
|
//
|
||||||
val productIdBody = productId.toString().toRequestBody("text/plain".toMediaTypeOrNull())
|
// // Add text fields
|
||||||
|
// requestBodyBuilder.addFormDataPart("store_id", storeId.toString())
|
||||||
// Create multipart body for the image file
|
// requestBodyBuilder.addFormDataPart("message", message)
|
||||||
val imageMultipart = if (imageFile != null && imageFile.exists()) {
|
// requestBodyBuilder.addFormDataPart("product_id", productId.toString())
|
||||||
val requestFile = imageFile.asRequestBody("image/*".toMediaTypeOrNull())
|
//
|
||||||
MultipartBody.Part.createFormData("chatimg", imageFile.name, requestFile)
|
// // Add image if it exists
|
||||||
} else {
|
// if (imageFile != null && imageFile.exists()) {
|
||||||
// Create an empty part if no image is provided
|
// val requestFile = imageFile.asRequestBody("image/*".toMediaTypeOrNull())
|
||||||
val emptyRequest = "".toRequestBody("text/plain".toMediaTypeOrNull())
|
// requestBodyBuilder.addFormDataPart("chatimg", imageFile.name, requestFile)
|
||||||
MultipartBody.Part.createFormData("chatimg", "", emptyRequest)
|
// }
|
||||||
}
|
//
|
||||||
|
// // Build the final request body
|
||||||
// Make the API call
|
// val requestBody = requestBodyBuilder.build()
|
||||||
val response = apiService.sendChatLine(
|
//
|
||||||
storeId = storeIdBody,
|
// // Make the API call using a custom endpoint that takes a plain MultipartBody
|
||||||
message = messageBody,
|
// val response = apiService.sendChatLineWithBody(requestBody)
|
||||||
productId = productIdBody,
|
//
|
||||||
chatimg = imageMultipart
|
// if (response.isSuccessful) {
|
||||||
)
|
// response.body()?.let {
|
||||||
|
// Result.Success(it)
|
||||||
if (response.isSuccessful) {
|
// } ?: Result.Error(Exception("Send chat response is empty"))
|
||||||
response.body()?.let {
|
// } else {
|
||||||
Result.Success(it)
|
// val errorBody = response.errorBody()?.string() ?: "Unknown error"
|
||||||
} ?: Result.Error(Exception("Send chat response is empty"))
|
// Log.e("ChatRepository", "HTTP Error: ${response.code()}, Body: $errorBody")
|
||||||
} else {
|
// Result.Error(Exception("API Error: ${response.code()} - $errorBody"))
|
||||||
Result.Error(Exception(response.errorBody()?.string() ?: "Unknown error"))
|
// }
|
||||||
}
|
// } catch (e: Exception) {
|
||||||
} catch (e: Exception) {
|
// Log.e("ChatRepository", "Exception sending message", e)
|
||||||
Result.Error(e)
|
// e.printStackTrace()
|
||||||
}
|
// Result.Error(e)
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
/**
|
//
|
||||||
* Updates the status of a message (sent, delivered, read)
|
// /**
|
||||||
*
|
// * 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
|
// * @param messageId The ID of the message to update
|
||||||
* @return Result containing the updated message details or error
|
// * @param status The new status to set
|
||||||
*/
|
// * @return Result containing the updated message details or error
|
||||||
suspend fun updateMessageStatus(
|
// */
|
||||||
messageId: Int,
|
// suspend fun updateMessageStatus(
|
||||||
status: String
|
// messageId: Int,
|
||||||
): Result<UpdateChatResponse> {
|
// status: String
|
||||||
return try {
|
// ): Result<UpdateChatResponse> {
|
||||||
val requestBody = UpdateChatRequest(
|
// return try {
|
||||||
id = messageId,
|
// val requestBody = UpdateChatRequest(
|
||||||
status = status
|
// id = messageId,
|
||||||
)
|
// status = status
|
||||||
|
// )
|
||||||
val response = apiService.updateChatStatus(requestBody)
|
//
|
||||||
|
// val response = apiService.updateChatStatus(requestBody)
|
||||||
if (response.isSuccessful) {
|
//
|
||||||
response.body()?.let {
|
// if (response.isSuccessful) {
|
||||||
Result.Success(it)
|
// response.body()?.let {
|
||||||
} ?: Result.Error(Exception("Update status response is empty"))
|
// Result.Success(it)
|
||||||
} else {
|
// } ?: Result.Error(Exception("Update status response is empty"))
|
||||||
Result.Error(Exception(response.errorBody()?.string() ?: "Unknown error"))
|
// } else {
|
||||||
}
|
// Result.Error(Exception(response.errorBody()?.string() ?: "Unknown error"))
|
||||||
} catch (e: Exception) {
|
// }
|
||||||
Result.Error(e)
|
// } catch (e: Exception) {
|
||||||
}
|
// Result.Error(e)
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
/**
|
//
|
||||||
* Gets the chat history for a specific chat room
|
// /**
|
||||||
*
|
// * 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
|
// * @param chatRoomId The ID of the chat room
|
||||||
*/
|
// * @return Result containing the list of chat messages or error
|
||||||
suspend fun getChatHistory(chatRoomId: Int): Result<ChatHistoryResponse> {
|
// */
|
||||||
return try {
|
// suspend fun getChatHistory(chatRoomId: Int): Result<ChatHistoryResponse> {
|
||||||
val response = apiService.getChatDetail(chatRoomId)
|
// return try {
|
||||||
|
// val response = apiService.getChatDetail(chatRoomId)
|
||||||
if (response.isSuccessful) {
|
//
|
||||||
response.body()?.let {
|
// if (response.isSuccessful) {
|
||||||
Result.Success(it)
|
// response.body()?.let {
|
||||||
} ?: Result.Error(Exception("Chat history response is empty"))
|
// Result.Success(it)
|
||||||
} else {
|
// } ?: Result.Error(Exception("Chat history response is empty"))
|
||||||
Result.Error(Exception(response.errorBody()?.string() ?: "Unknown error"))
|
// } else {
|
||||||
}
|
// Result.Error(Exception(response.errorBody()?.string() ?: "Unknown error"))
|
||||||
} catch (e: Exception) {
|
// }
|
||||||
Result.Error(e)
|
// } catch (e: Exception) {
|
||||||
}
|
// Result.Error(e)
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.alya.ecommerce_serang.di
|
package com.alya.ecommerce_serang.di
|
||||||
|
|
||||||
import com.alya.ecommerce_serang.data.api.retrofit.ApiService
|
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.data.repository.UserRepository
|
||||||
import com.alya.ecommerce_serang.ui.chat.SocketIOService
|
import com.alya.ecommerce_serang.ui.chat.SocketIOService
|
||||||
import com.alya.ecommerce_serang.utils.SessionManager
|
import com.alya.ecommerce_serang.utils.SessionManager
|
||||||
@ -16,7 +17,13 @@ object ChatModule {
|
|||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideChatRepository(apiService: ApiService): UserRepository {
|
fun provideChatRepository(apiService: ApiService): ChatRepository {
|
||||||
|
return ChatRepository(apiService)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun provideUserRepository(apiService: ApiService): UserRepository {
|
||||||
return UserRepository(apiService)
|
return UserRepository(apiService)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +27,8 @@ import androidx.lifecycle.Observer
|
|||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.alya.ecommerce_serang.BuildConfig.BASE_URL
|
import com.alya.ecommerce_serang.BuildConfig.BASE_URL
|
||||||
import com.alya.ecommerce_serang.R
|
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.databinding.ActivityChatBinding
|
||||||
import com.alya.ecommerce_serang.ui.auth.LoginActivity
|
import com.alya.ecommerce_serang.ui.auth.LoginActivity
|
||||||
import com.alya.ecommerce_serang.utils.Constants
|
import com.alya.ecommerce_serang.utils.Constants
|
||||||
@ -47,6 +49,9 @@ class ChatActivity : AppCompatActivity() {
|
|||||||
@Inject
|
@Inject
|
||||||
lateinit var sessionManager: SessionManager
|
lateinit var sessionManager: SessionManager
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var apiService: ApiService
|
||||||
|
|
||||||
private lateinit var chatAdapter: ChatAdapter
|
private lateinit var chatAdapter: ChatAdapter
|
||||||
|
|
||||||
private val viewModel: ChatViewModel by viewModels()
|
private val viewModel: ChatViewModel by viewModels()
|
||||||
@ -92,8 +97,10 @@ class ChatActivity : AppCompatActivity() {
|
|||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
|
|
||||||
sessionManager = SessionManager(this)
|
sessionManager = SessionManager(this)
|
||||||
|
apiService = ApiConfig.getApiService(sessionManager)
|
||||||
|
|
||||||
Log.d("ChatActivity", "Token in storage: '${sessionManager.getToken()}'")
|
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)
|
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||||
enableEdgeToEdge()
|
enableEdgeToEdge()
|
||||||
@ -121,7 +128,6 @@ class ChatActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
|
|
||||||
// Check if user is logged in
|
// Check if user is logged in
|
||||||
val userId = sessionManager.getUserId()
|
|
||||||
val token = sessionManager.getToken()
|
val token = sessionManager.getToken()
|
||||||
|
|
||||||
if (token.isEmpty()) {
|
if (token.isEmpty()) {
|
||||||
|
@ -76,7 +76,7 @@ class ChatAdapter : ListAdapter<ChatUiMessage, RecyclerView.ViewHolder>(ChatMess
|
|||||||
binding.imgStatus.setImageResource(statusIcon)
|
binding.imgStatus.setImageResource(statusIcon)
|
||||||
|
|
||||||
// Handle attachment if exists
|
// Handle attachment if exists
|
||||||
if (message.attachment.isNotEmpty()) {
|
if (message.attachment?.isNotEmpty() == true) {
|
||||||
binding.imgAttachment.visibility = View.VISIBLE
|
binding.imgAttachment.visibility = View.VISIBLE
|
||||||
Glide.with(binding.root.context)
|
Glide.with(binding.root.context)
|
||||||
.load(BASE_URL + message.attachment)
|
.load(BASE_URL + message.attachment)
|
||||||
@ -101,7 +101,7 @@ class ChatAdapter : ListAdapter<ChatUiMessage, RecyclerView.ViewHolder>(ChatMess
|
|||||||
binding.tvTimestamp.text = message.time
|
binding.tvTimestamp.text = message.time
|
||||||
|
|
||||||
// Handle attachment if exists
|
// Handle attachment if exists
|
||||||
if (message.attachment.isNotEmpty()) {
|
if (message.attachment?.isNotEmpty() == true) {
|
||||||
binding.imgAttachment.visibility = View.VISIBLE
|
binding.imgAttachment.visibility = View.VISIBLE
|
||||||
Glide.with(binding.root.context)
|
Glide.with(binding.root.context)
|
||||||
.load(BASE_URL + message.attachment)
|
.load(BASE_URL + message.attachment)
|
||||||
|
@ -8,7 +8,7 @@ import android.view.ViewGroup
|
|||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
|
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.databinding.FragmentChatListBinding
|
||||||
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
|
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
|
||||||
import com.alya.ecommerce_serang.utils.SessionManager
|
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 {
|
private val viewModel: com.alya.ecommerce_serang.ui.chat.ChatViewModel by viewModels {
|
||||||
BaseViewModelFactory {
|
BaseViewModelFactory {
|
||||||
val apiService = ApiConfig.getApiService(sessionManager)
|
val apiService = ApiConfig.getApiService(sessionManager)
|
||||||
val userRepository = UserRepository(apiService)
|
val chatRepository = ChatRepository(apiService)
|
||||||
ChatViewModel(userRepository, socketService, sessionManager)
|
ChatViewModel(chatRepository, socketService, sessionManager)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
@ -7,8 +7,8 @@ import androidx.lifecycle.ViewModel
|
|||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.alya.ecommerce_serang.data.api.response.chat.ChatItem
|
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.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.Result
|
||||||
import com.alya.ecommerce_serang.data.repository.UserRepository
|
|
||||||
import com.alya.ecommerce_serang.utils.Constants
|
import com.alya.ecommerce_serang.utils.Constants
|
||||||
import com.alya.ecommerce_serang.utils.SessionManager
|
import com.alya.ecommerce_serang.utils.SessionManager
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
@ -20,7 +20,7 @@ import javax.inject.Inject
|
|||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class ChatViewModel @Inject constructor(
|
class ChatViewModel @Inject constructor(
|
||||||
private val chatRepository: UserRepository,
|
private val chatRepository: ChatRepository,
|
||||||
private val socketService: SocketIOService,
|
private val socketService: SocketIOService,
|
||||||
private val sessionManager: SessionManager
|
private val sessionManager: SessionManager
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
@ -37,10 +37,9 @@ class ChatViewModel @Inject constructor(
|
|||||||
// Store and product parameters
|
// Store and product parameters
|
||||||
private var storeId: Int = 0
|
private var storeId: Int = 0
|
||||||
private var productId: Int = 0
|
private var productId: Int = 0
|
||||||
private var currentUserId: Int? = 0
|
private var currentUserId: Int? = null
|
||||||
private var defaultUserId: Int = 0
|
private var defaultUserId: Int = 0
|
||||||
|
|
||||||
|
|
||||||
// Product details for display
|
// Product details for display
|
||||||
private var productName: String = ""
|
private var productName: String = ""
|
||||||
private var productPrice: String = ""
|
private var productPrice: String = ""
|
||||||
@ -52,7 +51,7 @@ class ChatViewModel @Inject constructor(
|
|||||||
private var selectedImageFile: File? = null
|
private var selectedImageFile: File? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// Try to get current user ID from SessionManager
|
// Try to get current user ID from the repository
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
when (val result = chatRepository.fetchUserProfile()) {
|
when (val result = chatRepository.fetchUserProfile()) {
|
||||||
is Result.Success -> {
|
is Result.Success -> {
|
||||||
@ -60,7 +59,7 @@ class ChatViewModel @Inject constructor(
|
|||||||
Log.e(TAG, "User ID: $currentUserId")
|
Log.e(TAG, "User ID: $currentUserId")
|
||||||
|
|
||||||
// Move the validation and subsequent logic inside the coroutine
|
// 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")
|
Log.e(TAG, "Error: User ID is not set or invalid")
|
||||||
updateState { it.copy(error = "User authentication error. Please login again.") }
|
updateState { it.copy(error = "User authentication error. Please login again.") }
|
||||||
} else {
|
} else {
|
||||||
@ -188,7 +187,7 @@ class ChatViewModel @Inject constructor(
|
|||||||
/**
|
/**
|
||||||
* Loads chat history
|
* Loads chat history
|
||||||
*/
|
*/
|
||||||
fun loadChatHistory(chatRoomId : Int) {
|
fun loadChatHistory(chatRoomId: Int) {
|
||||||
if (chatRoomId <= 0) {
|
if (chatRoomId <= 0) {
|
||||||
Log.e(TAG, "Cannot load chat history: Chat room ID is 0")
|
Log.e(TAG, "Cannot load chat history: Chat room ID is 0")
|
||||||
return
|
return
|
||||||
@ -198,7 +197,7 @@ class ChatViewModel @Inject constructor(
|
|||||||
updateState { it.copy(isLoading = true) }
|
updateState { it.copy(isLoading = true) }
|
||||||
|
|
||||||
when (val result = chatRepository.getChatHistory(chatRoomId)) {
|
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 ->
|
val messages = result.data.chat.map { chatLine ->
|
||||||
convertChatLineToUiMessageHistory(chatLine)
|
convertChatLineToUiMessageHistory(chatLine)
|
||||||
}
|
}
|
||||||
@ -218,7 +217,7 @@ class ChatViewModel @Inject constructor(
|
|||||||
.filter { it.senderId != currentUserId && it.status != Constants.STATUS_READ }
|
.filter { it.senderId != currentUserId && it.status != Constants.STATUS_READ }
|
||||||
.forEach { updateMessageStatus(it.id, Constants.STATUS_READ) }
|
.forEach { updateMessageStatus(it.id, Constants.STATUS_READ) }
|
||||||
}
|
}
|
||||||
is com.alya.ecommerce_serang.data.repository.Result.Error -> {
|
is Result.Error -> {
|
||||||
updateState {
|
updateState {
|
||||||
it.copy(
|
it.copy(
|
||||||
isLoading = false,
|
isLoading = false,
|
||||||
@ -238,7 +237,7 @@ class ChatViewModel @Inject constructor(
|
|||||||
* Sends a chat message
|
* Sends a chat message
|
||||||
*/
|
*/
|
||||||
fun sendMessage(message: String) {
|
fun sendMessage(message: String) {
|
||||||
if (message.isBlank() && selectedImageFile == null) return
|
if (message.isBlank()) return
|
||||||
|
|
||||||
if (storeId == 0 || productId == 0) {
|
if (storeId == 0 || productId == 0) {
|
||||||
Log.e(TAG, "Cannot send message: Store ID or Product ID is 0")
|
Log.e(TAG, "Cannot send message: Store ID or Product ID is 0")
|
||||||
@ -255,7 +254,7 @@ class ChatViewModel @Inject constructor(
|
|||||||
productId = productId,
|
productId = productId,
|
||||||
imageFile = selectedImageFile
|
imageFile = selectedImageFile
|
||||||
)) {
|
)) {
|
||||||
is com.alya.ecommerce_serang.data.repository.Result.Success -> {
|
is Result.Success -> {
|
||||||
// Add new message to the list
|
// Add new message to the list
|
||||||
val chatLine = result.data.chatLine
|
val chatLine = result.data.chatLine
|
||||||
val newMessage = convertChatLineToUiMessage(chatLine)
|
val newMessage = convertChatLineToUiMessage(chatLine)
|
||||||
@ -293,16 +292,22 @@ class ChatViewModel @Inject constructor(
|
|||||||
// Clear the image attachment
|
// Clear the image attachment
|
||||||
selectedImageFile = null
|
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 {
|
updateState {
|
||||||
it.copy(
|
it.copy(
|
||||||
isSending = false,
|
isSending = false,
|
||||||
error = result.exception.message
|
error = errorMsg
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Log.e(TAG, "Error sending message: ${result.exception.message}")
|
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) }
|
updateState { it.copy(isSending = true) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -317,7 +322,7 @@ class ChatViewModel @Inject constructor(
|
|||||||
try {
|
try {
|
||||||
val result = chatRepository.updateMessageStatus(messageId, status)
|
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
|
// Update local message status
|
||||||
val currentMessages = _state.value?.messages ?: listOf()
|
val currentMessages = _state.value?.messages ?: listOf()
|
||||||
val updatedMessages = currentMessages.map { message ->
|
val updatedMessages = currentMessages.map { message ->
|
||||||
@ -330,7 +335,7 @@ class ChatViewModel @Inject constructor(
|
|||||||
updateState { it.copy(messages = updatedMessages) }
|
updateState { it.copy(messages = updatedMessages) }
|
||||||
|
|
||||||
Log.d(TAG, "Message status updated: $messageId -> $status")
|
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}")
|
Log.e(TAG, "Error updating message status: ${result.exception.message}")
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@ -386,7 +391,7 @@ class ChatViewModel @Inject constructor(
|
|||||||
return ChatUiMessage(
|
return ChatUiMessage(
|
||||||
id = chatLine.id,
|
id = chatLine.id,
|
||||||
message = chatLine.message,
|
message = chatLine.message,
|
||||||
attachment = chatLine.attachment,
|
attachment = chatLine.attachment ?: "", // Handle null attachment
|
||||||
status = chatLine.status,
|
status = chatLine.status,
|
||||||
time = formattedTime,
|
time = formattedTime,
|
||||||
isSentByMe = chatLine.senderId == currentUserId
|
isSentByMe = chatLine.senderId == currentUserId
|
||||||
@ -408,7 +413,7 @@ class ChatViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return ChatUiMessage(
|
return ChatUiMessage(
|
||||||
attachment = "",
|
attachment = chatItem.attachment, // Handle null attachment
|
||||||
id = chatItem.id,
|
id = chatItem.id,
|
||||||
message = chatItem.message,
|
message = chatItem.message,
|
||||||
status = chatItem.status,
|
status = chatItem.status,
|
||||||
@ -451,7 +456,7 @@ data class ChatUiState(
|
|||||||
data class ChatUiMessage(
|
data class ChatUiMessage(
|
||||||
val id: Int,
|
val id: Int,
|
||||||
val message: String,
|
val message: String,
|
||||||
val attachment: String,
|
val attachment: String?,
|
||||||
val status: String,
|
val status: String,
|
||||||
val time: String,
|
val time: String,
|
||||||
val isSentByMe: Boolean
|
val isSentByMe: Boolean
|
||||||
|
Reference in New Issue
Block a user