Skip to content

Commit

Permalink
Merge pull request #98 from team-JMT/feat/upgrade_search
Browse files Browse the repository at this point in the history
그룹 전용 검색 화면 플래그로 구별하도록 구현, 그룹 검색 API 연결
  • Loading branch information
soopeach authored Mar 18, 2024
2 parents 314bbce + b818d45 commit decc5d5
Show file tree
Hide file tree
Showing 20 changed files with 331 additions and 31 deletions.
Original file line number Diff line number Diff line change
@@ -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<Int, GroupPreview>() {
override fun getRefreshKey(state: PagingState<Int, GroupPreview>): 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<Int>): LoadResult<Int, GroupPreview> {
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)
}
}
}
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -10,4 +12,6 @@ interface GroupDataSource {
suspend fun selectGroup(groupId: Int): String

suspend fun searchGroup(keyword: String, limitCount: Int): List<GroupPreview>

suspend fun searchPagingGroup(keyword: String): Flow<PagingData<GroupPreview>>
}
28 changes: 25 additions & 3 deletions data/src/main/java/org/gdsc/data/datasource/GroupDataSourceImpl.kt
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -10,7 +16,7 @@ import javax.inject.Inject

class GroupDataSourceImpl @Inject constructor(
private val groupAPI: GroupAPI,
): GroupDataSource {
) : GroupDataSource {
override suspend fun getMyGroups(): List<Group> {
runCatching {
groupAPI.getMyGroups()
Expand All @@ -36,4 +42,20 @@ class GroupDataSourceImpl @Inject constructor(
override suspend fun searchGroup(keyword: String, limitCount: Int): List<GroupPreview> {
return groupAPI.searchGroup(GroupSearchRequest(keyword)).data.groupList.take(limitCount)
}

override suspend fun searchPagingGroup(
keyword: String,
): Flow<PagingData<GroupPreview>> {
return Pager(
config = PagingConfig(
pageSize = 20,
enablePlaceholders = true
)
) {
GroupBySearchPagingSource(
groupAPI,
GroupSearchRequest(keyword))
}.flow.cachedIn(CoroutineScope(Dispatchers.IO))

}
}
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -20,4 +22,8 @@ class GroupRepositoryImpl @Inject constructor(
override suspend fun searchGroup(keyword: String, limitCount: Int): List<GroupPreview> {
return groupDataSource.searchGroup(keyword, limitCount)
}

override suspend fun searchPagingGroup(keyword: String): Flow<PagingData<GroupPreview>> {
return groupDataSource.searchPagingGroup(keyword)
}
}
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -9,4 +11,6 @@ interface GroupRepository {
suspend fun selectGroup(groupId: Int): String

suspend fun searchGroup(keyword: String, limitCount: Int): List<GroupPreview>

suspend fun searchPagingGroup(keyword: String): Flow<PagingData<GroupPreview>>
}
Original file line number Diff line number Diff line change
@@ -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<PagingData<GroupPreview>> {

return groupRepository.searchPagingGroup(keyword)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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?
Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 {
Expand All @@ -48,6 +50,10 @@ class AllSearchViewModel @Inject constructor(
}
}

private var _isForGroup = MutableStateFlow(false)
val isForGroup: StateFlow<Boolean>
get() = _isForGroup

private var _searchKeyword = MutableStateFlow("")
val searchKeyword: StateFlow<String>
get() = _searchKeyword
Expand Down Expand Up @@ -77,6 +83,12 @@ class AllSearchViewModel @Inject constructor(
private var _searchedGroupPreviewState =
MutableStateFlow<List<GroupPreview>>(emptyList())

val searchedGroupState: StateFlow<PagingData<GroupPreview>>
get() = _searchedGroupState

private var _searchedGroupState =
MutableStateFlow<PagingData<GroupPreview>>(PagingData.empty())

val searchedGroupPreviewState: StateFlow<List<GroupPreview>>
get() = _searchedGroupPreviewState

Expand All @@ -85,6 +97,10 @@ class AllSearchViewModel @Inject constructor(
val searchedKeywordsState: StateFlow<List<String>>
get() = _searchedKeywordsState


fun isForGroupState(isForGroup: Boolean) {
_isForGroup.value = isForGroup
}
fun setSearchKeyword(keyword: String) {
_searchKeyword.value = keyword
}
Expand Down Expand Up @@ -158,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
}
}
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.viewbinding.ViewBinding
Expand All @@ -31,12 +32,13 @@ import org.gdsc.presentation.view.allsearch.adapter.SearchCategoryRestaurantPrev
@AndroidEntryPoint
class SearchCategoryAllFragment(
private val searchKeyword: String
) : Fragment() {
): Fragment(), ViewHolderBindListener {

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(this)
private val searchCategoryGroupPreviewAdapter = SearchCategoryGroupPreviewAdapter(this)
Expand All @@ -52,6 +54,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.warningNoRestaurant.root.visibility = View.VISIBLE
} else {
binding.restaurantRecyclerView.visibility = View.VISIBLE
binding.warningNoRestaurant.root.visibility = View.GONE
}
}
}

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

}
}
}

viewModel.searchRestaurantPreviewWithKeyword()
viewModel.searchGroupPreviewWithKeyword()

Expand All @@ -63,7 +90,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<ViewPager2>(R.id.search_category_pager)
Expand All @@ -73,6 +101,7 @@ class SearchCategoryAllFragment(
viewModel.setSearchKeyword(searchKeyword)
observeState()
}

private fun observeState() {
repeatWhenUiStarted {
viewModel.searchedRestaurantPreviewState.collect {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.viewbinding.ViewBinding
import com.bumptech.glide.Glide
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
Expand Down Expand Up @@ -46,6 +47,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.warningNoRestaurant.root.visibility = View.VISIBLE
} else {
binding.restaurantRecyclerView.visibility = View.VISIBLE
binding.warningNoRestaurant.root.visibility = View.GONE
}
}
}

observeState()
setSpinners()

Expand Down
Loading

0 comments on commit decc5d5

Please sign in to comment.