diff --git a/core/designsystem/src/main/java/com/dpm/designsystem/SpotSnackBar.kt b/core/designsystem/src/main/java/com/dpm/designsystem/SpotSnackBar.kt
index 1f857428..84a5feda 100644
--- a/core/designsystem/src/main/java/com/dpm/designsystem/SpotSnackBar.kt
+++ b/core/designsystem/src/main/java/com/dpm/designsystem/SpotSnackBar.kt
@@ -2,20 +2,26 @@ package com.dpm.designsystem
import android.graphics.Paint
import android.view.LayoutInflater
+import android.view.MotionEvent
import android.view.View
import androidx.core.content.ContextCompat
+import com.depromeet.designsystem.R
import com.depromeet.designsystem.databinding.SpotSnackbarBinding
+import com.dpm.designsystem.extension.dpToPx
import com.google.android.material.snackbar.Snackbar
+
class SpotSnackBar(
view: View,
+ private val snackBarBackground: Int,
private val message: String,
+ private val marginBottom: Int,
private val endMessage: String,
private val onClick: () -> Unit
) {
companion object {
- fun make(view: View, message: String = "", endMessage: String = "", onClick: () -> Unit) =
- SpotSnackBar(view, message, endMessage, onClick)
+ fun make(view: View, background: Int = R.drawable.rect_transfer_black_03_fill_60, marginBottom: Int = 0, message: String = "", endMessage: String = "", onClick: () -> Unit) =
+ SpotSnackBar(view = view, snackBarBackground = background, marginBottom = marginBottom, message= message, endMessage = endMessage, onClick = onClick)
}
private val context = view.context
@@ -32,14 +38,24 @@ class SpotSnackBar(
private fun initView() {
with(snackbarLayout) {
removeAllViews()
- setPadding(0, 0, 0, 0)
+ setPadding(0, 0, 0, marginBottom.dpToPx(context))
setBackgroundColor(ContextCompat.getColor(context, android.R.color.transparent))
addView(snackbarBinding.root, 0)
+
+ setOnTouchListener { v, event ->
+ if (event.action == MotionEvent.ACTION_UP) {
+ v.performClick()
+ true
+ } else {
+ false
+ }
+ }
}
with(snackbarBinding) {
tvDescription.text = message
tvTrigger.paintFlags = Paint.UNDERLINE_TEXT_FLAG
tvTrigger.text = endMessage
+ clContainer.setBackgroundResource(snackBarBackground)
}
}
diff --git a/core/designsystem/src/main/res/drawable/rect_body_subtitle_fill_60.xml b/core/designsystem/src/main/res/drawable/rect_body_subtitle_fill_60.xml
new file mode 100644
index 00000000..155fb747
--- /dev/null
+++ b/core/designsystem/src/main/res/drawable/rect_body_subtitle_fill_60.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/core/designsystem/src/main/res/layout/spot_snackbar.xml b/core/designsystem/src/main/res/layout/spot_snackbar.xml
index 4781c3b9..a0011f5b 100644
--- a/core/designsystem/src/main/res/layout/spot_snackbar.xml
+++ b/core/designsystem/src/main/res/layout/spot_snackbar.xml
@@ -4,34 +4,44 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="@drawable/rect_transfer_black_03_fill_60">
+ android:background="@android:color/transparent">
-
+ tools:background="@drawable/rect_transfer_black_03_fill_60">
-
+
+
+
+
\ No newline at end of file
diff --git a/data/src/main/java/com/dpm/data/datasource/ViewfinderDataSource.kt b/data/src/main/java/com/dpm/data/datasource/ViewfinderDataSource.kt
index 1d804959..8b3c5039 100644
--- a/data/src/main/java/com/dpm/data/datasource/ViewfinderDataSource.kt
+++ b/data/src/main/java/com/dpm/data/datasource/ViewfinderDataSource.kt
@@ -15,4 +15,6 @@ interface ViewfinderDataSource {
queryParam: RequestBlockReviewQueryDto
): ResponseBlockReviewDto
suspend fun getBlockRow(stadiumId: Int, blockCode: String): ResponseBlockRowDto
+ suspend fun updateScrap(reviewId: Int)
+ suspend fun updateLike(reviewId: Int)
}
\ No newline at end of file
diff --git a/data/src/main/java/com/dpm/data/datasource/remote/ViewfinderDataSourceImpl.kt b/data/src/main/java/com/dpm/data/datasource/remote/ViewfinderDataSourceImpl.kt
index d4d35dc4..dbf295e4 100644
--- a/data/src/main/java/com/dpm/data/datasource/remote/ViewfinderDataSourceImpl.kt
+++ b/data/src/main/java/com/dpm/data/datasource/remote/ViewfinderDataSourceImpl.kt
@@ -41,4 +41,12 @@ class ViewfinderDataSourceImpl @Inject constructor(
override suspend fun getBlockRow(stadiumId: Int, blockCode: String): ResponseBlockRowDto {
return viewfinderService.getBlockRow(stadiumId, blockCode)
}
+
+ override suspend fun updateScrap(reviewId: Int) {
+ viewfinderService.postScrap(reviewId)
+ }
+
+ override suspend fun updateLike(reviewId: Int) {
+ viewfinderService.postLike(reviewId)
+ }
}
\ No newline at end of file
diff --git a/data/src/main/java/com/dpm/data/model/response/viewfinder/ResponseBlockReviewDto.kt b/data/src/main/java/com/dpm/data/model/response/viewfinder/ResponseBlockReviewDto.kt
index 537e237f..42993643 100644
--- a/data/src/main/java/com/dpm/data/model/response/viewfinder/ResponseBlockReviewDto.kt
+++ b/data/src/main/java/com/dpm/data/model/response/viewfinder/ResponseBlockReviewDto.kt
@@ -13,7 +13,7 @@ data class ResponseBlockReviewDto(
@SerialName("reviews")
val reviews: List,
@SerialName("topReviewImages")
- val topReviewImages: List,
+ val topReviewImages: List,
@SerialName("totalElements")
val totalElements: Long,
@SerialName("nextCursor")
@@ -56,7 +56,7 @@ data class ResponseBlockReviewDto(
@SerialName("block")
val block: ResponseReviewBlockDto,
@SerialName("row")
- val row: ResponseReviewRowDto,
+ val row: ResponseReviewRowDto?,
@SerialName("seat")
val seat: ResponseReviewSeatDto?,
@SerialName("dateTime")
@@ -67,8 +67,13 @@ data class ResponseBlockReviewDto(
val images: List,
@SerialName("keywords")
val keywords: List,
-
- ) {
+ @SerialName("likesCount")
+ val likesCount: Long,
+ @SerialName("scrapsCount")
+ val scrapsCount: Long,
+ @SerialName("reviewType")
+ val reviewType: String?,
+ ) {
@Serializable
data class ResponseReviewImageDto(
@SerialName("id")
@@ -126,9 +131,9 @@ data class ResponseBlockReviewDto(
@Serializable
data class ResponseReviewRowDto(
@SerialName("id")
- val id: Int,
+ val id: Int?,
@SerialName("number")
- val number: Int,
+ val number: Int?,
)
@Serializable
@@ -140,20 +145,6 @@ data class ResponseBlockReviewDto(
)
}
- @Serializable
- data class ResponseTopReviewImagesDto(
- @SerialName("url")
- val url: String,
- @SerialName("reviewId")
- val reviewId: Int,
- @SerialName("blockCode")
- val blockCode: String,
- @SerialName("rowNumber")
- val rowNumber: Int,
- @SerialName("seatNumber")
- val seatNumber: Int?,
- )
-
@Serializable
data class ResponseReviewFilterDto(
@SerialName("rowNumber")
@@ -171,7 +162,7 @@ fun ResponseBlockReviewDto.toBlockReviewResponse() = ResponseBlockReview(
location = location?.toLocationResponse() ?: ResponseBlockReview.ResponseLocation(),
keywords = keywords.map { it.toKeywordResponse() },
reviews = reviews.map { it.toReviewResponse() },
- topReviewImages = topReviewImages.map { it.toTopReviewImagesResponse() },
+ topReviewImages = topReviewImages.map { it.toReviewResponse() },
totalElements = totalElements,
nextCursor = nextCursor ?: "",
hasNext = hasNext,
@@ -185,15 +176,6 @@ fun ResponseBlockReviewDto.ResponseKeywordDto.toKeywordResponse() =
isPositive = isPositive
)
-fun ResponseBlockReviewDto.ResponseTopReviewImagesDto.toTopReviewImagesResponse() =
- ResponseBlockReview.ResponseTopReviewImages(
- url = url,
- reviewId = reviewId,
- blockCode = blockCode,
- rowNumber = rowNumber,
- seatNumber = seatNumber ?: 0
- )
-
fun ResponseBlockReviewDto.ResponseReviewFilterDto.toReviewFilterResponse() =
ResponseBlockReview.ResponseReviewFilter(
rowNumber = rowNumber ?: 0,
@@ -209,12 +191,18 @@ fun ResponseBlockReviewDto.ResponseReviewDto.toReviewResponse() =
stadium = stadium.toReviewStadiumResponse(),
section = section.toReviewSectionResponse(),
block = block.toReviewBlockResponse(),
- row = row.toReviewRowResponse(),
- seat = seat?.toReviewSeatResponse() ?: ResponseBlockReview.ResponseReview.ResponseReviewSeat(),
+ row = row?.toReviewRowResponse() ?: ResponseBlockReview.ResponseReview.ResponseReviewRow(),
+ seat = seat?.toReviewSeatResponse()
+ ?: ResponseBlockReview.ResponseReview.ResponseReviewSeat(),
dateTime = dateTime,
content = content ?: "",
images = images.map { it.toReviewImageResponse() },
- keywords = keywords.map { it.toReviewKeywordResponse() }
+ keywords = keywords.map { it.toReviewKeywordResponse() },
+ isLike = false,
+ isScrap = false,
+ likesCount = likesCount,
+ scrapsCount = scrapsCount,
+ reviewType = reviewType ?: ""
)
fun ResponseBlockReviewDto.ResponseLocationDto.toLocationResponse() =
@@ -263,8 +251,8 @@ fun ResponseBlockReviewDto.ResponseReviewDto.ResponseReviewBlockDto.toReviewBloc
fun ResponseBlockReviewDto.ResponseReviewDto.ResponseReviewRowDto.toReviewRowResponse() =
ResponseBlockReview.ResponseReview.ResponseReviewRow(
- id = id,
- number = number
+ id = id ?: 0,
+ number = number ?: 0
)
fun ResponseBlockReviewDto.ResponseReviewDto.ResponseReviewSeatDto.toReviewSeatResponse() =
diff --git a/data/src/main/java/com/dpm/data/preference/SharedPreference.kt b/data/src/main/java/com/dpm/data/preference/SharedPreference.kt
index b9567759..61c37b9d 100644
--- a/data/src/main/java/com/dpm/data/preference/SharedPreference.kt
+++ b/data/src/main/java/com/dpm/data/preference/SharedPreference.kt
@@ -47,7 +47,7 @@ class DefaultSharedPreference @Inject constructor(
}
override var teamName: String
- get() = preferences.getString("teamName","").orEmpty()
+ get() = preferences.getString("teamName", "").orEmpty()
set(value) {
preferences.edit(commit = true) {
putString("teamName", value)
@@ -64,12 +64,12 @@ class DefaultSharedPreference @Inject constructor(
get() = preferences.getString("levelTitle", "").orEmpty()
set(value) {
preferences.edit(commit = true) {
- putString("levelTitle",value)
+ putString("levelTitle", value)
}
}
override var profileImage: String
- get() = preferences.getString("profileImage","").orEmpty()
+ get() = preferences.getString("profileImage", "").orEmpty()
set(value) {
preferences.edit(commit = true) {
putString("profileImage", value)
@@ -83,4 +83,20 @@ class DefaultSharedPreference @Inject constructor(
putBoolean("isFirstTime", value)
}
}
+
+ override var isFirstShare: Boolean
+ get() = preferences.getBoolean("isFirstShare", true)
+ set(value) {
+ preferences.edit(commit = true) {
+ putBoolean("isFirstShare", value)
+ }
+ }
+
+ override var isFirstLike: Boolean
+ get() = preferences.getBoolean("isFirstLike", true)
+ set(value) {
+ preferences.edit(commit = true) {
+ putBoolean("isFirstLike", value)
+ }
+ }
}
diff --git a/data/src/main/java/com/dpm/data/remote/ViewfinderService.kt b/data/src/main/java/com/dpm/data/remote/ViewfinderService.kt
index 1e0107e0..f28b2d38 100644
--- a/data/src/main/java/com/dpm/data/remote/ViewfinderService.kt
+++ b/data/src/main/java/com/dpm/data/remote/ViewfinderService.kt
@@ -5,6 +5,7 @@ import com.dpm.data.model.response.viewfinder.ResponseBlockRowDto
import com.dpm.data.model.response.viewfinder.ResponseStadiumDto
import com.dpm.data.model.response.viewfinder.ResponseStadiumsDto
import retrofit2.http.GET
+import retrofit2.http.POST
import retrofit2.http.Path
import retrofit2.http.Query
@@ -35,4 +36,15 @@ interface ViewfinderService {
@Path("stadiumId") stadiumId: Int,
@Path("blockCode") blockCode: String
): ResponseBlockRowDto
+
+ @POST("/api/v1/reviews/{reviewId}/scrap")
+ suspend fun postScrap(
+ @Path("reviewId") reviewId: Int
+ )
+
+
+ @POST("/api/v1/reviews/{reviewId}/like")
+ suspend fun postLike(
+ @Path("reviewId") reviewId: Int
+ )
}
\ No newline at end of file
diff --git a/data/src/main/java/com/dpm/data/repository/ViewfinderRepositoryImpl.kt b/data/src/main/java/com/dpm/data/repository/ViewfinderRepositoryImpl.kt
index 16e0edb0..3475ea2f 100644
--- a/data/src/main/java/com/dpm/data/repository/ViewfinderRepositoryImpl.kt
+++ b/data/src/main/java/com/dpm/data/repository/ViewfinderRepositoryImpl.kt
@@ -48,4 +48,16 @@ class ViewfinderRepositoryImpl @Inject constructor(
viewfinderDataSource.getBlockRow(stadiumId, blockCode).toBlockRowResponse()
}
}
+
+ override suspend fun updateScrap(reviewId: Int): Result {
+ return runCatching {
+ viewfinderDataSource.updateScrap(reviewId)
+ }
+ }
+
+ override suspend fun updateLike(reviewId: Int): Result {
+ return runCatching {
+ viewfinderDataSource.updateLike(reviewId)
+ }
+ }
}
\ No newline at end of file
diff --git a/domain/src/main/java/com/dpm/domain/entity/response/viewfinder/ResponseBlockReview.kt b/domain/src/main/java/com/dpm/domain/entity/response/viewfinder/ResponseBlockReview.kt
index 40504f0c..c5e4e5e5 100644
--- a/domain/src/main/java/com/dpm/domain/entity/response/viewfinder/ResponseBlockReview.kt
+++ b/domain/src/main/java/com/dpm/domain/entity/response/viewfinder/ResponseBlockReview.kt
@@ -31,7 +31,7 @@ data class ResponseBlockReview(
val location: ResponseLocation = ResponseLocation(),
val keywords: List = emptyList(),
val reviews: List = emptyList(),
- val topReviewImages: List = emptyList(),
+ val topReviewImages: List = emptyList(),
val totalElements: Long = 0,
val nextCursor: String = "",
val hasNext: Boolean = false,
@@ -61,8 +61,12 @@ data class ResponseBlockReview(
val content: String = "",
val images: List = emptyList(),
val keywords: List = emptyList(),
-
- ) {
+ val isLike: Boolean,
+ val isScrap: Boolean,
+ val likesCount: Long,
+ val scrapsCount: Long,
+ val reviewType: String,
+ ) {
data class ResponseReviewImage(
val id: Int,
val url: String = "",
@@ -97,7 +101,7 @@ data class ResponseBlockReview(
)
data class ResponseReviewRow(
- val id: Int,
+ val id: Int = 0,
val number: Int = 0,
)
@@ -116,14 +120,6 @@ data class ResponseBlockReview(
}
}
- data class ResponseTopReviewImages(
- val url: String = "",
- val reviewId: Int,
- val blockCode: String = "",
- val rowNumber: Int = 0,
- val seatNumber: Int = 0,
- )
-
data class ResponseReviewFilter(
val rowNumber: Int = 0,
val seatNumber: Int = 0,
diff --git a/domain/src/main/java/com/dpm/domain/preference/NetworkPreference.kt b/domain/src/main/java/com/dpm/domain/preference/NetworkPreference.kt
index 20234379..dbedf655 100644
--- a/domain/src/main/java/com/dpm/domain/preference/NetworkPreference.kt
+++ b/domain/src/main/java/com/dpm/domain/preference/NetworkPreference.kt
@@ -10,5 +10,7 @@ interface SharedPreference {
var nickname: String
var profileImage : String
var isFirstTime: Boolean
+ var isFirstShare: Boolean
+ var isFirstLike: Boolean
fun clear()
}
diff --git a/domain/src/main/java/com/dpm/domain/repository/ViewfinderRepository.kt b/domain/src/main/java/com/dpm/domain/repository/ViewfinderRepository.kt
index 589d2003..e8fff763 100644
--- a/domain/src/main/java/com/dpm/domain/repository/ViewfinderRepository.kt
+++ b/domain/src/main/java/com/dpm/domain/repository/ViewfinderRepository.kt
@@ -11,4 +11,6 @@ interface ViewfinderRepository {
suspend fun getStadium(id : Int) : Result
suspend fun getBlockReviews(stadiumId:Int, blockCode: String, query: RequestBlockReviewQuery): Result
suspend fun getBlockRow(stadiumId: Int, blockCode: String): Result
+ suspend fun updateScrap(reviewId: Int): Result
+ suspend fun updateLike(reviewId: Int): Result
}
\ No newline at end of file
diff --git a/presentation/src/main/java/com/dpm/presentation/util/KakaoUtils.kt b/presentation/src/main/java/com/dpm/presentation/util/KakaoUtils.kt
index d76090d4..d31873de 100644
--- a/presentation/src/main/java/com/dpm/presentation/util/KakaoUtils.kt
+++ b/presentation/src/main/java/com/dpm/presentation/util/KakaoUtils.kt
@@ -32,6 +32,28 @@ val mockDefaultFeed = FeedTemplate(
)
)
+fun seatFeed(
+ title: String,
+ description: String,
+ imageUrl: String,
+ queryParams: Map
+) = FeedTemplate(
+ content = Content(
+ title = title,
+ description = description,
+ imageUrl = imageUrl,
+ link = Link()
+ ),
+ buttons = listOf(
+ Button(
+ title = "SPOT! 앱에서 열기",
+ link = Link(
+ androidExecutionParams = queryParams
+ )
+ )
+ )
+)
+
class KakaoUtils() {
fun share(
context: Context,
diff --git a/presentation/src/main/java/com/dpm/presentation/util/StadiumUxWritingUtils.kt b/presentation/src/main/java/com/dpm/presentation/util/StadiumUxWritingUtils.kt
index 4b89d50e..15a796be 100644
--- a/presentation/src/main/java/com/dpm/presentation/util/StadiumUxWritingUtils.kt
+++ b/presentation/src/main/java/com/dpm/presentation/util/StadiumUxWritingUtils.kt
@@ -3,6 +3,7 @@ package com.dpm.presentation.util
import com.dpm.domain.entity.response.viewfinder.BASE
import com.dpm.domain.entity.response.viewfinder.ResponseBlockReview
import com.dpm.domain.entity.response.viewfinder.base
+import com.dpm.presentation.viewfinder.uistate.StadiumDetailUiState
/**
* @UX_Writing : 야구장 구장 화면에서 블록 클릭 후, 블록 별 리뷰 화면의 [n루]•[구역]•[블록]
@@ -96,16 +97,23 @@ fun ResponseBlockReview.ResponseReview.toBlockContent(): String {
return numberString
}
-fun ResponseBlockReview.ResponseTopReviewImages.toBlockContent(): String {
- var numberString = ""
- if (blockCode.isNotEmpty()) {
- numberString += if (blockCode in listOf("exciting1", "exciting3", "premium")) {
- ""
- } else {
- "${blockCode}블록 "
- }
+
+/**
+ * @UX_Writing : 카카오 공유하기 제목 -> 서울 잠실 야구장 1루 네이비석 101블록 3열 12번 좌석시야
+ * @author : 조관희
+ */
+fun StadiumDetailUiState.ReviewsData.kakaoShareSeatFeedTitle(
+ index: Int
+): String {
+ val base = when (stadiumContent.stadiumName.base(stadiumContent.blockCode)) {
+ BASE.Base1 -> "1루"
+ BASE.Base3 -> "3루"
+ else -> ""
}
- if (rowNumber > 0) numberString += "${rowNumber}열 "
- if (seatNumber > 0) numberString += "${seatNumber}번"
- return numberString
+ val section = base + stadiumContent.sectionName
+ val block = if (stadiumContent.blockCode in listOf("exciting1", "exciting3", "premium")) "" else "${stadiumContent.blockCode}블록"
+ val column = if (reviews[index].row.number == 0) "" else "${reviews[index].row.number}열"
+ val seatNumber = if (reviews[index].seat.seatNumber == 0) "" else "${reviews[index].seat.seatNumber}번"
+
+ return "${stadiumContent.stadiumName} $section $block $column $seatNumber 좌석시야"
}
\ No newline at end of file
diff --git a/presentation/src/main/java/com/dpm/presentation/viewfinder/StadiumDetailActivity.kt b/presentation/src/main/java/com/dpm/presentation/viewfinder/StadiumDetailActivity.kt
index f04f1b04..8a8c3a59 100644
--- a/presentation/src/main/java/com/dpm/presentation/viewfinder/StadiumDetailActivity.kt
+++ b/presentation/src/main/java/com/dpm/presentation/viewfinder/StadiumDetailActivity.kt
@@ -12,7 +12,10 @@ import com.dpm.core.base.BaseActivity
import com.dpm.domain.entity.response.viewfinder.ResponseBlockReview
import com.depromeet.presentation.R
import com.depromeet.presentation.databinding.ActivityStadiumDetailBinding
+import com.dpm.domain.preference.SharedPreference
import com.dpm.presentation.home.HomeActivity
+import com.dpm.presentation.util.KakaoUtils
+import com.dpm.presentation.util.seatFeed
import com.dpm.presentation.util.toEmptyBlock
import com.dpm.presentation.viewfinder.compose.StadiumDetailScreen
import com.dpm.presentation.viewfinder.dialog.ReportDialog
@@ -20,6 +23,7 @@ import com.dpm.presentation.viewfinder.dialog.StadiumFilterMonthsDialog
import com.dpm.presentation.viewfinder.dialog.StadiumSelectSeatDialog
import com.dpm.presentation.viewfinder.viewmodel.StadiumDetailViewModel
import dagger.hilt.android.AndroidEntryPoint
+import javax.inject.Inject
@AndroidEntryPoint
class StadiumDetailActivity : BaseActivity({
@@ -29,10 +33,15 @@ class StadiumDetailActivity : BaseActivity({
const val REVIEW_ID = "review_id"
const val REVIEW_INDEX = "review_index"
const val REVIEW_TITLE_WITH_STADIUM = "review_title_with_stadium"
+ const val REVIEW_TYPE = "review_type"
+
const val STADIUM_HEADER = "stadium_header"
const val STADIUM_REVIEW_CONTENT = "stadium_review_content"
}
+ @Inject
+ lateinit var sharedPreference: SharedPreference
+
private val viewModel: StadiumDetailViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
@@ -56,9 +65,10 @@ class StadiumDetailActivity : BaseActivity({
MaterialTheme {
StadiumDetailScreen(
emptyBlockName = toEmptyBlock(viewModel.stadiumId, viewModel.blockCode),
+ isFirstShare = sharedPreference.isFirstShare,
viewModel = viewModel,
- onClickReviewPicture = { reviewContent, index, title ->
- startToStadiumDetailPictureFragment(reviewContent, index, title)
+ onClickReviewPicture = { id, index, title ->
+ startToStadiumDetailPictureFragment(id, index, title, DetailReviewEntryPoint.MAIN_REVIEW)
},
onClickSelectSeat = {
StadiumSelectSeatDialog.apply {
@@ -86,6 +96,12 @@ class StadiumDetailActivity : BaseActivity({
},
onRefresh = {
viewModel.getBlockReviews()
+ },
+ onClickTopImage = { id, index, title ->
+ startToStadiumDetailPictureFragment(id, index, title, DetailReviewEntryPoint.TOP_REVIEW)
+ },
+ onClickShare = {
+ sharedPreference.isFirstShare = false
}
)
}
@@ -121,15 +137,17 @@ class StadiumDetailActivity : BaseActivity({
}
private fun startToStadiumDetailPictureFragment(
- reviewContent: ResponseBlockReview.ResponseReview,
+ id: Long,
index: Int,
- title: String
+ title: String,
+ type: DetailReviewEntryPoint
) {
val fragment = StadiumDetailPictureFragment.newInstance().apply {
arguments = bundleOf(
- REVIEW_ID to reviewContent.id,
+ REVIEW_ID to id,
REVIEW_INDEX to index,
- REVIEW_TITLE_WITH_STADIUM to title
+ REVIEW_TITLE_WITH_STADIUM to title,
+ REVIEW_TYPE to type.name,
)
}
diff --git a/presentation/src/main/java/com/dpm/presentation/viewfinder/StadiumDetailPictureFragment.kt b/presentation/src/main/java/com/dpm/presentation/viewfinder/StadiumDetailPictureFragment.kt
index 957d0025..9cf51f90 100644
--- a/presentation/src/main/java/com/dpm/presentation/viewfinder/StadiumDetailPictureFragment.kt
+++ b/presentation/src/main/java/com/dpm/presentation/viewfinder/StadiumDetailPictureFragment.kt
@@ -13,14 +13,21 @@ import androidx.core.view.updatePadding
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.activityViewModels
import androidx.fragment.app.commit
-import com.dpm.core.base.BindingFragment
import com.depromeet.presentation.R
import com.depromeet.presentation.databinding.FragmentStadiumDetailPictureBinding
+import com.dpm.core.base.BindingFragment
+import com.dpm.designsystem.SpotSnackBar
+import com.dpm.domain.preference.SharedPreference
import com.dpm.presentation.util.Utils
import com.dpm.presentation.viewfinder.compose.detailpicture.StadiumDetailPictureScreen
import com.dpm.presentation.viewfinder.dialog.ReportDialog
import com.dpm.presentation.viewfinder.viewmodel.StadiumDetailViewModel
import dagger.hilt.android.AndroidEntryPoint
+import javax.inject.Inject
+
+enum class DetailReviewEntryPoint {
+ TOP_REVIEW, MAIN_REVIEW
+}
@AndroidEntryPoint
class StadiumDetailPictureFragment : BindingFragment(
@@ -38,10 +45,14 @@ class StadiumDetailPictureFragment : BindingFragment
+ initSnackBar()
+ getReviewExtra { reviewId, reviewIndex, title, type ->
binding.spotAppbar.setText(title)
binding.cvReviewContent.setContent {
MaterialTheme {
StadiumDetailPictureScreen(
reviewId = reviewId,
reviewIndex = reviewIndex,
+ type = type,
+ isFirstLike = sharedPreference.isFirstLike,
stadiumDetailViewModel = stadiumDetailViewModel,
+ onClickLike = {
+ sharedPreference.isFirstLike = false
+ },
+ onClickScrap = { id ->
+ if (stadiumDetailViewModel.checkScrap(id)) {
+ snackBar.show()
+ }
+ },
+ onClickShare = {
+ sharedPreference.isFirstShare = false
+ }
)
}
}
@@ -66,6 +91,9 @@ class StadiumDetailPictureFragment : BindingFragment Unit) {
+ private fun initSnackBar() {
+ snackBar = SpotSnackBar.make(
+ view = binding.root.rootView,
+ background = com.depromeet.designsystem.R.drawable.rect_body_subtitle_fill_60,
+ message = getString(R.string.viewfinder_snackbar_scrap),
+ endMessage = getString(R.string.viewfinder_underscore_snackbar_scrap),
+ onClick = {
+ // TODO : 스크랩 화면으로 이동
+ })
+ }
+
+ private fun getReviewExtra(callback: (id: Long, index: Int, title: String, type: String) -> Unit) {
val reviewId = arguments?.getLong(StadiumDetailActivity.REVIEW_ID) ?: return
val reviewIndex = arguments?.getInt(StadiumDetailActivity.REVIEW_INDEX) ?: return
val title = arguments?.getString(StadiumDetailActivity.REVIEW_TITLE_WITH_STADIUM) ?: return
- callback(reviewId, reviewIndex, title)
+ val type = arguments?.getString(StadiumDetailActivity.REVIEW_TYPE) ?: return
+ callback(reviewId, reviewIndex, title, type)
}
private fun removeFragment() {
@@ -137,6 +177,7 @@ class StadiumDetailPictureFragment : BindingFragment Unit
) {
Row(
modifier = modifier
@@ -34,7 +37,7 @@ fun LikeButton(
)
.border(
width = 1.dp,
- color = SpotTheme.colors.strokeTertiary,
+ color = if (isLike) SpotTheme.colors.actionEnabled else SpotTheme.colors.strokeTertiary,
shape = RoundedCornerShape(72.dp)
)
.padding(
@@ -42,13 +45,15 @@ fun LikeButton(
vertical = 8.dp
)
.noRippleClickable {
-
+ onClick()
},
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) {
Icon(
- painter = painterResource(id = R.drawable.ic_like_inactive),
+ painter = painterResource(
+ id = if (isLike) R.drawable.ic_like_active else R.drawable.ic_like_inactive
+ ),
contentDescription = null,
modifier = Modifier.size(24.dp),
tint = Color.Unspecified
@@ -61,9 +66,9 @@ fun LikeButton(
)
Spacer(modifier = Modifier.width(4.dp))
Text(
- text = 12.toString(),
+ text = likeCount.toString(),
style = SpotTheme.typography.label09,
- color = SpotTheme.colors.foregroundDisabled
+ color = if (isLike) SpotTheme.colors.actionEnabled else SpotTheme.colors.strokeTertiary
)
}
}
@@ -71,5 +76,9 @@ fun LikeButton(
@Preview
@Composable
private fun LikeButtonPreview() {
- LikeButton()
+ LikeButton(
+ isLike = true,
+ likeCount = 1,
+ onClick = {}
+ )
}
\ No newline at end of file
diff --git a/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/ReviewContentBottom.kt b/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/ReviewContentBottom.kt
index 32df4b19..a8803a39 100644
--- a/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/ReviewContentBottom.kt
+++ b/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/ReviewContentBottom.kt
@@ -8,6 +8,10 @@ import androidx.compose.foundation.layout.width
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
@@ -18,28 +22,38 @@ import com.depromeet.designsystem.R
@Composable
fun ReviewContentBottom(
- modifier: Modifier = Modifier
+ isLike: Boolean,
+ isScrap: Boolean,
+ likeCount: Long,
+ modifier: Modifier = Modifier,
+ onClickLike: () -> Unit,
+ onClickScrap: () -> Unit,
+ onClickShare: () -> Unit
) {
Row(
modifier = modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
- LikeButton()
+ LikeButton(
+ isLike = isLike,
+ likeCount = likeCount,
+ onClick = onClickLike
+ )
Row {
IconButton(
- onClick = { }
+ onClick = onClickScrap
) {
Icon(
painter = painterResource(
- id = R.drawable.ic_scrap_inactive_button
+ id = if (isScrap) R.drawable.ic_scrap_active_button else R.drawable.ic_scrap_inactive_button
), contentDescription = null,
tint = Color.Unspecified
)
}
Spacer(modifier = Modifier.width(6.dp))
IconButton(
- onClick = { }
+ onClick = onClickShare
) {
Icon(
painter = painterResource(
@@ -55,5 +69,51 @@ fun ReviewContentBottom(
@Preview
@Composable
private fun ReviewContentBottomPreview() {
- ReviewContentBottom()
+ ReviewContentBottom(
+ isLike = false,
+ isScrap = false,
+ likeCount = 0,
+ onClickLike = {},
+ onClickScrap = {},
+ onClickShare = {}
+ )
+}
+
+@Preview
+@Composable
+private fun ReviewContentBottomLikePreview() {
+ ReviewContentBottom(
+ isLike = true,
+ isScrap = false,
+ likeCount = 0,
+ onClickLike = {},
+ onClickScrap = {},
+ onClickShare = {}
+ )
+}
+
+@Preview
+@Composable
+private fun ReviewContentBottomScrapPreview() {
+ ReviewContentBottom(
+ isLike = false,
+ isScrap = true,
+ likeCount = 0,
+ onClickLike = {},
+ onClickScrap = {},
+ onClickShare = {}
+ )
+}
+
+@Preview
+@Composable
+private fun ReviewContentBottomLikeAndScrapPreview() {
+ ReviewContentBottom(
+ isLike = true,
+ isScrap = true,
+ likeCount = 0,
+ onClickLike = {},
+ onClickScrap = {},
+ onClickShare = {}
+ )
}
\ No newline at end of file
diff --git a/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/StadiumDetailScreen.kt b/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/StadiumDetailScreen.kt
index ad8f3af5..0c8bbad3 100644
--- a/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/StadiumDetailScreen.kt
+++ b/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/StadiumDetailScreen.kt
@@ -1,6 +1,7 @@
package com.dpm.presentation.viewfinder.compose
import android.content.Context
+import android.util.Log
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
@@ -19,37 +20,49 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
+import androidx.core.content.ContextCompat.startActivity
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
+import com.dpm.domain.entity.response.viewfinder.BASE
import com.dpm.domain.entity.response.viewfinder.ResponseBlockReview
+import com.dpm.domain.entity.response.viewfinder.base
import com.dpm.presentation.mapper.toKeyword
+import com.dpm.presentation.util.KakaoUtils
+import com.dpm.presentation.util.kakaoShareSeatFeedTitle
+import com.dpm.presentation.util.seatFeed
import com.dpm.presentation.util.stadiumBlock
import com.dpm.presentation.util.toTitle
import com.dpm.presentation.viewfinder.StadiumDetailActivity
import com.dpm.presentation.viewfinder.uistate.StadiumDetailUiState
import com.dpm.presentation.viewfinder.viewmodel.StadiumDetailViewModel
+import timber.log.Timber
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun StadiumDetailScreen(
emptyBlockName: String,
+ isFirstShare: Boolean,
context: Context = LocalContext.current,
modifier: Modifier = Modifier,
viewModel: StadiumDetailViewModel = viewModel(),
- onClickReviewPicture: (reviewContent: ResponseBlockReview.ResponseReview, index: Int, title: String) -> Unit,
+ onClickReviewPicture: (id: Long, index: Int, title: String) -> Unit,
+ onClickTopImage: (id: Long, index: Int, title: String) -> Unit,
onClickSelectSeat: () -> Unit,
onClickFilterMonthly: () -> Unit,
onClickReport: () -> Unit,
onClickGoBack: () -> Unit,
+ onClickShare:() -> Unit = {},
onRefresh: () -> Unit
) {
var isMore by remember { mutableStateOf(false) }
+ var isFirstShare by remember { mutableStateOf(isFirstShare) }
val verticalScrollState = rememberLazyListState()
val scrollState by viewModel.scrollState.collectAsStateWithLifecycle()
val reviewFilter by viewModel.reviewFilter.collectAsStateWithLifecycle()
val detailUiState by viewModel.detailUiState.collectAsStateWithLifecycle()
val currentIndex by viewModel.currentIndex.collectAsStateWithLifecycle()
+
LaunchedEffect(key1 = scrollState) {
verticalScrollState.scrollToItem(0)
viewModel.updateScrollState(false)
@@ -108,7 +121,10 @@ fun StadiumDetailScreen(
keywords = uiState.keywords.map { it.toKeyword() },
onChangeIsMore = { isMore = it },
onClickSelectSeat = onClickSelectSeat,
- onCancelSeat = viewModel::clearSeat
+ onCancelSeat = viewModel::clearSeat,
+ onClickTopImage = { id, index ->
+ onClickTopImage(id, index, uiState.stadiumContent.toTitle())
+ }
)
Spacer(modifier = Modifier.height(30.dp))
}
@@ -138,15 +154,42 @@ fun StadiumDetailScreen(
) { index ->
StadiumReviewContent(
context = context,
+ isFirstShare = isFirstShare,
+ firstReview = uiState.reviews[index] == uiState.reviews.firstOrNull(),
reviewContent = uiState.reviews[index],
onClick = { reviewContent, index ->
onClickReviewPicture(
- reviewContent,
+ reviewContent.id,
index,
uiState.stadiumContent.toTitle()
)
},
- onClickReport = onClickReport
+ onClickReport = onClickReport,
+ onClickLike = viewModel::updateLike,
+ onClickScrap = viewModel::updateScrap,
+ onClickShare = {
+ onClickShare()
+ isFirstShare = false
+ KakaoUtils().share(
+ context,
+ seatFeed(
+ title = uiState.kakaoShareSeatFeedTitle(index),
+ description = "출처 : ${uiState.reviews[index].member.nickname}",
+ imageUrl = uiState.reviews[index].images.firstOrNull()?.url
+ ?: "",
+ queryParams = mapOf(
+ "stadiumId" to viewModel.stadiumId.toString(),
+ "blockCode" to viewModel.blockCode
+ )
+ ),
+ onSuccess = { sharingIntent ->
+ context.startActivity(sharingIntent)
+ },
+ onFailure = {
+ Timber.e("error : ${it.message}")
+ }
+ )
+ }
)
Spacer(modifier = Modifier.height(40.dp))
}
@@ -162,13 +205,15 @@ fun StadiumDetailScreen(
private fun StadiumDetailScreenPreview() {
Box(modifier = Modifier.background(Color.White)) {
StadiumDetailScreen(
+ isFirstShare = true,
emptyBlockName = "207",
onClickReviewPicture = { _, _, _ -> },
onClickSelectSeat = {},
onClickFilterMonthly = {},
onClickReport = {},
onClickGoBack = {},
- onRefresh = {}
+ onRefresh = {},
+ onClickTopImage = { _, _, _ -> },
)
}
}
\ No newline at end of file
diff --git a/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/StadiumHeaderContent.kt b/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/StadiumHeaderContent.kt
index fdc7aec0..02d8d144 100644
--- a/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/StadiumHeaderContent.kt
+++ b/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/StadiumHeaderContent.kt
@@ -30,10 +30,11 @@ fun StadiumHeaderContent(
stadiumTitle: String,
keywords: List,
reviewFilter: RequestBlockReviewQuery,
- topReviewImages: List,
+ topReviewImages: List,
modifier: Modifier = Modifier,
onChangeIsMore: (Boolean) -> Unit,
onClickSelectSeat: () -> Unit,
+ onClickTopImage:(id: Long, index: Int) -> Unit,
onCancelSeat: () -> Unit
) {
Column(
@@ -43,7 +44,8 @@ fun StadiumHeaderContent(
StadiumPictureViewPager(
context = context,
topReviewImages = topReviewImages,
- modifier = Modifier.fillMaxWidth()
+ modifier = Modifier.fillMaxWidth(),
+ onClick = onClickTopImage
)
Spacer(modifier = Modifier.height(20.dp))
StadiumAreaText(
@@ -84,24 +86,74 @@ fun StadiumHeaderContent(
@Preview(showBackground = true)
@Composable
private fun StadiumHeaderContentPreview() {
+ val review = ResponseBlockReview.ResponseReview(
+ id = 1,
+ dateTime = "2023-03-01T19:00:00",
+ content = "asdfsdfsafsfda",
+ images = listOf(
+ ResponseBlockReview.ResponseReview.ResponseReviewImage(
+ id = 1,
+ url = "https://picsum.photos/200/300"
+ ),
+ ResponseBlockReview.ResponseReview.ResponseReviewImage(
+ id = 1,
+ url = "https://picsum.photos/200/300"
+ ),
+ ),
+ member = ResponseBlockReview.ResponseReview.ResponseReviewMember(
+ "https://picsum.photos/200/300",
+ nickname = "엘지의 왕자",
+ level = 0
+ ),
+ stadium = ResponseBlockReview.ResponseReview.ResponseReviewStadium(
+ id = 1,
+ name = "서울 잠실 야구장"
+ ),
+ section = ResponseBlockReview.ResponseReview.ResponseReviewSection(
+ id = 1,
+ name = "오렌지석",
+ alias = "응원석"
+ ),
+ block = ResponseBlockReview.ResponseReview.ResponseReviewBlock(
+ id = 1,
+ code = "207"
+ ),
+ row = ResponseBlockReview.ResponseReview.ResponseReviewRow(
+ id = 1,
+ number = 1
+ ),
+ seat = ResponseBlockReview.ResponseReview.ResponseReviewSeat(
+ id = 1,
+ seatNumber = 12
+ ),
+ keywords = listOf(
+ ResponseBlockReview.ResponseReview.ResponseReviewKeyword(
+ id = 1,
+ content = "",
+ isPositive = false
+ ),
+ ResponseBlockReview.ResponseReview.ResponseReviewKeyword(
+ id = 1,
+ content = "",
+ isPositive = false
+ ),
+ ResponseBlockReview.ResponseReview.ResponseReviewKeyword(
+ id = 1,
+ content = "",
+ isPositive = false
+ )
+ ),
+ isLike = false,
+ isScrap = false,
+ likesCount = 1,
+ scrapsCount = 0,
+ reviewType = ""
+ )
StadiumHeaderContent(
context = LocalContext.current,
isMore = false,
topReviewImages = listOf(
- ResponseBlockReview.ResponseTopReviewImages(
- url = "",
- reviewId = 1,
- blockCode = "207",
- rowNumber = 1,
- seatNumber = 12
- ),
- ResponseBlockReview.ResponseTopReviewImages(
- url = "",
- reviewId = 1,
- blockCode = "207",
- rowNumber = 1,
- seatNumber = 12
- ),
+ review, review
),
reviewFilter = RequestBlockReviewQuery(
rowNumber = null,
@@ -121,6 +173,7 @@ private fun StadiumHeaderContentPreview() {
),
onChangeIsMore = {},
onClickSelectSeat = {},
+ onClickTopImage = {_,_ ->},
onCancelSeat = {}
)
}
@@ -128,25 +181,75 @@ private fun StadiumHeaderContentPreview() {
@Preview(showBackground = true)
@Composable
private fun StadiumHeaderContentIsMorePreview() {
+ val review = ResponseBlockReview.ResponseReview(
+ id = 1,
+ dateTime = "2023-03-01T19:00:00",
+ content = "asdfsdfsafsfda",
+ images = listOf(
+ ResponseBlockReview.ResponseReview.ResponseReviewImage(
+ id = 1,
+ url = "https://picsum.photos/200/300"
+ ),
+ ResponseBlockReview.ResponseReview.ResponseReviewImage(
+ id = 1,
+ url = "https://picsum.photos/200/300"
+ ),
+ ),
+ member = ResponseBlockReview.ResponseReview.ResponseReviewMember(
+ "https://picsum.photos/200/300",
+ nickname = "엘지의 왕자",
+ level = 0
+ ),
+ stadium = ResponseBlockReview.ResponseReview.ResponseReviewStadium(
+ id = 1,
+ name = "서울 잠실 야구장"
+ ),
+ section = ResponseBlockReview.ResponseReview.ResponseReviewSection(
+ id = 1,
+ name = "오렌지석",
+ alias = "응원석"
+ ),
+ block = ResponseBlockReview.ResponseReview.ResponseReviewBlock(
+ id = 1,
+ code = "207"
+ ),
+ row = ResponseBlockReview.ResponseReview.ResponseReviewRow(
+ id = 1,
+ number = 1
+ ),
+ seat = ResponseBlockReview.ResponseReview.ResponseReviewSeat(
+ id = 1,
+ seatNumber = 12
+ ),
+ keywords = listOf(
+ ResponseBlockReview.ResponseReview.ResponseReviewKeyword(
+ id = 1,
+ content = "",
+ isPositive = false
+ ),
+ ResponseBlockReview.ResponseReview.ResponseReviewKeyword(
+ id = 1,
+ content = "",
+ isPositive = false
+ ),
+ ResponseBlockReview.ResponseReview.ResponseReviewKeyword(
+ id = 1,
+ content = "",
+ isPositive = false
+ )
+ ),
+ isLike = false,
+ isScrap = false,
+ likesCount = 1,
+ scrapsCount = 0,
+ reviewType = ""
+ )
StadiumHeaderContent(
context = LocalContext.current,
isMore = true,
topReviewImages = listOf(
- ResponseBlockReview.ResponseTopReviewImages(
- url = "",
- reviewId = 1,
- blockCode = "207",
- rowNumber = 1,
- seatNumber = 12
-
- ),
- ResponseBlockReview.ResponseTopReviewImages(
- url = "",
- reviewId = 1,
- blockCode = "207",
- rowNumber = 1,
- seatNumber = 12
- ),
+ review,
+ review,
),
reviewFilter = RequestBlockReviewQuery(
rowNumber = 1,
@@ -167,6 +270,7 @@ private fun StadiumHeaderContentIsMorePreview() {
),
onChangeIsMore = {},
onClickSelectSeat = {},
+ onClickTopImage = {_,_ ->},
onCancelSeat = {}
)
}
diff --git a/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/StadiumPictureViewPager.kt b/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/StadiumPictureViewPager.kt
index 0198eb79..c655c07e 100644
--- a/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/StadiumPictureViewPager.kt
+++ b/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/StadiumPictureViewPager.kt
@@ -29,14 +29,16 @@ import coil.compose.AsyncImage
import coil.request.ImageRequest
import com.dpm.designsystem.compose.ui.SpotTheme
import com.dpm.domain.entity.response.viewfinder.ResponseBlockReview
+import com.dpm.presentation.extension.noRippleClickable
import com.dpm.presentation.util.toBlockContent
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun StadiumPictureViewPager(
context: Context,
- topReviewImages: List,
- modifier: Modifier = Modifier
+ topReviewImages: List,
+ modifier: Modifier = Modifier,
+ onClick: (id: Long, index: Int) -> Unit
) {
val pagerState = rememberPagerState(pageCount = { topReviewImages.size })
@@ -51,7 +53,7 @@ fun StadiumPictureViewPager(
) { page ->
AsyncImage(
model = ImageRequest.Builder(context)
- .data(topReviewImages.getOrNull(page)?.url)
+ .data(topReviewImages.getOrNull(page)?.images?.getOrNull(0)?.url)
.crossfade(true)
.build(),
contentDescription = null,
@@ -66,7 +68,10 @@ fun StadiumPictureViewPager(
),
modifier = Modifier
.fillMaxWidth()
- .clip(RectangleShape),
+ .clip(RectangleShape)
+ .noRippleClickable {
+ onClick(topReviewImages[page].id, page)
+ },
)
}
@@ -121,24 +126,75 @@ fun StadiumPictureViewPager(
@Preview
@Composable
private fun StadiumPictureViewPagerPreview() {
+ val review = ResponseBlockReview.ResponseReview(
+ id = 1,
+ dateTime = "2023-03-01T19:00:00",
+ content = "asdfsdfsafsfda",
+ images = listOf(
+ ResponseBlockReview.ResponseReview.ResponseReviewImage(
+ id = 1,
+ url = "https://picsum.photos/200/300"
+ ),
+ ResponseBlockReview.ResponseReview.ResponseReviewImage(
+ id = 1,
+ url = "https://picsum.photos/200/300"
+ ),
+ ),
+ member = ResponseBlockReview.ResponseReview.ResponseReviewMember(
+ "https://picsum.photos/200/300",
+ nickname = "엘지의 왕자",
+ level = 0
+ ),
+ stadium = ResponseBlockReview.ResponseReview.ResponseReviewStadium(
+ id = 1,
+ name = "서울 잠실 야구장"
+ ),
+ section = ResponseBlockReview.ResponseReview.ResponseReviewSection(
+ id = 1,
+ name = "오렌지석",
+ alias = "응원석"
+ ),
+ block = ResponseBlockReview.ResponseReview.ResponseReviewBlock(
+ id = 1,
+ code = "207"
+ ),
+ row = ResponseBlockReview.ResponseReview.ResponseReviewRow(
+ id = 1,
+ number = 1
+ ),
+ seat = ResponseBlockReview.ResponseReview.ResponseReviewSeat(
+ id = 1,
+ seatNumber = 12
+ ),
+ keywords = listOf(
+ ResponseBlockReview.ResponseReview.ResponseReviewKeyword(
+ id = 1,
+ content = "",
+ isPositive = false
+ ),
+ ResponseBlockReview.ResponseReview.ResponseReviewKeyword(
+ id = 1,
+ content = "",
+ isPositive = false
+ ),
+ ResponseBlockReview.ResponseReview.ResponseReviewKeyword(
+ id = 1,
+ content = "",
+ isPositive = false
+ )
+ ),
+ isLike = true,
+ isScrap = true,
+ likesCount = 1,
+ scrapsCount = 0,
+ reviewType = ""
+ )
StadiumPictureViewPager(
context = LocalContext.current,
topReviewImages = listOf(
- ResponseBlockReview.ResponseTopReviewImages(
- url = "",
- reviewId = 1,
- blockCode = "207",
- rowNumber = 1,
- seatNumber = 12
- ),
- ResponseBlockReview.ResponseTopReviewImages(
- url = "",
- reviewId = 1,
- blockCode = "207",
- rowNumber = 1,
- seatNumber = 12
- ),
+ review, review
),
- modifier = Modifier.fillMaxWidth()
+ modifier = Modifier.fillMaxWidth(),
+ onClick = {_,_ ->}
)
}
diff --git a/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/StadiumReviewContent.kt b/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/StadiumReviewContent.kt
index 45c6a706..0edacde0 100644
--- a/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/StadiumReviewContent.kt
+++ b/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/StadiumReviewContent.kt
@@ -39,9 +39,9 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage
import coil.request.ImageRequest
+import com.depromeet.presentation.R
import com.dpm.designsystem.compose.ui.SpotTheme
import com.dpm.domain.entity.response.viewfinder.ResponseBlockReview
-import com.depromeet.presentation.R
import com.dpm.presentation.extension.noRippleClickable
import com.dpm.presentation.mapper.toKeyword
import com.dpm.presentation.util.toBlockContent
@@ -53,10 +53,15 @@ private enum class ReviewContentShowMoreState {
@Composable
fun StadiumReviewContent(
context: Context,
+ isFirstShare: Boolean,
+ firstReview: Boolean,
reviewContent: ResponseBlockReview.ResponseReview,
modifier: Modifier = Modifier,
onClick: (reviewContent: ResponseBlockReview.ResponseReview, index: Int) -> Unit,
- onClickReport: () -> Unit
+ onClickReport: () -> Unit,
+ onClickLike: (id: Long) -> Unit = {},
+ onClickScrap: (id: Long) -> Unit = {},
+ onClickShare: () -> Unit
) {
val minimumLineLength = 3
var showMoreButtonState by remember {
@@ -240,8 +245,28 @@ fun StadiumReviewContent(
)
Spacer(modifier = Modifier.height(12.dp))
ReviewContentBottom(
- modifier = Modifier.padding(start = 32.dp, end = 16.dp)
+ isLike = reviewContent.isLike,
+ isScrap = reviewContent.isScrap,
+ likeCount = reviewContent.likesCount,
+ modifier = Modifier.padding(start = 32.dp, end = 16.dp),
+ onClickLike = {
+ onClickLike(reviewContent.id)
+ },
+ onClickScrap = {
+ onClickScrap(reviewContent.id)
+ },
+ onClickShare = onClickShare
)
+ if (firstReview && isFirstShare) {
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(end = 16.dp),
+ contentAlignment = Alignment.CenterEnd
+ ) {
+ ShareTooltip(bias = 0.875f, content = "카카오톡으로 같이가는 친구에게 공유하기")
+ }
+ }
}
}
@@ -255,6 +280,8 @@ private fun StadiumReviewContentPreview() {
) {
StadiumReviewContent(
context = LocalContext.current,
+ isFirstShare = false,
+ firstReview = false,
reviewContent = ResponseBlockReview.ResponseReview(
id = 1,
dateTime = "2023-03-01T19:00:00",
@@ -311,10 +338,16 @@ private fun StadiumReviewContentPreview() {
content = "",
isPositive = false
)
- )
+ ),
+ isLike = false,
+ isScrap = false,
+ likesCount = 1,
+ scrapsCount = 0,
+ reviewType = ""
),
onClick = { _, _ -> },
- onClickReport = {}
+ onClickReport = {},
+ onClickShare = {}
)
}
diff --git a/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/detailpicture/DetailCotentLayer.kt b/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/detailpicture/DetailCotentLayer.kt
index 15c891ea..83004774 100644
--- a/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/detailpicture/DetailCotentLayer.kt
+++ b/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/detailpicture/DetailCotentLayer.kt
@@ -262,7 +262,12 @@ private fun DetailContentLayerPreview() {
ResponseBlockReview.ResponseReview.ResponseReviewKeyword(
id = 2, content = "싫어요", isPositive = false
)
- )
+ ),
+ isLike = false,
+ isScrap = false,
+ likesCount = 1,
+ scrapsCount = 1,
+ reviewType = ""
)
DetailContentLayer(
context = LocalContext.current,
diff --git a/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/detailpicture/DetailReviewInteractionItems.kt b/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/detailpicture/DetailReviewInteractionItems.kt
index b2a4ff8a..9c65cd8c 100644
--- a/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/detailpicture/DetailReviewInteractionItems.kt
+++ b/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/detailpicture/DetailReviewInteractionItems.kt
@@ -2,6 +2,8 @@ package com.dpm.presentation.viewfinder.compose.detailpicture
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
@@ -17,11 +19,15 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.depromeet.designsystem.R
import com.dpm.designsystem.compose.ui.SpotTheme
-import com.dpm.presentation.extension.noRippleClickable
@Composable
fun DetailReviewInteractionItems(
- modifier: Modifier = Modifier
+ isLike: Boolean,
+ likeCount: Long,
+ modifier: Modifier = Modifier,
+ onClickLike: () -> Unit,
+ onClickScrap: () -> Unit,
+ onClickShare: () -> Unit
) {
Column(
modifier = modifier
@@ -35,39 +41,61 @@ fun DetailReviewInteractionItems(
),
horizontalAlignment = Alignment.CenterHorizontally
) {
- IconButton(onClick = { /*TODO*/ }) {
+ IconButton(onClick = onClickLike) {
Icon(
- painter = painterResource(id = R.drawable.ic_like_inactive),
+ painter = painterResource(id = if (isLike) R.drawable.ic_like_active else R.drawable.ic_like_inactive),
contentDescription = null,
tint = Color.Unspecified,
- modifier = Modifier.size(30.dp)
+ modifier = Modifier.size(24.dp)
)
}
Text(
- text = 10.toString(),
+ text = likeCount.toString(),
style = SpotTheme.typography.label10,
- color = SpotTheme.colors.foregroundWhite
+ color = if (isLike) SpotTheme.colors.actionEnabled else SpotTheme.colors.foregroundWhite
)
- IconButton(onClick = { /*TODO*/ }) {
+ Spacer(modifier = Modifier.height(5.dp))
+ IconButton(onClick = {
+ onClickScrap()
+ }) {
Icon(
painter = painterResource(id = R.drawable.ic_scrap),
contentDescription = null,
- tint = Color.Unspecified,
- modifier = Modifier.size(23.dp)
+ tint = SpotTheme.colors.foregroundWhite,
+ modifier = Modifier.size(24.dp)
)
}
- IconButton(onClick = { /*TODO*/ }) {
+ Spacer(modifier = Modifier.height(5.dp))
+ IconButton(onClick = onClickShare) {
Icon(
painter = painterResource(id = R.drawable.ic_share),
contentDescription = null,
- tint = Color.Unspecified,
+ tint = SpotTheme.colors.foregroundWhite,
)
}
}
}
+@Preview
+@Composable
+private fun DetailReviewInteractionItemsLikePreview() {
+ DetailReviewInteractionItems(
+ isLike = true,
+ likeCount = 1,
+ onClickLike = {},
+ onClickScrap = {},
+ onClickShare = {}
+ )
+}
+
@Preview
@Composable
private fun DetailReviewInteractionItemsPreview() {
- DetailReviewInteractionItems()
+ DetailReviewInteractionItems(
+ isLike = false,
+ likeCount = 1,
+ onClickLike = {},
+ onClickScrap = {},
+ onClickShare = {}
+ )
}
\ No newline at end of file
diff --git a/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/detailpicture/DetailViewPagerLayer.kt b/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/detailpicture/DetailViewPagerLayer.kt
index 79ae4518..330843cf 100644
--- a/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/detailpicture/DetailViewPagerLayer.kt
+++ b/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/detailpicture/DetailViewPagerLayer.kt
@@ -19,10 +19,16 @@ import com.dpm.domain.entity.response.viewfinder.ResponseBlockReview
@Composable
fun DetailViewPagerLayer(
context: Context,
+ isLike: Boolean,
+ likeCount: Long,
+ isFirstLike: Boolean,
isDimmed: Boolean,
verticalPagerState: PagerState,
pictures: List,
- modifier: Modifier = Modifier
+ modifier: Modifier = Modifier,
+ onClickLike: () -> Unit,
+ onClickScrap: () -> Unit,
+ onClickShare: () -> Unit
) {
Column(
modifier = modifier
@@ -32,9 +38,15 @@ fun DetailViewPagerLayer(
horizontalAlignment = Alignment.CenterHorizontally
) {
StadiumDetailPictureViewPager(
+ isLike = isLike,
context = context,
pictures = pictures,
+ isFirstLike = isFirstLike,
+ likeCount = likeCount,
verticalPagerState = verticalPagerState,
+ onClickLike = onClickLike,
+ onClickScrap = onClickScrap,
+ onClickShare = onClickShare
)
}
}
@@ -63,7 +75,13 @@ private fun DetailViewPagerLayerPreview() {
DetailViewPagerLayer(
context = LocalContext.current,
isDimmed = true,
+ isLike = true,
+ isFirstLike = true,
+ likeCount = 1,
pictures = pictures,
- verticalPagerState = pagerState
+ verticalPagerState = pagerState,
+ onClickLike = { },
+ onClickScrap = { },
+ onClickShare = { }
)
}
\ No newline at end of file
diff --git a/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/detailpicture/LikeTooltip.kt b/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/detailpicture/LikeTooltip.kt
index e260c7dd..53ce9dad 100644
--- a/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/detailpicture/LikeTooltip.kt
+++ b/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/detailpicture/LikeTooltip.kt
@@ -64,7 +64,6 @@ fun LikeTooltip(
Canvas(
modifier = Modifier
.height(triangleHeight)
- .offset(y = tooltipHeight.dp)
) {
val trianglePath = Path().apply {
moveTo(tooltipWidth * bias, triangleHeight.toPx()) // 삼각형 꼭짓점
diff --git a/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/detailpicture/StadiumDetailPictureScreen.kt b/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/detailpicture/StadiumDetailPictureScreen.kt
index c90f6a12..5c87b121 100644
--- a/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/detailpicture/StadiumDetailPictureScreen.kt
+++ b/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/detailpicture/StadiumDetailPictureScreen.kt
@@ -1,103 +1,85 @@
package com.dpm.presentation.viewfinder.compose.detailpicture
import android.content.Context
-import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateListOf
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.runtime.snapshotFlow
-import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
-import com.dpm.presentation.viewfinder.uistate.StadiumDetailUiState
+import com.dpm.presentation.viewfinder.DetailReviewEntryPoint
+import com.dpm.presentation.viewfinder.compose.detailpicture.main.StadiumDetailPictureTopScreen
+import com.dpm.presentation.viewfinder.compose.detailpicture.top.StadiumDetailPictureMainScreen
import com.dpm.presentation.viewfinder.viewmodel.StadiumDetailViewModel
-@OptIn(ExperimentalFoundationApi::class)
@Composable
fun StadiumDetailPictureScreen(
- context: Context = LocalContext.current,
- stadiumDetailViewModel: StadiumDetailViewModel = viewModel(),
reviewId: Long,
reviewIndex: Int,
- modifier: Modifier = Modifier
+ type: String,
+ isFirstLike: Boolean,
+ context: Context = LocalContext.current,
+ stadiumDetailViewModel: StadiumDetailViewModel = viewModel(),
+ onClickLike:() -> Unit = {},
+ onClickScrap: (id: Long) -> Unit = {},
+ onClickShare: () -> Unit = {}
) {
val reviews = stadiumDetailViewModel.detailUiState.collectAsStateWithLifecycle()
val bottomPadding by stadiumDetailViewModel.bottomPadding.collectAsStateWithLifecycle()
- reviews.value.let { uiState ->
- when (uiState) {
- is StadiumDetailUiState.ReviewsData -> {
- val visited = remember {
- mutableStateListOf(
- *List(uiState.reviews.size) { false }.toTypedArray()
- )
- }
-
- if (uiState.reviews.size - visited.size > 0) {
- visited.addAll(List(uiState.reviews.size - visited.size) { false })
- }
-
- val initPage by remember {
- mutableStateOf(uiState.reviews.indexOfFirst { it.id == reviewId })
- }
-
- val pagerState = rememberPagerState(
- pageCount = { uiState.reviews.size },
- initialPage = initPage
- )
-
- var pageIndex by remember {
- mutableStateOf(0)
- }
-
- LaunchedEffect(key1 = pagerState) {
- snapshotFlow { pagerState.currentPage }.collect {
- pageIndex = it
- stadiumDetailViewModel.updateCurrentIndex(it)
- if (visited[it]) return@collect
-
- if (it == initPage) {
- visited[it] = true
- }
-
- if (!visited[it]) {
- visited[it] = true
- }
- }
- }
-
- StadiumDetailReviewViewPager(
- context = context,
- reviews = uiState.reviews,
- visited = visited,
- position = reviewIndex,
- pageState = uiState.hasNext,
- pagerState = pagerState,
- pageIndex = pageIndex,
- bottomPadding = bottomPadding,
- modifier = modifier,
- onLoadPaging = stadiumDetailViewModel::getBlockReviews,
- )
- }
+ when (type) {
+ DetailReviewEntryPoint.TOP_REVIEW.name -> {
+ StadiumDetailPictureTopScreen(
+ context = context,
+ uiState = reviews.value,
+ reviewId = reviewId,
+ reviewIndex = reviewIndex,
+ bottomPadding = bottomPadding,
+ isFirstLike = isFirstLike,
+ stadiumDetailViewModel = stadiumDetailViewModel,
+ onClickLike = onClickLike,
+ onClickScrap = onClickScrap,
+ onClickShare = onClickShare
+ )
+ }
- else -> Unit
+ DetailReviewEntryPoint.MAIN_REVIEW.name -> {
+ StadiumDetailPictureMainScreen(
+ context = context,
+ uiState = reviews.value,
+ reviewId = reviewId,
+ reviewIndex = reviewIndex,
+ bottomPadding = bottomPadding,
+ isFirstLike = isFirstLike,
+ stadiumDetailViewModel = stadiumDetailViewModel,
+ onClickLike = onClickLike,
+ onClickScrap = onClickScrap,
+ onClickShare = onClickShare
+ )
}
}
}
@Preview
@Composable
-private fun StadiumDetailPictureScreenPreview() {
+private fun StadiumDetailPictureScreenMainPreview() {
+ StadiumDetailPictureScreen(
+ isFirstLike = true,
+ context = LocalContext.current,
+ reviewId = 1,
+ reviewIndex = 0,
+ type = DetailReviewEntryPoint.MAIN_REVIEW.name
+ )
+}
+
+@Preview
+@Composable
+private fun StadiumDetailPictureScreenTopPreview() {
StadiumDetailPictureScreen(
+ isFirstLike = true,
context = LocalContext.current,
reviewId = 1,
- reviewIndex = 0
+ reviewIndex = 0,
+ type = DetailReviewEntryPoint.TOP_REVIEW.name
)
}
\ No newline at end of file
diff --git a/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/detailpicture/StadiumDetailPictureViewPager.kt b/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/detailpicture/StadiumDetailPictureViewPager.kt
index 156ee923..2fd6218a 100644
--- a/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/detailpicture/StadiumDetailPictureViewPager.kt
+++ b/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/detailpicture/StadiumDetailPictureViewPager.kt
@@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
+import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentHeight
@@ -39,68 +40,105 @@ import com.dpm.domain.entity.response.viewfinder.ResponseBlockReview
@Composable
fun StadiumDetailPictureViewPager(
context: Context,
+ isLike: Boolean,
+ isFirstLike: Boolean,
+ likeCount: Long,
verticalPagerState: PagerState,
pictures: List,
modifier: Modifier = Modifier,
+ onClickLike: () -> Unit,
+ onClickScrap: () -> Unit,
+ onClickShare: () -> Unit
) {
- Column(
+ Box(
modifier = modifier,
- horizontalAlignment = Alignment.CenterHorizontally
+ contentAlignment = Alignment.Center
) {
- HorizontalPager(
- modifier = Modifier,
- state = verticalPagerState,
- ) { page ->
- AsyncImage(
- model = ImageRequest.Builder(context)
- .data(pictures.getOrNull(page)?.url)
- .crossfade(true)
- .build(),
- contentScale = ContentScale.FillWidth,
- contentDescription = null,
- placeholder = ColorPainter(Color.LightGray),
- modifier = Modifier
- .fillMaxWidth()
- .heightIn(max = ((context.resources.displayMetrics.heightPixels / context.resources.displayMetrics.density) * 0.58).dp)
- .clip(RectangleShape),
-
- )
- }
- Spacer(modifier = Modifier.height(8.dp))
- Row(
- Modifier
- .wrapContentHeight()
- .fillMaxWidth(),
- horizontalArrangement = Arrangement.Center
+ Column(
+ modifier = modifier,
+ horizontalAlignment = Alignment.CenterHorizontally
) {
- repeat(verticalPagerState.pageCount) { iteration ->
- if (verticalPagerState.currentPage == iteration) {
- Box(
- modifier = Modifier
- .padding(end = 4.dp)
- .clip(RoundedCornerShape(8.dp))
- .background(
- color = SpotTheme.colors.actionEnabled
- )
- .size(
- height = 6.dp,
- width = 15.dp
- )
- )
- } else {
- Box(
- modifier = Modifier
- .padding(end = 4.dp)
- .clip(CircleShape)
- .background(
- color = SpotTheme.colors.backgroundPrimary
- )
- .size(6.dp)
+ HorizontalPager(
+ modifier = Modifier,
+ state = verticalPagerState,
+ ) { page ->
+ AsyncImage(
+ model = ImageRequest.Builder(context)
+ .data(pictures.getOrNull(page)?.url)
+ .crossfade(true)
+ .build(),
+ contentScale = ContentScale.FillWidth,
+ contentDescription = null,
+ placeholder = ColorPainter(Color.LightGray),
+ modifier = Modifier
+ .fillMaxWidth()
+ .heightIn(max = ((context.resources.displayMetrics.heightPixels / context.resources.displayMetrics.density) * 0.58).dp)
+ .clip(RectangleShape),
+
)
+ }
+ Spacer(modifier = Modifier.height(8.dp))
+ Row(
+ Modifier
+ .wrapContentHeight()
+ .fillMaxWidth(),
+ horizontalArrangement = Arrangement.Center
+ ) {
+ repeat(verticalPagerState.pageCount) { iteration ->
+ if (verticalPagerState.currentPage == iteration) {
+ Box(
+ modifier = Modifier
+ .padding(end = 4.dp)
+ .clip(RoundedCornerShape(8.dp))
+ .background(
+ color = SpotTheme.colors.actionEnabled
+ )
+ .size(
+ height = 6.dp,
+ width = 15.dp
+ )
+ )
+ } else {
+ Box(
+ modifier = Modifier
+ .padding(end = 4.dp)
+ .clip(CircleShape)
+ .background(
+ color = SpotTheme.colors.backgroundPrimary
+ )
+ .size(6.dp)
+ )
+ }
}
}
}
+
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(end = 12.dp),
+ contentAlignment = Alignment.CenterEnd
+ ) {
+ DetailReviewInteractionItems(
+ isLike = isLike,
+ likeCount = likeCount,
+ onClickLike = onClickLike,
+ onClickScrap = onClickScrap,
+ onClickShare = onClickShare
+ )
+ if (isFirstLike) {
+ LikeTooltip(
+ modifier = Modifier
+ .align(Alignment.TopEnd)
+ .offset(y = (-32).dp),
+ bias = 0.8f,
+ content = "유용했다면, 도움돼요를 눌러주세요!",
+ )
+ }
+ }
}
+
+
}
@OptIn(ExperimentalFoundationApi::class)
@@ -112,6 +150,9 @@ private fun StadiumDetailPictureViewPagerPreview() {
}
StadiumDetailPictureViewPager(
context = LocalContext.current,
+ isLike = true,
+ likeCount = 1,
+ isFirstLike = true,
verticalPagerState = pagerState,
pictures = listOf(
ResponseBlockReview.ResponseReview.ResponseReviewImage(
@@ -124,5 +165,8 @@ private fun StadiumDetailPictureViewPagerPreview() {
id = 1, url = ""
)
),
+ onClickLike = { },
+ onClickScrap = { },
+ onClickShare = { }
)
}
\ No newline at end of file
diff --git a/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/detailpicture/StadiumDetailReviewViewPager.kt b/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/detailpicture/StadiumDetailReviewViewPager.kt
index c7d07af0..5b85794a 100644
--- a/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/detailpicture/StadiumDetailReviewViewPager.kt
+++ b/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/detailpicture/StadiumDetailReviewViewPager.kt
@@ -1,7 +1,6 @@
package com.dpm.presentation.viewfinder.compose.detailpicture
import android.content.Context
-import android.util.Log
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
@@ -26,16 +25,20 @@ import com.dpm.domain.entity.response.viewfinder.ResponseBlockReview
fun StadiumDetailReviewViewPager(
context: Context,
position: Int,
- pageState: Boolean,
+ hasNext: Boolean = false,
+ isFirstLike: Boolean,
pagerState: PagerState,
pageIndex: Int,
bottomPadding: Float,
reviews: List,
- visited: List,
+ visited: List = emptyList(),
modifier: Modifier = Modifier,
- onLoadPaging: () -> Unit,
+ onLoadPaging: () -> Unit = {},
+ onClickLike: (id: Long) -> Unit = {},
+ onClickScrap: (id: Long) -> Unit = {},
+ onClickShare: (imagePosition: Int) -> Unit = {}
) {
- if (pageIndex == reviews.size - 1 && pageState) {
+ if (pageIndex == reviews.size - 1 && hasNext) {
onLoadPaging()
}
@@ -63,8 +66,10 @@ fun StadiumDetailReviewViewPager(
)
LaunchedEffect(key1 = Unit) {
- if (!visited[page]) {
- verticalPagerState.scrollToPage(0)
+ if (visited.isNotEmpty()) {
+ if (!visited[page]) {
+ verticalPagerState.scrollToPage(0)
+ }
}
}
@@ -80,8 +85,20 @@ fun StadiumDetailReviewViewPager(
DetailViewPagerLayer(
context = context,
isDimmed = isDimmed,
+ isLike = reviews[page].isLike,
+ isFirstLike = isFirstLike,
+ likeCount = reviews[page].likesCount,
pictures = reviews[page].images,
verticalPagerState = verticalPagerState,
+ onClickLike = {
+ onClickLike(reviews[page].id)
+ },
+ onClickScrap = {
+ onClickScrap(reviews[page].id)
+ },
+ onClickShare = {
+ onClickShare(verticalPagerState.currentPage)
+ }
)
DetailContentLayer(
context = context,
@@ -151,7 +168,12 @@ private fun StadiumDetailReviewViewPagerPreview() {
ResponseBlockReview.ResponseReview.ResponseReviewKeyword(
id = 2, content = "싫어요", isPositive = false
)
- )
+ ),
+ isLike = false,
+ isScrap = false,
+ likesCount = 0,
+ scrapsCount = 0,
+ reviewType = ""
)
)
val pagerState = rememberPagerState(
@@ -160,9 +182,10 @@ private fun StadiumDetailReviewViewPagerPreview() {
StadiumDetailReviewViewPager(
context = LocalContext.current,
reviews = reviews,
+ isFirstLike = true,
visited = emptyList(),
position = 1,
- pageState = false,
+ hasNext = false,
pagerState = pagerState,
pageIndex = 0,
bottomPadding = 0f,
@@ -229,7 +252,12 @@ private fun StadiumDetailReviewViewPagerMorePreview() {
ResponseBlockReview.ResponseReview.ResponseReviewKeyword(
id = 2, content = "싫어요", isPositive = false
)
- )
+ ),
+ isLike = false,
+ isScrap = false,
+ likesCount = 0,
+ scrapsCount = 0,
+ reviewType = ""
)
)
val pagerState = rememberPagerState(
@@ -240,7 +268,8 @@ private fun StadiumDetailReviewViewPagerMorePreview() {
reviews = reviews,
visited = emptyList(),
position = 1,
- pageState = false,
+ isFirstLike = true,
+ hasNext = false,
pagerState = pagerState,
pageIndex = 0,
bottomPadding = 0f,
diff --git a/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/detailpicture/main/StadiumDetailPictureTopScreen.kt b/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/detailpicture/main/StadiumDetailPictureTopScreen.kt
new file mode 100644
index 00000000..1d7baaa0
--- /dev/null
+++ b/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/detailpicture/main/StadiumDetailPictureTopScreen.kt
@@ -0,0 +1,116 @@
+package com.dpm.presentation.viewfinder.compose.detailpicture.main
+
+import android.content.Context
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.pager.rememberPagerState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.runtime.snapshotFlow
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.lifecycle.viewmodel.compose.viewModel
+import com.dpm.presentation.util.KakaoUtils
+import com.dpm.presentation.util.kakaoShareSeatFeedTitle
+import com.dpm.presentation.util.seatFeed
+import com.dpm.presentation.viewfinder.DetailReviewEntryPoint
+import com.dpm.presentation.viewfinder.compose.detailpicture.StadiumDetailReviewViewPager
+import com.dpm.presentation.viewfinder.uistate.StadiumDetailUiState
+import com.dpm.presentation.viewfinder.viewmodel.StadiumDetailViewModel
+
+@OptIn(ExperimentalFoundationApi::class)
+@Composable
+fun StadiumDetailPictureTopScreen(
+ context: Context,
+ reviewId: Long,
+ reviewIndex: Int,
+ bottomPadding: Float,
+ isFirstLike: Boolean,
+ uiState: StadiumDetailUiState,
+ modifier: Modifier = Modifier,
+ stadiumDetailViewModel: StadiumDetailViewModel = viewModel(),
+ onClickLike: () -> Unit = {},
+ onClickScrap: (id: Long) -> Unit = {},
+ onClickShare: () -> Unit = {}
+) {
+ when (uiState) {
+ is StadiumDetailUiState.ReviewsData -> {
+ val initPage by remember {
+ mutableStateOf(uiState.topReviewImages.indexOfFirst { it.id == reviewId })
+ }
+
+ val pagerState = rememberPagerState(
+ pageCount = { uiState.topReviewImages.size },
+ initialPage = initPage
+ )
+
+ var isFirstLikeState by remember {
+ mutableStateOf(isFirstLike)
+ }
+
+ var pageIndex by remember {
+ mutableStateOf(0)
+ }
+
+ LaunchedEffect(key1 = pagerState) {
+ snapshotFlow { pagerState.currentPage }.collect {
+ pageIndex = it
+ }
+ }
+
+ StadiumDetailReviewViewPager(
+ context = context,
+ reviews = uiState.topReviewImages,
+ position = reviewIndex,
+ isFirstLike = isFirstLikeState,
+ pagerState = pagerState,
+ pageIndex = pageIndex,
+ bottomPadding = bottomPadding,
+ modifier = modifier,
+ onClickLike = { id ->
+ onClickLike()
+ isFirstLikeState = false
+ stadiumDetailViewModel.updateLike(id)
+ },
+ onClickScrap = onClickScrap,
+ onClickShare = { imagePosition ->
+ onClickShare()
+ KakaoUtils().share(
+ context,
+ seatFeed(
+ title = uiState.kakaoShareSeatFeedTitle(pageIndex),
+ description = "출처 : ${uiState.reviews[pageIndex].member.nickname}",
+ imageUrl = uiState.reviews[pageIndex].images[imagePosition].url,
+ queryParams = mapOf(
+ "stadiumId" to stadiumDetailViewModel.stadiumId.toString(),
+ "blockCode" to stadiumDetailViewModel.blockCode
+ )
+ ),
+ onSuccess = { sharingIntent ->
+ context.startActivity(sharingIntent)
+ }
+ )
+ }
+ )
+ }
+
+ else -> Unit
+ }
+}
+
+@Preview
+@Composable
+private fun StadiumDetailPictureTopScreenPreview() {
+ StadiumDetailPictureTopScreen(
+ context = LocalContext.current,
+ reviewId = 1,
+ reviewIndex = 0,
+ bottomPadding = 0f,
+ isFirstLike = false,
+ uiState = StadiumDetailUiState.Empty,
+ )
+}
\ No newline at end of file
diff --git a/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/detailpicture/top/StadiumDetailPictureMainScreen.kt b/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/detailpicture/top/StadiumDetailPictureMainScreen.kt
new file mode 100644
index 00000000..c6e6c345
--- /dev/null
+++ b/presentation/src/main/java/com/dpm/presentation/viewfinder/compose/detailpicture/top/StadiumDetailPictureMainScreen.kt
@@ -0,0 +1,138 @@
+package com.dpm.presentation.viewfinder.compose.detailpicture.top
+
+import android.content.Context
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.pager.rememberPagerState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateListOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.runtime.snapshotFlow
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.lifecycle.viewmodel.compose.viewModel
+import com.dpm.presentation.util.KakaoUtils
+import com.dpm.presentation.util.kakaoShareSeatFeedTitle
+import com.dpm.presentation.util.seatFeed
+import com.dpm.presentation.viewfinder.compose.detailpicture.StadiumDetailReviewViewPager
+import com.dpm.presentation.viewfinder.uistate.StadiumDetailUiState
+import com.dpm.presentation.viewfinder.viewmodel.StadiumDetailViewModel
+
+@OptIn(ExperimentalFoundationApi::class)
+@Composable
+fun StadiumDetailPictureMainScreen(
+ context: Context,
+ reviewId: Long,
+ reviewIndex: Int,
+ bottomPadding: Float,
+ isFirstLike: Boolean,
+ uiState: StadiumDetailUiState,
+ modifier: Modifier = Modifier,
+ stadiumDetailViewModel: StadiumDetailViewModel = viewModel(),
+ onClickLike: () -> Unit = {},
+ onClickScrap: (id: Long) -> Unit = {},
+ onClickShare: () -> Unit = {}
+) {
+ when(uiState) {
+ is StadiumDetailUiState.ReviewsData -> {
+ val visited = remember {
+ mutableStateListOf(
+ *List(uiState.reviews.size) { false }.toTypedArray()
+ )
+ }
+
+ if (uiState.reviews.size - visited.size > 0) {
+ visited.addAll(List(uiState.reviews.size - visited.size) { false })
+ }
+
+ val initPage by remember {
+ mutableStateOf(uiState.reviews.indexOfFirst { it.id == reviewId })
+ }
+
+ val pagerState = rememberPagerState(
+ pageCount = { uiState.reviews.size },
+ initialPage = initPage
+ )
+
+ var pageIndex by remember {
+ mutableStateOf(0)
+ }
+
+ var isFirstLikeState by remember {
+ mutableStateOf(isFirstLike)
+ }
+
+ LaunchedEffect(key1 = pagerState) {
+ snapshotFlow { pagerState.currentPage }.collect {
+ pageIndex = it
+ stadiumDetailViewModel.updateCurrentIndex(it)
+ if (visited[it]) return@collect
+
+ if (it == initPage) {
+ visited[it] = true
+ }
+
+ if (!visited[it]) {
+ visited[it] = true
+ }
+ }
+ }
+
+ StadiumDetailReviewViewPager(
+ context = context,
+ reviews = uiState.reviews,
+ visited = visited,
+ position = reviewIndex,
+ isFirstLike = isFirstLikeState,
+ hasNext = uiState.hasNext,
+ pagerState = pagerState,
+ pageIndex = pageIndex,
+ bottomPadding = bottomPadding,
+ modifier = modifier,
+ onLoadPaging = stadiumDetailViewModel::getBlockReviews,
+ onClickLike = { id ->
+ onClickLike()
+ isFirstLikeState = false
+ stadiumDetailViewModel.updateLike(id)
+ },
+ onClickScrap = onClickScrap,
+ onClickShare = { imagePosition ->
+ onClickShare()
+ KakaoUtils().share(
+ context,
+ seatFeed(
+ title = uiState.kakaoShareSeatFeedTitle(pageIndex),
+ description = "출처 : ${uiState.reviews[pageIndex].member.nickname}",
+ imageUrl = uiState.reviews[pageIndex].images[imagePosition].url,
+ queryParams = mapOf(
+ "stadiumId" to stadiumDetailViewModel.stadiumId.toString(),
+ "blockCode" to stadiumDetailViewModel.blockCode
+ )
+ ),
+ onSuccess = { sharingIntent ->
+ context.startActivity(sharingIntent)
+ }
+ )
+ }
+ )
+ }
+ else -> Unit
+ }
+}
+
+@Preview
+@Composable
+private fun StadiumDetailPictureMainScreenPreview() {
+ StadiumDetailPictureMainScreen(
+ context = LocalContext.current,
+ reviewId = 1,
+ reviewIndex = 0,
+ bottomPadding = 0f,
+ isFirstLike = false,
+ uiState = StadiumDetailUiState.Empty,
+ )
+}
\ No newline at end of file
diff --git a/presentation/src/main/java/com/dpm/presentation/viewfinder/uistate/StadiumDetailUiState.kt b/presentation/src/main/java/com/dpm/presentation/viewfinder/uistate/StadiumDetailUiState.kt
index 7d5d7edb..21cbffde 100644
--- a/presentation/src/main/java/com/dpm/presentation/viewfinder/uistate/StadiumDetailUiState.kt
+++ b/presentation/src/main/java/com/dpm/presentation/viewfinder/uistate/StadiumDetailUiState.kt
@@ -4,7 +4,7 @@ import com.dpm.domain.entity.response.viewfinder.ResponseBlockReview
sealed class StadiumDetailUiState {
data class ReviewsData(
- val topReviewImages: List,
+ val topReviewImages: List,
val stadiumContent: ResponseBlockReview.ResponseLocation,
val keywords: List,
val total: Long,
diff --git a/presentation/src/main/java/com/dpm/presentation/viewfinder/viewmodel/StadiumDetailViewModel.kt b/presentation/src/main/java/com/dpm/presentation/viewfinder/viewmodel/StadiumDetailViewModel.kt
index 63610275..965fb723 100644
--- a/presentation/src/main/java/com/dpm/presentation/viewfinder/viewmodel/StadiumDetailViewModel.kt
+++ b/presentation/src/main/java/com/dpm/presentation/viewfinder/viewmodel/StadiumDetailViewModel.kt
@@ -64,6 +64,66 @@ class StadiumDetailViewModel @Inject constructor(
_scrollState.value = state
}
+ fun updateLike(id: Long) {
+ viewModelScope.launch {
+ viewfinderRepository.updateLike(id.toInt())
+ _detailUiState.value = when (val currentState = _detailUiState.value) {
+ is StadiumDetailUiState.ReviewsData -> {
+ val updatedReviews = currentState.reviews.map { review ->
+ if (review.id == id) {
+ if (review.isLike) {
+ review.copy(
+ isLike = !review.isLike,
+ likesCount = review.likesCount - 1
+ )
+ } else {
+ review.copy(
+ isLike = !review.isLike,
+ likesCount = review.likesCount + 1
+ )
+ }
+ } else {
+ review
+ }
+ }
+ currentState.copy(reviews = updatedReviews)
+ }
+
+ else -> currentState
+ }
+ }
+ }
+
+ fun updateScrap(id: Long) {
+ viewModelScope.launch {
+ viewfinderRepository.updateScrap(id.toInt())
+ _detailUiState.value = when (val currentState = _detailUiState.value) {
+ is StadiumDetailUiState.ReviewsData -> {
+ val updatedReviews = currentState.reviews.map { review ->
+ if (review.id == id) {
+ if (review.isScrap) {
+ review.copy(
+ isScrap = !review.isScrap,
+ scrapsCount = review.scrapsCount - 1
+ )
+ } else {
+ review.copy(
+ isScrap = !review.isScrap,
+ scrapsCount = review.scrapsCount + 1
+ )
+ }
+ } else {
+ review
+ }
+ }
+ currentState.copy(reviews = updatedReviews)
+ }
+
+ else -> currentState
+ }
+ }
+ }
+
fun updateMonth(month: Int?) {
if (month != _reviewFilter.value.month) {
reset = true
@@ -80,8 +140,11 @@ class StadiumDetailViewModel @Inject constructor(
}
fun updateSort(sortBy: String) {
- _reviewFilter.value = _reviewFilter.value.copy(sortBy = sortBy)
- getBlockReviews(query = _reviewFilter.value)
+ if (_reviewFilter.value.sortBy != sortBy) {
+ reset = true
+ _reviewFilter.value = _reviewFilter.value.copy(sortBy = sortBy, cursor = null)
+ getBlockReviews(query = _reviewFilter.value)
+ }
}
fun updateRequestPathVariable(stadiumId: Int, blockCode: String) {
@@ -108,6 +171,7 @@ class StadiumDetailViewModel @Inject constructor(
(_detailUiState.value as StadiumDetailUiState.ReviewsData)
if (reset) {
+ reset = false
_detailUiState.value = reviewsData.copy(
reviews = blockReviews.reviews,
hasNext = blockReviews.hasNext,
@@ -153,6 +217,15 @@ class StadiumDetailViewModel @Inject constructor(
}
}
+ fun checkScrap(id: Long): Boolean {
+ (_detailUiState.value as StadiumDetailUiState.ReviewsData).reviews.map { review ->
+ if (review.id == id && review.isScrap) {
+ return true
+ }
+ }
+ return false
+ }
+
fun handleColumNumber(
column: Int,
number: Int,
diff --git a/presentation/src/main/res/values/strings.xml b/presentation/src/main/res/values/strings.xml
index f27e6516..c55d93e7 100644
--- a/presentation/src/main/res/values/strings.xml
+++ b/presentation/src/main/res/values/strings.xml
@@ -89,7 +89,9 @@
꾸준히 다른 구장에 대한 정보가 추가될 예정이에요
화면을 손가락으로 확대하여\n구역을 더 크게 보세요!
현재 잠실야구장만 이용할 수 있어요!
+ 스크랩이 완료되었어요!
""잠실야구장 보기""
+ ""스크랩으로 이동""
다른 블록에서 새로운 시야를 찾아보세요!
열과 번을 선택해 빠르게 자리를 찾아보세요⚡