From a71ffe0bcaa99dfb7708b8d866aae101a62eeaa2 Mon Sep 17 00:00:00 2001 From: soopeach Date: Mon, 18 Mar 2024 23:22:44 +0900 Subject: [PATCH 1/2] =?UTF-8?q?[feat/upgrade=5Fsearch]:=20=EA=B7=B8?= =?UTF-8?q?=EB=A3=B9=20=EC=A0=84=EC=9A=A9=20=EA=B2=80=EC=83=89=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20=ED=94=8C=EB=9E=98=EA=B7=B8=EB=A1=9C=20=EA=B5=AC?= =?UTF-8?q?=EB=B3=84=ED=95=98=EB=8F=84=EB=A1=9D=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../view/allsearch/AllSearchFragment.kt | 6 +++ .../view/allsearch/AllSearchViewModel.kt | 8 +++ .../allsearch/SearchCategoryAllFragment.kt | 34 ++++++++++-- .../SearchCategoryRestaurantFragment.kt | 13 +++++ .../view/mypage/MyPageFragment.kt | 3 ++ .../res/drawable/ic_jmt_new_logo_grey200.png | Bin 0 -> 4412 bytes .../layout/fragment_search_category_all.xml | 51 +++++++++++++----- .../fragment_search_category_restaurant.xml | 9 ++++ ...restaurant_is_only_for_people_in_group.xml | 20 +++++++ .../src/main/res/layout/there_is_no_group.xml | 29 ++++++++++ .../main/res/navigation/main_nav_graph.xml | 4 ++ 11 files changed, 161 insertions(+), 16 deletions(-) create mode 100644 presentation/src/main/res/drawable/ic_jmt_new_logo_grey200.png create mode 100644 presentation/src/main/res/layout/restaurant_is_only_for_people_in_group.xml create mode 100644 presentation/src/main/res/layout/there_is_no_group.xml diff --git a/presentation/src/main/java/org/gdsc/presentation/view/allsearch/AllSearchFragment.kt b/presentation/src/main/java/org/gdsc/presentation/view/allsearch/AllSearchFragment.kt index 62358a27..01fdfda8 100644 --- a/presentation/src/main/java/org/gdsc/presentation/view/allsearch/AllSearchFragment.kt +++ b/presentation/src/main/java/org/gdsc/presentation/view/allsearch/AllSearchFragment.kt @@ -11,6 +11,7 @@ import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.navigation.fragment.findNavController +import androidx.navigation.fragment.navArgs import com.google.android.material.chip.Chip import dagger.hilt.android.AndroidEntryPoint import org.gdsc.presentation.R @@ -30,6 +31,8 @@ class AllSearchFragment : Fragment() { private val viewModel: AllSearchViewModel by activityViewModels() + private val navArgs: AllSearchFragmentArgs by navArgs() + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? @@ -41,6 +44,9 @@ class AllSearchFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + // 그룹 전용인지 아닌지 판별하는 용도 + viewModel.isForGroupState(navArgs.isForGroup) + parent.changeToolbarTitle("검색") binding.searchBar.setSearchViewListener(searchListener) diff --git a/presentation/src/main/java/org/gdsc/presentation/view/allsearch/AllSearchViewModel.kt b/presentation/src/main/java/org/gdsc/presentation/view/allsearch/AllSearchViewModel.kt index e18b1357..64146b9c 100644 --- a/presentation/src/main/java/org/gdsc/presentation/view/allsearch/AllSearchViewModel.kt +++ b/presentation/src/main/java/org/gdsc/presentation/view/allsearch/AllSearchViewModel.kt @@ -48,6 +48,10 @@ class AllSearchViewModel @Inject constructor( } } + private var _isForGroup = MutableStateFlow(false) + val isForGroup: StateFlow + get() = _isForGroup + private var _searchKeyword = MutableStateFlow("") val searchKeyword: StateFlow get() = _searchKeyword @@ -85,6 +89,10 @@ class AllSearchViewModel @Inject constructor( val searchedKeywordsState: StateFlow> get() = _searchedKeywordsState + + fun isForGroupState(isForGroup: Boolean) { + _isForGroup.value = isForGroup + } fun setSearchKeyword(keyword: String) { _searchKeyword.value = keyword } diff --git a/presentation/src/main/java/org/gdsc/presentation/view/allsearch/SearchCategoryAllFragment.kt b/presentation/src/main/java/org/gdsc/presentation/view/allsearch/SearchCategoryAllFragment.kt index 8c2247ec..168dd3a0 100644 --- a/presentation/src/main/java/org/gdsc/presentation/view/allsearch/SearchCategoryAllFragment.kt +++ b/presentation/src/main/java/org/gdsc/presentation/view/allsearch/SearchCategoryAllFragment.kt @@ -7,6 +7,7 @@ import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels +import androidx.navigation.fragment.navArgs import androidx.recyclerview.widget.LinearLayoutManager import androidx.viewpager2.widget.ViewPager2 import dagger.hilt.android.AndroidEntryPoint @@ -22,12 +23,12 @@ import org.gdsc.presentation.view.allsearch.adapter.SearchCategoryRestaurantPrev @AndroidEntryPoint class SearchCategoryAllFragment( private val searchKeyword: String -): Fragment() { +) : Fragment() { private var _binding: FragmentSearchCategoryAllBinding? = null private val binding get() = _binding!! - val viewModel: AllSearchViewModel by activityViewModels() + private val viewModel: AllSearchViewModel by activityViewModels() private val searchCategoryRestaurantPreviewAdapter = SearchCategoryRestaurantPreviewAdapter() private val searchCategoryGroupPreviewAdapter = SearchCategoryGroupPreviewAdapter() @@ -43,6 +44,31 @@ class SearchCategoryAllFragment( override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + repeatWhenUiStarted { + viewModel.isForGroup.collect { isForGroup -> + if (isForGroup.not()) { + binding.restaurantRecyclerView.visibility = View.GONE + binding.waringNoRestaurant.root.visibility = View.VISIBLE + } else { + binding.restaurantRecyclerView.visibility = View.VISIBLE + binding.waringNoRestaurant.root.visibility = View.GONE + } + } + } + + repeatWhenUiStarted { + viewModel.searchedGroupPreviewState.collect { + if (it.isEmpty()) { + binding.groupRecyclerView.visibility = View.GONE + binding.waringNoGroup.root.visibility = View.VISIBLE + } else { + binding.groupRecyclerView.visibility = View.VISIBLE + binding.waringNoGroup.root.visibility = View.GONE + + } + } + } + viewModel.searchRestaurantPreviewWithKeyword() viewModel.searchGroupPreviewWithKeyword() @@ -54,7 +80,8 @@ class SearchCategoryAllFragment( // Todo: New Adapter With Real APi binding.recommendedRestaurantRecyclerView.adapter = searchCategoryRestaurantPreviewAdapter - binding.recommendedRestaurantRecyclerView.layoutManager = LinearLayoutManager(requireContext()) + binding.recommendedRestaurantRecyclerView.layoutManager = + LinearLayoutManager(requireContext()) binding.icRestaurant.setOnClickListener { val viewPager = requireActivity().findViewById(R.id.search_category_pager) @@ -64,6 +91,7 @@ class SearchCategoryAllFragment( viewModel.setSearchKeyword(searchKeyword) observeState() } + private fun observeState() { repeatWhenUiStarted { viewModel.searchedRestaurantPreviewState.collect { diff --git a/presentation/src/main/java/org/gdsc/presentation/view/allsearch/SearchCategoryRestaurantFragment.kt b/presentation/src/main/java/org/gdsc/presentation/view/allsearch/SearchCategoryRestaurantFragment.kt index af2d5526..da482f58 100644 --- a/presentation/src/main/java/org/gdsc/presentation/view/allsearch/SearchCategoryRestaurantFragment.kt +++ b/presentation/src/main/java/org/gdsc/presentation/view/allsearch/SearchCategoryRestaurantFragment.kt @@ -9,6 +9,7 @@ import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import androidx.recyclerview.widget.LinearLayoutManager import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.collectLatest import org.gdsc.domain.DrinkPossibility import org.gdsc.domain.FoodCategory @@ -38,6 +39,18 @@ class SearchCategoryRestaurantFragment( override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + repeatWhenUiStarted { + viewModel.isForGroup.collect{ isForGroup -> + if (isForGroup.not()) { + binding.restaurantRecyclerView.visibility = View.GONE + binding.waringNoRestaurant.root.visibility = View.VISIBLE + } else { + binding.restaurantRecyclerView.visibility = View.VISIBLE + binding.waringNoRestaurant.root.visibility = View.GONE + } + } + } + observeState() setSpinners() diff --git a/presentation/src/main/java/org/gdsc/presentation/view/mypage/MyPageFragment.kt b/presentation/src/main/java/org/gdsc/presentation/view/mypage/MyPageFragment.kt index 9acd504f..e6ca3067 100644 --- a/presentation/src/main/java/org/gdsc/presentation/view/mypage/MyPageFragment.kt +++ b/presentation/src/main/java/org/gdsc/presentation/view/mypage/MyPageFragment.kt @@ -43,6 +43,9 @@ class MyPageFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) +// val navigation = MyPageFragmentDirections.actionMyPageFragmentToRestaurantDetailFragment() +// findNavController().navigate(navigation) + initUserInfo() setPager() setTabLayout() diff --git a/presentation/src/main/res/drawable/ic_jmt_new_logo_grey200.png b/presentation/src/main/res/drawable/ic_jmt_new_logo_grey200.png new file mode 100644 index 0000000000000000000000000000000000000000..f6f0ff3aecaac9aff539f4490630c4393f05ef8e GIT binary patch literal 4412 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 - - + android:layout_marginHorizontal="@dimen/default_spacing"> + + + + + + @@ -77,16 +91,27 @@ app:layout_constraintTop_toTopOf="@+id/title_group" app:layout_constraintBottom_toBottomOf="@+id/title_group"/> - - + app:layout_constraintEnd_toEndOf="parent"> + + + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/layout/restaurant_is_only_for_people_in_group.xml b/presentation/src/main/res/layout/restaurant_is_only_for_people_in_group.xml new file mode 100644 index 00000000..42852b2f --- /dev/null +++ b/presentation/src/main/res/layout/restaurant_is_only_for_people_in_group.xml @@ -0,0 +1,20 @@ + + + + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/layout/there_is_no_group.xml b/presentation/src/main/res/layout/there_is_no_group.xml new file mode 100644 index 00000000..f3da2aba --- /dev/null +++ b/presentation/src/main/res/layout/there_is_no_group.xml @@ -0,0 +1,29 @@ + + + + + + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/navigation/main_nav_graph.xml b/presentation/src/main/res/navigation/main_nav_graph.xml index 84b88c04..36c6da66 100644 --- a/presentation/src/main/res/navigation/main_nav_graph.xml +++ b/presentation/src/main/res/navigation/main_nav_graph.xml @@ -22,6 +22,10 @@ + Date: Tue, 19 Mar 2024 01:32:47 +0900 Subject: [PATCH 2/2] =?UTF-8?q?[feat/upgrade=5Fsearch]:=20=EA=B7=B8?= =?UTF-8?q?=EB=A3=B9=20=EA=B0=80=EC=A0=B8=EC=98=A4=EB=8A=94=20API=20?= =?UTF-8?q?=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../database/GroupBySearchPagingSource.kt | 33 +++++++++++++++++++ .../gdsc/data/datasource/GroupDataSource.kt | 4 +++ .../data/datasource/GroupDataSourceImpl.kt | 28 ++++++++++++++-- .../data/repository/GroupRepositoryImpl.kt | 6 ++++ .../gdsc/domain/repository/GroupRepository.kt | 4 +++ .../domain/usecase/GetGroupBySearchUseCase.kt | 18 ++++++++++ .../view/allsearch/AllSearchViewModel.kt | 16 ++++++++- .../allsearch/SearchCategoryAllFragment.kt | 8 ++--- .../allsearch/SearchCategoryGroupFragment.kt | 27 +++++++++++++++ .../SearchCategoryRestaurantFragment.kt | 4 +-- .../adapter/SearchCategoryGroupAdapter.kt | 14 ++++---- .../layout/fragment_search_category_all.xml | 4 +-- .../layout/fragment_search_category_group.xml | 26 +++++++++++---- .../fragment_search_category_restaurant.xml | 2 +- 14 files changed, 167 insertions(+), 27 deletions(-) create mode 100644 data/src/main/java/org/gdsc/data/database/GroupBySearchPagingSource.kt create mode 100644 domain/src/main/java/org/gdsc/domain/usecase/GetGroupBySearchUseCase.kt diff --git a/data/src/main/java/org/gdsc/data/database/GroupBySearchPagingSource.kt b/data/src/main/java/org/gdsc/data/database/GroupBySearchPagingSource.kt new file mode 100644 index 00000000..a24d7c3c --- /dev/null +++ b/data/src/main/java/org/gdsc/data/database/GroupBySearchPagingSource.kt @@ -0,0 +1,33 @@ +package org.gdsc.data.database + +import androidx.paging.PagingSource +import androidx.paging.PagingState +import org.gdsc.data.network.GroupAPI +import org.gdsc.domain.model.GroupPreview +import org.gdsc.domain.model.request.GroupSearchRequest + +class GroupBySearchPagingSource( + private val api: GroupAPI, + private val groupSearchRequest: GroupSearchRequest, +): PagingSource() { + override fun getRefreshKey(state: PagingState): Int? { + return state.anchorPosition?.let { anchorPosition -> + val anchorPage = state.closestPageToPosition(anchorPosition) + anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1) + } + } + + override suspend fun load(params: LoadParams): LoadResult { + val page = params.key ?: 1 + return try { + val items = api.searchGroup(groupSearchRequest) + LoadResult.Page( + data = items.data.groupList, + prevKey = null, + nextKey = if (items.data.groupList.isEmpty()) null else page + 1 + ) + } catch (e: Exception) { + return LoadResult.Error(e) + } + } +} \ No newline at end of file diff --git a/data/src/main/java/org/gdsc/data/datasource/GroupDataSource.kt b/data/src/main/java/org/gdsc/data/datasource/GroupDataSource.kt index 8653d6e9..f4559e39 100644 --- a/data/src/main/java/org/gdsc/data/datasource/GroupDataSource.kt +++ b/data/src/main/java/org/gdsc/data/datasource/GroupDataSource.kt @@ -1,5 +1,7 @@ package org.gdsc.data.datasource +import androidx.paging.PagingData +import kotlinx.coroutines.flow.Flow import org.gdsc.domain.model.GroupPreview import org.gdsc.domain.model.response.Group @@ -10,4 +12,6 @@ interface GroupDataSource { suspend fun selectGroup(groupId: Int): String suspend fun searchGroup(keyword: String, limitCount: Int): List + + suspend fun searchPagingGroup(keyword: String): Flow> } \ No newline at end of file diff --git a/data/src/main/java/org/gdsc/data/datasource/GroupDataSourceImpl.kt b/data/src/main/java/org/gdsc/data/datasource/GroupDataSourceImpl.kt index 5ce7b982..c4cab792 100644 --- a/data/src/main/java/org/gdsc/data/datasource/GroupDataSourceImpl.kt +++ b/data/src/main/java/org/gdsc/data/datasource/GroupDataSourceImpl.kt @@ -1,7 +1,13 @@ package org.gdsc.data.datasource -import android.util.Log -import org.gdsc.data.database.GroupPaging +import androidx.paging.Pager +import androidx.paging.PagingConfig +import androidx.paging.PagingData +import androidx.paging.cachedIn +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import org.gdsc.data.database.GroupBySearchPagingSource import org.gdsc.data.network.GroupAPI import org.gdsc.domain.model.GroupPreview import org.gdsc.domain.model.request.GroupSearchRequest @@ -10,7 +16,7 @@ import javax.inject.Inject class GroupDataSourceImpl @Inject constructor( private val groupAPI: GroupAPI, -): GroupDataSource { +) : GroupDataSource { override suspend fun getMyGroups(): List { runCatching { groupAPI.getMyGroups() @@ -36,4 +42,20 @@ class GroupDataSourceImpl @Inject constructor( override suspend fun searchGroup(keyword: String, limitCount: Int): List { return groupAPI.searchGroup(GroupSearchRequest(keyword)).data.groupList.take(limitCount) } + + override suspend fun searchPagingGroup( + keyword: String, + ): Flow> { + return Pager( + config = PagingConfig( + pageSize = 20, + enablePlaceholders = true + ) + ) { + GroupBySearchPagingSource( + groupAPI, + GroupSearchRequest(keyword)) + }.flow.cachedIn(CoroutineScope(Dispatchers.IO)) + + } } \ No newline at end of file diff --git a/data/src/main/java/org/gdsc/data/repository/GroupRepositoryImpl.kt b/data/src/main/java/org/gdsc/data/repository/GroupRepositoryImpl.kt index 6be11426..0ef2de30 100644 --- a/data/src/main/java/org/gdsc/data/repository/GroupRepositoryImpl.kt +++ b/data/src/main/java/org/gdsc/data/repository/GroupRepositoryImpl.kt @@ -1,5 +1,7 @@ package org.gdsc.data.repository +import androidx.paging.PagingData +import kotlinx.coroutines.flow.Flow import org.gdsc.data.datasource.GroupDataSource import org.gdsc.domain.model.GroupPreview import org.gdsc.domain.model.response.Group @@ -20,4 +22,8 @@ class GroupRepositoryImpl @Inject constructor( override suspend fun searchGroup(keyword: String, limitCount: Int): List { return groupDataSource.searchGroup(keyword, limitCount) } + + override suspend fun searchPagingGroup(keyword: String): Flow> { + return groupDataSource.searchPagingGroup(keyword) + } } \ No newline at end of file diff --git a/domain/src/main/java/org/gdsc/domain/repository/GroupRepository.kt b/domain/src/main/java/org/gdsc/domain/repository/GroupRepository.kt index b81a58c0..6d8f050a 100644 --- a/domain/src/main/java/org/gdsc/domain/repository/GroupRepository.kt +++ b/domain/src/main/java/org/gdsc/domain/repository/GroupRepository.kt @@ -1,5 +1,7 @@ package org.gdsc.domain.repository +import androidx.paging.PagingData +import kotlinx.coroutines.flow.Flow import org.gdsc.domain.model.GroupPreview import org.gdsc.domain.model.response.Group @@ -9,4 +11,6 @@ interface GroupRepository { suspend fun selectGroup(groupId: Int): String suspend fun searchGroup(keyword: String, limitCount: Int): List + + suspend fun searchPagingGroup(keyword: String): Flow> } \ No newline at end of file diff --git a/domain/src/main/java/org/gdsc/domain/usecase/GetGroupBySearchUseCase.kt b/domain/src/main/java/org/gdsc/domain/usecase/GetGroupBySearchUseCase.kt new file mode 100644 index 00000000..ad3a0c1e --- /dev/null +++ b/domain/src/main/java/org/gdsc/domain/usecase/GetGroupBySearchUseCase.kt @@ -0,0 +1,18 @@ +package org.gdsc.domain.usecase + +import androidx.paging.PagingData +import kotlinx.coroutines.flow.Flow +import org.gdsc.domain.model.GroupPreview +import org.gdsc.domain.repository.GroupRepository +import javax.inject.Inject + +class GetGroupBySearchUseCase @Inject constructor( + private val groupRepository: GroupRepository +) { + suspend operator fun invoke( + keyword: String, + ): Flow> { + + return groupRepository.searchPagingGroup(keyword) + } +} \ No newline at end of file diff --git a/presentation/src/main/java/org/gdsc/presentation/view/allsearch/AllSearchViewModel.kt b/presentation/src/main/java/org/gdsc/presentation/view/allsearch/AllSearchViewModel.kt index 64146b9c..43982ffa 100644 --- a/presentation/src/main/java/org/gdsc/presentation/view/allsearch/AllSearchViewModel.kt +++ b/presentation/src/main/java/org/gdsc/presentation/view/allsearch/AllSearchViewModel.kt @@ -16,6 +16,7 @@ import org.gdsc.domain.SortType import org.gdsc.domain.model.GroupPreview import org.gdsc.domain.model.Location import org.gdsc.domain.model.RegisteredRestaurant +import org.gdsc.domain.usecase.GetGroupBySearchUseCase import org.gdsc.domain.usecase.GetGroupBySearchWithLimitCountUseCase import org.gdsc.domain.usecase.GetRestaurantBySearchUseCase import org.gdsc.domain.usecase.GetRestaurantBySearchWithLimitCountUseCase @@ -35,7 +36,8 @@ class AllSearchViewModel @Inject constructor( private val deleteSearchedKeywordUseCase: DeleteSearchedKeywordUseCase, private val initSearchedKeywordUseCase: InitSearchedKeywordUseCase, private val getRestaurantBySearchWithLimitCountUseCase: GetRestaurantBySearchWithLimitCountUseCase, - private val getGroupBySearchWithLimitCountUseCase: GetGroupBySearchWithLimitCountUseCase + private val getGroupBySearchWithLimitCountUseCase: GetGroupBySearchWithLimitCountUseCase, + private val getGroupBySearchUseCase: GetGroupBySearchUseCase ) : ViewModel() { init { @@ -81,6 +83,12 @@ class AllSearchViewModel @Inject constructor( private var _searchedGroupPreviewState = MutableStateFlow>(emptyList()) + val searchedGroupState: StateFlow> + get() = _searchedGroupState + + private var _searchedGroupState = + MutableStateFlow>(PagingData.empty()) + val searchedGroupPreviewState: StateFlow> get() = _searchedGroupPreviewState @@ -166,6 +174,12 @@ class AllSearchViewModel @Inject constructor( val result = getGroupBySearchWithLimitCountUseCase(searchKeyword.value, 3) _searchedGroupPreviewState.value = result } + viewModelScope.launch { + getGroupBySearchUseCase(searchKeyword.value).distinctUntilChanged() + .collect { + _searchedGroupState.value = it + } + } } diff --git a/presentation/src/main/java/org/gdsc/presentation/view/allsearch/SearchCategoryAllFragment.kt b/presentation/src/main/java/org/gdsc/presentation/view/allsearch/SearchCategoryAllFragment.kt index 168dd3a0..090aa9ac 100644 --- a/presentation/src/main/java/org/gdsc/presentation/view/allsearch/SearchCategoryAllFragment.kt +++ b/presentation/src/main/java/org/gdsc/presentation/view/allsearch/SearchCategoryAllFragment.kt @@ -48,10 +48,10 @@ class SearchCategoryAllFragment( viewModel.isForGroup.collect { isForGroup -> if (isForGroup.not()) { binding.restaurantRecyclerView.visibility = View.GONE - binding.waringNoRestaurant.root.visibility = View.VISIBLE + binding.warningNoRestaurant.root.visibility = View.VISIBLE } else { binding.restaurantRecyclerView.visibility = View.VISIBLE - binding.waringNoRestaurant.root.visibility = View.GONE + binding.warningNoRestaurant.root.visibility = View.GONE } } } @@ -60,10 +60,10 @@ class SearchCategoryAllFragment( viewModel.searchedGroupPreviewState.collect { if (it.isEmpty()) { binding.groupRecyclerView.visibility = View.GONE - binding.waringNoGroup.root.visibility = View.VISIBLE + binding.warningNoGroup.root.visibility = View.VISIBLE } else { binding.groupRecyclerView.visibility = View.VISIBLE - binding.waringNoGroup.root.visibility = View.GONE + binding.warningNoGroup.root.visibility = View.GONE } } diff --git a/presentation/src/main/java/org/gdsc/presentation/view/allsearch/SearchCategoryGroupFragment.kt b/presentation/src/main/java/org/gdsc/presentation/view/allsearch/SearchCategoryGroupFragment.kt index 487d0ff7..5cc44206 100644 --- a/presentation/src/main/java/org/gdsc/presentation/view/allsearch/SearchCategoryGroupFragment.kt +++ b/presentation/src/main/java/org/gdsc/presentation/view/allsearch/SearchCategoryGroupFragment.kt @@ -6,7 +6,11 @@ import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels +import androidx.recyclerview.widget.LinearLayoutManager import org.gdsc.presentation.databinding.FragmentSearchCategoryGroupBinding +import org.gdsc.presentation.utils.repeatWhenUiStarted +import org.gdsc.presentation.view.allsearch.adapter.SearchCategoryGroupAdapter +import org.gdsc.presentation.view.allsearch.adapter.SearchCategoryGroupPreviewAdapter class SearchCategoryGroupFragment( private val searchKeyword: String @@ -27,5 +31,28 @@ class SearchCategoryGroupFragment( override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + + repeatWhenUiStarted { + viewModel.searchedGroupPreviewState.collect { + if (it.isEmpty()) { + binding.groupRecyclerView.visibility = View.GONE + binding.warningNoGroup.root.visibility = View.VISIBLE + } else { + binding.groupRecyclerView.visibility = View.VISIBLE + binding.warningNoGroup.root.visibility = View.GONE + + } + } + } + + val adapter = SearchCategoryGroupAdapter() + binding.groupRecyclerView.adapter = adapter + binding.groupRecyclerView.layoutManager = LinearLayoutManager(requireContext()) + + repeatWhenUiStarted { + viewModel.searchedGroupState.collect { + adapter.submitData(it) + } + } } } \ No newline at end of file diff --git a/presentation/src/main/java/org/gdsc/presentation/view/allsearch/SearchCategoryRestaurantFragment.kt b/presentation/src/main/java/org/gdsc/presentation/view/allsearch/SearchCategoryRestaurantFragment.kt index da482f58..6b0c2233 100644 --- a/presentation/src/main/java/org/gdsc/presentation/view/allsearch/SearchCategoryRestaurantFragment.kt +++ b/presentation/src/main/java/org/gdsc/presentation/view/allsearch/SearchCategoryRestaurantFragment.kt @@ -43,10 +43,10 @@ class SearchCategoryRestaurantFragment( viewModel.isForGroup.collect{ isForGroup -> if (isForGroup.not()) { binding.restaurantRecyclerView.visibility = View.GONE - binding.waringNoRestaurant.root.visibility = View.VISIBLE + binding.warningNoRestaurant.root.visibility = View.VISIBLE } else { binding.restaurantRecyclerView.visibility = View.VISIBLE - binding.waringNoRestaurant.root.visibility = View.GONE + binding.warningNoRestaurant.root.visibility = View.GONE } } } diff --git a/presentation/src/main/java/org/gdsc/presentation/view/allsearch/adapter/SearchCategoryGroupAdapter.kt b/presentation/src/main/java/org/gdsc/presentation/view/allsearch/adapter/SearchCategoryGroupAdapter.kt index e8d5e37a..19be5427 100644 --- a/presentation/src/main/java/org/gdsc/presentation/view/allsearch/adapter/SearchCategoryGroupAdapter.kt +++ b/presentation/src/main/java/org/gdsc/presentation/view/allsearch/adapter/SearchCategoryGroupAdapter.kt @@ -6,21 +6,21 @@ import androidx.paging.PagingDataAdapter import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView import com.bumptech.glide.Glide -import org.gdsc.domain.model.GroupInfo +import org.gdsc.domain.model.GroupPreview import org.gdsc.presentation.R import org.gdsc.presentation.databinding.ItemSearchGroupBinding class SearchCategoryGroupAdapter() : - PagingDataAdapter( + PagingDataAdapter( DiffCallback ) { - companion object DiffCallback : DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: GroupInfo, newItem: GroupInfo): Boolean { + companion object DiffCallback : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: GroupPreview, newItem: GroupPreview): Boolean { return oldItem.groupId == newItem.groupId } - override fun areContentsTheSame(oldItem: GroupInfo, newItem: GroupInfo): Boolean { + override fun areContentsTheSame(oldItem: GroupPreview, newItem: GroupPreview): Boolean { return oldItem == newItem } } @@ -28,10 +28,10 @@ class SearchCategoryGroupAdapter() : class SearchCategoryGroupViewHolder( private val binding: ItemSearchGroupBinding, ) : RecyclerView.ViewHolder(binding.root) { - fun bind(item: GroupInfo) { + fun bind(item: GroupPreview) { binding.run { Glide.with(itemView.context) - .load(item.groupProfileImageUrl) + .load("https://picsum.photos/200") .placeholder(R.drawable.base_profile_image) .into(ivGroupImage) diff --git a/presentation/src/main/res/layout/fragment_search_category_all.xml b/presentation/src/main/res/layout/fragment_search_category_all.xml index be857990..b9776597 100644 --- a/presentation/src/main/res/layout/fragment_search_category_all.xml +++ b/presentation/src/main/res/layout/fragment_search_category_all.xml @@ -43,7 +43,7 @@ android:layout_marginHorizontal="@dimen/default_spacing"> @@ -102,7 +102,7 @@ app:layout_constraintEnd_toEndOf="parent"> diff --git a/presentation/src/main/res/layout/fragment_search_category_group.xml b/presentation/src/main/res/layout/fragment_search_category_group.xml index f5cb348d..da6c9225 100644 --- a/presentation/src/main/res/layout/fragment_search_category_group.xml +++ b/presentation/src/main/res/layout/fragment_search_category_group.xml @@ -1,15 +1,27 @@ + android:layout_height="wrap_content" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools"> - + tools:layout_editor_absoluteX="0dp" /> + + \ No newline at end of file diff --git a/presentation/src/main/res/layout/fragment_search_category_restaurant.xml b/presentation/src/main/res/layout/fragment_search_category_restaurant.xml index d1193312..9650ccd7 100644 --- a/presentation/src/main/res/layout/fragment_search_category_restaurant.xml +++ b/presentation/src/main/res/layout/fragment_search_category_restaurant.xml @@ -65,7 +65,7 @@