diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 54fdf3b..4455d19 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -82,11 +82,11 @@
-
+
+
+
+
+
,
- @field:SerializedName("message")
+ @field:SerializedName("message")
val message: String
)
data class AddressesItem(
+ @field:SerializedName("village_id")
+ val villageId: String,
+
@field:SerializedName("is_store_location")
val isStoreLocation: Boolean,
@@ -23,7 +26,7 @@ data class AddressesItem(
val userId: Int,
@field:SerializedName("province_id")
- val provinceId: Int,
+ val provinceId: String,
@field:SerializedName("phone")
val phone: String,
@@ -50,5 +53,5 @@ data class AddressesItem(
val longitude: String,
@field:SerializedName("city_id")
- val cityId: Int
+ val cityId: String
)
diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/repository/MyStoreRepository.kt b/app/src/main/java/com/alya/ecommerce_serang/data/repository/MyStoreRepository.kt
index 0da1f9b..0100c5e 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/data/repository/MyStoreRepository.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/data/repository/MyStoreRepository.kt
@@ -1,6 +1,7 @@
package com.alya.ecommerce_serang.data.repository
import android.util.Log
+import com.alya.ecommerce_serang.data.api.dto.ProductsItem
import com.alya.ecommerce_serang.data.api.dto.Store
import com.alya.ecommerce_serang.data.api.response.auth.ListStoreTypeResponse
import com.alya.ecommerce_serang.data.api.response.customer.product.StoreResponse
@@ -123,6 +124,20 @@ class MyStoreRepository(private val apiService: ApiService) {
}
}
+ suspend fun fetchMyStoreProducts(): List {
+ return try {
+ val response = apiService.getStoreProduct()
+ if (response.isSuccessful) {
+ response.body()?.products?.filterNotNull() ?: emptyList()
+ } else {
+ throw Exception("Failed to fetch store products: ${response.message()}")
+ }
+ } catch (e: Exception) {
+ Log.e("ProductRepository", "Error fetching store products", e)
+ throw e
+ }
+ }
+
// private fun fetchBalance() {
// showLoading(true)
// lifecycleScope.launch {
diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/auth/fragments/RegisterStep3Fragment.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/auth/fragments/RegisterStep3Fragment.kt
index f5757a1..7c9df73 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/ui/auth/fragments/RegisterStep3Fragment.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/ui/auth/fragments/RegisterStep3Fragment.kt
@@ -30,6 +30,7 @@ import com.alya.ecommerce_serang.utils.BaseViewModelFactory
import com.alya.ecommerce_serang.utils.SessionManager
import com.alya.ecommerce_serang.utils.viewmodel.RegisterViewModel
import com.google.android.material.progressindicator.LinearProgressIndicator
+import com.google.gson.Gson
class RegisterStep3Fragment : Fragment() {
private var _binding: FragmentRegisterStep3Binding? = null
@@ -376,6 +377,8 @@ class RegisterStep3Fragment : Fragment() {
val subDistrict = registerViewModel.selectedSubdistrict.toString()
val postalCode = registerViewModel.selectedPostalCode.toString()
+ val villageId = registerViewModel.selectedVillages ?: ""
+
Log.d(TAG, "Address data - Street: $street, SubDistrict: $subDistrict, PostalCode: $postalCode")
Log.d(TAG, "Address data - Recipient: $recipient, Phone: $phone")
Log.d(TAG, "Address data - ProvinceId: $provinceId, CityId: $cityId")
@@ -383,21 +386,25 @@ class RegisterStep3Fragment : Fragment() {
// Create address request
val addressRequest = CreateAddressRequest(
+ userId = user.id, // must match the type expected in the DB
lat = defaultLatitude,
long = defaultLongitude,
street = street,
subDistrict = subDistrict,
- cityId = cityId,
+ cityId = cityId, // ⚠️ Make sure this is Int
provId = provinceId,
postCode = postalCode,
+ idVillage = villageId, // Or provide a real ID if needed
detailAddress = street,
- userId = userId,
+ isStoreLocation = false,
recipient = recipient,
- phone = phone,
- isStoreLocation = false
+ phone = phone
)
Log.d(TAG, "Address request created: $addressRequest")
+ val gson = Gson()
+ val jsonString = gson.toJson(addressRequest)
+ Log.d(TAG, "Request JSON: $jsonString")
// Show loading
binding.progressBar.visibility = View.VISIBLE
diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/cart/CartActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/cart/CartActivity.kt
index fe0d9b3..6a6c69d 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/ui/cart/CartActivity.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/ui/cart/CartActivity.kt
@@ -146,7 +146,9 @@ class CartActivity : AppCompatActivity() {
private fun observeViewModel() {
viewModel.cartItems.observe(this) { cartItems ->
if (cartItems.isNullOrEmpty()) {
+ binding.emptyCart.visibility = View.VISIBLE
showEmptyState(true)
+
} else {
showEmptyState(false)
storeAdapter.submitList(cartItems)
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 e677422..cf9fa48 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,7 @@ import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsAnimationCompat
import androidx.core.view.WindowInsetsCompat
import androidx.lifecycle.Observer
+import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import com.alya.ecommerce_serang.BuildConfig.BASE_URL
import com.alya.ecommerce_serang.R
@@ -170,8 +171,7 @@ class ChatActivity : AppCompatActivity() {
// If opened from ChatListFragment with a valid chatRoomId
if (chatRoomId > 0) {
- // Directly set the chatRoomId and load chat history
- viewModel._chatRoomId.value = chatRoomId
+ viewModel.setChatRoomId(chatRoomId)
}
}
@@ -405,68 +405,71 @@ class ChatActivity : AppCompatActivity() {
}
})
- viewModel.state.observe(this, Observer { state ->
- Log.d(TAG, "State updated - Messages: ${state.messages.size}")
+ lifecycleScope.launchWhenStarted {
+ viewModel.state.collect() { state ->
+ Log.d(TAG, "State updated - Messages: ${state.messages.size}")
- // Update messages
- val previousCount = chatAdapter.itemCount
+ // Update messages
+ val previousCount = chatAdapter.itemCount
- val displayItems = viewModel.getDisplayItems()
+ val displayItems = viewModel.getDisplayItems()
- chatAdapter.submitList(displayItems) {
- Log.d(TAG, "Messages submitted to adapter")
- // Only auto-scroll for new messages or initial load
- if (previousCount == 0 || state.messages.size > previousCount) {
- scrollToBottomInstant()
- }
- }
-
- // layout attach product
- if (!state.productName.isNullOrEmpty()) {
- binding.tvProductName.text = state.productName
- binding.tvProductPrice.text = state.productPrice
- binding.ratingBar.rating = state.productRating
- binding.tvRating.text = state.productRating.toString()
- binding.tvSellerName.text = state.storeName
- binding.tvStoreName.text = state.storeName
-
- val fullImageUrl = when (val img = state.productImageUrl) {
- is String -> {
- if (img.startsWith("/")) BASE_URL + img.substring(1) else img
+ chatAdapter.submitList(displayItems) {
+ Log.d(TAG, "Messages submitted to adapter")
+ // Only auto-scroll for new messages or initial load
+ if (previousCount == 0 || state.messages.size > previousCount) {
+ scrollToBottomInstant()
}
- else -> R.drawable.placeholder_image
}
- if (!state.productImageUrl.isNullOrEmpty()) {
- Glide.with(this@ChatActivity)
- .load(fullImageUrl)
- .centerCrop()
- .placeholder(R.drawable.placeholder_image)
- .error(R.drawable.placeholder_image)
- .into(binding.imgProduct)
+ // layout attach product
+ if (!state.productName.isNullOrEmpty()) {
+ binding.tvProductName.text = state.productName
+ binding.tvProductPrice.text = state.productPrice
+ binding.ratingBar.rating = state.productRating
+ binding.tvRating.text = state.productRating.toString()
+ binding.tvSellerName.text = state.storeName
+ binding.tvStoreName.text = state.storeName
+
+ val fullImageUrl = when (val img = state.productImageUrl) {
+ is String -> {
+ if (img.startsWith("/")) BASE_URL + img.substring(1) else img
+ }
+
+ else -> R.drawable.placeholder_image
+ }
+
+ if (!state.productImageUrl.isNullOrEmpty()) {
+ Glide.with(this@ChatActivity)
+ .load(fullImageUrl)
+ .centerCrop()
+ .placeholder(R.drawable.placeholder_image)
+ .error(R.drawable.placeholder_image)
+ .into(binding.imgProduct)
+ }
+ updateProductCardUI(state.hasProductAttachment)
+
+ binding.productContainer.visibility = View.GONE
+ } else {
+ binding.productContainer.visibility = View.GONE
}
- updateProductCardUI(state.hasProductAttachment)
- binding.productContainer.visibility = View.GONE
- } else {
- binding.productContainer.visibility = View.GONE
+ updateInputHint(state)
+
+ // Update attachment hint
+ if (state.hasAttachment) {
+ binding.layoutAttachImage.visibility = View.VISIBLE
+ } else {
+ binding.editTextMessage.hint = getString(R.string.write_message)
+ }
+
+ // Show error if any
+ state.error?.let { error ->
+ Toast.makeText(this@ChatActivity, error, Toast.LENGTH_SHORT).show()
+ viewModel.clearError()
+ }
}
-
- updateInputHint(state)
-
- // Update attachment hint
- if (state.hasAttachment) {
- binding.layoutAttachImage.visibility = View.VISIBLE
- } else {
- binding.editTextMessage.hint = getString(R.string.write_message)
- }
-
- // Show error if any
- state.error?.let { error ->
- Toast.makeText(this@ChatActivity, error, Toast.LENGTH_SHORT).show()
- viewModel.clearError()
- }
- })
+ }
}
private fun updateInputHint(state: ChatUiState) {
@@ -492,7 +495,7 @@ class ChatActivity : AppCompatActivity() {
Toast.makeText(this, "Opening: ${productInfo.productName}", Toast.LENGTH_SHORT).show()
// You can navigate to product detail here
- navigateToProductDetail(productInfo.productId)
+ navigateToProductDetail(productInfo.productId)
}
private fun navigateToProductDetail(productId: Int) {
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 8a136f0..68cd079 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
@@ -209,7 +209,7 @@ class ChatAdapter(
binding.tvProductPrice.text = product.productPrice
// Load product image
- val fullImageUrl = if (product.productImage.startsWith("/")) {
+ val fullImageUrl = if (product.productImage!!.startsWith("/")) {
BASE_URL + product.productImage.substring(1)
} else {
product.productImage
@@ -246,7 +246,7 @@ class ChatAdapter(
binding.tvProductPrice.text = product.productPrice
// Load product image
- val fullImageUrl = if (product.productImage.startsWith("/")) {
+ val fullImageUrl = if (product.productImage!!.startsWith("/")) {
BASE_URL + product.productImage.substring(1)
} else {
product.productImage
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 8a496cb..8a6d001 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
@@ -14,6 +14,9 @@ import com.alya.ecommerce_serang.data.repository.Result
import com.alya.ecommerce_serang.utils.Constants
import com.alya.ecommerce_serang.utils.SessionManager
import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import java.io.File
import java.text.SimpleDateFormat
@@ -56,9 +59,9 @@ class ChatViewModel @Inject constructor(
// Product attachment flag
private var shouldAttachProduct = false
- // UI state using LiveData
- private val _state = MutableLiveData(ChatUiState())
- val state: LiveData = _state
+ // use state for more seamless responsive
+ private val _state = MutableStateFlow(ChatUiState())
+ val state: StateFlow = _state
private val _isLoading = MutableLiveData()
val isLoading: LiveData = _isLoading
@@ -93,6 +96,8 @@ class ChatViewModel @Inject constructor(
init {
Log.d(TAG, "ChatViewModel initialized")
+ socketService.connect() // 🛠 force connection
+ setupSocketListeners() // 🛠 always listen, even before user data
initializeUser()
}
@@ -113,6 +118,7 @@ class ChatViewModel @Inject constructor(
updateState { it.copy(error = "User authentication error. Please login again.") }
} else {
Log.d(TAG, "Setting up socket listeners...")
+ socketService.connect()
setupSocketListeners()
}
}
@@ -231,26 +237,116 @@ class ChatViewModel @Inject constructor(
if (connectionState is ConnectionState.Connected) {
Log.d(TAG, "Socket connected, joining room...")
- socketService.joinRoom()
+ val roomId = _chatRoomId.value
+ if (roomId != null && roomId > 0) {
+ socketService.joinRoom(roomId)
+ }
}
}
}
+// viewModelScope.launch {
+// socketService.newMessages.collect { chatLine ->
+// chatLine?.let {
+// Log.d(TAG, "NEW message received in ViewModel: ${it.message}")
+// val updatedMessages = _state.value.messages.toMutableList()
+// updatedMessages.add(convertChatLineToUiMessage(it))
+// updateState { it.copy(messages = updatedMessages) }
+//
+// if (it.senderId != currentUserId) {
+// updateMessageStatus(it.id, Constants.STATUS_READ)
+// }
+// }
+// }
+// }
viewModelScope.launch {
socketService.newMessages.collect { chatLine ->
- chatLine?.let {
- Log.d(TAG, "New message received via socket - ID: ${it.id}, SenderID: ${it.senderId}")
- val currentMessages = _state.value?.messages ?: listOf()
- val updatedMessages = currentMessages.toMutableList().apply {
- add(convertChatLineToUiMessage(it))
+ Log.d("ChatViewModel", "Collected new message from SocketIOService: ${chatLine.message}")
+ chatLine?.let { incomingChatLine ->
+ // 1. First update: Add the message to the list (potentially without full product info)
+ _state.update { currentState ->
+ val existingMessageIndex =
+ currentState.messages.indexOfFirst { it.id == incomingChatLine.id }
+ val messagesAfterInitialUpdate = if (existingMessageIndex != -1) {
+ // If message exists (e.g., status update), just update it
+ val updatedList = currentState.messages.toMutableList()
+ updatedList[existingMessageIndex] = mapChatLineToUiMessage(
+ incomingChatLine,
+ updatedList[existingMessageIndex].productInfo
+ ) // Preserve existing productInfo if any
+ updatedList
+ } else {
+ // New message, add it
+ (currentState.messages + mapChatLineToUiMessage(incomingChatLine)).distinctBy { msg -> msg.id }
+ }
+ // Sort after any update/addition
+ currentState.copy(messages = messagesAfterInitialUpdate.sortedBy { msg ->
+ SimpleDateFormat(
+ "yyyy-MM-dd HH:mm:ss",
+ Locale.getDefault()
+ ).parse(msg.createdAt)?.time
+ })
}
- updateState { it.copy(messages = updatedMessages) }
- if (it.senderId != currentUserId) {
- Log.d(TAG, "Marking message as read: ${it.id}")
- updateMessageStatus(it.id, Constants.STATUS_READ)
+ // 2. If it's a product message and needs details, fetch them
+ if (incomingChatLine.productId != 0) { // Check if it's a product message
+ viewModelScope.launch {
+ Log.d(
+ TAG,
+ "Fetching product detail for ID: ${incomingChatLine.productId}"
+ )
+
+ // Call your repository function directly
+ val productResponse =
+ chatRepository.fetchProductDetail(incomingChatLine.productId)
+
+ if (productResponse != null && productResponse.product != null) {
+ val fetchedProduct =
+ productResponse.product // Access the nested product object
+ Log.d(
+ TAG,
+ "Successfully fetched product: ${fetchedProduct.productName}"
+ )
+
+ // Create a complete ProductInfo object
+ val fullProductInfo = ProductInfo(
+ productId = fetchedProduct.productId,
+ productName = fetchedProduct.productName, // Use productName from fetched data
+ productPrice = fetchedProduct.price, // Use productPrice from fetched data
+ productImage = fetchedProduct.image, // Use productImage from fetched data
+ productRating = fetchedProduct.rating.toFloat(),
+ storeName = fetchedProduct.productName // Use storeName from fetched data
+ )
+
+ // --- PHASE 3: Second UI update (fill in full product info) ---
+ _state.update { currentState ->
+ val updatedMessages = currentState.messages.map { msg ->
+ if (msg.id == incomingChatLine.id) {
+ // Found the message, update its productInfo with full details
+ msg.copy(productInfo = fullProductInfo)
+ } else {
+ msg
+ }
+ }
+ currentState.copy(messages = updatedMessages)
+ }
+ } else {
+ Log.e(
+ TAG,
+ "Failed to fetch product detail for ID ${incomingChatLine.productId} or product data is null."
+ )
+ // Optionally, update message status to indicate error in product loading
+ }
+ }
}
+
}
+
+// // Your existing logic for clearing typing status etc.
+// if (incomingChatLine.isTyping == false && incomingChatLine.from?.id != sessionManager.getUserId()?.toIntOrNull()) {
+// _state.update { it.copy(isOtherUserTyping = false) }
+// }
+
}
}
@@ -271,10 +367,10 @@ class ChatViewModel @Inject constructor(
if (roomId <= 0) {
Log.e(TAG, "Cannot join room: Invalid room ID")
return
+ } else if (roomId > 0){
+ Log.d(TAG, "Joining socket room: $roomId")
+ socketService.joinRoom(roomId)
}
-
- Log.d(TAG, "Joining socket room: $roomId")
- socketService.joinRoom()
}
fun sendTypingStatus(isTyping: Boolean) {
@@ -728,7 +824,7 @@ class ChatViewModel @Inject constructor(
}
}
- //update message status
+ //update message status
fun updateMessageStatus(messageId: Int, status: String) {
Log.d(TAG, "Updating message status - ID: $messageId, Status: $status")
@@ -756,7 +852,7 @@ class ChatViewModel @Inject constructor(
}
}
- //set image attachment
+ //set image attachment
fun setSelectedImageFile(file: File?) {
selectedImageFile = file
updateState { it.copy(hasAttachment = file != null) }
@@ -791,7 +887,7 @@ class ChatViewModel @Inject constructor(
)
}
- // convert chat history item to ui
+ // convert chat history item to ui
private fun convertChatLineToUiMessageHistory(chatItem: ChatItem): ChatUiMessage {
val formattedTime = formatTimestamp(chatItem.createdAt)
@@ -932,7 +1028,7 @@ class ChatViewModel @Inject constructor(
}
}
- //format price
+ //format price
private fun formatPrice(price: String): String {
return if (price.startsWith("Rp")) price else "Rp$price"
}
@@ -958,9 +1054,7 @@ class ChatViewModel @Inject constructor(
// helper function to update live data
private fun updateState(update: (ChatUiState) -> ChatUiState) {
- _state.value?.let {
- _state.value = update(it)
- }
+ _state.value = update(_state.value)
}
//clear any error messages
@@ -1053,6 +1147,73 @@ class ChatViewModel @Inject constructor(
private fun isThisYear(messageCalendar: Calendar, today: Calendar): Boolean {
return messageCalendar.get(Calendar.YEAR) == today.get(Calendar.YEAR)
}
+
+ fun setChatRoomId(roomId: Int) {
+ _chatRoomId.value = roomId
+ joinSocketRoom(roomId)
+ loadChatHistory(roomId)
+ }
+
+ private fun convertToUiMessage(chatLine: ChatLine): ChatUiMessage {
+
+ val formattedTime = formatTimestamp(chatLine.createdAt)
+ return ChatUiMessage(
+ id = chatLine.id,
+ message = chatLine.message,
+ attachment = chatLine.attachment,
+ status = chatLine.status,
+ time = formattedTime, // or format from createdAt if needed
+ isSentByMe = chatLine.senderId == currentUserId,
+ messageType = MessageType.TEXT, // or detect from chatLine if needed
+ productInfo = null, // optional, if applicable
+ createdAt = chatLine.createdAt
+ )
+ }
+
+ private fun mapChatLineToUiMessage(chatLine: ChatLine, fetchedProductInfo: ProductInfo? = null): ChatUiMessage {
+ val isSentByMe = chatLine.senderId == sessionManager.getUserId()?.toIntOrNull() // Using senderId now
+ val formattedTime = try {
+ val inputFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault()).apply {
+ timeZone = TimeZone.getTimeZone("UTC")
+ }
+ val outputFormat = SimpleDateFormat("HH:mm", Locale.getDefault())
+ val date = inputFormat.parse(chatLine.createdAt)
+ date?.let { outputFormat.format(it) } ?: ""
+ } catch (e: Exception) {
+ Log.e("ChatViewModel", "Error parsing date: ${chatLine.createdAt}", e)
+ ""
+ }
+
+ // Determine message type based on what ChatLine provides
+ val messageType = when {
+ chatLine.attachment?.isNotEmpty() == true -> MessageType.IMAGE
+ chatLine.productId != 0 -> MessageType.PRODUCT // If productId is non-zero, it's a product message
+ else -> MessageType.TEXT
+ }
+
+ // Initialize productInfo: if fetchedProductInfo is provided, use it.
+ // Otherwise, if ChatLine has a productId, create a ProductInfo with just the ID.
+ // If no productId, it's null.
+ val productInfo = fetchedProductInfo ?: if (chatLine.productId != 0) {
+ // Create a placeholder ProductInfo with just the ID for initial display
+ // The full details will be fetched later
+ ProductInfo(productId = chatLine.productId)
+ } else {
+ null
+ }
+
+ return ChatUiMessage(
+ id = chatLine.id,
+ message = chatLine.message,
+ attachment = chatLine.attachment,
+ status = chatLine.status,
+ time = formattedTime,
+ isSentByMe = isSentByMe,
+ messageType = messageType,
+ productInfo = productInfo, // Use the determined productInfo
+ createdAt = chatLine.createdAt
+ )
+ }
}
enum class MessageType {
@@ -1062,12 +1223,12 @@ enum class MessageType {
}
data class ProductInfo(
- val productId: Int,
- val productName: String,
- val productPrice: String,
- val productImage: String,
- val productRating: Float,
- val storeName: String
+ val productId: Int, // Keep productId here
+ val productName: String? = null, // Make nullable
+ val productPrice: String? = null, // Make nullable
+ val productImage: String? = null, // Make nullable
+ val productRating: Float = 0f, // Default value
+ val storeName: String? = null
)
// representing chat messages to UI
@@ -1083,8 +1244,6 @@ data class ChatUiMessage(
val createdAt: String
)
-
-
// representing UI state to screen
data class ChatUiState(
val messages: List = emptyList(),
@@ -1102,4 +1261,8 @@ data class ChatUiState(
val productImageUrl: String = "",
val productRating: Float = 0f,
val storeName: String = ""
-)
\ No newline at end of file
+)
+
+//data class ChatUiState(
+// val messages: List = emptyList()
+//)
diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/chat/SocketIOService.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/chat/SocketIOService.kt
index 1ac378f..bb088c0 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/ui/chat/SocketIOService.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/ui/chat/SocketIOService.kt
@@ -10,14 +10,24 @@ import com.alya.ecommerce_serang.utils.SessionManager
import com.google.gson.Gson
import io.socket.client.IO
import io.socket.client.Socket
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.launch
import org.json.JSONObject
import java.net.URISyntaxException
+import javax.inject.Inject
+import javax.inject.Singleton
-class SocketIOService(
+@Singleton
+class SocketIOService @Inject constructor(
private val sessionManager: SessionManager
) {
+ private val serviceScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
private val TAG = "SocketIOService"
// Socket.IO client
@@ -30,8 +40,8 @@ class SocketIOService(
private val _connectionState = MutableStateFlow(ConnectionState.Disconnected())
val connectionState: StateFlow = _connectionState
- private val _newMessages = MutableStateFlow(null)
- val newMessages: StateFlow = _newMessages
+ private val _newMessages = MutableSharedFlow(extraBufferCapacity = 1) // Using extraBufferCapacity for a non-suspending emit
+ val newMessages: SharedFlow = _newMessages
private val _typingStatus = MutableStateFlow(null)
val typingStatus: StateFlow = _typingStatus
@@ -85,63 +95,95 @@ class SocketIOService(
* Sets up Socket.IO event listeners
*/
private fun setupSocketListeners() {
- socket?.let { socket ->
- // Connection events
- socket.on(Socket.EVENT_CONNECT) {
- Log.d(TAG, "Socket.IO connected")
- isConnected = true
- _connectionState.value = ConnectionState.Connected
- _connectionStateLiveData.postValue(ConnectionState.Connected)
- }
- socket.on(Socket.EVENT_DISCONNECT) {
- Log.d(TAG, "Socket.IO disconnected")
- isConnected = false
- _connectionState.value = ConnectionState.Disconnected("Disconnected from server")
- _connectionStateLiveData.postValue(ConnectionState.Disconnected("Disconnected from server"))
- }
+ socket?.on(Constants.EVENT_NEW_MESSAGE) { args -> // Use the event name your server emits
+ Log.d(TAG, "Raw event received on ${Constants.EVENT_NEW_MESSAGE}: ${args.firstOrNull()}") // Check raw args
- socket.on(Socket.EVENT_CONNECT_ERROR) { args ->
- val error = if (args.isNotEmpty() && args[0] != null) args[0].toString() else "Unknown error"
- Log.e(TAG, "Socket.IO connection error: $error")
- isConnected = false
- _connectionState.value = ConnectionState.Error("Connection error: $error")
- _connectionStateLiveData.postValue(ConnectionState.Error("Connection error: $error"))
- }
-
- // Chat events
- socket.on(Constants.EVENT_NEW_MESSAGE) { args ->
+ if (args.isNotEmpty()) {
try {
- if (args.isNotEmpty() && args[0] != null) {
- val messageJson = args[0].toString()
- Log.d(TAG, "Received new message: $messageJson")
- val chatLine = Gson().fromJson(messageJson, ChatLine::class.java)
- _newMessages.value = chatLine
- _newMessagesLiveData.postValue(chatLine)
+ val messageJson = args[0].toString()
+ val chatLine = Gson().fromJson(messageJson, ChatLine::class.java)
+ Log.d(TAG, "Successfully parsed ChatLine: ${chatLine.message}")
+ Log.d(TAG, "Emitting new message to _newMessages SharedFlow...") // New log
+
+ // Use the serviceScope to launch a coroutine for emit()
+ serviceScope.launch {
+ _newMessages.emit(chatLine) // This ensures every message is processed
+ Log.d(TAG, "New message emitted to SharedFlow.") // New log after emit
+
}
} catch (e: Exception) {
- Log.e(TAG, "Error parsing new message event", e)
- }
- }
-
- socket.on(Constants.EVENT_TYPING) { args ->
- try {
- if (args.isNotEmpty() && args[0] != null) {
- val typingData = args[0] as JSONObject
- val userId = typingData.getInt("userId")
- val roomId = typingData.getInt("roomId")
- val isTyping = typingData.getBoolean("isTyping")
-
- Log.d(TAG, "Received typing status: User $userId in room $roomId is typing: $isTyping")
- val status = TypingStatus(userId, roomId, isTyping)
- _typingStatus.value = status
- _typingStatusLiveData.postValue(status)
- }
- } catch (e: Exception) {
- Log.e(TAG, "Error parsing typing event", e)
+ Log.e(TAG, "Error parsing or emitting new message: ${e.message}", e)
}
+ } else {
+ Log.w(TAG, "Received empty args for ${Constants.EVENT_NEW_MESSAGE}")
}
}
+// socket?.on(Constants.EVENT_NEW_MESSAGE) { args ->
+// if (args.isNotEmpty()) {
+// val messageJson = args[0].toString()
+// val chatLine = Gson().fromJson(messageJson, ChatLine::class.java)
+// Log.d("SocketIOService", "Message received: ${chatLine.message}")
+// _newMessages.value = chatLine
+// }
+// }
+// socket?.let { socket ->
+// // Connection events
+// socket.on(Socket.EVENT_CONNECT) {
+// Log.d(TAG, "Socket.IO connected")
+// isConnected = true
+// _connectionState.value = ConnectionState.Connected
+// _connectionStateLiveData.postValue(ConnectionState.Connected)
+// }
+//
+// socket.on(Socket.EVENT_DISCONNECT) {
+// Log.d(TAG, "Socket.IO disconnected")
+// isConnected = false
+// _connectionState.value = ConnectionState.Disconnected("Disconnected from server")
+// _connectionStateLiveData.postValue(ConnectionState.Disconnected("Disconnected from server"))
+// }
+//
+// socket.on(Socket.EVENT_CONNECT_ERROR) { args ->
+// val error = if (args.isNotEmpty() && args[0] != null) args[0].toString() else "Unknown error"
+// Log.e(TAG, "Socket.IO connection error: $error")
+// isConnected = false
+// _connectionState.value = ConnectionState.Error("Connection error: $error")
+// _connectionStateLiveData.postValue(ConnectionState.Error("Connection error: $error"))
+// }
+//
+// // Chat events
+// socket.on(Constants.EVENT_NEW_MESSAGE) { args ->
+// try {
+// if (args.isNotEmpty() && args[0] != null) {
+// val messageJson = args[0].toString()
+// Log.d(TAG, "Received new message: $messageJson")
+// val chatLine = Gson().fromJson(messageJson, ChatLine::class.java)
+// _newMessages.value = chatLine
+// _newMessagesLiveData.postValue(chatLine)
+// }
+// } catch (e: Exception) {
+// Log.e(TAG, "Error parsing new message event", e)
+// }
+// }
+//
+// socket.on(Constants.EVENT_TYPING) { args ->
+// try {
+// if (args.isNotEmpty() && args[0] != null) {
+// val typingData = args[0] as JSONObject
+// val userId = typingData.getInt("userId")
+// val roomId = typingData.getInt("roomId")
+// val isTyping = typingData.getBoolean("isTyping")
+//
+// Log.d(TAG, "Received typing status: User $userId in room $roomId is typing: $isTyping")
+// val status = TypingStatus(userId, roomId, isTyping)
+// _typingStatus.value = status
+// _typingStatusLiveData.postValue(status)
+// }
+// } catch (e: Exception) {
+// Log.e(TAG, "Error parsing typing event", e)
+// }
+// }
+// }
}
/**
@@ -159,22 +201,29 @@ class SocketIOService(
/**
* Joins a specific chat room
*/
- fun joinRoom() {
+ fun joinRoom(roomId: Int) {
+// if (!isConnected) {
+// connect()
+// return
+// }
+//
+// // Get user ID from SessionManager
+// val userId = sessionManager.getUserId()
+// if (userId.isNullOrEmpty()) {
+// Log.e(TAG, "Cannot join room: User ID is null or empty")
+// return
+// }
+//
+// // Join the room using the current user's ID
+// socket?.emit("joinRoom", roomId) // ✅
+// Log.d(TAG, "Joined room ID: $roomId")
+// Log.d(TAG, "Joined room for user: $userId")
if (!isConnected) {
connect()
- return
}
- // Get user ID from SessionManager
- val userId = sessionManager.getUserId()
- if (userId.isNullOrEmpty()) {
- Log.e(TAG, "Cannot join room: User ID is null or empty")
- return
- }
-
- // Join the room using the current user's ID
- socket?.emit("joinRoom", userId)
- Log.d(TAG, "Joined room for user: $userId")
+ socket?.emit("joinRoom", roomId)
+ Log.d(TAG, "Joined room ID: $roomId")
}
/**
diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/order/address/AddAddressActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/order/address/AddAddressActivity.kt
index 4c8f0c1..cd36fe7 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/ui/order/address/AddAddressActivity.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/ui/order/address/AddAddressActivity.kt
@@ -333,18 +333,19 @@ class AddAddressActivity : AppCompatActivity() {
// Create request with all fields
val request = CreateAddressRequest(
+ userId = userId,
lat = latitude!!, // Safe to use !! as we've checked above
long = longitude!!,
street = street,
subDistrict = subDistrict,
- cityId = cityId,
+ cityId = cityId, // ⚠️ Make sure this is Int
provId = provinceId,
postCode = postalCode,
+ idVillage = "", // Or provide a real ID if needed
detailAddress = street,
- userId = userId,
+ isStoreLocation = false,
recipient = recipient,
- phone = phone,
- isStoreLocation = isStoreLocation
+ phone = phone
)
Log.d(TAG, "Form validation successful, submitting address: $request")
diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/order/address/ProvinceAdapter.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/order/address/ProvinceAdapter.kt
index 20d5718..85f674b 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/ui/order/address/ProvinceAdapter.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/ui/order/address/ProvinceAdapter.kt
@@ -14,85 +14,6 @@ class ProvinceAdapter(
resource: Int = android.R.layout.simple_dropdown_item_1line
) : ArrayAdapter(context, resource, ArrayList()) {
-// companion object {
-// private const val TAG = "ProvinceAdapter"
-// }
-//
-// // --- Static list of provinces ---------------------------------------------------------------
-// private val provinces = listOf(
-// ProvincesItem(1, "Aceh"),
-// ProvincesItem(2, "Sumatera Utara"),
-// ProvincesItem(3, "Sumatera Barat"),
-// ProvincesItem(4, "Riau"),
-// ProvincesItem(5, "Kepulauan Riau"),
-// ProvincesItem(6, "Jambi"),
-// ProvincesItem(7, "Sumatera Selatan"),
-// ProvincesItem(8, "Kepulauan Bangka Belitung"),
-// ProvincesItem(9, "Bengkulu"),
-// ProvincesItem(10, "Lampung"),
-// ProvincesItem(11, "DKI Jakarta"),
-// ProvincesItem(12, "Jawa Barat"),
-// ProvincesItem(13, "Banten"),
-// ProvincesItem(14, "Jawa Tengah"),
-// ProvincesItem(15, "Daerah Istimewa Yogyakarta"),
-// ProvincesItem(16, "Jawa Timur"),
-// ProvincesItem(17, "Bali"),
-// ProvincesItem(18, "Nusa Tenggara Barat"),
-// ProvincesItem(19, "Nusa Tenggara Timur"),
-// ProvincesItem(20, "Kalimantan Barat"),
-// ProvincesItem(21, "Kalimantan Tengah"),
-// ProvincesItem(22, "Kalimantan Selatan"),
-// ProvincesItem(23, "Kalimantan Timur"),
-// ProvincesItem(24, "Kalimantan Utara"),
-// ProvincesItem(25, "Sulawesi Utara"),
-// ProvincesItem(26, "Gorontalo"),
-// ProvincesItem(27, "Sulawesi Tengah"),
-// ProvincesItem(28, "Sulawesi Barat"),
-// ProvincesItem(29, "Sulawesi Selatan"),
-// ProvincesItem(30, "Sulawesi Tenggara"),
-// ProvincesItem(31, "Maluku"),
-// ProvincesItem(32, "Maluku Utara"),
-// ProvincesItem(33, "Papua Barat"),
-// ProvincesItem(34, "Papua"),
-// ProvincesItem(35, "Papua Tengah"),
-// ProvincesItem(36, "Papua Pegunungan"),
-// ProvincesItem(37, "Papua Selatan"),
-// ProvincesItem(38, "Papua Barat Daya")
-// )
-//
-// // --- Init block -----------------------------------------------------------------------------
-// init {
-// addAll(getProvinceNames()) // pre‑populate adapter
-// Log.d(TAG, "Adapter created with ${count} provinces")
-// }
-//
-// // --- Public helper functions ----------------------------------------------------------------
-// fun updateData(newProvinces: List) {
-// // If you actually want to replace the list, comment this line
-// // provinces = newProvinces // (make `provinces` var instead of val)
-//
-// clear()
-// addAll(newProvinces.map { it.province })
-// notifyDataSetChanged()
-//
-// Log.d(TAG, "updateData(): updated with ${newProvinces.size} provinces")
-// }
-//
-// fun getProvinceId(position: Int): Int? {
-// val id = provinces.getOrNull(position)?.provinceId
-// Log.d(TAG, "getProvinceId(): position=$position, id=$id")
-// return id
-// }
-//
-// fun getProvinceItem(position: Int): ProvincesItem? {
-// val item = provinces.getOrNull(position)
-// Log.d(TAG, "getProvinceItem(): position=$position, item=$item")
-// return item
-// }
-//
-// // --- Private helpers ------------------------------------------------------------------------
-// private fun getProvinceNames(): List = provinces.map { it.province }
-
//call from endpoint
private val provinces = ArrayList()
diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/MyStoreActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/MyStoreActivity.kt
index 4b89434..d32939a 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/MyStoreActivity.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/MyStoreActivity.kt
@@ -21,7 +21,6 @@ import com.alya.ecommerce_serang.ui.profile.mystore.chat.ChatListStoreActivity
import com.alya.ecommerce_serang.ui.profile.mystore.product.ProductActivity
import com.alya.ecommerce_serang.ui.profile.mystore.profile.DetailStoreProfileActivity
import com.alya.ecommerce_serang.ui.profile.mystore.review.ReviewActivity
-import com.alya.ecommerce_serang.ui.profile.mystore.review.ReviewFragment
import com.alya.ecommerce_serang.ui.profile.mystore.sells.SellsActivity
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
import com.alya.ecommerce_serang.utils.SessionManager
@@ -52,14 +51,16 @@ class MyStoreActivity : AppCompatActivity() {
enableEdgeToEdge()
- binding.header.headerTitle.text = "Toko Saya"
- binding.header.headerLeftIcon.setOnClickListener {
+ binding.headerMyStore.headerTitle.text = "Toko Saya"
+
+ binding.headerMyStore.headerLeftIcon.setOnClickListener {
onBackPressed()
finish()
}
viewModel.loadMyStore()
+ viewModel.loadMyStoreProducts()
viewModel.myStoreProfile.observe(this){ user ->
user?.let { myStoreProfileOverview(it) }
@@ -68,9 +69,9 @@ class MyStoreActivity : AppCompatActivity() {
viewModel.errorMessage.observe(this) { error ->
Toast.makeText(this, error, Toast.LENGTH_SHORT).show()
}
-
setUpClickListeners()
getCountOrder()
+ observeViewModel()
viewModel.fetchBalance()
fetchBalance()
}
@@ -170,13 +171,11 @@ class MyStoreActivity : AppCompatActivity() {
when (result) {
is com.alya.ecommerce_serang.data.repository.Result.Loading ->
null
-// binding.progressBar.isVisible = true
is com.alya.ecommerce_serang.data.repository.Result.Success ->
viewModel.formattedBalance.observe(this) {
binding.tvBalance.text = it
}
is Result.Error -> {
-// binding.progressBar.isVisible = false
Log.e(
"MyStoreActivity",
"Gagal memuat saldo: ${result.exception.localizedMessage}"
@@ -186,15 +185,29 @@ class MyStoreActivity : AppCompatActivity() {
}
}
+ private fun observeViewModel() {
+ viewModel.productList.observe(this) { result ->
+ when (result) {
+ is Result.Loading -> {
+ null
+ }
+ is Result.Success -> {
+ val productList = result.data
+ val count = productList.size
+ Log.d("MyStoreActivty", "You have $count products")
+
+ // Example: update UI
+ binding.tvNumProduct.text = "$count produk"
+ }
+ is Result.Error -> {
+ Log.e("MyStoreActivity", "Failed load product : ${result.exception.message}" )
+ }
+ }
+ }
+ }
+
companion object {
private const val PROFILE_REQUEST_CODE = 100
}
-// private fun navigateToSellsFragment(status: String) {
-// val sellsFragment = SellsListFragment.newInstance(status)
-// supportFragmentManager.beginTransaction()
-// .replace(android.R.id.content, sellsFragment)
-// .addToBackStack(null)
-// .commit()
-// }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/chat/ChatStoreActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/chat/ChatStoreActivity.kt
index f988123..6dcdf7c 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/chat/ChatStoreActivity.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/chat/ChatStoreActivity.kt
@@ -25,6 +25,7 @@ import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsAnimationCompat
import androidx.core.view.WindowInsetsCompat
import androidx.lifecycle.Observer
+import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import com.alya.ecommerce_serang.BuildConfig.BASE_URL
import com.alya.ecommerce_serang.R
@@ -373,7 +374,8 @@ class ChatStoreActivity : AppCompatActivity() {
}
})
- viewModel.state.observe(this, Observer { state ->
+ lifecycleScope.launchWhenStarted {
+ viewModel.state.collect { state ->
Log.d(TAG, "State updated - Messages: ${state.messages.size}")
// Update messages
@@ -434,7 +436,8 @@ class ChatStoreActivity : AppCompatActivity() {
Toast.makeText(this@ChatStoreActivity, error, Toast.LENGTH_SHORT).show()
viewModel.clearError()
}
- })
+ }
+ }
}
private fun showOptionsMenu() {
diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/product/DetailStoreProductActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/product/DetailStoreProductActivity.kt
index cb7385d..9e0bca5 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/product/DetailStoreProductActivity.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/product/DetailStoreProductActivity.kt
@@ -12,30 +12,29 @@ import android.view.View
import android.widget.ArrayAdapter
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
-import com.alya.ecommerce_serang.R
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
+import androidx.core.net.toUri
+import androidx.core.widget.doAfterTextChanged
+import com.alya.ecommerce_serang.BuildConfig.BASE_URL
+import com.alya.ecommerce_serang.R
import com.alya.ecommerce_serang.data.api.dto.CategoryItem
import com.alya.ecommerce_serang.data.api.dto.Preorder
+import com.alya.ecommerce_serang.data.api.dto.Wholesale
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
import com.alya.ecommerce_serang.data.repository.ProductRepository
import com.alya.ecommerce_serang.data.repository.Result
import com.alya.ecommerce_serang.databinding.ActivityDetailStoreProductBinding
-import com.alya.ecommerce_serang.utils.viewmodel.ProductViewModel
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
import com.alya.ecommerce_serang.utils.SessionManager
+import com.alya.ecommerce_serang.utils.viewmodel.ProductViewModel
import com.bumptech.glide.Glide
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import okhttp3.RequestBody
import java.io.File
import java.io.FileOutputStream
-import kotlin.getValue
-import androidx.core.net.toUri
-import androidx.core.widget.doAfterTextChanged
-import com.alya.ecommerce_serang.BuildConfig.BASE_URL
-import com.alya.ecommerce_serang.data.api.dto.Wholesale
class DetailStoreProductActivity : AppCompatActivity() {
@@ -93,7 +92,7 @@ class DetailStoreProductActivity : AppCompatActivity() {
val isEditing = intent.getBooleanExtra("is_editing", false)
productId = intent.getIntExtra("product_id", -1)
- binding.header.headerTitle.text = if (isEditing) "Ubah Produk" else "Tambah Produk"
+ binding.headerStoreProduct.headerTitle.text = if (isEditing) "Ubah Produk" else "Tambah Produk"
if (isEditing && productId != null && productId != -1) {
viewModel.loadProductDetail(productId!!)
@@ -140,7 +139,7 @@ class DetailStoreProductActivity : AppCompatActivity() {
}
}
- binding.header.headerLeftIcon.setOnClickListener {
+ binding.headerStoreProduct.headerLeftIcon.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
}
diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/product/ProductActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/product/ProductActivity.kt
index e28b2f6..aac131e 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/product/ProductActivity.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/product/ProductActivity.kt
@@ -11,9 +11,9 @@ import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
import com.alya.ecommerce_serang.data.repository.ProductRepository
import com.alya.ecommerce_serang.data.repository.Result
import com.alya.ecommerce_serang.databinding.ActivityProductBinding
-import com.alya.ecommerce_serang.utils.viewmodel.ProductViewModel
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
import com.alya.ecommerce_serang.utils.SessionManager
+import com.alya.ecommerce_serang.utils.viewmodel.ProductViewModel
class ProductActivity : AppCompatActivity() {
@@ -94,14 +94,14 @@ class ProductActivity : AppCompatActivity() {
}
private fun setupHeader() {
- binding.header.headerTitle.text = "Produk Saya"
- binding.header.headerRightText.visibility = View.VISIBLE
+ binding.headerListProduct.headerTitle.text = "Produk Saya"
+ binding.headerListProduct.headerRightText.visibility = View.VISIBLE
- binding.header.headerLeftIcon.setOnClickListener {
+ binding.headerListProduct.headerLeftIcon.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
- binding.header.headerRightText.setOnClickListener {
+ binding.headerListProduct.headerRightText.setOnClickListener {
val intent = Intent(this, DetailStoreProductActivity::class.java)
intent.putExtra("is_editing", false)
startActivity(intent)
@@ -111,4 +111,6 @@ class ProductActivity : AppCompatActivity() {
private fun setupRecyclerView() {
binding.rvStoreProduct.layoutManager = LinearLayoutManager(this)
}
+
+
}
\ No newline at end of file
diff --git a/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/MyStoreViewModel.kt b/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/MyStoreViewModel.kt
index 6fcc546..2a8792b 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/MyStoreViewModel.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/MyStoreViewModel.kt
@@ -6,6 +6,7 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.map
import androidx.lifecycle.viewModelScope
+import com.alya.ecommerce_serang.data.api.dto.ProductsItem
import com.alya.ecommerce_serang.data.api.dto.Store
import com.alya.ecommerce_serang.data.api.response.auth.StoreTypesItem
import com.alya.ecommerce_serang.data.api.response.store.StoreResponse
@@ -38,6 +39,9 @@ class MyStoreViewModel(private val repository: MyStoreRepository): ViewModel() {
private val _balanceResult = MutableLiveData>()
val balanceResult: LiveData> get() = _balanceResult
+ private val _productList = MutableLiveData>>()
+ val productList: LiveData>> get() = _productList
+
fun loadMyStore(){
viewModelScope.launch {
when (val result = repository.fetchMyStoreProfile()){
@@ -158,6 +162,18 @@ class MyStoreViewModel(private val repository: MyStoreRepository): ViewModel() {
}
}
+ fun loadMyStoreProducts() {
+ viewModelScope.launch {
+ _productList.value = Result.Loading
+ try {
+ val result = repository.fetchMyStoreProducts()
+ _productList.value = Result.Success(result)
+ } catch (e: Exception) {
+ _productList.value = Result.Error(e)
+ }
+ }
+ }
+
private fun String.toRequestBody(): RequestBody =
RequestBody.create("text/plain".toMediaTypeOrNull(), this)
}
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_cart.xml b/app/src/main/res/layout/activity_cart.xml
index 57d1ece..daf3864 100644
--- a/app/src/main/res/layout/activity_cart.xml
+++ b/app/src/main/res/layout/activity_cart.xml
@@ -122,12 +122,14 @@
android:src="@drawable/outline_shopping_cart_24" />
+ android:textSize="16sp" />
diff --git a/app/src/main/res/layout/fragment_chat_list.xml b/app/src/main/res/layout/fragment_chat_list.xml
index 69d919e..4a1a8c9 100644
--- a/app/src/main/res/layout/fragment_chat_list.xml
+++ b/app/src/main/res/layout/fragment_chat_list.xml
@@ -48,8 +48,8 @@
android:layout_marginTop="16dp"
android:gravity="center"
android:visibility="gone"
- android:text="Keranjang Anda kosong"
+ android:text="Pesan anda kosong"
android:textColor="@android:color/black"
- android:textSize="18sp" />
+ android:textSize="16sp" />
\ No newline at end of file