Skip to content

Commit

Permalink
[feat/register_review]: 후기 등록 기능 구현
Browse files Browse the repository at this point in the history
  • Loading branch information
soopeach committed Mar 11, 2024
1 parent 48159f6 commit 0b9127f
Show file tree
Hide file tree
Showing 9 changed files with 384 additions and 139 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package org.gdsc.presentation.view.mypage.adapter

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.net.toUri
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import org.gdsc.presentation.databinding.ItemPhotoWillBeUploadedBinding

class PhotoWillBeUploadedAdapter(
private val onDeleteButtonClicked: (String) -> Unit
) :
ListAdapter<String, PhotoWillBeUploadedAdapter.PhotoWillBeUploadedViewHolder>(
diffUtil
) {
inner class PhotoWillBeUploadedViewHolder(private val binding: ItemPhotoWillBeUploadedBinding) :
RecyclerView.ViewHolder(binding.root) {

fun bind(url: String) {

Glide.with(binding.root)
.load(url.toUri())
.into(binding.photoWillBeUploaded)

binding.deleteButton.setOnClickListener {
onDeleteButtonClicked(url)
}
}
}

companion object {
val diffUtil = object : DiffUtil.ItemCallback<String>() {
override fun areItemsTheSame(oldItem: String, newItem: String): Boolean {
return oldItem == newItem
}

override fun areContentsTheSame(oldItem: String, newItem: String): Boolean {
return oldItem == newItem
}
}
}

override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): PhotoWillBeUploadedViewHolder {
val inflater = LayoutInflater.from(parent.context)
return PhotoWillBeUploadedViewHolder(
ItemPhotoWillBeUploadedBinding.inflate(
inflater,
parent,
false
)
)
}

override fun onBindViewHolder(holder: PhotoWillBeUploadedViewHolder, position: Int) {
holder.apply {
bind(getItem(position))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,26 @@ import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.core.net.toUri
import androidx.fragment.app.activityViewModels
import androidx.fragment.app.setFragmentResultListener
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import com.bumptech.glide.Glide
import com.google.android.material.tabs.TabLayoutMediator
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import okhttp3.MediaType
import okhttp3.MultipartBody
import okhttp3.RequestBody
import org.gdsc.presentation.R
import org.gdsc.presentation.databinding.FragmentRestaurantDetailBinding
import org.gdsc.presentation.utils.BitmapUtils.getCompressedBitmapFromUri
import org.gdsc.presentation.utils.BitmapUtils.saveBitmapToFile
import org.gdsc.presentation.utils.CalculatorUtils
import org.gdsc.presentation.utils.repeatWhenUiStarted
import org.gdsc.presentation.view.mypage.adapter.PhotoWillBeUploadedAdapter
import org.gdsc.presentation.view.mypage.adapter.RestaurantDetailPagerAdapter
import org.gdsc.presentation.view.mypage.viewmodel.RestaurantDetailViewModel

Expand All @@ -28,6 +39,10 @@ class RestaurantDetailFragment : Fragment() {

private val viewModel: RestaurantDetailViewModel by activityViewModels()

private val adapter = PhotoWillBeUploadedAdapter {
viewModel.deletePhotoForReviewState(it)
}

override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
Expand All @@ -41,6 +56,66 @@ class RestaurantDetailFragment : Fragment() {
setButtons()
setTabLayout()
observeData()

repeatWhenUiStarted {
viewModel.photosForReviewState.collect {
adapter.submitList(it)
}
}

binding.rvImageListWillBeUploaded.adapter = adapter

binding.addImageIcon.setOnClickListener {
val directions =
RestaurantDetailFragmentDirections.actionRestaurantDetailFragmentToMultiImagePickerFragment()

findNavController().navigate(directions)
}

binding.btnRegister.setOnClickListener {

val pictures = mutableListOf<MultipartBody.Part>()

viewModel.photosForReviewState.value.forEachIndexed { index, sUri ->

sUri.toUri()
.getCompressedBitmapFromUri(requireContext())
?.saveBitmapToFile(requireContext(), "$index.jpg")?.let { imageFile ->

val requestFile =
RequestBody.create(
MediaType.parse("image/png"),
imageFile
)

val body =
MultipartBody.Part.createFormData(
"reviewImages",
imageFile.name,
requestFile
)

pictures.add(body)

}

}

viewModel.postReview(
binding.etReview.text.toString(),
pictures
) {
binding.etReview.text.clear()
Toast.makeText(requireContext(), "후기가 등록되었습니다!", Toast.LENGTH_SHORT).show()
}
}

setFragmentResultListener("pickImages") { _, bundle ->
val images = bundle.getStringArray("imagesUri")
viewModel.setPhotosForReviewState(images?.toList() ?: emptyList())

if (images.isNullOrEmpty()) return@setFragmentResultListener
}
}

private fun setButtons() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import okhttp3.MultipartBody
import org.gdsc.domain.model.Review
import org.gdsc.domain.model.UserInfo
import org.gdsc.domain.model.response.RestaurantInfoResponse
import org.gdsc.domain.usecase.GetRestaurantInfoUseCase
import org.gdsc.domain.usecase.GetRestaurantReviewsUseCase
import org.gdsc.domain.usecase.PostReviewUseCase
import org.gdsc.domain.usecase.user.GetOtherUserInfoUseCase
import org.gdsc.presentation.JmtLocationManager
import javax.inject.Inject
Expand All @@ -21,7 +24,8 @@ class RestaurantDetailViewModel
private val jmtLocationManager: JmtLocationManager,
private val getRestaurantInfoUseCase: GetRestaurantInfoUseCase,
private val getOtherUserInfoUseCase: GetOtherUserInfoUseCase,
private val getRestaurantReviewsUseCase: GetRestaurantReviewsUseCase
private val getRestaurantReviewsUseCase: GetRestaurantReviewsUseCase,
private val postReviewUseCase: PostReviewUseCase
): ViewModel() {

private var _restaurantInfo: MutableStateFlow<RestaurantInfoResponse?> = MutableStateFlow(null)
Expand All @@ -36,6 +40,17 @@ class RestaurantDetailViewModel
val reviews: StateFlow<List<Review>>
get() = _reviews

private var _photosForReviewState: MutableStateFlow<List<String>> =
MutableStateFlow(emptyList())
val photosForReviewState = _photosForReviewState.asStateFlow()

fun setPhotosForReviewState(images: List<String>) {
_photosForReviewState.value = images
}

fun deletePhotoForReviewState(image: String) {
_photosForReviewState.value = _photosForReviewState.value - image
}

init {
viewModelScope.launch {
Expand All @@ -54,4 +69,15 @@ class RestaurantDetailViewModel

}

fun postReview(content: String, pictures: List<MultipartBody.Part>, onSuccess: () -> Unit) {
viewModelScope.launch {
val isSuccess = postReviewUseCase(1, content, pictures)

if (isSuccess) {
_photosForReviewState.value = emptyList()
onSuccess()
}
}
}

}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions presentation/src/main/res/drawable/bg_rounded_6_main500.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<selector >
<item xmlns:android="http://schemas.android.com/apk/res/android">
<shape android:shape="rectangle">
<solid android:color="@color/main500" />
<corners android:radius="6dp" />
</shape>
</item>
</selector>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 0b9127f

Please sign in to comment.