From 0bcd8d73268b446b77c59b845a18f85ddc55b5d9 Mon Sep 17 00:00:00 2001 From: SeongHoonC <108349655+SeongHoonC@users.noreply.github.com> Date: Wed, 6 Mar 2024 13:28:15 +0900 Subject: [PATCH] =?UTF-8?q?[AN/USER]=20feat:=20=EC=B6=95=EC=A0=9C=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20=ED=99=94=EB=A9=B4=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=95=20=EC=9E=91=EC=97=85(#673)=20(#724)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(FakeFestivalRepository): 가짜 축제 저장소를 필터링 작업에 용이하게 변경 * feat(FestivalFilterUiState): FestivalFilterUiState 는 tabPosition 을 가진다 * feat(FestivalTabUiState): selected 람다에 포지션이 아닌 FestivalFilterUiState 를 넘긴다 * feat(FakeFestivalRepository): 가짜 축제 저장소는 예정된 축제를 반환할 수 있다 * feat(FestivalListViewModel): 같은 필터가 아니면 축제 목록을 변경한다 * feat(FestivalListMoreItemViewHolder): more item 프로그래스바 뷰 아이템 생성 * feat(FestivalListAdapter): 축제 목록에 더보기 아이템 추가 * feat(FestivalListAdapter): isLast 가 false 일때만 더보기를 추가한다. * feat(FestivalListViewModel): 현재 축제 목록 불러오기 함수 분리 * feat(FestivalList): Success State recyclerView get items 로직 병합 * feat(FestivalListViewModel): 축제 목록 초기화와 축제 목록 추가 로드를 분리한다 --- .../data/repository/FakeFestivalRepository.kt | 21 +++++-- .../festago/data/repository/FakeFestivals.kt | 10 ++-- .../home/festivallist/FestivalListFragment.kt | 55 ++++++++++++------- .../festivallist/FestivalListViewModel.kt | 53 +++++++++++++++--- .../festival/FestivalListAdapter.kt | 10 +++- .../FestivalListMoreItemViewHolder.kt | 24 ++++++++ .../festival/FestivalListTabViewHolder.kt | 28 ++++++---- .../uistate/FestivalFilterUiState.kt | 4 +- .../uistate/FestivalListUiState.kt | 1 + .../uistate/FestivalMoreItemUiState.kt | 3 + .../uistate/FestivalTabUiState.kt | 3 +- .../layout/item_festival_list_more_item.xml | 19 +++++++ 12 files changed, 179 insertions(+), 52 deletions(-) create mode 100644 android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/festival/FestivalListMoreItemViewHolder.kt create mode 100644 android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/uistate/FestivalMoreItemUiState.kt create mode 100644 android/festago/presentation/src/main/res/layout/item_festival_list_more_item.xml diff --git a/android/festago/data/src/main/java/com/festago/festago/data/repository/FakeFestivalRepository.kt b/android/festago/data/src/main/java/com/festago/festago/data/repository/FakeFestivalRepository.kt index 1831a1e5c..f17408dd5 100644 --- a/android/festago/data/src/main/java/com/festago/festago/data/repository/FakeFestivalRepository.kt +++ b/android/festago/data/src/main/java/com/festago/festago/data/repository/FakeFestivalRepository.kt @@ -14,7 +14,12 @@ import javax.inject.Inject class FakeFestivalRepository @Inject constructor() : FestivalRepository { override suspend fun loadPopularFestivals(): Result { - return Result.success(PopularFestivals("인기 축제 목록", FakeFestivals.popularFestivals)) + return Result.success( + PopularFestivals( + title = "인기 축제 목록", + festivals = FakeFestivals.popularFestivals, + ), + ) } override suspend fun loadFestivals( @@ -27,18 +32,24 @@ class FakeFestivalRepository @Inject constructor() : FestivalRepository { val notNullSize = size ?: DEFAULT_SIZE val notNullLastFestivalId = lastFestivalId ?: DEFAULT_LAST_FESTIVAL_ID + if (festivalFilter == FestivalFilter.PLANNED) { + return Result.success( + FestivalsPage(isLastPage = true, festivals = FakeFestivals.plannedFestivals), + ) + } + if (notNullLastFestivalId + notNullSize < LAST_ITEM_ID) { return Result.success( FestivalsPage( - false, - getFestivals((notNullLastFestivalId + 1)..(notNullLastFestivalId + notNullSize)), + isLastPage = false, + festivals = getFestivals((notNullLastFestivalId + 1)..(notNullLastFestivalId + notNullSize)), ), ) } return Result.success( FestivalsPage( - true, - getFestivals((notNullLastFestivalId + 1)..LAST_ITEM_ID), + isLastPage = true, + festivals = getFestivals((notNullLastFestivalId + 1)..LAST_ITEM_ID), ), ) } diff --git a/android/festago/data/src/main/java/com/festago/festago/data/repository/FakeFestivals.kt b/android/festago/data/src/main/java/com/festago/festago/data/repository/FakeFestivals.kt index 7e0986b3d..53080dbed 100644 --- a/android/festago/data/src/main/java/com/festago/festago/data/repository/FakeFestivals.kt +++ b/android/festago/data/src/main/java/com/festago/festago/data/repository/FakeFestivals.kt @@ -127,7 +127,7 @@ object FakeFestivals { val plannedFestivals = listOf( Festival( - id = 1, + id = 30, name = "뉴진스 콘서트", startDate = LocalDate.now().plusDays(1L), endDate = LocalDate.MAX, @@ -142,7 +142,7 @@ object FakeFestivals { ), ), Festival( - id = 2, + id = 31, name = "아이브 콘서트", startDate = LocalDate.now().plusDays(3L), endDate = LocalDate.MAX, @@ -177,7 +177,7 @@ object FakeFestivals { ), ), Festival( - id = 3, + id = 32, name = "아이들 콘서트", startDate = LocalDate.now().plusDays(5L), endDate = LocalDate.MAX, @@ -192,7 +192,7 @@ object FakeFestivals { ), ), Festival( - id = 4, + id = 33, name = "뉴진스 콘서트", startDate = LocalDate.now().plusDays(20L), endDate = LocalDate.MAX, @@ -207,7 +207,7 @@ object FakeFestivals { ), ), Festival( - id = 5, + id = 34, name = "아이브 콘서트", startDate = LocalDate.now().plusDays(40L), endDate = LocalDate.MAX, diff --git a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/FestivalListFragment.kt b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/FestivalListFragment.kt index d0930335c..38213de80 100644 --- a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/FestivalListFragment.kt +++ b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/FestivalListFragment.kt @@ -12,13 +12,14 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentTransaction import androidx.fragment.app.commit import androidx.fragment.app.viewModels +import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.festago.festago.presentation.R import com.festago.festago.presentation.databinding.FragmentFestivalListBinding import com.festago.festago.presentation.ui.artistdetail.ArtistDetailFragment import com.festago.festago.presentation.ui.home.festivallist.festival.FestivalListAdapter -import com.festago.festago.presentation.ui.home.festivallist.uistate.FestivalFilterUiState import com.festago.festago.presentation.ui.home.festivallist.uistate.FestivalListUiState +import com.festago.festago.presentation.ui.home.festivallist.uistate.FestivalMoreItemUiState import com.festago.festago.presentation.ui.home.festivallist.uistate.FestivalTabUiState import com.festago.festago.presentation.ui.schooldetail.SchoolDetailFragment import com.festago.festago.presentation.util.repeatOnStarted @@ -66,7 +67,7 @@ class FestivalListFragment : Fragment() { } private fun initView() { - vm.loadFestivals() + vm.initFestivalList() initViewPager() initRecyclerView() initRefresh() @@ -74,7 +75,7 @@ class FestivalListFragment : Fragment() { private fun initRefresh() { binding.srlFestivalList.setOnRefreshListener { - vm.loadFestivals() + vm.initFestivalList() binding.srlFestivalList.isRefreshing = false } binding.ivSearch.setOnClickListener { // 임시 연결 @@ -97,6 +98,30 @@ class FestivalListFragment : Fragment() { } private fun initRecyclerView() { + initScrollEvent() + initDecoration() + } + + private fun initScrollEvent() { + binding.rvFestivalList.addOnScrollListener(object : RecyclerView.OnScrollListener() { + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + super.onScrolled(recyclerView, dx, dy) + + val festivalListUiState = vm.uiState.value as? FestivalListUiState.Success ?: return + if (festivalListUiState.isLastPage) return + + val lastVisibleItemPosition = + (recyclerView.layoutManager as LinearLayoutManager?)!!.findLastCompletelyVisibleItemPosition() + + val itemTotalCount = recyclerView.adapter!!.itemCount - 1 + if (lastVisibleItemPosition == itemTotalCount) { + vm.loadFestivals() + } + } + }) + } + + private fun initDecoration() { binding.rvFestivalList.addItemDecoration(object : RecyclerView.ItemDecoration() { override fun getItemOffsets( outRect: Rect, @@ -135,22 +160,14 @@ class FestivalListFragment : Fragment() { } private fun FestivalListUiState.Success.getItems(): List { - val items = mutableListOf() - if (popularFestivalUiState.festivals.isNotEmpty()) { - items.add(popularFestivalUiState) - } - items.add( - FestivalTabUiState { - val festivalFilter = when (it) { - 0 -> FestivalFilterUiState.PROGRESS - 1 -> FestivalFilterUiState.PLANNED - else -> FestivalFilterUiState.PROGRESS - } - vm.loadFestivals(festivalFilter) - }, - ) - items.addAll(festivals) - return items.toList() + return mutableListOf().apply { + if (popularFestivalUiState.festivals.isNotEmpty()) { + add(popularFestivalUiState) + } + add(FestivalTabUiState(festivalFilter) { vm.loadFestivals(it) }) + addAll(festivals) + if (!isLastPage) add(FestivalMoreItemUiState) + }.toList() } private fun showSchoolDetail() { diff --git a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/FestivalListViewModel.kt b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/FestivalListViewModel.kt index fb362b89d..77e194129 100644 --- a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/FestivalListViewModel.kt +++ b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/FestivalListViewModel.kt @@ -32,16 +32,12 @@ class FestivalListViewModel @Inject constructor( private var festivalFilter: FestivalFilter = FestivalFilter.PROGRESS - fun loadFestivals(festivalFilterUiState: FestivalFilterUiState? = null) { + fun initFestivalList() { viewModelScope.launch { - if (festivalFilterUiState != null) { - festivalFilter = festivalFilterUiState.toDomain() - } - val deferredPopularFestivals = async { festivalRepository.loadPopularFestivals() } - val deferredFestivals = - async { festivalRepository.loadFestivals(festivalFilter = festivalFilter) } - + val deferredFestivals = async { + festivalRepository.loadFestivals(festivalFilter = festivalFilter) + } runCatching { val festivalsPage = deferredFestivals.await().getOrThrow() val popularFestivals = deferredPopularFestivals.await().getOrThrow() @@ -52,6 +48,7 @@ class FestivalListViewModel @Inject constructor( festivals = popularFestivals.festivals.map { it.toUiState() }, ), festivals = festivalsPage.festivals.map { it.toUiState() }, + festivalFilter = festivalFilter.toUiState(), isLastPage = festivalsPage.isLastPage, ) }.onFailure { @@ -64,11 +61,51 @@ class FestivalListViewModel @Inject constructor( } } + fun loadFestivals(festivalFilterUiState: FestivalFilterUiState? = null) { + val successUiState = uiState.value as? FestivalListUiState.Success ?: return + + viewModelScope.launch { + val currentFestivals = getCurrentFestivals(festivalFilterUiState) + + festivalRepository.loadFestivals( + festivalFilter = festivalFilter, + lastFestivalId = currentFestivals.lastOrNull()?.id, + lastStartDate = currentFestivals.lastOrNull()?.startDate, + ).onSuccess { festivalsPage -> + _uiState.value = FestivalListUiState.Success( + PopularFestivalUiState( + title = successUiState.popularFestivalUiState.title, + festivals = successUiState.popularFestivalUiState.festivals, + ), + festivals = currentFestivals + festivalsPage.festivals.map { it.toUiState() }, + festivalFilter = festivalFilter.toUiState(), + isLastPage = festivalsPage.isLastPage, + ) + } + } + } + + private fun getCurrentFestivals(festivalFilterUiState: FestivalFilterUiState?): List { + var festivals = (uiState.value as? FestivalListUiState.Success)?.festivals ?: listOf() + + if (festivalFilterUiState != null && festivalFilter != festivalFilterUiState.toDomain()) { + festivalFilter = festivalFilterUiState.toDomain() + festivals = listOf() + } + return festivals + } + private fun FestivalFilterUiState.toDomain() = when (this) { FestivalFilterUiState.PLANNED -> FestivalFilter.PLANNED FestivalFilterUiState.PROGRESS -> FestivalFilter.PROGRESS } + private fun FestivalFilter.toUiState() = when (this) { + FestivalFilter.PLANNED -> FestivalFilterUiState.PLANNED + FestivalFilter.PROGRESS -> FestivalFilterUiState.PROGRESS + else -> FestivalFilterUiState.PLANNED + } + private fun Festival.toUiState() = FestivalItemUiState( id = id, name = name, diff --git a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/festival/FestivalListAdapter.kt b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/festival/FestivalListAdapter.kt index 4215c3fb2..abbb0c0b3 100644 --- a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/festival/FestivalListAdapter.kt +++ b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/festival/FestivalListAdapter.kt @@ -4,6 +4,7 @@ import android.view.ViewGroup import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter import com.festago.festago.presentation.ui.home.festivallist.uistate.FestivalItemUiState +import com.festago.festago.presentation.ui.home.festivallist.uistate.FestivalMoreItemUiState import com.festago.festago.presentation.ui.home.festivallist.uistate.FestivalTabUiState import com.festago.festago.presentation.ui.home.festivallist.uistate.PopularFestivalUiState @@ -16,6 +17,7 @@ class FestivalListAdapter( 1 -> FestivalListPopularViewHolder.of(parent) 2 -> FestivalListFestivalViewHolder.of(parent, onArtistClick) 3 -> FestivalListTabViewHolder.of(parent) + 4 -> FestivalListMoreItemViewHolder.of(parent) else -> throw IllegalArgumentException("Invalid viewType") } } @@ -26,6 +28,7 @@ class FestivalListAdapter( is FestivalListPopularViewHolder -> holder.bind(item as PopularFestivalUiState) is FestivalListFestivalViewHolder -> holder.bind(item as FestivalItemUiState) is FestivalListTabViewHolder -> holder.bind(item as FestivalTabUiState) + is FestivalListMoreItemViewHolder -> holder.bind(item as FestivalMoreItemUiState) } } @@ -34,6 +37,7 @@ class FestivalListAdapter( is PopularFestivalUiState -> 1 is FestivalItemUiState -> 2 is FestivalTabUiState -> 3 + is FestivalMoreItemUiState -> 4 else -> throw IllegalArgumentException("Invalid item") } } @@ -44,6 +48,7 @@ class FestivalListAdapter( oldItem is PopularFestivalUiState && newItem is PopularFestivalUiState -> true oldItem is FestivalItemUiState && newItem is FestivalItemUiState -> oldItem.id == newItem.id oldItem is FestivalTabUiState && newItem is FestivalTabUiState -> true + oldItem is FestivalMoreItemUiState && newItem is FestivalMoreItemUiState -> true else -> false } @@ -54,8 +59,9 @@ class FestivalListAdapter( oldItem is FestivalItemUiState && newItem is FestivalItemUiState -> oldItem as FestivalItemUiState == newItem - oldItem is FestivalTabUiState && newItem is FestivalTabUiState - -> true + oldItem is FestivalTabUiState && newItem is FestivalTabUiState -> true + + oldItem is FestivalMoreItemUiState && newItem is FestivalMoreItemUiState -> true else -> false } diff --git a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/festival/FestivalListMoreItemViewHolder.kt b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/festival/FestivalListMoreItemViewHolder.kt new file mode 100644 index 000000000..3d5000844 --- /dev/null +++ b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/festival/FestivalListMoreItemViewHolder.kt @@ -0,0 +1,24 @@ +package com.festago.festago.presentation.ui.home.festivallist.festival + +import android.view.LayoutInflater +import android.view.ViewGroup +import com.festago.festago.presentation.databinding.ItemFestivalListMoreItemBinding +import com.festago.festago.presentation.ui.home.festivallist.uistate.FestivalMoreItemUiState + +class FestivalListMoreItemViewHolder(val binding: ItemFestivalListMoreItemBinding) : + FestivalListViewHolder(binding) { + + fun bind(festivalMoreItemUiState: FestivalMoreItemUiState) { + } + + companion object { + fun of(parent: ViewGroup): FestivalListMoreItemViewHolder { + val binding = ItemFestivalListMoreItemBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false, + ) + return FestivalListMoreItemViewHolder(binding) + } + } +} diff --git a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/festival/FestivalListTabViewHolder.kt b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/festival/FestivalListTabViewHolder.kt index dcfd2f13f..9db20206a 100644 --- a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/festival/FestivalListTabViewHolder.kt +++ b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/festival/FestivalListTabViewHolder.kt @@ -4,6 +4,7 @@ import android.view.LayoutInflater import android.view.ViewGroup import com.festago.festago.presentation.R import com.festago.festago.presentation.databinding.ItemFestivalListTabBinding +import com.festago.festago.presentation.ui.home.festivallist.uistate.FestivalFilterUiState import com.festago.festago.presentation.ui.home.festivallist.uistate.FestivalTabUiState import com.google.android.material.tabs.TabLayout @@ -23,19 +24,26 @@ class FestivalListTabViewHolder(val binding: ItemFestivalListTabBinding) : } fun bind(festivalTabUiState: FestivalTabUiState) { - binding.tlFestivalListTab.clearOnTabSelectedListeners() - binding.tlFestivalListTab.addOnTabSelectedListener( - object : TabLayout.OnTabSelectedListener { - override fun onTabSelected(tab: TabLayout.Tab) { - festivalTabUiState.onClick(tab.position) - } + with(binding.tlFestivalListTab) { + clearOnTabSelectedListeners() + selectTab(getTabAt(festivalTabUiState.selectedFilter.tabPosition)) + addOnTabSelectedListener( + object : TabLayout.OnTabSelectedListener { + override fun onTabSelected(tab: TabLayout.Tab) { + festivalTabUiState.onFilterSelected(getFestivalFilterAt(tab.position)) + } - override fun onTabUnselected(tab: TabLayout.Tab) = Unit - override fun onTabReselected(tab: TabLayout.Tab) = Unit - }, - ) + override fun onTabUnselected(tab: TabLayout.Tab) = Unit + override fun onTabReselected(tab: TabLayout.Tab) = Unit + }, + ) + } } + private fun getFestivalFilterAt(position: Int): FestivalFilterUiState = + FestivalFilterUiState.values().find { it.tabPosition == position } + ?: FestivalFilterUiState.PROGRESS + companion object { fun of(parent: ViewGroup): FestivalListTabViewHolder { val binding = ItemFestivalListTabBinding.inflate( diff --git a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/uistate/FestivalFilterUiState.kt b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/uistate/FestivalFilterUiState.kt index 44ade4542..092ad203c 100644 --- a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/uistate/FestivalFilterUiState.kt +++ b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/uistate/FestivalFilterUiState.kt @@ -1,5 +1,5 @@ package com.festago.festago.presentation.ui.home.festivallist.uistate -enum class FestivalFilterUiState { - PROGRESS, PLANNED +enum class FestivalFilterUiState(val tabPosition: Int) { + PROGRESS(0), PLANNED(1) } diff --git a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/uistate/FestivalListUiState.kt b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/uistate/FestivalListUiState.kt index 0a4ef1618..0d90779c1 100644 --- a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/uistate/FestivalListUiState.kt +++ b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/uistate/FestivalListUiState.kt @@ -6,6 +6,7 @@ sealed interface FestivalListUiState { data class Success( val popularFestivalUiState: PopularFestivalUiState, val festivals: List, + val festivalFilter: FestivalFilterUiState, val isLastPage: Boolean, ) : FestivalListUiState diff --git a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/uistate/FestivalMoreItemUiState.kt b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/uistate/FestivalMoreItemUiState.kt new file mode 100644 index 000000000..1cc932b64 --- /dev/null +++ b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/uistate/FestivalMoreItemUiState.kt @@ -0,0 +1,3 @@ +package com.festago.festago.presentation.ui.home.festivallist.uistate + +object FestivalMoreItemUiState diff --git a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/uistate/FestivalTabUiState.kt b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/uistate/FestivalTabUiState.kt index fbdfb7147..214a38777 100644 --- a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/uistate/FestivalTabUiState.kt +++ b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/uistate/FestivalTabUiState.kt @@ -1,5 +1,6 @@ package com.festago.festago.presentation.ui.home.festivallist.uistate data class FestivalTabUiState( - val onClick: (Int) -> Unit, + val selectedFilter: FestivalFilterUiState, + val onFilterSelected: (FestivalFilterUiState) -> Unit, ) diff --git a/android/festago/presentation/src/main/res/layout/item_festival_list_more_item.xml b/android/festago/presentation/src/main/res/layout/item_festival_list_more_item.xml new file mode 100644 index 000000000..41942b174 --- /dev/null +++ b/android/festago/presentation/src/main/res/layout/item_festival_list_more_item.xml @@ -0,0 +1,19 @@ + + + + + + + + +