diff --git a/buildSrc/build/kotlin/compileKotlin/cacheable/last-build.bin b/buildSrc/build/kotlin/compileKotlin/cacheable/last-build.bin index 1035ce15..7f3e2a35 100644 Binary files a/buildSrc/build/kotlin/compileKotlin/cacheable/last-build.bin and b/buildSrc/build/kotlin/compileKotlin/cacheable/last-build.bin differ diff --git a/buildSrc/build/kotlin/compileKotlin/local-state/build-history.bin b/buildSrc/build/kotlin/compileKotlin/local-state/build-history.bin index c96b11df..0802f9a6 100644 Binary files a/buildSrc/build/kotlin/compileKotlin/local-state/build-history.bin and b/buildSrc/build/kotlin/compileKotlin/local-state/build-history.bin differ diff --git a/buildSrc/build/libs/buildSrc.jar b/buildSrc/build/libs/buildSrc.jar index b64f4517..fd94d043 100644 Binary files a/buildSrc/build/libs/buildSrc.jar and b/buildSrc/build/libs/buildSrc.jar differ diff --git a/domain/src/main/java/com/dpm/domain/entity/response/home/ResponseMySeatRecord.kt b/domain/src/main/java/com/dpm/domain/entity/response/home/ResponseMySeatRecord.kt index 782fb601..2de09fb4 100644 --- a/domain/src/main/java/com/dpm/domain/entity/response/home/ResponseMySeatRecord.kt +++ b/domain/src/main/java/com/dpm/domain/entity/response/home/ResponseMySeatRecord.kt @@ -40,16 +40,7 @@ data class ResponseMySeatRecord( } fun kakaoShareSeatFeedTitle() : String { - val base = when(stadiumName.base(blockCode)) { - BASE.Base1 -> "1루 " - BASE.Base3 -> "3루 " - else -> "" - } - val section = sectionName - val block = "${rowNumber} 열 " - val seat = if(seatNumber == null) "" else "${seatNumber}번 " - - return "$stadiumName $base $section $block $seat".trim() + return "$stadiumName ${formattedBaseName()}${formattedSectionName()}${formattedBlockName()}${formattedRowNumber()}${formattedSeatNumber()}".trim() } private fun formattedBaseName(): String { @@ -77,7 +68,8 @@ data class ResponseMySeatRecord( private fun formattedBlockName() = when(stadiumId) { 1 -> { when(blockCode) { - in listOf("101w", "102w", "122w", "121w", "109w", "114w") -> "휠체어석 ${blockCode.replace("w", "")}블록 " + in listOf("101w", "102w", "122w", "121w") -> "휠체어석-레드 ${blockCode.replace("w", "")}블록" + in listOf("109w", "114w") -> "휠체어석-블루 ${blockCode.replace("w", "")}블록" in listOf("exciting1") -> "1루 익사이팅석 " in listOf("exciting3") -> "3루 익사이팅석 " in listOf("premium") -> "프리미엄석 " diff --git a/domain/src/main/java/com/dpm/domain/entity/response/home/ResponseScrap.kt b/domain/src/main/java/com/dpm/domain/entity/response/home/ResponseScrap.kt index c7534582..a65f1fb3 100644 --- a/domain/src/main/java/com/dpm/domain/entity/response/home/ResponseScrap.kt +++ b/domain/src/main/java/com/dpm/domain/entity/response/home/ResponseScrap.kt @@ -8,7 +8,7 @@ data class ResponseScrap( val nextCursor: String? = null, val hasNext: Boolean = false, val totalScrapCount: Int = 0, - val filter: ResponseFilter, + val filter: ResponseFilter = ResponseFilter(), ) { data class ResponseReviewWrapper( val baseReview: ResponseBaseReview, @@ -35,8 +35,13 @@ data class ResponseScrap( val isLiked: Boolean = false, val isScrapped: Boolean = false, ) { + /** + * @param + * formatted는 띄워쓰기 x parameter들은 띄워쓰기 o 일괄 적용 + * 띄워쓰기가 모두 적용되어있기 때문에 마지막에 trim 일괄 적용 + */ fun formattedStadiumToSection() : String = - "${stadium.name} ${formattedBaseName()} ${formattedSectionName()}".trim() + "${stadium.name} ${formattedBaseName()}${formattedSectionName()}".trim() fun formattedBlockToSeat(): String = if (seat != null) { @@ -51,11 +56,9 @@ data class ResponseScrap( BASE.Base3 -> "3루 " else -> "" } - val section = section.name - val block = "${row.number}열 " val seat = if(seat == null) "" else "${seat.seatNumber}번 " - return "${stadium.name}$base$section$block$seat".trim() + return "${stadium.name} ${formattedBaseName()}${formattedSectionName()}${formattedBlockToSeat()}".trim() } fun formattedBaseToBlock() : String = @@ -79,7 +82,11 @@ data class ResponseScrap( section.name.trim() } return when(block.code){ - in listOf("101w", "102w", "122w", "121w", "109w", "114w","exciting1","exciting3","premium") -> "" + in listOf("101w", "102w", "122w", "121w") -> "휠체어석-레드" + in listOf("109w", "114w") -> "휠체어석-블루" + in listOf("exciting1") -> "1루 익사이팅석 " + in listOf("exciting3") -> "3루 익사이팅석 " + in listOf("premium") -> "프리미엄석 " else -> "$section " } } @@ -87,10 +94,8 @@ data class ResponseScrap( private fun formattedBlockName() = when(stadium.id) { 1 -> { when(block.code) { - in listOf("101w", "102w", "122w", "121w", "109w", "114w") -> "휠체어석 ${block.code.replace("w", "")}블록 " - in listOf("exciting1") -> "1루 익사이팅석 " - in listOf("exciting3") -> "3루 익사이팅석 " - in listOf("premium") -> "프리미엄석 " + in listOf("101w", "102w", "122w", "121w", "109w", "114w") -> "${block.code.replace("w", "")}블록 " + in listOf("exciting1","exciting3","premium") -> "" else -> "${block.code}블록 " } } diff --git a/presentation/src/main/java/com/dpm/presentation/home/HomeActivity.kt b/presentation/src/main/java/com/dpm/presentation/home/HomeActivity.kt index 349421ca..d99d4d1b 100644 --- a/presentation/src/main/java/com/dpm/presentation/home/HomeActivity.kt +++ b/presentation/src/main/java/com/dpm/presentation/home/HomeActivity.kt @@ -71,6 +71,7 @@ class HomeActivity : BaseActivity( } private fun initView() { + setLayoutBorder() initReviewDialog() initViewStatusBar() homeViewModel.getStadiums() @@ -117,6 +118,10 @@ class HomeActivity : BaseActivity( } } + private fun setLayoutBorder(){ + binding.clHomeArchiving.clipToOutline = true + binding.clHomeScrap.clipToOutline = true + } private fun initEvent() = with(binding) { clHomeArchiving.setOnClickListener { diff --git a/presentation/src/main/java/com/dpm/presentation/scrap/ScrapActivity.kt b/presentation/src/main/java/com/dpm/presentation/scrap/ScrapActivity.kt index 30e1b7d1..ae73a4cd 100644 --- a/presentation/src/main/java/com/dpm/presentation/scrap/ScrapActivity.kt +++ b/presentation/src/main/java/com/dpm/presentation/scrap/ScrapActivity.kt @@ -14,7 +14,6 @@ import com.dpm.core.base.BaseActivity import com.dpm.core.state.UiState import com.dpm.designsystem.extension.dpToPx import com.dpm.presentation.extension.setOnSingleClickListener -import com.dpm.presentation.global.GlobalVariable import com.dpm.presentation.scrap.adapter.ScrapFilterAdapter import com.dpm.presentation.scrap.adapter.ScrapGridSpacingItemDecoration import com.dpm.presentation.scrap.adapter.ScrapRecordAdapter @@ -77,7 +76,7 @@ class ScrapActivity : BaseActivity( viewModel.scrap.asLiveData().observe(this) { state -> when (state) { is UiState.Success -> { - binding.tvScrapCount.text = state.data.totalScrapCount .toString() + binding.tvScrapCount.text = state.data.totalScrapCount.toString() scrapAdapter.submitList(state.data.reviews) isLoading = false setScrapScreenVisibility(ScrapScreenState.SUCCESS) @@ -120,7 +119,8 @@ class ScrapActivity : BaseActivity( binding.rvScrapRecord.itemAnimator = null binding.rvScrapRecord.addItemDecoration( ScrapGridSpacingItemDecoration( - spanCount = 2, spacing = 12.dpToPx(this), bottomSpacing = 40.dpToPx(this) + spanCount = 2, spacing = 12.dpToPx(this), bottomSpacing = 40.dpToPx(this), + borderMargin = 16.dpToPx(this) ) ) diff --git a/presentation/src/main/java/com/dpm/presentation/scrap/ScrapDetailPictureFragment.kt b/presentation/src/main/java/com/dpm/presentation/scrap/ScrapDetailPictureFragment.kt index 5a8a4198..7ac908f7 100644 --- a/presentation/src/main/java/com/dpm/presentation/scrap/ScrapDetailPictureFragment.kt +++ b/presentation/src/main/java/com/dpm/presentation/scrap/ScrapDetailPictureFragment.kt @@ -2,7 +2,11 @@ package com.dpm.presentation.scrap import android.os.Bundle import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE import android.view.ViewGroup +import android.view.animation.Animation +import android.view.animation.ScaleAnimation import androidx.activity.OnBackPressedCallback import androidx.core.view.ViewCompat import androidx.core.view.WindowCompat @@ -13,14 +17,18 @@ import androidx.fragment.app.DialogFragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.commit import androidx.lifecycle.asLiveData +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.SimpleItemAnimator import androidx.viewpager2.widget.ViewPager2 import com.depromeet.presentation.R import com.depromeet.presentation.databinding.FragmentScrapDetailPictureBinding import com.dpm.core.base.BindingFragment import com.dpm.core.state.UiState +import com.dpm.designsystem.SpotSnackBar import com.dpm.domain.entity.response.home.ResponseScrap import com.dpm.presentation.scheme.SchemeKey import com.dpm.presentation.scrap.adapter.ScrapDetailAdapter +import com.dpm.presentation.scrap.adapter.ScrapDetailViewHolder import com.dpm.presentation.scrap.viewmodel.ScrapViewModel import com.dpm.presentation.util.KakaoUtils import com.dpm.presentation.util.Utils @@ -40,6 +48,8 @@ class ScrapDetailPictureFragment : BindingFragment - when (state) { - is UiState.Success -> { - adapter.submitList(state.data.reviews.map { it.baseReview }.toList()) - binding.vpScrap.post { - binding.vpScrap.setCurrentItem(viewModel.currentPage.value, false) - } - isLoading = false - } + viewModel.detailScrap.asLiveData().observe(viewLifecycleOwner) { data -> + adapter.submitList(data.map { it.baseReview }.toList()) + binding.vpScrap.setCurrentItem(viewModel.currentPage.value, false) + isLoading = false + } - else -> {} + viewModel.isFirstLike.asLiveData().observe(viewLifecycleOwner) { isFirstLike -> + if (!isFirstLike) { + hideLikeDescriptionView() } } } private fun initViewPager() { adapter = ScrapDetailAdapter( - scrapClick = { - viewModel.updateScrap(it) + scrapClick = { id, isScrap -> + viewModel.updateScrap(id) + if (isScrap) { + snackbar = SpotSnackBar.make( + view = binding.root, + message = "스크랩이 해제되었어요!", + background = R.drawable.rect_gray800_fill_60, + marginBottom = 20, + onClick = {}, + ) + } else { + snackbar = SpotSnackBar.make( + view = binding.root, + message = "스크랩이 완료되었어요!", + background = R.drawable.rect_gray800_fill_60, + endMessage = "스크랩으로 이동", + marginBottom = 20, + ) { + removeFragment() + } + } + snackbar?.show() }, likeClick = { viewModel.updateLike(it) @@ -95,26 +124,42 @@ class ScrapDetailPictureFragment : BindingFragment= adapter.itemCount - 2 && (viewModel.scrap.value as UiState.Success).data.hasNext) { isLoading = true viewModel.getNextScrapRecord() @@ -123,12 +168,55 @@ class ScrapDetailPictureFragment : BindingFragment requireContext().startActivity(sharingIntent) + viewModel.updateIsFirstShare(true) }, onFailure = { Timber.d("링크 공유 실패 : ${it.message}") @@ -210,4 +294,5 @@ class ScrapDetailPictureFragment : BindingFragment Unit, + private val scrapClick: (Int, Boolean) -> Unit, private val likeClick: (Int) -> Unit, private val shareClick: (ResponseScrap.ResponseBaseReview, Int) -> Unit, ) : ListAdapter( ItemDiffCallback( - onItemsTheSame = { oldItem, newItem -> oldItem.id == newItem.id }, - onContentsTheSame = { oldItem, newItem -> oldItem == newItem } + onItemsTheSame = { oldItem, newItem -> + oldItem.id == newItem.id + }, + onContentsTheSame = { oldItem, newItem -> + oldItem == newItem + } ) ) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ScrapDetailViewHolder { @@ -52,8 +54,8 @@ class ScrapDetailAdapter( } class ScrapDetailViewHolder( - private val binding: ItemScrapDetailBinding, - private val scrapClick: (Int) -> Unit, + internal val binding: ItemScrapDetailBinding, + private val scrapClick: (Int, Boolean) -> Unit, private val likeClick: (Int) -> Unit, private val shareClick: (ResponseScrap.ResponseBaseReview, Int) -> Unit, ) : RecyclerView.ViewHolder(binding.root) { @@ -68,6 +70,7 @@ class ScrapDetailViewHolder( tvScrapLevel.text = item.member.formattedLevel() tvSectionName.text = item.formattedBlockToSeat() tvLikeCount.text = item.likesCount.toString() + binding.csbvLikeDescription.setTextPart("유용했다면,","도움돼요","를 눌러주세요!") if (item.content.isNotEmpty()) { tvScrapContent.text = item.content } else { @@ -75,17 +78,15 @@ class ScrapDetailViewHolder( } initScrapImageAdapter(item) - - cvScrapKeyword.apply { - setContent { - MaterialTheme { - KeywordFlowRow( - keywords = item.keywords.map { it.toUiKeyword() }, - overflowIndex = 2 - ) - } + cvScrapKeyword.setContent { + MaterialTheme { + KeywordFlowRow( + keywords = item.keywords.map { it.toUiKeyword() }, + overflowIndex = 2 + ) } } + if (item.isScrapped) { ivScrap.load(com.depromeet.designsystem.R.drawable.ic_scrap_active) ivScrap.setColorFilter( @@ -118,7 +119,7 @@ class ScrapDetailViewHolder( } tvScrapContent.post { if (tvScrapContent.layout != null) { - if (!(tvScrapContent.layout.getEllipsisCount(0) > 0)) { + if (tvScrapContent.layout.getEllipsisCount(0) <= 0) { tvMore.visibility = INVISIBLE } } @@ -154,20 +155,11 @@ class ScrapDetailViewHolder( ivLike.setOnSingleClickListener { if (!item.isLiked) { lottieLike.playAnimation() - ivLike.load(com.depromeet.designsystem.R.drawable.ic_like_active) - lottieLike.addAnimatorListener(object : AnimatorListenerAdapter() { - override fun onAnimationEnd(animation: Animator, isReverse: Boolean) { - super.onAnimationEnd(animation, isReverse) - likeClick(item.id) - } - }) - } else { - likeClick(item.id) } - + likeClick(item.id) } ivScrap.setOnSingleClickListener { - scrapClick(item.id) + scrapClick(item.id, item.isScrapped) } ivShare.setOnSingleClickListener { shareClick(item, binding.vpImage.currentItem) @@ -178,20 +170,21 @@ class ScrapDetailViewHolder( if (!::scrapImageAdapter.isInitialized) { scrapImageAdapter = ScrapImageAdapter() binding.vpImage.adapter = scrapImageAdapter - scrapImageAdapter.submitList(item.images.map { it.url }) - setupIndicators(scrapImageAdapter.itemCount) - binding.vpImage.registerOnPageChangeCallback(object : - ViewPager2.OnPageChangeCallback() { - override fun onPageSelected(position: Int) { - updateIndicators(position) - if (position >= 0 && position < item.images.size) { - binding.ivBackground.loadAndClip(item.images[position].url) - } else { - binding.ivBackground.loadAndClip(item.images[0].url) - } - } - }) } + + scrapImageAdapter.submitList(item.images.map { it.url }) + setupIndicators(scrapImageAdapter.itemCount) + binding.vpImage.registerOnPageChangeCallback(object : + ViewPager2.OnPageChangeCallback() { + override fun onPageSelected(position: Int) { + updateIndicators(position) + if (position >= 0 && position < item.images.size) { + binding.ivBackground.loadAndClip(item.images[position].url) + } else { + binding.ivBackground.loadAndClip(item.images[0].url) + } + } + }) } private fun setupIndicators(count: Int) { @@ -199,31 +192,26 @@ class ScrapDetailViewHolder( binding.llIndicator.removeAllViews() val context = binding.root.context - if (count < 2) { - binding.llIndicator.visibility = GONE - } else { - binding.llIndicator.visibility = VISIBLE - for (i in 0 until count) { - val indicator = ImageView(context).apply { - layoutParams = LinearLayout.LayoutParams( - 6.dpToPx(context), 6.dpToPx(context) - ).apply { - setMargins(2.dpToPx(context), 0, 2.dpToPx(context), 0) - } - scaleType = ImageView.ScaleType.FIT_XY - setImageDrawable( - ContextCompat.getDrawable( - context, - R.drawable.indicator_unselected - ) - ) + for (i in 0 until count) { + val indicator = ImageView(context).apply { + layoutParams = LinearLayout.LayoutParams( + 6.dpToPx(context), 6.dpToPx(context) + ).apply { + setMargins(2.dpToPx(context), 0, 2.dpToPx(context), 0) } - indicators.add(indicator) - binding.llIndicator.addView(indicator) + scaleType = ImageView.ScaleType.FIT_XY + setImageDrawable( + ContextCompat.getDrawable( + context, + R.drawable.indicator_unselected + ) + ) } - - updateIndicators(0) + indicators.add(indicator) + binding.llIndicator.addView(indicator) } + + updateIndicators(0) } private fun updateIndicators(selectedPosition: Int) { diff --git a/presentation/src/main/java/com/dpm/presentation/scrap/adapter/ScrapFilterAdapter.kt b/presentation/src/main/java/com/dpm/presentation/scrap/adapter/ScrapFilterAdapter.kt index 22d4f6d4..d8e6cffa 100644 --- a/presentation/src/main/java/com/dpm/presentation/scrap/adapter/ScrapFilterAdapter.kt +++ b/presentation/src/main/java/com/dpm/presentation/scrap/adapter/ScrapFilterAdapter.kt @@ -2,12 +2,14 @@ package com.dpm.presentation.scrap.adapter import android.annotation.SuppressLint import android.view.LayoutInflater +import android.view.View.GONE import android.view.ViewGroup import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import com.depromeet.presentation.databinding.ItemScrapFilterBinding import com.depromeet.presentation.databinding.ItemScrapFilterSelectedBinding import com.dpm.presentation.scrap.viewmodel.FilterNameData +import com.dpm.presentation.scrap.viewmodel.ScrapViewModel import com.dpm.presentation.util.ItemDiffCallback import timber.log.Timber @@ -88,6 +90,7 @@ class ScrapFilterViewHolder( private val filterClick: () -> Unit, ) : RecyclerView.ViewHolder(binding.root) { fun bind() { + binding.root.clipToOutline = true binding.root.setOnClickListener { filterClick() } @@ -99,6 +102,11 @@ class ScrapFilterSelectedViewHolder( private val selectedClick: (FilterNameData) -> Unit, ) : RecyclerView.ViewHolder(binding.root) { fun bind(item: FilterNameData) = with(binding) { + /** 현재 잠실야구장만 있어 임시 처리**/ + if(item.filterType == ScrapViewModel.ScrapFilterType.STADIUM){ + ivClose.visibility = GONE + } + root.clipToOutline = true ivClose.setOnClickListener { selectedClick(item) } tvScrapFilter.text = item.name } diff --git a/presentation/src/main/java/com/dpm/presentation/scrap/adapter/ScrapRecordAdapter.kt b/presentation/src/main/java/com/dpm/presentation/scrap/adapter/ScrapRecordAdapter.kt index 24593de5..c717b01a 100644 --- a/presentation/src/main/java/com/dpm/presentation/scrap/adapter/ScrapRecordAdapter.kt +++ b/presentation/src/main/java/com/dpm/presentation/scrap/adapter/ScrapRecordAdapter.kt @@ -46,8 +46,10 @@ class ScrapRecordViewHolder( ) : RecyclerView.ViewHolder(binding.root) { fun bind(item: ResponseScrap.ResponseReviewWrapper, position: Int) = with(binding) { itemView.layoutParams.height = 193.dpToPx(itemView.context) + root.clipToOutline = true ivScrap.setOnSingleClickListener { + ivScrap.load(com.depromeet.designsystem.R.drawable.ic_scrap_inactive) scrapClick(item) } root.setOnClickListener { @@ -64,7 +66,6 @@ class ScrapRecordViewHolder( ivScrapImage.loadAndClip(item.baseReview.images[0].url) tvScrapStadium.text = item.stadiumName tvScrapSeat.text = item.baseReview.formattedBaseToBlock() -// root.clipToOutline = true } } @@ -72,6 +73,7 @@ class ScrapGridSpacingItemDecoration( private val spanCount: Int, private val spacing: Int = 0, private val bottomSpacing: Int = 0, + private val borderMargin : Int = 0 ) : RecyclerView.ItemDecoration() { override fun getItemOffsets( outRect: Rect, @@ -89,9 +91,9 @@ class ScrapGridSpacingItemDecoration( val row = position / spanCount with(outRect) { - left = if (column == 0) 3 else spacing / 2 - right = if (column == spanCount - 1) 3 else spacing / 2 - top = if (row == 0) 3 else spacing + left = if (column == 0) borderMargin else spacing / 2 + right = if (column == spanCount - 1) borderMargin else spacing / 2 + top = if (row == 0) 6 else spacing bottom = if (row == totalRows - 1) bottomSpacing else 0 } diff --git a/presentation/src/main/java/com/dpm/presentation/scrap/viewmodel/ScrapViewModel.kt b/presentation/src/main/java/com/dpm/presentation/scrap/viewmodel/ScrapViewModel.kt index 79d345d8..d1cd0b0d 100644 --- a/presentation/src/main/java/com/dpm/presentation/scrap/viewmodel/ScrapViewModel.kt +++ b/presentation/src/main/java/com/dpm/presentation/scrap/viewmodel/ScrapViewModel.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.viewModelScope import com.dpm.core.state.UiState import com.dpm.domain.entity.request.home.RequestScrap import com.dpm.domain.entity.response.home.ResponseScrap +import com.dpm.domain.preference.SharedPreference import com.dpm.domain.repository.HomeRepository import com.dpm.domain.repository.ViewfinderRepository import com.dpm.presentation.global.GlobalVariable @@ -41,6 +42,7 @@ data class BadReviewData( class ScrapViewModel @Inject constructor( private val homeRepository: HomeRepository, private val viewfinderRepository: ViewfinderRepository, + private val sharedPreference: SharedPreference ) : ViewModel() { private var monthsSelected: List = emptyList() @@ -51,6 +53,9 @@ class ScrapViewModel @Inject constructor( private val _scrap = MutableStateFlow>(UiState.Loading) val scrap = _scrap.asStateFlow() + private val _detailScrap = MutableStateFlow>(emptyList()) + val detailScrap = _detailScrap.asStateFlow() + private val _filter = MutableStateFlow>(emptyList()) val filter = _filter.asStateFlow() @@ -66,6 +71,9 @@ class ScrapViewModel @Inject constructor( private val _currentPage = MutableStateFlow(0) val currentPage = _currentPage.asStateFlow() + private val _isFirstLike = MutableStateFlow(sharedPreference.isFirstLike) + val isFirstLike = _isFirstLike.asStateFlow() + fun getScrapRecord() { viewModelScope.launch { @@ -82,6 +90,7 @@ class ScrapViewModel @Inject constructor( ).onSuccess { data -> if (data.reviews.isNotEmpty()) { _scrap.value = UiState.Success(data) + _detailScrap.value = data.reviews } else { _scrap.value = UiState.Empty } @@ -91,12 +100,37 @@ class ScrapViewModel @Inject constructor( } } + fun updateIsFirstLike(isFirstLike : Boolean) { + sharedPreference.isFirstLike = isFirstLike + _isFirstLike.value = isFirstLike + } + + fun updateIsFirstShare(isFirstShare : Boolean) { + sharedPreference.isFirstShare = isFirstShare + } + + fun getDetailScrap() { + _detailScrap.value = (_scrap.value as UiState.Success).data.reviews + } + fun reloadScrap() { if(GlobalVariable.isScrap && _scrap.value is UiState.Empty) { getScrapRecord() } } + fun updateScrapRecord() { + if(_detailScrap.value.count { it.baseReview.isScrapped } == 0){ + _scrap.value = UiState.Empty + }else{ + val currentState = (_scrap.value as UiState.Success).data + _scrap.value = UiState.Success(currentState.copy( + reviews = _detailScrap.value.filter { it.baseReview.isScrapped } + )) + } + } + + fun getNextScrapRecord() { viewModelScope.launch { homeRepository.getScrap( @@ -122,6 +156,8 @@ class ScrapViewModel @Inject constructor( filter = it.filter ) _scrap.value = UiState.Success(updatedScrap) + val currentDetailScrap = _detailScrap.value + _detailScrap.value = currentDetailScrap + it.reviews }.onFailure {} } } @@ -191,6 +227,7 @@ class ScrapViewModel @Inject constructor( } fun updateLike(id: Int) { + updateIsFirstLike(false) viewModelScope.launch { viewfinderRepository.updateLike(id).onSuccess { val currentState = (_scrap.value as? UiState.Success)?.data @@ -211,7 +248,23 @@ class ScrapViewModel @Inject constructor( review } } - + val updatedDetailList = detailScrap.value.map { review -> + if(review.baseReview.id == id){ + review.copy( + baseReview = review.baseReview.copy( + isLiked = !review.baseReview.isLiked, + likesCount = if (review.baseReview.isLiked) { + review.baseReview.likesCount - 1 + } else { + review.baseReview.likesCount + 1 + } + ) + ) + } else { + review + } + } + _detailScrap.value = updatedDetailList _scrap.value = UiState.Success( data = currentState.copy(reviews = updatedList) ) @@ -226,8 +279,14 @@ class ScrapViewModel @Inject constructor( viewModelScope.launch { viewfinderRepository.updateScrap(id).onSuccess { val currentState = (_scrap.value as UiState.Success).data - val updatedList = currentState.reviews.map { review -> - if (review.baseReview.id == id) { + val updatedList = currentState.reviews.filter { it.baseReview.id != id } + _scrap.value = if(updatedList.isEmpty()){ + UiState.Empty + }else { + UiState.Success(currentState.copy(reviews = currentState.reviews.filter { it.baseReview.id != id })) + } + val detailScrapUpdatedList = detailScrap.value.map { review -> + if(review.baseReview.id == id){ review.copy( baseReview = review.baseReview.copy( isScrapped = !review.baseReview.isScrapped @@ -237,9 +296,7 @@ class ScrapViewModel @Inject constructor( review } } - _scrap.value = UiState.Success( - data = currentState.copy(reviews = updatedList) - ) + _detailScrap.value = detailScrapUpdatedList }.onFailure { Timber.d("test 스크랩 업데이트 실패 $it") } diff --git a/presentation/src/main/java/com/dpm/presentation/seatrecord/SeatRecordActivity.kt b/presentation/src/main/java/com/dpm/presentation/seatrecord/SeatRecordActivity.kt index 8856e5b1..6cb466b5 100644 --- a/presentation/src/main/java/com/dpm/presentation/seatrecord/SeatRecordActivity.kt +++ b/presentation/src/main/java/com/dpm/presentation/seatrecord/SeatRecordActivity.kt @@ -12,6 +12,7 @@ import androidx.activity.viewModels import androidx.core.widget.NestedScrollView import androidx.fragment.app.commit import androidx.lifecycle.asLiveData +import androidx.recyclerview.widget.SimpleItemAnimator import com.depromeet.presentation.R import com.depromeet.presentation.databinding.ActivitySeatRecordBinding import com.dpm.core.base.BaseActivity @@ -545,8 +546,10 @@ class SeatRecordActivity : BaseActivity( private fun initReviewList() { monthSeatReviewAdapter = MonthRecordAdapter() binding.rvSeatReview.adapter = monthSeatReviewAdapter +// (binding.rvSeatReview.itemAnimator as? SimpleItemAnimator)?.supportsChangeAnimations = false binding.rvSeatReview.itemAnimator = null + monthSeatReviewAdapter.itemRecordClickListener = object : MonthRecordAdapter.OnItemRecordClickListener { diff --git a/presentation/src/main/java/com/dpm/presentation/seatrecord/adapter/MonthRecordAdapter.kt b/presentation/src/main/java/com/dpm/presentation/seatrecord/adapter/MonthRecordAdapter.kt index b522d833..3a1e3785 100644 --- a/presentation/src/main/java/com/dpm/presentation/seatrecord/adapter/MonthRecordAdapter.kt +++ b/presentation/src/main/java/com/dpm/presentation/seatrecord/adapter/MonthRecordAdapter.kt @@ -2,14 +2,13 @@ package com.dpm.presentation.seatrecord.adapter import android.view.LayoutInflater import android.view.ViewGroup -import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView -import com.dpm.domain.entity.response.home.ResponseMySeatRecord +import androidx.recyclerview.widget.SimpleItemAnimator import com.depromeet.presentation.databinding.ItemRecentMonthBinding +import com.dpm.domain.entity.response.home.ResponseMySeatRecord import com.dpm.presentation.seatrecord.uiMapper.MonthReviewData import com.dpm.presentation.util.ItemDiffCallback -import timber.log.Timber class MonthRecordAdapter() : @@ -49,49 +48,41 @@ class MonthRecordViewHolder( private val itemRecordClickListener: MonthRecordAdapter.OnItemRecordClickListener?, ) : RecyclerView.ViewHolder(binding.root) { - private lateinit var adapter: RecentRecordAdapter + private lateinit var adapter : RecentRecordAdapter fun bind(item: MonthReviewData) { with(binding) { - initReviewAdapter() + initReviewAdapter(item) "${item.month}월".also { tvRecentMonth.text = it } - adapter.submitList(item.reviews) } } - private fun initReviewAdapter() { + private fun initReviewAdapter(item : MonthReviewData) { + if(!::adapter.isInitialized){ adapter = RecentRecordAdapter() binding.rvRecentPost.adapter = adapter - adapter.itemRecordClickListener = - object : RecentRecordAdapter.OnItemRecordClickListener { - override fun onItemRecordClick(item: ResponseMySeatRecord.ReviewResponse) { - itemRecordClickListener?.onItemRecordClick(item) - } - - override fun onItemMoreClick(item: ResponseMySeatRecord.ReviewResponse) { - itemRecordClickListener?.onMoreRecordClick(item.id) - } - - override fun onLikeClick(reviewId: Int) { - itemRecordClickListener?.onLikeClick(reviewId) - } + (binding.rvRecentPost.itemAnimator as? SimpleItemAnimator)?.supportsChangeAnimations = false + binding.rvRecentPost.itemAnimator = null + } + adapter.submitList(item.reviews.toList()) + adapter.itemRecordClickListener = + object : RecentRecordAdapter.OnItemRecordClickListener { + override fun onItemRecordClick(item: ResponseMySeatRecord.ReviewResponse) { + itemRecordClickListener?.onItemRecordClick(item) + } - override fun onScrapClick(reviewId: Int) { - itemRecordClickListener?.onScrapClick(reviewId) - } + override fun onItemMoreClick(item: ResponseMySeatRecord.ReviewResponse) { + itemRecordClickListener?.onMoreRecordClick(item.id) } - binding.rvRecentPost.addOnScrollListener(object : RecyclerView.OnScrollListener() { - override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { - super.onScrolled(recyclerView, dx, dy) - val scrollBottom = !binding.rvRecentPost.canScrollVertically(1) - val layoutManager = binding.rvRecentPost.layoutManager as LinearLayoutManager - val lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition() - val itemCount = layoutManager.itemCount - 1 + override fun onLikeClick(reviewId: Int) { + itemRecordClickListener?.onLikeClick(reviewId) + } - Timber.d("test scroll $scrollBottom / $lastVisibleItemPosition / $itemCount") + override fun onScrapClick(reviewId: Int) { + itemRecordClickListener?.onScrapClick(reviewId) } - }) + } } } \ No newline at end of file diff --git a/presentation/src/main/java/com/dpm/presentation/seatrecord/adapter/RecentRecordAdapter.kt b/presentation/src/main/java/com/dpm/presentation/seatrecord/adapter/RecentRecordAdapter.kt index 0f3e5376..6ce74fcb 100644 --- a/presentation/src/main/java/com/dpm/presentation/seatrecord/adapter/RecentRecordAdapter.kt +++ b/presentation/src/main/java/com/dpm/presentation/seatrecord/adapter/RecentRecordAdapter.kt @@ -1,7 +1,5 @@ package com.dpm.presentation.seatrecord.adapter -import android.animation.Animator -import android.animation.AnimatorListenerAdapter import android.view.LayoutInflater import android.view.View.GONE import android.view.ViewGroup @@ -63,15 +61,8 @@ class RecentRecordAdapter( binding.ivRecordLike.setOnSingleClickListener { if (!getItem(position).isLiked) { binding.lottieLike.playAnimation() - binding.lottieLike.addAnimatorListener(object : AnimatorListenerAdapter() { - override fun onAnimationEnd(animation: Animator) { - super.onAnimationEnd(animation) - itemRecordClickListener?.onLikeClick(getItem(position).id) - } - }) - } else { - itemRecordClickListener?.onLikeClick(getItem(position).id) } + itemRecordClickListener?.onLikeClick(getItem(position).id) } } @@ -113,6 +104,12 @@ class RecentRecordViewHolder( tvRecordScrapCount.text = item.scrapsCount.toString() if (item.isScrapped) { ivRecordScrap.load(com.depromeet.designsystem.R.drawable.ic_scrap_active) + ivRecordScrap.setColorFilter( + ContextCompat.getColor( + binding.root.context, + com.depromeet.designsystem.R.color.color_action_enabled + ) + ) } else { ivRecordScrap.load(com.depromeet.designsystem.R.drawable.ic_scrap_inactive) ivRecordScrap.setColorFilter( diff --git a/presentation/src/main/res/drawable/rect_gray800_fill_60.xml b/presentation/src/main/res/drawable/rect_gray800_fill_60.xml new file mode 100644 index 00000000..e5305fb8 --- /dev/null +++ b/presentation/src/main/res/drawable/rect_gray800_fill_60.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/drawable/rect_white_fill_gray200_stroke_999.xml b/presentation/src/main/res/drawable/rect_white_fill_gray200_stroke_999.xml new file mode 100644 index 00000000..bd80f776 --- /dev/null +++ b/presentation/src/main/res/drawable/rect_white_fill_gray200_stroke_999.xml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/layout/activity_scrap.xml b/presentation/src/main/res/layout/activity_scrap.xml index de92f3af..355cee36 100644 --- a/presentation/src/main/res/layout/activity_scrap.xml +++ b/presentation/src/main/res/layout/activity_scrap.xml @@ -76,20 +76,19 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/tvScrapTitle" tools:itemCount="5" - tools:listitem="@layout/item_scrap_filter_selected"/> + tools:listitem="@layout/item_scrap_filter_selected" /> - - - - - - - - - - - - - + + + + + + + + + + + + + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> \ No newline at end of file diff --git a/presentation/src/main/res/layout/fragment_scrap_detail_picture.xml b/presentation/src/main/res/layout/fragment_scrap_detail_picture.xml index 2dc835f7..6127b26c 100644 --- a/presentation/src/main/res/layout/fragment_scrap_detail_picture.xml +++ b/presentation/src/main/res/layout/fragment_scrap_detail_picture.xml @@ -11,6 +11,7 @@ android:layout_height="wrap_content" android:background="@android:color/transparent" android:elevation="4dp" + android:text="" android:textColor="@color/color_foreground_white" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" @@ -21,8 +22,7 @@ app:navigationIcon="@drawable/ic_chevron_left_big" app:navigationIconColor="@color/color_foreground_white" app:navigationIconSize="24" - app:titleTextAppearance="@style/TextAppearance.Spot.Label03" - android:text=""/> + app:titleTextAppearance="@style/TextAppearance.Spot.Label03" /> + + \ No newline at end of file diff --git a/presentation/src/main/res/layout/item_scrap_detail.xml b/presentation/src/main/res/layout/item_scrap_detail.xml index 8000318a..b6ddfec4 100644 --- a/presentation/src/main/res/layout/item_scrap_detail.xml +++ b/presentation/src/main/res/layout/item_scrap_detail.xml @@ -48,8 +48,10 @@ android:orientation="horizontal" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_bias="0.499" /> + +