diff --git a/app/src/main/java/com/poti/android/data/mapper/party/ProductPartyListMapper.kt b/app/src/main/java/com/poti/android/data/mapper/party/ProductPartyListMapper.kt new file mode 100644 index 00000000..5c991292 --- /dev/null +++ b/app/src/main/java/com/poti/android/data/mapper/party/ProductPartyListMapper.kt @@ -0,0 +1,26 @@ +package com.poti.android.data.mapper.party + +import com.poti.android.data.remote.dto.response.party.PartyDto +import com.poti.android.data.remote.dto.response.party.ProductPartyListResponseDto +import com.poti.android.domain.model.party.PartySummary +import com.poti.android.domain.model.party.ProductPartyList + +fun ProductPartyListResponseDto.toDomain(): ProductPartyList = + ProductPartyList( + partyTitle = postTitle, + artistName = artist, + partySummaries = pots.map { it.toDomain() }, + ) + +private fun PartyDto.toDomain(): PartySummary = + PartySummary( + partyId = potId, + price = price, + goodsImageUrl = thumbnailUrl.orEmpty(), + currentCount = currentCount, + totalCount = totalCount, + availableMembers = availableMembers, + profileImageUrl = uploader.profileImage, + nickname = uploader.nickname, + rating = uploader.rating, + ) diff --git a/app/src/main/java/com/poti/android/data/remote/datasource/PartyRemoteDataSource.kt b/app/src/main/java/com/poti/android/data/remote/datasource/PartyRemoteDataSource.kt index d1e8286a..c0355e71 100644 --- a/app/src/main/java/com/poti/android/data/remote/datasource/PartyRemoteDataSource.kt +++ b/app/src/main/java/com/poti/android/data/remote/datasource/PartyRemoteDataSource.kt @@ -11,6 +11,7 @@ import com.poti.android.data.remote.dto.response.party.MyRecruitListDto import com.poti.android.data.remote.dto.response.party.PartyDetailResponseDto import com.poti.android.data.remote.dto.response.party.PartyJoinOptionsDto import com.poti.android.data.remote.dto.response.party.PartyJoinResponseDto +import com.poti.android.data.remote.dto.response.party.ProductPartyListResponseDto import com.poti.android.data.remote.dto.response.party.ProductSearchResponseDto import com.poti.android.data.remote.dto.response.party.ShippingOptionResponseDto import com.poti.android.data.remote.service.PartyService @@ -51,4 +52,21 @@ class PartyRemoteDataSource @Inject constructor( suspend fun getRecruitPostParticipant(postId: Long): BaseResponse = partyService.getRecruitPostParticipant(postId) + + suspend fun getProductPartyList( + page: Int?, + size: Int?, + title: String, + artistId: Long, + sort: String, + memberIds: List?, + ): BaseResponse = + partyService.getProductPartyList( + page = page, + size = size, + title = title, + artistId = artistId, + sort = sort, + memberIds = memberIds, + ) } diff --git a/app/src/main/java/com/poti/android/data/remote/dto/response/party/ProductPartyListResponseDto.kt b/app/src/main/java/com/poti/android/data/remote/dto/response/party/ProductPartyListResponseDto.kt new file mode 100644 index 00000000..253cf331 --- /dev/null +++ b/app/src/main/java/com/poti/android/data/remote/dto/response/party/ProductPartyListResponseDto.kt @@ -0,0 +1,52 @@ +package com.poti.android.data.remote.dto.response.party + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class ProductPartyListResponseDto( + @SerialName("postTitle") + val postTitle: String, + @SerialName("artistId") + val artistId: Long, + @SerialName("artist") + val artist: String, + @SerialName("currentPage") + val currentPage: Int, + @SerialName("hasNext") + val hasNext: Boolean, + @SerialName("pots") + val pots: List, +) + +@Serializable +data class PartyDto( + @SerialName("potId") + val potId: Long, + @SerialName("price") + val price: Int, + @SerialName("thumbnailUrl") + val thumbnailUrl: String?, + @SerialName("currentCount") + val currentCount: Int, + @SerialName("totalCount") + val totalCount: Int, + @SerialName("status") + val status: String, + @SerialName("availableMembers") + val availableMembers: List, + @SerialName("uploader") + val uploader: PartyUploaderDto, +) + +@Serializable +data class PartyUploaderDto( + @SerialName("userId") + val userId: Long, + @SerialName("nickname") + val nickname: String, + @SerialName("profileImage") + val profileImage: String?, + @SerialName("rating") + val rating: Double, +) diff --git a/app/src/main/java/com/poti/android/data/remote/service/PartyService.kt b/app/src/main/java/com/poti/android/data/remote/service/PartyService.kt index 6f7b7e45..ba6a4456 100644 --- a/app/src/main/java/com/poti/android/data/remote/service/PartyService.kt +++ b/app/src/main/java/com/poti/android/data/remote/service/PartyService.kt @@ -11,6 +11,7 @@ import com.poti.android.data.remote.dto.response.party.MyRecruitListDto import com.poti.android.data.remote.dto.response.party.PartyDetailResponseDto import com.poti.android.data.remote.dto.response.party.PartyJoinOptionsDto import com.poti.android.data.remote.dto.response.party.PartyJoinResponseDto +import com.poti.android.data.remote.dto.response.party.ProductPartyListResponseDto import com.poti.android.data.remote.dto.response.party.ProductSearchResponseDto import com.poti.android.data.remote.dto.response.party.ShippingOptionResponseDto import retrofit2.http.Body @@ -68,4 +69,14 @@ interface PartyService { suspend fun getRecruitPostParticipant( @Path("postId") postId: Long, ): BaseResponse + + @GET("/api/v1/posts/pots") + suspend fun getProductPartyList( + @Query("page") page: Int?, + @Query("size") size: Int?, + @Query("title") title: String, + @Query("artistId") artistId: Long, + @Query("sort") sort: String, + @Query("memberIds") memberIds: List?, + ): BaseResponse } diff --git a/app/src/main/java/com/poti/android/data/repository/PartyRepositoryImpl.kt b/app/src/main/java/com/poti/android/data/repository/PartyRepositoryImpl.kt index 32f73a17..a35fd46d 100644 --- a/app/src/main/java/com/poti/android/data/repository/PartyRepositoryImpl.kt +++ b/app/src/main/java/com/poti/android/data/repository/PartyRepositoryImpl.kt @@ -20,6 +20,7 @@ import com.poti.android.domain.model.history.RecruiterDetail import com.poti.android.domain.model.party.PartyDetail import com.poti.android.domain.model.party.PartyJoinInfo import com.poti.android.domain.model.party.PartyJoinOption +import com.poti.android.domain.model.party.ProductPartyList import com.poti.android.domain.repository.PartyRepository import javax.inject.Inject @@ -124,4 +125,25 @@ class PartyRepositoryImpl @Inject constructor( .getOrThrow() .toDomain() } + + override suspend fun getProductPartyList( + page: Int?, + size: Int?, + title: String, + artistId: Long, + sort: String, + memberIds: List?, + ): Result = httpResponseHandler.safeApiCall { + partyRemoteDataSource.getProductPartyList( + page = page, + size = size, + title = title, + artistId = artistId, + sort = sort, + memberIds = memberIds, + ) + .handleApiResponse() + .getOrThrow() + .toDomain() + } } diff --git a/app/src/main/java/com/poti/android/domain/model/party/PartyList.kt b/app/src/main/java/com/poti/android/domain/model/party/ProductPartyList.kt similarity index 77% rename from app/src/main/java/com/poti/android/domain/model/party/PartyList.kt rename to app/src/main/java/com/poti/android/domain/model/party/ProductPartyList.kt index ff2ba476..6c1102e4 100644 --- a/app/src/main/java/com/poti/android/domain/model/party/PartyList.kt +++ b/app/src/main/java/com/poti/android/domain/model/party/ProductPartyList.kt @@ -1,8 +1,6 @@ package com.poti.android.domain.model.party -import com.poti.android.domain.model.artist.Member - -data class PartyList( +data class ProductPartyList( val partyTitle: String, val artistName: String, val partySummaries: List, @@ -14,7 +12,7 @@ data class PartySummary( val goodsImageUrl: String, val currentCount: Int, val totalCount: Int, - val availableMembers: List, + val availableMembers: List, val profileImageUrl: String?, val nickname: String, val rating: Double, diff --git a/app/src/main/java/com/poti/android/domain/repository/PartyRepository.kt b/app/src/main/java/com/poti/android/domain/repository/PartyRepository.kt index b6bb74ae..ddda0475 100644 --- a/app/src/main/java/com/poti/android/domain/repository/PartyRepository.kt +++ b/app/src/main/java/com/poti/android/domain/repository/PartyRepository.kt @@ -9,6 +9,7 @@ import com.poti.android.domain.model.history.RecruiterDetail import com.poti.android.domain.model.party.PartyDetail import com.poti.android.domain.model.party.PartyJoinInfo import com.poti.android.domain.model.party.PartyJoinOption +import com.poti.android.domain.model.party.ProductPartyList interface PartyRepository { suspend fun searchProductTitle( @@ -45,4 +46,13 @@ interface PartyRepository { suspend fun getRecruitDetail(postId: Long): Result suspend fun getRecruitPostParticipant(postId: Long): Result + + suspend fun getProductPartyList( + page: Int?, + size: Int?, + title: String, + artistId: Long, + sort: String, + memberIds: List?, + ): Result } diff --git a/app/src/main/java/com/poti/android/presentation/party/goodsfilter/DummyPotsDate.kt b/app/src/main/java/com/poti/android/presentation/party/goodsfilter/DummyPotsDate.kt index 3252828f..7cec2ba7 100644 --- a/app/src/main/java/com/poti/android/presentation/party/goodsfilter/DummyPotsDate.kt +++ b/app/src/main/java/com/poti/android/presentation/party/goodsfilter/DummyPotsDate.kt @@ -1,10 +1,9 @@ package com.poti.android.presentation.party.goodsfilter -import com.poti.android.domain.model.artist.Member -import com.poti.android.domain.model.party.PartyList import com.poti.android.domain.model.party.PartySummary +import com.poti.android.domain.model.party.ProductPartyList -val dummyPartyList = PartyList( +val dummyProductPartyList = ProductPartyList( partyTitle = "러브다이브 위드뮤", artistName = "IVE(아이브)", partySummaries = listOf( @@ -14,10 +13,7 @@ val dummyPartyList = PartyList( goodsImageUrl = "", currentCount = 3, totalCount = 5, - availableMembers = listOf( - Member(1, "원영"), - Member(2, "유진"), - ), + availableMembers = listOf("원영", "유진"), profileImageUrl = "", nickname = "포티공주", rating = 4.8, @@ -28,10 +24,7 @@ val dummyPartyList = PartyList( goodsImageUrl = "", currentCount = 6, totalCount = 6, - availableMembers = listOf( - Member(1, "원영"), - Member(2, "유진"), - ), + availableMembers = listOf("원영", "유진"), profileImageUrl = "", nickname = "굿즈요정", rating = 4.5, @@ -42,10 +35,7 @@ val dummyPartyList = PartyList( goodsImageUrl = "", currentCount = 1, totalCount = 4, - availableMembers = listOf( - Member(1, "가을"), - Member(2, "이서"), - ), + availableMembers = listOf("원영", "유진"), profileImageUrl = "", nickname = "공구마스터", rating = 5.0, diff --git a/app/src/main/java/com/poti/android/presentation/party/goodsfilter/GoodsCategoryScreen.kt b/app/src/main/java/com/poti/android/presentation/party/goodsfilter/GoodsCategoryScreen.kt index 08b437fc..2fdb9a02 100644 --- a/app/src/main/java/com/poti/android/presentation/party/goodsfilter/GoodsCategoryScreen.kt +++ b/app/src/main/java/com/poti/android/presentation/party/goodsfilter/GoodsCategoryScreen.kt @@ -38,7 +38,7 @@ fun GoodsCategoryRoute( artistId: Long, onPopBackStack: () -> Unit, onNavigateToPartyCreate: () -> Unit, - onNavigateToGoodsPartyList: (Long) -> Unit, + onNavigateToGoodsPartyList: (Long, String) -> Unit, modifier: Modifier = Modifier, viewModel: GoodsCategoryViewModel = hiltViewModel(), ) { @@ -48,7 +48,7 @@ fun GoodsCategoryRoute( when (effect) { GoodsCategoryUiEffect.NavigateBack -> onPopBackStack() GoodsCategoryUiEffect.NavigateToPartyCreate -> onNavigateToPartyCreate() - GoodsCategoryUiEffect.NavigateToGoodsFilter -> onNavigateToGoodsPartyList(artistId) + is GoodsCategoryUiEffect.NavigateToGoodsPartyList -> onNavigateToGoodsPartyList(artistId, effect.title) } } @@ -72,8 +72,8 @@ fun GoodsCategoryRoute( onSortDismiss = { viewModel.processIntent(GoodsCategoryUiIntent.OnSortDismiss) }, - onCardClick = { - viewModel.processIntent(GoodsCategoryUiIntent.OnCardClick) + onCardClick = { artistId, title -> + viewModel.processIntent(GoodsCategoryUiIntent.OnCardClick(artistId, title)) }, modifier = modifier, ) @@ -90,7 +90,7 @@ private fun GoodsCategoryScreen( onSortFilterClick: () -> Unit, onSortSelect: (GoodsSortType) -> Unit, onSortDismiss: () -> Unit, - onCardClick: () -> Unit, + onCardClick: (Long, String) -> Unit, modifier: Modifier = Modifier, ) { if (isSortBottomSheetVisible) { @@ -139,10 +139,11 @@ private fun GoodsCategoryScreen( title = groupItem.postTitle, partyCount = groupItem.postCount, tag = groupItem.tag, - onClick = onCardClick, // TODO: [예림] 굿즈별 페이지로 이동; 타이틀, 아티스트 아이디 + onClick = { id, title -> onCardClick(id, title) }, modifier = Modifier .fillMaxWidth() .padding(bottom = 16.dp), + artistId = groupItem.artistId, ) } item { @@ -165,7 +166,7 @@ private fun GoodsCategoryScreenPreview() { onSortFilterClick = {}, onSortSelect = {}, onSortDismiss = {}, - onCardClick = {}, + onCardClick = { _, _ -> }, ) } } diff --git a/app/src/main/java/com/poti/android/presentation/party/goodsfilter/GoodsCategoryViewModel.kt b/app/src/main/java/com/poti/android/presentation/party/goodsfilter/GoodsCategoryViewModel.kt index 9037f074..1dcfe36c 100644 --- a/app/src/main/java/com/poti/android/presentation/party/goodsfilter/GoodsCategoryViewModel.kt +++ b/app/src/main/java/com/poti/android/presentation/party/goodsfilter/GoodsCategoryViewModel.kt @@ -23,16 +23,9 @@ class GoodsCategoryViewModel @Inject constructor( override fun processIntent(intent: GoodsCategoryUiIntent) { when (intent) { - GoodsCategoryUiIntent.OnBackClick -> - sendEffect(GoodsCategoryUiEffect.NavigateBack) - - GoodsCategoryUiIntent.OnFloatingClick -> - sendEffect(GoodsCategoryUiEffect.NavigateToPartyCreate) - - GoodsCategoryUiIntent.OnSortFilterClick -> { - updateState { copy(isSortBottomSheetVisible = true) } - } - + GoodsCategoryUiIntent.OnBackClick -> sendEffect(GoodsCategoryUiEffect.NavigateBack) + GoodsCategoryUiIntent.OnFloatingClick -> sendEffect(GoodsCategoryUiEffect.NavigateToPartyCreate) + GoodsCategoryUiIntent.OnSortFilterClick -> updateState { copy(isSortBottomSheetVisible = true) } is GoodsCategoryUiIntent.OnSortSelected -> { updateState { copy( @@ -42,13 +35,8 @@ class GoodsCategoryViewModel @Inject constructor( } loadGoodsCategoryList(intent.sortType) } - - GoodsCategoryUiIntent.OnSortDismiss -> { - updateState { copy(isSortBottomSheetVisible = false) } - } - - GoodsCategoryUiIntent.OnCardClick -> - sendEffect(GoodsCategoryUiEffect.NavigateToGoodsFilter) + GoodsCategoryUiIntent.OnSortDismiss -> updateState { copy(isSortBottomSheetVisible = false) } + is GoodsCategoryUiIntent.OnCardClick -> sendEffect(GoodsCategoryUiEffect.NavigateToGoodsPartyList(intent.artistId, intent.title)) } } diff --git a/app/src/main/java/com/poti/android/presentation/party/goodsfilter/GoodsFilterViewModel.kt b/app/src/main/java/com/poti/android/presentation/party/goodsfilter/GoodsFilterViewModel.kt index 4cb1554f..1af1e74a 100644 --- a/app/src/main/java/com/poti/android/presentation/party/goodsfilter/GoodsFilterViewModel.kt +++ b/app/src/main/java/com/poti/android/presentation/party/goodsfilter/GoodsFilterViewModel.kt @@ -1,50 +1,160 @@ package com.poti.android.presentation.party.goodsfilter +import androidx.lifecycle.SavedStateHandle +import androidx.navigation.toRoute import com.poti.android.core.base.BaseViewModel import com.poti.android.core.common.state.ApiState +import com.poti.android.domain.repository.ArtistRepository +import com.poti.android.domain.repository.PartyRepository import com.poti.android.presentation.party.goodsfilter.model.GoodsFilterUiEffect import com.poti.android.presentation.party.goodsfilter.model.GoodsFilterUiIntent import com.poti.android.presentation.party.goodsfilter.model.GoodsFilterUiState +import com.poti.android.presentation.party.goodsfilter.navigation.GoodsRoute.GoodsPartyList import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject @HiltViewModel -class GoodsFilterViewModel @Inject constructor() : - BaseViewModel( +class GoodsFilterViewModel @Inject constructor( + private val artistRepository: ArtistRepository, + private val partyRepository: PartyRepository, + savedStateHandle: SavedStateHandle, +) : BaseViewModel( initialState = GoodsFilterUiState(), ) { - init { - loadGoodsPots() - } + private val artistId: Long = savedStateHandle.toRoute().artistId + private val title: String = savedStateHandle.toRoute().title - override fun processIntent(intent: GoodsFilterUiIntent) { - when (intent) { - GoodsFilterUiIntent.LoadGoodsPots -> loadGoodsPots() - GoodsFilterUiIntent.OnBackClick -> sendEffect(GoodsFilterUiEffect.NavigateBack) - GoodsFilterUiIntent.OnFloatingClick -> sendEffect(GoodsFilterUiEffect.NavigateToPartyCreate) - is GoodsFilterUiIntent.OnPartyClick -> - sendEffect(GoodsFilterUiEffect.NavigateToPartyDetail(intent.partyId)) - GoodsFilterUiIntent.OnMemberFilterClick -> { - // TODO: [예림] 바텀시트 open - } - is GoodsFilterUiIntent.OnMembersSelect -> { - updateState { copy(selectedMember = intent.members) } - } - GoodsFilterUiIntent.OnSortFilterClick -> { - // TODO: [예림] 바텀시트 open - } - is GoodsFilterUiIntent.OnSortSelect -> { - updateState { copy(goodsSortFilter = intent.sort) } + init { + fetchArtistMembers() + loadPartyList() + } + + override fun processIntent(intent: GoodsFilterUiIntent) { + when (intent) { + GoodsFilterUiIntent.LoadGoodsPots -> loadPartyList() + GoodsFilterUiIntent.OnBackClick -> sendEffect(GoodsFilterUiEffect.NavigateBack) + GoodsFilterUiIntent.OnFloatingClick -> sendEffect(GoodsFilterUiEffect.NavigateToPartyCreate) + is GoodsFilterUiIntent.OnPartyClick -> sendEffect(GoodsFilterUiEffect.NavigateToPartyDetail(intent.partyId)) + GoodsFilterUiIntent.OnMemberFilterClick -> { + refreshMemberSelectBottomSheet() + updateState { copy(isMemberFilterBottomSheetVisible = true) } + } + + is GoodsFilterUiIntent.OnMemberSelect -> { + onBottomSheetMemberChanged(intent.index) + } + + GoodsFilterUiIntent.OnSortFilterClick -> { + updateState { copy(isSortFilterBottomSheetVisible = true) } + } + + GoodsFilterUiIntent.CloseSortFilterBottomSheet -> { + updateState { copy(isSortFilterBottomSheetVisible = false) } + } + + is GoodsFilterUiIntent.OnSortSelect -> { + updateState { copy(goodsPartySortType = intent.sort, isSortFilterBottomSheetVisible = false) } + loadPartyList() + } + + GoodsFilterUiIntent.CloseMemberFilterBottomSheet -> updateState { copy(isMemberFilterBottomSheetVisible = false) } + + GoodsFilterUiIntent.OnMemberFilterDone -> saveSelectedMember() + + GoodsFilterUiIntent.OnMemberFilterRefresh -> { + if (uiState.value.bottomSheetSelectedMembersIdices.isNotEmpty()) { + clearSelectedMembers() } } } + } + + private fun loadPartyList() = launchScope { + val currentState = uiState.value + val sort = currentState.goodsPartySortType.request + val memberIds = if (currentState.selectedMembers.isNotEmpty()) { + currentState.selectedMembers.map { it.memberId } + } else { + null + } - private fun loadGoodsPots() = launchScope { + updateState { copy(productPartyListInfo = ApiState.Loading) } + + partyRepository.getProductPartyList( + page = 0, + size = 10, + title = title, + artistId = artistId, + sort = sort, + memberIds = memberIds, + ).onSuccess { partyList -> + updateState { + copy( + productPartyListInfo = ApiState.Success(partyList), + cachedTitle = partyList.partyTitle, + cachedSubTitle = partyList.artistName, + ) + } + }.onFailure { throwable -> updateState { copy( - partyListInfo = ApiState.Success(dummyPartyList), - membersLoadState = ApiState.Success(emptyList()), + productPartyListInfo = ApiState.Failure( + throwable.message ?: "Failed", + ), ) } } } + + private fun fetchArtistMembers() = launchScope { + updateState { copy(membersLoadState = ApiState.Loading) } + + artistRepository.getMemberList(artistId) + .onSuccess { + updateState { + copy( + membersLoadState = ApiState.Success(it), + displayMembers = it, + ) + } + } + .onFailure { e -> + updateState { copy(membersLoadState = ApiState.Failure(e.message ?: "Failed")) } + } + } + + private fun refreshMemberSelectBottomSheet() { + val newSelectedIndices = uiState.value.displayMembers.mapIndexed { index, member -> + if (member in uiState.value.selectedMembers) index else null + } + .filterNotNull() + .toSet() + + updateState { + copy( + bottomSheetSelectedMembersIdices = newSelectedIndices, + isMemberBottomSheetToucehd = false, + ) + } + } + + private fun clearSelectedMembers() { + updateState { copy(bottomSheetSelectedMembersIdices = setOf(), isMemberBottomSheetToucehd = true) } + } + + private fun saveSelectedMember() { + val newSelectedMembers = uiState.value.displayMembers.mapIndexedNotNull { index, member -> + if (index in uiState.value.bottomSheetSelectedMembersIdices) member else null + } + + updateState { copy(selectedMembers = newSelectedMembers) } + + loadPartyList() + } + + private fun onBottomSheetMemberChanged(index: Int) { + val selectedIndices = uiState.value.bottomSheetSelectedMembersIdices + val newSelectedIndices = if (index in selectedIndices) selectedIndices - index else selectedIndices + index + updateState { copy(bottomSheetSelectedMembersIdices = newSelectedIndices, isMemberBottomSheetToucehd = true) } + } +} diff --git a/app/src/main/java/com/poti/android/presentation/party/goodsfilter/GoodsFilteredPartyListScreen.kt b/app/src/main/java/com/poti/android/presentation/party/goodsfilter/GoodsFilteredPartyListScreen.kt index 0d237457..f1ec8a1b 100644 --- a/app/src/main/java/com/poti/android/presentation/party/goodsfilter/GoodsFilteredPartyListScreen.kt +++ b/app/src/main/java/com/poti/android/presentation/party/goodsfilter/GoodsFilteredPartyListScreen.kt @@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding @@ -18,20 +19,22 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.poti.android.core.common.extension.onSuccess +import com.poti.android.R +import com.poti.android.core.common.extension.getSuccessDataOrNull import com.poti.android.core.common.util.HandleSideEffects import com.poti.android.core.common.util.screenWidthDp +import com.poti.android.core.designsystem.component.bottomsheet.MemberSelectBottomSheet import com.poti.android.core.designsystem.component.button.PotiFloatingButton import com.poti.android.core.designsystem.component.button.PotiSmallButton import com.poti.android.core.designsystem.component.navigation.PotiHeaderPage import com.poti.android.core.designsystem.theme.PotiTheme -import com.poti.android.domain.model.artist.Member -import com.poti.android.domain.model.party.PartyList import com.poti.android.domain.model.party.PartySummary +import com.poti.android.domain.model.party.ProductPartyList +import com.poti.android.presentation.party.goodsfilter.component.FilteredSortBottomSheet import com.poti.android.presentation.party.goodsfilter.component.PartyCard +import com.poti.android.presentation.party.goodsfilter.model.FilteredSortType import com.poti.android.presentation.party.goodsfilter.model.GoodsFilterUiEffect import com.poti.android.presentation.party.goodsfilter.model.GoodsFilterUiIntent -import com.poti.android.presentation.party.goodsfilter.model.SortFilter import com.poti.android.presentation.party.goodsfilter.model.membersText import com.poti.android.presentation.party.goodsfilter.model.priceText import com.poti.android.presentation.party.goodsfilter.model.ratingText @@ -55,39 +58,62 @@ fun GoodsFilteredPartyListRoute( } } - uiState.partyListInfo.onSuccess { potsInfo -> - GoodsFilteredPartyListScreen( - partyListInfo = potsInfo, - displayMembers = uiState.displayMembers, - selectedMember = uiState.selectedMember, - sortFilter = uiState.goodsSortFilter, - memberFilterText = uiState.memberFilterText, - onBackClick = { - viewModel.processIntent(GoodsFilterUiIntent.OnBackClick) - }, - onFloatingClick = { - viewModel.processIntent(GoodsFilterUiIntent.OnFloatingClick) - }, - onMemberFilterClick = { - viewModel.processIntent(GoodsFilterUiIntent.OnMemberFilterClick) - }, - onSortFilterClick = { - viewModel.processIntent(GoodsFilterUiIntent.OnSortFilterClick) - }, - onCardClick = { potId -> - viewModel.processIntent(GoodsFilterUiIntent.OnPartyClick(potId)) - }, - modifier = modifier, + if (uiState.isMemberFilterBottomSheetVisible) { + MemberSelectBottomSheet( + title = R.string.goods_filter_member_select_label, + onDismiss = { viewModel.processIntent(GoodsFilterUiIntent.CloseMemberFilterBottomSheet) }, + mainBtnText = R.string.action_button_done, + onMainBtnClick = { viewModel.processIntent(GoodsFilterUiIntent.OnMemberFilterDone) }, + mainEnabled = uiState.isMemberBottomSheetToucehd, + subBtnText = R.string.action_button_refresh, + onSubBtnClick = { viewModel.processIntent(GoodsFilterUiIntent.OnMemberFilterRefresh) }, + subEnabled = true, + members = uiState.allMemberNames, + selectedIndices = uiState.bottomSheetSelectedMembersIdices, + onMemberClick = { viewModel.processIntent(GoodsFilterUiIntent.OnMemberSelect(it)) }, + autoCloseSubBtn = false, ) } + + if (uiState.isSortFilterBottomSheetVisible) { + FilteredSortBottomSheet( + selectedSortType = uiState.goodsPartySortType, + onSelect = { viewModel.processIntent(GoodsFilterUiIntent.OnSortSelect(it)) }, + onDismissRequest = { viewModel.processIntent(GoodsFilterUiIntent.CloseSortFilterBottomSheet) }, + ) + } + + GoodsFilteredPartyListScreen( + productPartyListInfo = uiState.productPartyListInfo.getSuccessDataOrNull() ?: ProductPartyList( + partyTitle = uiState.cachedTitle, + artistName = uiState.cachedSubTitle, + partySummaries = emptyList(), + ), + partySortType = uiState.goodsPartySortType, + memberFilterText = uiState.memberFilterText, + onBackClick = { + viewModel.processIntent(GoodsFilterUiIntent.OnBackClick) + }, + onFloatingClick = { + viewModel.processIntent(GoodsFilterUiIntent.OnFloatingClick) + }, + onMemberFilterClick = { + viewModel.processIntent(GoodsFilterUiIntent.OnMemberFilterClick) + }, + onSortFilterClick = { + viewModel.processIntent(GoodsFilterUiIntent.OnSortFilterClick) + }, + onCardClick = { potId -> + viewModel.processIntent(GoodsFilterUiIntent.OnPartyClick(potId)) + }, + modifier = modifier, + ) } @Composable private fun GoodsFilteredPartyListScreen( - partyListInfo: PartyList, - displayMembers: List, - selectedMember: List, - sortFilter: SortFilter, + productPartyListInfo: ProductPartyList, + partySortType: FilteredSortType, memberFilterText: String, onBackClick: () -> Unit, onFloatingClick: () -> Unit, @@ -99,11 +125,12 @@ private fun GoodsFilteredPartyListScreen( Scaffold( modifier = modifier, containerColor = PotiTheme.colors.white, + contentWindowInsets = WindowInsets(0), topBar = { PotiHeaderPage( onNavigationClick = onBackClick, - title = partyListInfo.partyTitle, - subTitle = partyListInfo.artistName, + title = productPartyListInfo.partyTitle, + subTitle = productPartyListInfo.artistName, ) }, floatingActionButton = { @@ -129,13 +156,13 @@ private fun GoodsFilteredPartyListScreen( ) PotiSmallButton( - text = stringResource(sortFilter.displayRes), + text = stringResource(partySortType.displayRes), onClick = onSortFilterClick, ) } } - items(partyListInfo.partySummaries) { party -> + items(productPartyListInfo.partySummaries) { party -> PartyCard( potId = party.partyId, profileImageUrl = party.profileImageUrl ?: "", @@ -164,7 +191,7 @@ private fun GoodsFilteredPartyListScreen( @Composable private fun GoodsFilteredPartyListScreenPreveiw() { GoodsFilteredPartyListScreen( - partyListInfo = PartyList( + productPartyListInfo = ProductPartyList( partyTitle = "헤더 타이틀", artistName = "서브타이틀", partySummaries = listOf( @@ -174,10 +201,7 @@ private fun GoodsFilteredPartyListScreenPreveiw() { goodsImageUrl = "", currentCount = 5, totalCount = 7, - availableMembers = listOf( - Member(1, "원영"), - Member(2, "유진"), - ), + availableMembers = listOf("원영", "유진", "이서"), profileImageUrl = "", nickname = "닉네임", rating = 1.2, @@ -188,19 +212,14 @@ private fun GoodsFilteredPartyListScreenPreveiw() { goodsImageUrl = "", currentCount = 6, totalCount = 6, - availableMembers = listOf( - Member(1, "원영"), - Member(2, "유진"), - ), + availableMembers = listOf("원영", "유진"), profileImageUrl = "", nickname = "닉네임", rating = 1.2, ), ), ), - displayMembers = emptyList(), - selectedMember = emptyList(), - sortFilter = SortFilter.LATEST, + partySortType = FilteredSortType.DEADLINE, memberFilterText = "", onBackClick = {}, onFloatingClick = {}, diff --git a/app/src/main/java/com/poti/android/presentation/party/goodsfilter/component/FilteredSortBottomSheet.kt b/app/src/main/java/com/poti/android/presentation/party/goodsfilter/component/FilteredSortBottomSheet.kt new file mode 100644 index 00000000..4f2a7814 --- /dev/null +++ b/app/src/main/java/com/poti/android/presentation/party/goodsfilter/component/FilteredSortBottomSheet.kt @@ -0,0 +1,57 @@ +package com.poti.android.presentation.party.goodsfilter.component + +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.rememberModalBottomSheetState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import com.poti.android.core.common.util.screenWidthDp +import com.poti.android.core.designsystem.component.bottomsheet.PotiBottomSheet +import com.poti.android.core.designsystem.component.display.PotiListRadio +import com.poti.android.presentation.party.goodsfilter.model.FilteredSortType +import kotlinx.collections.immutable.toImmutableList +import kotlinx.coroutines.launch + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun FilteredSortBottomSheet( + selectedSortType: FilteredSortType, + onSelect: (FilteredSortType) -> Unit, + onDismissRequest: () -> Unit, + modifier: Modifier = Modifier, +) { + val sortTypes = remember { FilteredSortType.entries.toList() } + + val options = remember { + sortTypes.map { it.displayRes }.toImmutableList() + } + + val selectedIndex = sortTypes.indexOf(selectedSortType) + val sheetState = rememberModalBottomSheetState() + + val scope = rememberCoroutineScope() + + PotiBottomSheet( + onDismissRequest = onDismissRequest, + sheetState = sheetState, + modifier = modifier.padding(), + ) { + PotiListRadio( + options = options.map { stringResource(it) }.toImmutableList(), + selectedOptionIndex = selectedIndex, + onClick = { index -> + onSelect(sortTypes[index]) + scope.launch { + sheetState.hide() + } + }, + modifier = Modifier + .padding(horizontal = screenWidthDp(16.dp)) + .padding(top = 16.dp, bottom = 48.dp), + ) + } +} diff --git a/app/src/main/java/com/poti/android/presentation/party/goodsfilter/model/Contract.kt b/app/src/main/java/com/poti/android/presentation/party/goodsfilter/model/Contract.kt deleted file mode 100644 index 0f74d0b2..00000000 --- a/app/src/main/java/com/poti/android/presentation/party/goodsfilter/model/Contract.kt +++ /dev/null @@ -1,95 +0,0 @@ -package com.poti.android.presentation.party.goodsfilter.model - -import androidx.annotation.StringRes -import androidx.compose.runtime.Composable -import androidx.compose.ui.res.stringResource -import com.poti.android.R -import com.poti.android.core.base.UiEffect -import com.poti.android.core.base.UiIntent -import com.poti.android.core.base.UiState -import com.poti.android.core.common.state.ApiState -import com.poti.android.domain.model.artist.Member -import com.poti.android.domain.model.party.PartyList - -enum class SortFilter( - val request: String, - @StringRes val displayRes: Int, -) { - LATEST( - request = "LATEST", - displayRes = R.string.goods_filter_sort_latest, - ), - DEADLINE( - request = "DEADLINE", - displayRes = R.string.goods_filter_sort_deadline, - ), - RATING( - request = "RATING", - displayRes = R.string.goods_filter_sort_rating, - ), -} - -data class GoodsFilterUiState( - val partyListInfo: ApiState = ApiState.Loading, - val membersLoadState: ApiState> = ApiState.Loading, - val displayMembers: List = emptyList(), - val selectedMember: List = emptyList(), - val goodsSortFilter: SortFilter = SortFilter.LATEST, -) : UiState { - val memberFilterText: String - @Composable get() = when { - selectedMember.isEmpty() -> stringResource(R.string.goods_filter_member_select) - selectedMember.size == 1 -> selectedMember[0].name - selectedMember.size == 2 -> stringResource( - R.string.goods_filter_member_two_format, - selectedMember[0].name, - selectedMember[1].name, - ) - else -> stringResource( - R.string.goods_filter_member_more_format, - selectedMember[0].name, - selectedMember[1].name, - selectedMember.size - 2, - ) - } - - val loadState: ApiState - get() = when { - partyListInfo is ApiState.Loading && - membersLoadState is ApiState.Loading -> ApiState.Loading - - partyListInfo is ApiState.Failure || - membersLoadState is ApiState.Failure -> ApiState.Failure("") - - partyListInfo is ApiState.Success && - membersLoadState is ApiState.Success -> ApiState.Success(Unit) - - else -> ApiState.Loading - } -} - -sealed interface GoodsFilterUiIntent : UiIntent { - data object LoadGoodsPots : GoodsFilterUiIntent - - data object OnBackClick : GoodsFilterUiIntent - - data object OnFloatingClick : GoodsFilterUiIntent - - data class OnPartyClick(val partyId: Long) : GoodsFilterUiIntent - - data object OnMemberFilterClick : GoodsFilterUiIntent - - data class OnMembersSelect(val members: List) : GoodsFilterUiIntent - - data object OnSortFilterClick : GoodsFilterUiIntent - - data class OnSortSelect(val sort: SortFilter) : GoodsFilterUiIntent -} - -sealed interface GoodsFilterUiEffect : UiEffect { - data object NavigateBack : GoodsFilterUiEffect - - data object NavigateToPartyCreate : GoodsFilterUiEffect - - data class NavigateToPartyDetail(val partyId: Long) : GoodsFilterUiEffect -} diff --git a/app/src/main/java/com/poti/android/presentation/party/goodsfilter/model/Extension.kt b/app/src/main/java/com/poti/android/presentation/party/goodsfilter/model/Extension.kt index fcc7c300..bff20a38 100644 --- a/app/src/main/java/com/poti/android/presentation/party/goodsfilter/model/Extension.kt +++ b/app/src/main/java/com/poti/android/presentation/party/goodsfilter/model/Extension.kt @@ -22,4 +22,4 @@ fun PartySummary.ratingText(): String = fun PartySummary.membersText(): String = availableMembers.joinToString( separator = stringResource(R.string.goods_filter_members_separator), - ) { it.name } + ) { it } diff --git a/app/src/main/java/com/poti/android/presentation/party/goodsfilter/model/GoodsCategoryUiState.kt b/app/src/main/java/com/poti/android/presentation/party/goodsfilter/model/GoodsCategoryUiState.kt index dd780eeb..1e92324a 100644 --- a/app/src/main/java/com/poti/android/presentation/party/goodsfilter/model/GoodsCategoryUiState.kt +++ b/app/src/main/java/com/poti/android/presentation/party/goodsfilter/model/GoodsCategoryUiState.kt @@ -20,10 +20,6 @@ enum class GoodsSortType( request = "HOT", displayRes = R.string.goods_filter_sort_hot, ), - RANDOM( - request = "RANDOM", - displayRes = R.string.goods_filter_sort_random, - ), } data class GoodsCategoryUiState( @@ -43,7 +39,7 @@ sealed interface GoodsCategoryUiIntent : UiIntent { data object OnSortDismiss : GoodsCategoryUiIntent - data object OnCardClick : GoodsCategoryUiIntent + data class OnCardClick(val artistId: Long, val title: String) : GoodsCategoryUiIntent } sealed interface GoodsCategoryUiEffect : UiEffect { @@ -51,5 +47,5 @@ sealed interface GoodsCategoryUiEffect : UiEffect { data object NavigateToPartyCreate : GoodsCategoryUiEffect - data object NavigateToGoodsFilter : GoodsCategoryUiEffect + data class NavigateToGoodsPartyList(val artistId: Long, val title: String) : GoodsCategoryUiEffect } diff --git a/app/src/main/java/com/poti/android/presentation/party/goodsfilter/model/GoodsFilteredContracts.kt b/app/src/main/java/com/poti/android/presentation/party/goodsfilter/model/GoodsFilteredContracts.kt new file mode 100644 index 00000000..d6178515 --- /dev/null +++ b/app/src/main/java/com/poti/android/presentation/party/goodsfilter/model/GoodsFilteredContracts.kt @@ -0,0 +1,96 @@ +package com.poti.android.presentation.party.goodsfilter.model + +import androidx.annotation.StringRes +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import com.poti.android.R +import com.poti.android.core.base.UiEffect +import com.poti.android.core.base.UiIntent +import com.poti.android.core.base.UiState +import com.poti.android.core.common.state.ApiState +import com.poti.android.domain.model.artist.Member +import com.poti.android.domain.model.party.ProductPartyList + +enum class FilteredSortType( + val request: String, + @StringRes val displayRes: Int, +) { + LATEST("LATEST", R.string.goods_filter_sort_latest), + DEADLINE("DEADLINE", R.string.goods_filter_sort_deadline), + RATING("RATING", R.string.goods_filter_sort_rating), +} + +data class GoodsFilterUiState( + val productPartyListInfo: ApiState = ApiState.Loading, + val cachedTitle: String = "", + val cachedSubTitle: String = "", + val membersLoadState: ApiState> = ApiState.Loading, + val displayMembers: List = emptyList(), + val selectedMembers: List = emptyList(), + val goodsPartySortType: FilteredSortType = FilteredSortType.DEADLINE, + val isMemberFilterBottomSheetVisible: Boolean = false, + val isSortFilterBottomSheetVisible: Boolean = false, + val bottomSheetSelectedMembersIdices: Set = setOf(), + val isMemberBottomSheetToucehd: Boolean = false, +) : UiState { + val allMemberNames: List + get() = displayMembers.map { it.name } + + val memberFilterText: String + @Composable get() = when { + selectedMembers.isEmpty() -> + stringResource(R.string.goods_filter_member_select) + + selectedMembers.size == 1 -> + selectedMembers[0].name + + selectedMembers.size == 2 -> + stringResource( + R.string.goods_filter_member_two_format, + selectedMembers[0].name, + selectedMembers[1].name, + ) + + else -> + stringResource( + R.string.goods_filter_member_more_format, + selectedMembers[0].name, + selectedMembers[1].name, + selectedMembers.size - 2, + ) + } +} + +sealed interface GoodsFilterUiIntent : UiIntent { + data object LoadGoodsPots : GoodsFilterUiIntent + + data object OnBackClick : GoodsFilterUiIntent + + data object OnFloatingClick : GoodsFilterUiIntent + + data class OnPartyClick(val partyId: Long) : GoodsFilterUiIntent + + data object OnMemberFilterClick : GoodsFilterUiIntent + + data class OnMemberSelect(val index: Int) : GoodsFilterUiIntent + + data object OnSortFilterClick : GoodsFilterUiIntent + + data class OnSortSelect(val sort: FilteredSortType) : GoodsFilterUiIntent + + data object CloseMemberFilterBottomSheet : GoodsFilterUiIntent + + data object CloseSortFilterBottomSheet : GoodsFilterUiIntent + + data object OnMemberFilterDone : GoodsFilterUiIntent + + data object OnMemberFilterRefresh : GoodsFilterUiIntent +} + +sealed interface GoodsFilterUiEffect : UiEffect { + data object NavigateBack : GoodsFilterUiEffect + + data object NavigateToPartyCreate : GoodsFilterUiEffect + + data class NavigateToPartyDetail(val partyId: Long) : GoodsFilterUiEffect +} diff --git a/app/src/main/java/com/poti/android/presentation/party/goodsfilter/navigation/GoodsNavigation.kt b/app/src/main/java/com/poti/android/presentation/party/goodsfilter/navigation/GoodsNavigation.kt index 0dce262f..9fb7d499 100644 --- a/app/src/main/java/com/poti/android/presentation/party/goodsfilter/navigation/GoodsNavigation.kt +++ b/app/src/main/java/com/poti/android/presentation/party/goodsfilter/navigation/GoodsNavigation.kt @@ -21,6 +21,7 @@ sealed interface GoodsRoute : Route { @Serializable data class GoodsPartyList( val artistId: Long, + val title: String, ) : GoodsRoute @Serializable @@ -33,8 +34,11 @@ fun NavController.navigateToGoodsList() { navigate(GoodsRoute.GoodsList) } -fun NavController.navigateToGoodsPartyList(artistId: Long) { - navigate(GoodsRoute.GoodsPartyList(artistId)) +fun NavController.navigateToGoodsPartyList( + artistId: Long, + title: String, +) { + navigate(GoodsRoute.GoodsPartyList(artistId, title)) } fun NavController.navigateToGoodsCategory(artistId: Long) { diff --git a/app/src/main/java/com/poti/android/presentation/party/home/HomeScreen.kt b/app/src/main/java/com/poti/android/presentation/party/home/HomeScreen.kt index a3ea9b6b..d9b822b0 100644 --- a/app/src/main/java/com/poti/android/presentation/party/home/HomeScreen.kt +++ b/app/src/main/java/com/poti/android/presentation/party/home/HomeScreen.kt @@ -64,7 +64,7 @@ val fakeMyGroupItems = listOf( fun HomeRoute( onNavigateToPartyCreate: () -> Unit, onNavigateToPartyDetail: (Long) -> Unit, - onNavigateToGoodsPartyList: (Long) -> Unit, + onNavigateToGoodsPartyList: (Long, String) -> Unit, onNavigateToGoodsCategory: (Long) -> Unit, modifier: Modifier = Modifier, viewModel: HomeViewModel = hiltViewModel(), @@ -75,7 +75,7 @@ fun HomeRoute( when (effect) { HomeUiEffect.NavigateToPartyCreate -> onNavigateToPartyCreate() is HomeUiEffect.NavigateToPartyDetail -> onNavigateToPartyDetail(effect.postId) - is HomeUiEffect.NavigateToGoodsPartyList -> onNavigateToGoodsPartyList(effect.artistId) + is HomeUiEffect.NavigateToGoodsPartyList -> onNavigateToGoodsPartyList(effect.artistId, effect.title) is HomeUiEffect.NavigateToGoodsCategory -> onNavigateToGoodsCategory(effect.artistId) } } @@ -90,8 +90,8 @@ fun HomeRoute( onMoreClick = { artistId -> viewModel.processIntent(HomeUiIntent.OnMoreClick(artistId)) }, - onCardClick = { artistId -> - viewModel.processIntent(HomeUiIntent.OnCardClick(artistId)) + onCardClick = { artistId, title -> + viewModel.processIntent(HomeUiIntent.OnCardClick(artistId, title)) }, modifier = modifier, ) @@ -104,7 +104,7 @@ private fun HomeScreen( onFloatingClick: () -> Unit, onBannerClick: (Long) -> Unit, onMoreClick: (Long) -> Unit, - onCardClick: (Long) -> Unit, + onCardClick: (Long, String) -> Unit, modifier: Modifier = Modifier, ) { val scrollState = rememberScrollState() @@ -148,7 +148,7 @@ private fun HomeScreen( Spacer(modifier = Modifier.height(28.dp)) HomeGoodsSection( - artistId = 0L, + artistId = homeContent.mainArtistId, title = R.string.home_other_goods, nickname = homeContent.nickname, groupItems = homeContent.otherGroupItems, @@ -188,7 +188,7 @@ private fun HomeScreenPreview() { onFloatingClick = { }, onBannerClick = { }, onMoreClick = { }, - onCardClick = { }, + onCardClick = { _, _ -> }, ) } } diff --git a/app/src/main/java/com/poti/android/presentation/party/home/HomeViewModel.kt b/app/src/main/java/com/poti/android/presentation/party/home/HomeViewModel.kt index f1e50afc..dc8cff44 100644 --- a/app/src/main/java/com/poti/android/presentation/party/home/HomeViewModel.kt +++ b/app/src/main/java/com/poti/android/presentation/party/home/HomeViewModel.kt @@ -20,7 +20,7 @@ class HomeViewModel @Inject constructor( HomeUiIntent.OnFloatingClick -> sendEffect(HomeUiEffect.NavigateToPartyCreate) is HomeUiIntent.OnBannerClick -> sendEffect(HomeUiEffect.NavigateToPartyDetail(intent.postId)) is HomeUiIntent.OnMoreClick -> sendEffect(HomeUiEffect.NavigateToGoodsCategory(intent.artistId)) - is HomeUiIntent.OnCardClick -> sendEffect(HomeUiEffect.NavigateToGoodsPartyList(intent.artistId)) + is HomeUiIntent.OnCardClick -> sendEffect(HomeUiEffect.NavigateToGoodsPartyList(intent.artistId, intent.title)) HomeUiIntent.LoadHomeContent -> loadHomeContent() } } diff --git a/app/src/main/java/com/poti/android/presentation/party/home/component/GoodsLargeCard.kt b/app/src/main/java/com/poti/android/presentation/party/home/component/GoodsLargeCard.kt index 11c4eadf..01935a17 100644 --- a/app/src/main/java/com/poti/android/presentation/party/home/component/GoodsLargeCard.kt +++ b/app/src/main/java/com/poti/android/presentation/party/home/component/GoodsLargeCard.kt @@ -37,10 +37,11 @@ import com.poti.android.core.designsystem.theme.PotiTheme fun GoodsLargeCard( imageUrl: String, artist: String, + artistId: Long, title: String, partyCount: Int, tag: String?, - onClick: () -> Unit, + onClick: (Long, String) -> Unit, modifier: Modifier = Modifier, ) { Column( @@ -52,7 +53,7 @@ fun GoodsLargeCard( color = PotiTheme.colors.gray300, shape = RoundedCornerShape(12.dp), ) - .noRippleClickable(onClick), + .noRippleClickable { onClick(artistId, title) }, verticalArrangement = Arrangement.Top, horizontalAlignment = Alignment.CenterHorizontally, ) { @@ -136,20 +137,22 @@ private fun GoodsLargeCardPreview() { GoodsLargeCard( imageUrl = "", artist = "아티스트명", + artistId = 0L, title = "상품 종류명", partyCount = 3, tag = "인기", - onClick = {}, + onClick = { _, _ -> }, modifier = Modifier.fillMaxWidth(), ) GoodsLargeCard( imageUrl = "", artist = "아티스트명 ".repeat(10), + artistId = 0L, title = "상품 종류명 ".repeat(10), partyCount = 3, tag = "인기", - onClick = {}, + onClick = { _, _ -> }, modifier = Modifier.fillMaxWidth(), ) } diff --git a/app/src/main/java/com/poti/android/presentation/party/home/component/GoodsSmallCard.kt b/app/src/main/java/com/poti/android/presentation/party/home/component/GoodsSmallCard.kt index d8f126c5..7be3044a 100644 --- a/app/src/main/java/com/poti/android/presentation/party/home/component/GoodsSmallCard.kt +++ b/app/src/main/java/com/poti/android/presentation/party/home/component/GoodsSmallCard.kt @@ -40,10 +40,11 @@ import com.poti.android.core.designsystem.theme.PotiTheme fun GoodsSmallCard( imageUrl: String, artist: String, - goodsType: String, + artistId: Long, + title: String, partyCount: Int, tag: String, - onClick: () -> Unit, + onClick: (Long, String) -> Unit, modifier: Modifier = Modifier, ) { Column( @@ -56,7 +57,7 @@ fun GoodsSmallCard( color = PotiTheme.colors.gray300, shape = RoundedCornerShape(12.dp), ) - .noRippleClickable(onClick), + .noRippleClickable { onClick(artistId, title) }, verticalArrangement = Arrangement.Top, horizontalAlignment = Alignment.Start, ) { @@ -101,7 +102,7 @@ fun GoodsSmallCard( ) Text( - text = goodsType, + text = title, modifier = Modifier.fillMaxWidth(), color = PotiTheme.colors.black, overflow = TextOverflow.Ellipsis, @@ -131,20 +132,22 @@ private fun GoodsSmallCardPreview() { GoodsSmallCard( imageUrl = "", artist = "아티스트명", - goodsType = "상품 종류명", + artistId = 0L, + title = "상품 종류명", partyCount = 3, tag = "인기", - onClick = {}, + onClick = { _, _ -> }, modifier = Modifier, ) GoodsSmallCard( imageUrl = "", artist = "아티스트명 아티스트명 아티스트명 아티스트명 ", - goodsType = "상품 종류명 상품 종류명 상품 종류명 ", + artistId = 0L, + title = "상품 종류명 상품 종류명 상품 종류명 ", partyCount = 3, tag = "인기", - onClick = {}, + onClick = { _, _ -> }, modifier = Modifier, ) } diff --git a/app/src/main/java/com/poti/android/presentation/party/home/component/HomeGoodsSection.kt b/app/src/main/java/com/poti/android/presentation/party/home/component/HomeGoodsSection.kt index d6e41adc..dee28f94 100644 --- a/app/src/main/java/com/poti/android/presentation/party/home/component/HomeGoodsSection.kt +++ b/app/src/main/java/com/poti/android/presentation/party/home/component/HomeGoodsSection.kt @@ -30,7 +30,7 @@ fun HomeGoodsSection( nickname: String, groupItems: List, onMoreClick: (Long) -> Unit, - onCardClick: (Long) -> Unit, + onCardClick: (Long, String) -> Unit, modifier: Modifier = Modifier, ) { Column( @@ -68,10 +68,11 @@ fun HomeGoodsSection( GoodsSmallCard( imageUrl = item.postImage, artist = item.artist, - goodsType = item.postTitle, - partyCount = item.postCount.toInt(), + artistId = item.artistId, + title = item.postTitle, + partyCount = item.postCount, tag = item.tag, - onClick = { onCardClick(artistId) }, + onClick = { id, title -> onCardClick(id, title) }, ) } } @@ -88,7 +89,7 @@ private fun HomeGoodsSectionPreview() { nickname = "포티", groupItems = fakeMyGroupItems, onMoreClick = {}, - onCardClick = {}, + onCardClick = { id, title -> }, ) } } diff --git a/app/src/main/java/com/poti/android/presentation/party/home/model/Contracts.kt b/app/src/main/java/com/poti/android/presentation/party/home/model/Contracts.kt index c68bf6ca..98ac1825 100644 --- a/app/src/main/java/com/poti/android/presentation/party/home/model/Contracts.kt +++ b/app/src/main/java/com/poti/android/presentation/party/home/model/Contracts.kt @@ -16,7 +16,7 @@ sealed interface HomeUiIntent : UiIntent { data class OnMoreClick(val artistId: Long) : HomeUiIntent - data class OnCardClick(val artistId: Long) : HomeUiIntent + data class OnCardClick(val artistId: Long, val title: String) : HomeUiIntent data object OnFloatingClick : HomeUiIntent @@ -30,5 +30,5 @@ sealed interface HomeUiEffect : UiEffect { data class NavigateToGoodsCategory(val artistId: Long) : HomeUiEffect - data class NavigateToGoodsPartyList(val artistId: Long) : HomeUiEffect + data class NavigateToGoodsPartyList(val artistId: Long, val title: String) : HomeUiEffect } diff --git a/app/src/main/java/com/poti/android/presentation/user/mypage/MyPageScreen.kt b/app/src/main/java/com/poti/android/presentation/user/mypage/MyPageScreen.kt index c31541b8..396983cf 100644 --- a/app/src/main/java/com/poti/android/presentation/user/mypage/MyPageScreen.kt +++ b/app/src/main/java/com/poti/android/presentation/user/mypage/MyPageScreen.kt @@ -117,7 +117,7 @@ private fun MyPageScreen( BadgeButton( bias = biasText, - onClick = onArtistClick, + onClick = { }, modifier = Modifier, ) } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 82a3aa18..03959f89 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -18,6 +18,7 @@ 완료 계속 전체 선택 + 초기화 이름 @@ -126,6 +127,16 @@ 멤버 편집 아티스트 검색 검색 결과가 없어요\n다른 키워드로 다시 검색해보세요 + 분철할 그룹을 검색해보세요 + 모집자 안내 사항 + 굿즈 구매 가능 기간을 고려해 분철팟 마감 기한을 설정해주세요. + 마감 기한까지 모집 인원이 과반수 이상 충족되지 않을 경우, 해당 분철팟은 자동으로 종료되며 분철은 진행되지 않습니다. + 분철팟 모집, 굿즈 구매, 배송 및 참여자와의 거래 과정에서 발생하는 사항에 대한 책임은 모집자에게 있습니다. + 지금 나가면 내용이 저장되지 않아요 + 계속 작성할까요? + 나가기 + 계속 작성하기 + 유효한 날짜 형식으로 입력해주세요 모집 중 @@ -218,16 +229,6 @@ %1$s님을 위한 추천 굿즈 다른 굿즈 구경하기 더보기 - 분철할 그룹을 검색해보세요 - 모집자 안내 사항 - 굿즈 구매 가능 기간을 고려해 분철팟 마감 기한을 설정해주세요. - 마감 기한까지 모집 인원이 과반수 이상 충족되지 않을 경우, 해당 분철팟은 자동으로 종료되며 분철은 진행되지 않습니다. - 분철팟 모집, 굿즈 구매, 배송 및 참여자와의 거래 과정에서 발생하는 사항에 대한 책임은 모집자에게 있습니다. - 지금 나가면 내용이 저장되지 않아요 - 계속 작성할까요? - 나가기 - 계속 작성하기 - 유효한 날짜 형식으로 입력해주세요 최신순 @@ -241,6 +242,6 @@ %1$s원~ %.1f  |  - + 멤버 선택