diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 94597d8d..39342c75 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -8,16 +8,18 @@ - - + + + android:maxSdkVersion="32" /> + + + diff --git a/app/src/main/java/com/eatssu/android/data/dto/request/InquiriesRequest.kt b/app/src/main/java/com/eatssu/android/data/dto/request/InquiriesRequest.kt index b9f936e9..23288a0e 100644 --- a/app/src/main/java/com/eatssu/android/data/dto/request/InquiriesRequest.kt +++ b/app/src/main/java/com/eatssu/android/data/dto/request/InquiriesRequest.kt @@ -1,4 +1,4 @@ -package com.eatssu.android.data.model.request +package com.eatssu.android.data.dto.request data class InquiriesRequest( val content: String, diff --git a/app/src/main/java/com/eatssu/android/data/dto/request/ModifyReviewRequest.kt b/app/src/main/java/com/eatssu/android/data/dto/request/ModifyReviewRequest.kt index 097289e8..c4f433a6 100644 --- a/app/src/main/java/com/eatssu/android/data/dto/request/ModifyReviewRequest.kt +++ b/app/src/main/java/com/eatssu/android/data/dto/request/ModifyReviewRequest.kt @@ -1,7 +1,10 @@ package com.eatssu.android.data.dto.request +import com.google.gson.annotations.SerializedName + data class ModifyReviewRequest( - val content: String, - val grade: Int, - val reviewTags: List, + @SerializedName("mainRate") var mainRate: Int? = null, + @SerializedName("amountRate") var amountRate: Int? = null, + @SerializedName("tasteRate") var tasteRate: Int? = null, + @SerializedName("content") var content: String? = null, ) \ No newline at end of file diff --git a/app/src/main/java/com/eatssu/android/data/dto/request/WriteReviewRequest.kt b/app/src/main/java/com/eatssu/android/data/dto/request/WriteReviewRequest.kt new file mode 100644 index 00000000..1042fd6c --- /dev/null +++ b/app/src/main/java/com/eatssu/android/data/dto/request/WriteReviewRequest.kt @@ -0,0 +1,13 @@ +package com.eatssu.android.data.dto.request + +import com.google.gson.annotations.SerializedName + +data class WriteReviewRequest( + + @SerializedName("mainRating") var mainRating: Int? = null, + @SerializedName("amountRating") var amountRating: Int? = null, + @SerializedName("tasteRating") var tasteRating: Int? = null, + @SerializedName("content") var content: String? = null, + @SerializedName("imageUrl") var imageUrl: String? = null, + + ) \ No newline at end of file diff --git a/app/src/main/java/com/eatssu/android/data/dto/response/GetFixedMenuResponse.kt b/app/src/main/java/com/eatssu/android/data/dto/response/GetFixedMenuResponse.kt index d71c7849..adb8b075 100644 --- a/app/src/main/java/com/eatssu/android/data/dto/response/GetFixedMenuResponse.kt +++ b/app/src/main/java/com/eatssu/android/data/dto/response/GetFixedMenuResponse.kt @@ -5,7 +5,7 @@ import com.google.gson.annotations.SerializedName data class FixMenuInfoList( - @SerializedName("menuId") var menuId: Int? = null, + @SerializedName("menuId") var menuId: Long? = null, @SerializedName("name") var name: String? = null, @SerializedName("price") var price: Int? = null, @SerializedName("mainRating") var mainRating: String? = null, @@ -14,10 +14,10 @@ data class FixMenuInfoList( fun ArrayList.mapFixedMenuResponseToMenu(): List { return this.map { fixMenuInfo -> Menu( - id = fixMenuInfo.menuId, - name = fixMenuInfo.name, - price = fixMenuInfo.price, - rate = fixMenuInfo.mainRating + id = fixMenuInfo.menuId ?: 0, + name = fixMenuInfo.name ?: "", + price = fixMenuInfo.price ?: 0, + rate = fixMenuInfo.mainRating ?: "" ) } } \ No newline at end of file diff --git a/app/src/main/java/com/eatssu/android/data/dto/response/GetReviewListResponse.kt b/app/src/main/java/com/eatssu/android/data/dto/response/GetReviewListResponse.kt index 58ff0735..2516587d 100644 --- a/app/src/main/java/com/eatssu/android/data/dto/response/GetReviewListResponse.kt +++ b/app/src/main/java/com/eatssu/android/data/dto/response/GetReviewListResponse.kt @@ -1,14 +1,16 @@ package com.eatssu.android.data.dto.response +import com.eatssu.android.data.model.Reviews + data class GetReviewListResponse( val dataList: List?, val hasNext: Boolean, - val numberOfElements: Int -){ + val numberOfElements: Int, +) { data class Data( val reviewId: Long, val menu: String, - val writerId : Int, + val writerId: Int, val isWriter: Boolean, val writerNickname: String, val mainGrade: Int, @@ -16,6 +18,21 @@ data class GetReviewListResponse( val tasteGrade: Int, val writeDate: String, val content: String, - val imgUrlList: List + val imgUrlList: List, ) +} + +fun GetReviewListResponse.toReviewList(): List { + return dataList.orEmpty().map { data -> + Reviews( + menu = data.menu, + writerNickname = data.writerNickname, + mainGrade = data.mainGrade, + amountGrade = data.amountGrade, + tasteGrade = data.tasteGrade, + writeDate = data.writeDate, + content = data.content, + imgUrlList = data.imgUrlList + ) + } } \ No newline at end of file diff --git a/app/src/main/java/com/eatssu/android/data/dto/response/GetTodayMealResponse.kt b/app/src/main/java/com/eatssu/android/data/dto/response/GetTodayMealResponse.kt index 4cfcb20e..49d5c990 100644 --- a/app/src/main/java/com/eatssu/android/data/dto/response/GetTodayMealResponse.kt +++ b/app/src/main/java/com/eatssu/android/data/dto/response/GetTodayMealResponse.kt @@ -9,14 +9,14 @@ data class GetTodayMealResponseDto( data class MealInformationResponseList( - @SerializedName("mealId") var mealId: Int? = null, + @SerializedName("mealId") var mealId: Long? = null, @SerializedName("price") var price: Int? = null, @SerializedName("mainRating") var mainRating: String? = null, @SerializedName("menusInformation") var menusInformation: ArrayList = arrayListOf(), ) data class MenusInformation( - @SerializedName("menuId") var menuId: Int? = null, + @SerializedName("menuId") var menuId: Long? = null, @SerializedName("name") var name: String? = null, ) @@ -31,7 +31,7 @@ fun GetTodayMealResponseDto.mapTodayMenuResponseToMenu(): List { id = mealInfo.mealId ?: -1, // Default value if mealId is null name = name, price = mealInfo.price ?: 0, // Default value if price is null - rate = mealInfo.mainRating + rate = mealInfo.mainRating ?: "" ) menuList.add(menu) } diff --git a/app/src/main/java/com/eatssu/android/data/dto/response/ImageResponse.kt b/app/src/main/java/com/eatssu/android/data/dto/response/ImageResponse.kt new file mode 100644 index 00000000..062b47b3 --- /dev/null +++ b/app/src/main/java/com/eatssu/android/data/dto/response/ImageResponse.kt @@ -0,0 +1,7 @@ +package com.eatssu.android.data.dto.response + +import com.google.gson.annotations.SerializedName + +data class ImageResponse( + @SerializedName("url") var url: String? = null, +) \ No newline at end of file diff --git a/app/src/main/java/com/eatssu/android/data/model/Menu.kt b/app/src/main/java/com/eatssu/android/data/model/Menu.kt index 903993b5..7bcfa3d7 100644 --- a/app/src/main/java/com/eatssu/android/data/model/Menu.kt +++ b/app/src/main/java/com/eatssu/android/data/model/Menu.kt @@ -1,8 +1,8 @@ package com.eatssu.android.data.model data class Menu( - val id: Int?, - val name: String?, - val price: Int?, - val rate: String?, + val id: Long, + val name: String, + val price: Int, + val rate: String, ) \ No newline at end of file diff --git a/app/src/main/java/com/eatssu/android/data/model/Review.kt b/app/src/main/java/com/eatssu/android/data/model/Review.kt index 424c5f9c..7690e3bd 100644 --- a/app/src/main/java/com/eatssu/android/data/model/Review.kt +++ b/app/src/main/java/com/eatssu/android/data/model/Review.kt @@ -1,13 +1,33 @@ package com.eatssu.android.data.model data class Review( - val multipartFileList: List, - val reviewCreate: ReviewCreate, + var name: String? = null, + var reviewCnt: Int? = null, + var mainRating: Double? = null, + var amountRating: Double? = null, + var tasteRating: Double? = null, + var ratingDetails: RatingDetails, + var reviewList: List? = null, ) -data class ReviewCreate( - val content: String, +data class RatingDetails( + var one: Int, + var two: Int, + var three: Int, + var four: Int, + var five: Int, +) + +data class Reviews( + val menu: String, + val writerNickname: String, + val mainGrade: Int, val amountGrade: Int, val tasteGrade: Int, + + val writeDate: String, + + val content: String, + val imgUrlList: List, ) \ No newline at end of file diff --git a/app/src/main/java/com/eatssu/android/data/model/WriteReview.kt b/app/src/main/java/com/eatssu/android/data/model/WriteReview.kt new file mode 100644 index 00000000..3dcfb48a --- /dev/null +++ b/app/src/main/java/com/eatssu/android/data/model/WriteReview.kt @@ -0,0 +1,13 @@ +package com.eatssu.android.data.model + +data class WriteReview( + val multipartFileList: List, + val reviewCreate: ReviewCreate, +) + +data class ReviewCreate( + val content: String, + val mainGrade: Int, + val amountGrade: Int, + val tasteGrade: Int, +) \ No newline at end of file diff --git a/app/src/main/java/com/eatssu/android/data/repository/ImageRepository.kt b/app/src/main/java/com/eatssu/android/data/repository/ImageRepository.kt new file mode 100644 index 00000000..87cefe0b --- /dev/null +++ b/app/src/main/java/com/eatssu/android/data/repository/ImageRepository.kt @@ -0,0 +1,11 @@ +package com.eatssu.android.data.repository + + +interface ImageRepository { + +// suspend fun getImageString( +// image: MultipartBody.Part +// ): Flow + +// suspend fun login(idToken: String): Flow +} \ No newline at end of file diff --git a/app/src/main/java/com/eatssu/android/data/repository/ImageRepositoryImpl.kt b/app/src/main/java/com/eatssu/android/data/repository/ImageRepositoryImpl.kt new file mode 100644 index 00000000..b01a0423 --- /dev/null +++ b/app/src/main/java/com/eatssu/android/data/repository/ImageRepositoryImpl.kt @@ -0,0 +1,17 @@ +package com.eatssu.android.data.repository + +import com.eatssu.android.data.service.ImageService +import javax.inject.Inject + +class ImageRepositoryImpl @Inject constructor(private val imageService: ImageService) : + ImageRepository { + + +// override suspend fun getImageString( +// image: MultipartBody.Part +// ): Flow = flow { +// emit(imageService.getImageUrl(image)) +// } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/eatssu/android/data/service/ImageService.kt b/app/src/main/java/com/eatssu/android/data/service/ImageService.kt new file mode 100644 index 00000000..5f821b65 --- /dev/null +++ b/app/src/main/java/com/eatssu/android/data/service/ImageService.kt @@ -0,0 +1,18 @@ +package com.eatssu.android.data.service + +import com.eatssu.android.base.BaseResponse +import com.eatssu.android.data.dto.response.ImageResponse +import okhttp3.MultipartBody +import retrofit2.Call +import retrofit2.http.Multipart +import retrofit2.http.POST +import retrofit2.http.Part + +interface ImageService { + + @Multipart + @POST("/reviews/upload/image") //리뷰 이미지 업로드 + fun getImageUrl( + @Part image: MultipartBody.Part, + ): Call> +} \ No newline at end of file diff --git a/app/src/main/java/com/eatssu/android/data/service/InquiresService.kt b/app/src/main/java/com/eatssu/android/data/service/InquiresService.kt index 921ab24f..8eb6f6e6 100644 --- a/app/src/main/java/com/eatssu/android/data/service/InquiresService.kt +++ b/app/src/main/java/com/eatssu/android/data/service/InquiresService.kt @@ -1,7 +1,7 @@ package com.eatssu.android.data.service import com.eatssu.android.base.BaseResponse -import com.eatssu.android.data.model.request.InquiriesRequest +import com.eatssu.android.data.dto.request.InquiriesRequest import retrofit2.Call import retrofit2.http.Body import retrofit2.http.POST diff --git a/app/src/main/java/com/eatssu/android/data/service/ReviewService.kt b/app/src/main/java/com/eatssu/android/data/service/ReviewService.kt index 8bd3f036..d03fa544 100644 --- a/app/src/main/java/com/eatssu/android/data/service/ReviewService.kt +++ b/app/src/main/java/com/eatssu/android/data/service/ReviewService.kt @@ -2,50 +2,48 @@ package com.eatssu.android.data.service import com.eatssu.android.base.BaseResponse +import com.eatssu.android.data.dto.request.ModifyReviewRequest +import com.eatssu.android.data.dto.request.WriteReviewRequest import com.eatssu.android.data.dto.response.GetReviewInfoResponse import com.eatssu.android.data.dto.response.GetReviewListResponse -import okhttp3.MultipartBody -import okhttp3.RequestBody import retrofit2.Call import retrofit2.http.* interface ReviewService { - @Multipart - @POST("review/{menuId}") - fun writeReview( - @Path("menuId") menuId: Long, - @Part files: List, // Remove the part name from the annotation - @Part("reviewCreate") reviewData: RequestBody, - ): Call +// @Multipart +// @POST("reviews/{menuId}") +// fun writeReview( +// @Path("menuId") menuId: Long, +// @Part files: List, // Remove the part name from the annotation +// @Part("reviewCreate") reviewData: RequestBody, +// ): Call> +// +// @Multipart +// @POST("reviews/{menuId}") +// fun writeReview( +// @Path("menuId") menuId: Long, +// @Part("reviewCreate") reviewData: RequestBody, +// ): Call> - @Multipart - @POST("review/{menuId}") + @POST("/reviews/write/{menuId}") //리뷰 작성 fun writeReview( @Path("menuId") menuId: Long, - @Part("reviewCreate") reviewData: RequestBody, - ): Call - + @Body request: WriteReviewRequest, + ): Call> @DELETE("/reviews/{reviewId}") //리뷰 삭제 - fun delReview(@Path("reviewId") reviewId: Long): Call> + fun deleteReview( + @Path("reviewId") reviewId: Long, + ): Call> @PATCH("/review/{reviewId}") //리뷰 수정(글 수정) fun modifyReview( @Path("reviewId") reviewId: Long, - @Body request: RequestBody, + @Body request: ModifyReviewRequest, ): Call> - - @GET("/review/info") // Retrieve menu review information (rating, etc.) for changeable menus - fun getRreviewInfo( - @Query("menuType") menuType: String, - @Query("mealId") mealId: Long?, - @Query("menuId") menuId: Long?, - ): Call - - - @GET("/review/list") //메뉴 리뷰 리스트 조회 - 고정메뉴 + @GET("/reviews") //리뷰 리스트 조회 fun getReviewList( @Query("menuType") menuType: String, @Query("mealId") mealId: Long?, @@ -54,6 +52,17 @@ interface ReviewService { // @Query("page") page: Int?, // @Query("size") size: Int?, // @Query("black") black: List? - ): Call + ): Call> + + @GET("/reviews/menus/{menuId}") //고정 메뉴 리뷰 정보 조회(메뉴명, 평점 등등) + fun getMenuReviewInfo( + @Query("menuType") menuType: String, + @Path("menuId") menuId: Long, + ): Call> + + @GET("/reviews/menus/{mealId}") //식단(변동 메뉴) 리뷰 정보 조회(메뉴명, 평점 등등) + fun getMealReviewInfo( + @Path("mealId") mealId: Long, + ): Call> } \ No newline at end of file diff --git a/app/src/main/java/com/eatssu/android/ui/main/menu/MenuViewModel.kt b/app/src/main/java/com/eatssu/android/ui/main/menu/MenuViewModel.kt index 8d0263db..8ccbb119 100644 --- a/app/src/main/java/com/eatssu/android/ui/main/menu/MenuViewModel.kt +++ b/app/src/main/java/com/eatssu/android/ui/main/menu/MenuViewModel.kt @@ -130,8 +130,8 @@ class MenuViewModel(private val menuService: MenuService) : ViewModel() { call: Call>, response: Response>, ) { - val data = response.body()?.result!! if (response.isSuccessful) { + val data = response.body()?.result!! Log.d("post", "onResponse 성공" + response.body()) menuBymealId.postValue(data) diff --git a/app/src/main/java/com/eatssu/android/ui/mypage/Inquire/InquireActivity.kt b/app/src/main/java/com/eatssu/android/ui/mypage/Inquire/InquireActivity.kt index efb0f435..422d2153 100644 --- a/app/src/main/java/com/eatssu/android/ui/mypage/Inquire/InquireActivity.kt +++ b/app/src/main/java/com/eatssu/android/ui/mypage/Inquire/InquireActivity.kt @@ -5,7 +5,7 @@ import android.util.Log import android.widget.Toast import com.eatssu.android.base.BaseActivity import com.eatssu.android.base.BaseResponse -import com.eatssu.android.data.model.request.InquiriesRequest +import com.eatssu.android.data.dto.request.InquiriesRequest import com.eatssu.android.data.service.InquiresService import com.eatssu.android.databinding.ActivityInquireBinding import com.eatssu.android.util.MySharedPreferences diff --git a/app/src/main/java/com/eatssu/android/ui/review/etc/DeleteViewModel.kt b/app/src/main/java/com/eatssu/android/ui/review/etc/DeleteViewModel.kt index fa8e9149..f9af73c2 100644 --- a/app/src/main/java/com/eatssu/android/ui/review/etc/DeleteViewModel.kt +++ b/app/src/main/java/com/eatssu/android/ui/review/etc/DeleteViewModel.kt @@ -24,8 +24,11 @@ class DeleteViewModel : ViewModel() { val service = RetrofitImpl.retrofit.create(ReviewService::class.java) viewModelScope.launch(Dispatchers.IO) { - service.delReview(reviewId).enqueue(object : Callback> { - override fun onResponse(call: Call>, response: Response>) { + service.deleteReview(reviewId).enqueue(object : Callback> { + override fun onResponse( + call: Call>, + response: Response>, + ) { if (response.isSuccessful) { if (response.code() == 200) { handleSuccessResponse("삭제가 완료되었습니다.") diff --git a/app/src/main/java/com/eatssu/android/ui/review/etc/FixViewModel.kt b/app/src/main/java/com/eatssu/android/ui/review/etc/FixViewModel.kt index 9091e443..1979448a 100644 --- a/app/src/main/java/com/eatssu/android/ui/review/etc/FixViewModel.kt +++ b/app/src/main/java/com/eatssu/android/ui/review/etc/FixViewModel.kt @@ -5,12 +5,11 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.eatssu.android.base.BaseResponse +import com.eatssu.android.data.dto.request.ModifyReviewRequest import com.eatssu.android.data.service.ReviewService import com.eatssu.android.util.RetrofitImpl import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import okhttp3.MediaType.Companion.toMediaTypeOrNull -import okhttp3.RequestBody.Companion.toRequestBody import retrofit2.Call import retrofit2.Callback import retrofit2.Response @@ -26,14 +25,7 @@ class FixViewModel : ViewModel() { fun postData(reviewId: Long, comment: String, mainGrade: Int, amountGrade: Int, tasteGrade: Int) { val service = RetrofitImpl.retrofit.create(ReviewService::class.java) - val reviewData = """ - { - "mainGrade": $mainGrade, - "amountGrade": $amountGrade, - "tasteGrade": $tasteGrade, - "content": "$comment" - } - """.trimIndent().toRequestBody("application/json".toMediaTypeOrNull()) + val reviewData = ModifyReviewRequest(mainGrade, amountGrade, tasteGrade, comment) viewModelScope.launch(Dispatchers.IO) { service.modifyReview(reviewId, reviewData).enqueue(object : Callback> { diff --git a/app/src/main/java/com/eatssu/android/ui/review/list/ReviewActivity.kt b/app/src/main/java/com/eatssu/android/ui/review/list/ReviewActivity.kt index deafdbe9..0fbd9092 100644 --- a/app/src/main/java/com/eatssu/android/ui/review/list/ReviewActivity.kt +++ b/app/src/main/java/com/eatssu/android/ui/review/list/ReviewActivity.kt @@ -5,6 +5,7 @@ import android.os.Bundle import android.util.Log import android.view.View import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import com.eatssu.android.base.BaseActivity import com.eatssu.android.data.enums.MenuType @@ -13,6 +14,8 @@ import com.eatssu.android.databinding.ActivityReviewBinding import com.eatssu.android.ui.review.write.ReviewWriteMenuActivity import com.eatssu.android.ui.review.write.ReviewWriteRateActivity import com.eatssu.android.util.RetrofitImpl.retrofit +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.launch import kotlin.properties.Delegates class ReviewActivity : @@ -47,7 +50,7 @@ class ReviewActivity : when (menuType) { "FIX" -> { viewModel.loadReviewList(MenuType.FIX, 0, itemId) - viewModel.loadReviewInfo(MenuType.FIX, 0, itemId) + viewModel.loadMenuReviewInfo(MenuType.FIX, itemId) binding.btnNextReview.setOnClickListener { val intent = Intent(this, ReviewWriteRateActivity::class.java) // 인텐트를 생성해줌, @@ -62,7 +65,7 @@ class ReviewActivity : "CHANGE" -> { viewModel.loadReviewList(MenuType.CHANGE, itemId, 0) - viewModel.loadReviewInfo(MenuType.CHANGE, itemId, 0) + viewModel.loadMealReviewInfo(MenuType.CHANGE, itemId) val mealId = itemId binding.btnNextReview.setOnClickListener { @@ -86,67 +89,62 @@ class ReviewActivity : } } - setInfoData() - setListData() + setData() } - private fun setListData() { - viewModel.reviewList.observe(this) { reviewList -> - Log.d("ReviewActivity", reviewList.dataList.toString()) - - if (reviewList.numberOfElements == 0) { - Log.d("ReviewListActivity","리뷰가 없음") - binding.llNonReview.visibility = View.VISIBLE - binding.rvReview.visibility = View.INVISIBLE - } else { - binding.llNonReview.visibility = View.INVISIBLE - binding.rvReview.visibility = View.VISIBLE - adapter = ReviewAdapter(reviewList) - val recyclerView = binding.rvReview - recyclerView.adapter = adapter - recyclerView.layoutManager = LinearLayoutManager(this) - recyclerView.setHasFixedSize(true) -// recyclerView.addItemDecoration(DividerItemDecoration(this, LinearLayout.VERTICAL))//구분선 주석처리 + private fun setData() { + lifecycleScope.launch { + viewModel.state.collectLatest { + if (!it.error && !it.loading) { + it.reviewList + if (it.isEmpty) { + Log.d("ReviewListActivity", "리뷰가 없음") + binding.llNonReview.visibility = View.VISIBLE + binding.rvReview.visibility = View.INVISIBLE + } else { + binding.llNonReview.visibility = View.INVISIBLE + binding.rvReview.visibility = View.VISIBLE + adapter = ReviewAdapter(it.reviewList) + val recyclerView = binding.rvReview + recyclerView.adapter = adapter + recyclerView.layoutManager = LinearLayoutManager(applicationContext) + recyclerView.setHasFixedSize(true) + + + it.review?.apply { + binding.tvMenu.text = name.toString().replace(Regex("[\\[\\]]"), "") + binding.tvRate.text = + String.format("%.1f", mainRating?.toFloat()) + .toFloat().toString() + binding.tvGradeTaste.text = + String.format("%.1f", tasteRating?.toFloat()) + .toFloat().toString() + binding.tvGradeAmount.text = + String.format("%.1f", amountRating?.toFloat()) + .toFloat().toString() + binding.tvReviewNumCount.text = reviewCnt.toString() + + val totalReviewCount = reviewCnt ?: 0 + binding.progressBar1.max = totalReviewCount + binding.progressBar2.max = totalReviewCount + binding.progressBar3.max = totalReviewCount + binding.progressBar4.max = totalReviewCount + binding.progressBar5.max = totalReviewCount + + + binding.progressBar1.progress = ratingDetails.one + binding.progressBar2.progress = ratingDetails.two + binding.progressBar3.progress = ratingDetails.three + binding.progressBar4.progress = ratingDetails.four + binding.progressBar5.progress = ratingDetails.five + } + } + } } } } - private fun setInfoData() { - - viewModel.reviewInfo.observe(this) { reviewInfo -> - Log.d("post", reviewInfo.toString()) - - binding.tvMenu.text = reviewInfo.menuName.toString().replace(Regex("[\\[\\]]"), "") - - binding.tvRate.text = - String.format("%.1f", reviewInfo.mainGrade.toFloat()) - .toFloat().toString() - binding.tvGradeTaste.text = - String.format("%.1f", reviewInfo.tasteGrade.toFloat()) - .toFloat().toString() - binding.tvGradeAmount.text = - String.format("%.1f", reviewInfo.amountGrade.toFloat()) - .toFloat().toString() - binding.tvReviewNumCount.text = reviewInfo.totalReviewCount.toString() - - val cnt = reviewInfo.totalReviewCount - - binding.progressBar1.max = cnt - binding.progressBar2.max = cnt - binding.progressBar3.max = cnt - binding.progressBar4.max = cnt - binding.progressBar5.max = cnt - - binding.progressBar1.progress = reviewInfo.reviewGradeCnt.oneCnt - binding.progressBar2.progress = reviewInfo.reviewGradeCnt.twoCnt - binding.progressBar3.progress = reviewInfo.reviewGradeCnt.threeCnt - binding.progressBar4.progress = reviewInfo.reviewGradeCnt.fourCnt - binding.progressBar5.progress = reviewInfo.reviewGradeCnt.fiveCnt - - } - } - override fun onRestart() { super.onRestart() @@ -156,12 +154,12 @@ class ReviewActivity : when (menuType) { "FIX" -> { viewModel.loadReviewList(MenuType.FIX, 0, itemId) - viewModel.loadReviewInfo(MenuType.FIX, 0, itemId) + viewModel.loadMenuReviewInfo(MenuType.FIX, itemId) } "CHANGE" -> { viewModel.loadReviewList(MenuType.CHANGE, itemId, 0) - viewModel.loadReviewInfo(MenuType.CHANGE, itemId, 0) + viewModel.loadMealReviewInfo(MenuType.CHANGE, itemId) } else -> { @@ -170,43 +168,39 @@ class ReviewActivity : } Log.d("post", "onRestart") - setInfoData() - - Log.d("post", "onRestart" + viewModel.reviewInfo.value) - setListData() + setData() Log.d("post", "onRestart") } - override fun onResume() { - super.onResume() - Log.d("post", "resume") - - // 다시 데이터를 로드하고 어댑터를 업데이트 - when (menuType) { - "FIX" -> { - viewModel.loadReviewList(MenuType.FIX, 0, itemId) - viewModel.loadReviewInfo(MenuType.FIX, 0, itemId) - } - - "CHANGE" -> { - viewModel.loadReviewList(MenuType.CHANGE, itemId, 0) - viewModel.loadReviewInfo(MenuType.CHANGE, itemId, 0) - } - - else -> { - Log.d("post", "잘못된 식당 정보입니다.") - } - } - Log.d("post", "resume시작") - - setInfoData() - - Log.d("post", "resume중간") - - setListData() - - Log.d("post", "resume끝") - } +// override fun onResume() { +// super.onResume() +// Log.d("post", "resume") +// +// // 다시 데이터를 로드하고 어댑터를 업데이트 +// when (menuType) { +// "FIX" -> { +// viewModel.loadReviewList(MenuType.FIX, 0, itemId) +// viewModel.loadMenuReviewInfo(MenuType.FIX, itemId) +// } +// +// "CHANGE" -> { +// viewModel.loadReviewList(MenuType.CHANGE, itemId, 0) +// viewModel.loadMealReviewInfo(MenuType.CHANGE, itemId) +// } +// +// else -> { +// Log.d("post", "잘못된 식당 정보입니다.") +// } +// } +// Log.d("post", "resume시작") +// +// +// Log.d("post", "resume중간") +// +// setListData() +// +// Log.d("post", "resume끝") +// } } diff --git a/app/src/main/java/com/eatssu/android/ui/review/list/ReviewAdapter.kt b/app/src/main/java/com/eatssu/android/ui/review/list/ReviewAdapter.kt index 8bc87deb..7409686a 100644 --- a/app/src/main/java/com/eatssu/android/ui/review/list/ReviewAdapter.kt +++ b/app/src/main/java/com/eatssu/android/ui/review/list/ReviewAdapter.kt @@ -15,14 +15,14 @@ import com.eatssu.android.ui.review.etc.MyReviewDialogActivity import com.eatssu.android.ui.review.etc.ReportActivity -class ReviewAdapter(private val dataList: GetReviewListResponse) : +class ReviewAdapter(private val dataList: GetReviewListResponse?) : RecyclerView.Adapter() { inner class ViewHolder(private val binding: ItemReviewBinding) : RecyclerView.ViewHolder(binding.root) { fun bind(position: Int) { - val data = dataList.dataList?.get(position) + val data = dataList?.dataList?.get(position) binding.tvWriterNickname.text = data?.writerNickname.toString() binding.tvReviewItemComment.text = data?.content binding.tvReviewItemDate.text = data?.writeDate @@ -62,7 +62,7 @@ class ReviewAdapter(private val dataList: GetReviewListResponse) : RecyclerView.ViewHolder(binding.root) { fun bind(position: Int) { - val data = dataList.dataList?.get(position) + val data = dataList?.dataList?.get(position) binding.tvWriterNickname.text = data?.writerNickname.toString() binding.tvReviewItemComment.text = data?.content binding.tvReviewItemDate.text = data?.writeDate @@ -120,14 +120,14 @@ class ReviewAdapter(private val dataList: GetReviewListResponse) : } override fun getItemViewType(position: Int): Int { - return if (dataList.dataList?.get(position)?.isWriter == true) { + return if (dataList?.dataList?.get(position)?.isWriter == true) { VIEW_TYPE_MY_REVIEW } else { VIEW_TYPE_OTHERS_REVIEW } } - override fun getItemCount(): Int = dataList.dataList?.size ?: 0 + override fun getItemCount(): Int = dataList?.dataList?.size ?: 0 companion object { private const val VIEW_TYPE_MY_REVIEW = 1 diff --git a/app/src/main/java/com/eatssu/android/ui/review/list/ReviewViewModel.kt b/app/src/main/java/com/eatssu/android/ui/review/list/ReviewViewModel.kt index 177b7abb..19e91560 100644 --- a/app/src/main/java/com/eatssu/android/ui/review/list/ReviewViewModel.kt +++ b/app/src/main/java/com/eatssu/android/ui/review/list/ReviewViewModel.kt @@ -1,14 +1,18 @@ package com.eatssu.android.ui.review.list import android.util.Log -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.eatssu.android.base.BaseResponse import com.eatssu.android.data.dto.response.GetReviewInfoResponse import com.eatssu.android.data.dto.response.GetReviewListResponse import com.eatssu.android.data.enums.MenuType +import com.eatssu.android.data.model.Review import com.eatssu.android.data.service.ReviewService +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import retrofit2.Call import retrofit2.Callback @@ -17,39 +21,92 @@ import retrofit2.Response class ReviewViewModel(private val reviewService: ReviewService) : ViewModel() { - private val _reviewList = MutableLiveData() - val reviewList: LiveData = _reviewList + private val _itemState: MutableStateFlow = + MutableStateFlow(ItemState(MenuType.FIX, 0)) + val ItemState: StateFlow = _itemState.asStateFlow() - private val _reviewInfo = MutableLiveData() - val reviewInfo: LiveData = _reviewInfo + private val _state: MutableStateFlow = MutableStateFlow(ReviewState()) + val state: StateFlow = _state.asStateFlow() - fun loadReviewList( +// private val _reviewList = MutableLiveData() +// val reviewList: LiveData = _reviewList +// +// private val _reviewInfo = MutableLiveData() +// val reviewInfo: LiveData = _reviewInfo + + + fun loadMenuReviewInfo( menuType: MenuType, - mealId: Long?, - menuId: Long?, -// lastReviewId: Long?, -// page: Int?, -// size: Int?, + menuId: Long, ) { viewModelScope.launch { - reviewService.getReviewList(menuType.toString(), mealId, menuId) - .enqueue(object : Callback { + reviewService.getMenuReviewInfo(menuType.toString(), menuId) + .enqueue(object : Callback> { override fun onResponse( - call: Call, response: Response + call: Call>, + response: Response>, ) { if (response.isSuccessful) { + val data = response.body()?.result!! + + // 정상적으로 통신이 성공된 경우 Log.d("post", "onResponse 성공: " + response.body().toString()) - _reviewList.postValue(response.body()) + _state.update { + it.copy( +// review = data.asReview() + ) + } + + } else { + // 통신이 실패한 경우(응답코드 3xx, 4xx 등) + Log.d("post", "onResponse 실패") + } + } + + override fun onFailure( + call: Call>, + t: Throwable, + ) { + // 통신 실패 (인터넷 끊킴, 예외 발생 등 시스템적인 이유) + Log.d("post", "onFailure 에러: " + t.message.toString()) + } + }) + } + } + + fun loadMealReviewInfo( + menuType: MenuType, + menuId: Long, + ) { + viewModelScope.launch { + reviewService.getMealReviewInfo(menuId) + .enqueue(object : Callback> { + override fun onResponse( + call: Call>, + response: Response>, + ) { + if (response.isSuccessful) { + val data = response.body()?.result!! + // 정상적으로 통신이 성공된 경우 + Log.d("post", "onResponse 성공: " + response.body().toString()) + _state.update { + it.copy( +// review = data.asReview() + ) + } } else { // 통신이 실패한 경우(응답코드 3xx, 4xx 등) Log.d("post", "onResponse 실패") } } - override fun onFailure(call: Call, t: Throwable) { + override fun onFailure( + call: Call>, + t: Throwable, + ) { // 통신 실패 (인터넷 끊킴, 예외 발생 등 시스템적인 이유) Log.d("post", "onFailure 에러: " + t.message.toString()) } @@ -57,23 +114,39 @@ class ReviewViewModel(private val reviewService: ReviewService) : ViewModel() { } } - fun loadReviewInfo( + fun loadReviewList( menuType: MenuType, mealId: Long?, menuId: Long?, +// lastReviewId: Long?, +// page: Int?, +// size: Int?, ) { viewModelScope.launch { - reviewService.getRreviewInfo(menuType.toString(), mealId, menuId) - .enqueue(object : Callback { + reviewService.getReviewList(menuType.toString(), mealId, menuId) + .enqueue(object : Callback> { override fun onResponse( - call: Call, - response: Response, + call: Call>, + response: Response>, ) { if (response.isSuccessful) { + + val data = response.body()?.result!! + + if (data.dataList!!.isNotEmpty()) { + Log.d("post", "onResponse 성공: " + response.body().toString()) + _state.update { + it.copy( + error = true, + reviewList = data, + isEmpty = false + ) + } + + } // 정상적으로 통신이 성공된 경우 - Log.d("post", "onResponse 성공: " + response.body().toString()) - _reviewInfo.postValue(response.body()) + } else { // 통신이 실패한 경우(응답코드 3xx, 4xx 등) @@ -81,11 +154,31 @@ class ReviewViewModel(private val reviewService: ReviewService) : ViewModel() { } } - override fun onFailure(call: Call, t: Throwable) { + override fun onFailure( + call: Call>, + t: Throwable, + ) { // 통신 실패 (인터넷 끊킴, 예외 발생 등 시스템적인 이유) Log.d("post", "onFailure 에러: " + t.message.toString()) } }) } } -} \ No newline at end of file +} + +data class ItemState( + var menuType: MenuType, + var itemid: Long, +) + +data class ReviewState( +// var toastMessage: String = "", + var loading: Boolean = true, + var error: Boolean = false, +// var tokens: TokenResponse? = null, + var review: Review? = null, + var reviewList: GetReviewListResponse? = null, + var isEmpty: Boolean = true, + var menuType: MenuType? = MenuType.FIX, + var itemId: Long? = 0, +) \ No newline at end of file diff --git a/app/src/main/java/com/eatssu/android/ui/review/write/ImageViewModel.kt b/app/src/main/java/com/eatssu/android/ui/review/write/ImageViewModel.kt new file mode 100644 index 00000000..ac235cb1 --- /dev/null +++ b/app/src/main/java/com/eatssu/android/ui/review/write/ImageViewModel.kt @@ -0,0 +1,89 @@ +package com.eatssu.android.ui.review.write + +import android.util.Log +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.eatssu.android.App +import com.eatssu.android.base.BaseResponse +import com.eatssu.android.data.dto.response.ImageResponse +import com.eatssu.android.data.service.ImageService +import id.zelory.compressor.Compressor +import id.zelory.compressor.constraint.quality +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.MultipartBody +import okhttp3.RequestBody.Companion.asRequestBody +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response +import java.io.File + +class ImageViewModel( + private val imageService: ImageService, +) : ViewModel() { + + private val _imageUrl = MutableLiveData() + val imageUrl: LiveData get() = _imageUrl + + private var _imageFile = MutableLiveData() + val imageFile: LiveData get() = _imageFile + + fun setImageFile(imageFile: File) { + _imageFile.value = imageFile + } + + fun deleteFile() { + _imageFile.value?.delete() + _imageUrl.value = "" + } + + private suspend fun compressImage(imageFile: File): MultipartBody.Part { + + val compressedFile = Compressor.compress(App.appContext, imageFile) { quality(80) } + val requestFile = compressedFile.asRequestBody("image/*".toMediaTypeOrNull()) + + return MultipartBody.Part.createFormData( + "image", + compressedFile.name, + requestFile + ) + } + + suspend fun saveS3() { + val compressImageString = imageFile.value?.let { compressImage(it) } + + if (compressImageString != null) { + imageService.getImageUrl(compressImageString).enqueue( + object : Callback> { + override fun onResponse( + call: Call>, + response: Response>, + ) { + if (response.isSuccessful) { + // 정상적으로 통신이 성공된 경우 + _imageUrl.value = response.body()?.result?.url.toString() + Log.d("ImageViewModel", _imageUrl.value.toString()) + + + } else { + // 통신이 실패한 경우(응답코드 3xx, 4xx 등) + Log.d("ImageViewModel", "onResponse 리뷰 작성 실패") + } + } + + override fun onFailure( + call: Call>, + t: Throwable, + ) { + // 통신 실패 (인터넷 끊킴, 예외 발생 등 시스템적인 이유) + Log.d( + "ImageViewModel", + "onFailure 에러: " + t.message.toString() + ) + } + }) + } + } +} + + diff --git a/app/src/main/java/com/eatssu/android/ui/review/write/ImageViewModelFactory.kt b/app/src/main/java/com/eatssu/android/ui/review/write/ImageViewModelFactory.kt new file mode 100644 index 00000000..38e7c67c --- /dev/null +++ b/app/src/main/java/com/eatssu/android/ui/review/write/ImageViewModelFactory.kt @@ -0,0 +1,18 @@ +package com.eatssu.android.ui.review.write + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.eatssu.android.data.service.ImageService + +class ImageViewModelFactory(private val imageService: ImageService) : + ViewModelProvider.Factory { + + override fun create(modelClass: Class): T { + if (modelClass.isAssignableFrom(ImageViewModel::class.java)) { + @Suppress("UNCHECKED_CAST") + return ImageViewModel(imageService) as T + } + throw IllegalArgumentException("Unknown ViewModel class") + } +} + diff --git a/app/src/main/java/com/eatssu/android/ui/review/write/ReviewWriteRateActivity.kt b/app/src/main/java/com/eatssu/android/ui/review/write/ReviewWriteRateActivity.kt index b4c378ff..216e1b53 100644 --- a/app/src/main/java/com/eatssu/android/ui/review/write/ReviewWriteRateActivity.kt +++ b/app/src/main/java/com/eatssu/android/ui/review/write/ReviewWriteRateActivity.kt @@ -1,9 +1,10 @@ package com.eatssu.android.ui.review.write -import android.app.Activity +import android.Manifest import android.content.Intent import android.content.pm.PackageManager import android.net.Uri +import android.os.Build import android.os.Bundle import android.provider.MediaStore import android.text.Editable @@ -11,34 +12,43 @@ import android.text.TextWatcher import android.util.Log import android.view.View import android.widget.Toast +import androidx.activity.result.contract.ActivityResultContracts +import androidx.core.app.ActivityCompat +import androidx.core.content.ContextCompat import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.viewModelScope +import com.bumptech.glide.Glide +import com.bumptech.glide.request.RequestOptions import com.eatssu.android.base.BaseActivity +import com.eatssu.android.data.service.ImageService import com.eatssu.android.data.service.ReviewService import com.eatssu.android.databinding.ActivityReviewWriteRateBinding import com.eatssu.android.util.RetrofitImpl.mRetrofit -import id.zelory.compressor.Compressor -import id.zelory.compressor.constraint.quality +import com.eatssu.android.util.RetrofitImpl.retrofit +import com.eatssu.android.util.extension.showToast +import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch -import okhttp3.MediaType.Companion.toMediaTypeOrNull -import okhttp3.MultipartBody -import okhttp3.RequestBody.Companion.asRequestBody -import okhttp3.RequestBody.Companion.toRequestBody import java.io.File class ReviewWriteRateActivity : BaseActivity(ActivityReviewWriteRateBinding::inflate) { private lateinit var viewModel: UploadReviewViewModel + private lateinit var imageviewModel: ImageViewModel + private lateinit var reviewService: ReviewService + private lateinit var imageService: ImageService private val PERMISSION_REQUEST_CODE = 1 - private var selectedImagePath: String? = null - private var itemId: Long = 0 private lateinit var itemName: String private var comment: String? = "" +// private var imageUrlString = "" + + private lateinit var imageFile: File + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -47,7 +57,7 @@ class ReviewWriteRateActivity : itemName = intent.getStringExtra("itemName").toString() Log.d("post", "고정메뉴${itemName}") - itemId = intent.getLongExtra("itemId", 0) + itemId = intent.getLongExtra("itemId", 16) // 현재 메뉴명을 표시합니다. binding.menu.text = itemName @@ -59,33 +69,162 @@ class ReviewWriteRateActivity : setupTextReviewInput() // 이미지 추가 버튼 클릭 리스너 설정 - binding.ibAddPic.setOnClickListener { openGallery() } + binding.ibAddPic.setOnClickListener { + Log.d("re", "클릭") - reviewService = mRetrofit.create(ReviewService::class.java) - viewModel = ViewModelProvider(this, UploadReviewViewModelFactory(reviewService)) - .get(UploadReviewViewModel::class.java) + checkPermission() + } + imageService = mRetrofit.create(ImageService::class.java) + reviewService = retrofit.create(ReviewService::class.java) - setupUI() - observeViewModel() + viewModel = ViewModelProvider( + this, + ReviewWriteViewModelFactory(reviewService) + )[UploadReviewViewModel::class.java] + imageviewModel = + ViewModelProvider(this, ImageViewModelFactory(imageService))[ImageViewModel::class.java] + + setupUI() } - private fun setupUI() { - binding.btnNextReview2.setOnClickListener { postReview() } - binding.btnDelete.setOnClickListener { deleteImage() } + + // // 이미지를 결과값으로 받는 변수 + private val imageResult = registerForActivityResult( + ActivityResultContracts.StartActivityForResult() + ) { result -> + if (result.resultCode == RESULT_OK) { + // 이미지를 받으면 ImageView에 적용한다 + val imageUri = result.data?.data + imageUri?.let { + + // 서버 업로드를 위해 파일 형태로 변환한다 + imageFile = File(getRealPathFromURI(it)) + + // 이미지를 불러온다 + Glide.with(this) + .load(imageUri) + .fitCenter() + .apply(RequestOptions().override(500, 500)) + .into(binding.ivImage) + + + imageviewModel.viewModelScope.launch { + + imageviewModel.setImageFile(imageFile) + imageviewModel.saveS3() //이미지 url 반환 api 호출 + + } + + binding.ivImage.visibility = View.VISIBLE + binding.btnDelete.visibility = View.VISIBLE + } + } + } - private fun observeViewModel() { - viewModel.isUpload.observe(this) { isUpload -> - if (isUpload == true) { finish() } + // 이미지 실제 경로 반환 + fun getRealPathFromURI(uri: Uri): String { + val buildName = Build.MANUFACTURER + if (buildName.equals("Xiaomi")) { + return uri.path!! + } + var columnIndex = 0 + val proj = arrayOf(MediaStore.Images.Media.DATA) + val cursor = contentResolver.query(uri, proj, null, null, null) + if (cursor!!.moveToFirst()) { + columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA) } - viewModel.toastMessage.observe(this) { message -> - Toast.makeText(this, message, Toast.LENGTH_SHORT).show() + val result = cursor.getString(columnIndex) + cursor.close() + + Log.d("ReviewWriteRateActivity", result) + return result + } + + // 갤러리를 부르는 메서드 + private fun checkPermission() { + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + + checkSelfPermission(Manifest.permission.READ_MEDIA_IMAGES) + + val readMediaImagePermission = + ContextCompat.checkSelfPermission(this, Manifest.permission.READ_MEDIA_IMAGES) + + //권한 확인 + if (readMediaImagePermission == PackageManager.PERMISSION_DENIED) { + ActivityCompat.requestPermissions( + this, arrayOf( + Manifest.permission.READ_MEDIA_IMAGES, + ), REQ_GALLERY + ) + Log.d("ReviewWriteRateActivity", "권한 없음") + + } else { + openGallery() + + } + + } else { + checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) + + val writePermission = + ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) + val readPermission = + ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) + //권한 확인 + if (writePermission == PackageManager.PERMISSION_DENIED || + readPermission == PackageManager.PERMISSION_DENIED + ) { + + // 권한 요청 + ActivityCompat.requestPermissions( + this, arrayOf( + Manifest.permission.WRITE_EXTERNAL_STORAGE, + Manifest.permission.READ_EXTERNAL_STORAGE + ), REQ_GALLERY + ) + Log.d("ReviewWriteRateActivity", "권한 없음") + + } else { + openGallery() + } } } + + private fun openGallery() { + val intent = Intent(Intent.ACTION_PICK) + // intent의 data와 type을 동시에 설정하는 메서드 + intent.setDataAndType( + MediaStore.Images.Media.EXTERNAL_CONTENT_URI, + "image/*" + ) + + imageResult.launch(intent) + } + + + private fun setupUI() { + binding.btnNextReview2.setOnClickListener { postReview() } + binding.btnDelete.setOnClickListener { deleteImage() } + } + +// private fun observeViewModel() { +// viewModel.isUpload.observe(this) { isUpload -> +// if (isUpload == true) { +// finish() +// } +// +// } +// viewModel.toastMessage.observe(this) { message -> +// Toast.makeText(this, message, Toast.LENGTH_SHORT).show() +// } +// } + private fun requestStoragePermission() { if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED @@ -97,6 +236,7 @@ class ReviewWriteRateActivity : } } + private fun setupTextReviewInput() { binding.etReview2Comment.addTextChangedListener(object : TextWatcher { override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {} @@ -109,121 +249,80 @@ class ReviewWriteRateActivity : }) } - private fun openGallery() { - val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI) - startActivityForResult(intent, REQUEST_IMAGE_PICK) - } - - @Deprecated("Deprecated in Java") - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) - if (requestCode == REQUEST_IMAGE_PICK && resultCode == Activity.RESULT_OK && data != null) { - val imageUri = data.data - binding.ivImage.setImageURI(imageUri) - selectedImagePath = imageUri?.let { getImagePath(it) } - selectedImagePath?.let { Log.d("path", it) } - binding.ivImage.visibility = View.VISIBLE - binding.btnDelete.visibility = View.VISIBLE - } - } - - private fun getImagePath(uri: Uri): String? { - val projection = arrayOf(MediaStore.Images.Media.DATA) - val cursor = contentResolver.query(uri, projection, null, null, null) - cursor?.use { - if (it.moveToFirst()) { - val columnIndex = it.getColumnIndexOrThrow(MediaStore.Images.Media.DATA) - return it.getString(columnIndex) - } - } - return null - } private fun deleteImage() { - Log.d("ReviewWriteRateActivity", selectedImagePath.toString()) - if (selectedImagePath != null) { - val imageFile = File(selectedImagePath) - if (imageFile.exists()) { - Toast.makeText(this, "이미지가 삭제되었습니다.", Toast.LENGTH_SHORT).show() - binding.ivImage.setImageDrawable(null) - selectedImagePath = null - binding.ivImage.visibility = View.GONE - binding.btnDelete.visibility = View.GONE - } else { - Toast.makeText(this, "이미지를 삭제할 수 없습니다.", Toast.LENGTH_SHORT).show() - } + Log.d("ReviewWriteRateActivity", imageFile.toString()) + if (imageFile.exists()) { + Toast.makeText(this, "이미지가 삭제되었습니다.", Toast.LENGTH_SHORT).show() + binding.ivImage.setImageDrawable(null) + imageFile.delete() //file을 날린다. + binding.ivImage.visibility = View.GONE + binding.btnDelete.visibility = View.GONE + + imageviewModel.deleteFile() } else { - Toast.makeText(this, "이미지가 선택되지 않았습니다.", Toast.LENGTH_SHORT).show() + Toast.makeText(this, "이미지를 삭제할 수 없습니다.", Toast.LENGTH_SHORT).show() } } private fun postReview() { - // Check if any of the main, amount, or taste grades is 0 if (binding.rbMain.rating.toInt() == 0 || binding.rbAmount.rating.toInt() == 0 || binding.rbTaste.rating.toInt() == 0) { Toast.makeText(this, "별점을 등록해주세요", Toast.LENGTH_SHORT).show() - return // Do not proceed with the review posting if any grade is 0 + return } - // Check if the comment has at least 3 characters if ((comment?.trim()?.length ?: 0) < 3) { Toast.makeText(this, "3자 이상 입력해주세요", Toast.LENGTH_SHORT).show() - return // Do not proceed with the review posting if the comment is too short + return } - // Proceed with posting the review - val reviewData = """ - { - "mainGrade": ${binding.rbMain.rating.toInt()}, - "amountGrade": ${binding.rbAmount.rating.toInt()}, - "tasteGrade": ${binding.rbTaste.rating.toInt()}, - "content": "$comment" - } -""".trimIndent().toRequestBody("application/json".toMediaTypeOrNull()) - val fileList: List = listOf(selectedImagePath) + //Todo imageurl을 체크해야하는 이유? + + + viewModel.setReviewData( + itemId, + binding.rbMain.rating.toInt(), + binding.rbAmount.rating.toInt(), + binding.rbTaste.rating.toInt(), + comment.toString(), + imageviewModel.imageUrl.value ?: "" + ) + + viewModel.postReview() lifecycleScope.launch { - val compressedPartsList = compressImage(fileList) - // Make the file list nullable - if (compressedPartsList == null) { - viewModel.postReview(itemId, reviewData) - Log.d("ReviewWriteRateActivity", "사진 없는 리뷰") - } else { - viewModel.postReview(itemId, compressedPartsList, reviewData) - Log.d("ReviewWriteRateActivity", "사진 있는 리뷰") + viewModel.state.collectLatest { + if (it.error) { + showToast(viewModel.state.value.toastMessage) + } + if (it.isUpload) { + showToast(viewModel.state.value.toastMessage) +// finish() + } + } + + Log.d("ReviewWriteRateActivity", "리뷰 씀") } + val resultIntent = Intent() setResult(RESULT_OK, resultIntent) Log.d("ReviewWriteRateActivity", "리뷰 다씀") } - private suspend fun compressImage(fileList: List): List? { - val compressedPartsList: MutableList = mutableListOf() - - fileList.forEach { filePath -> - // Check if the file path is not null and compress the image - if (filePath != null) { - val file = File(filePath) - val compressedFile = Compressor.compress(this@ReviewWriteRateActivity, file) { - quality(80) - } - val requestFile = compressedFile.asRequestBody("image/*".toMediaTypeOrNull()) - val part = MultipartBody.Part.createFormData( - "multipartFileList", - compressedFile.name, - requestFile - ) - compressedPartsList.add(part) - } - } + companion object { + const val REVIEW_MIN_LENGTH = 10 - return if (compressedPartsList.isEmpty()) null else compressedPartsList - } + // 갤러리 권한 요청 + const val REQ_GALLERY = 1 - companion object { - private const val REQUEST_IMAGE_PICK = 1 + // API 호출시 Parameter key값 + const val PARAM_KEY_IMAGE = "image" + const val PARAM_KEY_PRODUCT_ID = "product_id" + const val PARAM_KEY_REVIEW = "review_content" + const val PARAM_KEY_RATING = "rating" } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/eatssu/android/ui/review/write/ReviewWriteViewModel.kt b/app/src/main/java/com/eatssu/android/ui/review/write/ReviewWriteViewModel.kt new file mode 100644 index 00000000..efc5890c --- /dev/null +++ b/app/src/main/java/com/eatssu/android/ui/review/write/ReviewWriteViewModel.kt @@ -0,0 +1,91 @@ +package com.eatssu.android.ui.review.write + +import android.util.Log +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.eatssu.android.base.BaseResponse +import com.eatssu.android.data.dto.request.WriteReviewRequest +import com.eatssu.android.data.service.ReviewService +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response + + +class UploadReviewViewModel(private val reviewService: ReviewService) : ViewModel() { + + private val _state: MutableStateFlow = MutableStateFlow(UploadReviewState()) + val state: StateFlow = _state.asStateFlow() + + private val _reviewData: MutableStateFlow = + MutableStateFlow(WriteReviewRequest()) + val reviewData: StateFlow = _reviewData.asStateFlow() + + private val _menuId: MutableStateFlow = MutableStateFlow(-1) + val menuId: StateFlow = _menuId.asStateFlow() + + fun setReviewData( + menuId: Long, + mainRating: Int, + amountRating: Int, + tasteRating: Int, + comment: String, + imageUrl: String, + ) { + _menuId.value = menuId + _reviewData.value = + WriteReviewRequest(mainRating, amountRating, tasteRating, comment, imageUrl) + } + + + fun postReview() { + viewModelScope.launch { + reviewService.writeReview( + menuId.value, reviewData.value + ).enqueue(object : Callback> { + override fun onResponse( + call: Call>, + response: Response>, + ) { + if (response.isSuccessful) { + if (response.body()?.isSuccess == true) { + + _state.value.toastMessage = "리뷰 작성에 성공하였습니다. " + _state.value.isUpload = true + + Log.d( + "UploadReviewViewModel", + "onResponse 리뷰 작성 성공: " + response.body().toString() + ) + + } + } else { + // 통신이 실패한 경우(응답코드 3xx, 4xx 등) + _state.value.error = true + _state.value.toastMessage = "리뷰 작성에 실패하였습니다. " + + Log.d("UploadReviewViewModel", "onResponse 리뷰 작성 실패") + } + } + + override fun onFailure(call: Call>, t: Throwable) { + // 통신 실패 (인터넷 끊킴, 예외 발생 등 시스템적인 이유) + _state.value.error = true + _state.value.toastMessage = "리뷰 작성에 실패하였습니다. " + + Log.d("UploadReviewViewModel", "onFailure 에러: " + t.message.toString()) + } + }) + } + } +} + +data class UploadReviewState( + var toastMessage: String = "", + var loading: Boolean = true, + var error: Boolean = false, + var isUpload: Boolean = false, +) \ No newline at end of file diff --git a/app/src/main/java/com/eatssu/android/ui/review/write/UploadReviewViewModelFactory.kt b/app/src/main/java/com/eatssu/android/ui/review/write/ReviewWriteViewModelFactory.kt similarity index 87% rename from app/src/main/java/com/eatssu/android/ui/review/write/UploadReviewViewModelFactory.kt rename to app/src/main/java/com/eatssu/android/ui/review/write/ReviewWriteViewModelFactory.kt index bc1423c1..40a55abc 100644 --- a/app/src/main/java/com/eatssu/android/ui/review/write/UploadReviewViewModelFactory.kt +++ b/app/src/main/java/com/eatssu/android/ui/review/write/ReviewWriteViewModelFactory.kt @@ -4,7 +4,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import com.eatssu.android.data.service.ReviewService -class UploadReviewViewModelFactory(private val reviewService: ReviewService) : +class ReviewWriteViewModelFactory(private val reviewService: ReviewService) : ViewModelProvider.Factory { override fun create(modelClass: Class): T { diff --git a/app/src/main/java/com/eatssu/android/ui/review/write/UploadReviewViewModel.kt b/app/src/main/java/com/eatssu/android/ui/review/write/UploadReviewViewModel.kt deleted file mode 100644 index a2afa1b9..00000000 --- a/app/src/main/java/com/eatssu/android/ui/review/write/UploadReviewViewModel.kt +++ /dev/null @@ -1,127 +0,0 @@ -package com.eatssu.android.ui.review.write - -import android.util.Log -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.eatssu.android.data.service.ReviewService -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import okhttp3.MultipartBody -import okhttp3.RequestBody -import retrofit2.Call -import retrofit2.Callback -import retrofit2.Response - - -class UploadReviewViewModel(private val reviewService: ReviewService) : ViewModel() { - - private val _isUpload = MutableLiveData() - val isUpload: LiveData get() = _isUpload - - private val _toastMessage = MutableLiveData() - val toastMessage: LiveData get() = _toastMessage - - /* - private val _reviewMenuId = MutableLiveData() - val reviewMenuId: LiveData = _reviewMenuId - - private val _reviewFiles = MutableLiveData>() - val reviewFiles: LiveData> = _reviewFiles - - private val _reviewData = MutableLiveData() - val reviewData: LiveData = _reviewData*/ - - - - - fun postReview( - menuId: Long, - compressedPartsList : List, - reviewData: RequestBody - ) { - viewModelScope.launch { - - reviewService.writeReview( - menuId, compressedPartsList, reviewData - ).enqueue(object : Callback { - override fun onResponse( - call: Call, - response: Response - ) { - if (response.isSuccessful) { - handleSuccessResponse("리뷰 작성에 성공하였습니다.") - // 정상적으로 통신이 성공된 경우 - Log.d("post", "onResponse 리뷰 작성 성공: " + response.body().toString()) -// onClickEvent() - - } else { - // 통신이 실패한 경우(응답코드 3xx, 4xx 등) - handleErrorResponse("리뷰 작성에 실패했습니다.") - Log.d("post", "onResponse 리뷰 작성 실패") - } - } - - override fun onFailure(call: Call, t: Throwable) { - // 통신 실패 (인터넷 끊킴, 예외 발생 등 시스템적인 이유) - handleErrorResponse("리뷰 작성에 실패했습니다.") - Log.d("post", "onFailure 에러: " + t.message.toString()) - } - }) - } - } - - fun postReview( - menuId: Long, - reviewData: RequestBody - ) { - viewModelScope.launch { - - reviewService.writeReview( - menuId, reviewData - ).enqueue(object : Callback { - override fun onResponse( - call: Call, - response: Response - ) { - if (response.isSuccessful) { - handleSuccessResponse("리뷰 작성에 성공하였습니다.") - // 정상적으로 통신이 성공된 경우 - Log.d("post", "onResponse 리뷰 작성 성공: " + response.body().toString()) -// onClickEvent() - - } else { - // 통신이 실패한 경우(응답코드 3xx, 4xx 등) - handleErrorResponse("리뷰 작성에 실패했습니다.") - Log.d("post", "onResponse 리뷰 작성 실패") - } - } - - override fun onFailure(call: Call, t: Throwable) { - // 통신 실패 (인터넷 끊킴, 예외 발생 등 시스템적인 이유) - handleErrorResponse("리뷰 작성에 실패했습니다.") - Log.d("post", "onFailure 에러: " + t.message.toString()) - } - }) - } - } - - - private fun handleSuccessResponse(message: String) { - viewModelScope.launch(Dispatchers.Main) { - _toastMessage.value = message - _isUpload.value = true - - } - } - - - - private fun handleErrorResponse(message: String) { - viewModelScope.launch(Dispatchers.Main) { - _toastMessage.value = message - _isUpload.value = false - } - } -} \ No newline at end of file